Skip to content

Commit

Permalink
Merge pull request #1237 from keymapperorg/1218-delay-when-inputting-…
Browse files Browse the repository at this point in the history
…key-events-through-input-method-on-android-14

Delay when inputting key events through input method on android 14
  • Loading branch information
sds100 authored Jun 30, 2024
2 parents 8e5d7e2 + 3d9c4bf commit bca5053
Show file tree
Hide file tree
Showing 18 changed files with 406 additions and 169 deletions.
2 changes: 1 addition & 1 deletion app/src/ci/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<permission android:name="io.github.sds100.keymapper.ci.KEY_EVENT_RECEIVER">
<permission android:name="io.github.sds100.keymapper.ci.KEY_EVENT_RELAY_SERVICE">
<!-- I would make this have signature protection level
but I already released the Key Mapper GUI Keyboard
with a different signature -->
Expand Down
2 changes: 1 addition & 1 deletion app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<permission android:name="io.github.sds100.keymapper.debug.KEY_EVENT_RECEIVER">
<permission android:name="io.github.sds100.keymapper.debug.KEY_EVENT_RELAY_SERVICE">
<!-- I would make this have signature protection level
but I already released the Key Mapper GUI Keyboard
with a different signature -->
Expand Down
2 changes: 1 addition & 1 deletion app/src/debug_release/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<permission android:name="io.github.sds100.keymapper.KEY_EVENT_RECEIVER">
<permission android:name="io.github.sds100.keymapper.KEY_EVENT_RELAY_SERVICE">
<!-- I would make this have signature protection level
but I already released the Key Mapper GUI Keyboard
with a different signature -->
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<uses-permission android:name="com.termux.permission.RUN_COMMAND" />

<!-- This permission is in each build variant's AndroidManifest -->
<!-- <permission android:name="io.github.sds100.keymapper.KEY_EVENT_RECEIVER">-->
<!-- <permission android:name="io.github.sds100.keymapper.KEY_EVENT_RELAY_SERVICE">-->
<!-- </permission>-->

<uses-permission
Expand Down Expand Up @@ -238,9 +238,9 @@
android:permission="android.permission.BIND_JOB_SERVICE" />

<service
android:name=".api.KeyEventReceiver"
android:name=".api.KeyEventRelayService"
android:exported="true"
android:permission="io.github.sds100.keymapper.KEY_EVENT_RECEIVER" />
android:permission="io.github.sds100.keymapper.KEY_EVENT_RELAY_SERVICE" />

<service
android:name=".system.inputmethod.ObserveInputMethodsJob"
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.github.sds100.keymapper.api;

import android.view.KeyEvent;
import io.github.sds100.keymapper.api.IKeyEventRelayServiceCallback;

