diff --git a/server/src/main/java/com/genymobile/scrcpy/Controller.java b/server/src/main/java/com/genymobile/scrcpy/Controller.java index 4442188ccd..8ea42c0794 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/Controller.java @@ -1,10 +1,7 @@ package com.genymobile.scrcpy; -import com.genymobile.scrcpy.wrappers.InputManager; - import android.os.SystemClock; import android.view.InputDevice; -import android.view.InputEvent; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; @@ -50,7 +47,7 @@ private void initPointers() { public void control() throws IOException { // on start, power on the device if (!device.isScreenOn()) { - injectKeycode(KeyEvent.KEYCODE_POWER); + device.injectKeycode(KeyEvent.KEYCODE_POWER); // dirty hack // After POWER is injected, the device is powered on asynchronously. @@ -133,7 +130,7 @@ private void handleEvent() throws IOException { } private boolean injectKeycode(int action, int keycode, int metaState) { - return injectKeyEvent(action, keycode, 0, metaState); + return device.injectKeyEvent(action, keycode, 0, metaState); } private boolean injectChar(char c) { @@ -144,7 +141,7 @@ private boolean injectChar(char c) { return false; } for (KeyEvent event : events) { - if (!injectEvent(event)) { + if (!device.injectEvent(event)) { return false; } } @@ -152,6 +149,10 @@ private boolean injectChar(char c) { } private int injectText(String text) { + if (device.injectTextPaste(text)) { + // The best method (fastest and UTF-8) worked! + return text.length(); + } int successCount = 0; for (char c : text.toCharArray()) { if (!injectChar(c)) { @@ -200,7 +201,7 @@ private boolean injectTouch(int action, long pointerId, Position position, float MotionEvent event = MotionEvent .obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEVICE_ID_VIRTUAL, 0, InputDevice.SOURCE_TOUCHSCREEN, 0); - return injectEvent(event); + return device.injectEvent(event); } private boolean injectScroll(Position position, int hScroll, int vScroll) { @@ -223,26 +224,11 @@ private boolean injectScroll(Position position, int hScroll, int vScroll) { MotionEvent event = MotionEvent .obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, 0, 1f, 1f, DEVICE_ID_VIRTUAL, 0, InputDevice.SOURCE_TOUCHSCREEN, 0); - return injectEvent(event); - } - - private boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState) { - long now = SystemClock.uptimeMillis(); - KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, - InputDevice.SOURCE_KEYBOARD); - return injectEvent(event); - } - - private boolean injectKeycode(int keyCode) { - return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0); - } - - private boolean injectEvent(InputEvent event) { - return device.injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + return device.injectEvent(event); } private boolean pressBackOrTurnScreenOn() { int keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER; - return injectKeycode(keycode); + return device.injectKeycode(keycode); } } diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index ec28bd7cb4..a5f7bfe7b3 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -10,8 +10,12 @@ import android.graphics.Rect; import android.os.Build; import android.os.IBinder; +import android.os.SystemClock; import android.view.IRotationWatcher; +import android.view.InputDevice; import android.view.InputEvent; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; import java.util.concurrent.atomic.AtomicBoolean; @@ -147,7 +151,7 @@ public boolean supportsInputEvents() { return supportsInputEvents; } - public boolean injectInputEvent(InputEvent inputEvent, int mode) { + public boolean injectEvent(InputEvent inputEvent, int mode) { if (!supportsInputEvents()) { throw new AssertionError("Could not inject input event if !supportsInputEvents()"); } @@ -159,6 +163,50 @@ public boolean injectInputEvent(InputEvent inputEvent, int mode) { return serviceManager.getInputManager().injectInputEvent(inputEvent, mode); } + public boolean injectEvent(InputEvent event) { + return injectEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + + public boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState, int mode) { + long now = SystemClock.uptimeMillis(); + KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, + InputDevice.SOURCE_KEYBOARD); + return injectEvent(event, mode); + } + + public boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState) { + return injectKeyEvent(action, keyCode, repeat, metaState, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + + public boolean injectKeycode(int keyCode, int mode) { + return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0, mode); + } + + public boolean injectKeycode(int keyCode) { + return injectKeycode(keyCode, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + + public boolean injectTextPaste(String text) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + return false; + } + + // On Android >= 7, we can inject UTF-8 text as follow: + // - set the clipboard + // - inject the PASTE key event + // - restore the clipboard + + String clipboardBackup = getClipboardText(); + isSettingClipboard.set(true); + + rawSetClipboardText(text); + boolean ok = injectKeycode(KeyEvent.KEYCODE_PASTE, InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT); + rawSetClipboardText(clipboardBackup); + + isSettingClipboard.set(false); + return ok; + } + public boolean isScreenOn() { return serviceManager.getPowerManager().isScreenOn(); } @@ -187,9 +235,13 @@ public String getClipboardText() { return s.toString(); } + private boolean rawSetClipboardText(String text) { + return serviceManager.getClipboardManager().setText(text); + } + public boolean setClipboardText(String text) { isSettingClipboard.set(true); - boolean ok = serviceManager.getClipboardManager().setText(text); + boolean ok = rawSetClipboardText(text); isSettingClipboard.set(false); return ok; }