From c0e8dae7f40b3a9fe2c3b6bab4bb82831bb89629 Mon Sep 17 00:00:00 2001 From: Hepolise Date: Sat, 6 Dec 2025 22:05:43 +0300 Subject: [PATCH] feat: split long press settings into separate components and update UI - Add `material-icons-extended` dependency for additional icons - Split `LongPressSetting` into: - `LongPressSetting`: now only handles long press duration configuration - `LongPressActionSetting`: new component for rewind action type and duration settings - Update SettingsScreen to use separate cards for long press duration and long press action - Add proper icons for different settings sections (Vibration, Track Change, Rewind) - Fix `BootViewModel` to use consolidated `isHooked()` check from repository - Update Russian translations for consistency - Improve UI organization with dedicated sections for related settings --- app/build.gradle.kts | 1 + .../repository/BootRepository.kt | 5 + .../ui/component/LongPressActionSetting.kt | 142 ++++++++++++++++++ .../ui/component/LongPressSetting.kt | 130 +--------------- .../ui/screen/SettingsScreen.kt | 39 +++-- .../viewmodel/BootViewModel.kt | 2 +- app/src/main/res/values-ru-rRU/strings.xml | 18 +-- 7 files changed, 190 insertions(+), 147 deletions(-) create mode 100644 app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/component/LongPressActionSetting.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f735708..d67b1c4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -54,6 +54,7 @@ dependencies { implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.material3:material3:1.4.0") implementation("androidx.compose.material:material-icons-core:1.7.8") + implementation("androidx.compose.material:material-icons-extended:1.7.8") implementation("androidx.core:core-splashscreen:1.2.0") // Compose navigation diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/repository/BootRepository.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/repository/BootRepository.kt index 844cdf7..47e4149 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/repository/BootRepository.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/repository/BootRepository.kt @@ -12,6 +12,7 @@ import ru.hepolise.volumekeytrackcontrol.util.LSPosedLogger import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.LAST_BOOT_COMPLETED_TIME import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.getStatusSharedPreferences import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.isBootCompleted +import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.isHooked import ru.hepolise.volumekeytrackcontrol.util.StatusSysPropsHelper class BootRepository private constructor(private val sharedPreferences: SharedPreferences) { @@ -31,6 +32,10 @@ class BootRepository private constructor(private val sharedPreferences: SharedPr return sharedPreferences.isBootCompleted() } + fun isHooked(): Boolean { + return StatusSysPropsHelper.isHooked || sharedPreferences.isHooked() + } + fun setBootCompleted() { sharedPreferences.edit { putLong(LAST_BOOT_COMPLETED_TIME, System.currentTimeMillis()) diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/component/LongPressActionSetting.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/component/LongPressActionSetting.kt new file mode 100644 index 0000000..cfc5325 --- /dev/null +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/component/LongPressActionSetting.kt @@ -0,0 +1,142 @@ +package ru.hepolise.volumekeytrackcontrol.ui.component + +import android.content.SharedPreferences +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandVertically +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.SegmentedButton +import androidx.compose.material3.SegmentedButtonDefaults +import androidx.compose.material3.SingleChoiceSegmentedButtonRow +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.core.content.edit +import ru.hepolise.volumekeytrackcontrol.util.RewindActionType +import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.REWIND_ACTION_TYPE +import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.REWIND_DURATION +import ru.hepolise.volumekeytrackcontrolmodule.R + +data class RewindSettingData( + val rewindActionType: RewindActionType, + val rewindDuration: Int +) + +@Composable +fun LongPressActionSetting( + data: RewindSettingData, + sharedPreferences: SharedPreferences, + onValueChange: (RewindSettingData) -> Unit +) { + val rewindActionType = data.rewindActionType + val rewindDuration = data.rewindDuration + + var showRewindDurationDialog by remember { mutableStateOf(false) } + + Row(verticalAlignment = Alignment.CenterVertically) { + SingleChoiceSegmentedButtonRow { + RewindActionType.entries.forEachIndexed { index, actionType -> + SegmentedButton( + selected = rewindActionType == actionType, + onClick = { + onValueChange(data.copy(rewindActionType = actionType)) + sharedPreferences.edit { + putString(REWIND_ACTION_TYPE, actionType.name) + } + }, + shape = SegmentedButtonDefaults.itemShape( + index = index, + count = RewindActionType.entries.size + ), + modifier = Modifier + .defaultMinSize(minWidth = 140.dp) + .weight(1f) + ) { + Text( + text = when (actionType) { + RewindActionType.TRACK_CHANGE -> stringResource(R.string.track_change) + RewindActionType.REWIND -> stringResource(R.string.rewind) + }, + maxLines = 1, + overflow = TextOverflow.Visible, + softWrap = false + ) + } + } + } + } + + Box { + AnimatedVisibility( + visible = rewindActionType == RewindActionType.REWIND, + enter = fadeIn() + expandVertically(expandFrom = Alignment.Top), + exit = fadeOut() + shrinkVertically(shrinkTowards = Alignment.Top), + modifier = Modifier.fillMaxWidth() + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + PrefsSlider( + value = rewindDuration, + onValueChange = { + onValueChange(data.copy(rewindDuration = it)) + }, + valueRange = 1f..60f, + prefKey = REWIND_DURATION, + sharedPreferences = sharedPreferences + ) + + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + text = stringResource(R.string.rewind_duration, rewindDuration), + modifier = Modifier.clickable { showRewindDurationDialog = true } + ) + IconButton( + onClick = { + showRewindDurationDialog = true + } + ) { + Icon( + imageVector = Icons.Default.Edit, + contentDescription = stringResource(R.string.edit_rewind_duration) + ) + } + } + } + } + } + + if (showRewindDurationDialog) { + NumberAlertDialog( + title = stringResource(R.string.rewind_duration_dialog_title), + defaultValue = rewindDuration, + minValue = 1, + maxValue = 60, + onDismissRequest = { showRewindDurationDialog = false }, + onConfirm = { + onValueChange(data.copy(rewindDuration = it)) + sharedPreferences.edit { putInt(REWIND_DURATION, it) } + showRewindDurationDialog = false + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/component/LongPressSetting.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/component/LongPressSetting.kt index f1ef797..dc96181 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/component/LongPressSetting.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/component/LongPressSetting.kt @@ -1,24 +1,12 @@ package ru.hepolise.volumekeytrackcontrol.ui.component import android.content.SharedPreferences -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.expandVertically -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Edit import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.SegmentedButton -import androidx.compose.material3.SegmentedButtonDefaults -import androidx.compose.material3.SingleChoiceSegmentedButtonRow import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -28,37 +16,21 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp import androidx.core.content.edit -import ru.hepolise.volumekeytrackcontrol.util.RewindActionType import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.LONG_PRESS_DURATION -import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.REWIND_ACTION_TYPE -import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.REWIND_DURATION import ru.hepolise.volumekeytrackcontrolmodule.R -data class LongPressSettingData( - val longPressDuration: Int, - val rewindActionType: RewindActionType, - val rewindDuration: Int -) - @Composable fun LongPressSetting( - data: LongPressSettingData, + longPressDuration: Int, sharedPreferences: SharedPreferences, - onValueChange: (LongPressSettingData) -> Unit + onValueChange: (Int) -> Unit ) { - val longPressDuration = data.longPressDuration - val rewindActionType = data.rewindActionType - val rewindDuration = data.rewindDuration - var showLongPressTimeoutDialog by remember { mutableStateOf(false) } - var showRewindDurationDialog by remember { mutableStateOf(false) } PrefsSlider( value = longPressDuration, - onValueChange = { onValueChange(data.copy(longPressDuration = it)) }, + onValueChange = { onValueChange(it) }, valueRange = 100f..1000f, prefKey = LONG_PRESS_DURATION, sharedPreferences = sharedPreferences @@ -81,85 +53,6 @@ fun LongPressSetting( } } - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = stringResource(R.string.long_press_action), - ) - } - - Row(verticalAlignment = Alignment.CenterVertically) { - SingleChoiceSegmentedButtonRow { - RewindActionType.entries.forEachIndexed { index, actionType -> - SegmentedButton( - selected = rewindActionType == actionType, - onClick = { - onValueChange(data.copy(rewindActionType = actionType)) - sharedPreferences.edit { - putString(REWIND_ACTION_TYPE, actionType.name) - } - }, - shape = SegmentedButtonDefaults.itemShape( - index = index, - count = RewindActionType.entries.size - ), - modifier = Modifier - .defaultMinSize(minWidth = 140.dp) - .weight(1f) - ) { - Text( - text = when (actionType) { - RewindActionType.TRACK_CHANGE -> stringResource(R.string.track_change) - RewindActionType.REWIND -> stringResource(R.string.rewind) - }, - maxLines = 1, - overflow = TextOverflow.Visible, - softWrap = false - ) - } - } - } - } - - Box { - AnimatedVisibility( - visible = rewindActionType == RewindActionType.REWIND, - enter = fadeIn() + expandVertically(expandFrom = Alignment.Top), - exit = fadeOut() + shrinkVertically(shrinkTowards = Alignment.Top), - modifier = Modifier.fillMaxWidth() - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { - PrefsSlider( - value = rewindDuration, - onValueChange = { - onValueChange(data.copy(rewindDuration = it)) - }, - valueRange = 1f..60f, - prefKey = REWIND_DURATION, - sharedPreferences = sharedPreferences - ) - - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = stringResource(R.string.rewind_duration, rewindDuration), - modifier = Modifier.clickable { showRewindDurationDialog = true } - ) - IconButton( - onClick = { - showRewindDurationDialog = true - } - ) { - Icon( - imageVector = Icons.Default.Edit, - contentDescription = stringResource(R.string.edit_rewind_duration) - ) - } - } - } - } - } - if (showLongPressTimeoutDialog) { NumberAlertDialog( title = stringResource(R.string.long_press_duration_dialog_title), @@ -168,25 +61,10 @@ fun LongPressSetting( maxValue = 1000, onDismissRequest = { showLongPressTimeoutDialog = false }, onConfirm = { - onValueChange(data.copy(longPressDuration = it)) + onValueChange(it) sharedPreferences.edit { putInt(LONG_PRESS_DURATION, it) } showLongPressTimeoutDialog = false } ) } - - if (showRewindDurationDialog) { - NumberAlertDialog( - title = stringResource(R.string.rewind_duration_dialog_title), - defaultValue = rewindDuration, - minValue = 1, - maxValue = 60, - onDismissRequest = { showRewindDurationDialog = false }, - onConfirm = { - onValueChange(data.copy(rewindDuration = it)) - sharedPreferences.edit { putInt(REWIND_DURATION, it) } - showRewindDurationDialog = false - } - ) - } } \ No newline at end of file diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/screen/SettingsScreen.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/screen/SettingsScreen.kt index bb4b8cd..328d7af 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/screen/SettingsScreen.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/screen/SettingsScreen.kt @@ -34,11 +34,13 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Done import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.FastForward import androidx.compose.material.icons.filled.Info -import androidx.compose.material.icons.filled.Notifications import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Settings +import androidx.compose.material.icons.filled.SkipNext import androidx.compose.material.icons.filled.Star +import androidx.compose.material.icons.filled.Vibration import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button @@ -92,14 +94,16 @@ import androidx.core.net.toUri import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import ru.hepolise.volumekeytrackcontrol.ui.component.AppFilterSetting +import ru.hepolise.volumekeytrackcontrol.ui.component.LongPressActionSetting import ru.hepolise.volumekeytrackcontrol.ui.component.LongPressSetting -import ru.hepolise.volumekeytrackcontrol.ui.component.LongPressSettingData +import ru.hepolise.volumekeytrackcontrol.ui.component.RewindSettingData import ru.hepolise.volumekeytrackcontrol.ui.component.SwapButtonsSetting import ru.hepolise.volumekeytrackcontrol.ui.component.VibrationEffectSetting import ru.hepolise.volumekeytrackcontrol.ui.component.VibrationSettingData import ru.hepolise.volumekeytrackcontrol.ui.isInstalledAfterReboot import ru.hepolise.volumekeytrackcontrol.util.AppFilterType import ru.hepolise.volumekeytrackcontrol.util.Constants +import ru.hepolise.volumekeytrackcontrol.util.RewindActionType import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.APP_FILTER_TYPE_DEFAULT_VALUE import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.EFFECT_DEFAULT_VALUE import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.IS_SWAP_BUTTONS_DEFAULT_VALUE @@ -263,15 +267,10 @@ fun SettingsScreen( title = stringResource(R.string.long_press_settings) ) { LongPressSetting( - LongPressSettingData( - longPressDuration, - rewindActionType, - rewindDuration - ), settingsPrefs + longPressDuration, + settingsPrefs ) { - longPressDuration = it.longPressDuration - rewindActionType = it.rewindActionType - rewindDuration = it.rewindDuration + longPressDuration = it } SwapButtonsSetting( isSwapButtons = isSwapButtons, @@ -282,7 +281,25 @@ fun SettingsScreen( } SettingsCard( - icon = Icons.Default.Notifications, + icon = when (rewindActionType) { + RewindActionType.TRACK_CHANGE -> Icons.Default.SkipNext + RewindActionType.REWIND -> Icons.Default.FastForward + }, + title = stringResource(R.string.long_press_action) + ) { + LongPressActionSetting( + RewindSettingData( + rewindActionType, + rewindDuration + ), settingsPrefs + ) { + rewindActionType = it.rewindActionType + rewindDuration = it.rewindDuration + } + } + + SettingsCard( + icon = Icons.Default.Vibration, title = stringResource(R.string.vibration_settings) ) { VibrationEffectSetting( diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/viewmodel/BootViewModel.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/viewmodel/BootViewModel.kt index 2fa7bf6..5eed922 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/viewmodel/BootViewModel.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/viewmodel/BootViewModel.kt @@ -49,7 +49,7 @@ class BootViewModel( delay(1_000) StatusSysPropsHelper.refreshIsHooked() - if (StatusSysPropsHelper.isHooked) { + if (bootRepository.isHooked()) { LSPosedLogger.log("Boot completed - hook detected") bootRepository.setBootCompleted() return diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index de2e4de..c6bfebb 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -37,16 +37,16 @@ Можно выбрать не более %1$d приложений. Очистить - Удалить из списка %d приложение - Удалить из списка %d приложения - Удалить из списка %d приложений - Удалить из списка %d приложения + Очистить %d приложение + Очистить %d приложения + Очистить %d приложений + Очистить %d приложения - Удалить из списка %d приложение? - Удалить из списка %d приложения? - Удалить из списка %d приложений? - Удалить из списка %d приложения? + Очистить %d приложение? + Очистить %d приложения? + Очистить %d приложений? + Очистить %d приложения? Длительность: %d мс @@ -54,7 +54,7 @@ Поменять кнопки Вверх/Вниз для переключения - Переключение трека + Переключение Действие при долгом нажатии Перемотка Длительность перемотки: %d сек