add events module, including EventEmiiter, KeyEvent, TouchEvent

This commit is contained in:
hyb1996
2017-07-20 15:39:17 +08:00
parent 1d23640362
commit 52cc5214f1
47 changed files with 755 additions and 82 deletions

View File

@@ -1,7 +1,8 @@
### sleep(n)
### sleep(\[n\])
* n \<Number\> 毫秒数
暂停运行n**毫秒**的时间。1秒等于1000毫秒。
如果不加参数n,则会一直暂停,通常用于有事件监听的脚本中使用。
### launchPackage(packageName)
* packageName \<String\> 应用包名。参见《Shell命令:应用包名》章节。

View File

@@ -0,0 +1,20 @@
# events
events也是一个\[EventEmitter\],这里只介绍其自身的函数。
### events.emitter()
返回一个新的EventEmitter。
### events.observeKey()
启用按键监听,例如音量键、Home键。
在安卓4.3以上才能使用。
### events.onKeyDown(keyName, listener)
* keyName \<String\> 要监听的按键名称
* listener \<Function\> 参数为一个\[KeyEvent\]
注册一个按键监听函数,。按键名称包括

View File

@@ -62,7 +62,7 @@ function isImage(item){
function notLiked(likeIcon){
var x = likeIcon.bounds().centerX();
var y = likeIcon.bounds().centerY();
return images.detectsColor(capture, x, y, 0x767886);
return images.detectsColor(capture, 0x767886, x, y);
}
toast("请打开QQ空间并慢慢下滑");

View File

@@ -5,6 +5,7 @@ import android.app.Application;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.multidex.MultiDexApplication;
import android.view.KeyEvent;
import com.squareup.leakcanary.LeakCanary;
import com.stardust.app.SimpleActivityLifecycleCallbacks;
@@ -19,6 +20,8 @@ import com.stardust.theme.ThemeColor;
import com.stardust.theme.ThemeColorManager;
import com.stardust.util.ScreenMetrics;
import com.stardust.util.UiHandler;
import com.stardust.view.accessibility.AccessibilityService;
import com.stardust.view.accessibility.OnKeyListener;
import java.lang.ref.WeakReference;
@@ -71,7 +74,7 @@ public class App extends MultiDexApplication {
}
private void initVolumeChangeObserver() {
registerReceiver(mVolumeChangeObserver, new IntentFilter(VolumeChangeObserver.ACTION_VOLUME_CHANGE));
//registerReceiver(mVolumeChangeObserver, new IntentFilter(VolumeChangeObserver.ACTION_VOLUME_CHANGE));
mVolumeChangeObserver.addOnVolumeChangeListener(new VolumeChangeObserver.OnVolumeChangeListener() {
@Override
public void onVolumeChange() {
@@ -80,6 +83,14 @@ public class App extends MultiDexApplication {
}
}
});
AccessibilityService.getStickOnKeyObserver().addListener(new OnKeyListener() {
@Override
public void onKeyEvent(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && Pref.isRunningVolumeControlEnabled()) {
AutoJs.getInstance().getScriptEngineService().stopAllAndToast();
}
}
});
}

View File

