From 389a17dd62850a9c88d81727cf7e2d6df3afe6b4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 00:27:12 +0000 Subject: [PATCH 1/5] #2163 feat: add ringer mode constraints Add three new constraints to check the current ringer mode: Ring, Vibrate, and Silent. These appear in the Phone category of the constraint picker. - Add RINGER_MODE_NORMAL, RINGER_MODE_VIBRATE, RINGER_MODE_SILENT to ConstraintId - Add corresponding ConstraintData sealed objects and entity mapping - Add ringerModeFlow to VolumeAdapter, implemented via BroadcastReceiver for AudioManager.RINGER_MODE_CHANGED_ACTION in AndroidVolumeAdapter - Wire VolumeAdapter into LazyConstraintSnapshot and DetectConstraintsUseCase - Add RINGER_MODE to ConstraintDependency - Add UI strings, icons, and titles - Update TestConstraintSnapshot with ringerMode field --- CHANGELOG.md | 1 + .../keymapper/base/constraints/Constraint.kt | 34 +++++++++++++++++++ .../base/constraints/ConstraintDependency.kt | 1 + .../base/constraints/ConstraintId.kt | 4 +++ .../base/constraints/ConstraintSnapshot.kt | 8 +++++ .../base/constraints/ConstraintUiHelper.kt | 3 ++ .../base/constraints/ConstraintUtils.kt | 12 +++++++ .../constraints/DetectConstraintsUseCase.kt | 4 +++ base/src/main/res/values/strings.xml | 3 ++ .../base/utils/TestConstraintSnapshot.kt | 5 +++ .../data/entities/ConstraintEntity.kt | 4 +++ .../system/volume/AndroidVolumeAdapter.kt | 31 +++++++++++++++++ .../keymapper/system/volume/VolumeAdapter.kt | 2 ++ 13 files changed, 112 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4ca051484..b3cc9aa9b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ## Added +- #2163 Add ringer mode constraints (Ring, Vibrate, Silent). - #2140 Add monochrome app icon layer for themed icon support on Android 13+. ## Fixed diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt index a2aa471fbd..e8cf0a8361 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt @@ -189,6 +189,21 @@ sealed class ConstraintData { override val id: ConstraintId = ConstraintId.PHONE_RINGING } + @Serializable + data object RingerModeNormal : ConstraintData() { + override val id: ConstraintId = ConstraintId.RINGER_MODE_NORMAL + } + + @Serializable + data object RingerModeVibrate : ConstraintData() { + override val id: ConstraintId = ConstraintId.RINGER_MODE_VIBRATE + } + + @Serializable + data object RingerModeSilent : ConstraintData() { + override val id: ConstraintId = ConstraintId.RINGER_MODE_SILENT + } + @Serializable data object Charging : ConstraintData() { override val id: ConstraintId = ConstraintId.CHARGING @@ -382,6 +397,10 @@ object ConstraintEntityMapper { ConstraintEntity.IN_PHONE_CALL -> ConstraintData.InPhoneCall ConstraintEntity.NOT_IN_PHONE_CALL -> ConstraintData.NotInPhoneCall + ConstraintEntity.RINGER_MODE_NORMAL -> ConstraintData.RingerModeNormal + ConstraintEntity.RINGER_MODE_VIBRATE -> ConstraintData.RingerModeVibrate + ConstraintEntity.RINGER_MODE_SILENT -> ConstraintData.RingerModeSilent + ConstraintEntity.CHARGING -> ConstraintData.Charging ConstraintEntity.DISCHARGING -> ConstraintData.Discharging @@ -673,6 +692,21 @@ object ConstraintEntityMapper { ConstraintEntity.PHONE_RINGING, ) + is ConstraintData.RingerModeNormal -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.RINGER_MODE_NORMAL, + ) + + is ConstraintData.RingerModeVibrate -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.RINGER_MODE_VIBRATE, + ) + + is ConstraintData.RingerModeSilent -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.RINGER_MODE_SILENT, + ) + is ConstraintData.Charging -> ConstraintEntity( uid = constraint.uid, ConstraintEntity.CHARGING, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintDependency.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintDependency.kt index 4bbbcf1721..afab1a49ca 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintDependency.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintDependency.kt @@ -16,6 +16,7 @@ enum class ConstraintDependency { DEVICE_LOCKED_STATE, LOCK_SCREEN_SHOWING, PHONE_STATE, + RINGER_MODE, CHARGING_STATE, HINGE_STATE, } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintId.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintId.kt index 06463c6fd7..1118a9d186 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintId.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintId.kt @@ -54,6 +54,10 @@ enum class ConstraintId { NOT_IN_PHONE_CALL, PHONE_RINGING, + RINGER_MODE_NORMAL, + RINGER_MODE_VIBRATE, + RINGER_MODE_SILENT, + CHARGING, DISCHARGING, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt index 00ac3781ee..9ee0521dcb 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt @@ -21,6 +21,8 @@ import io.github.sds100.keymapper.system.network.NetworkAdapter import io.github.sds100.keymapper.system.phone.CallState import io.github.sds100.keymapper.system.phone.PhoneAdapter import io.github.sds100.keymapper.system.power.PowerAdapter +import io.github.sds100.keymapper.system.volume.RingerMode +import io.github.sds100.keymapper.system.volume.VolumeAdapter import java.time.LocalTime /** @@ -38,6 +40,7 @@ class LazyConstraintSnapshot( phoneAdapter: PhoneAdapter, powerAdapter: PowerAdapter, private val foldableAdapter: FoldableAdapter, + volumeAdapter: VolumeAdapter, ) : ConstraintSnapshot { private val appInForeground: String? by lazy { accessibilityService.rootNode?.packageName } private val connectedBluetoothDevices: Set by lazy { @@ -66,6 +69,7 @@ class LazyConstraintSnapshot( } private val callState: CallState by lazy { phoneAdapter.getCallState() } private val isCharging: Boolean by lazy { powerAdapter.isCharging.value } + private val ringerMode: RingerMode by lazy { volumeAdapter.ringerMode } private val isLocked: Boolean by lazy { lockScreenAdapter.isLocked() @@ -165,6 +169,10 @@ class LazyConstraintSnapshot( callState == CallState.RINGING || audioVolumeStreams.contains(AudioManager.STREAM_RING) + is ConstraintData.RingerModeNormal -> ringerMode == RingerMode.NORMAL + is ConstraintData.RingerModeVibrate -> ringerMode == RingerMode.VIBRATE + is ConstraintData.RingerModeSilent -> ringerMode == RingerMode.SILENT + is ConstraintData.Charging -> isCharging is ConstraintData.Discharging -> !isCharging diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt index 94b02aebc9..e9bb6064f4 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt @@ -163,6 +163,9 @@ class ConstraintUiHelper( is ConstraintData.InPhoneCall -> getString(R.string.constraint_in_phone_call) is ConstraintData.NotInPhoneCall -> getString(R.string.constraint_not_in_phone_call) is ConstraintData.PhoneRinging -> getString(R.string.constraint_phone_ringing) + is ConstraintData.RingerModeNormal -> getString(R.string.constraint_ringer_mode_normal) + is ConstraintData.RingerModeVibrate -> getString(R.string.constraint_ringer_mode_vibrate) + is ConstraintData.RingerModeSilent -> getString(R.string.constraint_ringer_mode_silent) is ConstraintData.Charging -> getString(R.string.constraint_charging) is ConstraintData.Discharging -> getString(R.string.constraint_discharging) is ConstraintData.HingeClosed -> getString(R.string.constraint_hinge_closed_description) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt index 80cd56c1c7..7bb26b672f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt @@ -15,6 +15,9 @@ import androidx.compose.material.icons.outlined.KeyboardHide import androidx.compose.material.icons.outlined.Lock import androidx.compose.material.icons.outlined.LockOpen import androidx.compose.material.icons.outlined.MobileOff +import androidx.compose.material.icons.outlined.Notifications +import androidx.compose.material.icons.outlined.NotificationsOff +import androidx.compose.material.icons.outlined.Vibration import androidx.compose.material.icons.outlined.PlayArrow import androidx.compose.material.icons.outlined.RingVolume import androidx.compose.material.icons.outlined.ScreenLockPortrait @@ -101,6 +104,9 @@ object ConstraintUtils { ConstraintId.IN_PHONE_CALL, ConstraintId.NOT_IN_PHONE_CALL, ConstraintId.PHONE_RINGING, + ConstraintId.RINGER_MODE_NORMAL, + ConstraintId.RINGER_MODE_VIBRATE, + ConstraintId.RINGER_MODE_SILENT, -> ConstraintCategory.PHONE ConstraintId.CHARGING, @@ -180,6 +186,9 @@ object ConstraintUtils { ConstraintId.IN_PHONE_CALL -> ComposeIconInfo.Vector(Icons.Outlined.Call) ConstraintId.NOT_IN_PHONE_CALL -> ComposeIconInfo.Vector(Icons.Outlined.CallEnd) ConstraintId.PHONE_RINGING -> ComposeIconInfo.Vector(Icons.Outlined.RingVolume) + ConstraintId.RINGER_MODE_NORMAL -> ComposeIconInfo.Vector(Icons.Outlined.Notifications) + ConstraintId.RINGER_MODE_VIBRATE -> ComposeIconInfo.Vector(Icons.Outlined.Vibration) + ConstraintId.RINGER_MODE_SILENT -> ComposeIconInfo.Vector(Icons.Outlined.NotificationsOff) ConstraintId.CHARGING -> ComposeIconInfo.Vector(Icons.Outlined.BatteryChargingFull) ConstraintId.DISCHARGING -> ComposeIconInfo.Vector(Icons.Outlined.Battery2Bar) @@ -236,6 +245,9 @@ object ConstraintUtils { ConstraintId.IN_PHONE_CALL -> R.string.constraint_in_phone_call ConstraintId.NOT_IN_PHONE_CALL -> R.string.constraint_not_in_phone_call ConstraintId.PHONE_RINGING -> R.string.constraint_phone_ringing + ConstraintId.RINGER_MODE_NORMAL -> R.string.constraint_ringer_mode_normal + ConstraintId.RINGER_MODE_VIBRATE -> R.string.constraint_ringer_mode_vibrate + ConstraintId.RINGER_MODE_SILENT -> R.string.constraint_ringer_mode_silent ConstraintId.CHARGING -> R.string.constraint_charging ConstraintId.DISCHARGING -> R.string.constraint_discharging ConstraintId.HINGE_CLOSED -> R.string.constraint_hinge_closed diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt index 8bf75d6f24..23da542247 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt @@ -16,6 +16,7 @@ import io.github.sds100.keymapper.system.media.MediaAdapter import io.github.sds100.keymapper.system.network.NetworkAdapter import io.github.sds100.keymapper.system.phone.PhoneAdapter import io.github.sds100.keymapper.system.power.PowerAdapter +import io.github.sds100.keymapper.system.volume.VolumeAdapter import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.map @@ -34,6 +35,7 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor( private val phoneAdapter: PhoneAdapter, private val powerAdapter: PowerAdapter, private val foldableAdapter: FoldableAdapter, + private val volumeAdapter: VolumeAdapter, ) : DetectConstraintsUseCase { @AssistedFactory @@ -53,6 +55,7 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor( phoneAdapter, powerAdapter, foldableAdapter, + volumeAdapter, ) override fun onDependencyChanged(dependency: ConstraintDependency): Flow { @@ -92,6 +95,7 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor( ).map { dependency } ConstraintDependency.PHONE_STATE -> phoneAdapter.callStateFlow.map { dependency } + ConstraintDependency.RINGER_MODE -> volumeAdapter.ringerModeFlow.map { dependency } ConstraintDependency.CHARGING_STATE -> powerAdapter.isCharging.map { dependency } ConstraintDependency.HINGE_STATE -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index e6561e8dc0..a10a98b4e1 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -297,6 +297,9 @@ In phone call Not in phone call Phone ringing + Ringer mode: Ring + Ringer mode: Vibrate + Ringer mode: Silent Charging Discharging diff --git a/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt index be77315302..9039b9823a 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt @@ -11,6 +11,7 @@ import io.github.sds100.keymapper.system.foldable.HingeState import io.github.sds100.keymapper.system.foldable.isClosed import io.github.sds100.keymapper.system.foldable.isOpen import io.github.sds100.keymapper.system.phone.CallState +import io.github.sds100.keymapper.system.volume.RingerMode import java.time.LocalTime import timber.log.Timber @@ -26,6 +27,7 @@ class TestConstraintSnapshot( val chosenImeId: String? = null, val isKeyboardShowing: Boolean = false, val callState: CallState = CallState.NONE, + val ringerMode: RingerMode = RingerMode.NORMAL, val isCharging: Boolean = false, val isLocked: Boolean = false, val isBackFlashlightOn: Boolean = false, @@ -107,6 +109,9 @@ class TestConstraintSnapshot( is ConstraintData.InPhoneCall -> callState == CallState.IN_PHONE_CALL is ConstraintData.NotInPhoneCall -> callState == CallState.NONE is ConstraintData.PhoneRinging -> callState == CallState.RINGING + is ConstraintData.RingerModeNormal -> ringerMode == RingerMode.NORMAL + is ConstraintData.RingerModeVibrate -> ringerMode == RingerMode.VIBRATE + is ConstraintData.RingerModeSilent -> ringerMode == RingerMode.SILENT is ConstraintData.Charging -> isCharging is ConstraintData.Discharging -> !isCharging is ConstraintData.LockScreenShowing -> isLockscreenShowing diff --git a/data/src/main/java/io/github/sds100/keymapper/data/entities/ConstraintEntity.kt b/data/src/main/java/io/github/sds100/keymapper/data/entities/ConstraintEntity.kt index 3deebac611..d81bdddddf 100644 --- a/data/src/main/java/io/github/sds100/keymapper/data/entities/ConstraintEntity.kt +++ b/data/src/main/java/io/github/sds100/keymapper/data/entities/ConstraintEntity.kt @@ -88,6 +88,10 @@ data class ConstraintEntity( const val NOT_IN_PHONE_CALL = "not_in_phone_call" const val PHONE_RINGING = "phone_ringing" + const val RINGER_MODE_NORMAL = "ringer_mode_normal" + const val RINGER_MODE_VIBRATE = "ringer_mode_vibrate" + const val RINGER_MODE_SILENT = "ringer_mode_silent" + const val CHARGING = "charging" const val DISCHARGING = "discharging" diff --git a/system/src/main/java/io/github/sds100/keymapper/system/volume/AndroidVolumeAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/volume/AndroidVolumeAdapter.kt index 8a7e3ea0eb..3e6017648e 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/volume/AndroidVolumeAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/volume/AndroidVolumeAdapter.kt @@ -1,10 +1,14 @@ package io.github.sds100.keymapper.system.volume import android.app.NotificationManager +import android.content.BroadcastReceiver import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.media.AudioManager import android.os.Build import android.provider.Settings +import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import dagger.hilt.android.qualifiers.ApplicationContext import io.github.sds100.keymapper.common.utils.KMResult @@ -18,6 +22,9 @@ import io.github.sds100.keymapper.system.SystemError import io.github.sds100.keymapper.system.permissions.Permission import javax.inject.Inject import javax.inject.Singleton +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import timber.log.Timber @Singleton @@ -30,6 +37,30 @@ class AndroidVolumeAdapter @Inject constructor( private val audioManager: AudioManager by lazy { ctx.getSystemService()!! } private val notificationManager: NotificationManager by lazy { ctx.getSystemService()!! } + private val ringerModeReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + intent ?: return + if (intent.action == AudioManager.RINGER_MODE_CHANGED_ACTION) { + _ringerModeFlow.value = ringerMode + } + } + } + + private val _ringerModeFlow: MutableStateFlow by lazy { + MutableStateFlow(ringerMode) + } + override val ringerModeFlow: StateFlow by lazy { _ringerModeFlow.asStateFlow() } + + init { + val filter = IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION) + ContextCompat.registerReceiver( + ctx, + ringerModeReceiver, + filter, + ContextCompat.RECEIVER_NOT_EXPORTED, + ) + } + override val ringerMode: RingerMode get() { // Get the current ringer mode with the setting because the AudioManager diff --git a/system/src/main/java/io/github/sds100/keymapper/system/volume/VolumeAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/volume/VolumeAdapter.kt index 7bae48cc79..c5c88f8961 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/volume/VolumeAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/volume/VolumeAdapter.kt @@ -1,9 +1,11 @@ package io.github.sds100.keymapper.system.volume import io.github.sds100.keymapper.common.utils.KMResult +import kotlinx.coroutines.flow.Flow interface VolumeAdapter { val ringerMode: RingerMode + val ringerModeFlow: Flow fun raiseVolume(stream: VolumeStream? = null, showVolumeUi: Boolean): KMResult<*> fun lowerVolume(stream: VolumeStream? = null, showVolumeUi: Boolean): KMResult<*> From f3398a61dc39f3749dff5ba1fc92ef9219cc46e1 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 29 Jun 2026 12:20:57 +0000 Subject: [PATCH 2/5] #2163 refactor: replace three RingerMode data objects with single data class Use ConstraintData.RingerMode(val ringerMode: RingerMode) instead of three separate data objects (RingerModeNormal/Vibrate/Silent), following the OrientationCustom pattern. Entity backward compatibility is preserved via the existing three entity type strings. --- .../keymapper/base/constraints/Constraint.kt | 54 +++++++++---------- .../base/constraints/ConstraintSnapshot.kt | 4 +- .../base/constraints/ConstraintUiHelper.kt | 9 ++-- .../base/utils/TestConstraintSnapshot.kt | 4 +- 4 files changed, 33 insertions(+), 38 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt index e8cf0a8361..23181d469d 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt @@ -190,18 +190,14 @@ sealed class ConstraintData { } @Serializable - data object RingerModeNormal : ConstraintData() { - override val id: ConstraintId = ConstraintId.RINGER_MODE_NORMAL - } - - @Serializable - data object RingerModeVibrate : ConstraintData() { - override val id: ConstraintId = ConstraintId.RINGER_MODE_VIBRATE - } - - @Serializable - data object RingerModeSilent : ConstraintData() { - override val id: ConstraintId = ConstraintId.RINGER_MODE_SILENT + data class RingerMode( + val ringerMode: io.github.sds100.keymapper.system.volume.RingerMode, + ) : ConstraintData() { + override val id: ConstraintId = when (ringerMode) { + io.github.sds100.keymapper.system.volume.RingerMode.NORMAL -> ConstraintId.RINGER_MODE_NORMAL + io.github.sds100.keymapper.system.volume.RingerMode.VIBRATE -> ConstraintId.RINGER_MODE_VIBRATE + io.github.sds100.keymapper.system.volume.RingerMode.SILENT -> ConstraintId.RINGER_MODE_SILENT + } } @Serializable @@ -397,9 +393,9 @@ object ConstraintEntityMapper { ConstraintEntity.IN_PHONE_CALL -> ConstraintData.InPhoneCall ConstraintEntity.NOT_IN_PHONE_CALL -> ConstraintData.NotInPhoneCall - ConstraintEntity.RINGER_MODE_NORMAL -> ConstraintData.RingerModeNormal - ConstraintEntity.RINGER_MODE_VIBRATE -> ConstraintData.RingerModeVibrate - ConstraintEntity.RINGER_MODE_SILENT -> ConstraintData.RingerModeSilent + ConstraintEntity.RINGER_MODE_NORMAL -> ConstraintData.RingerMode(io.github.sds100.keymapper.system.volume.RingerMode.NORMAL) + ConstraintEntity.RINGER_MODE_VIBRATE -> ConstraintData.RingerMode(io.github.sds100.keymapper.system.volume.RingerMode.VIBRATE) + ConstraintEntity.RINGER_MODE_SILENT -> ConstraintData.RingerMode(io.github.sds100.keymapper.system.volume.RingerMode.SILENT) ConstraintEntity.CHARGING -> ConstraintData.Charging ConstraintEntity.DISCHARGING -> ConstraintData.Discharging @@ -692,20 +688,20 @@ object ConstraintEntityMapper { ConstraintEntity.PHONE_RINGING, ) - is ConstraintData.RingerModeNormal -> ConstraintEntity( - uid = constraint.uid, - ConstraintEntity.RINGER_MODE_NORMAL, - ) - - is ConstraintData.RingerModeVibrate -> ConstraintEntity( - uid = constraint.uid, - ConstraintEntity.RINGER_MODE_VIBRATE, - ) - - is ConstraintData.RingerModeSilent -> ConstraintEntity( - uid = constraint.uid, - ConstraintEntity.RINGER_MODE_SILENT, - ) + is ConstraintData.RingerMode -> when (constraint.data.ringerMode) { + io.github.sds100.keymapper.system.volume.RingerMode.NORMAL -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.RINGER_MODE_NORMAL, + ) + io.github.sds100.keymapper.system.volume.RingerMode.VIBRATE -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.RINGER_MODE_VIBRATE, + ) + io.github.sds100.keymapper.system.volume.RingerMode.SILENT -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.RINGER_MODE_SILENT, + ) + } is ConstraintData.Charging -> ConstraintEntity( uid = constraint.uid, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt index 9ee0521dcb..822bc32361 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt @@ -169,9 +169,7 @@ class LazyConstraintSnapshot( callState == CallState.RINGING || audioVolumeStreams.contains(AudioManager.STREAM_RING) - is ConstraintData.RingerModeNormal -> ringerMode == RingerMode.NORMAL - is ConstraintData.RingerModeVibrate -> ringerMode == RingerMode.VIBRATE - is ConstraintData.RingerModeSilent -> ringerMode == RingerMode.SILENT + is ConstraintData.RingerMode -> ringerMode == constraint.ringerMode is ConstraintData.Charging -> isCharging is ConstraintData.Discharging -> !isCharging diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt index e9bb6064f4..039e922691 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt @@ -11,6 +11,7 @@ import io.github.sds100.keymapper.common.utils.TimeUtils import io.github.sds100.keymapper.common.utils.handle import io.github.sds100.keymapper.common.utils.valueIfFailure import io.github.sds100.keymapper.system.camera.CameraLens +import io.github.sds100.keymapper.system.volume.RingerMode import java.time.format.FormatStyle class ConstraintUiHelper( @@ -163,9 +164,11 @@ class ConstraintUiHelper( is ConstraintData.InPhoneCall -> getString(R.string.constraint_in_phone_call) is ConstraintData.NotInPhoneCall -> getString(R.string.constraint_not_in_phone_call) is ConstraintData.PhoneRinging -> getString(R.string.constraint_phone_ringing) - is ConstraintData.RingerModeNormal -> getString(R.string.constraint_ringer_mode_normal) - is ConstraintData.RingerModeVibrate -> getString(R.string.constraint_ringer_mode_vibrate) - is ConstraintData.RingerModeSilent -> getString(R.string.constraint_ringer_mode_silent) + is ConstraintData.RingerMode -> when (constraint.ringerMode) { + RingerMode.NORMAL -> getString(R.string.constraint_ringer_mode_normal) + RingerMode.VIBRATE -> getString(R.string.constraint_ringer_mode_vibrate) + RingerMode.SILENT -> getString(R.string.constraint_ringer_mode_silent) + } is ConstraintData.Charging -> getString(R.string.constraint_charging) is ConstraintData.Discharging -> getString(R.string.constraint_discharging) is ConstraintData.HingeClosed -> getString(R.string.constraint_hinge_closed_description) diff --git a/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt index 9039b9823a..91598bb35e 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt @@ -109,9 +109,7 @@ class TestConstraintSnapshot( is ConstraintData.InPhoneCall -> callState == CallState.IN_PHONE_CALL is ConstraintData.NotInPhoneCall -> callState == CallState.NONE is ConstraintData.PhoneRinging -> callState == CallState.RINGING - is ConstraintData.RingerModeNormal -> ringerMode == RingerMode.NORMAL - is ConstraintData.RingerModeVibrate -> ringerMode == RingerMode.VIBRATE - is ConstraintData.RingerModeSilent -> ringerMode == RingerMode.SILENT + is ConstraintData.RingerMode -> ringerMode == constraint.ringerMode is ConstraintData.Charging -> isCharging is ConstraintData.Discharging -> !isCharging is ConstraintData.LockScreenShowing -> isLockscreenShowing From 30e2f1a6aa247de22c572a1a328c83d1c482cbdf Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 29 Jun 2026 12:37:42 +0000 Subject: [PATCH 3/5] #2163 refactor: use single RingerMode constraint data class with ringerMode field Replace three separate data objects (RingerModeNormal, RingerModeVibrate, RingerModeSilent) with one data class ConstraintData.RingerMode(ringerMode: SystemRingerMode), following the OrientationCustom pattern. DB entity backward compatibility is preserved via three separate entity type strings. Add @Serializable to RingerMode enum for kotlinx.serialization support. Fix exhaustive when expressions in ChooseConstraintViewModel and KeyMapConstraintsComparator. --- .../constraints/ChooseConstraintViewModel.kt | 10 +++++++ .../keymapper/base/constraints/Constraint.kt | 26 ++++++++++--------- .../base/constraints/ConstraintUtils.kt | 2 +- .../KeyMapConstraintsComparator.kt | 1 + .../keymapper/system/volume/RingerMode.kt | 3 +++ 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintViewModel.kt index 836834e723..4f41af9308 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintViewModel.kt @@ -25,6 +25,7 @@ import io.github.sds100.keymapper.common.utils.Orientation import io.github.sds100.keymapper.common.utils.PhysicalOrientation import io.github.sds100.keymapper.common.utils.State import io.github.sds100.keymapper.system.camera.CameraLens +import io.github.sds100.keymapper.system.volume.RingerMode import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow @@ -249,6 +250,15 @@ class ChooseConstraintViewModel @Inject constructor( ConstraintId.PHONE_RINGING -> returnResult.emit(ConstraintData.PhoneRinging) + ConstraintId.RINGER_MODE_NORMAL -> + returnResult.emit(ConstraintData.RingerMode(RingerMode.NORMAL)) + + ConstraintId.RINGER_MODE_VIBRATE -> + returnResult.emit(ConstraintData.RingerMode(RingerMode.VIBRATE)) + + ConstraintId.RINGER_MODE_SILENT -> + returnResult.emit(ConstraintData.RingerMode(RingerMode.SILENT)) + ConstraintId.CHARGING -> returnResult.emit(ConstraintData.Charging) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt index 23181d469d..b855f57524 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt @@ -8,6 +8,7 @@ import io.github.sds100.keymapper.data.entities.ConstraintEntity import io.github.sds100.keymapper.data.entities.EntityExtra import io.github.sds100.keymapper.data.entities.getData import io.github.sds100.keymapper.system.camera.CameraLens +import io.github.sds100.keymapper.system.volume.RingerMode as SystemRingerMode import java.time.LocalTime import java.util.UUID import kotlinx.serialization.Serializable @@ -190,13 +191,11 @@ sealed class ConstraintData { } @Serializable - data class RingerMode( - val ringerMode: io.github.sds100.keymapper.system.volume.RingerMode, - ) : ConstraintData() { + data class RingerMode(val ringerMode: SystemRingerMode) : ConstraintData() { override val id: ConstraintId = when (ringerMode) { - io.github.sds100.keymapper.system.volume.RingerMode.NORMAL -> ConstraintId.RINGER_MODE_NORMAL - io.github.sds100.keymapper.system.volume.RingerMode.VIBRATE -> ConstraintId.RINGER_MODE_VIBRATE - io.github.sds100.keymapper.system.volume.RingerMode.SILENT -> ConstraintId.RINGER_MODE_SILENT + SystemRingerMode.NORMAL -> ConstraintId.RINGER_MODE_NORMAL + SystemRingerMode.VIBRATE -> ConstraintId.RINGER_MODE_VIBRATE + SystemRingerMode.SILENT -> ConstraintId.RINGER_MODE_SILENT } } @@ -393,9 +392,12 @@ object ConstraintEntityMapper { ConstraintEntity.IN_PHONE_CALL -> ConstraintData.InPhoneCall ConstraintEntity.NOT_IN_PHONE_CALL -> ConstraintData.NotInPhoneCall - ConstraintEntity.RINGER_MODE_NORMAL -> ConstraintData.RingerMode(io.github.sds100.keymapper.system.volume.RingerMode.NORMAL) - ConstraintEntity.RINGER_MODE_VIBRATE -> ConstraintData.RingerMode(io.github.sds100.keymapper.system.volume.RingerMode.VIBRATE) - ConstraintEntity.RINGER_MODE_SILENT -> ConstraintData.RingerMode(io.github.sds100.keymapper.system.volume.RingerMode.SILENT) + ConstraintEntity.RINGER_MODE_NORMAL -> + ConstraintData.RingerMode(SystemRingerMode.NORMAL) + ConstraintEntity.RINGER_MODE_VIBRATE -> + ConstraintData.RingerMode(SystemRingerMode.VIBRATE) + ConstraintEntity.RINGER_MODE_SILENT -> + ConstraintData.RingerMode(SystemRingerMode.SILENT) ConstraintEntity.CHARGING -> ConstraintData.Charging ConstraintEntity.DISCHARGING -> ConstraintData.Discharging @@ -689,15 +691,15 @@ object ConstraintEntityMapper { ) is ConstraintData.RingerMode -> when (constraint.data.ringerMode) { - io.github.sds100.keymapper.system.volume.RingerMode.NORMAL -> ConstraintEntity( + SystemRingerMode.NORMAL -> ConstraintEntity( uid = constraint.uid, ConstraintEntity.RINGER_MODE_NORMAL, ) - io.github.sds100.keymapper.system.volume.RingerMode.VIBRATE -> ConstraintEntity( + SystemRingerMode.VIBRATE -> ConstraintEntity( uid = constraint.uid, ConstraintEntity.RINGER_MODE_VIBRATE, ) - io.github.sds100.keymapper.system.volume.RingerMode.SILENT -> ConstraintEntity( + SystemRingerMode.SILENT -> ConstraintEntity( uid = constraint.uid, ConstraintEntity.RINGER_MODE_SILENT, ) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt index 7bb26b672f..384490b13b 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt @@ -17,8 +17,8 @@ import androidx.compose.material.icons.outlined.LockOpen import androidx.compose.material.icons.outlined.MobileOff import androidx.compose.material.icons.outlined.Notifications import androidx.compose.material.icons.outlined.NotificationsOff -import androidx.compose.material.icons.outlined.Vibration import androidx.compose.material.icons.outlined.PlayArrow +import androidx.compose.material.icons.outlined.Vibration import androidx.compose.material.icons.outlined.RingVolume import androidx.compose.material.icons.outlined.ScreenLockPortrait import androidx.compose.material.icons.outlined.SignalWifiStatusbarNull diff --git a/base/src/main/java/io/github/sds100/keymapper/base/sorting/comparators/KeyMapConstraintsComparator.kt b/base/src/main/java/io/github/sds100/keymapper/base/sorting/comparators/KeyMapConstraintsComparator.kt index a48843773b..ffbd85c775 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/sorting/comparators/KeyMapConstraintsComparator.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/sorting/comparators/KeyMapConstraintsComparator.kt @@ -112,6 +112,7 @@ class KeyMapConstraintsComparator( is ConstraintData.OrientationLandscape -> Success("") is ConstraintData.OrientationPortrait -> Success("") is ConstraintData.PhoneRinging -> Success("") + is ConstraintData.RingerMode -> Success("") is ConstraintData.ScreenOff -> Success("") is ConstraintData.ScreenOn -> Success("") is ConstraintData.WifiConnected -> if (constraint.data.ssid == null) { diff --git a/system/src/main/java/io/github/sds100/keymapper/system/volume/RingerMode.kt b/system/src/main/java/io/github/sds100/keymapper/system/volume/RingerMode.kt index 2548902c5e..4af85213c7 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/volume/RingerMode.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/volume/RingerMode.kt @@ -1,5 +1,8 @@ package io.github.sds100.keymapper.system.volume +import kotlinx.serialization.Serializable + +@Serializable enum class RingerMode { NORMAL, VIBRATE, From f08ac663f06ebf04913ccbe5a7fdd971cd47a0f2 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 29 Jun 2026 12:42:31 +0000 Subject: [PATCH 4/5] #2163 style: fix import ordering in ConstraintUtils --- .../github/sds100/keymapper/base/constraints/ConstraintUtils.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt index 384490b13b..048854c7b6 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt @@ -18,7 +18,6 @@ import androidx.compose.material.icons.outlined.MobileOff import androidx.compose.material.icons.outlined.Notifications import androidx.compose.material.icons.outlined.NotificationsOff import androidx.compose.material.icons.outlined.PlayArrow -import androidx.compose.material.icons.outlined.Vibration import androidx.compose.material.icons.outlined.RingVolume import androidx.compose.material.icons.outlined.ScreenLockPortrait import androidx.compose.material.icons.outlined.SignalWifiStatusbarNull @@ -26,6 +25,7 @@ import androidx.compose.material.icons.outlined.StayCurrentLandscape import androidx.compose.material.icons.outlined.StayCurrentPortrait import androidx.compose.material.icons.outlined.StopCircle import androidx.compose.material.icons.outlined.Timer +import androidx.compose.material.icons.outlined.Vibration import androidx.compose.material.icons.outlined.Wifi import androidx.compose.material.icons.outlined.WifiOff import androidx.compose.material.icons.rounded.Android From 56899ee7fbb8cb4689a12e0b2822cf763489b0ef Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 29 Jun 2026 12:46:56 +0000 Subject: [PATCH 5/5] #2163 fix: use data.ringerMode not constraint.ringerMode in when branches --- .../sds100/keymapper/base/constraints/ConstraintSnapshot.kt | 2 +- .../sds100/keymapper/base/constraints/ConstraintUiHelper.kt | 2 +- .../sds100/keymapper/base/utils/TestConstraintSnapshot.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt index 822bc32361..b568952133 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt @@ -169,7 +169,7 @@ class LazyConstraintSnapshot( callState == CallState.RINGING || audioVolumeStreams.contains(AudioManager.STREAM_RING) - is ConstraintData.RingerMode -> ringerMode == constraint.ringerMode + is ConstraintData.RingerMode -> ringerMode == constraint.data.ringerMode is ConstraintData.Charging -> isCharging is ConstraintData.Discharging -> !isCharging diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt index 039e922691..14fd7ed7a1 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt @@ -164,7 +164,7 @@ class ConstraintUiHelper( is ConstraintData.InPhoneCall -> getString(R.string.constraint_in_phone_call) is ConstraintData.NotInPhoneCall -> getString(R.string.constraint_not_in_phone_call) is ConstraintData.PhoneRinging -> getString(R.string.constraint_phone_ringing) - is ConstraintData.RingerMode -> when (constraint.ringerMode) { + is ConstraintData.RingerMode -> when (constraint.data.ringerMode) { RingerMode.NORMAL -> getString(R.string.constraint_ringer_mode_normal) RingerMode.VIBRATE -> getString(R.string.constraint_ringer_mode_vibrate) RingerMode.SILENT -> getString(R.string.constraint_ringer_mode_silent) diff --git a/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt index 91598bb35e..89748d53df 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt @@ -109,7 +109,7 @@ class TestConstraintSnapshot( is ConstraintData.InPhoneCall -> callState == CallState.IN_PHONE_CALL is ConstraintData.NotInPhoneCall -> callState == CallState.NONE is ConstraintData.PhoneRinging -> callState == CallState.RINGING - is ConstraintData.RingerMode -> ringerMode == constraint.ringerMode + is ConstraintData.RingerMode -> ringerMode == data.ringerMode is ConstraintData.Charging -> isCharging is ConstraintData.Discharging -> !isCharging is ConstraintData.LockScreenShowing -> isLockscreenShowing