From 7dd397827fef101a1a9f4f42cde58bc3ef9ec7cc Mon Sep 17 00:00:00 2001 From: Hepolise Date: Fri, 18 Jul 2025 01:30:11 +0300 Subject: [PATCH 1/3] Refactor VolumeKeyControlModuleHandlers: - Introduce sealed class MediaEvent for long-press actions - Add KeyHelper and Action enums for clearer key logic - Refactor hook methods into MethodHookParam extensions - Improve readability and unify delayed event handling --- .../module/VolumeKeyControlModuleHandlers.kt | 274 ++++++++++-------- 1 file changed, 154 insertions(+), 120 deletions(-) diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/module/VolumeKeyControlModuleHandlers.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/module/VolumeKeyControlModuleHandlers.kt index 3f2b3ba..74586b9 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/module/VolumeKeyControlModuleHandlers.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/module/VolumeKeyControlModuleHandlers.kt @@ -24,11 +24,6 @@ import ru.hepolise.volumekeytrackcontrol.util.VibratorUtil.triggerVibration object VolumeKeyControlModuleHandlers { - - private const val VOLUME_UP_LONG_PRESS = "mVolumeUpLongPress" - private const val VOLUME_DOWN_LONG_PRESS = "mVolumeDownLongPress" - private const val VOLUME_BOTH_LONG_PRESS = "mVolumeBothLongPress" - private const val CLASS_MEDIA_SESSION_LEGACY_HELPER = "android.media.session.MediaSessionLegacyHelper" private const val CLASS_COMPONENT_NAME = "android.content.ComponentName" @@ -48,53 +43,42 @@ object VolumeKeyControlModuleHandlers { val handleInterceptKeyBeforeQueueing: XC_MethodHook = object : XC_MethodHook() { override fun beforeHookedMethod(param: MethodHookParam) { - val event = param.args[0] as KeyEvent - val keyCode = event.keyCode - initManagers(param) - if (needHook(keyCode, event)) { - doHook(keyCode, event, param) + with(param) { + val event = args[0] as KeyEvent + try { + initManagers() + initControllers() + } catch (t: Throwable) { + log("init failed") + t.printStackTrace() + return + } + if (needHook(event)) { + doHook(event) + } } } } val handleConstructPhoneWindowManager: XC_MethodHook = object : XC_MethodHook() { override fun afterHookedMethod(param: MethodHookParam) { - mapOf( - VOLUME_UP_LONG_PRESS to Runnable { - log("sending next") - isLongPress = true - sendMediaButtonEventAndTriggerVibration(KeyEvent.KEYCODE_MEDIA_NEXT) - }, - VOLUME_DOWN_LONG_PRESS to Runnable { - log("sending prev") - isLongPress = true - sendMediaButtonEventAndTriggerVibration(KeyEvent.KEYCODE_MEDIA_PREVIOUS) - }, - VOLUME_BOTH_LONG_PRESS to Runnable { - if (isUpPressed && isDownPressed) { - log("sending play/pause") - isLongPress = true - getMediaController()?.transportControls?.also { - sendMediaButtonEventAndTriggerVibration(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) - } - } else { - log("NOT sending play/pause, down: $isDownPressed, up: $isUpPressed") - } - }, - ).forEach { (key, runnable) -> - XposedHelpers.setAdditionalInstanceField(param.thisObject, key, runnable) + MediaEvent.entries.forEach { event -> + val runnable = Runnable { event.handle() } + XposedHelpers.setAdditionalInstanceField(param.thisObject, event.field, runnable) } } } - private fun needHook(keyCode: Int, event: KeyEvent): Boolean { + private fun needHook(event: KeyEvent): Boolean { + val keyCode = event.keyCode log("========") - log("current audio manager mode: ${audioManager.mode}, required: ${AudioManager.MODE_NORMAL}") + log("audioManager mode: ${audioManager.mode}, required: ${AudioManager.MODE_NORMAL}") log("keyCode: ${keyCode}, required: ${KeyEvent.KEYCODE_VOLUME_DOWN} or ${KeyEvent.KEYCODE_VOLUME_UP}") log("displayInteractive: ${isDisplayInteractive()}, required: false") log("isDownPressed: $isDownPressed") log("isUpPressed: $isUpPressed") log("hasActiveMediaController: ${hasActiveMediaController()}, required: true") + log("event.repeatCount: ${event.repeatCount}, required: 0") // TODO val needHook = (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) && event.flags and KeyEvent.FLAG_FROM_SYSTEM != 0 @@ -110,7 +94,7 @@ object VolumeKeyControlModuleHandlers { if (!powerManager.isInteractive) { return false } - log("displays count: ${displayManager.displays.size}") + log("Displays count: ${displayManager.displays.size}") // TODO if (displayManager.displays.size > 1) { return true @@ -118,30 +102,35 @@ object VolumeKeyControlModuleHandlers { val display = displayManager.displays[0] val disabledStates = listOf(Display.STATE_OFF, Display.STATE_DOZE, Display.STATE_DOZE_SUSPEND) - log("checking display: ${display.displayId}, state: ${display.state}, required: $disabledStates") + log("Checking display: ${display.displayId}, state: ${display.state}, required: $disabledStates") return !disabledStates.contains(display.state) } - private fun initManagers(param: MethodHookParam) { - val context = param.thisObject.getContext() - with(context) { + private fun MethodHookParam.initManagers() { + with(getContext()) { audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager? ?: throw NullPointerException("Unable to obtain audio service") powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager? ?: throw NullPointerException("Unable to obtain power service") displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager? ?: throw NullPointerException("Unable to obtain display service") - vibrator = context.getVibrator() + vibrator = getVibrator() } + } + private fun MethodHookParam.initControllers() { val mediaSessionHelperClass = XposedHelpers.findClass( CLASS_MEDIA_SESSION_LEGACY_HELPER, - param.thisObject.javaClass.classLoader + thisObject.javaClass.classLoader + ) + val helper = XposedHelpers.callStaticMethod( + mediaSessionHelperClass, + "getHelper", + getContext() ) - val helper = XposedHelpers.callStaticMethod(mediaSessionHelperClass, "getHelper", context) val mSessionManager = XposedHelpers.getObjectField(helper, "mSessionManager") val componentNameClass = - XposedHelpers.findClass(CLASS_COMPONENT_NAME, param.thisObject.javaClass.classLoader) + XposedHelpers.findClass(CLASS_COMPONENT_NAME, thisObject.javaClass.classLoader) @Suppress("UNCHECKED_CAST") mediaControllers = XposedHelpers.callMethod( @@ -152,59 +141,40 @@ object VolumeKeyControlModuleHandlers { ) as List? } - private fun doHook(keyCode: Int, event: KeyEvent, param: MethodHookParam) { - val isSwap = SharedPreferencesUtil.prefs().isSwapButtons() - log("isSwap: $isSwap") - val isDown = when (keyCode) { - KeyEvent.KEYCODE_VOLUME_DOWN -> !isSwap - KeyEvent.KEYCODE_VOLUME_UP -> isSwap - else -> throw IllegalStateException("Unknown key code: $keyCode") - } - when (event.action) { - KeyEvent.ACTION_DOWN -> handleDownAction(isDown, param) - KeyEvent.ACTION_UP -> handleUpAction(isDown, keyCode, param) + private fun MethodHookParam.doHook(event: KeyEvent) { + val action = Action.entries.find { it.actionCode == event.action }!! + val keyHelper = KeyHelper(event.keyCode) + keyHelper.updateFlags(action) + when (action) { + Action.PRESS -> handleDownAction(keyHelper) + Action.UNPRESS -> handleUpAction(keyHelper) } - param.setResult(0) + setResult(0) } - private fun handleDownAction(isDown: Boolean, param: MethodHookParam) { - if (isDown) { - isDownPressed = true - } else { - isUpPressed = true - } - log("down action received, down: $isDownPressed, up: $isUpPressed") + private fun MethodHookParam.handleDownAction(keyHelper: KeyHelper) { + log("Volume down action received, down: $isDownPressed, up: $isUpPressed") isLongPress = false if (isUpPressed && isDownPressed) { - log("aborting delayed skip") - handleVolumeSkipPressAbort(param.thisObject) + log("Aborting delayed skip") + handleVolumeSkipPressAbort() } else { // only one button pressed if (getMediaController().isMusicActive()) { - log("music is active, creating delayed skip") - handleVolumeSkipPress(param.thisObject, isDown) + log("Music is active, creating delayed skip") + handleVolumeSkipPress(keyHelper) } - log("creating delayed play pause") - handleVolumePlayPausePress(param.thisObject) + log("Creating delayed play pause") + handleVolumePlayPausePress() } } - private fun handleUpAction(isDown: Boolean, keyCode: Int, param: MethodHookParam) { - if (isDown) { - isDownPressed = false - } else { - isUpPressed = false - } - log("up action received, down: $isDownPressed, up: $isUpPressed") - handleVolumeAllPressAbort(param.thisObject) + private fun MethodHookParam.handleUpAction(keyHelper: KeyHelper) { + log("Volume up action received, down: $isDownPressed, up: $isUpPressed") + handleVolumeAllPressAbort() if (!isLongPress && getMediaController().isMusicActive()) { - log("adjusting music volume") - val direction = when (keyCode) { - KeyEvent.KEYCODE_VOLUME_UP -> AudioManager.ADJUST_RAISE - KeyEvent.KEYCODE_VOLUME_DOWN -> AudioManager.ADJUST_LOWER - else -> return - } - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, direction, 0) + log("Adjusting music volume") + keyHelper.adjustStreamVolume(audioManager) } } @@ -219,7 +189,7 @@ object VolumeKeyControlModuleHandlers { private fun getFirstMediaController(): MediaController? { return mediaControllers?.firstOrNull() - ?.also { log("first media controller: ${it.packageName}") } + ?.also { log("First media controller: ${it.packageName}") } } private fun getMediaController(): MediaController? { @@ -232,7 +202,7 @@ object VolumeKeyControlModuleHandlers { SharedPreferencesUtil.AppFilterType.WhiteList -> it.packageName in apps SharedPreferencesUtil.AppFilterType.BlackList -> it.packageName !in apps } - }?.also { log("chosen media controller: ${it.packageName}") } + }?.also { log("Chosen media controller: ${it.packageName}") } } private fun MediaController?.isMusicActive() = when (this?.playbackState?.state) { @@ -244,71 +214,135 @@ object VolumeKeyControlModuleHandlers { else -> false } - private fun sendMediaButtonEventAndTriggerVibration(keyCode: Int) { + private fun sendMediaButtonEventAndTriggerVibration(mediaEvent: MediaEvent) { getMediaController()?.also { controller -> val controls = controller.transportControls - when (keyCode) { - KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> { + when (mediaEvent) { + MediaEvent.PlayPause -> { if (controller.isMusicActive()) controls.pause() else controls.play() } - KeyEvent.KEYCODE_MEDIA_NEXT -> controls.skipToNext() - KeyEvent.KEYCODE_MEDIA_PREVIOUS -> controls.skipToPrevious() - else -> return + MediaEvent.Next -> controls.skipToNext() + MediaEvent.Prev -> controls.skipToPrevious() } vibrator.triggerVibration() } } - private fun handleVolumePlayPausePress(instance: Any) { - val handler = instance.getHandler() + private fun MethodHookParam.handleVolumePlayPausePress() { + val handler = getHandler() handler.postDelayed( - getRunnable(instance, VOLUME_BOTH_LONG_PRESS), + getRunnable(MediaEvent.PlayPause.field), SharedPreferencesUtil.prefs().getLongPressDuration().toLong() ) } - private fun handleVolumeSkipPress(instance: Any, isDown: Boolean) { - val handler = instance.getHandler() + private fun MethodHookParam.handleVolumeSkipPress(keyHelper: KeyHelper) { + val handler = getHandler() handler.postDelayed( - getRunnable(instance, if (isDown) VOLUME_DOWN_LONG_PRESS else VOLUME_UP_LONG_PRESS), + getRunnable(keyHelper.mediaEvent.field), SharedPreferencesUtil.prefs().getLongPressDuration().toLong() ) } - private fun handleVolumeSkipPressAbort(instance: Any) { - log("aborting skip") - val handler = instance.getHandler() - listOf(VOLUME_UP_LONG_PRESS, VOLUME_DOWN_LONG_PRESS).forEach { - handler.removeCallbacks(getRunnable(instance, it)) + private fun MethodHookParam.handleVolumeSkipPressAbort() { + log("Aborting skip") + val handler = getHandler() + listOf(MediaEvent.Prev, MediaEvent.Next).forEach { event -> + handler.removeCallbacks(getRunnable(event.field)) } } - private fun handleVolumePlayPausePressAbort(instance: Any) { - log("aborting play/pause") - val handler = instance.getHandler() - handler.removeCallbacks(getRunnable(instance, VOLUME_BOTH_LONG_PRESS)) + private fun MethodHookParam.handleVolumePlayPausePressAbort() { + log("Aborting play/pause") + val handler = getHandler() + handler.removeCallbacks(getRunnable(MediaEvent.PlayPause.field)) } - private fun handleVolumeAllPressAbort(phoneWindowManager: Any) { - log("aborting all") - handleVolumePlayPausePressAbort(phoneWindowManager) - handleVolumeSkipPressAbort(phoneWindowManager) + private fun MethodHookParam.handleVolumeAllPressAbort() { + log("Aborting all") + handleVolumePlayPausePressAbort() + handleVolumeSkipPressAbort() } - private fun getRunnable(instance: Any, fieldName: String): Runnable { - return XposedHelpers.getAdditionalInstanceField(instance, fieldName) as Runnable + private fun MethodHookParam.getRunnable(fieldName: String): Runnable { + return XposedHelpers.getAdditionalInstanceField(thisObject, fieldName) as Runnable } - private fun Any.getContext(): Context { - return getObjectField("mContext") as Context + private fun MethodHookParam.getContext() = getObjectField("mContext") as Context + + private fun MethodHookParam.getHandler() = getObjectField("mHandler") as Handler + + private fun MethodHookParam.getObjectField(fieldName: String) = + XposedHelpers.getObjectField(thisObject, fieldName) + + sealed class MediaEvent(val field: String) { + + open fun handle() { + log("Sending ${this::class.simpleName}") + isLongPress = true + sendMediaButtonEventAndTriggerVibration(this) + } + + object PlayPause : MediaEvent("mVolumeBothLongPress") { + override fun handle() { + if (isUpPressed && isDownPressed) { + super.handle() + } else { + log("Not sending ${this::class.simpleName}, down: $isDownPressed, up: $isUpPressed") + } + } + } + + object Next : MediaEvent("mVolumeUpLongPress") + + object Prev : MediaEvent("mVolumeDownLongPress") + + companion object { + val entries = listOf(PlayPause, Next, Prev) + } } - private fun Any.getHandler(): Handler { - return getObjectField("mHandler") as Handler + + private class KeyHelper(keyCode: Int) { + private val origKey = Key.entries.find { it.keyCode == keyCode }!! + private val isSwap = SharedPreferencesUtil.prefs().isSwapButtons() + private val key = if (isSwap) { + when (origKey) { + Key.UP -> Key.DOWN + Key.DOWN -> Key.UP + } + } else { + origKey + } + val mediaEvent = key.mediaEvent + + fun updateFlags(action: Action) { + val pressed = action == Action.PRESS + when (key) { + Key.UP -> isUpPressed = pressed + Key.DOWN -> isDownPressed = pressed + } + } + + fun adjustStreamVolume(audioManager: AudioManager) { + val direction = when (origKey) { + Key.UP -> AudioManager.ADJUST_RAISE + Key.DOWN -> AudioManager.ADJUST_LOWER + } + audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, direction, 0) + } + + private companion object { + private enum class Key(val keyCode: Int, val mediaEvent: MediaEvent) { + UP(KeyEvent.KEYCODE_VOLUME_UP, MediaEvent.Prev), + DOWN(KeyEvent.KEYCODE_VOLUME_DOWN, MediaEvent.Next), + } + } } - private fun Any.getObjectField(fieldName: String): Any { - return XposedHelpers.getObjectField(this, fieldName) + private enum class Action(val actionCode: Int) { + PRESS(KeyEvent.ACTION_DOWN), + UNPRESS(KeyEvent.ACTION_UP); } } From 9aa270567f9f8ba321736751f2d4bfa7272a4a50 Mon Sep 17 00:00:00 2001 From: Hepolise Date: Sat, 19 Jul 2025 14:19:00 +0300 Subject: [PATCH 2/3] Refactor: Convert AppFilterType to enum and simplify volume key handling - Changed AppFilterType from sealed class to enum for simpler implementation - Updated all references to use uppercase enum constants (DISABLED, WHITE_LIST, BLACK_LIST) - Simplified volume key control module: - Moved initialization logic to Context extension functions - Renamed action types to PRESSED/UNPRESSED for clarity - Consolidated delay/abort logic into dedicated methods - Improved media event handling structure - Fixed minor IDE warnings and improved code consistency --- .../module/VolumeKeyControlModuleHandlers.kt | 109 ++++++++---------- .../ui/component/AppFilterSetting.kt | 4 +- .../ui/navigation/AppNavigation.kt | 2 +- .../ui/screen/AppFilterScreen.kt | 6 +- .../util/SharedPreferencesUtil.kt | 40 +++---- 5 files changed, 71 insertions(+), 90 deletions(-) diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/module/VolumeKeyControlModuleHandlers.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/module/VolumeKeyControlModuleHandlers.kt index 74586b9..1ba8979 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/module/VolumeKeyControlModuleHandlers.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/module/VolumeKeyControlModuleHandlers.kt @@ -46,8 +46,10 @@ object VolumeKeyControlModuleHandlers { with(param) { val event = args[0] as KeyEvent try { - initManagers() - initControllers() + with(getContext()) { + initManagers() + initControllers() + } } catch (t: Throwable) { log("init failed") t.printStackTrace() @@ -78,7 +80,6 @@ object VolumeKeyControlModuleHandlers { log("isDownPressed: $isDownPressed") log("isUpPressed: $isUpPressed") log("hasActiveMediaController: ${hasActiveMediaController()}, required: true") - log("event.repeatCount: ${event.repeatCount}, required: 0") // TODO val needHook = (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) && event.flags and KeyEvent.FLAG_FROM_SYSTEM != 0 @@ -106,31 +107,31 @@ object VolumeKeyControlModuleHandlers { return !disabledStates.contains(display.state) } - private fun MethodHookParam.initManagers() { - with(getContext()) { - audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager? - ?: throw NullPointerException("Unable to obtain audio service") - powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager? - ?: throw NullPointerException("Unable to obtain power service") - displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager? - ?: throw NullPointerException("Unable to obtain display service") - vibrator = getVibrator() - } + private fun Context.initManagers() { + audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager? + ?: throw NullPointerException("Unable to obtain audio service") + powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager? + ?: throw NullPointerException("Unable to obtain power service") + displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager? + ?: throw NullPointerException("Unable to obtain display service") + vibrator = getVibrator() } - private fun MethodHookParam.initControllers() { + private fun Context.initControllers() { + val context = this + val classLoader = javaClass.classLoader val mediaSessionHelperClass = XposedHelpers.findClass( CLASS_MEDIA_SESSION_LEGACY_HELPER, - thisObject.javaClass.classLoader + classLoader ) val helper = XposedHelpers.callStaticMethod( mediaSessionHelperClass, "getHelper", - getContext() + context ) val mSessionManager = XposedHelpers.getObjectField(helper, "mSessionManager") val componentNameClass = - XposedHelpers.findClass(CLASS_COMPONENT_NAME, thisObject.javaClass.classLoader) + XposedHelpers.findClass(CLASS_COMPONENT_NAME, classLoader) @Suppress("UNCHECKED_CAST") mediaControllers = XposedHelpers.callMethod( @@ -146,32 +147,32 @@ object VolumeKeyControlModuleHandlers { val keyHelper = KeyHelper(event.keyCode) keyHelper.updateFlags(action) when (action) { - Action.PRESS -> handleDownAction(keyHelper) - Action.UNPRESS -> handleUpAction(keyHelper) + Action.PRESSED -> handlePressedAction(keyHelper) + Action.UNPRESSED -> handleUnpressedAction(keyHelper) } setResult(0) } - private fun MethodHookParam.handleDownAction(keyHelper: KeyHelper) { - log("Volume down action received, down: $isDownPressed, up: $isUpPressed") + private fun MethodHookParam.handlePressedAction(keyHelper: KeyHelper) { + log("Volume pressed action received, down: $isDownPressed, up: $isUpPressed") isLongPress = false if (isUpPressed && isDownPressed) { log("Aborting delayed skip") - handleVolumeSkipPressAbort() + abortSkip() } else { // only one button pressed if (getMediaController().isMusicActive()) { log("Music is active, creating delayed skip") - handleVolumeSkipPress(keyHelper) + delay(keyHelper.mediaEvent) } log("Creating delayed play pause") - handleVolumePlayPausePress() + delay(MediaEvent.PlayPause) } } - private fun MethodHookParam.handleUpAction(keyHelper: KeyHelper) { - log("Volume up action received, down: $isDownPressed, up: $isUpPressed") - handleVolumeAllPressAbort() + private fun MethodHookParam.handleUnpressedAction(keyHelper: KeyHelper) { + log("Volume unpressed action received, down: $isDownPressed, up: $isUpPressed") + abortAll() if (!isLongPress && getMediaController().isMusicActive()) { log("Adjusting music volume") keyHelper.adjustStreamVolume(audioManager) @@ -198,9 +199,9 @@ object VolumeKeyControlModuleHandlers { val filterType = prefs.getAppFilterType() val apps = prefs.getApps(filterType) when (filterType) { - SharedPreferencesUtil.AppFilterType.Disabled -> true - SharedPreferencesUtil.AppFilterType.WhiteList -> it.packageName in apps - SharedPreferencesUtil.AppFilterType.BlackList -> it.packageName !in apps + SharedPreferencesUtil.AppFilterType.DISABLED -> true + SharedPreferencesUtil.AppFilterType.WHITE_LIST -> it.packageName in apps + SharedPreferencesUtil.AppFilterType.BLACK_LIST -> it.packageName !in apps } }?.also { log("Chosen media controller: ${it.packageName}") } } @@ -229,46 +230,34 @@ object VolumeKeyControlModuleHandlers { } } - private fun MethodHookParam.handleVolumePlayPausePress() { + private fun MethodHookParam.delay(event: MediaEvent) { val handler = getHandler() handler.postDelayed( - getRunnable(MediaEvent.PlayPause.field), + getRunnable(event.field), SharedPreferencesUtil.prefs().getLongPressDuration().toLong() ) } - private fun MethodHookParam.handleVolumeSkipPress(keyHelper: KeyHelper) { - val handler = getHandler() - handler.postDelayed( - getRunnable(keyHelper.mediaEvent.field), - SharedPreferencesUtil.prefs().getLongPressDuration().toLong() - ) - } - - private fun MethodHookParam.handleVolumeSkipPressAbort() { + private fun MethodHookParam.abortSkip() { log("Aborting skip") - val handler = getHandler() - listOf(MediaEvent.Prev, MediaEvent.Next).forEach { event -> - handler.removeCallbacks(getRunnable(event.field)) - } - } - - private fun MethodHookParam.handleVolumePlayPausePressAbort() { - log("Aborting play/pause") - val handler = getHandler() - handler.removeCallbacks(getRunnable(MediaEvent.PlayPause.field)) + abortEvents(MediaEvent.Prev, MediaEvent.Next) } - private fun MethodHookParam.handleVolumeAllPressAbort() { + private fun MethodHookParam.abortAll() { log("Aborting all") - handleVolumePlayPausePressAbort() - handleVolumeSkipPressAbort() + abortEvents(MediaEvent.Prev, MediaEvent.Next, MediaEvent.PlayPause) } - private fun MethodHookParam.getRunnable(fieldName: String): Runnable { - return XposedHelpers.getAdditionalInstanceField(thisObject, fieldName) as Runnable + private fun MethodHookParam.abortEvents(vararg events: MediaEvent) { + val handler = getHandler() + events.forEach { event -> + handler.removeCallbacks(getRunnable(event.field)) + } } + private fun MethodHookParam.getRunnable(fieldName: String) = + XposedHelpers.getAdditionalInstanceField(thisObject, fieldName) as Runnable + private fun MethodHookParam.getContext() = getObjectField("mContext") as Context private fun MethodHookParam.getHandler() = getObjectField("mHandler") as Handler @@ -276,7 +265,7 @@ object VolumeKeyControlModuleHandlers { private fun MethodHookParam.getObjectField(fieldName: String) = XposedHelpers.getObjectField(thisObject, fieldName) - sealed class MediaEvent(val field: String) { + private sealed class MediaEvent(val field: String) { open fun handle() { log("Sending ${this::class.simpleName}") @@ -318,7 +307,7 @@ object VolumeKeyControlModuleHandlers { val mediaEvent = key.mediaEvent fun updateFlags(action: Action) { - val pressed = action == Action.PRESS + val pressed = action == Action.PRESSED when (key) { Key.UP -> isUpPressed = pressed Key.DOWN -> isDownPressed = pressed @@ -342,7 +331,7 @@ object VolumeKeyControlModuleHandlers { } private enum class Action(val actionCode: Int) { - PRESS(KeyEvent.ACTION_DOWN), - UNPRESS(KeyEvent.ACTION_UP); + PRESSED(KeyEvent.ACTION_DOWN), + UNPRESSED(KeyEvent.ACTION_UP); } } diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/component/AppFilterSetting.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/component/AppFilterSetting.kt index b427fe4..7da11ae 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/component/AppFilterSetting.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/component/AppFilterSetting.kt @@ -60,7 +60,7 @@ fun AppFilterSetting( ExposedDropdownMenu( expanded = expanded, onDismissRequest = { expanded = false }) { - AppFilterType.values.forEach { type -> + AppFilterType.entries.forEach { type -> DropdownMenuItem( text = { Text(stringResource(type.resourceId)) }, onClick = { @@ -77,7 +77,7 @@ fun AppFilterSetting( Box { AnimatedVisibility( - visible = value == AppFilterType.WhiteList || value == AppFilterType.BlackList, + visible = value == AppFilterType.WHITE_LIST || value == AppFilterType.BLACK_LIST, enter = fadeIn() + expandVertically(expandFrom = Alignment.Top), exit = fadeOut() + shrinkVertically(shrinkTowards = Alignment.Top), modifier = Modifier.fillMaxWidth() diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/navigation/AppNavigation.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/navigation/AppNavigation.kt index 56cdfb8..19def5a 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/navigation/AppNavigation.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/navigation/AppNavigation.kt @@ -55,7 +55,7 @@ fun AppNavigation(sharedPreferences: SharedPreferences, vibrator: Vibrator) { ) { backStackEntry -> val filterType = SharedPreferencesUtil.AppFilterType.fromKey( backStackEntry.arguments?.getString("filterType") - ?: SharedPreferencesUtil.AppFilterType.Disabled.key + ?: SharedPreferencesUtil.AppFilterType.DISABLED.key ) AppFilterScreen( filterType = filterType, diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/screen/AppFilterScreen.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/screen/AppFilterScreen.kt index 02f7541..8e20251 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/screen/AppFilterScreen.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/ui/screen/AppFilterScreen.kt @@ -193,8 +193,8 @@ fun AppFilterScreen( sharedPreferences.edit { putStringSet( when (filterType) { - SharedPreferencesUtil.AppFilterType.BlackList -> BLACK_LIST_APPS - SharedPreferencesUtil.AppFilterType.WhiteList -> WHITE_LIST_APPS + SharedPreferencesUtil.AppFilterType.BLACK_LIST -> BLACK_LIST_APPS + SharedPreferencesUtil.AppFilterType.WHITE_LIST -> WHITE_LIST_APPS else -> throw IllegalStateException("Invalid filter type: $filterType") }, selectedApps.toSet() @@ -686,7 +686,7 @@ private class AppListComparator( @Composable fun PreviewAppFilterScreen() { AppFilterScreen( - filterType = SharedPreferencesUtil.AppFilterType.WhiteList, + filterType = SharedPreferencesUtil.AppFilterType.WHITE_LIST, sharedPreferences = LocalContext.current.getSharedPreferences( SETTINGS_PREFS_NAME, Context.MODE_PRIVATE, 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 9d2235f..112ae3b 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/util/SharedPreferencesUtil.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/util/SharedPreferencesUtil.kt @@ -25,7 +25,7 @@ object SharedPreferencesUtil { const val VIBRATION_AMPLITUDE_DEFAULT_VALUE = 128 val LONG_PRESS_DURATION_DEFAULT_VALUE = ViewConfiguration.getLongPressTimeout() const val IS_SWAP_BUTTONS_DEFAULT_VALUE = false - val APP_FILTER_TYPE_DEFAULT_VALUE = AppFilterType.Disabled.key + val APP_FILTER_TYPE_DEFAULT_VALUE = AppFilterType.DISABLED.key fun SharedPreferences?.getVibrationType(): VibrationType { val defaultValue = EFFECT_DEFAULT_VALUE @@ -59,9 +59,12 @@ object SharedPreferencesUtil { fun SharedPreferences?.getApps(appFilterType: AppFilterType = getAppFilterType()): Set { return when (appFilterType) { - AppFilterType.Disabled -> emptySet() - AppFilterType.WhiteList -> this?.getStringSet(WHITE_LIST_APPS, emptySet()) ?: emptySet() - AppFilterType.BlackList -> this?.getStringSet(BLACK_LIST_APPS, emptySet()) ?: emptySet() + AppFilterType.DISABLED -> emptySet() + AppFilterType.WHITE_LIST -> this?.getStringSet(WHITE_LIST_APPS, emptySet()) + ?: emptySet() + + AppFilterType.BLACK_LIST -> this?.getStringSet(BLACK_LIST_APPS, emptySet()) + ?: emptySet() } } @@ -71,28 +74,17 @@ object SharedPreferencesUtil { return if (pref.file.canRead()) pref else null } - sealed class AppFilterType(val value: Int, val key: String, val resourceId: Int) { - data object Disabled : AppFilterType(0, "disabled", R.string.app_filter_disabled) - data object WhiteList : AppFilterType(1, "whitelist", R.string.app_filter_white_list) - data object BlackList : AppFilterType(2, "blacklist", R.string.app_filter_black_list) + enum class AppFilterType( + val value: Int, + val key: String, + val resourceId: Int + ) { + DISABLED(0, "disabled", R.string.app_filter_disabled), + WHITE_LIST(1, "whitelist", R.string.app_filter_white_list), + BLACK_LIST(2, "blacklist", R.string.app_filter_black_list); companion object { - val values: List by lazy { - mutableListOf().apply { - add(Disabled) - add(WhiteList) - add(BlackList) - } - } - - fun fromKey(key: String): AppFilterType { - return when (key) { - Disabled.key -> Disabled - WhiteList.key -> WhiteList - BlackList.key -> BlackList - else -> Disabled - } - } + fun fromKey(key: String) = entries.find { it.key == key } ?: DISABLED } } From ea25de0044a40ca9fe361eb97521873f8049131a Mon Sep 17 00:00:00 2001 From: Nikita Date: Sat, 19 Jul 2025 14:41:44 +0300 Subject: [PATCH 3/3] Fix: swapped buttons --- .../module/VolumeKeyControlModuleHandlers.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/module/VolumeKeyControlModuleHandlers.kt b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/module/VolumeKeyControlModuleHandlers.kt index 1ba8979..2ec73d3 100644 --- a/app/src/main/java/ru/hepolise/volumekeytrackcontrol/module/VolumeKeyControlModuleHandlers.kt +++ b/app/src/main/java/ru/hepolise/volumekeytrackcontrol/module/VolumeKeyControlModuleHandlers.kt @@ -324,8 +324,8 @@ object VolumeKeyControlModuleHandlers { private companion object { private enum class Key(val keyCode: Int, val mediaEvent: MediaEvent) { - UP(KeyEvent.KEYCODE_VOLUME_UP, MediaEvent.Prev), - DOWN(KeyEvent.KEYCODE_VOLUME_DOWN, MediaEvent.Next), + UP(KeyEvent.KEYCODE_VOLUME_UP, MediaEvent.Next), + DOWN(KeyEvent.KEYCODE_VOLUME_DOWN, MediaEvent.Prev), } } }