diff --git a/.gitignore b/.gitignore
index 39fb081a..c7ee9658 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,4 @@
.DS_Store
/build
/captures
-.externalNativeBuild
+.externalNativeBuild
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index acf4a50f..6d885aad 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -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')
+
}
diff --git a/app/libs/JSTransformer.jar b/app/libs/JSTransformer.jar
new file mode 100644
index 00000000..b154ed4c
Binary files /dev/null and b/app/libs/JSTransformer.jar differ
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2be358e3..c365a792 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -8,6 +8,7 @@
diff --git a/app/src/main/assets/javasccript_engine_init.js b/app/src/main/assets/javasccript_engine_init.js
new file mode 100644
index 00000000..60d9c6be
--- /dev/null
+++ b/app/src/main/assets/javasccript_engine_init.js
@@ -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);
+}
\ No newline at end of file
diff --git a/app/src/main/assets/test.js b/app/src/main/assets/test.js
new file mode 100644
index 00000000..a340bb34
--- /dev/null
+++ b/app/src/main/assets/test.js
@@ -0,0 +1,3 @@
+
+droid.toast("略略略");
+droid.click(droid.text("哈哈哈"));
\ No newline at end of file
diff --git a/app/src/main/java/com/stardust/scriptdroid/App.java b/app/src/main/java/com/stardust/scriptdroid/App.java
new file mode 100644
index 00000000..1d542898
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/App.java
@@ -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;
+ }
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/MainActivity.java b/app/src/main/java/com/stardust/scriptdroid/MainActivity.java
index 29714dc3..7564c34b 100644
--- a/app/src/main/java/com/stardust/scriptdroid/MainActivity.java
+++ b/app/src/main/java/com/stardust/scriptdroid/MainActivity.java
@@ -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 mOptionActionMap = new MapEntries()
.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();
diff --git a/app/src/main/java/com/stardust/scriptdroid/ShortcutActivity.java b/app/src/main/java/com/stardust/scriptdroid/ShortcutActivity.java
index 4a6bd36f..3508aed6 100644
--- a/app/src/main/java/com/stardust/scriptdroid/ShortcutActivity.java
+++ b/app/src/main/java/com/stardust/scriptdroid/ShortcutActivity.java
@@ -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();
diff --git a/app/src/main/java/com/stardust/scriptdroid/action/Action.java b/app/src/main/java/com/stardust/scriptdroid/action/Action.java
deleted file mode 100644
index a2cfc429..00000000
--- a/app/src/main/java/com/stardust/scriptdroid/action/Action.java
+++ /dev/null
@@ -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 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 mActions;
- private boolean mDependent;
-
- public MultiAction(List 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 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 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 findEditText(AccessibilityNodeInfo rootNodeInfo) {
- if (rootNodeInfo == null) {
- return Collections.EMPTY_LIST;
- }
- if (rootNodeInfo.isEditable()) {
- return Collections.singletonList(rootNodeInfo);
- }
- List 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 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 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 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 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);
- }
- }
-}
diff --git a/app/src/main/java/com/stardust/scriptdroid/action/ActionPerformService.java b/app/src/main/java/com/stardust/scriptdroid/action/ActionPerformService.java
deleted file mode 100644
index c851a2fb..00000000
--- a/app/src/main/java/com/stardust/scriptdroid/action/ActionPerformService.java
+++ /dev/null
@@ -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 NO_ACTION = Collections.EMPTY_LIST;
-
- public static final List actions = new ArrayList<>();
- private static int state = STATE_WAIT;
-
- public static boolean assistModeEnable = true;
- private AccessibilityNodeInfo mLastFocus;
-
- public static void setActions(Collection 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 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;
- }
-
-}
diff --git a/app/src/main/java/com/stardust/scriptdroid/data/ScriptFile.java b/app/src/main/java/com/stardust/scriptdroid/data/ScriptFile.java
deleted file mode 100644
index ef49ddde..00000000
--- a/app/src/main/java/com/stardust/scriptdroid/data/ScriptFile.java
+++ /dev/null
@@ -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 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);
- }
-
-}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/Droid.java b/app/src/main/java/com/stardust/scriptdroid/droid/Droid.java
index 8b9dbfad..cfce138d 100644
--- a/app/src/main/java/com/stardust/scriptdroid/droid/Droid.java
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/Droid.java
@@ -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());
}
}
+
+
}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/Interpreter.java b/app/src/main/java/com/stardust/scriptdroid/droid/Interpreter.java
deleted file mode 100644
index 09466c8f..00000000
--- a/app/src/main/java/com/stardust/scriptdroid/droid/Interpreter.java
+++ /dev/null
@@ -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> 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 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 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 list = new ArrayList<>(actions.length);
- for (String action : actions) {
- list.add(interpreterInner(action));
- }
- return new Action.MultiAction(list, independent);
- }
-
-
- public List interpreterAll(String str) {
- String[] lines = str.split("\n");
- List list = new ArrayList<>(lines.length);
- for (String line : lines) {
- list.add(interpreter(line));
- }
- return list;
- }
-
-}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/StringTool.java b/app/src/main/java/com/stardust/scriptdroid/droid/StringTool.java
deleted file mode 100644
index 06816efe..00000000
--- a/app/src/main/java/com/stardust/scriptdroid/droid/StringTool.java
+++ /dev/null
@@ -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);
- }
-}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/SyntaxDefinition.java b/app/src/main/java/com/stardust/scriptdroid/droid/SyntaxDefinition.java
deleted file mode 100644
index 776012c8..00000000
--- a/app/src/main/java/com/stardust/scriptdroid/droid/SyntaxDefinition.java
+++ /dev/null
@@ -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 编号 [幅度]
- *
- *
- *
- */
-
-}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/DroidRuntime.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/DroidRuntime.java
new file mode 100644
index 00000000..03b2b264
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/DroidRuntime.java
@@ -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 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();
+ }
+ }
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/Able.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/Able.java
new file mode 100644
index 00000000..412e598f
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/Able.java
@@ -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_MAP = new SparseArrayEntries()
+ .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);
+
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/Action.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/Action.java
new file mode 100644
index 00000000..52909c8f
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/Action.java
@@ -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;
+ }
+
+
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionFactory.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionFactory.java
new file mode 100644
index 00000000..611f275b
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionFactory.java
@@ -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 searchUpAction = new MapEntries()
+ .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);
+ }
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionPerformService.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionPerformService.java
new file mode 100644
index 00000000..a4591c94
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionPerformService.java
@@ -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 NO_ACTION = Collections.EMPTY_LIST;
+ public static final int STATE_INACTIVE = -1;
+ public static final int STATE_PERFORM = 0;
+
+ public static final List actions = new ArrayList<>();
+ private static int state = STATE_INACTIVE;
+
+ private static boolean assistModeEnable = false;
+
+
+ public static void setActions(Collection 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);
+ }
+
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionTarget.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionTarget.java
new file mode 100644
index 00000000..55cdcacf
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionTarget.java
@@ -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());
+ }
+ }
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/DepthFirstSearchTargetAction.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/DepthFirstSearchTargetAction.java
new file mode 100644
index 00000000..261f3f33
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/DepthFirstSearchTargetAction.java
@@ -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;
+ }
+
+
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/FilterAction.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/FilterAction.java
new file mode 100644
index 00000000..1b7c5b50
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/FilterAction.java
@@ -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 filter(AccessibilityNodeInfo root);
+ }
+
+ public static class TextFilter implements Filter {
+
+ String mText;
+
+ public TextFilter(String text) {
+ mText = text;
+ }
+
+ @Override
+ public List filter(AccessibilityNodeInfo root) {
+ return root.findAccessibilityNodeInfosByText(mText);
+ }
+ }
+
+ public static class BoundsFilter implements Filter {
+
+ Rect mBoundsInScreen;
+
+ public BoundsFilter(Rect boundsInScreen) {
+ mBoundsInScreen = boundsInScreen;
+ }
+
+ @Override
+ public List 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 nodes);
+
+ public static class EditableFilter implements Filter {
+
+ private int mIndex;
+
+ public EditableFilter(int index) {
+ mIndex = index;
+ }
+
+ @Override
+ public List filter(AccessibilityNodeInfo root) {
+ List editableList = findEditable(root);
+ if (mIndex == -1)
+ return editableList;
+ return Collections.singletonList(editableList.get(mIndex));
+ }
+
+ @SuppressWarnings("unchecked")
+ private List findEditable(AccessibilityNodeInfo root) {
+ if (root == null) {
+ return Collections.EMPTY_LIST;
+ }
+ if (root.isEditable()) {
+ return Collections.singletonList(root);
+ }
+ List list = new LinkedList<>();
+ for (int i = 0; i < root.getChildCount(); i++) {
+ list.addAll(findEditable(root.getChild(i)));
+ }
+ return list;
+ }
+ }
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ScrollAllAction.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ScrollAllAction.java
new file mode 100644
index 00000000..dbbfb956
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ScrollAllAction.java
@@ -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;
+ }
+
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/SearchTargetAction.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/SearchTargetAction.java
new file mode 100644
index 00000000..6e07ec83
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/SearchTargetAction.java
@@ -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 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;
+ }
+
+
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/SearchUpTargetAction.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/SearchUpTargetAction.java
new file mode 100644
index 00000000..117f628c
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/SearchUpTargetAction.java
@@ -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;
+ }
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/api/IDroidRuntime.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/api/IDroidRuntime.java
new file mode 100644
index 00000000..c7b1364f
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/api/IDroidRuntime.java
@@ -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);
+
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/script/DuktapeJavaScriptEngine.java b/app/src/main/java/com/stardust/scriptdroid/droid/script/DuktapeJavaScriptEngine.java
new file mode 100644
index 00000000..59b04fa8
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/script/DuktapeJavaScriptEngine.java
@@ -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 void set(String varName, Class 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();
+ }
+
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/script/GBJDuktapeJavaScriptEngine.java b/app/src/main/java/com/stardust/scriptdroid/droid/script/GBJDuktapeJavaScriptEngine.java
new file mode 100644
index 00000000..884e45a2
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/script/GBJDuktapeJavaScriptEngine.java
@@ -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 void set(String varName, Class c, T value) {
+ mDuktapeEngine.put(varName, value);
+ }
+
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/script/JavaScriptEngine.java b/app/src/main/java/com/stardust/scriptdroid/droid/script/JavaScriptEngine.java
new file mode 100644
index 00000000..53bce244
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/script/JavaScriptEngine.java
@@ -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);
+
+ void set(String varName, Class c, T value);
+
+ class Init {
+ static String readInitScript() {
+ return AssetScript.read(App.getApp(), "javasccript_engine_init.js");
+ }
+ }
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/script/file/AssetScript.java b/app/src/main/java/com/stardust/scriptdroid/droid/script/file/AssetScript.java
new file mode 100644
index 00000000..2e63dd65
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/script/file/AssetScript.java
@@ -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);
+ }
+ }
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/script/file/ScriptFile.java b/app/src/main/java/com/stardust/scriptdroid/droid/script/file/ScriptFile.java
new file mode 100644
index 00000000..e5c9ff97
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/script/file/ScriptFile.java
@@ -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);
+ }
+
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/data/ScriptFileList.java b/app/src/main/java/com/stardust/scriptdroid/droid/script/file/ScriptFileList.java
similarity index 90%
rename from app/src/main/java/com/stardust/scriptdroid/data/ScriptFileList.java
rename to app/src/main/java/com/stardust/scriptdroid/droid/script/file/ScriptFileList.java
index 6cc46d75..ab9fb6dd 100644
--- a/app/src/main/java/com/stardust/scriptdroid/data/ScriptFileList.java
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/script/file/ScriptFileList.java
@@ -1,4 +1,4 @@
-package com.stardust.scriptdroid.data;
+package com.stardust.scriptdroid.droid.script.file;
import java.io.File;
diff --git a/app/src/main/java/com/stardust/scriptdroid/data/SharedPrefScriptFileList.java b/app/src/main/java/com/stardust/scriptdroid/droid/script/file/SharedPrefScriptFileList.java
similarity index 97%
rename from app/src/main/java/com/stardust/scriptdroid/data/SharedPrefScriptFileList.java
rename to app/src/main/java/com/stardust/scriptdroid/droid/script/file/SharedPrefScriptFileList.java
index 694b43eb..fa36f833 100644
--- a/app/src/main/java/com/stardust/scriptdroid/data/SharedPrefScriptFileList.java
+++ b/app/src/main/java/com/stardust/scriptdroid/droid/script/file/SharedPrefScriptFileList.java
@@ -1,4 +1,4 @@
-package com.stardust.scriptdroid.data;
+package com.stardust.scriptdroid.droid.script.file;
import android.content.Context;
import android.content.SharedPreferences;
diff --git a/app/src/main/java/com/stardust/scriptdroid/file/FileUtils.java b/app/src/main/java/com/stardust/scriptdroid/file/FileUtils.java
index eb396ec5..31f1287b 100644
--- a/app/src/main/java/com/stardust/scriptdroid/file/FileUtils.java
+++ b/app/src/main/java/com/stardust/scriptdroid/file/FileUtils.java
@@ -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");
+ }
}
diff --git a/app/src/main/java/com/stardust/scriptdroid/runningservices/RunningPackageTool.java b/app/src/main/java/com/stardust/scriptdroid/runningservices/RunningPackageTool.java
deleted file mode 100644
index 1e58da6f..00000000
--- a/app/src/main/java/com/stardust/scriptdroid/runningservices/RunningPackageTool.java
+++ /dev/null
@@ -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 = 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 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 getPastTwoSecondsUsageStats(Context context) {
- long ts = System.currentTimeMillis();
- UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
- List usageStats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, ts - 2000, ts);
- if (usageStats == null) {
- usageStats = Collections.EMPTY_LIST;
- }
- return usageStats;
- }
-}
diff --git a/app/src/main/java/com/stardust/scriptdroid/tile/BoundsAssistEnableTileService.java b/app/src/main/java/com/stardust/scriptdroid/tile/BoundsAssistEnableTileService.java
new file mode 100644
index 00000000..88478cc3
--- /dev/null
+++ b/app/src/main/java/com/stardust/scriptdroid/tile/BoundsAssistEnableTileService.java
@@ -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();
+ }
+}
diff --git a/app/src/main/java/com/stardust/scriptdroid/tile/TileService.java b/app/src/main/java/com/stardust/scriptdroid/tile/TileService.java
deleted file mode 100644
index ecb234b7..00000000
--- a/app/src/main/java/com/stardust/scriptdroid/tile/TileService.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/ScriptFileOperation.java b/app/src/main/java/com/stardust/scriptdroid/ui/ScriptFileOperation.java
index f6bdf138..824b9e6c 100644
--- a/app/src/main/java/com/stardust/scriptdroid/ui/ScriptFileOperation.java
+++ b/app/src/main/java/com/stardust/scriptdroid/ui/ScriptFileOperation.java
@@ -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;
diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/ScriptListRecyclerView.java b/app/src/main/java/com/stardust/scriptdroid/ui/ScriptListRecyclerView.java
index 449ecb01..81cac3f1 100644
--- a/app/src/main/java/com/stardust/scriptdroid/ui/ScriptListRecyclerView.java
+++ b/app/src/main/java/com/stardust/scriptdroid/ui/ScriptListRecyclerView.java
@@ -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.$;
diff --git a/app/src/main/java/com/stardust/view/accessibility/AccessibilityNodeInfoReflect.java b/app/src/main/java/com/stardust/view/accessibility/AccessibilityNodeInfoReflect.java
deleted file mode 100644
index bc9cfb6a..00000000
--- a/app/src/main/java/com/stardust/view/accessibility/AccessibilityNodeInfoReflect.java
+++ /dev/null
@@ -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;
- }
-
-}
diff --git a/app/src/main/java/com/stardust/view/accessibility/AccessibilityServiceUtils.java b/app/src/main/java/com/stardust/view/accessibility/AccessibilityServiceUtils.java
new file mode 100644
index 00000000..5d8c90d8
--- /dev/null
+++ b/app/src/main/java/com/stardust/view/accessibility/AccessibilityServiceUtils.java
@@ -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;
+ }
+}
diff --git a/app/src/main/res/drawable/navigation_show_more_vertical_512.png b/app/src/main/res/drawable/ic_navigation_show_more_vertical_512.png
similarity index 100%
rename from app/src/main/res/drawable/navigation_show_more_vertical_512.png
rename to app/src/main/res/drawable/ic_navigation_show_more_vertical_512.png
diff --git a/app/src/main/res/drawable/robot_icon_green.png b/app/src/main/res/drawable/ic_robot_green.png
similarity index 100%
rename from app/src/main/res/drawable/robot_icon_green.png
rename to app/src/main/res/drawable/ic_robot_green.png
diff --git a/app/src/main/res/drawable/robot_head.png b/app/src/main/res/drawable/ic_robot_head.png
similarity index 100%
rename from app/src/main/res/drawable/robot_head.png
rename to app/src/main/res/drawable/ic_robot_head.png
diff --git a/app/src/main/res/drawable/ic_script_droid_40.png b/app/src/main/res/drawable/ic_script_droid_40.png
new file mode 100644
index 00000000..c8de8c42
Binary files /dev/null and b/app/src/main/res/drawable/ic_script_droid_40.png differ
diff --git a/app/src/main/res/drawable/sdcard_icon_black.png b/app/src/main/res/drawable/ic_sdcard_black.png
similarity index 100%
rename from app/src/main/res/drawable/sdcard_icon_black.png
rename to app/src/main/res/drawable/ic_sdcard_black.png
diff --git a/app/src/main/res/drawable/pencil_and_paper.png b/app/src/main/res/drawable/pencil_and_paper.png
deleted file mode 100644
index ac57d42b..00000000
Binary files a/app/src/main/res/drawable/pencil_and_paper.png and /dev/null differ
diff --git a/app/src/main/res/drawable/play_blue.png b/app/src/main/res/drawable/play_blue.png
deleted file mode 100644
index 4c830925..00000000
Binary files a/app/src/main/res/drawable/play_blue.png and /dev/null differ
diff --git a/app/src/main/res/drawable/rename.png b/app/src/main/res/drawable/rename.png
deleted file mode 100644
index 6002c9d3..00000000
Binary files a/app/src/main/res/drawable/rename.png and /dev/null differ
diff --git a/app/src/main/res/drawable/robot_head_green.png b/app/src/main/res/drawable/robot_head_green.png
deleted file mode 100644
index 56c82ee4..00000000
Binary files a/app/src/main/res/drawable/robot_head_green.png and /dev/null differ
diff --git a/app/src/main/res/drawable/run.png b/app/src/main/res/drawable/run.png
deleted file mode 100644
index f9910c1d..00000000
Binary files a/app/src/main/res/drawable/run.png and /dev/null differ
diff --git a/app/src/main/res/drawable/script_droid_100.png b/app/src/main/res/drawable/script_droid_100.png
deleted file mode 100644
index a075d63d..00000000
Binary files a/app/src/main/res/drawable/script_droid_100.png and /dev/null differ
diff --git a/app/src/main/res/drawable/script_droid_50.png b/app/src/main/res/drawable/script_droid_50.png
deleted file mode 100644
index 0f991ebe..00000000
Binary files a/app/src/main/res/drawable/script_droid_50.png and /dev/null differ
diff --git a/app/src/main/res/drawable/shortcut_icon.png b/app/src/main/res/drawable/shortcut_icon.png
deleted file mode 100644
index 6c51f31f..00000000
Binary files a/app/src/main/res/drawable/shortcut_icon.png and /dev/null differ
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 0c0fa597..2c90e0f3 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -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"/>
+ android:src="@drawable/ic_robot_green"/>
+ android:src="@drawable/ic_navigation_show_more_vertical_512"/>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 21079a73..2903c800 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,5 +1,5 @@
- 免Root脚本精灵
+ 免Root脚本机器人
设置
关闭服务
略略略
diff --git a/app/src/test/java/com/stardust/scriptdroid/ExampleUnitTest.java b/app/src/test/java/com/stardust/scriptdroid/ExampleUnitTest.java
index 0dbcd093..86168640 100644
--- a/app/src/test/java/com/stardust/scriptdroid/ExampleUnitTest.java
+++ b/app/src/test/java/com/stardust/scriptdroid/ExampleUnitTest.java
@@ -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 {
-
}
}
\ No newline at end of file