11package io.github.sds100.keymapper.base.constraints
22
3+ import androidx.compose.material.icons.Icons
4+ import androidx.compose.material.icons.outlined.StayCurrentPortrait
35import androidx.compose.runtime.getValue
46import androidx.compose.runtime.mutableStateOf
57import androidx.compose.runtime.setValue
@@ -15,10 +17,12 @@ import io.github.sds100.keymapper.base.utils.navigation.navigate
1517import io.github.sds100.keymapper.base.utils.ui.DialogModel
1618import io.github.sds100.keymapper.base.utils.ui.DialogProvider
1719import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
20+ import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo
1821import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemGroup
1922import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemModel
2023import io.github.sds100.keymapper.base.utils.ui.showDialog
2124import io.github.sds100.keymapper.common.utils.Orientation
25+ import io.github.sds100.keymapper.common.utils.PhysicalOrientation
2226import io.github.sds100.keymapper.common.utils.State
2327import io.github.sds100.keymapper.system.camera.CameraLens
2428import javax.inject.Inject
@@ -46,6 +50,10 @@ class ChooseConstraintViewModel @Inject constructor(
4650 NavigationProvider by navigationProvider {
4751
4852 companion object {
53+ // Synthetic IDs for consolidated orientation list items (not actual ConstraintIds)
54+ private const val DISPLAY_ORIENTATION_LIST_ITEM_ID = " display_orientation"
55+ private const val PHYSICAL_ORIENTATION_LIST_ITEM_ID = " physical_orientation"
56+
4957 private val CATEGORY_ORDER = arrayOf(
5058 ConstraintCategory .APPS ,
5159 ConstraintCategory .MEDIA ,
@@ -111,6 +119,18 @@ class ChooseConstraintViewModel @Inject constructor(
111119
112120 fun onListItemClick (id : String ) {
113121 viewModelScope.launch {
122+ // Handle synthetic list item IDs for consolidated orientation constraints
123+ when (id) {
124+ DISPLAY_ORIENTATION_LIST_ITEM_ID -> {
125+ onSelectDisplayOrientationConstraint()
126+ return @launch
127+ }
128+ PHYSICAL_ORIENTATION_LIST_ITEM_ID -> {
129+ onSelectPhysicalOrientationConstraint()
130+ return @launch
131+ }
132+ }
133+
114134 when (val constraintType = ConstraintId .valueOf(id)) {
115135 ConstraintId .APP_IN_FOREGROUND ,
116136 ConstraintId .APP_NOT_IN_FOREGROUND ,
@@ -131,32 +151,60 @@ class ChooseConstraintViewModel @Inject constructor(
131151
132152 ConstraintId .SCREEN_OFF -> returnResult.emit(ConstraintData .ScreenOff )
133153
134- ConstraintId .ORIENTATION_PORTRAIT ->
154+ ConstraintId .DISPLAY_ORIENTATION_PORTRAIT ->
135155 returnResult.emit(ConstraintData .OrientationPortrait )
136156
137- ConstraintId .ORIENTATION_LANDSCAPE ->
157+ ConstraintId .DISPLAY_ORIENTATION_LANDSCAPE ->
138158 returnResult.emit(ConstraintData .OrientationLandscape )
139159
140- ConstraintId .ORIENTATION_0 ->
160+ ConstraintId .DISPLAY_ORIENTATION_0 ->
141161 returnResult.emit(
142162 ConstraintData .OrientationCustom (orientation = Orientation .ORIENTATION_0 ),
143163 )
144164
145- ConstraintId .ORIENTATION_90 ->
165+ ConstraintId .DISPLAY_ORIENTATION_90 ->
146166 returnResult.emit(
147167 ConstraintData .OrientationCustom (orientation = Orientation .ORIENTATION_90 ),
148168 )
149169
150- ConstraintId .ORIENTATION_180 ->
170+ ConstraintId .DISPLAY_ORIENTATION_180 ->
151171 returnResult.emit(
152172 ConstraintData .OrientationCustom (orientation = Orientation .ORIENTATION_180 ),
153173 )
154174
155- ConstraintId .ORIENTATION_270 ->
175+ ConstraintId .DISPLAY_ORIENTATION_270 ->
156176 returnResult.emit(
157177 ConstraintData .OrientationCustom (orientation = Orientation .ORIENTATION_270 ),
158178 )
159179
180+ ConstraintId .PHYSICAL_ORIENTATION_PORTRAIT ->
181+ returnResult.emit(
182+ ConstraintData .PhysicalOrientation (
183+ physicalOrientation = PhysicalOrientation .PORTRAIT ,
184+ ),
185+ )
186+
187+ ConstraintId .PHYSICAL_ORIENTATION_LANDSCAPE ->
188+ returnResult.emit(
189+ ConstraintData .PhysicalOrientation (
190+ physicalOrientation = PhysicalOrientation .LANDSCAPE ,
191+ ),
192+ )
193+
194+ ConstraintId .PHYSICAL_ORIENTATION_PORTRAIT_INVERTED ->
195+ returnResult.emit(
196+ ConstraintData .PhysicalOrientation (
197+ physicalOrientation = PhysicalOrientation .PORTRAIT_INVERTED ,
198+ ),
199+ )
200+
201+ ConstraintId .PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED ->
202+ returnResult.emit(
203+ ConstraintData .PhysicalOrientation (
204+ physicalOrientation = PhysicalOrientation .LANDSCAPE_INVERTED ,
205+ ),
206+ )
207+
160208 ConstraintId .FLASHLIGHT_ON -> {
161209 val lens = chooseFlashlightLens() ? : return @launch
162210 returnResult.emit(ConstraintData .FlashlightOn (lens = lens))
@@ -251,18 +299,117 @@ class ChooseConstraintViewModel @Inject constructor(
251299 return cameraLens
252300 }
253301
302+ private suspend fun onSelectDisplayOrientationConstraint () {
303+ val items = listOf (
304+ ConstraintId .DISPLAY_ORIENTATION_PORTRAIT to
305+ getString(R .string.constraint_choose_orientation_portrait),
306+ ConstraintId .DISPLAY_ORIENTATION_LANDSCAPE to
307+ getString(R .string.constraint_choose_orientation_landscape),
308+ ConstraintId .DISPLAY_ORIENTATION_0 to
309+ getString(R .string.constraint_choose_orientation_0),
310+ ConstraintId .DISPLAY_ORIENTATION_90 to
311+ getString(R .string.constraint_choose_orientation_90),
312+ ConstraintId .DISPLAY_ORIENTATION_180 to
313+ getString(R .string.constraint_choose_orientation_180),
314+ ConstraintId .DISPLAY_ORIENTATION_270 to
315+ getString(R .string.constraint_choose_orientation_270),
316+ )
317+
318+ val dialog = DialogModel .SingleChoice (items)
319+ val selectedOrientation = showDialog(" choose_display_orientation" , dialog) ? : return
320+
321+ val constraintData = when (selectedOrientation) {
322+ ConstraintId .DISPLAY_ORIENTATION_PORTRAIT -> ConstraintData .OrientationPortrait
323+ ConstraintId .DISPLAY_ORIENTATION_LANDSCAPE -> ConstraintData .OrientationLandscape
324+ ConstraintId .DISPLAY_ORIENTATION_0 ->
325+ ConstraintData .OrientationCustom (orientation = Orientation .ORIENTATION_0 )
326+ ConstraintId .DISPLAY_ORIENTATION_90 ->
327+ ConstraintData .OrientationCustom (orientation = Orientation .ORIENTATION_90 )
328+ ConstraintId .DISPLAY_ORIENTATION_180 ->
329+ ConstraintData .OrientationCustom (orientation = Orientation .ORIENTATION_180 )
330+ ConstraintId .DISPLAY_ORIENTATION_270 ->
331+ ConstraintData .OrientationCustom (orientation = Orientation .ORIENTATION_270 )
332+ else -> return
333+ }
334+
335+ returnResult.emit(constraintData)
336+ }
337+
338+ private suspend fun onSelectPhysicalOrientationConstraint () {
339+ val items = listOf (
340+ PhysicalOrientation .PORTRAIT to
341+ getString(R .string.constraint_choose_physical_orientation_portrait),
342+ PhysicalOrientation .LANDSCAPE to
343+ getString(R .string.constraint_choose_physical_orientation_landscape),
344+ PhysicalOrientation .PORTRAIT_INVERTED to
345+ getString(R .string.constraint_choose_physical_orientation_portrait_inverted),
346+ PhysicalOrientation .LANDSCAPE_INVERTED to
347+ getString(R .string.constraint_choose_physical_orientation_landscape_inverted),
348+ )
349+
350+ val dialog = DialogModel .SingleChoice (items)
351+ val selectedOrientation = showDialog(" choose_physical_orientation" , dialog) ? : return
352+
353+ returnResult.emit(
354+ ConstraintData .PhysicalOrientation (physicalOrientation = selectedOrientation),
355+ )
356+ }
357+
254358 private fun buildListGroups (): List <SimpleListItemGroup > = buildList {
255- val listItems = buildListItems(ConstraintId .entries)
359+ // Filter out individual orientation constraints - show only the consolidated ones
360+ val filteredConstraints = ConstraintId .entries.filter { constraintId ->
361+ constraintId !in listOf (
362+ ConstraintId .DISPLAY_ORIENTATION_PORTRAIT ,
363+ ConstraintId .DISPLAY_ORIENTATION_LANDSCAPE ,
364+ ConstraintId .DISPLAY_ORIENTATION_0 ,
365+ ConstraintId .DISPLAY_ORIENTATION_90 ,
366+ ConstraintId .DISPLAY_ORIENTATION_180 ,
367+ ConstraintId .DISPLAY_ORIENTATION_270 ,
368+ ConstraintId .PHYSICAL_ORIENTATION_PORTRAIT ,
369+ ConstraintId .PHYSICAL_ORIENTATION_LANDSCAPE ,
370+ ConstraintId .PHYSICAL_ORIENTATION_PORTRAIT_INVERTED ,
371+ ConstraintId .PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED ,
372+ )
373+ }
374+
375+ val listItems = buildListItems(filteredConstraints)
376+
377+ // Add synthetic orientation list items
378+ val displayOrientationItem = SimpleListItemModel (
379+ id = DISPLAY_ORIENTATION_LIST_ITEM_ID ,
380+ title = getString(R .string.constraint_choose_screen_orientation),
381+ icon = ComposeIconInfo .Vector (Icons .Outlined .StayCurrentPortrait ),
382+ isEnabled = true ,
383+ )
384+
385+ val physicalOrientationItem = SimpleListItemModel (
386+ id = PHYSICAL_ORIENTATION_LIST_ITEM_ID ,
387+ title = getString(R .string.constraint_choose_physical_orientation),
388+ icon = ComposeIconInfo .Vector (Icons .Outlined .StayCurrentPortrait ),
389+ isEnabled = true ,
390+ )
256391
257392 for (category in CATEGORY_ORDER ) {
258393 val header = getString(ConstraintUtils .getCategoryLabel(category))
259394
395+ val categoryItems = listItems.filter { item ->
396+ item.isEnabled &&
397+ try {
398+ ConstraintUtils .getCategory(ConstraintId .valueOf(item.id)) == category
399+ } catch (e: IllegalArgumentException ) {
400+ false
401+ }
402+ }.toMutableList()
403+
404+ // Add synthetic orientation items to DISPLAY category
405+ if (category == ConstraintCategory .DISPLAY ) {
406+ categoryItems.add(displayOrientationItem)
407+ categoryItems.add(physicalOrientationItem)
408+ }
409+
260410 val group = SimpleListItemGroup (
261411 header,
262- items = listItems.filter {
263- it.isEnabled &&
264- ConstraintUtils .getCategory(ConstraintId .valueOf(it.id)) == category
265- },
412+ items = categoryItems,
266413 )
267414
268415 if (group.items.isNotEmpty()) {
0 commit comments