@@ -32,12 +32,12 @@ import com.stardust.util.ScreenMetrics;
import com.stardust.util.Supplier;
import com.stardust.util.UiHandler;
import com.stardust.view.accessibility.AccessibilityInfoProvider;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.LayoutInspector;
import com.stardust.scriptdroid.external.floatingwindow.menu.record.accessibility.AccessibilityActionRecorder;
import com.stardust.autojs.runtime.record.accessibility.AccessibilityActionRecorder;
import com.stardust.view.accessibility.AccessibilityService;
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;
/**
@@ -177,7 +177,7 @@ public class AutoJs implements AccessibilityBridge {
@Nullable
@Override
public android.accessibilityservice.AccessibilityService getService() {
public AccessibilityService getService() {
return AccessibilityService.getInstance();
}

View File

@@ -17,7 +17,7 @@ import com.stardust.hover.SimpleHoverMenuTransitionListener;
import com.stardust.hover.WindowHoverMenu;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.autojs.AutoJs;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo;
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;

View File

@@ -12,12 +12,12 @@ import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.autojs.AutoJs;
import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.LayoutInspector;
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
import com.stardust.scriptdroid.ui.main.MainActivity_;
import com.stardust.util.ClipboardUtil;
import com.stardust.util.MessageEvent;
import com.stardust.view.accessibility.AccessibilityService;
import com.stardust.view.accessibility.LayoutInspector;
import org.greenrobot.eventbus.Subscribe;

View File

@@ -10,16 +10,16 @@ import android.widget.TextView;
import android.widget.Toast;
import com.stardust.app.VolumeChangeObserver;
import com.stardust.autojs.runtime.record.Recorder;
import com.stardust.autojs.runtime.record.accessibility.AccessibilityActionRecorder;
import com.stardust.autojs.runtime.record.inputevent.KeyObserver;
import com.stardust.autojs.runtime.record.inputevent.TouchRecorder;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.accessibility.AccessibilityEventHelper;
import com.stardust.scriptdroid.autojs.AutoJs;
import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService;
import com.stardust.scriptdroid.external.floatingwindow.menu.record.Recorder;
import com.stardust.scriptdroid.external.floatingwindow.menu.record.accessibility.AccessibilityActionRecorder;
import com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent.KeyObserver;
import com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent.TouchRecorder;
import com.stardust.scriptdroid.ui.main.MainActivity;
import com.stardust.util.MessageEvent;

View File

@@ -13,7 +13,7 @@ import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo;
import com.stardust.view.accessibility.NodeInfo;
import com.stardust.util.ViewUtil;
import java.util.ArrayList;

View File

@@ -14,7 +14,7 @@ import android.widget.ListView;
import android.widget.TextView;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo;
import com.stardust.view.accessibility.NodeInfo;
import com.stardust.util.ViewUtil;
import com.stardust.widget.LevelBeamView;

View File

@@ -12,7 +12,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo;
import com.stardust.view.accessibility.NodeInfo;
import com.stardust.util.ClipboardUtil;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
@@ -22,7 +22,6 @@ import java.util.Arrays;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnLongClick;
import butterknife.Optional;
/**

View File

@@ -1,6 +1,6 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo;
import com.stardust.view.accessibility.NodeInfo;
/**
* Created by Stardust on 2017/3/10.

View File

@@ -11,7 +11,7 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.Theme;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo;
import com.stardust.view.accessibility.NodeInfo;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.LayoutBoundsView;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.NodeInfoView;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.OnNodeInfoSelectListener;

View File

@@ -11,7 +11,7 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.Theme;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo;
import com.stardust.view.accessibility.NodeInfo;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.LayoutHierarchyView;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.NodeInfoView;
import com.stardust.util.MessageIntent;

View File

@@ -1,6 +1,7 @@
package com.stardust.scriptdroid.ui.main.task;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.ThemeColorRecyclerView;
@@ -11,6 +12,8 @@ import android.view.ViewGroup;
import android.widget.TextView;
import android.workground.WrapContentLinearLayoutManager;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.autojs.ScriptEngineService;
import com.stardust.autojs.execution.ScriptExecution;
import com.stardust.autojs.execution.ScriptExecutionListener;
@@ -43,9 +46,19 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView implements Abst
@Override
public void onClick(View v) {
int position = getChildViewHolder((View) v.getParent()).getAdapterPosition();
if(position >= 0){
mScriptEngines.get(position).forceStop();
}else {
if (position >= 0) {
final ScriptEngine engine = mScriptEngines.get(position);
engine.forceStop();
postDelayed(new Runnable() {
@Override
public void run() {
if (!engine.isDestroyed()) {
onScriptANR(engine);
}
}
}, 3000);
} else {
updateEngineList();
}
}
@@ -128,6 +141,10 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView implements Abst
AutoJs.getInstance().getScriptEngineService().unregisterGlobalScriptExecutionListener(mScriptExecutionListener);
}
private void onScriptANR(final ScriptEngine engine) {
// TODO: 2017/7/19 强制停止
}
@Override
public void onEngineCreate(final ScriptEngine engine) {
synchronized (mScriptEngines) {

View File

@@ -208,6 +208,8 @@
<string name="text_accessibility_settings">打开无障碍服务</string>
<string name="text_discard_record">放弃录制</string>
<string name="text_inspect_failed">布局抓取失败,请关闭悬浮窗后动一下页面重试</string>
<string name="script_anr">没有相应,要强制停止吗</string>
<string-array name="record_control_keys">
<item>无</item>

View File

@@ -27,7 +27,7 @@ require("__general__")(__runtime__, this);
(function(scope){
var modules = ['app', 'automator', 'console', 'dialogs', 'io', 'selector', 'shell', 'web', 'ui', "images"];
var modules = ['app', 'automator', 'console', 'dialogs', 'io', 'selector', 'shell', 'web', 'ui', "images", "events"];
var len = modules.length;
for(var i = 0; i < len; i++) {
var m = modules[i];

View File

@@ -0,0 +1,24 @@
module.exports = function(__runtime__, scope){
var events = Object.create(__runtime__.events);
var caller = function(func, args){
var arr = [];
var len = args.length;
for(var i = 0; i < len; i++){
arr.push(args[i]);
}
return func.apply(null, arr);
};
events.setFunctionCaller(caller);
events.emitter = function(){
var e = new com.stardust.autojs.runtime.api.EventEmitter();
e.setFunctionCaller(caller);
return e;
}
return events;
}

View File

@@ -9,9 +9,7 @@ module.exports = function(__runtime__, scope){
scope.log(text);
}
scope.sleep = function(millis){
__runtime__.sleep(millis);
}
scope.sleep = __runtime__.sleep.bind(__runtime__);
scope.isStopped = function(){
return __runtime__.isStopped();

View File

@@ -1,10 +1,12 @@
package com.stardust.autojs.engine;
import android.os.Looper;
import android.util.Log;
import com.stardust.autojs.rhino.AndroidContextFactory;
import com.stardust.autojs.rhino.RhinoAndroidHelper;
import com.stardust.autojs.runtime.ScriptInterruptedException;
import com.stardust.autojs.runtime.api.Events;
import com.stardust.autojs.script.ScriptSource;
import com.stardust.pio.UncheckedIOException;
@@ -41,6 +43,7 @@ public class RhinoJavaScriptEngine implements ScriptEngine {
private Thread mThread;
private RhinoJavaScriptEngineManager mEngineManager;
private Map<String, Object> mTags = new ConcurrentHashMap<>();
private volatile boolean mDestroyed = false;
public RhinoJavaScriptEngine(RhinoJavaScriptEngineManager engineManager) {
mEngineManager = engineManager;
@@ -73,6 +76,7 @@ public class RhinoJavaScriptEngine implements ScriptEngine {
public void forceStop() {
Log.d(LOG_TAG, "forceStop: interrupt Thread: " + mThread);
mThread.interrupt();
Events.quitLooperIfNeeded(mThread);
}
public RhinoJavaScriptEngineManager getEngineManager() {
@@ -86,6 +90,13 @@ public class RhinoJavaScriptEngine implements ScriptEngine {
contextCount--;
Log.d(LOG_TAG, "contextCount = " + contextCount);
mEngineManager.removeEngine(this);
Events.removeThreadRecord(mThread);
mDestroyed = true;
}
@Override
public boolean isDestroyed() {
return mDestroyed;
}
@Override

View File

@@ -25,6 +25,8 @@ public interface ScriptEngine {
void destroy();
boolean isDestroyed();
void setTag(String key, Object value);
Object getTag(String key);

View File

@@ -8,6 +8,7 @@ import com.stardust.autojs.engine.ScriptEngine;
import com.stardust.autojs.runtime.api.AbstractShell;
import com.stardust.autojs.runtime.api.AppUtils;
import com.stardust.autojs.runtime.api.Console;
import com.stardust.autojs.runtime.api.Events;
import com.stardust.autojs.runtime.api.UiSelector;
import com.stardust.autojs.runtime.api.image.Images;
import com.stardust.autojs.runtime.api.image.ScreenCaptureRequester;
@@ -41,10 +42,12 @@ public abstract class AbstractScriptRuntime {
@ScriptVariable
public UI ui;
@ScriptVariable
public Dialogs dialogs;
@ScriptVariable
public Events events;
private Images images;
private static WeakReference<Context> applicationContext;
@@ -54,11 +57,13 @@ public abstract class AbstractScriptRuntime {
this.console = console;
this.automator = new SimpleActionAutomator(bridge, this);
this.info = bridge.getInfoProvider();
this.ui = new UI(uiHandler.getContext());
Context context = uiHandler.getContext();
this.ui = new UI(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
images = new Images(uiHandler.getContext(), this, screenCaptureRequester);
images = new Images(context, this, screenCaptureRequester);
}
dialogs = new Dialogs(app, uiHandler);
events = new Events(context, bridge);
}
public static void setApplicationContext(Context context) {
@@ -115,6 +120,7 @@ public abstract class AbstractScriptRuntime {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
images.releaseScreenCapturer();
}
events.recycle();
}
public Object getImages() {

View File

@@ -1,11 +1,11 @@
package com.stardust.autojs.runtime;
import android.accessibilityservice.AccessibilityService;
import android.support.annotation.Nullable;
import com.stardust.automator.AccessibilityEventCommandHost;
import com.stardust.automator.simple_action.SimpleActionPerformHost;
import com.stardust.view.accessibility.AccessibilityInfoProvider;
import com.stardust.view.accessibility.AccessibilityService;
/**
* Created by Stardust on 2017/4/2.

View File

@@ -1,6 +1,7 @@
package com.stardust.autojs.runtime;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import com.stardust.autojs.R;
@@ -85,7 +86,6 @@ public class ScriptRuntime extends AbstractScriptRuntime {
private UiHandler mUiHandler;
private AccessibilityBridge mAccessibilityBridge;
private AbstractShell mRootShell;
private Supplier<AbstractShell> mShellSupplier;
private ScreenMetrics mScreenMetrics = new ScreenMetrics();

View File

@@ -0,0 +1,195 @@
package com.stardust.autojs.runtime.api;
import android.support.annotation.NonNull;
import com.stardust.autojs.runtime.ScriptStopException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TooManyListenersException;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Created by Stardust on 2017/7/19.
*/
public class EventEmitter {
public interface FunctionCaller {
Object call(Object func, Object[] arg);
}
private static class ListenerWrapper {
Object listener;
boolean isOnce;
public ListenerWrapper(Object listener, boolean isOnce) {
this.listener = listener;
this.isOnce = isOnce;
}
}
private class Listeners {
private CopyOnWriteArrayList<ListenerWrapper> mListenerWrappers = new CopyOnWriteArrayList<>();
void add(Object listener, boolean once) {
ensureListenersNotAtLimit();
mListenerWrappers.add(new ListenerWrapper(listener, once));
}
private void ensureListenersNotAtLimit() {
if (mMaxListeners != 0 && mListenersMap.size() >= mMaxListeners) {
throw new ScriptStopException(new TooManyListenersException("max = " + mMaxListeners));
}
}
boolean empty() {
return mListenerWrappers.isEmpty();
}
void emit(Object[] args) {
Iterator<ListenerWrapper> listenerIterator = mListenerWrappers.iterator();
while (listenerIterator.hasNext()) {
ListenerWrapper listenerWrapper = listenerIterator.next();
onEvent(listenerWrapper, args);
if (listenerWrapper.isOnce) {
listenerIterator.remove();
}
}
}
private void onEvent(ListenerWrapper listenerWrapper, Object[] args) {
if (mFunctionCaller != null) {
mFunctionCaller.call(listenerWrapper.listener, args);
}
}
int count() {
return mListenerWrappers.size();
}
Object[] toArray() {
Iterator<ListenerWrapper> listenerIterator = mListenerWrappers.iterator();
ArrayList<Object> listeners = new ArrayList<>(mListenerWrappers.size());
while (listenerIterator.hasNext()) {
listeners.add(listenerIterator.next().listener);
}
return listeners.toArray(new Object[listeners.size()]);
}
void prepend(Object listener, boolean once) {
ensureListenersNotAtLimit();
mListenerWrappers.add(0, new ListenerWrapper(listener, once));
}
void remove(Object listener) {
Iterator<ListenerWrapper> listenerIterator = mListenerWrappers.iterator();
while (listenerIterator.hasNext()) {
ListenerWrapper l = listenerIterator.next();
if (l.listener == listener) {
listenerIterator.remove();
break;
}
}
}
}
private Map<String, Listeners> mListenersMap = new HashMap<>();
private int mMaxListeners = defaultMaxListeners();
private FunctionCaller mFunctionCaller;
public EventEmitter once(String eventName, Object listener) {
getListeners(eventName).add(listener, true);
return this;
}
@NonNull
private Listeners getListeners(String eventName) {
Listeners listeners = mListenersMap.get(eventName);
if (listeners == null) {
listeners = new Listeners();
mListenersMap.put(eventName, listeners);
}
return listeners;
}
public EventEmitter on(String eventName, Object listener) {
getListeners(eventName).add(listener, false);
return this;
}
public EventEmitter addListener(String eventName, Object listener) {
return on(eventName, listener);
}
public boolean emit(String eventName, Object... args) {
Listeners listeners = mListenersMap.get(eventName);
if (listeners == null || listeners.empty())
return false;
listeners.emit(args);
return true;
}
public String[] eventNames() {
return mListenersMap.keySet().toArray(new String[mListenersMap.size()]);
}
public int listenerCount(String eventName) {
Listeners listeners = mListenersMap.get(eventName);
if (listeners == null)
return 0;
return listeners.count();
}
public Object[] listeners(String eventName) {
return getListeners(eventName).toArray();
}
public EventEmitter prependListener(String eventName, Object listener) {
getListeners(eventName).prepend(listener, false);
return this;
}
public EventEmitter prependOnceListener(String eventName, Object listener) {
getListeners(eventName).prepend(listener, true);
return this;
}
public EventEmitter removeAllListeners() {
mListenersMap.clear();
return this;
}
public EventEmitter removeAllListeners(String eventName) {
mListenersMap.remove(eventName);
return this;
}
public EventEmitter removeListener(String eventName, Object listener) {
Listeners listeners = mListenersMap.get(eventName);
if (listeners != null)
listeners.remove(listener);
return this;
}
public EventEmitter setMaxListeners(int n) {
mMaxListeners = n;
return this;
}
public int getMaxListeners() {
return mMaxListeners;
}
public static int defaultMaxListeners() {
return 0;
}
public void setFunctionCaller(FunctionCaller functionCaller) {
mFunctionCaller = functionCaller;
}
}

