feat: Add AudioRecordingStart and VideoRecordingStart

This commit is contained in:
Myzel394 2023-12-02 18:45:25 +01:00
parent e7989e2eba
commit 261753ad75
No known key found for this signature in database
GPG Key ID: 79CC92F37B3E1A2B
6 changed files with 207 additions and 72 deletions

View File

@ -23,9 +23,8 @@ import app.myzel394.alibi.ui.enums.Screen
import app.myzel394.alibi.ui.models.AudioRecorderModel
import app.myzel394.alibi.ui.models.VideoRecorderModel
import app.myzel394.alibi.ui.screens.AboutScreen
import app.myzel394.alibi.ui.screens.AudioRecorderScreen
import app.myzel394.alibi.ui.screens.RecorderScreen
import app.myzel394.alibi.ui.screens.CustomRecordingNotificationsScreen
import app.myzel394.alibi.ui.screens.POCVideo
import app.myzel394.alibi.ui.screens.SettingsScreen
import app.myzel394.alibi.ui.screens.WelcomeScreen
@ -73,9 +72,10 @@ fun Navigation(
scaleOut(targetScale = SCALE_IN) + fadeOut(tween(durationMillis = 150))
}
) {
AudioRecorderScreen(
RecorderScreen(
navController = navController,
audioRecorder = audioRecorder,
videoRecorder = videoRecorder,
)
}
composable(

View File

@ -0,0 +1,90 @@
package app.myzel394.alibi.ui.components.AudioRecorder.atoms
import android.Manifest
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Mic
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import app.myzel394.alibi.R
import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.ui.components.atoms.PermissionRequester
import app.myzel394.alibi.ui.models.AudioRecorderModel
@Composable
fun AudioRecordingStart(
audioRecorder: AudioRecorderModel,
appSettings: AppSettings,
) {
val context = LocalContext.current
// We can't get the current `notificationDetails` inside the
// `onPermissionAvailable` function. We'll instead use this hack
// with `LaunchedEffect` to get the current value.
var startRecording by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(startRecording) {
if (startRecording) {
startRecording = false
audioRecorder.startRecording(context, appSettings)
}
}
PermissionRequester(
permission = Manifest.permission.RECORD_AUDIO,
icon = Icons.Default.Mic,
onPermissionAvailable = {
startRecording = true
}
) { trigger ->
val label = stringResource(R.string.ui_audioRecorder_action_start_label)
Button(
onClick = trigger,
modifier = Modifier
.semantics {
contentDescription = label
}
.size(200.dp)
.clip(shape = CircleShape),
colors = ButtonDefaults.outlinedButtonColors(),
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
Icons.Default.Mic,
contentDescription = null,
modifier = Modifier
.size(80.dp),
)
Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing))
Text(
label,
style = MaterialTheme.typography.titleSmall,
)
}
}
}
}

View File

@ -0,0 +1,92 @@
package app.myzel394.alibi.ui.components.AudioRecorder.atoms
import android.Manifest
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CameraAlt
import androidx.compose.material.icons.filled.Mic
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import app.myzel394.alibi.R
import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.ui.components.atoms.PermissionRequester
import app.myzel394.alibi.ui.models.AudioRecorderModel
import app.myzel394.alibi.ui.models.VideoRecorderModel
@Composable
fun VideoRecordingStart(
videoRecorder: VideoRecorderModel,
appSettings: AppSettings,
) {
val context = LocalContext.current
// We can't get the current `notificationDetails` inside the
// `onPermissionAvailable` function. We'll instead use this hack
// with `LaunchedEffect` to get the current value.
var startRecording by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(startRecording) {
if (startRecording) {
startRecording = false
videoRecorder.startRecording(context, appSettings)
}
}
PermissionRequester(
permission = Manifest.permission.RECORD_AUDIO,
icon = Icons.Default.Mic,
onPermissionAvailable = {
startRecording = true
}
) { trigger ->
val label = stringResource(R.string.ui_videoRecorder_action_start_label)
Button(
onClick = trigger,
modifier = Modifier
.semantics {
contentDescription = label
}
.size(200.dp)
.clip(shape = CircleShape),
colors = ButtonDefaults.outlinedButtonColors(),
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
Icons.Default.CameraAlt,
contentDescription = null,
modifier = Modifier
.size(80.dp),
)
Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing))
Text(
label,
style = MaterialTheme.typography.titleSmall,
)
}
}
}
}

View File

