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