View File

@@ -0,0 +1,174 @@
package com.stardust.autojs.runtime.api;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.method.KeyListener;
import android.view.KeyEvent;
import com.stardust.autojs.runtime.AccessibilityBridge;
import com.stardust.autojs.runtime.ScriptStopException;
import com.stardust.autojs.runtime.record.inputevent.InputEventRecorder;
import com.stardust.autojs.runtime.record.inputevent.TouchObserver;
import com.stardust.view.accessibility.AccessibilityService;
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 {
private static final String PREFIX_KEY_DOWN = "__key_down__#";
private static final String PREFIX_KEY_UP = "__key_up__#";
private static ConcurrentHashMap<Thread, Looper> sLoopers = new ConcurrentHashMap<>();
private AccessibilityBridge mAccessibilityBridge;
private boolean mListeningKey = false;
private Handler mHandler;
private Context mContext;
private TouchObserver mTouchObserver;
private long mLastTouchEventMillis;
private long mTouchEventTimeout = 10;
public Events(Context context, AccessibilityBridge accessibilityBridge) {
mAccessibilityBridge = accessibilityBridge;
mContext = context;
}
public void observeKey() {
ensureListeningKey();
}
public void observeTouch() {
if (mTouchObserver != null)
return;
mTouchObserver = new TouchObserver(mContext);
mTouchObserver.setOnTouchEventListener(this);
mTouchObserver.observe();
}
private void ensureListeningKey() {
if (mListeningKey) {
return;
}
mListeningKey = true;
mAccessibilityBridge.ensureServiceEnabled();
AccessibilityService service = mAccessibilityBridge.getService();
if (service == null)
throw new ScriptStopException();
service.getOnKeyObserver().addListener(this);
Looper.prepare();
sLoopers.put(Thread.currentThread(), Looper.myLooper());
mHandler = new Handler();
}
public void loop() {
Looper.loop();
}
public Events onKeyDown(String keyName, Object listener) {
on(PREFIX_KEY_DOWN + keyName, listener);
return this;
}
public Events onceKeyDown(String keyName, Object listener) {
once(PREFIX_KEY_DOWN + keyName, listener);
return this;
}
public Events removeAllKeyDownListeners(String keyName) {
removeAllListeners(PREFIX_KEY_DOWN + keyName);
return this;
}
public Events onKeyUp(String keyName, Object listener) {
on(PREFIX_KEY_UP + keyName, listener);
return this;
}
public Events onceKeyUp(String keyName, Object listener) {
once(PREFIX_KEY_UP + keyName, listener);
return this;
}
public Events removeAllKeyUpListeners(String keyName) {
removeAllListeners(PREFIX_KEY_UP + keyName);
return this;
}
public Events onTouch(Object listener) {
on("touch", listener);
return this;
}
public Events removeAllTouchListeners() {
removeAllListeners("touch");
return this;
}
public long getTouchEventTimeout() {
return mTouchEventTimeout;
}
public void setTouchEventTimeout(long touchEventTimeout) {
mTouchEventTimeout = touchEventTimeout;
}
public void recycle() {
if (mListeningKey) {
AccessibilityService service = mAccessibilityBridge.getService();
if (service != null) {
service.getOnKeyObserver().removeListener(this);
mListeningKey = false;
}
}
if (mTouchObserver != null) {
mTouchObserver.stop();
}
}
@Override
public void onKeyEvent(final int keyCode, final KeyEvent event) {
mHandler.post(new Runnable() {
@Override
public void run() {
String keyName = KeyEvent.keyCodeToString(keyCode).substring(8).toLowerCase();
emit(keyName, event);
if (event.getAction() == KeyEvent.ACTION_DOWN) {
emit(PREFIX_KEY_DOWN + keyName, event);
emit("key_down", keyCode, event);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
emit(PREFIX_KEY_UP + keyName, event);
emit("key_up", keyCode, event);
}
emit("key", keyCode, event);
}
});
}
@Override
public void onTouch(int x, int y) {
if (System.currentTimeMillis() - mLastTouchEventMillis < mTouchEventTimeout) {
return;
}
mLastTouchEventMillis = System.currentTimeMillis();
emit("touch", x, y);
}
public static void removeThreadRecord(Thread thread) {
sLoopers.remove(thread);
}
public static void quitLooperIfNeeded(Thread thread) {
Looper looper = sLoopers.get(thread);
if (looper != null) {
looper.quit();
}
}
}

