diff --git a/.gitignore b/.gitignore index 0e13346..1af3171 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ # Ignore Gradle build output directory build +release + +local.properties \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..acbd6f1 --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +使用参考 [minitouch](https://github.com/DeviceFarmer/minitouch) + +说明 + +c 提交指令 + +r 重置触点指令 + +d id x y pressure 按下触点 + +m id x y pressure 移动触点 + +u id 抬起触点 + +w ms 指令执行等待 + +k key d 按键按下 + +k key u 按键抬起 + +k key o 单次按键,按下抬起 + +t text 输入文本 + +示例: + +1:在10 10点击 + +d 0 10 10 1 + +u 0 + +c + +2:滑动 + +d 0 10 10 1 + +w 100 + +m 0 20 20 1 + +w 100 + +m 0 30 30 1 + +w 100 + +u 0 + +c + +3: 点亮屏幕 + +k 224 o + +c + +4:删除文本框内容 + +k 123 o + +k 67 d + +w 1000 + +k 67 u + +c + +5:输入文本 + +t 内容 + +c diff --git a/app/src/main/java/com/shxyke/MaaTouch/App.java b/app/src/main/java/com/shxyke/MaaTouch/App.java index ee5d11e..23a7fab 100644 --- a/app/src/main/java/com/shxyke/MaaTouch/App.java +++ b/app/src/main/java/com/shxyke/MaaTouch/App.java @@ -8,13 +8,14 @@ import java.io.BufferedReader; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; public class App { - private static final BufferedReader STDIN = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedReader STDIN = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)); private static final ServiceManager SERVICE_MANAGER = new ServiceManager(); private static final Controller CONTROLLER = new Controller(SERVICE_MANAGER); private static final DisplayManager DISPLAY_MANAGER = SERVICE_MANAGER.getDisplayManager(); diff --git a/app/src/main/java/com/shxyke/MaaTouch/ControlMessage.java b/app/src/main/java/com/shxyke/MaaTouch/ControlMessage.java index 5d0353c..7dab916 100644 --- a/app/src/main/java/com/shxyke/MaaTouch/ControlMessage.java +++ b/app/src/main/java/com/shxyke/MaaTouch/ControlMessage.java @@ -12,6 +12,8 @@ public final class ControlMessage { public static final int TYPE_EVENT_TOUCH_RESET = 4; public static final int TYPE_EVENT_KEY_DOWN = 5; public static final int TYPE_EVENT_KEY_UP = 6; + public static final int TYPE_EVENT_TEXT = 7; + public static final int TYPE_EVENT_KEY = 8; private int type; private long pointerId; @@ -21,11 +23,17 @@ public final class ControlMessage { private int keycode; private int repeat; private int metaState; + private String text; private ControlMessage(int type) { this.type = type; } + public static ControlMessage createTextEvent(String text) { + ControlMessage msg = new ControlMessage(TYPE_EVENT_TEXT); + msg.text = text; + return msg; + } public static ControlMessage createKeyDownEvent(int keycode, int repeat, int metaState) { ControlMessage msg = new ControlMessage(TYPE_EVENT_KEY_DOWN); msg.keycode = keycode; @@ -33,6 +41,13 @@ public static ControlMessage createKeyDownEvent(int keycode, int repeat, int met msg.metaState = metaState; return msg; } + public static ControlMessage createKeyEvent(int keycode) { + ControlMessage msg = new ControlMessage(TYPE_EVENT_KEY); + msg.keycode = keycode; + msg.repeat = 0; + msg.metaState = 0; + return msg; + } public static ControlMessage createKeyUpEvent(int keycode, int repeat, int metaState) { ControlMessage msg = new ControlMessage(TYPE_EVENT_KEY_UP); @@ -103,6 +118,9 @@ public int getMetaState() { public int getKeycode() { return keycode; } + public String getText() { + return text; + } public int getRepeat() { return repeat; diff --git a/app/src/main/java/com/shxyke/MaaTouch/ControlThread.java b/app/src/main/java/com/shxyke/MaaTouch/ControlThread.java index 6a4b582..e31d294 100644 --- a/app/src/main/java/com/shxyke/MaaTouch/ControlThread.java +++ b/app/src/main/java/com/shxyke/MaaTouch/ControlThread.java @@ -1,5 +1,6 @@ package com.shxyke.MaaTouch; +import java.util.HashMap; import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; @@ -7,10 +8,30 @@ public class ControlThread { private final LinkedBlockingQueue> queue; private final Controller controller; + private final HashMap KeyThreads; public ControlThread(LinkedBlockingQueue> queue, Controller controller) { this.queue = queue; this.controller = controller; + KeyThreads = new HashMap<>(); + } + public void KeyDown(int key) + { + if(!KeyThreads.containsKey(key)) + { + KeyThread thread = new KeyThread(this.controller,key); + thread.start(); + KeyThreads.put(key,thread); + } + } + public void KeyUp(int key) + { + if(KeyThreads.containsKey(key)) + { + KeyThread thread = KeyThreads.get(key); + thread.stopThread(); + KeyThreads.remove(key); + } } public void handleMessage(ControlMessage msg) { @@ -28,10 +49,16 @@ public void handleMessage(ControlMessage msg) { controller.injectTouchUp(msg.getPointerId()); break; case ControlMessage.TYPE_EVENT_KEY_DOWN: - controller.injectKeyDown(msg.getKeycode(), msg.getRepeat(), msg.getMetaState()); + KeyDown(msg.getKeycode()); break; case ControlMessage.TYPE_EVENT_KEY_UP: - controller.injectKeyUp(msg.getKeycode(), msg.getRepeat(), msg.getMetaState()); + KeyUp(msg.getKeycode()); + break; + case ControlMessage.TYPE_EVENT_KEY: + controller.pressReleaseKeycode(msg.getKeycode()); + break; + case ControlMessage.TYPE_EVENT_TEXT: + controller.setClipboard(msg.getText()); break; case ControlMessage.TYPE_EVENT_WAIT: try { diff --git a/app/src/main/java/com/shxyke/MaaTouch/Controller.java b/app/src/main/java/com/shxyke/MaaTouch/Controller.java index c0c009d..570749f 100644 --- a/app/src/main/java/com/shxyke/MaaTouch/Controller.java +++ b/app/src/main/java/com/shxyke/MaaTouch/Controller.java @@ -1,8 +1,10 @@ package com.shxyke.MaaTouch; +import com.shxyke.MaaTouch.wrappers.ClipboardManager; import com.shxyke.MaaTouch.wrappers.InputManager; import com.shxyke.MaaTouch.wrappers.ServiceManager; +import android.os.Build; import android.os.SystemClock; import android.view.InputDevice; import android.view.InputEvent; @@ -53,6 +55,41 @@ public void resetAll() { } } + public String getClipboardText() { + ClipboardManager clipboardManager = serviceManager.getClipboardManager(); + if (clipboardManager == null) { + return null; + } + CharSequence s = clipboardManager.getText(); + if (s == null) { + return null; + } + return s.toString(); + } + public boolean setClipboardText(String text) { + ClipboardManager clipboardManager = serviceManager.getClipboardManager(); + if (clipboardManager == null) { + return false; + } + + String currentClipboard = getClipboardText(); + if (currentClipboard != null && currentClipboard.equals(text)) { + return true; + } + return clipboardManager.setText(text); + } + public boolean pressReleaseKeycode(int keyCode) { + return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0) + && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0); + } + public boolean setClipboard(String text) { + boolean ok = setClipboardText(text); + if (ok && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + pressReleaseKeycode(KeyEvent.KEYCODE_PASTE); + } + return ok; + } + public boolean injectKeyDown(int keyCode, int repeat, int metaState) { return injectKeyEvent(MotionEvent.ACTION_DOWN, keyCode, repeat, metaState); } diff --git a/app/src/main/java/com/shxyke/MaaTouch/InputThread.java b/app/src/main/java/com/shxyke/MaaTouch/InputThread.java index 074ac23..2635a33 100644 --- a/app/src/main/java/com/shxyke/MaaTouch/InputThread.java +++ b/app/src/main/java/com/shxyke/MaaTouch/InputThread.java @@ -17,11 +17,12 @@ public class InputThread extends Thread { private static final Pattern DOWN_PATTERN = Pattern.compile("d\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)"); private static final Pattern MOVE_PATTERN = Pattern.compile("m\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)"); - private static final Pattern KEY_PATTERN = Pattern.compile("k\\s+(\\d+)\\s+([du])"); + private static final Pattern KEY_PATTERN = Pattern.compile("k\\s+(\\d+)\\s+([duo])"); private static final Pattern WAIT_PATTERN = Pattern.compile("w\\s+(\\d+)"); private static final Pattern UP_PATTERN = Pattern.compile("u\\s+(\\d+)"); private static final Pattern COMMIT_PATTERN = Pattern.compile("c"); private static final Pattern RESET_PATTERN = Pattern.compile("r"); + private static final Pattern TEXT_PATTERN = Pattern.compile("t\\s+(.+)"); private Queue subqueue = new LinkedList<>(); @@ -38,6 +39,9 @@ private void parseKey(String s) { while (!subqueue.offer(ControlMessage.createKeyUpEvent(keycode, 0, 0))); } else if (m.group(2).equals("d")) { while (!subqueue.offer(ControlMessage.createKeyDownEvent(keycode, 0, 0))); + }else + { + while (!subqueue.offer(ControlMessage.createKeyEvent(keycode))); } } } @@ -104,6 +108,13 @@ private void parseTouchReset(String s) { commitSubqueue(); } } + private void parseText(String s) { + Matcher m = TEXT_PATTERN.matcher(s); + if (m.find()) { + String text = m.group(1); + while (!subqueue.offer(ControlMessage.createTextEvent(text))); + } + } private void parseInput(String s) { switch (s.charAt(0)) { @@ -128,6 +139,9 @@ private void parseInput(String s) { case 'k': parseKey(s); break; + case 't': + parseText(s); + break; default: break; } diff --git a/app/src/main/java/com/shxyke/MaaTouch/KeyThread.java b/app/src/main/java/com/shxyke/MaaTouch/KeyThread.java new file mode 100644 index 0000000..27270a5 --- /dev/null +++ b/app/src/main/java/com/shxyke/MaaTouch/KeyThread.java @@ -0,0 +1,27 @@ +package com.shxyke.MaaTouch; + +public class KeyThread extends Thread { + private boolean isRunning = true; + private final Controller controller; + private int keyCode; + private int repeat = 0; + public KeyThread(Controller controller,int keyCode) { + this.controller = controller; + this.keyCode = keyCode; + } + @Override + public void run() { + do { + controller.injectKeyDown(keyCode,repeat++,0); + try { + Thread.sleep(1000 / 20); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }while(this.isRunning); + controller.injectKeyUp(keyCode,0,0); + } + public void stopThread() { + this.isRunning = false; + } +} diff --git a/app/src/main/java/com/shxyke/MaaTouch/Ln.java b/app/src/main/java/com/shxyke/MaaTouch/Ln.java index 25038c3..373b90b 100644 --- a/app/src/main/java/com/shxyke/MaaTouch/Ln.java +++ b/app/src/main/java/com/shxyke/MaaTouch/Ln.java @@ -2,6 +2,9 @@ import android.util.Log; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + /** * Log both to Android logger (so that logs are visible in "adb logcat") and standard output/error (so that they are visible in the terminal * directly). @@ -53,7 +56,14 @@ public static void d(String message) { public static void i(String message) { if (isEnabled(Level.INFO)) { Log.i(TAG, message); - System.out.println(PREFIX + "INFO: " + message); + String info = "INFO: "; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss "); + String formattedDateTime = now.format(formatter); + info = formattedDateTime + info; + } + System.out.println(PREFIX + info + message); } } diff --git a/app/src/main/java/com/shxyke/MaaTouch/wrappers/ClipboardManager.java b/app/src/main/java/com/shxyke/MaaTouch/wrappers/ClipboardManager.java new file mode 100644 index 0000000..d01eef7 --- /dev/null +++ b/app/src/main/java/com/shxyke/MaaTouch/wrappers/ClipboardManager.java @@ -0,0 +1,103 @@ +package com.shxyke.MaaTouch.wrappers; + +import android.content.ClipData; +import android.os.Build; +import android.os.IInterface; + +import com.shxyke.MaaTouch.Ln; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class ClipboardManager { + private final IInterface manager; + private Method getPrimaryClipMethod; + private Method setPrimaryClipMethod; + private boolean alternativeGetMethod; + private boolean alternativeSetMethod; + + public ClipboardManager(IInterface manager) { + this.manager = manager; + } + + private Method getGetPrimaryClipMethod() throws NoSuchMethodException { + if (getPrimaryClipMethod == null) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class); + } else { + try { + getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class); + } catch (NoSuchMethodException e) { + getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class); + alternativeGetMethod = true; + } + } + } + return getPrimaryClipMethod; + } + + private Method getSetPrimaryClipMethod() throws NoSuchMethodException { + if (setPrimaryClipMethod == null) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class); + } else { + try { + setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, int.class); + } catch (NoSuchMethodException e) { + setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, String.class, int.class); + alternativeSetMethod = true; + } + } + } + return setPrimaryClipMethod; + } + + private static ClipData getPrimaryClip(Method method, boolean alternativeMethod, IInterface manager) + throws InvocationTargetException, IllegalAccessException { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME); + } + if (alternativeMethod) { + return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME, null, ServiceManager.USER_ID); + } + return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID); + } + + private static void setPrimaryClip(Method method, boolean alternativeMethod, IInterface manager, ClipData clipData) + throws InvocationTargetException, IllegalAccessException { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME); + } else if (alternativeMethod) { + method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME, null, ServiceManager.USER_ID); + } else { + method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID); + } + } + + public CharSequence getText() { + try { + Method method = getGetPrimaryClipMethod(); + ClipData clipData = getPrimaryClip(method, alternativeGetMethod, manager); + if (clipData == null || clipData.getItemCount() == 0) { + return null; + } + return clipData.getItemAt(0).getText(); + } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { + Ln.e("Could not invoke method", e); + return null; + } + } + + public boolean setText(CharSequence text) { + try { + Method method = getSetPrimaryClipMethod(); + ClipData clipData = ClipData.newPlainText(null, text); + setPrimaryClip(method, alternativeSetMethod, manager, clipData); + return true; + } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { + Ln.e("Could not invoke method", e); + return false; + } + } + +} diff --git a/app/src/main/java/com/shxyke/MaaTouch/wrappers/ServiceManager.java b/app/src/main/java/com/shxyke/MaaTouch/wrappers/ServiceManager.java index 2281542..30c7dd1 100644 --- a/app/src/main/java/com/shxyke/MaaTouch/wrappers/ServiceManager.java +++ b/app/src/main/java/com/shxyke/MaaTouch/wrappers/ServiceManager.java @@ -18,6 +18,7 @@ public final class ServiceManager { private DisplayManager displayManager; private InputManager inputManager; + private ClipboardManager clipboardManager; public ServiceManager() { try { @@ -62,4 +63,14 @@ public InputManager getInputManager() { } return inputManager; } + public ClipboardManager getClipboardManager() { + if (clipboardManager == null) { + IInterface clipboard = getService("clipboard", "android.content.IClipboard"); + if (clipboard == null) { + return null; + } + clipboardManager = new ClipboardManager(clipboard); + } + return clipboardManager; + } }