add: root_automator
This commit is contained in:
1
.idea/dictionaries/Stardust.xml
generated
1
.idea/dictionaries/Stardust.xml
generated
@@ -3,6 +3,7 @@
|
||||
<words>
|
||||
<w>dismissable</w>
|
||||
<w>interruptible</w>
|
||||
<w>loopers</w>
|
||||
<w>prefill</w>
|
||||
<w>scriptable</w>
|
||||
<w>tasker</w>
|
||||
|
||||
@@ -89,7 +89,7 @@ events.onKeyDown("menu", function(event){
|
||||
### events.onTouch(listener)
|
||||
* listener \<Function\> 参数为Point的函数
|
||||
|
||||
注册一个触摸监听函数。
|
||||
注册一个触摸监听函数。相当于`on("touch", listener)`。
|
||||
|
||||
例如:
|
||||
```
|
||||
@@ -147,6 +147,28 @@ events.loop();
|
||||
```
|
||||
|
||||
|
||||
### obverseNotification()
|
||||
|
||||
|
||||
|
||||
|
||||
### toast事件
|
||||
|
||||
|
||||
|
||||
### 事件: 'notification'
|
||||
* notification \<Notification\> 通知
|
||||
|
||||
```
|
||||
events.observeNotification();
|
||||
events.on("notification", function(notification){
|
||||
log(notification.
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
# EventEmitter
|
||||
|
||||
### EventEmitter.defaultMaxListeners
|
||||
@@ -155,7 +177,7 @@ events.loop();
|
||||
|
||||
设置 EventEmitter.defaultMaxListeners 要谨慎,因为会影响所有 EventEmitter 实例,包括之前创建的。 因而,调用 emitter.setMaxListeners(n) 优先于 EventEmitter.defaultMaxListeners。
|
||||
|
||||
注意,与NodeJs不同,**这是一个硬性限制**。 EventEmitter 实例不允许添加更多的监听器,监听器超过最大数量时会抛出TooManyListenersException。
|
||||
注意,与Node.js不同,**这是一个硬性限制**。 EventEmitter 实例不允许添加更多的监听器,监听器超过最大数量时会抛出TooManyListenersException。
|
||||
```
|
||||
emitter.setMaxListeners(emitter.getMaxListeners() + 1);
|
||||
emitter.once('event', () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"global" : {
|
||||
"global": {
|
||||
"modules": [
|
||||
"app",
|
||||
"automator",
|
||||
@@ -39,7 +39,7 @@
|
||||
"openConsole",
|
||||
"clearConsole"
|
||||
],
|
||||
"dialogs":[
|
||||
"dialogs": [
|
||||
"rawInput",
|
||||
"input",
|
||||
"alert",
|
||||
@@ -142,12 +142,12 @@
|
||||
"Text"
|
||||
],
|
||||
"timers": [
|
||||
"loop",
|
||||
"setTimeout",
|
||||
"clearTimeout",
|
||||
"setInterval",
|
||||
"clearInterval",
|
||||
"setImmediate",
|
||||
"loop",
|
||||
"setTimeout",
|
||||
"clearTimeout",
|
||||
"setInterval",
|
||||
"clearInterval",
|
||||
"setImmediate",
|
||||
"clearImmediate"
|
||||
],
|
||||
"web": [
|
||||
@@ -211,7 +211,7 @@
|
||||
"error",
|
||||
"assert"
|
||||
],
|
||||
"dialogs":[
|
||||
"dialogs": [
|
||||
"select",
|
||||
"singleChoice",
|
||||
"multiChoice",
|
||||
@@ -248,6 +248,8 @@
|
||||
"onKeyUp",
|
||||
"onceKeyDown",
|
||||
"onceKeyUp",
|
||||
"onToast",
|
||||
"onNotification",
|
||||
"removeAllKeyDownListeners",
|
||||
"removeAllKeyUpListeners",
|
||||
"onTouch",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.stardust.scriptdroid.autojs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -36,6 +37,7 @@ import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
|
||||
import com.stardust.scriptdroid.ui.console.JraskaConsole;
|
||||
import com.stardust.view.accessibility.AccessibilityServiceUtils;
|
||||
import com.stardust.view.accessibility.LayoutInspector;
|
||||
import com.stardust.view.accessibility.NotificationListener;
|
||||
|
||||
|
||||
/**
|
||||
@@ -55,6 +57,7 @@ public class AutoJs {
|
||||
}
|
||||
|
||||
private final AccessibilityActionRecorder mAccessibilityActionRecorder = new AccessibilityActionRecorder();
|
||||
private final NotificationListener.Observer mNotificationObserver;
|
||||
private final LayoutInspector mLayoutInspector = new LayoutInspector();
|
||||
private final Context mContext;
|
||||
private final UiHandler mUiHandler;
|
||||
@@ -68,6 +71,7 @@ public class AutoJs {
|
||||
mContext = context;
|
||||
mUiHandler = new UiHandler(context);
|
||||
mAppUtils = new AppUtils(context);
|
||||
mNotificationObserver = new NotificationListener.Observer(context);
|
||||
mAccessibilityInfoProvider = new AccessibilityInfoProvider(context.getPackageManager());
|
||||
mScriptEngineService = buildScriptEngineService();
|
||||
addAccessibilityServiceDelegates();
|
||||
@@ -130,6 +134,7 @@ public class AutoJs {
|
||||
|
||||
private void addAccessibilityServiceDelegates() {
|
||||
AccessibilityService.addDelegate(100, mAccessibilityInfoProvider);
|
||||
AccessibilityService.addDelegate(200, mNotificationObserver);
|
||||
AccessibilityService.addDelegate(300, mAccessibilityActionRecorder);
|
||||
}
|
||||
|
||||
@@ -198,6 +203,11 @@ public class AutoJs {
|
||||
return mAccessibilityInfoProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationListener.Observer getNotificationObserver() {
|
||||
return mNotificationObserver;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ScreenCaptureRequesterImpl extends ScreenCaptureRequester.AbstractScreenCaptureRequester {
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.stardust.scriptdroid.autojs.AutoJs;
|
||||
import com.stardust.view.accessibility.NodeInfo;
|
||||
import com.stardust.scriptdroid.external.floatingwindow.menu.view.FloatingLayoutBoundsView;
|
||||
import com.stardust.scriptdroid.external.floatingwindow.menu.view.FloatingLayoutHierarchyView;
|
||||
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
|
||||
import com.stardust.theme.ThemeColorManagerCompat;
|
||||
import com.stardust.util.IntentUtil;
|
||||
import com.stardust.util.MessageEvent;
|
||||
@@ -189,7 +188,6 @@ public class HoverMenuService extends Service {
|
||||
tryInitViews();
|
||||
if (mWindowHoverMenu != null)
|
||||
mWindowHoverMenu.show();
|
||||
AccessibilityServiceTool.enableAccessibilityServiceByRootIfNeeded();
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@@ -200,6 +198,8 @@ public class HoverMenuService extends Service {
|
||||
if (eventBus.isRegistered(this))
|
||||
eventBus.unregister(this);
|
||||
setIsRunning(false);
|
||||
if (mWindowViewController == null)
|
||||
return;
|
||||
mWindowViewController.removeView(mFloatingLayoutBoundsView);
|
||||
mWindowViewController.removeView(mFloatingLayoutHierarchyView);
|
||||
}
|
||||
|
||||
@@ -85,9 +85,6 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
|
||||
ButterKnife.bind(this, mView);
|
||||
HoverMenuService.getEventBus().register(this);
|
||||
AccessibilityService.getStickOnKeyObserver().addListener(mVolumeKeyListener);
|
||||
if (Pref.isRecordVolumeControlEnable()) {
|
||||
AutoJs.getInstance().ensureAccessibilityServiceEnabled();
|
||||
}
|
||||
if (Pref.hasRecordTrigger()) {
|
||||
mKeyObserver = new KeyObserver(mContext);
|
||||
mKeyObserver.startListening();
|
||||
|
||||
@@ -14,10 +14,14 @@ public class ProgressDialog {
|
||||
private MaterialDialog mDialog;
|
||||
|
||||
public ProgressDialog(Context context) {
|
||||
this(context, R.string.text_on_progress);
|
||||
}
|
||||
|
||||
public ProgressDialog(Context context, int resId) {
|
||||
mDialog = new MaterialDialog.Builder(context)
|
||||
.progress(true, 0)
|
||||
.cancelable(false)
|
||||
.content(R.string.text_on_progress)
|
||||
.content(resId)
|
||||
.show();
|
||||
}
|
||||
|
||||
|
||||
@@ -133,7 +133,12 @@ public class SideMenuFragment extends android.support.v4.app.Fragment {
|
||||
AccessibilityServiceTool.goToAccessibilitySetting();
|
||||
return;
|
||||
}
|
||||
final ProgressDialog progress = new ProgressDialog(getContext());
|
||||
enableAccessibilityServiceByRoot();
|
||||
|
||||
}
|
||||
|
||||
private void enableAccessibilityServiceByRoot() {
|
||||
final ProgressDialog progress = new ProgressDialog(getContext(), R.string.text_enable_accessibility_service_by_root_ing);
|
||||
UnderuseExecutors.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -156,6 +161,9 @@ public class SideMenuFragment extends android.support.v4.app.Fragment {
|
||||
void setFloatingWindowEnable(CompoundButton button, boolean enable) {
|
||||
if (enable && !HoverMenuManger.isHoverMenuShowing()) {
|
||||
HoverMenuManger.showHoverMenu();
|
||||
if (Pref.enableAccessibilityServiceByRoot()) {
|
||||
enableAccessibilityServiceByRoot();
|
||||
}
|
||||
} else if (!enable && HoverMenuManger.isHoverMenuShowing()) {
|
||||
HoverMenuManger.hideHoverMenu();
|
||||
}
|
||||
|
||||
@@ -212,6 +212,7 @@
|
||||
<string name="text_enable_accessibitliy_service_by_root_failed">使用root权限开启失败</string>
|
||||
<string name="text_on_progress">处理中</string>
|
||||
<string name="text_delete_failed">删除失败</string>
|
||||
<string name="text_enable_accessibility_service_by_root_ing">正在使用root权限启用无障碍服务</string>
|
||||
|
||||
|
||||
<string-array name="record_control_keys">
|
||||
|
||||
BIN
autojs/src/main/assets/binary/root_automator
Normal file
BIN
autojs/src/main/assets/binary/root_automator
Normal file
Binary file not shown.
@@ -47,4 +47,4 @@ require("__general__")(__runtime__, this);
|
||||
|
||||
__importClass__(android.view.KeyEvent);
|
||||
__importClass__(com.stardust.autojs.runtime.api.Shell);
|
||||
__importClass__(com.stardust.autojs.runtime.api.InputEventSender);
|
||||
__importClass__(com.stardust.autojs.runtime.api.RootAutomator);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.stardust.autojs.engine.preprocess;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/8/1.
|
||||
*/
|
||||
|
||||
public class RootAutomatorEngine {
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package com.stardust.autojs.runtime;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityInfoProvider;
|
||||
import com.stardust.view.accessibility.AccessibilityService;
|
||||
import com.stardust.view.accessibility.NotificationListener;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/2.
|
||||
@@ -37,6 +39,7 @@ public abstract class AccessibilityBridge {
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public abstract AccessibilityInfoProvider getInfoProvider();
|
||||
|
||||
|
||||
@@ -44,4 +47,6 @@ public abstract class AccessibilityBridge {
|
||||
mMode = mode;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public abstract NotificationListener.Observer getNotificationObserver();
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
package com.stardust.autojs.runtime.api;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.SparseArray;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import com.stardust.autojs.runtime.AccessibilityBridge;
|
||||
import com.stardust.autojs.runtime.ScriptException;
|
||||
import com.stardust.autojs.runtime.record.inputevent.TouchObserver;
|
||||
import com.stardust.view.accessibility.AccessibilityService;
|
||||
import com.stardust.view.accessibility.NotificationListener;
|
||||
import com.stardust.view.accessibility.OnKeyListener;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/7/18.
|
||||
*/
|
||||
|
||||
public class Events extends EventEmitter implements OnKeyListener, TouchObserver.OnTouchEventListener {
|
||||
public class Events extends EventEmitter implements OnKeyListener, TouchObserver.OnTouchEventListener, NotificationListener {
|
||||
|
||||
private static final String PREFIX_KEY_DOWN = "__key_down__#";
|
||||
private static final String PREFIX_KEY_UP = "__key_up__#";
|
||||
@@ -32,6 +31,7 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver
|
||||
private boolean mListeningKey = false;
|
||||
private Loopers mLoopers;
|
||||
private Handler mHandler;
|
||||
private boolean mListeningNotification = false;
|
||||
|
||||
public Events(Context context, AccessibilityBridge accessibilityBridge, ScriptBridges bridges, Loopers loopers) {
|
||||
super(bridges);
|
||||
@@ -122,6 +122,25 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver
|
||||
}
|
||||
|
||||
|
||||
public void observeNotification() {
|
||||
if (mListeningNotification)
|
||||
return;
|
||||
mListeningNotification = true;
|
||||
mAccessibilityBridge.getNotificationObserver()
|
||||
.addListener(this);
|
||||
}
|
||||
|
||||
public Events onNotification(Object listener) {
|
||||
on("notification", listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Events onToast(Object listener) {
|
||||
on("toast", listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public void recycle() {
|
||||
if (mListeningKey) {
|
||||
AccessibilityService service = mAccessibilityBridge.getService();
|
||||
@@ -133,6 +152,9 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver
|
||||
if (mTouchObserver != null) {
|
||||
mTouchObserver.stop();
|
||||
}
|
||||
if (mListeningNotification) {
|
||||
mAccessibilityBridge.getNotificationObserver().removeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,4 +191,24 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onNotification(final AccessibilityEvent event, final String[] notification) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
emit("toast", new Object[]{notification});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotification(final AccessibilityEvent event, final Notification notification) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
emit("notification", notification);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
package com.stardust.autojs.runtime.api;
|
||||
|
||||
import com.stardust.util.ScreenMetrics;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/7/16.
|
||||
*/
|
||||
|
||||
public class InputEventSender {
|
||||
private DataOutputStream mDeviceFile;
|
||||
private ScreenMetrics mScreenMetrics;
|
||||
|
||||
|
||||
public InputEventSender(String devicePath) throws FileNotFoundException {
|
||||
mDeviceFile = new DataOutputStream(new FileOutputStream(devicePath));
|
||||
}
|
||||
|
||||
public InputEventSender(int i) throws FileNotFoundException {
|
||||
this("/dev/input/event" + i);
|
||||
}
|
||||
|
||||
public InputEventSender() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void sendEvent(int type, int code, int value) throws IOException {
|
||||
for (int i = 0; i < 16; i++)
|
||||
mDeviceFile.writeByte(0);
|
||||
mDeviceFile.writeShort(type);
|
||||
mDeviceFile.writeShort(code);
|
||||
mDeviceFile.writeInt(value);
|
||||
mDeviceFile.flush();
|
||||
}
|
||||
|
||||
|
||||
public void setInputDevice(int i) throws IOException {
|
||||
if (mDeviceFile != null) {
|
||||
mDeviceFile.close();
|
||||
}
|
||||
mDeviceFile = new DataOutputStream(new FileOutputStream("/dev/input/event" + i));
|
||||
}
|
||||
|
||||
public void Touch(int x, int y) throws IOException {
|
||||
TouchX(x);
|
||||
TouchY(y);
|
||||
}
|
||||
|
||||
public void setScreenMetrics(int width, int height) {
|
||||
if (mScreenMetrics == null) {
|
||||
mScreenMetrics = new ScreenMetrics();
|
||||
}
|
||||
mScreenMetrics.setScreenMetrics(width, height);
|
||||
}
|
||||
|
||||
public void TouchX(int x) throws IOException {
|
||||
sendEvent(3, 53, scaleX(x));
|
||||
}
|
||||
|
||||
private int scaleX(int x) {
|
||||
return mScreenMetrics.scaleX(x);
|
||||
}
|
||||
|
||||
public void TouchY(int y) throws IOException {
|
||||
sendEvent(3, 54, scaleY(y));
|
||||
}
|
||||
|
||||
private int scaleY(int y) {
|
||||
return mScreenMetrics.scaleY(y);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void close() throws IOException {
|
||||
mDeviceFile.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.stardust.autojs.runtime.api;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.stardust.pio.PFile;
|
||||
import com.stardust.pio.UncheckedIOException;
|
||||
import com.stardust.util.ScreenMetrics;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/7/16.
|
||||
*/
|
||||
|
||||
public class RootAutomator {
|
||||
|
||||
|
||||
private static final int DATA_TYPE_SLEEP = 0;
|
||||
private static final int DATA_TYPE_EVENT = 1;
|
||||
|
||||
private static final String ROOT_AUTOMATOR_EXECUTABLE_ASSET = "binary/root_automator";
|
||||
private DataOutputStream mTmpFileOutputStream;
|
||||
private File mEventTmpFile;
|
||||
private ScreenMetrics mScreenMetrics;
|
||||
private String mDevicePath;
|
||||
|
||||
public RootAutomator() {
|
||||
try {
|
||||
mEventTmpFile = File.createTempFile(String.valueOf(System.currentTimeMillis()), ".auto");
|
||||
mEventTmpFile.deleteOnExit();
|
||||
mTmpFileOutputStream = new DataOutputStream(new FileOutputStream(mEventTmpFile));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void sendEvent(int type, int code, int value) throws IOException {
|
||||
mTmpFileOutputStream.writeByte(DATA_TYPE_EVENT);
|
||||
mTmpFileOutputStream.writeShort(type);
|
||||
mTmpFileOutputStream.writeShort(code);
|
||||
mTmpFileOutputStream.writeInt(value);
|
||||
}
|
||||
|
||||
public void setInputDevice(int i) throws IOException {
|
||||
mDevicePath = "/dev/input/event" + i;
|
||||
}
|
||||
|
||||
public void touch(int x, int y) throws IOException {
|
||||
touchX(x);
|
||||
touchY(y);
|
||||
}
|
||||
|
||||
public void setScreenMetrics(int width, int height) {
|
||||
if (mScreenMetrics == null) {
|
||||
mScreenMetrics = new ScreenMetrics();
|
||||
}
|
||||
mScreenMetrics.setScreenMetrics(width, height);
|
||||
}
|
||||
|
||||
public void touchX(int x) throws IOException {
|
||||
sendEvent(3, 53, scaleX(x));
|
||||
}
|
||||
|
||||
private int scaleX(int x) {
|
||||
return mScreenMetrics.scaleX(x);
|
||||
}
|
||||
|
||||
public void touchY(int y) throws IOException {
|
||||
sendEvent(3, 54, scaleY(y));
|
||||
}
|
||||
|
||||
public void sendSync() throws IOException {
|
||||
sendEvent(0, 0, 0);
|
||||
}
|
||||
|
||||
private int scaleY(int y) {
|
||||
return mScreenMetrics.scaleY(y);
|
||||
|
||||
}
|
||||
|
||||
public void sleep(int n) throws IOException {
|
||||
mTmpFileOutputStream.writeByte(DATA_TYPE_SLEEP);
|
||||
mTmpFileOutputStream.writeInt(n);
|
||||
}
|
||||
|
||||
public AbstractShell.Result writeToDevice(Context context) {
|
||||
try {
|
||||
mTmpFileOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
String executablePath = getExecutablePath(context);
|
||||
return ProcessShell.execCommand(new String[]{
|
||||
"chmod 777 " + executablePath,
|
||||
executablePath + " " + mEventTmpFile.getAbsolutePath() + " " + mDevicePath
|
||||
}, true);
|
||||
}
|
||||
|
||||
public static String getExecutablePath(Context context) {
|
||||
File tmp = new File(context.getCacheDir(), "root_automator");
|
||||
if (!tmp.exists()) {
|
||||
PFile.copyAsset(context, ROOT_AUTOMATOR_EXECUTABLE_ASSET, tmp.getAbsolutePath());
|
||||
}
|
||||
return tmp.getAbsolutePath();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.stardust.autojs.runtime.record.inputevent;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/8/1.
|
||||
*/
|
||||
|
||||
public class InputDevice {
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.stardust.autojs.runtime.record.inputevent;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.stardust.util.ScreenMetrics.getDeviceScreenHeight;
|
||||
import static com.stardust.util.ScreenMetrics.getDeviceScreenWidth;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/8/1.
|
||||
*/
|
||||
|
||||
public class InputEventToRootAutomatorConverter extends InputEventConverter {
|
||||
|
||||
|
||||
private double mLastEventTime;
|
||||
private StringBuilder mCode = new StringBuilder();
|
||||
private int mTouchDevice = -1;
|
||||
private int mLastTouchX = -1;
|
||||
private int mLastTouchY = -1;
|
||||
|
||||
public InputEventToRootAutomatorConverter() {
|
||||
mCode.append("var ra = new RootAutomator();\n")
|
||||
.append("ra.setScreenMetrics(").append(getDeviceScreenWidth()).append(", ")
|
||||
.append(getDeviceScreenHeight()).append(");\n");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void convertEvent(@NonNull Event event) {
|
||||
if (mLastEventTime == 0) {
|
||||
mLastEventTime = event.time;
|
||||
} else if (event.time - mLastEventTime > 0.005) {
|
||||
mCode.append("ra.sleep(").append((long) (1000L * (event.time - mLastEventTime))).append(");\n");
|
||||
mLastEventTime = event.time;
|
||||
}
|
||||
int device = parseDeviceNumber(event.device);
|
||||
int type = (int) Long.parseLong(event.type, 16);
|
||||
int code = (int) Long.parseLong(event.code, 16);
|
||||
int value = (int) Long.parseLong(event.value, 16);
|
||||
if (type == 3) {
|
||||
if (code == 53) {
|
||||
onTouchX(device, value);
|
||||
return;
|
||||
}
|
||||
if (code == 54) {
|
||||
onTouchY(device, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
checkLastTouch();
|
||||
if (device != mTouchDevice) {
|
||||
return;
|
||||
}
|
||||
if (type == 0 && code == 0 && value == 0) {
|
||||
mCode.append("ra.sendSync();\n");
|
||||
return;
|
||||
}
|
||||
mCode.append("ra.sendEvent(");
|
||||
mCode.append(type).append(", ")
|
||||
.append(code).append(", ")
|
||||
.append(value).append(");\n");
|
||||
}
|
||||
|
||||
private void checkLastTouch() {
|
||||
if (mLastTouchX >= 0) {
|
||||
mCode.append("ra.touchX(").append(mLastTouchX).append(");\n");
|
||||
mLastTouchX = -1;
|
||||
}
|
||||
if (mLastTouchY >= 0) {
|
||||
mCode.append("ra.touchY(").append(mLastTouchY).append(");\n");
|
||||
mLastTouchY = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void onTouchX(int device, int value) {
|
||||
if (mTouchDevice == -1) {
|
||||
setTouchDevice(device);
|
||||
}
|
||||
mLastTouchX = value;
|
||||
}
|
||||
|
||||
private void onTouchY(int device, int value) {
|
||||
if (mTouchDevice == -1) {
|
||||
setTouchDevice(device);
|
||||
}
|
||||
if (mLastTouchX >= 0) {
|
||||
mCode.append("ra.touch(")
|
||||
.append(mLastTouchX).append(", ")
|
||||
.append(value).append(");\n");
|
||||
mLastTouchX = -1;
|
||||
} else {
|
||||
mLastTouchY = value;
|
||||
}
|
||||
}
|
||||
|
||||
private void setTouchDevice(int i) {
|
||||
mCode.append("ra.setInputDevice(").append(i).append(");\n");
|
||||
mTouchDevice = i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGetEventCommand() {
|
||||
return "getevent -t";
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return mCode.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
mCode.append("log(ra.writeToDevice(context));");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import android.content.Context;
|
||||
public class TouchRecorder extends InputEventRecorder {
|
||||
|
||||
public TouchRecorder(Context context) {
|
||||
super(context, new InputEventToSendEventJsConverter());
|
||||
super(context, new InputEventToRootAutomatorConverter());
|
||||
listen();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accessibilityEventTypes="typeAllMask"
|
||||
android:accessibilityFeedbackType="feedbackGeneric"
|
||||
android:accessibilityFeedbackType="feedbackAllMask"
|
||||
android:accessibilityFlags="flagIncludeNotImportantViews|flagReportViewIds|flagRetrieveInteractiveWindows|flagRequestEnhancedWebAccessibility|flagRequestFilterKeyEvents"
|
||||
android:canPerformGestures="true"
|
||||
android:canRequestEnhancedWebAccessibility="true"
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.stardust.view.accessibility;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import com.stardust.util.ArrayUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/8/1.
|
||||
*/
|
||||
|
||||
public interface NotificationListener {
|
||||
|
||||
void onNotification(AccessibilityEvent event, String[] notification);
|
||||
|
||||
void onNotification(AccessibilityEvent event, Notification notification);
|
||||
|
||||
class Observer implements NotificationListener, AccessibilityDelegate {
|
||||
|
||||
private static final Set<Integer> EVENT_TYPES = Collections.singleton(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
|
||||
|
||||
private static final String TAG = "NotificationObserver";
|
||||
private static final String[] EMPTY = new String[0];
|
||||
|
||||
private Context mContext;
|
||||
private CopyOnWriteArrayList<NotificationListener> mNotificationListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
public Observer(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotification(AccessibilityEvent event, String[] notification) {
|
||||
for (NotificationListener listener : mNotificationListeners) {
|
||||
try {
|
||||
listener.onNotification(event, notification);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error onNotification: " + Arrays.toString(notification) + " Listener: " + listener, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotification(AccessibilityEvent event, Notification notification) {
|
||||
for (NotificationListener listener : mNotificationListeners) {
|
||||
try {
|
||||
listener.onNotification(event, notification);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error onNotification: " + notification + " Listener: " + listener, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(NotificationListener listener) {
|
||||
mNotificationListeners.add(listener);
|
||||
}
|
||||
|
||||
public boolean removeListener(NotificationListener listener) {
|
||||
return mNotificationListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
|
||||
if (event.getParcelableData() instanceof Notification) {
|
||||
Notification notification = (Notification) event.getParcelableData();
|
||||
Log.d(TAG, "onNotification: " + notification + "; " + event);
|
||||
onNotification(event, notification);
|
||||
} else {
|
||||
List<CharSequence> list = event.getText();
|
||||
Log.d(TAG, "onNotification: " + list + "; " + event);
|
||||
if (event.getPackageName().equals(mContext.getPackageName())) {
|
||||
return false;
|
||||
}
|
||||
if (list != null) {
|
||||
onNotification(event, ArrayUtils.toStringArray(list));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Set<Integer> getEventTypes() {
|
||||
return EVENT_TYPES;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.stardust.util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/5/8.
|
||||
*/
|
||||
@@ -23,4 +25,13 @@ public class ArrayUtils {
|
||||
}
|
||||
return unbox;
|
||||
}
|
||||
|
||||
public static String[] toStringArray(List<?> list) {
|
||||
int i = 0;
|
||||
String[] str = new String[list.size()];
|
||||
for (Object o : list) {
|
||||
str[i] = o == null ? null : o.toString();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user