add: javascript support
2
.gitignore
vendored
@@ -6,4 +6,4 @@
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.externalNativeBuild
|
||||
@@ -40,4 +40,10 @@ dependencies {
|
||||
compile 'com.afollestad.material-dialogs:core:0.9.2.3'
|
||||
compile 'com.google.code.gson:gson:2.8.0'
|
||||
compile 'com.sothree.slidinguppanel:library:3.3.1'
|
||||
|
||||
compile 'com.squareup.duktape:duktape-android:1.1.0'
|
||||
compile 'com.furture.react:DuktapeJava:1.1.0'
|
||||
|
||||
compile files('libs/JSTransformer.jar')
|
||||
|
||||
}
|
||||
|
||||
BIN
app/libs/JSTransformer.jar
Normal file
@@ -8,6 +8,7 @@
|
||||
<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"/>
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/script_droid"
|
||||
android:label="@string/app_name"
|
||||
@@ -36,7 +37,7 @@
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".action.ActionPerformService"
|
||||
android:name=".droid.runtime.action.ActionPerformService"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:label="脚本执行机器人"
|
||||
@@ -51,8 +52,8 @@
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name="com.stardust.scriptdroid.tile.TileService"
|
||||
android:icon="@drawable/ic_accessibility_service"
|
||||
android:name="com.stardust.scriptdroid.tile.BoundsAssistEnableTileService"
|
||||
android:icon="@drawable/ic_robot_head"
|
||||
android:label="脚本辅助"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||
<intent-filter>
|
||||
|
||||
112
app/src/main/assets/javasccript_engine_init.js
Normal file
@@ -0,0 +1,112 @@
|
||||
|
||||
var toast = function(text){
|
||||
droid.toast(text);
|
||||
}
|
||||
|
||||
var launchPackage = function(package){
|
||||
droid.launchPackage(package);
|
||||
}
|
||||
|
||||
var launch = function(a, b){
|
||||
if(arguments.length == 2){
|
||||
droid.launch(a, b);
|
||||
}else{
|
||||
droid.launchPackage(a);
|
||||
}
|
||||
}
|
||||
|
||||
var launchApp = function(appName){
|
||||
droid.launchApp(appName);
|
||||
}
|
||||
|
||||
|
||||
var text = function(str){
|
||||
return droid.text(str);
|
||||
}
|
||||
|
||||
var bounds = function(left, top, right, bottom){
|
||||
return droid.bounds(left, top, right, bottom);
|
||||
}
|
||||
|
||||
|
||||
function performAction(action, args){
|
||||
if(args.length == 4){
|
||||
return action(bounds(args[0], args[1], args[2], args[3]));
|
||||
}else{
|
||||
return action(text(args[0]));
|
||||
}
|
||||
}
|
||||
|
||||
var click = function(){
|
||||
return performAction(function(target){
|
||||
return droid.click(target);
|
||||
}, arguments);
|
||||
}
|
||||
|
||||
var longClick = function(a, b, c, d){
|
||||
return performAction(function(target){
|
||||
return droid.longClick(target);
|
||||
}, arguments);
|
||||
}
|
||||
|
||||
var scrollAllUp = function(){
|
||||
droid.scrollAllUp();
|
||||
}
|
||||
|
||||
var scrollAllDown = function(){
|
||||
droid.scrollAllDown();
|
||||
}
|
||||
|
||||
var scrollUp = function(a, b, c, d){
|
||||
if(arguments.length == 0)
|
||||
return scrollAllUp();
|
||||
return performAction(function(target){
|
||||
return droid.scrollUp(target);
|
||||
}, arguments);
|
||||
}
|
||||
|
||||
var scrollDown = function(a, b, c, d){
|
||||
if(arguments.length == 0)
|
||||
return scrollAllDown();
|
||||
return performAction(function(target){
|
||||
return droid.scrollDown(target);
|
||||
}, arguments);
|
||||
}
|
||||
|
||||
var select = function(a, b, c, d){
|
||||
return performAction(function(target){
|
||||
return droid.select(target);
|
||||
}, arguments);
|
||||
}
|
||||
|
||||
var focus = function(a, b, c, d){
|
||||
return performAction(function(target){
|
||||
return droid.focus(target);
|
||||
}, arguments);
|
||||
}
|
||||
|
||||
var paste = function(a, b, c, d){
|
||||
return performAction(function(target){
|
||||
return droid.paste(target);
|
||||
}, arguments);
|
||||
}
|
||||
|
||||
var editable = function(i){
|
||||
return droid.editable(i);
|
||||
}
|
||||
|
||||
var setText = function(target, str){
|
||||
return droid.setText(target, str);
|
||||
}
|
||||
|
||||
var input = function(a, b){
|
||||
if(arguments.length == 1){
|
||||
return droid.setText(editable(-1), a);
|
||||
}else{
|
||||
return droid.setText(editable(a), b);
|
||||
}
|
||||
}
|
||||
|
||||
var sleep = function(millis){
|
||||
droid.sleep(millis);
|
||||
}
|
||||
3
app/src/main/assets/test.js
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
droid.toast("略略略");
|
||||
droid.click(droid.text("哈哈哈"));
|
||||
21
app/src/main/java/com/stardust/scriptdroid/App.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.stardust.scriptdroid;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class App extends Application {
|
||||
|
||||
private static App instance;
|
||||
|
||||
public static App getApp() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
instance = this;
|
||||
}
|
||||
}
|
||||
@@ -10,16 +10,17 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.stardust.scriptdroid.action.ActionPerformService;
|
||||
import com.stardust.scriptdroid.data.ScriptFile;
|
||||
import com.stardust.scriptdroid.data.ScriptFileList;
|
||||
import com.stardust.scriptdroid.data.SharedPrefScriptFileList;
|
||||
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformService;
|
||||
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
|
||||
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
|
||||
import com.stardust.scriptdroid.droid.script.file.SharedPrefScriptFileList;
|
||||
import com.stardust.scriptdroid.file.FileChooser;
|
||||
import com.stardust.scriptdroid.file.FileUtils;
|
||||
import com.stardust.scriptdroid.ui.ScriptFileOperation;
|
||||
import com.stardust.scriptdroid.ui.ScriptListRecyclerView;
|
||||
import com.stardust.scriptdroid.ui.SlidingUpPanel;
|
||||
import com.stardust.util.MapEntries;
|
||||
import com.stardust.view.accessibility.AccessibilityServiceUtils;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -42,13 +43,27 @@ public class MainActivity extends BaseActivity {
|
||||
setUpFileChooser();
|
||||
|
||||
checkPermissions();
|
||||
|
||||
}
|
||||
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
}
|
||||
|
||||
private void checkPermissions() {
|
||||
//checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
ActionPerformService.goToPermissionSettingIfDisabled(this);
|
||||
goToAccessibilityPermissionSettingIfDisabled();
|
||||
}
|
||||
|
||||
private void goToAccessibilityPermissionSettingIfDisabled() {
|
||||
if (!AccessibilityServiceUtils.isAccessibilityServiceEnabled(this, ActionPerformService.class)) {
|
||||
new MaterialDialog.Builder(this)
|
||||
.content(R.string.explain_accessibility_permission)
|
||||
.positiveText(R.string.text_go_to_setting)
|
||||
.negativeText(R.string.text_cancel)
|
||||
.onPositive((dialog, which) -> AccessibilityServiceUtils.goToPermissionSetting(this)).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void setUpFileChooser() {
|
||||
mFileChooser = new FileChooser(this);
|
||||
@@ -80,7 +95,7 @@ public class MainActivity extends BaseActivity {
|
||||
private void setUpToolbar() {
|
||||
Toolbar toolbar = $(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
toolbar.setNavigationIcon(R.drawable.script_droid_50);
|
||||
toolbar.setNavigationIcon(R.drawable.ic_script_droid_40);
|
||||
toolbar.setTitle(R.string.app_name);
|
||||
}
|
||||
|
||||
@@ -100,7 +115,7 @@ public class MainActivity extends BaseActivity {
|
||||
new MaterialDialog.Builder(this).title(R.string.text_name)
|
||||
.inputType(InputType.TYPE_CLASS_TEXT)
|
||||
.input(getString(R.string.text_please_input_name), "", (dialog, input) -> {
|
||||
String path = ScriptFile.DEFAULT_FOLDER + input + ".text";
|
||||
String path = ScriptFile.DEFAULT_FOLDER + input + ".js";
|
||||
createScriptFile(input.toString(), path);
|
||||
}).show();
|
||||
}
|
||||
@@ -131,7 +146,6 @@ public class MainActivity extends BaseActivity {
|
||||
private final Map<Integer, Runnable> mOptionActionMap = new MapEntries<Integer, Runnable>()
|
||||
.entry(R.id.action_exit, this::finish)
|
||||
.entry(R.id.action_disable_service, this::disableAccessibilityService)
|
||||
.entry(R.id.action_settings, () -> ActionPerformService.setActions(ActionPerformService.NO_ACTION))
|
||||
.map();
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.stardust.scriptdroid.droid.Droid;
|
||||
@@ -38,9 +39,14 @@ public class ShortcutActivity extends Activity {
|
||||
.fall();
|
||||
}
|
||||
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
finish();
|
||||
}
|
||||
|
||||
private void runScriptFile(String path) {
|
||||
try {
|
||||
Droid.run(this, new File(path));
|
||||
Droid.getInstance().runScriptFile(path);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
|
||||
@@ -1,466 +0,0 @@
|
||||
package com.stardust.scriptdroid.action;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.util.SparseArray;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_FOCUS;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SCROLL_FORWARD;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/21.
|
||||
*/
|
||||
|
||||
public abstract class Action {
|
||||
|
||||
private static WeakReference<Context> mContext;
|
||||
|
||||
public static void setActionContext(Context context) {
|
||||
mContext = new WeakReference<>(context);
|
||||
}
|
||||
|
||||
public abstract boolean perform(AccessibilityNodeInfo rootNodeInfo);
|
||||
|
||||
interface TargetFilter {
|
||||
AccessibilityNodeInfo findTarget(AccessibilityNodeInfo nodeInfo);
|
||||
}
|
||||
|
||||
public static class MultiAction extends Action {
|
||||
|
||||
private List<Action> mActions;
|
||||
private boolean mDependent;
|
||||
|
||||
public MultiAction(List<Action> actions, boolean dependent) {
|
||||
if (actions == null)
|
||||
throw new NullPointerException("actions = null");
|
||||
mDependent = dependent;
|
||||
mActions = actions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean perform(AccessibilityNodeInfo rootNodeInfo) {
|
||||
boolean succeed = true;
|
||||
for (Action action : mActions) {
|
||||
if (!action.perform(rootNodeInfo)) {
|
||||
succeed = false;
|
||||
if (!mDependent)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return succeed;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class TargetAction extends Action {
|
||||
|
||||
static final int TYPE_ID = 1;
|
||||
static final int TYPE_TEXT = 2;
|
||||
static final int TYPE_BOUNDS = 3;
|
||||
static final int TYPE_DESCRIPTION = 4;
|
||||
|
||||
private int mType;
|
||||
private String mString;
|
||||
private Rect mBoundsInScreen;
|
||||
|
||||
private TargetAction(String str, int type) {
|
||||
if (str == null)
|
||||
throw new NullPointerException("str == null");
|
||||
if (type != TYPE_TEXT && type != TYPE_ID && type != TYPE_DESCRIPTION)
|
||||
throw new IllegalArgumentException("type illegal");
|
||||
mString = str;
|
||||
mType = type;
|
||||
}
|
||||
|
||||
private TargetAction(Rect boundsInScreen) {
|
||||
mType = TYPE_BOUNDS;
|
||||
mBoundsInScreen = boundsInScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean perform(AccessibilityNodeInfo rootNodeInfo) {
|
||||
List<AccessibilityNodeInfo> target = null;
|
||||
switch (mType) {
|
||||
case TYPE_TEXT:
|
||||
target = rootNodeInfo.findAccessibilityNodeInfosByText(mString);
|
||||
break;
|
||||
case TYPE_ID:
|
||||
target = rootNodeInfo.findAccessibilityNodeInfosByViewId(mString);
|
||||
break;
|
||||
case TYPE_BOUNDS:
|
||||
AccessibilityNodeInfo nodeInfo = findAccessibilityNodeInfosByBounds(rootNodeInfo, mBoundsInScreen);
|
||||
target = nodeInfo == null ? Collections.EMPTY_LIST : Collections.singletonList(nodeInfo);
|
||||
break;
|
||||
case TYPE_DESCRIPTION:
|
||||
target = findAccessibilityNodeInfosByDescription(rootNodeInfo, mString);
|
||||
}
|
||||
return target != null && perform(target);
|
||||
}
|
||||
|
||||
|
||||
private List<AccessibilityNodeInfo> findAccessibilityNodeInfosByDescription(AccessibilityNodeInfo rootNodeInfo, String description) {
|
||||
if (description.startsWith("edittext") && !description.equals("edittext")) {
|
||||
int i = Integer.parseInt(description.substring(8));
|
||||
return Collections.singletonList(findEditText(rootNodeInfo).get(i));
|
||||
}
|
||||
if (description.equals("edittext")) {
|
||||
return findEditText(rootNodeInfo);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<AccessibilityNodeInfo> findEditText(AccessibilityNodeInfo rootNodeInfo) {
|
||||
if (rootNodeInfo == null) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
if (rootNodeInfo.isEditable()) {
|
||||
return Collections.singletonList(rootNodeInfo);
|
||||
}
|
||||
List<AccessibilityNodeInfo> list = new LinkedList<>();
|
||||
for (int i = 0; i < rootNodeInfo.getChildCount(); i++) {
|
||||
list.addAll(findEditText(rootNodeInfo.getChild(i)));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
//(882, 1722 - 1036, 1876)
|
||||
private AccessibilityNodeInfo findAccessibilityNodeInfosByBounds(AccessibilityNodeInfo root, Rect boundsInScreen) {
|
||||
if (root == null)
|
||||
return null;
|
||||
Rect rect = new Rect();
|
||||
root.getBoundsInScreen(rect);
|
||||
if (rect.equals(boundsInScreen)) {
|
||||
return root;
|
||||
}
|
||||
for (int i = 0; i < root.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo child = root.getChild(i);
|
||||
if (child == null)
|
||||
continue;
|
||||
AccessibilityNodeInfo nodeInfo = findAccessibilityNodeInfosByBounds(child, boundsInScreen);
|
||||
if (nodeInfo != null)
|
||||
return nodeInfo;
|
||||
else
|
||||
child.recycle();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
abstract boolean perform(@NonNull List<AccessibilityNodeInfo> nodeInfoList);
|
||||
}
|
||||
|
||||
public static class SimpleAction extends TargetAction {
|
||||
|
||||
private int mAction;
|
||||
|
||||
SimpleAction(int action, String str, int type) {
|
||||
super(str, type);
|
||||
mAction = action;
|
||||
}
|
||||
|
||||
SimpleAction(int action, Rect boundsInScreen) {
|
||||
super(boundsInScreen);
|
||||
mAction = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean perform(@NonNull List<AccessibilityNodeInfo> nodeInfoList) {
|
||||
for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
|
||||
performAction(nodeInfo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean performAction(AccessibilityNodeInfo nodeInfo) {
|
||||
return nodeInfo.performAction(mAction);
|
||||
}
|
||||
|
||||
void setAction(int action) {
|
||||
mAction = action;
|
||||
}
|
||||
|
||||
int getAction() {
|
||||
return mAction;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimpleFilterAction extends SimpleAction {
|
||||
|
||||
private TargetFilter mFilter;
|
||||
|
||||
SimpleFilterAction(int action, String str, int type, TargetFilter filter) {
|
||||
super(action, str, type);
|
||||
mFilter = filter;
|
||||
}
|
||||
|
||||
SimpleFilterAction(int action, Rect boundsInScreen, TargetFilter filter) {
|
||||
super(action, boundsInScreen);
|
||||
mFilter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean perform(@NonNull List<AccessibilityNodeInfo> nodeInfoList) {
|
||||
boolean performed = false;
|
||||
for (AccessibilityNodeInfo node : nodeInfoList) {
|
||||
node = mFilter.findTarget(node);
|
||||
if (node != null) {
|
||||
performAction(node);
|
||||
performed = true;
|
||||
}
|
||||
}
|
||||
return performed;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface Able {
|
||||
boolean isAble(AccessibilityNodeInfo node);
|
||||
}
|
||||
|
||||
private static final SparseArray<Able> ACTION_ABLE_MAP = new SparseArray<>();
|
||||
|
||||
static {
|
||||
ACTION_ABLE_MAP.put(ACTION_CLICK, new Able() {
|
||||
@Override
|
||||
public boolean isAble(AccessibilityNodeInfo node) {
|
||||
return node.isClickable();
|
||||
}
|
||||
});
|
||||
ACTION_ABLE_MAP.put(ACTION_LONG_CLICK, new Able() {
|
||||
@Override
|
||||
public boolean isAble(AccessibilityNodeInfo node) {
|
||||
return node.isLongClickable();
|
||||
}
|
||||
});
|
||||
ACTION_ABLE_MAP.put(ACTION_FOCUS, new Able() {
|
||||
@Override
|
||||
public boolean isAble(AccessibilityNodeInfo node) {
|
||||
return node.isFocusable();
|
||||
}
|
||||
});
|
||||
ACTION_ABLE_MAP.put(ACTION_SCROLL_FORWARD, new Able() {
|
||||
@Override
|
||||
public boolean isAble(AccessibilityNodeInfo node) {
|
||||
return node.isScrollable();
|
||||
}
|
||||
});
|
||||
ACTION_ABLE_MAP.put(ACTION_SCROLL_BACKWARD, new Able() {
|
||||
@Override
|
||||
public boolean isAble(AccessibilityNodeInfo node) {
|
||||
return node.isScrollable();
|
||||
}
|
||||
});
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
ACTION_ABLE_MAP.put(ACTION_SET_TEXT, new Able() {
|
||||
@Override
|
||||
public boolean isAble(AccessibilityNodeInfo node) {
|
||||
return node.isEditable();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class FindDownwardlyDfsFilterAction extends SimpleFilterAction {
|
||||
|
||||
public static FindDownwardlyDfsFilterAction createActionByBounds(int action, Rect boundInScreen) {
|
||||
Able able = ACTION_ABLE_MAP.get(action);
|
||||
return new FindDownwardlyDfsFilterAction(action, boundInScreen, able);
|
||||
}
|
||||
|
||||
FindDownwardlyDfsFilterAction(int action, Rect boundInScreen, Able able) {
|
||||
super(action, boundInScreen, new FindDownwardlyDfsTargetFilter(able));
|
||||
}
|
||||
|
||||
FindDownwardlyDfsFilterAction(int action, String str, int type, Able able) {
|
||||
super(action, str, type, new FindDownwardlyDfsTargetFilter(able));
|
||||
}
|
||||
|
||||
|
||||
private static class FindDownwardlyDfsTargetFilter implements TargetFilter {
|
||||
Able mAble;
|
||||
|
||||
FindDownwardlyDfsTargetFilter(Able able) {
|
||||
mAble = able;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeInfo findTarget(AccessibilityNodeInfo n) {
|
||||
if (n == null)
|
||||
return null;
|
||||
if (mAble.isAble(n))
|
||||
return n;
|
||||
for (int i = 0; i < n.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo child = n.getChild(i);
|
||||
if (child == null)
|
||||
continue;
|
||||
AccessibilityNodeInfo node = findTarget(child);
|
||||
if (node != null)
|
||||
return node;
|
||||
else
|
||||
child.recycle();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class FindUpwardlyFilterAction extends SimpleFilterAction {
|
||||
|
||||
|
||||
public static FindUpwardlyFilterAction createActionById(int action, String id) {
|
||||
return createActionInner(action, id, TYPE_ID);
|
||||
}
|
||||
|
||||
public static FindUpwardlyFilterAction createActionByText(int action, String text) {
|
||||
return createActionInner(action, text, TYPE_TEXT);
|
||||
}
|
||||
|
||||
public static Action createActionByDescription(int action, String description) {
|
||||
return createActionInner(action, description, TYPE_DESCRIPTION);
|
||||
}
|
||||
|
||||
private static FindUpwardlyFilterAction createActionInner(int action, String str, int type) {
|
||||
Able able = ACTION_ABLE_MAP.get(action);
|
||||
return new FindUpwardlyFilterAction(action, str, type, able);
|
||||
}
|
||||
|
||||
private static class FindUpwardlyTargetFilter implements TargetFilter {
|
||||
Able mAble;
|
||||
|
||||
FindUpwardlyTargetFilter(Able able) {
|
||||
mAble = able;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeInfo findTarget(AccessibilityNodeInfo n) {
|
||||
AccessibilityNodeInfo node = n;
|
||||
while (node != null && !mAble.isAble(node)) {
|
||||
AccessibilityNodeInfo parent = node.getParent();
|
||||
if (node != n) {
|
||||
node.recycle();
|
||||
}
|
||||
node = parent;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
FindUpwardlyFilterAction(int action, String str, int type, Able able) {
|
||||
super(action, str, type, new FindUpwardlyTargetFilter(able));
|
||||
}
|
||||
|
||||
FindUpwardlyFilterAction(int action, Rect boundsInScreen, Able able) {
|
||||
super(action, boundsInScreen, new FindUpwardlyTargetFilter(able));
|
||||
}
|
||||
}
|
||||
|
||||
public static class ScrollAction extends Action {
|
||||
|
||||
public static final int SCROLL_FORWARD = AccessibilityNodeInfo.ACTION_SCROLL_FORWARD;
|
||||
public static final int SCROLL_BACKWARD = AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD;
|
||||
|
||||
private int mScrollAction;
|
||||
private int mTimes;
|
||||
|
||||
public ScrollAction(int scrollAction) {
|
||||
this(scrollAction, 1);
|
||||
}
|
||||
|
||||
public ScrollAction(int scrollAction, int times) {
|
||||
if (scrollAction != SCROLL_BACKWARD && scrollAction != SCROLL_FORWARD)
|
||||
throw new IllegalArgumentException("scrollAction illegal");
|
||||
mScrollAction = scrollAction;
|
||||
mTimes = times;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean perform(AccessibilityNodeInfo rootNodeInfo) {
|
||||
AccessibilityNodeInfo scrollableNodeInfo = findScrollableNodeInfo(rootNodeInfo);
|
||||
if (scrollableNodeInfo == null) {
|
||||
return false;
|
||||
} else {
|
||||
for (int i = 0; i < mTimes; i++) {
|
||||
scrollableNodeInfo.performAction(mScrollAction);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private AccessibilityNodeInfo findScrollableNodeInfo(AccessibilityNodeInfo nodeInfo) {
|
||||
if (nodeInfo == null)
|
||||
return null;
|
||||
if (nodeInfo.isScrollable()) {
|
||||
return nodeInfo;
|
||||
}
|
||||
for (int i = 0; i < nodeInfo.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo node = findScrollableNodeInfo(nodeInfo.getChild(i));
|
||||
if (node != null) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class IntentAction extends Action {
|
||||
|
||||
private final Intent mIntent;
|
||||
|
||||
public IntentAction(Intent intent) {
|
||||
mIntent = intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean perform(AccessibilityNodeInfo rootNodeInfo) {
|
||||
if (mContext == null || mContext.get() == null)
|
||||
return false;
|
||||
mContext.get().startActivity(mIntent);
|
||||
return true;
|
||||
}
|
||||
|
||||
public Intent getIntent() {
|
||||
return mIntent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public static class InputAction extends FindUpwardlyFilterAction {
|
||||
|
||||
private static final Able EDITABLE = new Able() {
|
||||
@Override
|
||||
public boolean isAble(AccessibilityNodeInfo node) {
|
||||
return node.isEditable();
|
||||
}
|
||||
};
|
||||
private String mText;
|
||||
|
||||
public InputAction(String description, String text) {
|
||||
super(ACTION_SET_TEXT, description, TYPE_DESCRIPTION, EDITABLE);
|
||||
mText = text;
|
||||
}
|
||||
|
||||
boolean performAction(AccessibilityNodeInfo nodeInfo) {
|
||||
Bundle arg = new Bundle();
|
||||
arg.putCharSequence(ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, mText);
|
||||
return nodeInfo.performAction(ACTION_SET_TEXT, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
package com.stardust.scriptdroid.action;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.stardust.scriptdroid.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/21.
|
||||
*/
|
||||
|
||||
public class ActionPerformService extends AccessibilityService {
|
||||
|
||||
private static final String TAG = "SettingRunningServiceSS";
|
||||
private static ActionPerformService instance;
|
||||
|
||||
public static void goToPermissionSettingIfDisabled(final Context context) {
|
||||
if (!isAccessibilityServiceEnabled(context, ActionPerformService.class)) {
|
||||
new MaterialDialog.Builder(context)
|
||||
.content(R.string.explain_accessibility_permission)
|
||||
.positiveText(R.string.text_go_to_setting)
|
||||
.negativeText(R.string.text_cancel)
|
||||
.onPositive((dialog, which) -> context.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))).show();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isAccessibilityServiceEnabled(Context context, Class<?> accessibilityService) {
|
||||
ComponentName expectedComponentName = new ComponentName(context, accessibilityService);
|
||||
|
||||
String enabledServicesSetting = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
|
||||
if (enabledServicesSetting == null)
|
||||
return false;
|
||||
|
||||
TextUtils.SimpleStringSplitter colonSplitter = new TextUtils.SimpleStringSplitter(':');
|
||||
colonSplitter.setString(enabledServicesSetting);
|
||||
|
||||
while (colonSplitter.hasNext()) {
|
||||
String componentNameString = colonSplitter.next();
|
||||
ComponentName enabledService = ComponentName.unflattenFromString(componentNameString);
|
||||
|
||||
if (enabledService != null && enabledService.equals(expectedComponentName))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final int STATE_WAIT = 0;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final List<Action> NO_ACTION = Collections.EMPTY_LIST;
|
||||
|
||||
public static final List<Action> actions = new ArrayList<>();
|
||||
private static int state = STATE_WAIT;
|
||||
|
||||
public static boolean assistModeEnable = true;
|
||||
private AccessibilityNodeInfo mLastFocus;
|
||||
|
||||
public static void setActions(Collection<Action> collection) {
|
||||
synchronized (actions) {
|
||||
actions.clear();
|
||||
actions.addAll(collection);
|
||||
state = STATE_WAIT;
|
||||
}
|
||||
}
|
||||
|
||||
public static ActionPerformService getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||
Log.v(TAG, "event type=" + event.getEventType());
|
||||
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
|
||||
performAssistance(event, event.getSource());
|
||||
Log.v(TAG, "rootInActiveWindow = " + nodeInfo);
|
||||
if (nodeInfo == null)
|
||||
return;
|
||||
Log.v(TAG, "state = " + state);
|
||||
Action action = nextAction();
|
||||
if (action == null) {
|
||||
reset();
|
||||
} else if (action.perform(nodeInfo)) {
|
||||
state++;
|
||||
}
|
||||
}
|
||||
|
||||
private void performAssistance(AccessibilityEvent event, AccessibilityNodeInfo nodeInfo) {
|
||||
if (!assistModeEnable || nodeInfo == null)
|
||||
return;
|
||||
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED || event.getEventType() == AccessibilityEvent.TYPE_VIEW_LONG_CLICKED) {
|
||||
nodeInfo.refresh();
|
||||
Log.v(TAG, "click: " + nodeInfo);
|
||||
Rect rect = new Rect();
|
||||
nodeInfo.getBoundsInScreen(rect);
|
||||
String str = rect.toString().replace('-', ',').replace(" ", "");
|
||||
ClipboardManager manager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||||
manager.setPrimaryClip(ClipData.newPlainText("", str));
|
||||
Toast.makeText(this, "id=" + nodeInfo.getViewIdResourceName() + " bounds=" + str, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private int[] findAddress(AccessibilityNodeInfo nodeInfo) {
|
||||
List<Integer> address = new ArrayList<>();
|
||||
AccessibilityNodeInfo lastNodeInfo = null;
|
||||
while (nodeInfo.getParent() != null) {
|
||||
address.add(findPositionInParent(nodeInfo));
|
||||
if (lastNodeInfo != null)
|
||||
lastNodeInfo.recycle();
|
||||
lastNodeInfo = nodeInfo;
|
||||
nodeInfo = nodeInfo.getParent();
|
||||
}
|
||||
int[] array = new int[address.size()];
|
||||
for (int i = 0; i < address.size(); i++) {
|
||||
array[i] = address.get(address.size() - i - 1);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int findPositionInParent(AccessibilityNodeInfo nodeInfo) {
|
||||
AccessibilityNodeInfo parent = nodeInfo.getParent();
|
||||
for (int i = 0; i < parent.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo child = parent.getChild(i);
|
||||
if (child != null && child.equals(nodeInfo)) {
|
||||
parent.recycle();
|
||||
child.recycle();
|
||||
return i;
|
||||
}
|
||||
if (child != null) {
|
||||
child.recycle();
|
||||
}
|
||||
}
|
||||
parent.recycle();
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
state = STATE_WAIT;
|
||||
synchronized (actions) {
|
||||
actions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private Action nextAction() {
|
||||
synchronized (actions) {
|
||||
if (state >= actions.size()) {
|
||||
return null;
|
||||
}
|
||||
return actions.get(state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInterrupt() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected() {
|
||||
Log.v(TAG, "onServiceConnected");
|
||||
Action.setActionContext(this);
|
||||
instance = this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.stardust.scriptdroid.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
|
||||
import com.stardust.scriptdroid.action.Action;
|
||||
import com.stardust.scriptdroid.action.ActionPerformService;
|
||||
import com.stardust.scriptdroid.droid.Interpreter;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/23.
|
||||
*/
|
||||
|
||||
public class ScriptFile {
|
||||
|
||||
public static final String DEFAULT_FOLDER = Environment.getExternalStorageDirectory() + "/脚本/";
|
||||
public String name;
|
||||
|
||||
public String path;
|
||||
|
||||
public ScriptFile(String name, String path) {
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void run(Context context) {
|
||||
Interpreter interpreter = new Interpreter(context);
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
|
||||
List<Action> actions = new ArrayList<>();
|
||||
String line = reader.readLine();
|
||||
while (line != null) {
|
||||
actions.add(interpreter.interpreter(line));
|
||||
line = reader.readLine();
|
||||
}
|
||||
ActionPerformService.setActions(actions);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public File toFile() {
|
||||
return new File(path);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.stardust.scriptdroid.droid;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.stardust.scriptdroid.action.ActionPerformService;
|
||||
import com.stardust.scriptdroid.droid.runtime.DroidRuntime;
|
||||
import com.stardust.scriptdroid.droid.script.GBJDuktapeJavaScriptEngine;
|
||||
import com.stardust.scriptdroid.droid.script.JavaScriptEngine;
|
||||
import com.stardust.scriptdroid.file.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/23.
|
||||
@@ -14,16 +14,48 @@ import java.io.IOException;
|
||||
|
||||
public class Droid {
|
||||
|
||||
public static void run(Context context, File file) {
|
||||
Interpreter interpreter = new Interpreter(context);
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
byte[] bytes = new byte[fis.available()];
|
||||
fis.read(bytes);
|
||||
fis.close();
|
||||
String str = new String(bytes);
|
||||
ActionPerformService.setActions(interpreter.interpreterAll(str));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
private static final DroidRuntime RUNTIME = DroidRuntime.getRuntime();
|
||||
private static final JavaScriptEngine JAVA_SCRIPT_ENGINE = new GBJDuktapeJavaScriptEngine(RUNTIME);
|
||||
private static Droid instance = new Droid();
|
||||
|
||||
protected Droid() {
|
||||
|
||||
}
|
||||
|
||||
public static Droid getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void runScriptFile(File file) {
|
||||
checkFile(file);
|
||||
runScript(FileUtils.readString(file));
|
||||
}
|
||||
|
||||
public void runScriptFile(String path) {
|
||||
runScriptFile(new File(path));
|
||||
}
|
||||
|
||||
public void runScript(String script) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
JAVA_SCRIPT_ENGINE.execute(script);
|
||||
} catch (Exception e) {
|
||||
RUNTIME.toast("错误" + e.getMessage());
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void checkFile(File file) {
|
||||
if (file == null) {
|
||||
throw new NullPointerException("file = null");
|
||||
}
|
||||
if (!file.exists()) {
|
||||
throw new RuntimeException(new FileNotFoundException(file.getAbsolutePath()));
|
||||
}
|
||||
if (!file.canRead()) {
|
||||
throw new RuntimeException("file is not readable: path=" + file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
package com.stardust.scriptdroid.droid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.stardust.scriptdroid.action.Action;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_FOCUS;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SCROLL_FORWARD;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SELECT;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/21.
|
||||
*/
|
||||
|
||||
public class Interpreter {
|
||||
|
||||
interface Filter {
|
||||
boolean filter(String str);
|
||||
}
|
||||
|
||||
private static final Filter FILTER_STRING = new Filter() {
|
||||
@Override
|
||||
public boolean filter(String str) {
|
||||
return str.startsWith("\"") && str.endsWith("\"");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private static final Filter FILTER_ADDRESS = (str) -> str.startsWith("(") && str.endsWith(")");
|
||||
|
||||
private static final Filter FILTER_DESCRIPTION = new Filter() {
|
||||
@Override
|
||||
public boolean filter(String str) {
|
||||
return str.startsWith("[") && str.endsWith("]");
|
||||
}
|
||||
};
|
||||
|
||||
private static final Filter FILTER_ANY = new Filter() {
|
||||
@Override
|
||||
public boolean filter(String str) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private static class StateTree {
|
||||
|
||||
private List<Pair<Filter, StateTree>> mChildren = new LinkedList<>();
|
||||
|
||||
StateTree child(final String str, StateTree child) {
|
||||
return child(new Filter() {
|
||||
@Override
|
||||
public boolean filter(String s) {
|
||||
return str.equals(s);
|
||||
}
|
||||
}, child);
|
||||
}
|
||||
|
||||
StateTree child(Filter filter, StateTree child) {
|
||||
mChildren.add(new Pair<>(filter, child));
|
||||
return this;
|
||||
}
|
||||
|
||||
StateLeaf match(String[] command, int startIndex) {
|
||||
for (Pair<Filter, StateTree> child : mChildren) {
|
||||
if (child.first.filter(command[startIndex])) {
|
||||
return child.second.match(command, startIndex + 1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public StateTree child(String command, final int action) {
|
||||
return child(command, new StateTree()
|
||||
.child(FILTER_STRING, new StateLeaf() {
|
||||
@Override
|
||||
Action handle(String[] command) {
|
||||
return Action.FindUpwardlyFilterAction.createActionByText(action, StringTool.removeDoubleQuotes(command[1]));
|
||||
}
|
||||
})
|
||||
.child(FILTER_DESCRIPTION, new StateLeaf() {
|
||||
@Override
|
||||
Action handle(String[] command) {
|
||||
return Action.FindUpwardlyFilterAction.createActionByDescription(action, StringTool.removeDoubleQuotes(command[1]));
|
||||
}
|
||||
})
|
||||
.child(FILTER_ADDRESS, new StateLeaf() {
|
||||
@Override
|
||||
Action handle(String[] command) {
|
||||
String[] intStrings = StringTool.removeDoubleQuotes(command[1]).split(",");
|
||||
int[] bounds = Stream.of(intStrings).mapToInt(Integer::parseInt).toArray();
|
||||
Rect rect = new Rect(bounds[0], bounds[1], bounds[2], bounds[3]);
|
||||
return Action.FindDownwardlyDfsFilterAction.createActionByBounds(action, rect);
|
||||
}
|
||||
})
|
||||
.child(FILTER_ANY, new StateLeaf() {
|
||||
@Override
|
||||
Action handle(String[] command) {
|
||||
return Action.FindUpwardlyFilterAction.createActionById(action, StringTool.removeDoubleQuotes(command[1]));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private abstract static class StateLeaf extends StateTree {
|
||||
|
||||
abstract Action handle(String[] command);
|
||||
|
||||
StateLeaf match(String[] command, int startIndex) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private StateTree stateTree = new StateTree()
|
||||
.child("click", ACTION_CLICK)
|
||||
.child("longclick", ACTION_LONG_CLICK)
|
||||
.child("focus", ACTION_FOCUS)
|
||||
.child("select", ACTION_SELECT)
|
||||
.child("forward", ACTION_SCROLL_FORWARD)
|
||||
.child("backward", ACTION_SCROLL_BACKWARD)
|
||||
.child("scrollup", new StateLeaf() {
|
||||
@Override
|
||||
Action handle(String[] command) {
|
||||
return new Action.ScrollAction(Action.ScrollAction.SCROLL_BACKWARD);
|
||||
}
|
||||
}).child("scrolldown", new StateLeaf() {
|
||||
@Override
|
||||
Action handle(String[] command) {
|
||||
return new Action.ScrollAction(Action.ScrollAction.SCROLL_FORWARD);
|
||||
}
|
||||
}).child("input", new StateTree().child(FILTER_DESCRIPTION, new StateLeaf() {
|
||||
@Override
|
||||
Action handle(String[] command) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return new Action.InputAction(StringTool.removeDoubleQuotes(command[1]), command[2]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
})).child("open", new StateTree()
|
||||
.child(FILTER_STRING, new StateLeaf() {
|
||||
@Override
|
||||
Action handle(String[] command) {
|
||||
String appName = StringTool.removeDoubleQuotes(command[1]);
|
||||
List<ApplicationInfo> installedApplications = mContext.getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA);
|
||||
for (ApplicationInfo applicationInfo : installedApplications) {
|
||||
if (mContext.getPackageManager().getApplicationLabel(applicationInfo).toString().equals(appName)) {
|
||||
return new Action.IntentAction(mContext.getPackageManager().getLaunchIntentForPackage(applicationInfo.packageName));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}).child(FILTER_ANY, new StateLeaf() {
|
||||
@Override
|
||||
Action handle(String[] command) {
|
||||
return new Action.IntentAction(mContext.getPackageManager().getLaunchIntentForPackage(command[1]));
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
public Interpreter(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public Action interpreterInner(String line) {
|
||||
String[] words = line.split(" ");
|
||||
return stateTree.match(words, 0).handle(words);
|
||||
}
|
||||
|
||||
public Action interpreter(String line) {
|
||||
boolean independent = true;
|
||||
String[] actions = line.split("\\|");
|
||||
if (actions.length == 1) {
|
||||
actions = line.split("\\&");
|
||||
independent = false;
|
||||
}
|
||||
if (actions.length == 1)
|
||||
return interpreterInner(line);
|
||||
List<Action> list = new ArrayList<>(actions.length);
|
||||
for (String action : actions) {
|
||||
list.add(interpreterInner(action));
|
||||
}
|
||||
return new Action.MultiAction(list, independent);
|
||||
}
|
||||
|
||||
|
||||
public List<Action> interpreterAll(String str) {
|
||||
String[] lines = str.split("\n");
|
||||
List<Action> list = new ArrayList<>(lines.length);
|
||||
for (String line : lines) {
|
||||
list.add(interpreter(line));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.stardust.scriptdroid.droid;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/22.
|
||||
*/
|
||||
public class StringTool {
|
||||
public static String removeDoubleQuotes(String str) {
|
||||
return str.substring(1, str.length() - 1);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.stardust.scriptdroid.droid;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/21.
|
||||
*/
|
||||
|
||||
public class SyntaxDefinition {
|
||||
|
||||
/**
|
||||
* click 文本
|
||||
* 点击文本所在区域。e.g. click 朋友圈
|
||||
* click 编号
|
||||
* 点击编号对应区域
|
||||
* longclick 文本
|
||||
*
|
||||
* longclick 编号
|
||||
*
|
||||
* scrollup
|
||||
*
|
||||
* scrolldown
|
||||
*
|
||||
* scrollup 编号 [幅度]
|
||||
*
|
||||
* scrolldown 编号 [幅度]
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package com.stardust.scriptdroid.droid.runtime;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.stardust.scriptdroid.App;
|
||||
import com.stardust.scriptdroid.droid.runtime.action.Action;
|
||||
import com.stardust.scriptdroid.droid.runtime.action.ActionFactory;
|
||||
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformService;
|
||||
import com.stardust.scriptdroid.droid.runtime.action.ActionTarget;
|
||||
import com.stardust.scriptdroid.droid.runtime.api.IDroidRuntime;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class DroidRuntime implements IDroidRuntime {
|
||||
|
||||
private static final String TAG = "DroidRuntime";
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private static DroidRuntime runtime = new DroidRuntime();
|
||||
private boolean mActionPerformResult;
|
||||
private Handler mUIHandler;
|
||||
|
||||
public static DroidRuntime getRuntime() {
|
||||
return runtime;
|
||||
}
|
||||
|
||||
protected DroidRuntime() {
|
||||
mUIHandler = new Handler(App.getApp().getMainLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchPackage(String packageName) {
|
||||
PackageManager packageManager = App.getApp().getPackageManager();
|
||||
App.getApp().startActivity(packageManager.getLaunchIntentForPackage(packageName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch(String packageName, String className) {
|
||||
App.getApp().startActivity(new Intent().setClassName(packageName, className));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchApp(String appName) {
|
||||
PackageManager packageManager = App.getApp().getPackageManager();
|
||||
List<ApplicationInfo> installedApplications = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
|
||||
for (ApplicationInfo applicationInfo : installedApplications) {
|
||||
if (packageManager.getApplicationLabel(applicationInfo).toString().equals(appName)) {
|
||||
launchPackage(applicationInfo.packageName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPackageName(String appName) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionTarget text(String text) {
|
||||
return new ActionTarget.TextActionTarget(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionTarget bounds(int left, int top, int right, int bottom) {
|
||||
return new ActionTarget.BoundsActionTarget(new Rect(left, top, right, bottom));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionTarget editable(int i) {
|
||||
return new ActionTarget.EditableActionTarget(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean click(ActionTarget target) {
|
||||
return performAction(target.createAction(AccessibilityNodeInfo.ACTION_CLICK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean longClick(ActionTarget target) {
|
||||
return performAction(target.createAction(AccessibilityNodeInfo.ACTION_LONG_CLICK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean scrollUp(ActionTarget target) {
|
||||
return performAction(target.createAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean scrollDown(ActionTarget target) {
|
||||
return performAction(target.createAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD));
|
||||
}
|
||||
|
||||
public boolean scrollAllUp(){
|
||||
return performAction(ActionFactory.createScrollAllAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD));
|
||||
}
|
||||
|
||||
public boolean scrollAllDown(){
|
||||
return performAction(ActionFactory.createScrollAllAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean focus(ActionTarget target) {
|
||||
return performAction(target.createAction(AccessibilityNodeInfo.ACTION_FOCUS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean select(ActionTarget target) {
|
||||
return performAction(target.createAction(AccessibilityNodeInfo.ACTION_SELECT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setText(ActionTarget target, String text) {
|
||||
return performAction(target.createAction(AccessibilityNodeInfo.ACTION_SET_TEXT, text));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean paste(ActionTarget target) {
|
||||
return performAction(target.createAction(AccessibilityNodeInfo.ACTION_PASTE));
|
||||
}
|
||||
|
||||
|
||||
private boolean performAction(Action action) {
|
||||
if (ActionPerformService.getInstance() == null) {
|
||||
toast("没有权限");
|
||||
return false;
|
||||
}
|
||||
ActionPerformService.setAction(action);
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
mLock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return mActionPerformResult;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void toast(String text) {
|
||||
mUIHandler.post(() -> Toast.makeText(App.getApp(), text, Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sleep(long millis) {
|
||||
try{
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void notifyActionPerformed(boolean succeed) {
|
||||
mActionPerformResult = succeed;
|
||||
synchronized (mLock) {
|
||||
mLock.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.stardust.scriptdroid.droid.runtime.action;
|
||||
|
||||
import android.util.SparseArray;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.util.SparseArrayEntries;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Able {
|
||||
|
||||
SparseArray<Able> ABLE_MAP = new SparseArrayEntries<Able>()
|
||||
.entry(AccessibilityNodeInfo.ACTION_CLICK, AccessibilityNodeInfo::isClickable)
|
||||
.entry(AccessibilityNodeInfo.ACTION_LONG_CLICK, AccessibilityNodeInfo::isLongClickable)
|
||||
.entry(AccessibilityNodeInfo.ACTION_FOCUS, AccessibilityNodeInfo::isFocusable)
|
||||
.entry(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD, AccessibilityNodeInfo::isScrollable)
|
||||
.entry(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, AccessibilityNodeInfo::isScrollable)
|
||||
.sparseArray();
|
||||
|
||||
boolean isAble(AccessibilityNodeInfo node);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.stardust.scriptdroid.droid.runtime.action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public abstract class Action {
|
||||
|
||||
|
||||
private boolean mPerformUtilSucceed = false;
|
||||
|
||||
public Action(boolean performUtilSucceed) {
|
||||
mPerformUtilSucceed = performUtilSucceed;
|
||||
}
|
||||
|
||||
public Action() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public abstract boolean perform(AccessibilityNodeInfo root);
|
||||
|
||||
public boolean performUtilSucceed() {
|
||||
return mPerformUtilSucceed;
|
||||
}
|
||||
|
||||
public Action setPerformUtilSucceed(boolean performUtilSucceed) {
|
||||
mPerformUtilSucceed = performUtilSucceed;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.stardust.scriptdroid.droid.runtime.action;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.util.MapEntries;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class ActionFactory {
|
||||
|
||||
private static Map<Integer, Object> searchUpAction = new MapEntries<Integer, Object>()
|
||||
.entry(AccessibilityNodeInfo.ACTION_CLICK, null)
|
||||
.entry(AccessibilityNodeInfo.ACTION_LONG_CLICK, null)
|
||||
.entry(AccessibilityNodeInfo.ACTION_SELECT, null)
|
||||
.entry(AccessibilityNodeInfo.ACTION_FOCUS, null)
|
||||
.entry(AccessibilityNodeInfo.ACTION_SELECT, null)
|
||||
.entry(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD, null)
|
||||
.entry(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, null)
|
||||
.map();
|
||||
|
||||
public static Action createActionWithTextFilter(int action, String text) {
|
||||
if (searchUpAction.containsKey(action))
|
||||
return new SearchUpTargetAction(action, new FilterAction.TextFilter(text));
|
||||
else
|
||||
return new DepthFirstSearchTargetAction(action, new FilterAction.TextFilter(text));
|
||||
}
|
||||
|
||||
public static Action createActionWithBoundsFilter(int action, Rect rect) {
|
||||
if (searchUpAction.containsKey(action))
|
||||
return new SearchUpTargetAction(action, new FilterAction.BoundsFilter(rect));
|
||||
else
|
||||
return new DepthFirstSearchTargetAction(action, new FilterAction.BoundsFilter(rect));
|
||||
}
|
||||
|
||||
public static Action createActionWithEditableFilter(int action, int index, final String text) {
|
||||
return new SearchTargetAction(action, new FilterAction.EditableFilter(index)) {
|
||||
|
||||
@Override
|
||||
protected void performAction(AccessibilityNodeInfo node) {
|
||||
Bundle args = new Bundle();
|
||||
args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
|
||||
node.performAction(getAction(), args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Action createScrollAllAction(int action) {
|
||||
return new ScrollAllAction(action);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.stardust.scriptdroid.droid.runtime.action;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.graphics.Rect;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.stardust.scriptdroid.App;
|
||||
import com.stardust.scriptdroid.droid.runtime.DroidRuntime;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/21.
|
||||
*/
|
||||
|
||||
public class ActionPerformService extends AccessibilityService {
|
||||
|
||||
private static final String TAG = "ActionPerformService";
|
||||
private static final String KEY_ASSIST_MODE_ENABLE = "ASSIST_MODE_ENABLE";
|
||||
private static ActionPerformService instance;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final List<Action> NO_ACTION = Collections.EMPTY_LIST;
|
||||
public static final int STATE_INACTIVE = -1;
|
||||
public static final int STATE_PERFORM = 0;
|
||||
|
||||
public static final List<Action> actions = new ArrayList<>();
|
||||
private static int state = STATE_INACTIVE;
|
||||
|
||||
private static boolean assistModeEnable = false;
|
||||
|
||||
|
||||
public static void setActions(Collection<Action> collection) {
|
||||
synchronized (actions) {
|
||||
actions.clear();
|
||||
actions.addAll(collection);
|
||||
state = actions.size() > 0 ? STATE_PERFORM : STATE_INACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAction(Action action) {
|
||||
setActions(Collections.singletonList(action));
|
||||
}
|
||||
|
||||
public static ActionPerformService getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static boolean isAssistModeEnable() {
|
||||
return assistModeEnable;
|
||||
}
|
||||
|
||||
public static void setAssistModeEnable(boolean assistModeEnable) {
|
||||
ActionPerformService.assistModeEnable = assistModeEnable;
|
||||
PreferenceManager.getDefaultSharedPreferences(App.getApp()).edit().putBoolean(KEY_ASSIST_MODE_ENABLE, assistModeEnable).apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||
Log.v(TAG, "onAccessibilityEvent: state=" + state + " event=" + event);
|
||||
performAssistance(event);
|
||||
if (state == STATE_INACTIVE)
|
||||
return;
|
||||
AccessibilityNodeInfo root = getRootInActiveWindow();
|
||||
if (root == null) {
|
||||
Log.v(TAG, "root = null");
|
||||
}
|
||||
Action action = nextAction();
|
||||
if (action == null) {
|
||||
onActionPerformed(true);
|
||||
} else {
|
||||
Log.i(TAG, "perform action:" + action);
|
||||
if (action.perform(root)) {
|
||||
state++;
|
||||
} else if (!action.performUtilSucceed()) {
|
||||
onActionPerformed(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void onActionPerformed(boolean succeed) {
|
||||
state = STATE_INACTIVE;
|
||||
synchronized (actions) {
|
||||
actions.clear();
|
||||
}
|
||||
DroidRuntime.getRuntime().notifyActionPerformed(succeed);
|
||||
}
|
||||
|
||||
private Action nextAction() {
|
||||
synchronized (actions) {
|
||||
if (state >= actions.size()) {
|
||||
return null;
|
||||
}
|
||||
return actions.get(state);
|
||||
}
|
||||
}
|
||||
|
||||
private void performAssistance(AccessibilityEvent event) {
|
||||
if (!assistModeEnable) {
|
||||
return;
|
||||
}
|
||||
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED || event.getEventType() == AccessibilityEvent.TYPE_VIEW_LONG_CLICKED) {
|
||||
AccessibilityNodeInfo nodeInfo = event.getSource();
|
||||
if (nodeInfo == null) {
|
||||
return;
|
||||
}nodeInfo.refresh();
|
||||
Log.v(TAG, "click: " + nodeInfo);
|
||||
Rect rect = new Rect();
|
||||
nodeInfo.getBoundsInScreen(rect);
|
||||
String str = rect.toString().replace('-', ',').replace(" ", "").substring(4);
|
||||
ClipboardManager manager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||||
manager.setPrimaryClip(ClipData.newPlainText("", str));
|
||||
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
|
||||
nodeInfo.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInterrupt() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected() {
|
||||
Log.v(TAG, "onServiceConnected");
|
||||
instance = this;
|
||||
}
|
||||
|
||||
static {
|
||||
assistModeEnable = PreferenceManager.getDefaultSharedPreferences(App.getApp()).getBoolean(KEY_ASSIST_MODE_ENABLE, false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.stardust.scriptdroid.droid.runtime.action;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public interface ActionTarget {
|
||||
|
||||
Action createAction(int action, Object... params);
|
||||
|
||||
class TextActionTarget implements ActionTarget {
|
||||
|
||||
String mText;
|
||||
|
||||
public TextActionTarget(String text) {
|
||||
mText = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action createAction(int action, Object... params) {
|
||||
return ActionFactory.createActionWithTextFilter(action, mText);
|
||||
}
|
||||
}
|
||||
|
||||
class BoundsActionTarget implements ActionTarget {
|
||||
|
||||
Rect mBoundsInRect;
|
||||
|
||||
public BoundsActionTarget(Rect rect) {
|
||||
mBoundsInRect = rect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action createAction(int action, Object... params) {
|
||||
return ActionFactory.createActionWithBoundsFilter(action, mBoundsInRect);
|
||||
}
|
||||
}
|
||||
|
||||
class EditableActionTarget implements ActionTarget {
|
||||
private int mIndex;
|
||||
|
||||
public EditableActionTarget(int index) {
|
||||
mIndex = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action createAction(int action, Object... params) {
|
||||
return ActionFactory.createActionWithEditableFilter(action, mIndex, params[0].toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.stardust.scriptdroid.droid.runtime.action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class DepthFirstSearchTargetAction extends SearchTargetAction {
|
||||
|
||||
|
||||
private Able mAble;
|
||||
|
||||
public DepthFirstSearchTargetAction(int action, Filter filter) {
|
||||
super(action, filter);
|
||||
mAble = Able.ABLE_MAP.get(action);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeInfo searchTarget(AccessibilityNodeInfo n) {
|
||||
if (n == null)
|
||||
return null;
|
||||
if (mAble.isAble(n))
|
||||
return n;
|
||||
for (int i = 0; i < n.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo child = n.getChild(i);
|
||||
if (child == null)
|
||||
continue;
|
||||
AccessibilityNodeInfo node = searchTarget(child);
|
||||
if (node != null)
|
||||
return node;
|
||||
else
|
||||
child.recycle();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.stardust.scriptdroid.droid.runtime.action;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public abstract class FilterAction extends Action {
|
||||
|
||||
|
||||
public interface Filter {
|
||||
|
||||
List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root);
|
||||
}
|
||||
|
||||
public static class TextFilter implements Filter {
|
||||
|
||||
String mText;
|
||||
|
||||
public TextFilter(String text) {
|
||||
mText = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root) {
|
||||
return root.findAccessibilityNodeInfosByText(mText);
|
||||
}
|
||||
}
|
||||
|
||||
public static class BoundsFilter implements Filter {
|
||||
|
||||
Rect mBoundsInScreen;
|
||||
|
||||
public BoundsFilter(Rect boundsInScreen) {
|
||||
mBoundsInScreen = boundsInScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root) {
|
||||
return Collections.singletonList(findAccessibilityNodeInfosByBounds(root));
|
||||
}
|
||||
|
||||
private AccessibilityNodeInfo findAccessibilityNodeInfosByBounds(AccessibilityNodeInfo root) {
|
||||
if (root == null)
|
||||
return null;
|
||||
Rect rect = new Rect();
|
||||
root.getBoundsInScreen(rect);
|
||||
if (rect.equals(mBoundsInScreen)) {
|
||||
return root;
|
||||
}
|
||||
for (int i = 0; i < root.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo child = root.getChild(i);
|
||||
if (child == null)
|
||||
continue;
|
||||
AccessibilityNodeInfo nodeInfo = findAccessibilityNodeInfosByBounds(child);
|
||||
if (nodeInfo != null)
|
||||
return nodeInfo;
|
||||
else
|
||||
child.recycle();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Filter mFilter;
|
||||
|
||||
public FilterAction(Filter filter) {
|
||||
mFilter = filter;
|
||||
}
|
||||
|
||||
public boolean perform(AccessibilityNodeInfo root) {
|
||||
if (root == null)
|
||||
return false;
|
||||
return perform(mFilter.filter(root));
|
||||
}
|
||||
|
||||
public abstract boolean perform(List<AccessibilityNodeInfo> nodes);
|
||||
|
||||
public static class EditableFilter implements Filter {
|
||||
|
||||
private int mIndex;
|
||||
|
||||
public EditableFilter(int index) {
|
||||
mIndex = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root) {
|
||||
List<AccessibilityNodeInfo> editableList = findEditable(root);
|
||||
if (mIndex == -1)
|
||||
return editableList;
|
||||
return Collections.singletonList(editableList.get(mIndex));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<AccessibilityNodeInfo> findEditable(AccessibilityNodeInfo root) {
|
||||
if (root == null) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
if (root.isEditable()) {
|
||||
return Collections.singletonList(root);
|
||||
}
|
||||
List<AccessibilityNodeInfo> list = new LinkedList<>();
|
||||
for (int i = 0; i < root.getChildCount(); i++) {
|
||||
list.addAll(findEditable(root.getChild(i)));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.stardust.scriptdroid.droid.runtime.action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class ScrollAllAction extends Action {
|
||||
|
||||
private int mScrollAction;
|
||||
|
||||
public ScrollAllAction(int scrollAction) {
|
||||
mScrollAction = scrollAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean perform(AccessibilityNodeInfo rootNodeInfo) {
|
||||
AccessibilityNodeInfo scrollableNodeInfo = findScrollableNodeInfo(rootNodeInfo);
|
||||
return scrollableNodeInfo != null && scrollableNodeInfo.performAction(mScrollAction);
|
||||
}
|
||||
|
||||
private AccessibilityNodeInfo findScrollableNodeInfo(AccessibilityNodeInfo nodeInfo) {
|
||||
if (nodeInfo == null)
|
||||
return null;
|
||||
if (nodeInfo.isScrollable()) {
|
||||
return nodeInfo;
|
||||
}
|
||||
for (int i = 0; i < nodeInfo.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo node = findScrollableNodeInfo(nodeInfo.getChild(i));
|
||||
if (node != null) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.stardust.scriptdroid.droid.runtime.action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public abstract class SearchTargetAction extends FilterAction {
|
||||
|
||||
private int mAction;
|
||||
|
||||
public SearchTargetAction(int action, Filter filter) {
|
||||
super(filter);
|
||||
mAction = action;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean perform(List<AccessibilityNodeInfo> nodes) {
|
||||
boolean performed = false;
|
||||
for (AccessibilityNodeInfo node : nodes) {
|
||||
node = searchTarget(node);
|
||||
if (node != null) {
|
||||
performAction(node);
|
||||
performed = true;
|
||||
}
|
||||
}
|
||||
return performed;
|
||||
}
|
||||
|
||||
protected void performAction(AccessibilityNodeInfo node) {
|
||||
node.performAction(mAction);
|
||||
}
|
||||
|
||||
public int getAction(){
|
||||
return mAction;
|
||||
}
|
||||
|
||||
public AccessibilityNodeInfo searchTarget(AccessibilityNodeInfo node) {
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.stardust.scriptdroid.droid.runtime.action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class SearchUpTargetAction extends SearchTargetAction {
|
||||
|
||||
private Able mAble;
|
||||
|
||||
public SearchUpTargetAction(int action, Filter filter) {
|
||||
super(action, filter);
|
||||
mAble = Able.ABLE_MAP.get(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeInfo searchTarget(AccessibilityNodeInfo n) {
|
||||
AccessibilityNodeInfo node = n;
|
||||
while (node != null && !mAble.isAble(node)) {
|
||||
AccessibilityNodeInfo parent = node.getParent();
|
||||
if (node != n) {
|
||||
node.recycle();
|
||||
}
|
||||
node = parent;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.stardust.scriptdroid.droid.runtime.api;
|
||||
|
||||
import com.stardust.scriptdroid.droid.runtime.action.ActionTarget;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
public interface IDroidRuntime {
|
||||
|
||||
void launchPackage(String packageName);
|
||||
|
||||
void launch(String packageName, String className);
|
||||
|
||||
void launchApp(String appName);
|
||||
|
||||
String[] getPackageName(String appName);
|
||||
|
||||
ActionTarget text(String text);
|
||||
|
||||
ActionTarget bounds(int left, int top, int right, int bottom);
|
||||
|
||||
ActionTarget editable(int i);
|
||||
|
||||
boolean click(ActionTarget target);
|
||||
|
||||
boolean longClick(ActionTarget target);
|
||||
|
||||
boolean scrollUp(ActionTarget target);
|
||||
|
||||
boolean scrollDown(ActionTarget target);
|
||||
|
||||
boolean focus(ActionTarget target);
|
||||
|
||||
boolean select(ActionTarget target);
|
||||
|
||||
boolean setText(ActionTarget target, String text);
|
||||
|
||||
boolean paste(ActionTarget target);
|
||||
|
||||
void toast(String text);
|
||||
|
||||
void sleep(long millis);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.stardust.scriptdroid.droid.script;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.squareup.duktape.Duktape;
|
||||
import com.stardust.scriptdroid.droid.runtime.api.IDroidRuntime;
|
||||
|
||||
import static com.stardust.scriptdroid.droid.script.JavaScriptEngine.Init.readInitScript;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class DuktapeJavaScriptEngine implements JavaScriptEngine {
|
||||
|
||||
private static final String TAG = "DuktapeJavaScriptEngine";
|
||||
|
||||
private Duktape mDuktape = Duktape.create();
|
||||
private boolean mRecycled = false;
|
||||
|
||||
public DuktapeJavaScriptEngine(IDroidRuntime runtime) {
|
||||
setRuntime(runtime);
|
||||
execute(readInitScript());
|
||||
}
|
||||
|
||||
public void setRuntime(IDroidRuntime runtime) {
|
||||
set("droid", IDroidRuntime.class, runtime);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void execute(String script) {
|
||||
mDuktape.evaluate(script);
|
||||
}
|
||||
|
||||
public <T> void set(String varName, Class<T> c, T value) {
|
||||
mDuktape.set(varName, c, value);
|
||||
}
|
||||
|
||||
public void recycle() {
|
||||
if (!mRecycled) {
|
||||
mDuktape.close();
|
||||
mRecycled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finalize() throws Throwable {
|
||||
try {
|
||||
if (!mRecycled) {
|
||||
Log.w(TAG, "Not recycled before finalize");
|
||||
recycle();
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.stardust.scriptdroid.droid.script;
|
||||
|
||||
import com.efurture.script.JSTransformer;
|
||||
import com.furture.react.DuktapeEngine;
|
||||
import com.stardust.scriptdroid.App;
|
||||
import com.stardust.scriptdroid.droid.runtime.api.IDroidRuntime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class GBJDuktapeJavaScriptEngine implements JavaScriptEngine {
|
||||
|
||||
|
||||
private DuktapeEngine mDuktapeEngine = new DuktapeEngine();
|
||||
private boolean mRecycled = false;
|
||||
private String init = Init.readInitScript();
|
||||
|
||||
public GBJDuktapeJavaScriptEngine(IDroidRuntime runtime) {
|
||||
setRuntime(runtime);
|
||||
mDuktapeEngine.put("context", App.getApp());
|
||||
execute(init);
|
||||
}
|
||||
|
||||
private void setRuntime(IDroidRuntime runtime) {
|
||||
set("droid", IDroidRuntime.class, runtime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String script) {
|
||||
try {
|
||||
mDuktapeEngine.execute(JSTransformer.parse(new StringReader(script)));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void set(String varName, Class<T> c, T value) {
|
||||
mDuktapeEngine.put(varName, value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.stardust.scriptdroid.droid.script;
|
||||
|
||||
import com.stardust.scriptdroid.App;
|
||||
import com.stardust.scriptdroid.droid.script.file.AssetScript;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public interface JavaScriptEngine {
|
||||
|
||||
void execute(String script);
|
||||
|
||||
<T> void set(String varName, Class<T> c, T value);
|
||||
|
||||
class Init {
|
||||
static String readInitScript() {
|
||||
return AssetScript.read(App.getApp(), "javasccript_engine_init.js");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.stardust.scriptdroid.droid.script.file;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.efurture.script.JSTransformer;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class AssetScript {
|
||||
|
||||
|
||||
|
||||
public static String read(Context context, String fileName){
|
||||
try {
|
||||
InputStream is = context.getAssets().open(fileName);
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
|
||||
BufferedInputStream bufferIn = new BufferedInputStream(is);
|
||||
byte[] buffer = new byte[2048];
|
||||
int length = 0;
|
||||
while ((length = bufferIn.read(buffer)) >= 0){
|
||||
output.write(buffer, 0, length);
|
||||
}
|
||||
String script = output.toString();
|
||||
Log.e("compiled ScriptEngine", script);
|
||||
return script;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String toScript(Context context, String fileName){
|
||||
try {
|
||||
InputStream is = context.getAssets().open(fileName);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is));
|
||||
String script = JSTransformer.parse(br);
|
||||
Log.e("ScriptEngine", script);
|
||||
return script;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String toScript(String fileName){
|
||||
try {
|
||||
InputStream is = new FileInputStream(fileName);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is));
|
||||
String script = JSTransformer.parse(br);
|
||||
Log.e("ScriptEngine", script);
|
||||
return script;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String toScript(InputStream is){
|
||||
try {
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is));
|
||||
String script = JSTransformer.parse(br);
|
||||
Log.e("ScriptEngine", script);
|
||||
return script;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.stardust.scriptdroid.droid.script.file;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
|
||||
import com.stardust.scriptdroid.droid.Droid;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/23.
|
||||
*/
|
||||
|
||||
public class ScriptFile {
|
||||
|
||||
public static final String DEFAULT_FOLDER = Environment.getExternalStorageDirectory() + "/脚本/";
|
||||
public String name;
|
||||
|
||||
public String path;
|
||||
|
||||
public ScriptFile(String name, String path) {
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void run(Context context) {
|
||||
Droid.getInstance().runScriptFile(toFile());
|
||||
}
|
||||
|
||||
public File toFile() {
|
||||
return new File(path);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.stardust.scriptdroid.data;
|
||||
package com.stardust.scriptdroid.droid.script.file;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.stardust.scriptdroid.data;
|
||||
package com.stardust.scriptdroid.droid.script.file;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
@@ -76,4 +76,20 @@ public class FileUtils {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String readString(File file, String encoding) {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
byte[] bytes = new byte[fis.available()];
|
||||
fis.read(bytes);
|
||||
return new String(bytes, encoding);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String readString(File file) {
|
||||
return readString(file, "utf-8");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
package com.stardust.scriptdroid.runningservices;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.usage.UsageStats;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/20.
|
||||
*/
|
||||
|
||||
public class RunningPackageTool {
|
||||
|
||||
|
||||
public static String getRunningPackage(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return getRunningPackageLollipop(context);
|
||||
} else {
|
||||
return getRunningPackageBeforeLollipop(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static String getRunningPackageBeforeLollipop(Context context) {
|
||||
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
@SuppressWarnings("deprecation")
|
||||
ComponentName runningActivity = am.getRunningTasks(1).get(0).topActivity;
|
||||
return runningActivity.getPackageName();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private static String getRunningPackageLollipop(Context context) {
|
||||
List<UsageStats> usageStats = getPastTwoSecondsUsageStats(context);
|
||||
UsageStats latestStats = findLatestUsageStats(usageStats);
|
||||
if (latestStats == null)
|
||||
return null;
|
||||
return latestStats.getPackageName();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private static void goToUsageAccessSettings(Context context) {
|
||||
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private static UsageStats findLatestUsageStats(List<UsageStats> usageStats) {
|
||||
UsageStats latestStats = null;
|
||||
for (UsageStats us : usageStats) {
|
||||
if (latestStats == null || latestStats.getLastTimeUsed() < us.getLastTimeUsed()) {
|
||||
latestStats = us;
|
||||
}
|
||||
}
|
||||
return latestStats;
|
||||
}
|
||||
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<UsageStats> getPastTwoSecondsUsageStats(Context context) {
|
||||
long ts = System.currentTimeMillis();
|
||||
UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
|
||||
List<UsageStats> usageStats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, ts - 2000, ts);
|
||||
if (usageStats == null) {
|
||||
usageStats = Collections.EMPTY_LIST;
|
||||
}
|
||||
return usageStats;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.stardust.scriptdroid.tile;
|
||||
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.TileService;
|
||||
|
||||
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformService;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/26.
|
||||
*/
|
||||
|
||||
public class BoundsAssistEnableTileService extends TileService {
|
||||
|
||||
public void onClick() {
|
||||
ActionPerformService.setAssistModeEnable(!ActionPerformService.isAssistModeEnable());
|
||||
updateTile();
|
||||
}
|
||||
|
||||
private void updateTile() {
|
||||
getQsTile().setState(ActionPerformService.isAssistModeEnable() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
|
||||
getQsTile().updateTile();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onStartListening() {
|
||||
updateTile();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.stardust.scriptdroid.tile;
|
||||
|
||||
import com.stardust.scriptdroid.action.ActionPerformService;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/26.
|
||||
*/
|
||||
|
||||
public class TileService extends android.service.quicksettings.TileService {
|
||||
|
||||
public void onClick(){
|
||||
ActionPerformService.assistModeEnable = !ActionPerformService.assistModeEnable;
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ import android.support.design.widget.Snackbar;
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.stardust.scriptdroid.R;
|
||||
import com.stardust.scriptdroid.ShortcutActivity;
|
||||
import com.stardust.scriptdroid.data.ScriptFile;
|
||||
import com.stardust.scriptdroid.data.ScriptFileList;
|
||||
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
|
||||
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
|
||||
import com.stardust.scriptdroid.shortcut.Shortcut;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -14,8 +14,8 @@ import android.widget.TextView;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.stardust.scriptdroid.R;
|
||||
import com.stardust.scriptdroid.data.ScriptFile;
|
||||
import com.stardust.scriptdroid.data.ScriptFileList;
|
||||
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
|
||||
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
|
||||
import com.stardust.scriptdroid.tool.ClassTool;
|
||||
|
||||
import static com.stardust.scriptdroid.tool.ViewTool.$;
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.stardust.view.accessibility;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/26.
|
||||
*/
|
||||
|
||||
public class AccessibilityNodeInfoReflect {
|
||||
|
||||
private final AccessibilityNodeInfo mAccessibilityNodeInfo;
|
||||
|
||||
public AccessibilityNodeInfoReflect(AccessibilityNodeInfo accessibilityNodeInfo) {
|
||||
mAccessibilityNodeInfo = accessibilityNodeInfo;
|
||||
}
|
||||
|
||||
public void getChildNodeIds() {
|
||||
try {
|
||||
Field mChildNodeIdsField = AccessibilityNodeInfo.class.getDeclaredField("mChildNodeIds");
|
||||
mChildNodeIdsField.setAccessible(true);
|
||||
Object o = mChildNodeIdsField.get(mAccessibilityNodeInfo);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public long getSourceNodeId() {
|
||||
try {
|
||||
Field mSourceNodeIdField = AccessibilityNodeInfo.class.getDeclaredField("mSourceNodeId");
|
||||
mSourceNodeIdField.setAccessible(true);
|
||||
return (long) mSourceNodeIdField.get(mSourceNodeIdField);
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.stardust.view.accessibility;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/26.
|
||||
*/
|
||||
|
||||
public class AccessibilityServiceUtils {
|
||||
|
||||
public static void goToPermissionSetting(Context context) {
|
||||
context.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
|
||||
}
|
||||
|
||||
public static boolean isAccessibilityServiceEnabled(Context context, Class<?> accessibilityService) {
|
||||
ComponentName expectedComponentName = new ComponentName(context, accessibilityService);
|
||||
|
||||
String enabledServicesSetting = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
|
||||
if (enabledServicesSetting == null)
|
||||
return false;
|
||||
|
||||
TextUtils.SimpleStringSplitter colonSplitter = new TextUtils.SimpleStringSplitter(':');
|
||||
colonSplitter.setString(enabledServicesSetting);
|
||||
|
||||
while (colonSplitter.hasNext()) {
|
||||
String componentNameString = colonSplitter.next();
|
||||
ComponentName enabledService = ComponentName.unflattenFromString(componentNameString);
|
||||
|
||||
if (enabledService != null && enabledService.equals(expectedComponentName))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
BIN
app/src/main/res/drawable/ic_script_droid_40.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
@@ -78,7 +78,7 @@
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/sdcard_icon_black"/>
|
||||
android:src="@drawable/ic_sdcard_black"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
android:layout_centerVertical="true"
|
||||
android:contentDescription="@string/app_name"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/robot_icon_green"/>
|
||||
android:src="@drawable/ic_robot_green"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -81,7 +81,7 @@
|
||||
android:layout_height="20dp"
|
||||
android:layout_gravity="center"
|
||||
android:contentDescription="@string/app_name"
|
||||
android:src="@drawable/navigation_show_more_vertical_512"/>
|
||||
android:src="@drawable/ic_navigation_show_more_vertical_512"/>
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<resources>
|
||||
<string name="app_name">免Root脚本精灵</string>
|
||||
<string name="app_name">免Root脚本机器人</string>
|
||||
<string name="action_settings">设置</string>
|
||||
<string name="action_disable_service">关闭服务</string>
|
||||
<string name="text_accesibility_service_description">略略略</string>
|
||||
|
||||
@@ -2,9 +2,6 @@ package com.stardust.scriptdroid;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
@@ -14,6 +11,5 @@ public class ExampleUnitTest {
|
||||
@Test
|
||||
public void testStack() throws Exception {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||