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

@@ -1,70 +0,0 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.view.accessibility.AccessibilityService;
import com.stardust.util.UnderuseExecutors;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Created by Stardust on 2017/3/10.
*/
public class LayoutInspector {
private static final String LOG_TAG = LayoutInspector.class.getSimpleName();
private volatile NodeInfo mCapture;
private volatile boolean mDumping = false;
private Executor mExecutor = Executors.newSingleThreadExecutor();
public void captureCurrentWindow() {
AccessibilityService service = AccessibilityService.getInstance();
if (service == null) {
Log.d(LOG_TAG, "captureCurrentWindow: service = null");
mCapture = null;
} else {
final AccessibilityNodeInfo root = service.getRootInActiveWindow();
if (root == null) {
Log.d(LOG_TAG, "captureCurrentWindow: root = null");
mCapture = null;
} else {
mExecutor.execute(new Runnable() {
@Override
public void run() {
mDumping = true;
mCapture = NodeInfo.capture(root);
mDumping = false;
}
});
}
}
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void refreshChildList(AccessibilityNodeInfo root) {
if (root == null)
return;
root.refresh();
int childCount = root.getChildCount();
for (int i = 0; i < childCount; i++) {
refreshChildList(root.getChild(i));
}
}
public boolean isDumping() {
return mDumping;
}
public void clearCapture() {
mCapture = null;
}
public NodeInfo getCapture() {
return mCapture;
}
}

View File

@@ -1,109 +0,0 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector;
import android.graphics.Rect;
import android.support.annotation.Keep;
import android.support.annotation.NonNull;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.automator.UiObject;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Stardust on 2017/3/10.
*/
@Keep
public class NodeInfo {
private List<NodeInfo> children = new ArrayList<>();
private Rect mBoundsInScreen;
public String id;
public CharSequence desc;
public CharSequence className;
public CharSequence packageName;
public CharSequence text;
public int drawingOrder;
public boolean accessibilityFocused;
public boolean checked;
public boolean clickable;
public boolean contextClickable;
public boolean dismissable;
public boolean editable;
public boolean enabled;
public boolean focusable;
public boolean longClickable;
public boolean selected;
public boolean scrollable;
public String bounds;
public NodeInfo(AccessibilityNodeInfoCompat node) {
id = simplifyId(node.getViewIdResourceName());
desc = node.getContentDescription();
className = node.getClassName();
packageName = node.getPackageName();
text = node.getText();
drawingOrder = node.getDrawingOrder();
accessibilityFocused = node.isAccessibilityFocused();
checked = node.isChecked();
clickable = node.isClickable();
contextClickable = node.isContextClickable();
dismissable = node.isDismissable();
enabled = node.isEnabled();
editable = node.isEditable();
focusable = node.isFocusable();
longClickable = node.isLongClickable();
selected = node.isSelected();
scrollable = node.isScrollable();
mBoundsInScreen = new Rect();
node.getBoundsInScreen(mBoundsInScreen);
bounds = boundsToString(mBoundsInScreen);
}
private String simplifyId(String idResourceName) {
if (idResourceName == null)
return null;
int i = idResourceName.indexOf('/');
return idResourceName.substring(i + 1);
}
public Rect getBoundsInScreen() {
return mBoundsInScreen;
}
public static String boundsToString(Rect rect) {
return rect.toString().replace('-', ',').replace(" ", "").substring(4);
}
public NodeInfo(AccessibilityNodeInfo node) {
this(new AccessibilityNodeInfoCompat(node));
}
public static NodeInfo capture(@NonNull UiObject root) {
NodeInfo nodeInfo = new NodeInfo(root);
int childCount = root.getChildCount();
for (int i = 0; i < childCount; i++) {
UiObject child = root.child(i);
if (child != null) {
nodeInfo.children.add(capture(child));
}
}
return nodeInfo;
}
public static NodeInfo capture(@NonNull AccessibilityNodeInfo root) {
UiObject r = UiObject.createRoot(root);
return capture(r);
}
@NonNull
public List<NodeInfo> getChildren() {
return children;
}
}

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

@@ -1,175 +0,0 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.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;
}
}
}

View File

@@ -1,144 +0,0 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.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.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo;
import com.stardust.util.SparseArrayEntries;
import com.stardust.view.accessibility.AccessibilityNodeInfoHelper;
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;
}
}
}

View File

@@ -1,92 +0,0 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.record.accessibility;
import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
import com.stardust.scriptdroid.external.floatingwindow.menu.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;
}
}

View File

@@ -1,25 +0,0 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.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);
}
}

View File

@@ -1,130 +0,0 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.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 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 + '\'' +
'}';
}
}
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) {
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;
}
}
}

View File

@@ -1,80 +0,0 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.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;
/**
* 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);
}
}

View File

@@ -1,158 +0,0 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.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();
}
}

View File

@@ -1,52 +0,0 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.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);
}
}
}

View File

@@ -1,126 +0,0 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent;
import android.media.Image;
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 final static Pattern LAST_INT_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$");
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 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);
}
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();");
}
}

View File

@@ -1,67 +0,0 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.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);
}
}
}

View File

@@ -1,22 +0,0 @@
package com.stardust.scriptdroid.external.floatingwindow.menu.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();
}
}

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>