interface IKeyEventRelayService {
/**
* Send a key event to the target package that is registered with
* a callback.
*/
boolean sendKeyEvent(in KeyEvent event, in String targetPackageName);

/**
* Register a callback to receive key events from this relay service. The service
* checks the process uid of the caller to this method and only permits certain applications
* from connecting.
*/
void registerCallback(IKeyEventRelayServiceCallback client);

/**
* Unregister a callback to receive key events from this relay service. The service
* checks the process uid of the caller to this method and only permits certain applications
* from connecting.
*/
void unregisterCallback();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.github.sds100.keymapper.api;

import android.view.KeyEvent;

interface IKeyEventRelayServiceCallback {
boolean onKeyEvent(in KeyEvent event, in String sourcePackageName);
}
59 changes: 37 additions & 22 deletions app/src/main/java/io/github/sds100/keymapper/UseCases.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import io.github.sds100.keymapper.actions.CreateActionUseCaseImpl
import io.github.sds100.keymapper.actions.GetActionErrorUseCaseImpl
import io.github.sds100.keymapper.actions.IsActionSupportedUseCaseImpl
import io.github.sds100.keymapper.actions.PerformActionsUseCaseImpl
import io.github.sds100.keymapper.api.KeyEventRelayServiceWrapper
import io.github.sds100.keymapper.constraints.DetectConstraintsUseCaseImpl
import io.github.sds100.keymapper.constraints.GetConstraintErrorUseCaseImpl
import io.github.sds100.keymapper.mappings.DetectMappingUseCaseImpl
Expand Down Expand Up @@ -140,7 +141,11 @@ object UseCases {
ServiceLocator.powerAdapter(service),
)

fun performActions(ctx: Context, service: IAccessibilityService) =
fun performActions(
ctx: Context,
service: IAccessibilityService,
keyEventRelayService: KeyEventRelayServiceWrapper,
) =
PerformActionsUseCaseImpl(
(ctx.applicationContext as KeyMapperApp).appCoroutineScope,
service,
Expand All @@ -150,7 +155,7 @@ object UseCases {
Shell,
ServiceLocator.intentAdapter(ctx),
getActionError(ctx),
keyMapperImeMessenger(ctx),
keyMapperImeMessenger(ctx, keyEventRelayService),
ShizukuInputEventInjector(),
ServiceLocator.packageManagerAdapter(ctx),
ServiceLocator.appShortcutAdapter(ctx),
Expand Down Expand Up @@ -181,19 +186,23 @@ object UseCases {
ServiceLocator.resourceProvider(ctx),
)

fun detectKeyMaps(service: MyAccessibilityService) = DetectKeyMapsUseCaseImpl(
detectMappings(service),
ServiceLocator.roomKeymapRepository(service),
ServiceLocator.settingsRepository(service),
ServiceLocator.suAdapter(service),
ServiceLocator.displayAdapter(service),
ServiceLocator.audioAdapter(service),
keyMapperImeMessenger(service),
fun detectKeyMaps(
ctx: Context,
service: IAccessibilityService,
keyEventRelayService: KeyEventRelayServiceWrapper,
) = DetectKeyMapsUseCaseImpl(
detectMappings(ctx),
ServiceLocator.roomKeymapRepository(ctx),
ServiceLocator.settingsRepository(ctx),
ServiceLocator.suAdapter(ctx),
ServiceLocator.displayAdapter(ctx),
ServiceLocator.audioAdapter(ctx),
keyMapperImeMessenger(ctx, keyEventRelayService),
service,
ShizukuInputEventInjector(),
ServiceLocator.permissionAdapter(service),
ServiceLocator.phoneAdapter(service),
ServiceLocator.inputMethodAdapter(service),
ServiceLocator.permissionAdapter(ctx),
ServiceLocator.phoneAdapter(ctx),
ServiceLocator.inputMethodAdapter(ctx),
)

fun detectFingerprintMaps(ctx: Context) = DetectFingerprintMapsUseCaseImpl(
Expand All @@ -202,18 +211,24 @@ object UseCases {
detectMappings(ctx),
)

fun rerouteKeyEvents(ctx: Context) = RerouteKeyEventsUseCaseImpl(
ServiceLocator.inputMethodAdapter(ctx),
keyMapperImeMessenger(ctx),
ServiceLocator.settingsRepository(ctx),
)
fun rerouteKeyEvents(ctx: Context, keyEventRelayService: KeyEventRelayServiceWrapper) =
RerouteKeyEventsUseCaseImpl(
ServiceLocator.inputMethodAdapter(ctx),
keyMapperImeMessenger(ctx, keyEventRelayService),
ServiceLocator.settingsRepository(ctx),
)

fun createAction(ctx: Context) = CreateActionUseCaseImpl(
ServiceLocator.inputMethodAdapter(ctx),
)

private fun keyMapperImeMessenger(ctx: Context) = KeyMapperImeMessengerImpl(
ctx,
ServiceLocator.inputMethodAdapter(ctx),
)
private fun keyMapperImeMessenger(
ctx: Context,
keyEventRelayService: KeyEventRelayServiceWrapper,
) =
KeyMapperImeMessengerImpl(
ctx,
keyEventRelayService,
ServiceLocator.inputMethodAdapter(ctx),
)
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package io.github.sds100.keymapper.api

import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.DeadObjectException
import android.os.IBinder
import android.view.KeyEvent
import io.github.sds100.keymapper.system.inputmethod.KeyMapperImeHelper
import timber.log.Timber
import java.util.concurrent.ConcurrentHashMap

/**
* This service is used as a relay between the accessibility service and input method service to pass
* key events back and forth. A separate service has to be used because you can't bind to an
* accessibility or input method service.
*
* This is used for key event actions. The input method service registers a callback
* and the accessibility service sends the key events.
*
* This was implemented in issue #850 for the action to answer phone calls because Android doesn't
* pass volume down key events to the accessibility service when the phone is ringing or it is
* in a phone call. The accessibility service registers a callback and the input method service
* sends the key events.
*/
class KeyEventRelayService : Service() {
val permittedPackages = KeyMapperImeHelper.KEY_MAPPER_IME_PACKAGE_LIST

private val binderInterface: IKeyEventRelayService = object : IKeyEventRelayService.Stub() {
override fun sendKeyEvent(event: KeyEvent?, targetPackageName: String?): Boolean {
synchronized(callbackLock) {
Timber.d("KeyEventRelayService: onKeyEvent ${event?.keyCode}")

val callback = callbacks[targetPackageName]

if (callback == null) {
return false
} else {
try {
// Get the process ID of the app that called this service.
val sourcePackageName = getCallerPackageName() ?: return false

if (!permittedPackages.contains(sourcePackageName)) {
Timber.d("An unrecognized package $sourcePackageName tried to send a key event.")

return false
}

return callback.onKeyEvent(event, targetPackageName)
} catch (e: DeadObjectException) {
// If the application is no longer connected then delete the callback.
callbacks.remove(targetPackageName)
return false
}
}
}
}

override fun registerCallback(client: IKeyEventRelayServiceCallback?) {
val sourcePackageName = getCallerPackageName() ?: return

if (client == null || !permittedPackages.contains(sourcePackageName)) {
Timber.d("An unrecognized package $sourcePackageName tried to register a key event relay callback.")
return
}

synchronized(callbackLock) {
Timber.d("Package $sourcePackageName registered a key event relay callback.")
callbacks[sourcePackageName] = client
}
}

override fun unregisterCallback() {
synchronized(callbackLock) {
val sourcePackageName = getCallerPackageName() ?: return

Timber.d("Package $sourcePackageName unregistered a key event relay callback.")

callbacks.remove(sourcePackageName)
}
}
}

private val callbackLock: Any = Any()
private var callbacks: ConcurrentHashMap<String, IKeyEventRelayServiceCallback> =
ConcurrentHashMap()

override fun onBind(intent: Intent?): IBinder? = binderInterface.asBinder()

private fun getCallerPackageName(): String? {
val sourceUid = Binder.getCallingUid()
return applicationContext.packageManager.getNameForUid(sourceUid)
}
}
Loading

0 comments on commit bca5053

Please sign in to comment.