Merge pull request #228 from hyb1996/dev

release 2.0.16 Beta2.1
This commit is contained in:
Stardust
2017-08-16 11:13:21 +08:00
committed by GitHub
15 changed files with 109 additions and 72 deletions

View File

@@ -1,19 +1,31 @@
# AutoJs # Auto.js
## 简介 ## 简介
一个**不需要Root权限**的类似按键精灵的自动操作软件,可以实现自动点击、滑动、输入文字、打开应用等。 一个主要由无障碍服务实现的**不需要Root权限**的类似按键精灵的自动操作软件,可以实现自动点击、滑动、输入文字、打开应用等。
同时有[Sublime Text 插件](https://github.com/hyb1996/AutoJs-Sublime-Plugin)可提供基础的在桌面开发的功能。 同时有[Sublime Text 插件](https://github.com/hyb1996/AutoJs-Sublime-Plugin)可提供基础的在桌面开发的功能。
下载地址:[酷安](http://www.coolapk.com/apk/com.stardust.scriptdroid) 下载地址:[酷安](http://www.coolapk.com/apk/com.stardust.scriptdroid)
特性: ### 特性
* 简单易用的自动操作函数 * 简单易用的自动操作函数
* 悬浮窗录制和运行 * 悬浮窗录制和运行
* 丰富的文档、教程与示例
* 更专业&强大的选择器API提供对屏幕上的控件的寻找、遍历、获取信息、操作等。类似于Google的UI测试框架UiAutomator您也可以把他当做移动版UI测试框架使用 * 更专业&强大的选择器API提供对屏幕上的控件的寻找、遍历、获取信息、操作等。类似于Google的UI测试框架UiAutomator您也可以把他当做移动版UI测试框架使用
* 采用JavaScript为脚本语言支持简单的代码补全。您也可以把他当作简便的JavaScript IDE使用 * 采用JavaScript为脚本语言支持简单的代码补全。您也可以把他当作简便的JavaScript IDE使用
* 带有界面分析工具类似Android Studio的LayoutInspector可以分析界面层次和范围、获取界面上的控件信息 * 带有界面分析工具类似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) ![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) ![screen-capture5](https://raw.githubusercontent.com/hyb1996/NoRootScriptDroid/master/screen-captures/ss06.png)
### Todo
* 脚本社区或脚本市场
* 更方便地悬浮窗编辑、运行脚本
* 更方便地脚本编辑,在编辑器界面可搜索、查看函数
* 智能生成选择器代码
* 更详细的文档和向导(Developer Guide)
* 全新的脚本
* 找图功能
* 脚本打包为独立应用功能
## License ## License
基于[Mozilla Public License Version 2.0](https://github.com/hyb1996/NoRootScriptDroid/blob/master/LICENSE.md)并附加以下条款: 基于[Mozilla Public License Version 2.0](https://github.com/hyb1996/NoRootScriptDroid/blob/master/LICENSE.md)并附加以下条款:
* **非商业性使用** — 不得将此项目及其衍生的项目的源代码和二进制产品用于任何商业和盈利用途 * **非商业性使用** — 不得将此项目及其衍生的项目的源代码和二进制产品用于任何商业和盈利用途

View File

@@ -9,8 +9,8 @@ android {
applicationId "com.stardust.scriptdroid" applicationId "com.stardust.scriptdroid"
minSdkVersion 17 minSdkVersion 17
targetSdkVersion 23 targetSdkVersion 23
versionCode 162 versionCode 163
versionName "2.0.16 Beta2" versionName "2.0.16 Beta2.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
ndk { ndk {
@@ -116,7 +116,6 @@ dependencies {
compile 'io.mattcarroll.hover:hover:0.9.7' compile 'io.mattcarroll.hover:hover:0.9.7'
compile 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1' compile 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1'
compile 'com.yqritc:recyclerview-flexibledivider:1.4.0' 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 'com.wang.avi:library:2.1.3'
compile "io.reactivex.rxjava2:rxjava:2.1.0" compile "io.reactivex.rxjava2:rxjava:2.1.0"
compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
@@ -126,7 +125,7 @@ dependencies {
compile 'com.android.volley:volley:1.0.0' compile 'com.android.volley:volley:1.0.0'
compile 'com.flurry.android:analytics:7.0.0@aar' compile 'com.flurry.android:analytics:7.0.0@aar'
compile 'com.pushtorefresh.storio:sqlite:1.12.3' 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.afollestad.material-dialogs:commons:0.9.2.3'
compile 'com.makeramen:roundedimageview:2.2.1' compile 'com.makeramen:roundedimageview:2.2.1'
compile 'com.rengwuxian.materialedittext:library:2.0.3' compile 'com.rengwuxian.materialedittext:library:2.0.3'

View File

@@ -58,13 +58,13 @@ for(var i = 0; i < 100; i++){
模拟按下物理按键上。 模拟按下物理按键上。
### Down() ### Down()
模拟按下物理按键 模拟按下物理按键
### Left() ### Left()
模拟按下物理按键 模拟按下物理按键
### Right() ### Right()
模拟按下物理按键 模拟按下物理按键
### OK() ### OK()
模拟按下物理按键确定。 模拟按下物理按键确定。

View File

@@ -2,9 +2,8 @@
RootAutomator是一个使用Root权限来模拟触摸的对象用它可以完成触摸与多点触摸并且这些动作的执行没有延迟。 RootAutomator是一个使用Root权限来模拟触摸的对象用它可以完成触摸与多点触摸并且这些动作的执行没有延迟。
构造RootAutomator需要一个context参数。
``` ```
var ra = RootAutomator(context); var ra = RootAutomator();
``` ```
### RootAutomator.tap(x, y\[, id\]) ### RootAutomator.tap(x, y\[, id\])
@@ -14,7 +13,7 @@ var ra = RootAutomator(context);
点击位置(x, y)。其中id是一个整数值用于区分多点触摸不同的id表示不同的"手指",例如: 点击位置(x, y)。其中id是一个整数值用于区分多点触摸不同的id表示不同的"手指",例如:
``` ```
var ra = RootAutomator(context); var ra = RootAutomator();
//让"手指1"点击位置(100, 100) //让"手指1"点击位置(100, 100)
ra.tap(100, 100, 1); ra.tap(100, 100, 1);
//让"手指2"点击位置(200, 200); //让"手指2"点击位置(200, 200);

View File

@@ -12,4 +12,3 @@ setInterval(function(){
toast(i * 5 + "秒"); toast(i * 5 + "秒");
}, 5000); }, 5000);
loop();

View File

@@ -8,4 +8,3 @@ setInterval(function(){
} }
}, 4000); }, 4000);
loop();

View File

@@ -40,6 +40,7 @@ public class DocumentationActivity extends BaseActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setUpUI(); setUpUI();
handleIntent(getIntent()); handleIntent(getIntent());
setToolbarAsBack(mTitle);
} }
private void handleIntent(Intent intent) { private void handleIntent(Intent intent) {
@@ -68,7 +69,6 @@ public class DocumentationActivity extends BaseActivity {
private void setUpUI() { private void setUpUI() {
setContentView(R.layout.activity_document); setContentView(R.layout.activity_document);
setToolbarAsBack(mTitle);
setUpLoadingView(); setUpLoadingView();
} }

View File

@@ -107,9 +107,13 @@ public class HelpCatalogueActivity extends BaseActivity {
if (CATALOGUES == null) { if (CATALOGUES == null) {
readCatalogues(); readCatalogues();
} else { } else {
mLoadingIndicatorView.hide(); onCataloguesAvailable();
handleIntent();
} }
}
private void onCataloguesAvailable(){
mLoadingIndicatorView.hide();
handleIntent();
setToolbarAsBack(mTitle); setToolbarAsBack(mTitle);
} }
@@ -121,8 +125,7 @@ public class HelpCatalogueActivity extends BaseActivity {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
mLoadingIndicatorView.hide(); onCataloguesAvailable();
handleIntent();
} }
}); });
} }

