@@ -4,20 +4,20 @@ package `in`.hridayan.ashell.settings.presentation.page.backup.screens
44
55import androidx.activity.compose.rememberLauncherForActivityResult
66import androidx.activity.result.contract.ActivityResultContracts
7- import androidx.compose.animation.AnimatedVisibility
8- import androidx.compose.animation.scaleIn
9- import androidx.compose.animation.scaleOut
7+ import androidx.compose.foundation.layout.Arrangement
8+ import androidx.compose.foundation.layout.Column
9+ import androidx.compose.foundation.layout.Row
1010import androidx.compose.foundation.layout.Spacer
1111import androidx.compose.foundation.layout.fillMaxWidth
1212import androidx.compose.foundation.layout.height
1313import androidx.compose.foundation.layout.padding
1414import androidx.compose.foundation.lazy.LazyColumn
1515import androidx.compose.foundation.lazy.itemsIndexed
1616import androidx.compose.foundation.lazy.rememberLazyListState
17- import androidx.compose.material3.Card
18- import androidx.compose.material3.CardDefaults
17+ import androidx.compose.foundation.shape.RoundedCornerShape
1918import androidx.compose.material3.ExperimentalMaterial3Api
2019import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
20+ import androidx.compose.material3.Icon
2121import androidx.compose.material3.MaterialTheme
2222import androidx.compose.material3.Text
2323import androidx.compose.runtime.Composable
@@ -27,24 +27,30 @@ import androidx.compose.runtime.getValue
2727import androidx.compose.runtime.mutableStateOf
2828import androidx.compose.runtime.saveable.rememberSaveable
2929import androidx.compose.runtime.setValue
30+ import androidx.compose.ui.Alignment
3031import androidx.compose.ui.Modifier
31- import androidx.compose.ui.draw.clip
32+ import androidx.compose.ui.draw.alpha
33+ import androidx.compose.ui.graphics.painter.Painter
3234import androidx.compose.ui.input.nestedscroll.nestedScroll
3335import androidx.compose.ui.platform.LocalContext
3436import androidx.compose.ui.platform.LocalResources
37+ import androidx.compose.ui.res.painterResource
3538import androidx.compose.ui.res.stringResource
39+ import androidx.compose.ui.text.font.FontWeight
3640import androidx.compose.ui.unit.dp
3741import androidx.core.net.toUri
3842import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
3943import `in`.hridayan.ashell.R
4044import `in`.hridayan.ashell.core.common.LocalDialogManager
45+ import `in`.hridayan.ashell.core.common.LocalSettings
46+ import `in`.hridayan.ashell.core.presentation.components.card.RoundedCornerCard
4147import `in`.hridayan.ashell.core.presentation.components.dialog.DialogKey
4248import `in`.hridayan.ashell.core.presentation.components.dialog.createDialog
49+ import `in`.hridayan.ashell.core.presentation.components.shape.CardCornerShape
4350import `in`.hridayan.ashell.core.presentation.components.shape.CardCornerShape.getRoundedShape
44- import `in`.hridayan.ashell.core.presentation.components.text.AutoResizeableText
4551import `in`.hridayan.ashell.core.utils.getFileNameFromUri
4652import `in`.hridayan.ashell.core.utils.showToast
47- import `in`.hridayan.ashell.settings.data.SettingsKeys
53+ import `in`.hridayan.ashell.settings.domain.model.BackupOption
4854import `in`.hridayan.ashell.settings.presentation.components.dialog.BackupDestinationDialog
4955import `in`.hridayan.ashell.settings.presentation.components.dialog.CloudOperationDialog
5056import `in`.hridayan.ashell.settings.presentation.components.dialog.ResetSettingsDialog
@@ -68,8 +74,8 @@ fun BackupAndRestoreScreen(
6874 val settings = settingsViewModel.backupPageList
6975 val dialogManager = LocalDialogManager .current
7076 val backupTime by backupAndRestoreViewModel.backupTime.collectAsState()
71- val lastBackupTime by settingsViewModel.getString( SettingsKeys . LAST_BACKUP_TIME )
72- .collectAsState(initial = " " )
77+ val lastLocalBackupTime = LocalSettings .current.lastLocalBackupTime
78+ val lastCloudBackupTime = LocalSettings .current.lastCloudBackupTime
7379
7480 val googleUserState by backupAndRestoreViewModel.googleUserState.collectAsState()
7581 val isSigningIn by backupAndRestoreViewModel.isSigningIn.collectAsState()
@@ -84,7 +90,7 @@ fun BackupAndRestoreScreen(
8490 val launcherBackup = rememberLauncherForActivityResult(
8591 contract = ActivityResultContracts .CreateDocument (" application/octet-stream" )
8692 ) { uri ->
87- uri?.let { backupAndRestoreViewModel.performBackup (it) }
93+ uri?.let { backupAndRestoreViewModel.performLocalBackup (it) }
8894 }
8995
9096 val launcherRestore = rememberLauncherForActivityResult(
@@ -240,7 +246,11 @@ fun BackupAndRestoreScreen(
240246
241247 " last_backup_time" -> {
242248 LastBackupTimeCard (
243- lastBackupTime = lastBackupTime
249+ modifier = Modifier
250+ .fillMaxWidth()
251+ .padding(top = 20 .dp),
252+ lastLocalBackupTime = lastLocalBackupTime,
253+ lastCloudBackupTime = lastCloudBackupTime
244254 )
245255 }
246256 }
@@ -274,16 +284,14 @@ fun BackupAndRestoreScreen(
274284 )
275285 }
276286
277- // Backup destination dialog (Local vs Google Drive)
278287 DialogKey .Settings .BackupDestination (
279288 backupOption = backupAndRestoreViewModel.run {
280- // Default option — the actual option comes from the dialog key
281- `in `.hridayan.ashell.settings.domain.model.BackupOption .SETTINGS_AND_DATABASE
289+ BackupOption .SETTINGS_AND_DATABASE
282290 }
283291 ).createDialog { dialogViewModel ->
284292 val activeKey = dialogManager.activeDialog
285293 val backupOption = (activeKey as ? DialogKey .Settings .BackupDestination )?.backupOption
286- ? : ` in `.hridayan.ashell.settings.domain.model. BackupOption .SETTINGS_AND_DATABASE
294+ ? : BackupOption .SETTINGS_AND_DATABASE
287295
288296 BackupDestinationDialog (
289297 onDismiss = { dialogViewModel.dismiss() },
@@ -326,53 +334,92 @@ fun BackupAndRestoreScreen(
326334}
327335
328336@Composable
329- private fun LastBackupTimeCard (modifier : Modifier = Modifier , lastBackupTime : String ) {
337+ private fun LastBackupTimeCard (
338+ modifier : Modifier = Modifier ,
339+ lastLocalBackupTime : String ,
340+ lastCloudBackupTime : String
341+ ) {
342+ Column (modifier = modifier) {
343+ RoundedCornerCard (
344+ modifier = Modifier .fillMaxWidth(),
345+ roundedCornerShape = CardCornerShape .FIRST_CARD
346+ ) {
347+ Text (
348+ modifier = Modifier .padding(horizontal = 25 .dp, vertical = 10 .dp),
349+ text = stringResource(R .string.last_backup_details),
350+ style = MaterialTheme .typography.labelLarge,
351+ color = MaterialTheme .colorScheme.primary
352+ )
353+ }
354+
355+ TimeCard (
356+ modifier = Modifier .fillMaxWidth(),
357+ roundedCornerShape = CardCornerShape .MIDDLE_CARD ,
358+ icon = painterResource(R .drawable.ic_mobile),
359+ title = stringResource(R .string.device_backup_local),
360+ timeDescription = lastLocalBackupTime
361+ )
362+
363+ TimeCard (
364+ modifier = Modifier .fillMaxWidth(),
365+ roundedCornerShape = CardCornerShape .LAST_CARD ,
366+ icon = painterResource(R .drawable.ic_cloud_done),
367+ title = stringResource(R .string.cloud_backup_google_drive),
368+ timeDescription = lastCloudBackupTime
369+ )
370+ }
371+ }
372+
330373
331- val (date, time) = (lastBackupTime).split(" " ).let {
374+ @Composable
375+ private fun TimeCard (
376+ modifier : Modifier = Modifier ,
377+ roundedCornerShape : RoundedCornerShape ,
378+ icon : Painter ,
379+ title : String ,
380+ timeDescription : String
381+ ) {
382+
383+ val (date, time) = (timeDescription).split(" " ).let {
332384 Pair (
333385 it.getOrNull(0 ) ? : " " ,
334386 it.getOrNull(1 ) ? : " "
335387 )
336388 }
337389
338- AnimatedVisibility (
339- visible = date.isNotEmpty() && time.isNotEmpty(),
340- enter = scaleIn(
341- animationSpec = MaterialTheme .motionScheme.slowEffectsSpec(),
342- ),
343- exit = scaleOut(animationSpec = MaterialTheme .motionScheme.slowEffectsSpec())
390+ RoundedCornerCard (
391+ modifier = modifier,
392+ roundedCornerShape = roundedCornerShape
344393 ) {
345- Card (
346- modifier = modifier
394+ Row (
395+ modifier = Modifier
347396 .fillMaxWidth()
348- .padding(start = 20 .dp, end = 20 .dp, top = 15 .dp, bottom = 20 .dp)
349- .clip(
350- MaterialTheme .shapes.large
351- ),
352- shape = MaterialTheme .shapes.large,
353- colors = CardDefaults .cardColors(
354- containerColor = MaterialTheme .colorScheme.surface,
355- contentColor = MaterialTheme .colorScheme.onSurface
356- ),
357- border = CardDefaults .outlinedCardBorder()
397+ .padding(horizontal = 20 .dp, vertical = 17 .dp),
398+ verticalAlignment = Alignment .CenterVertically ,
399+ horizontalArrangement = Arrangement .spacedBy(15 .dp)
358400 ) {
359- AutoResizeableText (
360- text = stringResource(R .string.last_backup_time) + " : " + time,
361- style = MaterialTheme .typography.labelLarge,
362- color = MaterialTheme .colorScheme.primary,
363- modifier = Modifier .padding(horizontal = 25 .dp, vertical = 15 .dp)
401+ Icon (
402+ painter = icon,
403+ contentDescription = null ,
404+ tint = MaterialTheme .colorScheme.primary
364405 )
365406
366- AutoResizeableText (
367- text = stringResource( R .string.last_backup_date) + " : " + date ,
368- style = MaterialTheme .typography.labelLarge,
369- color = MaterialTheme .colorScheme.primary,
370- modifier = Modifier .padding (
371- start = 25 .dp ,
372- end = 25 .dp ,
373- bottom = 20 .dp
407+ Column (
408+ modifier = Modifier .weight( 1f ) ,
409+ verticalArrangement = Arrangement .spacedBy( 7 .dp)
410+ ) {
411+ Text (
412+ text = title ,
413+ fontWeight = FontWeight . SemiBold ,
414+ style = MaterialTheme .typography.titleMediumEmphasized,
374415 )
375- )
416+
417+ Text (
418+ text = if (timeDescription.isEmpty()) stringResource(R .string.none) else " $date | $time " ,
419+ style = MaterialTheme .typography.bodySmall,
420+ modifier = Modifier .alpha(0.9f )
421+ )
422+ }
376423 }
377424 }
378425}
0 commit comments