Skip to content

Commit 345c6c1

Browse files
committed
#1235 fix: use new key event relay service WIP
1 parent 7bf7116 commit 345c6c1

6 files changed

Lines changed: 163 additions & 58 deletions

File tree

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
2323
<uses-permission android:name="android.permission.VIBRATE" />
2424
<uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" />
25-
<uses-permission android:name="io.github.sds100.keymapper.KEY_EVENT_RECEIVER" />
25+
<uses-permission android:name="io.github.sds100.keymapper.KEY_EVENT_RELAY_SERVICE" />
2626

27-
<!-- A signature-protected permission to ask AOSP Keyboard to close the software keyboard.
27+
<!-- A signature-protected permission to ask AOSP Keyboar d to close the software keyboard.
2828
To use this, add the following line into calling application's AndroidManifest.xml
2929
<pre>
3030
{@code
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.github.sds100.keymapper;
2+
3+
import android.view.KeyEvent;
4+
import io.github.sds100.keymapper.IKeyEventRelayServiceCallback;
5+
6+
interface IKeyEventRelayService {
7+
/**
8+
* Send a key event to the target package that is registered with
9+
* a callback.
10+
*/
11+
boolean sendKeyEvent(in KeyEvent event, in String targetPackageName);
12+
13+
/**
14+
* Register a callback to receive key events from this relay service. The service
15+
* checks the process uid of the caller to this method and only permits certain applications
16+
* from connecting.
17+
*/
18+
void registerCallback(IKeyEventRelayServiceCallback client);
19+
20+
/**
21+
* Unregister a callback to receive key events from this relay service. The service
22+
* checks the process uid of the caller to this method and only permits certain applications
23+
* from connecting.
24+
*/
25+
void unregisterCallback();
26+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.github.sds100.keymapper;
2+
3+
import android.view.KeyEvent;
4+
5+
interface IKeyEventRelayServiceCallback {
6+
boolean onKeyEvent(in KeyEvent event, in String sourcePackageName);
7+
}

app/src/main/aidl/io/github/sds100/keymapper/api/IKeyEventReceiver.aidl

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package io.github.sds100.keymapper.inputmethod.latin
2+
3+
import android.content.ComponentName
4+
import android.content.Context
5+
import android.content.Intent
6+
import android.content.ServiceConnection
7+
import android.os.DeadObjectException
8+
import android.os.IBinder
9+
import android.util.Log
10+
import android.view.KeyEvent
11+
import io.github.sds100.keymapper.IKeyEventRelayService
12+
import io.github.sds100.keymapper.IKeyEventRelayServiceCallback
13+
14+
/**
15+
* This handles connecting to the relay service and exposes an interface
16+
* so other parts of the app can get a reference to the service even when it isn't
17+
* bound yet.
18+
*/
19+
class KeyEventRelayServiceWrapperImpl(
20+
context: Context,
21+
private val callback: IKeyEventRelayServiceCallback,
22+
) : KeyEventRelayServiceWrapper {
23+
private val ctx: Context = context.applicationContext
24+
25+
private val keyEventRelayServiceLock: Any = Any()
26+
private var keyEventRelayService: IKeyEventRelayService? = null
27+
28+
private val keyEventReceiverConnection: ServiceConnection =
29+
object : ServiceConnection {
30+
override fun onServiceConnected(
31+
name: ComponentName?,
32+
service: IBinder?,
33+
) {
34+
synchronized(keyEventRelayServiceLock) {
35+
keyEventRelayService = IKeyEventRelayService.Stub.asInterface(service)
36+
Log.e(LatinIME.TAG, "Register callback")
37+
keyEventRelayService?.registerCallback(callback)
38+
}
39+
}
40+
41+
override fun onServiceDisconnected(name: ComponentName?) {
42+
synchronized(keyEventRelayServiceLock) {
43+
keyEventRelayService?.unregisterCallback()
44+
keyEventRelayService = null
45+
}
46+
}
47+
}
48+
49+
override fun sendKeyEvent(
50+
event: KeyEvent?,
51+
targetPackageName: String?,
52+
): Boolean {
53+
synchronized(keyEventRelayServiceLock) {
54+
if (keyEventRelayService == null) {
55+
return false
56+
}
57+
58+
try {
59+
return keyEventRelayService!!.sendKeyEvent(event, targetPackageName)
60+
} catch (e: DeadObjectException) {
61+
keyEventRelayService = null
62+
return false
63+
}
64+
}
65+
}
66+
67+
fun bind() {
68+
try {
69+
val keyEventReceiverComponent =
70+
ComponentName("io.github.sds100.keymapper.debug", "io.github.sds100.keymapper.api.KeyEventRelayService")
71+
val keyEventReceiverServiceIntent = Intent()
72+
keyEventReceiverServiceIntent.setComponent(keyEventReceiverComponent)
73+
ctx.bindService(keyEventReceiverServiceIntent, keyEventReceiverConnection, 0)
74+
75+
Log.e(LatinIME.TAG, "Bind to service")
76+
} catch (e: SecurityException) {
77+
Log.e(LatinIME.TAG, e.toString())
78+
}
79+
}
80+
81+
fun unbind() {
82+
try {
83+
ctx.unbindService(keyEventReceiverConnection)
84+
} catch (e: DeadObjectException) {
85+
// do nothing
86+
}
87+
}
88+
}
89+
90+
interface KeyEventRelayServiceWrapper {
91+
fun sendKeyEvent(
92+
event: KeyEvent?,
93+
targetPackageName: String?,
94+
): Boolean
95+
}

app/src/main/java/io/github/sds100/keymapper/inputmethod/latin/LatinIME.java

Lines changed: 33 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,13 @@
1616

1717
package io.github.sds100.keymapper.inputmethod.latin;
1818

19-
import static io.github.sds100.keymapper.inputmethod.latin.common.Constants.ImeOption.FORCE_ASCII;
20-
import static io.github.sds100.keymapper.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE;
21-
import static io.github.sds100.keymapper.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE_COMPAT;
22-
2319
import android.app.AlertDialog;
2420
import android.content.BroadcastReceiver;
25-
import android.content.ComponentName;
2621
import android.content.Context;
2722
import android.content.DialogInterface;
2823
import android.content.DialogInterface.OnClickListener;
2924
import android.content.Intent;
3025
import android.content.IntentFilter;
31-
import android.content.ServiceConnection;
3226
import android.content.res.Configuration;
3327
import android.content.res.Resources;
3428
import android.graphics.Color;
@@ -39,7 +33,6 @@
3933
import android.os.IBinder;
4034
import android.os.Message;
4135
import android.os.Process;
42-
import android.os.RemoteException;
4336
import android.text.InputType;
4437
import android.util.Log;
4538
import android.util.PrintWriterPrinter;
@@ -65,7 +58,7 @@
6558

6659
import javax.annotation.Nonnull;
6760

68-
import io.github.sds100.keymapper.api.IKeyEventReceiver;
61+
import io.github.sds100.keymapper.IKeyEventRelayServiceCallback;
6962
import io.github.sds100.keymapper.inputmethod.accessibility.AccessibilityUtils;
7063
import io.github.sds100.keymapper.inputmethod.annotations.UsedForTesting;
7164
import io.github.sds100.keymapper.inputmethod.compat.EditorInfoCompatUtils;
@@ -108,6 +101,10 @@
108101
import io.github.sds100.keymapper.inputmethod.latin.utils.SubtypeLocaleUtils;
109102
import io.github.sds100.keymapper.inputmethod.latin.utils.ViewLayoutUtils;
110103

104+
import static io.github.sds100.keymapper.inputmethod.latin.common.Constants.ImeOption.FORCE_ASCII;
105+
import static io.github.sds100.keymapper.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE;
106+
import static io.github.sds100.keymapper.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE_COMPAT;
107+
111108
/**
112109
* Input method implementation for Qwerty'ish keyboard.
113110
*/
@@ -167,7 +164,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
167164
private SuggestionStripView mSuggestionStripView;
168165

169166
private RichInputMethodManager mRichImm;
170-
@UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
167+
@UsedForTesting
168+
final KeyboardSwitcher mKeyboardSwitcher;
171169
private final SubtypeState mSubtypeState = new SubtypeState();
172170
private EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector;
173171
private StatsUtilsManager mStatsUtilsManager;
@@ -199,6 +197,7 @@ public void onReceive(Context context, Intent intent) {
199197
}
200198
}
201199
}
200+
202201
final HideSoftInputReceiver mHideSoftInputReceiver = new HideSoftInputReceiver(this);
203202

