diff --git a/app/build.gradle b/app/build.gradle index a875da02..c5e6e837 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "com.stardust.scriptdroid" minSdkVersion 17 targetSdkVersion 23 - versionCode 151 - versionName "2.0.14 Beta2.1" + versionCode 155 + versionName "2.0.15 Alpha3" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true ndk { diff --git a/app/src/main/assets/help/catalogue.json b/app/src/main/assets/help/catalogue.json index 13871677..fb0922dc 100644 --- a/app/src/main/assets/help/catalogue.json +++ b/app/src/main/assets/help/catalogue.json @@ -49,6 +49,16 @@ "type": "markdown", "path":"documentation" }, + { + "title": "事件与按键、触摸监听", + "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 7198c4bd..a74e205d 100644 --- a/app/src/main/assets/help/documentation/一般常用函数.md +++ b/app/src/main/assets/help/documentation/一般常用函数.md @@ -1,7 +1,8 @@ -### sleep(n) +### sleep(\[n\]) * n \ 毫秒数 暂停运行n**毫秒**的时间。1秒等于1000毫秒。 +如果不加参数n,则会一直暂停,通常用于有事件监听的脚本中使用。 ### launchPackage(packageName) * packageName \ 应用包名。参见《Shell命令:应用包名》章节。 diff --git a/app/src/main/assets/help/documentation/事件与按键、触摸监听.md b/app/src/main/assets/help/documentation/事件与按键、触摸监听.md new file mode 100644 index 00000000..2512ff83 --- /dev/null +++ b/app/src/main/assets/help/documentation/事件与按键、触摸监听.md @@ -0,0 +1,380 @@ + +目录: +* [events](#events) +* [EventEmitter](#EventEmitter) +* [KeyEvent](#KeyEvent) + +# events + +events也是一个\[EventEmitter\](#EventEmitter),可以使用EventEmitter的所有函数,这里只介绍其自身的函数。 + +注意,events内置的触摸、按键事件必须调用loop()函数才能正常工作。例如: +``` +events.observeKey(); +events.onKeyDown("volume_up", function(event){ + toast("音量上键被按下了"); +}); +events.onKeyDown("menu", function(event){ + toast("菜单键被按下了"); +}); + +loop(); +//后面的语句不会再执行了 +``` + + +### events.emitter() + +返回一个新的EventEmitter。 + +### events.observeKey() + +启用按键监听,例如音量键、Home键。 + +在安卓4.3以上才能使用。 + +### events.onKeyDown(keyName, listener) +* keyName \ 要监听的按键名称 +* listener \ 参数为一个[KeyEvent](#KeyEvent) + +注册一个按键监听函数,当有keyName对应的按键被按下会调用该函数。按键名称主要包括: +* volume_up 音量上键 +* volume_down 音量下键 +* home 主屏幕键 +* back 返回键 +* menu 菜单键 + +### events.onKeyUp(keyName, listener) +* keyName \ 要监听的按键名称 +* listener \ 参数为一个[KeyEvent](#KeyEvent) + + +注册一个按键监听函数,当有keyName对应的按键弹起时会调用该函数。 + +### events.onceKeyDown(keyName, listener) +* keyName \ 要监听的按键名称 +* listener \ 参数为一个[KeyEvent](#KeyEvent) + +注册一个按键监听函数,当有keyName对应的按键被按下时会调用该函数,之后会注销该按键监听。也就是listener只会被调用一次。 + +### events.onceKeyUp(keyName, listener) +* keyName \ 要监听的按键名称 +* listener \ 参数为一个[KeyEvent](#KeyEvent) + +注册一个按键监听函数,当有keyName对应的按键弹起时会调用该函数,之后会注销该按键监听。也就是listener只会被调用一次。 + +### events.removeAllKeyDownListeners(keyName) +* keyName \ 按键名称 + +删除该按键的KeyDown事件的所有监听。 + +### events.removeAllKeyUpListeners(keyName) +* keyName \ 按键名称 + +删除该按键的KeyUp事件的所有监听。 + +### events.observeTouch() + +启用屏幕触摸监听。(需要root权限) + +### events.setTouchEventTimeout(timeout) +* timeout \ 两个触摸事件的最小间隔。单位毫秒。默认为10毫秒。 + +设置两个触摸事件分发的最小时间间隔。 + +例如间隔为10毫秒的话,前一个触摸事件发生并被注册的监听器处理后,至少要过10毫秒才能分发和处理下一个触摸事件,这10毫秒之间的触摸将会被忽略。 + +### events.getTouchEventTimeout() + +返回触摸事件的最小时间间隔。 + +### events.onTouch(listener) +* listener \ 参数为Point的函数 + +注册一个触摸监听函数。 + +例如: +``` +events.observeTouch(); +events.onTouch(function(p){ + log(p.x + ", " + p.y); +}); +events.loop(); +``` + +### events.removeAllTouchListeners() + +删除所有事件监听函数。 + +### key事件 + +当有按键被按下或弹起时会触发该事件。 +``` +"auto"; +events.observeKey(); +events.on("key", function(keyCode, event){ + //处理按键事件 +}); +events.loop(); +``` +其中监听器的参数KeyCode包括: +* KeyEvent.KEYCODE_HOME 主页键 +* KeyEvent.KEYCODE_BACK 返回键 +* KeyEvent.KEYCODE_MENU 菜单键 +* KeyEvent.KEYCODE_VOLUME_UP 音量上键 +* KeyEvent.KEYCODE_VOLUME_DOWN 音量下键 + +### key_down事件 + +当有按键被按下时会触发该事件。 +``` +"auto"; +events.observeKey(); +events.on("key_down", function(keyCode, event){ + //处理按键按下事件 +}); +events.loop(); +``` + +### key_up事件 + +当有按键弹起时会触发该事件。 +``` +"auto"; +events.observeKey(); +events.on("key_up", function(keyCode, event){ + //处理按键弹起事件 +}); +events.loop(); +``` + + +# EventEmitter + +### EventEmitter.defaultMaxListeners + +每个事件默认可以注册最多 10 个监听器。 单个 EventEmitter 实例的限制可以使用 emitter.setMaxListeners(n) 方法改变。 所有 EventEmitter 实例的默认值可以使用 EventEmitter.defaultMaxListeners 属性改变。 + +设置 EventEmitter.defaultMaxListeners 要谨慎,因为会影响所有 EventEmitter 实例,包括之前创建的。 因而,调用 emitter.setMaxListeners(n) 优先于 EventEmitter.defaultMaxListeners。 + +注意,与NodeJs不同,**这是一个硬性限制**。 EventEmitter 实例不允许添加更多的监听器,监听器超过最大数量时会抛出TooManyListenersException。 +``` +emitter.setMaxListeners(emitter.getMaxListeners() + 1); +emitter.once('event', () => { + // 做些操作 + emitter.setMaxListeners(Math.max(emitter.getMaxListeners() - 1, 0)); +}); +``` +## EventEmitter.addListener(eventName, listener) +* eventName \ +* listener \ +emitter.on(eventName, listener) 的别名。 + +### EventEmitter.emit(eventName\[, ...args\]) +* eventName +* args + +按监听器的注册顺序,同步地调用每个注册到名为 eventName 事件的监听器,并传入提供的参数。 + +如果事件有监听器,则返回 true ,否则返回 false。 + +### EventEmitter.eventNames() + +返回一个列出触发器已注册监听器的事件的数组。 数组中的值为字符串或符号。 +``` +const myEE = events.emitter(); +myEE.on('foo', () => {}); +myEE.on('bar', () => {}); + +const sym = Symbol('symbol'); +myEE.on(sym, () => {}); + +console.log(myEE.eventNames()); +// 打印: [ 'foo', 'bar', Symbol(symbol) ] +``` +### EventEmitter.getMaxListeners()# + +返回 EventEmitter 当前的最大监听器限制值,该值可以通过 emitter.setMaxListeners(n) 设置或默认为 EventEmitter.defaultMaxListeners。 + +### EventEmitter.listenerCount(eventName) +* eventName 正在被监听的事件名 + +返回正在监听名为 eventName 的事件的监听器的数量。 + +### EventEmitter.listeners(eventName) +* eventName + +返回名为 eventName 的事件的监听器数组的副本。 +``` +server.on('connection', (stream) => { + console.log('someone connected!'); +}); +console.log(util.inspect(server.listeners('connection'))); +// 打印: [ [Function] ] +``` + +### EventEmitter.on(eventName, listener) +* eventName 事件名 +* listener 回调函数 + +添加 listener 函数到名为 eventName 的事件的监听器数组的末尾。 不会检查 listener 是否已被添加。 多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次。 +``` +server.on('connection', (stream) => { + console.log('有连接!'); +}); +``` +返回一个 EventEmitter 引用,可以链式调用。 + +默认情况下,事件监听器会按照添加的顺序依次调用。 emitter.prependListener() 方法可用于将事件监听器添加到监听器数组的开头。 +``` +const myEE = events.emitter(); +myEE.on('foo', () => console.log('a')); +myEE.prependListener('foo', () => console.log('b')); +myEE.emit('foo'); +// 打印: +// b +// a +``` + + +### EventEmitter.once(eventName, listener)# +* eventName 事件名 +* listener 回调函数 + +添加一个单次 listener 函数到名为 eventName 的事件。 下次触发 eventName 事件时,监听器会被移除,然后调用。 +``` +server.once('connection', (stream) => { + console.log('首次调用!'); +}); +``` +返回一个 EventEmitter 引用,可以链式调用。 + +默认情况下,事件监听器会按照添加的顺序依次调用。 emitter.prependOnceListener() 方法可用于将事件监听器添加到监听器数组的开头。 +``` +const myEE = events.emitter(); +myEE.once('foo', () => console.log('a')); +myEE.prependOnceListener('foo', () => console.log('b')); +myEE.emit('foo'); +// 打印: +// b +// a +``` + +### EventEmitter.prependListener(eventName, listener) +* eventName 事件名 +* listener 回调函数 + +添加 listener 函数到名为 eventName 的事件的监听器数组的开头。 不会检查 listener 是否已被添加。 多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次。 +``` +server.prependListener('connection', (stream) => { + console.log('有连接!'); +}); +``` +返回一个 EventEmitter 引用,可以链式调用。 + +### EventEmitter.prependOnceListener(eventName, listener) +* eventName 事件名 +* listener 回调函数 + +添加一个单次 listener 函数到名为 eventName 的事件的监听器数组的开头。 下次触发 eventName 事件时,监听器会被移除,然后调用。 +``` +server.prependOnceListener('connection', (stream) => { + console.log('首次调用!'); +}); +``` +返回一个 EventEmitter 引用,可以链式调用。 + +### EventEmitter.removeAllListeners(\[eventName\]) +* eventName + +移除全部或指定 eventName 的监听器。 + +注意,在代码中移除其他地方添加的监听器是一个不好的做法,尤其是当 EventEmitter 实例是其他组件或模块创建的。 + +返回一个 EventEmitter 引用,可以链式调用。 + +### EventEmitter.removeListener(eventName, listener) +* eventName +* listener + +从名为 eventName 的事件的监听器数组中移除指定的 listener。 +``` +const callback = (stream) => { + console.log('有连接!'); +}; +server.on('connection', callback); +// ... +server.removeListener('connection', callback); +``` +removeListener 最多只会从监听器数组里移除一个监听器实例。 如果任何单一的监听器被多次添加到指定 eventName 的监听器数组中,则必须多次调用 removeListener 才能移除每个实例。 + + +注意,一旦一个事件被触发,所有绑定到它的监听器都会按顺序依次触发。 这意味着,在事件触发后、最后一个监听器完成执行前,任何 removeListener() 或 removeAllListeners() 调用都不会从 emit() 中移除它们。 随后的事件会像预期的那样发生。 +``` +const myEmitter = events.emitter(); + +const callbackA = () => { + console.log('A'); + myEmitter.removeListener('event', callbackB); +}; + +const callbackB = () => { + console.log('B'); +}; + +myEmitter.on('event', callbackA); + +myEmitter.on('event', callbackB); + +// callbackA 移除了监听器 callbackB,但它依然会被调用。 +// 触发是内部的监听器数组为 [callbackA, callbackB] +myEmitter.emit('event'); +// 打印: +// A +// B + +// callbackB 被移除了。 +// 内部监听器数组为 [callbackA] +myEmitter.emit('event'); +// 打印: +// A +``` +因为监听器是使用内部数组进行管理的,所以调用它会改变在监听器被移除后注册的任何监听器的位置索引。 虽然这不会影响监听器的调用顺序,但意味着由 emitter.listeners() 方法返回的监听器数组副本需要被重新创建。 + +返回一个 EventEmitter 引用,可以链式调用。 + +### EventEmitter.setMaxListeners(n) +* n + +默认情况下,如果为特定事件添加了超过 10 个监听器,则 EventEmitter 会打印一个警告。 此限制有助于寻找内存泄露。 但是,并不是所有的事件都要被限为 10 个。 emitter.setMaxListeners() 方法允许修改指定的 EventEmitter 实例的限制。 值设为 Infinity(或 0)表明不限制监听器的数量。 + +返回一个 EventEmitter 引用,可以链式调用。 + +# KeyEvent + +### KeyEvent.getAction() + +返回事件的动作。包括: +* KeyEvent.ACTION_DOWN 按下事件 +* KeyEvent.ACTION_UP 弹起事件 + +### KeyEvent.getKeyCode() + +返回按键的键值。包括: +* KeyEvent.KEYCODE_HOME 主页键 +* KeyEvent.KEYCODE_BACK 返回键 +* KeyEvent.KEYCODE_MENU 菜单键 +* KeyEvent.KEYCODE_VOLUME_UP 音量上键 +* KeyEvent.KEYCODE_VOLUME_DOWN 音量下键 + +### KeyEvent.getEventTime() + +返回事件发生的时间戳。 + +### KeyEvent.getDownTime() + +返回最近一次按下事件的时间戳。如果本身是按下事件,则与getEventTime()相同。 + +### KeyEvent.keyCodeToString(keyCode) + +把键值转换为字符串。例如KEYCODE_HOME转换为"KEYCODE_HOME"。 \ No newline at end of file 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/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/assets/sample/QQ与微信/QQ加群成员脚本.js b/app/src/main/assets/sample/QQ与微信/QQ加群成员脚本.js index 344fb9fb..92665414 100644 --- a/app/src/main/assets/sample/QQ与微信/QQ加群成员脚本.js +++ b/app/src/main/assets/sample/QQ与微信/QQ加群成员脚本.js @@ -27,6 +27,7 @@ while(true){ addAsFriend(); sleep(延迟); } + list.scrollForward(); } function isGroupMember(child){ diff --git a/app/src/main/assets/sample/QQ与微信/QQ空间点赞.js b/app/src/main/assets/sample/QQ与微信/QQ空间点赞.js index 3b6a4209..a3e349b4 100644 --- a/app/src/main/assets/sample/QQ与微信/QQ空间点赞.js +++ b/app/src/main/assets/sample/QQ与微信/QQ空间点赞.js @@ -62,7 +62,7 @@ function isImage(item){ function notLiked(likeIcon){ var x = likeIcon.bounds().centerX(); var y = likeIcon.bounds().centerY(); - return images.detectsColor(capture, x, y, 0x767886); + return images.detectsColor(capture, 0x767886, x, y); } toast("请打开QQ空间并慢慢下滑"); diff --git a/app/src/main/assets/sample/QQ与微信/循环发送消息.js b/app/src/main/assets/sample/QQ与微信/循环发送消息.js index a83816da..0dadf495 100644 --- a/app/src/main/assets/sample/QQ与微信/循环发送消息.js +++ b/app/src/main/assets/sample/QQ与微信/循环发送消息.js @@ -7,5 +7,4 @@ sleep(500); while(true){ setText("我喜欢你"); click("发送"); - sleep(200); } \ No newline at end of file diff --git a/app/src/main/assets/sample/QQ与微信/自动取消金豆.js b/app/src/main/assets/sample/QQ与微信/自动取消金豆.js new file mode 100644 index 00000000..dca7d488 --- /dev/null +++ b/app/src/main/assets/sample/QQ与微信/自动取消金豆.js @@ -0,0 +1,10 @@ +"auto"; + +toast("本脚本需要和名片点赞脚本同时运行才有效"); + +while(true){ + if(currentActivity() == "com.tencent.mobileqq.activity.VisitorsActivity"){ + click("取消"); + sleep(300); + } +} \ 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..f49e92e4 --- /dev/null +++ b/app/src/main/assets/sample/事件与按键、触摸监听/按键监听.js @@ -0,0 +1,34 @@ +"auto"; + +events.observeKey(); + +var keyNames = { + "KEYCODE_VOLUME_UP": "音量上键", + "KEYCODE_VOLUME_DOWN": "音量下键", + "KEYCODE_HOME": "Home键", + "KEYCODE_BACK": "返回键", + "KEYCODE_MENU": "菜单键", + "KEYCODE_POWER": "电源键", +}; + +events.on("key", function(code, event){ + var keyName = getKeyName(code, event); + if(event.getAction() == event.ACTION_DOWN){ + toast(keyName + "被按下"); + }else if(event.getAction() == event.ACTION_UP){ + toast(keyName + "弹起"); + } +}); + +loop(); + + + +function getKeyName(code, event){ + var keyCodeStr = event.keyCodeToString(code); + var keyName = keyNames[keyCodeStr]; + if(!keyName){ + return keyCodeStr; + } + return keyName; +} \ 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..b82cdde3 --- /dev/null +++ b/app/src/main/assets/sample/事件与按键、触摸监听/触摸监听.js @@ -0,0 +1,12 @@ + +events.observeTouch(); + +events.setTouchEventTimeout(30); + +toast("请在日志中查看触摸的点的坐标"); + +events.on("touch", function(point){ + log(point); +}); + +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..a0b88751 --- /dev/null +++ b/app/src/main/assets/sample/事件与按键、触摸监听/长按返回退出当前程序.js @@ -0,0 +1,28 @@ +"auto"; + +var 长按间隔 = 1500; + +var curPackage = null; +var timeoutId = null; + +events.observeKey(); + +events.onKeyDown("back", function(event){ + curPackage = currentPackage(); + timeoutId = setTimeout(function(){ + backBackBackBack(); + }, 长按间隔); +}); + +events.onKeyUp("back", function(event){ + clearTimeout(timeoutId); +}); + +loop(); + +function backBackBackBack(){ + while(curPackage == currentPackage()){ + back(); + sleep(200); + } +} \ 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..f5782d60 --- /dev/null +++ b/app/src/main/assets/sample/事件与按键、触摸监听/音量键控制程序.js @@ -0,0 +1,34 @@ +"auto"; + +events.observeKey(); + +var interval = 5000; +var task = task1; + +events.onKeyDown("volume_up", function(event){ + if(task == task1){ + task = task2; + }else{ + task = task1; + } + toast("任务已切换"); +}); + +events.onKeyDown("volume_down", function(event){ + toast("程序结束"); + exit(); +}); + +task(); + +loop(); + +function task1(){ + toast("任务1运行中,音量下键结束,音量上键切换任务"); + setTimeout(task, interval); +} + +function task2(){ + toast("任务2运行中,音量下键结束,音量上键切换任务"); + 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 index 5921958a..b6e784cb 100644 --- a/app/src/main/assets/sample/其他应用/贴吧一键签到.js +++ b/app/src/main/assets/sample/其他应用/贴吧一键签到.js @@ -2,39 +2,48 @@ launchApp("百度贴吧"); sleep(1000); -while(!click("进吧")); +id("tabcontainer").untilFind().each(function(tab){ + if(tab.childCount() != 5){ + return; + } + var ba = tab.child(1); + if(ba){ + ba.click(); + } +}); toast("开始签到"); var signed = {}; while(true){ - var list = id("fragment_pager").findOne().findOne(id("listview")); + var list = id("tab_content").findOne().findOne(id("listview")); list.children().each(function(child){ - if(child && child.className().endsWith("RelativeLayout")){ + if(child && child.className().endsWith("LinearLayout")){ if(child.childCount() > 1){ - sign(child); + sign(child.findOne(id("left_container"))); + sign(child.findOne(id("right_container"))); } } }); list.scrollForward(); } - -function sign(child){ - var li = child.child(1); - if(li && li.childCount() > 0){ - var name = li.child(0).text(); - if(signed[name]){ - return; - } - child.click(); - sleep(400); - for(var i = 0; i < 3; i ++){ - if(click("签到")){ - break; - } - sleep(200); - } - sleep(400); - while(!back()); - signed[name] = true; + +function sign(c){ + if(!c){ + return; + } + var name = c.child(0).text(); + if(signed[name]){ + return; + } + c.click(); + sleep(400); + for(var i = 0; i < 3; i ++){ + if(click("签到")){ + break; } + sleep(200); + } + sleep(600); + while(!back()); + signed[name] = true; } \ 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 5e44b536..a58f0e21 100644 --- a/app/src/main/java/com/stardust/scriptdroid/App.java +++ b/app/src/main/java/com/stardust/scriptdroid/App.java @@ -5,6 +5,7 @@ import android.app.Application; import android.content.IntentFilter; import android.os.Bundle; import android.support.multidex.MultiDexApplication; +import android.view.KeyEvent; import com.squareup.leakcanary.LeakCanary; import com.stardust.app.SimpleActivityLifecycleCallbacks; @@ -19,6 +20,8 @@ import com.stardust.theme.ThemeColor; import com.stardust.theme.ThemeColorManager; import com.stardust.util.ScreenMetrics; import com.stardust.util.UiHandler; +import com.stardust.view.accessibility.AccessibilityService; +import com.stardust.view.accessibility.OnKeyListener; import java.lang.ref.WeakReference; @@ -37,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); @@ -71,11 +72,12 @@ public class App extends MultiDexApplication { } private void initVolumeChangeObserver() { - registerReceiver(mVolumeChangeObserver, new IntentFilter(VolumeChangeObserver.ACTION_VOLUME_CHANGE)); - mVolumeChangeObserver.addOnVolumeChangeListener(new VolumeChangeObserver.OnVolumeChangeListener() { + AccessibilityService.getStickOnKeyObserver().addListener(new OnKeyListener() { @Override - public void onVolumeChange() { - if (Pref.isRunningVolumeControlEnabled()) { + public void onKeyEvent(int keyCode, KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN && + (keyCode == KeyEvent.KEYCODE_VOLUME_UP) + && Pref.isRunningVolumeControlEnabled()) { AutoJs.getInstance().getScriptEngineService().stopAllAndToast(); } } @@ -83,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/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/autojs/AutoJs.java b/app/src/main/java/com/stardust/scriptdroid/autojs/AutoJs.java index 22e03df3..cae2104b 100644 --- a/app/src/main/java/com/stardust/scriptdroid/autojs/AutoJs.java +++ b/app/src/main/java/com/stardust/scriptdroid/autojs/AutoJs.java @@ -6,6 +6,7 @@ import android.os.Build; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; +import android.view.KeyEvent; import com.stardust.app.OnActivityResultDelegate; import com.stardust.app.SimpleActivityLifecycleCallbacks; @@ -32,12 +33,12 @@ import com.stardust.util.ScreenMetrics; import com.stardust.util.Supplier; import com.stardust.util.UiHandler; import com.stardust.view.accessibility.AccessibilityInfoProvider; -import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.LayoutInspector; -import com.stardust.scriptdroid.external.floatingwindow.menu.record.accessibility.AccessibilityActionRecorder; +import com.stardust.autojs.runtime.record.accessibility.AccessibilityActionRecorder; import com.stardust.view.accessibility.AccessibilityService; import com.stardust.scriptdroid.tool.AccessibilityServiceTool; import com.stardust.scriptdroid.ui.console.JraskaConsole; import com.stardust.view.accessibility.AccessibilityServiceUtils; +import com.stardust.view.accessibility.LayoutInspector; /** @@ -177,7 +178,7 @@ public class AutoJs implements AccessibilityBridge { @Nullable @Override - public android.accessibilityservice.AccessibilityService getService() { + public AccessibilityService getService() { return AccessibilityService.getInstance(); } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/HoverMenuService.java b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/HoverMenuService.java index 8a52e75f..962a28f3 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/HoverMenuService.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/HoverMenuService.java @@ -17,7 +17,7 @@ import com.stardust.hover.SimpleHoverMenuTransitionListener; import com.stardust.hover.WindowHoverMenu; import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.autojs.AutoJs; -import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo; +import com.stardust.view.accessibility.NodeInfo; import com.stardust.scriptdroid.external.floatingwindow.menu.view.FloatingLayoutBoundsView; import com.stardust.scriptdroid.external.floatingwindow.menu.view.FloatingLayoutHierarchyView; import com.stardust.scriptdroid.tool.AccessibilityServiceTool; diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/content/MainMenuNavigatorContent.java b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/content/MainMenuNavigatorContent.java index dc6ed7c0..bcc261ab 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/content/MainMenuNavigatorContent.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/content/MainMenuNavigatorContent.java @@ -12,12 +12,12 @@ import com.stardust.scriptdroid.App; import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.autojs.AutoJs; import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService; -import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.LayoutInspector; import com.stardust.scriptdroid.tool.AccessibilityServiceTool; import com.stardust.scriptdroid.ui.main.MainActivity_; import com.stardust.util.ClipboardUtil; import com.stardust.util.MessageEvent; import com.stardust.view.accessibility.AccessibilityService; +import com.stardust.view.accessibility.LayoutInspector; import org.greenrobot.eventbus.Subscribe; 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 5f67a444..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,24 +4,26 @@ 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; +import com.stardust.autojs.runtime.record.inputevent.TouchRecorder; import com.stardust.scriptdroid.App; import com.stardust.scriptdroid.Pref; import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.accessibility.AccessibilityEventHelper; import com.stardust.scriptdroid.autojs.AutoJs; import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService; -import com.stardust.scriptdroid.external.floatingwindow.menu.record.Recorder; -import com.stardust.scriptdroid.external.floatingwindow.menu.record.accessibility.AccessibilityActionRecorder; -import com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent.KeyObserver; -import com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent.TouchRecorder; 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/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/LayoutBoundsView.java b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/LayoutBoundsView.java index b22fef68..95750881 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/LayoutBoundsView.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/LayoutBoundsView.java @@ -13,7 +13,7 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; -import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo; +import com.stardust.view.accessibility.NodeInfo; import com.stardust.util.ViewUtil; import java.util.ArrayList; diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/LayoutHierarchyView.java b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/LayoutHierarchyView.java index 0da221ff..c0381c7e 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/LayoutHierarchyView.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/LayoutHierarchyView.java @@ -14,7 +14,7 @@ import android.widget.ListView; import android.widget.TextView; import com.stardust.scriptdroid.R; -import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo; +import com.stardust.view.accessibility.NodeInfo; import com.stardust.util.ViewUtil; import com.stardust.widget.LevelBeamView; diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/NodeInfoView.java b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/NodeInfoView.java index 22762a6c..2a8c68e3 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/NodeInfoView.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/NodeInfoView.java @@ -12,7 +12,7 @@ import android.widget.TextView; import android.widget.Toast; import com.stardust.scriptdroid.R; -import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo; +import com.stardust.view.accessibility.NodeInfo; import com.stardust.util.ClipboardUtil; import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; @@ -22,7 +22,6 @@ import java.util.Arrays; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; -import butterknife.OnLongClick; import butterknife.Optional; /** diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/OnNodeInfoSelectListener.java b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/OnNodeInfoSelectListener.java index 04627361..5fb29295 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/OnNodeInfoSelectListener.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/view/OnNodeInfoSelectListener.java @@ -1,6 +1,6 @@ package com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view; -import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo; +import com.stardust.view.accessibility.NodeInfo; /** * Created by Stardust on 2017/3/10. diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/view/FloatingLayoutBoundsView.java b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/view/FloatingLayoutBoundsView.java index 100c3184..f6c6ba1a 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/view/FloatingLayoutBoundsView.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/view/FloatingLayoutBoundsView.java @@ -11,7 +11,7 @@ import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.Theme; import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService; -import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo; +import com.stardust.view.accessibility.NodeInfo; import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.LayoutBoundsView; import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.NodeInfoView; import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.OnNodeInfoSelectListener; diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/view/FloatingLayoutHierarchyView.java b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/view/FloatingLayoutHierarchyView.java index 534154e6..1ad55694 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/view/FloatingLayoutHierarchyView.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/view/FloatingLayoutHierarchyView.java @@ -11,7 +11,7 @@ import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.Theme; import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService; -import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo; +import com.stardust.view.accessibility.NodeInfo; import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.LayoutHierarchyView; import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.view.NodeInfoView; import com.stardust.util.MessageIntent; 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..e6bdae80 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,98 @@ 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); + if (c.isEmpty()) { + mOnCodeCompletionChangeListener.OnCodeCompletionChange(DEFAULT_CODE_COMPLETION_LIST, Collections.emptyList()); + return false; + } else { + mOnCodeCompletionChangeListener.OnCodeCompletionChange(c, Collections.emptyList()); + return true; + } + } + + 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..c030f5ac 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,20 @@ 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); + } + }); + }else { + mCodeCompletion.setGlobal(global); + mCodeCompletion.setVariableProperties(variables); + } + } public void setEditTextBridge(EditTextBridge editTextBridge) { @@ -91,16 +121,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 +149,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/java/com/stardust/scriptdroid/ui/main/task/TaskListRecyclerView.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/task/TaskListRecyclerView.java index 4771dc96..0750f619 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/task/TaskListRecyclerView.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/task/TaskListRecyclerView.java @@ -1,6 +1,7 @@ package com.stardust.scriptdroid.ui.main.task; import android.content.Context; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.ThemeColorRecyclerView; @@ -11,6 +12,8 @@ import android.view.ViewGroup; import android.widget.TextView; import android.workground.WrapContentLinearLayoutManager; +import com.afollestad.materialdialogs.DialogAction; +import com.afollestad.materialdialogs.MaterialDialog; import com.stardust.autojs.ScriptEngineService; import com.stardust.autojs.execution.ScriptExecution; import com.stardust.autojs.execution.ScriptExecutionListener; @@ -43,9 +46,19 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView implements Abst @Override public void onClick(View v) { int position = getChildViewHolder((View) v.getParent()).getAdapterPosition(); - if(position >= 0){ - mScriptEngines.get(position).forceStop(); - }else { + if (position >= 0) { + final ScriptEngine engine = mScriptEngines.get(position); + engine.forceStop(); + + postDelayed(new Runnable() { + @Override + public void run() { + if (!engine.isDestroyed()) { + onScriptANR(engine); + } + } + }, 3000); + } else { updateEngineList(); } } @@ -128,6 +141,10 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView implements Abst AutoJs.getInstance().getScriptEngineService().unregisterGlobalScriptExecutionListener(mScriptExecutionListener); } + private void onScriptANR(final ScriptEngine engine) { + // TODO: 2017/7/19 强制停止 + } + @Override public void onEngineCreate(final ScriptEngine engine) { synchronized (mScriptEngines) { diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index f4b7e1fc..3b0dd3b8 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -1,5 +1,5 @@ - AutoJs + Auto.js Settings Required by the script automatic operation (click, long press, slide, etc.). New File diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1c8dfea1..517fee27 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - AutoJs + Auto.js 设置 新建文件 从文件导入 @@ -81,8 +81,8 @@ 文件写入失败 加入QQ互赞&交流群 已复制到剪贴板 - 使用音量键控制 - 开启后每次音量变化会开始或停止脚本录制 + 使用音量下键控制 + 开启后每次音量下键会开始或停止脚本录制 key_use_volume_control_record 未安装手机QQ 编辑 @@ -129,7 +129,7 @@ 无障碍服务->AutoJs并开启]]> 脚本运行 key_use_volume_control_running - 每次音量键变化停止所有脚本 + 音量上键停止所有脚本 需要启用无障碍服务 运行方式 已开启 @@ -208,6 +208,8 @@ 打开无障碍服务 放弃录制 布局抓取失败,请关闭悬浮窗后动一下页面重试 + 没有相应,要强制停止吗 + 无 @@ -226,13 +228,11 @@ 默认 每天显示一次 - 关闭广告 Default OncePerDay - Off diff --git a/autojs/src/main/assets/javascript_engine_init.js b/autojs/src/main/assets/javascript_engine_init.js index 57803c14..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"]; + 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]; @@ -35,5 +44,6 @@ require("__general__")(__runtime__, this); } })(__that__); +__importClass__(android.view.KeyEvent); __importClass__(com.stardust.autojs.runtime.api.Shell); __importClass__(com.stardust.autojs.runtime.api.InputEventSender); diff --git a/autojs/src/main/assets/modules/__events__.js b/autojs/src/main/assets/modules/__events__.js new file mode 100644 index 00000000..44caf715 --- /dev/null +++ b/autojs/src/main/assets/modules/__events__.js @@ -0,0 +1,7 @@ + +module.exports = function(__runtime__, scope){ + var events = Object.create(__runtime__.events); + + return events; +} + diff --git a/autojs/src/main/assets/modules/__general__.js b/autojs/src/main/assets/modules/__general__.js index 07c3523d..88a25f8c 100644 --- a/autojs/src/main/assets/modules/__general__.js +++ b/autojs/src/main/assets/modules/__general__.js @@ -9,9 +9,7 @@ module.exports = function(__runtime__, scope){ scope.log(text); } - scope.sleep = function(millis){ - __runtime__.sleep(millis); - } + scope.sleep = __runtime__.sleep.bind(__runtime__); scope.isStopped = function(){ return __runtime__.isStopped(); @@ -21,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 bc252fbf..b289ba7b 100644 --- a/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.java +++ b/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.java @@ -1,10 +1,13 @@ package com.stardust.autojs.engine; +import android.os.Looper; import android.util.Log; 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; @@ -41,6 +44,7 @@ public class RhinoJavaScriptEngine implements ScriptEngine { private Thread mThread; private RhinoJavaScriptEngineManager mEngineManager; private Map mTags = new ConcurrentHashMap<>(); + private volatile boolean mDestroyed = false; public RhinoJavaScriptEngine(RhinoJavaScriptEngineManager engineManager) { mEngineManager = engineManager; @@ -73,6 +77,7 @@ public class RhinoJavaScriptEngine implements ScriptEngine { public void forceStop() { Log.d(LOG_TAG, "forceStop: interrupt Thread: " + mThread); mThread.interrupt(); + Timers.quitLooperIfNeeded(mThread); } public RhinoJavaScriptEngineManager getEngineManager() { @@ -86,6 +91,13 @@ public class RhinoJavaScriptEngine implements ScriptEngine { contextCount--; Log.d(LOG_TAG, "contextCount = " + contextCount); mEngineManager.removeEngine(this); + Timers.removeThreadRecord(mThread); + mDestroyed = true; + } + + @Override + public boolean isDestroyed() { + return mDestroyed; } @Override diff --git a/autojs/src/main/java/com/stardust/autojs/engine/ScriptEngine.java b/autojs/src/main/java/com/stardust/autojs/engine/ScriptEngine.java index 525fe2a0..f2b88fde 100644 --- a/autojs/src/main/java/com/stardust/autojs/engine/ScriptEngine.java +++ b/autojs/src/main/java/com/stardust/autojs/engine/ScriptEngine.java @@ -25,6 +25,8 @@ public interface ScriptEngine { void destroy(); + boolean isDestroyed(); + void setTag(String key, Object value); Object getTag(String key); 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 0ac908a8..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,7 +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; @@ -18,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; /** @@ -41,10 +46,18 @@ public abstract class AbstractScriptRuntime { @ScriptVariable public UI ui; - @ScriptVariable public Dialogs dialogs; + @ScriptVariable + public Events events; + + @ScriptVariable + public ScriptBridges bridges = new ScriptBridges(); + + @ScriptVariable + public Timers timers = new Timers(bridges); + private Images images; private static WeakReference applicationContext; @@ -54,11 +67,13 @@ public abstract class AbstractScriptRuntime { this.console = console; this.automator = new SimpleActionAutomator(bridge, this); this.info = bridge.getInfoProvider(); - this.ui = new UI(uiHandler.getContext()); + Context context = uiHandler.getContext(); + this.ui = new UI(context); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - images = new Images(uiHandler.getContext(), this, screenCaptureRequester); + images = new Images(context, this, screenCaptureRequester); } dialogs = new Dialogs(app, uiHandler); + events = new Events(context, bridge, bridges, timers); } public static void setApplicationContext(Context context) { @@ -100,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); @@ -111,10 +126,11 @@ 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(); } + events.recycle(); } public Object getImages() { diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/AccessibilityBridge.java b/autojs/src/main/java/com/stardust/autojs/runtime/AccessibilityBridge.java index 9c8235e9..a35301e5 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/AccessibilityBridge.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/AccessibilityBridge.java @@ -1,11 +1,11 @@ package com.stardust.autojs.runtime; -import android.accessibilityservice.AccessibilityService; import android.support.annotation.Nullable; import com.stardust.automator.AccessibilityEventCommandHost; import com.stardust.automator.simple_action.SimpleActionPerformHost; import com.stardust.view.accessibility.AccessibilityInfoProvider; +import com.stardust.view.accessibility.AccessibilityService; /** * Created by Stardust on 2017/4/2. 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 68204ba2..7d4188e0 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java @@ -85,7 +85,6 @@ public class ScriptRuntime extends AbstractScriptRuntime { private UiHandler mUiHandler; private AccessibilityBridge mAccessibilityBridge; - private AbstractShell mRootShell; private Supplier mShellSupplier; private ScreenMetrics mScreenMetrics = new ScreenMetrics(); @@ -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 new file mode 100644 index 00000000..dacce47b --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/EventEmitter.java @@ -0,0 +1,188 @@ +package com.stardust.autojs.runtime.api; + +import android.support.annotation.NonNull; + +import com.stardust.autojs.runtime.ScriptStopException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.TooManyListenersException; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Created by Stardust on 2017/7/19. + */ + +public class EventEmitter { + + private static class ListenerWrapper { + Object listener; + boolean isOnce; + + public ListenerWrapper(Object listener, boolean isOnce) { + this.listener = listener; + this.isOnce = isOnce; + } + } + + + private class Listeners { + private CopyOnWriteArrayList mListenerWrappers = new CopyOnWriteArrayList<>(); + + void add(Object listener, boolean once) { + ensureListenersNotAtLimit(); + mListenerWrappers.add(new ListenerWrapper(listener, once)); + } + + private void ensureListenersNotAtLimit() { + if (mMaxListeners != 0 && mListenersMap.size() >= mMaxListeners) { + throw new ScriptStopException(new TooManyListenersException("max = " + mMaxListeners)); + } + } + + boolean empty() { + return mListenerWrappers.isEmpty(); + } + + void emit(Object[] args) { + Iterator listenerIterator = mListenerWrappers.iterator(); + while (listenerIterator.hasNext()) { + ListenerWrapper listenerWrapper = listenerIterator.next(); + mBridges.callFunction(listenerWrapper.listener, EventEmitter.this, args); + if (listenerWrapper.isOnce) { + listenerIterator.remove(); + } + } + } + + int count() { + return mListenerWrappers.size(); + } + + Object[] toArray() { + Iterator listenerIterator = mListenerWrappers.iterator(); + ArrayList listeners = new ArrayList<>(mListenerWrappers.size()); + while (listenerIterator.hasNext()) { + listeners.add(listenerIterator.next().listener); + } + return listeners.toArray(new Object[listeners.size()]); + } + + void prepend(Object listener, boolean once) { + ensureListenersNotAtLimit(); + mListenerWrappers.add(0, new ListenerWrapper(listener, once)); + } + + void remove(Object listener) { + Iterator listenerIterator = mListenerWrappers.iterator(); + while (listenerIterator.hasNext()) { + ListenerWrapper l = listenerIterator.next(); + if (l.listener == listener) { + listenerIterator.remove(); + break; + } + } + } + } + + private Map mListenersMap = new HashMap<>(); + public static int defaultMaxListeners = 10; + private int mMaxListeners = defaultMaxListeners; + ScriptBridges mBridges; + + public EventEmitter(ScriptBridges bridges) { + mBridges = bridges; + } + + public EventEmitter once(String eventName, Object listener) { + getListeners(eventName).add(listener, true); + return this; + } + + @NonNull + private Listeners getListeners(String eventName) { + Listeners listeners = mListenersMap.get(eventName); + if (listeners == null) { + listeners = new Listeners(); + mListenersMap.put(eventName, listeners); + } + return listeners; + } + + + public EventEmitter on(String eventName, Object listener) { + getListeners(eventName).add(listener, false); + return this; + } + + public EventEmitter addListener(String eventName, Object listener) { + return on(eventName, listener); + } + + public boolean emit(String eventName, Object... args) { + Listeners listeners = mListenersMap.get(eventName); + if (listeners == null || listeners.empty()) + return false; + listeners.emit(args); + return true; + } + + public String[] eventNames() { + return mListenersMap.keySet().toArray(new String[mListenersMap.size()]); + } + + public int listenerCount(String eventName) { + Listeners listeners = mListenersMap.get(eventName); + if (listeners == null) + return 0; + return listeners.count(); + } + + public Object[] listeners(String eventName) { + return getListeners(eventName).toArray(); + } + + public EventEmitter prependListener(String eventName, Object listener) { + getListeners(eventName).prepend(listener, false); + return this; + } + + public EventEmitter prependOnceListener(String eventName, Object listener) { + getListeners(eventName).prepend(listener, true); + return this; + } + + public EventEmitter removeAllListeners() { + mListenersMap.clear(); + return this; + } + + public EventEmitter removeAllListeners(String eventName) { + mListenersMap.remove(eventName); + return this; + } + + public EventEmitter removeListener(String eventName, Object listener) { + Listeners listeners = mListenersMap.get(eventName); + if (listeners != null) + listeners.remove(listener); + return this; + } + + public EventEmitter setMaxListeners(int n) { + mMaxListeners = n; + return this; + } + + public int getMaxListeners() { + return mMaxListeners; + } + + public static int defaultMaxListeners() { + return defaultMaxListeners; + } + + +} 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 new file mode 100644 index 00000000..65b5170c --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Events.java @@ -0,0 +1,163 @@ +package com.stardust.autojs.runtime.api; + +import android.content.Context; +import android.graphics.Point; +import android.os.Handler; +import android.os.Looper; +import android.util.SparseArray; +import android.view.KeyEvent; + +import com.stardust.autojs.runtime.AccessibilityBridge; +import com.stardust.autojs.runtime.ScriptStopException; +import com.stardust.autojs.runtime.record.inputevent.TouchObserver; +import com.stardust.view.accessibility.AccessibilityService; +import com.stardust.view.accessibility.OnKeyListener; + +import java.util.concurrent.ConcurrentHashMap; + +/** + * Created by Stardust on 2017/7/18. + */ + +public class Events extends EventEmitter implements OnKeyListener, TouchObserver.OnTouchEventListener { + + private static final String PREFIX_KEY_DOWN = "__key_down__#"; + private static final String PREFIX_KEY_UP = "__key_up__#"; + + private AccessibilityBridge mAccessibilityBridge; + 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, 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; + mTimers.prepareLoopIfNeeded(); + mAccessibilityBridge.ensureServiceEnabled(); + AccessibilityService service = mAccessibilityBridge.getService(); + if (service == null) + throw new ScriptStopException(); + service.getOnKeyObserver().addListener(this); + } + + public void observeTouch() { + mTimers.prepareLoopIfNeeded(); + if (mTouchObserver != null) + return; + mTouchObserver = new TouchObserver(mContext); + mTouchObserver.setOnTouchEventListener(this); + mTouchObserver.observe(); + } + + public Events onKeyDown(String keyName, Object listener) { + on(PREFIX_KEY_DOWN + keyName, listener); + return this; + } + + public Events onceKeyDown(String keyName, Object listener) { + once(PREFIX_KEY_DOWN + keyName, listener); + return this; + } + + public Events removeAllKeyDownListeners(String keyName) { + removeAllListeners(PREFIX_KEY_DOWN + keyName); + return this; + } + + public Events onKeyUp(String keyName, Object listener) { + on(PREFIX_KEY_UP + keyName, listener); + return this; + } + + public Events onceKeyUp(String keyName, Object listener) { + once(PREFIX_KEY_UP + keyName, listener); + return this; + } + + public Events removeAllKeyUpListeners(String keyName) { + removeAllListeners(PREFIX_KEY_UP + keyName); + return this; + } + + public Events onTouch(Object listener) { + on("touch", listener); + return this; + } + + public Events removeAllTouchListeners() { + removeAllListeners("touch"); + return this; + } + + public long getTouchEventTimeout() { + return mTouchEventTimeout; + } + + public void setTouchEventTimeout(long touchEventTimeout) { + mTouchEventTimeout = touchEventTimeout; + } + + + public void recycle() { + if (mListeningKey) { + AccessibilityService service = mAccessibilityBridge.getService(); + if (service != null) { + service.getOnKeyObserver().removeListener(this); + mListeningKey = false; + } + } + if (mTouchObserver != null) { + mTouchObserver.stop(); + } + } + + @Override + public void onKeyEvent(final int keyCode, final KeyEvent event) { + mTimers.post(new Runnable() { + @Override + public void run() { + String keyName = KeyEvent.keyCodeToString(keyCode).substring(8).toLowerCase(); + emit(keyName, event); + if (event.getAction() == KeyEvent.ACTION_DOWN) { + emit(PREFIX_KEY_DOWN + keyName, event); + emit("key_down", keyCode, event); + } else if (event.getAction() == KeyEvent.ACTION_UP) { + emit(PREFIX_KEY_UP + keyName, event); + emit("key_up", keyCode, event); + } + emit("key", keyCode, event); + } + }); + } + + @Override + public void onTouch(final int x, final int y) { + if (System.currentTimeMillis() - mLastTouchEventMillis < mTouchEventTimeout) { + return; + } + mLastTouchEventMillis = System.currentTimeMillis(); + mTimers.post(new Runnable() { + @Override + public void run() { + emit("touch", new Point(x, y)); + } + }); + } + + +} 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..3746008e --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Timers.java @@ -0,0 +1,125 @@ +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 callback, long delay, final Object... args) { + prepareLoopIfNeeded(); + mCallbackMaxId++; + final int id = mCallbackMaxId; + Runnable r = new Runnable() { + @Override + public void run() { + mBridges.callFunction(callback, 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(); + Looper looper = Looper.myLooper(); + if (looper != null) { + // null check is not necessary, just to make Android Studio happy + sLoopers.put(Thread.currentThread(), looper); + } + 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/autojs/src/main/java/com/stardust/autojs/runtime/api/UiSelector.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/UiSelector.java index 6bf3ea40..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; @@ -117,8 +104,7 @@ public class UiSelector extends UiGlobalSelector { @ScriptInterface @NonNull public UiObjectCollection untilFind() { - UiObjectCollection uiObjectCollection; - uiObjectCollection = find(); + UiObjectCollection uiObjectCollection = find(); while (uiObjectCollection.empty()) { if (Thread.currentThread().isInterrupted()) { throw new ScriptInterruptedException(); diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/internal/Functions.java b/autojs/src/main/java/com/stardust/autojs/runtime/internal/Functions.java new file mode 100644 index 00000000..c677cb06 --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/runtime/internal/Functions.java @@ -0,0 +1,50 @@ +package com.stardust.autojs.runtime.internal; + +/** + * Created by Stardust on 2017/7/19. + */ + +public class Functions { + + @SuppressWarnings("unchecked") + public static Object call(Func func, Object[] args) { + if (func instanceof Func0) { + return ((Func0) func).call(); + } else if (func instanceof Func1) { + return ((Func1) func).call(args[0]); + } else if (func instanceof Func2) { + return ((Func2) func).call(args[0], args[1]); + } else if (func instanceof Func3) { + return ((Func3) func).call(args[0], args[1], args[2]); + } else if (func instanceof Func4) { + return ((Func4) func).call(args[0], args[1], args[2], args[3]); + } + throw new IllegalArgumentException("Unknown func: " + func); + } + + public interface Func { + + } + + public interface Func0 extends Func { + R call(); + } + + public interface Func1 extends Func { + R call(T1 t1); + } + + public interface Func2 extends Func { + R call(T1 t1, T2 t2); + } + + public interface Func3 extends Func { + R call(T1 t1, T2 t2, T3 t3); + } + + public interface Func4 extends Func { + R call(T1 t1, T2 t2, T3 t3, T4 t4); + } + + +} diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/Recorder.java b/autojs/src/main/java/com/stardust/autojs/runtime/record/Recorder.java similarity index 98% rename from app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/Recorder.java rename to autojs/src/main/java/com/stardust/autojs/runtime/record/Recorder.java index fb0f7f44..12780d7c 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/Recorder.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/record/Recorder.java @@ -1,4 +1,4 @@ -package com.stardust.scriptdroid.external.floatingwindow.menu.record; +package com.stardust.autojs.runtime.record; import java.util.Arrays; diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/accessibility/AccessibilityActionConverter.java b/autojs/src/main/java/com/stardust/autojs/runtime/record/accessibility/AccessibilityActionConverter.java similarity index 96% rename from app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/accessibility/AccessibilityActionConverter.java rename to autojs/src/main/java/com/stardust/autojs/runtime/record/accessibility/AccessibilityActionConverter.java index 8e00e5dd..23aef3d0 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/accessibility/AccessibilityActionConverter.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/record/accessibility/AccessibilityActionConverter.java @@ -1,4 +1,4 @@ -package com.stardust.scriptdroid.external.floatingwindow.menu.record.accessibility; +package com.stardust.autojs.runtime.record.accessibility; import android.accessibilityservice.AccessibilityService; import android.os.Build; @@ -8,9 +8,9 @@ import android.view.accessibility.AccessibilityNodeInfo; import com.stardust.automator.UiObject; import com.stardust.automator.simple_action.FilterAction; -import com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector.NodeInfo; import com.stardust.util.SparseArrayEntries; import com.stardust.view.accessibility.AccessibilityNodeInfoHelper; +import com.stardust.view.accessibility.NodeInfo; import org.greenrobot.eventbus.EventBus; diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/accessibility/AccessibilityActionRecorder.java b/autojs/src/main/java/com/stardust/autojs/runtime/record/accessibility/AccessibilityActionRecorder.java similarity index 93% rename from app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/accessibility/AccessibilityActionRecorder.java rename to autojs/src/main/java/com/stardust/autojs/runtime/record/accessibility/AccessibilityActionRecorder.java index dc2a0ac2..5d2c6b1a 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/accessibility/AccessibilityActionRecorder.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/record/accessibility/AccessibilityActionRecorder.java @@ -1,9 +1,9 @@ -package com.stardust.scriptdroid.external.floatingwindow.menu.record.accessibility; +package com.stardust.autojs.runtime.record.accessibility; import android.accessibilityservice.AccessibilityService; import android.view.accessibility.AccessibilityEvent; -import com.stardust.scriptdroid.external.floatingwindow.menu.record.Recorder; +import com.stardust.autojs.runtime.record.Recorder; import com.stardust.view.accessibility.AccessibilityDelegate; import java.util.Arrays; diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/EventFormatException.java b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/EventFormatException.java similarity index 87% rename from app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/EventFormatException.java rename to autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/EventFormatException.java index c33c6c47..f12aa1d3 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/EventFormatException.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/EventFormatException.java @@ -1,4 +1,4 @@ -package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent; +package com.stardust.autojs.runtime.record.inputevent; /** * Created by Stardust on 2017/3/7. diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventConverter.java b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventConverter.java similarity index 83% rename from app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventConverter.java rename to autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventConverter.java index 76212612..0facfcb7 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventConverter.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventConverter.java @@ -1,14 +1,10 @@ -package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent; +package com.stardust.autojs.runtime.record.inputevent; import android.support.annotation.NonNull; import android.text.TextUtils; import android.widget.Toast; -import com.flurry.android.FlurryAgent; -import com.stardust.scriptdroid.App; -import com.stardust.scriptdroid.R; -import com.stardust.scriptdroid.external.floatingwindow.menu.record.Recorder; -import com.stardust.util.MapEntries; +import com.stardust.autojs.runtime.record.Recorder; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -66,6 +62,9 @@ public abstract class InputEventConverter { } + private final static Pattern LAST_INT_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$"); + + protected boolean mConverting = false; private int mState = Recorder.STATE_NOT_START; @@ -117,14 +116,21 @@ public abstract class InputEventConverter { } catch (EventFormatException e) { e.printStackTrace(); if (mFirstEventFormatError) { - Toast.makeText(App.getApp(), R.string.text_record_format_error, Toast.LENGTH_SHORT).show(); mFirstEventFormatError = false; - FlurryAgent.logEvent("EventFormatException", new MapEntries() - .entry("message", e.getMessage()) - .map()); } return null; } + } + public static int parseDeviceNumber(String device) { + Matcher matcher = LAST_INT_PATTERN.matcher(device); + if (matcher.find()) { + String someNumberStr = matcher.group(1); + return Integer.parseInt(someNumberStr); + } + return -1; + } + + } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventRecorder.java b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventRecorder.java similarity index 90% rename from app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventRecorder.java rename to autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventRecorder.java index bf8acc79..8a0c823b 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventRecorder.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventRecorder.java @@ -1,10 +1,9 @@ -package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent; +package com.stardust.autojs.runtime.record.inputevent; import android.content.Context; import com.stardust.autojs.runtime.api.Shell; -import com.stardust.scriptdroid.App; -import com.stardust.scriptdroid.external.floatingwindow.menu.record.Recorder; +import com.stardust.autojs.runtime.record.Recorder; /** * Created by Stardust on 2017/3/6. diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventToJsConverter.java b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventToJsConverter.java similarity index 98% rename from app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventToJsConverter.java rename to autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventToJsConverter.java index 98349601..0d836079 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventToJsConverter.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventToJsConverter.java @@ -1,4 +1,4 @@ -package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent; +package com.stardust.autojs.runtime.record.inputevent; import android.support.annotation.NonNull; diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventToSendEventConverter.java b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventToSendEventConverter.java similarity index 94% rename from app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventToSendEventConverter.java rename to autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventToSendEventConverter.java index e2dc2539..aa7c9261 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventToSendEventConverter.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventToSendEventConverter.java @@ -1,4 +1,4 @@ -package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent; +package com.stardust.autojs.runtime.record.inputevent; import android.support.annotation.NonNull; diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventToSendEventJsConverter.java b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventToSendEventJsConverter.java similarity index 87% rename from app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventToSendEventJsConverter.java rename to autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventToSendEventJsConverter.java index c9be24bc..d1263f6d 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/InputEventToSendEventJsConverter.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/InputEventToSendEventJsConverter.java @@ -1,6 +1,5 @@ -package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent; +package com.stardust.autojs.runtime.record.inputevent; -import android.media.Image; import android.support.annotation.NonNull; import java.util.regex.Matcher; @@ -15,7 +14,6 @@ import static com.stardust.util.ScreenMetrics.getDeviceScreenWidth; public class InputEventToSendEventJsConverter extends InputEventConverter { - private final static Pattern LAST_INT_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$"); private double mLastEventTime; private StringBuilder mCode = new StringBuilder(); private int mTouchDevice = -1; @@ -73,15 +71,6 @@ public class InputEventToSendEventJsConverter extends InputEventConverter { } - private int parseDeviceNumber(String device) { - Matcher matcher = LAST_INT_PATTERN.matcher(device); - if (matcher.find()) { - String someNumberStr = matcher.group(1); - return Integer.parseInt(someNumberStr); - } - return -1; - } - private void onTouchX(int device, int value) { if (mTouchDevice == -1) { setTouchDevice(device); diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/KeyObserver.java b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/KeyObserver.java similarity index 94% rename from app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/KeyObserver.java rename to autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/KeyObserver.java index 5c8bc501..f8f8de1f 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/KeyObserver.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/KeyObserver.java @@ -1,4 +1,4 @@ -package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent; +package com.stardust.autojs.runtime.record.inputevent; import android.content.Context; import android.support.annotation.NonNull; diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/TouchObserver.java b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/TouchObserver.java new file mode 100644 index 00000000..0d047fc7 --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/TouchObserver.java @@ -0,0 +1,102 @@ +package com.stardust.autojs.runtime.record.inputevent; + +import android.content.Context; +import android.support.annotation.NonNull; + +/** + * Created by Stardust on 2017/7/20. + */ + +public class TouchObserver { + + public interface OnTouchEventListener { + void onTouch(int x, int y); + } + + private int mTouchX, mTouchY; + private InputEventRecorder mRecorder; + private OnTouchEventListener mOnTouchEventListener; + + public TouchObserver(Context context) { + mRecorder = new InputEventRecorder(context, new TouchEventObserver()); + } + + public void observe() { + mRecorder.listen(); + mRecorder.start(); + } + + public void stop() { + mRecorder.stop(); + } + + public void setOnTouchEventListener(OnTouchEventListener onTouchEventListener) { + mOnTouchEventListener = onTouchEventListener; + } + + private void onTouch(int x, int y) { + mTouchX = x; + mTouchY = y; + if(mOnTouchEventListener != null){ + mOnTouchEventListener.onTouch(x, y); + } + } + + + + private class TouchEventObserver extends InputEventConverter { + + private int mLastTouchX = -1, mLastTouchY = -1; + + @Override + public void convertEvent(@NonNull Event event) { + int device = parseDeviceNumber(event.device); + int type = (int) Long.parseLong(event.type, 16); + int code = (int) Long.parseLong(event.code, 16); + int value = (int) Long.parseLong(event.value, 16); + if (type != 3) { + return; + } + if (code == 53) { + onTouchX(value); + return; + } + if (code == 54) { + onTouchY(value); + return; + } + if (mLastTouchX >= 0) { + onTouch(mLastTouchX, mTouchY); + mLastTouchX = -1; + return; + } + if (mLastTouchY >= 0) { + onTouch(mTouchX, mLastTouchY); + mLastTouchY = -1; + } + } + + private void onTouchX(int value) { + mLastTouchX = value; + } + + private void onTouchY(int value) { + if (mLastTouchX > 0) { + onTouch(mLastTouchX, value); + return; + } + mLastTouchY = value; + } + + @Override + public String getGetEventCommand() { + return "getevent -t"; + } + + @Override + public String getCode() { + return null; + } + } + +} diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/TouchRecorder.java b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/TouchRecorder.java similarity index 80% rename from app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/TouchRecorder.java rename to autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/TouchRecorder.java index 74288e42..d63d3d5b 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/record/inputevent/TouchRecorder.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/record/inputevent/TouchRecorder.java @@ -1,4 +1,4 @@ -package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent; +package com.stardust.autojs.runtime.record.inputevent; import android.content.Context; diff --git a/autojs/src/main/res/xml/accessibility_service_config.xml b/autojs/src/main/res/xml/accessibility_service_config.xml index 62a664d6..84c6464a 100644 --- a/autojs/src/main/res/xml/accessibility_service_config.xml +++ b/autojs/src/main/res/xml/accessibility_service_config.xml @@ -2,9 +2,10 @@ \ No newline at end of file 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/automator/src/main/java/com/stardust/automator/simple_action/SearchTargetAction.java b/automator/src/main/java/com/stardust/automator/simple_action/SearchTargetAction.java index e1c41c89..22f7e453 100644 --- a/automator/src/main/java/com/stardust/automator/simple_action/SearchTargetAction.java +++ b/automator/src/main/java/com/stardust/automator/simple_action/SearchTargetAction.java @@ -1,5 +1,7 @@ package com.stardust.automator.simple_action; +import android.graphics.BitmapFactory; + import com.stardust.automator.UiObject; import java.util.List; 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 12cd0993..929e0d37 100644 --- a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java +++ b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java @@ -1,19 +1,12 @@ package com.stardust.view.accessibility; -import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; -import android.graphics.Rect; import android.os.Build; import android.os.Handler; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.util.Log; -import android.view.InputDevice; +import android.view.KeyEvent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; -import android.widget.FrameLayout; - -import com.stardust.util.ScreenMetrics; import java.util.HashSet; import java.util.Map; @@ -22,6 +15,9 @@ import java.util.SortedMap; import java.util.Timer; import java.util.TimerTask; import java.util.TreeMap; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; @@ -39,11 +35,12 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi private static final ReentrantLock LOCK = new ReentrantLock(); private static final Condition ENABLED = LOCK.newCondition(); private static AccessibilityService instance; + private static final OnKeyListener.Observer stickOnKeyObserver = new OnKeyListener.Observer(); private static boolean containsAllEventTypes = false; private static final Set eventTypes = new HashSet<>(); - private volatile AccessibilityNodeInfo mRootInActiveWindow; - private Timer mTimer; + private OnKeyListener.Observer mOnKeyObserver = new OnKeyListener.Observer(); private Handler mHandler; + private ExecutorService mKeyEventExecutor; public static void addDelegate(int uniquePriority, AccessibilityDelegate delegate) { mDelegates.put(uniquePriority, delegate); @@ -85,15 +82,39 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi } + @Override + protected boolean onKeyEvent(final KeyEvent event) { + if (mKeyEventExecutor == null) { + mKeyEventExecutor = Executors.newSingleThreadExecutor(); + } + mKeyEventExecutor.execute(new Runnable() { + @Override + public void run() { + stickOnKeyObserver.onKeyEvent(event.getKeyCode(), event); + mOnKeyObserver.onKeyEvent(event.getKeyCode(), event); + } + }); + return false; + } + + public OnKeyListener.Observer getOnKeyObserver() { + return mOnKeyObserver; + } + @Override public AccessibilityNodeInfo getRootInActiveWindow() { - return mRootInActiveWindow; + try { + return super.getRootInActiveWindow(); + } catch (IllegalStateException e) { + return null; + } } @Override public void onDestroy() { instance = null; - mTimer.cancel(); + if (mKeyEventExecutor != null) + mKeyEventExecutor.shutdownNow(); super.onDestroy(); } @@ -107,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??? } @@ -155,5 +159,7 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi } } - + public static OnKeyListener.Observer getStickOnKeyObserver() { + return stickOnKeyObserver; + } } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/LayoutInspector.java b/automator/src/main/java/com/stardust/view/accessibility/LayoutInspector.java similarity index 96% rename from app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/LayoutInspector.java rename to automator/src/main/java/com/stardust/view/accessibility/LayoutInspector.java index d81f4470..9cc30e1b 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/LayoutInspector.java +++ b/automator/src/main/java/com/stardust/view/accessibility/LayoutInspector.java @@ -1,4 +1,4 @@ -package com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector; +package com.stardust.view.accessibility; import android.os.Build; import android.support.annotation.RequiresApi; diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/NodeInfo.java b/automator/src/main/java/com/stardust/view/accessibility/NodeInfo.java similarity index 97% rename from app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/NodeInfo.java rename to automator/src/main/java/com/stardust/view/accessibility/NodeInfo.java index 7fd1b7c5..e961a2e3 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/layout_inspector/NodeInfo.java +++ b/automator/src/main/java/com/stardust/view/accessibility/NodeInfo.java @@ -1,4 +1,4 @@ -package com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector; +package com.stardust.view.accessibility; import android.graphics.Rect; import android.support.annotation.Keep; diff --git a/automator/src/main/java/com/stardust/view/accessibility/OnKeyListener.java b/automator/src/main/java/com/stardust/view/accessibility/OnKeyListener.java new file mode 100644 index 00000000..0453a6ef --- /dev/null +++ b/automator/src/main/java/com/stardust/view/accessibility/OnKeyListener.java @@ -0,0 +1,42 @@ +package com.stardust.view.accessibility; + +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; + +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Created by Stardust on 2017/7/18. + */ + +public interface OnKeyListener { + + void onKeyEvent(int keyCode, KeyEvent event); + + class Observer implements OnKeyListener { + + private static final String TAG = "OnKeyListenerObserver"; + + private CopyOnWriteArrayList mOnKeyListeners = new CopyOnWriteArrayList<>(); + + @Override + public void onKeyEvent(int keyCode, KeyEvent event) { + for (OnKeyListener listener : mOnKeyListeners) { + try { + listener.onKeyEvent(keyCode, event); + } catch (Exception e) { + Log.e(TAG, "Error OnKeyEvent: " + event + " Listener: " + listener, e); + } + } + } + + public void addListener(OnKeyListener listener) { + mOnKeyListeners.add(listener); + } + + public boolean removeListener(OnKeyListener listener) { + return mOnKeyListeners.remove(listener); + } + } +} 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; } diff --git a/inrt/src/main/java/com/stardust/auojs/inrt/rt/AutoJs.java b/inrt/src/main/java/com/stardust/auojs/inrt/rt/AutoJs.java index bbfd74c7..e79e5ffb 100644 --- a/inrt/src/main/java/com/stardust/auojs/inrt/rt/AutoJs.java +++ b/inrt/src/main/java/com/stardust/auojs/inrt/rt/AutoJs.java @@ -151,7 +151,7 @@ public class AutoJs implements AccessibilityBridge { @Nullable @Override - public android.accessibilityservice.AccessibilityService getService() { + public AccessibilityService getService() { return AccessibilityService.getInstance(); }