add events module, including EventEmiiter, KeyEvent, TouchEvent
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -25,6 +25,8 @@ public interface ScriptEngine {
|
||||
|
||||
void destroy();
|
||||
|
||||
boolean isDestroyed();
|
||||
|
||||
void setTag(String key, Object value);
|
||||
|
||||
Object getTag(String key);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
174
autojs/src/main/java/com/stardust/autojs/runtime/api/Events.java
Normal file
174
autojs/src/main/java/com/stardust/autojs/runtime/api/Events.java
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package com.stardust.autojs.runtime.record;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/16.
|
||||
*/
|
||||
|
||||
public interface Recorder {
|
||||
|
||||
interface OnStateChangedListener {
|
||||
|
||||
void onStart();
|
||||
|
||||
void onStop();
|
||||
|
||||
void onPause();
|
||||
|
||||
void onResume();
|
||||
|
||||
}
|
||||
|
||||
class StateChangeEvent {
|
||||
|
||||
private int mOldState;
|
||||
private int mCurrentState;
|
||||
|
||||
public StateChangeEvent(int oldState, int currentState) {
|
||||
mOldState = oldState;
|
||||
mCurrentState = currentState;
|
||||
}
|
||||
|
||||
public int getOldState() {
|
||||
return mOldState;
|
||||
}
|
||||
|
||||
public int getCurrentState() {
|
||||
return mCurrentState;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int STATE_NOT_START = 0;
|
||||
int STATE_RECORDING = 1;
|
||||
int STATE_PAUSED = 2;
|
||||
int STATE_STOPPED = 3;
|
||||
|
||||
void start();
|
||||
|
||||
void stop();
|
||||
|
||||
void pause();
|
||||
|
||||
void resume();
|
||||
|
||||
String getCode();
|
||||
|
||||
int getState();
|
||||
|
||||
void setOnStateChangedListener(OnStateChangedListener onStateChangedListener);
|
||||
|
||||
abstract class AbstractRecorder implements Recorder {
|
||||
|
||||
private static final OnStateChangedListener NO_OPERATION_LISTENER = new OnStateChangedListener() {
|
||||
@Override
|
||||
public void onStart() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private OnStateChangedListener mOnStateChangedListener = NO_OPERATION_LISTENER;
|
||||
|
||||
private final boolean mSync;
|
||||
private int mState = STATE_NOT_START;
|
||||
|
||||
public AbstractRecorder(boolean syncOfState) {
|
||||
mSync = syncOfState;
|
||||
}
|
||||
|
||||
public AbstractRecorder() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
ensureIsStateOf(STATE_NOT_START);
|
||||
setState(STATE_RECORDING);
|
||||
startImpl();
|
||||
mOnStateChangedListener.onStart();
|
||||
}
|
||||
|
||||
|
||||
private void ensureIsStateOf(int... expectedStates) {
|
||||
for (int expectedState : expectedStates) {
|
||||
if (mState == expectedState)
|
||||
return;
|
||||
}
|
||||
throw new IllegalStateException("expected=" + Arrays.toString(expectedStates) + " state=" + mState);
|
||||
}
|
||||
|
||||
|
||||
protected abstract void startImpl();
|
||||
|
||||
public void stop() {
|
||||
ensureIsStateOf(STATE_RECORDING, STATE_PAUSED);
|
||||
setState(STATE_STOPPED);
|
||||
stopImpl();
|
||||
mOnStateChangedListener.onStop();
|
||||
}
|
||||
|
||||
protected abstract void stopImpl();
|
||||
|
||||
public void pause() {
|
||||
ensureIsStateOf(STATE_RECORDING);
|
||||
setState(STATE_PAUSED);
|
||||
pauseImpl();
|
||||
mOnStateChangedListener.onPause();
|
||||
}
|
||||
|
||||
protected synchronized void setState(int state) {
|
||||
if (mSync) {
|
||||
synchronized (this) {
|
||||
mState = state;
|
||||
}
|
||||
} else {
|
||||
mState = state;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized int getState() {
|
||||
if (mSync) {
|
||||
synchronized (this) {
|
||||
return mState;
|
||||
}
|
||||
} else {
|
||||
return mState;
|
||||
}
|
||||
}
|
||||
|
||||
protected void pauseImpl() {
|
||||
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
ensureIsStateOf(STATE_PAUSED);
|
||||
setState(STATE_RECORDING);
|
||||
resumeImpl();
|
||||
mOnStateChangedListener.onResume();
|
||||
}
|
||||
|
||||
protected void resumeImpl() {
|
||||
|
||||
}
|
||||
|
||||
public void setOnStateChangedListener(OnStateChangedListener onStateChangedListener) {
|
||||
mOnStateChangedListener = onStateChangedListener == null ? NO_OPERATION_LISTENER : onStateChangedListener;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.stardust.autojs.runtime.record.accessibility;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.os.Build;
|
||||
import android.util.SparseArray;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.automator.UiObject;
|
||||
import com.stardust.automator.simple_action.FilterAction;
|
||||
import com.stardust.util.SparseArrayEntries;
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoHelper;
|
||||
import com.stardust.view.accessibility.NodeInfo;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/2/14.
|
||||
*/
|
||||
|
||||
public class AccessibilityActionConverter {
|
||||
|
||||
private static final SparseArray<EventToScriptConverter> CONVERTER_MAP = new SparseArrayEntries<EventToScriptConverter>()
|
||||
.entry(AccessibilityEvent.TYPE_VIEW_CLICKED, new DoUtilSucceedConverter("click"))
|
||||
.entry(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED, new DoUtilSucceedConverter("longClick"))
|
||||
.entry(AccessibilityEvent.TYPE_VIEW_SCROLLED, new DoOnceConverter("//scroll???"))
|
||||
.entry(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED, new SetTextEventConverter())
|
||||
.sparseArray();
|
||||
|
||||
static {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
CONVERTER_MAP.put(AccessibilityEvent.TYPE_VIEW_CONTEXT_CLICKED, new DoOnceConverter("contextClick"));
|
||||
}
|
||||
}
|
||||
|
||||
private StringBuilder mScript = new StringBuilder();
|
||||
private boolean mFirstAction = true;
|
||||
|
||||
public AccessibilityActionConverter(boolean shouldIgnoreFirstAction) {
|
||||
mShouldIgnoreFirstAction = shouldIgnoreFirstAction;
|
||||
}
|
||||
|
||||
private boolean mShouldIgnoreFirstAction = false;
|
||||
|
||||
public void record(AccessibilityService service, AccessibilityEvent event) {
|
||||
EventToScriptConverter converter = CONVERTER_MAP.get(event.getEventType());
|
||||
if (converter != null) {
|
||||
if (mFirstAction && mShouldIgnoreFirstAction) {
|
||||
mFirstAction = false;
|
||||
return;
|
||||
}
|
||||
converter.onAccessibilityEvent(service, event, mScript);
|
||||
mScript.append("\n");
|
||||
EventBus.getDefault().post(new AccessibilityActionRecorder.AccessibilityActionRecordEvent(event));
|
||||
}
|
||||
}
|
||||
|
||||
public String getScript() {
|
||||
return mScript.toString();
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
mFirstAction = true;
|
||||
}
|
||||
|
||||
interface EventToScriptConverter {
|
||||
|
||||
void onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event, StringBuilder sb);
|
||||
}
|
||||
|
||||
private static abstract class BoundsEventConverter implements EventToScriptConverter {
|
||||
|
||||
@Override
|
||||
public void onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event, StringBuilder sb) {
|
||||
AccessibilityNodeInfo source = event.getSource();
|
||||
if (source == null)
|
||||
return;
|
||||
String bounds = NodeInfo.boundsToString(AccessibilityNodeInfoHelper.getBoundsInScreen(source));
|
||||
source.recycle();
|
||||
onAccessibilityEvent(event, bounds, sb);
|
||||
}
|
||||
|
||||
protected abstract void onAccessibilityEvent(AccessibilityEvent event, String bounds, StringBuilder sb);
|
||||
|
||||
}
|
||||
|
||||
private static class DoOnceConverter extends BoundsEventConverter {
|
||||
|
||||
private String mActionFunction;
|
||||
|
||||
DoOnceConverter(String actionFunction) {
|
||||
mActionFunction = actionFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAccessibilityEvent(AccessibilityEvent event, String bounds, StringBuilder sb) {
|
||||
sb.append(mActionFunction).append(bounds).append(";");
|
||||
}
|
||||
}
|
||||
|
||||
private static class DoUtilSucceedConverter extends BoundsEventConverter {
|
||||
|
||||
private String mActionFunction;
|
||||
|
||||
DoUtilSucceedConverter(String actionFunction) {
|
||||
mActionFunction = actionFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAccessibilityEvent(AccessibilityEvent event, String bounds, StringBuilder sb) {
|
||||
sb.append("while(!").append(mActionFunction).append(bounds).append(");");
|
||||
}
|
||||
}
|
||||
|
||||
private static class SetTextEventConverter implements EventToScriptConverter {
|
||||
|
||||
@Override
|
||||
public void onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event, StringBuilder sb) {
|
||||
AccessibilityNodeInfo source = event.getSource();
|
||||
if (source == null)
|
||||
return;
|
||||
UiObject uiObject = UiObject.createRoot(service.getRootInActiveWindow());
|
||||
List<UiObject> editableList = FilterAction.EditableFilter.findEditable(uiObject);
|
||||
int i = findInEditableList(editableList, source);
|
||||
sb.append("while(!input(").append(i).append(", \"").append(source.getText()).append("\"));");
|
||||
source.recycle();
|
||||
}
|
||||
|
||||
private static int findInEditableList(List<UiObject> editableList, AccessibilityNodeInfo editable) {
|
||||
int i = 0;
|
||||
for (UiObject nodeInfo : editableList) {
|
||||
if (AccessibilityNodeInfoHelper.getBoundsInScreen(nodeInfo).equals(AccessibilityNodeInfoHelper.getBoundsInScreen(editable))) {
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.stardust.autojs.runtime.record.accessibility;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import com.stardust.autojs.runtime.record.Recorder;
|
||||
import com.stardust.view.accessibility.AccessibilityDelegate;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/2/14.
|
||||
*/
|
||||
|
||||
public class AccessibilityActionRecorder extends Recorder.AbstractRecorder implements AccessibilityDelegate {
|
||||
|
||||
public static class AccessibilityActionRecordEvent {
|
||||
|
||||
private final AccessibilityEvent mAccessibilityEvent;
|
||||
|
||||
public AccessibilityActionRecordEvent(AccessibilityEvent event) {
|
||||
mAccessibilityEvent = event;
|
||||
}
|
||||
|
||||
public AccessibilityEvent getAccessibilityEvent() {
|
||||
return mAccessibilityEvent;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Set<Integer> EVENT_TYPES = new HashSet<>(Arrays.asList(AccessibilityEvent.TYPE_VIEW_CLICKED, AccessibilityEvent.TYPE_VIEW_LONG_CLICKED, AccessibilityEvent.TYPE_VIEW_SCROLLED, AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED));
|
||||
|
||||
private static final long RECORD_TIME_OUT = 10 * 60 * 1000;
|
||||
|
||||
private boolean mShouldIgnoreFirstAction = false;
|
||||
|
||||
|
||||
private AccessibilityActionConverter mConverter;
|
||||
private long mRecordStartMillis;
|
||||
|
||||
public AccessibilityActionRecorder() {
|
||||
super(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startImpl() {
|
||||
mConverter = new AccessibilityActionConverter(mShouldIgnoreFirstAction);
|
||||
mRecordStartMillis = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopImpl() {
|
||||
setState(STATE_NOT_START);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resumeImpl() {
|
||||
mConverter.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
|
||||
if (getState() == STATE_RECORDING) {
|
||||
mConverter.record(service, event);
|
||||
checkTimeOut();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Integer> getEventTypes() {
|
||||
return EVENT_TYPES;
|
||||
}
|
||||
|
||||
private void checkTimeOut() {
|
||||
if (System.currentTimeMillis() - mRecordStartMillis > RECORD_TIME_OUT) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return mConverter.getScript();
|
||||
}
|
||||
|
||||
public void setShouldIgnoreFirstAction(boolean shouldIgnoreFirstAction) {
|
||||
mShouldIgnoreFirstAction = shouldIgnoreFirstAction;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.stardust.autojs.runtime.record.inputevent;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/7.
|
||||
*/
|
||||
|
||||
public class EventFormatException extends RuntimeException {
|
||||
|
||||
|
||||
public static EventFormatException forEventStr(String eventStr, NumberFormatException e) {
|
||||
return new EventFormatException(eventStr, e);
|
||||
}
|
||||
|
||||
public EventFormatException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public EventFormatException(String eventStr, Exception e) {
|
||||
super("eventStr=" + eventStr, e);
|
||||
}
|
||||
|
||||
public EventFormatException(String eventStr) {
|
||||
super("eventStr=" + eventStr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.stardust.autojs.runtime.record.inputevent;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.stardust.autojs.runtime.record.Recorder;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/7.
|
||||
*/
|
||||
|
||||
public abstract class InputEventConverter {
|
||||
|
||||
static class Event {
|
||||
|
||||
static final Pattern PATTERN = Pattern.compile("^\\[([^\\]]*)\\]\\s+([^:]*):\\s+([^\\s]*)\\s+([^\\s]*)\\s+([^\\s]*)\\s*$");
|
||||
|
||||
static Event parseEvent(String eventStr) {
|
||||
Matcher matcher = Event.PATTERN.matcher(eventStr);
|
||||
if (!matcher.matches()) {
|
||||
throw new EventFormatException(eventStr);
|
||||
}
|
||||
double time;
|
||||
try {
|
||||
time = Double.parseDouble(matcher.group(1));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new EventFormatException(eventStr, e);
|
||||
}
|
||||
return new Event(time, matcher.group(2), matcher.group(3), matcher.group(4), matcher.group(5));
|
||||
}
|
||||
|
||||
|
||||
double time;
|
||||
String device;
|
||||
String type;
|
||||
String code;
|
||||
String value;
|
||||
|
||||
public Event(double time, String device, String type, String code, String value) {
|
||||
this.time = time;
|
||||
this.device = device;
|
||||
this.type = type;
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Event{" +
|
||||
"time=" + time +
|
||||
", device='" + device + '\'' +
|
||||
", type='" + type + '\'' +
|
||||
", code='" + code + '\'' +
|
||||
", value='" + value + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final static Pattern LAST_INT_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$");
|
||||
|
||||
|
||||
protected boolean mConverting = false;
|
||||
private int mState = Recorder.STATE_NOT_START;
|
||||
|
||||
public void convertEventIfFormatCorrect(String eventStr) {
|
||||
if (!mConverting)
|
||||
return;
|
||||
if (TextUtils.isEmpty(eventStr) || !eventStr.startsWith("["))
|
||||
return;
|
||||
Event event = parseEventOrNull(eventStr);
|
||||
if (event != null) {
|
||||
convertEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void convertEvent(@NonNull Event event);
|
||||
|
||||
public String getGetEventCommand() {
|
||||
return "getevent -t -l";
|
||||
}
|
||||
|
||||
|
||||
public void start() {
|
||||
mConverting = true;
|
||||
mState = Recorder.STATE_RECORDING;
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
mConverting = true;
|
||||
mState = Recorder.STATE_RECORDING;
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
mConverting = false;
|
||||
mState = Recorder.STATE_PAUSED;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
mConverting = false;
|
||||
mState = Recorder.STATE_STOPPED;
|
||||
}
|
||||
|
||||
public abstract String getCode();
|
||||
|
||||
private boolean mFirstEventFormatError = true;
|
||||
|
||||
public Event parseEventOrNull(String eventStr) {
|
||||
try {
|
||||
return Event.parseEvent(eventStr);
|
||||
} catch (EventFormatException e) {
|
||||
e.printStackTrace();
|
||||
if (mFirstEventFormatError) {
|
||||
mFirstEventFormatError = false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.stardust.autojs.runtime.record.inputevent;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.stardust.autojs.runtime.api.Shell;
|
||||
import com.stardust.autojs.runtime.record.Recorder;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/6.
|
||||
*/
|
||||
|
||||
public class InputEventRecorder extends Recorder.AbstractRecorder {
|
||||
|
||||
private static final String TAG = "InputEventRecorder";
|
||||
private String mGetEventCommand;
|
||||
private Shell mShell;
|
||||
protected InputEventConverter mInputEventConverter;
|
||||
private Context mContext;
|
||||
|
||||
|
||||
protected InputEventRecorder(Context context, InputEventConverter inputEventConverter) {
|
||||
mGetEventCommand = inputEventConverter.getGetEventCommand();
|
||||
mInputEventConverter = inputEventConverter;
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void listen() {
|
||||
mShell = new Shell(mContext, true);
|
||||
mShell.setCallback(new Shell.SimpleCallback() {
|
||||
@Override
|
||||
public void onNewLine(String str) {
|
||||
if (mShell.isInitialized()) {
|
||||
convertEvent(str);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitialized() {
|
||||
mShell.exec(mGetEventCommand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInterrupted(InterruptedException e) {
|
||||
stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startImpl() {
|
||||
mInputEventConverter.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void pauseImpl() {
|
||||
mInputEventConverter.pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resumeImpl() {
|
||||
mInputEventConverter.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopImpl() {
|
||||
mShell.exit();
|
||||
mInputEventConverter.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return mInputEventConverter.getCode();
|
||||
}
|
||||
|
||||
protected void convertEvent(String eventStr) {
|
||||
mInputEventConverter.convertEventIfFormatCorrect(eventStr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package com.stardust.autojs.runtime.record.inputevent;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.stardust.util.MapEntries;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/7.
|
||||
*/
|
||||
|
||||
public class InputEventToJsConverter extends InputEventConverter {
|
||||
|
||||
interface EventHandler {
|
||||
void handle(Event event);
|
||||
}
|
||||
|
||||
abstract static class Router implements EventHandler {
|
||||
|
||||
private Map<String, EventHandler> mEventHandlerMap = new HashMap<>();
|
||||
|
||||
Router put(String key, EventHandler handler) {
|
||||
mEventHandlerMap.put(key, handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Event event) {
|
||||
EventHandler handler = mEventHandlerMap.get(getKey(event));
|
||||
if (handler != null) {
|
||||
handler.handle(event);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract String getKey(Event event);
|
||||
}
|
||||
|
||||
private class TypeRouter extends Router {
|
||||
|
||||
TypeRouter() {
|
||||
put("EV_ABS", new AbsHandler());
|
||||
put("EV_SYN", new SyncHandler());
|
||||
put("EV_KEY", new KeyHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getKey(Event event) {
|
||||
return event.type;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Point {
|
||||
int x, y;
|
||||
}
|
||||
|
||||
private class AbsHandler implements EventHandler {
|
||||
|
||||
@Override
|
||||
public void handle(Event event) {
|
||||
if (event.code.equals("ABS_MT_POSITION_X")) {
|
||||
mTouchPoint.x = Integer.parseInt(event.value, 16);
|
||||
mTouchTime = event.time;
|
||||
} else if (event.code.equals("ABS_MT_POSITION_Y")) {
|
||||
mTouchPoint.y = Integer.parseInt(event.value, 16);
|
||||
mTouchTime = event.time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SyncHandler implements EventHandler {
|
||||
|
||||
@Override
|
||||
public void handle(Event event) {
|
||||
if (mTouchDown) {
|
||||
if (mSwipe) {
|
||||
appendCodeIfNotStopped(event);
|
||||
mSwipe = false;
|
||||
} else {
|
||||
mLastTouchPoint.x = mTouchPoint.x;
|
||||
mLastTouchPoint.y = mTouchPoint.y;
|
||||
mSwipe = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void appendCodeIfNotStopped(Event event) {
|
||||
if (!mConverting)
|
||||
return;
|
||||
long interval = (long) (1000 * (event.time - mTouchTime));
|
||||
mCode.append("sh.Swipe(")
|
||||
.append(mLastTouchPoint.x).append(", ").append(mLastTouchPoint.y).append(", ")
|
||||
.append(mTouchPoint.x).append(", ").append(mTouchPoint.y);
|
||||
if (interval >= 1) {
|
||||
mCode.append(", ").append(interval);
|
||||
}
|
||||
mCode.append(");\n");
|
||||
}
|
||||
}
|
||||
|
||||
private class KeyHandler implements EventHandler {
|
||||
|
||||
private Map<String, String> mKeyPressCodeMap = new MapEntries<String, String>()
|
||||
.entry("KEY_HOME", "Home")
|
||||
.entry("KEY_MENU", "Menu")
|
||||
.entry("KEY_VOLUMEDOWN", "VolumeDown")
|
||||
.entry("KEY_VOLUMEUP", "VolumeUp")
|
||||
.entry("KEY_BACK", "Back")
|
||||
.entry("KEY_CAMERA", "Camera")
|
||||
.map();
|
||||
|
||||
@Override
|
||||
public void handle(Event event) {
|
||||
if (event.code.equals("BTN_TOUCH")) {
|
||||
mTouchDown = event.value.equals("DOWN");
|
||||
if (!mTouchDown && mSwipe) {
|
||||
if (mConverting)
|
||||
mCode.append("sh.Tap(").append(mLastTouchPoint.x).append(", ").append(mLastTouchPoint.y).append(");\n");
|
||||
mSwipe = false;
|
||||
}
|
||||
} else if (event.value.equals("UP")) {
|
||||
appendKeyPressCode(event);
|
||||
}
|
||||
}
|
||||
|
||||
private void appendKeyPressCode(Event event) {
|
||||
String code = mKeyPressCodeMap.get(event.code);
|
||||
if (code != null) {
|
||||
mCode.append("sh.").append(code).append("();\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EventHandler mEventHandler = new TypeRouter();
|
||||
private StringBuilder mCode = new StringBuilder().append("var sh = new Shell(true);\n");
|
||||
private Point mTouchPoint = new Point(), mLastTouchPoint = new Point();
|
||||
private boolean mTouchDown = false, mSwipe = false;
|
||||
private double mTouchTime;
|
||||
|
||||
|
||||
@Override
|
||||
public void convertEvent(@NonNull Event event) {
|
||||
mEventHandler.handle(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
mCode.append("sh.exitAndWaitFor();");
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return mCode.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.stardust.autojs.runtime.record.inputevent;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/7.
|
||||
*/
|
||||
|
||||
public class InputEventToSendEventConverter extends InputEventConverter {
|
||||
|
||||
private static final DecimalFormat DELAY_FORMAT = new DecimalFormat("#.###");
|
||||
|
||||
private double mLastEventTime;
|
||||
private StringBuilder mSendEventCommands = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public void convertEvent(@NonNull Event event) {
|
||||
if (mLastEventTime == 0) {
|
||||
mLastEventTime = event.time;
|
||||
} else if (event.time - mLastEventTime > 0.1) {
|
||||
mSendEventCommands.append("sleep ").append(DELAY_FORMAT.format(event.time - mLastEventTime)).append("\n");
|
||||
mLastEventTime = event.time;
|
||||
}
|
||||
mSendEventCommands.append("sendevent ")
|
||||
.append(event.device).append(" ")
|
||||
.append(hex2dec(event.type)).append(" ")
|
||||
.append(hex2dec(event.code)).append(" ")
|
||||
.append(hex2dec(event.value)).append("\n");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGetEventCommand() {
|
||||
return "getevent -t";
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return mSendEventCommands.toString();
|
||||
}
|
||||
|
||||
private static String hex2dec(String hex) {
|
||||
try {
|
||||
return String.valueOf((int) Long.parseLong(hex, 16));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new EventFormatException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
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/5/3.
|
||||
*/
|
||||
|
||||
public class InputEventToSendEventJsConverter extends InputEventConverter {
|
||||
|
||||
private double mLastEventTime;
|
||||
private StringBuilder mCode = new StringBuilder();
|
||||
private int mTouchDevice = -1;
|
||||
private int mLastTouchX = -1;
|
||||
private int mLastTouchY = -1;
|
||||
|
||||
public InputEventToSendEventJsConverter() {
|
||||
mCode.append("var sh = new Shell(true);\n")
|
||||
.append("sh.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.03) {
|
||||
mCode.append("sh.usleep(").append((long) (1000000 * (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();
|
||||
mCode.append("sh.SendEvent(");
|
||||
if (device != mTouchDevice) {
|
||||
mCode.append(device).append(", ");
|
||||
}
|
||||
mCode.append(type).append(", ")
|
||||
.append(code).append(", ")
|
||||
.append(value).append(");\n");
|
||||
}
|
||||
|
||||
private void checkLastTouch() {
|
||||
if (mLastTouchX >= 0) {
|
||||
mCode.append("sh.TouchX(").append(mLastTouchX).append(");\n");
|
||||
mLastTouchX = -1;
|
||||
}
|
||||
if (mLastTouchY >= 0) {
|
||||
mCode.append("sh.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("sh.Touch(")
|
||||
.append(mLastTouchX).append(", ")
|
||||
.append(value).append(");\n");
|
||||
mLastTouchX = -1;
|
||||
} else {
|
||||
mLastTouchY = value;
|
||||
}
|
||||
}
|
||||
|
||||
private void setTouchDevice(int i) {
|
||||
mCode.append("sh.SetTouchDevice(").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("sh.exitAndWaitFor();");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.stardust.autojs.runtime.record.inputevent;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/5/4.
|
||||
*/
|
||||
|
||||
public class KeyObserver {
|
||||
|
||||
public interface KeyListener {
|
||||
|
||||
void onKeyDown(String keyName);
|
||||
|
||||
void onKeyUp(String keyName);
|
||||
|
||||
}
|
||||
|
||||
private InputEventRecorder mObserver;
|
||||
private KeyListener mKeyListener;
|
||||
|
||||
public KeyObserver(Context context) {
|
||||
mObserver = new InputEventRecorder(context, new InputEventConverter() {
|
||||
@Override
|
||||
public void convertEvent(@NonNull Event event) {
|
||||
if (event.value.equalsIgnoreCase("UP")) {
|
||||
notifyKeyUp(event.code);
|
||||
}
|
||||
if (event.value.equalsIgnoreCase("DOWN")) {
|
||||
notifyKeyDown(event.code);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setKeyListener(KeyListener keyListener) {
|
||||
mKeyListener = keyListener;
|
||||
}
|
||||
|
||||
public void startListening() {
|
||||
mObserver.listen();
|
||||
mObserver.start();
|
||||
}
|
||||
|
||||
public void stopListening() {
|
||||
mObserver.stopImpl();
|
||||
}
|
||||
|
||||
private void notifyKeyDown(String keyName) {
|
||||
if (mKeyListener != null) {
|
||||
mKeyListener.onKeyDown(keyName);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyKeyUp(String keyName) {
|
||||
if (mKeyListener != null) {
|
||||
mKeyListener.onKeyUp(keyName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.stardust.autojs.runtime.record.inputevent;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/16.
|
||||
*/
|
||||
|
||||
public class TouchRecorder extends InputEventRecorder {
|
||||
|
||||
public TouchRecorder(Context context) {
|
||||
super(context, new InputEventToSendEventJsConverter());
|
||||
listen();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user