From a6e12f49816592a11d6b617891cfcc659c86cd7f Mon Sep 17 00:00:00 2001 From: Super12138 <70494801+Super12138@users.noreply.github.com> Date: Sat, 24 Jan 2026 10:36:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BD=BF=E7=94=A8=E8=A7=84=E8=8C=83?= =?UTF-8?q?=E5=8C=96=E5=B8=83=E5=B1=80=EF=BC=88=E5=AF=BC=E8=88=AA=E6=96=B9?= =?UTF-8?q?=E5=BC=8F1=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../todo/logic/model/ContrastLevel.kt | 19 ++ .../super12138/todo/logic/model/DarkMode.kt | 19 ++ .../todo/logic/model/PaletteStyle.kt | 23 +++ .../super12138/todo/logic/model/Priority.kt | 28 +-- .../todo/logic/model/SortingMethod.kt | 31 ++-- .../cn/super12138/todo/ui/TodoDefaults.kt | 5 +- .../todo/ui/activities/CrashActivity.kt | 12 +- .../todo/ui/activities/MainActivity.kt | 50 +++-- .../todo/ui/components/Scaffolds.kt | 85 +++++++-- .../todo/ui/navigation/SettingsNavigation.kt | 2 + .../todo/ui/navigation/TodoDestinations.kt | 32 ++++ .../todo/ui/navigation/TodoNavigation.kt | 66 +++++-- .../todo/ui/navigation/TodoScreen.kt | 9 +- .../todo/ui/navigation/TopLevelBackStack.kt | 62 +++++++ .../todo/ui/pages/editor/TodoEditorPage.kt | 10 +- .../editor/components/TodoPrioritySlider.kt | 3 +- .../todo/ui/pages/main/ManagerFragment.kt | 86 --------- .../pages/main/{MainPage.kt => TasksPage.kt} | 166 ++++++++--------- .../todo/ui/pages/main/components/TodoCard.kt | 2 +- .../ui/pages/main/components/TodoTopAppBar.kt | 61 ++----- .../todo/ui/pages/overview/OverviewPage.kt | 27 +++ .../todo/ui/pages/settings/SettingsAbout.kt | 108 ++++++----- .../ui/pages/settings/SettingsAboutLicence.kt | 171 +++++++++--------- .../ui/pages/settings/SettingsAppearance.kt | 76 ++++---- .../todo/ui/pages/settings/SettingsData.kt | 77 ++++---- .../ui/pages/settings/SettingsDataCategory.kt | 9 +- .../settings/SettingsDeveloperOptions.kt | 24 +-- .../settings/SettingsInterfaceInteraction.kt | 108 ++++++----- .../todo/ui/pages/settings/SettingsMain.kt | 76 ++++---- .../appearance/contrast/ContrastPicker.kt | 5 +- .../appearance/darkmode/DarkModePicker.kt | 6 +- .../appearance/palette/PaletteItem.kt | 9 +- .../appearance/palette/PalettePicker.kt | 6 +- .../super12138/todo/ui/theme/ContrastLevel.kt | 27 --- .../cn/super12138/todo/ui/theme/DarkMode.kt | 26 --- .../super12138/todo/ui/theme/PaletteStyle.kt | 35 ---- .../cn/super12138/todo/ui/theme/Theme.kt | 1 + .../cn/super12138/todo/ui/theme/ThemeExt.kt | 6 +- .../todo/ui/viewmodels/MainViewModel.kt | 5 + .../cn/super12138/todo/utils/SystemUtils.kt | 11 ++ app/src/main/res/drawable/ic_ballot.xml | 9 + .../main/res/drawable/ic_ballot_filled.xml | 9 + .../main/res/drawable/ic_settings_filled.xml | 9 + app/src/main/res/drawable/ic_view_kanban.xml | 9 + .../res/drawable/ic_view_kanban_filled.xml | 9 + app/src/main/res/values-zh-rCN/strings.xml | 3 + app/src/main/res/values/strings.xml | 2 + 47 files changed, 867 insertions(+), 767 deletions(-) create mode 100644 app/src/main/kotlin/cn/super12138/todo/logic/model/ContrastLevel.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/logic/model/DarkMode.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/logic/model/PaletteStyle.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/ui/navigation/SettingsNavigation.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoDestinations.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/ui/navigation/TopLevelBackStack.kt delete mode 100644 app/src/main/kotlin/cn/super12138/todo/ui/pages/main/ManagerFragment.kt rename app/src/main/kotlin/cn/super12138/todo/ui/pages/main/{MainPage.kt => TasksPage.kt} (54%) create mode 100644 app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/OverviewPage.kt delete mode 100644 app/src/main/kotlin/cn/super12138/todo/ui/theme/ContrastLevel.kt delete mode 100644 app/src/main/kotlin/cn/super12138/todo/ui/theme/DarkMode.kt delete mode 100644 app/src/main/kotlin/cn/super12138/todo/ui/theme/PaletteStyle.kt create mode 100644 app/src/main/res/drawable/ic_ballot.xml create mode 100644 app/src/main/res/drawable/ic_ballot_filled.xml create mode 100644 app/src/main/res/drawable/ic_settings_filled.xml create mode 100644 app/src/main/res/drawable/ic_view_kanban.xml create mode 100644 app/src/main/res/drawable/ic_view_kanban_filled.xml diff --git a/app/src/main/kotlin/cn/super12138/todo/logic/model/ContrastLevel.kt b/app/src/main/kotlin/cn/super12138/todo/logic/model/ContrastLevel.kt new file mode 100644 index 0000000..eb6ae6c --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/logic/model/ContrastLevel.kt @@ -0,0 +1,19 @@ +package cn.super12138.todo.logic.model + +import androidx.annotation.StringRes +import cn.super12138.todo.R + +enum class ContrastLevel( + val value: Float, + @StringRes val nameRes: Int +) { + VeryLow(value = -1f, nameRes = R.string.contrast_very_low), + Low(value = -0.5f, nameRes = R.string.contrast_low), + Default(value = 0f, nameRes = R.string.contrast_default), + Medium(value = 0.5f, nameRes = R.string.contrast_high), + High(value = 1f, nameRes = R.string.contrast_very_high); + + companion object { + fun fromFloat(float: Float) = entries.find { it.value == float } ?: Default + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/logic/model/DarkMode.kt b/app/src/main/kotlin/cn/super12138/todo/logic/model/DarkMode.kt new file mode 100644 index 0000000..55b5392 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/logic/model/DarkMode.kt @@ -0,0 +1,19 @@ +package cn.super12138.todo.logic.model + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import cn.super12138.todo.R + +enum class DarkMode( + val id: Int, + @DrawableRes val iconRes: Int, + @StringRes val nameRes: Int +) { + FollowSystem(id = -1, iconRes = R.drawable.ic_lightbulb_2, nameRes = R.string.dark_mode_system), + Light(id = 1, iconRes = R.drawable.ic_light_mode, nameRes = R.string.dark_mode_light), + Dark(id = 2, iconRes = R.drawable.ic_dark_mode, nameRes = R.string.dark_mode_dark); + + companion object { + fun fromId(id: Int) = entries.find { it.id == id } ?: FollowSystem + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/logic/model/PaletteStyle.kt b/app/src/main/kotlin/cn/super12138/todo/logic/model/PaletteStyle.kt new file mode 100644 index 0000000..8f0341b --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/logic/model/PaletteStyle.kt @@ -0,0 +1,23 @@ +package cn.super12138.todo.logic.model + +import androidx.annotation.StringRes +import cn.super12138.todo.R + +enum class PaletteStyle( + val id: Int, + @StringRes val nameRes: Int +) { + TonalSpot(id = 1, nameRes = R.string.palette_tonal_spot), + Neutral(id = 2, nameRes = R.string.palette_neutral), + Vibrant(id = 3, nameRes = R.string.palette_vibrant), + Expressive(id = 4, nameRes = R.string.palette_expressive), + Rainbow(id = 5, nameRes = R.string.palette_rainbow), + FruitSalad(id = 6, nameRes = R.string.palette_fruit_salad), + Monochrome(id = 7, nameRes = R.string.palette_monochrome), + Fidelity(id = 8, nameRes = R.string.palette_fidelity), + Content(id = 9, nameRes = R.string.palette_content); + + companion object { + fun fromId(id: Int) = entries.firstOrNull { it.id == id } ?: TonalSpot + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/logic/model/Priority.kt b/app/src/main/kotlin/cn/super12138/todo/logic/model/Priority.kt index b2eb31e..50378f2 100644 --- a/app/src/main/kotlin/cn/super12138/todo/logic/model/Priority.kt +++ b/app/src/main/kotlin/cn/super12138/todo/logic/model/Priority.kt @@ -1,25 +1,17 @@ package cn.super12138.todo.logic.model -import android.content.Context +import androidx.annotation.StringRes import cn.super12138.todo.R -enum class Priority(val value: Float) { - Urgent(2f), - Important(1f), - Default(0f), - NotImportant(-1f), - NotUrgent(-2f); - - fun displayName(context: Context): String { - val resId = when (this) { - Urgent -> R.string.priority_urgent - Important -> R.string.priority_important - Default -> R.string.priority_default - NotImportant -> R.string.priority_not_important - NotUrgent -> R.string.priority_not_urgent - } - return context.getString(resId) - } +enum class Priority( + val value: Float, + @StringRes val nameRes: Int +) { + Urgent(value = 2f, nameRes = R.string.priority_urgent), + Important(value = 1f, nameRes = R.string.priority_important), + Default(value = 0f, nameRes = R.string.priority_default), + NotImportant(value = -1f, nameRes = R.string.priority_not_important), + NotUrgent(value = -2f, nameRes = R.string.priority_not_urgent); companion object { fun fromFloat(float: Float) = entries.find { it.value == float } ?: Default diff --git a/app/src/main/kotlin/cn/super12138/todo/logic/model/SortingMethod.kt b/app/src/main/kotlin/cn/super12138/todo/logic/model/SortingMethod.kt index 2c73a4c..81dadf0 100644 --- a/app/src/main/kotlin/cn/super12138/todo/logic/model/SortingMethod.kt +++ b/app/src/main/kotlin/cn/super12138/todo/logic/model/SortingMethod.kt @@ -1,38 +1,29 @@ package cn.super12138.todo.logic.model -import android.content.Context +import androidx.annotation.StringRes import cn.super12138.todo.R -enum class SortingMethod(val id: Int) { +enum class SortingMethod( + val id: Int, + @StringRes val nameRes: Int +) { // 按添加先后顺序 - Sequential(1), + Sequential(id = 1, nameRes = R.string.sorting_sequential), // 按学科 - Category(2), + Category(id = 2, nameRes = R.string.sorting_category), // 按优先级 - Priority(3), + Priority(id = 3, nameRes = R.string.sorting_priority), // 按完成情况 - Completion(4), + Completion(id = 4, nameRes = R.string.sorting_completion), // 按字母升序 - AlphabeticalAscending(5), + AlphabeticalAscending(id = 5, nameRes = R.string.sorting_alphabetical_ascending), // 按字母降序 - AlphabeticalDescending(6); - - fun getDisplayName(context: Context): String { - val resId = when (this) { - Sequential -> R.string.sorting_sequential - Category -> R.string.sorting_category - Priority -> R.string.sorting_priority - Completion -> R.string.sorting_completion - AlphabeticalAscending -> R.string.sorting_alphabetical_ascending - AlphabeticalDescending -> R.string.sorting_alphabetical_descending - } - return context.getString(resId) - } + AlphabeticalDescending(id = 6, nameRes = R.string.sorting_alphabetical_descending); companion object { fun fromId(id: Int) = entries.find { it.id == id } ?: Sequential diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/TodoDefaults.kt b/app/src/main/kotlin/cn/super12138/todo/ui/TodoDefaults.kt index c3b4264..c326260 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/TodoDefaults.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/TodoDefaults.kt @@ -1,7 +1,6 @@ package cn.super12138.todo.ui import androidx.compose.foundation.shape.CornerBasedShape -import androidx.compose.foundation.shape.ZeroCornerSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color @@ -58,10 +57,10 @@ object TodoDefaults { val ScreenContainerShape: Shape @Composable - get() = MaterialTheme.shapes.large.copy( + get() = MaterialTheme.shapes.large/*.copy( bottomStart = ZeroCornerSize, bottomEnd = ZeroCornerSize - ) + )*/ val ContainerColor: Color @Composable diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/activities/CrashActivity.kt b/app/src/main/kotlin/cn/super12138/todo/ui/activities/CrashActivity.kt index 0bd774e..1c4f57f 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/activities/CrashActivity.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/activities/CrashActivity.kt @@ -1,10 +1,8 @@ package cn.super12138.todo.ui.activities -import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.LaunchedEffect @@ -16,11 +14,12 @@ import androidx.core.view.WindowCompat import cn.super12138.todo.R import cn.super12138.todo.constants.Constants import cn.super12138.todo.logic.datastore.DataStoreManager +import cn.super12138.todo.logic.model.DarkMode +import cn.super12138.todo.logic.model.PaletteStyle import cn.super12138.todo.ui.pages.crash.CrashPage -import cn.super12138.todo.ui.theme.DarkMode -import cn.super12138.todo.ui.theme.PaletteStyle import cn.super12138.todo.ui.theme.ToDoTheme import cn.super12138.todo.utils.VibrationUtils +import cn.super12138.todo.utils.configureEdgeToEdge class CrashActivity : ComponentActivity() { companion object { @@ -32,10 +31,7 @@ class CrashActivity : ComponentActivity() { } override fun onCreate(savedInstanceState: Bundle?) { - enableEdgeToEdge() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - window.isNavigationBarContrastEnforced = false - } + configureEdgeToEdge() super.onCreate(savedInstanceState) val crashLogs = intent.getStringExtra("crash_logs") diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/activities/MainActivity.kt b/app/src/main/kotlin/cn/super12138/todo/ui/activities/MainActivity.kt index c2d7f72..32bd9dd 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/activities/MainActivity.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/activities/MainActivity.kt @@ -1,42 +1,45 @@ package cn.super12138.todo.ui.activities -import android.os.Build import android.os.Bundle import android.view.WindowManager import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Icon import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.WindowCompat import androidx.lifecycle.viewmodel.compose.viewModel import cn.super12138.todo.constants.Constants import cn.super12138.todo.logic.datastore.DataStoreManager +import cn.super12138.todo.logic.model.DarkMode +import cn.super12138.todo.logic.model.PaletteStyle import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.components.Konfetti +import cn.super12138.todo.ui.navigation.TodoDestinations import cn.super12138.todo.ui.navigation.TodoNavigation -import cn.super12138.todo.ui.theme.DarkMode -import cn.super12138.todo.ui.theme.PaletteStyle import cn.super12138.todo.ui.theme.ToDoTheme import cn.super12138.todo.ui.viewmodels.MainViewModel import cn.super12138.todo.utils.VibrationUtils +import cn.super12138.todo.utils.configureEdgeToEdge class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { installSplashScreen() - enableEdgeToEdge() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - window.isNavigationBarContrastEnforced = false - } + configureEdgeToEdge() super.onCreate(savedInstanceState) setContent { val mainViewModel: MainViewModel = viewModel() + val navigationBackStack = mainViewModel.topLevelBackStack val showConfetti = mainViewModel.showConfetti // 主题 val dynamicColor by DataStoreManager.dynamicColorFlow.collectAsState(initial = Constants.PREF_DYNAMIC_COLOR_DEFAULT) @@ -84,10 +87,33 @@ class MainActivity : ComponentActivity() { dynamicColor = dynamicColor ) { Surface(color = TodoDefaults.BackgroundColor) { - TodoNavigation( - viewModel = mainViewModel, - modifier = Modifier.fillMaxSize() - ) + NavigationSuiteScaffold( + navigationSuiteItems = { + TodoDestinations.entries.forEach { + val selected = it.route == navigationBackStack.topLevelKey + item( + icon = { + Icon( + painter = if (selected) painterResource(it.selectedIcon) else painterResource( + it.icon + ), + contentDescription = null + ) + }, + label = { Text(stringResource(it.label)) }, + selected = selected, + onClick = { navigationBackStack.addTopLevel(it.route) } + ) + } + }, + containerColor = TodoDefaults.BackgroundColor + ) { + TodoNavigation( + backStack = navigationBackStack, + viewModel = mainViewModel, + modifier = Modifier.fillMaxSize() + ) + } Konfetti(state = showConfetti) } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/components/Scaffolds.kt b/app/src/main/kotlin/cn/super12138/todo/ui/components/Scaffolds.kt index 895cbf5..bb7b125 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/components/Scaffolds.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/components/Scaffolds.kt @@ -1,7 +1,5 @@ package cn.super12138.todo.ui.components -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.padding @@ -20,6 +18,8 @@ import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -33,10 +33,9 @@ import cn.super12138.todo.utils.VibrationUtils * * 内容默认由 Box 容器包裹;实际使用时推荐配合 Column 或 Row * * @param title 标题文本 - * @param onBack 当返回按钮被按下时的操作 * @param contentWindowInsets 内容边距,通常用于将内容和系统状态栏等隔开;可以使用 `WindowInsets.safeContent` */ -@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun TopAppBarScaffold( modifier: Modifier = Modifier, @@ -46,7 +45,7 @@ fun TopAppBarScaffold( floatingActionButton: @Composable () -> Unit = {}, floatingActionButtonPosition: FabPosition = FabPosition.End, contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets, - content: @Composable (PaddingValues) -> Unit + content: @Composable () -> Unit ) { val view = LocalView.current TopAppBarScaffold( @@ -77,10 +76,37 @@ fun TopAppBarScaffold( floatingActionButtonPosition = floatingActionButtonPosition, contentWindowInsets = contentWindowInsets, modifier = modifier, - ) { innerPadding -> - //TODO: 应用圆角效果 - Box { content(innerPadding) } - } + content = content + ) +} + +@Composable +fun TopAppBarScaffold( + modifier: Modifier = Modifier, + title: String, + navigationIcon: @Composable () -> Unit = {}, + snackbarHost: @Composable () -> Unit = {}, + floatingActionButton: @Composable () -> Unit = {}, + floatingActionButtonPosition: FabPosition = FabPosition.End, + contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets, + content: @Composable () -> Unit +) { + TopAppBarScaffold( + title = { + Text( + text = title, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + }, + navigationIcon = navigationIcon, + snackbarHost = snackbarHost, + floatingActionButton = floatingActionButton, + floatingActionButtonPosition = floatingActionButtonPosition, + contentWindowInsets = contentWindowInsets, + modifier = modifier, + content = content + ) } @OptIn(ExperimentalMaterial3Api::class) @@ -94,9 +120,9 @@ fun TopAppBarScaffold( floatingActionButton: @Composable () -> Unit = {}, floatingActionButtonPosition: FabPosition = FabPosition.End, contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets, - content: @Composable (PaddingValues) -> Unit + content: @Composable () -> Unit ) { - Scaffold( + TopAppBarScaffold( modifier = modifier, topBar = { TopAppBar( @@ -112,8 +138,37 @@ fun TopAppBarScaffold( floatingActionButton = floatingActionButton, floatingActionButtonPosition = floatingActionButtonPosition, contentWindowInsets = contentWindowInsets, - containerColor = TodoDefaults.BackgroundColor, - ) { innerPadding -> - Box { content(innerPadding) } - } + content = content, + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TopAppBarScaffold( + modifier: Modifier = Modifier, + topBar: @Composable () -> Unit = {}, + snackbarHost: @Composable () -> Unit = {}, + floatingActionButton: @Composable () -> Unit = {}, + floatingActionButtonPosition: FabPosition = FabPosition.End, + contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets, + containerColor: Color = TodoDefaults.BackgroundColor, + screenShape: Shape = TodoDefaults.ScreenContainerShape, + content: @Composable () -> Unit +) = Scaffold( + modifier = modifier, + topBar = topBar, + snackbarHost = snackbarHost, + floatingActionButton = floatingActionButton, + floatingActionButtonPosition = floatingActionButtonPosition, + contentWindowInsets = contentWindowInsets, + containerColor = containerColor, +) { innerPadding -> + Surface( + modifier = Modifier + .padding(paddingValues = innerPadding) + .padding(horizontal = TodoDefaults.screenHorizontalPadding), + color = containerColor, + shape = screenShape, + content = content + ) } \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/navigation/SettingsNavigation.kt b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/SettingsNavigation.kt new file mode 100644 index 0000000..0b31233 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/SettingsNavigation.kt @@ -0,0 +1,2 @@ +package cn.super12138.todo.ui.navigation + diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoDestinations.kt b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoDestinations.kt new file mode 100644 index 0000000..732f77a --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoDestinations.kt @@ -0,0 +1,32 @@ +package cn.super12138.todo.ui.navigation + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.navigation3.runtime.NavKey + +enum class TodoDestinations( + val route: NavKey, + @StringRes val label: Int, + @DrawableRes val icon: Int, + @DrawableRes val selectedIcon: Int +) { + Overview( + route = TodoScreen.Overview, + label = cn.super12138.todo.R.string.page_overview, + icon = cn.super12138.todo.R.drawable.ic_view_kanban, + selectedIcon = cn.super12138.todo.R.drawable.ic_view_kanban_filled + ), + Tasks( + route = TodoScreen.Tasks, + label = cn.super12138.todo.R.string.page_tasks, + icon = cn.super12138.todo.R.drawable.ic_ballot, + selectedIcon = cn.super12138.todo.R.drawable.ic_ballot_filled + ), + Settings( + route = TodoScreen.Settings, + label = cn.super12138.todo.R.string.page_settings, + icon = cn.super12138.todo.R.drawable.ic_settings, + selectedIcon = cn.super12138.todo.R.drawable.ic_settings_filled + ) +} + diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoNavigation.kt b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoNavigation.kt index f5804c0..fd8f2c6 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoNavigation.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoNavigation.kt @@ -1,16 +1,23 @@ package cn.super12138.todo.ui.navigation +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.SharedTransitionLayout +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.togetherWith import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.navigation3.runtime.NavBackStack import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.entryProvider -import androidx.navigation3.runtime.rememberNavBackStack import androidx.navigation3.ui.NavDisplay import cn.super12138.todo.ui.pages.editor.TodoEditorPage import cn.super12138.todo.ui.pages.main.MainPage +import cn.super12138.todo.ui.pages.overview.OverviewPage import cn.super12138.todo.ui.pages.settings.SettingsAbout import cn.super12138.todo.ui.pages.settings.SettingsAboutLicence import cn.super12138.todo.ui.pages.settings.SettingsAppearance @@ -28,17 +35,39 @@ private const val INITIAL_OFFSET_FACTOR = 0.10f @OptIn(ExperimentalSharedTransitionApi::class) @Composable fun TodoNavigation( + backStack: TopLevelBackStack, modifier: Modifier = Modifier, - backStack: NavBackStack = rememberNavBackStack(TodoScreen.Main), viewModel: MainViewModel ) { fun onBack() { - backStack.removeAt(backStack.lastIndex) + backStack.removeLast() + } + + val anim = NavDisplay.transitionSpec { + // Slide new content up, keeping the old content in place underneath + slideInVertically( + initialOffsetY = { it / 5 }, + animationSpec = tween(200) + ) + fadeIn(tween(200)) togetherWith ExitTransition.KeepUntilTransitionsFinished + } + NavDisplay.popTransitionSpec { + // Slide old content down, revealing the new content in place underneath + EnterTransition.None togetherWith + slideOutVertically( + targetOffsetY = { it / 5 }, + animationSpec = tween(200) + ) + fadeOut(tween(200)) + } + NavDisplay.predictivePopTransitionSpec { + // Slide old content down, revealing the new content in place underneath + EnterTransition.None togetherWith + slideOutVertically( + targetOffsetY = { it / 5 }, + animationSpec = tween(200) + ) + fadeOut(tween(200)) } SharedTransitionLayout { NavDisplay( - backStack = backStack, + backStack = backStack.backStack, onBack = ::onBack, transitionSpec = { materialSharedAxisX( @@ -50,16 +79,27 @@ fun TodoNavigation( initialOffsetX = { -(it * INITIAL_OFFSET_FACTOR).toInt() }, targetOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() }) }, - entryProvider = entryProvider { - entry { + entry(metadata = anim) { + OverviewPage() + } + entry(metadata = anim) { MainPage( viewModel = viewModel, toTodoEditPage = { backStack.add(TodoScreen.Editor(it)) }, - toSettingsPage = { backStack.add(TodoScreen.SettingsMain) }, sharedTransitionScope = this@SharedTransitionLayout ) } + + entry(metadata = anim) { + SettingsMain( + toAppearancePage = { backStack.add(TodoScreen.SettingsAppearance) }, + toAboutPage = { backStack.add(TodoScreen.SettingsAbout) }, + toInterfacePage = { backStack.add(TodoScreen.SettingsInterface) }, + toDataPage = { backStack.add(TodoScreen.SettingsData) }, + ) + } + entry { editorArgs -> TodoEditorPage( toDo = editorArgs.toDo, @@ -82,16 +122,6 @@ fun TodoNavigation( ) } - entry { - SettingsMain( - toAppearancePage = { backStack.add(TodoScreen.SettingsAppearance) }, - toAboutPage = { backStack.add(TodoScreen.SettingsAbout) }, - toInterfacePage = { backStack.add(TodoScreen.SettingsInterface) }, - toDataPage = { backStack.add(TodoScreen.SettingsData) }, - onNavigateUp = ::onBack, - ) - } - entry { SettingsAppearance(onNavigateUp = ::onBack) } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoScreen.kt b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoScreen.kt index 7de325c..298959c 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoScreen.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoScreen.kt @@ -7,13 +7,16 @@ import kotlinx.serialization.Serializable @Serializable sealed class TodoScreen : NavKey { @Serializable - data object Main : TodoScreen() + data object Overview : TodoScreen() + + @Serializable + data object Tasks : TodoScreen() @Serializable data class Editor(val toDo: TodoEntity?) : TodoScreen() @Serializable - data object SettingsMain : TodoScreen() + data object Settings : TodoScreen() @Serializable data object SettingsAppearance : TodoScreen() @@ -41,4 +44,4 @@ sealed class TodoScreen : NavKey { @Serializable data object SettingsDevPadding : TodoScreen() -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TopLevelBackStack.kt b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TopLevelBackStack.kt new file mode 100644 index 0000000..3fb2f42 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TopLevelBackStack.kt @@ -0,0 +1,62 @@ +package cn.super12138.todo.ui.navigation + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshots.SnapshotStateList + +@Composable +fun rememberTopLevelBackStack(startKey: T): TopLevelBackStack = + remember { TopLevelBackStack(startKey = startKey) } + +class TopLevelBackStack(startKey: T) { + // Maintain a stack for each top level route + private var topLevelStacks: LinkedHashMap> = linkedMapOf( + startKey to mutableStateListOf(startKey) + ) + + // Expose the current top level route for consumers + var topLevelKey: T by mutableStateOf(startKey) + private set + + // Expose the back stack so it can be rendered by the NavDisplay + val backStack = mutableStateListOf(startKey) + + private fun updateBackStack() = + backStack.apply { + clear() + addAll(topLevelStacks.flatMap { it.value }) + } + + fun addTopLevel(key: T) { + // If the top level doesn't exist, add it + if (topLevelStacks[key] == null) { + topLevelStacks.put(key, mutableStateListOf(key)) + } else { + // Otherwise just move it to the end of the stacks + topLevelStacks.apply { + remove(key)?.let { + put(key, it) + } + } + } + topLevelKey = key + updateBackStack() + } + + fun add(key: T) { + topLevelStacks[topLevelKey]?.add(key) + updateBackStack() + } + + fun removeLast() { + val removedKey = topLevelStacks[topLevelKey]?.removeLastOrNull() + // If the removed key was a top level key, remove the associated top level stack + topLevelStacks.remove(removedKey) + topLevelKey = topLevelStacks.keys.last() + updateBackStack() + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/TodoEditorPage.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/TodoEditorPage.kt index 36e453c..fb1bbae 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/TodoEditorPage.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/TodoEditorPage.kt @@ -41,11 +41,10 @@ import cn.super12138.todo.R import cn.super12138.todo.constants.Constants import cn.super12138.todo.logic.database.TodoEntity import cn.super12138.todo.logic.datastore.DataStoreManager -import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.components.ChipItem import cn.super12138.todo.ui.components.ConfirmDialog -import cn.super12138.todo.ui.components.TopAppBarScaffold import cn.super12138.todo.ui.components.TodoFloatingActionButton +import cn.super12138.todo.ui.components.TopAppBarScaffold import cn.super12138.todo.ui.pages.editor.components.TodoCategoryChip import cn.super12138.todo.ui.pages.editor.components.TodoCategoryTextField import cn.super12138.todo.ui.pages.editor.components.TodoContentTextField @@ -159,13 +158,10 @@ fun TodoEditorPage( }, onBack = ::checkModifiedBeforeBack, modifier = modifier - ) { innerPadding -> + ) { LazyColumn( verticalArrangement = Arrangement.spacedBy(5.dp), - modifier = Modifier - .padding(innerPadding) - .padding(horizontal = TodoDefaults.screenHorizontalPadding) - .fillMaxSize() + modifier = Modifier.fillMaxSize() ) { item { TodoContentTextField( diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/components/TodoPrioritySlider.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/components/TodoPrioritySlider.kt index 008a182..1e0981b 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/components/TodoPrioritySlider.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/components/TodoPrioritySlider.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.LiveRegionMode import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.liveRegion @@ -35,7 +36,7 @@ fun TodoPrioritySlider( val view = LocalView.current val context = LocalContext.current - val priorityName = remember { Priority.entries.map { it.displayName(context) } } + val priorityName = Priority.entries.map { stringResource(it.nameRes) } val interactionSource = remember { MutableInteractionSource() } Slider( diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/ManagerFragment.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/ManagerFragment.kt deleted file mode 100644 index fbb07ca..0000000 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/ManagerFragment.kt +++ /dev/null @@ -1,86 +0,0 @@ -package cn.super12138.todo.ui.pages.main - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.items -import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import cn.super12138.todo.R -import cn.super12138.todo.logic.database.TodoEntity -import cn.super12138.todo.logic.model.Priority -import cn.super12138.todo.ui.TodoDefaults -import cn.super12138.todo.ui.pages.main.components.TodoCard - -@OptIn(ExperimentalMaterial3ExpressiveApi::class) -@Composable -fun ManagerFragment( - modifier: Modifier = Modifier, - state: LazyListState, - list: List, - onItemClick: (TodoEntity) -> Unit = {}, - onItemLongClick: (TodoEntity) -> Unit = {}, - onItemChecked: (TodoEntity) -> Unit = {}, - selectedTodoIds: List -) { - LazyColumn( - state = state, - contentPadding = PaddingValues( - start = TodoDefaults.screenHorizontalPadding, - bottom = TodoDefaults.toDoCardHeight / 2, - end = TodoDefaults.screenHorizontalPadding - ), - modifier = modifier - /*.clip( - MaterialTheme.shapes.extraLarge.copy( - bottomEnd = CornerSize(0.dp), - bottomStart = CornerSize(0.dp) - ) - ) - .background(MaterialTheme.colorScheme.surfaceContainerHighest)*/ - // verticalArrangement = Arrangement.spacedBy(10.dp) - ) { - if (list.isEmpty()) { - item { - Text( - text = stringResource(R.string.tip_no_task), - style = MaterialTheme.typography.titleMedium, - textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth() - ) - } - } else { - items( - items = list, - key = { it.id } - ) { item -> - TodoCard( - // id = item.id, - content = item.content, - category = item.category, - completed = item.isCompleted, - priority = Priority.fromFloat(item.priority), - selected = selectedTodoIds.contains(item.id), - onCardClick = { onItemClick(item) }, - onCardLongClick = { onItemLongClick(item) }, - onChecked = { onItemChecked(item) }, - modifier = Modifier - .padding(vertical = 5.dp) - .animateItem( - fadeInSpec = MaterialTheme.motionScheme.defaultEffectsSpec(), - placementSpec = MaterialTheme.motionScheme.defaultSpatialSpec(), - fadeOutSpec = MaterialTheme.motionScheme.fastEffectsSpec() - ) - ) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/MainPage.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/TasksPage.kt similarity index 54% rename from app/src/main/kotlin/cn/super12138/todo/ui/pages/main/MainPage.kt rename to app/src/main/kotlin/cn/super12138/todo/ui/pages/main/TasksPage.kt index e9788a0..2efff83 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/MainPage.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/TasksPage.kt @@ -2,13 +2,18 @@ package cn.super12138.todo.ui.pages.main import androidx.activity.compose.BackHandler import androidx.compose.animation.SharedTransitionScope -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.Scaffold +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.material3.animateFloatingActionButton import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -21,13 +26,19 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp import androidx.navigation3.ui.LocalNavAnimatedContentScope import cn.super12138.todo.R import cn.super12138.todo.constants.Constants import cn.super12138.todo.logic.database.TodoEntity import cn.super12138.todo.logic.datastore.DataStoreManager +import cn.super12138.todo.logic.model.Priority +import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.components.ConfirmDialog import cn.super12138.todo.ui.components.TodoFloatingActionButton +import cn.super12138.todo.ui.components.TopAppBarScaffold +import cn.super12138.todo.ui.pages.main.components.TodoCard import cn.super12138.todo.ui.pages.main.components.TodoTopAppBar import cn.super12138.todo.ui.viewmodels.MainViewModel @@ -37,9 +48,7 @@ fun MainPage( modifier: Modifier = Modifier, viewModel: MainViewModel, toTodoEditPage: (TodoEntity?) -> Unit, - toSettingsPage: () -> Unit, sharedTransitionScope: SharedTransitionScope, - // windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().windowSizeClass, ) { val animatedVisibilityScope = LocalNavAnimatedContentScope.current @@ -53,8 +62,8 @@ fun MainPage( val selectedTodoIds by remember { derivedStateOf { selectedTodos.value } } val inSelectedMode by remember { derivedStateOf { !selectedTodoIds.isEmpty() } } val toDoList by remember { derivedStateOf { toDos.value } } - val totalTasks by remember { derivedStateOf { toDoList.size } } - val completedTasks by remember { derivedStateOf { toDoList.count { it.isCompleted } } } + /*val totalTasks by remember { derivedStateOf { toDoList.size } } + val completedTasks by remember { derivedStateOf { toDoList.count { it.isCompleted } } }*/ val filteredTodoList = if (showCompleted) toDoList else toDoList.filter { item -> !item.isCompleted } val expandedFab by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } } @@ -62,15 +71,14 @@ fun MainPage( // 当按下返回键(或进行返回操作)时清空选择,仅在非选择模式下生效 BackHandler(inSelectedMode) { viewModel.clearAllTodoSelection() } - Scaffold( + TopAppBarScaffold( topBar = { TodoTopAppBar( selectedTodoIds = selectedTodoIds, selectedMode = inSelectedMode, onCancelSelect = { viewModel.clearAllTodoSelection() }, onSelectAll = { viewModel.selectAllTodos() }, - onDeleteSelectedTodo = { showDeleteConfirmDialog = true }, - toSettingsPage = toSettingsPage + onDeleteSelectedTodo = { showDeleteConfirmDialog = true } ) }, floatingActionButton = { @@ -94,84 +102,68 @@ fun MainPage( }, contentWindowInsets = WindowInsets(0, 0, 0, 0), modifier = modifier - ) { innerPadding -> - /*val isMediumScreen = - windowSizeClass.isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND) - if (isMediumScreen) { - Row( - modifier = Modifier.padding( - top = innerPadding.calculateTopPadding(), - bottom = innerPadding.calculateBottomPadding() - ) - ) { - ProgressFragment( - totalTasks = totalTasks, - completedTasks = completedTasks, - modifier = Modifier - .weight(2f) - .fillMaxSize() - ) - ManagerFragment( - state = listState, - list = filteredTodoList, - onItemClick = { item -> - if (inSelectedMode) { - viewModel.toggleTodoSelection(item) - } else { - toTodoEditPage(item) - } - }, - onItemLongClick = { viewModel.toggleTodoSelection(it) }, - onItemChecked = { - viewModel.updateTodo(it.copy(isCompleted = true)) - viewModel.playConfetti() - }, - selectedTodoIds = selectedTodoIds, - // sharedTransitionScope = sharedTransitionScope, - // animatedVisibilityScope = animatedVisibilityScope, - modifier = Modifier - .weight(3f) - .fillMaxSize() - ) - } - } else {*/ - Column( - modifier = Modifier.padding( - top = innerPadding.calculateTopPadding(), - bottom = innerPadding.calculateBottomPadding() - ) + ) { + LazyColumn( + state = listState, + verticalArrangement = Arrangement.spacedBy(5.dp), + modifier = Modifier.fillMaxSize() ) { - ProgressFragment( - totalTasks = totalTasks, - completedTasks = completedTasks, - modifier = Modifier.fillMaxWidth() - ) - ManagerFragment( - state = listState, - list = filteredTodoList, - onItemClick = { item -> - if (inSelectedMode) { - viewModel.toggleTodoSelection(item) - } else { - toTodoEditPage(item) - } - }, - onItemLongClick = { viewModel.toggleTodoSelection(it) }, - onItemChecked = { - viewModel.updateTodo(it.copy(isCompleted = true)) - viewModel.playConfetti() - }, - selectedTodoIds = selectedTodoIds, - modifier = Modifier.fillMaxWidth() - ) + item { + Spacer(modifier = Modifier.size(TodoDefaults.settingsItemVerticalPadding)) + } + + if (filteredTodoList.isEmpty()) { + item { + Text( + text = stringResource(R.string.tip_no_task), + style = MaterialTheme.typography.titleMedium, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + } + } else { + items( + items = filteredTodoList, + key = { it.id } + ) { + TodoCard( + // id = item.id, + content = it.content, + category = it.category, + completed = it.isCompleted, + priority = Priority.fromFloat(it.priority), + selected = selectedTodoIds.contains(it.id), + onCardClick = { + if (inSelectedMode) { + viewModel.toggleTodoSelection(it) + } else { + toTodoEditPage(it) + } + }, + onCardLongClick = { viewModel.toggleTodoSelection(it) }, + onChecked = { + viewModel.updateTodo(it.copy(isCompleted = true)) + viewModel.playConfetti() + }, + modifier = Modifier + .animateItem( + fadeInSpec = MaterialTheme.motionScheme.defaultEffectsSpec(), + placementSpec = MaterialTheme.motionScheme.defaultSpatialSpec(), + fadeOutSpec = MaterialTheme.motionScheme.fastEffectsSpec() + ) + ) + } + item { + Spacer(modifier = Modifier.size(TodoDefaults.settingsItemVerticalPadding)) + } + } } - /*}*/ - ConfirmDialog( - visible = showDeleteConfirmDialog, - iconRes = R.drawable.ic_delete, - text = stringResource(R.string.tip_delete_task, selectedTodoIds.size), - onConfirm = { viewModel.deleteSelectedTodo() }, - onDismiss = { showDeleteConfirmDialog = false } - ) } + ConfirmDialog( + visible = showDeleteConfirmDialog, + iconRes = R.drawable.ic_delete, + text = stringResource(R.string.tip_delete_task, selectedTodoIds.size), + onConfirm = { viewModel.deleteSelectedTodo() }, + onDismiss = { showDeleteConfirmDialog = false } + ) } \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/TodoCard.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/TodoCard.kt index 2c7c960..57db1c5 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/TodoCard.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/TodoCard.kt @@ -121,7 +121,7 @@ fun TodoCard( modifier = Modifier.padding(start = 5.dp) ) { Text( - text = priority.displayName(context), + text = stringResource(priority.nameRes), textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None, ) } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/TodoTopAppBar.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/TodoTopAppBar.kt index 2f00f64..d570b0b 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/TodoTopAppBar.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/TodoTopAppBar.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults @@ -30,6 +31,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import cn.super12138.todo.R +import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.utils.VibrationUtils @OptIn(ExperimentalMaterial3Api::class) @@ -40,15 +42,14 @@ fun TodoTopAppBar( onCancelSelect: () -> Unit, onSelectAll: () -> Unit, onDeleteSelectedTodo: () -> Unit, - toSettingsPage: () -> Unit, modifier: Modifier = Modifier ) { val view = LocalView.current val animatedTopAppBarColors by animateColorAsState( targetValue = if (selectedMode) { - TopAppBarDefaults.topAppBarColors().scrolledContainerColor + MaterialTheme.colorScheme.surfaceContainerHighest } else { - TopAppBarDefaults.topAppBarColors().containerColor + TodoDefaults.BackgroundColor } ) @@ -103,52 +104,29 @@ fun TodoTopAppBar( } }, actions = { - AnimatedContent( - targetState = selectedMode, - transitionSpec = { - ( - fadeIn() + scaleIn(initialScale = 0.92f) - ).togetherWith( - fadeOut(animationSpec = tween(90)) - ) - } - ) { inSelectedMode -> - if (!inSelectedMode) { + AnimatedVisibility(visible = selectedMode) { + Row { IconButton( onClick = { VibrationUtils.performHapticFeedback(view) - toSettingsPage() + onSelectAll() } ) { Icon( - painter = painterResource(R.drawable.ic_settings), - contentDescription = stringResource(R.string.page_settings) + painter = painterResource(R.drawable.ic_select_all), + contentDescription = stringResource(R.string.tip_select_all) ) } - } else { - Row { - IconButton( - onClick = { - VibrationUtils.performHapticFeedback(view) - onSelectAll() - } - ) { - Icon( - painter = painterResource(R.drawable.ic_select_all), - contentDescription = stringResource(R.string.tip_select_all) - ) - } - IconButton( - onClick = { - VibrationUtils.performHapticFeedback(view) - onDeleteSelectedTodo() - } - ) { - Icon( - painter = painterResource(R.drawable.ic_delete), - contentDescription = stringResource(R.string.action_delete) - ) + IconButton( + onClick = { + VibrationUtils.performHapticFeedback(view) + onDeleteSelectedTodo() } + ) { + Icon( + painter = painterResource(R.drawable.ic_delete), + contentDescription = stringResource(R.string.action_delete) + ) } } } @@ -170,7 +148,6 @@ private fun TodoTopAppBarPreview() { selectedMode = selectedMode, onCancelSelect = { selectedMode = !selectedMode }, onSelectAll = { }, - onDeleteSelectedTodo = { }, - toSettingsPage = { selectedMode = !selectedMode } + onDeleteSelectedTodo = { } ) } \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/OverviewPage.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/OverviewPage.kt new file mode 100644 index 0000000..106e7bb --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/OverviewPage.kt @@ -0,0 +1,27 @@ +package cn.super12138.todo.ui.pages.overview + +import androidx.compose.foundation.layout.Row +import androidx.compose.material3.Card +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import cn.super12138.todo.R +import cn.super12138.todo.ui.components.TopAppBarScaffold + +@Composable +fun OverviewPage(modifier: Modifier = Modifier) { + TopAppBarScaffold( + title = stringResource(R.string.page_overview), + modifier = modifier + ) { + Row { + Card { + Icon(painter = painterResource(R.drawable.ic_ballot), contentDescription = null) + Text("总任务") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAbout.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAbout.kt index 717c55d..b090140 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAbout.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAbout.kt @@ -2,7 +2,6 @@ package cn.super12138.todo.ui.pages.settings import android.widget.Toast import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.runtime.Composable @@ -19,7 +18,6 @@ import androidx.compose.ui.res.stringResource import cn.super12138.todo.R import cn.super12138.todo.constants.Constants import cn.super12138.todo.ui.components.TopAppBarScaffold -import cn.super12138.todo.ui.components.RoundedScreenContainer import cn.super12138.todo.ui.icons.GitHubIcon import cn.super12138.todo.ui.pages.settings.components.SettingsContainer import cn.super12138.todo.ui.pages.settings.components.SettingsItem @@ -39,7 +37,7 @@ fun SettingsAbout( title = stringResource(R.string.pref_about), onBack = onNavigateUp, modifier = modifier, - ) { innerPadding -> + ) { val context = LocalContext.current val uriHandler = LocalUriHandler.current var clickCount by remember { mutableIntStateOf(0) } @@ -57,60 +55,58 @@ fun SettingsAbout( } } - RoundedScreenContainer(Modifier.padding(innerPadding)) { - SettingsContainer(Modifier.fillMaxSize()) { - item { - SettingsItem( - leadingIconRes = R.drawable.ic_numbers, - title = stringResource(R.string.pref_app_version), - description = SystemUtils.getAppVersion(context), - onClick = { - clickCount++ - if (clickCount == 5) { - if ((System.currentTimeMillis() % 2) == 0.toLong()) { - Toast.makeText(context, "🍨", Toast.LENGTH_SHORT).show() - } else { - Toast.makeText(context, "✈️", Toast.LENGTH_SHORT).show() - } - clickCount = 0 + SettingsContainer(Modifier.fillMaxSize()) { + item { + SettingsItem( + leadingIconRes = R.drawable.ic_numbers, + title = stringResource(R.string.pref_app_version), + description = SystemUtils.getAppVersion(context), + onClick = { + clickCount++ + if (clickCount == 5) { + if ((System.currentTimeMillis() % 2) == 0.toLong()) { + Toast.makeText(context, "🍨", Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(context, "✈️", Toast.LENGTH_SHORT).show() } - }, - topRounded = true - ) - } - item { - SettingsItem( - leadingIconRes = R.drawable.ic_person_4, - title = stringResource(R.string.pref_developer), - description = stringResource(R.string.developer_name), - onClick = { uriHandler.openUri(Constants.DEVELOPER_GITHUB) }, - ) - } - item { - SettingsItem( - leadingIcon = GitHubIcon, - title = stringResource(R.string.pref_view_on_github), - description = stringResource(R.string.pref_view_on_github_desc), - onClick = { uriHandler.openUri(Constants.GITHUB_REPO) } - ) - } - item { - SettingsItem( - leadingIconRes = R.drawable.ic_balance, - title = stringResource(R.string.pref_licence), - description = stringResource(R.string.pref_licence_desc), - onClick = toLicencePage - ) - } - item { - SettingsItem( - leadingIconRes = R.drawable.ic_code_blocks, - title = stringResource(R.string.pref_developer_options), - description = stringResource(R.string.pref_developer_options_desc), - onClick = toDevPage, - bottomRounded = true - ) - } + clickCount = 0 + } + }, + topRounded = true + ) + } + item { + SettingsItem( + leadingIconRes = R.drawable.ic_person_4, + title = stringResource(R.string.pref_developer), + description = stringResource(R.string.developer_name), + onClick = { uriHandler.openUri(Constants.DEVELOPER_GITHUB) }, + ) + } + item { + SettingsItem( + leadingIcon = GitHubIcon, + title = stringResource(R.string.pref_view_on_github), + description = stringResource(R.string.pref_view_on_github_desc), + onClick = { uriHandler.openUri(Constants.GITHUB_REPO) } + ) + } + item { + SettingsItem( + leadingIconRes = R.drawable.ic_balance, + title = stringResource(R.string.pref_licence), + description = stringResource(R.string.pref_licence_desc), + onClick = toLicencePage + ) + } + item { + SettingsItem( + leadingIconRes = R.drawable.ic_code_blocks, + title = stringResource(R.string.pref_developer_options), + description = stringResource(R.string.pref_developer_options_desc), + onClick = toDevPage, + bottomRounded = true + ) } } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutLicence.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutLicence.kt index a5b0e73..b79d198 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutLicence.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutLicence.kt @@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.selection.SelectionContainer @@ -30,7 +29,6 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import cn.super12138.todo.R import cn.super12138.todo.ui.components.BasicDialog -import cn.super12138.todo.ui.components.RoundedScreenContainer import cn.super12138.todo.ui.components.TopAppBarScaffold import cn.super12138.todo.ui.pages.settings.components.SettingsContainer import cn.super12138.todo.ui.pages.settings.components.SettingsItem @@ -53,103 +51,100 @@ fun SettingsAboutLicence( title = stringResource(R.string.pref_licence), onBack = onNavigateUp, modifier = modifier - ) { innerPadding -> + ) { + SettingsContainer(Modifier.fillMaxSize()) { + itemsIndexed( + items = libraries?.libraries ?: listOf(), + key = { _, library -> library.artifactId } + ) { index, library -> + var openDialog by rememberSaveable { mutableStateOf(false) } - RoundedScreenContainer(Modifier.padding(innerPadding)) { - SettingsContainer(Modifier.fillMaxSize()) { - itemsIndexed( - items = libraries?.libraries ?: listOf(), - key = { _, library -> library.artifactId } - ) { index, library -> - var openDialog by rememberSaveable { mutableStateOf(false) } - - SettingsItem( - headlineContent = { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { + SettingsItem( + headlineContent = { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = library.name, + modifier = Modifier.weight(1f), + style = MaterialTheme.typography.titleLarge, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + val version = library.artifactVersion + if (version != null) { Text( - text = library.name, - modifier = Modifier.weight(1f), - style = MaterialTheme.typography.titleLarge, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - val version = library.artifactVersion - if (version != null) { - Text( - text = version, - style = MaterialTheme.typography.bodyMedium, - textAlign = TextAlign.Center - ) - } - } - val author = library.author - if (author.isNotBlank()) { - Text( - text = author, - style = MaterialTheme.typography.bodyMedium + text = version, + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Center ) } - }, - supportingContent = { - if (library.licenses.isNotEmpty()) { - FlowRow { - library.licenses.forEach { - Badge(containerColor = MaterialTheme.colorScheme.primary) { - Text( - maxLines = 1, - text = it.name, - // style = MaterialTheme.typography.labelSmall, - textAlign = TextAlign.Center, - overflow = TextOverflow.Ellipsis, - ) - } + } + val author = library.author + if (author.isNotBlank()) { + Text( + text = author, + style = MaterialTheme.typography.bodyMedium + ) + } + }, + supportingContent = { + if (library.licenses.isNotEmpty()) { + FlowRow { + library.licenses.forEach { + Badge(containerColor = MaterialTheme.colorScheme.primary) { + Text( + maxLines = 1, + text = it.name, + // style = MaterialTheme.typography.labelSmall, + textAlign = TextAlign.Center, + overflow = TextOverflow.Ellipsis, + ) } } } - }, - onClick = { - val license = library.licenses.firstOrNull() - if (!license?.htmlReadyLicenseContent.isNullOrBlank()) { - openDialog = true - } else if (!license?.url.isNullOrBlank()) { - license.url?.also { - try { - uriHandler.openUri(it) - } catch (t: Throwable) { - throw Exception("Failed to open licence URL: $it", t) - } + } + }, + onClick = { + val license = library.licenses.firstOrNull() + if (!license?.htmlReadyLicenseContent.isNullOrBlank()) { + openDialog = true + } else if (!license?.url.isNullOrBlank()) { + license.url?.also { + try { + uriHandler.openUri(it) + } catch (t: Throwable) { + throw Exception("Failed to open licence URL: $it", t) } } - }, - topRounded = index == 0, - bottomRounded = index == (libraries?.libraries?.size ?: 1) - 1 - ) + } + }, + topRounded = index == 0, + bottomRounded = index == (libraries?.libraries?.size ?: 1) - 1 + ) - BasicDialog( - visible = openDialog, - title = { Text(library.name) }, - text = { - library.licenses.firstOrNull()?.licenseContent?.let { - Column(Modifier.verticalScroll(rememberScrollState())) { - SelectionContainer { Text(text = it) } - } + BasicDialog( + visible = openDialog, + title = { Text(library.name) }, + text = { + library.licenses.firstOrNull()?.licenseContent?.let { + Column(Modifier.verticalScroll(rememberScrollState())) { + SelectionContainer { Text(text = it) } } - }, - confirmButton = { - FilledTonalButton( - onClick = { - openDialog = false - VibrationUtils.performHapticFeedback(view) - }, - shapes = ButtonDefaults.shapes() - ) { Text(stringResource(R.string.action_confirm)) } - }, - onDismissRequest = { openDialog = false } - ) - } + } + }, + confirmButton = { + FilledTonalButton( + onClick = { + openDialog = false + VibrationUtils.performHapticFeedback(view) + }, + shapes = ButtonDefaults.shapes() + ) { Text(stringResource(R.string.action_confirm)) } + }, + onDismissRequest = { openDialog = false } + ) } } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAppearance.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAppearance.kt index afdc329..7eac076 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAppearance.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAppearance.kt @@ -1,7 +1,6 @@ package cn.super12138.todo.ui.pages.settings import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -12,16 +11,15 @@ import androidx.compose.ui.res.stringResource import cn.super12138.todo.R import cn.super12138.todo.constants.Constants import cn.super12138.todo.logic.datastore.DataStoreManager +import cn.super12138.todo.logic.model.ContrastLevel +import cn.super12138.todo.logic.model.DarkMode +import cn.super12138.todo.logic.model.PaletteStyle import cn.super12138.todo.ui.components.TopAppBarScaffold -import cn.super12138.todo.ui.components.RoundedScreenContainer import cn.super12138.todo.ui.pages.settings.components.SettingsContainer import cn.super12138.todo.ui.pages.settings.components.SwitchSettingsItem import cn.super12138.todo.ui.pages.settings.components.appearance.contrast.ContrastPicker import cn.super12138.todo.ui.pages.settings.components.appearance.darkmode.DarkModePicker import cn.super12138.todo.ui.pages.settings.components.appearance.palette.PalettePicker -import cn.super12138.todo.ui.theme.ContrastLevel -import cn.super12138.todo.ui.theme.DarkMode -import cn.super12138.todo.ui.theme.PaletteStyle import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @@ -40,44 +38,42 @@ fun SettingsAppearance( title = stringResource(R.string.pref_appearance), onBack = onNavigateUp, modifier = modifier, - ) { innerPadding -> - RoundedScreenContainer(Modifier.padding(innerPadding)) { - SettingsContainer(Modifier.fillMaxSize()) { - item(key = 1) { - SwitchSettingsItem( - checked = dynamicColor, - leadingIconRes = R.drawable.ic_wand_stars, - title = stringResource(R.string.pref_appearance_dynamic_color), - description = stringResource(R.string.pref_appearance_dynamic_color_desc), - onCheckedChange = { scope.launch { DataStoreManager.setDynamicColor(it) } }, - topRounded = true - ) - } + ) { + SettingsContainer(Modifier.fillMaxSize()) { + item(key = 1) { + SwitchSettingsItem( + checked = dynamicColor, + leadingIconRes = R.drawable.ic_wand_stars, + title = stringResource(R.string.pref_appearance_dynamic_color), + description = stringResource(R.string.pref_appearance_dynamic_color_desc), + onCheckedChange = { scope.launch { DataStoreManager.setDynamicColor(it) } }, + topRounded = true + ) + } - item(key = 2) { - DarkModePicker( - currentDarkMode = { DarkMode.fromId(darkMode) }, - onDarkModeChange = { scope.launch { DataStoreManager.setDarkMode(it.id) } } - ) - } + item(key = 2) { + DarkModePicker( + currentDarkMode = { DarkMode.fromId(darkMode) }, + onDarkModeChange = { scope.launch { DataStoreManager.setDarkMode(it.id) } } + ) + } - item(key = 3) { - PalettePicker( - currentPalette = { PaletteStyle.fromId(paletteStyle) }, - onPaletteChange = { scope.launch { DataStoreManager.setPaletteStyle(it.id) } }, - isDynamicColor = dynamicColor, - isDarkMode = DarkMode.fromId(darkMode), - contrastLevel = ContrastLevel.fromFloat(contrastLevel) - ) - } + item(key = 3) { + PalettePicker( + currentPalette = { PaletteStyle.fromId(paletteStyle) }, + onPaletteChange = { scope.launch { DataStoreManager.setPaletteStyle(it.id) } }, + isDynamicColor = dynamicColor, + isDarkMode = DarkMode.fromId(darkMode), + contrastLevel = ContrastLevel.fromFloat(contrastLevel) + ) + } - item(key = 4) { - ContrastPicker( - currentContrast = ContrastLevel.fromFloat(contrastLevel), - onContrastChange = { scope.launch { DataStoreManager.setContrastLevel(it.value) } }, - bottomRounded = true - ) - } + item(key = 4) { + ContrastPicker( + currentContrast = ContrastLevel.fromFloat(contrastLevel), + onContrastChange = { scope.launch { DataStoreManager.setContrastLevel(it.value) } }, + bottomRounded = true + ) } } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsData.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsData.kt index 24c0d06..c42671d 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsData.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsData.kt @@ -5,7 +5,6 @@ import android.content.Intent import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.SnackbarHost @@ -25,7 +24,6 @@ import cn.super12138.todo.R import cn.super12138.todo.ui.activities.MainActivity import cn.super12138.todo.ui.components.ConfirmDialog import cn.super12138.todo.ui.components.TopAppBarScaffold -import cn.super12138.todo.ui.components.RoundedScreenContainer import cn.super12138.todo.ui.pages.settings.components.SettingsCategory import cn.super12138.todo.ui.pages.settings.components.SettingsContainer import cn.super12138.todo.ui.pages.settings.components.SettingsItem @@ -102,44 +100,45 @@ fun SettingsData( onBack = onNavigateUp, snackbarHost = { SnackbarHost(snackbarHostState) }, modifier = modifier, - ) { innerPadding -> - RoundedScreenContainer(Modifier.padding(innerPadding)) { - SettingsContainer(Modifier.fillMaxSize()) { - item { - SettingsCategory(title = stringResource(R.string.pref_category_data_management), first = true) - SettingsItem( - leadingIconRes = R.drawable.ic_download, - title = stringResource(R.string.pref_backup), - description = stringResource(R.string.pref_backup_desc), - onClick = { - backupLauncher.launch("Todo-backup-${SystemUtils.getTime()}.zip") - }, - topRounded = true - ) - } - item { - SettingsItem( - leadingIconRes = R.drawable.ic_upload, - title = stringResource(R.string.pref_restore), - description = stringResource(R.string.pref_restore_desc), - onClick = { - restoreLauncher.launch(arrayOf("application/zip")) - }, - bottomRounded = true - ) - } + ) { + SettingsContainer(Modifier.fillMaxSize()) { + item { + SettingsCategory( + title = stringResource(R.string.pref_category_data_management), + first = true + ) + SettingsItem( + leadingIconRes = R.drawable.ic_download, + title = stringResource(R.string.pref_backup), + description = stringResource(R.string.pref_backup_desc), + onClick = { + backupLauncher.launch("Todo-backup-${SystemUtils.getTime()}.zip") + }, + topRounded = true + ) + } + item { + SettingsItem( + leadingIconRes = R.drawable.ic_upload, + title = stringResource(R.string.pref_restore), + description = stringResource(R.string.pref_restore_desc), + onClick = { + restoreLauncher.launch(arrayOf("application/zip")) + }, + bottomRounded = true + ) + } - item { - SettingsCategory(stringResource(R.string.pref_category_category_management)) - SettingsItem( - leadingIconRes = R.drawable.ic_category, - title = stringResource(R.string.pref_category_category_management), - description = stringResource(R.string.pref_category_management_desc), - onClick = toCategoryManager, - topRounded = true, - bottomRounded = true - ) - } + item { + SettingsCategory(stringResource(R.string.pref_category_category_management)) + SettingsItem( + leadingIconRes = R.drawable.ic_category, + title = stringResource(R.string.pref_category_category_management), + description = stringResource(R.string.pref_category_management_desc), + onClick = toCategoryManager, + topRounded = true, + bottomRounded = true + ) } } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDataCategory.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDataCategory.kt index ab3db90..162834c 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDataCategory.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDataCategory.kt @@ -6,7 +6,6 @@ import androidx.compose.animation.core.spring import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState @@ -30,8 +29,8 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.IntOffset import cn.super12138.todo.R import cn.super12138.todo.logic.datastore.DataStoreManager -import cn.super12138.todo.ui.components.TopAppBarScaffold import cn.super12138.todo.ui.components.TodoFloatingActionButton +import cn.super12138.todo.ui.components.TopAppBarScaffold import cn.super12138.todo.ui.pages.settings.components.category.CategoryItem import cn.super12138.todo.ui.pages.settings.components.category.CategoryPromptDialog import kotlinx.coroutines.launch @@ -70,12 +69,10 @@ fun SettingsDataCategory( ) }, modifier = modifier, - ) { innerPadding -> + ) { LazyColumn( state = listState, - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) //TODO: 可能需要清除屏幕边距 + modifier = Modifier.fillMaxSize() ) { if (categories.isEmpty()) { item { diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDeveloperOptions.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDeveloperOptions.kt index 9e539c9..f57ff34 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDeveloperOptions.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDeveloperOptions.kt @@ -1,14 +1,12 @@ package cn.super12138.todo.ui.pages.settings import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import cn.super12138.todo.R import cn.super12138.todo.ui.components.TopAppBarScaffold -import cn.super12138.todo.ui.components.RoundedScreenContainer import cn.super12138.todo.ui.pages.settings.components.SettingsContainer import cn.super12138.todo.ui.pages.settings.components.SettingsItem @@ -23,18 +21,16 @@ fun SettingsDeveloperOptions( title = stringResource(R.string.pref_developer_options), onBack = onNavigateUp, modifier = modifier, - ) { innerPadding -> - RoundedScreenContainer(Modifier.padding(innerPadding)) { - SettingsContainer(Modifier.fillMaxSize()) { - item { - SettingsItem( - leadingIconRes = R.drawable.ic_padding, - title = stringResource(R.string.pref_padding), - onClick = toPaddingPage, - topRounded = true, - bottomRounded = true - ) - } + ) { + SettingsContainer(Modifier.fillMaxSize()) { + item { + SettingsItem( + leadingIconRes = R.drawable.ic_padding, + title = stringResource(R.string.pref_padding), + onClick = toPaddingPage, + topRounded = true, + bottomRounded = true + ) } } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterfaceInteraction.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterfaceInteraction.kt index 80677ea..afbd75b 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterfaceInteraction.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterfaceInteraction.kt @@ -1,14 +1,12 @@ package cn.super12138.todo.ui.pages.settings import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue @@ -19,7 +17,6 @@ import cn.super12138.todo.R import cn.super12138.todo.constants.Constants import cn.super12138.todo.logic.datastore.DataStoreManager import cn.super12138.todo.logic.model.SortingMethod -import cn.super12138.todo.ui.components.RoundedScreenContainer import cn.super12138.todo.ui.components.TopAppBarScaffold import cn.super12138.todo.ui.pages.settings.components.SettingsCategory import cn.super12138.todo.ui.pages.settings.components.SettingsContainer @@ -48,70 +45,69 @@ fun SettingsInterface( title = stringResource(R.string.pref_interface_interaction), onBack = onNavigateUp, modifier = modifier, - ) { innerPadding -> - RoundedScreenContainer(Modifier.padding(innerPadding)) { - SettingsContainer(Modifier.fillMaxSize()) { - item { - SettingsCategory(title = stringResource(R.string.pref_category_todo_list), first = true) - SwitchSettingsItem( - leadingIconRes = R.drawable.ic_checklist, - title = stringResource(R.string.pref_show_completed), - description = stringResource(R.string.pref_show_completed_desc), - checked = showCompleted, - onCheckedChange = { scope.launch { DataStoreManager.setShowCompleted(it) } }, - topRounded = true - ) - } - item { - SettingsItem( - leadingIconRes = R.drawable.ic_sort, - title = stringResource(R.string.pref_sorting_method), - description = SortingMethod.fromId(sortingMethod).getDisplayName(context), - onClick = { showSortingMethodDialog = true }, - bottomRounded = true - ) - } + ) { + SettingsContainer(Modifier.fillMaxSize()) { + item { + SettingsCategory( + title = stringResource(R.string.pref_category_todo_list), + first = true + ) + SwitchSettingsItem( + leadingIconRes = R.drawable.ic_checklist, + title = stringResource(R.string.pref_show_completed), + description = stringResource(R.string.pref_show_completed_desc), + checked = showCompleted, + onCheckedChange = { scope.launch { DataStoreManager.setShowCompleted(it) } }, + topRounded = true + ) + } + item { + SettingsItem( + leadingIconRes = R.drawable.ic_sort, + title = stringResource(R.string.pref_sorting_method), + description = stringResource(SortingMethod.fromId(sortingMethod).nameRes), + onClick = { showSortingMethodDialog = true }, + bottomRounded = true + ) + } - item { - SettingsCategory(stringResource(R.string.pref_category_global)) - SwitchSettingsItem( - checked = secureMode, - leadingIconRes = R.drawable.ic_shield, - title = stringResource(R.string.pref_secure_mode), - description = stringResource(R.string.pref_secure_mode_desc), - onCheckedChange = { scope.launch { DataStoreManager.setSecureMode(it) } }, - topRounded = true - ) - } + item { + SettingsCategory(stringResource(R.string.pref_category_global)) + SwitchSettingsItem( + checked = secureMode, + leadingIconRes = R.drawable.ic_shield, + title = stringResource(R.string.pref_secure_mode), + description = stringResource(R.string.pref_secure_mode_desc), + onCheckedChange = { scope.launch { DataStoreManager.setSecureMode(it) } }, + topRounded = true + ) + } - item { - SwitchSettingsItem( - checked = hapticFeedback, - leadingIconRes = R.drawable.ic_touch_long, - title = stringResource(R.string.pref_haptic_feedback), - description = stringResource(R.string.pref_haptic_feedback_desc), - onCheckedChange = { scope.launch { DataStoreManager.setHapticFeedback(it) } }, - bottomRounded = true - ) - SettingsPlainBox(stringResource(R.string.pref_haptic_feedback_more_info)) - } + item { + SwitchSettingsItem( + checked = hapticFeedback, + leadingIconRes = R.drawable.ic_touch_long, + title = stringResource(R.string.pref_haptic_feedback), + description = stringResource(R.string.pref_haptic_feedback_desc), + onCheckedChange = { scope.launch { DataStoreManager.setHapticFeedback(it) } }, + bottomRounded = true + ) + SettingsPlainBox(stringResource(R.string.pref_haptic_feedback_more_info)) } } - val sortingList = remember { - SortingMethod.entries.map { - SettingsRadioOptions( - id = it.id, - text = it.getDisplayName(context) - ) - } + val sortingList = SortingMethod.entries.map { + SettingsRadioOptions( + id = it.id, + text = stringResource(it.nameRes) + ) } SettingsRadioDialog( visible = showSortingMethodDialog, title = stringResource(R.string.pref_sorting_method), currentOptions = SettingsRadioOptions( id = sortingMethod, - text = SortingMethod.fromId(sortingMethod).getDisplayName(context) + text = stringResource(SortingMethod.fromId(sortingMethod).nameRes) ), options = sortingList, onSelect = { scope.launch { DataStoreManager.setSortingMethod(it) } }, diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsMain.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsMain.kt index 45f90aa..1ee7715 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsMain.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsMain.kt @@ -1,7 +1,6 @@ package cn.super12138.todo.ui.pages.settings import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.runtime.Composable @@ -9,7 +8,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import cn.super12138.todo.R import cn.super12138.todo.ui.components.TopAppBarScaffold -import cn.super12138.todo.ui.components.RoundedScreenContainer import cn.super12138.todo.ui.pages.settings.components.SettingsContainer import cn.super12138.todo.ui.pages.settings.components.SettingsItem @@ -20,53 +18,49 @@ fun SettingsMain( toInterfacePage: () -> Unit, toDataPage: () -> Unit, toAboutPage: () -> Unit, - onNavigateUp: () -> Unit, modifier: Modifier = Modifier ) { TopAppBarScaffold( title = stringResource(R.string.page_settings), - onBack = onNavigateUp, modifier = modifier - ) { innerPadding -> - RoundedScreenContainer(Modifier.padding(innerPadding)) { - SettingsContainer(Modifier.fillMaxSize()) { - item { - SettingsItem( - leadingIconRes = R.drawable.ic_palette, - title = stringResource(R.string.pref_appearance), - description = stringResource(R.string.pref_appearance_desc), - onClick = toAppearancePage, - topRounded = true - ) - } + ) { + SettingsContainer(Modifier.fillMaxSize()) { + item { + SettingsItem( + leadingIconRes = R.drawable.ic_palette, + title = stringResource(R.string.pref_appearance), + description = stringResource(R.string.pref_appearance_desc), + onClick = toAppearancePage, + topRounded = true + ) + } - item { - SettingsItem( - leadingIconRes = R.drawable.ic_view_comfy, - title = stringResource(R.string.pref_interface_interaction), - description = stringResource(R.string.pref_interface_interaction_desc), - onClick = toInterfacePage - ) - } + item { + SettingsItem( + leadingIconRes = R.drawable.ic_view_comfy, + title = stringResource(R.string.pref_interface_interaction), + description = stringResource(R.string.pref_interface_interaction_desc), + onClick = toInterfacePage + ) + } - item { - SettingsItem( - leadingIconRes = R.drawable.ic_dns, - title = stringResource(R.string.pref_data), - description = stringResource(R.string.pref_data_desc), - onClick = toDataPage - ) - } + item { + SettingsItem( + leadingIconRes = R.drawable.ic_dns, + title = stringResource(R.string.pref_data), + description = stringResource(R.string.pref_data_desc), + onClick = toDataPage + ) + } - item { - SettingsItem( - leadingIconRes = R.drawable.ic_info, - title = stringResource(R.string.pref_about), - description = stringResource(R.string.pref_about_desc), - onClick = toAboutPage, - bottomRounded = true - ) - } + item { + SettingsItem( + leadingIconRes = R.drawable.ic_info, + title = stringResource(R.string.pref_about), + description = stringResource(R.string.pref_about_desc), + onClick = toAboutPage, + bottomRounded = true + ) } } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/contrast/ContrastPicker.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/contrast/ContrastPicker.kt index 3ba95bc..2edea55 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/contrast/ContrastPicker.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/contrast/ContrastPicker.kt @@ -29,8 +29,8 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.dp import cn.super12138.todo.R +import cn.super12138.todo.logic.model.ContrastLevel import cn.super12138.todo.ui.pages.settings.components.MoreContentSettingsItem -import cn.super12138.todo.ui.theme.ContrastLevel import cn.super12138.todo.utils.VibrationUtils @Composable @@ -50,8 +50,7 @@ fun ContrastPicker( bottomRounded = bottomRounded, modifier = modifier ) { - val contrastLevelName = - ContrastLevel.entries.map { it.getDisplayName(context) } + val contrastLevelName = ContrastLevel.entries.map { stringResource(it.nameRes) } var lastVibratedLevel by remember { mutableFloatStateOf(currentContrast.value) } Slider( diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/darkmode/DarkModePicker.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/darkmode/DarkModePicker.kt index b777ade..1d5f177 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/darkmode/DarkModePicker.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/darkmode/DarkModePicker.kt @@ -9,13 +9,12 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import cn.super12138.todo.R +import cn.super12138.todo.logic.model.DarkMode import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.pages.settings.components.LazyRowSettingsItem -import cn.super12138.todo.ui.theme.DarkMode @Composable fun DarkModePicker( @@ -25,7 +24,6 @@ fun DarkModePicker( topRounded: Boolean = false, bottomRounded: Boolean = false ) { - val context = LocalContext.current val isInDarkTheme = isSystemInDarkTheme() val darkModeList = remember { DarkMode.entries.toList() } @@ -50,7 +48,7 @@ fun DarkModePicker( DarkModeItem( iconRes = it.iconRes, - name = it.getDisplayName(context), + name = stringResource(it.nameRes), contentColor = contentColor, containerColor = containerColor, selected = isSelected, diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/palette/PaletteItem.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/palette/PaletteItem.kt index 4e53edf..93a26ac 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/palette/PaletteItem.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/palette/PaletteItem.kt @@ -22,14 +22,14 @@ 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.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastForEach -import cn.super12138.todo.ui.theme.ContrastLevel -import cn.super12138.todo.ui.theme.PaletteStyle +import cn.super12138.todo.logic.model.ContrastLevel +import cn.super12138.todo.logic.model.PaletteStyle import cn.super12138.todo.ui.theme.dynamicColorScheme import cn.super12138.todo.utils.VibrationUtils @@ -44,7 +44,6 @@ fun PaletteItem( modifier: Modifier = Modifier ) { val view = LocalView.current - val context = LocalContext.current Column( modifier = modifier @@ -107,7 +106,7 @@ fun PaletteItem( Spacer(Modifier.size(8.dp)) Text( - text = paletteStyle.getDisplayName(context), + text = stringResource(paletteStyle.nameRes), style = MaterialTheme.typography.bodyMedium, color = if (selected) { MaterialTheme.colorScheme.primary diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/palette/PalettePicker.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/palette/PalettePicker.kt index c0b266d..d0dc5ec 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/palette/PalettePicker.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/palette/PalettePicker.kt @@ -11,11 +11,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import cn.super12138.todo.R +import cn.super12138.todo.logic.model.ContrastLevel +import cn.super12138.todo.logic.model.DarkMode +import cn.super12138.todo.logic.model.PaletteStyle import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.pages.settings.components.LazyRowSettingsItem -import cn.super12138.todo.ui.theme.ContrastLevel -import cn.super12138.todo.ui.theme.DarkMode -import cn.super12138.todo.ui.theme.PaletteStyle @Composable fun PalettePicker( diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/theme/ContrastLevel.kt b/app/src/main/kotlin/cn/super12138/todo/ui/theme/ContrastLevel.kt deleted file mode 100644 index 5446f75..0000000 --- a/app/src/main/kotlin/cn/super12138/todo/ui/theme/ContrastLevel.kt +++ /dev/null @@ -1,27 +0,0 @@ -package cn.super12138.todo.ui.theme - -import android.content.Context -import cn.super12138.todo.R - -enum class ContrastLevel(val value: Float) { - VeryLow(-1f), - Low(-0.5f), - Default(0f), - Medium(0.5f), - High(1f); - - fun getDisplayName(context: Context): String { - val resId = when (this) { - VeryLow -> R.string.contrast_very_low - Low -> R.string.contrast_low - Default -> R.string.contrast_default - Medium -> R.string.contrast_high - High -> R.string.contrast_very_high - } - return context.getString(resId) - } - - companion object { - fun fromFloat(float: Float) = entries.find { it.value == float } ?: Default - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/theme/DarkMode.kt b/app/src/main/kotlin/cn/super12138/todo/ui/theme/DarkMode.kt deleted file mode 100644 index d63eb4b..0000000 --- a/app/src/main/kotlin/cn/super12138/todo/ui/theme/DarkMode.kt +++ /dev/null @@ -1,26 +0,0 @@ -package cn.super12138.todo.ui.theme - -import android.content.Context -import cn.super12138.todo.R - -enum class DarkMode( - val id: Int, - val iconRes: Int -) { - FollowSystem(-1, R.drawable.ic_lightbulb_2), - Light(1, R.drawable.ic_light_mode), - Dark(2, R.drawable.ic_dark_mode); - - fun getDisplayName(context: Context): String { - val resId = when (this) { - FollowSystem -> R.string.dark_mode_system - Light -> R.string.dark_mode_light - Dark -> R.string.dark_mode_dark - } - return context.getString(resId) - } - - companion object { - fun fromId(id: Int) = entries.find { it.id == id } ?: FollowSystem - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/theme/PaletteStyle.kt b/app/src/main/kotlin/cn/super12138/todo/ui/theme/PaletteStyle.kt deleted file mode 100644 index c112917..0000000 --- a/app/src/main/kotlin/cn/super12138/todo/ui/theme/PaletteStyle.kt +++ /dev/null @@ -1,35 +0,0 @@ -package cn.super12138.todo.ui.theme - -import android.content.Context -import cn.super12138.todo.R - -enum class PaletteStyle(val id: Int) { - TonalSpot(1), - Neutral(2), - Vibrant(3), - Expressive(4), - Rainbow(5), - FruitSalad(6), - Monochrome(7), - Fidelity(8), - Content(9); - - fun getDisplayName(context: Context): String { - val resId = when (this) { - TonalSpot -> R.string.palette_tonal_spot - Neutral -> R.string.palette_neutral - Vibrant -> R.string.palette_vibrant - Expressive -> R.string.palette_expressive - Rainbow -> R.string.palette_rainbow - FruitSalad -> R.string.palette_fruit_salad - Monochrome -> R.string.palette_monochrome - Fidelity -> R.string.palette_fidelity - Content -> R.string.palette_content - } - return context.getString(resId) - } - - companion object { - fun fromId(id: Int) = entries.firstOrNull { it.id == id } ?: TonalSpot - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/theme/Theme.kt b/app/src/main/kotlin/cn/super12138/todo/ui/theme/Theme.kt index 25505ab..0b0bda7 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/theme/Theme.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/theme/Theme.kt @@ -7,6 +7,7 @@ import androidx.compose.material3.MaterialExpressiveTheme import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource +import cn.super12138.todo.logic.model.PaletteStyle @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/theme/ThemeExt.kt b/app/src/main/kotlin/cn/super12138/todo/ui/theme/ThemeExt.kt index dba667a..947d34e 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/theme/ThemeExt.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/theme/ThemeExt.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb +import cn.super12138.todo.logic.model.PaletteStyle import com.kyant.m3color.hct.Hct import com.kyant.m3color.scheme.SchemeContent import com.kyant.m3color.scheme.SchemeExpressive @@ -98,6 +99,5 @@ private inline fun Int.toColor(): Color = Color(this) // https://github.com/jordond/MaterialKolor/blob/main/material-kolor/src/commonMain/kotlin/com/materialkolor/DynamicMaterialTheme.kt @Composable -private fun Color.animate(animationSpec: AnimationSpec = spring()): Color { - return animateColorAsState(this, animationSpec).value -} \ No newline at end of file +private fun Color.animate(animationSpec: AnimationSpec = spring()): Color = + animateColorAsState(this, animationSpec).value \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/viewmodels/MainViewModel.kt b/app/src/main/kotlin/cn/super12138/todo/ui/viewmodels/MainViewModel.kt index 4095f91..c97ace1 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/viewmodels/MainViewModel.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/viewmodels/MainViewModel.kt @@ -5,12 +5,15 @@ import android.net.Uri import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.navigation3.runtime.NavKey import cn.super12138.todo.TodoApp import cn.super12138.todo.constants.Constants import cn.super12138.todo.logic.Repository import cn.super12138.todo.logic.database.TodoEntity import cn.super12138.todo.logic.datastore.DataStoreManager import cn.super12138.todo.logic.model.SortingMethod +import cn.super12138.todo.ui.navigation.TodoScreen +import cn.super12138.todo.ui.navigation.TopLevelBackStack import cn.super12138.todo.utils.FileUtils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -32,6 +35,8 @@ import java.util.zip.ZipOutputStream @OptIn(ExperimentalCoroutinesApi::class) class MainViewModel : ViewModel() { + val topLevelBackStack = TopLevelBackStack(startKey = TodoScreen.Overview) + // 待办 private val toDos: Flow> = Repository.getAllTodos() val sortedTodos: Flow> = diff --git a/app/src/main/kotlin/cn/super12138/todo/utils/SystemUtils.kt b/app/src/main/kotlin/cn/super12138/todo/utils/SystemUtils.kt index b594f69..cf3b318 100644 --- a/app/src/main/kotlin/cn/super12138/todo/utils/SystemUtils.kt +++ b/app/src/main/kotlin/cn/super12138/todo/utils/SystemUtils.kt @@ -2,6 +2,8 @@ package cn.super12138.todo.utils import android.content.Context import android.os.Build +import androidx.activity.ComponentActivity +import androidx.activity.enableEdgeToEdge import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale @@ -39,4 +41,13 @@ object SystemUtils { return sdf.format(currentTime) } +} + +fun ComponentActivity.configureEdgeToEdge() { + enableEdgeToEdge() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + // Force the 3-button navigation bar to be transparent + // See: https://developer.android.com/develop/ui/views/layout/edge-to-edge#create-transparent + window.isNavigationBarContrastEnforced = false + } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_ballot.xml b/app/src/main/res/drawable/ic_ballot.xml new file mode 100644 index 0000000..caadbc3 --- /dev/null +++ b/app/src/main/res/drawable/ic_ballot.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_ballot_filled.xml b/app/src/main/res/drawable/ic_ballot_filled.xml new file mode 100644 index 0000000..618d530 --- /dev/null +++ b/app/src/main/res/drawable/ic_ballot_filled.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_settings_filled.xml b/app/src/main/res/drawable/ic_settings_filled.xml new file mode 100644 index 0000000..de2bf2a --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_filled.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_view_kanban.xml b/app/src/main/res/drawable/ic_view_kanban.xml new file mode 100644 index 0000000..68826de --- /dev/null +++ b/app/src/main/res/drawable/ic_view_kanban.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_view_kanban_filled.xml b/app/src/main/res/drawable/ic_view_kanban_filled.xml new file mode 100644 index 0000000..79137ec --- /dev/null +++ b/app/src/main/res/drawable/ic_view_kanban_filled.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 862ecdf..99dc1c9 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -115,4 +115,7 @@ 开发者选项 一些调试选项,仅供开发使用 边距 + 已选择 + 待办 + 概览 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ec1df71..5aa16fe 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -117,4 +117,6 @@ Some debugging options, only for DEVELOPMENT use Padding Selected + Tasks + Overview \ No newline at end of file