diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/atoms/SaveFolderSelection.kt b/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/atoms/SaveFolderSelection.kt index c84fe60..02b79e7 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/atoms/SaveFolderSelection.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/atoms/SaveFolderSelection.kt @@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Folder import androidx.compose.material.icons.filled.Lock @@ -29,6 +31,7 @@ import androidx.compose.ui.unit.dp import app.myzel394.alibi.R import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE +import app.myzel394.alibi.ui.SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS import app.myzel394.alibi.ui.components.atoms.MessageBox import app.myzel394.alibi.ui.components.atoms.MessageType @@ -48,9 +51,21 @@ fun SaveFolderSelection( CUSTOM_FOLDER to (stringResource(R.string.ui_welcome_saveFolder_values_custom) to Icons.Default.Folder), ) + @Composable + fun createModifier(a11yLabel: String, onClick: () -> Unit) = + Modifier + .fillMaxWidth() + .clip(MaterialTheme.shapes.medium) + .semantics { + contentDescription = a11yLabel + } + .clickable(onClick = onClick) + .padding(16.dp) + .padding(end = 8.dp) + Column( verticalArrangement = Arrangement.spacedBy(16.dp), - horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.verticalScroll(rememberScrollState()), ) { Column( modifier = Modifier @@ -60,27 +75,20 @@ fun SaveFolderSelection( .then(modifier), verticalArrangement = Arrangement.Center, ) { - for ((folder, pair) in OPTIONS) { - val (label, icon) = pair + let { + val label = stringResource(R.string.ui_welcome_saveFolder_values_internal) val a11yLabel = stringResource( R.string.a11y_selectValue, label ) + val folder = null Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier - .fillMaxWidth() - .clip(MaterialTheme.shapes.medium) - .semantics { - contentDescription = a11yLabel - } - .clickable { - onSaveFolderChange(folder) - } - .padding(16.dp) - .padding(end = 8.dp) + modifier = createModifier(a11yLabel) { + onSaveFolderChange(folder) + }, ) { Row( verticalAlignment = Alignment.CenterVertically, @@ -93,13 +101,100 @@ fun SaveFolderSelection( Text(label) } Icon( - icon, + Icons.Default.Lock, contentDescription = null, modifier = Modifier .size(ButtonDefaults.IconSize) ) } } + let { + val label = stringResource(R.string.ui_welcome_saveFolder_values_media) + val a11yLabel = stringResource( + R.string.a11y_selectValue, + label + ) + val folder = RECORDER_MEDIA_SELECTED_VALUE + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = createModifier(a11yLabel) { + onSaveFolderChange(folder) + }, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + RadioButton( + selected = saveFolder == folder, + onClick = { onSaveFolderChange(folder) }, + ) + Text(label) + } + Icon( + Icons.Default.Lock, + contentDescription = null, + modifier = Modifier + .size(ButtonDefaults.IconSize) + ) + } + } + let { + val label = stringResource(R.string.ui_welcome_saveFolder_values_custom) + val a11yLabel = stringResource( + R.string.a11y_selectValue, + label + ) + val folder = CUSTOM_FOLDER + + Column( + horizontalAlignment = Alignment.Start, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = createModifier(a11yLabel) { + onSaveFolderChange(folder) + }, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + RadioButton( + selected = saveFolder == folder, + onClick = { onSaveFolderChange(folder) }, + ) + Text(label) + } + Icon( + Icons.Default.Lock, + contentDescription = null, + modifier = Modifier + .size(ButtonDefaults.IconSize) + ) + } + if (!SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS) { + Column( + modifier = Modifier + .padding(horizontal = 32.dp, vertical = 12.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + stringResource(R.string.ui_settings_option_saveFolder_videoUnsupported), + fontSize = MaterialTheme.typography.titleSmall.fontSize, + ) + Text( + stringResource(R.string.ui_minApiRequired, 8, 26), + fontSize = MaterialTheme.typography.bodySmall.fontSize, + ) + } + } + } + } } if (isLowOnStorage) MessageBox( diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/pages/SaveFolderPage.kt b/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/pages/SaveFolderPage.kt index d45ea15..19d499c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/pages/SaveFolderPage.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/pages/SaveFolderPage.kt @@ -1,5 +1,6 @@ package app.myzel394.alibi.ui.components.WelcomeScreen.pages +import android.Manifest import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -16,12 +17,15 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.InsertDriveFile import androidx.compose.material.icons.filled.ChevronLeft import androidx.compose.material.icons.filled.ChevronRight +import androidx.compose.material.icons.filled.Folder +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -37,7 +41,14 @@ import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE +import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE +import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE import app.myzel394.alibi.ui.components.WelcomeScreen.atoms.SaveFolderSelection +import app.myzel394.alibi.ui.components.atoms.MessageBox +import app.myzel394.alibi.ui.components.atoms.MessageType +import app.myzel394.alibi.ui.components.atoms.PermissionRequester +import app.myzel394.alibi.ui.components.atoms.VisualDensity +import app.myzel394.alibi.ui.utils.rememberFolderSelectorDialog @Composable fun SaveFolderPage( @@ -67,7 +78,8 @@ fun SaveFolderPage( Column( - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize(), verticalArrangement = Arrangement.SpaceBetween, horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -94,7 +106,7 @@ fun SaveFolderPage( stringResource(R.string.ui_welcome_saveFolder_message), ) } - Spacer(modifier = Modifier.weight(1f)) + Spacer(modifier = Modifier.weight(2f)) Box( modifier = Modifier.widthIn(max = 400.dp) ) { @@ -106,6 +118,16 @@ fun SaveFolderPage( ) } Spacer(modifier = Modifier.weight(1f)) + Box( + modifier = Modifier.widthIn(max = 400.dp) + ) { + MessageBox( + type = MessageType.INFO, + message = stringResource(R.string.ui_welcome_timeSettings_changeableHint), + density = VisualDensity.DENSE, + ) + } + Spacer(modifier = Modifier.weight(2f)) Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier @@ -123,22 +145,101 @@ fun SaveFolderPage( contentDescription = null, ) } - Button( - onClick = onContinue, - enabled = !isLowOnStorage, - modifier = Modifier - .fillMaxWidth() - .height(BIG_PRIMARY_BUTTON_SIZE), - contentPadding = ButtonDefaults.ButtonWithIconContentPadding, - ) { - Icon( - Icons.Default.ChevronRight, - contentDescription = null, - modifier = Modifier.size(ButtonDefaults.IconSize) - ) - Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) - Text(stringResource(R.string.continue_label)) + PermissionRequester( + permission = Manifest.permission.WRITE_EXTERNAL_STORAGE, + icon = Icons.AutoMirrored.Filled.InsertDriveFile, + onPermissionAvailable = onContinue, + ) { requestWritePermission -> + val selectFolder = rememberFolderSelectorDialog { folder -> + if (folder == null) { + return@rememberFolderSelectorDialog + } + + onContinue() + } + var showCustomFolderHint by rememberSaveable { mutableStateOf(false) } + + if (showCustomFolderHint) { + _CustomFolderDialog( + onAbort = { showCustomFolderHint = false }, + onOk = { + showCustomFolderHint = false + selectFolder() + }, + ) + } + + Button( + onClick = { + when (saveFolder) { + null -> onContinue() + RECORDER_MEDIA_SELECTED_VALUE -> { + if (SUPPORTS_SCOPED_STORAGE) { + onContinue() + } else { + requestWritePermission() + } + } + + else -> { + showCustomFolderHint = true + } + } + }, + enabled = !isLowOnStorage, + modifier = Modifier + .fillMaxWidth() + .height(BIG_PRIMARY_BUTTON_SIZE), + contentPadding = ButtonDefaults.ButtonWithIconContentPadding, + ) { + Icon( + Icons.Default.ChevronRight, + contentDescription = null, + modifier = Modifier.size(ButtonDefaults.IconSize) + ) + Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) + Text(stringResource(R.string.continue_label)) + } } } } +} + +@Composable +fun _CustomFolderDialog( + onAbort: () -> Unit, + onOk: () -> Unit, +) { + AlertDialog( + onDismissRequest = onAbort, + icon = { + Icon( + Icons.Default.Folder, + contentDescription = null, + ) + }, + title = { + Text(stringResource(R.string.ui_welcome_saveFolder_customFolder_title)) + }, + text = { + Text(stringResource(R.string.ui_welcome_saveFolder_customFolder_message)) + }, + dismissButton = { + TextButton( + onClick = onAbort, + contentPadding = ButtonDefaults.TextButtonContentPadding, + colors = ButtonDefaults.textButtonColors(), + ) { + Text(stringResource(R.string.dialog_close_cancel_label)) + } + }, + confirmButton = { + Button( + onClick = onOk, + ) { + Text(stringResource(R.string.dialog_close_neutral_label)) + } + } + ) + } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cc23b40..15abe38 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -210,4 +210,6 @@ Custom Folder Media Folder Please select either the Media Folder or a Custom Folder. Alibi has not enough space to store the batches in the internal storage. Alternatively, go back one step and select a shorter duration. + Select a Custom Folder + You will now be asked to select a folder where Alibi should store the batches. Please select a folder where you have write access to. \ No newline at end of file