From d6ab56f02779bf997513aab2df3e99d3090391bc Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 21:07:34 +0100 Subject: [PATCH] feat: Add selection sheet for SaveFolderTile --- .../java/app/myzel394/alibi/ui/Constants.kt | 3 + .../SettingsScreen/Tiles/SaveFolderTile.kt | 181 +++++++++++++----- app/src/main/res/values/strings.xml | 2 + 3 files changed, 138 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index c294b56..b8c9816 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -4,6 +4,9 @@ import android.os.Build import androidx.compose.ui.unit.dp val BIG_PRIMARY_BUTTON_SIZE = 64.dp + +// TODO: Add everywhere +val SHEET_BOTTOM_OFFSET = 56.dp val MAX_AMPLITUDE = 20000 val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q val RECORDER_SUBFOLDER_NAME = ".recordings" diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index 765bb50..2436856 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -2,26 +2,34 @@ package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import android.content.Intent import android.net.Uri +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer 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.layout.width import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.AudioFile import androidx.compose.material.icons.filled.Cancel import androidx.compose.material.icons.filled.Folder +import androidx.compose.material.icons.filled.InsertDriveFile import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.PermMedia import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -30,21 +38,30 @@ import androidx.compose.runtime.rememberCoroutineScope 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.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector 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.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.RECORDER_MEDIA_SELECTED_VALUE +import app.myzel394.alibi.ui.SHEET_BOTTOM_OFFSET import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE +import app.myzel394.alibi.ui.components.atoms.MessageBox +import app.myzel394.alibi.ui.components.atoms.MessageType import app.myzel394.alibi.ui.components.atoms.SettingsTile import app.myzel394.alibi.ui.utils.rememberFolderSelectorDialog +import com.maxkeppeker.sheets.core.models.base.SelectionButton import kotlinx.coroutines.launch import java.net.URLDecoder +@OptIn(ExperimentalMaterial3Api::class) @Composable fun SaveFolderTile( settings: AppSettings, @@ -138,33 +155,107 @@ fun SaveFolderTile( ) } + var selectionVisible by remember { mutableStateOf(false) } + val selectionSheetState = rememberModalBottomSheetState(true) + + fun hideSheet() { + scope.launch { + selectionSheetState.hide() + selectionVisible = false + } + } + + if (selectionVisible) { + ModalBottomSheet( + sheetState = selectionSheetState, + onDismissRequest = ::hideSheet, + ) { + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(bottom = SHEET_BOTTOM_OFFSET), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(24.dp), + ) { + Text( + stringResource(R.string.ui_settings_option_saveFolder_title), + style = MaterialTheme.typography.headlineMedium, + textAlign = TextAlign.Center, + ) + + SelectionButton( + label = stringResource(R.string.ui_settings_option_saveFolder_action_default_label), + icon = Icons.Default.Lock, + onClick = { + hideSheet() + updateValue(null) + }, + ) + + Divider() + + Column { + SelectionButton( + label = stringResource(R.string.ui_settings_option_saveFolder_action_custom_label), + icon = Icons.Default.Folder, + onClick = { + hideSheet() + showWarning = true + }, + ) + if (!SUPPORTS_SCOPED_STORAGE) { + Column( + modifier = Modifier.padding(horizontal = 32.dp, vertical = 12.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + MessageBox( + type = MessageType.INFO, + message = stringResource(R.string.ui_settings_option_saveFolder_videoUnsupported), + ) + Text( + stringResource(R.string.ui_minApiRequired, 8, 26), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurface, + ) + } + } + } + + Divider() + + SelectionButton( + label = stringResource(R.string.ui_settings_option_saveFolder_action_dcim_label), + icon = Icons.Default.PermMedia, + onClick = { + hideSheet() + updateValue(RECORDER_MEDIA_SELECTED_VALUE) + }, + ) + } + } + } SettingsTile( title = stringResource(R.string.ui_settings_option_saveFolder_title), description = stringResource(R.string.ui_settings_option_saveFolder_explanation), leading = { Icon( - Icons.Default.AudioFile, + Icons.Default.InsertDriveFile, contentDescription = null, ) }, trailing = { Button( onClick = { - showWarning = true + scope.launch { + selectionVisible = true + } }, colors = ButtonDefaults.filledTonalButtonColors( containerColor = MaterialTheme.colorScheme.surfaceVariant, ), shape = MaterialTheme.shapes.medium, ) { - Icon( - Icons.Default.Folder, - contentDescription = null, - modifier = Modifier.size(ButtonDefaults.IconSize), - ) - Spacer( - modifier = Modifier.size(ButtonDefaults.IconSpacing) - ) Text( text = stringResource(R.string.ui_settings_option_saveFolder_action_select_label), ) @@ -218,48 +309,42 @@ fun SaveFolderTile( modifier = Modifier.fillMaxWidth(), ) } - if (!SUPPORTS_SCOPED_STORAGE) { - Row( - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp), - ) { - Icon( - Icons.Default.Warning, - contentDescription = null, - tint = Color.Yellow, - modifier = Modifier.size(ButtonDefaults.IconSize), - ) - Column( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(12.dp), - ) { - Text( - stringResource(R.string.ui_settings_option_saveFolder_videoUnsupported), - style = MaterialTheme.typography.bodySmall, - color = Color.Yellow, - ) - Text( - stringResource(R.string.ui_minApiRequired, 8, 26), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurface, - ) - } - } - } - - Button( - onClick = { updateValue(RECORDER_MEDIA_SELECTED_VALUE) } - ) { - Text("Use Media") - } } } ) } +@Composable +fun SelectionButton( + label: String, + icon: ImageVector, + onClick: () -> Unit, +) { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .height(48.dp) + .clip(MaterialTheme.shapes.medium) + .semantics { + contentDescription = label + } + .clickable { + onClick() + } + .padding(horizontal = 16.dp) + ) { + Icon( + icon, + contentDescription = null, + modifier = Modifier.size(ButtonDefaults.IconSize), + ) + Text(label) + Box {} + } +} + fun splitPath(path: String): List { return try { URLDecoder diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5e2bfbd..859b479 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -169,4 +169,6 @@ You need to verify your identity to continue Custom folders for Video Recordings aren\'t supported by your Android version. Alibi will fallback to the internal folder instead for Video Recordings. You will need an Android phone running at least Android %s (API-Level: %s) to use this feature + Select a custom location + Use the DCIM folder \ No newline at end of file