diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1e8793d..6e5938d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,8 +17,6 @@ on: push: branches: - main - - dev - - test pull_request: branches: - main diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/SettingsActivity.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/SettingsActivity.kt index 6747bf3..e733fcc 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/SettingsActivity.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/SettingsActivity.kt @@ -7,13 +7,11 @@ import android.content.Intent import android.net.Uri import android.os.Build import android.os.Bundle -import android.os.VibrationEffect import android.os.Vibrator import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.clickable import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -22,24 +20,31 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ColorScheme import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MenuAnchorType +import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Slider -import androidx.compose.material3.Switch import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.TextField import androidx.compose.material3.TopAppBar import androidx.compose.material3.darkColorScheme @@ -47,6 +52,7 @@ import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableLongStateOf @@ -55,8 +61,11 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -65,15 +74,15 @@ import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.LONG_PRESS_D import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.SELECTED_EFFECT import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.SELECTED_EFFECT_DEFAULT_VALUE import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.SETTINGS_PREFS_NAME +import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.VIBRATION_AMPLITUDE +import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.VIBRATION_AMPLITUDE_DEFAULT_VALUE import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.VIBRATION_LENGTH import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.VIBRATION_LENGTH_DEFAULT_VALUE -import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.VIBRATION_MODE -import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.VIBRATION_PREDEFINED_MODE_DEFAULT_VALUE import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.getLongPressDuration import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.getSelectedEffect +import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.getVibrationAmplitude import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.getVibrationLength -import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.isVibrationModePredefined -import ru.hepolise.volumekeytrackcontrol.util.VibratorUtil.PredefinedEffects +import ru.hepolise.volumekeytrackcontrol.util.VibrationType import ru.hepolise.volumekeytrackcontrol.util.VibratorUtil.getVibrator import ru.hepolise.volumekeytrackcontrol.util.VibratorUtil.triggerVibration import ru.hepolise.volumekeytrackcontrolmodule.R @@ -93,14 +102,25 @@ class SettingsActivity : ComponentActivity() { } } -private val VibrationEffectTitles = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - mapOf( - VibrationEffect.EFFECT_CLICK to R.string.effect_click, - VibrationEffect.EFFECT_DOUBLE_CLICK to R.string.effect_double_click, - VibrationEffect.EFFECT_HEAVY_CLICK to R.string.effect_heavy_click, - VibrationEffect.EFFECT_TICK to R.string.effect_tick - ) -} else emptyMap() +private val VibrationEffectTitles = VibrationType.values.associateWith { + when (it) { + VibrationType.Disabled -> R.string.vibration_disabled + VibrationType.Manual -> R.string.vibration_manual + else -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + when (it) { + VibrationType.Click -> R.string.vibration_effect_click + VibrationType.DoubleClick -> R.string.vibration_effect_double_click + VibrationType.HeavyClick -> R.string.vibration_effect_heavy_click + VibrationType.Tick -> R.string.vibration_effect_tick + else -> throw IllegalStateException("Unknown VibrationType: $it") + } + } else { + throw IllegalStateException("VibrationType is not supported on this API level") + } + } + } +} @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -117,120 +137,113 @@ fun VibrationSettingsScreen(vibrator: Vibrator?) { exitProcess(0) } - var isVibrationModePredefined by remember { mutableStateOf(sharedPreferences.isVibrationModePredefined()) } + var longPressDuration by remember { mutableLongStateOf(sharedPreferences.getLongPressDuration()) } var selectedEffect by remember { mutableIntStateOf(sharedPreferences.getSelectedEffect()) } var vibrationLength by remember { mutableLongStateOf(sharedPreferences.getVibrationLength()) } - var longPressDuration by remember { mutableLongStateOf(sharedPreferences.getLongPressDuration()) } + var vibrationAmplitude by remember { mutableIntStateOf(sharedPreferences.getVibrationAmplitude()) } Scaffold( topBar = { TopAppBar(title = { Text(stringResource(R.string.app_name)) }) } ) { padding -> - Column( + Box( modifier = Modifier .fillMaxSize() - .padding(padding) - .padding(16.dp) - .verticalScroll(rememberScrollState()), - verticalArrangement = Arrangement.spacedBy(16.dp), - horizontalAlignment = Alignment.CenterHorizontally ) { - - Text(text = stringResource(R.string.long_press_settings), fontSize = 20.sp) - - Slider( - value = longPressDuration.toFloat(), - onValueChange = { - longPressDuration = it.toLong() - }, - valueRange = 100f..1000f, - onValueChangeFinished = { - sharedPreferences.edit().putLong(LONG_PRESS_DURATION, longPressDuration).apply() - }, - modifier = Modifier.widthIn(max = 300.dp) - ) - Text(stringResource(R.string.long_press_duration, longPressDuration)) - - Spacer(modifier = Modifier.height(20.dp)) - - Text(text = stringResource(R.string.vibration_settings), fontSize = 20.sp) - Column( modifier = Modifier - .fillMaxWidth() - .height(116.dp), + .fillMaxSize() + .padding(padding) + .padding(12.dp) + .verticalScroll(rememberScrollState()) + .padding(bottom = 48.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - Box( - modifier = Modifier.widthIn(min = 150.dp), - contentAlignment = Alignment.CenterStart - ) { - Text( - text = if (isVibrationModePredefined) { - stringResource(id = R.string.predefined_vibration) - } else { - stringResource(id = R.string.manual_vibration) - }, - modifier = Modifier - .padding(end = 8.dp) - .clickable { - isVibrationModePredefined = !isVibrationModePredefined - sharedPreferences - .edit() - .putBoolean(VIBRATION_MODE, isVibrationModePredefined) - .apply() - } - ) + + Text(text = stringResource(R.string.long_press_settings), fontSize = 20.sp) + + Slider( + value = longPressDuration.toFloat(), + onValueChange = { + longPressDuration = it.toLong() + }, + valueRange = 100f..1000f, + onValueChangeFinished = { + sharedPreferences.edit().putLong(LONG_PRESS_DURATION, longPressDuration) + .apply() + }, + modifier = Modifier.widthIn(max = 300.dp) + ) + + var showLongPressTimeoutDialog by remember { mutableStateOf(false) } + Row(verticalAlignment = Alignment.CenterVertically) { + Text(stringResource(R.string.long_press_duration, longPressDuration)) + IconButton( + onClick = { + showLongPressTimeoutDialog = true } - Switch( - checked = isVibrationModePredefined, - onCheckedChange = { - isVibrationModePredefined = it - sharedPreferences.edit().putBoolean(VIBRATION_MODE, it).apply() - } + ) { + Icon( + imageVector = Icons.Default.Edit, + contentDescription = stringResource(R.string.edit) ) } } - if (isVibrationModePredefined && PredefinedEffects.isNotEmpty()) { - var effectExpanded by remember { mutableStateOf(false) } - ExposedDropdownMenuBox( + if (showLongPressTimeoutDialog) { + NumberAlertDialog( + title = stringResource(R.string.long_press_duration_dialog_title), + defaultValue = longPressDuration, + minValue = 100, + maxValue = 1000, + onDismissRequest = { showLongPressTimeoutDialog = false }, + onConfirm = { + longPressDuration = it + sharedPreferences.edit().putLong(LONG_PRESS_DURATION, it).apply() + showLongPressTimeoutDialog = false + } + ) + } + + HorizontalDivider(modifier = Modifier.widthIn(max = 300.dp)) + + Text(text = stringResource(R.string.vibration_settings), fontSize = 20.sp) + + val vibrationType = VibrationType.values[selectedEffect] + var effectExpanded by remember { mutableStateOf(false) } + ExposedDropdownMenuBox( + expanded = effectExpanded, + onExpandedChange = { effectExpanded = !effectExpanded } + ) { + TextField( + value = stringResource(VibrationEffectTitles[vibrationType]!!), + onValueChange = {}, + readOnly = true, + trailingIcon = { + ExposedDropdownMenuDefaults.TrailingIcon(expanded = effectExpanded) + }, + modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable) + ) + ExposedDropdownMenu( expanded = effectExpanded, - onExpandedChange = { effectExpanded = !effectExpanded } - ) { - TextField( - value = stringResource(VibrationEffectTitles[PredefinedEffects[selectedEffect]]!!), - onValueChange = {}, - readOnly = true, - trailingIcon = { - ExposedDropdownMenuDefaults.TrailingIcon(expanded = effectExpanded) - }, - modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable) - ) - ExposedDropdownMenu( - expanded = effectExpanded, - onDismissRequest = { effectExpanded = false }) { - PredefinedEffects.forEachIndexed { index, effect -> - DropdownMenuItem( - text = { Text(stringResource(VibrationEffectTitles[effect]!!)) }, - onClick = { - selectedEffect = index - sharedPreferences.edit().putInt(SELECTED_EFFECT, index) - .apply() - effectExpanded = false - } - ) - } + onDismissRequest = { effectExpanded = false }) { + VibrationType.values.forEachIndexed { index, effect -> + DropdownMenuItem( + text = { Text(stringResource(VibrationEffectTitles[effect]!!)) }, + onClick = { + selectedEffect = index + sharedPreferences.edit().putInt(SELECTED_EFFECT, index) + .apply() + effectExpanded = false + } + ) } } - } else if (!isVibrationModePredefined) { + } + + if (vibrationType == VibrationType.Manual) { Slider( value = vibrationLength.toFloat(), onValueChange = { @@ -243,35 +256,112 @@ fun VibrationSettingsScreen(vibrator: Vibrator?) { }, modifier = Modifier.widthIn(max = 300.dp) ) - Text(stringResource(R.string.vibration_length, vibrationLength)) - } - } - Button(onClick = { - vibrator?.triggerVibration(sharedPreferences) - }) { - Text(stringResource(R.string.test_vibration)) - } + var showManualVibrationLengthDialog by remember { mutableStateOf(false) } + Row(verticalAlignment = Alignment.CenterVertically) { + Text(stringResource(R.string.vibration_length, vibrationLength)) + IconButton( + onClick = { + showManualVibrationLengthDialog = true + } + ) { + Icon( + imageVector = Icons.Default.Edit, + contentDescription = stringResource(R.string.edit) + ) + } + } + + if (showManualVibrationLengthDialog) { + NumberAlertDialog( + title = stringResource(R.string.vibration_length_dialog_title), + defaultValue = vibrationLength, + minValue = 10, + maxValue = 500, + onDismissRequest = { showManualVibrationLengthDialog = false }, + onConfirm = { + vibrationLength = it + sharedPreferences.edit().putLong(VIBRATION_LENGTH, it).apply() + showManualVibrationLengthDialog = false + } + ) + } + + Slider( + value = vibrationAmplitude.toFloat(), + onValueChange = { + vibrationAmplitude = it.toInt() + }, + valueRange = 1f..255f, + onValueChangeFinished = { + sharedPreferences.edit().putInt(VIBRATION_AMPLITUDE, vibrationAmplitude) + .apply() + }, + modifier = Modifier.widthIn(max = 300.dp) + ) + + var showVibrationAmplitudeDialog by remember { mutableStateOf(false) } + Row(verticalAlignment = Alignment.CenterVertically) { + Text(stringResource(R.string.vibration_amplitude, vibrationAmplitude)) + IconButton( + onClick = { + showVibrationAmplitudeDialog = true + } + ) { + Icon( + imageVector = Icons.Default.Edit, + contentDescription = stringResource(R.string.edit) + ) + } + } - Spacer(modifier = Modifier.weight(1f)) + if (showVibrationAmplitudeDialog) { + NumberAlertDialog( + title = stringResource(R.string.vibration_amplitude_dialog_title), + defaultValue = vibrationAmplitude.toLong(), + minValue = 1, + maxValue = 255, + onDismissRequest = { showVibrationAmplitudeDialog = false }, + onConfirm = { + vibrationAmplitude = it.toInt() + sharedPreferences.edit().putInt(VIBRATION_AMPLITUDE, it.toInt()) + .apply() + showVibrationAmplitudeDialog = false + } + ) + } - Row(modifier = Modifier.fillMaxWidth()) { + } + if (vibrationType != VibrationType.Disabled) { + Button(onClick = { + vibrator?.triggerVibration(sharedPreferences) + }) { + Text(stringResource(R.string.test_vibration)) + } + } + } + Row( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.BottomCenter) + .padding(16.dp) + ) { Spacer(modifier = Modifier.weight(1f)) Button(onClick = { sharedPreferences.edit().clear().apply() - isVibrationModePredefined = VIBRATION_PREDEFINED_MODE_DEFAULT_VALUE selectedEffect = SELECTED_EFFECT_DEFAULT_VALUE vibrationLength = VIBRATION_LENGTH_DEFAULT_VALUE + vibrationAmplitude = VIBRATION_AMPLITUDE_DEFAULT_VALUE longPressDuration = LONG_PRESS_DURATION_DEFAULT_VALUE Toast.makeText( context, - context.getString(R.string.settings_reset), + context.getString(R.string.settings_reset_toast), Toast.LENGTH_SHORT ).show() }) { - Text(stringResource(R.string.restore_default)) + Text(stringResource(R.string.settings_reset)) } Spacer(modifier = Modifier.width(8.dp)) @@ -302,6 +392,54 @@ fun dynamicColorScheme(context: Context): ColorScheme { } } +@Composable +fun NumberAlertDialog( + title: String, + defaultValue: Long, + onDismissRequest: () -> Unit, + onConfirm: (Long) -> Unit, + minValue: Long = 0, + maxValue: Long = Long.MAX_VALUE, + validate: (Long) -> Boolean = { it in minValue..maxValue } +) { + var value by remember { mutableStateOf(defaultValue.toString()) } + val focusRequester = remember { FocusRequester() } + AlertDialog( + onDismissRequest = onDismissRequest, + title = { Text(text = title) }, + text = { + Column { + OutlinedTextField( + value = value, + onValueChange = { value = it }, + label = { Text(stringResource(R.string.value_in_range, minValue, maxValue)) }, + keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number), + isError = value.toLongOrNull() == null || !validate(value.toLong()), + modifier = Modifier.focusRequester(focusRequester) + ) + } + }, + confirmButton = { + TextButton( + onClick = { + onConfirm(value.toLong()) + }, + enabled = value.toLongOrNull() != null && validate(value.toLong()) + ) { + Text(text = stringResource(R.string.ok)) + } + }, + dismissButton = { + TextButton(onClick = onDismissRequest) { + Text(text = stringResource(R.string.cancel)) + } + }, + ) + LaunchedEffect(true) { + focusRequester.requestFocus() + } +} + @Preview(showBackground = true) @Composable fun PreviewVibrationSettingsScreen() { diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/util/SharedPreferencesUtil.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/util/SharedPreferencesUtil.kt index 6933c18..2808932 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/util/SharedPreferencesUtil.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/util/SharedPreferencesUtil.kt @@ -1,7 +1,6 @@ package ru.hepolise.volumekeytrackcontrol.util import android.content.SharedPreferences -import android.os.Build import android.view.ViewConfiguration import de.robv.android.xposed.XSharedPreferences import ru.hepolise.volumekeytrackcontrolmodule.BuildConfig @@ -9,32 +8,35 @@ import ru.hepolise.volumekeytrackcontrolmodule.BuildConfig object SharedPreferencesUtil { const val SETTINGS_PREFS_NAME = "settings_prefs" - const val VIBRATION_MODE = "vibrationMode" const val SELECTED_EFFECT = "selectedEffect" const val VIBRATION_LENGTH = "vibrationLength" + const val VIBRATION_AMPLITUDE = "vibrationAmplitude" const val LONG_PRESS_DURATION = "longPressDuration" - val VIBRATION_PREDEFINED_MODE_DEFAULT_VALUE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q const val SELECTED_EFFECT_DEFAULT_VALUE = 0 const val VIBRATION_LENGTH_DEFAULT_VALUE = 50L + const val VIBRATION_AMPLITUDE_DEFAULT_VALUE = 128 val LONG_PRESS_DURATION_DEFAULT_VALUE = ViewConfiguration.getLongPressTimeout().toLong() - fun SharedPreferences?.isVibrationModePredefined(): Boolean { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) return false - val defaultValue = VIBRATION_PREDEFINED_MODE_DEFAULT_VALUE - return this?.getBoolean(VIBRATION_MODE, defaultValue) ?: defaultValue - } - fun SharedPreferences?.getSelectedEffect(): Int { val defaultValue = SELECTED_EFFECT_DEFAULT_VALUE return this?.getInt(SELECTED_EFFECT, defaultValue) ?: defaultValue } + fun SharedPreferences?.getVibrationType(): VibrationType { + return VibrationType.values[getSelectedEffect()] + } + fun SharedPreferences?.getVibrationLength(): Long { val defaultValue = VIBRATION_LENGTH_DEFAULT_VALUE return this?.getLong(VIBRATION_LENGTH, defaultValue) ?: defaultValue } + fun SharedPreferences?.getVibrationAmplitude(): Int { + val defaultValue = VIBRATION_AMPLITUDE_DEFAULT_VALUE + return this?.getInt(VIBRATION_AMPLITUDE, defaultValue) ?: defaultValue + } + fun SharedPreferences?.getLongPressDuration(): Long { val defaultValue = LONG_PRESS_DURATION_DEFAULT_VALUE return this?.getLong(LONG_PRESS_DURATION, defaultValue) ?: defaultValue diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/util/VibratorUtil.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/util/VibratorUtil.kt index 453da92..3778f00 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/util/VibratorUtil.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/util/VibratorUtil.kt @@ -6,21 +6,13 @@ import android.os.Build import android.os.VibrationEffect import android.os.Vibrator import android.os.VibratorManager -import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.getSelectedEffect +import androidx.annotation.RequiresApi +import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.getVibrationAmplitude import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.getVibrationLength -import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.isVibrationModePredefined +import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.getVibrationType object VibratorUtil { - val PredefinedEffects = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - listOf( - VibrationEffect.EFFECT_CLICK, - VibrationEffect.EFFECT_DOUBLE_CLICK, - VibrationEffect.EFFECT_HEAVY_CLICK, - VibrationEffect.EFFECT_TICK - ) - } else emptyList() - fun Context.getVibrator(): Vibrator { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val vibratorManager = @@ -33,19 +25,51 @@ object VibratorUtil { } fun Vibrator.triggerVibration(prefs: SharedPreferences? = SharedPreferencesUtil.prefs()) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && prefs.isVibrationModePredefined()) { - this.vibrate( - VibrationEffect.createPredefined(PredefinedEffects[prefs.getSelectedEffect()]) - ) + val vibrationType = prefs.getVibrationType() + if (vibrationType == VibrationType.Disabled) return + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && vibrationType != VibrationType.Manual) { + this.vibrate(VibrationEffect.createPredefined(vibrationType.value)) } else { - val millis = prefs.getVibrationLength() this.vibrate( VibrationEffect.createOneShot( - millis, - VibrationEffect.DEFAULT_AMPLITUDE + prefs.getVibrationLength(), + prefs.getVibrationAmplitude() ) ) } } +} + + +sealed class VibrationType(val value: Int) { + data object Disabled : VibrationType(-1) + data object Manual : VibrationType(-1) + + companion object { + val values: List by lazy { + mutableListOf().apply { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + add(Click) + add(DoubleClick) + add(HeavyClick) + add(Tick) + } + + add(Manual) + add(Disabled) + } + } + } + + @RequiresApi(Build.VERSION_CODES.Q) + data object Click : VibrationType(VibrationEffect.EFFECT_CLICK) + + @RequiresApi(Build.VERSION_CODES.Q) + data object DoubleClick : VibrationType(VibrationEffect.EFFECT_DOUBLE_CLICK) + + @RequiresApi(Build.VERSION_CODES.Q) + data object HeavyClick : VibrationType(VibrationEffect.EFFECT_HEAVY_CLICK) + @RequiresApi(Build.VERSION_CODES.Q) + data object Tick : VibrationType(VibrationEffect.EFFECT_TICK) } \ 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 7003d0a..9a9314f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,22 +4,32 @@ Vibration Settings Long Press Settings - Predefined Vibration - Manual Vibration + Click + Double Click + Heavy Click + Tick - Click - Double Click - Heavy Click - Tick + Disabled + Manual Vibration Length: %dms + Vibration Amplitude: %d + Vibration Length + Vibration Amplitude Test Vibration Long Press Duration: %dms + Long Press Duration - Restore Default - Settings are restored to default + Reset settings + Settings are reset to default About Module is not enabled in LSPosed + + OK + Cancel + Edit + + Value in range %d..%d diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 06760df..6a215fa 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Feb 03 20:40:17 MSK 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME