diff --git a/app/src/main/kotlin/cn/super12138/todo/constants/Constants.kt b/app/src/main/kotlin/cn/super12138/todo/constants/Constants.kt
index 95ef5c0..79d453e 100644
--- a/app/src/main/kotlin/cn/super12138/todo/constants/Constants.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/constants/Constants.kt
@@ -26,4 +26,7 @@ object Constants {
const val PREF_SORTING_METHOD = "sorting_method"
const val PREF_SORTING_METHOD_DEFAULT = 1
+
+ const val PREF_HAPTIC_FEEDBACK = "haptic_feedback"
+ const val PREF_HAPTIC_FEEDBACK_DEFAULT = true
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/cn/super12138/todo/constants/GlobalValues.kt b/app/src/main/kotlin/cn/super12138/todo/constants/GlobalValues.kt
index 823b10c..aa4630c 100644
--- a/app/src/main/kotlin/cn/super12138/todo/constants/GlobalValues.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/constants/GlobalValues.kt
@@ -27,4 +27,8 @@ object GlobalValues {
key = Constants.PREF_SORTING_METHOD,
default = Constants.PREF_SORTING_METHOD_DEFAULT
)
+ var hapticFeedback: Boolean by SPDelegates(
+ key = Constants.PREF_HAPTIC_FEEDBACK,
+ default = Constants.PREF_HAPTIC_FEEDBACK_DEFAULT
+ )
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/components/ChipGroup.kt b/app/src/main/kotlin/cn/super12138/todo/ui/components/ChipGroup.kt
index b6f25ad..4776d3e 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/components/ChipGroup.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/components/ChipGroup.kt
@@ -17,9 +17,11 @@ import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import cn.super12138.todo.R
+import cn.super12138.todo.utils.VibrationUtils
/**
* 部分参考:https://github.com/Rhythamtech/FilterChipGroup-Compose-Android/blob/main/FilterChipGroup.kt
@@ -32,6 +34,7 @@ fun FilterChipGroup(
onSelectedChanged: (Int) -> Unit = {},
modifier: Modifier = Modifier
) {
+ val view = LocalView.current
var selectedItemIndex by rememberSaveable { mutableIntStateOf(defaultSelectedItemIndex) }
FlowRow(modifier = modifier) {
@@ -40,6 +43,7 @@ fun FilterChipGroup(
selected = items[selectedItemIndex] == items[index],
onClick = {
selectedItemIndex = index
+ VibrationUtils.performHapticFeedback(view)
onSelectedChanged(index)
},
leadingIcon = {
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/components/Dialogs.kt b/app/src/main/kotlin/cn/super12138/todo/ui/components/Dialogs.kt
index 313faca..3e278f4 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/components/Dialogs.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/components/Dialogs.kt
@@ -10,8 +10,10 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import cn.super12138.todo.R
+import cn.super12138.todo.utils.VibrationUtils
@Composable
fun WarningDialog(
@@ -50,6 +52,7 @@ fun BasicDialog(
onDismiss: () -> Unit,
modifier: Modifier = Modifier
) {
+ val view = LocalView.current
BasicDialog(
visible = visible,
icon = {
@@ -61,12 +64,22 @@ fun BasicDialog(
title = { Text(title) },
text = text,
confirmButton = {
- FilledTonalButton(onClick = onConfirm) {
+ FilledTonalButton(
+ onClick = {
+ VibrationUtils.performHapticFeedback(view)
+ onConfirm()
+ }
+ ) {
Text(confirmButton)
}
},
dismissButton = {
- TextButton(onClick = onDismiss) {
+ TextButton(
+ onClick = {
+ VibrationUtils.performHapticFeedback(view)
+ onDismiss()
+ }
+ ) {
Text(dismissButton)
}
},
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/components/FloatingActionButton.kt b/app/src/main/kotlin/cn/super12138/todo/ui/components/FloatingActionButton.kt
index 1102f2a..7460a3e 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/components/FloatingActionButton.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/components/FloatingActionButton.kt
@@ -13,7 +13,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.dp
+import cn.super12138.todo.utils.VibrationUtils
/**
* 带动画且比 Material 3 内置组件动画好看的的可扩展 FAB
@@ -37,8 +39,12 @@ fun AnimatedExtendedFloatingActionButton(
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
+ val view = LocalView.current
FloatingActionButton(
- onClick = onClick,
+ onClick = {
+ VibrationUtils.performHapticFeedback(view)
+ onClick()
+ },
elevation = elevation,
containerColor = containerColor,
contentColor = contentColor,
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 7f9ef93..12ece54 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
@@ -21,9 +21,11 @@ import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import cn.super12138.todo.R
+import cn.super12138.todo.utils.VibrationUtils
/**
* 带有顶部大标题栏的通用脚手架
@@ -47,6 +49,7 @@ fun LargeTopAppBarScaffold(
modifier: Modifier = Modifier,
content: @Composable (PaddingValues) -> Unit
) {
+ val view = LocalView.current
LargeTopAppBarScaffold(
title = {
Text(
@@ -56,7 +59,12 @@ fun LargeTopAppBarScaffold(
)
},
navigationIcon = {
- IconButton(onClick = onBack) {
+ IconButton(
+ onClick = {
+ VibrationUtils.performHapticFeedback(view)
+ onBack()
+ }
+ ) {
Icon(
imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
contentDescription = stringResource(R.string.action_back)
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/crash/CrashPage.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/crash/CrashPage.kt
index 212d63f..920ba0b 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/crash/CrashPage.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/crash/CrashPage.kt
@@ -25,6 +25,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextOverflow
@@ -32,6 +33,7 @@ import androidx.compose.ui.unit.dp
import cn.super12138.todo.R
import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.ui.components.AnimatedExtendedFloatingActionButton
+import cn.super12138.todo.utils.VibrationUtils
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -40,6 +42,7 @@ fun CrashPage(
exitApp: () -> Unit,
modifier: Modifier = Modifier
) {
+ val view = LocalView.current
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
val scrollState = rememberScrollState()
val isExpanded by remember {
@@ -66,7 +69,10 @@ fun CrashPage(
},
floatingActionButton = {
AnimatedExtendedFloatingActionButton(
- onClick = exitApp,
+ onClick = {
+ VibrationUtils.performHapticFeedback(view)
+ exitApp()
+ },
icon = {
Icon(
imageVector = Icons.AutoMirrored.Outlined.ExitToApp,
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 c323d8c..aa51cad 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
@@ -1,5 +1,6 @@
package cn.super12138.todo.ui.pages.editor
+import android.view.HapticFeedbackConstants
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.AnimatedVisibilityScope
@@ -42,6 +43,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
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.contentDescription
import androidx.compose.ui.semantics.semantics
@@ -56,6 +58,7 @@ import cn.super12138.todo.ui.components.AnimatedExtendedFloatingActionButton
import cn.super12138.todo.ui.components.FilterChipGroup
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold
import cn.super12138.todo.ui.components.WarningDialog
+import cn.super12138.todo.utils.VibrationUtils
@OptIn(ExperimentalMaterial3Api::class, ExperimentalSharedTransitionApi::class)
@Composable
@@ -71,6 +74,7 @@ fun TodoEditorPage(
var showExitConfirmDialog by rememberSaveable { mutableStateOf(false) }
var showDeleteConfirmDialog by rememberSaveable { mutableStateOf(false) }
+ val view = LocalView.current
val context = LocalContext.current
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
@@ -220,7 +224,10 @@ fun TodoEditorPage(
contentDescription = context.getString(R.string.label_priority)
},
value = priorityState,
- onValueChange = { priorityState = it },
+ onValueChange = {
+ VibrationUtils.performHapticFeedback(view, HapticFeedbackConstants.LONG_PRESS)
+ priorityState = it
+ },
valueRange = -10f..10f,
steps = 3,
interactionSource = interactionSource,
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
index 43e2bd8..447943b 100644
--- 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
@@ -3,10 +3,7 @@ package cn.super12138.todo.ui.pages.main
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionScope
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
@@ -15,7 +12,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@@ -57,12 +53,12 @@ fun ManagerFragment(
) {
if (list.isEmpty()) {
item {
- Text(
- text = stringResource(R.string.tip_no_task),
- style = MaterialTheme.typography.titleMedium,
- textAlign = TextAlign.Center,
- modifier = Modifier.fillMaxWidth()
- )
+ Text(
+ text = stringResource(R.string.tip_no_task),
+ style = MaterialTheme.typography.titleMedium,
+ textAlign = TextAlign.Center,
+ modifier = Modifier.fillMaxWidth()
+ )
}
} else {
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 cc52891..79e5ed6 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
@@ -1,5 +1,6 @@
package cn.super12138.todo.ui.pages.main.components
+import android.view.HapticFeedbackConstants
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.ExperimentalSharedTransitionApi
@@ -32,6 +33,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
@@ -39,6 +41,7 @@ import androidx.compose.ui.unit.dp
import cn.super12138.todo.R
import cn.super12138.todo.constants.Constants
import cn.super12138.todo.logic.model.Priority
+import cn.super12138.todo.utils.VibrationUtils
@OptIn(ExperimentalFoundationApi::class, ExperimentalSharedTransitionApi::class)
@Composable
@@ -56,6 +59,7 @@ fun TodoCard(
animatedVisibilityScope: AnimatedVisibilityScope,
modifier: Modifier = Modifier
) {
+ val view = LocalView.current
val context = LocalContext.current
ElevatedCard(
modifier = modifier
@@ -67,8 +71,17 @@ fun TodoCard(
modifier = Modifier
.fillMaxSize()
.combinedClickable(
- onClick = onCardClick,
- onLongClick = onCardLongClick
+ onClick = {
+ VibrationUtils.performHapticFeedback(view)
+ onCardClick()
+ },
+ onLongClick = {
+ VibrationUtils.performHapticFeedback(
+ view,
+ HapticFeedbackConstants.LONG_PRESS
+ )
+ onCardLongClick()
+ }
)
.padding(horizontal = 15.dp)
) {
@@ -137,7 +150,12 @@ fun TodoCard(
}
AnimatedVisibility(!selected && !completed) {
- IconButton(onClick = onChecked) {
+ IconButton(
+ onClick = {
+ VibrationUtils.performHapticFeedback(view)
+ onChecked()
+ }
+ ) {
Icon(
imageVector = Icons.Outlined.Check,
tint = MaterialTheme.colorScheme.primary,
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 ea4ffab..892e50b 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
@@ -27,9 +27,11 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import cn.super12138.todo.R
+import cn.super12138.todo.utils.VibrationUtils
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -42,6 +44,7 @@ fun TodoTopAppBar(
toSettingsPage: () -> Unit,
modifier: Modifier = Modifier
) {
+ val view = LocalView.current
val animatedTopAppBarColors by animateColorAsState(
targetValue = if (selectedMode) MaterialTheme.colorScheme.surfaceContainerHighest else MaterialTheme.colorScheme.surface
)
@@ -49,7 +52,12 @@ fun TodoTopAppBar(
TopAppBar(
navigationIcon = {
AnimatedVisibility(selectedMode) {
- IconButton(onClick = onCancelSelect) {
+ IconButton(
+ onClick = {
+ VibrationUtils.performHapticFeedback(view)
+ onCancelSelect()
+ }
+ ) {
Icon(
imageVector = Icons.Outlined.Close,
contentDescription = stringResource(R.string.tip_clear_selected_items)
@@ -73,14 +81,15 @@ fun TodoTopAppBar(
AnimatedContent(
targetState = selectedMode,
modifier = Modifier.windowInsetsPadding(
- WindowInsets.safeContent.exclude(
- WindowInsets.ime
- )
+ WindowInsets.safeContent.exclude(WindowInsets.ime)
)
) { inSelectedMode ->
if (!inSelectedMode) {
IconButton(
- onClick = toSettingsPage
+ onClick = {
+ VibrationUtils.performHapticFeedback(view)
+ toSettingsPage()
+ }
) {
Icon(
imageVector = Icons.Outlined.Settings,
@@ -89,13 +98,23 @@ fun TodoTopAppBar(
}
} else {
Row {
- IconButton(onClick = onSelectAll) {
+ IconButton(
+ onClick = {
+ VibrationUtils.performHapticFeedback(view)
+ onSelectAll()
+ }
+ ) {
Icon(
imageVector = Icons.Outlined.SelectAll,
contentDescription = stringResource(R.string.tip_select_all)
)
}
- IconButton(onClick = onDeleteSelectedTodo) {
+ IconButton(
+ onClick = {
+ VibrationUtils.performHapticFeedback(view)
+ onDeleteSelectedTodo()
+ }
+ ) {
Icon(
imageVector = Icons.Outlined.Delete,
contentDescription = stringResource(R.string.action_delete)
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 ce8f713..7cc6ed9 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
@@ -23,7 +23,7 @@ import cn.super12138.todo.constants.Constants
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold
import cn.super12138.todo.ui.icons.GithubIcon
import cn.super12138.todo.ui.pages.settings.components.SettingsItem
-import cn.super12138.todo.utils.getAppVersion
+import cn.super12138.todo.utils.SystemUtils
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -49,7 +49,7 @@ fun SettingsAbout(
SettingsItem(
leadingIcon = Icons.Outlined.Numbers,
title = stringResource(R.string.pref_app_version),
- description = getAppVersion(context)
+ description = SystemUtils.getAppVersion(context)
)
SettingsItem(
leadingIcon = Icons.Outlined.Person4,
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterface.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterfaceInteraction.kt
similarity index 86%
rename from app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterface.kt
rename to app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterfaceInteraction.kt
index c3689ff..da47e9a 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterface.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterfaceInteraction.kt
@@ -8,6 +8,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Sort
import androidx.compose.material.icons.outlined.Checklist
+import androidx.compose.material.icons.outlined.Vibration
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
@@ -42,7 +43,7 @@ fun SettingsInterface(
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
var showSortingMethodDialog by rememberSaveable { mutableStateOf(false) }
LargeTopAppBarScaffold(
- title = stringResource(R.string.pref_interface),
+ title = stringResource(R.string.pref_interface_interaction),
onBack = onNavigateUp,
scrollBehavior = scrollBehavior,
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
@@ -70,6 +71,16 @@ fun SettingsInterface(
description = viewModel.appSortingMethod.getDisplayName(context),
onClick = { showSortingMethodDialog = true }
)
+
+ SettingsCategory(stringResource(R.string.pref_category_global_interaction))
+ SwitchSettingsItem(
+ key = Constants.PREF_HAPTIC_FEEDBACK,
+ default = Constants.PREF_HAPTIC_FEEDBACK_DEFAULT,
+ leadingIcon = Icons.Outlined.Vibration,
+ title = stringResource(R.string.pref_haptic_feedback),
+ description = stringResource(R.string.pref_haptic_feedback_desc),
+ onCheckedChange = {}
+ )
}
}
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 799fd94..25b349e 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
@@ -47,8 +47,8 @@ fun SettingsMain(
)
SettingsItem(
leadingIcon = Icons.Outlined.ViewComfy,
- title = stringResource(R.string.pref_interface),
- description = stringResource(R.string.pref_interface_desc),
+ title = stringResource(R.string.pref_interface_interaction),
+ description = stringResource(R.string.pref_interface_interaction_desc),
onClick = toInterfacePage
)
SettingsItem(
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsDialogs.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsDialogs.kt
index dda4819..1ce5b84 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsDialogs.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsDialogs.kt
@@ -15,10 +15,12 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
import cn.super12138.todo.ui.components.BasicDialog
import cn.super12138.todo.ui.pages.settings.state.rememberPrefIntState
+import cn.super12138.todo.utils.VibrationUtils
@Composable
fun SettingsRadioDialog(
@@ -31,6 +33,7 @@ fun SettingsRadioDialog(
onDismiss: () -> Unit,
modifier: Modifier = Modifier,
) {
+ val view = LocalView.current
SettingsDialog(
visible = visible,
title = title,
@@ -47,6 +50,7 @@ fun SettingsRadioDialog(
selected = option.id == selectedItemIndex,
onClick = {
selectedItemIndex = option.id
+ VibrationUtils.performHapticFeedback(view)
onSelect(option.id)
onDismiss()
},
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsItem.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsItem.kt
index 3b1bb74..8707a16 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsItem.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsItem.kt
@@ -15,9 +15,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import cn.super12138.todo.utils.VibrationUtils
@Composable
fun SettingsItem(
@@ -80,6 +82,7 @@ fun SettingsItem(
onClick: () -> Unit = {},
modifier: Modifier = Modifier
) {
+ val view = LocalView.current
Row(
modifier = modifier
.fillMaxWidth()
@@ -87,7 +90,10 @@ fun SettingsItem(
.clip(shape)
.clickable(
enabled = enableClick,
- onClick = onClick // TODO: HapticFeedback
+ onClick = {
+ VibrationUtils.performHapticFeedback(view)
+ onClick()
+ }
)
.padding(horizontal = 24.dp, vertical = 20.dp),
verticalAlignment = Alignment.CenterVertically
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SwitchSettingsItem.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SwitchSettingsItem.kt
index 6f819c6..3a31e33 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SwitchSettingsItem.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SwitchSettingsItem.kt
@@ -7,8 +7,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.dp
import cn.super12138.todo.ui.pages.settings.state.rememberPrefBooleanState
+import cn.super12138.todo.utils.VibrationUtils
@Composable
fun SwitchSettingsItem(
@@ -20,6 +22,7 @@ fun SwitchSettingsItem(
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier
) {
+ val view = LocalView.current
var switchState by rememberPrefBooleanState(key, default)
SettingsItem(
leadingIcon = leadingIcon,
@@ -30,6 +33,7 @@ fun SwitchSettingsItem(
checked = switchState,
onCheckedChange = {
switchState = it
+ VibrationUtils.performHapticFeedback(view)
onCheckedChange(it)
},
modifier = Modifier.padding(start = 14.dp)
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/contrast/ContrastPicker.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/contrast/ContrastPicker.kt
index 4d0047f..ad31ab9 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/contrast/ContrastPicker.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/contrast/ContrastPicker.kt
@@ -1,5 +1,6 @@
package cn.super12138.todo.ui.pages.settings.components.contrast
+import android.view.HapticFeedbackConstants
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -15,6 +16,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
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.contentDescription
import androidx.compose.ui.semantics.semantics
@@ -24,12 +26,14 @@ import cn.super12138.todo.constants.Constants
import cn.super12138.todo.logic.model.ContrastLevel
import cn.super12138.todo.ui.pages.settings.components.MoreContentSettingsItem
import cn.super12138.todo.ui.pages.settings.state.rememberPrefFloatState
+import cn.super12138.todo.utils.VibrationUtils
@Composable
fun ContrastPicker(
onContrastChange: (ContrastLevel) -> Unit,
modifier: Modifier = Modifier
) {
+ val view = LocalView.current
val context = LocalContext.current
MoreContentSettingsItem(
title = stringResource(R.string.pref_contrast_level),
@@ -46,6 +50,7 @@ fun ContrastPicker(
},
value = contrastState,
onValueChange = {
+ VibrationUtils.performHapticFeedback(view, HapticFeedbackConstants.LONG_PRESS)
contrastState = it
onContrastChange(ContrastLevel.fromFloat(it))
},
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/darkmode/DarkModeItem.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/darkmode/DarkModeItem.kt
index 9cea4c0..fa76169 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/darkmode/DarkModeItem.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/darkmode/DarkModeItem.kt
@@ -19,7 +19,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.dp
+import cn.super12138.todo.utils.VibrationUtils
@Composable
fun DarkModeItem(
@@ -31,11 +33,15 @@ fun DarkModeItem(
onSelect: () -> Unit,
modifier: Modifier = Modifier
) {
+ val view = LocalView.current
val borderWidth by animateDpAsState(if (selected) 3.dp else (-1).dp)
Column(
modifier = modifier
.clip(MaterialTheme.shapes.large)
- .clickable { onSelect() }
+ .clickable {
+ VibrationUtils.performHapticFeedback(view)
+ onSelect()
+ }
.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/palette/PaletteItem.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/palette/PaletteItem.kt
index 23219f0..f60529c 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/palette/PaletteItem.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/palette/PaletteItem.kt
@@ -24,6 +24,7 @@ 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.unit.dp
import androidx.compose.ui.util.fastForEach
@@ -31,6 +32,7 @@ import cn.super12138.todo.constants.GlobalValues
import cn.super12138.todo.logic.model.ContrastLevel
import cn.super12138.todo.ui.theme.PaletteStyle
import cn.super12138.todo.ui.theme.dynamicColorScheme
+import cn.super12138.todo.utils.VibrationUtils
@Composable
fun PaletteItem(
@@ -40,12 +42,14 @@ fun PaletteItem(
onSelect: () -> Unit,
modifier: Modifier = Modifier
) {
+ val view = LocalView.current
val context = LocalContext.current
Column(
modifier = Modifier
.width(90.dp)
.clip(MaterialTheme.shapes.large)
.clickable {
+ VibrationUtils.performHapticFeedback(view)
onSelect()
}
.padding(8.dp),
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 57e5604..6389357 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
@@ -2,7 +2,6 @@ package cn.super12138.todo.ui.theme
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.Composable
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 430c9f0..63401a4 100644
--- a/app/src/main/kotlin/cn/super12138/todo/utils/SystemUtils.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/utils/SystemUtils.kt
@@ -3,17 +3,19 @@ package cn.super12138.todo.utils
import android.content.Context
import android.os.Build
-/**
- * 获取应用版本号
- * @return 版本名称(版本代码)
- */
-fun getAppVersion(context: Context): String {
- val pkgInfo = context.packageManager.getPackageInfo(context.packageName, 0)
- val verName = pkgInfo.versionName
- val verCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- pkgInfo.longVersionCode.toInt()
- } else {
- pkgInfo.versionCode
+object SystemUtils {
+ /**
+ * 获取应用版本号
+ * @return 版本名称(版本代码)
+ */
+ fun getAppVersion(context: Context): String {
+ val pkgInfo = context.packageManager.getPackageInfo(context.packageName, 0)
+ val verName = pkgInfo.versionName
+ val verCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ pkgInfo.longVersionCode.toInt()
+ } else {
+ pkgInfo.versionCode
+ }
+ return "$verName ($verCode)"
}
- return "$verName ($verCode)"
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/cn/super12138/todo/utils/VibrationUtils.kt b/app/src/main/kotlin/cn/super12138/todo/utils/VibrationUtils.kt
new file mode 100644
index 0000000..f20feba
--- /dev/null
+++ b/app/src/main/kotlin/cn/super12138/todo/utils/VibrationUtils.kt
@@ -0,0 +1,16 @@
+package cn.super12138.todo.utils
+
+import android.view.HapticFeedbackConstants
+import android.view.View
+import cn.super12138.todo.constants.GlobalValues
+
+object VibrationUtils {
+ fun performHapticFeedback(
+ view: View,
+ feedbackConstants: Int = HapticFeedbackConstants.CONTEXT_CLICK
+ ) {
+ if (GlobalValues.hapticFeedback) {
+ view.performHapticFeedback(feedbackConstants)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 9d6ee8c..97f9488 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -74,8 +74,8 @@
高
极高
更改颜色对比度
- 界面
- 待办列表、排序方式
+ 界面与交互
+ 待办列表、触感反馈
显示已完成待办
在待办列表里显示已完成的待办
待办列表
@@ -89,4 +89,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 6adbc49..399ba84 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -75,8 +75,8 @@
High
Very High
Change the contrast level
- Interface
- To-Do List, sorting method
+ Interface & Interaction
+ To-Do List, haptic feedback
Show Completed Tasks
Show completed tasks in to-do list
To-Do List
@@ -90,4 +90,7 @@
Alphabetical (Ascending)
Alphabetical (Descending)
Sorting Method
+ Global Interaction
+ Haptic Feedback
+ Provide slight vibration feedback for some operations (must be enabled in system settings first)
\ No newline at end of file