mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-19 07:15:25 +02:00
feat: Add selection sheet for SaveFolderTile
This commit is contained in:
parent
1187d83e86
commit
d6ab56f027
@ -4,6 +4,9 @@ import android.os.Build
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
val BIG_PRIMARY_BUTTON_SIZE = 64.dp
|
val BIG_PRIMARY_BUTTON_SIZE = 64.dp
|
||||||
|
|
||||||
|
// TODO: Add everywhere
|
||||||
|
val SHEET_BOTTOM_OFFSET = 56.dp
|
||||||
val MAX_AMPLITUDE = 20000
|
val MAX_AMPLITUDE = 20000
|
||||||
val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||||
val RECORDER_SUBFOLDER_NAME = ".recordings"
|
val RECORDER_SUBFOLDER_NAME = ".recordings"
|
||||||
|
@ -2,26 +2,34 @@ package app.myzel394.alibi.ui.components.SettingsScreen.Tiles
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
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.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material.icons.Icons
|
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.Cancel
|
||||||
import androidx.compose.material.icons.filled.Folder
|
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.Lock
|
||||||
|
import androidx.compose.material.icons.filled.PermMedia
|
||||||
import androidx.compose.material.icons.filled.Warning
|
import androidx.compose.material.icons.filled.Warning
|
||||||
import androidx.compose.material3.AlertDialog
|
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.Divider
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
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
|
||||||
@ -30,21 +38,30 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
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.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import app.myzel394.alibi.R
|
import app.myzel394.alibi.R
|
||||||
import app.myzel394.alibi.dataStore
|
import app.myzel394.alibi.dataStore
|
||||||
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.SHEET_BOTTOM_OFFSET
|
||||||
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
|
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.components.atoms.SettingsTile
|
||||||
import app.myzel394.alibi.ui.utils.rememberFolderSelectorDialog
|
import app.myzel394.alibi.ui.utils.rememberFolderSelectorDialog
|
||||||
|
import com.maxkeppeker.sheets.core.models.base.SelectionButton
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SaveFolderTile(
|
fun SaveFolderTile(
|
||||||
settings: AppSettings,
|
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(
|
SettingsTile(
|
||||||
title = stringResource(R.string.ui_settings_option_saveFolder_title),
|
title = stringResource(R.string.ui_settings_option_saveFolder_title),
|
||||||
description = stringResource(R.string.ui_settings_option_saveFolder_explanation),
|
description = stringResource(R.string.ui_settings_option_saveFolder_explanation),
|
||||||
leading = {
|
leading = {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.AudioFile,
|
Icons.Default.InsertDriveFile,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
trailing = {
|
trailing = {
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
showWarning = true
|
scope.launch {
|
||||||
|
selectionVisible = true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
colors = ButtonDefaults.filledTonalButtonColors(
|
colors = ButtonDefaults.filledTonalButtonColors(
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
),
|
),
|
||||||
shape = MaterialTheme.shapes.medium,
|
shape = MaterialTheme.shapes.medium,
|
||||||
) {
|
) {
|
||||||
Icon(
|
|
||||||
Icons.Default.Folder,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(ButtonDefaults.IconSize),
|
|
||||||
)
|
|
||||||
Spacer(
|
|
||||||
modifier = Modifier.size(ButtonDefaults.IconSpacing)
|
|
||||||
)
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.ui_settings_option_saveFolder_action_select_label),
|
text = stringResource(R.string.ui_settings_option_saveFolder_action_select_label),
|
||||||
)
|
)
|
||||||
@ -218,46 +309,40 @@ fun SaveFolderTile(
|
|||||||
modifier = Modifier.fillMaxWidth(),
|
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(
|
@Composable
|
||||||
onClick = { updateValue(RECORDER_MEDIA_SELECTED_VALUE) }
|
fun SelectionButton(
|
||||||
|
label: String,
|
||||||
|
icon: ImageVector,
|
||||||
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Text("Use Media")
|
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<String> {
|
fun splitPath(path: String): List<String> {
|
||||||
|
@ -169,4 +169,6 @@
|
|||||||
<string name="identityVerificationRequired_subtitle">You need to verify your identity to continue</string>
|
<string name="identityVerificationRequired_subtitle">You need to verify your identity to continue</string>
|
||||||
<string name="ui_settings_option_saveFolder_videoUnsupported">Custom folders for Video Recordings aren\'t supported by your Android version. Alibi will fallback to the internal folder instead for Video Recordings.</string>
|
<string name="ui_settings_option_saveFolder_videoUnsupported">Custom folders for Video Recordings aren\'t supported by your Android version. Alibi will fallback to the internal folder instead for Video Recordings.</string>
|
||||||
<string name="ui_minApiRequired">You will need an Android phone running at least Android %s (API-Level: %s) to use this feature</string>
|
<string name="ui_minApiRequired">You will need an Android phone running at least Android %s (API-Level: %s) to use this feature</string>
|
||||||
|
<string name="ui_settings_option_saveFolder_action_custom_label">Select a custom location</string>
|
||||||
|
<string name="ui_settings_option_saveFolder_action_dcim_label">Use the DCIM folder</string>
|
||||||
</resources>
|
</resources>
|
Loading…
x
Reference in New Issue
Block a user