parent
0748a16547
commit
29d71fc060
25 changed files with 421 additions and 174 deletions
|
|
@ -2,6 +2,7 @@ plugins {
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
alias(libs.plugins.kotlin.compose)
|
alias(libs.plugins.kotlin.compose)
|
||||||
|
alias(libs.plugins.kotlin.serialization)
|
||||||
alias(libs.plugins.ksp)
|
alias(libs.plugins.ksp)
|
||||||
alias(libs.plugins.aboutlibraries)
|
alias(libs.plugins.aboutlibraries)
|
||||||
}
|
}
|
||||||
|
|
@ -34,7 +35,7 @@ android {
|
||||||
applicationId = "cn.super12138.todo"
|
applicationId = "cn.super12138.todo"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 735
|
versionCode = 742
|
||||||
versionName = "2.1.2"
|
versionName = "2.1.2"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
@ -108,9 +109,10 @@ dependencies {
|
||||||
implementation(libs.nl.dionsegijn.konfetti.compose)
|
implementation(libs.nl.dionsegijn.konfetti.compose)
|
||||||
// Lazy Column Scrollbar
|
// Lazy Column Scrollbar
|
||||||
implementation(libs.lazycolumnscrollbar)
|
implementation(libs.lazycolumnscrollbar)
|
||||||
// Kotlin Coroutines
|
// Kotlin
|
||||||
implementation(libs.kotlinx.coroutines.core)
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
implementation(libs.kotlinx.coroutines.android)
|
implementation(libs.kotlinx.coroutines.android)
|
||||||
|
implementation(libs.kotlinx.serialization.json)
|
||||||
// Room
|
// Room
|
||||||
implementation(libs.androidx.room.runtime)
|
implementation(libs.androidx.room.runtime)
|
||||||
implementation(libs.androidx.room.ktx)
|
implementation(libs.androidx.room.ktx)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 4,
|
||||||
|
"identityHash": "80864d24cabaf6ae6bfa6debb235a034",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "todo",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`content` TEXT NOT NULL, `category` TEXT NOT NULL, `completed` INTEGER NOT NULL, `priority` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "content",
|
||||||
|
"columnName": "content",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "category",
|
||||||
|
"columnName": "category",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isCompleted",
|
||||||
|
"columnName": "completed",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "priority",
|
||||||
|
"columnName": "priority",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '80864d24cabaf6ae6bfa6debb235a034')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ object Constants {
|
||||||
|
|
||||||
const val KEY_TODO_FAB_TRANSITION = "todo_fab"
|
const val KEY_TODO_FAB_TRANSITION = "todo_fab"
|
||||||
const val KEY_TODO_CONTENT_TRANSITION = "todo_content"
|
const val KEY_TODO_CONTENT_TRANSITION = "todo_content"
|
||||||
const val KEY_TODO_SUBJECT_TRANSITION = "todo_subject"
|
const val KEY_TODO_CATEGORY_TRANSITION = "todo_category"
|
||||||
|
|
||||||
const val DB_NAME = "todo"
|
const val DB_NAME = "todo"
|
||||||
const val DB_TABLE_NAME = "todo"
|
const val DB_TABLE_NAME = "todo"
|
||||||
|
|
@ -36,4 +36,9 @@ object Constants {
|
||||||
|
|
||||||
const val PREF_HAPTIC_FEEDBACK = "haptic_feedback"
|
const val PREF_HAPTIC_FEEDBACK = "haptic_feedback"
|
||||||
const val PREF_HAPTIC_FEEDBACK_DEFAULT = true
|
const val PREF_HAPTIC_FEEDBACK_DEFAULT = true
|
||||||
|
|
||||||
|
const val PREF_CATEGORIES = "categories"
|
||||||
|
const val PREF_CATEGORIES_DEFAULT = "[]"
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ import androidx.room.migration.Migration
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
import cn.super12138.todo.constants.Constants
|
import cn.super12138.todo.constants.Constants
|
||||||
|
|
||||||
@Database(entities = [TodoEntity::class], version = 3)
|
@Database(entities = [TodoEntity::class], version = 4)
|
||||||
abstract class TodoDatabase : RoomDatabase() {
|
abstract class TodoDatabase : RoomDatabase() {
|
||||||
abstract fun toDoDao(): TodoDao
|
abstract fun toDoDao(): TodoDao
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ abstract class TodoDatabase : RoomDatabase() {
|
||||||
TodoDatabase::class.java,
|
TodoDatabase::class.java,
|
||||||
Constants.DB_NAME
|
Constants.DB_NAME
|
||||||
)
|
)
|
||||||
.addMigrations(MIGRATION_2_3)
|
.addMigrations(MIGRATION_2_3, MIGRATION_3_4)
|
||||||
.fallbackToDestructiveMigration(false)
|
.fallbackToDestructiveMigration(false)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|
@ -36,5 +36,19 @@ abstract class TodoDatabase : RoomDatabase() {
|
||||||
db.execSQL("ALTER TABLE ${Constants.DB_TABLE_NAME} ADD COLUMN custom_subject TEXT NOT NULL DEFAULT ''")
|
db.execSQL("ALTER TABLE ${Constants.DB_TABLE_NAME} ADD COLUMN custom_subject TEXT NOT NULL DEFAULT ''")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 为自定义学科功能进行迁移
|
||||||
|
private val MIGRATION_3_4 = object : Migration(3, 4) {
|
||||||
|
override fun migrate(db: SupportSQLiteDatabase) {
|
||||||
|
// 创建一个新表,其中不含有subject,并且有一个新的category字段(由custom_subject迁移而来)
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS todo_new (content TEXT NOT NULL, category TEXT NOT NULL DEFAULT 'Default Value', completed INTEGER NOT NULL, priority REAL NOT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)")
|
||||||
|
// 将旧表中的数据迁移到新表中
|
||||||
|
db.execSQL("INSERT INTO todo_new (content, category, completed, priority, id) SELECT content, COALESCE(NULLIF(custom_subject, ''), 'Default Value') AS category, completed, priority, id FROM todo")
|
||||||
|
// 删除旧表
|
||||||
|
db.execSQL("DROP TABLE todo")
|
||||||
|
// 重命名新表
|
||||||
|
db.execSQL("ALTER TABLE todo_new RENAME TO todo")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,8 +8,7 @@ import cn.super12138.todo.constants.Constants
|
||||||
@Entity(tableName = Constants.DB_TABLE_NAME)
|
@Entity(tableName = Constants.DB_TABLE_NAME)
|
||||||
data class TodoEntity(
|
data class TodoEntity(
|
||||||
@ColumnInfo(name = "content") val content: String,
|
@ColumnInfo(name = "content") val content: String,
|
||||||
@ColumnInfo(name = "subject") val subject: Int,
|
@ColumnInfo(name = "category") val category: String = "",
|
||||||
@ColumnInfo(name = "custom_subject") val customSubject: String = "",
|
|
||||||
@ColumnInfo(name = "completed") val isCompleted: Boolean = false,
|
@ColumnInfo(name = "completed") val isCompleted: Boolean = false,
|
||||||
@ColumnInfo(name = "priority") val priority: Float,
|
@ColumnInfo(name = "priority") val priority: Float,
|
||||||
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") val id: Int = 0,
|
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") val id: Int = 0,
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,13 @@ import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||||
import androidx.datastore.preferences.core.edit
|
import androidx.datastore.preferences.core.edit
|
||||||
import androidx.datastore.preferences.core.floatPreferencesKey
|
import androidx.datastore.preferences.core.floatPreferencesKey
|
||||||
import androidx.datastore.preferences.core.intPreferencesKey
|
import androidx.datastore.preferences.core.intPreferencesKey
|
||||||
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
import androidx.datastore.preferences.preferencesDataStore
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
import cn.super12138.todo.TodoApp
|
import cn.super12138.todo.TodoApp
|
||||||
import cn.super12138.todo.constants.Constants
|
import cn.super12138.todo.constants.Constants
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
object DataStoreManager {
|
object DataStoreManager {
|
||||||
private val Context.dataStore by preferencesDataStore(
|
private val Context.dataStore by preferencesDataStore(
|
||||||
|
|
@ -40,6 +42,9 @@ object DataStoreManager {
|
||||||
private val SECURE_MODE = booleanPreferencesKey(Constants.PREF_SECURE_MODE)
|
private val SECURE_MODE = booleanPreferencesKey(Constants.PREF_SECURE_MODE)
|
||||||
private val HAPTIC_FEEDBACK = booleanPreferencesKey(Constants.PREF_HAPTIC_FEEDBACK)
|
private val HAPTIC_FEEDBACK = booleanPreferencesKey(Constants.PREF_HAPTIC_FEEDBACK)
|
||||||
|
|
||||||
|
// 数据
|
||||||
|
private val CATEGORIES = stringPreferencesKey(Constants.PREF_CATEGORIES)
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
val dynamicColorFlow: Flow<Boolean> = dataStore.data.map { preferences ->
|
val dynamicColorFlow: Flow<Boolean> = dataStore.data.map { preferences ->
|
||||||
preferences[DYNAMIC_COLOR] ?: Constants.PREF_DYNAMIC_COLOR_DEFAULT
|
preferences[DYNAMIC_COLOR] ?: Constants.PREF_DYNAMIC_COLOR_DEFAULT
|
||||||
|
|
@ -73,6 +78,10 @@ object DataStoreManager {
|
||||||
preferences[HAPTIC_FEEDBACK] ?: Constants.PREF_HAPTIC_FEEDBACK_DEFAULT
|
preferences[HAPTIC_FEEDBACK] ?: Constants.PREF_HAPTIC_FEEDBACK_DEFAULT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val categoriesFlow: Flow<List<String>> = dataStore.data.map { preferences ->
|
||||||
|
Json.decodeFromString(preferences[CATEGORIES] ?: Constants.PREF_CATEGORIES_DEFAULT)
|
||||||
|
}
|
||||||
|
|
||||||
// Setters
|
// Setters
|
||||||
suspend fun setDynamicColor(value: Boolean) {
|
suspend fun setDynamicColor(value: Boolean) {
|
||||||
dataStore.edit { preferences ->
|
dataStore.edit { preferences ->
|
||||||
|
|
@ -121,4 +130,10 @@ object DataStoreManager {
|
||||||
preferences[HAPTIC_FEEDBACK] = value
|
preferences[HAPTIC_FEEDBACK] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun setCategories(value: List<String>) {
|
||||||
|
dataStore.edit { preferences ->
|
||||||
|
preferences[CATEGORIES] = Json.encodeToString(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ enum class SortingMethod(val id: Int) {
|
||||||
Sequential(1),
|
Sequential(1),
|
||||||
|
|
||||||
// 按学科
|
// 按学科
|
||||||
Subject(2),
|
Category(2),
|
||||||
|
|
||||||
// 按优先级
|
// 按优先级
|
||||||
Priority(3),
|
Priority(3),
|
||||||
|
|
@ -25,7 +25,7 @@ enum class SortingMethod(val id: Int) {
|
||||||
fun getDisplayName(context: Context): String {
|
fun getDisplayName(context: Context): String {
|
||||||
val resId = when (this) {
|
val resId = when (this) {
|
||||||
Sequential -> R.string.sorting_sequential
|
Sequential -> R.string.sorting_sequential
|
||||||
Subject -> R.string.sorting_subject
|
Category -> R.string.sorting_category
|
||||||
Priority -> R.string.sorting_priority
|
Priority -> R.string.sorting_priority
|
||||||
Completion -> R.string.sorting_completion
|
Completion -> R.string.sorting_completion
|
||||||
AlphabeticalAscending -> R.string.sorting_alphabetical_ascending
|
AlphabeticalAscending -> R.string.sorting_alphabetical_ascending
|
||||||
|
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
package cn.super12138.todo.logic.model
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import cn.super12138.todo.R
|
|
||||||
|
|
||||||
enum class Subjects(val id: Int) {
|
|
||||||
Chinese(0),
|
|
||||||
Math(1),
|
|
||||||
English(2),
|
|
||||||
Biology(3),
|
|
||||||
Geography(4),
|
|
||||||
Physics(5),
|
|
||||||
Moral(6),
|
|
||||||
Chemistry(7),
|
|
||||||
History(8),
|
|
||||||
Others(99),
|
|
||||||
Custom(100);
|
|
||||||
|
|
||||||
fun getDisplayName(context: Context): String {
|
|
||||||
val resId = when (this) {
|
|
||||||
Chinese -> R.string.subject_chinese
|
|
||||||
Math -> R.string.subject_math
|
|
||||||
English -> R.string.subject_english
|
|
||||||
Biology -> R.string.subject_biology
|
|
||||||
Geography -> R.string.subject_geography
|
|
||||||
Physics -> R.string.subject_physics
|
|
||||||
Moral -> R.string.subject_moral
|
|
||||||
Chemistry -> R.string.subject_chemistry
|
|
||||||
History -> R.string.subject_history
|
|
||||||
Others -> R.string.subject_others
|
|
||||||
Custom -> R.string.subject_customization
|
|
||||||
}
|
|
||||||
return context.getString(resId) // 返回资源中的文本
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
// 根据 ID 获取 Subjects
|
|
||||||
fun fromId(id: Int) = entries.find { it.id == id } ?: Others
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package cn.super12138.todo.ui.components
|
package cn.super12138.todo.ui.components
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.expandIn
|
import androidx.compose.animation.expandIn
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
|
|
@ -16,6 +17,8 @@ import androidx.compose.material3.FilterChipDefaults
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.SideEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
|
@ -27,6 +30,7 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import cn.super12138.todo.R
|
import cn.super12138.todo.R
|
||||||
import cn.super12138.todo.utils.VibrationUtils
|
import cn.super12138.todo.utils.VibrationUtils
|
||||||
|
import kotlin.math.log
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 部分参考:https://github.com/Rhythamtech/FilterChipGroup-Compose-Android/blob/main/FilterChipGroup.kt
|
* 部分参考:https://github.com/Rhythamtech/FilterChipGroup-Compose-Android/blob/main/FilterChipGroup.kt
|
||||||
|
|
@ -39,16 +43,23 @@ fun FilterChipGroup(
|
||||||
defaultSelectedItemIndex: Int = 0,
|
defaultSelectedItemIndex: Int = 0,
|
||||||
onSelectedChanged: (Int) -> Unit = {}
|
onSelectedChanged: (Int) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
|
SideEffect {
|
||||||
|
Log.d("TAG", "来自 FilterChipGroup:重组啦")
|
||||||
|
}
|
||||||
val view = LocalView.current
|
val view = LocalView.current
|
||||||
var selectedItemId by rememberSaveable { mutableIntStateOf(defaultSelectedItemIndex) }
|
var selectedItemIndex by rememberSaveable { mutableIntStateOf(defaultSelectedItemIndex) }
|
||||||
|
|
||||||
|
LaunchedEffect(defaultSelectedItemIndex) {
|
||||||
|
selectedItemIndex = defaultSelectedItemIndex
|
||||||
|
}
|
||||||
|
|
||||||
FlowRow(modifier = modifier) {
|
FlowRow(modifier = modifier) {
|
||||||
items.forEach { item ->
|
items.forEach { item ->
|
||||||
FilterChipItem(
|
FilterChipItem(
|
||||||
selected = item.id == selectedItemId,
|
selected = item.id == selectedItemIndex,
|
||||||
text = item.text,
|
text = item.name,
|
||||||
onClick = {
|
onClick = {
|
||||||
selectedItemId = item.id
|
selectedItemIndex = item.id
|
||||||
VibrationUtils.performHapticFeedback(view)
|
VibrationUtils.performHapticFeedback(view)
|
||||||
onSelectedChanged(item.id)
|
onSelectedChanged(item.id)
|
||||||
}
|
}
|
||||||
|
|
@ -87,5 +98,5 @@ private fun FilterChipItem(
|
||||||
|
|
||||||
data class ChipItem(
|
data class ChipItem(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val text: String
|
val name: String
|
||||||
)
|
)
|
||||||
|
|
@ -2,15 +2,24 @@ package cn.super12138.todo.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.text.input.TextFieldLineLimits
|
||||||
|
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.ErrorOutline
|
import androidx.compose.material.icons.outlined.ErrorOutline
|
||||||
|
import androidx.compose.material.icons.outlined.Info
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.FilledTonalButton
|
import androidx.compose.material3.FilledTonalButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
|
|
@ -19,6 +28,66 @@ import androidx.compose.ui.window.DialogProperties
|
||||||
import cn.super12138.todo.R
|
import cn.super12138.todo.R
|
||||||
import cn.super12138.todo.utils.VibrationUtils
|
import cn.super12138.todo.utils.VibrationUtils
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FiveCharPromptDialog(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
visible: Boolean,
|
||||||
|
icon: ImageVector = Icons.Outlined.Info,
|
||||||
|
title: String = stringResource(R.string.tip_tips),
|
||||||
|
text: String,
|
||||||
|
confirmButtonText: String = stringResource(R.string.action_save),
|
||||||
|
showDismissButton: Boolean = true,
|
||||||
|
dismissButtonText: String = stringResource(R.string.action_cancel),
|
||||||
|
properties: DialogProperties = DialogProperties(),
|
||||||
|
onSave: (String) -> Unit,
|
||||||
|
onDismiss: () -> Unit
|
||||||
|
) {
|
||||||
|
val textFieldState = rememberTextFieldState()
|
||||||
|
var isError by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val supportingText = listOf(
|
||||||
|
stringResource(R.string.tip_max_length_5),
|
||||||
|
stringResource(R.string.error_no_content_entered),
|
||||||
|
stringResource(R.string.error_exceeds_5_chars)
|
||||||
|
)
|
||||||
|
|
||||||
|
var currentSupportingText by remember { mutableStateOf(supportingText[0]) }
|
||||||
|
|
||||||
|
BasicDialog(
|
||||||
|
visible = visible,
|
||||||
|
icon = icon,
|
||||||
|
title = title,
|
||||||
|
text = { // 已经是实现好滚动的Column布局
|
||||||
|
Text(text)
|
||||||
|
OutlinedTextField(
|
||||||
|
state = textFieldState,
|
||||||
|
lineLimits = TextFieldLineLimits.SingleLine,
|
||||||
|
label = { Text(stringResource(R.string.label_enter_sth)) },
|
||||||
|
supportingText = { Text(currentSupportingText) },
|
||||||
|
isError = isError
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmButton = confirmButtonText,
|
||||||
|
dismissButton = if (showDismissButton) dismissButtonText else null,
|
||||||
|
onConfirm = {
|
||||||
|
if (textFieldState.text.trim().isEmpty()) {
|
||||||
|
isError = true
|
||||||
|
currentSupportingText = supportingText[1]
|
||||||
|
return@BasicDialog
|
||||||
|
} else if (textFieldState.text.trim().length > 5) {
|
||||||
|
isError = true
|
||||||
|
currentSupportingText = supportingText[2]
|
||||||
|
} else {
|
||||||
|
onSave(textFieldState.text.toString())
|
||||||
|
onDismiss()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDismiss = onDismiss,
|
||||||
|
properties = properties,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ConfirmDialog(
|
fun ConfirmDialog(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import cn.super12138.todo.ui.pages.settings.SettingsAbout
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsAboutLicence
|
import cn.super12138.todo.ui.pages.settings.SettingsAboutLicence
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsAppearance
|
import cn.super12138.todo.ui.pages.settings.SettingsAppearance
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsData
|
import cn.super12138.todo.ui.pages.settings.SettingsData
|
||||||
|
import cn.super12138.todo.ui.pages.settings.SettingsDataCategory
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsInterface
|
import cn.super12138.todo.ui.pages.settings.SettingsInterface
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsMain
|
import cn.super12138.todo.ui.pages.settings.SettingsMain
|
||||||
import cn.super12138.todo.ui.theme.materialSharedAxisXIn
|
import cn.super12138.todo.ui.theme.materialSharedAxisXIn
|
||||||
|
|
@ -111,10 +112,15 @@ fun TodoNavigation(
|
||||||
composable(TodoScreen.SettingsData.name) {
|
composable(TodoScreen.SettingsData.name) {
|
||||||
SettingsData(
|
SettingsData(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
|
toCategoryManager = {navController.navigate(TodoScreen.SettingsDataCategory.name)},
|
||||||
onNavigateUp = { navController.navigateUp() }
|
onNavigateUp = { navController.navigateUp() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composable(TodoScreen.SettingsDataCategory.name) {
|
||||||
|
SettingsDataCategory(onNavigateUp = {navController.navigateUp()})
|
||||||
|
}
|
||||||
|
|
||||||
composable(TodoScreen.SettingsAbout.name) {
|
composable(TodoScreen.SettingsAbout.name) {
|
||||||
SettingsAbout(
|
SettingsAbout(
|
||||||
//toSpecialPage = { navController.navigate(TodoScreen.SettingsAboutSpecial.name) },
|
//toSpecialPage = { navController.navigate(TodoScreen.SettingsAboutSpecial.name) },
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ enum class TodoScreen {
|
||||||
SettingsAppearance,
|
SettingsAppearance,
|
||||||
SettingsInterface,
|
SettingsInterface,
|
||||||
SettingsData,
|
SettingsData,
|
||||||
|
SettingsDataCategory,
|
||||||
SettingsAbout,
|
SettingsAbout,
|
||||||
//SettingsAboutSpecial,
|
//SettingsAboutSpecial,
|
||||||
SettingsAboutLicence
|
SettingsAboutLicence
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package cn.super12138.todo.ui.pages.editor
|
package cn.super12138.todo.ui.pages.editor
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.AnimatedVisibilityScope
|
import androidx.compose.animation.AnimatedVisibilityScope
|
||||||
|
|
@ -26,28 +27,32 @@ import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import cn.super12138.todo.R
|
import cn.super12138.todo.R
|
||||||
import cn.super12138.todo.constants.Constants
|
import cn.super12138.todo.constants.Constants
|
||||||
import cn.super12138.todo.logic.database.TodoEntity
|
import cn.super12138.todo.logic.database.TodoEntity
|
||||||
import cn.super12138.todo.logic.model.Subjects
|
import cn.super12138.todo.logic.datastore.DataStoreManager
|
||||||
import cn.super12138.todo.ui.TodoDefaults
|
import cn.super12138.todo.ui.TodoDefaults
|
||||||
import cn.super12138.todo.ui.components.AnimatedExtendedFloatingActionButton
|
import cn.super12138.todo.ui.components.AnimatedExtendedFloatingActionButton
|
||||||
import cn.super12138.todo.ui.components.ChipItem
|
import cn.super12138.todo.ui.components.ChipItem
|
||||||
import cn.super12138.todo.ui.components.ConfirmDialog
|
import cn.super12138.todo.ui.components.ConfirmDialog
|
||||||
import cn.super12138.todo.ui.components.FilterChipGroup
|
import cn.super12138.todo.ui.components.FilterChipGroup
|
||||||
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold
|
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold
|
||||||
|
import cn.super12138.todo.ui.pages.editor.components.TodoCategoryTextField
|
||||||
import cn.super12138.todo.ui.pages.editor.components.TodoContentTextField
|
import cn.super12138.todo.ui.pages.editor.components.TodoContentTextField
|
||||||
import cn.super12138.todo.ui.pages.editor.components.TodoPrioritySlider
|
import cn.super12138.todo.ui.pages.editor.components.TodoPrioritySlider
|
||||||
import cn.super12138.todo.ui.pages.editor.components.TodoSubjectTextField
|
|
||||||
import cn.super12138.todo.ui.pages.editor.state.rememberEditorState
|
import cn.super12138.todo.ui.pages.editor.state.rememberEditorState
|
||||||
import cn.super12138.todo.utils.VibrationUtils
|
import cn.super12138.todo.utils.VibrationUtils
|
||||||
|
|
||||||
|
|
@ -63,11 +68,40 @@ fun TodoEditorPage(
|
||||||
animatedVisibilityScope: AnimatedVisibilityScope
|
animatedVisibilityScope: AnimatedVisibilityScope
|
||||||
) {
|
) {
|
||||||
val view = LocalView.current
|
val view = LocalView.current
|
||||||
val context = LocalContext.current
|
|
||||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||||
|
|
||||||
val uiState = rememberEditorState(initialTodo = toDo)
|
val uiState = rememberEditorState(initialTodo = toDo)
|
||||||
val isCustomSubject by remember { derivedStateOf { uiState.selectedSubjectId == Subjects.Custom.id } }
|
|
||||||
|
val originalCategories by DataStoreManager.categoriesFlow.collectAsState(initial = emptyList())
|
||||||
|
val categories = originalCategories
|
||||||
|
.mapIndexed { index, category ->
|
||||||
|
ChipItem(
|
||||||
|
id = index,
|
||||||
|
name = category
|
||||||
|
)
|
||||||
|
} + ChipItem(id = -1, name = "自定义")
|
||||||
|
|
||||||
|
var defaultIndex by remember { mutableIntStateOf(0) }
|
||||||
|
LaunchedEffect(originalCategories, toDo) {
|
||||||
|
if (toDo == null) {
|
||||||
|
val index = if (categories.size == 1) -1 else 0
|
||||||
|
defaultIndex = index
|
||||||
|
uiState.selectedCategoryIndex = index
|
||||||
|
} else {
|
||||||
|
val index = categories.firstOrNull { it.name == toDo.category }?.id ?: -1
|
||||||
|
defaultIndex = index
|
||||||
|
uiState.selectedCategoryIndex = index
|
||||||
|
if (index == -1) {
|
||||||
|
uiState.categoryContent = toDo.category
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val isCustomCategory by remember {
|
||||||
|
derivedStateOf {
|
||||||
|
uiState.selectedCategoryIndex == -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun checkModifiedBeforeBack() {
|
fun checkModifiedBeforeBack() {
|
||||||
if (uiState.isModified()) {
|
if (uiState.isModified()) {
|
||||||
|
|
@ -77,9 +111,7 @@ fun TodoEditorPage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BackHandler {
|
BackHandler { checkModifiedBeforeBack() }
|
||||||
checkModifiedBeforeBack()
|
|
||||||
}
|
|
||||||
|
|
||||||
LargeTopAppBarScaffold(
|
LargeTopAppBarScaffold(
|
||||||
title = stringResource(if (toDo != null) R.string.title_edit_task else R.string.action_add_task),
|
title = stringResource(if (toDo != null) R.string.title_edit_task else R.string.action_add_task),
|
||||||
|
|
@ -106,7 +138,14 @@ fun TodoEditorPage(
|
||||||
return@AnimatedExtendedFloatingActionButton
|
return@AnimatedExtendedFloatingActionButton
|
||||||
} else {
|
} else {
|
||||||
uiState.clearError()
|
uiState.clearError()
|
||||||
onSave(uiState.getEntity())
|
val newTodo = TodoEntity(
|
||||||
|
id = toDo?.id ?: 0,
|
||||||
|
content = uiState.toDoContent,
|
||||||
|
category = if (isCustomCategory) uiState.categoryContent else categories[uiState.selectedCategoryIndex].name,
|
||||||
|
priority = uiState.priorityState,
|
||||||
|
isCompleted = uiState.isCompleted
|
||||||
|
)
|
||||||
|
onSave(newTodo)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -137,50 +176,34 @@ fun TodoEditorPage(
|
||||||
isError = uiState.isErrorContent,
|
isError = uiState.isErrorContent,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.sharedBounds(
|
|
||||||
sharedContentState = rememberSharedContentState("${Constants.KEY_TODO_CONTENT_TRANSITION}_${toDo?.id}"),
|
|
||||||
animatedVisibilityScope = animatedVisibilityScope
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item {
|
item {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.label_subject),
|
text = stringResource(R.string.label_category),
|
||||||
style = MaterialTheme.typography.titleMedium
|
style = MaterialTheme.typography.titleMedium
|
||||||
)
|
)
|
||||||
|
|
||||||
val subjects = remember {
|
|
||||||
Subjects.entries.map {
|
|
||||||
ChipItem(
|
|
||||||
id = it.id,
|
|
||||||
text = it.getDisplayName(context)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FilterChipGroup(
|
FilterChipGroup(
|
||||||
items = subjects,
|
items = categories,
|
||||||
defaultSelectedItemIndex = toDo?.subject ?: Subjects.Chinese.id,
|
defaultSelectedItemIndex = defaultIndex,
|
||||||
onSelectedChanged = { uiState.selectedSubjectId = it },
|
onSelectedChanged = { uiState.selectedCategoryIndex = it },
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = isCustomSubject,
|
visible = isCustomCategory,
|
||||||
enter = fadeIn() + expandVertically(),
|
enter = fadeIn() + expandVertically(),
|
||||||
exit = fadeOut() + shrinkVertically()
|
exit = fadeOut() + shrinkVertically()
|
||||||
) {
|
) {
|
||||||
with(sharedTransitionScope) {
|
with(sharedTransitionScope) {
|
||||||
TodoSubjectTextField(
|
TodoCategoryTextField(
|
||||||
value = uiState.subjectContent,
|
value = uiState.categoryContent,
|
||||||
onValueChange = { uiState.subjectContent = it },
|
onValueChange = { uiState.categoryContent = it },
|
||||||
isError = uiState.isErrorSubject,
|
isError = uiState.isErrorCategory,
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth()
|
||||||
.fillMaxWidth()
|
|
||||||
.sharedBounds(
|
|
||||||
sharedContentState = rememberSharedContentState("${Constants.KEY_TODO_SUBJECT_TRANSITION}_${toDo?.id}"),
|
|
||||||
animatedVisibilityScope = animatedVisibilityScope
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ fun TodoContentTextField(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TodoSubjectTextField(
|
fun TodoCategoryTextField(
|
||||||
value: String,
|
value: String,
|
||||||
onValueChange: (String) -> Unit,
|
onValueChange: (String) -> Unit,
|
||||||
isError: Boolean,
|
isError: Boolean,
|
||||||
|
|
@ -39,7 +39,7 @@ fun TodoSubjectTextField(
|
||||||
TextField(
|
TextField(
|
||||||
value = value,
|
value = value,
|
||||||
onValueChange = onValueChange,
|
onValueChange = onValueChange,
|
||||||
label = { Text(stringResource(R.string.label_enter_subject_name)) },
|
label = { Text(stringResource(R.string.label_enter_category_name)) },
|
||||||
isError = isError,
|
isError = isError,
|
||||||
supportingText = {
|
supportingText = {
|
||||||
AnimatedVisibility(isError) {
|
AnimatedVisibility(isError) {
|
||||||
|
|
|
||||||
|
|
@ -9,18 +9,16 @@ import androidx.compose.runtime.saveable.SaverScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import cn.super12138.todo.logic.database.TodoEntity
|
import cn.super12138.todo.logic.database.TodoEntity
|
||||||
import cn.super12138.todo.logic.model.Subjects
|
|
||||||
|
|
||||||
class EditorState(
|
class EditorState(val initialTodo: TodoEntity? = null) {
|
||||||
val initialTodo: TodoEntity? = null,
|
|
||||||
) {
|
|
||||||
var toDoContent by mutableStateOf(initialTodo?.content ?: "")
|
var toDoContent by mutableStateOf(initialTodo?.content ?: "")
|
||||||
var isErrorContent by mutableStateOf(false)
|
var isErrorContent by mutableStateOf(false)
|
||||||
var selectedSubjectId by mutableIntStateOf(initialTodo?.subject ?: 0)
|
var selectedCategoryIndex by mutableIntStateOf(0)
|
||||||
var subjectContent by mutableStateOf(initialTodo?.customSubject ?: "")
|
var categoryContent by mutableStateOf(initialTodo?.category ?: "")
|
||||||
var isErrorSubject by mutableStateOf(false)
|
var isErrorCategory by mutableStateOf(false)
|
||||||
var priorityState by mutableFloatStateOf(initialTodo?.priority ?: 0f)
|
var priorityState by mutableFloatStateOf(initialTodo?.priority ?: 0f)
|
||||||
var isCompleted by mutableStateOf(initialTodo?.isCompleted == true)
|
var isCompleted by mutableStateOf(initialTodo?.isCompleted == true)
|
||||||
|
|
||||||
var showExitConfirmDialog by mutableStateOf(false)
|
var showExitConfirmDialog by mutableStateOf(false)
|
||||||
var showDeleteConfirmDialog by mutableStateOf(false)
|
var showDeleteConfirmDialog by mutableStateOf(false)
|
||||||
|
|
||||||
|
|
@ -31,9 +29,8 @@ class EditorState(
|
||||||
*/
|
*/
|
||||||
fun setErrorIfNotValid(): Boolean {
|
fun setErrorIfNotValid(): Boolean {
|
||||||
isErrorContent = toDoContent.trim().isEmpty()
|
isErrorContent = toDoContent.trim().isEmpty()
|
||||||
isErrorSubject = subjectContent.trim().isEmpty() &&
|
isErrorCategory = if (selectedCategoryIndex == -1) categoryContent.trim().isEmpty() else false
|
||||||
selectedSubjectId == Subjects.Custom.id
|
return isErrorContent || isErrorCategory
|
||||||
return isErrorContent || isErrorSubject
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -41,31 +38,16 @@ class EditorState(
|
||||||
*/
|
*/
|
||||||
fun clearError() {
|
fun clearError() {
|
||||||
isErrorContent = false
|
isErrorContent = false
|
||||||
isErrorSubject = false
|
isErrorCategory = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取编辑后的待办实体
|
|
||||||
*
|
|
||||||
* @return TodoEntity 待办实体
|
|
||||||
*/
|
|
||||||
fun getEntity(): TodoEntity = TodoEntity(
|
|
||||||
id = initialTodo?.id ?: 0,
|
|
||||||
content = toDoContent,
|
|
||||||
subject = selectedSubjectId,
|
|
||||||
customSubject = subjectContent,
|
|
||||||
priority = priorityState,
|
|
||||||
isCompleted = isCompleted
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查待办是否被编辑修改
|
* 检查待办是否被编辑修改
|
||||||
*/
|
*/
|
||||||
fun isModified(): Boolean {
|
fun isModified(): Boolean {
|
||||||
var isModified = false
|
var isModified = false
|
||||||
if ((initialTodo?.content ?: "") != toDoContent) isModified = true
|
if ((initialTodo?.content ?: "") != toDoContent) isModified = true
|
||||||
if ((initialTodo?.subject ?: 0) != selectedSubjectId) isModified = true
|
if ((initialTodo?.category ?: "") != categoryContent) isModified = true
|
||||||
if ((initialTodo?.customSubject ?: "") != subjectContent) isModified = true
|
|
||||||
if ((initialTodo?.priority ?: 0f) != priorityState) isModified = true
|
if ((initialTodo?.priority ?: 0f) != priorityState) isModified = true
|
||||||
if ((initialTodo?.isCompleted == true) != isCompleted) isModified = true
|
if ((initialTodo?.isCompleted == true) != isCompleted) isModified = true
|
||||||
return isModified
|
return isModified
|
||||||
|
|
@ -80,9 +62,9 @@ class EditorState(
|
||||||
value.initialTodo?.id ?: 0,
|
value.initialTodo?.id ?: 0,
|
||||||
value.toDoContent,
|
value.toDoContent,
|
||||||
value.isErrorContent,
|
value.isErrorContent,
|
||||||
value.selectedSubjectId,
|
value.selectedCategoryIndex,
|
||||||
value.subjectContent,
|
value.categoryContent,
|
||||||
value.isErrorSubject,
|
value.isErrorCategory,
|
||||||
value.priorityState,
|
value.priorityState,
|
||||||
value.isCompleted,
|
value.isCompleted,
|
||||||
value.showExitConfirmDialog,
|
value.showExitConfirmDialog,
|
||||||
|
|
@ -96,9 +78,9 @@ class EditorState(
|
||||||
return EditorState(initialTodo).apply {
|
return EditorState(initialTodo).apply {
|
||||||
toDoContent = list[1] as String
|
toDoContent = list[1] as String
|
||||||
isErrorContent = list[2] as Boolean
|
isErrorContent = list[2] as Boolean
|
||||||
selectedSubjectId = list[3] as Int
|
selectedCategoryIndex = list[3] as Int
|
||||||
subjectContent = list[4] as String
|
categoryContent = list[4] as String
|
||||||
isErrorSubject = list[5] as Boolean
|
isErrorCategory = list[5] as Boolean
|
||||||
priorityState = list[6] as Float
|
priorityState = list[6] as Float
|
||||||
isCompleted = list[7] as Boolean
|
isCompleted = list[7] as Boolean
|
||||||
showExitConfirmDialog = list[8] as Boolean
|
showExitConfirmDialog = list[8] as Boolean
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ fun MainPage(
|
||||||
viewModel.updateTodo(
|
viewModel.updateTodo(
|
||||||
TodoEntity(
|
TodoEntity(
|
||||||
content = content,
|
content = content,
|
||||||
subject = subject,
|
category = category,
|
||||||
isCompleted = true,
|
isCompleted = true,
|
||||||
priority = priority,
|
priority = priority,
|
||||||
id = id
|
id = id
|
||||||
|
|
@ -193,7 +193,7 @@ fun MainPage(
|
||||||
viewModel.updateTodo(
|
viewModel.updateTodo(
|
||||||
TodoEntity(
|
TodoEntity(
|
||||||
content = content,
|
content = content,
|
||||||
subject = subject,
|
category = category,
|
||||||
isCompleted = true,
|
isCompleted = true,
|
||||||
priority = priority,
|
priority = priority,
|
||||||
id = id
|
id = id
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,12 @@ import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import cn.super12138.todo.R
|
import cn.super12138.todo.R
|
||||||
import cn.super12138.todo.logic.database.TodoEntity
|
import cn.super12138.todo.logic.database.TodoEntity
|
||||||
import cn.super12138.todo.logic.model.Priority
|
import cn.super12138.todo.logic.model.Priority
|
||||||
import cn.super12138.todo.logic.model.Subjects
|
|
||||||
import cn.super12138.todo.ui.TodoDefaults
|
import cn.super12138.todo.ui.TodoDefaults
|
||||||
import cn.super12138.todo.ui.components.LazyColumnCustomScrollBar
|
import cn.super12138.todo.ui.components.LazyColumnCustomScrollBar
|
||||||
import cn.super12138.todo.ui.pages.main.components.TodoCard
|
import cn.super12138.todo.ui.pages.main.components.TodoCard
|
||||||
|
|
@ -38,8 +36,6 @@ fun ManagerFragment(
|
||||||
sharedTransitionScope: SharedTransitionScope,
|
sharedTransitionScope: SharedTransitionScope,
|
||||||
animatedVisibilityScope: AnimatedVisibilityScope
|
animatedVisibilityScope: AnimatedVisibilityScope
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
LazyColumnCustomScrollBar(
|
LazyColumnCustomScrollBar(
|
||||||
state = state,
|
state = state,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|
@ -67,16 +63,10 @@ fun ManagerFragment(
|
||||||
items = list,
|
items = list,
|
||||||
key = { it.id }
|
key = { it.id }
|
||||||
) { item ->
|
) { item ->
|
||||||
val subject = if (item.subject == Subjects.Custom.id) {
|
|
||||||
item.customSubject
|
|
||||||
} else {
|
|
||||||
Subjects.fromId(item.subject).getDisplayName(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
TodoCard(
|
TodoCard(
|
||||||
id = item.id,
|
id = item.id,
|
||||||
content = item.content,
|
content = item.content,
|
||||||
subject = subject,
|
category = item.category,
|
||||||
completed = item.isCompleted,
|
completed = item.isCompleted,
|
||||||
priority = Priority.fromFloat(item.priority),
|
priority = Priority.fromFloat(item.priority),
|
||||||
selected = selectedTodoIds.contains(item.id),
|
selected = selectedTodoIds.contains(item.id),
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ fun TodoCard(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
id: Int,
|
id: Int,
|
||||||
content: String,
|
content: String,
|
||||||
subject: String,
|
category: String,
|
||||||
completed: Boolean,
|
completed: Boolean,
|
||||||
priority: Priority,
|
priority: Priority,
|
||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
|
|
@ -141,12 +141,12 @@ fun TodoCard(
|
||||||
|
|
||||||
with(sharedTransitionScope) {
|
with(sharedTransitionScope) {
|
||||||
Text(
|
Text(
|
||||||
text = subject,
|
text = category,
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None,
|
textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
modifier = Modifier.sharedBounds(
|
modifier = Modifier.sharedBounds(
|
||||||
sharedContentState = rememberSharedContentState("${Constants.KEY_TODO_SUBJECT_TRANSITION}_$id"),
|
sharedContentState = rememberSharedContentState("${Constants.KEY_TODO_CATEGORY_TRANSITION}_$id"),
|
||||||
animatedVisibilityScope = animatedVisibilityScope
|
animatedVisibilityScope = animatedVisibilityScope
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Category
|
||||||
import androidx.compose.material.icons.outlined.FileDownload
|
import androidx.compose.material.icons.outlined.FileDownload
|
||||||
import androidx.compose.material.icons.outlined.FileUpload
|
import androidx.compose.material.icons.outlined.FileUpload
|
||||||
import androidx.compose.material.icons.outlined.RestartAlt
|
import androidx.compose.material.icons.outlined.RestartAlt
|
||||||
|
|
@ -31,6 +32,7 @@ import cn.super12138.todo.R
|
||||||
import cn.super12138.todo.ui.activities.MainActivity
|
import cn.super12138.todo.ui.activities.MainActivity
|
||||||
import cn.super12138.todo.ui.components.ConfirmDialog
|
import cn.super12138.todo.ui.components.ConfirmDialog
|
||||||
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold
|
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold
|
||||||
|
import cn.super12138.todo.ui.pages.settings.components.SettingsCategory
|
||||||
import cn.super12138.todo.ui.pages.settings.components.SettingsItem
|
import cn.super12138.todo.ui.pages.settings.components.SettingsItem
|
||||||
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
||||||
import cn.super12138.todo.utils.SystemUtils
|
import cn.super12138.todo.utils.SystemUtils
|
||||||
|
|
@ -41,6 +43,7 @@ import kotlin.system.exitProcess
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsData(
|
fun SettingsData(
|
||||||
viewModel: MainViewModel,
|
viewModel: MainViewModel,
|
||||||
|
toCategoryManager: () -> Unit,
|
||||||
onNavigateUp: () -> Unit,
|
onNavigateUp: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
|
@ -114,6 +117,7 @@ fun SettingsData(
|
||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
|
SettingsCategory(stringResource(R.string.pref_category_data_management))
|
||||||
SettingsItem(
|
SettingsItem(
|
||||||
leadingIcon = Icons.Outlined.FileDownload,
|
leadingIcon = Icons.Outlined.FileDownload,
|
||||||
title = stringResource(R.string.pref_backup),
|
title = stringResource(R.string.pref_backup),
|
||||||
|
|
@ -133,6 +137,15 @@ fun SettingsData(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
item {
|
||||||
|
SettingsCategory(stringResource(R.string.pref_category_category_management))
|
||||||
|
SettingsItem(
|
||||||
|
leadingIcon = Icons.Outlined.Category,
|
||||||
|
title = stringResource(R.string.pref_category_category_management),
|
||||||
|
description = stringResource(R.string.pref_category_management_desc),
|
||||||
|
onClick = toCategoryManager
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
package cn.super12138.todo.ui.pages.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Add
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
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
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import cn.super12138.todo.R
|
||||||
|
import cn.super12138.todo.logic.datastore.DataStoreManager
|
||||||
|
import cn.super12138.todo.ui.components.AnimatedExtendedFloatingActionButton
|
||||||
|
import cn.super12138.todo.ui.components.FiveCharPromptDialog
|
||||||
|
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold
|
||||||
|
import cn.super12138.todo.ui.pages.settings.components.SettingsCategory
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun SettingsDataCategory(
|
||||||
|
onNavigateUp: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||||
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
var showDialog by rememberSaveable { mutableStateOf(false) }
|
||||||
|
val categories by DataStoreManager.categoriesFlow.collectAsState(initial = emptyList())
|
||||||
|
|
||||||
|
val isExpanded by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } }
|
||||||
|
|
||||||
|
LargeTopAppBarScaffold(
|
||||||
|
title = stringResource(R.string.pref_category_category_management),
|
||||||
|
onBack = onNavigateUp,
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||||
|
floatingActionButton = {
|
||||||
|
AnimatedExtendedFloatingActionButton(
|
||||||
|
icon = Icons.Outlined.Add,
|
||||||
|
text = stringResource(R.string.action_add_category),
|
||||||
|
expanded = isExpanded,
|
||||||
|
onClick = { showDialog = true }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
) { innerPadding ->
|
||||||
|
LazyColumn(
|
||||||
|
state = listState,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(innerPadding)
|
||||||
|
) {
|
||||||
|
if (categories.isEmpty()) {
|
||||||
|
item {
|
||||||
|
Text("空")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items(items = categories, key = { it }) {
|
||||||
|
SettingsCategory(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FiveCharPromptDialog(
|
||||||
|
visible = showDialog,
|
||||||
|
text = stringResource(R.string.tip_enter_category),
|
||||||
|
onSave = {
|
||||||
|
if (!categories.contains(it)) {
|
||||||
|
scope.launch {
|
||||||
|
DataStoreManager.setCategories(categories + it)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scope.launch {
|
||||||
|
snackbarHostState.showSnackbar(
|
||||||
|
message = context.getString(R.string.error_category_duplicate)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDismiss = { showDialog = false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -41,7 +41,7 @@ class MainViewModel : ViewModel() {
|
||||||
toDos.map { list ->
|
toDos.map { list ->
|
||||||
when (SortingMethod.fromId(sortingMethod)) {
|
when (SortingMethod.fromId(sortingMethod)) {
|
||||||
SortingMethod.Sequential -> list.sortedBy { it.id }
|
SortingMethod.Sequential -> list.sortedBy { it.id }
|
||||||
SortingMethod.Subject -> list.sortedBy { it.subject }
|
SortingMethod.Category -> list.sortedBy { it.category }
|
||||||
SortingMethod.Priority -> list.sortedByDescending { it.priority } // 优先级高的在前
|
SortingMethod.Priority -> list.sortedByDescending { it.priority } // 优先级高的在前
|
||||||
SortingMethod.Completion -> list.sortedBy { it.isCompleted } // 未完成的在前
|
SortingMethod.Completion -> list.sortedBy { it.isCompleted } // 未完成的在前
|
||||||
SortingMethod.AlphabeticalAscending -> list.sortedBy { it.content }
|
SortingMethod.AlphabeticalAscending -> list.sortedBy { it.content }
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,6 @@
|
||||||
<string name="error_no_content_entered">没有输入内容</string>
|
<string name="error_no_content_entered">没有输入内容</string>
|
||||||
<string name="tip_select_this">选择该项</string>
|
<string name="tip_select_this">选择该项</string>
|
||||||
<string name="tip_mark_completed">标记为已完成</string>
|
<string name="tip_mark_completed">标记为已完成</string>
|
||||||
<string name="subject_chinese">语文</string>
|
|
||||||
<string name="subject_math">数学</string>
|
|
||||||
<string name="subject_english">英语</string>
|
|
||||||
<string name="subject_biology">生物</string>
|
|
||||||
<string name="subject_geography">地理</string>
|
|
||||||
<string name="subject_physics">物理</string>
|
|
||||||
<string name="subject_moral">道法</string>
|
|
||||||
<string name="subject_chemistry">化学</string>
|
|
||||||
<string name="subject_history">历史</string>
|
|
||||||
<string name="subject_others">其它</string>
|
|
||||||
<string name="title_edit_task">修改待办</string>
|
<string name="title_edit_task">修改待办</string>
|
||||||
<string name="tip_remain_tasks">剩余 %s 项任务</string>
|
<string name="tip_remain_tasks">剩余 %s 项任务</string>
|
||||||
<string name="page_crash">应用程序出现错误</string>
|
<string name="page_crash">应用程序出现错误</string>
|
||||||
|
|
@ -39,7 +29,7 @@
|
||||||
<string name="pref_developer">开发者</string>
|
<string name="pref_developer">开发者</string>
|
||||||
<string name="pref_licence">开放源代码许可</string>
|
<string name="pref_licence">开放源代码许可</string>
|
||||||
<string name="pref_licence_desc">查看应用使用的开源库及其许可</string>
|
<string name="pref_licence_desc">查看应用使用的开源库及其许可</string>
|
||||||
<string name="label_subject">学科</string>
|
<string name="label_category">类别</string>
|
||||||
<string name="priority_not_urgent">不紧急</string>
|
<string name="priority_not_urgent">不紧急</string>
|
||||||
<string name="priority_not_important">不重要</string>
|
<string name="priority_not_important">不重要</string>
|
||||||
<string name="priority_default">默认</string>
|
<string name="priority_default">默认</string>
|
||||||
|
|
@ -82,7 +72,7 @@
|
||||||
<string name="pref_view_on_github_desc">查看源代码、提交错误报告和改进建议</string>
|
<string name="pref_view_on_github_desc">查看源代码、提交错误报告和改进建议</string>
|
||||||
<string name="tip_discard_changes">退出编辑后将无法找回你修改过的数据。确定退出编辑吗?</string>
|
<string name="tip_discard_changes">退出编辑后将无法找回你修改过的数据。确定退出编辑吗?</string>
|
||||||
<string name="sorting_sequential">添加先后顺序</string>
|
<string name="sorting_sequential">添加先后顺序</string>
|
||||||
<string name="sorting_subject">学科</string>
|
<string name="sorting_category">类别</string>
|
||||||
<string name="sorting_priority">优先级</string>
|
<string name="sorting_priority">优先级</string>
|
||||||
<string name="sorting_completion">完成状态</string>
|
<string name="sorting_completion">完成状态</string>
|
||||||
<string name="sorting_alphabetical_ascending">首字母(升序)</string>
|
<string name="sorting_alphabetical_ascending">首字母(升序)</string>
|
||||||
|
|
@ -106,9 +96,16 @@
|
||||||
<string name="pref_secure_mode">安全模式</string>
|
<string name="pref_secure_mode">安全模式</string>
|
||||||
<string name="pref_secure_mode_desc">阻止截屏并保护后台预览图</string>
|
<string name="pref_secure_mode_desc">阻止截屏并保护后台预览图</string>
|
||||||
<string name="label_more">更多</string>
|
<string name="label_more">更多</string>
|
||||||
<string name="happy_birthday">待办1岁生日快乐</string>
|
<string name="label_enter_category_name">输入类别名称</string>
|
||||||
<string name="subject_customization">自定义</string>
|
|
||||||
<string name="label_enter_subject_name">输入学科名称</string>
|
|
||||||
<string name="accessibility_progress_tasks">当前共有 %1$d 项任务,其中 %2$d 项已完成,%3$d 项未完成</string>
|
<string name="accessibility_progress_tasks">当前共有 %1$d 项任务,其中 %2$d 项已完成,%3$d 项未完成</string>
|
||||||
<string name="accessibility_progress_no_tasks">当前没有任务</string>
|
<string name="accessibility_progress_no_tasks">当前没有任务</string>
|
||||||
|
<string name="pref_category_category_management">类别管理</string>
|
||||||
|
<string name="pref_category_data_management">数据管理</string>
|
||||||
|
<string name="pref_category_management_desc">管理任务的类别标签</string>
|
||||||
|
<string name="action_add_category">添加类别</string>
|
||||||
|
<string name="label_enter_sth">输入内容</string>
|
||||||
|
<string name="tip_max_length_5">不超过 5 个字</string>
|
||||||
|
<string name="error_exceeds_5_chars">超过 5 个字</string>
|
||||||
|
<string name="tip_enter_category">输入你想添加的类别</string>
|
||||||
|
<string name="error_category_duplicate">该类别已经存在</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -9,16 +9,6 @@
|
||||||
<string name="error_no_content_entered">No content entered</string>
|
<string name="error_no_content_entered">No content entered</string>
|
||||||
<string name="tip_select_this">Select this</string>
|
<string name="tip_select_this">Select this</string>
|
||||||
<string name="tip_mark_completed">Mark as completed</string>
|
<string name="tip_mark_completed">Mark as completed</string>
|
||||||
<string name="subject_chinese">Chinese</string>
|
|
||||||
<string name="subject_math">Math</string>
|
|
||||||
<string name="subject_english">English</string>
|
|
||||||
<string name="subject_biology">Biology</string>
|
|
||||||
<string name="subject_geography">Geography</string>
|
|
||||||
<string name="subject_physics">Physics</string>
|
|
||||||
<string name="subject_moral">Morality and Rule of Law</string>
|
|
||||||
<string name="subject_chemistry">Chemistry</string>
|
|
||||||
<string name="subject_history">History</string>
|
|
||||||
<string name="subject_others">Others</string>
|
|
||||||
<string name="title_edit_task">Edit Task</string>
|
<string name="title_edit_task">Edit Task</string>
|
||||||
<string name="tip_remain_tasks">%s tasks remaining</string>
|
<string name="tip_remain_tasks">%s tasks remaining</string>
|
||||||
<string name="page_crash">Oops! App went wrong</string>
|
<string name="page_crash">Oops! App went wrong</string>
|
||||||
|
|
@ -40,7 +30,7 @@
|
||||||
<string name="pref_developer">Developer</string>
|
<string name="pref_developer">Developer</string>
|
||||||
<string name="pref_licence">Open Source Licences</string>
|
<string name="pref_licence">Open Source Licences</string>
|
||||||
<string name="pref_licence_desc">Check the open source libraries used by the application and their licences.</string>
|
<string name="pref_licence_desc">Check the open source libraries used by the application and their licences.</string>
|
||||||
<string name="label_subject">Subject</string>
|
<string name="label_category">Category</string>
|
||||||
<string name="priority_not_urgent">Not Urgent</string>
|
<string name="priority_not_urgent">Not Urgent</string>
|
||||||
<string name="priority_not_important">Not Important</string>
|
<string name="priority_not_important">Not Important</string>
|
||||||
<string name="priority_default">Default</string>
|
<string name="priority_default">Default</string>
|
||||||
|
|
@ -83,7 +73,7 @@
|
||||||
<string name="pref_view_on_github_desc">View source code, submit bug reports, and improvement suggestions</string>
|
<string name="pref_view_on_github_desc">View source code, submit bug reports, and improvement suggestions</string>
|
||||||
<string name="tip_discard_changes">After exiting edit mode, you will not be able to retrieve the data you have modified. Are you sure you want to exit editing?</string>
|
<string name="tip_discard_changes">After exiting edit mode, you will not be able to retrieve the data you have modified. Are you sure you want to exit editing?</string>
|
||||||
<string name="sorting_sequential">Sequential</string>
|
<string name="sorting_sequential">Sequential</string>
|
||||||
<string name="sorting_subject">Subject</string>
|
<string name="sorting_category">category</string>
|
||||||
<string name="sorting_priority">Priority</string>
|
<string name="sorting_priority">Priority</string>
|
||||||
<string name="sorting_completion">Completion</string>
|
<string name="sorting_completion">Completion</string>
|
||||||
<string name="sorting_alphabetical_ascending">Alphabetical (Ascending)</string>
|
<string name="sorting_alphabetical_ascending">Alphabetical (Ascending)</string>
|
||||||
|
|
@ -107,9 +97,16 @@
|
||||||
<string name="pref_secure_mode">Secure Mode</string>
|
<string name="pref_secure_mode">Secure Mode</string>
|
||||||
<string name="pref_secure_mode_desc">Prevent screenshots and protect the background preview image</string>
|
<string name="pref_secure_mode_desc">Prevent screenshots and protect the background preview image</string>
|
||||||
<string name="label_more">More</string>
|
<string name="label_more">More</string>
|
||||||
<string name="happy_birthday">Happy 1st birthday to ToDo</string>
|
<string name="label_enter_category_name">Enter category name</string>
|
||||||
<string name="subject_customization">Customization</string>
|
|
||||||
<string name="label_enter_subject_name">Enter subject name</string>
|
|
||||||
<string name="accessibility_progress_tasks">Currently, there are %1$d tasks in total, with %2$d completed and %3$d unfinished.</string>
|
<string name="accessibility_progress_tasks">Currently, there are %1$d tasks in total, with %2$d completed and %3$d unfinished.</string>
|
||||||
<string name="accessibility_progress_no_tasks">There are no tasks at the moment.</string>
|
<string name="accessibility_progress_no_tasks">There are no tasks at the moment.</string>
|
||||||
|
<string name="pref_category_category_management">Category Management</string>
|
||||||
|
<string name="pref_category_data_management">Data Management</string>
|
||||||
|
<string name="pref_category_management_desc">Manage the category of tasks</string>
|
||||||
|
<string name="action_add_category">Add Category</string>
|
||||||
|
<string name="label_enter_sth">Enter something</string>
|
||||||
|
<string name="tip_max_length_5">Up to 5 characters</string>
|
||||||
|
<string name="error_exceeds_5_chars">Exceeds 5 characters</string>
|
||||||
|
<string name="tip_enter_category">Enter the category you want to add</string>
|
||||||
|
<string name="error_category_duplicate">The category is duplicate</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -3,6 +3,7 @@ plugins {
|
||||||
alias(libs.plugins.android.application) apply false
|
alias(libs.plugins.android.application) apply false
|
||||||
alias(libs.plugins.kotlin.android) apply false
|
alias(libs.plugins.kotlin.android) apply false
|
||||||
alias(libs.plugins.kotlin.compose) apply false
|
alias(libs.plugins.kotlin.compose) apply false
|
||||||
|
alias(libs.plugins.kotlin.serialization) apply false
|
||||||
alias(libs.plugins.ksp) apply false
|
alias(libs.plugins.ksp) apply false
|
||||||
alias(libs.plugins.aboutlibraries) apply false
|
alias(libs.plugins.aboutlibraries) apply false
|
||||||
}
|
}
|
||||||
|
|
@ -21,9 +21,10 @@ m3color = "2025.3"
|
||||||
# Konfetti
|
# Konfetti
|
||||||
konfetti = "2.0.5"
|
konfetti = "2.0.5"
|
||||||
# Lazy Column Scrollbar
|
# Lazy Column Scrollbar
|
||||||
lazycolumnscrollbar = "2.2.0"
|
lazyColumnScrollbar = "2.2.0"
|
||||||
# Kotlin
|
# Kotlin
|
||||||
kotlinCoroutines = "1.10.2"
|
kotlinCoroutines = "1.10.2"
|
||||||
|
kotlinSerialization = "1.9.0"
|
||||||
# Test
|
# Test
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitVersion = "1.2.1"
|
junitVersion = "1.2.1"
|
||||||
|
|
@ -75,11 +76,12 @@ com-kyant0-m3color = { group = "com.github.Kyant0", name = "m3color", version.re
|
||||||
nl-dionsegijn-konfetti-compose = { group = "nl.dionsegijn", name = "konfetti-compose", version.ref = "konfetti" }
|
nl-dionsegijn-konfetti-compose = { group = "nl.dionsegijn", name = "konfetti-compose", version.ref = "konfetti" }
|
||||||
|
|
||||||
# Lazy Column Scrollbar
|
# Lazy Column Scrollbar
|
||||||
lazycolumnscrollbar = { group = "com.github.nanihadesuka", name = "LazyColumnScrollbar", version.ref = "lazycolumnscrollbar" }
|
lazycolumnscrollbar = { group = "com.github.nanihadesuka", name = "LazyColumnScrollbar", version.ref = "lazyColumnScrollbar" }
|
||||||
|
|
||||||
# Kotlin
|
# Kotlin
|
||||||
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinCoroutines" }
|
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinCoroutines" }
|
||||||
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinCoroutines" }
|
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinCoroutines" }
|
||||||
|
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinSerialization" }
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
|
|
@ -90,5 +92,6 @@ androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-co
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
|
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||||
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
||||||
aboutlibraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "aboutLibsReleasePlugin" }
|
aboutlibraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "aboutLibsReleasePlugin" }
|
||||||
Loading…
Reference in a new issue