diff --git a/.idea/misc.xml b/.idea/misc.xml index 274735b3..c69e71bb 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -48,7 +48,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 2a46fe0a..37d1004d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "com.stardust.scriptdroid" minSdkVersion 17 targetSdkVersion 23 - versionCode 153 - versionName "2.0.15 Beta" + versionCode 154 + versionName "2.0.15 Alpha2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true ndk { diff --git a/app/src/main/assets/help/documentation/文件读写.md b/app/src/main/assets/help/documentation/文件读写.md index 052c9177..e26e0a38 100644 --- a/app/src/main/assets/help/documentation/文件读写.md +++ b/app/src/main/assets/help/documentation/文件读写.md @@ -29,7 +29,7 @@ 返回路径path是否是文件夹。 -### files.isDirEmpty(path) +### files.isEmptyDir(path) * path \ 路径 返回文件夹path是否为空文件夹。如果该路径并非文件夹,则直接返回false。 diff --git a/app/src/main/assets/js/functions.json b/app/src/main/assets/js/functions.json index 71bf1ec8..3a6c03dc 100644 --- a/app/src/main/assets/js/functions.json +++ b/app/src/main/assets/js/functions.json @@ -1,62 +1,305 @@ -[ "toast", - "app", - "launchPackage", - "launch", - "launchApp", - "getPackageName", - "openAppSetting", - "sleep", - "isStopped", - "notStopped", - "stop", - "console", - "log", - "print", - "err", - "openConsole", - "clearConsole", - "shell", - "currentPackage", - "currentActivity", - "back", - "home", - "powerDialog", - "notifications", - "quickSettings", - "recents", - "splitScreen", - "click", - "longClick", - "scrollDown", - "scrollUp", - "input", - "setClip", - "SetScreenMetrics", - "Tap", - "Swipe", - "Screencap", - "KeyCode", - "Home", - "Back", - "Power", - "Up", - "Down", - "Left", - "Right", - "OK", - "VolumeUp", - "VolumeDown", - "Menu", - "Camera", - "Text", - "selector", - "open", - "newInjectableWebClient", - "newInjectableWebView", - "dialogs", - "rawInput", - "input", - "alert", - "confirm", - "prompt" -] \ No newline at end of file +{ + "global" : { + "modules": [ + "app", + "automator", + "console", + "dialogs", + "events", + "images", + "files", + "timers", + "ui" + ], + "app": [ + "launchPackage", + "launch", + "launchApp", + "getPackageName", + "openAppSetting" + ], + "automator": [ + "click", + "longClick", + "press", + "swipe", + "gesture", + "gestures", + "gestureAsync", + "gesturesAsync", + "scrollDown", + "scrollUp", + "input", + "setText" + ], + "console": [ + "print", + "log", + "err", + "openConsole", + "clearConsole" + ], + "dialogs":[ + "rawInput", + "input", + "alert", + "confirm", + "prompt" + ], + "images": [ + "requestScreenCapture", + "captureScreen", + "findColor", + "findColorInRegion", + "findColorEquals" + ], + "files": [ + "open" + ], + "selector": [ + "id", + "idContains", + "idStartsWith", + "idEndsWith", + "idMatches", + "text", + "textContains", + "textStartsWith", + "textEndsWith", + "textMatches", + "desc", + "descContains", + "descStartsWith", + "descEndsWith", + "descMatches", + "className", + "classNameContains", + "classNameStartsWith", + "classNameEndsWith", + "classNameMatches", + "packageName", + "packageNameContains", + "packageNameStartsWith", + "packageNameEndsWith", + "packageNameMatches", + "bounds", + "boundsInside", + "boundsContains", + "drawingOrder", + "checkable", + "checked", + "focusable", + "focused", + "visibleToUser", + "accessibilityFocused", + "selected", + "clickable", + "longClickable", + "enabled", + "password", + "scrollable", + "editable", + "contentInvalid", + "contextClickable", + "multiLine", + "dismissable", + "checkable", + "checked", + "focusable", + "focused", + "visibleToUser", + "accessibilityFocused", + "selected", + "clickable", + "longClickable", + "enabled", + "password", + "scrollable", + "editable", + "contentInvalid", + "contextClickable", + "multiLine", + "dismissable" + ], + "shell": [ + "SetScreenMetrics", + "Tap", + "Swipe", + "Screencap", + "KeyCode", + "Home", + "Back", + "Power", + "Up", + "Down", + "Left", + "Right", + "OK", + "VolumeUp", + "VolumeDown", + "Menu", + "Camera", + "Text" + ], + "timers": [ + "loop", + "setTimeout", + "clearTimeout", + "setInterval", + "clearInterval", + "setImmediate", + "clearImmediate" + ], + "web": [ + "newInjectableWebClient", + "newInjectableWebView" + ], + "general": [ + "toast", + "toastLog", + "sleep", + "isStopped", + "notStopped", + "exit", + "setClip", + "getClip", + "currentPackage", + "currentActivity", + "waitForActivity", + "waitForPackage", + "setScreenMetrics" + ], + "variables": [ + "context", + "activity" + ] + }, + "app": [ + "uninstall", + "viewFile", + "editFile", + "openUrl", + "launchPackage", + "launch", + "launchApp", + "getPackageName", + "openAppSetting" + ], + "automator": [ + "click", + "longClick", + "press", + "swipe", + "gesture", + "gestures", + "gestureAsync", + "gesturesAsync", + "scrollDown", + "scrollUp", + "input", + "setText" + ], + "console": [ + "show", + "hide", + "clear", + "verbose", + "print", + "info", + "log", + "warn", + "error", + "assert" + ], + "dialogs":[ + "select", + "singleChoice", + "multiChoice", + "rawInput", + "input", + "alert", + "confirm", + "prompt" + ], + "images": [ + "saveImage", + "pixel", + "read", + "requestScreenCapture", + "captureScreen", + "findColor", + "findColorInRegion", + "findColorEquals" + ], + "colors": [ + "red", + "green", + "blue", + "alpha", + "toString", + "rgb", + "argb" + ], + "events": [ + "emitter", + "observeKey", + "observeTouch", + "onKeyDown", + "onKeyUp", + "onceKeyDown", + "onceKeyUp", + "removeAllKeyDownListeners", + "removeAllKeyUpListeners", + "onTouch", + "removeAllTouchListeners", + "getTouchEventTimeout", + "setTouchEventTimeout", + "on", + "once", + "emit", + "getListeners", + "addListener", + "eventNames", + "listenerCount", + "listeners", + "prependListener", + "prependOnceListener", + "removeAllListeners", + "removeAllListeners", + "removeListener", + "setMaxListeners", + "getMaxListeners", + "defaultMaxListeners" + ], + "files": [ + "open", + "isFile", + "isDir", + "isEmptyDir", + "join", + "create", + "createIfNotExists", + "exists", + "ensureDir", + "read", + "write", + "copy", + "rename", + "renameWithoutExtension", + "getName", + "getExtension", + "remove", + "removeDir", + "getSdcardPath", + "listDir" + ], + "timers": [ + "loop", + "setTimeout", + "clearTimeout", + "setInterval", + "clearInterval", + "setImmediate", + "clearImmediate" + ] +} diff --git a/app/src/main/java/com/stardust/scriptdroid/Pref.java b/app/src/main/java/com/stardust/scriptdroid/Pref.java index e1fd28ca..492cad14 100644 --- a/app/src/main/java/com/stardust/scriptdroid/Pref.java +++ b/app/src/main/java/com/stardust/scriptdroid/Pref.java @@ -8,6 +8,8 @@ import com.stardust.autojs.runtime.api.AutomatorConfig; import com.stardust.automator.AccessibilityEventCommandHost; import com.stardust.scriptdroid.autojs.AutoJs; +import org.mozilla.javascript.NativeArray; + import java.util.concurrent.TimeUnit; /** @@ -143,8 +145,6 @@ public class Pref { } def().edit().putLong(KEY_LAST_SHOW_AD_MILLIS, System.currentTimeMillis()).apply(); return true; - case "Off": - return false; } return true; } diff --git a/app/src/main/java/com/stardust/scriptdroid/tool/GsonUtils.java b/app/src/main/java/com/stardust/scriptdroid/tool/GsonUtils.java new file mode 100644 index 00000000..a41e9d50 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/tool/GsonUtils.java @@ -0,0 +1,32 @@ +package com.stardust.scriptdroid.tool; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Stardust on 2017/7/21. + */ + +public class GsonUtils { + + public static List toReservedStringList(JsonElement element) { + JsonArray array = element.getAsJsonArray(); + List list = new ArrayList<>(array.size()); + for (int i = array.size() - 1; i >= 0; i--) { + list.add(array.get(i).getAsString()); + } + return list; + } + + public static List toStringList(JsonElement element) { + JsonArray array = element.getAsJsonArray(); + List list = new ArrayList<>(array.size()); + for (int i = 0; i < array.size(); i++) { + list.add(array.get(i).getAsString()); + } + return list; + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/CodeCompletion.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/CodeCompletion.java index 49c70492..da74edb0 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/CodeCompletion.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/CodeCompletion.java @@ -1,22 +1,20 @@ package com.stardust.scriptdroid.ui.edit.completion; -import android.content.Context; +import android.support.annotation.NonNull; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; -import com.google.gson.Gson; -import com.stardust.pio.PFile; -import com.stardust.pio.UncheckedIOException; -import com.stardust.scriptdroid.App; import com.stardust.scriptdroid.Pref; import com.jecelyin.editor.v2.core.widget.TextView; -import java.io.IOException; -import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.TreeSet; @@ -48,19 +46,26 @@ public class CodeCompletion implements TextWatcher { } @Override - public int compareTo(CodeCompletionItem o) { + public int compareTo(@NonNull CodeCompletionItem o) { return mDisplayText.compareTo(o.mDisplayText); } } interface OnCodeCompletionChangeListener { - void OnCodeCompletionChange(Collection... list); + void OnCodeCompletionChange(@NonNull Collection list1, @NonNull Collection list2); } private static final String TAG = "CodeCompletion"; private OnCodeCompletionChangeListener mOnCodeCompletionChangeListener; private TextView mEditText; + private static final List KEYWORDS = Arrays.asList("arguments", "break", "case", "catch", "class", "continue", "default", "do", "else", "eval", "export", "false", "for", "function", "if", "import", "in", "int", "new", "null", "package", "return", "switch", "this", "throw", "throws", "true", "try", "typeof", "var", "volatile", "while", "with", "Array", "Date", "hasOwnProperty", "Infinity", "isFinite", "isNaN", "isPrototypeOf", "length", "Math", "NaN", "name", "Number", "Object", "prototype", "String", "toString", "undefined", "valueOf"); + private static final int KEY_WORD_LENGTH_MAX = 15; + + + private List mGlobal = Collections.emptyList(); + private Map> mVariableProperties = new HashMap<>(); + public CodeCompletion(OnCodeCompletionChangeListener listener) { mOnCodeCompletionChangeListener = listener; @@ -71,6 +76,14 @@ public class CodeCompletion implements TextWatcher { mEditText.addTextChangedListener(this); } + public void setGlobal(List functions) { + mGlobal = functions; + } + + public void setVariableProperties(Map> variableProperties) { + mVariableProperties = variableProperties; + } + @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @@ -82,60 +95,94 @@ public class CodeCompletion implements TextWatcher { @Override public void afterTextChanged(Editable s) { int position = mEditText.getSelectionStart(); - String str = parseWordBefore(s, position); - if (!TextUtils.isEmpty(str)) - searchCodeCompletion(str); - else - mOnCodeCompletionChangeListener.OnCodeCompletionChange(DEFAULT_CODE_COMPLETION_LIST); + + String[] str = parseWordBefore(s, position); + if (str != null) { + if (str[1] != null) { + searchCodeCompletionForVariable(str[0], str[1]); + } else { + searchCodeCompletionForGlobal(str[0]); + } + } else { + mOnCodeCompletionChangeListener.OnCodeCompletionChange(DEFAULT_CODE_COMPLETION_LIST, Collections.emptyList()); + } } - private String parseWordBefore(Editable s, int position) { + private String[] parseWordBefore(Editable s, int position) { + int i; + for (i = position - 1; i >= 0; i--) { + if (position - i > KEY_WORD_LENGTH_MAX) { + return null; + } + char c = s.charAt(i); + if (c == '.') { + return new String[]{parseWordBeforeDot(s, i), s.subSequence(i + 1, position).toString()}; + } + if (!Character.isLetter(s.charAt(i))) { + if (i < position - 1) { + return new String[]{s.subSequence(i + 1, position).toString(), null}; + } else { + return null; + } + } + } + return null; + } + + private String parseWordBeforeDot(Editable s, int position) { int i; for (i = position - 1; i >= 0; i--) { if (position - i > KEY_WORD_LENGTH_MAX) { return null; } if (!Character.isLetter(s.charAt(i))) { - break; + if (i < position - 1) { + return s.subSequence(i + 1, position).toString(); + } else { + return null; + } } } - if (i < position - 1) { - return s.subSequence(i + 1, position).toString(); - } return null; } - private static final String[] KEYWORDS = {"arguments", "break", "case", "catch", "class", "continue", "default", "do", "else", "eval", "export", "false", "for", "function", "if", "import", "in", "int", "new", "null", "package", "return", "switch", "this", "throw", "throws", "true", "try", "typeof", "var", "volatile", "while", "with", "Array", "Date", "hasOwnProperty", "Infinity", "isFinite", "isNaN", "isPrototypeOf", "length", "Math", "NaN", "name", "Number", "Object", "prototype", "String", "toString", "undefined", "valueOf"}; - private static final int KEY_WORD_LENGTH_MAX = 15; - - public void setFunctions(String[] functions) { - mFunctions = functions; - } - - private String[] mFunctions = new String[0]; - - private boolean searchCodeCompletion(String str) { + private boolean searchCodeCompletionForGlobal(String str) { Collection c = searchWordCompletion(str); - c.addAll(searchCodeCompletion(str, mFunctions)); + c.addAll(searchCodeCompletion(str, mGlobal)); c.addAll(searchKeyWordCompletion(str)); if (c.size() > 0) { mOnCodeCompletionChangeListener.OnCodeCompletionChange(c, DEFAULT_CODE_COMPLETION_LIST); return true; } else { - mOnCodeCompletionChangeListener.OnCodeCompletionChange(DEFAULT_CODE_COMPLETION_LIST); + mOnCodeCompletionChangeListener.OnCodeCompletionChange(DEFAULT_CODE_COMPLETION_LIST, Collections.emptyList()); return false; } } + private boolean searchCodeCompletionForVariable(String variable, String str) { + List properties = getPropertiesForVariable(variable); + Collection c = searchCodeCompletion(str, properties); + mOnCodeCompletionChangeListener.OnCodeCompletionChange(c, Collections.emptyList()); + return !c.isEmpty(); + } + + private List getPropertiesForVariable(String str) { + List properties = mVariableProperties.get(str); + if (properties == null) + return Collections.emptyList(); + return properties; + } + + private Collection searchWordCompletion(String str) { if (mEditText.getEditableText().length() < Pref.getMaxTextLengthForCodeCompletion()) { - return searchCodeCompletion(str, splitWord(mEditText.getEditableText().toString())); + return searchCodeCompletion(str, Arrays.asList(splitWord(mEditText.getEditableText().toString()))); } return new TreeSet<>(); } - private Collection searchCodeCompletion(String str, String[] words) { + private Collection searchCodeCompletion(String str, Iterable words) { Set set = new TreeSet<>(); for (String word : words) { // TODO: 2017/2/18 优化 字典树 diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/InputMethodEnhanceBar.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/InputMethodEnhanceBar.java index ffe79afe..72c520f9 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/InputMethodEnhanceBar.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/InputMethodEnhanceBar.java @@ -3,8 +3,8 @@ package com.stardust.scriptdroid.ui.edit.completion; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.LayoutInflater; @@ -15,21 +15,27 @@ import android.widget.Toast; import android.workground.WrapContentLinearLayoutManager; import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.stardust.pio.UncheckedIOException; -import com.stardust.scriptdroid.App; import com.stardust.scriptdroid.R; -import com.stardust.scriptdroid.autojs.AutoJs; +import com.stardust.scriptdroid.tool.GsonUtils; +import com.stardust.util.UnderuseExecutors; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Created by Stardust on 2017/2/17. */ +// TODO: 2017/7/21 refactor public class InputMethodEnhanceBar extends RecyclerView implements CodeCompletion.OnCodeCompletionChangeListener { @@ -48,6 +54,14 @@ public class InputMethodEnhanceBar extends RecyclerView implements CodeCompletio init(); } + @Override + public void OnCodeCompletionChange(@NonNull Collection list1, @NonNull Collection list2) { + mCodeCompletionList.clear(); + mCodeCompletionList.addAll(list1); + mCodeCompletionList.addAll(list2); + getAdapter().notifyDataSetChanged(); + } + public interface EditTextBridge { void appendText(CharSequence text); @@ -56,6 +70,9 @@ public class InputMethodEnhanceBar extends RecyclerView implements CodeCompletio com.jecelyin.editor.v2.core.widget.TextView getEditText(); } + private static List global; + private static Map> variables = new HashMap<>(); + EditTextBridge mEditTextBridge; private CodeCompletion mCodeCompletion = new CodeCompletion(this); private List mCodeCompletionList = new ArrayList<>(); @@ -83,7 +100,16 @@ public class InputMethodEnhanceBar extends RecyclerView implements CodeCompletio private void init() { setAdapter(new CodeCompletionAdapter()); setLayoutManager(new WrapContentLinearLayoutManager(getContext(), HORIZONTAL, false)); - mCodeCompletion.setFunctions(readFunctions(getContext(), "js/functions.json")); + if (global == null) { + UnderuseExecutors.execute(new Runnable() { + @Override + public void run() { + readCompletions(getContext(), "js/functions.json"); + mCodeCompletion.setGlobal(global); + mCodeCompletion.setVariableProperties(variables); + } + }); + } } public void setEditTextBridge(EditTextBridge editTextBridge) { @@ -91,16 +117,6 @@ public class InputMethodEnhanceBar extends RecyclerView implements CodeCompletio mCodeCompletion.setEditText(mEditTextBridge.getEditText()); } - @Override - public void OnCodeCompletionChange(Collection... lists) { - mCodeCompletionList.clear(); - for (Collection list : lists) { - mCodeCompletionList.addAll(list); - } - getAdapter().notifyDataSetChanged(); - - } - private class CodeCompletionAdapter extends RecyclerView.Adapter { @@ -129,13 +145,29 @@ public class InputMethodEnhanceBar extends RecyclerView implements CodeCompletio } } - private static String[] readFunctions(Context context, String path) { - Gson gson = new Gson(); + private static void readCompletions(Context context, String path) { try { - return gson.fromJson(new InputStreamReader(context.getAssets().open(path)), String[].class); + JsonParser parser = new JsonParser(); + JsonObject object = parser.parse(new InputStreamReader(context.getAssets().open(path))).getAsJsonObject(); + InputMethodEnhanceBar.global = readGlobal(object.remove("global").getAsJsonObject()); + readModules(object); } catch (IOException e) { throw new UncheckedIOException(e); } } + private static void readModules(JsonObject object) { + for (Map.Entry module : object.entrySet()) { + variables.put(module.getKey(), GsonUtils.toStringList(module.getValue())); + } + } + + private static List readGlobal(JsonObject global) { + List globalFunctions = new ArrayList<>(); + for (Map.Entry moduleGlobal : global.entrySet()) { + globalFunctions.addAll(GsonUtils.toStringList(moduleGlobal.getValue())); + } + return globalFunctions; + } + } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ab5a457a..517fee27 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -228,13 +228,11 @@ 默认 每天显示一次 - 关闭广告 Default OncePerDay - Off diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/UiSelector.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/UiSelector.java index d09a1060..5f7983da 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/UiSelector.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/UiSelector.java @@ -20,6 +20,8 @@ import com.stardust.automator.filter.DfsFilter; import com.stardust.util.DeveloperUtils; import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator; +import org.mozilla.javascript.NativeArray; + import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS; import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ARGUMENT_COLUMN_INT; import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ARGUMENT_PROGRESS_VALUE; @@ -58,21 +60,6 @@ import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat. public class UiSelector extends UiGlobalSelector { - @Deprecated - private class FindCommand extends AccessibilityEventCommandHost.AbstractCommand { - - UiObjectCollection result; - - @Override - public void execute(AccessibilityService service, AccessibilityEvent event) { - AccessibilityNodeInfo root = service.getRootInActiveWindow(); - if (root != null) { - result = findOf(UiObject.createRoot(root, mAllocator)); - } - } - - } - private static final String TAG = "UiSelector"; private AccessibilityBridge mAccessibilityBridge; diff --git a/automator/src/main/java/com/stardust/automator/UiObjectCollection.java b/automator/src/main/java/com/stardust/automator/UiObjectCollection.java index 1d6f7015..4f89a4e0 100644 --- a/automator/src/main/java/com/stardust/automator/UiObjectCollection.java +++ b/automator/src/main/java/com/stardust/automator/UiObjectCollection.java @@ -238,4 +238,5 @@ public class UiObjectCollection { public boolean nonEmpty() { return size() != 0; } + } diff --git a/common/src/main/java/com/stardust/pio/PFile.java b/common/src/main/java/com/stardust/pio/PFile.java index c474f088..e1617dd2 100644 --- a/common/src/main/java/com/stardust/pio/PFile.java +++ b/common/src/main/java/com/stardust/pio/PFile.java @@ -322,7 +322,7 @@ public class PFile { return new File(path).isDirectory(); } - public static boolean isDirEmpty(String path) { + public static boolean isEmptyDir(String path) { File file = new File(path); return file.isDirectory() && file.list().length == 0; }