204203
final static class RestartAfterDeviceUnlockReceiver extends BroadcastReceiver {
@@ -220,22 +219,17 @@ public void onReceive(Context context, Intent intent) {
220219

221220
final RestartAfterDeviceUnlockReceiver mRestartAfterDeviceUnlockReceiver = new RestartAfterDeviceUnlockReceiver();
222221

223-
private final Object mKeyEventReceiverLock = new Object();
224-
private IKeyEventReceiver mKeyEventReceiverBinder = null;
225-
226-
private final ServiceConnection mKeyEventReceiverConnection = new ServiceConnection() {
222+
private KeyEventRelayServiceWrapperImpl mKeyEventRelayServiceWrapper;
223+
private IKeyEventRelayServiceCallback mKeyEventRelayServiceCallback = new IKeyEventRelayServiceCallback.Stub() {
227224
@Override
228-
public void onServiceConnected(ComponentName name, IBinder service) {
229-
synchronized (mKeyEventReceiverLock) {
230-
mKeyEventReceiverBinder = IKeyEventReceiver.Stub.asInterface(service);
231-
}
232-
}
225+
public boolean onKeyEvent(KeyEvent event, String sourcePackageName) {
226+
InputConnection ic = getCurrentInputConnection();
233227

234-
@Override
235-
public void onServiceDisconnected(ComponentName name) {
236-
synchronized (mKeyEventReceiverLock) {
237-
mKeyEventReceiverBinder = null;
228+
if (ic == null) {
229+
return false;
238230
}
231+
232+
return ic.sendKeyEvent(event);
239233
}
240234
};
241235

@@ -438,7 +432,7 @@ public void handleMessage(final Message msg) {
438432
latinIme.deallocateMemory();
439433
break;
440434
case MSG_SWITCH_LANGUAGE_AUTOMATICALLY:
441-
latinIme.switchLanguage((InputMethodSubtype)msg.obj);
435+
latinIme.switchLanguage((InputMethodSubtype) msg.obj);
442436
break;
443437
case MSG_UPDATE_CLIPBOARD_PINNED_CLIPS:
444438
@SuppressWarnings("unchecked")
@@ -782,14 +776,10 @@ public void onCreate() {
782776

783777
registerReceiver(mKeyMapperBroadcastReceiver, keyMapperIntentFilter);
784778

785-
try {
786-
ComponentName keyEventReceiverComponent = new ComponentName("io.github.sds100.keymapper", "io.github.sds100.keymapper.api.KeyEventReceiver");
787-
Intent keyEventReceiverServiceIntent = new Intent();
788-
keyEventReceiverServiceIntent.setComponent(keyEventReceiverComponent);
789-
bindService(keyEventReceiverServiceIntent, mKeyEventReceiverConnection, 0);
790-
} catch (SecurityException e) {
791-
Log.e(TAG, e.toString());
792-
}
779+
mKeyEventRelayServiceWrapper =
780+
new KeyEventRelayServiceWrapperImpl(getApplicationContext(), mKeyEventRelayServiceCallback);
781+
782+
mKeyEventRelayServiceWrapper.bind();
793783

794784
StatsUtils.onCreate(mSettings.getCurrent(), mRichImm);
795785
}
@@ -903,7 +893,7 @@ public void onDestroy() {
903893
unregisterReceiver(mRestartAfterDeviceUnlockReceiver);
904894
unregisterReceiver(mKeyMapperBroadcastReceiver);
905895
mStatsUtilsManager.onDestroy(this /* context */);
906-
unbindService(mKeyEventReceiverConnection);
896+
mKeyEventRelayServiceWrapper.unbind();
907897
super.onDestroy();
908898
}
909899

@@ -1483,7 +1473,7 @@ public int getCurrentRecapitalizeState() {
14831473

14841474
/**
14851475
* @param codePoints code points to get coordinates for.
1486-
* @return x,y coordinates for this keyboard, as a flattened array.
1476+
* @return x, y coordinates for this keyboard, as a flattened array.
14871477
*/
14881478
public int[] getCoordinatesForCurrentKeyboard(final int[] codePoints) {
14891479
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
@@ -1528,7 +1518,7 @@ public void onMovePointer(int steps) {
15281518
// for RTL languages we want to invert pointer movement
15291519
if (mRichImm.getCurrentSubtype().isRtlSubtype())
15301520
steps = -steps;
1531-
1521+
15321522
mInputLogic.finishInput();
15331523
if (steps < 0) {
15341524
int availableCharacters = mInputLogic.mConnection.getTextBeforeCursor(64, 0).length();
@@ -1687,6 +1677,7 @@ public void onCancelBatchInput() {
16871677
* IME for the full gesture, possibly updating the TextView to reflect the first suggestion.
16881678
* <p>
16891679
* This method must be run on the UI Thread.
1680+
*
16901681
* @param suggestedWords suggested words by the IME for the full gesture.
16911682
*/
16921683
public void onTailBatchInputResultShown(final SuggestedWords suggestedWords) {
@@ -1836,6 +1827,7 @@ void loadKeyboard() {
18361827
* After an input transaction has been executed, some state must be updated. This includes
18371828
* the shift state of the keyboard and suggestions. This method looks at the finished
18381829
* inputTransaction to find out what is necessary and updates the state accordingly.
1830+
*
18391831
* @param inputTransaction The transaction that has been executed.
18401832
*/
18411833
private void updateStateAfterInputTransaction(final InputTransaction inputTransaction) {
@@ -1923,13 +1915,9 @@ private HardwareEventDecoder getHardwareKeyEventDecoder(final int deviceId) {
19231915
@Override
19241916
public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) {
19251917

1926-
if (mKeyEventReceiverBinder != null) {
1927-
try {
1928-
if (mKeyEventReceiverBinder.onKeyEvent(keyEvent)) {
1929-
return true;
1930-
}
1931-
} catch (RemoteException e) {
1932-
1918+
if (mKeyEventRelayServiceWrapper != null) {
1919+
if (mKeyEventRelayServiceWrapper.sendKeyEvent(keyEvent, "io.github.sds100.keymapper")) {
1920+
return true;
19331921
}
19341922
}
19351923

@@ -1959,13 +1947,9 @@ public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) {
19591947

19601948
@Override
19611949
public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) {
1962-
if (mKeyEventReceiverBinder != null) {
1963-
try {
1964-
if (mKeyEventReceiverBinder.onKeyEvent(keyEvent)) {
1965-
return true;
1966-
}
1967-
} catch (RemoteException e) {
1968-
1950+
if (mKeyEventRelayServiceWrapper != null) {
1951+
if (mKeyEventRelayServiceWrapper.sendKeyEvent(keyEvent, "io.github.sds100.keymapper")) {
1952+
return true;
19691953
}
19701954
}
19711955

@@ -2024,7 +2008,7 @@ private void showSubtypeSelectorAndSettings() {
20242008
final CharSequence title = getString(R.string.english_ime_input_options);
20252009
// TODO: Should use new string "Select active input modes".
20262010
final CharSequence languageSelectionTitle = getString(R.string.language_selection_title);
2027-
final CharSequence[] items = new CharSequence[] {
2011+
final CharSequence[] items = new CharSequence[]{
20282012
languageSelectionTitle,
20292013
getString(ApplicationUtils.getActivityTitleResId(this, SettingsActivity.class))
20302014
};

0 commit comments

Comments
 (0)