diff --git a/CHANGELOG.md b/CHANGELOG.md index a2cb693525..2555a723e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,33 +1,54 @@ +## [4.3.0](https://github.com/sds100/KeyMapper/releases/tag/v4.3.0) + +#### 13 July 2026 + +## Added + +- #2184 Add a display resolution constraint. Pick from the display's supported resolutions or enter a + custom width and height. +- #2163 Add ringer mode constraints (Ring, Vibrate, Silent). +- #2174 Add "Do not remap by default" preference to the default options settings page. + +## Fixed +- #2187 App constraints do not work with floating buttons + ## [4.2.1](https://github.com/sds100/KeyMapper/releases/tag/v4.2.1) #### 09 June 2026 +## Added + +- #2140 Add monochrome app icon layer for themed icon support on Android 13+. + ## Fixed -- #2154 The expert mode debug screen is now only accessible after the expert mode warning has been acknowledged. -- #2156 Do not throw an error when the Talkback application can not be found because there are many different package names out there. -- #2153 Prevent Direct Boot startup from initializing credential-encrypted app storage before the user unlocks. -- #2157 The "choose setting" screen now uses `settings list` via the system bridge when expert mode is active, surfacing all device settings instead of only those visible through the ContentProvider. +- #2154 The expert mode debug screen is now only accessible after the expert mode warning has been + acknowledged. +- #2156 Do not throw an error when the Talkback application can not be found because there are many + different package names out there. +- #2153 Prevent Direct Boot startup from initializing credential-encrypted app storage before the + user unlocks. +- #2157 The "choose setting" screen now uses `settings list` via the system bridge when expert mode + is active, surfacing all device settings instead of only those visible through the + ContentProvider. ## [4.2.0](https://github.com/sds100/KeyMapper/releases/tag/v4.2.0) #### 03 June 2026 -## Added - -- #2140 Add monochrome app icon layer for themed icon support on Android 13+. - ## Fixed -- #2074 Scrolling the action or trigger list no longer accidentally moves items; reordering by drag now only activates from the drag handle or via long-press. +- #2074 Scrolling the action or trigger list no longer accidentally moves items; reordering by drag + now only activates from the drag handle or via long-press. ## Changed -- #1369 Add content descriptions to drag handles and custom "Move up"/"Move down" accessibility actions for trigger and action list items, improving TalkBack support for reordering. -- #262 Add "TalkBack gesture" action to simulate TalkBack navigation gestures (swipes, multi-finger taps, and multi-directional swipes). +- #1369 Add content descriptions to drag handles and custom "Move up"/"Move down" accessibility + actions for trigger and action list items, improving TalkBack support for reordering. +- #262 Add "TalkBack gesture" action to simulate TalkBack navigation gestures (swipes, multi-finger + taps, and multi-directional swipes). - #2076 Use "any input device" as the default for triggers. - ## [4.1.0](https://github.com/sds100/KeyMapper/releases/tag/v4.1.0) #### 10 May 2026 @@ -36,11 +57,13 @@ - #2067 add action to select all text in the focused field. - #2045 add action to input on-screen keyboard enter/send button. -- #2106 disable the keyboard auto-switching setting when manually switching the keyboard in the Key Mapper homescreen menu. +- #2106 disable the keyboard auto-switching setting when manually switching the keyboard in the Key + Mapper homescreen menu. - #1029 add action to show a toast message. - #2081 add getevent debug screen. - #2087 small segmented button text is not readable in dark mode. -- #2077 rename "This device" and "any device" to "This Android device" and "Any input device" to prevent confusion. +- #2077 rename "This device" and "any device" to "This Android device" and "Any input device" to + prevent confusion. ## Changed @@ -78,7 +101,8 @@ - #2030 do not filter out unknown evdev key events. - #2028 work around Shizuku bug on Mediatek devices that prevents Expert mode from starting. -- #2034 catch errors when injecting events with Expert mode on Xiaomi devices and show warning to fix on home screen. +- #2034 catch errors when injecting events with Expert mode on Xiaomi devices and show warning to + fix on home screen. ## [4.0.3](https://github.com/sds100/KeyMapper/releases/tag/v4.0.3) @@ -86,8 +110,10 @@ ## Fixed -- [#2006](https://github.com/keymapperorg/KeyMapper/issues/2006) Actually fixed the bug with crash with Locale switching -- [#2014](https://github.com/keymapperorg/KeyMapper/issues/2014) Reduce mouse latency when remapped with Expert mode. +- [#2006](https://github.com/keymapperorg/KeyMapper/issues/2006) Actually fixed the bug with crash + with Locale switching +- [#2014](https://github.com/keymapperorg/KeyMapper/issues/2014) Reduce mouse latency when remapped + with Expert mode. ## [4.0.2](https://github.com/sds100/KeyMapper/releases/tag/v4.0.2) @@ -99,18 +125,21 @@ ## Removed -- [#1973](https://github.com/keymapperorg/KeyMapper/issues/1973) [#2006](https://github.com/keymapperorg/KeyMapper/issues/2006) Removed language switching feature due to crash with no solution. +- [#1973](https://github.com/keymapperorg/KeyMapper/issues/1973) [#2006](https://github.com/keymapperorg/KeyMapper/issues/2006) + Removed language switching feature due to crash with no solution. ## [4.0.1](https://github.com/sds100/KeyMapper/releases/tag/v4.0.1) #### 01 February 2026 ## Fixed + - #2007 Volume up/down action bottom sheet is cut off on some devices. - #2004 Do not crash when launching wireless debugging screen on some devices. - #2005 NPE in onSaveInstanceState. - #2000 tell the user to tap OS version or build number. -- #1996 Check if Night Shift is supported on a device before activating to prevent the screen going black when activated. +- #1996 Check if Night Shift is supported on a device before activating to prevent the screen going + black when activated. - #1999 Use a more reliable method to check whether Shell has GRANT_RUNTIME_PERMISSIONS permission. ## [4.0.0](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0) @@ -125,25 +154,39 @@ See the changes in the previous beta releases for everything new. There is _a lo ## Added -- [#1970](https://github.com/keymapperorg/KeyMapper/issues/1970) dynamically build the key code list so key codes in new Android releases are automatically +- [#1970](https://github.com/keymapperorg/KeyMapper/issues/1970) dynamically build the key code list + so key codes in new Android releases are automatically included. -- [#1939](https://github.com/keymapperorg/KeyMapper/issues/1939) show notification when Expert Mode fails to start due to be being disconnected from WiFi. -- [#1973](https://github.com/keymapperorg/KeyMapper/issues/1973) add setting to change app language on Android 13+. +- [#1939](https://github.com/keymapperorg/KeyMapper/issues/1939) show notification when Expert Mode + fails to start due to be being disconnected from WiFi. +- [#1973](https://github.com/keymapperorg/KeyMapper/issues/1973) add setting to change app language + on Android 13+. ## Fixed -- [#1986](https://github.com/keymapperorg/KeyMapper/issues/1986) trigger screen is usable on slightly rectangular screens with a low DPI. +- [#1986](https://github.com/keymapperorg/KeyMapper/issues/1986) trigger screen is usable on + slightly rectangular screens with a low DPI. - [#1972](https://github.com/keymapperorg/KeyMapper/issues/1972) Expert Mode works on Android 10. -- [#1976](https://github.com/keymapperorg/KeyMapper/issues/1976) Panic in Rust system bridge code on some devices. -- [#1971](https://github.com/keymapperorg/KeyMapper/issues/1971) Media actions work again in some apps, like YouTube. -- [#1961](https://github.com/keymapperorg/KeyMapper/issues/1961) Disabling setup assistant shows a notification asking for pairing code immediately. -- [#1983](https://github.com/keymapperorg/KeyMapper/issues/1983) Inputting a modifier key and another key as actions through Expert mode applies the correct key character map. -- [#1990](https://github.com/keymapperorg/KeyMapper/issues/1990) Passthrough the device id of the trigger to the key event action if one is not manually specified -- [#1982](https://github.com/keymapperorg/KeyMapper/issues/1982) Text action does not need Key Mapper input method on Android 13+. -- [#1989](https://github.com/keymapperorg/KeyMapper/issues/1989) center the "Trigger and actions" and "Constraint and more" tabs. -- [#1392](https://github.com/keymapperorg/KeyMapper/issues/1392) Add action to enable/disable/toggle night shift. -- [#1675](https://github.com/keymapperorg/KeyMapper/issues/1675) Option to make floating buttons movable. -- [#1949](https://github.com/keymapperorg/KeyMapper/issues/1949) Floating buttons are completely invisible when pressed if background and border opacity is set to 0. +- [#1976](https://github.com/keymapperorg/KeyMapper/issues/1976) Panic in Rust system bridge code on + some devices. +- [#1971](https://github.com/keymapperorg/KeyMapper/issues/1971) Media actions work again in some + apps, like YouTube. +- [#1961](https://github.com/keymapperorg/KeyMapper/issues/1961) Disabling setup assistant shows a + notification asking for pairing code immediately. +- [#1983](https://github.com/keymapperorg/KeyMapper/issues/1983) Inputting a modifier key and + another key as actions through Expert mode applies the correct key character map. +- [#1990](https://github.com/keymapperorg/KeyMapper/issues/1990) Passthrough the device id of the + trigger to the key event action if one is not manually specified +- [#1982](https://github.com/keymapperorg/KeyMapper/issues/1982) Text action does not need Key + Mapper input method on Android 13+. +- [#1989](https://github.com/keymapperorg/KeyMapper/issues/1989) center the "Trigger and actions" + and "Constraint and more" tabs. +- [#1392](https://github.com/keymapperorg/KeyMapper/issues/1392) Add action to enable/disable/toggle + night shift. +- [#1675](https://github.com/keymapperorg/KeyMapper/issues/1675) Option to make floating buttons + movable. +- [#1949](https://github.com/keymapperorg/KeyMapper/issues/1949) Floating buttons are completely + invisible when pressed if background and border opacity is set to 0. ## [4.0.0 Beta 6](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.06) @@ -151,14 +194,18 @@ See the changes in the previous beta releases for everything new. There is _a lo ## Added -- [#1964](https://github.com/keymapperorg/KeyMapper/issues/1964) show the command to start Expert Mode with a shell command. +- [#1964](https://github.com/keymapperorg/KeyMapper/issues/1964) show the command to start Expert + Mode with a shell command. ## Bug fixes -- [#1968](https://github.com/keymapperorg/KeyMapper/issues/1968) Device controls action no longer works on Android 16+ so it has been disabled on new Android +- [#1968](https://github.com/keymapperorg/KeyMapper/issues/1968) Device controls action no longer + works on Android 16+ so it has been disabled on new Android versions. -- [#1967](https://github.com/keymapperorg/KeyMapper/issues/1967) Still start system bridge if granting WRITE_SECURE_SETTINGS fails. -- [#1965](https://github.com/keymapperorg/KeyMapper/issues/1965) Better system bridge support on Xiaomi devices and ask to enable "USB debugging security +- [#1967](https://github.com/keymapperorg/KeyMapper/issues/1967) Still start system bridge if + granting WRITE_SECURE_SETTINGS fails. +- [#1965](https://github.com/keymapperorg/KeyMapper/issues/1965) Better system bridge support on + Xiaomi devices and ask to enable "USB debugging security settings" in developer options. ## [4.0.0 Beta 5](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.05) @@ -169,12 +216,15 @@ Happy new year! ## Added -- [#1947](https://github.com/keymapperorg/KeyMapper/issues/1947) show tip to use expert mode where the old option for screen off remapping used to be +- [#1947](https://github.com/keymapperorg/KeyMapper/issues/1947) show tip to use expert mode where + the old option for screen off remapping used to be ## Bug fixes -- [#1955](https://github.com/keymapperorg/KeyMapper/issues/1955) step forward and step backward media actions support more apps. -- [#1940](https://github.com/keymapperorg/KeyMapper/issues/1940) improve reliability of clicking pairing code button in Wireless Debugging settings. +- [#1955](https://github.com/keymapperorg/KeyMapper/issues/1955) step forward and step backward + media actions support more apps. +- [#1940](https://github.com/keymapperorg/KeyMapper/issues/1940) improve reliability of clicking + pairing code button in Wireless Debugging settings. ## [4.0.0 Beta 4](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.04) @@ -187,21 +237,32 @@ free. ## Added -- [#1915](https://github.com/keymapperorg/KeyMapper/issues/1915) ask user to remove "adb shell" from Shell command. -- [#1904](https://github.com/keymapperorg/KeyMapper/issues/1904) inform the user how to enable the accessibility service with PRO mode or ADB. -- [#1911](https://github.com/keymapperorg/KeyMapper/issues/1911) constraint for physical device orientation that ignores auto rotate setting. -- [#1918](https://github.com/keymapperorg/KeyMapper/issues/1918) improve how key event actions are performed with system bridge. -- [#1905](https://github.com/keymapperorg/KeyMapper/issues/1905) system bridge log is now visible in Key Mapper log. -- [#1941](https://github.com/keymapperorg/KeyMapper/issues/1941) show loading indicator when starting system bridge. +- [#1915](https://github.com/keymapperorg/KeyMapper/issues/1915) ask user to remove "adb shell" from + Shell command. +- [#1904](https://github.com/keymapperorg/KeyMapper/issues/1904) inform the user how to enable the + accessibility service with PRO mode or ADB. +- [#1911](https://github.com/keymapperorg/KeyMapper/issues/1911) constraint for physical device + orientation that ignores auto rotate setting. +- [#1918](https://github.com/keymapperorg/KeyMapper/issues/1918) improve how key event actions are + performed with system bridge. +- [#1905](https://github.com/keymapperorg/KeyMapper/issues/1905) system bridge log is now visible in + Key Mapper log. +- [#1941](https://github.com/keymapperorg/KeyMapper/issues/1941) show loading indicator when + starting system bridge. ## Bug fixes -- [#1913](https://github.com/keymapperorg/KeyMapper/issues/1913) actually save the option to detect with scan code -- [#1931](https://github.com/keymapperorg/KeyMapper/issues/1931) fix Close and Remove From Recents action on some Android 13 revisions -- [#1926](https://github.com/keymapperorg/KeyMapper/issues/1926) PRO mode triggers for external devices work when the device reconnects. -- [#1918](https://github.com/keymapperorg/KeyMapper/issues/1918) PRO mode key maps can input key codes that aren't originally supported by the trigger +- [#1913](https://github.com/keymapperorg/KeyMapper/issues/1913) actually save the option to detect + with scan code +- [#1931](https://github.com/keymapperorg/KeyMapper/issues/1931) fix Close and Remove From Recents + action on some Android 13 revisions +- [#1926](https://github.com/keymapperorg/KeyMapper/issues/1926) PRO mode triggers for external + devices work when the device reconnects. +- [#1918](https://github.com/keymapperorg/KeyMapper/issues/1918) PRO mode key maps can input key + codes that aren't originally supported by the trigger device. -- [#1934](https://github.com/keymapperorg/KeyMapper/issues/1934) hold down option for Tap Screen action is added back. +- [#1934](https://github.com/keymapperorg/KeyMapper/issues/1934) hold down option for Tap Screen + action is added back. - Log less verbose. ## [4.0.0 Beta 3](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.03) @@ -210,18 +271,27 @@ free. ## Added -- [#1871](https://github.com/keymapperorg/KeyMapper/issues/1871) action to modify any system settings. -- [#1221](https://github.com/keymapperorg/KeyMapper/issues/1221) action to show a custom notification. -- [#1491](https://github.com/keymapperorg/KeyMapper/issues/1491) action to toggle/enable/disable hotspot. -- [#1414](https://github.com/keymapperorg/KeyMapper/issues/1414) constraint for when the keyboard is showing. -- [#1900](https://github.com/keymapperorg/KeyMapper/issues/1900) log to logcat if extra logging is enabled. -- [#1902](https://github.com/keymapperorg/KeyMapper/issues/1902) add toggle next to record trigger button to use PRO mode. -- [#1909](https://github.com/keymapperorg/KeyMapper/issues/1909) categorise constraints similar to actions. +- [#1871](https://github.com/keymapperorg/KeyMapper/issues/1871) action to modify any system + settings. +- [#1221](https://github.com/keymapperorg/KeyMapper/issues/1221) action to show a custom + notification. +- [#1491](https://github.com/keymapperorg/KeyMapper/issues/1491) action to toggle/enable/disable + hotspot. +- [#1414](https://github.com/keymapperorg/KeyMapper/issues/1414) constraint for when the keyboard is + showing. +- [#1900](https://github.com/keymapperorg/KeyMapper/issues/1900) log to logcat if extra logging is + enabled. +- [#1902](https://github.com/keymapperorg/KeyMapper/issues/1902) add toggle next to record trigger + button to use PRO mode. +- [#1909](https://github.com/keymapperorg/KeyMapper/issues/1909) categorise constraints similar to + actions. ## Bug fixes -- [#1901](https://github.com/keymapperorg/KeyMapper/issues/1901) prompt user to set default USB configuration to 'No data transfer' after starting pro mode. -- [#1898](https://github.com/keymapperorg/KeyMapper/issues/1898) do not launch directly into the Wireless Debugging activity on Xiaomi devices due to a bug +- [#1901](https://github.com/keymapperorg/KeyMapper/issues/1901) prompt user to set default USB + configuration to 'No data transfer' after starting pro mode. +- [#1898](https://github.com/keymapperorg/KeyMapper/issues/1898) do not launch directly into the + Wireless Debugging activity on Xiaomi devices due to a bug they introduced. ## [4.0.0 Beta 2](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.02) @@ -230,7 +300,8 @@ free. ## Added -- [#1890](https://github.com/keymapperorg/KeyMapper/issues/1890) add button to save log to file and share it. The clipboard button now cuts off older entries +- [#1890](https://github.com/keymapperorg/KeyMapper/issues/1890) add button to save log to file and + share it. The clipboard button now cuts off older entries and keeps newest ones. ## Fixed @@ -239,7 +310,8 @@ free. method with Wireless Debugging and WRITE_SECURE_SETTINGS permission. - Starting system bridge for the first time would be janky because granting READ_LOGS kills the app process. Only grant for READ_LOGS when sharing logcat from settings. -- [#1886](https://github.com/keymapperorg/KeyMapper/issues/1886) mobile data actions work in PRO mode. +- [#1886](https://github.com/keymapperorg/KeyMapper/issues/1886) mobile data actions work in PRO + mode. ## [4.0.0 Beta 1](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.01) @@ -247,22 +319,32 @@ free. ## Added -- [#761](https://github.com/keymapperorg/KeyMapper/issues/761) Detect keys with scancodes. Key Mapper will do this automatically if the key code is unknown +- [#761](https://github.com/keymapperorg/KeyMapper/issues/761) Detect keys with scancodes. Key + Mapper will do this automatically if the key code is unknown or you record different physical keys from the same device with the same key code. - Redesign the Settings screen. - Shortcuts on the trigger screen that guide you how to set up different types of buttons. -- [#1788](https://github.com/keymapperorg/KeyMapper/issues/1788) dismiss lockscreen when launching app action from lockscreen +- [#1788](https://github.com/keymapperorg/KeyMapper/issues/1788) dismiss lockscreen when launching + app action from lockscreen - Show tips for parallel and sequence triggers, and constraints in the trigger screen -- [#397](https://github.com/keymapperorg/KeyMapper/issues/397) enable/disable all key maps in a group -- [#1773](https://github.com/keymapperorg/KeyMapper/issues/1773) Option to show floating buttons on top of keyboard or notification panel. -- [#1335](https://github.com/keymapperorg/KeyMapper/issues/1335) Intent API to enable/disable/toggle a key map. -- [#114](https://github.com/keymapperorg/KeyMapper/issues/114) action to force stop app, and an action to clear an app from recents -- [#727](https://github.com/keymapperorg/KeyMapper/issues/727) Actions to send SMS messages: "Send SMS" and "Compose SMS" -- [#1819](https://github.com/keymapperorg/KeyMapper/issues/1819) Explain how to enable the accessibility service restricted setting +- [#397](https://github.com/keymapperorg/KeyMapper/issues/397) enable/disable all key maps in a + group +- [#1773](https://github.com/keymapperorg/KeyMapper/issues/1773) Option to show floating buttons on + top of keyboard or notification panel. +- [#1335](https://github.com/keymapperorg/KeyMapper/issues/1335) Intent API to enable/disable/toggle + a key map. +- [#114](https://github.com/keymapperorg/KeyMapper/issues/114) action to force stop app, and an + action to clear an app from recents +- [#727](https://github.com/keymapperorg/KeyMapper/issues/727) Actions to send SMS messages: "Send + SMS" and "Compose SMS" +- [#1819](https://github.com/keymapperorg/KeyMapper/issues/1819) Explain how to enable the + accessibility service restricted setting - [#661](https://github.com/keymapperorg/KeyMapper/issues/661) Action to execute shell commands. -- [#991](https://github.com/keymapperorg/KeyMapper/issues/991) Consolidated volume and stream actions. +- [#991](https://github.com/keymapperorg/KeyMapper/issues/991) Consolidated volume and stream + actions. - [#1066](https://github.com/keymapperorg/KeyMapper/issues/1066) Action to mute/unmute microphone. -- [#985](https://github.com/keymapperorg/KeyMapper/issues/985) Constraints for foldable hinge being open/closed. +- [#985](https://github.com/keymapperorg/KeyMapper/issues/985) Constraints for foldable hinge being + open/closed. ## Removed @@ -280,16 +362,20 @@ free. - Restoring subgroups works and does not freeze Key Mapper. - Do not show duplicate constraint shortcuts. - Make WiFi connected constraints more reliable -- [#1818](https://github.com/keymapperorg/KeyMapper/issues/1818) auto switching of the Key Mapper keyboard when typing is more reliable and quicker on +- [#1818](https://github.com/keymapperorg/KeyMapper/issues/1818) auto switching of the Key Mapper + keyboard when typing is more reliable and quicker on Android 13+ -- [#1818](https://github.com/keymapperorg/KeyMapper/issues/1818) auto switching of the Key Mapper keyboard now requires Android 11+. On older versions it was +- [#1818](https://github.com/keymapperorg/KeyMapper/issues/1818) auto switching of the Key Mapper + keyboard now requires Android 11+. On older versions it was only possible with WRITE_SECURE_SETTINGS but very few users are on these old Android versions so it is not worth the extra maintenance effort. -- [#1818](https://github.com/keymapperorg/KeyMapper/issues/1818) the Key Mapper GUI Keyboard is no longer mentioned in the app. It still works but PRO mode +- [#1818](https://github.com/keymapperorg/KeyMapper/issues/1818) the Key Mapper GUI Keyboard is no + longer mentioned in the app. It still works but PRO mode and the auto switching feature are the preferred way to work around the limitations of the Key Mapper keyboard. - Allow selecting notification and alarm sound and not just ringtones for Sound action. -- [#1064](https://github.com/keymapperorg/KeyMapper/issues/1064) wait for switch keyboard action to complete before doing next action. +- [#1064](https://github.com/keymapperorg/KeyMapper/issues/1064) wait for switch keyboard action to + complete before doing next action. ## [3.2.1](https://github.com/sds100/KeyMapper/releases/tag/v3.2.1) @@ -305,33 +391,46 @@ free. ## Added -- [#1466](https://github.com/keymapperorg/KeyMapper/issues/1466) show onboarding when creating a key map for the first time +- [#1466](https://github.com/keymapperorg/KeyMapper/issues/1466) show onboarding when creating a key + map for the first time - [#1729](https://github.com/keymapperorg/KeyMapper/issues/1729) target Android 16. -- [#1725](https://github.com/keymapperorg/KeyMapper/issues/1725) action to move cursor to previous/next character, word, line, paragraph, or page. +- [#1725](https://github.com/keymapperorg/KeyMapper/issues/1725) action to move cursor to + previous/next character, word, line, paragraph, or page. - Names for new key codes introduced in recent Android versions ## Changed -- [#1711](https://github.com/keymapperorg/KeyMapper/issues/1711) major refactoring of the entire codebase into separate Gradle modules. -- [#1701](https://github.com/keymapperorg/KeyMapper/issues/1701) improve the order of the actions and categories +- [#1711](https://github.com/keymapperorg/KeyMapper/issues/1711) major refactoring of the entire + codebase into separate Gradle modules. +- [#1701](https://github.com/keymapperorg/KeyMapper/issues/1701) improve the order of the actions + and categories - Key Mapper keyboard or Shizuku are no longer required for the action to move the cursor to the end ## Bug fixes -- [#1686](https://github.com/keymapperorg/KeyMapper/issues/1686) (more fixes) do not show some screens behind system bars on the left/right side of the +- [#1686](https://github.com/keymapperorg/KeyMapper/issues/1686) (more fixes) do not show some + screens behind system bars on the left/right side of the device. -- [#1701](https://github.com/keymapperorg/KeyMapper/issues/1701) optimize the trigger screen for smaller screens so elements are less cut off. -- [#1699](https://github.com/keymapperorg/KeyMapper/issues/1699) Do not highlight a floating button as if it is pressed after triggering a key event action +- [#1701](https://github.com/keymapperorg/KeyMapper/issues/1701) optimize the trigger screen for + smaller screens so elements are less cut off. +- [#1699](https://github.com/keymapperorg/KeyMapper/issues/1699) Do not highlight a floating button + as if it is pressed after triggering a key event action from it. - Button to copy the key map UID to the clipboard is invisible on small screens. -- [#1709](https://github.com/keymapperorg/KeyMapper/issues/1709) Quick settings tiles were causing crashes on Android 15 -- [#1714](https://github.com/keymapperorg/KeyMapper/issues/1714) Editing "interact with app element" actions works. -- [#1707](https://github.com/keymapperorg/KeyMapper/issues/1707) do not back up sound files if no key maps are using them -- [#797](https://github.com/keymapperorg/KeyMapper/issues/797) [#1719](https://github.com/keymapperorg/KeyMapper/issues/1719) execute key maps that can fix themselves. E.g having an action to select the Key Mapper +- [#1709](https://github.com/keymapperorg/KeyMapper/issues/1709) Quick settings tiles were causing + crashes on Android 15 +- [#1714](https://github.com/keymapperorg/KeyMapper/issues/1714) Editing "interact with app element" + actions works. +- [#1707](https://github.com/keymapperorg/KeyMapper/issues/1707) do not back up sound files if no + key maps are using them +- [#797](https://github.com/keymapperorg/KeyMapper/issues/797) [#1719](https://github.com/keymapperorg/KeyMapper/issues/1719) + execute key maps that can fix themselves. E.g having an action to select the Key Mapper Keyboard before a key code action. -- [#1735](https://github.com/keymapperorg/KeyMapper/issues/1735) Floating buttons no longer flash on screen when the accessibility service restarts if they +- [#1735](https://github.com/keymapperorg/KeyMapper/issues/1735) Floating buttons no longer flash on + screen when the accessibility service restarts if they are not supposed to be visible. -- [#1717](https://github.com/keymapperorg/KeyMapper/issues/1717) do not show floating buttons if quick settings is expanded on the lockscreen. +- [#1717](https://github.com/keymapperorg/KeyMapper/issues/1717) do not show floating buttons if + quick settings is expanded on the lockscreen. - Correctly show error that Airplane mode actions require root ## [3.1.1](https://github.com/sds100/KeyMapper/releases/tag/v3.1.1) @@ -340,13 +439,16 @@ free. ## Added -- [#1637](https://github.com/keymapperorg/KeyMapper/issues/1637) show a home screen error if notification permission is not granted. -- [#1435](https://github.com/keymapperorg/KeyMapper/issues/1435) Pick system sounds/ringtones for the Sound action. +- [#1637](https://github.com/keymapperorg/KeyMapper/issues/1637) show a home screen error if + notification permission is not granted. +- [#1435](https://github.com/keymapperorg/KeyMapper/issues/1435) Pick system sounds/ringtones for + the Sound action. ## Bug fixes - Do not automatically select the key mapper keyboard when the accessibility service starts. -- [#1686](https://github.com/keymapperorg/KeyMapper/issues/1686) do not show some screens behind system bars on the left/right side of the device. +- [#1686](https://github.com/keymapperorg/KeyMapper/issues/1686) do not show some screens behind + system bars on the left/right side of the device. - Use same sized list items when choosing a constraint. ## [3.1.0](https://github.com/sds100/KeyMapper/releases/tag/v3.1.0) @@ -356,9 +458,12 @@ free. ## Added - [#699](https://github.com/keymapperorg/KeyMapper/issues/699) Time constraints ⏰ -- [#257](https://github.com/keymapperorg/KeyMapper/issues/257) Action to interact with user interface elements inside other apps. -- [#1663](https://github.com/keymapperorg/KeyMapper/issues/1663) Actions to stop, step forward, and step backward playing media. -- [#1682](https://github.com/keymapperorg/KeyMapper/issues/1682) Show "Purchased!" text next to the use button for advanced triggers. +- [#257](https://github.com/keymapperorg/KeyMapper/issues/257) Action to interact with user + interface elements inside other apps. +- [#1663](https://github.com/keymapperorg/KeyMapper/issues/1663) Actions to stop, step forward, and + step backward playing media. +- [#1682](https://github.com/keymapperorg/KeyMapper/issues/1682) Show "Purchased!" text next to the + use button for advanced triggers. ## Changed @@ -366,11 +471,14 @@ free. ## Bug fixes -- [#1683](https://github.com/keymapperorg/KeyMapper/issues/1683) key event actions work in Minecraft and other apps again. +- [#1683](https://github.com/keymapperorg/KeyMapper/issues/1683) key event actions work in Minecraft + and other apps again. - Export log files as .txt instead of .zip files. -- [#1684](https://github.com/keymapperorg/KeyMapper/issues/1684) Removed the redundant and broken refresh devices button when configuring a key event action +- [#1684](https://github.com/keymapperorg/KeyMapper/issues/1684) Removed the redundant and broken + refresh devices button when configuring a key event action because they are automatically refreshed anyway. -- [#1687](https://github.com/keymapperorg/KeyMapper/issues/1687) restoring key map groups would sometimes fail. +- [#1687](https://github.com/keymapperorg/KeyMapper/issues/1687) restoring key map groups would + sometimes fail. ## [3.0.1](https://github.com/sds100/KeyMapper/releases/tag/v3.0.1) @@ -378,15 +486,20 @@ free. ## Added -- [#1652](https://github.com/keymapperorg/KeyMapper/issues/1652) Bring back the menu button to show input method picker. -- [#1657](https://github.com/keymapperorg/KeyMapper/issues/1657) Turn on repeat by default for volume actions. +- [#1652](https://github.com/keymapperorg/KeyMapper/issues/1652) Bring back the menu button to show + input method picker. +- [#1657](https://github.com/keymapperorg/KeyMapper/issues/1657) Turn on repeat by default for + volume actions. ## Changed -- [#1654](https://github.com/keymapperorg/KeyMapper/issues/1654) The Key Mapper keyboard is now required again for Text actions because the accessibility +- [#1654](https://github.com/keymapperorg/KeyMapper/issues/1654) The Key Mapper keyboard is now + required again for Text actions because the accessibility service API does not work in all situations. -- [#1653](https://github.com/keymapperorg/KeyMapper/issues/1653) Hide the export/import menu buttons in groups. -- [#1553](https://github.com/keymapperorg/KeyMapper/issues/1553) Hide double press option for side key and fingerprint gesture triggers because it is +- [#1653](https://github.com/keymapperorg/KeyMapper/issues/1653) Hide the export/import menu buttons + in groups. +- [#1553](https://github.com/keymapperorg/KeyMapper/issues/1553) Hide double press option for side + key and fingerprint gesture triggers because it is misleading. Double activations can be done with sequence triggers instead. - [#1669](https://github.com/keymapperorg/KeyMapper/issues/1669) Change quick settings tile text. @@ -394,15 +507,23 @@ free. - Inputting key events with Shizuku does not crash the app if a Key Mapper keyboard is being used at the same time. And latency when inputting key events has been improved in some apps. -- [#1646](https://github.com/keymapperorg/KeyMapper/issues/1646) disabling Bluetooth clears the list of connected devices. -- [#1655](https://github.com/keymapperorg/KeyMapper/issues/1655) do not crash when restoring key map groups. -- [#1649](https://github.com/keymapperorg/KeyMapper/issues/1649) show purchase verification failed error if no network connection. -- [#1648](https://github.com/keymapperorg/KeyMapper/issues/1648) caching purchases works so you can use floating buttons and assistant trigger without an +- [#1646](https://github.com/keymapperorg/KeyMapper/issues/1646) disabling Bluetooth clears the list + of connected devices. +- [#1655](https://github.com/keymapperorg/KeyMapper/issues/1655) do not crash when restoring key map + groups. +- [#1649](https://github.com/keymapperorg/KeyMapper/issues/1649) show purchase verification failed + error if no network connection. +- [#1648](https://github.com/keymapperorg/KeyMapper/issues/1648) caching purchases works so you can + use floating buttons and assistant trigger without an internet connection. -- [#1658](https://github.com/keymapperorg/KeyMapper/issues/1658) floating buttons appear in the wrong place in portrait if saved in landscape. -- [#1659](https://github.com/keymapperorg/KeyMapper/issues/1659) Use trigger does not work if the screen orientation changes when re-entering the app. -- [#1668](https://github.com/keymapperorg/KeyMapper/issues/1668) Crashes when floating menu does not fit in the display height. -- [#1667](https://github.com/keymapperorg/KeyMapper/issues/1667) Hold down mode UI is missing from 2.8. +- [#1658](https://github.com/keymapperorg/KeyMapper/issues/1658) floating buttons appear in the + wrong place in portrait if saved in landscape. +- [#1659](https://github.com/keymapperorg/KeyMapper/issues/1659) Use trigger does not work if the + screen orientation changes when re-entering the app. +- [#1668](https://github.com/keymapperorg/KeyMapper/issues/1668) Crashes when floating menu does not + fit in the display height. +- [#1667](https://github.com/keymapperorg/KeyMapper/issues/1667) Hold down mode UI is missing from + 2.8. ## [3.0.0](https://github.com/sds100/KeyMapper/releases/tag/v3.0.0) @@ -410,7 +531,8 @@ _See the changes from previous 3.0 Beta releases._ #### 10 April 2025 -- [#1635](https://github.com/keymapperorg/KeyMapper/issues/1635) do not crash if the URL for the HTTP action is malformed +- [#1635](https://github.com/keymapperorg/KeyMapper/issues/1635) do not crash if the URL for the + HTTP action is malformed ## [3.0 Beta 5](https://github.com/sds100/KeyMapper/releases/tag/v3.0.0-beta.5) @@ -422,7 +544,8 @@ _See the changes from previous 3.0 Beta releases as well._ ## Bug fixes -- [#1627](https://github.com/keymapperorg/KeyMapper/issues/1627) open camera app action does not work when device is locked +- [#1627](https://github.com/keymapperorg/KeyMapper/issues/1627) open camera app action does not + work when device is locked ## [3.0 Beta 4](https://github.com/sds100/KeyMapper/releases/tag/v3.0.0-beta.4) @@ -432,8 +555,10 @@ _See the changes from previous 3.0 Beta releases as well._ ## Added -- [#1620](https://github.com/keymapperorg/KeyMapper/issues/1620) enable Key Mapper Basic Input Method without user interaction on Android 13+. -- [#1619](https://github.com/keymapperorg/KeyMapper/issues/1619) Automatically select the non key mapper keyboard when the device is locked and wanting to +- [#1620](https://github.com/keymapperorg/KeyMapper/issues/1620) enable Key Mapper Basic Input + Method without user interaction on Android 13+. +- [#1619](https://github.com/keymapperorg/KeyMapper/issues/1619) Automatically select the non key + mapper keyboard when the device is locked and wanting to type. ## Changed @@ -442,7 +567,8 @@ _See the changes from previous 3.0 Beta releases as well._ ## Bug fixes -- [#1618](https://github.com/keymapperorg/KeyMapper/issues/1618), [#1532](https://github.com/keymapperorg/KeyMapper/issues/1532), [#1590](https://github.com/keymapperorg/KeyMapper/issues/1590) The Key Mapper keyboard is no longer required for Text actions. +- [#1618](https://github.com/keymapperorg/KeyMapper/issues/1618), [#1532](https://github.com/keymapperorg/KeyMapper/issues/1532), [#1590](https://github.com/keymapperorg/KeyMapper/issues/1590) + The Key Mapper keyboard is no longer required for Text actions. - Flashlight action works again on devices that do not support variable brightness ## [3.0 Beta 3](https://github.com/sds100/KeyMapper/releases/tag/v3.0.0-beta.3) @@ -455,10 +581,13 @@ This is not an April Fool's joke ;) ## Added -- [#320](https://github.com/keymapperorg/KeyMapper/issues/320) 🗂️ Key map groups! You can now sort key maps into groups and share constraints across all the +- [#320](https://github.com/keymapperorg/KeyMapper/issues/320) 🗂️ Key map groups! You can now sort + key maps into groups and share constraints across all the key maps in the group. -- [#1586](https://github.com/keymapperorg/KeyMapper/issues/1586) 🎨 Customise floating button border and background opacity. -- [#1276](https://github.com/keymapperorg/KeyMapper/issues/1276) Use key event scan code as fallback if the key code is unrecognized. +- [#1586](https://github.com/keymapperorg/KeyMapper/issues/1586) 🎨 Customise floating button border + and background opacity. +- [#1276](https://github.com/keymapperorg/KeyMapper/issues/1276) Use key event scan code as fallback + if the key code is unrecognized. - Make it clearer that the instructions need to be read for the assistant trigger. ## Changed @@ -472,8 +601,10 @@ This is not an April Fool's joke ;) the status bar. - Do not show floating buttons on the always-on display or when the display is "off". - Prompt to unlock device when tapping "Go back" on the floating menu. -- [#1596](https://github.com/keymapperorg/KeyMapper/issues/1596) Do not show the option for front flashlight if the device does not have one. -- [#1598](https://github.com/keymapperorg/KeyMapper/issues/1598) Do not allow changing flashlight brightness on devices that do not support it. +- [#1596](https://github.com/keymapperorg/KeyMapper/issues/1596) Do not show the option for front + flashlight if the device does not have one. +- [#1598](https://github.com/keymapperorg/KeyMapper/issues/1598) Do not allow changing flashlight + brightness on devices that do not support it. - Omit "Back" from Back flashlight actions and constraints since most devices only have a back flashlight anyway. - Do not ask for which flashlight to use in constraints if the device only has one @@ -484,21 +615,25 @@ This is not an April Fool's joke ;) ## Added -- [#1560](https://github.com/keymapperorg/KeyMapper/issues/1560) Action to change flashlight brightness and also set a custom brightness when enabling the +- [#1560](https://github.com/keymapperorg/KeyMapper/issues/1560) Action to change flashlight + brightness and also set a custom brightness when enabling the flashlight. - Prompt to unlock device when using a floating button as a trigger from the lock screen ## Changed -- [#1577](https://github.com/keymapperorg/KeyMapper/issues/1577) Move unsupported actions to the bottom of the list and do not allow selecting root actions +- [#1577](https://github.com/keymapperorg/KeyMapper/issues/1577) Move unsupported actions to the + bottom of the list and do not allow selecting root actions if root permission is not granted. -- [#1593](https://github.com/keymapperorg/KeyMapper/issues/1593) Deprecate the 'Open menu' action by not letting new key maps use it. It is a relic of the +- [#1593](https://github.com/keymapperorg/KeyMapper/issues/1593) Deprecate the 'Open menu' action by + not letting new key maps use it. It is a relic of the past when most apps had a 3-dot menu with a consistent content description making it somewhat easy to identify. ## Bug fixes -- [#1585](https://github.com/keymapperorg/KeyMapper/issues/1585) Track changes when editing key maps and only prompt to discard changes if there were indeed +- [#1585](https://github.com/keymapperorg/KeyMapper/issues/1585) Track changes when editing key maps + and only prompt to discard changes if there were indeed changes. ## [3.0 Beta 1](https://github.com/sds100/KeyMapper/releases/tag/v3.0.0-beta.1) @@ -510,26 +645,38 @@ in Jetpack Compose, resulting in many improvements to the user experience. ## Added -- [#1407](https://github.com/keymapperorg/KeyMapper/issues/1407) New trigger! Add floating buttons on top of other apps to input key maps. +- [#1407](https://github.com/keymapperorg/KeyMapper/issues/1407) New trigger! Add floating buttons + on top of other apps to input key maps. - Key maps are much more dense on the home screen. - Button to pause/resume key maps at the top of the home screen. -- [#1502](https://github.com/keymapperorg/KeyMapper/issues/1502) Constraint for lockscreen is (not) showing. -- [#1203](https://github.com/keymapperorg/KeyMapper/issues/1203) Show a share sheet after exporting key maps rather than asking where to store it. This +- [#1502](https://github.com/keymapperorg/KeyMapper/issues/1502) Constraint for lockscreen is (not) + showing. +- [#1203](https://github.com/keymapperorg/KeyMapper/issues/1203) Show a share sheet after exporting + key maps rather than asking where to store it. This solves the problem when no apps are installed to select where to back it up. You can still find the file in the Downloads file. -- [#1531](https://github.com/keymapperorg/KeyMapper/issues/1531) Show shortcuts to quickly add recently used actions and constraints. -- [#1487](https://github.com/keymapperorg/KeyMapper/issues/1487) Add confirmation dialog when importing key maps and offer the option to replace all the key +- [#1531](https://github.com/keymapperorg/KeyMapper/issues/1531) Show shortcuts to quickly add + recently used actions and constraints. +- [#1487](https://github.com/keymapperorg/KeyMapper/issues/1487) Add confirmation dialog when + importing key maps and offer the option to replace all the key maps or append to the list. -- [#1546](https://github.com/keymapperorg/KeyMapper/issues/1546) Add short explanation of what constraints mean on top of the list. -- [#1548](https://github.com/keymapperorg/KeyMapper/issues/1548) Dynamically change key map enabled switch label. -- [#1562](https://github.com/keymapperorg/KeyMapper/issues/1562) Import key maps by opening .json and .zip files from other apps and file managers. +- [#1546](https://github.com/keymapperorg/KeyMapper/issues/1546) Add short explanation of what + constraints mean on top of the list. +- [#1548](https://github.com/keymapperorg/KeyMapper/issues/1548) Dynamically change key map enabled + switch label. +- [#1562](https://github.com/keymapperorg/KeyMapper/issues/1562) Import key maps by opening .json + and .zip files from other apps and file managers. ## Bug fixes -- [#1518](https://github.com/keymapperorg/KeyMapper/issues/1518) detect more apps that are playing media (fix to previous fix). -- [#1545](https://github.com/keymapperorg/KeyMapper/issues/1545) support phone call constraints in more apps. -- [#1536](https://github.com/keymapperorg/KeyMapper/issues/1536) 'Edit action' sometimes does not appear. -- [#1507](https://github.com/keymapperorg/KeyMapper/issues/1507) only vibrate once when mixing short, long, and double press key maps. +- [#1518](https://github.com/keymapperorg/KeyMapper/issues/1518) detect more apps that are playing + media (fix to previous fix). +- [#1545](https://github.com/keymapperorg/KeyMapper/issues/1545) support phone call constraints in + more apps. +- [#1536](https://github.com/keymapperorg/KeyMapper/issues/1536) 'Edit action' sometimes does not + appear. +- [#1507](https://github.com/keymapperorg/KeyMapper/issues/1507) only vibrate once when mixing + short, long, and double press key maps. - Prevent various system errors from crashing the apps. ## [2.8.3](https://github.com/sds100/KeyMapper/releases/tag/v2.8.3) @@ -538,12 +685,15 @@ in Jetpack Compose, resulting in many improvements to the user experience. ## Changed -- [#1474](https://github.com/keymapperorg/KeyMapper/issues/1474) always allow specifying a name for key map launcher shortcuts. -- [#1533](https://github.com/keymapperorg/KeyMapper/issues/1533) simplify naming of ringer mode actions. +- [#1474](https://github.com/keymapperorg/KeyMapper/issues/1474) always allow specifying a name for + key map launcher shortcuts. +- [#1533](https://github.com/keymapperorg/KeyMapper/issues/1533) simplify naming of ringer mode + actions. ## Bug fixes -- [#1535](https://github.com/keymapperorg/KeyMapper/issues/1535) side key/assistant trigger does not trigger from non-assistant buttons. +- [#1535](https://github.com/keymapperorg/KeyMapper/issues/1535) side key/assistant trigger does not + trigger from non-assistant buttons. ## [2.8.2](https://github.com/sds100/KeyMapper/releases/tag/v2.8.2) @@ -551,13 +701,16 @@ in Jetpack Compose, resulting in many improvements to the user experience. ## Changes -- [#1514](https://github.com/keymapperorg/KeyMapper/issues/1514), [#1454](https://github.com/keymapperorg/KeyMapper/issues/1454) Improving naming of assistant trigger to also refer to the side key and do not force +- [#1514](https://github.com/keymapperorg/KeyMapper/issues/1514), [#1454](https://github.com/keymapperorg/KeyMapper/issues/1454) + Improving naming of assistant trigger to also refer to the side key and do not force the user to select Key Mapper as the assistant. ## Bug fixes -- [#1461](https://github.com/keymapperorg/KeyMapper/issues/1461) fix: crash on startup due to getting MotionEvent device -- [#1518](https://github.com/keymapperorg/KeyMapper/issues/1518) fix: detect apps playing media without a notification for media constraints +- [#1461](https://github.com/keymapperorg/KeyMapper/issues/1461) fix: crash on startup due to + getting MotionEvent device +- [#1518](https://github.com/keymapperorg/KeyMapper/issues/1518) fix: detect apps playing media + without a notification for media constraints ## [2.8.1](https://github.com/sds100/KeyMapper/releases/tag/v2.8.1) @@ -565,14 +718,21 @@ in Jetpack Compose, resulting in many improvements to the user experience. ## Bug fixes -- [#1433](https://github.com/keymapperorg/KeyMapper/issues/1433) open Key Mapper by default and not the Assistant Trigger app. -- [#1386](https://github.com/keymapperorg/KeyMapper/issues/1386) wait for sequence trigger timeout before triggering other overlapping triggers. -- [#1449](https://github.com/keymapperorg/KeyMapper/issues/1449) improve the key mapper crashed dialog. -- [#1415](https://github.com/keymapperorg/KeyMapper/issues/1415) make the discard changes dialog less confusing. -- [#1440](https://github.com/keymapperorg/KeyMapper/issues/1440) do not show the "Button not detected?" bottom sheet every time you open the config key map +- [#1433](https://github.com/keymapperorg/KeyMapper/issues/1433) open Key Mapper by default and not + the Assistant Trigger app. +- [#1386](https://github.com/keymapperorg/KeyMapper/issues/1386) wait for sequence trigger timeout + before triggering other overlapping triggers. +- [#1449](https://github.com/keymapperorg/KeyMapper/issues/1449) improve the key mapper crashed + dialog. +- [#1415](https://github.com/keymapperorg/KeyMapper/issues/1415) make the discard changes dialog + less confusing. +- [#1440](https://github.com/keymapperorg/KeyMapper/issues/1440) do not show the "Button not + detected?" bottom sheet every time you open the config key map screen in some cases. -- [#1447](https://github.com/keymapperorg/KeyMapper/issues/1447) the app bar when configuring an Intent action would extend to the top of the screen. -- [#1444](https://github.com/keymapperorg/KeyMapper/issues/1444) use the correct icon for screen on/off constraints. +- [#1447](https://github.com/keymapperorg/KeyMapper/issues/1447) the app bar when configuring an + Intent action would extend to the top of the screen. +- [#1444](https://github.com/keymapperorg/KeyMapper/issues/1444) use the correct icon for screen + on/off constraints. ## [2.8.0](https://github.com/sds100/KeyMapper/releases/tag/v2.8.0) @@ -581,29 +741,42 @@ in Jetpack Compose, resulting in many improvements to the user experience. ## Added - [#491](https://github.com/keymapperorg/KeyMapper/issues/491) remap DPAD buttons. -- [#1223](https://github.com/keymapperorg/KeyMapper/issues/1223) sort key maps by triggers, actions, constraints and options. -- [#1344](https://github.com/keymapperorg/KeyMapper/issues/1344) target Android 15 and support edge-to-edge display mode. -- [#1372](https://github.com/keymapperorg/KeyMapper/issues/1372) allow Shizuku features to work with Sui. -- [#1391](https://github.com/keymapperorg/KeyMapper/issues/1391) button in Settings to reset all settings to their defaults. +- [#1223](https://github.com/keymapperorg/KeyMapper/issues/1223) sort key maps by triggers, actions, + constraints and options. +- [#1344](https://github.com/keymapperorg/KeyMapper/issues/1344) target Android 15 and support + edge-to-edge display mode. +- [#1372](https://github.com/keymapperorg/KeyMapper/issues/1372) allow Shizuku features to work with + Sui. +- [#1391](https://github.com/keymapperorg/KeyMapper/issues/1391) button in Settings to reset all + settings to their defaults. ## Changed -- [#1412](https://github.com/keymapperorg/KeyMapper/issues/1412) make the record trigger text clearer by saying it is recording. +- [#1412](https://github.com/keymapperorg/KeyMapper/issues/1412) make the record trigger text + clearer by saying it is recording. ## Removed -- [#1411](https://github.com/keymapperorg/KeyMapper/issues/1411) remove the app intro screen for remapping fingerprint gestures because almost all new phones +- [#1411](https://github.com/keymapperorg/KeyMapper/issues/1411) remove the app intro screen for + remapping fingerprint gestures because almost all new phones do not support them anyway. ## Bug fixes -- [#1426](https://github.com/keymapperorg/KeyMapper/issues/1426), [#1434](https://github.com/keymapperorg/KeyMapper/issues/1434) key map launcher shortcut icons were white. -- [#1410](https://github.com/keymapperorg/KeyMapper/issues/1410) vibrations not working on Android 13+. -- [#1342](https://github.com/keymapperorg/KeyMapper/issues/1342) add missing Meta modifier options for key event actions. -- [#1375](https://github.com/keymapperorg/KeyMapper/issues/1375) memory leak when rebinding to the relay service in the Key Mapper GUI Keyboard. -- [#1376](https://github.com/keymapperorg/KeyMapper/issues/1376) Key Mapper Basic Input Method would not work on Android 14+ in some situations. -- [#1094](https://github.com/keymapperorg/KeyMapper/issues/1094) wrong repository name in the introduction screen. -- [#1387](https://github.com/keymapperorg/KeyMapper/issues/1387) some app shortcuts would not open on Android 14+. +- [#1426](https://github.com/keymapperorg/KeyMapper/issues/1426), [#1434](https://github.com/keymapperorg/KeyMapper/issues/1434) + key map launcher shortcut icons were white. +- [#1410](https://github.com/keymapperorg/KeyMapper/issues/1410) vibrations not working on Android + 13+. +- [#1342](https://github.com/keymapperorg/KeyMapper/issues/1342) add missing Meta modifier options + for key event actions. +- [#1375](https://github.com/keymapperorg/KeyMapper/issues/1375) memory leak when rebinding to the + relay service in the Key Mapper GUI Keyboard. +- [#1376](https://github.com/keymapperorg/KeyMapper/issues/1376) Key Mapper Basic Input Method would + not work on Android 14+ in some situations. +- [#1094](https://github.com/keymapperorg/KeyMapper/issues/1094) wrong repository name in the + introduction screen. +- [#1387](https://github.com/keymapperorg/KeyMapper/issues/1387) some app shortcuts would not open + on Android 14+. ## [2.7.2](https://github.com/sds100/KeyMapper/releases/tag/v2.7.2) @@ -611,15 +784,20 @@ in Jetpack Compose, resulting in many improvements to the user experience. ## Added -- [#1298](https://github.com/keymapperorg/KeyMapper/issues/1298) add action to launch the Android device controls screen for managing Home devices. +- [#1298](https://github.com/keymapperorg/KeyMapper/issues/1298) add action to launch the Android + device controls screen for managing Home devices. ## Bug fixes -- [#1342](https://github.com/keymapperorg/KeyMapper/issues/1342) add Meta modifier keys to the key event action. -- [#1101](https://github.com/keymapperorg/KeyMapper/issues/1101) deprecate the toggle split screen action on Android 12L and newer. -- [#1370](https://github.com/keymapperorg/KeyMapper/issues/1370) warn the user that extra permissions are required for the Launch app action on Xiaomi +- [#1342](https://github.com/keymapperorg/KeyMapper/issues/1342) add Meta modifier keys to the key + event action. +- [#1101](https://github.com/keymapperorg/KeyMapper/issues/1101) deprecate the toggle split screen + action on Android 12L and newer. +- [#1370](https://github.com/keymapperorg/KeyMapper/issues/1370) warn the user that extra + permissions are required for the Launch app action on Xiaomi devices -- [#1371](https://github.com/keymapperorg/KeyMapper/issues/1371) try to fix the app not opening on people's devices +- [#1371](https://github.com/keymapperorg/KeyMapper/issues/1371) try to fix the app not opening on + people's devices ## [2.7.1](https://github.com/sds100/KeyMapper/releases/tag/v2.7.1) @@ -627,9 +805,12 @@ in Jetpack Compose, resulting in many improvements to the user experience. ## Bug fixes -- [#1360](https://github.com/keymapperorg/KeyMapper/issues/1360) complete the documentation for advanced triggers at docs.keymapper.club. -- [#1364](https://github.com/keymapperorg/KeyMapper/issues/1364) key event actions no longer crash when using Shizuku. -- [#1362](https://github.com/keymapperorg/KeyMapper/issues/1362) backing up and restoring key maps works again. +- [#1360](https://github.com/keymapperorg/KeyMapper/issues/1360) complete the documentation for + advanced triggers at docs.keymapper.club. +- [#1364](https://github.com/keymapperorg/KeyMapper/issues/1364) key event actions no longer crash + when using Shizuku. +- [#1362](https://github.com/keymapperorg/KeyMapper/issues/1362) backing up and restoring key maps + works again. ## [2.7.0](https://github.com/sds100/KeyMapper/releases/tag/v2.7.0) @@ -637,21 +818,25 @@ in Jetpack Compose, resulting in many improvements to the user experience. ## Added -- [#1274](https://github.com/keymapperorg/KeyMapper/issues/1274) New trigger! You can now trigger your key maps from any of the ways your phone launches the +- [#1274](https://github.com/keymapperorg/KeyMapper/issues/1274) New trigger! You can now trigger + your key maps from any of the ways your phone launches the assistant! This could be the Bixby button, Power button, or a button on your headset. - [#1304](https://github.com/keymapperorg/KeyMapper/issues/1304) Vietnamese translations. ## Bug fixes -- [#1222](https://github.com/keymapperorg/KeyMapper/issues/1222) [#1307](https://github.com/keymapperorg/KeyMapper/issues/1307) Key Mapper doesn't execute the correct app shortcut action if you created multiple +- [#1222](https://github.com/keymapperorg/KeyMapper/issues/1222) [#1307](https://github.com/keymapperorg/KeyMapper/issues/1307) + Key Mapper doesn't execute the correct app shortcut action if you created multiple from the same app. -- [#1328](https://github.com/keymapperorg/KeyMapper/issues/1328) Single-character non-ASCII TEXT_BLOCK input crashes the service +- [#1328](https://github.com/keymapperorg/KeyMapper/issues/1328) Single-character non-ASCII + TEXT_BLOCK input crashes the service ## [2.6.2](https://github.com/sds100/KeyMapper/releases/tag/v2.6.2) #### 9 September 2024 -- [#1293](https://github.com/keymapperorg/KeyMapper/issues/1293) Checkbox buttons were invisible when configuring some actions. +- [#1293](https://github.com/keymapperorg/KeyMapper/issues/1293) Checkbox buttons were invisible + when configuring some actions. ## [2.6.1](https://github.com/sds100/KeyMapper/releases/tag/v2.6.1) @@ -661,29 +846,42 @@ This release adds support for Android 14 and fixes some bugs associated with it. ### Added -- [#1256](https://github.com/keymapperorg/KeyMapper/issues/1256) Add Russian and Chinese Simplified translations. Update other languages. -- [#1282](https://github.com/keymapperorg/KeyMapper/issues/1282) Add Assist key code as screen off trigger for Bixby button. +- [#1256](https://github.com/keymapperorg/KeyMapper/issues/1256) Add Russian and Chinese Simplified + translations. Update other languages. +- [#1282](https://github.com/keymapperorg/KeyMapper/issues/1282) Add Assist key code as screen off + trigger for Bixby button. ### Bug fixes -- [#1218](https://github.com/keymapperorg/KeyMapper/issues/1218), [#1251](https://github.com/keymapperorg/KeyMapper/issues/1251) Key event actions and triggering key maps from an intent were delayed by 1 second on +- [#1218](https://github.com/keymapperorg/KeyMapper/issues/1218), [#1251](https://github.com/keymapperorg/KeyMapper/issues/1251) + Key event actions and triggering key maps from an intent were delayed by 1 second on Android 14 due to new broadcast receiver restrictions. -- [#1175](https://github.com/keymapperorg/KeyMapper/issues/1175) Bypass the do not disturb permission requirement for volume button triggers. -- [#1234](https://github.com/keymapperorg/KeyMapper/issues/1234) Granting permissions with Shizuku crashes on Android 14. -- [#1249](https://github.com/keymapperorg/KeyMapper/issues/1249) Crash when opening help page from the home page if no browser app for custom tabs was found. -- [#1250](https://github.com/keymapperorg/KeyMapper/issues/1250) Random crashes when picking a screenshot for actions. -- [#1227](https://github.com/keymapperorg/KeyMapper/issues/1227) Deprecate Bluetooth actions on Android 13+ due to new restrictions. -- [#1252](https://github.com/keymapperorg/KeyMapper/issues/1252) Add another Camera key code as supported for screen off triggers. -- [#1219](https://github.com/keymapperorg/KeyMapper/issues/1219) Key Mapper notifications could not be enabled on Android 14. -- [#1194](https://github.com/keymapperorg/KeyMapper/issues/1194) Deprecate closing the status bar on Android 14 due to new restrictions. -- [#1190](https://github.com/keymapperorg/KeyMapper/issues/1190) Add a 3 second delay after the screenshot action before showing the on-screen message +- [#1175](https://github.com/keymapperorg/KeyMapper/issues/1175) Bypass the do not disturb + permission requirement for volume button triggers. +- [#1234](https://github.com/keymapperorg/KeyMapper/issues/1234) Granting permissions with Shizuku + crashes on Android 14. +- [#1249](https://github.com/keymapperorg/KeyMapper/issues/1249) Crash when opening help page from + the home page if no browser app for custom tabs was found. +- [#1250](https://github.com/keymapperorg/KeyMapper/issues/1250) Random crashes when picking a + screenshot for actions. +- [#1227](https://github.com/keymapperorg/KeyMapper/issues/1227) Deprecate Bluetooth actions on + Android 13+ due to new restrictions. +- [#1252](https://github.com/keymapperorg/KeyMapper/issues/1252) Add another Camera key code as + supported for screen off triggers. +- [#1219](https://github.com/keymapperorg/KeyMapper/issues/1219) Key Mapper notifications could not + be enabled on Android 14. +- [#1194](https://github.com/keymapperorg/KeyMapper/issues/1194) Deprecate closing the status bar on + Android 14 due to new restrictions. +- [#1190](https://github.com/keymapperorg/KeyMapper/issues/1190) Add a 3 second delay after the + screenshot action before showing the on-screen message confirming it happened. ## [2.6.0](https://github.com/sds100/KeyMapper/releases/tag/v2.6.0) #### 7 October 2023 -- [#550](https://github.com/keymapperorg/KeyMapper/issues/550) Action for doing pinches and swipes on the screen with 2 or more fingers. Many thanks to +- [#550](https://github.com/keymapperorg/KeyMapper/issues/550) Action for doing pinches and swipes + on the screen with 2 or more fingers. Many thanks to Tino (@pixel-shock) for working on this feature. 😊 ## [2.5.0](https://github.com/sds100/KeyMapper/releases/tag/v2.5.0) @@ -1056,61 +1254,91 @@ These are all the changes from 2.2.0. - 🎉 A new website with a tutorial! 🎉 [docs.keymapper.club](https://docs.keymapper.club) -- Action to broadcast intent, start activity and start service. [#112](https://github.com/keymapperorg/KeyMapper/issues/112) -- Action to show the input method picker by using the Key Mapper keyboard. [#531](https://github.com/keymapperorg/KeyMapper/issues/531) -- Action to toggle the notification drawer and the quick settings drawer. [#242](https://github.com/keymapperorg/KeyMapper/issues/242) +- Action to broadcast intent, start activity and start + service. [#112](https://github.com/keymapperorg/KeyMapper/issues/112) +- Action to show the input method picker by using the Key Mapper + keyboard. [#531](https://github.com/keymapperorg/KeyMapper/issues/531) +- Action to toggle the notification drawer and the quick settings + drawer. [#242](https://github.com/keymapperorg/KeyMapper/issues/242) - Action to call a phone number. [#516](https://github.com/keymapperorg/KeyMapper/issues/516) - Action to play a sound. - A workaround for the Android 11 bug that sets the language of external keyboards to English-US - when an accessibility service is enabled. [#618](https://github.com/keymapperorg/KeyMapper/issues/618) Read the guide + when an accessibility service is + enabled. [#618](https://github.com/keymapperorg/KeyMapper/issues/618) Read the guide here https://docs.keymapper.club/redirects/android-11-device-id-bug-work-around - Prompt the user to read the quick start guide on the website the first time the app is opened. [#544](https://github.com/keymapperorg/KeyMapper/issues/544) -- Links to a relevant online guide in each screen in the app. [#539](https://github.com/keymapperorg/KeyMapper/issues/539) -- Option in key event action to input the key event through the shell. [#559](https://github.com/keymapperorg/KeyMapper/issues/559) +- Links to a relevant online guide in each screen in the + app. [#539](https://github.com/keymapperorg/KeyMapper/issues/539) +- Option in key event action to input the key event through the + shell. [#559](https://github.com/keymapperorg/KeyMapper/issues/559) - Splash screen [#561](https://github.com/keymapperorg/KeyMapper/issues/561) -- Data migrations when restoring from backups. [#574](https://github.com/keymapperorg/KeyMapper/issues/574) -- Enable hold down and disable repeat by default for modifier key actions. [#579](https://github.com/keymapperorg/KeyMapper/issues/579) -- Ability to change the input method with the accessibility service on Android 11+. [#619](https://github.com/keymapperorg/KeyMapper/issues/619) -- Make it clearer that selecting a screenshot to set up a tap coordinate action is optional. [#632](https://github.com/keymapperorg/KeyMapper/issues/632) -- Show a prompt to install the Key Mapper GUI Keyboard when a key event action is created. [#645](https://github.com/keymapperorg/KeyMapper/issues/645) -- Back up default key map settings in back ups. [#659](https://github.com/keymapperorg/KeyMapper/issues/659) -- Warnings when the accessibility service is turned on but isn't actually running. [#643](https://github.com/keymapperorg/KeyMapper/issues/643) -- Show a message at the top of the home screen when mappings are paused. [#642](https://github.com/keymapperorg/KeyMapper/issues/642) -- A caution message to avoid locking the user when using screen pinning mode. [#602](https://github.com/keymapperorg/KeyMapper/issues/602) -- A logging page in the app which can be used instead of bug reports. [#651](https://github.com/keymapperorg/KeyMapper/issues/651) -- A button in the settings to reset sliders to their default. [#589](https://github.com/keymapperorg/KeyMapper/issues/589) +- Data migrations when restoring from + backups. [#574](https://github.com/keymapperorg/KeyMapper/issues/574) +- Enable hold down and disable repeat by default for modifier key + actions. [#579](https://github.com/keymapperorg/KeyMapper/issues/579) +- Ability to change the input method with the accessibility service on Android + 11+. [#619](https://github.com/keymapperorg/KeyMapper/issues/619) +- Make it clearer that selecting a screenshot to set up a tap coordinate action is + optional. [#632](https://github.com/keymapperorg/KeyMapper/issues/632) +- Show a prompt to install the Key Mapper GUI Keyboard when a key event action is + created. [#645](https://github.com/keymapperorg/KeyMapper/issues/645) +- Back up default key map settings in back + ups. [#659](https://github.com/keymapperorg/KeyMapper/issues/659) +- Warnings when the accessibility service is turned on but isn't actually + running. [#643](https://github.com/keymapperorg/KeyMapper/issues/643) +- Show a message at the top of the home screen when mappings are + paused. [#642](https://github.com/keymapperorg/KeyMapper/issues/642) +- A caution message to avoid locking the user when using screen pinning + mode. [#602](https://github.com/keymapperorg/KeyMapper/issues/602) +- A logging page in the app which can be used instead of bug + reports. [#651](https://github.com/keymapperorg/KeyMapper/issues/651) +- A button in the settings to reset sliders to their + default. [#589](https://github.com/keymapperorg/KeyMapper/issues/589) - A repeat limit action option. [#663](https://github.com/keymapperorg/KeyMapper/issues/663) - Show a dialog before resetting fingerprint gesture maps. -- A new Key Mapper keyboard that is designed for Android TV. [#493](https://github.com/keymapperorg/KeyMapper/issues/493) -- An Intent API to pause/resume key maps. [#668](https://github.com/keymapperorg/KeyMapper/issues/668) -- Allow Key Mapper to be launched from the Android TV launcher. [#695](https://github.com/keymapperorg/KeyMapper/issues/695) -- Make it much easier to report bugs and turn off aggressive app killing. [#728](https://github.com/keymapperorg/KeyMapper/issues/728) There is now a button +- A new Key Mapper keyboard that is designed for Android + TV. [#493](https://github.com/keymapperorg/KeyMapper/issues/493) +- An Intent API to pause/resume key + maps. [#668](https://github.com/keymapperorg/KeyMapper/issues/668) +- Allow Key Mapper to be launched from the Android TV + launcher. [#695](https://github.com/keymapperorg/KeyMapper/issues/695) +- Make it much easier to report bugs and turn off aggressive app + killing. [#728](https://github.com/keymapperorg/KeyMapper/issues/728) There is now a button in the home screen menu to send a bug report and the user is now prompted to read dontkillmyapp.com when the accessibility service crashes. -- Support for repeat until limit reached action option in fingerprint gesture maps. [#710](https://github.com/keymapperorg/KeyMapper/issues/710) +- Support for repeat until limit reached action option in fingerprint gesture + maps. [#710](https://github.com/keymapperorg/KeyMapper/issues/710) - Polish translations. - Czech translations. ### Changed -- Move action option to show a toast message to the same place as the vibrate option. [#565](https://github.com/keymapperorg/KeyMapper/issues/565) +- Move action option to show a toast message to the same place as the vibrate + option. [#565](https://github.com/keymapperorg/KeyMapper/issues/565) - Replace setting to choose Bluetooth device in settings with setting to choose any input device. [#620](https://github.com/keymapperorg/KeyMapper/issues/620) -- Rename 'action count' option to 'how many times'. [#611](https://github.com/keymapperorg/KeyMapper/issues/611) -- Move option to show the volume ui for an action to when the action is created. [#639](https://github.com/keymapperorg/KeyMapper/issues/639) -- Tapping the pause/resume key maps notification now opens Key Mapper. [#665](https://github.com/keymapperorg/KeyMapper/issues/665) -- Make action descriptions more descriptive when repeat is turned on. [#666](https://github.com/keymapperorg/KeyMapper/issues/666) +- Rename 'action count' option to 'how many + times'. [#611](https://github.com/keymapperorg/KeyMapper/issues/611) +- Move option to show the volume ui for an action to when the action is + created. [#639](https://github.com/keymapperorg/KeyMapper/issues/639) +- Tapping the pause/resume key maps notification now opens Key + Mapper. [#665](https://github.com/keymapperorg/KeyMapper/issues/665) +- Make action descriptions more descriptive when repeat is turned + on. [#666](https://github.com/keymapperorg/KeyMapper/issues/666) - Alerts at the top of the home screen have been simplified. ### Removed -- Dex slide in the app intro because it didn't work. [#646](https://github.com/keymapperorg/KeyMapper/issues/646) -- Buttons to enable all and disable all key maps in the home screen menu. [#647](https://github.com/keymapperorg/KeyMapper/issues/647) -- Support for Android KitKat 4.4 and older. [#627](https://github.com/keymapperorg/KeyMapper/issues/627) +- Dex slide in the app intro because it didn't + work. [#646](https://github.com/keymapperorg/KeyMapper/issues/646) +- Buttons to enable all and disable all key maps in the home screen + menu. [#647](https://github.com/keymapperorg/KeyMapper/issues/647) +- Support for Android KitKat 4.4 and + older. [#627](https://github.com/keymapperorg/KeyMapper/issues/627) - Ability to view changelog, license and privacy policy in an in-app dialog. They now open a link in the browser. [#648](https://github.com/keymapperorg/KeyMapper/issues/648) - Alerts at the top of the home screen to enable a Key Mapper keyboard, grant WRITE_SECURE_SETTINGS @@ -1127,7 +1355,8 @@ See the 2.3.0 Beta releases below. ### Changes - Never show the "key mapper has crashed" dialog automatically since this causes a lot of confusion. -- Prompt the user to restart the accessibility service rather than report a bug. [#736](https://github.com/keymapperorg/KeyMapper/issues/736) +- Prompt the user to restart the accessibility service rather than report a + bug. [#736](https://github.com/keymapperorg/KeyMapper/issues/736) ### Added @@ -1151,8 +1380,10 @@ See the 2.3.0 Beta releases below. ### Bug Fixes -- Write Secure Settings section in settings is enabled even if permission is revoked. [#732](https://github.com/keymapperorg/KeyMapper/issues/732) -- Many random crashes. [#744](https://github.com/keymapperorg/KeyMapper/issues/744), [#743](https://github.com/keymapperorg/KeyMapper/issues/743), [#742](https://github.com/keymapperorg/KeyMapper/issues/742), [#741](https://github.com/keymapperorg/KeyMapper/issues/741), [#740](https://github.com/keymapperorg/KeyMapper/issues/740), [#738](https://github.com/keymapperorg/KeyMapper/issues/738), [#737](https://github.com/keymapperorg/KeyMapper/issues/737) +- Write Secure Settings section in settings is enabled even if permission is + revoked. [#732](https://github.com/keymapperorg/KeyMapper/issues/732) +- Many random + crashes. [#744](https://github.com/keymapperorg/KeyMapper/issues/744), [#743](https://github.com/keymapperorg/KeyMapper/issues/743), [#742](https://github.com/keymapperorg/KeyMapper/issues/742), [#741](https://github.com/keymapperorg/KeyMapper/issues/741), [#740](https://github.com/keymapperorg/KeyMapper/issues/740), [#738](https://github.com/keymapperorg/KeyMapper/issues/738), [#737](https://github.com/keymapperorg/KeyMapper/issues/737) - Don't crash when restoring back ups without a sounds folder in it. - Don't restore a back up from a newer version of key mapper to prevent the app crashing when reading the restored data. @@ -1163,19 +1394,25 @@ See the 2.3.0 Beta releases below. ### Added -- Make it much easier to report bugs and turn off aggressive app killing. [#728](https://github.com/keymapperorg/KeyMapper/issues/728) There is now a button +- Make it much easier to report bugs and turn off aggressive app + killing. [#728](https://github.com/keymapperorg/KeyMapper/issues/728) There is now a button in the home screen menu to send a bug report and the user is now prompted to read dontkillmyapp.com when the accessibility service crashes. - Action to play a sound ### Bug Fixes -- Close notification drawer after the notification has been pressed. [#719](https://github.com/keymapperorg/KeyMapper/issues/719) +- Close notification drawer after the notification has been + pressed. [#719](https://github.com/keymapperorg/KeyMapper/issues/719) - Crash if couldn't find input device. [#730](https://github.com/keymapperorg/KeyMapper/issues/730) -- Crash if couldn't find chosen input method. [#731](https://github.com/keymapperorg/KeyMapper/issues/731) -- Crash when failing to get package info. [#721](https://github.com/keymapperorg/KeyMapper/issues/721) -- Crash if couldn't find Bluetooth device. [#723](https://github.com/keymapperorg/KeyMapper/issues/723) -- Crash when disabling accessibility service. [#720](https://github.com/keymapperorg/KeyMapper/issues/720) +- Crash if couldn't find chosen input + method. [#731](https://github.com/keymapperorg/KeyMapper/issues/731) +- Crash when failing to get package + info. [#721](https://github.com/keymapperorg/KeyMapper/issues/721) +- Crash if couldn't find Bluetooth + device. [#723](https://github.com/keymapperorg/KeyMapper/issues/723) +- Crash when disabling accessibility + service. [#720](https://github.com/keymapperorg/KeyMapper/issues/720) - Reduce memory usage. [#725](https://github.com/keymapperorg/KeyMapper/issues/725) - Ensure log doesn't grow forever. [#729](https://github.com/keymapperorg/KeyMapper/issues/729) @@ -1185,12 +1422,14 @@ See the 2.3.0 Beta releases below. ### Added -- Support for repeat until limit reached action option in fingerprint gesture maps. [#710](https://github.com/keymapperorg/KeyMapper/issues/710) +- Support for repeat until limit reached action option in fingerprint gesture + maps. [#710](https://github.com/keymapperorg/KeyMapper/issues/710) ### Bug Fixes - Crash on start up on some devices. [#706](https://github.com/keymapperorg/KeyMapper/issues/706) -- Notification advertising fingerprint gesture maps is shown on every update [#709](https://github.com/keymapperorg/KeyMapper/issues/709) +- Notification advertising fingerprint gesture maps is shown on every + update [#709](https://github.com/keymapperorg/KeyMapper/issues/709) - Key map launcher shortcut repeats indefinitely when triggered if repeat until released is chosen. [#707](https://github.com/keymapperorg/KeyMapper/issues/707) @@ -1205,52 +1444,80 @@ See the 2.3.0 Beta releases below. - 🎉 A new website with a tutorial! 🎉 [docs.keymapper.club](https://docs.keymapper.club) -- Action to broadcast intent, start activity and start service. [#112](https://github.com/keymapperorg/KeyMapper/issues/112) -- Action to show the input method picker by using the Key Mapper keyboard. [#531](https://github.com/keymapperorg/KeyMapper/issues/531) -- Action to toggle the notification drawer and the quick settings drawer. [#242](https://github.com/keymapperorg/KeyMapper/issues/242) +- Action to broadcast intent, start activity and start + service. [#112](https://github.com/keymapperorg/KeyMapper/issues/112) +- Action to show the input method picker by using the Key Mapper + keyboard. [#531](https://github.com/keymapperorg/KeyMapper/issues/531) +- Action to toggle the notification drawer and the quick settings + drawer. [#242](https://github.com/keymapperorg/KeyMapper/issues/242) - Action to call a phone number. [#516](https://github.com/keymapperorg/KeyMapper/issues/516) - A workaround for the Android 11 bug that sets the language of external keyboards to English-US - when an accessibility service is enabled. [#618](https://github.com/keymapperorg/KeyMapper/issues/618) Read the guide + when an accessibility service is + enabled. [#618](https://github.com/keymapperorg/KeyMapper/issues/618) Read the guide here https://docs.keymapper.club/redirects/android-11-device-id-bug-work-around - Prompt the user to read the quick start guide on the website the first time the app is opened. [#544](https://github.com/keymapperorg/KeyMapper/issues/544) -- Links to a relevant online guide in each screen in the app. [#539](https://github.com/keymapperorg/KeyMapper/issues/539) -- Option in key event action to input the key event through the shell. [#559](https://github.com/keymapperorg/KeyMapper/issues/559) +- Links to a relevant online guide in each screen in the + app. [#539](https://github.com/keymapperorg/KeyMapper/issues/539) +- Option in key event action to input the key event through the + shell. [#559](https://github.com/keymapperorg/KeyMapper/issues/559) - Splash screen [#561](https://github.com/keymapperorg/KeyMapper/issues/561) -- Data migrations when restoring from backups. [#574](https://github.com/keymapperorg/KeyMapper/issues/574) -- Enable hold down and disable repeat by default for modifier key actions. [#579](https://github.com/keymapperorg/KeyMapper/issues/579) -- Ability to change the input method with the accessibility service on Android 11+. [#619](https://github.com/keymapperorg/KeyMapper/issues/619) -- Make it clearer that selecting a screenshot to set up a tap coordinate action is optional. [#632](https://github.com/keymapperorg/KeyMapper/issues/632) -- Show a prompt to install the Key Mapper GUI Keyboard when a key event action is created. [#645](https://github.com/keymapperorg/KeyMapper/issues/645) -- Back up default key map settings in back ups. [#659](https://github.com/keymapperorg/KeyMapper/issues/659) -- Warnings when the accessibility service is turned on but isn't actually running. [#643](https://github.com/keymapperorg/KeyMapper/issues/643) -- Show a message at the top of the home screen when mappings are paused. [#642](https://github.com/keymapperorg/KeyMapper/issues/642) -- A caution message to avoid locking the user when using screen pinning mode. [#602](https://github.com/keymapperorg/KeyMapper/issues/602) -- A logging page in the app which can be used instead of bug reports. [#651](https://github.com/keymapperorg/KeyMapper/issues/651) -- A button in the settings to reset sliders to their default. [#589](https://github.com/keymapperorg/KeyMapper/issues/589) +- Data migrations when restoring from + backups. [#574](https://github.com/keymapperorg/KeyMapper/issues/574) +- Enable hold down and disable repeat by default for modifier key + actions. [#579](https://github.com/keymapperorg/KeyMapper/issues/579) +- Ability to change the input method with the accessibility service on Android + 11+. [#619](https://github.com/keymapperorg/KeyMapper/issues/619) +- Make it clearer that selecting a screenshot to set up a tap coordinate action is + optional. [#632](https://github.com/keymapperorg/KeyMapper/issues/632) +- Show a prompt to install the Key Mapper GUI Keyboard when a key event action is + created. [#645](https://github.com/keymapperorg/KeyMapper/issues/645) +- Back up default key map settings in back + ups. [#659](https://github.com/keymapperorg/KeyMapper/issues/659) +- Warnings when the accessibility service is turned on but isn't actually + running. [#643](https://github.com/keymapperorg/KeyMapper/issues/643) +- Show a message at the top of the home screen when mappings are + paused. [#642](https://github.com/keymapperorg/KeyMapper/issues/642) +- A caution message to avoid locking the user when using screen pinning + mode. [#602](https://github.com/keymapperorg/KeyMapper/issues/602) +- A logging page in the app which can be used instead of bug + reports. [#651](https://github.com/keymapperorg/KeyMapper/issues/651) +- A button in the settings to reset sliders to their + default. [#589](https://github.com/keymapperorg/KeyMapper/issues/589) - A repeat limit action option. [#663](https://github.com/keymapperorg/KeyMapper/issues/663) - Show a dialog before resetting fingerprint gesture maps. -- A new Key Mapper keyboard that is designed for Android TV. [#493](https://github.com/keymapperorg/KeyMapper/issues/493) -- An Intent API to pause/resume key maps. [#668](https://github.com/keymapperorg/KeyMapper/issues/668) -- Allow Key Mapper to be launched from the Android TV launcher. [#695](https://github.com/keymapperorg/KeyMapper/issues/695) +- A new Key Mapper keyboard that is designed for Android + TV. [#493](https://github.com/keymapperorg/KeyMapper/issues/493) +- An Intent API to pause/resume key + maps. [#668](https://github.com/keymapperorg/KeyMapper/issues/668) +- Allow Key Mapper to be launched from the Android TV + launcher. [#695](https://github.com/keymapperorg/KeyMapper/issues/695) ### Changed -- Move action option to show a toast message to the same place as the vibrate option. [#565](https://github.com/keymapperorg/KeyMapper/issues/565) +- Move action option to show a toast message to the same place as the vibrate + option. [#565](https://github.com/keymapperorg/KeyMapper/issues/565) - Replace setting to choose Bluetooth device in settings with setting to choose any input device. [#620](https://github.com/keymapperorg/KeyMapper/issues/620) -- Rename 'action count' option to 'how many times'. [#611](https://github.com/keymapperorg/KeyMapper/issues/611) -- Move option to show the volume ui for an action to when the action is created. [#639](https://github.com/keymapperorg/KeyMapper/issues/639) -- Tapping the pause/resume key maps notification now opens Key Mapper. [#665](https://github.com/keymapperorg/KeyMapper/issues/665) -- Make action descriptions more descriptive when repeat is turned on. [#666](https://github.com/keymapperorg/KeyMapper/issues/666) +- Rename 'action count' option to 'how many + times'. [#611](https://github.com/keymapperorg/KeyMapper/issues/611) +- Move option to show the volume ui for an action to when the action is + created. [#639](https://github.com/keymapperorg/KeyMapper/issues/639) +- Tapping the pause/resume key maps notification now opens Key + Mapper. [#665](https://github.com/keymapperorg/KeyMapper/issues/665) +- Make action descriptions more descriptive when repeat is turned + on. [#666](https://github.com/keymapperorg/KeyMapper/issues/666) - Alerts at the top of the home screen have been simplified. ### Removed -- Dex slide in the app intro because it didn't work. [#646](https://github.com/keymapperorg/KeyMapper/issues/646) -- Buttons to enable all and disable all key maps in the home screen menu. [#647](https://github.com/keymapperorg/KeyMapper/issues/647) -- Support for Android KitKat 4.4 and older. [#627](https://github.com/keymapperorg/KeyMapper/issues/627) +- Dex slide in the app intro because it didn't + work. [#646](https://github.com/keymapperorg/KeyMapper/issues/646) +- Buttons to enable all and disable all key maps in the home screen + menu. [#647](https://github.com/keymapperorg/KeyMapper/issues/647) +- Support for Android KitKat 4.4 and + older. [#627](https://github.com/keymapperorg/KeyMapper/issues/627) - Ability to view changelog, license and privacy policy in an in-app dialog. They now open a link in the browser. [#648](https://github.com/keymapperorg/KeyMapper/issues/648) - Alerts at the top of the home screen to enable a Key Mapper keyboard, grant WRITE_SECURE_SETTINGS @@ -1262,20 +1529,30 @@ See the 2.3.0 Beta releases below. - Fix text consistency [#543](https://github.com/keymapperorg/KeyMapper/issues/543) - A parallel trigger which contains another parallel trigger after the first key should cancel the other. [#571](https://github.com/keymapperorg/KeyMapper/issues/571) -- Actions go off screen for key maps on the home screen. [#613](https://github.com/keymapperorg/KeyMapper/issues/613) -- Remove uses of Android framework strings for dialog buttons. [#650](https://github.com/keymapperorg/KeyMapper/issues/650) -- Trigger key click type sometimes resets to short press. [#615](https://github.com/keymapperorg/KeyMapper/issues/615) +- Actions go off screen for key maps on the home + screen. [#613](https://github.com/keymapperorg/KeyMapper/issues/613) +- Remove uses of Android framework strings for dialog + buttons. [#650](https://github.com/keymapperorg/KeyMapper/issues/650) +- Trigger key click type sometimes resets to short + press. [#615](https://github.com/keymapperorg/KeyMapper/issues/615) - Wrong device id is used when performing key event actions and there are multiple devices with the same descriptor. [#637](https://github.com/keymapperorg/KeyMapper/issues/637) -- Trigger key isn't imitated after a failed double press. [#606](https://github.com/keymapperorg/KeyMapper/issues/606) -- Actions don't start repeating on a failed long press or failed double press. [#626](https://github.com/keymapperorg/KeyMapper/issues/626) -- Crash when modifying a huge number of key maps. [#641](https://github.com/keymapperorg/KeyMapper/issues/641) -- Home menu is chopped off on screens with small height. [#582](https://github.com/keymapperorg/KeyMapper/issues/582) -- Crash when double pressing button to open action or trigger key options. [#600](https://github.com/keymapperorg/KeyMapper/issues/600) -- Some action options disappear when adding a new trigger key. [#594](https://github.com/keymapperorg/KeyMapper/issues/594) +- Trigger key isn't imitated after a failed double + press. [#606](https://github.com/keymapperorg/KeyMapper/issues/606) +- Actions don't start repeating on a failed long press or failed double + press. [#626](https://github.com/keymapperorg/KeyMapper/issues/626) +- Crash when modifying a huge number of key + maps. [#641](https://github.com/keymapperorg/KeyMapper/issues/641) +- Home menu is chopped off on screens with small + height. [#582](https://github.com/keymapperorg/KeyMapper/issues/582) +- Crash when double pressing button to open action or trigger key + options. [#600](https://github.com/keymapperorg/KeyMapper/issues/600) +- Some action options disappear when adding a new trigger + key. [#594](https://github.com/keymapperorg/KeyMapper/issues/594) - An action can continue to repeat even when the trigger is released if delay until next action is not 0. [#662](https://github.com/keymapperorg/KeyMapper/issues/662) -- A lot of input latency when using a lot of constraints. [#599](https://github.com/keymapperorg/KeyMapper/issues/599) +- A lot of input latency when using a lot of + constraints. [#599](https://github.com/keymapperorg/KeyMapper/issues/599) - Trigger button isn't imitated when a short press trigger with multiple keys fails to be triggered. [#664](https://github.com/keymapperorg/KeyMapper/issues/664) - Overlapping triggers. [#653](https://github.com/keymapperorg/KeyMapper/issues/653) @@ -1288,7 +1565,8 @@ This sums up all the changes for 2.2 ### Added -- Remap fingerprint gestures! [#378](https://github.com/keymapperorg/KeyMapper/issues/378) Android 8.0+ and only on devices which support them. Even devices +- Remap fingerprint gestures! [#378](https://github.com/keymapperorg/KeyMapper/issues/378) Android + 8.0+ and only on devices which support them. Even devices with the setting to swipe down for notifications might not support this! The dev can't do anything about this. @@ -1297,17 +1575,23 @@ This sums up all the changes for 2.2 differentiated in Key Mapper lists. [#470](https://github.com/keymapperorg/KeyMapper/issues/470) - Show a warning at the top of the homescreen if the user hasn't disabled battery optimisation for Key Mapper. [#496](https://github.com/keymapperorg/KeyMapper/issues/496) -- Action option to hold down until the trigger is pressed again. [#479](https://github.com/keymapperorg/KeyMapper/issues/479) -- Action option to change the delay before the next action in the list. [#476](https://github.com/keymapperorg/KeyMapper/issues/476) +- Action option to hold down until the trigger is pressed + again. [#479](https://github.com/keymapperorg/KeyMapper/issues/479) +- Action option to change the delay before the next action in the + list. [#476](https://github.com/keymapperorg/KeyMapper/issues/476) - Orientation constraint. [#505](https://github.com/keymapperorg/KeyMapper/issues/505) -- Key Event action option to pretend that the Key Event came from a particular device. [#509](https://github.com/keymapperorg/KeyMapper/issues/509) -- Use duplicates of the same key in a sequence trigger. [#513](https://github.com/keymapperorg/KeyMapper/issues/513) -- Show the fingerprint gesture intro slide when updating to 2.2 [#545](https://github.com/keymapperorg/KeyMapper/issues/545) +- Key Event action option to pretend that the Key Event came from a particular + device. [#509](https://github.com/keymapperorg/KeyMapper/issues/509) +- Use duplicates of the same key in a sequence + trigger. [#513](https://github.com/keymapperorg/KeyMapper/issues/513) +- Show the fingerprint gesture intro slide when updating to + 2.2 [#545](https://github.com/keymapperorg/KeyMapper/issues/545) - Show a silent notification, which advertises the remapping fingerprint gesture feature, when the user updates to 2.2 [#546](https://github.com/keymapperorg/KeyMapper/issues/546) - Trigger key maps from an Intent [#490](https://github.com/keymapperorg/KeyMapper/issues/490) - Prompt the user to go to https://dontkillmyapp.com when they first setup the app. -- Add Fdroid link to the Key Mapper GUI Keyboard ad. [#524](https://github.com/keymapperorg/KeyMapper/issues/524) +- Add Fdroid link to the Key Mapper GUI Keyboard + ad. [#524](https://github.com/keymapperorg/KeyMapper/issues/524) ### BREAKING CHANGES @@ -1316,7 +1600,8 @@ This sums up all the changes for 2.2 ### Changes -- No max limit for sliders (except in settings). [#458](https://github.com/keymapperorg/KeyMapper/issues/458) +- No max limit for sliders (except in + settings). [#458](https://github.com/keymapperorg/KeyMapper/issues/458) - The app intro slides will show feedback if the steps have been done correctly. ### Removed @@ -1325,15 +1610,22 @@ This sums up all the changes for 2.2 ### Bug Fixes -- Save and restore state for all view models. [#519](https://github.com/keymapperorg/KeyMapper/issues/519) -- Use View Binding in fragments properly. This should stop random crashes for some users. [#518](https://github.com/keymapperorg/KeyMapper/issues/518) -- Hold Down action option doesn't work for long press triggers. [#504](https://github.com/keymapperorg/KeyMapper/issues/504) +- Save and restore state for all view + models. [#519](https://github.com/keymapperorg/KeyMapper/issues/519) +- Use View Binding in fragments properly. This should stop random crashes for some + users. [#518](https://github.com/keymapperorg/KeyMapper/issues/518) +- Hold Down action option doesn't work for long press + triggers. [#504](https://github.com/keymapperorg/KeyMapper/issues/504) - A trigger for a specific device can still be detected if the same buttons on another device are pressed. [#523](https://github.com/keymapperorg/KeyMapper/issues/523) -- Fix layout of the trigger fragment on some screen sizes so that some things aren't cut off. [#522](https://github.com/keymapperorg/KeyMapper/issues/522) -- Remapping modifier keys to the same key didn't work as expected. [#563](https://github.com/keymapperorg/KeyMapper/issues/563) -- Parallel triggers which contained another parallel trigger didn't cancel the other. [#571](https://github.com/keymapperorg/KeyMapper/issues/571) -- Don't allow screen on/off constraints for fingerprint gestures [#570](https://github.com/keymapperorg/KeyMapper/issues/570) +- Fix layout of the trigger fragment on some screen sizes so that some things aren't cut + off. [#522](https://github.com/keymapperorg/KeyMapper/issues/522) +- Remapping modifier keys to the same key didn't work as + expected. [#563](https://github.com/keymapperorg/KeyMapper/issues/563) +- Parallel triggers which contained another parallel trigger didn't cancel the + other. [#571](https://github.com/keymapperorg/KeyMapper/issues/571) +- Don't allow screen on/off constraints for fingerprint + gestures [#570](https://github.com/keymapperorg/KeyMapper/issues/570) - Rename Key Mapper CI Keyboard to Key Mapper CI Basic Input Method. - Notifications had no icon on Android Lollipop. - remove coloured navigation bar on Android Lollipop. @@ -1341,12 +1633,16 @@ This sums up all the changes for 2.2 - Detecting whether remapping fingerprint gestures are supported didn't work. - The flashlight action would sometimes crash the app. - The error message for an app being disabled was the wrong one. -- Actions to open Android TV apps didn't work [#503](https://github.com/keymapperorg/KeyMapper/issues/503) -- The app list didn't show Android TV-only apps. [#487](https://github.com/keymapperorg/KeyMapper/issues/487) +- Actions to open Android TV apps didn't + work [#503](https://github.com/keymapperorg/KeyMapper/issues/503) +- The app list didn't show Android TV-only + apps. [#487](https://github.com/keymapperorg/KeyMapper/issues/487) - Settings for repeat rate and delay until repeat didn't match their names when configuring an action. -- Text would move up/down when sliding between slides in the app intro. [#540](https://github.com/keymapperorg/KeyMapper/issues/540) -- Icon for "specific app playing media" constraint had the wrong tint. [#535](https://github.com/keymapperorg/KeyMapper/issues/535) +- Text would move up/down when sliding between slides in the app + intro. [#540](https://github.com/keymapperorg/KeyMapper/issues/540) +- Icon for "specific app playing media" constraint had the wrong + tint. [#535](https://github.com/keymapperorg/KeyMapper/issues/535) - Limit Media actions to Android 4.4 KitKat+ because they don't work on older versions. - Up Key Event was sent from all keymaps with the "hold down" action option regardless of whether the trigger was released. [#533](https://github.com/keymapperorg/KeyMapper/issues/533) @@ -1361,15 +1657,18 @@ This sums up all the changes for 2.2 ### Added -- Remap fingerprint gestures! [#378](https://github.com/keymapperorg/KeyMapper/issues/378) Android 8.0+ and only on devices which support them. Even devices +- Remap fingerprint gestures! [#378](https://github.com/keymapperorg/KeyMapper/issues/378) Android + 8.0+ and only on devices which support them. Even devices with the setting to swipe down for notifications might not support this! The dev can't do anything about this. -- Show the fingerprint gesture intro slide when updating to 2.2 [#545](https://github.com/keymapperorg/KeyMapper/issues/545) +- Show the fingerprint gesture intro slide when updating to + 2.2 [#545](https://github.com/keymapperorg/KeyMapper/issues/545) - Show a silent notification, which advertises the remapping fingerprint gesture feature, when the user updates to 2.2 [#546](https://github.com/keymapperorg/KeyMapper/issues/546) - Trigger key maps from an Intent [#490](https://github.com/keymapperorg/KeyMapper/issues/490) - Prompt the user to go to https://dontkillmyapp.com when they first setup the app. -- Add Fdroid link to the Key Mapper GUI Keyboard ad. [#524](https://github.com/keymapperorg/KeyMapper/issues/524) +- Add Fdroid link to the Key Mapper GUI Keyboard + ad. [#524](https://github.com/keymapperorg/KeyMapper/issues/524) ### BREAKING CHANGES @@ -1386,9 +1685,12 @@ This sums up all the changes for 2.2 ### Bug Fixes -- Remapping modifier keys to the same key didn't work as expected. [#563](https://github.com/keymapperorg/KeyMapper/issues/563) -- Parallel triggers which contained another parallel trigger didn't cancel the other. [#571](https://github.com/keymapperorg/KeyMapper/issues/571) -- Don't allow screen on/off constraints for fingerprint gestures [#570](https://github.com/keymapperorg/KeyMapper/issues/570) +- Remapping modifier keys to the same key didn't work as + expected. [#563](https://github.com/keymapperorg/KeyMapper/issues/563) +- Parallel triggers which contained another parallel trigger didn't cancel the + other. [#571](https://github.com/keymapperorg/KeyMapper/issues/571) +- Don't allow screen on/off constraints for fingerprint + gestures [#570](https://github.com/keymapperorg/KeyMapper/issues/570) - Rename Key Mapper CI Keyboard to Key Mapper CI Basic Input Method. - Notifications had no icon on Android Lollipop. - remove coloured navigation bar on Android Lollipop. @@ -1396,12 +1698,16 @@ This sums up all the changes for 2.2 - Detecting whether remapping fingerprint gestures are supported didn't work. - The flashlight action would sometimes crash the app. - The error message for an app being disabled was the wrong one. -- Actions to open Android TV apps didn't work [#503](https://github.com/keymapperorg/KeyMapper/issues/503) -- The app list didn't show Android TV-only apps. [#487](https://github.com/keymapperorg/KeyMapper/issues/487) +- Actions to open Android TV apps didn't + work [#503](https://github.com/keymapperorg/KeyMapper/issues/503) +- The app list didn't show Android TV-only + apps. [#487](https://github.com/keymapperorg/KeyMapper/issues/487) - Settings for repeat rate and delay until repeat didn't match their names when configuring an action. -- Text would move up/down when sliding between slides in the app intro. [#540](https://github.com/keymapperorg/KeyMapper/issues/540) -- Icon for "specific app playing media" constraint had the wrong tint. [#535](https://github.com/keymapperorg/KeyMapper/issues/535) +- Text would move up/down when sliding between slides in the app + intro. [#540](https://github.com/keymapperorg/KeyMapper/issues/540) +- Icon for "specific app playing media" constraint had the wrong + tint. [#535](https://github.com/keymapperorg/KeyMapper/issues/535) - Limit Media actions to Android 4.4 KitKat+ because they don't work on older versions. - Up Key Event was sent from all keymaps with the "hold down" action option regardless of whether the trigger was released. [#533](https://github.com/keymapperorg/KeyMapper/issues/533) @@ -1416,7 +1722,8 @@ This sums up all the changes for 2.2 ### Added -- Remap fingerprint gestures! [#378](https://github.com/keymapperorg/KeyMapper/issues/378) Android 8.0+ and only on devices which support them. Even devices +- Remap fingerprint gestures! [#378](https://github.com/keymapperorg/KeyMapper/issues/378) Android + 8.0+ and only on devices which support them. Even devices with the setting to swipe down for notifications might not support this! The dev can't do anything about this. @@ -1425,26 +1732,37 @@ This sums up all the changes for 2.2 differentiated in Key Mapper lists. [#470](https://github.com/keymapperorg/KeyMapper/issues/470) - Show a warning at the top of the homescreen if the user hasn't disabled battery optimisation for Key Mapper. [#496](https://github.com/keymapperorg/KeyMapper/issues/496) -- Action option to hold down until the trigger is pressed again. [#479](https://github.com/keymapperorg/KeyMapper/issues/479) -- Action option to change the delay before the next action in the list. [#476](https://github.com/keymapperorg/KeyMapper/issues/476) +- Action option to hold down until the trigger is pressed + again. [#479](https://github.com/keymapperorg/KeyMapper/issues/479) +- Action option to change the delay before the next action in the + list. [#476](https://github.com/keymapperorg/KeyMapper/issues/476) - Orientation constraint. [#505](https://github.com/keymapperorg/KeyMapper/issues/505) -- Constraint for when a specific app is playing media. [#508](https://github.com/keymapperorg/KeyMapper/issues/508) -- Key Event action option to pretend that the Key Event came from a particular device. [#509](https://github.com/keymapperorg/KeyMapper/issues/509) -- Use duplicates of the same key in a sequence trigger. [#513](https://github.com/keymapperorg/KeyMapper/issues/513) -- Hold down repeatedly if repeat and hold down are enabled. [#500](https://github.com/keymapperorg/KeyMapper/issues/500) +- Constraint for when a specific app is playing + media. [#508](https://github.com/keymapperorg/KeyMapper/issues/508) +- Key Event action option to pretend that the Key Event came from a particular + device. [#509](https://github.com/keymapperorg/KeyMapper/issues/509) +- Use duplicates of the same key in a sequence + trigger. [#513](https://github.com/keymapperorg/KeyMapper/issues/513) +- Hold down repeatedly if repeat and hold down are + enabled. [#500](https://github.com/keymapperorg/KeyMapper/issues/500) ### Changes -- No max limit for sliders (except in settings). [#458](https://github.com/keymapperorg/KeyMapper/issues/458) +- No max limit for sliders (except in + settings). [#458](https://github.com/keymapperorg/KeyMapper/issues/458) ### Bug Fixes -- Save and restore state for all view models. [#519](https://github.com/keymapperorg/KeyMapper/issues/519) -- Use View Binding in fragments properly. This should stop random crashes for some users. [#518](https://github.com/keymapperorg/KeyMapper/issues/518) -- Hold Down action option doesn't work for long press triggers. [#504](https://github.com/keymapperorg/KeyMapper/issues/504) +- Save and restore state for all view + models. [#519](https://github.com/keymapperorg/KeyMapper/issues/519) +- Use View Binding in fragments properly. This should stop random crashes for some + users. [#518](https://github.com/keymapperorg/KeyMapper/issues/518) +- Hold Down action option doesn't work for long press + triggers. [#504](https://github.com/keymapperorg/KeyMapper/issues/504) - A trigger for a specific device can still be detected if the same buttons on another device are pressed. [#523](https://github.com/keymapperorg/KeyMapper/issues/523) -- Fix layout of the trigger fragment on some screen sizes so that some things aren't cut off. [#522](https://github.com/keymapperorg/KeyMapper/issues/522) +- Fix layout of the trigger fragment on some screen sizes so that some things aren't cut + off. [#522](https://github.com/keymapperorg/KeyMapper/issues/522) ## [2.1.0](https://github.com/sds100/KeyMapper/releases/tag/v2.1.0) @@ -1857,7 +2175,8 @@ This is the first release to be released on F-Droid. ### Bug Fix -- KEYCODE_BACK appeared twice in the keycode action list. [#247](https://github.com/keymapperorg/KeyMapper/issues/247) +- KEYCODE_BACK appeared twice in the keycode action + list. [#247](https://github.com/keymapperorg/KeyMapper/issues/247) ## [1.1.4](https://github.com/sds100/KeyMapper/releases/tag/v1.1.4) diff --git a/app/version.properties b/app/version.properties index 0e1183310c..75456f40c1 100644 --- a/app/version.properties +++ b/app/version.properties @@ -1,2 +1,2 @@ -VERSION_NAME=4.2.1 -VERSION_CODE=256 +VERSION_NAME=4.3.0 +VERSION_CODE=257 diff --git a/base/src/main/assets/whats-new.txt b/base/src/main/assets/whats-new.txt index 35cd3d1f91..d64c53afb0 100644 --- a/base/src/main/assets/whats-new.txt +++ b/base/src/main/assets/whats-new.txt @@ -1,6 +1,6 @@ -• Talkback actions -• Monochrome app icon -• "Any input device" is the default for triggers -• Bug fixes and accessibility improvements +• Display resolution constraint +• Ringer mode constraint +• Set "do not remap" by default in settings +• Bug fixes 📖 View the complete changelog at: http://keymapper.app/changelog diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintScreen.kt index 857f30330c..fb243994b2 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintScreen.kt @@ -55,6 +55,7 @@ fun ChooseConstraintScreen(modifier: Modifier = Modifier, viewModel: ChooseConst val query by viewModel.searchQuery.collectAsStateWithLifecycle() TimeConstraintBottomSheet(viewModel) + DisplayResolutionConstraintBottomSheet(viewModel) ChooseConstraintScreen( modifier = modifier, 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..2367aa378f 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 @@ -23,8 +23,10 @@ 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.SizeKM 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 @@ -94,6 +96,9 @@ class ChooseConstraintViewModel @Inject constructor( var timeConstraintState: ConstraintData.Time? by mutableStateOf(null) + var displayResolutionState: DisplayResolutionSheetState? by mutableStateOf(null) + private set + init { viewModelScope.launch { returnResult.collect { constraintData -> @@ -111,6 +116,49 @@ class ChooseConstraintViewModel @Inject constructor( } } + fun onSelectDisplayResolution(resolution: SizeKM) { + displayResolutionState = displayResolutionState?.copy( + isCustom = false, + selectedResolution = resolution, + ) + } + + fun onSelectCustomDisplayResolution() { + displayResolutionState = displayResolutionState?.copy(isCustom = true) + } + + fun onDisplayResolutionWidthChange(width: String) { + displayResolutionState = displayResolutionState?.copy( + widthText = width.filter(Char::isDigit), + ) + } + + fun onDisplayResolutionHeightChange(height: String) { + displayResolutionState = displayResolutionState?.copy( + heightText = height.filter(Char::isDigit), + ) + } + + fun onDismissDisplayResolution() { + displayResolutionState = null + } + + fun onDoneConfigDisplayResolutionClick() { + val state = displayResolutionState ?: return + + val resolution = state.resolvedResolution ?: return + + viewModelScope.launch { + returnResult.emit( + ConstraintData.DisplayResolution( + width = resolution.width, + height = resolution.height, + ), + ) + displayResolutionState = null + } + } + fun onNavigateBack() { viewModelScope.launch { popBackStack() @@ -125,6 +173,7 @@ class ChooseConstraintViewModel @Inject constructor( onSelectDisplayOrientationConstraint() return@launch } + PHYSICAL_ORIENTATION_LIST_ITEM_ID -> { onSelectPhysicalOrientationConstraint() return@launch @@ -139,6 +188,7 @@ class ChooseConstraintViewModel @Inject constructor( -> onSelectAppConstraint(constraintType) ConstraintId.MEDIA_PLAYING -> returnResult.emit(ConstraintData.MediaPlaying) + ConstraintId.MEDIA_NOT_PLAYING -> returnResult.emit(ConstraintData.NoMediaPlaying) ConstraintId.BT_DEVICE_CONNECTED, @@ -151,6 +201,10 @@ class ChooseConstraintViewModel @Inject constructor( ConstraintId.SCREEN_OFF -> returnResult.emit(ConstraintData.ScreenOff) + ConstraintId.DISPLAY_RESOLUTION -> { + displayResolutionState = buildDisplayResolutionState() + } + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT -> returnResult.emit(ConstraintData.OrientationPortrait) @@ -216,6 +270,7 @@ class ChooseConstraintViewModel @Inject constructor( } ConstraintId.WIFI_ON -> returnResult.emit(ConstraintData.WifiOn) + ConstraintId.WIFI_OFF -> returnResult.emit(ConstraintData.WifiOff) ConstraintId.WIFI_CONNECTED, @@ -249,6 +304,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) @@ -267,6 +331,12 @@ class ChooseConstraintViewModel @Inject constructor( ConstraintId.LOCK_SCREEN_NOT_SHOWING -> returnResult.emit(ConstraintData.LockScreenNotShowing) + ConstraintId.NOTIFICATION_PANEL_SHOWING -> + returnResult.emit(ConstraintData.NotificationPanelShowing) + + ConstraintId.NOTIFICATION_PANEL_NOT_SHOWING -> + returnResult.emit(ConstraintData.NotificationPanelNotShowing) + ConstraintId.TIME -> { timeConstraintState = ConstraintData.Time( startHour = 0, @@ -279,6 +349,25 @@ class ChooseConstraintViewModel @Inject constructor( } } + private fun buildDisplayResolutionState(): DisplayResolutionSheetState { + val supportedResolutions = useCase.getSupportedResolutions() + val currentResolution = useCase.getCurrentResolution() + + val matchingResolution = supportedResolutions.firstOrNull { + it.matchesIgnoringOrientation(currentResolution) + } + + return DisplayResolutionSheetState( + supportedResolutions = supportedResolutions.sortedBy { it.width }, + // Show the text fields immediately when there is nothing meaningful to pick + // from or when the current resolution isn't one of the supported modes. + isCustom = supportedResolutions.size <= 1 || matchingResolution == null, + selectedResolution = matchingResolution ?: supportedResolutions.firstOrNull(), + widthText = currentResolution.width.toString(), + heightText = currentResolution.height.toString(), + ) + } + private suspend fun chooseFlashlightLens(): CameraLens? { val items = useCase.getFlashlightLenses().map { lens -> val label = when (lens) { @@ -320,15 +409,21 @@ class ChooseConstraintViewModel @Inject constructor( val constraintData = when (selectedOrientation) { ConstraintId.DISPLAY_ORIENTATION_PORTRAIT -> ConstraintData.OrientationPortrait + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE -> ConstraintData.OrientationLandscape + ConstraintId.DISPLAY_ORIENTATION_0 -> ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_0) + ConstraintId.DISPLAY_ORIENTATION_90 -> ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_90) + ConstraintId.DISPLAY_ORIENTATION_180 -> ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_180) + ConstraintId.DISPLAY_ORIENTATION_270 -> ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_270) + else -> return } @@ -582,3 +677,51 @@ class ChooseConstraintViewModel @Inject constructor( returnResult.emit(constraintData) } } + +/** + * State for the display resolution bottom sheet. + * + * @param supportedResolutions the resolutions the display reports as supported. + * @param isCustom whether the user is entering a custom resolution instead of picking a chip. + * @param selectedResolution the currently selected supported resolution, if any. + * @param widthText the custom width input. + * @param heightText the custom height input. + */ +data class DisplayResolutionSheetState( + val supportedResolutions: List, + val isCustom: Boolean, + val selectedResolution: SizeKM?, + val widthText: String, + val heightText: String, +) { + private val customWidth: Int? get() = widthText.toIntOrNull() + private val customHeight: Int? get() = heightText.toIntOrNull() + + /** + * The resolution that will be saved, or null when the current input is not valid. + */ + val resolvedResolution: SizeKM? + get() = if (isCustom) { + val width = customWidth + val height = customHeight + + if (width != null && width > 0 && height != null && height > 0) { + SizeKM(width, height) + } else { + null + } + } else { + selectedResolution + } + + val isValid: Boolean get() = resolvedResolution != null +} + +/** + * Compares two resolutions ignoring orientation so that e.g. 1080x1920 and 1920x1080 + * are treated as the same resolution. + */ +private fun SizeKM.matchesIgnoringOrientation(other: SizeKM): Boolean { + return (width == other.width && height == other.height) || + (width == other.height && height == other.width) +} 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..f5d3c0ec57 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 @@ -104,6 +105,11 @@ sealed class ConstraintData { } } + @Serializable + data class DisplayResolution(val width: Int, val height: Int) : ConstraintData() { + override val id: ConstraintId = ConstraintId.DISPLAY_RESOLUTION + } + @Serializable data class FlashlightOn(val lens: CameraLens) : ConstraintData() { override val id: ConstraintId = ConstraintId.FLASHLIGHT_ON @@ -189,6 +195,15 @@ sealed class ConstraintData { override val id: ConstraintId = ConstraintId.PHONE_RINGING } + @Serializable + data class RingerMode(val ringerMode: SystemRingerMode) : ConstraintData() { + override val id: ConstraintId = when (ringerMode) { + SystemRingerMode.NORMAL -> ConstraintId.RINGER_MODE_NORMAL + SystemRingerMode.VIBRATE -> ConstraintId.RINGER_MODE_VIBRATE + SystemRingerMode.SILENT -> ConstraintId.RINGER_MODE_SILENT + } + } + @Serializable data object Charging : ConstraintData() { override val id: ConstraintId = ConstraintId.CHARGING @@ -209,6 +224,16 @@ sealed class ConstraintData { override val id: ConstraintId = ConstraintId.HINGE_OPEN } + @Serializable + data object NotificationPanelShowing : ConstraintData() { + override val id: ConstraintId = ConstraintId.NOTIFICATION_PANEL_SHOWING + } + + @Serializable + data object NotificationPanelNotShowing : ConstraintData() { + override val id: ConstraintId = ConstraintId.NOTIFICATION_PANEL_NOT_SHOWING + } + @Serializable data class Time( val startHour: Int, @@ -282,6 +307,12 @@ object ConstraintEntityMapper { return extraValue } + fun getResolutionWidth(): Int = + entity.extras.getData(ConstraintEntity.EXTRA_RESOLUTION_WIDTH).valueOrNull()!!.toInt() + + fun getResolutionHeight(): Int = + entity.extras.getData(ConstraintEntity.EXTRA_RESOLUTION_HEIGHT).valueOrNull()!!.toInt() + val constraintData = when (entity.type) { ConstraintEntity.APP_FOREGROUND -> ConstraintData.AppInForeground( getPackageName(), @@ -342,6 +373,11 @@ object ConstraintEntityMapper { ConstraintEntity.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED -> ConstraintData.PhysicalOrientation(PhysicalOrientation.LANDSCAPE_INVERTED) + ConstraintEntity.DISPLAY_RESOLUTION -> ConstraintData.DisplayResolution( + width = getResolutionWidth(), + height = getResolutionHeight(), + ) + ConstraintEntity.SCREEN_OFF -> ConstraintData.ScreenOff ConstraintEntity.SCREEN_ON -> ConstraintData.ScreenOn @@ -382,12 +418,23 @@ object ConstraintEntityMapper { ConstraintEntity.IN_PHONE_CALL -> ConstraintData.InPhoneCall ConstraintEntity.NOT_IN_PHONE_CALL -> ConstraintData.NotInPhoneCall + 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 ConstraintEntity.HINGE_CLOSED -> ConstraintData.HingeClosed ConstraintEntity.HINGE_OPEN -> ConstraintData.HingeOpen + ConstraintEntity.NOTIFICATION_PANEL_SHOWING -> ConstraintData.NotificationPanelShowing + ConstraintEntity.NOTIFICATION_PANEL_NOT_SHOWING -> + ConstraintData.NotificationPanelNotShowing + ConstraintEntity.TIME -> { val startTime = entity.extras.getData(ConstraintEntity.EXTRA_START_TIME).valueOrNull()!! @@ -544,6 +591,19 @@ object ConstraintEntityMapper { ) } + is ConstraintData.DisplayResolution -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.DISPLAY_RESOLUTION, + EntityExtra( + ConstraintEntity.EXTRA_RESOLUTION_WIDTH, + constraint.data.width.toString(), + ), + EntityExtra( + ConstraintEntity.EXTRA_RESOLUTION_HEIGHT, + constraint.data.height.toString(), + ), + ) + is ConstraintData.ScreenOff -> ConstraintEntity( uid = constraint.uid, ConstraintEntity.SCREEN_OFF, @@ -673,6 +733,21 @@ object ConstraintEntityMapper { ConstraintEntity.PHONE_RINGING, ) + is ConstraintData.RingerMode -> when (constraint.data.ringerMode) { + SystemRingerMode.NORMAL -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.RINGER_MODE_NORMAL, + ) + SystemRingerMode.VIBRATE -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.RINGER_MODE_VIBRATE, + ) + SystemRingerMode.SILENT -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.RINGER_MODE_SILENT, + ) + } + is ConstraintData.Charging -> ConstraintEntity( uid = constraint.uid, ConstraintEntity.CHARGING, @@ -693,6 +768,16 @@ object ConstraintEntityMapper { ConstraintEntity.HINGE_OPEN, ) + is ConstraintData.NotificationPanelShowing -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.NOTIFICATION_PANEL_SHOWING, + ) + + is ConstraintData.NotificationPanelNotShowing -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.NOTIFICATION_PANEL_NOT_SHOWING, + ) + is ConstraintData.Time -> ConstraintEntity( uid = constraint.uid, type = ConstraintEntity.TIME, 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..ac326d39b4 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,9 @@ enum class ConstraintDependency { DEVICE_LOCKED_STATE, LOCK_SCREEN_SHOWING, PHONE_STATE, + RINGER_MODE, CHARGING_STATE, HINGE_STATE, + NOTIFICATION_PANEL_STATE, + DISPLAY_RESOLUTIONS, } 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..944eed883d 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 @@ -31,6 +31,8 @@ enum class ConstraintId { PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED, + DISPLAY_RESOLUTION, + FLASHLIGHT_ON, FLASHLIGHT_OFF, @@ -54,11 +56,18 @@ enum class ConstraintId { NOT_IN_PHONE_CALL, PHONE_RINGING, + RINGER_MODE_NORMAL, + RINGER_MODE_VIBRATE, + RINGER_MODE_SILENT, + CHARGING, DISCHARGING, HINGE_CLOSED, HINGE_OPEN, + NOTIFICATION_PANEL_SHOWING, + NOTIFICATION_PANEL_NOT_SHOWING, + TIME, } 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..0cc28dc291 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 @@ -5,6 +5,7 @@ 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.SizeKM import io.github.sds100.keymapper.common.utils.firstBlocking import io.github.sds100.keymapper.system.bluetooth.BluetoothDeviceInfo import io.github.sds100.keymapper.system.camera.CameraAdapter @@ -21,6 +22,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 +41,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 { @@ -48,6 +52,7 @@ class LazyConstraintSnapshot( displayAdapter.cachedPhysicalOrientation } private val isScreenOn: Boolean by lazy { displayAdapter.isScreenOn.firstBlocking() } + private val displaySize: SizeKM by lazy { displayAdapter.size } private val appsPlayingMedia: List by lazy { mediaAdapter.getActiveMediaSessionPackages() } @@ -66,6 +71,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() @@ -75,6 +81,10 @@ class LazyConstraintSnapshot( lockScreenAdapter.isLockScreenShowing() } + private val isNotificationShadeExpanded: Boolean by lazy { + accessibilityService.isNotificationShadeExpanded.firstBlocking() + } + private val localTime = LocalTime.now() private fun isMediaPlaying(): Boolean { @@ -126,6 +136,19 @@ class LazyConstraintSnapshot( is ConstraintData.ScreenOff -> !isScreenOn is ConstraintData.ScreenOn -> isScreenOn + + // Compare the resolution regardless of orientation so that the constraint + // holds whether the device is in portrait or landscape. + is ConstraintData.DisplayResolution -> + ( + displaySize.width == constraint.data.width && + displaySize.height == constraint.data.height + ) || + ( + displaySize.width == constraint.data.height && + displaySize.height == constraint.data.width + ) + is ConstraintData.FlashlightOff -> !cameraAdapter.isFlashlightOn(constraint.data.lens) is ConstraintData.FlashlightOn -> cameraAdapter.isFlashlightOn(constraint.data.lens) is ConstraintData.WifiConnected -> { @@ -165,6 +188,8 @@ class LazyConstraintSnapshot( callState == CallState.RINGING || audioVolumeStreams.contains(AudioManager.STREAM_RING) + is ConstraintData.RingerMode -> ringerMode == constraint.data.ringerMode + is ConstraintData.Charging -> isCharging is ConstraintData.Discharging -> !isCharging @@ -199,6 +224,9 @@ class LazyConstraintSnapshot( !isLockscreenShowing || appInForeground != "com.android.systemui" + is ConstraintData.NotificationPanelShowing -> isNotificationShadeExpanded + is ConstraintData.NotificationPanelNotShowing -> !isNotificationShadeExpanded + is ConstraintData.Time -> if (constraint.data.startTime.isAfter(constraint.data.endTime)) { localTime.isAfter(constraint.data.startTime) || 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..4990777498 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( @@ -104,6 +105,11 @@ class ConstraintUiHelper( is ConstraintData.ScreenOn -> getString(R.string.constraint_screen_on_description) + is ConstraintData.DisplayResolution -> getString( + R.string.constraint_display_resolution_description, + arrayOf(constraint.data.width, constraint.data.height), + ) + is ConstraintData.FlashlightOff -> if (constraint.data.lens == CameraLens.FRONT) { getString(R.string.constraint_front_flashlight_off_description) } else { @@ -163,6 +169,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.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) + } 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) @@ -171,6 +182,10 @@ class ConstraintUiHelper( is ConstraintData.LockScreenNotShowing -> getString( R.string.constraint_lock_screen_not_showing, ) + is ConstraintData.NotificationPanelShowing -> + getString(R.string.constraint_notification_panel_showing) + is ConstraintData.NotificationPanelNotShowing -> + getString(R.string.constraint_notification_panel_not_showing) is ConstraintData.Time -> getString( R.string.constraint_time_formatted, arrayOf( 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..cb9d2f2f79 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 @@ -2,6 +2,7 @@ package io.github.sds100.keymapper.base.constraints import androidx.annotation.StringRes import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.AspectRatio import androidx.compose.material.icons.outlined.Battery2Bar import androidx.compose.material.icons.outlined.BatteryChargingFull import androidx.compose.material.icons.outlined.BluetoothConnected @@ -15,6 +16,8 @@ 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.PlayArrow import androidx.compose.material.icons.outlined.RingVolume import androidx.compose.material.icons.outlined.ScreenLockPortrait @@ -23,6 +26,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 @@ -74,6 +78,7 @@ object ConstraintUtils { ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE, ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED, + ConstraintId.DISPLAY_RESOLUTION, -> ConstraintCategory.DISPLAY ConstraintId.FLASHLIGHT_ON, @@ -101,6 +106,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, @@ -111,6 +119,10 @@ object ConstraintUtils { ConstraintId.HINGE_OPEN, -> ConstraintCategory.DEVICE + ConstraintId.NOTIFICATION_PANEL_SHOWING, + ConstraintId.NOTIFICATION_PANEL_NOT_SHOWING, + -> ConstraintCategory.DISPLAY + ConstraintId.TIME -> ConstraintCategory.TIME } @@ -122,11 +134,13 @@ object ConstraintUtils { -> ComposeIconInfo.Vector(Icons.Rounded.Android) ConstraintId.MEDIA_PLAYING -> ComposeIconInfo.Vector(Icons.Outlined.PlayArrow) + ConstraintId.MEDIA_NOT_PLAYING -> ComposeIconInfo.Vector(Icons.Outlined.StopCircle) ConstraintId.BT_DEVICE_CONNECTED -> ComposeIconInfo.Vector( Icons.Outlined.BluetoothConnected, ) + ConstraintId.BT_DEVICE_DISCONNECTED -> ComposeIconInfo.Vector( Icons.Outlined.BluetoothDisabled, ) @@ -142,6 +156,7 @@ object ConstraintUtils { ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE -> ComposeIconInfo.Vector( Icons.Outlined.StayCurrentLandscape, ) + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT -> ComposeIconInfo.Vector( Icons.Outlined.StayCurrentPortrait, ) @@ -155,16 +170,23 @@ object ConstraintUtils { -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentLandscape) ConstraintId.SCREEN_OFF -> ComposeIconInfo.Vector(Icons.Outlined.MobileOff) + ConstraintId.SCREEN_ON -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait) + ConstraintId.DISPLAY_RESOLUTION -> ComposeIconInfo.Vector(Icons.Outlined.AspectRatio) + ConstraintId.FLASHLIGHT_OFF -> ComposeIconInfo.Vector(Icons.Outlined.FlashlightOff) + ConstraintId.FLASHLIGHT_ON -> ComposeIconInfo.Vector(Icons.Outlined.FlashlightOn) ConstraintId.WIFI_CONNECTED -> ComposeIconInfo.Vector(Icons.Outlined.Wifi) + ConstraintId.WIFI_DISCONNECTED -> ComposeIconInfo.Vector( Icons.Outlined.SignalWifiStatusbarNull, ) + ConstraintId.WIFI_OFF -> ComposeIconInfo.Vector(Icons.Outlined.WifiOff) + ConstraintId.WIFI_ON -> ComposeIconInfo.Vector(Icons.Outlined.Wifi) ConstraintId.IME_CHOSEN, @@ -172,76 +194,239 @@ object ConstraintUtils { -> ComposeIconInfo.Vector(Icons.Outlined.Keyboard) ConstraintId.KEYBOARD_SHOWING -> ComposeIconInfo.Vector(Icons.Outlined.Keyboard) + ConstraintId.KEYBOARD_NOT_SHOWING -> ComposeIconInfo.Vector(Icons.Outlined.KeyboardHide) ConstraintId.DEVICE_IS_LOCKED -> ComposeIconInfo.Vector(Icons.Outlined.Lock) + ConstraintId.DEVICE_IS_UNLOCKED -> ComposeIconInfo.Vector(Icons.Outlined.LockOpen) 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) ConstraintId.HINGE_CLOSED -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait) + ConstraintId.HINGE_OPEN -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentLandscape) ConstraintId.LOCK_SCREEN_SHOWING -> ComposeIconInfo.Vector( Icons.Outlined.ScreenLockPortrait, ) + ConstraintId.LOCK_SCREEN_NOT_SHOWING -> ComposeIconInfo.Vector(Icons.Outlined.LockOpen) + + ConstraintId.NOTIFICATION_PANEL_SHOWING -> + ComposeIconInfo.Vector(Icons.Outlined.Notifications) + + ConstraintId.NOTIFICATION_PANEL_NOT_SHOWING -> + ComposeIconInfo.Vector(Icons.Outlined.NotificationsOff) + ConstraintId.TIME -> ComposeIconInfo.Vector(Icons.Outlined.Timer) } fun getTitleStringId(constraintId: ConstraintId): Int = when (constraintId) { ConstraintId.APP_IN_FOREGROUND -> R.string.constraint_choose_app_foreground + ConstraintId.APP_NOT_IN_FOREGROUND -> R.string.constraint_choose_app_not_foreground + ConstraintId.APP_PLAYING_MEDIA -> R.string.constraint_choose_app_playing_media + ConstraintId.APP_NOT_PLAYING_MEDIA -> R.string.constraint_choose_app_not_playing_media + ConstraintId.MEDIA_NOT_PLAYING -> R.string.constraint_choose_media_not_playing + ConstraintId.MEDIA_PLAYING -> R.string.constraint_choose_media_playing + ConstraintId.BT_DEVICE_CONNECTED -> R.string.constraint_choose_bluetooth_device_connected + ConstraintId.BT_DEVICE_DISCONNECTED -> 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.DISPLAY_RESOLUTION -> R.string.constraint_choose_display_resolution + 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 -> 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 + ConstraintId.WIFI_OFF -> R.string.constraint_wifi_off + ConstraintId.WIFI_CONNECTED -> R.string.constraint_wifi_connected + ConstraintId.WIFI_DISCONNECTED -> R.string.constraint_wifi_disconnected + ConstraintId.IME_CHOSEN -> R.string.constraint_ime_chosen + ConstraintId.IME_NOT_CHOSEN -> R.string.constraint_ime_not_chosen + ConstraintId.KEYBOARD_SHOWING -> R.string.constraint_keyboard_showing + ConstraintId.KEYBOARD_NOT_SHOWING -> R.string.constraint_keyboard_not_showing + ConstraintId.DEVICE_IS_LOCKED -> R.string.constraint_device_is_locked + ConstraintId.DEVICE_IS_UNLOCKED -> R.string.constraint_device_is_unlocked + 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 + ConstraintId.HINGE_OPEN -> R.string.constraint_hinge_open + ConstraintId.LOCK_SCREEN_SHOWING -> R.string.constraint_lock_screen_showing + ConstraintId.LOCK_SCREEN_NOT_SHOWING -> R.string.constraint_lock_screen_not_showing + + ConstraintId.NOTIFICATION_PANEL_SHOWING -> + R.string.constraint_notification_panel_showing + + ConstraintId.NOTIFICATION_PANEL_NOT_SHOWING -> + R.string.constraint_notification_panel_not_showing + ConstraintId.TIME -> R.string.constraint_time } + + fun Constraint.getDependency(): Set { + return when (data) { + is ConstraintData.AppInForeground, + is ConstraintData.AppNotInForeground, + -> setOf(ConstraintDependency.FOREGROUND_APP) + + is ConstraintData.AppNotPlayingMedia, + is ConstraintData.AppPlayingMedia, + -> setOf(ConstraintDependency.APP_PLAYING_MEDIA) + + is ConstraintData.BtDeviceConnected, + is ConstraintData.BtDeviceDisconnected, + -> setOf(ConstraintDependency.CONNECTED_BT_DEVICES) + + is ConstraintData.Charging, + is ConstraintData.Discharging, + -> setOf(ConstraintDependency.CHARGING_STATE) + + is ConstraintData.DeviceIsLocked, + is ConstraintData.DeviceIsUnlocked, + -> setOf(ConstraintDependency.DEVICE_LOCKED_STATE) + + is ConstraintData.FlashlightOff, + is ConstraintData.FlashlightOn, + -> setOf(ConstraintDependency.FLASHLIGHT_STATE) + + is ConstraintData.ImeChosen, + is ConstraintData.ImeNotChosen, + -> setOf(ConstraintDependency.CHOSEN_IME) + + is ConstraintData.InPhoneCall, + is ConstraintData.NotInPhoneCall, + is ConstraintData.PhoneRinging, + -> setOf(ConstraintDependency.PHONE_STATE) + + is ConstraintData.MediaPlaying, + is ConstraintData.NoMediaPlaying, + -> setOf(ConstraintDependency.MEDIA_PLAYING) + + is ConstraintData.OrientationCustom, + is ConstraintData.OrientationLandscape, + is ConstraintData.OrientationPortrait, + -> setOf(ConstraintDependency.DISPLAY_ORIENTATION) + + is ConstraintData.ScreenOff, + is ConstraintData.ScreenOn, + -> setOf(ConstraintDependency.SCREEN_STATE) + + is ConstraintData.WifiConnected, + is ConstraintData.WifiDisconnected, + -> setOf(ConstraintDependency.WIFI_SSID) + + is ConstraintData.WifiOff, + is ConstraintData.WifiOn, + -> setOf(ConstraintDependency.WIFI_STATE) + + is ConstraintData.LockScreenShowing, + is ConstraintData.LockScreenNotShowing, + -> setOf(ConstraintDependency.LOCK_SCREEN_SHOWING) + + is ConstraintData.Time -> setOf( + ConstraintDependency.FOREGROUND_APP, + ConstraintDependency.SCREEN_STATE, + ) + + is ConstraintData.HingeClosed -> setOf(ConstraintDependency.HINGE_STATE) + + is ConstraintData.HingeOpen -> setOf(ConstraintDependency.HINGE_STATE) + + ConstraintData.KeyboardNotShowing, + ConstraintData.KeyboardShowing, + -> setOf(ConstraintDependency.KEYBOARD_VISIBLE) + + is ConstraintData.PhysicalOrientation -> setOf( + ConstraintDependency.PHYSICAL_ORIENTATION, + ) + + is ConstraintData.RingerMode -> setOf(ConstraintDependency.RINGER_MODE) + + ConstraintData.NotificationPanelNotShowing, ConstraintData.NotificationPanelShowing -> + setOf( + ConstraintDependency.NOTIFICATION_PANEL_STATE, + ) + + is ConstraintData.DisplayResolution -> setOf(ConstraintDependency.DISPLAY_RESOLUTIONS) + } + } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/CreateConstraintUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/CreateConstraintUseCase.kt index 93b1d72b9d..a9650ccbd5 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/CreateConstraintUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/CreateConstraintUseCase.kt @@ -3,10 +3,12 @@ package io.github.sds100.keymapper.base.constraints import android.content.pm.PackageManager import android.os.Build import io.github.sds100.keymapper.common.utils.KMError +import io.github.sds100.keymapper.common.utils.SizeKM import io.github.sds100.keymapper.data.Keys import io.github.sds100.keymapper.data.repositories.PreferenceRepository import io.github.sds100.keymapper.system.camera.CameraAdapter import io.github.sds100.keymapper.system.camera.CameraLens +import io.github.sds100.keymapper.system.display.DisplayAdapter import io.github.sds100.keymapper.system.inputmethod.ImeInfo import io.github.sds100.keymapper.system.inputmethod.InputMethodAdapter import io.github.sds100.keymapper.system.network.NetworkAdapter @@ -20,6 +22,7 @@ class CreateConstraintUseCaseImpl @Inject constructor( private val inputMethodAdapter: InputMethodAdapter, private val preferenceRepository: PreferenceRepository, private val cameraAdapter: CameraAdapter, + private val displayAdapter: DisplayAdapter, ) : CreateConstraintUseCase { override fun isSupported(constraint: ConstraintId): KMError? { @@ -31,11 +34,13 @@ class CreateConstraintUseCaseImpl @Inject constructor( return KMError.SystemFeatureNotSupported(PackageManager.FEATURE_CAMERA_FLASH) } } + ConstraintId.HINGE_CLOSED, ConstraintId.HINGE_OPEN -> { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { return KMError.SdkVersionTooLow(Build.VERSION_CODES.R) } } + else -> Unit } @@ -74,6 +79,10 @@ class CreateConstraintUseCaseImpl @Inject constructor( override fun getFlashlightLenses(): Set { return CameraLens.entries.filter { cameraAdapter.getFlashInfo(it) != null }.toSet() } + + override fun getSupportedResolutions(): Set = displayAdapter.supportedResolutions.value + + override fun getCurrentResolution(): SizeKM = displayAdapter.size } interface CreateConstraintUseCase { @@ -85,4 +94,7 @@ interface CreateConstraintUseCase { fun getSavedWifiSSIDs(): Flow> fun getFlashlightLenses(): Set + + fun getSupportedResolutions(): Set + fun getCurrentResolution(): SizeKM } 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..a2eb148de6 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 { @@ -60,6 +63,7 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor( ConstraintDependency.FOREGROUND_APP -> accessibilityService.activeWindowPackage.map { dependency } + ConstraintDependency.APP_PLAYING_MEDIA, ConstraintDependency.MEDIA_PLAYING -> merge( mediaAdapter.getActiveMediaSessionPackagesFlow(), @@ -68,11 +72,15 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor( ConstraintDependency.CONNECTED_BT_DEVICES -> devicesAdapter.connectedBluetoothDevices.map { dependency } + 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), @@ -80,8 +88,11 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor( ConstraintDependency.WIFI_SSID -> networkAdapter.connectedWifiSSIDFlow.map { dependency } + ConstraintDependency.WIFI_STATE -> networkAdapter.isWifiEnabledFlow().map { dependency } + ConstraintDependency.CHOSEN_IME -> inputMethodAdapter.chosenIme.map { dependency } + ConstraintDependency.DEVICE_LOCKED_STATE -> lockScreenAdapter.isLockedFlow().map { dependency } @@ -92,15 +103,27 @@ 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) { foldableAdapter.hingeState.map { dependency } } else { emptyFlow() } + ConstraintDependency.KEYBOARD_VISIBLE -> accessibilityService.isInputMethodVisible.map { dependency } + + ConstraintDependency.NOTIFICATION_PANEL_STATE -> + accessibilityService.isNotificationShadeExpanded.map { dependency } + + ConstraintDependency.DISPLAY_RESOLUTIONS -> displayAdapter.supportedResolutions.map { + dependency + } } } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/DisplayResolutionConstraintBottomSheet.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/DisplayResolutionConstraintBottomSheet.kt new file mode 100644 index 0000000000..5424ad8c9a --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/DisplayResolutionConstraintBottomSheet.kt @@ -0,0 +1,243 @@ +package io.github.sds100.keymapper.base.constraints + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FilterChip +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.SheetState +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.github.sds100.keymapper.base.R +import io.github.sds100.keymapper.base.compose.KeyMapperTheme +import io.github.sds100.keymapper.common.utils.SizeKM +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun DisplayResolutionConstraintBottomSheet(viewModel: ChooseConstraintViewModel) { + val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + val state = viewModel.displayResolutionState ?: return + + DisplayResolutionConstraintBottomSheet( + sheetState = sheetState, + state = state, + onSelectResolution = viewModel::onSelectDisplayResolution, + onSelectCustom = viewModel::onSelectCustomDisplayResolution, + onWidthChange = viewModel::onDisplayResolutionWidthChange, + onHeightChange = viewModel::onDisplayResolutionHeightChange, + onDismissRequest = viewModel::onDismissDisplayResolution, + onDoneClick = viewModel::onDoneConfigDisplayResolutionClick, + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun DisplayResolutionConstraintBottomSheet( + sheetState: SheetState, + state: DisplayResolutionSheetState, + onSelectResolution: (SizeKM) -> Unit = {}, + onSelectCustom: () -> Unit = {}, + onWidthChange: (String) -> Unit = {}, + onHeightChange: (String) -> Unit = {}, + onDismissRequest: () -> Unit = {}, + onDoneClick: () -> Unit = {}, +) { + val scope = rememberCoroutineScope() + + ModalBottomSheet( + onDismissRequest = onDismissRequest, + sheetState = sheetState, + dragHandle = null, + ) { + Column { + Spacer(modifier = Modifier.height(16.dp)) + + Text( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp), + textAlign = TextAlign.Center, + text = stringResource(R.string.constraint_display_resolution_bottom_sheet_title), + style = MaterialTheme.typography.headlineMedium, + ) + + Spacer(modifier = Modifier.height(16.dp)) + + // The chips are only useful when there is more than one supported mode to pick from. + if (state.supportedResolutions.size > 1) { + FlowRow( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + for (resolution in state.supportedResolutions) { + FilterChip( + selected = !state.isCustom && state.selectedResolution == resolution, + onClick = { onSelectResolution(resolution) }, + label = { Text("${resolution.width} × ${resolution.height}") }, + ) + } + + FilterChip( + selected = state.isCustom, + onClick = onSelectCustom, + label = { + Text( + stringResource( + R.string.constraint_display_resolution_custom_chip, + ), + ) + }, + ) + } + + Spacer(modifier = Modifier.height(16.dp)) + } + + if (state.isCustom) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp), + ) { + OutlinedTextField( + modifier = Modifier.weight(1f), + value = state.widthText, + onValueChange = onWidthChange, + singleLine = true, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + label = { + Text(stringResource(R.string.constraint_display_resolution_width)) + }, + ) + + OutlinedTextField( + modifier = Modifier.weight(1f), + value = state.heightText, + onValueChange = onHeightChange, + singleLine = true, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + label = { + Text(stringResource(R.string.constraint_display_resolution_height)) + }, + ) + } + + Spacer(modifier = Modifier.height(16.dp)) + } + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + OutlinedButton( + modifier = Modifier.weight(1f), + onClick = { + scope.launch { + sheetState.hide() + onDismissRequest() + } + }, + ) { + Text(stringResource(R.string.neg_cancel)) + } + + Spacer(modifier = Modifier.width(16.dp)) + + Button( + modifier = Modifier.weight(1f), + enabled = state.isValid, + onClick = { + scope.launch { + sheetState.hide() + onDoneClick() + } + }, + ) { + Text(stringResource(R.string.pos_done)) + } + } + + Spacer(Modifier.height(16.dp)) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Preview +@Composable +private fun PreviewWithModes() { + KeyMapperTheme { + val sheetState = SheetState( + skipPartiallyExpanded = true, + positionalThreshold = { 0f }, + velocityThreshold = { 0f }, + ) + + DisplayResolutionConstraintBottomSheet( + sheetState = sheetState, + state = DisplayResolutionSheetState( + supportedResolutions = listOf( + SizeKM(1080, 2400), + SizeKM(1440, 3200), + ), + isCustom = false, + selectedResolution = SizeKM(1080, 2400), + widthText = "1080", + heightText = "2400", + ), + ) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Preview +@Composable +private fun PreviewCustomOnly() { + KeyMapperTheme { + val sheetState = SheetState( + skipPartiallyExpanded = true, + positionalThreshold = { 0f }, + velocityThreshold = { 0f }, + ) + + DisplayResolutionConstraintBottomSheet( + sheetState = sheetState, + state = DisplayResolutionSheetState( + supportedResolutions = emptyList(), + isCustom = true, + selectedResolution = null, + widthText = "1080", + heightText = "2400", + ), + ) + } +} diff --git a/base/src/main/java/io/github/sds100/keymapper/base/keymaps/GetDefaultKeyMapOptionsUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/keymaps/GetDefaultKeyMapOptionsUseCase.kt index 89fa437515..5b01513284 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/keymaps/GetDefaultKeyMapOptionsUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/keymaps/GetDefaultKeyMapOptionsUseCase.kt @@ -20,41 +20,48 @@ class GetDefaultKeyMapOptionsUseCaseImpl @Inject constructor( override val defaultRepeatDelay: StateFlow = preferenceRepository.get(Keys.defaultRepeatDelay) .map { it ?: PreferenceDefaults.REPEAT_DELAY } - .stateIn(coroutineScope, SharingStarted.Lazily, PreferenceDefaults.REPEAT_DELAY) + .stateIn(coroutineScope, SharingStarted.Eagerly, PreferenceDefaults.REPEAT_DELAY) override val defaultRepeatRate: StateFlow = preferenceRepository.get(Keys.defaultRepeatRate) .map { it ?: PreferenceDefaults.REPEAT_RATE } - .stateIn(coroutineScope, SharingStarted.Lazily, PreferenceDefaults.REPEAT_RATE) + .stateIn(coroutineScope, SharingStarted.Eagerly, PreferenceDefaults.REPEAT_RATE) override val defaultHoldDownDuration: StateFlow = preferenceRepository.get(Keys.defaultHoldDownDuration) .map { it ?: PreferenceDefaults.HOLD_DOWN_DURATION } - .stateIn(coroutineScope, SharingStarted.Lazily, PreferenceDefaults.HOLD_DOWN_DURATION) + .stateIn(coroutineScope, SharingStarted.Eagerly, PreferenceDefaults.HOLD_DOWN_DURATION) override val defaultLongPressDelay: StateFlow = preferenceRepository.get(Keys.defaultLongPressDelay) .map { it ?: PreferenceDefaults.LONG_PRESS_DELAY } - .stateIn(coroutineScope, SharingStarted.Lazily, PreferenceDefaults.LONG_PRESS_DELAY) + .stateIn(coroutineScope, SharingStarted.Eagerly, PreferenceDefaults.LONG_PRESS_DELAY) override val defaultDoublePressDelay: StateFlow = preferenceRepository.get(Keys.defaultDoublePressDelay) .map { it ?: PreferenceDefaults.DOUBLE_PRESS_DELAY } - .stateIn(coroutineScope, SharingStarted.Lazily, PreferenceDefaults.DOUBLE_PRESS_DELAY) + .stateIn(coroutineScope, SharingStarted.Eagerly, PreferenceDefaults.DOUBLE_PRESS_DELAY) override val defaultSequenceTriggerTimeout: StateFlow = preferenceRepository.get(Keys.defaultSequenceTriggerTimeout) .map { it ?: PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT } .stateIn( coroutineScope, - SharingStarted.Lazily, + SharingStarted.Eagerly, PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT, ) override val defaultVibrateDuration: StateFlow = preferenceRepository.get(Keys.defaultVibrateDuration) .map { it ?: PreferenceDefaults.VIBRATION_DURATION } - .stateIn(coroutineScope, SharingStarted.Lazily, PreferenceDefaults.VIBRATION_DURATION) + .stateIn(coroutineScope, SharingStarted.Eagerly, PreferenceDefaults.VIBRATION_DURATION) + + override val defaultDoNotRemap: StateFlow = + preferenceRepository.get(Keys.defaultDoNotRemap) + .map { it ?: PreferenceDefaults.DO_NOT_REMAP } + // Use Eagerly because otherwise this value is never updated if only .value is called + // and not collected. + .stateIn(coroutineScope, SharingStarted.Eagerly, PreferenceDefaults.DO_NOT_REMAP) } interface GetDefaultKeyMapOptionsUseCase { @@ -65,4 +72,5 @@ interface GetDefaultKeyMapOptionsUseCase { val defaultDoublePressDelay: StateFlow val defaultSequenceTriggerTimeout: StateFlow val defaultVibrateDuration: StateFlow + val defaultDoNotRemap: StateFlow } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/settings/DefaultOptionsSettingsScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/settings/DefaultOptionsSettingsScreen.kt index 2d807b2042..a93e1516c7 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/settings/DefaultOptionsSettingsScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/settings/DefaultOptionsSettingsScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack +import androidx.compose.material.icons.rounded.Keyboard import androidx.compose.material3.BottomAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -35,6 +36,7 @@ import io.github.sds100.keymapper.base.utils.ui.SliderMaximums import io.github.sds100.keymapper.base.utils.ui.SliderMinimums import io.github.sds100.keymapper.base.utils.ui.SliderStepSizes import io.github.sds100.keymapper.base.utils.ui.compose.SliderOptionText +import io.github.sds100.keymapper.base.utils.ui.compose.SwitchPreferenceCompose @Composable fun DefaultOptionsSettingsScreen(modifier: Modifier = Modifier, viewModel: SettingsViewModel) { @@ -209,6 +211,19 @@ private fun Content( stepSize = SliderStepSizes.TRIGGER_SEQUENCE_TRIGGER_TIMEOUT, ) Spacer(Modifier.height(8.dp)) + + // Do not remap + SwitchPreferenceCompose( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + title = stringResource(R.string.title_pref_default_do_not_remap), + text = stringResource(R.string.summary_pref_default_do_not_remap), + icon = Icons.Rounded.Keyboard, + isChecked = state.doNotRemap, + onCheckedChange = { callback.onDefaultDoNotRemapChanged(it) }, + ) + Spacer(Modifier.height(8.dp)) } } @@ -219,6 +234,7 @@ interface DefaultOptionsSettingsCallback { fun onRepeatDelayChanged(delay: Int) = run { } fun onRepeatRateChanged(rate: Int) = run { } fun onSequenceTriggerTimeoutChanged(timeout: Int) = run { } + fun onDefaultDoNotRemapChanged(doNotRemap: Boolean) = run { } } @Preview diff --git a/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt index 1a2ef5dbda..57dd804b14 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt @@ -72,27 +72,32 @@ class SettingsViewModel @Inject constructor( }.stateIn(viewModelScope, SharingStarted.Lazily, MainSettingsState()) val defaultSettingsScreenState: StateFlow = combine( - useCase.getPreference(Keys.defaultLongPressDelay), - useCase.getPreference(Keys.defaultDoublePressDelay), - useCase.getPreference(Keys.defaultVibrateDuration), - useCase.getPreference(Keys.defaultRepeatDelay), - useCase.getPreference(Keys.defaultRepeatRate), - useCase.getPreference(Keys.defaultSequenceTriggerTimeout), - ) { values -> - DefaultSettingsState( - longPressDelay = values[0] ?: PreferenceDefaults.LONG_PRESS_DELAY, - defaultLongPressDelay = PreferenceDefaults.LONG_PRESS_DELAY, - doublePressDelay = values[1] ?: PreferenceDefaults.DOUBLE_PRESS_DELAY, - defaultDoublePressDelay = PreferenceDefaults.DOUBLE_PRESS_DELAY, - vibrateDuration = values[2] ?: PreferenceDefaults.VIBRATION_DURATION, - defaultVibrateDuration = PreferenceDefaults.VIBRATION_DURATION, - repeatDelay = values[3] ?: PreferenceDefaults.REPEAT_DELAY, - defaultRepeatDelay = PreferenceDefaults.REPEAT_DELAY, - repeatRate = values[4] ?: PreferenceDefaults.REPEAT_RATE, - defaultRepeatRate = PreferenceDefaults.REPEAT_RATE, - sequenceTriggerTimeout = values[5] ?: PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT, - defaultSequenceTriggerTimeout = PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT, - ) + combine( + useCase.getPreference(Keys.defaultLongPressDelay), + useCase.getPreference(Keys.defaultDoublePressDelay), + useCase.getPreference(Keys.defaultVibrateDuration), + useCase.getPreference(Keys.defaultRepeatDelay), + useCase.getPreference(Keys.defaultRepeatRate), + useCase.getPreference(Keys.defaultSequenceTriggerTimeout), + ) { values -> + DefaultSettingsState( + longPressDelay = values[0] ?: PreferenceDefaults.LONG_PRESS_DELAY, + defaultLongPressDelay = PreferenceDefaults.LONG_PRESS_DELAY, + doublePressDelay = values[1] ?: PreferenceDefaults.DOUBLE_PRESS_DELAY, + defaultDoublePressDelay = PreferenceDefaults.DOUBLE_PRESS_DELAY, + vibrateDuration = values[2] ?: PreferenceDefaults.VIBRATION_DURATION, + defaultVibrateDuration = PreferenceDefaults.VIBRATION_DURATION, + repeatDelay = values[3] ?: PreferenceDefaults.REPEAT_DELAY, + defaultRepeatDelay = PreferenceDefaults.REPEAT_DELAY, + repeatRate = values[4] ?: PreferenceDefaults.REPEAT_RATE, + defaultRepeatRate = PreferenceDefaults.REPEAT_RATE, + sequenceTriggerTimeout = values[5] ?: PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT, + defaultSequenceTriggerTimeout = PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT, + ) + }, + useCase.getPreference(Keys.defaultDoNotRemap), + ) { state, doNotRemap -> + state.copy(doNotRemap = doNotRemap ?: PreferenceDefaults.DO_NOT_REMAP) }.stateIn(viewModelScope, SharingStarted.Lazily, DefaultSettingsState()) val automaticChangeImeSettingsState: StateFlow = combine( @@ -241,6 +246,12 @@ class SettingsViewModel @Inject constructor( } } + override fun onDefaultDoNotRemapChanged(doNotRemap: Boolean) { + viewModelScope.launch { + useCase.setPreference(Keys.defaultDoNotRemap, doNotRemap) + } + } + fun onShowToastWhenAutoChangingImeToggled(enabled: Boolean) { viewModelScope.launch { useCase.setPreference(Keys.showToastWhenAutoChangingIme, enabled) @@ -387,6 +398,8 @@ data class DefaultSettingsState( val sequenceTriggerTimeout: Int = PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT, val defaultSequenceTriggerTimeout: Int = PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT, + + val doNotRemap: Boolean = PreferenceDefaults.DO_NOT_REMAP, ) data class AutomaticChangeImeSettingsState( 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..21cfdde574 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,8 +112,12 @@ 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.DisplayResolution -> Success( + "${constraint.data.width}x${constraint.data.height}", + ) is ConstraintData.WifiConnected -> if (constraint.data.ssid == null) { Success("") } else { @@ -143,6 +147,8 @@ class KeyMapConstraintsComparator( is ConstraintData.PhysicalOrientation -> Success( constraint.data.physicalOrientation.toString(), ) + ConstraintData.NotificationPanelShowing -> Success("") + ConstraintData.NotificationPanelNotShowing -> Success("") } } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityService.kt b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityService.kt index de12b4600d..8e1af05ff5 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityService.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityService.kt @@ -6,6 +6,7 @@ import android.accessibilityservice.GestureDescription import android.accessibilityservice.GestureDescription.StrokeDescription import android.accessibilityservice.InputMethod import android.app.ActivityManager +import android.app.KeyguardManager import android.content.Intent import android.content.res.Configuration import android.graphics.Path @@ -54,6 +55,8 @@ abstract class BaseAccessibilityService : IAccessibilityService, SavedStateRegistryOwner { + private val keyguardManager: KeyguardManager by lazy { getSystemService()!! } + @Inject lateinit var accessibilityServiceAdapterLazy: Lazy @@ -130,6 +133,13 @@ abstract class BaseAccessibilityService : override val isInputMethodVisible: Flow get() = _isInputMethodVisible + private val _isNotificationShadeExpanded by lazy { + MutableStateFlow(isNotificationShadeVisible()) + } + + override val isNotificationShadeExpanded: Flow + get() = _isNotificationShadeExpanded + override var serviceFlags: Int? get() = serviceInfo?.flags set(value) { @@ -295,6 +305,7 @@ abstract class BaseAccessibilityService : _activeWindowPackage.update { rootNode?.packageName?.toString() } _isInputMethodVisible.update { isImeWindowVisible() } + _isNotificationShadeExpanded.update { isNotificationShadeVisible() } } getController()?.onAccessibilityEvent(event) @@ -570,6 +581,31 @@ abstract class BaseAccessibilityService : return imeWindow != null && imeWindow.root?.isVisibleToUser == true } + fun isNotificationShadeVisible( + isKeyguardLocked: Boolean = keyguardManager.isKeyguardLocked, + ): Boolean { + if (rootInActiveWindow?.packageName != "com.android.systemui") { + return false + } + + // Use a simple method of determining whether the status bar is expanded. If the system + // ui is showing and the the user is not on the lock screen the assume it is the status + // bar. The latter method commented out below isn't reliable and it is unique + // to every skin and version of Android, which leads to false positives. + if (isKeyguardLocked) { + // Assume the quick settings drawer is expanded if the brightness seekbar is showing. + val seekbar = rootInActiveWindow?.findAccessibilityNodeInfosByViewId( + "com.android.systemui:id/slider", + ) + // isVisibleToUser is required on Samsung S5e tablet. + ?.any { it.className == "android.widget.SeekBar" && it.isVisibleToUser } == true + + return seekbar + } + + return true + } + @RequiresApi(Build.VERSION_CODES.TIRAMISU) override fun injectText(text: String) { inputMethod?.currentInputConnection?.commitText( diff --git a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt index 8d6bef80fd..4ba31e2b9f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt @@ -275,14 +275,6 @@ abstract class BaseAccessibilityServiceController( } } - // The accessibility event is only used on older than SDK 33. On newer versions the - // accessibility input method API is used. - val imeInputStartedEvents = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { - AccessibilityEvent.TYPE_WINDOWS_CHANGED - } else { - 0 - } - val recordNodeEvents = AccessibilityEvent.TYPE_VIEW_FOCUSED or AccessibilityEvent.TYPE_VIEW_CLICKED @@ -298,13 +290,8 @@ abstract class BaseAccessibilityServiceController( if (!changeImeOnInputFocus && recordState == RecordAccessibilityNodeState.Idle ) { - newEventTypes = - newEventTypes and (imeInputStartedEvents or recordNodeEvents).inv() + newEventTypes = newEventTypes and (recordNodeEvents).inv() } else { - if (changeImeOnInputFocus) { - newEventTypes = newEventTypes or imeInputStartedEvents - } - if (recordState is RecordAccessibilityNodeState.CountingDown) { newEventTypes = newEventTypes or recordNodeEvents } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/IAccessibilityService.kt b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/IAccessibilityService.kt index df4cd6c0d9..eae36db432 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/IAccessibilityService.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/IAccessibilityService.kt @@ -63,6 +63,12 @@ interface IAccessibilityService : SwitchImeInterface { */ val isInputMethodVisible: Flow + /** + * Whether the notification shade (notification panel / status bar) is expanded. + * Detection is best-effort; accuracy may vary across OEMs and Android versions. + */ + val isNotificationShadeExpanded: Flow + fun findFocussedNode(focus: Int): AccessibilityNodeModel? @RequiresApi(Build.VERSION_CODES.TIRAMISU) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerDelegate.kt index 65c30dfc77..8839dccb64 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerDelegate.kt @@ -66,6 +66,7 @@ class ConfigTriggerDelegate { device: KeyEventTriggerDevice, requiresIme: Boolean, otherTriggerKeys: List = emptyList(), + doNotRemap: Boolean = false, ): Trigger { val isPowerKey = KeyEventUtils.isPowerButtonKey(keyCode, scanCode) @@ -79,7 +80,7 @@ class ConfigTriggerDelegate { } } - var consumeKeyEvent = true + var consumeKeyEvent = !doNotRemap // Issue #753 if (KeyEventUtils.isModifierKey(keyCode)) { @@ -130,6 +131,7 @@ class ConfigTriggerDelegate { scanCode: Int, device: EvdevDeviceInfo, otherTriggerKeys: List = emptyList(), + doNotRemap: Boolean = false, ): Trigger { val isPowerKey = KeyEventUtils.isPowerButtonKey(keyCode, scanCode) @@ -158,7 +160,7 @@ class ConfigTriggerDelegate { scanCode = scanCode, device = device, clickType = clickType, - consumeEvent = true, + consumeEvent = !doNotRemap, detectWithScanCodeUserSetting = conflictingKeys.isNotEmpty(), ) @@ -186,6 +188,7 @@ class ConfigTriggerDelegate { val newMode = when { trigger.mode != TriggerMode.Sequence && containsKey -> TriggerMode.Sequence + newKeys.size <= 1 -> TriggerMode.Undefined /* Automatically make it a parallel trigger when the user makes a trigger with more than one key diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerUseCase.kt index 9c6a10005e..1b348dc47e 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerUseCase.kt @@ -63,7 +63,9 @@ class ConfigTriggerUseCaseImpl @Inject constructor( .mapNotNull { key -> when (key) { is EvdevTriggerKeyEntity -> EvdevTriggerKey.fromEntity(key) + is KeyEventTriggerKeyEntity -> KeyEventTriggerKey.fromEntity(key) + is AssistantTriggerKeyEntity, is FingerprintTriggerKeyEntity, is FloatingButtonKeyEntity, @@ -118,6 +120,7 @@ class ConfigTriggerUseCaseImpl @Inject constructor( device, requiresIme, otherTriggerKeys = otherTriggerKeys, + doNotRemap = defaultDoNotRemap.value, ) } @@ -129,6 +132,7 @@ class ConfigTriggerUseCaseImpl @Inject constructor( scanCode, device, otherTriggerKeys = otherTriggerKeys, + doNotRemap = defaultDoNotRemap.value, ) } diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index e6561e8dc0..02f183468d 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -262,6 +262,12 @@ Physical: Landscape Physical: Portrait (upside down) Physical: Landscape (inverted) + Display resolution + Resolution is %1$d × %2$d + Display resolution + Custom + Width + Height App playing media App not playing media Media is playing @@ -297,6 +303,9 @@ In phone call Not in phone call Phone ringing + Ringer mode: Ring + Ringer mode: Vibrate + Ringer mode: Silent Charging Discharging @@ -305,6 +314,9 @@ Hinge is closed Hinge is open + Notification panel is showing + Notification panel is not showing + Portrait (0°) Landscape (90°) Portrait (180°) @@ -674,6 +686,9 @@ Change default options For triggers and actions + Do not remap by default + New trigger keys will not consume the key event by default + Reset all DANGER! Are you sure you want to reset all settings in the app to the default? Your existing key maps will not be affected. The introductions and warning pop ups will show again. @@ -1530,9 +1545,9 @@ The advanced triggers are paid feature but you downloaded the FOSS build of Key Mapper that does not include this closed source module or Google Play billing. Please download Key Mapper from Google Play to get access to this feature. Key Mapper FOSS support Floating Buttons and Assistant trigger aren\'t sold in this FOSS build because RevenueCat and Google Play in-app billing aren\'t included.\n\nYou can still support development on Ko-fi. - If you donate and later want those products,\ + If you donate and later get Google Play on another device,\ contact us - \ and we can usually help convert your support so it counts toward Google Play purchases. + \ and we will give you the paid features so you do not need to pay again. Your support helps keep Key Mapper largely open source, without ads or tracking. We don\'t sell your personal data. Donations help fund Android compatibility updates, bug fixes and maintenance, improvements and documentation, and keeping the project sustainable. Support on Ko-fi 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..1af4cf04f2 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 @@ -5,12 +5,14 @@ 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.common.utils.SizeKM import io.github.sds100.keymapper.system.bluetooth.BluetoothDeviceInfo import io.github.sds100.keymapper.system.camera.CameraLens 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 +28,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, @@ -33,6 +36,8 @@ class TestConstraintSnapshot( val isLockscreenShowing: Boolean = false, val localTime: LocalTime = LocalTime.now(), val hingeState: HingeState = HingeState.Unavailable, + val isNotificationPanelShowing: Boolean = false, + val displaySize: SizeKM = SizeKM(1080, 1920), ) : ConstraintSnapshot { override fun isSatisfied(constraint: Constraint): Boolean { @@ -69,6 +74,9 @@ class TestConstraintSnapshot( is ConstraintData.ScreenOff -> !isScreenOn is ConstraintData.ScreenOn -> isScreenOn + is ConstraintData.DisplayResolution -> + (displaySize.width == data.width && displaySize.height == data.height) || + (displaySize.width == data.height && displaySize.height == data.width) is ConstraintData.FlashlightOff -> when (data.lens) { CameraLens.BACK -> !isBackFlashlightOn CameraLens.FRONT -> !isFrontFlashlightOn @@ -107,6 +115,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 == data.ringerMode is ConstraintData.Charging -> isCharging is ConstraintData.Discharging -> !isCharging is ConstraintData.LockScreenShowing -> isLockscreenShowing @@ -126,6 +135,8 @@ class TestConstraintSnapshot( hingeState is HingeState.Available && hingeState.isClosed() ConstraintData.HingeOpen -> hingeState is HingeState.Available && hingeState.isOpen() + ConstraintData.NotificationPanelShowing -> isNotificationPanelShowing + ConstraintData.NotificationPanelNotShowing -> !isNotificationPanelShowing } if (isSatisfied) { diff --git a/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt b/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt index 59e88eb0a4..2e0ec15787 100644 --- a/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt +++ b/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt @@ -40,6 +40,7 @@ object Keys { val defaultRepeatRate = intPreferencesKey("pref_repeat_delay") val defaultSequenceTriggerTimeout = intPreferencesKey("pref_sequence_trigger_timeout") val defaultHoldDownDuration = intPreferencesKey("pref_hold_down_duration") + val defaultDoNotRemap = booleanPreferencesKey("pref_default_do_not_remap") val toggleKeyboardOnToggleKeymaps = booleanPreferencesKey("key_toggle_keyboard_on_pause_resume_keymaps") val automaticBackupLocation = stringPreferencesKey("pref_automatic_backup_location") diff --git a/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt b/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt index f241647dd6..fe20a9229d 100644 --- a/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt +++ b/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt @@ -22,4 +22,6 @@ object PreferenceDefaults { // It is false by default and the first time they turn on the system bridge, // the preference will be set to true. const val KEY_EVENT_ACTIONS_USE_SYSTEM_BRIDGE = false + + const val DO_NOT_REMAP = false } 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..9e8a884ebe 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 @@ -65,6 +65,8 @@ data class ConstraintEntity( const val PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED = "constraint_physical_orientation_landscape_inverted" + const val DISPLAY_RESOLUTION = "constraint_display_resolution" + const val FLASHLIGHT_ON = "flashlight_on" const val FLASHLIGHT_OFF = "flashlight_off" @@ -88,12 +90,19 @@ 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" const val HINGE_CLOSED = "hinge_closed" const val HINGE_OPEN = "hinge_open" + const val NOTIFICATION_PANEL_SHOWING = "notification_panel_showing" + const val NOTIFICATION_PANEL_NOT_SHOWING = "notification_panel_not_showing" + const val TIME = "time" const val EXTRA_PACKAGE_NAME = "extra_package_name" @@ -104,6 +113,9 @@ data class ConstraintEntity( const val EXTRA_IME_ID = "extra_ime_id" const val EXTRA_IME_LABEL = "extra_ime_label" + const val EXTRA_RESOLUTION_WIDTH = "extra_resolution_width" + const val EXTRA_RESOLUTION_HEIGHT = "extra_resolution_height" + /** * The time is stored in the following format: 20:25. */ diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 7d694ae30b..dac41aeb1b 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -29,6 +29,13 @@ lane :production do version_code = get_properties_value(key: "VERSION_CODE", path: "./app/version.properties") version_name = get_properties_value(key: "VERSION_NAME", path: "./app/version.properties") + live_version_codes = google_play_track_version_codes() + latest_live_version_code = live_version_codes.max || 0 + + if version_code.to_i <= latest_live_version_code + UI.user_error!("VERSION_CODE (#{version_code}) in app/version.properties must be greater than the latest closed track version code (#{latest_live_version_code}).") + end + # Don't create changelog for f-droid because not committing it # File.write("metadata/android/en-US/changelogs/" + version_code + ".txt", whats_new) @@ -96,6 +103,13 @@ lane :internal do version_code = get_properties_value(key: "VERSION_CODE", path: "./app/version.properties") version_name = get_properties_value(key: "VERSION_NAME", path: "./app/version.properties") + live_version_codes = google_play_track_version_codes(track: "alpha") + latest_live_version_code = live_version_codes.max || 0 + + if version_code.to_i <= latest_live_version_code + UI.user_error!("VERSION_CODE (#{version_code}) in app/version.properties must be greater than the latest closed track version code (#{latest_live_version_code}).") + end + # Check Rust formatting and run tests Dir.chdir("../evdev/src/main/rust/evdev_manager") do sh("cargo fmt --check") 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 560e60e42d..48867bcfda 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 @@ -104,6 +104,9 @@ class AndroidDisplayAdapter @Inject constructor( override val isAmbientDisplayEnabled: MutableStateFlow = MutableStateFlow(isAodEnabled()) + override val supportedResolutions: MutableStateFlow> = + MutableStateFlow(getSupportedResolutions()) + private val orientationEventListener = object : OrientationEventListener(ctx) { override fun onOrientationChanged(orientationDegrees: Int) { if (orientationDegrees == ORIENTATION_UNKNOWN) { @@ -119,15 +122,15 @@ class AndroidDisplayAdapter @Inject constructor( displayManager.registerDisplayListener( object : DisplayManager.DisplayListener { override fun onDisplayAdded(displayId: Int) { - _orientation.update { getDisplayOrientation() } + onDisplaysChanged() } override fun onDisplayRemoved(displayId: Int) { - _orientation.update { getDisplayOrientation() } + onDisplaysChanged() } override fun onDisplayChanged(displayId: Int) { - _orientation.update { getDisplayOrientation() } + onDisplaysChanged() // This listener API has lower latency than the broadcast receiver so also use this. val isScreenStateOn = displayManager.displays.first().state == Display.STATE_ON @@ -283,7 +286,6 @@ class AndroidDisplayAdapter @Inject constructor( Surface.ROTATION_90 -> Orientation.ORIENTATION_90 Surface.ROTATION_180 -> Orientation.ORIENTATION_180 Surface.ROTATION_270 -> Orientation.ORIENTATION_270 - else -> throw Exception("Don't know how to convert $sdkRotation to Orientation") } @@ -291,6 +293,11 @@ class AndroidDisplayAdapter @Inject constructor( return SettingsUtils.getSecureSetting(ctx, "doze_always_on") == 1 } + private fun onDisplaysChanged() { + _orientation.update { getDisplayOrientation() } + supportedResolutions.update { getSupportedResolutions() } + } + /** * Converts sensor orientation degrees to PhysicalOrientation. * @@ -313,13 +320,25 @@ class AndroidDisplayAdapter @Inject constructor( degrees >= (360 - ORIENTATION_TOLERANCE) || degrees < 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 in transition zone } } + + private fun getSupportedResolutions(): Set { + val display = displayManager.displays.firstOrNull() ?: return emptySet() + + return display.supportedModes + .map { mode -> SizeKM(mode.physicalWidth, mode.physicalHeight) } + .toSet() + } } 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 377e148044..cec0291d52 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 @@ -5,6 +5,7 @@ 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 +import kotlinx.coroutines.flow.StateFlow interface DisplayAdapter { val isScreenOn: Flow @@ -15,6 +16,12 @@ interface DisplayAdapter { val size: SizeKM val isAmbientDisplayEnabled: Flow + /** + * The distinct resolutions supported by the default display, taken from the + * display's supported modes. The dimensions are in the display's natural orientation. + */ + val supportedResolutions: StateFlow> + fun isAutoRotateEnabled(): Boolean fun enableAutoRotate(): KMResult<*> fun disableAutoRotate(): KMResult<*> 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/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, 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<*>