From 8c6a66a7f99cd438fab23a60f392271eb113ebd0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Nov 2025 12:51:08 +0000 Subject: [PATCH 1/8] Initial plan From df64a8ac0748f18e4b88d3e2b0fda664c9d3ae68 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Nov 2025 13:04:49 +0000 Subject: [PATCH 2/8] feat: Add physical orientation constraint support using OrientationEventListener Co-authored-by: sds100 <16245954+sds100@users.noreply.github.com> --- .../constraints/ChooseConstraintViewModel.kt | 29 ++++++++++ .../keymapper/base/constraints/Constraint.kt | 41 ++++++++++++++ .../base/constraints/ConstraintDependency.kt | 1 + .../base/constraints/ConstraintId.kt | 5 ++ .../base/constraints/ConstraintSnapshot.kt | 5 ++ .../base/constraints/ConstraintUiHelper.kt | 16 ++++++ .../base/constraints/ConstraintUtils.kt | 20 +++++++ .../constraints/DetectConstraintsUseCase.kt | 2 + base/src/main/res/values/strings.xml | 4 ++ .../base/utils/TestConstraintSnapshot.kt | 5 ++ .../common/utils/PhysicalOrientation.kt | 12 ++++ .../data/entities/ConstraintEntity.kt | 5 ++ .../system/display/AndroidDisplayAdapter.kt | 56 +++++++++++++++++++ .../system/display/DisplayAdapter.kt | 3 + 14 files changed, 204 insertions(+) create mode 100644 common/src/main/java/io/github/sds100/keymapper/common/utils/PhysicalOrientation.kt 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 1b24b89c2d..509d683bf1 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 @@ -19,6 +19,7 @@ import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemGroup import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemModel import io.github.sds100.keymapper.base.utils.ui.showDialog 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 javax.inject.Inject @@ -157,6 +158,34 @@ class ChooseConstraintViewModel @Inject constructor( ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_270), ) + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT -> + returnResult.emit( + ConstraintData.PhysicalOrientationConstraint( + physicalOrientation = PhysicalOrientation.PORTRAIT, + ), + ) + + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE -> + returnResult.emit( + ConstraintData.PhysicalOrientationConstraint( + physicalOrientation = PhysicalOrientation.LANDSCAPE, + ), + ) + + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED -> + returnResult.emit( + ConstraintData.PhysicalOrientationConstraint( + physicalOrientation = PhysicalOrientation.PORTRAIT_INVERTED, + ), + ) + + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED -> + returnResult.emit( + ConstraintData.PhysicalOrientationConstraint( + physicalOrientation = PhysicalOrientation.LANDSCAPE_INVERTED, + ), + ) + ConstraintId.FLASHLIGHT_ON -> { val lens = chooseFlashlightLens() ?: return@launch returnResult.emit(ConstraintData.FlashlightOn(lens = lens)) 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 547802579f..2ce1cf7ff2 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 @@ -1,6 +1,7 @@ package io.github.sds100.keymapper.base.constraints import io.github.sds100.keymapper.common.utils.Orientation +import io.github.sds100.keymapper.common.utils.PhysicalOrientation import io.github.sds100.keymapper.common.utils.getKey import io.github.sds100.keymapper.common.utils.valueOrNull import io.github.sds100.keymapper.data.entities.ConstraintEntity @@ -87,6 +88,18 @@ sealed class ConstraintData { } } + @Serializable + data class PhysicalOrientationConstraint( + val physicalOrientation: PhysicalOrientation, + ) : ConstraintData() { + override val id: ConstraintId = when (physicalOrientation) { + PhysicalOrientation.PORTRAIT -> ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT + PhysicalOrientation.LANDSCAPE -> ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE + PhysicalOrientation.PORTRAIT_INVERTED -> ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED + PhysicalOrientation.LANDSCAPE_INVERTED -> ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED + } + } + @Serializable data class FlashlightOn(val lens: CameraLens) : ConstraintData() { override val id: ConstraintId = ConstraintId.FLASHLIGHT_ON @@ -316,6 +329,15 @@ object ConstraintEntityMapper { ConstraintEntity.ORIENTATION_PORTRAIT -> ConstraintData.OrientationPortrait ConstraintEntity.ORIENTATION_LANDSCAPE -> ConstraintData.OrientationLandscape + ConstraintEntity.PHYSICAL_ORIENTATION_PORTRAIT -> + ConstraintData.PhysicalOrientationConstraint(PhysicalOrientation.PORTRAIT) + ConstraintEntity.PHYSICAL_ORIENTATION_LANDSCAPE -> + ConstraintData.PhysicalOrientationConstraint(PhysicalOrientation.LANDSCAPE) + ConstraintEntity.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED -> + ConstraintData.PhysicalOrientationConstraint(PhysicalOrientation.PORTRAIT_INVERTED) + ConstraintEntity.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED -> + ConstraintData.PhysicalOrientationConstraint(PhysicalOrientation.LANDSCAPE_INVERTED) + ConstraintEntity.SCREEN_OFF -> ConstraintData.ScreenOff ConstraintEntity.SCREEN_ON -> ConstraintData.ScreenOn @@ -499,6 +521,25 @@ object ConstraintEntityMapper { ConstraintEntity.ORIENTATION_PORTRAIT, ) + is ConstraintData.PhysicalOrientationConstraint -> when (constraint.data.physicalOrientation) { + PhysicalOrientation.PORTRAIT -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.PHYSICAL_ORIENTATION_PORTRAIT, + ) + PhysicalOrientation.LANDSCAPE -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.PHYSICAL_ORIENTATION_LANDSCAPE, + ) + PhysicalOrientation.PORTRAIT_INVERTED -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, + ) + PhysicalOrientation.LANDSCAPE_INVERTED -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED, + ) + } + is ConstraintData.ScreenOff -> ConstraintEntity( uid = constraint.uid, ConstraintEntity.SCREEN_OFF, 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 e8b3f784e9..4bbbcf1721 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 @@ -7,6 +7,7 @@ enum class ConstraintDependency { CONNECTED_BT_DEVICES, SCREEN_STATE, DISPLAY_ORIENTATION, + PHYSICAL_ORIENTATION, FLASHLIGHT_STATE, WIFI_SSID, WIFI_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 b48b61b8c2..bc9e2ce0c5 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 @@ -26,6 +26,11 @@ enum class ConstraintId { ORIENTATION_180, ORIENTATION_270, + PHYSICAL_ORIENTATION_PORTRAIT, + PHYSICAL_ORIENTATION_LANDSCAPE, + PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, + PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED, + FLASHLIGHT_ON, FLASHLIGHT_OFF, 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 629a610c3a..9121d714c3 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 @@ -4,6 +4,7 @@ import android.media.AudioManager import android.os.Build import io.github.sds100.keymapper.base.system.accessibility.IAccessibilityService import io.github.sds100.keymapper.common.utils.Orientation +import io.github.sds100.keymapper.common.utils.PhysicalOrientation import io.github.sds100.keymapper.common.utils.firstBlocking import io.github.sds100.keymapper.system.bluetooth.BluetoothDeviceInfo import io.github.sds100.keymapper.system.camera.CameraAdapter @@ -43,6 +44,7 @@ class LazyConstraintSnapshot( devicesAdapter.connectedBluetoothDevices.value } private val orientation: Orientation by lazy { displayAdapter.cachedOrientation } + private val physicalOrientation: PhysicalOrientation by lazy { displayAdapter.cachedPhysicalOrientation } private val isScreenOn: Boolean by lazy { displayAdapter.isScreenOn.firstBlocking() } private val appsPlayingMedia: List by lazy { mediaAdapter.getActiveMediaSessionPackages() @@ -117,6 +119,9 @@ class LazyConstraintSnapshot( orientation == Orientation.ORIENTATION_0 || orientation == Orientation.ORIENTATION_180 + is ConstraintData.PhysicalOrientationConstraint -> + physicalOrientation == constraint.data.physicalOrientation + is ConstraintData.ScreenOff -> !isScreenOn is ConstraintData.ScreenOn -> isScreenOn is ConstraintData.FlashlightOff -> !cameraAdapter.isFlashlightOn(constraint.data.lens) 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 62246d6817..a36800578c 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 @@ -6,6 +6,7 @@ import io.github.sds100.keymapper.base.R import io.github.sds100.keymapper.base.utils.ui.ResourceProvider import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo import io.github.sds100.keymapper.common.utils.Orientation +import io.github.sds100.keymapper.common.utils.PhysicalOrientation import io.github.sds100.keymapper.common.utils.TimeUtils import io.github.sds100.keymapper.common.utils.handle import io.github.sds100.keymapper.common.utils.valueIfFailure @@ -82,6 +83,21 @@ class ConstraintUiHelper( is ConstraintData.OrientationPortrait -> getString(R.string.constraint_choose_orientation_portrait) + is ConstraintData.PhysicalOrientationConstraint -> { + val resId = when (constraint.data.physicalOrientation) { + PhysicalOrientation.PORTRAIT -> + R.string.constraint_choose_physical_orientation_portrait + PhysicalOrientation.LANDSCAPE -> + R.string.constraint_choose_physical_orientation_landscape + PhysicalOrientation.PORTRAIT_INVERTED -> + R.string.constraint_choose_physical_orientation_portrait_inverted + PhysicalOrientation.LANDSCAPE_INVERTED -> + R.string.constraint_choose_physical_orientation_landscape_inverted + } + + getString(resId) + } + is ConstraintData.ScreenOff -> getString(R.string.constraint_screen_off_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 679a1229e1..4a34c65282 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 @@ -70,6 +70,10 @@ object ConstraintUtils { ConstraintId.ORIENTATION_90, ConstraintId.ORIENTATION_180, ConstraintId.ORIENTATION_270, + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT, + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE, + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED, -> ConstraintCategory.DISPLAY ConstraintId.FLASHLIGHT_ON, @@ -142,6 +146,14 @@ object ConstraintUtils { Icons.Outlined.StayCurrentPortrait, ) + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT, + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, + -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait) + + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE, + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED, + -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentLandscape) + ConstraintId.SCREEN_OFF -> ComposeIconInfo.Vector(Icons.Outlined.MobileOff) ConstraintId.SCREEN_ON -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait) @@ -200,6 +212,14 @@ object ConstraintUtils { ConstraintId.ORIENTATION_90 -> R.string.constraint_choose_orientation_90 ConstraintId.ORIENTATION_180 -> R.string.constraint_choose_orientation_180 ConstraintId.ORIENTATION_270 -> R.string.constraint_choose_orientation_270 + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT -> + R.string.constraint_choose_physical_orientation_portrait + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE -> + R.string.constraint_choose_physical_orientation_landscape + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED -> + R.string.constraint_choose_physical_orientation_portrait_inverted + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED -> + R.string.constraint_choose_physical_orientation_landscape_inverted ConstraintId.FLASHLIGHT_ON -> R.string.constraint_flashlight_on ConstraintId.FLASHLIGHT_OFF -> R.string.constraint_flashlight_off ConstraintId.WIFI_ON -> R.string.constraint_wifi_on 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 87e38e6af3..8bf75d6f24 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 @@ -71,6 +71,8 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor( ConstraintDependency.SCREEN_STATE -> displayAdapter.isScreenOn.map { dependency } ConstraintDependency.DISPLAY_ORIENTATION -> displayAdapter.orientation.map { dependency } + ConstraintDependency.PHYSICAL_ORIENTATION -> + displayAdapter.physicalOrientation.map { dependency } ConstraintDependency.FLASHLIGHT_STATE -> merge( cameraAdapter.isFlashlightOnFlow(CameraLens.FRONT), cameraAdapter.isFlashlightOnFlow(CameraLens.BACK), diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index a99a76348c..3372fd5ec7 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -256,6 +256,10 @@ Landscape (270°) Portrait (any) Landscape (any) + Physical: Portrait + Physical: Landscape + Physical: Portrait (upside down) + Physical: Landscape (inverted) App playing media App not playing media Media is playing 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 063557172f..cffe18010a 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 @@ -4,6 +4,7 @@ import io.github.sds100.keymapper.base.constraints.Constraint import io.github.sds100.keymapper.base.constraints.ConstraintData import io.github.sds100.keymapper.base.constraints.ConstraintSnapshot import io.github.sds100.keymapper.common.utils.Orientation +import io.github.sds100.keymapper.common.utils.PhysicalOrientation import io.github.sds100.keymapper.system.bluetooth.BluetoothDeviceInfo import io.github.sds100.keymapper.system.camera.CameraLens import io.github.sds100.keymapper.system.foldable.HingeState @@ -17,6 +18,7 @@ class TestConstraintSnapshot( val appInForeground: String? = null, val connectedBluetoothDevices: Set = emptySet(), val orientation: Orientation = Orientation.ORIENTATION_0, + val physicalOrientation: PhysicalOrientation = PhysicalOrientation.PORTRAIT, val isScreenOn: Boolean = false, val appsPlayingMedia: List = emptyList(), val isWifiEnabled: Boolean = false, @@ -62,6 +64,9 @@ class TestConstraintSnapshot( orientation == Orientation.ORIENTATION_0 || orientation == Orientation.ORIENTATION_180 + is ConstraintData.PhysicalOrientationConstraint -> + physicalOrientation == data.physicalOrientation + is ConstraintData.ScreenOff -> !isScreenOn is ConstraintData.ScreenOn -> isScreenOn is ConstraintData.FlashlightOff -> when (data.lens) { diff --git a/common/src/main/java/io/github/sds100/keymapper/common/utils/PhysicalOrientation.kt b/common/src/main/java/io/github/sds100/keymapper/common/utils/PhysicalOrientation.kt new file mode 100644 index 0000000000..a89aa9a2d2 --- /dev/null +++ b/common/src/main/java/io/github/sds100/keymapper/common/utils/PhysicalOrientation.kt @@ -0,0 +1,12 @@ +package io.github.sds100.keymapper.common.utils + +/** + * Represents the physical orientation of the device based on the device's + * orientation sensor (accelerometer), independent of the screen rotation setting. + */ +enum class PhysicalOrientation { + PORTRAIT, + LANDSCAPE, + PORTRAIT_INVERTED, + LANDSCAPE_INVERTED, +} 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 24d5954c60..a7733dd66a 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 @@ -58,6 +58,11 @@ data class ConstraintEntity( const val ORIENTATION_PORTRAIT = "constraint_orientation_portrait" const val ORIENTATION_LANDSCAPE = "constraint_orientation_landscape" + const val PHYSICAL_ORIENTATION_PORTRAIT = "constraint_physical_orientation_portrait" + const val PHYSICAL_ORIENTATION_LANDSCAPE = "constraint_physical_orientation_landscape" + const val PHYSICAL_ORIENTATION_PORTRAIT_INVERTED = "constraint_physical_orientation_portrait_inverted" + const val PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED = "constraint_physical_orientation_landscape_inverted" + const val FLASHLIGHT_ON = "flashlight_on" const val FLASHLIGHT_OFF = "flashlight_off" diff --git a/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt index d234b680aa..2285856437 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt @@ -7,6 +7,7 @@ import android.content.IntentFilter import android.hardware.display.DisplayManager import android.provider.Settings import android.view.Display +import android.view.OrientationEventListener import android.view.Surface import androidx.core.content.ContextCompat import androidx.core.content.getSystemService @@ -14,6 +15,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import io.github.sds100.keymapper.common.utils.KMError import io.github.sds100.keymapper.common.utils.KMResult import io.github.sds100.keymapper.common.utils.Orientation +import io.github.sds100.keymapper.common.utils.PhysicalOrientation import io.github.sds100.keymapper.common.utils.SettingsUtils import io.github.sds100.keymapper.common.utils.SizeKM import io.github.sds100.keymapper.common.utils.Success @@ -39,6 +41,12 @@ class AndroidDisplayAdapter @Inject constructor( * How much to change the brightness by. */ private const val BRIGHTNESS_CHANGE_STEP = 20 + + /** + * Tolerance in degrees for orientation detection. + * This helps avoid rapid switching at orientation boundaries. + */ + private const val ORIENTATION_TOLERANCE = 45 } private val ctx = context.applicationContext @@ -77,12 +85,28 @@ class AndroidDisplayAdapter @Inject constructor( override val cachedOrientation: Orientation get() = _orientation.value + private val _physicalOrientation = MutableStateFlow(PhysicalOrientation.PORTRAIT) + override val physicalOrientation: Flow = _physicalOrientation + override val cachedPhysicalOrientation: PhysicalOrientation + get() = _physicalOrientation.value + override val size: SizeKM get() = ctx.getRealDisplaySize() override val isAmbientDisplayEnabled: MutableStateFlow = MutableStateFlow(isAodEnabled()) + private val orientationEventListener = object : OrientationEventListener(ctx) { + override fun onOrientationChanged(orientationDegrees: Int) { + if (orientationDegrees == ORIENTATION_UNKNOWN) { + return + } + + val newPhysicalOrientation = degreesToPhysicalOrientation(orientationDegrees) + _physicalOrientation.update { newPhysicalOrientation } + } + } + init { displayManager.registerDisplayListener( object : DisplayManager.DisplayListener { @@ -115,6 +139,11 @@ class AndroidDisplayAdapter @Inject constructor( filter, ContextCompat.RECEIVER_NOT_EXPORTED, ) + + // Enable physical orientation detection + if (orientationEventListener.canDetectOrientation()) { + orientationEventListener.enable() + } } override fun isAutoRotateEnabled(): Boolean = @@ -253,4 +282,31 @@ class AndroidDisplayAdapter @Inject constructor( private fun isAodEnabled(): Boolean { return SettingsUtils.getSecureSetting(ctx, "doze_always_on") == 1 } + + /** + * Converts sensor orientation degrees to PhysicalOrientation. + * + * The orientation degrees from OrientationEventListener represent how much + * the device is rotated from its natural orientation: + * - 0°: Device is upright (portrait for most phones) + * - 90°: Device is rotated 90° counter-clockwise (landscape, home button on right) + * - 180°: Device is upside down (portrait inverted) + * - 270°: Device is rotated 90° clockwise (landscape, home button on left) + * + * Using a tolerance helps avoid rapid orientation changes at boundaries. + */ + private fun degreesToPhysicalOrientation(degrees: Int): PhysicalOrientation { + return when { + degrees in (360 - ORIENTATION_TOLERANCE)..360 || + degrees in 0 until ORIENTATION_TOLERANCE -> + PhysicalOrientation.PORTRAIT + degrees in (90 - ORIENTATION_TOLERANCE) until (90 + ORIENTATION_TOLERANCE) -> + PhysicalOrientation.LANDSCAPE + degrees in (180 - ORIENTATION_TOLERANCE) until (180 + ORIENTATION_TOLERANCE) -> + PhysicalOrientation.PORTRAIT_INVERTED + degrees in (270 - ORIENTATION_TOLERANCE) until (270 + ORIENTATION_TOLERANCE) -> + PhysicalOrientation.LANDSCAPE_INVERTED + else -> _physicalOrientation.value // Keep current orientation if in transition zone + } + } } diff --git a/system/src/main/java/io/github/sds100/keymapper/system/display/DisplayAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/display/DisplayAdapter.kt index f8792d6fd4..377e148044 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/display/DisplayAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/display/DisplayAdapter.kt @@ -2,6 +2,7 @@ package io.github.sds100.keymapper.system.display import io.github.sds100.keymapper.common.utils.KMResult import io.github.sds100.keymapper.common.utils.Orientation +import io.github.sds100.keymapper.common.utils.PhysicalOrientation import io.github.sds100.keymapper.common.utils.SizeKM import kotlinx.coroutines.flow.Flow @@ -9,6 +10,8 @@ interface DisplayAdapter { val isScreenOn: Flow val orientation: Flow val cachedOrientation: Orientation + val physicalOrientation: Flow + val cachedPhysicalOrientation: PhysicalOrientation val size: SizeKM val isAmbientDisplayEnabled: Flow From a04a07a59b52ff2bd3dd5686c6ad783425d647d3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Nov 2025 13:06:29 +0000 Subject: [PATCH 3/8] fix: Address code review feedback for physical orientation Co-authored-by: sds100 <16245954+sds100@users.noreply.github.com> --- .../system/display/AndroidDisplayAdapter.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt index 2285856437..c7702e5d1a 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt @@ -30,6 +30,14 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.updateAndGet import kotlinx.coroutines.launch +/** + * Android implementation of DisplayAdapter. + * + * This is a Singleton that lives for the lifetime of the application. + * Listeners (DisplayManager.DisplayListener, BroadcastReceiver, OrientationEventListener) + * are intentionally not unregistered because the adapter needs to continuously track + * display state changes throughout the app's lifecycle. + */ @Singleton class AndroidDisplayAdapter @Inject constructor( @ApplicationContext private val context: Context, @@ -296,9 +304,11 @@ class AndroidDisplayAdapter @Inject constructor( * Using a tolerance helps avoid rapid orientation changes at boundaries. */ private fun degreesToPhysicalOrientation(degrees: Int): PhysicalOrientation { + // OrientationEventListener returns 0-359 degrees + // Handle wraparound at 0/360 boundary for portrait detection return when { - degrees in (360 - ORIENTATION_TOLERANCE)..360 || - degrees in 0 until ORIENTATION_TOLERANCE -> + degrees >= (360 - ORIENTATION_TOLERANCE) || + degrees < ORIENTATION_TOLERANCE -> PhysicalOrientation.PORTRAIT degrees in (90 - ORIENTATION_TOLERANCE) until (90 + ORIENTATION_TOLERANCE) -> PhysicalOrientation.LANDSCAPE From 9a3b672aa4f570aeb601ffc3b8ebd3b614167034 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Nov 2025 13:11:37 +0000 Subject: [PATCH 4/8] docs: Improve comments for orientation transition zone handling Co-authored-by: sds100 <16245954+sds100@users.noreply.github.com> --- .../keymapper/system/display/AndroidDisplayAdapter.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt index c7702e5d1a..560e60e42d 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt @@ -304,8 +304,11 @@ class AndroidDisplayAdapter @Inject constructor( * Using a tolerance helps avoid rapid orientation changes at boundaries. */ private fun degreesToPhysicalOrientation(degrees: Int): PhysicalOrientation { - // OrientationEventListener returns 0-359 degrees - // Handle wraparound at 0/360 boundary for portrait detection + // OrientationEventListener returns 0-359 degrees. + // Handle wraparound at 0/360 boundary for portrait detection. + // Degrees outside the defined tolerance zones (e.g., 45-89° between portrait and landscape) + // are intentionally kept as the current orientation to provide hysteresis and prevent + // rapid orientation switching when the device is tilted at boundary angles. return when { degrees >= (360 - ORIENTATION_TOLERANCE) || degrees < ORIENTATION_TOLERANCE -> @@ -316,7 +319,7 @@ class AndroidDisplayAdapter @Inject constructor( PhysicalOrientation.PORTRAIT_INVERTED degrees in (270 - ORIENTATION_TOLERANCE) until (270 + ORIENTATION_TOLERANCE) -> PhysicalOrientation.LANDSCAPE_INVERTED - else -> _physicalOrientation.value // Keep current orientation if in transition zone + else -> _physicalOrientation.value // Keep current orientation in transition zone } } } From e000b000438fd510fa1c1b1ff03a2e3a19f3e347 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Nov 2025 13:59:22 +0000 Subject: [PATCH 5/8] feat: Consolidate orientation constraints into single-select dialogs Co-authored-by: sds100 <16245954+sds100@users.noreply.github.com> --- .../constraints/ChooseConstraintViewModel.kt | 74 ++++++++++++++++++- .../base/constraints/ConstraintId.kt | 2 + .../base/constraints/ConstraintUtils.kt | 6 ++ base/src/main/res/values/strings.xml | 2 + 4 files changed, 83 insertions(+), 1 deletion(-) 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 509d683bf1..1d9b86009a 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 @@ -132,6 +132,8 @@ class ChooseConstraintViewModel @Inject constructor( ConstraintId.SCREEN_OFF -> returnResult.emit(ConstraintData.ScreenOff) + ConstraintId.SCREEN_ORIENTATION -> onSelectScreenOrientationConstraint() + ConstraintId.ORIENTATION_PORTRAIT -> returnResult.emit(ConstraintData.OrientationPortrait) @@ -158,6 +160,8 @@ class ChooseConstraintViewModel @Inject constructor( ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_270), ) + ConstraintId.PHYSICAL_ORIENTATION -> onSelectPhysicalOrientationConstraint() + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT -> returnResult.emit( ConstraintData.PhysicalOrientationConstraint( @@ -280,8 +284,76 @@ class ChooseConstraintViewModel @Inject constructor( return cameraLens } + private suspend fun onSelectScreenOrientationConstraint() { + val items = listOf( + ConstraintId.ORIENTATION_PORTRAIT to + getString(R.string.constraint_choose_orientation_portrait), + ConstraintId.ORIENTATION_LANDSCAPE to + getString(R.string.constraint_choose_orientation_landscape), + ConstraintId.ORIENTATION_0 to getString(R.string.constraint_choose_orientation_0), + ConstraintId.ORIENTATION_90 to getString(R.string.constraint_choose_orientation_90), + ConstraintId.ORIENTATION_180 to getString(R.string.constraint_choose_orientation_180), + ConstraintId.ORIENTATION_270 to getString(R.string.constraint_choose_orientation_270), + ) + + val dialog = DialogModel.SingleChoice(items) + val selectedOrientation = showDialog("choose_screen_orientation", dialog) ?: return + + val constraintData = when (selectedOrientation) { + ConstraintId.ORIENTATION_PORTRAIT -> ConstraintData.OrientationPortrait + ConstraintId.ORIENTATION_LANDSCAPE -> ConstraintData.OrientationLandscape + ConstraintId.ORIENTATION_0 -> + ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_0) + ConstraintId.ORIENTATION_90 -> + ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_90) + ConstraintId.ORIENTATION_180 -> + ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_180) + ConstraintId.ORIENTATION_270 -> + ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_270) + else -> return + } + + returnResult.emit(constraintData) + } + + private suspend fun onSelectPhysicalOrientationConstraint() { + val items = listOf( + PhysicalOrientation.PORTRAIT to + getString(R.string.constraint_choose_physical_orientation_portrait), + PhysicalOrientation.LANDSCAPE to + getString(R.string.constraint_choose_physical_orientation_landscape), + PhysicalOrientation.PORTRAIT_INVERTED to + getString(R.string.constraint_choose_physical_orientation_portrait_inverted), + PhysicalOrientation.LANDSCAPE_INVERTED to + getString(R.string.constraint_choose_physical_orientation_landscape_inverted), + ) + + val dialog = DialogModel.SingleChoice(items) + val selectedOrientation = showDialog("choose_physical_orientation", dialog) ?: return + + returnResult.emit( + ConstraintData.PhysicalOrientationConstraint(physicalOrientation = selectedOrientation), + ) + } + private fun buildListGroups(): List = buildList { - val listItems = buildListItems(ConstraintId.entries) + // Filter out individual orientation constraints - show only the consolidated ones + val filteredConstraints = ConstraintId.entries.filter { constraintId -> + constraintId !in listOf( + ConstraintId.ORIENTATION_PORTRAIT, + ConstraintId.ORIENTATION_LANDSCAPE, + ConstraintId.ORIENTATION_0, + ConstraintId.ORIENTATION_90, + ConstraintId.ORIENTATION_180, + ConstraintId.ORIENTATION_270, + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT, + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE, + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED, + ) + } + + val listItems = buildListItems(filteredConstraints) for (category in CATEGORY_ORDER) { val header = getString(ConstraintUtils.getCategoryLabel(category)) 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 bc9e2ce0c5..6f489f8c08 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 @@ -19,6 +19,7 @@ enum class ConstraintId { SCREEN_ON, SCREEN_OFF, + SCREEN_ORIENTATION, ORIENTATION_PORTRAIT, ORIENTATION_LANDSCAPE, ORIENTATION_0, @@ -26,6 +27,7 @@ enum class ConstraintId { ORIENTATION_180, ORIENTATION_270, + PHYSICAL_ORIENTATION, PHYSICAL_ORIENTATION_PORTRAIT, PHYSICAL_ORIENTATION_LANDSCAPE, PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, 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 4a34c65282..774be7058f 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 @@ -64,12 +64,14 @@ object ConstraintUtils { ConstraintId.SCREEN_ON, ConstraintId.SCREEN_OFF, + ConstraintId.SCREEN_ORIENTATION, ConstraintId.ORIENTATION_PORTRAIT, ConstraintId.ORIENTATION_LANDSCAPE, ConstraintId.ORIENTATION_0, ConstraintId.ORIENTATION_90, ConstraintId.ORIENTATION_180, ConstraintId.ORIENTATION_270, + ConstraintId.PHYSICAL_ORIENTATION, ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT, ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE, ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, @@ -131,6 +133,7 @@ object ConstraintUtils { Icons.Outlined.BluetoothDisabled, ) + ConstraintId.SCREEN_ORIENTATION, ConstraintId.ORIENTATION_0, ConstraintId.ORIENTATION_180, -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait) @@ -146,6 +149,7 @@ object ConstraintUtils { Icons.Outlined.StayCurrentPortrait, ) + ConstraintId.PHYSICAL_ORIENTATION, ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT, ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait) @@ -206,12 +210,14 @@ object ConstraintUtils { R.string.constraint_choose_bluetooth_device_disconnected ConstraintId.SCREEN_ON -> R.string.constraint_choose_screen_on_description ConstraintId.SCREEN_OFF -> R.string.constraint_choose_screen_off_description + ConstraintId.SCREEN_ORIENTATION -> R.string.constraint_choose_screen_orientation ConstraintId.ORIENTATION_PORTRAIT -> R.string.constraint_choose_orientation_portrait ConstraintId.ORIENTATION_LANDSCAPE -> R.string.constraint_choose_orientation_landscape ConstraintId.ORIENTATION_0 -> R.string.constraint_choose_orientation_0 ConstraintId.ORIENTATION_90 -> R.string.constraint_choose_orientation_90 ConstraintId.ORIENTATION_180 -> R.string.constraint_choose_orientation_180 ConstraintId.ORIENTATION_270 -> R.string.constraint_choose_orientation_270 + ConstraintId.PHYSICAL_ORIENTATION -> R.string.constraint_choose_physical_orientation ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT -> R.string.constraint_choose_physical_orientation_portrait ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE -> diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index 3372fd5ec7..a8737ed1c0 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -256,6 +256,8 @@ Landscape (270°) Portrait (any) Landscape (any) + Screen orientation + Physical orientation Physical: Portrait Physical: Landscape Physical: Portrait (upside down) From f1a7566169901384a9581f8b60e03c689b5c05f3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Nov 2025 14:40:24 +0000 Subject: [PATCH 6/8] refactor: Rename orientation constraints to DISPLAY_ORIENTATION_* and PhysicalOrientationConstraint to PhysicalOrientation Co-authored-by: sds100 <16245954+sds100@users.noreply.github.com> --- .../constraints/ChooseConstraintViewModel.kt | 119 ++++++++++++------ .../keymapper/base/constraints/Constraint.kt | 34 ++--- .../base/constraints/ConstraintId.kt | 14 +-- .../base/constraints/ConstraintSnapshot.kt | 2 +- .../base/constraints/ConstraintUiHelper.kt | 2 +- .../base/constraints/ConstraintUtils.kt | 42 +++---- .../base/utils/TestConstraintSnapshot.kt | 2 +- 7 files changed, 124 insertions(+), 91 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 1d9b86009a..889671c618 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 @@ -1,5 +1,7 @@ package io.github.sds100.keymapper.base.constraints +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.StayCurrentPortrait import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -15,6 +17,7 @@ import io.github.sds100.keymapper.base.utils.navigation.navigate import io.github.sds100.keymapper.base.utils.ui.DialogModel import io.github.sds100.keymapper.base.utils.ui.DialogProvider import io.github.sds100.keymapper.base.utils.ui.ResourceProvider +import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemGroup import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemModel import io.github.sds100.keymapper.base.utils.ui.showDialog @@ -47,6 +50,10 @@ class ChooseConstraintViewModel @Inject constructor( NavigationProvider by navigationProvider { companion object { + // Synthetic IDs for consolidated orientation list items (not actual ConstraintIds) + private const val DISPLAY_ORIENTATION_LIST_ITEM_ID = "display_orientation" + private const val PHYSICAL_ORIENTATION_LIST_ITEM_ID = "physical_orientation" + private val CATEGORY_ORDER = arrayOf( ConstraintCategory.APPS, ConstraintCategory.MEDIA, @@ -112,6 +119,18 @@ class ChooseConstraintViewModel @Inject constructor( fun onListItemClick(id: String) { viewModelScope.launch { + // Handle synthetic list item IDs for consolidated orientation constraints + when (id) { + DISPLAY_ORIENTATION_LIST_ITEM_ID -> { + onSelectDisplayOrientationConstraint() + return@launch + } + PHYSICAL_ORIENTATION_LIST_ITEM_ID -> { + onSelectPhysicalOrientationConstraint() + return@launch + } + } + when (val constraintType = ConstraintId.valueOf(id)) { ConstraintId.APP_IN_FOREGROUND, ConstraintId.APP_NOT_IN_FOREGROUND, @@ -132,60 +151,56 @@ class ChooseConstraintViewModel @Inject constructor( ConstraintId.SCREEN_OFF -> returnResult.emit(ConstraintData.ScreenOff) - ConstraintId.SCREEN_ORIENTATION -> onSelectScreenOrientationConstraint() - - ConstraintId.ORIENTATION_PORTRAIT -> + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT -> returnResult.emit(ConstraintData.OrientationPortrait) - ConstraintId.ORIENTATION_LANDSCAPE -> + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE -> returnResult.emit(ConstraintData.OrientationLandscape) - ConstraintId.ORIENTATION_0 -> + ConstraintId.DISPLAY_ORIENTATION_0 -> returnResult.emit( ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_0), ) - ConstraintId.ORIENTATION_90 -> + ConstraintId.DISPLAY_ORIENTATION_90 -> returnResult.emit( ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_90), ) - ConstraintId.ORIENTATION_180 -> + ConstraintId.DISPLAY_ORIENTATION_180 -> returnResult.emit( ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_180), ) - ConstraintId.ORIENTATION_270 -> + ConstraintId.DISPLAY_ORIENTATION_270 -> returnResult.emit( ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_270), ) - ConstraintId.PHYSICAL_ORIENTATION -> onSelectPhysicalOrientationConstraint() - ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT -> returnResult.emit( - ConstraintData.PhysicalOrientationConstraint( + ConstraintData.PhysicalOrientation( physicalOrientation = PhysicalOrientation.PORTRAIT, ), ) ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE -> returnResult.emit( - ConstraintData.PhysicalOrientationConstraint( + ConstraintData.PhysicalOrientation( physicalOrientation = PhysicalOrientation.LANDSCAPE, ), ) ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED -> returnResult.emit( - ConstraintData.PhysicalOrientationConstraint( + ConstraintData.PhysicalOrientation( physicalOrientation = PhysicalOrientation.PORTRAIT_INVERTED, ), ) ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED -> returnResult.emit( - ConstraintData.PhysicalOrientationConstraint( + ConstraintData.PhysicalOrientation( physicalOrientation = PhysicalOrientation.LANDSCAPE_INVERTED, ), ) @@ -284,31 +299,31 @@ class ChooseConstraintViewModel @Inject constructor( return cameraLens } - private suspend fun onSelectScreenOrientationConstraint() { + private suspend fun onSelectDisplayOrientationConstraint() { val items = listOf( - ConstraintId.ORIENTATION_PORTRAIT to + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT to getString(R.string.constraint_choose_orientation_portrait), - ConstraintId.ORIENTATION_LANDSCAPE to + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE to getString(R.string.constraint_choose_orientation_landscape), - ConstraintId.ORIENTATION_0 to getString(R.string.constraint_choose_orientation_0), - ConstraintId.ORIENTATION_90 to getString(R.string.constraint_choose_orientation_90), - ConstraintId.ORIENTATION_180 to getString(R.string.constraint_choose_orientation_180), - ConstraintId.ORIENTATION_270 to getString(R.string.constraint_choose_orientation_270), + ConstraintId.DISPLAY_ORIENTATION_0 to getString(R.string.constraint_choose_orientation_0), + ConstraintId.DISPLAY_ORIENTATION_90 to getString(R.string.constraint_choose_orientation_90), + ConstraintId.DISPLAY_ORIENTATION_180 to getString(R.string.constraint_choose_orientation_180), + ConstraintId.DISPLAY_ORIENTATION_270 to getString(R.string.constraint_choose_orientation_270), ) val dialog = DialogModel.SingleChoice(items) - val selectedOrientation = showDialog("choose_screen_orientation", dialog) ?: return + val selectedOrientation = showDialog("choose_display_orientation", dialog) ?: return val constraintData = when (selectedOrientation) { - ConstraintId.ORIENTATION_PORTRAIT -> ConstraintData.OrientationPortrait - ConstraintId.ORIENTATION_LANDSCAPE -> ConstraintData.OrientationLandscape - ConstraintId.ORIENTATION_0 -> + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT -> ConstraintData.OrientationPortrait + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE -> ConstraintData.OrientationLandscape + ConstraintId.DISPLAY_ORIENTATION_0 -> ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_0) - ConstraintId.ORIENTATION_90 -> + ConstraintId.DISPLAY_ORIENTATION_90 -> ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_90) - ConstraintId.ORIENTATION_180 -> + ConstraintId.DISPLAY_ORIENTATION_180 -> ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_180) - ConstraintId.ORIENTATION_270 -> + ConstraintId.DISPLAY_ORIENTATION_270 -> ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_270) else -> return } @@ -332,7 +347,7 @@ class ChooseConstraintViewModel @Inject constructor( val selectedOrientation = showDialog("choose_physical_orientation", dialog) ?: return returnResult.emit( - ConstraintData.PhysicalOrientationConstraint(physicalOrientation = selectedOrientation), + ConstraintData.PhysicalOrientation(physicalOrientation = selectedOrientation), ) } @@ -340,12 +355,12 @@ class ChooseConstraintViewModel @Inject constructor( // Filter out individual orientation constraints - show only the consolidated ones val filteredConstraints = ConstraintId.entries.filter { constraintId -> constraintId !in listOf( - ConstraintId.ORIENTATION_PORTRAIT, - ConstraintId.ORIENTATION_LANDSCAPE, - ConstraintId.ORIENTATION_0, - ConstraintId.ORIENTATION_90, - ConstraintId.ORIENTATION_180, - ConstraintId.ORIENTATION_270, + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT, + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE, + ConstraintId.DISPLAY_ORIENTATION_0, + ConstraintId.DISPLAY_ORIENTATION_90, + ConstraintId.DISPLAY_ORIENTATION_180, + ConstraintId.DISPLAY_ORIENTATION_270, ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT, ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE, ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, @@ -355,15 +370,41 @@ class ChooseConstraintViewModel @Inject constructor( val listItems = buildListItems(filteredConstraints) + // Add synthetic orientation list items + val displayOrientationItem = SimpleListItemModel( + id = DISPLAY_ORIENTATION_LIST_ITEM_ID, + title = getString(R.string.constraint_choose_screen_orientation), + icon = ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait), + isEnabled = true, + ) + + val physicalOrientationItem = SimpleListItemModel( + id = PHYSICAL_ORIENTATION_LIST_ITEM_ID, + title = getString(R.string.constraint_choose_physical_orientation), + icon = ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait), + isEnabled = true, + ) + for (category in CATEGORY_ORDER) { val header = getString(ConstraintUtils.getCategoryLabel(category)) + val categoryItems = listItems.filter { item -> + item.isEnabled && try { + ConstraintUtils.getCategory(ConstraintId.valueOf(item.id)) == category + } catch (e: IllegalArgumentException) { + false + } + }.toMutableList() + + // Add synthetic orientation items to DISPLAY category + if (category == ConstraintCategory.DISPLAY) { + categoryItems.add(displayOrientationItem) + categoryItems.add(physicalOrientationItem) + } + val group = SimpleListItemGroup( header, - items = listItems.filter { - it.isEnabled && - ConstraintUtils.getCategory(ConstraintId.valueOf(it.id)) == category - }, + items = categoryItems, ) if (group.items.isNotEmpty()) { 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 2ce1cf7ff2..9b7f91573d 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 @@ -70,33 +70,33 @@ sealed class ConstraintData { @Serializable data object OrientationPortrait : ConstraintData() { - override val id: ConstraintId = ConstraintId.ORIENTATION_PORTRAIT + override val id: ConstraintId = ConstraintId.DISPLAY_ORIENTATION_PORTRAIT } @Serializable data object OrientationLandscape : ConstraintData() { - override val id: ConstraintId = ConstraintId.ORIENTATION_LANDSCAPE + override val id: ConstraintId = ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE } @Serializable data class OrientationCustom(val orientation: Orientation) : ConstraintData() { override val id: ConstraintId = when (orientation) { - Orientation.ORIENTATION_0 -> ConstraintId.ORIENTATION_0 - Orientation.ORIENTATION_90 -> ConstraintId.ORIENTATION_90 - Orientation.ORIENTATION_180 -> ConstraintId.ORIENTATION_180 - Orientation.ORIENTATION_270 -> ConstraintId.ORIENTATION_270 + Orientation.ORIENTATION_0 -> ConstraintId.DISPLAY_ORIENTATION_0 + Orientation.ORIENTATION_90 -> ConstraintId.DISPLAY_ORIENTATION_90 + Orientation.ORIENTATION_180 -> ConstraintId.DISPLAY_ORIENTATION_180 + Orientation.ORIENTATION_270 -> ConstraintId.DISPLAY_ORIENTATION_270 } } @Serializable - data class PhysicalOrientationConstraint( - val physicalOrientation: PhysicalOrientation, + data class PhysicalOrientation( + val physicalOrientation: io.github.sds100.keymapper.common.utils.PhysicalOrientation, ) : ConstraintData() { override val id: ConstraintId = when (physicalOrientation) { - PhysicalOrientation.PORTRAIT -> ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT - PhysicalOrientation.LANDSCAPE -> ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE - PhysicalOrientation.PORTRAIT_INVERTED -> ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED - PhysicalOrientation.LANDSCAPE_INVERTED -> ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED + io.github.sds100.keymapper.common.utils.PhysicalOrientation.PORTRAIT -> ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT + io.github.sds100.keymapper.common.utils.PhysicalOrientation.LANDSCAPE -> ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE + io.github.sds100.keymapper.common.utils.PhysicalOrientation.PORTRAIT_INVERTED -> ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED + io.github.sds100.keymapper.common.utils.PhysicalOrientation.LANDSCAPE_INVERTED -> ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED } } @@ -330,13 +330,13 @@ object ConstraintEntityMapper { ConstraintEntity.ORIENTATION_LANDSCAPE -> ConstraintData.OrientationLandscape ConstraintEntity.PHYSICAL_ORIENTATION_PORTRAIT -> - ConstraintData.PhysicalOrientationConstraint(PhysicalOrientation.PORTRAIT) + ConstraintData.PhysicalOrientation(PhysicalOrientation.PORTRAIT) ConstraintEntity.PHYSICAL_ORIENTATION_LANDSCAPE -> - ConstraintData.PhysicalOrientationConstraint(PhysicalOrientation.LANDSCAPE) + ConstraintData.PhysicalOrientation(PhysicalOrientation.LANDSCAPE) ConstraintEntity.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED -> - ConstraintData.PhysicalOrientationConstraint(PhysicalOrientation.PORTRAIT_INVERTED) + ConstraintData.PhysicalOrientation(PhysicalOrientation.PORTRAIT_INVERTED) ConstraintEntity.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED -> - ConstraintData.PhysicalOrientationConstraint(PhysicalOrientation.LANDSCAPE_INVERTED) + ConstraintData.PhysicalOrientation(PhysicalOrientation.LANDSCAPE_INVERTED) ConstraintEntity.SCREEN_OFF -> ConstraintData.ScreenOff ConstraintEntity.SCREEN_ON -> ConstraintData.ScreenOn @@ -521,7 +521,7 @@ object ConstraintEntityMapper { ConstraintEntity.ORIENTATION_PORTRAIT, ) - is ConstraintData.PhysicalOrientationConstraint -> when (constraint.data.physicalOrientation) { + is ConstraintData.PhysicalOrientation -> when (constraint.data.physicalOrientation) { PhysicalOrientation.PORTRAIT -> ConstraintEntity( uid = constraint.uid, ConstraintEntity.PHYSICAL_ORIENTATION_PORTRAIT, 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 6f489f8c08..06463c6fd7 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 @@ -19,15 +19,13 @@ enum class ConstraintId { SCREEN_ON, SCREEN_OFF, - SCREEN_ORIENTATION, - ORIENTATION_PORTRAIT, - ORIENTATION_LANDSCAPE, - ORIENTATION_0, - ORIENTATION_90, - ORIENTATION_180, - ORIENTATION_270, + DISPLAY_ORIENTATION_PORTRAIT, + DISPLAY_ORIENTATION_LANDSCAPE, + DISPLAY_ORIENTATION_0, + DISPLAY_ORIENTATION_90, + DISPLAY_ORIENTATION_180, + DISPLAY_ORIENTATION_270, - PHYSICAL_ORIENTATION, PHYSICAL_ORIENTATION_PORTRAIT, PHYSICAL_ORIENTATION_LANDSCAPE, PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, 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 9121d714c3..2ed66aec8b 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 @@ -119,7 +119,7 @@ class LazyConstraintSnapshot( orientation == Orientation.ORIENTATION_0 || orientation == Orientation.ORIENTATION_180 - is ConstraintData.PhysicalOrientationConstraint -> + is ConstraintData.PhysicalOrientation -> physicalOrientation == constraint.data.physicalOrientation is ConstraintData.ScreenOff -> !isScreenOn 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 a36800578c..94b02aebc9 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 @@ -83,7 +83,7 @@ class ConstraintUiHelper( is ConstraintData.OrientationPortrait -> getString(R.string.constraint_choose_orientation_portrait) - is ConstraintData.PhysicalOrientationConstraint -> { + is ConstraintData.PhysicalOrientation -> { val resId = when (constraint.data.physicalOrientation) { PhysicalOrientation.PORTRAIT -> R.string.constraint_choose_physical_orientation_portrait 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 774be7058f..29377c3533 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 @@ -64,14 +64,12 @@ object ConstraintUtils { ConstraintId.SCREEN_ON, ConstraintId.SCREEN_OFF, - ConstraintId.SCREEN_ORIENTATION, - ConstraintId.ORIENTATION_PORTRAIT, - ConstraintId.ORIENTATION_LANDSCAPE, - ConstraintId.ORIENTATION_0, - ConstraintId.ORIENTATION_90, - ConstraintId.ORIENTATION_180, - ConstraintId.ORIENTATION_270, - ConstraintId.PHYSICAL_ORIENTATION, + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT, + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE, + ConstraintId.DISPLAY_ORIENTATION_0, + ConstraintId.DISPLAY_ORIENTATION_90, + ConstraintId.DISPLAY_ORIENTATION_180, + ConstraintId.DISPLAY_ORIENTATION_270, ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT, ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE, ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, @@ -133,23 +131,21 @@ object ConstraintUtils { Icons.Outlined.BluetoothDisabled, ) - ConstraintId.SCREEN_ORIENTATION, - ConstraintId.ORIENTATION_0, - ConstraintId.ORIENTATION_180, + ConstraintId.DISPLAY_ORIENTATION_0, + ConstraintId.DISPLAY_ORIENTATION_180, -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait) - ConstraintId.ORIENTATION_90, - ConstraintId.ORIENTATION_270, + ConstraintId.DISPLAY_ORIENTATION_90, + ConstraintId.DISPLAY_ORIENTATION_270, -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentLandscape) - ConstraintId.ORIENTATION_LANDSCAPE -> ComposeIconInfo.Vector( + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE -> ComposeIconInfo.Vector( Icons.Outlined.StayCurrentLandscape, ) - ConstraintId.ORIENTATION_PORTRAIT -> ComposeIconInfo.Vector( + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT -> ComposeIconInfo.Vector( Icons.Outlined.StayCurrentPortrait, ) - ConstraintId.PHYSICAL_ORIENTATION, ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT, ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait) @@ -210,14 +206,12 @@ object ConstraintUtils { R.string.constraint_choose_bluetooth_device_disconnected ConstraintId.SCREEN_ON -> R.string.constraint_choose_screen_on_description ConstraintId.SCREEN_OFF -> R.string.constraint_choose_screen_off_description - ConstraintId.SCREEN_ORIENTATION -> R.string.constraint_choose_screen_orientation - ConstraintId.ORIENTATION_PORTRAIT -> R.string.constraint_choose_orientation_portrait - ConstraintId.ORIENTATION_LANDSCAPE -> R.string.constraint_choose_orientation_landscape - ConstraintId.ORIENTATION_0 -> R.string.constraint_choose_orientation_0 - ConstraintId.ORIENTATION_90 -> R.string.constraint_choose_orientation_90 - ConstraintId.ORIENTATION_180 -> R.string.constraint_choose_orientation_180 - ConstraintId.ORIENTATION_270 -> R.string.constraint_choose_orientation_270 - ConstraintId.PHYSICAL_ORIENTATION -> R.string.constraint_choose_physical_orientation + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT -> R.string.constraint_choose_orientation_portrait + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE -> R.string.constraint_choose_orientation_landscape + ConstraintId.DISPLAY_ORIENTATION_0 -> R.string.constraint_choose_orientation_0 + ConstraintId.DISPLAY_ORIENTATION_90 -> R.string.constraint_choose_orientation_90 + ConstraintId.DISPLAY_ORIENTATION_180 -> R.string.constraint_choose_orientation_180 + ConstraintId.DISPLAY_ORIENTATION_270 -> R.string.constraint_choose_orientation_270 ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT -> R.string.constraint_choose_physical_orientation_portrait ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE -> 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 cffe18010a..be77315302 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 @@ -64,7 +64,7 @@ class TestConstraintSnapshot( orientation == Orientation.ORIENTATION_0 || orientation == Orientation.ORIENTATION_180 - is ConstraintData.PhysicalOrientationConstraint -> + is ConstraintData.PhysicalOrientation -> physicalOrientation == data.physicalOrientation is ConstraintData.ScreenOff -> !isScreenOn From 19bcdfb3356a8d36a98d5acc2b5fc77460b61455 Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 2 Dec 2025 20:40:01 +0100 Subject: [PATCH 7/8] #1911 feat: constraint for physical device orientation that ignores auto rotate setting --- CHANGELOG.md | 1 + .../base/sorting/comparators/KeyMapConstraintsComparator.kt | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66a87dd241..cb69c38418 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - #1915 ask user to remove "adb shell" from Shell command. - #1904 inform the user how to enable the accessibility service with PRO mode or ADB. +- #1911 constraint for physical device orientation that ignores auto rotate setting. ## Bug fixes 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 bb53bd5f66..a48843773b 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 @@ -140,6 +140,9 @@ class KeyMapConstraintsComparator( ConstraintData.HingeOpen -> Success("") ConstraintData.KeyboardNotShowing -> Success("") ConstraintData.KeyboardShowing -> Success("") + is ConstraintData.PhysicalOrientation -> Success( + constraint.data.physicalOrientation.toString(), + ) } } } From 119b77650a87a3a231d08fbd4e1a4ebb913eea27 Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 2 Dec 2025 20:40:18 +0100 Subject: [PATCH 8/8] style: reformat --- .../constraints/ChooseConstraintViewModel.kt | 23 +++++++++++-------- .../base/constraints/ConstraintSnapshot.kt | 4 +++- 2 files changed, 17 insertions(+), 10 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 889671c618..836834e723 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 @@ -305,10 +305,14 @@ class ChooseConstraintViewModel @Inject constructor( getString(R.string.constraint_choose_orientation_portrait), ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE to getString(R.string.constraint_choose_orientation_landscape), - ConstraintId.DISPLAY_ORIENTATION_0 to getString(R.string.constraint_choose_orientation_0), - ConstraintId.DISPLAY_ORIENTATION_90 to getString(R.string.constraint_choose_orientation_90), - ConstraintId.DISPLAY_ORIENTATION_180 to getString(R.string.constraint_choose_orientation_180), - ConstraintId.DISPLAY_ORIENTATION_270 to getString(R.string.constraint_choose_orientation_270), + ConstraintId.DISPLAY_ORIENTATION_0 to + getString(R.string.constraint_choose_orientation_0), + ConstraintId.DISPLAY_ORIENTATION_90 to + getString(R.string.constraint_choose_orientation_90), + ConstraintId.DISPLAY_ORIENTATION_180 to + getString(R.string.constraint_choose_orientation_180), + ConstraintId.DISPLAY_ORIENTATION_270 to + getString(R.string.constraint_choose_orientation_270), ) val dialog = DialogModel.SingleChoice(items) @@ -389,11 +393,12 @@ class ChooseConstraintViewModel @Inject constructor( val header = getString(ConstraintUtils.getCategoryLabel(category)) val categoryItems = listItems.filter { item -> - item.isEnabled && try { - ConstraintUtils.getCategory(ConstraintId.valueOf(item.id)) == category - } catch (e: IllegalArgumentException) { - false - } + item.isEnabled && + try { + ConstraintUtils.getCategory(ConstraintId.valueOf(item.id)) == category + } catch (e: IllegalArgumentException) { + false + } }.toMutableList() // Add synthetic orientation items to DISPLAY category 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 2ed66aec8b..00ac3781ee 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 @@ -44,7 +44,9 @@ class LazyConstraintSnapshot( devicesAdapter.connectedBluetoothDevices.value } private val orientation: Orientation by lazy { displayAdapter.cachedOrientation } - private val physicalOrientation: PhysicalOrientation by lazy { displayAdapter.cachedPhysicalOrientation } + private val physicalOrientation: PhysicalOrientation by lazy { + displayAdapter.cachedPhysicalOrientation + } private val isScreenOn: Boolean by lazy { displayAdapter.isScreenOn.firstBlocking() } private val appsPlayingMedia: List by lazy { mediaAdapter.getActiveMediaSessionPackages()