feat(settings): 支持自定义触感反馈

This commit is contained in:
Super12138 2025-02-06 19:19:38 +08:00
parent 7f1e3d9626
commit 4e1ede0aae
25 changed files with 197 additions and 50 deletions

View file

@ -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
}

View file

@ -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
)
}

View file

@ -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 = {

View file

@ -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)
}
},

View file

@ -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,

View file

@ -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)

View file

@ -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,

View file

@ -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,

View file

@ -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 {

View file

@ -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,

View file

@ -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)

View file

@ -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,

View file

@ -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 = {}
)
}
}

View file

@ -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(

View file

@ -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()
},

View file

@ -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

View file

@ -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)

View file

@ -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))
},

View file

@ -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
) {

View file

@ -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),

View file

@ -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

View file

@ -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)"
}

View file

@ -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)
}
}
}

View file

@ -74,8 +74,8 @@
<string name="contrast_high"></string>
<string name="contrast_very_high">极高</string>
<string name="tip_change_contrast_level">更改颜色对比度</string>
<string name="pref_interface">界面</string>
<string name="pref_interface_desc">待办列表、排序方式</string>
<string name="pref_interface_interaction">界面与交互</string>
<string name="pref_interface_interaction_desc">待办列表、触感反馈</string>
<string name="pref_show_completed">显示已完成待办</string>
<string name="pref_show_completed_desc">在待办列表里显示已完成的待办</string>
<string name="pref_category_todo_list">待办列表</string>
@ -89,4 +89,7 @@
<string name="sorting_alphabetical_ascending">首字母(升序)</string>
<string name="sorting_alphabetical_descending">首字母(降序)</string>
<string name="pref_sorting_method">排序方式</string>
<string name="pref_category_global_interaction">全局交互</string>
<string name="pref_haptic_feedback">触感反馈</string>
<string name="pref_haptic_feedback_desc">为某些操作提供轻微的震动反馈(须先在系统设置开启)</string>
</resources>

View file

@ -75,8 +75,8 @@
<string name="contrast_high">High</string>
<string name="contrast_very_high">Very High</string>
<string name="tip_change_contrast_level">Change the contrast level</string>
<string name="pref_interface">Interface</string>
<string name="pref_interface_desc">To-Do List, sorting method</string>
<string name="pref_interface_interaction">Interface &amp; Interaction</string>
<string name="pref_interface_interaction_desc">To-Do List, haptic feedback</string>
<string name="pref_show_completed">Show Completed Tasks</string>
<string name="pref_show_completed_desc">Show completed tasks in to-do list</string>
<string name="pref_category_todo_list">To-Do List</string>
@ -90,4 +90,7 @@
<string name="sorting_alphabetical_ascending">Alphabetical (Ascending)</string>
<string name="sorting_alphabetical_descending">Alphabetical (Descending)</string>
<string name="pref_sorting_method">Sorting Method</string>
<string name="pref_category_global_interaction">Global Interaction</string>
<string name="pref_haptic_feedback">Haptic Feedback</string>
<string name="pref_haptic_feedback_desc">Provide slight vibration feedback for some operations (must be enabled in system settings first)</string>
</resources>