View File

@@ -326,22 +326,4 @@ public class MainActivity extends BaseActivity implements OnActivityResultDelega
return mActivityResultMediator; 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);
}
} }

View File

@@ -19,7 +19,7 @@
<license>Apache Software License 2.0</license> <license>Apache Software License 2.0</license>
</notice> </notice>
<notice> <notice>
<name>StorIO </name> <name>StorIO</name>
<url>https://github.com/pushtorefresh/storio</url> <url>https://github.com/pushtorefresh/storio</url>
<license>Apache Software License 2.0</license> <license>Apache Software License 2.0</license>
</notice> </notice>
@@ -85,7 +85,7 @@
</notice> </notice>
<notice> <notice>
<name>Expandable RecyclerView</name> <name>Expandable RecyclerView</name>
<copyright>Copyright (c) 2014 Big Nerd Ranch</copyright> <copyright>Copyright (c) 2015 Big Nerd Ranch</copyright>
<url>https://github.com/bignerdranch/expandable-recycler-view</url> <url>https://github.com/bignerdranch/expandable-recycler-view</url>
<license>MIT License</license> <license>MIT License</license>
</notice> </notice>
@@ -107,4 +107,16 @@
<url>https://github.com/google/volley</url> <url>https://github.com/google/volley</url>
<license>Apache Software License 2.0</license> <license>Apache Software License 2.0</license>
</notice> </notice>
<notice>
<name>Settings Compat</name>
<copyright>Copyright 2016 czy1121</copyright>
<url>https://github.com/czy1121/settingscompat</url>
<license>Apache Software License 2.0</license>
</notice>
<notice>
<name>Zip4j</name>
<copyright/>
<url>http://www.lingala.net/zip4j/</url>
<license>Apache Software License 2.0</license>
</notice>
</notices> </notices>

