diff --git a/README.md b/README.md index a17b852..2f6cdc9 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Clipboard, Thanks to @fplust - https://github.com/appium/io.appium.settings/blob/5d3bc99ff35f3b816b4342395aba1bdea82ad48f/app/src/main/java/io/appium/settings/receivers/ClipboardReceiver.java # The buildin input method -**Fast input method** +**com.github.uiautomator/.AdbKeyboard** Encode the text into UTF-8 and then Base64 @@ -70,33 +70,59 @@ For example: "Hello 你好" -> (UTF-8 && Base64) = SGVsbG8g5L2g5aW9 -Send to FastInputIME with broadcast +Send to AdbKeyboard with broadcast +> Broadcast completed: result=-1 means success ```bash +# show ime list +$ adb shell ime list -s -a +com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME +com.google.android.tts/com.google.android.apps.speech.tts.googletts.settings.asr.voiceime.VoiceInputMethodService +com.github.uiautomator/.AdbKeyboard + +# enable and set ime +$ adb shell ime enable com.github.uiautomator/.AdbKeyboard +$ adb shell settings put secure default_input_method com.github.uiautomator/.AdbKeyboard + # Append text to input field -$ adb shell am broadcast -a ADB_INPUT_TEXT --es text SGVsbG8g5L2g5aW9 +$ adb shell am broadcast -a ADB_KEYBOARD_INPUT_TEXT --es text SGVsbG8g5L2g5aW9 +Broadcasting: Intent { act=ADB_KEYBOARD_INPUT_TEXT flg=0x400000 (has extras) } +Broadcast completed: result=-1 # Clear text -$ adb shell am broadcast -a ADB_CLEAR_TEXT +$ adb shell am broadcast -a ADB_KEYBOARD_CLEAR_TEXT +Broadcasting: Intent { act=ADB_KEYBOARD_CLEAR_TEXT flg=0x400000 } +Broadcast completed: result=-1 # Clear text before append text -$ adb shell am broadcast -a ADB_SET_TEXT --es text SGVsbG8g5L2g5aW9 +$ adb shell am broadcast -a ADB_KEYBOARD_SET_TEXT --es text SGVsbG8g5L2g5aW9 +Broadcasting: Intent { act=ADB_KEYBOARD_SET_TEXT flg=0x400000 (has extras) } +Broadcast completed: result=-1 -# Send keycode, eg: ENTER -$ adb shell am broadcast -a ADB_INPUT_KEYCODE --ei code 66 +# Send Keycode or Editor code according to the InputEditor requires +$ adb shell am broadcast -a ADB_KEYBOARD_SMART_ENTER +Broadcasting: Intent { act=ADB_KEYBOARD_SMART_ENTER flg=0x400000 } +Broadcast completed: result=-1 -# Send Editor code -$ adb shell am broadcast -a ADB_EDITOR_CODE --ei code 2 # IME_ACTION_GO +# Send keycode, eg: ENTER +$ adb shell am broadcast -a ADB_KEYBOARD_INPUT_KEYCODE --ei code 66 +# Send Editor code, eg: 2 +$ adb shell am broadcast -a ADB_KEYBOARD_EDITOR_CODE --ei code 2 # IME_ACTION_GO # Get clipboard (without data) -$ adb shell am broadcast -a ADB_GET_CLIPBOARD +$ adb shell am broadcast -a ADB_KEYBOARD_GET_CLIPBOARD Broadcasting: Intent { act=ADB_GET_CLIPBOARD flg=0x400000 } Broadcast completed: result=0 # Get clipboard (with data, base64 encoded) -$ adb shell am broadcast -a ADB_GET_CLIPBOARD +$ adb shell am broadcast -a ADB_GET_KEYBOARD_CLIPBOARD Broadcasting: Intent { act=ADB_GET_CLIPBOARD flg=0x400000 } Broadcast completed: result=-1, data="5LqG6Kej5Lyg57uf5paH5YyW" + +# show keyboard +$ adb shell am broadcast -a ADB_KEYBOARD_HIDE +# hide keyboard +$ adb shell am broadcast -a ADB_KEYBOARD_SHOW ``` - [Editor Code](https://developer.android.com/reference/android/view/inputmethod/EditorInfo) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dfdb1e7..3ff2fb9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,9 +91,9 @@ diff --git a/app/src/main/java/com/github/uiautomator/FastInputIME.java b/app/src/main/java/com/github/uiautomator/AdbKeyboard.java similarity index 55% rename from app/src/main/java/com/github/uiautomator/FastInputIME.java rename to app/src/main/java/com/github/uiautomator/AdbKeyboard.java index 5ae0f64..26c1bfd 100644 --- a/app/src/main/java/com/github/uiautomator/FastInputIME.java +++ b/app/src/main/java/com/github/uiautomator/AdbKeyboard.java @@ -2,18 +2,14 @@ import android.app.Activity; import android.content.BroadcastReceiver; +import android.content.ClipData; +import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.ClipData; -import android.content.ClipboardManager; import android.inputmethodservice.InputMethodService; import android.inputmethodservice.Keyboard; import android.inputmethodservice.KeyboardView; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; import android.util.Base64; import android.util.Log; import android.view.KeyEvent; @@ -23,54 +19,38 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; -import java.io.Writer; -import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; import java.util.Random; -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; - -public class FastInputIME extends InputMethodService { - private static final String TAG = "FastInputIME"; +public class AdbKeyboard extends InputMethodService { + private static final String TAG = "AdbKeyboard"; private BroadcastReceiver mReceiver = null; - protected OkHttpClient httpClient = new OkHttpClient(); - protected static final int INPUT_EDIT = 1; - Socket socketClient; - - private Handler handler = new Handler(new Handler.Callback() { - @Override - public boolean handleMessage(Message msg) { - switch (msg.what) { - case INPUT_EDIT: - String text = msg.getData().getString("text"); - setText(text); - break; - default: - break; - } - return true; - } - }); + private int imeAction; + private Keyboard keyboard; + private KeyboardView inputView; + + public enum KeyboardAction { + ADB_KEYBOARD_SMART_ENTER, + ADB_KEYBOARD_INPUT_TEXT, + ADB_KEYBOARD_CLEAR_TEXT, + ADB_KEYBOARD_SET_TEXT, + ADB_KEYBOARD_INPUT_KEYCODE, + ADB_KEYBOARD_EDITOR_CODE, + ADB_KEYBOARD_GET_CLIPBOARD, + ADB_KEYBOARD_HIDE, + ADB_KEYBOARD_SHOW; + } @Override public View onCreateInputView() { - KeyboardView keyboardView = (KeyboardView) getLayoutInflater().inflate(R.layout.keyboard, null); + inputView = (KeyboardView) getLayoutInflater().inflate(R.layout.input, null); + keyboard = new Keyboard(this, R.xml.keyboard); + inputView.setKeyboard(keyboard); + inputView.setOnKeyboardActionListener(new MyKeyboardActionListener()); - Keyboard keyboard = new Keyboard(this, R.xml.keyboard); - keyboardView.setKeyboard(keyboard); - keyboardView.setOnKeyboardActionListener(new MyKeyboardActionListener()); - - return keyboardView; + return inputView; } @Override @@ -80,15 +60,9 @@ public void onCreate() { if (mReceiver == null) { IntentFilter filter = new IntentFilter(); - filter.addAction("ADB_INPUT_TEXT"); - filter.addAction("ADB_INPUT_KEYCODE"); - filter.addAction("ADB_CLEAR_TEXT"); - filter.addAction("ADB_SET_TEXT"); // Equals to: Clear then Input - filter.addAction("ADB_EDITOR_CODE"); - filter.addAction("ADB_GET_CLIPBOARD"); - // TODO: filter.addAction("ADB_INPUT_CHARS"); - - // NONEED: filter.addAction(USB_STATE_CHANGE); + for (KeyboardAction ka: KeyboardAction.values()) { + filter.addAction(ka.toString()); + } mReceiver = new InputMessageReceiver(); registerReceiver(mReceiver, filter); } @@ -131,28 +105,38 @@ private String getClipboardText(Context context) { @Override public void onReceive(Context context, Intent intent) { + try { + String resultData = this.handleAction(context, intent); + setResultCode(Activity.RESULT_OK); + setResultData(resultData); + } catch (Exception ex) { + setResult(Activity.RESULT_CANCELED, "error:" + ex.getMessage(), null); + } + } + + public String handleAction(Context context, Intent intent) throws Exception { String action = intent.getAction(); String msgText; int code; InputConnection ic = getCurrentInputConnection(); if (ic == null) { - return; + return action; } - Log.i(TAG, action); - switch (action) { - case "ADB_INPUT_TEXT": + KeyboardAction keyAction = KeyboardAction.valueOf(action); + Log.i(TAG, "KeyboardAction received:" + keyAction); + switch (keyAction) { + case ADB_KEYBOARD_INPUT_TEXT: /* test method * TEXT=$(echo -n "Hello World" | base64) * adb shell am broadcast -a ADB_INPUT_TEXT --es text ${TEXT:-"SGVsbG8gd29ybGQ="} */ msgText = intent.getStringExtra("text"); if (msgText == null) { - return; + return action; } - Log.i(TAG, "input text(base64): " + msgText); inputTextBase64(msgText); break; - case "ADB_INPUT_KEYCODE": + case ADB_KEYBOARD_INPUT_KEYCODE: /* test method * Enter code 66 * adb shell am broadcast -a ADB_INPUT_KEYCODE --ei code 66 @@ -162,67 +146,90 @@ public void onReceive(Context context, Intent intent) { ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, code)); } break; - case "ADB_CLEAR_TEXT": - Log.i(TAG, "receive ADB_CLEAR_TEXT"); + case ADB_KEYBOARD_CLEAR_TEXT: clearText(); break; - case "ADB_SET_TEXT": - Log.i(TAG, "receive ADB_SET_TEXT"); + case ADB_KEYBOARD_SET_TEXT: msgText = intent.getStringExtra("text"); if (msgText == null) { msgText = ""; } - Log.i(TAG, "input text(base64): " + msgText); ic.beginBatchEdit(); clearText(); inputTextBase64(msgText); ic.endBatchEdit(); break; - case "ADB_EDITOR_CODE": + case ADB_KEYBOARD_EDITOR_CODE: code = intent.getIntExtra("code", -1); if (code != -1) { ic.performEditorAction(code); } break; - case "ADB_GET_CLIPBOARD": + case ADB_KEYBOARD_GET_CLIPBOARD: Log.i(TAG, "Getting current clipboard content"); final String clipboardContent = getClipboardText(context); if (clipboardContent == null) { - setResultCode(Activity.RESULT_CANCELED); - setResultData(""); + throw new Exception("clipboard empty"); } else { - try { - // TODO: Use StandardCharsets.UTF_8 after the minimum supported API version - // TODO: is bumped above 18 - //noinspection CharsetObjectCanBeUsed - String clipboardContentBase64 = Base64.encodeToString( - clipboardContent.getBytes("UTF-8"), Base64.NO_WRAP); - setResultCode(Activity.RESULT_OK); - setResultData(clipboardContentBase64); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - setResultCode(Activity.RESULT_CANCELED); - setResultData(""); - } + return Base64.encodeToString( + clipboardContent.getBytes(StandardCharsets.UTF_8), Base64.NO_WRAP); } + case ADB_KEYBOARD_HIDE: + // https://stackoverflow.com/questions/1109022/how-to-close-hide-the-android-soft-keyboard-programmatically#:~:text=You%20can%20force%20Android%20to,window%20containing%20your%20focused%20view.&text=This%20will%20force%20the%20keyboard,want%20to%20pass%20in%20InputMethodManager. + hideInputMethod(context); + break; + case ADB_KEYBOARD_SHOW: + showInputMethod(context); break; + case ADB_KEYBOARD_SMART_ENTER: + switch(imeAction) { + case EditorInfo.IME_ACTION_DONE: + case EditorInfo.IME_ACTION_GO: + case EditorInfo.IME_ACTION_NEXT: + case EditorInfo.IME_ACTION_SEARCH: + case EditorInfo.IME_ACTION_SEND: + case EditorInfo.IME_ACTION_NONE: + ic.performEditorAction(imeAction); + return convertActionToString(imeAction); + default: + ic.performEditorAction(KeyEvent.KEYCODE_ENTER); + return "Enter"; + } + } + return null; + } + } + + @Override + public void onStartInputView(EditorInfo attribute, boolean restarting) { + super.onStartInput(attribute, restarting); + this.imeAction = attribute.imeOptions & EditorInfo.IME_MASK_ACTION; + String actionName = convertActionToString(imeAction); + Log.i(TAG, "imeAction: " + imeAction + " " + actionName); + setEnterKeyLabel(actionName); + inputView.invalidateAllKeys(); + } + + private void setEnterKeyLabel(String label) { + List keys = keyboard.getKeys(); + for(Keyboard.Key key: keys) { + if(key.codes[0] == -8) { + key.label = label; } } } // Refs: https://www.jianshu.com/p/892168a57fe3 private class MyKeyboardActionListener implements KeyboardView.OnKeyboardActionListener { - @Override - public void onPress(int i) { - } + public void onPress(int i) {} @Override - public void onRelease(int i) { - } + public void onRelease(int i) {} @Override public void onKey(int primaryCode, int[] keyCodes) { + InputConnection ic = getCurrentInputConnection(); if (primaryCode == Keyboard.KEYCODE_CANCEL) { Log.d(TAG, "Keyboard CANCEL not implemented"); } else if (primaryCode == -10) { @@ -231,44 +238,35 @@ public void onKey(int primaryCode, int[] keyCodes) { changeInputMethod(); // switchToLastInputMethod(); } else if (primaryCode == -7) { - InputConnection ic = getCurrentInputConnection(); - ic.commitText(randomString(1), 0); + ic.commitText(randomString(1), 1); + } else if (primaryCode == -8) { + ic.performEditorAction(imeAction); } else { Log.w(TAG, "Unknown primaryCode " + primaryCode); } } @Override - - public void onText(CharSequence charSequence) { - } + public void onText(CharSequence charSequence) {} @Override - public void swipeLeft() { - } + public void swipeLeft() {} @Override - public void swipeRight() { - } + public void swipeRight() {} @Override - public void swipeDown() { - } + public void swipeDown() {} @Override - public void swipeUp() { - } + public void swipeUp() {} } private void inputTextBase64(String base64text) { byte[] data = Base64.decode(base64text, Base64.DEFAULT); - try { - String text = new String(data, "UTF-8"); - InputConnection ic = getCurrentInputConnection(); - ic.commitText(text, 1); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } + String text = new String(data, StandardCharsets.UTF_8); + InputConnection ic = getCurrentInputConnection(); + ic.commitText(text, 1); } private void clearText() { @@ -295,40 +293,48 @@ private String getText() { return text; } - private void setText(String text) { - InputConnection ic = getCurrentInputConnection(); - if (ic == null) { - return; - } - ic.beginBatchEdit(); - clearText(); - ic.commitText(text, 1); - ic.endBatchEdit(); - } - private void changeInputMethod() { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.showInputMethodPicker(); } - private void switchToLastInputMethod() { - final IBinder token = getWindow().getWindow().getAttributes().token; - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.switchToLastInputMethod(token); - } - - private void makeToast(String msg) { -// Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); - } - public String randomString(int length) { String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; Random random = new Random(); - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); for (int i = 0; i < length; i++) { int num = random.nextInt(62); buf.append(str.charAt(num)); } return buf.toString(); } + + private void showInputMethod(Context context) { + InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE); + imm.showSoftInputFromInputMethod(Objects.requireNonNull(getWindow().getWindow()).getAttributes().token, 0); + } + + private void hideInputMethod(Context context) { + InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromInputMethod(Objects.requireNonNull(getWindow().getWindow()).getAttributes().token, 0); + } + + public static String convertActionToString(int action) { + switch (action) { + case EditorInfo.IME_ACTION_NONE: + return "None"; + case EditorInfo.IME_ACTION_GO: + return "Go"; + case EditorInfo.IME_ACTION_SEARCH: + return "Search"; + case EditorInfo.IME_ACTION_SEND: + return "Send"; + case EditorInfo.IME_ACTION_NEXT: + return "Next"; + case EditorInfo.IME_ACTION_DONE: + return "Done"; + default: + return "Enter"; + } + } } diff --git a/app/src/main/res/layout/keyboard.xml b/app/src/main/res/layout/input.xml similarity index 100% rename from app/src/main/res/layout/keyboard.xml rename to app/src/main/res/layout/input.xml diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f8224d5..48a47ed 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,7 +31,7 @@ IP :agent is die :agent is live - FastInputIME + AdbKeyboard 停止ATXAGENT 启动ATXAGENT 停止UIAutomator diff --git a/app/src/main/res/xml/keyboard.xml b/app/src/main/res/xml/keyboard.xml index 7a88266..667ec69 100644 --- a/app/src/main/res/xml/keyboard.xml +++ b/app/src/main/res/xml/keyboard.xml @@ -9,17 +9,22 @@ +