diff --git a/.github/workflows/crowdin-actions.yml b/.github/workflows/crowdin-actions.yml index bdc008c7c9..e0f46f3c7f 100644 --- a/.github/workflows/crowdin-actions.yml +++ b/.github/workflows/crowdin-actions.yml @@ -20,6 +20,7 @@ jobs: - name: crowdin action uses: crowdin/github-action@v2 + if: github.event.repository.fork == false with: upload_sources: true upload_translations: false diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 754ebd0179..7bef735539 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -96,6 +96,7 @@ jobs: ruby-version: '3.3' - name: Create debug keystore + if: github.event.repository.fork == false env: CI_KEYSTORE: ${{ secrets.CI_KEYSTORE }} run: | @@ -123,6 +124,7 @@ jobs: - name: Upload to Discord uses: sinshutu/upload-to-discord@v2.0.0 + if: github.event.repository.fork == false env: DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} with: @@ -130,7 +132,7 @@ jobs: - name: Report build status to Discord uses: sarisia/actions-status-discord@v1 - if: failure() + if: github.event.repository.fork == false && failure() with: title: "Build apk" webhook: ${{ secrets.DISCORD_BUILD_STATUS_WEBHOOK }} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 82613d7e1e..c5a30e661a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -98,11 +98,14 @@ android { dimension "pro" File file = rootProject.file("local.properties") + String keyName = "REVENUECAT_API_KEY" if (file.exists()) { def localProperties = new Properties() localProperties.load(new FileInputStream(file)) - buildConfigField("String", "REVENUECAT_API_KEY", localProperties["REVENUECAT_API_KEY"]) + if (localProperties.hasProperty(keyName)) { + buildConfigField("String", keyName, localProperties[keyName]) + } } } } diff --git a/app/src/main/java/io/github/sds100/keymapper/data/Keys.kt b/app/src/main/java/io/github/sds100/keymapper/data/Keys.kt index ae579aede1..06ca3f1fec 100644 --- a/app/src/main/java/io/github/sds100/keymapper/data/Keys.kt +++ b/app/src/main/java/io/github/sds100/keymapper/data/Keys.kt @@ -52,6 +52,8 @@ object Keys { booleanPreferencesKey("key_shown_parallel_trigger_order_warning") val shownSequenceTriggerExplanation = booleanPreferencesKey("key_shown_sequence_trigger_explanation_dialog") + val shownKeyCodeToScanCodeTriggerExplanation = + booleanPreferencesKey("key_shown_keycode_to_scancode_trigger_explanation_dialog") val lastInstalledVersionCodeHomeScreen = intPreferencesKey("last_installed_version_home_screen") val lastInstalledVersionCodeBackground = diff --git a/app/src/main/java/io/github/sds100/keymapper/mappings/keymaps/trigger/BaseConfigTriggerViewModel.kt b/app/src/main/java/io/github/sds100/keymapper/mappings/keymaps/trigger/BaseConfigTriggerViewModel.kt index f65d21ee6d..7116cf1550 100644 --- a/app/src/main/java/io/github/sds100/keymapper/mappings/keymaps/trigger/BaseConfigTriggerViewModel.kt +++ b/app/src/main/java/io/github/sds100/keymapper/mappings/keymaps/trigger/BaseConfigTriggerViewModel.kt @@ -456,6 +456,24 @@ abstract class BaseConfigTriggerViewModel( // need to be dismissed before it is added. config.addKeyCodeTriggerKey(key.keyCode, key.device, key.detectionSource) + if (key.keyCode >= InputEventUtils.KEYCODE_TO_SCANCODE_OFFSET || key.keyCode < 0) { + if (onboarding.shownKeyCodeToScanCodeTriggerExplanation) { + return + } + + val dialog = PopupUi.Dialog( + title = getString(R.string.dialog_title_keycode_to_scancode_trigger_explanation), + message = getString(R.string.dialog_message_keycode_to_scancode_trigger_explanation), + positiveButtonText = getString(R.string.pos_understood), + ) + + val response = showPopup("keycode_to_scancode_message", dialog) + + if (response == DialogResponse.POSITIVE) { + onboarding.shownKeyCodeToScanCodeTriggerExplanation = true + } + } + if (key.keyCode == KeyEvent.KEYCODE_CAPS_LOCK) { val dialog = PopupUi.Ok( message = getString(R.string.dialog_message_enable_physical_keyboard_caps_lock_a_keyboard_layout), diff --git a/app/src/main/java/io/github/sds100/keymapper/onboarding/OnboardingUseCase.kt b/app/src/main/java/io/github/sds100/keymapper/onboarding/OnboardingUseCase.kt index bc12b49c18..73b90c0118 100644 --- a/app/src/main/java/io/github/sds100/keymapper/onboarding/OnboardingUseCase.kt +++ b/app/src/main/java/io/github/sds100/keymapper/onboarding/OnboardingUseCase.kt @@ -65,6 +65,10 @@ class OnboardingUseCaseImpl( Keys.shownSequenceTriggerExplanation, false, ) + override var shownKeyCodeToScanCodeTriggerExplanation by PrefDelegate( + Keys.shownKeyCodeToScanCodeTriggerExplanation, + false, + ) override val showWhatsNew = get(Keys.lastInstalledVersionCodeHomeScreen) .map { (it ?: -1) < Constants.VERSION_CODE } @@ -167,6 +171,7 @@ interface OnboardingUseCase { var shownParallelTriggerOrderExplanation: Boolean var shownSequenceTriggerExplanation: Boolean + var shownKeyCodeToScanCodeTriggerExplanation: Boolean val showFloatingButtonFeatureNotification: Flow fun showedFloatingButtonFeatureNotification() diff --git a/app/src/main/java/io/github/sds100/keymapper/system/accessibility/BaseAccessibilityServiceController.kt b/app/src/main/java/io/github/sds100/keymapper/system/accessibility/BaseAccessibilityServiceController.kt index 1c6981df4b..c56d83205e 100644 --- a/app/src/main/java/io/github/sds100/keymapper/system/accessibility/BaseAccessibilityServiceController.kt +++ b/app/src/main/java/io/github/sds100/keymapper/system/accessibility/BaseAccessibilityServiceController.kt @@ -24,6 +24,7 @@ import io.github.sds100.keymapper.mappings.keymaps.trigger.KeyEventDetectionSour import io.github.sds100.keymapper.reroutekeyevents.RerouteKeyEventsController import io.github.sds100.keymapper.reroutekeyevents.RerouteKeyEventsUseCase import io.github.sds100.keymapper.system.devices.DevicesAdapter +import io.github.sds100.keymapper.system.inputevents.InputEventUtils import io.github.sds100.keymapper.system.inputevents.MyKeyEvent import io.github.sds100.keymapper.system.inputevents.MyMotionEvent import io.github.sds100.keymapper.system.inputmethod.InputMethodAdapter @@ -296,6 +297,29 @@ abstract class BaseAccessibilityServiceController( open fun onConfigurationChanged(newConfig: Configuration) { } + /** + * Returns an MyKeyEvent which is either the same or more unique + */ + private fun getUniqueEvent(event: MyKeyEvent): MyKeyEvent { + // Guard to ignore processing when not applicable + if (event.keyCode != KeyEvent.KEYCODE_UNKNOWN) return event + + // Don't offset negative values + val scanCodeOffset: Int = if (event.scanCode >= 0) { + InputEventUtils.KEYCODE_TO_SCANCODE_OFFSET + } else { + 0 + } + + val eventProxy = event.copy( + // Fallback to scanCode when keyCode is unknown as it's typically more unique + // Add offset to go past possible keyCode values + keyCode = event.scanCode + scanCodeOffset, + ) + + return eventProxy + } + fun onKeyEvent( event: MyKeyEvent, detectionSource: KeyEventDetectionSource = KeyEventDetectionSource.ACCESSIBILITY_SERVICE, @@ -305,11 +329,14 @@ abstract class BaseAccessibilityServiceController( if (recordingTrigger) { if (event.action == KeyEvent.ACTION_DOWN) { Timber.d("Recorded key ${KeyEvent.keyCodeToString(event.keyCode)}, $detailedLogInfo") + + val uniqueEvent: MyKeyEvent = getUniqueEvent(event) + coroutineScope.launch { outputEvents.emit( ServiceEvent.RecordedTriggerKey( - event.keyCode, - event.device, + uniqueEvent.keyCode, + uniqueEvent.device, detectionSource, ), ) @@ -327,16 +354,17 @@ abstract class BaseAccessibilityServiceController( } else { try { var consume: Boolean + val uniqueEvent: MyKeyEvent = getUniqueEvent(event) - consume = keyMapController.onKeyEvent(event) + consume = keyMapController.onKeyEvent(uniqueEvent) if (!consume) { - consume = rerouteKeyEventsController.onKeyEvent(event) + consume = rerouteKeyEventsController.onKeyEvent(uniqueEvent) } - when (event.action) { - KeyEvent.ACTION_DOWN -> Timber.d("Down ${KeyEvent.keyCodeToString(event.keyCode)} - consumed: $consume, $detailedLogInfo") - KeyEvent.ACTION_UP -> Timber.d("Up ${KeyEvent.keyCodeToString(event.keyCode)} - consumed: $consume, $detailedLogInfo") + when (uniqueEvent.action) { + KeyEvent.ACTION_DOWN -> Timber.d("Down ${KeyEvent.keyCodeToString(uniqueEvent.keyCode)} - consumed: $consume, $detailedLogInfo") + KeyEvent.ACTION_UP -> Timber.d("Up ${KeyEvent.keyCodeToString(uniqueEvent.keyCode)} - consumed: $consume, $detailedLogInfo") } return consume diff --git a/app/src/main/java/io/github/sds100/keymapper/system/inputevents/InputEventUtils.kt b/app/src/main/java/io/github/sds100/keymapper/system/inputevents/InputEventUtils.kt index 385250d437..e70541825a 100644 --- a/app/src/main/java/io/github/sds100/keymapper/system/inputevents/InputEventUtils.kt +++ b/app/src/main/java/io/github/sds100/keymapper/system/inputevents/InputEventUtils.kt @@ -686,12 +686,22 @@ object InputEventUtils { KeyEvent.KEYCODE_FUNCTION, ) + /** + * Used for keyCode to scanCode fallback to go past possible keyCode values + */ + val KEYCODE_TO_SCANCODE_OFFSET: Int + get() = KeyEvent.getMaxKeyCode() + 1 + /** * Create a text representation of a key event. E.g if the control key was pressed, * "Ctrl" will be returned */ fun keyCodeToString(keyCode: Int): String = NON_CHARACTER_KEY_LABELS[keyCode].let { - it ?: "unknown keycode $keyCode" + if (keyCode >= KEYCODE_TO_SCANCODE_OFFSET || keyCode < 0) { + "scancode $keyCode" + } else { + it ?: "unknown keycode $keyCode" + } } fun isModifierKey(keyCode: Int): Boolean = keyCode in MODIFIER_KEYCODES diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a2fc856edd..fc0c5311e3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -492,6 +492,9 @@ Drag handle for %1$s Show example + Unrecognized key code + The pressed button was not recognized by the input system. In the past Key Mapper detected such buttons as one and the same. Currently the app tries to distinguish the button based on the scan code, which should be more unique. However, this is a makeshift incomplete solution, which doesn\'t guarantee uniqueness. + Done Guide Guide @@ -503,6 +506,7 @@ Apply Discard changes Save + Understood Turn off Cancel diff --git a/docs/includes/action-type-list.md b/docs/includes/action-type-list.md index a08a63c144..f2fe240cb6 100644 --- a/docs/includes/action-type-list.md +++ b/docs/includes/action-type-list.md @@ -6,7 +6,7 @@ Tab | Description | | [System](../user-guide/actions#system) | Choose a system operation (such as toggling Bluetooth, opening the home menu, toggling flashlight) | | [Key](../user-guide/actions#key) | An alternative way to choose a key press action, by pressing the key that you want to map to. | | [Tap screen (2.1.0+)](../user-guide/actions#tap-screen-210) | Emulate a screen tap at a specific location on your screen. | -| [Key event (2.1.0+)](../user-guide/actions#key-event-210) | Emulate a key press from a specifc connected device. | +| [Key event (2.1.0+)](../user-guide/actions#key-event-210) | Emulate a key press from a specific connected device. | | [Text](../user-guide/actions#text) | Emulate typing a string. | | [Intent (2.3.0+)](../user-guide/actions#intent-230) | See [this page.](../user-guide/actions/#intent-230) | | [Phone call (2.3.0+)](../user-guide/actions#phone-call-230) | Call a telephone number. Network and carrier rates will apply. | diff --git a/docs/includes/configuring-constraints.md b/docs/includes/configuring-constraints.md index 1788995a89..3621436a9e 100644 --- a/docs/includes/configuring-constraints.md +++ b/docs/includes/configuring-constraints.md @@ -1,6 +1,6 @@ Constraints allow you to restrict your mappings to only work in some situations. -To add a constraint fron the 'Constraints and more' or 'Options' tab, tap 'Add constraint'. +To add a constraint from the 'Constraints and more' or 'Options' tab, tap 'Add constraint'. Go [here](/user-guide/constraints) to see how you can configure constraints. \ No newline at end of file diff --git a/docs/user-guide/actions.md b/docs/user-guide/actions.md index d99dc17012..e1e2e879f5 100644 --- a/docs/user-guide/actions.md +++ b/docs/user-guide/actions.md @@ -3,7 +3,7 @@ Launch an app. !!! warning "Extra permission on Xiaomi devices!" - See issue [#1370](https://github.com/keymapperorg/KeyMapper/issues/1370https://github.com/keymapperorg/KeyMapper/issues/1370). Xioami blocks apps from launching apps when they are in the background unless you give permission to "Display pop-up windows while running in the background" and "Display pop-up window". Follow these steps through the Settings app: Apps > Manage apps > Key Mapper App Settings > Other Permissions > Display pop-up windows while running in the background. + See issue [#1370](https://github.com/keymapperorg/KeyMapper/issues/1370). Xiaomi blocks apps from launching apps when they are in the background unless you give permission to "Display pop-up windows while running in the background" and "Display pop-up window". Follow these steps through the Settings app: Apps > Manage apps > Key Mapper App Settings > Other Permissions > Display pop-up windows while running in the background. ### Launch app shortcut @@ -24,7 +24,7 @@ Android restricts what apps can do with this so you won't be able to tap the scr ### Swipe screen (2.5.0+, Android 7.0+) -This will swipe from a start point to and end point on your screen. You can also setup the amount of "fingers" to simulate and the duration for the gesture, **but** this is limitied due to your Android Version. +This will swipe from a start point to and end point on your screen. You can also setup the amount of "fingers" to simulate and the duration for the gesture, **but** this is limited due to your Android Version. See: [getMaxStrokeCount](https://developer.android.com/reference/android/accessibilityservice/GestureDescription#getMaxStrokeCount()) and [getMaxStrokeDuration](https://developer.android.com/reference/android/accessibilityservice/GestureDescription#getMaxGestureDuration()) for more information. @@ -32,7 +32,7 @@ See: [getMaxStrokeCount](https://developer.android.com/reference/android/accessi This will simulate a pinch gesture from a start point to and end point on your screen. You can choose between *pinch in* and *pinch out* and also the "pinch distance" for the pinch gesture. This is the distance between the start and the end point. The higher the distance, the stronger the pinch gesture, so you may want to start with a lower value for the pinch with max. 100. Later on you can adjust this by your needs, **but** the endpoints will never be less than 0 due to android restrictions. -You can also setup the amount of "fingers" to simulate and the duration for the gesture, **but** this is limitied due to your Android Version. +You can also setup the amount of "fingers" to simulate and the duration for the gesture, **but** this is limited due to your Android Version. See: [getMaxStrokeCount](https://developer.android.com/reference/android/accessibilityservice/GestureDescription#getMaxStrokeCount()) and [getMaxStrokeDuration](https://developer.android.com/reference/android/accessibilityservice/GestureDescription#getMaxGestureDuration()) for more information.