diff --git a/.idea/markdown-navigator/profiles_settings.xml b/.idea/markdown-navigator/profiles_settings.xml index 674a5916..57927c5a 100644 --- a/.idea/markdown-navigator/profiles_settings.xml +++ b/.idea/markdown-navigator/profiles_settings.xml @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/app-release-1.17.0223.apk b/app-release-1.17.0226.apk similarity index 66% rename from app-release-1.17.0223.apk rename to app-release-1.17.0226.apk index 26658ee9..e0369f5a 100644 Binary files a/app-release-1.17.0223.apk and b/app-release-1.17.0226.apk differ diff --git a/app-release-1.17.0305(1)内测.apk b/app-release-1.17.0305(1)内测.apk new file mode 100644 index 00000000..543956b6 Binary files /dev/null and b/app-release-1.17.0305(1)内测.apk differ diff --git a/app-release-1.17.0305内测.apk b/app-release-1.17.0305内测.apk new file mode 100644 index 00000000..8a8ffd55 Binary files /dev/null and b/app-release-1.17.0305内测.apk differ diff --git a/app-release-1.17.0306内测.apk b/app-release-1.17.0306内测.apk new file mode 100644 index 00000000..0ba1c2eb Binary files /dev/null and b/app-release-1.17.0306内测.apk differ diff --git a/app/build.gradle b/app/build.gradle index f373cf13..6fbc814b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,9 +7,12 @@ android { applicationId "com.stardust.scriptdroid" minSdkVersion 19 targetSdkVersion 23 - versionCode 34 - versionName "1.17.0223" + versionCode 38 + versionName "1.17.0306内测" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + multiDexEnabled true + + } buildTypes { release { @@ -44,7 +47,7 @@ repositories { } dependencies { - compile fileTree(include: ['*.jar'], dir: 'libs') + compile fileTree(include: ['*.jar', '*.so'], dir: 'libs') testCompile 'junit:junit:4.12' debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' @@ -53,17 +56,16 @@ dependencies { compile 'com.android.support:appcompat-v7:25.1.0' compile 'com.android.support:design:25.1.0' + compile 'com.github.hyb1996:MutableTheme:0.2.2' compile 'com.afollestad.material-dialogs:core:0.9.2.3' compile 'com.google.code.gson:gson:2.8.0' compile('de.psdev.licensesdialog:licensesdialog:1.8.1') - compile 'com.furture.react:DuktapeJava:1.1.0' - compile 'com.zzhoujay.markdown:markdown:1.0.2' + compile 'com.github.atlassian:commonmark-java:commonmark-parent-0.9.0' compile 'moe.feng:AlipayZeroSdk:1.1' compile 'com.jraska:console:0.4.3' compile 'com.jraska:console-timber-tree:0.4.3' compile 'com.heinrichreimersoftware:android-issue-reporter:eae4c73cbeb91e98da83fe79a3dea088a8b97448' - - compile files('libs/JSTransformer.jar') + compile group: 'org.mozilla', name: 'rhino', version: '1.7' compile 'com.afollestad.material-dialogs:commons:0.9.2.3' compile 'com.makeramen:roundedimageview:2.2.1' @@ -75,3 +77,7 @@ dependencies { compile(name: '920-app-debug', ext: 'aar') } + +configurations { + all*.exclude group: 'org.mozilla', module: 'rhino' +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 99a39b44..c347e9c4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -75,7 +75,8 @@ - + + diff --git a/app/src/main/assets/help/catalogue.json b/app/src/main/assets/help/catalogue.json new file mode 100644 index 00000000..e04b0abf --- /dev/null +++ b/app/src/main/assets/help/catalogue.json @@ -0,0 +1,13 @@ +{ + "main" : [ + {"title":"语法"}, + {"title":"JavaScript教程"}, + {"title":"自动操作函数"}, + {"title":"一般常用函数"}, + {"title":"控制台"}, + {"title":"UI(用户界面)"}, + {"title":"驻留模式与无障碍服务委托"}, + {"title":"shell命令"}, + {"title":"需要Root权限的自动操作函数"} + ] +} \ No newline at end of file diff --git a/app/src/main/assets/help/documentation/shell命令.md b/app/src/main/assets/help/documentation/shell命令.md new file mode 100644 index 00000000..21d749e2 --- /dev/null +++ b/app/src/main/assets/help/documentation/shell命令.md @@ -0,0 +1,358 @@ +shell命令是通过shell函数运行的命令。等同于"adb shell"。shell函数的参数如下: +## shell(cmd[, root]) +* cmd \ 要执行的命令 +* root \ 是否以root权限运行,默认为false。 +返回运行一个执行结果。该返回值一般不会用到,其属性如下: + * code \ 错误码。执行成功时为1,失败时为0。 + * result \ 运行结果。 + * error \ 运行的错误信息。例如执行需要root权限的命令但没有授予root权限会返回错误信息"Permission denied"。 + +例如``。(强制停止微信) +示例: +``` +var result = shell("am force-stop com.tencent.mm", true); +if(result.succeed){ + toast("执行成功"); +}else{ + toast("执行失败!请到控制台查看错误信息"); + err(result.error); + openConsole(); +} +``` + +以下关于shell命令的资料来自[AndroidStudio用户指南: 发出shell命令](https://developer.android.com/studio/command-line/adb.html#shellcommands)。 + +目录: +* [am命令](#am) +* [intent 参数的规范](#IntentSpec) +* [应用包名](#PackageName) +* [pm命令](#pm) +* [其他命令](#others) + +

am命令

+ +am命令即Activity Manager命令,用于管理应用程序活动、服务等。 + +**以下命令均以"am "开头,例如"shell(\"am start -p com.tencent.mm\");"(启动微信)** + +### start [options] intent +启动 intent 指定的 Activity(应用程序活动)。 +请参阅 [intent 参数的规范](#IntentSpec)。 + +选项包括: + +* -D:启用调试。 +* -W:等待启动完成。 +* --start-profiler file:启动分析器并将结果发送到 file。 +* -P file:类似于 --start-profiler,但当应用进入空闲状态时分析停止。 +* -R count:重复 Activity 启动 count 次数。在每次重复前,将完成顶部 Activity。 +* -S:启动 Activity 前强行停止目标应用。 +* --opengl-trace:启用 OpenGL 函数的跟踪。 +* --user user_id | current:指定要作为哪个用户运行;如果未指定,则作为当前用户运行。 + +### startservice [options] intent +启动 intent 指定的 Service(服务)。 +请参阅 [intent 参数的规范](#IntentSpec)。 +选项包括: + +* --user user_id | current:指定要作为哪个用户运行;如果未指定,则作为当前用户运行。 + +### force-stop package +强行停止与 package([应用包名](#PackageName))关联的所有应用。 + +### kill [options] package +终止与 package([应用包名](#PackageName))关联的所有进程。此命令仅终止可安全终止且不会影响用户体验的进程。 +选项包括: + +* --user user_id | all | current:指定将终止其进程的用户;如果未指定,则终止所有用户的进程。 +* kill-all 终止所有后台进程。 + +### broadcast [options] intent +发出广播 intent。 +请参阅 [intent 参数的规范](#IntentSpec)。 + +选项包括: + +* [--user user_id | all | current]:指定要发送到的用户;如果未指定,则发送到所有用户。 + +### instrument [options] component +使用 Instrumentation 实例启动监控。通常,目标 component 是表单 test_package/runner_class。 +选项包括: + +* -r:输出原始结果(否则对 report_key_streamresult 进行解码)。与 [-e perf true] 结合使用以生成性能测量的原始输出。 +* -e name value:将参数 name 设为 value。对于测试运行器,通用表单为 -e testrunner_flag value[,value...]。 +* -p file:将分析数据写入 file。 +* -w:先等待仪器完成,然后再返回。测试运行器需要使用此选项。 +* --no-window-animation:运行时关闭窗口动画。 +* --user user_id | current:指定仪器在哪个用户中运行;如果未指定,则在当前用户中运行。 +* profile start process file 启动 process 的分析器,将结果写入 file。 +* profile stop process 停止 process 的分析器。 + +### dumpheap [options] process file +转储 process 的堆,写入 file。 + +选项包括: + +* --user [user_id|current]:提供进程名称时,指定要转储的进程用户;如果未指定,则使用当前用户。 +* -n:转储原生堆,而非托管堆。 +* set-debug-app [options] package 将应用 package 设为调试。 + +选项包括: + +* -w:应用启动时等待调试程序。 +* --persistent:保留此值。 +* clear-debug-app 使用 set-debug-app 清除以前针对调试用途设置的软件包。 + +### monitor [options] 启动对崩溃或 ANR 的监控。 +选项包括: + +* --gdb:在崩溃/ANR 时在给定端口上启动 gdbserv。 +### screen-compat {on|off} package +控制 package 的屏幕兼容性模式。 + +### display-size [reset|widthxheight] +替换模拟器/设备显示尺寸。此命令对于在不同尺寸的屏幕上测试您的应用非常有用,它支持使用大屏设备模仿小屏幕分辨率(反之亦然)。 +示例: +``` +shell("am display-size 1280x800", true); + +``` +### display-density dpi +替换模拟器/设备显示密度。此命令对于在不同密度的屏幕上测试您的应用非常有用,它支持使用低密度屏幕在高密度环境环境上进行测试(反之亦然)。 +示例: +``` +shell("am display-density 480", true); +``` +### to-uri intent +将给定的 intent 规范以 URI 的形式输出。 +请参阅 [intent 参数的规范](#IntentSpec)。 + +### to-intent-uri intent +将给定的 intent 规范以 intent:URI 的形式输出。 +请参阅 intent 参数的规范。 + +

intent 参数的规范

+对于采用 intent 参数的 am 命令,您可以使用以下选项指定 intent: + +* -a action +指定 intent 操作,如“android.intent.action.VIEW”。此指定只能声明一次。 +* -d data_uri +指定 intent 数据 URI,如“content://contacts/people/1”。此指定只能声明一次。 +* -t mime_type +指定 intent MIME 类型,如“image/png”。此指定只能声明一次。 +* -c category +指定 intent 类别,如“android.intent.category.APP_CONTACTS”。 +* -n component +指定带有软件包名称前缀的组件名称以创建显式 intent,如“com.example.app/.ExampleActivity”。 +* -f flags +将标志添加到 setFlags() 支持的 intent。 +* --esn extra_key +添加一个 null extra。URI intent 不支持此选项。 +* -e|--es extra_key extra_string_value +添加字符串数据作为键值对。 +* --ez extra_key extra_boolean_value +添加布尔型数据作为键值对。 +* --ei extra_key extra_int_value +添加整数型数据作为键值对。 +* --el extra_key extra_long_value +添加长整型数据作为键值对。 +* --ef extra_key extra_float_value +添加浮点型数据作为键值对。 +* --eu extra_key extra_uri_value +添加 URI 数据作为键值对。 +* --ecn extra_key extra_component_name_value +添加组件名称,将其作为 ComponentName 对象进行转换和传递。 +* --eia extra_key extra_int_value[,extra_int_value...] +添加整数数组。 +* --ela extra_key extra_long_value[,extra_long_value...] +添加长整型数组。 +* --efa extra_key extra_float_value[,extra_float_value...] +添加浮点型数组。 +* --grant-read-uri-permission +包含标志 FLAG_GRANT_READ_URI_PERMISSION。 +* --grant-write-uri-permission +包含标志 FLAG_GRANT_WRITE_URI_PERMISSION。 +* --debug-log-resolution +包含标志 FLAG_DEBUG_LOG_RESOLUTION。 +* --exclude-stopped-packages +包含标志 FLAG_EXCLUDE_STOPPED_PACKAGES。 +* --include-stopped-packages +包含标志 FLAG_INCLUDE_STOPPED_PACKAGES。 +* --activity-brought-to-front +包含标志 FLAG_ACTIVITY_BROUGHT_TO_FRONT。 +* --activity-clear-top +包含标志 FLAG_ACTIVITY_CLEAR_TOP。 +* --activity-clear-when-task-reset +包含标志 FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET。 +* --activity-exclude-from-recents +包含标志 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS。 +* --activity-launched-from-history +包含标志 FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY。 +* --activity-multiple-task +包含标志 FLAG_ACTIVITY_MULTIPLE_TASK。 +* --activity-no-animation +包含标志 FLAG_ACTIVITY_NO_ANIMATION。 +* --activity-no-history +包含标志 FLAG_ACTIVITY_NO_HISTORY。 +* --activity-no-user-action +包含标志 FLAG_ACTIVITY_NO_USER_ACTION。 +* --activity-previous-is-top +包含标志 FLAG_ACTIVITY_PREVIOUS_IS_TOP。 +* --activity-reorder-to-front +包含标志 FLAG_ACTIVITY_REORDER_TO_FRONT。 +* --activity-reset-task-if-needed +包含标志 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED。 +* --activity-single-top +包含标志 FLAG_ACTIVITY_SINGLE_TOP。 +* --activity-clear-task +包含标志 FLAG_ACTIVITY_CLEAR_TASK。 +* --activity-task-on-home +包含标志 FLAG_ACTIVITY_TASK_ON_HOME。 +* --receiver-registered-only +包含标志 FLAG_RECEIVER_REGISTERED_ONLY。 +* --receiver-replace-pending +包含标志 FLAG_RECEIVER_REPLACE_PENDING。 +* --selector +需要使用 -d 和 -t 选项以设置 intent 数据和类型。 +#### URI component package +如果不受上述某一选项的限制,您可以直接指定 URI、软件包名称和组件名称。当参数不受限制时,如果参数包含一个“:”(冒号),则此工具假定参数是一个 URI;如果参数包含一个“/”(正斜杠),则此工具假定参数是一个组件名称;否则,此工具假定参数是一个软件包名称。 + +

应用包名

+所谓应用包名,是唯一确定应用的标识。例如微信的包名是"com.tencent.mm", QQ的包名是"com.tencent.mobileqq"。 +要获取一个应用的包名,可以通过函数`getPackageName(appName)`获取。参见帮助->其他一般函数。 + + +

pm命令

+ +pm命令用于管理应用程序,例如卸载应用、冻结应用等。 +**以下命令均以"pm "开头,例如"shell(\"pm disable com.tencent.mm\");"(冻结微信)** + +### list packages [options] filter +输出所有软件包,或者,仅输出包名称包含 filter 中的文本的软件包。 +选项: + +* -f:查看它们的关联文件。 +* -d:进行过滤以仅显示已停用的软件包。 +* -e:进行过滤以仅显示已启用的软件包。 +* -s:进行过滤以仅显示系统软件包。 +* -3:进行过滤以仅显示第三方软件包。 +* -i:查看软件包的安装程序。 +* -u:也包括卸载的软件包。 +* --user user_id:要查询的用户空间。 + +### list permission-groups +输出所有已知的权限组。 +### list permissions [options] group +输出所有已知权限,或者,仅输出 group 中的权限。 +选项: + +* -g:按组加以组织。 +* -f:输出所有信息。 +* -s:简短摘要。 +* -d:仅列出危险权限。 +* -u:仅列出用户将看到的权限。 + +### list instrumentation [options] +列出所有测试软件包。 +选项: + +* -f:列出用于测试软件包的 APK 文件。 +* target_package:列出仅用于此应用的测试软件包。 + +### list features +输出系统的所有功能。 +### list libraries +输出当前设备支持的所有库。 +### list users +输出系统上的所有用户。 +### path package +输出给定 package 的 APK 的路径。 +### install [options] path +将软件包(通过 path 指定)安装到系统。 +选项: + +* -l:安装具有转发锁定功能的软件包。 +* -r:重新安装现有应用,保留其数据。 +* -t:允许安装测试 APK。 +* -i installer_package_name:指定安装程序软件包名称。 +* -s:在共享的大容量存储(如 sdcard)上安装软件包。 +* -f:在内部系统内存上安装软件包。 +* -d:允许版本代码降级。 +* -g:授予应用清单文件中列出的所有权限。 + +### uninstall [options] package + +从系统中卸载软件包。 +选项: + +* -k:移除软件包后保留数据和缓存目录。 +### clear package +删除与软件包关联的所有数据。 + +### enable package_or_component +启用给定软件包或组件(作为“package/class”写入)。 + +### disable package_or_component +停用给定软件包或组件(作为“package/class”写入)。 + +### disable-user [options] package_or_component + +选项: + +* --user user_id:要停用的用户。 +### grant package_name permission +向应用授予权限。在运行 Android 6.0(API 级别 23)及更高版本的设备上,可以是应用清单中声明的任何权限。在运行 Android 5.1(API 级别 22)和更低版本的设备上,必须是应用定义的可选权限。 + +### revoke package_name permission +从应用中撤销权限。在运行 Android 6.0(API 级别 23)及更高版本的设备上,可以是应用清单中声明的任何权限。在运行 Android 5.1(API 级别 22)和更低版本的设备上,必须是应用定义的可选权限。 + +### set-install-location location +更改默认安装位置。位置值: +* 0:自动—让系统决定最佳位置。 +* 1:内部—安装在内部设备存储上。 +* 2:外部—安装在外部介质上。 + +> 注:此命令仅用于调试目的;使用此命令会导致应用中断和其他意外行为。 + +### get-install-location +返回当前安装位置。返回值: +* 0 [auto]:让系统决定最佳位置。 +* 1 [internal]:安装在内部设备存储上 +* 2 [external]:安装在外部介质上 + +### set-permission-enforced permission [true|false] +指定是否应强制执行给定的权限。 + +### trim-caches desired_free_space +减少缓存文件以达到给定的可用空间。 + +### create-user user_name +使用给定的 user_name 创建新用户,输出新用户的标识符。 + +### remove-user user_id +移除具有给定的 user_id 的用户,删除与该用户关联的所有数据。 + +### get-max-users +输出设备支持的最大用户数。 + +

其他命令

+ +### 进行屏幕截图 +screencap 命令是一个用于对设备显示屏进行屏幕截图的 shell 实用程序。在 shell 中,此语法为: +``` +screencap filename +``` + +例如: +``` +$ shell("screencap /sdcard/screen.png"); +``` + +### 列表文件 +``` +ls filepath +``` +例如: +``` +log(shell("ls /system/bin")); +``` \ 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..133f8676 --- /dev/null +++ b/app/src/main/assets/help/documentation/控制台.md @@ -0,0 +1,24 @@ +控制台通常用来输出一些调试信息和运算结果。 + +### openConsole() +显示控制台。 +### clearConsole() +清空控制台。 +### log(text) +* text \ | \ 要打印到控制台的信息 + +在控制台中输出日志,例如`log("Hello world");`。当text是一个对象时则会转换String以后再输出。 + +### err(error) +* error \ | \ + +在控制台中输出错误信息,以红色字体显示,例如: +``` +try{ + //do something +}catch(e){ + err("错误"); + err(e); + openConsole(); +} +``` \ 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..d11068dc --- /dev/null +++ b/app/src/main/assets/help/documentation/自动操作函数.md @@ -0,0 +1,48 @@ +**自动操作的函数都需要开启"自动操作服务"才能执行,否则执行到相应函数时脚本会停止运行。** + +您也可以在设置中开启"使用Root权限自动开启自动操作服务",从而在运行到相应语句时可以自动开启。 +### click(text[, i]) +* text \ 要点击的区域的文本 +* i \ 如果相同的文本在屏幕中出现多次,则i表示要点击第几个文本 + +返回是否点击成功。当屏幕中并未包含该文本,或者该文本所在区域不能点击时返回false,否则返回true。 + +该函数可以点击大部分包含文字的按钮。例如微信主界面下方的"微信", "联系人", "发现", "我"的按钮。 +通常与while同时使用以便点击按钮直至成功。例如: +``` +while(!click("扫一扫")); +``` +可选参数i通常不必指定,但当屏幕中出现相同的文字时,例如 +* `click(text, i)` 点击第i个文本text所在的区域,并返回是否点击成功。当不指定时点击所有区域。当前界面没有出现该文本或者该文本所在区域不可点击时返回false。例如`click("发现")`,是点击"发现"。如果要点击"发现"直至点击成功可以用`while(!click("发现"))`。 +> 文本所在区域指的是,从文本处向其父视图寻找,直至发现一个可点击的部件为止。 +### click(left, top, bottom, right) +* left 要点击的长方形区域左边与屏幕左边的像素距离 +* top 要点击的长方形区域上边与屏幕上边的像素距离 +* bottom 要点击的长方形区域下边与屏幕上边的像素距离 +* right 要点击的长方形区域右边与屏幕左边的像素距离 + +当屏幕中并未包含与该区域严格匹配的区域,或者该区域不能点击时返回false,否则返回true。 +有些按钮或者部件是图标而不是文字(例如发送朋友圈的照相机图标以及QQ下方的消息、联系人、动态图标),这时不能通过`click(text[, i])`来点击,只能通过描述图标所在的区域来点击。left, bottom, top, right描述的就是点击的区域。 +至于要定位点击的区域,可以在侧拉菜单开启"点击区域辅助"(或者安卓7.0以上在通知栏点击"修改"添加点击区域辅助快捷设定图标),之后每次点击或长按都会提示这次点击或长按的区域并自动保存,可以在编辑器的右侧拉菜单中插入。 +也可以通过录制脚本会生成类似语句。 + +**以下的longClick、select、scrollUp、scrollDown的参数均与click类似,不再赘述。** + +### longClick +长按 +### select +选择 +### scrollUp +上滑。另外不加参数时`scrollUp();`会寻找"最大"的可滑动的控件上滑,例如微信消息列表等。 +参数为一个整数i时会找到第i + 1个可滑动控件滑动。例如`scrollUp(0);`。 +### scrollDown +下滑。不加参数时与scrollUp类似。 +### input([i, ]text) +* i \ 表示要输入的为第i + 1个输入框 +* text \ 要输入的文本 + +返回是否输入成功。当找不到对应的文本框时返回false。 +这里的输入文本的意思是,把输入框的文本置为text,而不是在原来的文本上追加。 +不加参数i则会把所有输入框的文本都置为text。例如`input("测试")`。 +### input(i, text) +把第i + 1个输入框的文本设为text。 diff --git a/app/src/main/assets/help/documentation/语法.md b/app/src/main/assets/help/documentation/语法.md new file mode 100644 index 00000000..e69de29b diff --git a/app/src/main/assets/help/documentation/需要Root权限的自动操作函数.md b/app/src/main/assets/help/documentation/需要Root权限的自动操作函数.md new file mode 100644 index 00000000..722898a4 --- /dev/null +++ b/app/src/main/assets/help/documentation/需要Root权限的自动操作函数.md @@ -0,0 +1,143 @@ +以下函数均需要root权限,可以实现任意位置的点击、滑动、长按、模拟物理按键等。 + +这些函数通常首字母大写以表示其特殊的权限。 +函数是返回值均是一个布尔值。true表示成功,false表示失败。一般而言,只有没有root权限时才会运行失败。 + +### Tap(x, y) +点击位置(x, y), 您可以通过"开发者选项"开启指针位置来确定点击坐标。 + +### Swipe(x1, y1, x2, y2) +滑动。从(x1, y1)位置滑动到(x2, y2)位置。 + +### Home() +按下Home键。 + +### Back() +按下返回键。 + +### Power() +按下电源键。 + +### Menu() +按下菜单键。 + +### Up() +模拟按下物理按键上。 + +### Down() +模拟按下物理按键上。 + +### Left() +模拟按下物理按键上。 + +### Right() +模拟按下物理按键上。 + +### OK() +模拟按下物理按键确定。 + +### VolumeUp() +按下音量上键。 + +### VolumeDown() +按键音量上键。 + +### Text(text) +* text 要输入的文字 +输入文字text。例如`Text("测试");` + +### Camera() +模拟按下照相键。 + +### KeyCode(code) +* code | 要按下的按键的数字代码或名称。参见下表。 +模拟物理按键。例如`KeyCode(29)`和`KeyCode("KEYCODE_A")`是按下A键。 + +KeyCode KeyEvent Value +* KEYCODE_MENU 1 +* KEYCODE_SOFT_RIGHT 2 +* KEYCODE_HOME 3 +* KEYCODE_BACK 4 +* KEYCODE_CALL 5 +* KEYCODE_ENDCALL 6 +* KEYCODE_0 7 +* KEYCODE_1 8 +* KEYCODE_2 9 +* KEYCODE_3 10 +* KEYCODE_4 11 +* KEYCODE_5 12 +* KEYCODE_6 13 +* KEYCODE_7 14 +* KEYCODE_8 15 +* KEYCODE_9 16 +* KEYCODE_STAR 17 +* KEYCODE_POUND 18 +* KEYCODE_DPAD_UP 19 +* KEYCODE_DPAD_DOWN 20 +* KEYCODE_DPAD_LEFT 21 +* KEYCODE_DPAD_RIGHT 22 +* KEYCODE_DPAD_CENTER 23 +* KEYCODE_VOLUME_UP 24 +* KEYCODE_VOLUME_DOWN 25 +* KEYCODE_POWER 26 +* KEYCODE_CAMERA 27 +* KEYCODE_CLEAR 28 +* KEYCODE_A 29 +* KEYCODE_B 30 +* KEYCODE_C 31 +* KEYCODE_D 32 +* KEYCODE_E 33 +* KEYCODE_F 34 +* KEYCODE_G 35 +* KEYCODE_H 36 +* KEYCODE_I 37 +* KEYCODE_J 38 +* KEYCODE_K 39 +* KEYCODE_L 40 +* KEYCODE_M 41 +* KEYCODE_N 42 +* KEYCODE_O 43 +* KEYCODE_P 44 +* KEYCODE_Q 45 +* KEYCODE_R 46 +* KEYCODE_S 47 +* KEYCODE_T 48 +* KEYCODE_U 49 +* KEYCODE_V 50 +* KEYCODE_W 51 +* KEYCODE_X 52 +* KEYCODE_Y 53 +* KEYCODE_Z 54 +* KEYCODE_COMMA 55 +* KEYCODE_PERIOD 56 +* KEYCODE_ALT_LEFT 57 +* KEYCODE_ALT_RIGHT 58 +* KEYCODE_SHIFT_LEFT 59 +* KEYCODE_SHIFT_RIGHT 60 +* KEYCODE_TAB 61 +* KEYCODE_SPACE 62 +* KEYCODE_SYM 63 +* KEYCODE_EXPLORER 64 +* KEYCODE_ENVELOPE 65 +* KEYCODE_ENTER 66 +* KEYCODE_DEL 67 +* KEYCODE_GRAVE 68 +* KEYCODE_MINUS 69 +* KEYCODE_EQUALS 70 +* KEYCODE_LEFT_BRACKET 71 +* KEYCODE_RIGHT_BRACKET 72 +* KEYCODE_BACKSLASH 73 +* KEYCODE_SEMICOLON 74 +* KEYCODE_APOSTROPHE 75 +* KEYCODE_SLASH 76 +* KEYCODE_AT 77 +* KEYCODE_NUM 78 +* KEYCODE_HEADSETHOOK 79 +* KEYCODE_FOCUS 80 +* KEYCODE_PLUS 81 +* KEYCODE_MENU 82 +* KEYCODE_NOTIFICATION 83 +* KEYCODE_SEARCH 84 +* TAG_LAST_ KEYCODE 85 + + diff --git a/app/src/main/assets/javasccript_engine_init.js b/app/src/main/assets/javasccript_engine_init.js index f579d7c3..1773a277 100644 --- a/app/src/main/assets/javasccript_engine_init.js +++ b/app/src/main/assets/javasccript_engine_init.js @@ -1,4 +1,15 @@ +if(__engine__ == "rhino"){ + __importClassOld__ = importClass; + var importClass = function(pack){ + if(typeof(pack) == "string"){ + __importClassOld__(Packages[pack]); + }else{ + __importClassOld__(pack); + } + } +} + var toast = function(text){ droid.toast(text); } @@ -22,8 +33,10 @@ var launchApp = function(appName){ function performAction(action, args){ if(args.length == 4){ return action(droid.bounds(args[0], args[1], args[2], args[3])); - }else{ - return action(droid.text(args[0])); + }else if(args.length == 2){ + return action(droid.text(args[0], args[1])); + }else { + return action(droid.text(args[0], -1)); } } @@ -39,6 +52,10 @@ var longClick = function(a, b, c, d){ }, arguments); } +var clickId = function(id){ + return droid.click(droid.id(id)); +} + var scrollUp = function(a, b, c, d){ if(arguments.length == 0) return droid.scrollAllUp(); @@ -136,4 +153,76 @@ var getActivityName = function(){ var setClip = function(text){ droid.setClip(text); -} \ No newline at end of file +} + +var Tap = function(x, y){ + return shell("input tap " + x + " " + y, true).code == 1; +} + +var Swipe = function(x1, y1, x2, y2){ + return shell("input swipe " + x1 + " " + y1 + " " + x2 + " " + y2, true).code == 1; +} + +var KeyCode = function(keyCode){ + return shell("input keyevent " + keyCode, true).code == 1; +} + +var Home = function(){ + return KeyCode(3); +} + +var Back = function(){ + return KeyCode(4); +} + +var Power = function(){ + return KeyCode(26); +} + +var Up = function(){ + return KeyCode(19); +} + +var Down = function(){ + return KeyCode(20); +} + +var Left = function(){ + return KeyCode(21); +} + +var Right = function(){ + return KeyCode(22); +} + +var OK = function(){ + return KeyCode(23); +} + +var VolumeUp = function(){ + return KeyCode(24); +} + +var VolumeDown = function(){ + return KeyCode(25); +} + +var Menu = function(){ + return KeyCode(1); +} + +var Camera = function(){ + return KeyCode(27); +} + +var Text = function(text){ + return shell("input text " + text, true).code == 1; +} + +/* +importClass("com.stardust.scriptdroid.service.AccessibilityDelegate"); + +var addAccessibilityDelegate = function(delegate){ + droid.addAccessibilityDelegate(delegate); +} +*/ \ No newline at end of file diff --git a/app/src/main/assets/sample_force_qq_chat.js b/app/src/main/assets/sample_force_qq_chat.js index 500cb8c3..e352acb1 100644 --- a/app/src/main/assets/sample_force_qq_chat.js +++ b/app/src/main/assets/sample_force_qq_chat.js @@ -3,7 +3,6 @@ importClass("android.widget.EditText"); importClass("android.widget.Button"); importClass("android.widget.LinearLayout"); -importClass("android.view.View.OnClickListener"); importClass("android.content.Intent"); importClass("android.net.Uri"); @@ -16,7 +15,7 @@ container.addView(qq); container.addView(btnOk); activity.setContentView(container); -btnOk.setOnClickListener(new OnClickListener(function(view){ +btnOk.setOnClickListener(function(view){ activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("mqqwpa://im/chat?chat_type=wpa&uin=" + qq.getText()))); activity.finish(); -})); \ No newline at end of file +}); \ No newline at end of file diff --git a/app/src/main/assets/sample_open_running_services.js b/app/src/main/assets/sample_open_running_services.js index 2c989d37..37c266f0 100644 --- a/app/src/main/assets/sample_open_running_services.js +++ b/app/src/main/assets/sample_open_running_services.js @@ -14,9 +14,9 @@ function openRunningServices(){ } -importClass("android.os.Build.VERSION"); +importClass("android.os.Build"); -if(VERSION.SDK_INT < 23){ +if(Build.VERSION.SDK_INT < 23){ toast("本代码只适用于Android6.0以上"); }else{ openRunningServices(); diff --git a/app/src/main/assets/sample_simple_calculator.js b/app/src/main/assets/sample_simple_calculator.js index 903733f1..b480987a 100644 --- a/app/src/main/assets/sample_simple_calculator.js +++ b/app/src/main/assets/sample_simple_calculator.js @@ -1,17 +1,15 @@ "ui"; -importClass("com.afollestad.materialdialogs.MaterialDialog.Builder"); -importClass("com.afollestad.materialdialogs.MaterialDialog.InputCallback"); -importClass("android.content.DialogInterface.OnDismissListener"); +importClass("com.afollestad.materialdialogs.MaterialDialog"); -new Builder(activity) +new ThemeColorMaterialDialogBuilder(activity) .title("简单计算器") - .input("请输入算式", "1+1", new InputCallback(function(dialog, input){ + .input("请输入算式", "1+1", function(dialog, input){ eval("var ans = (" + input + ")"); toast(ans); - })) + }) .positiveText("计算") - .dismissListener(new OnDismissListener(function(dialog){ + .dismissListener(function(dialog){ activity.finish(); - })) + }) .show(); \ No newline at end of file diff --git a/app/src/main/java/com/stardust/app/NotRemindAgainDialog.java b/app/src/main/java/com/stardust/app/NotRemindAgainDialog.java index 1a10851f..93645a44 100644 --- a/app/src/main/java/com/stardust/app/NotRemindAgainDialog.java +++ b/app/src/main/java/com/stardust/app/NotRemindAgainDialog.java @@ -8,6 +8,7 @@ import android.widget.CompoundButton; import com.afollestad.materialdialogs.MaterialDialog; import com.stardust.scriptdroid.R; +import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; import static com.jecelyin.common.utils.StringUtils.md5; @@ -22,7 +23,7 @@ public class NotRemindAgainDialog extends MaterialDialog { } - public static class Builder extends MaterialDialog.Builder { + public static class Builder extends ThemeColorMaterialDialogBuilder { private String mKeyRemind; private boolean mRemind; diff --git a/app/src/main/java/com/stardust/app/OnActivityResultDelegate.java b/app/src/main/java/com/stardust/app/OnActivityResultDelegate.java new file mode 100644 index 00000000..78f6a085 --- /dev/null +++ b/app/src/main/java/com/stardust/app/OnActivityResultDelegate.java @@ -0,0 +1,47 @@ +package com.stardust.app; + +import android.app.Activity; +import android.content.Intent; +import android.util.SparseArray; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Stardust on 2017/3/5. + */ + +public interface OnActivityResultDelegate { + + + void onActivityResult(int requestCode, int resultCode, Intent data); + + interface DelegateHost { + Manager getDelegateManger(); + } + + class Manager implements OnActivityResultDelegate { + + private SparseArray specialDelegate = new SparseArray<>(); + private List mDelegates = new ArrayList<>(); + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + OnActivityResultDelegate delegate = specialDelegate.get(requestCode); + if (delegate != null) { + delegate.onActivityResult(requestCode, resultCode, data); + } + for (OnActivityResultDelegate d : mDelegates) { + d.onActivityResult(requestCode, resultCode, data); + } + } + + public void addDelegate(OnActivityResultDelegate delegate) { + mDelegates.add(delegate); + } + + public void addDelegate(int requestCode, OnActivityResultDelegate delegate) { + specialDelegate.put(requestCode, delegate); + } + } + +} diff --git a/app/src/main/java/com/stardust/scriptdroid/App.java b/app/src/main/java/com/stardust/scriptdroid/App.java index 5bcda458..ed64a42b 100644 --- a/app/src/main/java/com/stardust/scriptdroid/App.java +++ b/app/src/main/java/com/stardust/scriptdroid/App.java @@ -11,9 +11,13 @@ import com.stardust.scriptdroid.droid.runtime.action.ActionPerformAccessibilityD import com.stardust.scriptdroid.record.AccessibilityRecorderDelegate; import com.stardust.scriptdroid.service.AccessibilityWatchDogService; import com.stardust.scriptdroid.ui.error.ErrorReportActivity; +import com.stardust.theme.ThemeColorManager; import com.stardust.util.CrashHandler; import com.stardust.util.StateObserver; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; + import java.lang.ref.WeakReference; /** @@ -37,6 +41,7 @@ public class App extends Application { public void onCreate() { super.onCreate(); + ThemeColorManager.init(this); if (LeakCanary.isInAnalyzerProcess(this)) { return; } diff --git a/app/src/main/java/com/stardust/scriptdroid/Pref.java b/app/src/main/java/com/stardust/scriptdroid/Pref.java index 352617fd..db1666a1 100644 --- a/app/src/main/java/com/stardust/scriptdroid/Pref.java +++ b/app/src/main/java/com/stardust/scriptdroid/Pref.java @@ -12,6 +12,7 @@ public class Pref { private static final SharedPreferences DISPOSABLE_BOOLEAN = App.getApp().getSharedPreferences("DISPOSABLE_BOOLEAN", Context.MODE_PRIVATE); public static final String SAMPLE_SCRIPTS_COPIED = "SAMPLE_SCRIPTS_COPIED"; private static final String KEY_MAX_TEXT_LENGTH_FOR_CODE_COMPLETION = "KEY_MAX_TEXT_LENGTH_FOR_CODE_COMPLETION"; + public static final String KEY_DRAWER_HEADER_IMAGE_PATH = "KEY_DRAWER_HEADER_IMAGE_PATH"; public static SharedPreferences def() { return PreferenceManager.getDefaultSharedPreferences(App.getApp()); diff --git a/app/src/main/java/com/stardust/scriptdroid/bounds_assist/BoundsAssistant.java b/app/src/main/java/com/stardust/scriptdroid/bounds_assist/BoundsAssistant.java index 57fdc58b..b23dbc84 100644 --- a/app/src/main/java/com/stardust/scriptdroid/bounds_assist/BoundsAssistant.java +++ b/app/src/main/java/com/stardust/scriptdroid/bounds_assist/BoundsAssistant.java @@ -12,6 +12,7 @@ import com.stardust.scriptdroid.App; import com.stardust.scriptdroid.Pref; import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.service.AccessibilityDelegate; +import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; /** @@ -38,7 +39,7 @@ public class BoundsAssistant implements AccessibilityDelegate { } private static void showAssistModeInfoDialog() { - new MaterialDialog.Builder(App.currentActivity()) + new ThemeColorMaterialDialogBuilder(App.currentActivity()) .title(R.string.text_alert) .content(R.string.assist_mode_notice) .positiveText(R.string.ok) diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/Droid.java b/app/src/main/java/com/stardust/scriptdroid/droid/Droid.java index 01974fa3..4e7055ee 100644 --- a/app/src/main/java/com/stardust/scriptdroid/droid/Droid.java +++ b/app/src/main/java/com/stardust/scriptdroid/droid/Droid.java @@ -1,13 +1,14 @@ package com.stardust.scriptdroid.droid; -import android.app.Activity; +import android.support.annotation.NonNull; import com.jraska.console.timber.ConsoleTree; import com.stardust.scriptdroid.App; import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.droid.runtime.DroidRuntime; -import com.stardust.scriptdroid.droid.script.DuktapeJavaScriptEngine; +import com.stardust.scriptdroid.droid.runtime.ScriptStopException; import com.stardust.scriptdroid.droid.script.JavaScriptEngine; +import com.stardust.scriptdroid.droid.script.RhinoJavaScriptEngine; import com.stardust.scriptdroid.droid.script.ScriptExecuteActivity; import com.stardust.scriptdroid.file.FileUtils; @@ -23,6 +24,8 @@ import timber.log.Timber; public class Droid { + public static final java.lang.String STAY = "\"stay\";"; + static { Timber.plant(new ConsoleTree.Builder() .infoColor(0xcc000000) @@ -32,15 +35,30 @@ public class Droid { public static final String UI = "\"ui\";"; public interface OnRunFinishedListener extends Serializable { - void onRunFinished(Object result, Exception e); + void onRunFinished(Object result); + + void onException(@NonNull Exception e); + } + + public static class SimpleOnRunFinishedListener implements OnRunFinishedListener { + + @Override + public void onRunFinished(Object result) { + + } + + @Override + public void onException(@NonNull Exception e) { + + } } private static final DroidRuntime RUNTIME = DroidRuntime.getRuntime(); - public static final JavaScriptEngine JAVA_SCRIPT_ENGINE = new DuktapeJavaScriptEngine(RUNTIME); - private static final OnRunFinishedListener DEFAULT_LISTENER = new OnRunFinishedListener() { + public static final JavaScriptEngine JAVA_SCRIPT_ENGINE = new RhinoJavaScriptEngine(RUNTIME); + private static final OnRunFinishedListener DEFAULT_LISTENER = new SimpleOnRunFinishedListener() { @Override - public void onRunFinished(Object result, Exception e) { - if (e != null) { + public void onException(@NonNull Exception e) { + if (!(e instanceof ScriptStopException)) { RUNTIME.toast(App.getApp().getString(R.string.text_error) + ": " + e.getMessage()); Timber.e(e, App.getApp().getString(R.string.text_error)); } @@ -65,7 +83,7 @@ public class Droid { try { checkFile(file); } catch (Exception e) { - listener.onRunFinished(null, e); + listener.onException(e); return; } runScript(FileUtils.readString(file), listener, RunningConfig.getDefault()); @@ -125,15 +143,11 @@ public class Droid { @Override public void run() { try { - mOnRunFinishedListener.onRunFinished(JAVA_SCRIPT_ENGINE.execute(mScript), null); + mOnRunFinishedListener.onRunFinished(JAVA_SCRIPT_ENGINE.execute(mScript)); } catch (Exception e) { - mOnRunFinishedListener.onRunFinished(null, e); + mOnRunFinishedListener.onException(e); } } - public RunScriptRunnable setActivity(Activity a) { - JAVA_SCRIPT_ENGINE.set("activity", Activity.class, a); - return this; - } } } diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/DroidRuntime.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/DroidRuntime.java index 88009d0f..b74b5a7e 100644 --- a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/DroidRuntime.java +++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/DroidRuntime.java @@ -1,5 +1,6 @@ package com.stardust.scriptdroid.droid.runtime; +import android.accessibilityservice.AccessibilityService; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; @@ -7,14 +8,17 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.Rect; +import android.os.Environment; import android.os.Handler; import android.support.annotation.Nullable; +import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; import com.jraska.console.Console; import com.stardust.scriptdroid.App; +import com.stardust.scriptdroid.Pref; import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.droid.runtime.action.Action; import com.stardust.scriptdroid.droid.runtime.action.ActionFactory; @@ -22,10 +26,15 @@ import com.stardust.scriptdroid.droid.runtime.action.ActionPerformAccessibilityD import com.stardust.scriptdroid.droid.runtime.action.ActionTarget; import com.stardust.scriptdroid.droid.runtime.action.GetTextAction; import com.stardust.scriptdroid.droid.runtime.api.IDroidRuntime; +import com.stardust.scriptdroid.file.FileUtils; +import com.stardust.scriptdroid.service.AccessibilityDelegate; import com.stardust.scriptdroid.service.AccessibilityWatchDogService; import com.stardust.scriptdroid.tool.Shell; import com.stardust.scriptdroid.ui.console.ConsoleActivity; +import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; +import com.stardust.view.accessibility.AccessibilityServiceUtils; +import java.io.File; import java.util.List; import timber.log.Timber; @@ -81,8 +90,8 @@ public class DroidRuntime implements IDroidRuntime { } @Override - public ActionTarget text(String text) { - return new ActionTarget.TextActionTarget(text); + public ActionTarget text(String text, int i) { + return new ActionTarget.TextActionTarget(text, i); } @Override @@ -95,6 +104,10 @@ public class DroidRuntime implements IDroidRuntime { return new ActionTarget.EditableActionTarget(i); } + public ActionTarget id(String id) { + return new ActionTarget.IdActionTarget(id); + } + @Override public boolean click(ActionTarget target) { return performAction(target.createAction(AccessibilityNodeInfo.ACTION_CLICK)); @@ -194,8 +207,22 @@ public class DroidRuntime implements IDroidRuntime { private void ensureAccessibilityServiceEnable() { if (AccessibilityWatchDogService.getInstance() == null) { - toast(App.getApp().getString(R.string.text_no_accessibility_permission)); - throw new ScriptStopException(App.getApp().getString(R.string.text_no_accessibility_permission)); + String errorMessage = null; + if (AccessibilityServiceUtils.isAccessibilityServiceEnabled(App.getApp(), AccessibilityWatchDogService.class)) { + errorMessage = App.getApp().getString(R.string.text_auto_operate_service_enabled_but_not_running); + } else { + if (Pref.def().getBoolean(App.getApp().getString(R.string.key_enable_accessibility_service_by_root), false)) { + if (!AccessibilityServiceUtils.enableAccessibilityServiceByRootAndWaitFor(App.getApp(), AccessibilityWatchDogService.class, 3000)) { + errorMessage = App.getApp().getString(R.string.text_enable_accessibility_service_by_root_timeout); + } + } else { + errorMessage = App.getApp().getString(R.string.text_no_accessibility_permission); + } + } + if (errorMessage != null) { + toast(errorMessage); + throw new ScriptStopException(errorMessage); + } } } @@ -222,8 +249,20 @@ public class DroidRuntime implements IDroidRuntime { } } - public String shell(String cmd, int root) { - return Shell.execCommand(cmd, root != 0).toString(); + public void addAccessibilityDelegate(final Object delegate) { + if (delegate == null) + return; + AccessibilityWatchDogService.addDelegate(new AccessibilityDelegate() { + @Override + public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) { + + return false; + } + }, 500); + } + + public Shell.CommandResult shell(String cmd, int root) { + return Shell.execCommand(cmd, root != 0); } public String getPackageName() { @@ -246,14 +285,17 @@ public class DroidRuntime implements IDroidRuntime { Thread.interrupted(); } + public String readFile(String path) { + return FileUtils.readString(new File(Environment.getExternalStorageDirectory() + "/" + path)); + } @Override - public MaterialDialog.Builder dialog() { + public ThemeColorMaterialDialogBuilder dialog() { if (App.currentActivity() == null) { Toast.makeText(App.getApp(), R.string.text_cannot_create_dialog_when_app_invisible, Toast.LENGTH_SHORT).show(); throw new ScriptStopException(App.getApp().getString(R.string.text_cannot_create_dialog_when_app_invisible)); } - return new MaterialDialog.Builder(App.currentActivity()) { + return new ThemeColorMaterialDialogBuilder(App.currentActivity()) { public MaterialDialog show() { mUIHandler.post(new Runnable() { diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionFactory.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionFactory.java index b5e3be3f..efdb4d80 100644 --- a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionFactory.java +++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionFactory.java @@ -24,11 +24,11 @@ public class ActionFactory { .entry(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, null) .map(); - public static Action createActionWithTextFilter(int action, String text) { + public static Action createActionWithTextFilter(int action, String text, int index) { if (searchUpAction.containsKey(action)) - return new SearchUpTargetAction(action, new FilterAction.TextFilter(text)); + return new SearchUpTargetAction(action, new FilterAction.TextFilter(text, index)); else - return new DepthFirstSearchTargetAction(action, new FilterAction.TextFilter(text)); + return new DepthFirstSearchTargetAction(action, new FilterAction.TextFilter(text, index)); } public static Action createActionWithBoundsFilter(int action, Rect rect) { @@ -54,7 +54,11 @@ public class ActionFactory { return new ScrollMaxAction(action); } - public static Action createScrollAction(int action, int i){ + public static Action createScrollAction(int action, int i) { return new ScrollAction(action, i); } + + public static Action createActionWithIdFilter(int action, String id) { + return new FilterAction.SimpleFilterAction(action, new FilterAction.IdFilter(id)); + } } diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionPerformAccessibilityDelegate.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionPerformAccessibilityDelegate.java index 344197e2..be4979c7 100644 --- a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionPerformAccessibilityDelegate.java +++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionPerformAccessibilityDelegate.java @@ -43,22 +43,24 @@ public class ActionPerformAccessibilityDelegate implements AccessibilityDelegate @Override public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) { - setLatestComponent(event.getPackageName(), event.getClassName()); - if (action == NO_ACTION) + synchronized (ActionPerformAccessibilityDelegate.class) { + setLatestComponent(event.getPackageName(), event.getClassName()); + if (action == NO_ACTION) + return false; + AccessibilityNodeInfo root = service.getRootInActiveWindow(); + if (root == null) { + Log.v(TAG, "root = null"); + } + Log.i(TAG, "perform action:" + action); + if (action.perform(root)) { + action.setResult(true); + onActionPerformed(); + } else if (!action.performUtilSucceed()) { + action.setResult(false); + onActionPerformed(); + } return false; - AccessibilityNodeInfo root = service.getRootInActiveWindow(); - if (root == null) { - Log.v(TAG, "root = null"); } - Log.i(TAG, "perform action:" + action); - if (action.perform(root)) { - action.setResult(true); - onActionPerformed(); - } else if (!action.performUtilSucceed()) { - action.setResult(false); - onActionPerformed(); - } - return false; } @@ -69,18 +71,18 @@ public class ActionPerformAccessibilityDelegate implements AccessibilityDelegate } } - private synchronized static void setLatestComponent(CharSequence latestPackage, CharSequence latestClass) { + private static void setLatestComponent(CharSequence latestPackage, CharSequence latestClass) { if (latestPackage == null) return; ActionPerformAccessibilityDelegate.latestPackage = latestPackage.toString(); - if(latestClass == null) + if (latestClass == null) return; try { ComponentName componentName = new ComponentName(latestPackage.toString(), latestClass.toString()); ActivityInfo activityInfo = App.getApp().getPackageManager().getActivityInfo(componentName, 0); ActionPerformAccessibilityDelegate.latestActivity = activityInfo.name; - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); + } catch (PackageManager.NameNotFoundException ignored) { + } } diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionTarget.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionTarget.java index 55cdcacf..6a37b8e7 100644 --- a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionTarget.java +++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/ActionTarget.java @@ -13,14 +13,16 @@ public interface ActionTarget { class TextActionTarget implements ActionTarget { String mText; + int mIndex; - public TextActionTarget(String text) { + public TextActionTarget(String text, int i) { mText = text; + mIndex = i; } @Override public Action createAction(int action, Object... params) { - return ActionFactory.createActionWithTextFilter(action, mText); + return ActionFactory.createActionWithTextFilter(action, mText, mIndex); } } @@ -50,4 +52,17 @@ public interface ActionTarget { return ActionFactory.createActionWithEditableFilter(action, mIndex, params[0].toString()); } } + + class IdActionTarget implements ActionTarget { + private String mId; + + public IdActionTarget(String id) { + mId = id; + } + + @Override + public Action createAction(int action, Object... params) { + return ActionFactory.createActionWithIdFilter(action, mId); + } + } } diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/FilterAction.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/FilterAction.java index 39c657bf..40d2a05f 100644 --- a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/FilterAction.java +++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/action/FilterAction.java @@ -22,14 +22,22 @@ public abstract class FilterAction extends Action { public static class TextFilter implements Filter { String mText; + int mIndex; - public TextFilter(String text) { + public TextFilter(String text, int index) { mText = text; + mIndex = index; } @Override public List filter(AccessibilityNodeInfo root) { - return root.findAccessibilityNodeInfosByText(mText); + + List list = root.findAccessibilityNodeInfosByText(mText); + if (mIndex == -1) + return list; + if (mIndex >= list.size()) + return Collections.EMPTY_LIST; + return Collections.singletonList(list.get(mIndex)); } } @@ -68,21 +76,6 @@ public abstract class FilterAction extends Action { } } - - private Filter mFilter; - - public FilterAction(Filter filter) { - mFilter = filter; - } - - public boolean perform(AccessibilityNodeInfo root) { - if (root == null) - return false; - return perform(mFilter.filter(root)); - } - - public abstract boolean perform(List nodes); - public static class EditableFilter implements Filter { private int mIndex; @@ -116,4 +109,57 @@ public abstract class FilterAction extends Action { return list; } } + + public static class IdFilter implements Filter { + + private final String mId; + + public IdFilter(String id) { + this.mId = id; + } + + @Override + public List filter(AccessibilityNodeInfo root) { + return root.findAccessibilityNodeInfosByViewId(mId); + } + } + + + private Filter mFilter; + + public FilterAction(Filter filter) { + mFilter = filter; + } + + public boolean perform(AccessibilityNodeInfo root) { + if (root == null) + return false; + return perform(mFilter.filter(root)); + } + + public abstract boolean perform(List nodes); + + public static class SimpleFilterAction extends FilterAction { + + private int mAction; + + public SimpleFilterAction(int action, Filter filter) { + super(filter); + mAction = action; + } + + @Override + public boolean perform(List nodes) { + if(nodes == null || nodes.isEmpty()) + return false; + boolean succeed = true; + for(AccessibilityNodeInfo nodeInfo : nodes){ + if(!nodeInfo.performAction(mAction)){ + succeed = false; + } + } + return succeed; + } + } + } diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/api/IDroidRuntime.java b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/api/IDroidRuntime.java index 4f932863..27a9934f 100644 --- a/app/src/main/java/com/stardust/scriptdroid/droid/runtime/api/IDroidRuntime.java +++ b/app/src/main/java/com/stardust/scriptdroid/droid/runtime/api/IDroidRuntime.java @@ -2,6 +2,7 @@ package com.stardust.scriptdroid.droid.runtime.api; import com.afollestad.materialdialogs.MaterialDialog; import com.stardust.scriptdroid.droid.runtime.action.ActionTarget; +import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; /** * Created by Stardust on 2017/1/27. @@ -16,7 +17,7 @@ public interface IDroidRuntime { String[] getPackageName(String appName); - ActionTarget text(String text); + ActionTarget text(String text, int i); ActionTarget bounds(int left, int top, int right, int bottom); @@ -50,5 +51,5 @@ public interface IDroidRuntime { void stop(); - MaterialDialog.Builder dialog(); + ThemeColorMaterialDialogBuilder dialog(); } diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/script/DuktapeJavaScriptEngine.java b/app/src/main/java/com/stardust/scriptdroid/droid/script/DuktapeJavaScriptEngine.java index f3d63058..c158e974 100644 --- a/app/src/main/java/com/stardust/scriptdroid/droid/script/DuktapeJavaScriptEngine.java +++ b/app/src/main/java/com/stardust/scriptdroid/droid/script/DuktapeJavaScriptEngine.java @@ -1,13 +1,13 @@ package com.stardust.scriptdroid.droid.script; import com.efurture.script.JSTransformer; -import com.furture.react.DuktapeEngine; import com.stardust.scriptdroid.App; import com.stardust.scriptdroid.droid.Droid; import com.stardust.scriptdroid.droid.runtime.api.IDroidRuntime; import java.io.IOException; import java.io.StringReader; +import java.lang.reflect.Field; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; @@ -17,46 +17,40 @@ import java.util.TimerTask; /** * Created by Stardust on 2017/1/27. */ - -public class DuktapeJavaScriptEngine implements JavaScriptEngine { +/* +public class DuktapeJavaScriptEngine extends JavaScriptEngine { private final Map mThreadDuktapeEngineMap = new Hashtable<>(); - private static final String INIT_SCRIPT; + private static final String INIT_SCRIPT = parse(Init.getInitScript()); private Map mVariableMap = new HashMap<>(); - static { - try { - INIT_SCRIPT = JSTransformer.parse(new StringReader(Init.INIT_SCRIPT)); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - public DuktapeJavaScriptEngine(IDroidRuntime runtime) { setRuntime(runtime); } private void setRuntime(IDroidRuntime runtime) { - set("droid", IDroidRuntime.class, runtime); + set("droid", runtime); } @Override - public Object execute(String script) throws IOException { + public Object execute(String script) { DuktapeEngine duktapeEngine = new DuktapeEngine(); init(duktapeEngine); add(duktapeEngine, Thread.currentThread()); - Object result; + Object code = duktapeEngine.execute(parse(script)); + if (!script.startsWith(Droid.UI) && !script.startsWith(Droid.STAY)) + removeAndDestroy(); + return code; + } + + private static String parse(String script) { try { - result = duktapeEngine.execute(JSTransformer.parse(new StringReader(script))); + return JSTransformer.parse(new StringReader(script)); } catch (IOException e) { - removeAndStop(Thread.currentThread()); - throw e; + //Should not happen + throw new RuntimeException(e); } - if (!script.startsWith(Droid.UI)) - removeAndDestroy(Thread.currentThread()); - return result; } @@ -69,18 +63,11 @@ public class DuktapeJavaScriptEngine implements JavaScriptEngine { } - public void removeAndStop(Thread thread) { - synchronized (mThreadDuktapeEngineMap) { - DuktapeEngine engine = mThreadDuktapeEngineMap.remove(thread); - forceStop(engine, thread); - } - } - @Override - public void removeAndDestroy(Thread thread) { + public void removeAndDestroy() { synchronized (mThreadDuktapeEngineMap) { - DuktapeEngine engine = mThreadDuktapeEngineMap.remove(thread); + DuktapeEngine engine = mThreadDuktapeEngineMap.remove(Thread.currentThread()); if (engine != null) engine.destory(); } @@ -109,10 +96,6 @@ public class DuktapeJavaScriptEngine implements JavaScriptEngine { } } - @Override - public void set(String varName, Class c, T value) { - mVariableMap.put(varName, value); - } @Override public int stopAll() { @@ -127,4 +110,25 @@ public class DuktapeJavaScriptEngine implements JavaScriptEngine { return n; } + private static final Field ptrField; + + static { + Field field = null; + try { + field = DuktapeEngine.class.getDeclaredField("ptr"); + field.setAccessible(true); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + ptrField = field; + } + + public static boolean isDestroyed(DuktapeEngine engine) { + try { + return ((long) ptrField.get(engine)) == 0; + } catch (IllegalAccessException e) { + return true; + } + } } +*/ \ No newline at end of file diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/script/JavaScriptEngine.java b/app/src/main/java/com/stardust/scriptdroid/droid/script/JavaScriptEngine.java index 8909a275..9b95602e 100644 --- a/app/src/main/java/com/stardust/scriptdroid/droid/script/JavaScriptEngine.java +++ b/app/src/main/java/com/stardust/scriptdroid/droid/script/JavaScriptEngine.java @@ -1,29 +1,47 @@ package com.stardust.scriptdroid.droid.script; import com.stardust.scriptdroid.App; +import com.stardust.scriptdroid.BuildConfig; import com.stardust.scriptdroid.droid.script.file.AssetScript; -import java.io.IOException; +import java.util.HashMap; +import java.util.Map; /** * Created by Stardust on 2017/1/27. */ -public interface JavaScriptEngine { +public abstract class JavaScriptEngine { - Object execute(String script) throws IOException; + Map mVariableMap = new HashMap<>(); - void set(String varName, Class c, T value); + protected String preprocess(String script) { + return script; + } - int stopAll(); + public abstract Object execute(String script); - void removeAndDestroy(Thread thread); + public void set(String varName, Object value) { + mVariableMap.put(varName, value); + } - class Init { - public static final String INIT_SCRIPT = readInitScript(); + public abstract void removeAndDestroy(); - static String readInitScript() { + public abstract int stopAll(); + + static class Init { + private static final String INIT_SCRIPT = readInitScript(); + + private static String readInitScript() { return AssetScript.read(App.getApp(), "javasccript_engine_init.js"); } + + static String getInitScript() { + if (BuildConfig.DEBUG) { + return readInitScript(); + } else { + return INIT_SCRIPT; + } + } } } diff --git a/app/src/main/java/com/stardust/scriptdroid/droid/script/RhinoJavaScriptEngine.java b/app/src/main/java/com/stardust/scriptdroid/droid/script/RhinoJavaScriptEngine.java new file mode 100644 index 00000000..7274bdf2 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/droid/script/RhinoJavaScriptEngine.java @@ -0,0 +1,107 @@ +package com.stardust.scriptdroid.droid.script; + +import com.stardust.scriptdroid.App; +import com.stardust.scriptdroid.droid.Droid; +import com.stardust.scriptdroid.droid.runtime.ScriptStopException; +import com.stardust.scriptdroid.droid.runtime.api.IDroidRuntime; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ContextFactory; +import org.mozilla.javascript.ImporterTopLevel; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Created by Stardust on 2017/3/1. + */ + +public class RhinoJavaScriptEngine extends JavaScriptEngine { + + static { + ContextFactory.initGlobal(new InterruptibleContextFactory()); + } + + private final Set mThreads = new HashSet<>(); + + public RhinoJavaScriptEngine(IDroidRuntime runtime) { + set("droid", runtime); + } + + @Override + public Object execute(String script) { + Context context = createContext(); + Scriptable scope = createScope(context); + init(context, scope); + Object result = context.evaluateString(scope, script, "