mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-18 23:05:26 +02:00
feat: Adding SaveFolderSelection
Signed-off-by: Myzel394 <50424412+Myzel394@users.noreply.github.com>
This commit is contained in:
parent
e968e7e589
commit
f06a79c1a8
@ -0,0 +1,110 @@
|
||||
package app.myzel394.alibi.ui.components.WelcomeScreen.atoms
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Folder
|
||||
import androidx.compose.material.icons.filled.Lock
|
||||
import androidx.compose.material.icons.filled.PermMedia
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
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.db.AppSettings
|
||||
import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE
|
||||
import app.myzel394.alibi.ui.components.atoms.MessageBox
|
||||
import app.myzel394.alibi.ui.components.atoms.MessageType
|
||||
|
||||
const val CUSTOM_FOLDER = "custom"
|
||||
|
||||
@Composable
|
||||
fun SaveFolderSelection(
|
||||
modifier: Modifier = Modifier,
|
||||
appSettings: AppSettings,
|
||||
saveFolder: String?,
|
||||
isLowOnStorage: Boolean,
|
||||
onSaveFolderChange: (String?) -> Unit,
|
||||
) {
|
||||
val OPTIONS = mapOf<String?, Pair<String, ImageVector>>(
|
||||
null to (stringResource(R.string.ui_welcome_saveFolder_values_internal) to Icons.Default.Lock),
|
||||
RECORDER_MEDIA_SELECTED_VALUE to (stringResource(R.string.ui_welcome_saveFolder_values_media) to Icons.Default.PermMedia),
|
||||
CUSTOM_FOLDER to (stringResource(R.string.ui_welcome_saveFolder_values_custom) to Icons.Default.Folder),
|
||||
)
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(MaterialTheme.colorScheme.surfaceContainer)
|
||||
.then(modifier),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
for ((folder, pair) in OPTIONS) {
|
||||
val (label, icon) = pair
|
||||
val a11yLabel = stringResource(
|
||||
R.string.a11y_selectValue,
|
||||
label
|
||||
)
|
||||
|
||||
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)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
RadioButton(
|
||||
selected = saveFolder == folder,
|
||||
onClick = { onSaveFolderChange(folder) },
|
||||
)
|
||||
Text(label)
|
||||
}
|
||||
Icon(
|
||||
icon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(ButtonDefaults.IconSize)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isLowOnStorage)
|
||||
MessageBox(
|
||||
type = MessageType.ERROR,
|
||||
message = stringResource(R.string.ui_welcome_saveFolder_externalRequired)
|
||||
)
|
||||
}
|
||||
}
|
@ -69,7 +69,7 @@ fun TimeSelector(
|
||||
) {
|
||||
for ((duration, label) in OPTIONS) {
|
||||
val a11yLabel = stringResource(
|
||||
R.string.ui_welcome_timeSettings_selectTime,
|
||||
R.string.a11y_selectValue,
|
||||
label
|
||||
)
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package app.myzel394.alibi.ui.components.WelcomeScreen.pages
|
||||
|
||||
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
|
||||
@ -10,6 +11,7 @@ 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.foundation.layout.widthIn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.InsertDriveFile
|
||||
import androidx.compose.material.icons.filled.ChevronLeft
|
||||
@ -21,11 +23,19 @@ import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.myzel394.alibi.R
|
||||
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.components.WelcomeScreen.atoms.SaveFolderSelection
|
||||
|
||||
@ -33,7 +43,29 @@ import app.myzel394.alibi.ui.components.WelcomeScreen.atoms.SaveFolderSelection
|
||||
fun SaveFolderPage(
|
||||
onBack: () -> Unit,
|
||||
onContinue: () -> Unit,
|
||||
appSettings: AppSettings,
|
||||
) {
|
||||
var saveFolder by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
val isLowOnStorage = if (saveFolder != null)
|
||||
false
|
||||
else {
|
||||
val availableBytes = VideoBatchesFolder.viaInternalFolder(context).getAvailableBytes()
|
||||
|
||||
if (availableBytes == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val bytesPerMinute = BatchesFolder.requiredBytesForOneMinuteOfRecording(appSettings)
|
||||
val requiredBytes = appSettings.maxDuration / 1000 / 60 * bytesPerMinute
|
||||
|
||||
// Allow for a 10% margin of error
|
||||
availableBytes < requiredBytes * 1.1
|
||||
}
|
||||
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
@ -63,7 +95,16 @@ fun SaveFolderPage(
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
SaveFolderSelection()
|
||||
Box(
|
||||
modifier = Modifier.widthIn(max = 400.dp)
|
||||
) {
|
||||
SaveFolderSelection(
|
||||
appSettings = appSettings,
|
||||
saveFolder = saveFolder,
|
||||
isLowOnStorage = isLowOnStorage,
|
||||
onSaveFolderChange = { saveFolder = it },
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
@ -84,6 +125,7 @@ fun SaveFolderPage(
|
||||
}
|
||||
Button(
|
||||
onClick = onContinue,
|
||||
enabled = !isLowOnStorage,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(BIG_PRIMARY_BUTTON_SIZE),
|
||||
|
@ -8,7 +8,6 @@ import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -18,6 +17,7 @@ import app.myzel394.alibi.ui.components.WelcomeScreen.pages.ExplanationPage
|
||||
import app.myzel394.alibi.ui.components.WelcomeScreen.pages.ResponsibilityPage
|
||||
import app.myzel394.alibi.ui.components.WelcomeScreen.pages.SaveFolderPage
|
||||
import app.myzel394.alibi.ui.components.WelcomeScreen.pages.TimeSettingsPage
|
||||
import app.myzel394.alibi.ui.effects.rememberSettings
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@ -27,10 +27,7 @@ fun WelcomeScreen(
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val dataStore = context.dataStore
|
||||
val settings = dataStore
|
||||
.data
|
||||
.collectAsState(initial = null)
|
||||
.value ?: return
|
||||
val settings = rememberSettings()
|
||||
val scope = rememberCoroutineScope()
|
||||
val pagerState = rememberPagerState(
|
||||
initialPage = 0,
|
||||
@ -83,8 +80,11 @@ fun WelcomeScreen(
|
||||
}
|
||||
},
|
||||
onContinue = {
|
||||
finishTutorial()
|
||||
}
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(3)
|
||||
}
|
||||
},
|
||||
appSettings = settings
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@
|
||||
<string name="form_error_value_mustBeGreaterThan">Please enter a number greater than <xliff:g name="min">%s</xliff:g></string>
|
||||
<string name="form_value_selected">Selected: %s</string>
|
||||
|
||||
<string name="a11y_selectValue">Select %s</string>
|
||||
|
||||
<string name="notificationChannels_recorder_name">Recorder</string>
|
||||
<string name="notificationChannels_recorder_description">Shows the current recording status</string>
|
||||
|
||||
@ -201,8 +203,11 @@
|
||||
<string name="ui_welcome_timeSettings_values_15min">15 minutes</string>
|
||||
<string name="ui_welcome_timeSettings_values_30min">30 minutes</string>
|
||||
<string name="ui_welcome_timeSettings_values_1hour">1 hour</string>
|
||||
<string name="ui_welcome_timeSettings_selectTime">Select %s</string>
|
||||
<string name="ui_welcome_timeSettings_changeableHint">You can change this anytime</string>
|
||||
<string name="ui_welcome_saveFolder_title">Where should Alibi store the batches?</string>
|
||||
<string name="ui_welcome_saveFolder_message">By default, Alibi stores the batches into its own private, encrypted storage. You can change this and specify an external, unencrypted folder. If you want to let Alibi remember more than 15 minutes, you should choose an external folder, as the internal folder is very small.</string>
|
||||
<string name="ui_welcome_saveFolder_values_internal">Internal Storage</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_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>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user