View File

@@ -246,7 +246,7 @@
<string-array name="ad_showing_mode_keys"> <string-array name="ad_showing_mode_keys">
<item>默认</item> <item>默认</item>
<item>每天显示一次</item> <item>每天显示一次</item>
<item>关闭广告(╥﹏╥)</item> <item>关闭广告</item>
</string-array> </string-array>
<string-array name="ad_showing_mode_values"> <string-array name="ad_showing_mode_values">

View File

@@ -1,12 +1,13 @@
module.exports = function(__runtime__, scope){ module.exports = function(__runtime__, scope){
function RootAutomator(){ 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", var methods = ["sendEvent", "touch", "setScreenMetrics", "touchX", "touchY", "sendSync", "sendMtSync", "tap",
"swipe", "press", "longPress", "touchDown", "touchUp", "touchMove", "getDefaultId", "setDefaultId", "exit"]; "swipe", "press", "longPress", "touchDown", "touchUp", "touchMove", "getDefaultId", "setDefaultId", "exit"];
for(var i = 0; i < methods.length; i++){ for(var i = 0; i < methods.length; i++){
var method = methods[i]; var method = methods[i];
this[method] = this.__ra__[method].bind(this.__ra__); this[method] = this.__ra__[method].bind(this.__ra__);
} }
return this;
} }
var p = RootAutomator.prototype; var p = RootAutomator.prototype;
return RootAutomator; return RootAutomator;

View File

