diff --git a/Readme.md b/Readme.md index 39c99621..0ac1cc14 100644 --- a/Readme.md +++ b/Readme.md @@ -1,19 +1,31 @@ -# AutoJs +# Auto.js ## 简介 -一个**不需要Root权限**的类似按键精灵的自动操作软件,可以实现自动点击、滑动、输入文字、打开应用等。 +一个主要由无障碍服务实现的**不需要Root权限**的类似按键精灵的自动操作软件,可以实现自动点击、滑动、输入文字、打开应用等。 同时有[Sublime Text 插件](https://github.com/hyb1996/AutoJs-Sublime-Plugin)可提供基础的在桌面开发的功能。 下载地址:[酷安](http://www.coolapk.com/apk/com.stardust.scriptdroid) -特性: +### 特性 * 简单易用的自动操作函数 * 悬浮窗录制和运行 -* 丰富的文档、教程与示例 * 更专业&强大的选择器API,提供对屏幕上的控件的寻找、遍历、获取信息、操作等。类似于Google的UI测试框架UiAutomator,您也可以把他当做移动版UI测试框架使用 * 采用JavaScript为脚本语言,支持简单的代码补全。您也可以把他当作简便的JavaScript IDE使用 * 带有界面分析工具,类似Android Studio的LayoutInspector,可以分析界面层次和范围、获取界面上的控件信息 -* 支持使用Root权限以提供更强大的屏幕点击、滑动、录制功能和运行shell命令 +* 支持使用Root权限以提供更强大的屏幕点击、滑动、录制功能和运行shell命令。录制录制可产生js文件或二进制文件,录制动作的回放比较流畅 +* 提供截取屏幕、保存截图、图片找色等函数,可进行简单的游戏脚本制作;未来将加入找图功能 +* 与Python类似的文件处理API,以及更多日常工具函数 +* 可以用e4x编写简单的界面,并且未来将加入打包为独立应用功能,可用于制作简单的应用 + +### 文档 +可在[这里](https://github.com/hyb1996/NoRootScriptDroid/tree/master/app/src/main/assets/help/documentation)查看在线文档,或者在应用内帮助页面查看。 + +目前文档不是很完善。 + +### 示例 +可在[这里](https://github.com/hyb1996/NoRootScriptDroid/tree/master/app/src/main/assets/sample)查看一些示例,或者直接在应用内查看和运行。 + +### 截图 ![screen-capture1](https://raw.githubusercontent.com/hyb1996/NoRootScriptDroid/master/screen-captures/ss01.png) @@ -27,6 +39,17 @@ ![screen-capture5](https://raw.githubusercontent.com/hyb1996/NoRootScriptDroid/master/screen-captures/ss06.png) +### Todo + +* 脚本社区或脚本市场 +* 更方便地悬浮窗编辑、运行脚本 +* 更方便地脚本编辑,在编辑器界面可搜索、查看函数 +* 智能生成选择器代码 +* 更详细的文档和向导(Developer Guide) +* 全新的脚本 +* 找图功能 +* 脚本打包为独立应用功能 + ## License 基于[Mozilla Public License Version 2.0](https://github.com/hyb1996/NoRootScriptDroid/blob/master/LICENSE.md)并附加以下条款: * **非商业性使用** — 不得将此项目及其衍生的项目的源代码和二进制产品用于任何商业和盈利用途 diff --git a/app/build.gradle b/app/build.gradle index ad4c455c..466cadb0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "com.stardust.scriptdroid" minSdkVersion 17 targetSdkVersion 23 - versionCode 162 - versionName "2.0.16 Beta2" + versionCode 163 + versionName "2.0.16 Beta2.1" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true ndk { @@ -116,7 +116,6 @@ dependencies { compile 'io.mattcarroll.hover:hover:0.9.7' compile 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1' compile 'com.yqritc:recyclerview-flexibledivider:1.4.0' - compile 'com.ashokvarma.android:bottom-navigation-bar:2.0.1' compile 'com.wang.avi:library:2.1.3' compile "io.reactivex.rxjava2:rxjava:2.1.0" compile 'io.reactivex.rxjava2:rxandroid:2.0.1' @@ -126,7 +125,7 @@ dependencies { compile 'com.android.volley:volley:1.0.0' compile 'com.flurry.android:analytics:7.0.0@aar' compile 'com.pushtorefresh.storio:sqlite:1.12.3' - // 920 Text Editor + // 920 Text Editors compile 'com.afollestad.material-dialogs:commons:0.9.2.3' compile 'com.makeramen:roundedimageview:2.2.1' compile 'com.rengwuxian.materialedittext:library:2.0.3' diff --git a/app/src/main/assets/help/documentation/需要Root权限的自动操作函数.md b/app/src/main/assets/help/documentation/需要Root权限的自动操作函数.md index 15fb4bfe..4acfd791 100644 --- a/app/src/main/assets/help/documentation/需要Root权限的自动操作函数.md +++ b/app/src/main/assets/help/documentation/需要Root权限的自动操作函数.md @@ -58,13 +58,13 @@ for(var i = 0; i < 100; i++){ 模拟按下物理按键上。 ### Down() -模拟按下物理按键上。 +模拟按下物理按键下。 ### Left() -模拟按下物理按键上。 +模拟按下物理按键左。 ### Right() -模拟按下物理按键上。 +模拟按下物理按键右。 ### OK() 模拟按下物理按键确定。 diff --git a/app/src/main/assets/help/documentation/需要Root权限的触摸与多点触摸.md b/app/src/main/assets/help/documentation/需要Root权限的触摸与多点触摸.md index 2898bed5..c5877897 100644 --- a/app/src/main/assets/help/documentation/需要Root权限的触摸与多点触摸.md +++ b/app/src/main/assets/help/documentation/需要Root权限的触摸与多点触摸.md @@ -2,9 +2,8 @@ RootAutomator是一个使用Root权限来模拟触摸的对象,用它可以完成触摸与多点触摸,并且这些动作的执行没有延迟。 -构造RootAutomator需要一个context参数。 ``` -var ra = RootAutomator(context); +var ra = RootAutomator(); ``` ### RootAutomator.tap(x, y\[, id\]) @@ -14,7 +13,7 @@ var ra = RootAutomator(context); 点击位置(x, y)。其中id是一个整数值,用于区分多点触摸,不同的id表示不同的"手指",例如: ``` -var ra = RootAutomator(context); +var ra = RootAutomator(); //让"手指1"点击位置(100, 100) ra.tap(100, 100, 1); //让"手指2"点击位置(200, 200); diff --git a/app/src/main/assets/sample/定时器/定时执行.js b/app/src/main/assets/sample/定时器/定时执行.js index 54d3c23e..8fc44550 100644 --- a/app/src/main/assets/sample/定时器/定时执行.js +++ b/app/src/main/assets/sample/定时器/定时执行.js @@ -12,4 +12,3 @@ setInterval(function(){ toast(i * 5 + "秒"); }, 5000); -loop(); \ No newline at end of file diff --git a/app/src/main/assets/sample/定时器/循环执行.js b/app/src/main/assets/sample/定时器/循环执行.js index 472f2971..8747c0f8 100644 --- a/app/src/main/assets/sample/定时器/循环执行.js +++ b/app/src/main/assets/sample/定时器/循环执行.js @@ -8,4 +8,3 @@ setInterval(function(){ } }, 4000); -loop(); diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/help/DocumentationActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/help/DocumentationActivity.java index 78fe213b..f77bda5b 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/help/DocumentationActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/help/DocumentationActivity.java @@ -40,6 +40,7 @@ public class DocumentationActivity extends BaseActivity { super.onCreate(savedInstanceState); setUpUI(); handleIntent(getIntent()); + setToolbarAsBack(mTitle); } private void handleIntent(Intent intent) { @@ -68,7 +69,6 @@ public class DocumentationActivity extends BaseActivity { private void setUpUI() { setContentView(R.layout.activity_document); - setToolbarAsBack(mTitle); setUpLoadingView(); } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/help/HelpCatalogueActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/help/HelpCatalogueActivity.java index 160e2d19..df2d66b3 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/help/HelpCatalogueActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/help/HelpCatalogueActivity.java @@ -107,9 +107,13 @@ public class HelpCatalogueActivity extends BaseActivity { if (CATALOGUES == null) { readCatalogues(); } else { - mLoadingIndicatorView.hide(); - handleIntent(); + onCataloguesAvailable(); } + } + + private void onCataloguesAvailable(){ + mLoadingIndicatorView.hide(); + handleIntent(); setToolbarAsBack(mTitle); } @@ -121,8 +125,7 @@ public class HelpCatalogueActivity extends BaseActivity { runOnUiThread(new Runnable() { @Override public void run() { - mLoadingIndicatorView.hide(); - handleIntent(); + onCataloguesAvailable(); } }); } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java index 842cee11..e3d47ff7 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java @@ -326,22 +326,4 @@ public class MainActivity extends BaseActivity implements OnActivityResultDelega return mActivityResultMediator; } - public static void onRecordStop(Context context, String script) { - Intent intent = new Intent(context, MainActivity_.class) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra(EXTRA_ACTION, ACTION_ON_RECORD_STOP); - IntentExtras.newExtras() - .put(ARGUMENT_SCRIPT, script) - .putInIntent(intent); - context.startActivity(intent); - } - - - public static void onRootRecordStop(Context context, String path) { - Intent intent = new Intent(context, MainActivity_.class) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra(EXTRA_ACTION, ACTION_ON_ROOT_RECORD_STOP) - .putExtra(ARGUMENT_PATH, path); - context.startActivity(intent); - } } \ No newline at end of file diff --git a/app/src/main/res/raw/licenses.xml b/app/src/main/res/raw/licenses.xml index a098685d..35a6e568 100644 --- a/app/src/main/res/raw/licenses.xml +++ b/app/src/main/res/raw/licenses.xml @@ -19,7 +19,7 @@ Apache Software License 2.0 - StorIO + StorIO https://github.com/pushtorefresh/storio Apache Software License 2.0 @@ -85,7 +85,7 @@ Expandable RecyclerView - Copyright (c) 2014 Big Nerd Ranch + Copyright (c) 2015 Big Nerd Ranch https://github.com/bignerdranch/expandable-recycler-view MIT License @@ -107,4 +107,16 @@ https://github.com/google/volley Apache Software License 2.0 + + Settings Compat + Copyright 2016 czy1121 + https://github.com/czy1121/settingscompat + Apache Software License 2.0 + + + Zip4j + + http://www.lingala.net/zip4j/ + Apache Software License 2.0 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d3621d45..86be9313 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -246,7 +246,7 @@ 默认 每天显示一次 - 关闭广告(╥﹏╥) + 关闭广告 diff --git a/autojs/src/main/assets/modules/__RootAutomator__.js b/autojs/src/main/assets/modules/__RootAutomator__.js index 3bc1acf8..82a91ba4 100644 --- a/autojs/src/main/assets/modules/__RootAutomator__.js +++ b/autojs/src/main/assets/modules/__RootAutomator__.js @@ -1,12 +1,13 @@ module.exports = function(__runtime__, scope){ function RootAutomator(){ - this.__ra__ = Object.create(new com.stardust.autojs.runtime.api.RootAutomator(scope.context)); + this.__ra__ = Object.create(new com.stardust.autojs.runtime.api.RootAutomator(scope.context)); var methods = ["sendEvent", "touch", "setScreenMetrics", "touchX", "touchY", "sendSync", "sendMtSync", "tap", "swipe", "press", "longPress", "touchDown", "touchUp", "touchMove", "getDefaultId", "setDefaultId", "exit"]; for(var i = 0; i < methods.length; i++){ var method = methods[i]; this[method] = this.__ra__[method].bind(this.__ra__); } + return this; } var p = RootAutomator.prototype; return RootAutomator; diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java b/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java index 4c4682fe..af41f30e 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java @@ -177,9 +177,9 @@ public class ScriptRuntime { public void init() { if (loopers != null) throw new IllegalStateException("already initialized"); - loopers = new Loopers(); - events = new Events(mUiHandler.getContext(), accessibilityBridge, bridges, loopers); timers = new Timers(bridges); + loopers = new Loopers(timers); + events = new Events(mUiHandler.getContext(), accessibilityBridge, bridges, loopers); } public static void setApplicationContext(Context context) { diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Loopers.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Loopers.java index 6a7775b0..1685f2b9 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Loopers.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Loopers.java @@ -6,6 +6,7 @@ import android.os.MessageQueue; import com.stardust.autojs.runtime.exception.ScriptInterruptedException; import com.stardust.lang.ThreadCompat; +import java.lang.reflect.Field; import java.util.concurrent.ConcurrentHashMap; /** @@ -14,12 +15,13 @@ import java.util.concurrent.ConcurrentHashMap; public class Loopers { + public volatile boolean waitWhenIdle = false; private volatile Looper mServantLooper; private static volatile ConcurrentHashMap sLoopers = new ConcurrentHashMap<>(); + private Timers mTimers; - public volatile boolean waitWhenIdle = false; - - public Loopers() { + public Loopers(Timers timers) { + mTimers = timers; if (Looper.myLooper() == Looper.getMainLooper()) { waitWhenIdle = true; } @@ -27,13 +29,20 @@ public class Loopers { @Override public boolean queueIdle() { Looper l = Looper.myLooper(); - if (l != null && !waitWhenIdle) + if (l != null && shouldQuitLooper()) l.quit(); return true; } }); } + private boolean shouldQuitLooper() { + if (mTimers.hasPendingCallback()) { + return false; + } + return !waitWhenIdle; + } + private void initServantThread() { new ThreadCompat(new Runnable() { diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Timers.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Timers.java index eee7f4a8..4cdac47f 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Timers.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Timers.java @@ -1,6 +1,7 @@ package com.stardust.autojs.runtime.api; import android.os.Handler; +import android.os.SystemClock; import android.util.SparseArray; import com.stardust.autojs.runtime.ScriptBridges; @@ -15,19 +16,20 @@ public class Timers { private int mCallbackMaxId = 0; private ScriptBridges mBridges; private Handler mHandler; + private long mFutureCallbackUptimeMillis = 0; public Timers(ScriptBridges bridges) { mBridges = bridges; } - private void ensureHander(){ - if(mHandler == null){ + private void ensureHandler() { + if (mHandler == null) { mHandler = new Handler(); } } public int setTimeout(final Object callback, long delay, final Object... args) { - ensureHander(); + ensureHandler(); mCallbackMaxId++; final int id = mCallbackMaxId; Runnable r = new Runnable() { @@ -38,41 +40,44 @@ public class Timers { } }; mHandlerCallbacks.put(id, r); - mHandler.postDelayed(r, delay); + postDelayed(r, delay); return id; } - public void post(Runnable r) { - ensureHander(); - mHandler.post(r); - } - - public void clearTimeout(int id) { - clearCallback(id); + public boolean clearTimeout(int id) { + return clearCallback(id); } public int setInterval(final Object listener, final long interval, final Object... args) { - ensureHander(); + ensureHandler(); mCallbackMaxId++; final int id = mCallbackMaxId; - Runnable r = new Runnable() { + final Runnable r = new Runnable() { @Override public void run() { + if (mHandlerCallbacks.get(id) == null) + return; mBridges.callFunction(listener, null, args); - mHandler.postDelayed(this, interval); + postDelayed(this, interval); } }; mHandlerCallbacks.put(id, r); - mHandler.postDelayed(r, interval); + postDelayed(r, interval); return id; } - public void clearInterval(int id) { - clearTimeout(id); + private void postDelayed(Runnable r, long interval) { + long uptime = SystemClock.uptimeMillis() + interval; + mHandler.postAtTime(r, uptime); + mFutureCallbackUptimeMillis = Math.max(mFutureCallbackUptimeMillis, uptime); + } + + public boolean clearInterval(int id) { + return clearCallback(id); } public int setImmediate(final Object listener, final Object... args) { - ensureHander(); + ensureHandler(); mCallbackMaxId++; final int id = mCallbackMaxId; Runnable r = new Runnable() { @@ -83,21 +88,27 @@ public class Timers { } }; mHandlerCallbacks.put(id, r); - mHandler.post(r); + postDelayed(r, 0); return id; } - public void clearImmediate(int id) { - clearCallback(id); + public boolean clearImmediate(int id) { + return clearCallback(id); } - private void clearCallback(int id) { + private boolean clearCallback(int id) { Runnable callback = mHandlerCallbacks.get(id); if (callback != null) { mHandler.removeCallbacks(callback); mHandlerCallbacks.remove(id); + return true; } + return false; } + public boolean hasPendingCallback() { + + return mFutureCallbackUptimeMillis > SystemClock.uptimeMillis(); + } }