View File

@@ -117,8 +117,7 @@ public class UiSelector extends UiGlobalSelector {
@ScriptInterface
@NonNull
public UiObjectCollection untilFind() {
UiObjectCollection uiObjectCollection;
uiObjectCollection = find();
UiObjectCollection uiObjectCollection = find();
while (uiObjectCollection.empty()) {
if (Thread.currentThread().isInterrupted()) {
throw new ScriptInterruptedException();

View File

@@ -0,0 +1,50 @@
package com.stardust.autojs.runtime.internal;
/**
* Created by Stardust on 2017/7/19.
*/
public class Functions {
@SuppressWarnings("unchecked")
public static Object call(Func func, Object[] args) {
if (func instanceof Func0) {
return ((Func0) func).call();
} else if (func instanceof Func1) {
return ((Func1) func).call(args[0]);
} else if (func instanceof Func2) {
return ((Func2) func).call(args[0], args[1]);
} else if (func instanceof Func3) {
return ((Func3) func).call(args[0], args[1], args[2]);
} else if (func instanceof Func4) {
return ((Func4) func).call(args[0], args[1], args[2], args[3]);
}
throw new IllegalArgumentException("Unknown func: " + func);
}
public interface Func {
}
public interface Func0<R> extends Func {
R call();
}
public interface Func1<T1, R> extends Func {
R call(T1 t1);
}
public interface Func2<T1, T2, R> extends Func {
R call(T1 t1, T2 t2);
}
public interface Func3<T1, T2, T3, R> extends Func {
R call(T1 t1, T2 t2, T3 t3);
}
public interface Func4<T1, T2, T3, T4, R> extends Func {
R call(T1 t1, T2 t2, T3 t3, T4 t4);
}
}

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.record;
package com.stardust.autojs.runtime.record;
import java.util.Arrays;

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.record.accessibility;
package com.stardust.autojs.runtime.record.accessibility;
import android.accessibilityservice.AccessibilityService;
import android.os.Build;
@@ -8,9 +8,9 @@ import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.automator.UiObject;
import com.stardust.automator.simple_action.FilterAction;
import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo;
import com.stardust.util.SparseArrayEntries;
import com.stardust.view.accessibility.AccessibilityNodeInfoHelper;
import com.stardust.view.accessibility.NodeInfo;
import org.greenrobot.eventbus.EventBus;

View File

@@ -1,9 +1,9 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.record.accessibility;
package com.stardust.autojs.runtime.record.accessibility;
import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
import com.stardust.scriptdroid.external.floatingwindow.menu.record.Recorder;
import com.stardust.autojs.runtime.record.Recorder;
import com.stardust.view.accessibility.AccessibilityDelegate;
import java.util.Arrays;

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent;
package com.stardust.autojs.runtime.record.inputevent;
/**
* Created by Stardust on 2017/3/7.

View File

@@ -1,14 +1,10 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent;
package com.stardust.autojs.runtime.record.inputevent;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.widget.Toast;
import com.flurry.android.FlurryAgent;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.floatingwindow.menu.record.Recorder;
import com.stardust.util.MapEntries;
import com.stardust.autojs.runtime.record.Recorder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -66,6 +62,9 @@ public abstract class InputEventConverter {
}
private final static Pattern LAST_INT_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$");
protected boolean mConverting = false;
private int mState = Recorder.STATE_NOT_START;
@@ -117,14 +116,21 @@ public abstract class InputEventConverter {
} catch (EventFormatException e) {
e.printStackTrace();
if (mFirstEventFormatError) {
Toast.makeText(App.getApp(), R.string.text_record_format_error, Toast.LENGTH_SHORT).show();
mFirstEventFormatError = false;
FlurryAgent.logEvent("EventFormatException", new MapEntries<String, String>()
.entry("message", e.getMessage())
.map());
}
return null;
}
}
public static int parseDeviceNumber(String device) {
Matcher matcher = LAST_INT_PATTERN.matcher(device);
if (matcher.find()) {
String someNumberStr = matcher.group(1);
return Integer.parseInt(someNumberStr);
}
return -1;
}
}

View File

@@ -1,10 +1,9 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent;
package com.stardust.autojs.runtime.record.inputevent;
import android.content.Context;
import com.stardust.autojs.runtime.api.Shell;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.external.floatingwindow.menu.record.Recorder;
import com.stardust.autojs.runtime.record.Recorder;
/**
* Created by Stardust on 2017/3/6.

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent;
package com.stardust.autojs.runtime.record.inputevent;
import android.support.annotation.NonNull;

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent;
package com.stardust.autojs.runtime.record.inputevent;
import android.support.annotation.NonNull;

View File

@@ -1,6 +1,5 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent;
package com.stardust.autojs.runtime.record.inputevent;
import android.media.Image;
import android.support.annotation.NonNull;
import java.util.regex.Matcher;
@@ -15,7 +14,6 @@ import static com.stardust.util.ScreenMetrics.getDeviceScreenWidth;
public class InputEventToSendEventJsConverter extends InputEventConverter {
private final static Pattern LAST_INT_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$");
private double mLastEventTime;
private StringBuilder mCode = new StringBuilder();
private int mTouchDevice = -1;
@@ -73,15 +71,6 @@ public class InputEventToSendEventJsConverter extends InputEventConverter {
}
private int parseDeviceNumber(String device) {
Matcher matcher = LAST_INT_PATTERN.matcher(device);
if (matcher.find()) {
String someNumberStr = matcher.group(1);
return Integer.parseInt(someNumberStr);
}
return -1;
}
private void onTouchX(int device, int value) {
if (mTouchDevice == -1) {
setTouchDevice(device);

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent;
package com.stardust.autojs.runtime.record.inputevent;
import android.content.Context;
import android.support.annotation.NonNull;

View File

@@ -0,0 +1,102 @@
package com.stardust.autojs.runtime.record.inputevent;
import android.content.Context;
import android.support.annotation.NonNull;
/**
* Created by Stardust on 2017/7/20.
*/
public class TouchObserver {
public interface OnTouchEventListener {
void onTouch(int x, int y);
}
private int mTouchX, mTouchY;
private InputEventRecorder mRecorder;
private OnTouchEventListener mOnTouchEventListener;
public TouchObserver(Context context) {
mRecorder = new InputEventRecorder(context, new TouchEventObserver());
}
public void observe() {
mRecorder.listen();
mRecorder.start();
}
public void stop() {
mRecorder.stop();
}
public void setOnTouchEventListener(OnTouchEventListener onTouchEventListener) {
mOnTouchEventListener = onTouchEventListener;
}
private void onTouch(int x, int y) {
mTouchX = x;
mTouchY = y;
if(mOnTouchEventListener != null){
mOnTouchEventListener.onTouch(x, y);
}
}
private class TouchEventObserver extends InputEventConverter {
private int mLastTouchX, mLastTouchY;
@Override
public void convertEvent(@NonNull Event event) {
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) {
return;
}
if (code == 53) {
onTouchX(value);
return;
}
if (code == 54) {
onTouchY(value);
return;
}
if (mLastTouchX >= 0) {
onTouch(mLastTouchX, mTouchY);
mLastTouchX = -1;
return;
}
if (mLastTouchY >= 0) {
onTouch(mTouchX, mLastTouchY);
mLastTouchY = -1;
}
}
private void onTouchX(int value) {
mLastTouchX = value;
}
private void onTouchY(int value) {
if (mLastTouchX > 0) {
onTouch(mLastTouchX, value);
return;
}
mLastTouchY = value;
}
@Override
public String getGetEventCommand() {
return "getevent -t";
}
@Override
public String getCode() {
return null;
}
}
}

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent;
package com.stardust.autojs.runtime.record.inputevent;
import android.content.Context;

View File

@@ -2,9 +2,10 @@
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagIncludeNotImportantViews|flagReportViewIds|flagRetrieveInteractiveWindows|flagRequestEnhancedWebAccessibility"
android:accessibilityFlags="flagIncludeNotImportantViews|flagReportViewIds|flagRetrieveInteractiveWindows|flagRequestEnhancedWebAccessibility|flagRequestFilterKeyEvents"
android:canPerformGestures="true"
android:canRequestEnhancedWebAccessibility="true"
android:canRequestFilterKeyEvents="true"
android:canRetrieveWindowContent="true"
android:description="@string/text_accessibility_service_description"
android:notificationTimeout="100"/>

View File

@@ -1,5 +1,7 @@
package com.stardust.automator.simple_action;
import android.graphics.BitmapFactory;
import com.stardust.automator.UiObject;
import java.util.List;

View File

@@ -1,19 +1,12 @@
package com.stardust.view.accessibility;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import com.stardust.util.ScreenMetrics;
import java.util.HashSet;
import java.util.Map;
@@ -22,6 +15,9 @@ import java.util.SortedMap;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@@ -39,11 +35,14 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi
private static final ReentrantLock LOCK = new ReentrantLock();
private static final Condition ENABLED = LOCK.newCondition();
private static AccessibilityService instance;
private static final OnKeyListener.Observer stickOnKeyObserver = new OnKeyListener.Observer();
private static boolean containsAllEventTypes = false;
private static final Set<Integer> eventTypes = new HashSet<>();
private OnKeyListener.Observer mOnKeyObserver = new OnKeyListener.Observer();
private volatile AccessibilityNodeInfo mRootInActiveWindow;
private Timer mTimer;
private Handler mHandler;
private ExecutorService mKeyEventExecutor;
public static void addDelegate(int uniquePriority, AccessibilityDelegate delegate) {
mDelegates.put(uniquePriority, delegate);
@@ -85,6 +84,25 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi
}
@Override
protected boolean onKeyEvent(final KeyEvent event) {
if (mKeyEventExecutor == null) {
mKeyEventExecutor = Executors.newSingleThreadExecutor();
}
mKeyEventExecutor.execute(new Runnable() {
@Override
public void run() {
stickOnKeyObserver.onKeyEvent(event.getKeyCode(), event);
mOnKeyObserver.onKeyEvent(event.getKeyCode(), event);
}
});
return false;
}
public OnKeyListener.Observer getOnKeyObserver() {
return mOnKeyObserver;
}
@Override
public AccessibilityNodeInfo getRootInActiveWindow() {
return mRootInActiveWindow;
@@ -93,7 +111,10 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi
@Override
public void onDestroy() {
instance = null;
mTimer.cancel();
if (mTimer != null)
mTimer.cancel();
if (mKeyEventExecutor != null)
mKeyEventExecutor.shutdownNow();
super.onDestroy();
}
@@ -155,5 +176,7 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi
}
}
public static OnKeyListener.Observer getStickOnKeyObserver() {
return stickOnKeyObserver;
}
}

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector;
package com.stardust.view.accessibility;
import android.os.Build;
import android.support.annotation.RequiresApi;

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector;
package com.stardust.view.accessibility;
import android.graphics.Rect;
import android.support.annotation.Keep;

View File

@@ -0,0 +1,42 @@
package com.stardust.view.accessibility;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Created by Stardust on 2017/7/18.
*/
public interface OnKeyListener {
void onKeyEvent(int keyCode, KeyEvent event);
class Observer implements OnKeyListener {
private static final String TAG = "OnKeyListenerObserver";
private CopyOnWriteArrayList<OnKeyListener> mOnKeyListeners = new CopyOnWriteArrayList<>();
@Override
public void onKeyEvent(int keyCode, KeyEvent event) {
for (OnKeyListener listener : mOnKeyListeners) {
try {
listener.onKeyEvent(keyCode, event);
} catch (Exception e) {
Log.e(TAG, "Error OnKeyEvent: " + event + " Listener: " + listener, e);
}
}
}
public void addListener(OnKeyListener listener) {
mOnKeyListeners.add(listener);
}
public boolean removeListener(OnKeyListener listener) {
return mOnKeyListeners.remove(listener);
}
}
}

View File

@@ -151,7 +151,7 @@ public class AutoJs implements AccessibilityBridge {
@Nullable
@Override
public android.accessibilityservice.AccessibilityService getService() {
public AccessibilityService getService() {
return AccessibilityService.getInstance();
}