Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
37c6efc
Create the image behavior
DeweyReed Jan 29, 2024
cda1ae2
Pick an image for a step
DeweyReed Feb 5, 2024
37c50b8
Show the image in the list
DeweyReed Feb 5, 2024
c196298
Create ImagePreviewActivity with Jetpack Compose
DeweyReed Feb 7, 2024
fa43671
Replace Glide with Coil
DeweyReed Feb 9, 2024
69d7633
Show the image preview
DeweyReed Feb 11, 2024
ba83670
Copy images into the app's internal storage
DeweyReed Feb 20, 2024
ccfa3c2
Reduce the height of images
DeweyReed Feb 20, 2024
563e4e4
Enable Jetpack Compose in the backup module
DeweyReed Mar 4, 2024
16bf261
Create Export2Fragment
DeweyReed Mar 9, 2024
94fb348
Upgrade the compose dependencies
DeweyReed Apr 7, 2024
45b1b22
Improve the stability of the composables
DeweyReed Apr 7, 2024
392eead
Create Import2Fragment
DeweyReed Apr 14, 2024
fb47dd9
Export and import the data
DeweyReed Apr 17, 2024
23600ad
Rename ImageAction.path to data
DeweyReed Apr 19, 2024
d2de885
Convert the image data to different types
DeweyReed Apr 23, 2024
8422eb8
Fix the wrong base64 string's prefix
DeweyReed Apr 23, 2024
1338c6a
Stabilize ImagePreview
DeweyReed Apr 27, 2024
85bbbff
Load ImageAction with Coil
DeweyReed Apr 27, 2024
1cc66a3
Remove the support for Base64 because of its excessive size
DeweyReed May 4, 2024
00ad47e
Upgrade dependencies
DeweyReed Jun 17, 2024
9bf6d20
Disable the timer image manager to simplify the feature
DeweyReed Jun 17, 2024
1bd8ccf
Create the theme for Jetpack Compose
DeweyReed Jun 18, 2024
2904991
Retire the old backup fragments
DeweyReed Jun 18, 2024
4006b24
Add unit tests for the backup ViewModels
DeweyReed Jun 29, 2024
65e7b10
Improve the stability of composables
DeweyReed Jul 3, 2024
c5de669
Fix the broken backup tests
DeweyReed Jul 3, 2024
c68d7f2
Change the image in UpdateStepDialog
DeweyReed Jul 6, 2024
d77dd39
Add translations for the image behavior
DeweyReed Jul 7, 2024
8a18a0e
Fix typos
DeweyReed Jul 7, 2024
19ed7e0
Support changing the image in UpdateStepDialog
DeweyReed Jul 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions app-backup/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,31 @@ plugins {

android {
namespace 'xyz.aprildown.timer.app.backup'
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get()
}
}

