Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/crowdin-actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down Expand Up @@ -123,14 +124,15 @@ 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:
args: app/build/outputs/apk/free/ci/${{ env.APK_NAME }}.apk

- 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 }}
5 changes: 4 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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])
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/io/github/sds100/keymapper/data/Keys.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -167,6 +171,7 @@ interface OnboardingUseCase {

var shownParallelTriggerOrderExplanation: Boolean
var shownSequenceTriggerExplanation: Boolean
var shownKeyCodeToScanCodeTriggerExplanation: Boolean

val showFloatingButtonFeatureNotification: Flow<Boolean>
fun showedFloatingButtonFeatureNotification()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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,
),
)
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,9 @@
<string name="drag_handle_for">Drag handle for %1$s</string>
<string name="show_example">Show example</string>

<string name="dialog_title_keycode_to_scancode_trigger_explanation">Unrecognized key code</string>
<string name="dialog_message_keycode_to_scancode_trigger_explanation">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.</string>

<string name="pos_done">Done</string>
<string name="pos_grant_write_secure_settings_guide">Guide</string>
<string name="pos_start_service_with_adb_guide">Guide</string>
Expand All @@ -503,6 +506,7 @@
<string name="pos_apply">Apply</string>
<string name="pos_discard_changes">Discard changes</string>
<string name="pos_save">Save</string>
<string name="pos_understood">Understood</string>

<string name="neg_turn_off">Turn off</string>
<string name="neg_cancel">Cancel</string>
Expand Down
2 changes: 1 addition & 1 deletion docs/includes/action-type-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down
2 changes: 1 addition & 1 deletion docs/includes/configuring-constraints.md
Original file line number Diff line number Diff line change
@@ -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.
6 changes: 3 additions & 3 deletions docs/user-guide/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -24,15 +24,15 @@ 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.

### Pinch screen (2.5.0+, Android 7.0+)

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.

Expand Down