feat: Improve SaveFolder; Request permission: Show warning if custom folder not supported

Signed-off-by: Myzel394 <50424412+Myzel394@users.noreply.github.com>
This commit is contained in:
Myzel394 2024-03-22 00:10:42 +01:00
parent f06a79c1a8
commit 97acb6d977
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
3 changed files with 230 additions and 32 deletions

View File

@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size 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.Icons
import androidx.compose.material.icons.filled.Folder import androidx.compose.material.icons.filled.Folder
import androidx.compose.material.icons.filled.Lock 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.R
import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE 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.MessageBox
import app.myzel394.alibi.ui.components.atoms.MessageType 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), 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( Column(
verticalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.verticalScroll(rememberScrollState()),
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@ -60,27 +75,20 @@ fun SaveFolderSelection(
.then(modifier), .then(modifier),
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
) { ) {
for ((folder, pair) in OPTIONS) { let {
val (label, icon) = pair val label = stringResource(R.string.ui_welcome_saveFolder_values_internal)
val a11yLabel = stringResource( val a11yLabel = stringResource(
R.string.a11y_selectValue, R.string.a11y_selectValue,
label label
) )
val folder = null
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier modifier = createModifier(a11yLabel) {
.fillMaxWidth()
.clip(MaterialTheme.shapes.medium)
.semantics {
contentDescription = a11yLabel
}
.clickable {
onSaveFolderChange(folder) onSaveFolderChange(folder)
} },
.padding(16.dp)
.padding(end = 8.dp)
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@ -93,13 +101,100 @@ fun SaveFolderSelection(
Text(label) Text(label)
} }
Icon( Icon(
icon, Icons.Default.Lock,
contentDescription = null, contentDescription = null,
modifier = Modifier modifier = Modifier
.size(ButtonDefaults.IconSize) .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) if (isLowOnStorage)
MessageBox( MessageBox(

View File

@ -1,5 +1,6 @@
package app.myzel394.alibi.ui.components.WelcomeScreen.pages package app.myzel394.alibi.ui.components.WelcomeScreen.pages
import android.Manifest
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column 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.automirrored.filled.InsertDriveFile
import androidx.compose.material.icons.filled.ChevronLeft import androidx.compose.material.icons.filled.ChevronLeft
import androidx.compose.material.icons.filled.ChevronRight 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.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf 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.BatchesFolder
import app.myzel394.alibi.helpers.VideoBatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder
import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE 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.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 @Composable
fun SaveFolderPage( fun SaveFolderPage(
@ -67,7 +78,8 @@ fun SaveFolderPage(
Column( Column(
modifier = Modifier.fillMaxSize(), modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween, verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
@ -94,7 +106,7 @@ fun SaveFolderPage(
stringResource(R.string.ui_welcome_saveFolder_message), stringResource(R.string.ui_welcome_saveFolder_message),
) )
} }
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(2f))
Box( Box(
modifier = Modifier.widthIn(max = 400.dp) modifier = Modifier.widthIn(max = 400.dp)
) { ) {
@ -106,6 +118,16 @@ fun SaveFolderPage(
) )
} }
Spacer(modifier = Modifier.weight(1f)) 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( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier modifier = Modifier
@ -123,8 +145,47 @@ fun SaveFolderPage(
contentDescription = null, contentDescription = null,
) )
} }
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( Button(
onClick = onContinue, onClick = {
when (saveFolder) {
null -> onContinue()
RECORDER_MEDIA_SELECTED_VALUE -> {
if (SUPPORTS_SCOPED_STORAGE) {
onContinue()
} else {
requestWritePermission()
}
}
else -> {
showCustomFolderHint = true
}
}
},
enabled = !isLowOnStorage, enabled = !isLowOnStorage,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -142,3 +203,43 @@ fun SaveFolderPage(
} }
} }
} }
}
@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))
}
}
)
}

View File

@ -210,4 +210,6 @@
<string name="ui_welcome_saveFolder_values_custom">Custom Folder</string> <string name="ui_welcome_saveFolder_values_custom">Custom Folder</string>
<string name="ui_welcome_saveFolder_values_media">Media Folder</string> <string name="ui_welcome_saveFolder_values_media">Media Folder</string>
<string name="ui_welcome_saveFolder_externalRequired">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.</string> <string name="ui_welcome_saveFolder_externalRequired">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.</string>
<string name="ui_welcome_saveFolder_customFolder_title">Select a Custom Folder</string>
<string name="ui_welcome_saveFolder_customFolder_message">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.</string>
</resources> </resources>