@ -1,6 +1,5 @@
package app.myzel394.alibi.ui.components.AudioRecorder.molecules
import android.Manifest
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
@ -11,9 +10,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Mic
import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
@ -21,15 +18,8 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
@ -37,18 +27,20 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import app.myzel394.alibi.R
import app.myzel394.alibi.dataStore
import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE
import app.myzel394.alibi.ui.components.atoms.PermissionRequester
import app.myzel394.alibi.ui.components.AudioRecorder.atoms.AudioRecordingStart
import app.myzel394.alibi.ui.components.AudioRecorder.atoms.VideoRecordingStart
import app.myzel394.alibi.ui.effects.rememberForceUpdateOnLifeCycleChange
import app.myzel394.alibi.ui.models.AudioRecorderModel
import app.myzel394.alibi.ui.models.VideoRecorderModel
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
@Composable
fun StartRecording(
audioRecorder: AudioRecorderModel,
videoRecorder: VideoRecorderModel,
// Loading this from parent, because if we load it ourselves
// and permissions have already been granted, initial
// settings will be used, instead of the actual settings.
@ -57,70 +49,26 @@ fun StartRecording(
) {
val context = LocalContext.current
// We can't get the current `notificationDetails` inside the
// `onPermissionAvailable` function. We'll instead use this hack
// with `LaunchedEffect` to get the current value.
var startRecording by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(startRecording) {
if (startRecording) {
startRecording = false
audioRecorder.startRecording(context, appSettings)
}
}
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.weight(1f))
PermissionRequester(
permission = Manifest.permission.RECORD_AUDIO,
icon = Icons.Default.Mic,
onPermissionAvailable = {
startRecording = true
},
) { trigger ->
val label = stringResource(R.string.ui_audioRecorder_action_start_label)
Button(
onClick = trigger,
modifier = Modifier
.semantics {
contentDescription = label
}
.size(200.dp)
.clip(shape = CircleShape),
colors = ButtonDefaults.outlinedButtonColors(),
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
Icons.Default.Mic,
contentDescription = null,
modifier = Modifier
.size(80.dp),
)
Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing))
Text(
label,
style = MaterialTheme.typography.titleSmall,
)
}
}
}
val settings = LocalContext
.current
.dataStore
.data
.collectAsState(initial = AppSettings.getDefaultInstance())
.value
AudioRecordingStart(
audioRecorder = audioRecorder,
appSettings = appSettings,
)
VideoRecordingStart(
videoRecorder = videoRecorder,
appSettings = appSettings,
)
Text(
stringResource(
R.string.ui_audioRecorder_action_start_description,
settings.maxDuration / 1000 / 60
appSettings.maxDuration / 1000 / 60
),
style = MaterialTheme.typography.bodySmall.copy(
color = MaterialTheme.colorScheme.onSurfaceVariant,

View File

@ -48,14 +48,16 @@ import app.myzel394.alibi.helpers.AudioBatchesFolder
import app.myzel394.alibi.helpers.BatchesFolder
import app.myzel394.alibi.ui.effects.rememberSettings
import app.myzel394.alibi.ui.models.AudioRecorderModel
import app.myzel394.alibi.ui.models.VideoRecorderModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AudioRecorderScreen(
fun RecorderScreen(
navController: NavController,
audioRecorder: AudioRecorderModel,
videoRecorder: VideoRecorderModel,
) {
val snackbarHostState = remember { SnackbarHostState() }
val context = LocalContext.current
@ -299,7 +301,9 @@ fun AudioRecorderScreen(
RecordingStatus(audioRecorder = audioRecorder)
else
StartRecording(
audioRecorder = audioRecorder, appSettings = appSettings,
audioRecorder = audioRecorder,
videoRecorder = videoRecorder,
appSettings = appSettings,
onSaveLastRecording = ::saveRecording,
)
}

View File

@ -18,7 +18,7 @@
<string name="ui_permissions_request">Please grant the permission to continue</string>
<string name="ui_permissions_permanentlyDenied_message">You will be redirected to the app settings to grant the permission there.</string>
<string name="ui_audioRecorder_action_start_label">Start Recording</string>
<string name="ui_audioRecorder_action_start_label">Start Audio Recording</string>
<string name="ui_audioRecorder_action_saveOldRecording_label">Save Recording from <xliff:g name="date">%s</xliff:g></string>
<string name="ui_audioRecorder_action_delete_label">Delete</string>
<string name="ui_audioRecorder_action_delete_confirm_title">Delete Recording?</string>
@ -138,4 +138,5 @@
<string name="ui_settings_value_videoQuality_values_hd">HD</string>
<string name="ui_settings_value_videoQuality_values_sd">Standard</string>
<string name="ui_settings_value_videoQuality_values_lowest">Lowest</string>
<string name="ui_videoRecorder_action_start_label">Start Video Recording</string>
</resources>