From 8f2a088111604f5b4617cbc33dfab2c85b7cd1f5 Mon Sep 17 00:00:00 2001 From: hyb1996 <946994919@qq.com> Date: Fri, 21 Jul 2017 11:24:02 +0800 Subject: [PATCH] add timers modules --- app/src/main/assets/help/catalogue.json | 5 + .../documentation/事件与按键、触摸监听.md | 15 +++ .../main/assets/help/documentation/定时器.md | 66 ++++++++++ .../sample/事件与按键、触摸监听/按键监听.js | 2 +- .../sample/事件与按键、触摸监听/触摸监听.js | 2 +- .../长按返回退出当前程序.js | 19 +-- .../事件与按键、触摸监听/音量键控制程序.js | 8 +- app/src/main/assets/sample/定时器/定时执行.js | 15 +++ app/src/main/assets/sample/定时器/循环执行.js | 11 ++ .../java/com/stardust/scriptdroid/App.java | 19 +-- .../menu/content/RecordNavigatorContent.java | 19 ++- app/src/main/res/values/strings.xml | 6 +- .../src/main/assets/javascript_engine_init.js | 11 +- autojs/src/main/assets/modules/__events__.js | 17 --- autojs/src/main/assets/modules/__general__.js | 6 +- autojs/src/main/assets/modules/__timers__.js | 9 ++ .../stardust/autojs/ScriptEngineService.java | 2 +- .../autojs/engine/RhinoJavaScriptEngine.java | 5 +- .../autojs/runtime/AbstractScriptRuntime.java | 16 ++- .../autojs/runtime/ScriptRuntime.java | 13 +- .../stardust/autojs/runtime/api/AppUtils.java | 2 +- .../autojs/runtime/api/EventEmitter.java | 21 +-- .../stardust/autojs/runtime/api/Events.java | 52 ++------ .../autojs/runtime/api/ScriptBridges.java | 28 ++++ .../stardust/autojs/runtime/api/Timers.java | 121 ++++++++++++++++++ .../accessibility/AccessibilityService.java | 27 +--- 26 files changed, 365 insertions(+), 152 deletions(-) create mode 100644 app/src/main/assets/help/documentation/定时器.md create mode 100644 app/src/main/assets/sample/定时器/定时执行.js create mode 100644 app/src/main/assets/sample/定时器/循环执行.js create mode 100644 autojs/src/main/assets/modules/__timers__.js create mode 100644 autojs/src/main/java/com/stardust/autojs/runtime/api/ScriptBridges.java create mode 100644 autojs/src/main/java/com/stardust/autojs/runtime/api/Timers.java diff --git a/app/src/main/assets/help/catalogue.json b/app/src/main/assets/help/catalogue.json index 4dc56ada..fb0922dc 100644 --- a/app/src/main/assets/help/catalogue.json +++ b/app/src/main/assets/help/catalogue.json @@ -54,6 +54,11 @@ "type": "markdown", "path":"documentation" }, + { + "title": "定时器", + "type": "markdown", + "path":"documentation" + }, { "title": "shell命令", "type": "markdown", diff --git a/app/src/main/assets/help/documentation/事件与按键、触摸监听.md b/app/src/main/assets/help/documentation/事件与按键、触摸监听.md index 0489ddc9..a43221ee 100644 --- a/app/src/main/assets/help/documentation/事件与按键、触摸监听.md +++ b/app/src/main/assets/help/documentation/事件与按键、触摸监听.md @@ -110,6 +110,21 @@ events.loop(); 删除所有事件监听函数。 +### events.setTimeout(callback, delay) +* callback \ 回调函数 +* delay \ 延迟 + +延迟大约delay毫秒后执行callback。该函数只有在events.loop()被使用时才有效。 + +返回一个整数表示这个设置的timeout, 可由clearTimeout取消。 + + +### events.clearTimeout(id) + +* id \ 由 setTimeout() 返回的 ID 值。该值标识要取消的延迟执行回调。 + +取消由 setTimeout() 方法设置的 timeout。 + ### key事件 当有按键被按下或弹起时会触发该事件。 diff --git a/app/src/main/assets/help/documentation/定时器.md b/app/src/main/assets/help/documentation/定时器.md new file mode 100644 index 00000000..865d8b3a --- /dev/null +++ b/app/src/main/assets/help/documentation/定时器.md @@ -0,0 +1,66 @@ +timer 模块暴露了一个全局的 API,用于在某个未来时间段调用调度函数。 因为定时器函数是全局的,所以使用该 API 无需调用 timer.*** + +Auto.js 中的计时器函数实现了与 Web 浏览器提供的定时器类似的 API,除了它使用了一个不同的内部实现,它是基于 Android Looper-Handler机制构建的。其实现机制与Node.js比较相似。 + +不同于Web浏览器和Node.js,在Auto.js中,必须显式地调用loop()函数开始Looper循环才能使所有计时器开始工作。loop()函数是一个"死循环", 在他之后的语句将不会被执行。 + +### loop() + +开始Looper循环。使所有定时器工作所必须。 +注意,何时调用loop不会影响setTimeout等函数的计时,例如: +``` +setTimout(function(){ + toast("hello"); + exit(); +}, 2000); +sleep(1000); +loop(); +``` +这段代码将在运行后大约2秒显示"hello"。 + +### setImmediate(callback\[, ...args\]) +* callback \ 在 Auto.js 循环的当前回合结束时要调用的函数。 +* ...args \ 当调用 callback 时要传入的可选参数。 + +预定立即执行的 callback,它是在 I/O 事件的回调之后被触发。 返回一个用于 clearImmediate() 的 id。 + +当多次调用 setImmediate() 时,callback 函数会按照它们被创建的顺序依次执行。 每次事件循环迭代都会处理整个回调队列。 如果一个立即定时器是被一个正在执行的回调排入队列的,则该定时器直到下一次事件循环迭代才会被触发。 + +### setInterval(callback, delay\[, ...args\]) +* callback \ 当定时器到点时要调用的函数。 +* delay \ 调用 callback 之前要等待的毫秒数。 +* ...args \ 当调用 callback 时要传入的可选参数。 + +预定每隔 delay 毫秒重复执行的 callback。 返回一个用于 clearInterval() 的 id。 + +当 delay 小于 0 时,delay 会被设为 0。 + +### setTimeout(callback, delay\[, ...args\]) +* callback \ 当定时器到点时要调用的函数。 +* delay \ 调用 callback 之前要等待的毫秒数。 +* ...args \ 当调用 callback 时要传入的可选参数。 + +预定在 delay 毫秒之后执行的单次 callback。 返回一个用于 clearTimeout() 的 id。 + +callback 可能不会精确地在 delay 毫秒被调用。 Auto.js 不能保证回调被触发的确切时间,也不能保证它们的顺序。 回调会在尽可能接近所指定的时间上调用。 + +当 delay 小于 0 时,delay 会被设为 0。 + +setImmediate()、setInterval() 和 setTimeout() 方法每次都会返回表示预定的计时器的id。 它们可用于取消定时器并防止触发。 + + +### clearImmediate(id)# +* id \ 一个 setImmediate() 返回的 id。 + +取消一个由 setImmediate() 创建的 Immediate 对象。 + +### clearInterval(id)# +* id \ 一个 setInterval() 返回的 id。 + +取消一个由 setInterval() 创建的 Timeout 对象。 + +### clearTimeout(id) +* id \ 一个 setTimeout() 返回的 id。 + +取消一个由 setTimeout() 创建的 Timeout 对象。 + diff --git a/app/src/main/assets/sample/事件与按键、触摸监听/按键监听.js b/app/src/main/assets/sample/事件与按键、触摸监听/按键监听.js index 307a5726..f49e92e4 100644 --- a/app/src/main/assets/sample/事件与按键、触摸监听/按键监听.js +++ b/app/src/main/assets/sample/事件与按键、触摸监听/按键监听.js @@ -20,7 +20,7 @@ events.on("key", function(code, event){ } }); -events.loop(); +loop(); diff --git a/app/src/main/assets/sample/事件与按键、触摸监听/触摸监听.js b/app/src/main/assets/sample/事件与按键、触摸监听/触摸监听.js index 93fd7b86..b82cdde3 100644 --- a/app/src/main/assets/sample/事件与按键、触摸监听/触摸监听.js +++ b/app/src/main/assets/sample/事件与按键、触摸监听/触摸监听.js @@ -9,4 +9,4 @@ events.on("touch", function(point){ log(point); }); -events.loop(); \ No newline at end of file +loop(); \ No newline at end of file diff --git a/app/src/main/assets/sample/事件与按键、触摸监听/长按返回退出当前程序.js b/app/src/main/assets/sample/事件与按键、触摸监听/长按返回退出当前程序.js index 80950ed2..a0b88751 100644 --- a/app/src/main/assets/sample/事件与按键、触摸监听/长按返回退出当前程序.js +++ b/app/src/main/assets/sample/事件与按键、触摸监听/长按返回退出当前程序.js @@ -3,29 +3,22 @@ var 长按间隔 = 1500; var curPackage = null; -var backPressedCount = 0; -var backPressed = false; +var timeoutId = null; events.observeKey(); events.onKeyDown("back", function(event){ curPackage = currentPackage(); - backPressed = true; - backPressedCount++; - (function(count){ - events.setTimeout(function(){ - if(backPressed && count == backPressedCount){ - backBackBackBack(); - } - }, 长按间隔); - })(backPressedCount); + timeoutId = setTimeout(function(){ + backBackBackBack(); + }, 长按间隔); }); events.onKeyUp("back", function(event){ - backPressed = false; + clearTimeout(timeoutId); }); -events.loop(); +loop(); function backBackBackBack(){ while(curPackage == currentPackage()){ diff --git a/app/src/main/assets/sample/事件与按键、触摸监听/音量键控制程序.js b/app/src/main/assets/sample/事件与按键、触摸监听/音量键控制程序.js index ab7fd388..f5782d60 100644 --- a/app/src/main/assets/sample/事件与按键、触摸监听/音量键控制程序.js +++ b/app/src/main/assets/sample/事件与按键、触摸监听/音量键控制程序.js @@ -15,20 +15,20 @@ events.onKeyDown("volume_up", function(event){ }); events.onKeyDown("volume_down", function(event){ - stop(); toast("程序结束"); + exit(); }); task(); -events.loop(); +loop(); function task1(){ toast("任务1运行中,音量下键结束,音量上键切换任务"); - events.setTimeout(task, interval); + setTimeout(task, interval); } function task2(){ toast("任务2运行中,音量下键结束,音量上键切换任务"); - events.setTimeout(task, interval); + setTimeout(task, interval); } \ No newline at end of file diff --git a/app/src/main/assets/sample/定时器/定时执行.js b/app/src/main/assets/sample/定时器/定时执行.js new file mode 100644 index 00000000..54d3c23e --- /dev/null +++ b/app/src/main/assets/sample/定时器/定时执行.js @@ -0,0 +1,15 @@ +toast("静等20秒,你会看到想看的..."); + +var i = 0; + +setTimeout(function(){ + app.openUrl("http://music.163.com/#/song?id=109628&autoplay=true&market=baiduhd"); + exit(); +}, 20 * 1000); + +setInterval(function(){ + i++; + 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 new file mode 100644 index 00000000..472f2971 --- /dev/null +++ b/app/src/main/assets/sample/定时器/循环执行.js @@ -0,0 +1,11 @@ +var i = 0; + +setInterval(function(){ + i++; + toast(i * 4 + "秒"); + if(i == 5){ + exit(); + } +}, 4000); + +loop(); diff --git a/app/src/main/java/com/stardust/scriptdroid/App.java b/app/src/main/java/com/stardust/scriptdroid/App.java index db632249..a58f0e21 100644 --- a/app/src/main/java/com/stardust/scriptdroid/App.java +++ b/app/src/main/java/com/stardust/scriptdroid/App.java @@ -40,8 +40,6 @@ public class App extends MultiDexApplication { return instance.get(); } - private VolumeChangeObserver mVolumeChangeObserver = new VolumeChangeObserver(); - public void onCreate() { super.onCreate(); instance = new WeakReference<>(this); @@ -74,19 +72,12 @@ public class App extends MultiDexApplication { } private void initVolumeChangeObserver() { - //registerReceiver(mVolumeChangeObserver, new IntentFilter(VolumeChangeObserver.ACTION_VOLUME_CHANGE)); - mVolumeChangeObserver.addOnVolumeChangeListener(new VolumeChangeObserver.OnVolumeChangeListener() { - @Override - public void onVolumeChange() { - if (Pref.isRunningVolumeControlEnabled()) { - AutoJs.getInstance().getScriptEngineService().stopAllAndToast(); - } - } - }); AccessibilityService.getStickOnKeyObserver().addListener(new OnKeyListener() { @Override public void onKeyEvent(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && Pref.isRunningVolumeControlEnabled()) { + if (event.getAction() == KeyEvent.ACTION_DOWN && + (keyCode == KeyEvent.KEYCODE_VOLUME_UP) + && Pref.isRunningVolumeControlEnabled()) { AutoJs.getInstance().getScriptEngineService().stopAllAndToast(); } } @@ -94,10 +85,6 @@ public class App extends MultiDexApplication { } - public VolumeChangeObserver getVolumeChangeObserver() { - return mVolumeChangeObserver; - } - public UiHandler getUiHandler() { return mUiHandler; } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/content/RecordNavigatorContent.java b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/content/RecordNavigatorContent.java index fd501787..c68907b8 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/content/RecordNavigatorContent.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/content/RecordNavigatorContent.java @@ -4,12 +4,12 @@ import android.content.Context; import android.content.Intent; import android.support.annotation.NonNull; import android.support.v7.widget.SwitchCompat; +import android.view.KeyEvent; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; -import com.stardust.app.VolumeChangeObserver; import com.stardust.autojs.runtime.record.Recorder; import com.stardust.autojs.runtime.record.accessibility.AccessibilityActionRecorder; import com.stardust.autojs.runtime.record.inputevent.KeyObserver; @@ -22,6 +22,8 @@ import com.stardust.scriptdroid.autojs.AutoJs; import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService; import com.stardust.scriptdroid.ui.main.MainActivity; import com.stardust.util.MessageEvent; +import com.stardust.view.accessibility.AccessibilityService; +import com.stardust.view.accessibility.OnKeyListener; import org.greenrobot.eventbus.Subscribe; @@ -62,10 +64,12 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat private Context mContext; private boolean mDiscard = false; private KeyObserver mKeyObserver; - private VolumeChangeObserver.OnVolumeChangeListener mOnVolumeChangeListener = new VolumeChangeObserver.OnVolumeChangeListener() { + private OnKeyListener mVolumeKeyListener = new OnKeyListener() { @Override - public void onVolumeChange() { - if (Pref.isRecordVolumeControlEnable()) { + public void onKeyEvent(int keyCode, KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN && + (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) + && Pref.isRecordVolumeControlEnable()) { if (mRecorder == null) { startRecord(); } else if (alreadyStartedRecord()) { @@ -80,7 +84,10 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat mView = View.inflate(context, R.layout.floating_window_record, null); ButterKnife.bind(this, mView); HoverMenuService.getEventBus().register(this); - App.getApp().getVolumeChangeObserver().addOnVolumeChangeListener(mOnVolumeChangeListener); + AccessibilityService.getStickOnKeyObserver().addListener(mVolumeKeyListener); + if (Pref.isRecordVolumeControlEnable()) { + AutoJs.getInstance().ensureServiceEnabled(); + } if (Pref.hasRecordTrigger()) { mKeyObserver = new KeyObserver(mContext); mKeyObserver.startListening(); @@ -181,7 +188,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat public void onMenuExit() { HoverMenuService.getEventBus().unregister(this); - App.getApp().getVolumeChangeObserver().removeOnVolumeChangeListener(mOnVolumeChangeListener); + AccessibilityService.getStickOnKeyObserver().addListener(mVolumeKeyListener); if (mKeyObserver != null) { mKeyObserver.stopListening(); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4be83403..519847af 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -81,8 +81,8 @@ 文件写入失败 加入QQ互赞&交流群 已复制到剪贴板 - 使用音量键控制 - 开启后每次音量变化会开始或停止脚本录制 + 使用音量下键控制 + 开启后每次音量下键会开始或停止脚本录制 key_use_volume_control_record 未安装手机QQ 编辑 @@ -129,7 +129,7 @@ 无障碍服务->AutoJs并开启]]> 脚本运行 key_use_volume_control_running - 每次音量键变化停止所有脚本 + 音量上键停止所有脚本 需要启用无障碍服务 运行方式 已开启 diff --git a/autojs/src/main/assets/javascript_engine_init.js b/autojs/src/main/assets/javascript_engine_init.js index 5db49b0b..a5aa9c7e 100644 --- a/autojs/src/main/assets/javascript_engine_init.js +++ b/autojs/src/main/assets/javascript_engine_init.js @@ -13,6 +13,15 @@ if(__engine_name__ == "rhino"){ } } +__runtime__.bridges.setFunctionCaller(function(func, target, args){ + var arr = []; + var len = args.length; + for(var i = 0; i < len; i++){ + arr.push(args[i]); + } + return func.apply(target, arr); +}); + var __that__ = this; var __asGlobal__ = function(obj, functions){ @@ -27,7 +36,7 @@ require("__general__")(__runtime__, this); (function(scope){ - var modules = ['app', 'automator', 'console', 'dialogs', 'io', 'selector', 'shell', 'web', 'ui', "images", "events"]; + var modules = ['app', 'automator', 'console', 'dialogs', 'io', 'selector', 'shell', 'web', 'ui', "images", "timers", "events"]; var len = modules.length; for(var i = 0; i < len; i++) { var m = modules[i]; diff --git a/autojs/src/main/assets/modules/__events__.js b/autojs/src/main/assets/modules/__events__.js index b2ae7fcb..44caf715 100644 --- a/autojs/src/main/assets/modules/__events__.js +++ b/autojs/src/main/assets/modules/__events__.js @@ -2,23 +2,6 @@ module.exports = function(__runtime__, scope){ var events = Object.create(__runtime__.events); - var caller = function(func, args){ - var arr = []; - var len = args.length; - for(var i = 0; i < len; i++){ - arr.push(args[i]); - } - return func.apply(null, arr); - }; - - events.setFunctionCaller(caller); - - events.emitter = function(){ - var e = new com.stardust.autojs.runtime.api.EventEmitter(); - e.setFunctionCaller(caller); - return e; - } - return events; } diff --git a/autojs/src/main/assets/modules/__general__.js b/autojs/src/main/assets/modules/__general__.js index 043855e6..88a25f8c 100644 --- a/autojs/src/main/assets/modules/__general__.js +++ b/autojs/src/main/assets/modules/__general__.js @@ -19,10 +19,12 @@ module.exports = function(__runtime__, scope){ return !isStopped(); } - scope.stop = function(){ - __runtime__.stop(); + scope.exit = function(){ + __runtime__.exit(); } + scope.stop = scope.exit; + scope.setClip = function(text){ __runtime__.setClip(text); } diff --git a/autojs/src/main/assets/modules/__timers__.js b/autojs/src/main/assets/modules/__timers__.js new file mode 100644 index 00000000..f6dd3c08 --- /dev/null +++ b/autojs/src/main/assets/modules/__timers__.js @@ -0,0 +1,9 @@ + +module.exports = function(__runtime__, scope){ + var timers = Object.create(__runtime__.timers); + + scope.__asGlobal__(timers, ['loop', 'setTimeout', 'clearTimeout', 'setInterval', 'clearInterval', 'setImmediate', 'clearImmediate']); + + return timers; +} + diff --git a/autojs/src/main/java/com/stardust/autojs/ScriptEngineService.java b/autojs/src/main/java/com/stardust/autojs/ScriptEngineService.java index e5e8296f..92686195 100644 --- a/autojs/src/main/java/com/stardust/autojs/ScriptEngineService.java +++ b/autojs/src/main/java/com/stardust/autojs/ScriptEngineService.java @@ -55,7 +55,7 @@ public class ScriptEngineService { } private void onFinish(ScriptExecution execution) { - execution.getRuntime().onStop(); + execution.getRuntime().onExit(); } @Override diff --git a/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.java b/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.java index 362d02a6..b289ba7b 100644 --- a/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.java +++ b/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.java @@ -7,6 +7,7 @@ import com.stardust.autojs.rhino.AndroidContextFactory; import com.stardust.autojs.rhino.RhinoAndroidHelper; import com.stardust.autojs.runtime.ScriptInterruptedException; import com.stardust.autojs.runtime.api.Events; +import com.stardust.autojs.runtime.api.Timers; import com.stardust.autojs.script.ScriptSource; import com.stardust.pio.UncheckedIOException; @@ -76,7 +77,7 @@ public class RhinoJavaScriptEngine implements ScriptEngine { public void forceStop() { Log.d(LOG_TAG, "forceStop: interrupt Thread: " + mThread); mThread.interrupt(); - Events.quitLooperIfNeeded(mThread); + Timers.quitLooperIfNeeded(mThread); } public RhinoJavaScriptEngineManager getEngineManager() { @@ -90,7 +91,7 @@ public class RhinoJavaScriptEngine implements ScriptEngine { contextCount--; Log.d(LOG_TAG, "contextCount = " + contextCount); mEngineManager.removeEngine(this); - Events.removeThreadRecord(mThread); + Timers.removeThreadRecord(mThread); mDestroyed = true; } diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/AbstractScriptRuntime.java b/autojs/src/main/java/com/stardust/autojs/runtime/AbstractScriptRuntime.java index 0ca6081f..803991dd 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/AbstractScriptRuntime.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/AbstractScriptRuntime.java @@ -7,8 +7,10 @@ import android.support.annotation.CallSuper; import com.stardust.autojs.engine.ScriptEngine; import com.stardust.autojs.runtime.api.AbstractShell; import com.stardust.autojs.runtime.api.AppUtils; +import com.stardust.autojs.runtime.api.ScriptBridges; import com.stardust.autojs.runtime.api.Console; import com.stardust.autojs.runtime.api.Events; +import com.stardust.autojs.runtime.api.Timers; import com.stardust.autojs.runtime.api.UiSelector; import com.stardust.autojs.runtime.api.image.Images; import com.stardust.autojs.runtime.api.image.ScreenCaptureRequester; @@ -19,6 +21,8 @@ import com.stardust.util.ScreenMetrics; import com.stardust.util.UiHandler; import com.stardust.view.accessibility.AccessibilityInfoProvider; +import org.mozilla.javascript.annotations.JSFunction; + import java.lang.ref.WeakReference; /** @@ -48,6 +52,12 @@ public abstract class AbstractScriptRuntime { @ScriptVariable public Events events; + @ScriptVariable + public ScriptBridges bridges = new ScriptBridges(); + + @ScriptVariable + public Timers timers = new Timers(bridges); + private Images images; private static WeakReference applicationContext; @@ -63,7 +73,7 @@ public abstract class AbstractScriptRuntime { images = new Images(context, this, screenCaptureRequester); } dialogs = new Dialogs(app, uiHandler); - events = new Events(context, bridge); + events = new Events(context, bridge, bridges, timers); } public static void setApplicationContext(Context context) { @@ -105,7 +115,7 @@ public abstract class AbstractScriptRuntime { public abstract void loadJar(String path); @ScriptInterface - public abstract void stop(); + public abstract void exit(); @ScriptInterface public abstract void setScreenMetrics(int width, int height); @@ -116,7 +126,7 @@ public abstract class AbstractScriptRuntime { public abstract void ensureAccessibilityServiceEnabled(); @CallSuper - public void onStop() { + public void onExit() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { images.releaseScreenCapturer(); } 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 bda7e736..7d4188e0 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java @@ -1,7 +1,6 @@ package com.stardust.autojs.runtime; import android.os.Build; -import android.os.Handler; import android.os.Looper; import com.stardust.autojs.R; @@ -186,11 +185,17 @@ public class ScriptRuntime extends AbstractScriptRuntime { } } - public void stop() { + public void exit() { Thread.currentThread().interrupt(); throw new ScriptInterruptedException(); } + @Deprecated + public void stop() { + exit(); + } + + @Override public void setScreenMetrics(int width, int height) { mScreenMetrics.setScreenMetrics(width, height); @@ -206,8 +211,8 @@ public class ScriptRuntime extends AbstractScriptRuntime { } @Override - public void onStop() { - super.onStop(); + public void onExit() { + super.onExit(); if (mRootShell != null) { mRootShell.exit(); mRootShell = null; diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/AppUtils.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/AppUtils.java index 3a9d4047..89f2907e 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/AppUtils.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/AppUtils.java @@ -105,7 +105,7 @@ public class AppUtils { @ScriptInterface public void openUrl(String url) { - if (!url.startsWith("http://") || !url.startsWith("https://")) { + if (!url.startsWith("http://") && !url.startsWith("https://")) { url = "http://" + url; } mContext.startActivity(new Intent(Intent.ACTION_VIEW) diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/EventEmitter.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/EventEmitter.java index d88a7276..dacce47b 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/EventEmitter.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/EventEmitter.java @@ -17,10 +17,6 @@ import java.util.concurrent.CopyOnWriteArrayList; public class EventEmitter { - public interface FunctionCaller { - Object call(Object func, Object[] arg); - } - private static class ListenerWrapper { Object listener; boolean isOnce; @@ -54,7 +50,7 @@ public class EventEmitter { Iterator listenerIterator = mListenerWrappers.iterator(); while (listenerIterator.hasNext()) { ListenerWrapper listenerWrapper = listenerIterator.next(); - call(listenerWrapper.listener, args); + mBridges.callFunction(listenerWrapper.listener, EventEmitter.this, args); if (listenerWrapper.isOnce) { listenerIterator.remove(); } @@ -94,7 +90,11 @@ public class EventEmitter { private Map mListenersMap = new HashMap<>(); public static int defaultMaxListeners = 10; private int mMaxListeners = defaultMaxListeners; - private FunctionCaller mFunctionCaller; + ScriptBridges mBridges; + + public EventEmitter(ScriptBridges bridges) { + mBridges = bridges; + } public EventEmitter once(String eventName, Object listener) { getListeners(eventName).add(listener, true); @@ -184,14 +184,5 @@ public class EventEmitter { return defaultMaxListeners; } - public void setFunctionCaller(FunctionCaller functionCaller) { - mFunctionCaller = functionCaller; - } - - protected void call(Object func, Object[] args) { - if (mFunctionCaller != null) { - mFunctionCaller.call(func, args); - } - } } diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Events.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Events.java index 28badf3b..65b5170c 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Events.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Events.java @@ -23,27 +23,31 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver private static final String PREFIX_KEY_DOWN = "__key_down__#"; private static final String PREFIX_KEY_UP = "__key_up__#"; - private static final Object[] NO_ARGUMENT = new Object[0]; - private static ConcurrentHashMap sLoopers = new ConcurrentHashMap<>(); private AccessibilityBridge mAccessibilityBridge; - private Handler mHandler; private Context mContext; private TouchObserver mTouchObserver; + private Timers mTimers; private long mLastTouchEventMillis; private long mTouchEventTimeout = 10; private boolean mListeningKey = false; - public Events(Context context, AccessibilityBridge accessibilityBridge) { + public Events(Context context, AccessibilityBridge accessibilityBridge, ScriptBridges bridges, Timers timers) { + super(bridges); mAccessibilityBridge = accessibilityBridge; mContext = context; + mTimers = timers; + } + + public EventEmitter emitter(){ + return new EventEmitter(mBridges); } public void observeKey() { if (mListeningKey) return; mListeningKey = true; - prepareLoopIfNeeded(); + mTimers.prepareLoopIfNeeded(); mAccessibilityBridge.ensureServiceEnabled(); AccessibilityService service = mAccessibilityBridge.getService(); if (service == null) @@ -52,7 +56,7 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver } public void observeTouch() { - prepareLoopIfNeeded(); + mTimers.prepareLoopIfNeeded(); if (mTouchObserver != null) return; mTouchObserver = new TouchObserver(mContext); @@ -60,28 +64,6 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver mTouchObserver.observe(); } - public void setTimeout(final Object listener, long t) { - prepareLoopIfNeeded(); - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - call(listener, NO_ARGUMENT); - } - }, t); - } - - private void prepareLoopIfNeeded() { - if (Looper.myLooper() != null) - return; - Looper.prepare(); - sLoopers.put(Thread.currentThread(), Looper.myLooper()); - mHandler = new Handler(); - } - - public void loop() { - Looper.loop(); - } - public Events onKeyDown(String keyName, Object listener) { on(PREFIX_KEY_DOWN + keyName, listener); return this; @@ -146,7 +128,7 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver @Override public void onKeyEvent(final int keyCode, final KeyEvent event) { - mHandler.post(new Runnable() { + mTimers.post(new Runnable() { @Override public void run() { String keyName = KeyEvent.keyCodeToString(keyCode).substring(8).toLowerCase(); @@ -169,7 +151,7 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver return; } mLastTouchEventMillis = System.currentTimeMillis(); - mHandler.post(new Runnable() { + mTimers.post(new Runnable() { @Override public void run() { emit("touch", new Point(x, y)); @@ -177,15 +159,5 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver }); } - public static void removeThreadRecord(Thread thread) { - sLoopers.remove(thread); - } - - public static void quitLooperIfNeeded(Thread thread) { - Looper looper = sLoopers.get(thread); - if (looper != null) { - looper.quit(); - } - } } diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/ScriptBridges.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/ScriptBridges.java new file mode 100644 index 00000000..131b9767 --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/ScriptBridges.java @@ -0,0 +1,28 @@ +package com.stardust.autojs.runtime.api; + +/** + * Created by Stardust on 2017/7/21. + */ + +public class ScriptBridges { + + + public interface FunctionCaller { + + Object[] NO_ARGUMENTS = new Object[0]; + + Object call(Object func, Object target, Object[] arg); + } + + private FunctionCaller mFunctionCaller; + + public void setFunctionCaller(FunctionCaller functionCaller) { + mFunctionCaller = functionCaller; + } + + public Object callFunction(Object func, Object target, Object[] args) { + if (mFunctionCaller == null) + throw new IllegalStateException("no function caller"); + return mFunctionCaller.call(func, target, args); + } +} 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 new file mode 100644 index 00000000..ea949b0d --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Timers.java @@ -0,0 +1,121 @@ +package com.stardust.autojs.runtime.api; + +import android.os.Handler; +import android.os.Looper; +import android.util.SparseArray; + +import java.util.concurrent.ConcurrentHashMap; + +/** + * Created by Stardust on 2017/7/21. + */ + +public class Timers { + + private static ConcurrentHashMap sLoopers = new ConcurrentHashMap<>(); + private Handler mHandler; + private SparseArray mHandlerCallbacks = new SparseArray<>(); + private int mCallbackMaxId = 0; + private ScriptBridges mBridges; + + public Timers(ScriptBridges bridges) { + mBridges = bridges; + } + + public int setTimeout(final Object listener, long delay, final Object... args) { + prepareLoopIfNeeded(); + mCallbackMaxId++; + final int id = mCallbackMaxId; + Runnable r = new Runnable() { + @Override + public void run() { + mBridges.callFunction(listener, null, args); + mHandlerCallbacks.remove(id); + } + }; + mHandlerCallbacks.put(id, r); + mHandler.postDelayed(r, delay); + return id; + } + + public void post(Runnable r) { + mHandler.post(r); + } + + public void clearTimeout(int id) { + clearCallback(id); + } + + public int setInterval(final Object listener, final long interval, final Object... args) { + prepareLoopIfNeeded(); + mCallbackMaxId++; + final int id = mCallbackMaxId; + Runnable r = new Runnable() { + @Override + public void run() { + mBridges.callFunction(listener, null, args); + mHandler.postDelayed(this, interval); + } + }; + mHandlerCallbacks.put(id, r); + mHandler.postDelayed(r, interval); + return id; + } + + public void clearInterval(int id) { + clearTimeout(id); + } + + public int setImmediate(final Object listener, final Object... args) { + prepareLoopIfNeeded(); + mCallbackMaxId++; + final int id = mCallbackMaxId; + Runnable r = new Runnable() { + @Override + public void run() { + mBridges.callFunction(listener, null, args); + mHandlerCallbacks.remove(id); + } + }; + mHandlerCallbacks.put(id, r); + mHandler.post(r); + return id; + } + + public void clearImmediate(int id) { + clearCallback(id); + } + + private void clearCallback(int id) { + Runnable callback = mHandlerCallbacks.get(id); + if (callback != null) { + mHandler.removeCallbacks(callback); + mHandlerCallbacks.remove(id); + } + } + + public void prepareLoopIfNeeded() { + if (Looper.myLooper() != null) + return; + Looper.prepare(); + sLoopers.put(Thread.currentThread(), Looper.myLooper()); + mHandler = new Handler(); + } + + public void loop() { + Looper.loop(); + } + + public static void removeThreadRecord(Thread thread) { + sLoopers.remove(thread); + } + + public static void quitLooperIfNeeded(Thread thread) { + Looper looper = sLoopers.get(thread); + if (looper != null) { + looper.quit(); + } + } + + +} diff --git a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java index 6f595397..929e0d37 100644 --- a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java +++ b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java @@ -39,8 +39,6 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi private static boolean containsAllEventTypes = false; private static final Set eventTypes = new HashSet<>(); private OnKeyListener.Observer mOnKeyObserver = new OnKeyListener.Observer(); - private volatile AccessibilityNodeInfo mRootInActiveWindow; - private Timer mTimer; private Handler mHandler; private ExecutorService mKeyEventExecutor; @@ -105,14 +103,16 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi @Override public AccessibilityNodeInfo getRootInActiveWindow() { - return mRootInActiveWindow; + try { + return super.getRootInActiveWindow(); + } catch (IllegalStateException e) { + return null; + } } @Override public void onDestroy() { instance = null; - if (mTimer != null) - mTimer.cancel(); if (mKeyEventExecutor != null) mKeyEventExecutor.shutdownNow(); super.onDestroy(); @@ -128,23 +128,6 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi ENABLED.signalAll(); LOCK.unlock(); mHandler = new Handler(); - mTimer = new Timer(); - mTimer.schedule(new TimerTask() { - @Override - public void run() { - mHandler.post(new Runnable() { - @Override - public void run() { - AccessibilityNodeInfo root = superGetRootInActiveWindow(); - if (root != null) { - mRootInActiveWindow = root; - Log.d(TAG, "getRootInActiveWindow: " + root); - } - } - }); - - } - }, 0, 100); // FIXME: 2017/2/12 有时在无障碍中开启服务后这里不会调用服务也不会运行,安卓的BUG??? }