@@ -177,9 +177,9 @@ public class ScriptRuntime {
public void init() { public void init() {
if (loopers != null) if (loopers != null)
throw new IllegalStateException("already initialized"); throw new IllegalStateException("already initialized");
loopers = new Loopers();
events = new Events(mUiHandler.getContext(), accessibilityBridge, bridges, loopers);
timers = new Timers(bridges); timers = new Timers(bridges);
loopers = new Loopers(timers);
events = new Events(mUiHandler.getContext(), accessibilityBridge, bridges, loopers);
} }
public static void setApplicationContext(Context context) { public static void setApplicationContext(Context context) {

View File

@@ -6,6 +6,7 @@ import android.os.MessageQueue;
import com.stardust.autojs.runtime.exception.ScriptInterruptedException; import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
import com.stardust.lang.ThreadCompat; import com.stardust.lang.ThreadCompat;
import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
@@ -14,12 +15,13 @@ import java.util.concurrent.ConcurrentHashMap;
public class Loopers { public class Loopers {
public volatile boolean waitWhenIdle = false;
private volatile Looper mServantLooper; private volatile Looper mServantLooper;
private static volatile ConcurrentHashMap<Thread, Looper> sLoopers = new ConcurrentHashMap<>(); private static volatile ConcurrentHashMap<Thread, Looper> sLoopers = new ConcurrentHashMap<>();
private Timers mTimers;
public volatile boolean waitWhenIdle = false; public Loopers(Timers timers) {
mTimers = timers;
public Loopers() {
if (Looper.myLooper() == Looper.getMainLooper()) { if (Looper.myLooper() == Looper.getMainLooper()) {
waitWhenIdle = true; waitWhenIdle = true;
} }
@@ -27,13 +29,20 @@ public class Loopers {
@Override @Override
public boolean queueIdle() { public boolean queueIdle() {
Looper l = Looper.myLooper(); Looper l = Looper.myLooper();
if (l != null && !waitWhenIdle) if (l != null && shouldQuitLooper())
l.quit(); l.quit();
return true; return true;
} }
}); });
} }
private boolean shouldQuitLooper() {
if (mTimers.hasPendingCallback()) {
return false;
}
return !waitWhenIdle;
}
private void initServantThread() { private void initServantThread() {
new ThreadCompat(new Runnable() { new ThreadCompat(new Runnable() {

View File

@@ -1,6 +1,7 @@
package com.stardust.autojs.runtime.api; package com.stardust.autojs.runtime.api;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock;
import android.util.SparseArray; import android.util.SparseArray;
import com.stardust.autojs.runtime.ScriptBridges; import com.stardust.autojs.runtime.ScriptBridges;
@@ -15,19 +16,20 @@ public class Timers {
private int mCallbackMaxId = 0; private int mCallbackMaxId = 0;
private ScriptBridges mBridges; private ScriptBridges mBridges;
private Handler mHandler; private Handler mHandler;
private long mFutureCallbackUptimeMillis = 0;
public Timers(ScriptBridges bridges) { public Timers(ScriptBridges bridges) {
mBridges = bridges; mBridges = bridges;
} }
private void ensureHander(){ private void ensureHandler() {
if(mHandler == null){ if (mHandler == null) {
mHandler = new Handler(); mHandler = new Handler();
} }
} }
public int setTimeout(final Object callback, long delay, final Object... args) { public int setTimeout(final Object callback, long delay, final Object... args) {
ensureHander(); ensureHandler();
mCallbackMaxId++; mCallbackMaxId++;
final int id = mCallbackMaxId; final int id = mCallbackMaxId;
Runnable r = new Runnable() { Runnable r = new Runnable() {
@@ -38,41 +40,44 @@ public class Timers {
} }
}; };
mHandlerCallbacks.put(id, r); mHandlerCallbacks.put(id, r);
mHandler.postDelayed(r, delay); postDelayed(r, delay);
return id; return id;
} }
public void post(Runnable r) { public boolean clearTimeout(int id) {
ensureHander(); return clearCallback(id);
mHandler.post(r);
}
public void clearTimeout(int id) {
clearCallback(id);
} }
public int setInterval(final Object listener, final long interval, final Object... args) { public int setInterval(final Object listener, final long interval, final Object... args) {
ensureHander(); ensureHandler();
mCallbackMaxId++; mCallbackMaxId++;
final int id = mCallbackMaxId; final int id = mCallbackMaxId;
Runnable r = new Runnable() { final Runnable r = new Runnable() {
@Override @Override
public void run() { public void run() {
if (mHandlerCallbacks.get(id) == null)
return;
mBridges.callFunction(listener, null, args); mBridges.callFunction(listener, null, args);
mHandler.postDelayed(this, interval); postDelayed(this, interval);
} }
}; };
mHandlerCallbacks.put(id, r); mHandlerCallbacks.put(id, r);
mHandler.postDelayed(r, interval); postDelayed(r, interval);
return id; return id;
} }
public void clearInterval(int id) { private void postDelayed(Runnable r, long interval) {
clearTimeout(id); 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) { public int setImmediate(final Object listener, final Object... args) {
ensureHander(); ensureHandler();
mCallbackMaxId++; mCallbackMaxId++;
final int id = mCallbackMaxId; final int id = mCallbackMaxId;
Runnable r = new Runnable() { Runnable r = new Runnable() {
@@ -83,21 +88,27 @@ public class Timers {
} }
}; };
mHandlerCallbacks.put(id, r); mHandlerCallbacks.put(id, r);
mHandler.post(r); postDelayed(r, 0);
return id; return id;
} }
public void clearImmediate(int id) { public boolean clearImmediate(int id) {
clearCallback(id); return clearCallback(id);
} }
private void clearCallback(int id) { private boolean clearCallback(int id) {
Runnable callback = mHandlerCallbacks.get(id); Runnable callback = mHandlerCallbacks.get(id);
if (callback != null) { if (callback != null) {
mHandler.removeCallbacks(callback); mHandler.removeCallbacks(callback);
mHandlerCallbacks.remove(id); mHandlerCallbacks.remove(id);
return true;
} }
return false;
} }
public boolean hasPendingCallback() {
return mFutureCallbackUptimeMillis > SystemClock.uptimeMillis();
}
} }