dependencies {
implementation project(':app-base')
implementation project(':component-key')

testImplementation libs.junit
testImplementation libs.kotlin.coroutines.test
testImplementation libs.bundles.mockito.core

implementation libs.androidx.preference

implementation platform(libs.compose.bom)
implementation libs.bundles.compose

implementation libs.hilt.android
kapt libs.hilt.compiler

implementation libs.okio
implementation libs.permission

implementation libs.verticalStepperForm

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
package xyz.aprildown.timer.app.backup

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.selection.toggleable
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.NavigateNext
import androidx.compose.material.icons.rounded.Description
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import xyz.aprildown.timer.domain.usecases.Fruit
import xyz.aprildown.timer.app.base.R as RBase

@Composable
internal fun Backup(
screen: BaseBackupViewModel.Screen<*>,
contentLocationTitle: String,
contentLocationButtonText: String,
onChangeContentLocation: () -> Unit,
backupButtonText: String,
backupErrorHint: String,
modifier: Modifier = Modifier,
extraOptions: @Composable () -> Unit = {},
) {
Column(
modifier = modifier
.verticalScroll(rememberScrollState())
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Header(text = contentLocationTitle)

ContentLocation(
title = contentLocationButtonText,
location = screen.contentName,
modifier = Modifier.clickable(onClick = onChangeContentLocation),
)

Header(text = stringResource(id = RBase.string.backup_app_data))

extraOptions()

DataEntry(
iconRes = RBase.drawable.ic_timer,
nameRes = RBase.string.main_action_timers,
include = screen.includeTimers,
onIncludedChange = screen.onTimersChange,
)
DataEntry(
iconRes = RBase.drawable.ic_scheduler,
nameRes = RBase.string.main_action_schedulers,
include = screen.includeSchedulers,
onIncludedChange = screen.onSchedulersChange,
)
DataEntry(
iconRes = RBase.drawable.ic_stat,
nameRes = RBase.string.main_action_record,
include = screen.includeRecords,
onIncludedChange = screen.onRecordsChange,
)
DataEntry(
iconRes = RBase.drawable.ic_settings,
nameRes = RBase.string.main_action_settings,
include = screen.includeSettings,
onIncludedChange = screen.onSettingsChange,
)

Button(
onClick = screen.onBackup,
modifier = Modifier.align(Alignment.CenterHorizontally),
enabled = screen.content != null &&
(screen.includeTimers || screen.includeSettings) &&
!screen.backupOngoing,
) {
Text(text = backupButtonText)
}
}

if (screen.backupResult is Fruit.Rotten) {
BackupError(
hint = backupErrorHint,
message = screen.backupResult.exception.message.toString(),
consume = screen.consumeBackupError,
)
}

if (screen.backupOngoing) {
BackupOngoingDialog()
}
}

@Composable
private fun Header(
text: String,
modifier: Modifier = Modifier,
) {
Text(
text = text,
modifier = modifier.padding(horizontal = 12.dp, vertical = 12.dp),
color = MaterialTheme.colorScheme.onBackground,
style = MaterialTheme.typography.titleSmall,
)
}

@Composable
private fun ContentLocation(
title: String,
location: String?,
modifier: Modifier = Modifier,
) {
if (location.isNullOrBlank()) {
ContentLocationEmpty(title = title, modifier = modifier)
} else {
ContentLocationPath(title = title, location = location, modifier = modifier)
}
}

@Composable
private fun ContentLocationPath(
title: String,
location: String,
modifier: Modifier
) {
ListItem(
headlineContent = {
Text(text = location)
},
modifier = modifier,
overlineContent = {
Text(text = title)
},
leadingContent = {
Icon(imageVector = Icons.Rounded.Description, contentDescription = null)
},
trailingContent = {
Icon(
imageVector = Icons.AutoMirrored.Rounded.NavigateNext,
contentDescription = null,
)
},
)
}

@Composable
private fun ContentLocationEmpty(
title: String,
modifier: Modifier
) {
ListItem(
headlineContent = {
Text(text = title)
},
modifier = modifier,
leadingContent = {
Icon(imageVector = Icons.Rounded.Description, contentDescription = null)
},
trailingContent = {
Icon(
imageVector = Icons.AutoMirrored.Rounded.NavigateNext,
contentDescription = null,
)
},
)
}

@Composable
private fun DataEntry(
@DrawableRes iconRes: Int,
@StringRes nameRes: Int,
include: Boolean,
onIncludedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
ListItem(
headlineContent = {
Text(text = stringResource(id = nameRes))
},
modifier = modifier
.toggleable(
value = include,
role = Role.Switch,
onValueChange = onIncludedChange,
),
leadingContent = {
Icon(painter = painterResource(id = iconRes), contentDescription = null)
},
trailingContent = {
Switch(checked = include, onCheckedChange = null)
},
)
}

@Composable
private fun BackupError(
hint: String,
message: String,
consume: () -> Unit,
) {
AlertDialog(
onDismissRequest = consume,
confirmButton = {
TextButton(onClick = consume) {
Text(text = stringResource(id = RBase.string.ok))
}
},
text = {
Text(
text = buildString {
append(hint)

if (message.isNotBlank()) {
append("\n\n")
append(message)
}
},
)
}
)
}

@Composable
private fun BackupOngoingDialog() {
Dialog(onDismissRequest = {}) {
Surface(shape = MaterialTheme.shapes.medium) {
CircularProgressIndicator(modifier = Modifier.padding(24.dp))
}
}
}
Loading