From c0b165160812f5b578e360e3724c8db23ff200b9 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 6 Jan 2024 17:35:15 +0100 Subject: [PATCH] fix: Make recorder start screen more adaptive --- .../RecorderScreen/atoms/BigButton.kt | 86 ++++++++ .../molecules/AudioRecordingStart.kt | 32 +-- .../VideoRecorderPreparationSheet.kt | 204 +++++++++--------- .../molecules/VideoRecordingStart.kt | 91 +++----- 4 files changed, 227 insertions(+), 186 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BigButton.kt diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BigButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BigButton.kt new file mode 100644 index 0000000..e2f9c3f --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BigButton.kt @@ -0,0 +1,86 @@ +package app.myzel394.alibi.ui.components.RecorderScreen.atoms + +import android.Manifest +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +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.material.ripple.rememberRipple +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.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector +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.ui.utils.PermissionHelper + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun BigButton( + label: String, + icon: ImageVector, + description: String? = null, + onClick: () -> Unit, + onLongClick: () -> Unit = {}, +) { + BoxWithConstraints { + val isLarge = maxWidth > 500.dp + + Column( + modifier = Modifier + .size(if (isLarge) 250.dp else 200.dp) + .clip(CircleShape) + .semantics { + contentDescription = label + } + .combinedClickable( + interactionSource = remember { MutableInteractionSource() }, + indication = rememberRipple(color = MaterialTheme.colorScheme.primary), + onClick = onClick, + onLongClick = onLongClick, + ), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Icon( + icon, + contentDescription = null, + modifier = Modifier + .size(if (isLarge) 80.dp else 60.dp), + tint = MaterialTheme.colorScheme.primary, + ) + Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) + Text( + label, + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary, + ) + if (description != null) { + Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) + Text( + description, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt index 58a7551..c5612c2 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt @@ -32,6 +32,7 @@ import androidx.compose.ui.unit.dp import app.myzel394.alibi.R import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.BigButton import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.utils.PermissionHelper @@ -77,34 +78,11 @@ fun AudioRecordingStart( } } ) { triggerRecordAudio -> - val label = stringResource(R.string.ui_audioRecorder_action_start_label) - - Button( + BigButton( + label = stringResource(R.string.ui_audioRecorder_action_start_label), + icon = Icons.Default.Mic, onClick = triggerRecordAudio, - modifier = Modifier - .semantics { - contentDescription = label - } - .size(250.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, - ) - } - } + ) } } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt index 4f7074b..aaacbfb 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight @@ -16,7 +17,9 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CameraAlt import androidx.compose.material.icons.filled.Mic @@ -113,112 +116,119 @@ fun VideoRecorderPreparationSheet( cameraSelector = videoSettings.cameraSelector, ) } else { - Column( - modifier = Modifier - .padding(horizontal = 16.dp) - .padding(bottom = SHEET_BOTTOM_OFFSET, top = 24.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(30.dp), - ) { + BoxWithConstraints { + val constraints = this + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(bottom = SHEET_BOTTOM_OFFSET, top = 24.dp) + .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(30.dp), ) { - Icon( - Icons.Default.CameraAlt, - contentDescription = null, - modifier = Modifier - .size(80.dp), - ) - Text( - stringResource(R.string.ui_videoRecorder_action_start_settings_label), - style = MaterialTheme.typography.labelLarge, - ) - } - PermissionRequester( - permission = Manifest.permission.RECORD_AUDIO, - icon = Icons.Default.Mic, - onPermissionAvailable = { - videoSettings.enableAudio = !videoSettings.enableAudio - }, - ) { trigger -> - GlobalSwitch( - label = stringResource(R.string.ui_videoRecorder_action_start_settings_enableAudio_label), - checked = videoSettings.enableAudio, - onCheckedChange = { - trigger() - } - ) - } - - Column( - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - Text( - stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_selection_label), - style = MaterialTheme.typography.labelLarge, - textAlign = TextAlign.Start, - modifier = Modifier.fillMaxWidth() - ) - CamerasSelection( - cameras = cameras, - videoSettings = videoSettings, - ) - } - - val label = - stringResource(R.string.ui_videoRecorder_action_start_settings_start_label) - - Column( - verticalArrangement = Arrangement.spacedBy(8.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - PermissionRequester( - permission = Manifest.permission.CAMERA, - icon = Icons.Default.CameraAlt, - onPermissionAvailable = { - onStartRecording() - } - ) { trigger -> - Row( - modifier = Modifier - .fillMaxWidth() - .height(BIG_PRIMARY_BUTTON_SIZE) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.primary) - .padding(16.dp) - .semantics { - contentDescription = label - } - .pointerInput(Unit) { - detectTapGestures( - onLongPress = { - onPreviewVisible() - }, - onTap = { - trigger() - } - ) - }, - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - label, - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onPrimary, + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + if (constraints.maxHeight > 600.dp) { + Icon( + Icons.Default.CameraAlt, + contentDescription = null, + modifier = Modifier + .size(80.dp), ) } + Text( + stringResource(R.string.ui_videoRecorder_action_start_settings_label), + style = MaterialTheme.typography.labelLarge, + ) + } + PermissionRequester( + permission = Manifest.permission.RECORD_AUDIO, + icon = Icons.Default.Mic, + onPermissionAvailable = { + videoSettings.enableAudio = !videoSettings.enableAudio + }, + ) { trigger -> + GlobalSwitch( + label = stringResource(R.string.ui_videoRecorder_action_start_settings_enableAudio_label), + checked = videoSettings.enableAudio, + onCheckedChange = { + trigger() + } + ) } - if (PermissionHelper.hasGranted(context, Manifest.permission.CAMERA)) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { Text( - stringResource( - R.string.ui_videoRecorder_action_preview_label - ), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, + stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_selection_label), + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Start, + modifier = Modifier.fillMaxWidth() ) + CamerasSelection( + cameras = cameras, + videoSettings = videoSettings, + ) + } + + val label = + stringResource(R.string.ui_videoRecorder_action_start_settings_start_label) + + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + PermissionRequester( + permission = Manifest.permission.CAMERA, + icon = Icons.Default.CameraAlt, + onPermissionAvailable = { + onStartRecording() + } + ) { trigger -> + Row( + modifier = Modifier + .fillMaxWidth() + .height(BIG_PRIMARY_BUTTON_SIZE) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.primary) + .padding(16.dp) + .semantics { + contentDescription = label + } + .pointerInput(Unit) { + detectTapGestures( + onLongPress = { + onPreviewVisible() + }, + onTap = { + trigger() + } + ) + }, + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + label, + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onPrimary, + ) + } + } + + if (PermissionHelper.hasGranted(context, Manifest.permission.CAMERA)) { + Text( + stringResource( + R.string.ui_videoRecorder_action_preview_label + ), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } } } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt index 9716029..d6acb1e 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt @@ -46,6 +46,7 @@ import app.myzel394.alibi.R import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.BigButton import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.models.VideoRecorderModel import app.myzel394.alibi.ui.utils.PermissionHelper @@ -87,71 +88,37 @@ fun VideoRecordingStart( showSheet = true } ) { triggerExternalStorage -> - PermissionRequester( - permission = Manifest.permission.CAMERA, + BigButton( + label = stringResource(R.string.ui_videoRecorder_action_start_label), + description = stringResource(R.string.ui_videoRecorder_action_configure_label), icon = Icons.Default.CameraAlt, - onPermissionAvailable = { + onLongClick = { + if (appSettings.requiresExternalStoragePermission(context)) { + triggerExternalStorage() + return@BigButton + } + showSheet = true }, - ) { triggerCamera -> - val label = stringResource(R.string.ui_videoRecorder_action_start_label) + onClick = { + if (appSettings.requiresExternalStoragePermission(context)) { + triggerExternalStorage() + return@BigButton + } - Column( - modifier = Modifier - .size(250.dp) - .clip(CircleShape) - .semantics { - contentDescription = label - } - .combinedClickable( - interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(color = MaterialTheme.colorScheme.primary), - onClick = { - if (appSettings.requiresExternalStoragePermission(context)) { - triggerExternalStorage() - return@combinedClickable - } - - if (PermissionHelper.hasGranted( - context, - Manifest.permission.CAMERA - ) && PermissionHelper.hasGranted( - context, - Manifest.permission.RECORD_AUDIO - ) - ) { - videoRecorder.startRecording(context, appSettings) - } else { - showSheet = true - } - }, - onLongClick = { - showSheet = true - }, - ), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { - Icon( - Icons.Default.CameraAlt, - contentDescription = null, - modifier = Modifier - .size(80.dp), - tint = MaterialTheme.colorScheme.primary, - ) - Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) - Text( - label, - style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.primary, - ) - Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) - Text( - stringResource(R.string.ui_videoRecorder_action_configure_label), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - } + if (PermissionHelper.hasGranted( + context, + Manifest.permission.CAMERA + ) && PermissionHelper.hasGranted( + context, + Manifest.permission.RECORD_AUDIO + ) + ) { + videoRecorder.startRecording(context, appSettings) + } else { + showSheet = true + } + }, + ) } } \ No newline at end of file