@@ -9,8 +9,8 @@ android {
|
|||||||
applicationId "com.stardust.scriptdroid"
|
applicationId "com.stardust.scriptdroid"
|
||||||
minSdkVersion 17
|
minSdkVersion 17
|
||||||
targetSdkVersion 23
|
targetSdkVersion 23
|
||||||
versionCode 147
|
versionCode 151
|
||||||
versionName "2.0.14 Beta"
|
versionName "2.0.14 Beta2.1"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
ndk {
|
ndk {
|
||||||
|
|||||||
@@ -5,6 +5,11 @@
|
|||||||
"type": "markdown",
|
"type": "markdown",
|
||||||
"path":"documentation"
|
"path":"documentation"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "如何阅读本文档",
|
||||||
|
"type": "markdown",
|
||||||
|
"path":"documentation"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "ECMAScript教程",
|
"title": "ECMAScript教程",
|
||||||
"type": "catalogue"
|
"type": "catalogue"
|
||||||
@@ -24,16 +29,6 @@
|
|||||||
"type": "markdown",
|
"type": "markdown",
|
||||||
"path":"documentation"
|
"path":"documentation"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"title": "控制台与输出",
|
|
||||||
"type": "markdown",
|
|
||||||
"path":"documentation"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "UI(用户界面)",
|
|
||||||
"type": "markdown",
|
|
||||||
"path":"documentation"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "选择器",
|
"title": "选择器",
|
||||||
"type": "markdown",
|
"type": "markdown",
|
||||||
@@ -45,7 +40,12 @@
|
|||||||
"path": "documentation"
|
"path": "documentation"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "模块与第三方jar",
|
"title": "图片与图色处理",
|
||||||
|
"type": "markdown",
|
||||||
|
"path":"documentation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "控制台与输出",
|
||||||
"type": "markdown",
|
"type": "markdown",
|
||||||
"path":"documentation"
|
"path":"documentation"
|
||||||
},
|
},
|
||||||
@@ -59,6 +59,26 @@
|
|||||||
"type": "markdown",
|
"type": "markdown",
|
||||||
"path":"documentation"
|
"path":"documentation"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "UI(用户界面)",
|
||||||
|
"type": "markdown",
|
||||||
|
"path":"documentation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "文件读写",
|
||||||
|
"type": "markdown",
|
||||||
|
"path":"documentation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "应用",
|
||||||
|
"type": "markdown",
|
||||||
|
"path":"documentation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "模块与第三方jar",
|
||||||
|
"type": "markdown",
|
||||||
|
"path":"documentation"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "调用Java API",
|
"title": "调用Java API",
|
||||||
"type": "markdown",
|
"type": "markdown",
|
||||||
|
|||||||
@@ -41,16 +41,30 @@ launchApp("微信");
|
|||||||
|
|
||||||
设置剪贴板内容。此剪贴板即系统剪贴板,在一般应用的输入框中"粘贴"既可使用。
|
设置剪贴板内容。此剪贴板即系统剪贴板,在一般应用的输入框中"粘贴"既可使用。
|
||||||
|
|
||||||
|
### getClip()
|
||||||
|
|
||||||
|
返回系统剪贴板的内容。
|
||||||
|
|
||||||
|
### waitForActivity(activity\[, period = 200\])
|
||||||
|
* activity Activity名称
|
||||||
|
* period 轮询等待间隔(毫秒)
|
||||||
|
|
||||||
|
等待指定的Activity出现。
|
||||||
|
|
||||||
|
|
||||||
|
### waitForPackage(package\[, period = 200\])
|
||||||
|
* package 包名
|
||||||
|
* period 轮询等待间隔(毫秒)
|
||||||
|
|
||||||
|
等待指定的应用出现。例如`waitForPackage("com.tencent.mm")`为等待当前界面为微信。
|
||||||
|
|
||||||
### isStopped()
|
### isStopped()
|
||||||
当脚本处于应当停止运行的状态时返回true, 否则返回false。
|
当脚本处于应当停止运行的状态时返回true, 否则返回false。
|
||||||
由于脚本引擎的关系,有时即使强制停止正在运行的脚本也不会生效,此时会出现isStopped()为true但脚本仍在运行的情况。
|
由于脚本引擎的关系,有时即使强制停止正在运行的脚本也不会生效,此时会出现isStopped()为true但脚本仍在运行的情况。
|
||||||
|
|
||||||
### notStopped()
|
### notStopped()
|
||||||
当脚本处于应当停止运行的状态时返回false, 否则返回false。
|
当脚本处于应当停止运行的状态时返回false, 否则返回false。
|
||||||
为了避免脚本处于循环中无法正常结束,在诸如while(true)的循环中建议使用while(notStopped())代替,例如:
|
|
||||||
```
|
### stop()
|
||||||
while(notStopped()){
|
立即停止脚本运行。
|
||||||
//do something
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,179 @@
|
|||||||
|
目录:
|
||||||
|
* [截图](#截图)
|
||||||
|
* [images](#images)
|
||||||
|
* [colors](#colors)
|
||||||
|
* [Image](#Image)
|
||||||
|
* [Point](#Point)
|
||||||
|
|
||||||
|
# 截图
|
||||||
|
|
||||||
|
截图有关的函数需要安卓5.0以上才支持。
|
||||||
### requestScreenCapture(\[width, height\])
|
### requestScreenCapture(\[width, height\])
|
||||||
|
* width \<Number\> 截图宽度
|
||||||
|
* height \<Number\> 截图高度
|
||||||
|
|
||||||
参数width和height用于指定截图的分辨率,默认为屏幕宽高。
|
向系统申请屏幕截图权限,返回是否请求成功。
|
||||||
|
|
||||||
未完待续。
|
第一次使用该函数会弹出截图权限请求,建议选择“总是允许”。
|
||||||
|
|
||||||
|
这个函数只是申请截图权限,并不会真正执行截图,真正的截图函数是[captureScreen](#captureScreen())。
|
||||||
|
|
||||||
|
不指定参数时默认为屏幕宽高。指定参数时,并不会严格以width和height为截图宽度和高度,而是以和width, height接近的最适合的宽高为截图宽高。例如在1920\*1080屏幕中requestScreenCapture(600, 1000)请求的截图的高度一般是540\*960。
|
||||||
|
|
||||||
|
如果在第一次权限请求时选择"总是允许", 之后的脚本执行该函数通常耗时200毫秒以内(测试机型:小米6)。
|
||||||
|
|
||||||
|
建议在本软件界面运行该函数,在其他软件界面运行时容易出现一闪而过的黑屏。
|
||||||
|
|
||||||
|
### captureScreen()
|
||||||
|
|
||||||
|
截取当前屏幕并返回一个[Image](#Image)对象。该图片的宽高取决于requestScreenCapture所指定的宽高。
|
||||||
|
|
||||||
|
没有截图权限时执行该函数会抛出SecurityException。
|
||||||
|
|
||||||
|
该函数耗时一毫秒以内(测试机型:小米6)。因此不必担心截图的速度。图色处理的瓶颈通常在找色部分。
|
||||||
|
|
||||||
|
### captureScreen(path)
|
||||||
|
* path \<String\> 截图保存路径
|
||||||
|
|
||||||
|
截取当前屏幕并以PNG格式保存到path中。如果文件不存在会被创建;文件存在会被覆盖。
|
||||||
|
|
||||||
|
# images
|
||||||
|
|
||||||
|
images是图片与图色处理的工具。包括获取图片某点颜色,保存图片,找色等。
|
||||||
|
|
||||||
|
### images.pixel(image, x, y)
|
||||||
|
* image \<Image\> 图片
|
||||||
|
* x \<Number\> 要获取的像素的横坐标。坐标系以图片左上角为原点。
|
||||||
|
* y \<y\> 要获取的像素的纵坐标。
|
||||||
|
|
||||||
|
返回图片image在点(x, y)处的像素的ARGB值。
|
||||||
|
该值的格式为0xAARRGGBB。也就是,如果`var argb = images.pixel(image, x, y)`,那么可以通过`(argb & 0xFF000000) >> 24`获取透明度,通过`(argb & 0xFF0000) >> 16`获取R值,通过`(argb & 0xFF000000) >> 24`获取G值等,或者通过`images.alpha(argb)`, `images.red(argb)`等获取。
|
||||||
|
|
||||||
|
### images.saveImage(image, path)
|
||||||
|
|
||||||
|
* image \<Image\> 图片
|
||||||
|
* path \<String\> 路径
|
||||||
|
|
||||||
|
把图片image以PNG格式保存到path中。如果文件不存在会被创建;文件存在会被覆盖。
|
||||||
|
|
||||||
|
### images.findColor(image, color, options)
|
||||||
|
* image \<Image\> 图片
|
||||||
|
* color \<Number\>或\<String\> 要寻找的颜色的RGB值。如果是一个整数,则以0xRRGGBB的形式代表RGB值(A通道会被忽略);如果是字符串,则以"#RRGGBB"代表其RGB值。
|
||||||
|
* options \<Object\> 选项
|
||||||
|
|
||||||
|
在图片中寻找颜色color。找到时返回找到的点[Point](#Point),找不到时返回null。
|
||||||
|
选项包括:
|
||||||
|
* region \<Array\> 找色区域。是一个两个或四个元素的数组。(region\[0\], region\[1\])表示找色区域的左上角;region\[2\]*region\[3\]表示找色区域的宽高。如果只有region只有两个元素,则找色区域为(region\[0\], region\[1\])到屏幕右下角。如果不指定region选项,则找色区域为整张图片。
|
||||||
|
* threads \<Number\> 指定找色使用的线程数。不能超过16。最适合的线程数取决于设备。默认线程数为2。
|
||||||
|
* algorithm \<String\> 指定颜色匹配算法。包括:
|
||||||
|
* "equal": 相等匹配,只有与给定颜色color完全相等时才匹配。
|
||||||
|
* "diff": 差值匹配。与给定颜色的R、G、B差的绝对值之和小于threshold时匹配。
|
||||||
|
* "rgb": rgb欧拉距离相似度。与给定颜色color的rgb欧拉距离小于等于threshold时匹配。
|
||||||
|
* "rgb+": 加权rgb欧拉距离匹配([LAB Delta E](https://en.wikipedia.org/wiki/Color_difference))。
|
||||||
|
* "hs": hs欧拉距离匹配。hs为HSV空间的色调值。
|
||||||
|
* threshold \<Number\> 找色时颜色相似度的临界值,范围为0~255(越小越相似,0为颜色相等,255为任何颜色都能匹配)。默认为16。threshold和浮点数相似度(0.0~1.0)的换算为 similarity = (255 - threshold) / 255.
|
||||||
|
|
||||||
|
该函数也可以作为全局函数使用。
|
||||||
|
|
||||||
|
|
||||||
|
### images.findColorInRegion(img, color, x, y\[, width, height, threads, algorithm, threshold\])
|
||||||
|
|
||||||
|
相当于
|
||||||
|
```
|
||||||
|
images.findColor(img, color, {
|
||||||
|
region: [x, y, width, height],
|
||||||
|
algorithm: algorithm,
|
||||||
|
threshold: threshold,
|
||||||
|
threads: threads
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
该函数也可以作为全局函数使用。
|
||||||
|
|
||||||
|
### images.findColorEquals(img, color, x, y, width, height, threads)
|
||||||
|
|
||||||
|
相当于
|
||||||
|
```
|
||||||
|
images.findColor(img, color, {
|
||||||
|
region: [x, y, width, height],
|
||||||
|
algorithm: "equal",
|
||||||
|
threads: threads
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
该函数也可以作为全局函数使用。
|
||||||
|
|
||||||
|
|
||||||
|
### images.detectsColor(image, color, x, y\[, threshold = 16, algorithm = "rgb"\])
|
||||||
|
* image \<Image\> 图片
|
||||||
|
* color \<Number\> 要检测的颜色
|
||||||
|
* x \<Number\> 要检测的位置横坐标
|
||||||
|
* y \<Number\> 要检测的位置纵坐标
|
||||||
|
* threshold \<Number\> 颜色相似度临界值,默认为16
|
||||||
|
* algorithm \<String\> 颜色匹配算法,默认为rgb欧式距离
|
||||||
|
|
||||||
|
返回图片image在位置(x, y)处是否匹配到颜色color。有关threshold和algorithm的信息,参考findColor函数。
|
||||||
|
|
||||||
|
# colors
|
||||||
|
|
||||||
|
colors是颜色处理的工具对象。包含一些常用方法,除了android.graphics.Color的所有方法外,加上toString方法。
|
||||||
|
|
||||||
|
### colors.toString(color)
|
||||||
|
* color \<Number\> 整数RGB颜色值
|
||||||
|
|
||||||
|
返回颜色值的字符串,格式为 #AARRGGBB。
|
||||||
|
|
||||||
|
### colors.red(color)
|
||||||
|
* color \<Number\> 整数RGB颜色值
|
||||||
|
|
||||||
|
返回颜色color的R通道的值,范围0~255.
|
||||||
|
|
||||||
|
### colors.green(color)
|
||||||
|
* color \<Number\> 整数RGB颜色值
|
||||||
|
|
||||||
|
返回颜色color的G通道的值,范围0~255.
|
||||||
|
|
||||||
|
### colors.blue(color)
|
||||||
|
* color \<Number\> 整数RGB颜色值
|
||||||
|
|
||||||
|
返回颜色color的B通道的值,范围0~255.
|
||||||
|
|
||||||
|
### colors.alpha(color)
|
||||||
|
* color \<Number\> 整数RGB颜色值
|
||||||
|
|
||||||
|
返回颜色color的Alpha通道的值,范围0~255.
|
||||||
|
|
||||||
|
### colors.rgb(red, green, blue)
|
||||||
|
* red \<Number\> 颜色的R通道的值
|
||||||
|
* blue \<Number\> 颜色的G通道的值
|
||||||
|
* green \<Number\> 颜色的B通道的值
|
||||||
|
|
||||||
|
返回这些颜色通道构成的整数颜色值。Alpha通道将是255(不透明)。
|
||||||
|
|
||||||
|
### colors.argb(alpha, red, green, blue)
|
||||||
|
* alpha \<Number\> 颜色的Alpha通道的值
|
||||||
|
* red \<Number\> 颜色的R通道的值
|
||||||
|
* green \<Number\> 颜色的G通道的值
|
||||||
|
* blue \<Number\> 颜色的B通道的值
|
||||||
|
|
||||||
|
返回这些颜色通道构成的整数颜色值。
|
||||||
|
|
||||||
|
# Image
|
||||||
|
|
||||||
|
[captureScreen](#captureScreen()) 返回的对象。
|
||||||
|
|
||||||
|
### Image.getWidth()
|
||||||
|
|
||||||
|
返回以像素为单位图片宽度。
|
||||||
|
|
||||||
|
### Image.getHeight()
|
||||||
|
|
||||||
|
返回以像素为单位的图片高度。
|
||||||
|
|
||||||
|
# Point
|
||||||
|
|
||||||
|
findColor返回的对象。
|
||||||
|
|
||||||
|
### Point.x
|
||||||
|
### Point.y
|
||||||
|
坐标。
|
||||||
|
|||||||
30
app/src/main/assets/help/documentation/如何阅读本文档.md
Normal file
30
app/src/main/assets/help/documentation/如何阅读本文档.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
先看一个例子,下面是《自动操作函数》的章节中input函数的部分说明。
|
||||||
|
### input(\[i, \]text)
|
||||||
|
* i \<Number\> 表示要输入的为第i + 1个输入框
|
||||||
|
* text \<String\> 要输入的文本
|
||||||
|
|
||||||
|
input表示函数名,括号内为函数的参数。下面是参数列表,"\<Number\>"表示该参数类型为数值,"\<String\>"表示该参数类型为字符串。
|
||||||
|
|
||||||
|
例如`input(1, "啦啦啦")`,执行这个语句会在屏幕上的第2个输入框处输入"啦啦啦“。
|
||||||
|
|
||||||
|
方括号\[ \]表示参数为可选参数。也就是说,可以省略i直接调用input。例如`input("嘿嘿嘿")`,按照文档,这个语句会在屏幕上所有输入框输入"嘿嘿嘿"。
|
||||||
|
|
||||||
|
调用有可选参数的函数时请**不要**写上方括号。
|
||||||
|
|
||||||
|
我们再看第二个例子。图片和图色处理中detectsColor函数的部分说明。
|
||||||
|
|
||||||
|
### images.detectsColor(image, color, x, y\[, threshold = 16, algorithm = "rgb"\])
|
||||||
|
* image \<Image\> 图片
|
||||||
|
* color \<Number\> 要检测的颜色
|
||||||
|
* x \<Number\> 要检测的位置横坐标
|
||||||
|
* y \<Number\> 要检测的位置纵坐标
|
||||||
|
* threshold \<Number\> 颜色相似度临界值,默认为16
|
||||||
|
* algorithm \<String\> 颜色匹配算法,默认为rgb欧式距离
|
||||||
|
|
||||||
|
同样地,"\[, threshold = 16, algorithm = "rgb"\]"为可选参数,并且,等于号=后面的值为参数的默认值。也就是如果不指定该参数,则该参数将会为这个值。
|
||||||
|
|
||||||
|
例如 `images.detectsColor(captureScreen(), "#112233", 100, 200)` 相当于 `images.detectsColor(captureScreen(), "#112233", 100, 200, 16, "rgb")`,而`images.detectsColor(captureScreen(), "#112233", 100, 200, 64)` 相当于`images.detectsColor(captureScreen(), "#112233", 100, 200, 64, "rgb")` 。
|
||||||
|
|
||||||
|
调用有可选参数及默认值的函数时请**不要**写上方括号和等于号。
|
||||||
|
|
||||||
|
好了先这样吧 :-\)
|
||||||
46
app/src/main/assets/help/documentation/应用.md
Normal file
46
app/src/main/assets/help/documentation/应用.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
### app.intent(intent)
|
||||||
|
* intent \<Object\> 一个表示Intent对象,其属性可以包括:
|
||||||
|
* action \<String\> 这个Intent的Action,比如"android.intent.action.SEND"
|
||||||
|
* type \<String\> 这个Intent的MimeType,比如"text/plain"
|
||||||
|
* data \<String\> 这个Intent的Data(Uri),可以是文件路径或者Url等。
|
||||||
|
* category \<Array\> 这个Intent的Category的字符串数组。
|
||||||
|
* packageName \<String\> 目标包名
|
||||||
|
* className \<String\> 目标Activity或Service等组件的名称
|
||||||
|
* extras \<Object\> 以键值对构成的这个Intent的Extras。
|
||||||
|
|
||||||
|
返回用intent对象构造的android.content.Intent对象。
|
||||||
|
|
||||||
|
如果你看了一脸懵逼,请百度[安卓Intent](https://www.baidu.com/s?wd=android%20Intent)。
|
||||||
|
|
||||||
|
### app.startActivity(intent)
|
||||||
|
* intent \<Object\> 与app.intent函数的参数一样的intent对象。
|
||||||
|
|
||||||
|
相当于context.startActivity(intent)。
|
||||||
|
|
||||||
|
|
||||||
|
### app.sendBroadcast(intent)
|
||||||
|
* intent \<Object\> 与app.intent函数的参数一样的intent对象。
|
||||||
|
|
||||||
|
相当于context.sendBroadcast(intent)。
|
||||||
|
|
||||||
|
### app.viewFile(path)
|
||||||
|
* path \<String\> 文件路径
|
||||||
|
|
||||||
|
用其他应用查看文件。
|
||||||
|
|
||||||
|
### app.editFile(path)
|
||||||
|
* path \<String\> 文件路径
|
||||||
|
|
||||||
|
用其他应用编辑文件。
|
||||||
|
|
||||||
|
### app.uninstall(packageName)
|
||||||
|
* packageName \<String\> 应用包名
|
||||||
|
|
||||||
|
卸载应用。
|
||||||
|
|
||||||
|
### app.openUrl(url)
|
||||||
|
* url \<String\> 网站的Url
|
||||||
|
|
||||||
|
用浏览器打开网站url。
|
||||||
|
|
||||||
@@ -1,18 +1,27 @@
|
|||||||
控制台通常用来输出一些调试信息和运算结果。
|
控制台通常用来输出一些调试信息和运算结果。
|
||||||
|
|
||||||
### openConsole()
|
### openConsole()
|
||||||
|
### console.show()
|
||||||
|
|
||||||
显示控制台。
|
显示控制台。
|
||||||
### clearConsole()
|
### clearConsole()
|
||||||
|
### console.clear()
|
||||||
|
|
||||||
清空控制台。
|
清空控制台。
|
||||||
|
|
||||||
### log(text)
|
### log(text)
|
||||||
|
### console.log(text)
|
||||||
|
|
||||||
* text \<String\> | \<Object\> 要打印到控制台的信息
|
* text \<String\> | \<Object\> 要打印到控制台的信息
|
||||||
|
|
||||||
### print(text)
|
在控制台中输出文本text并换行,例如`log("Hello world");`。当text是一个对象时则会转换String以后再输出。
|
||||||
等同于log。
|
|
||||||
|
|
||||||
在控制台中输出日志,例如`log("Hello world");`。当text是一个对象时则会转换String以后再输出。
|
### print(text)
|
||||||
|
|
||||||
|
在控制台中输出文本text。
|
||||||
|
|
||||||
### err(error)
|
### err(error)
|
||||||
|
### console.error(error)
|
||||||
* error \<String\> | \<Object\>
|
* error \<String\> | \<Object\>
|
||||||
|
|
||||||
在控制台中输出错误信息,以红色字体显示,例如:
|
在控制台中输出错误信息,以红色字体显示,例如:
|
||||||
@@ -29,4 +38,19 @@ try{
|
|||||||
### toast(message)
|
### toast(message)
|
||||||
* message \<String\> | \<Object\> 要显示的信息
|
* message \<String\> | \<Object\> 要显示的信息
|
||||||
|
|
||||||
以气泡显示信息message几秒。(具体时间取决于安卓系统)
|
以气泡显示信息message几秒。(具体时间取决于安卓系统)
|
||||||
|
|
||||||
|
### toastLog(message)
|
||||||
|
* message \<String\> | \<Object\> 要显示的信息
|
||||||
|
|
||||||
|
相当于`toast(message);log(message)`。
|
||||||
|
|
||||||
|
### console.assert(value, message)
|
||||||
|
* value \<Boolean\> 要断言的布尔值
|
||||||
|
* message \<String\> value为false时要输出的信息
|
||||||
|
|
||||||
|
断言。如果value为false则输出message并停止脚本运行。
|
||||||
|
|
||||||
|
### console.input(data\[, ...args\])
|
||||||
|
|
||||||
|
### console.rawInput(data\[, ...args\])
|
||||||
|
|||||||
196
app/src/main/assets/help/documentation/文件读写.md
Normal file
196
app/src/main/assets/help/documentation/文件读写.md
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
|
||||||
|
### open(path\[, mode = "r", encoding = "utf-8", bufferSize = 8192\])
|
||||||
|
* path \<String\> 文件路径,例如"/sdcard/1.txt"。
|
||||||
|
* mode \<String\> 文件打开模式,包括:
|
||||||
|
* "r": 只读模式。该模式下只能对文件执行**文本**读取操作。
|
||||||
|
* "w": 只写模式。该模式下只能对文件执行**文本**覆盖写入操作。
|
||||||
|
* "a": 附加模式。该模式下将会把写入的文本附加到文件末尾。
|
||||||
|
目前暂不支持二进制模式,随机读写模式。
|
||||||
|
* encoding \<String\> 字符编码。
|
||||||
|
* bufferSize \<Number\> 文件读写的缓冲区大小。
|
||||||
|
|
||||||
|
打开一个文件。根据打开模式返回不同的文件对象。包括:
|
||||||
|
* "r": 返回一个ReadableTextFile对象。
|
||||||
|
* "w", "a": 返回一个WritableTextFile对象。
|
||||||
|
|
||||||
|
对于"w"模式,如果文件并不存在,则会创建一个,已存在则会清空该文件内容;其他模式文件不存在会抛出FileNotFoundException。
|
||||||
|
|
||||||
|
# files
|
||||||
|
|
||||||
|
文件处理的工具对象。
|
||||||
|
|
||||||
|
### files.isFile(path)
|
||||||
|
* path \<String\> 路径
|
||||||
|
|
||||||
|
返回路径path是否是文件。
|
||||||
|
|
||||||
|
### files.isDir(path)
|
||||||
|
* path \<String\> 路径
|
||||||
|
|
||||||
|
返回路径path是否是文件夹。
|
||||||
|
|
||||||
|
### files.isDirEmpty(path)
|
||||||
|
* path \<String\> 路径
|
||||||
|
|
||||||
|
返回文件夹path是否为空文件夹。如果该路径并非文件夹,则直接返回false。
|
||||||
|
|
||||||
|
### files.join(parent, child)
|
||||||
|
* parent \<String\> 父目录路径
|
||||||
|
* child \<String\> 子路径
|
||||||
|
|
||||||
|
连接两个路径并返回,例如`files.join("/sdcard/", "1.txt")`返回"/sdcard/1.txt"。
|
||||||
|
|
||||||
|
### files.create(path)
|
||||||
|
* path \<String\> 路径
|
||||||
|
|
||||||
|
创建一个文件并返回是否创建成功。
|
||||||
|
|
||||||
|
### files.createIfNotExists(path)
|
||||||
|
* path \<String\> 路径
|
||||||
|
|
||||||
|
创建一个文件并返回是否创建成功。
|
||||||
|
|
||||||
|
### files.exists(path)
|
||||||
|
* path \<String\> 路径
|
||||||
|
|
||||||
|
返回在路径path处的文件是否存在。
|
||||||
|
|
||||||
|
### files.ensureDir(path)
|
||||||
|
* path \<String\> 路径
|
||||||
|
|
||||||
|
确保路径path所在的文件夹存在。
|
||||||
|
|
||||||
|
例如对于路径"/sdcard/Download/ABC/1.txt",如果/Download/文件夹不存在,则会先创建Download,再创建ABC文件夹。
|
||||||
|
|
||||||
|
### files.read(path\[, encoding = "utf-8"\])
|
||||||
|
* path \<String\> 路径
|
||||||
|
* encoding \<String\> 字符编码
|
||||||
|
|
||||||
|
读取文件path的所有内容并返回。
|
||||||
|
|
||||||
|
### files.write(path, text\[, encoding = "utf-8"\])
|
||||||
|
* path \<String\> 路径
|
||||||
|
* text \<String\> 要写入的文本内容
|
||||||
|
* encoding \<String\> 字符编码
|
||||||
|
|
||||||
|
把text写入到文件path中。如果文件存在则覆盖,不存在则创建。
|
||||||
|
|
||||||
|
### files.copy(fromPath, toPath)
|
||||||
|
* fromPath \<String\> 要复制的原文件路径
|
||||||
|
* toPath \<String\> 复制到的文件路径
|
||||||
|
|
||||||
|
复制文件。例如`files.copy("/sdcard/1.txt", "/sdcard/Download/1.txt")`。
|
||||||
|
|
||||||
|
### files.rename(path, newName)
|
||||||
|
* path \<String\> 要重命名的原文件路径
|
||||||
|
* newName \<String\> 要重命名的新文件名
|
||||||
|
|
||||||
|
重命名文件,并返回是否重命名成功。例如`files.rename("/sdcard/1.txt", "2.txt")`。
|
||||||
|
|
||||||
|
也可以用作文件移动,例如`files.rename("/sdcard/1.txt", "/sdcard/Download/1.txt")`会把1.txt文件从sd卡根目录移动到Download文件夹。
|
||||||
|
|
||||||
|
### files.renameWithoutExtension(path, newName)
|
||||||
|
* path \<String\> 要重命名的原文件路径
|
||||||
|
* newName \<String\> 要重命名的新文件名
|
||||||
|
|
||||||
|
重命名文件,不包含拓展名,并返回是否重命名成功。例如`files.rename("/sdcard/1.txt", "2")`会把1.txt重命名为2.txt。
|
||||||
|
|
||||||
|
### files.getName(path)
|
||||||
|
* path \<String\> 路径
|
||||||
|
|
||||||
|
返回文件的文件名。例如`files.getName("/sdcard/1.txt")`返回"1.txt"。
|
||||||
|
|
||||||
|
### files.getNameWithoutExtension(path)
|
||||||
|
* path \<String\> 路径
|
||||||
|
|
||||||
|
返回不含拓展名的文件的文件名。例如`files.getName("/sdcard/1.txt")`返回"1"。
|
||||||
|
|
||||||
|
### files.getExtension(path)
|
||||||
|
* path \<String\> 路径
|
||||||
|
|
||||||
|
返回文件的拓展名。例如`files.getExtension("/sdcard/1.txt")`返回"txt"。
|
||||||
|
|
||||||
|
### files.remove(path)
|
||||||
|
* path \<String\> 路径
|
||||||
|
|
||||||
|
删除文件或**空文件夹**,返回是否删除成功。
|
||||||
|
|
||||||
|
### files.removeDir(path)
|
||||||
|
* path \<String\> 路径
|
||||||
|
* path \<String\> 路径
|
||||||
|
|
||||||
|
删除文件夹,如果文件夹不为空,则删除该文件夹的所有内容再删除该文件夹,返回是否全部删除成功。
|
||||||
|
|
||||||
|
### files.getSdcardPath()
|
||||||
|
|
||||||
|
返回SD卡路径。所谓SD卡,即外部存储器。
|
||||||
|
|
||||||
|
### files.listDir(path\[, filter\])
|
||||||
|
* path \<String\> 路径
|
||||||
|
* filter \<Function\> 过滤函数。接收一个String参数(文件名),返回一个Boolean值。
|
||||||
|
|
||||||
|
列出文件夹path下的满足条件的文件和文件夹的名称的数组。如果不加filter参数,则返回所有文件和文件夹。
|
||||||
|
|
||||||
|
例如,获取sdcard目录下的txt文件为
|
||||||
|
```
|
||||||
|
var txtFiles = files.listDir("/sdcard/", function(name){
|
||||||
|
return name.endsWith(".txt") && files.isFile("/sdcard/" + name);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
# ReadableTextFile
|
||||||
|
|
||||||
|
可读文件对象。
|
||||||
|
|
||||||
|
### ReadableTextFile.read()
|
||||||
|
|
||||||
|
返回该文件剩余的所有内容的字符串。
|
||||||
|
|
||||||
|
### ReadableTextFile.read(maxCount)
|
||||||
|
|
||||||
|
* maxCount \<Number\> 最大读取的字符数量
|
||||||
|
|
||||||
|
读取该文件接下来最长为maxCount的字符串并返回。即使文件剩余内容不足maxCount也不会出错。
|
||||||
|
|
||||||
|
### ReadableTextFile.readline()
|
||||||
|
|
||||||
|
读取一行并返回(不包含换行符)。
|
||||||
|
|
||||||
|
### ReadableTextFile.readlines()
|
||||||
|
|
||||||
|
读取剩余的所有行,并返回它们按顺序组成的字符串数组。
|
||||||
|
|
||||||
|
### close()
|
||||||
|
|
||||||
|
关闭该文件。
|
||||||
|
|
||||||
|
**打开一个文件不再使用时务必关闭**
|
||||||
|
|
||||||
|
# PWritableTextFile
|
||||||
|
|
||||||
|
可写文件对象。
|
||||||
|
|
||||||
|
### PWritableTextFile.write(text)
|
||||||
|
* text \<String\> 文本
|
||||||
|
|
||||||
|
把文本内容text写入到文件中。
|
||||||
|
|
||||||
|
### PWritableTextFile.writeline(line)
|
||||||
|
* text \<String\> 文本
|
||||||
|
|
||||||
|
把文本line写入到文件中并写入一个换行符。
|
||||||
|
|
||||||
|
### PWritableTextFile.writelines(lines)
|
||||||
|
* lines \<Array\> 字符串数组
|
||||||
|
|
||||||
|
把很多行写入到文件中....
|
||||||
|
|
||||||
|
### PWritableTextFile.flush()
|
||||||
|
|
||||||
|
把缓冲区内容输出到文件中。
|
||||||
|
|
||||||
|
### PWritableTextFile.close()
|
||||||
|
|
||||||
|
关闭文件。同时会被缓冲区内容输出到文件。
|
||||||
|
|
||||||
|
**打开一个文件写入后,不再使用时务必关闭,否则文件可能会丢失**
|
||||||
@@ -27,6 +27,8 @@ while(!click("扫一扫"));
|
|||||||
* bottom 要点击的长方形区域下边与屏幕上边的像素距离
|
* bottom 要点击的长方形区域下边与屏幕上边的像素距离
|
||||||
* right 要点击的长方形区域右边与屏幕左边的像素距离
|
* right 要点击的长方形区域右边与屏幕左边的像素距离
|
||||||
|
|
||||||
|
**注意,该函数一般只用于录制的脚本中使用,在自己写的代码中使用该函数一般没有作用。**
|
||||||
|
|
||||||
当屏幕中并未包含与该区域严格匹配的区域,或者该区域不能点击时返回false,否则返回true。
|
当屏幕中并未包含与该区域严格匹配的区域,或者该区域不能点击时返回false,否则返回true。
|
||||||
有些按钮或者部件是图标而不是文字(例如发送朋友圈的照相机图标以及QQ下方的消息、联系人、动态图标),这时不能通过`click(text[, i])`来点击,只能通过描述图标所在的区域来点击。left, bottom, top, right描述的就是点击的区域。
|
有些按钮或者部件是图标而不是文字(例如发送朋友圈的照相机图标以及QQ下方的消息、联系人、动态图标),这时不能通过`click(text[, i])`来点击,只能通过描述图标所在的区域来点击。left, bottom, top, right描述的就是点击的区域。
|
||||||
至于要定位点击的区域,可以在侧拉菜单开启"点击区域辅助"(或者安卓7.0以上在通知栏点击"修改"添加点击区域辅助快捷设定图标),之后每次点击或长按都会提示这次点击或长按的区域并自动保存,可以在编辑器的右侧拉菜单中插入。
|
至于要定位点击的区域,可以在侧拉菜单开启"点击区域辅助"(或者安卓7.0以上在通知栏点击"修改"添加点击区域辅助快捷设定图标),之后每次点击或长按都会提示这次点击或长按的区域并自动保存,可以在编辑器的右侧拉菜单中插入。
|
||||||
@@ -43,13 +45,21 @@ while(!click("扫一扫"));
|
|||||||
参数为一个整数i时会找到第i + 1个可滑动控件滑动。例如`scrollUp(0);`。
|
参数为一个整数i时会找到第i + 1个可滑动控件滑动。例如`scrollUp(0);`。
|
||||||
### scrollDown
|
### scrollDown
|
||||||
下滑。不加参数时与scrollUp类似。
|
下滑。不加参数时与scrollUp类似。
|
||||||
### input([i, ]text)
|
### input(\[i, \]text)
|
||||||
|
* i \<Number\> 表示要输入的为第i + 1个输入框
|
||||||
|
* text \<String\> 要输入的文本
|
||||||
|
|
||||||
|
返回是否输入成功。当找不到对应的文本框时返回false。
|
||||||
|
不加参数i则会在所有输入框都输入text。例如`input("测试")`。
|
||||||
|
|
||||||
|
### setText(\[i, \]text)
|
||||||
* i \<Number\> 表示要输入的为第i + 1个输入框
|
* i \<Number\> 表示要输入的为第i + 1个输入框
|
||||||
* text \<String\> 要输入的文本
|
* text \<String\> 要输入的文本
|
||||||
|
|
||||||
返回是否输入成功。当找不到对应的文本框时返回false。
|
返回是否输入成功。当找不到对应的文本框时返回false。
|
||||||
这里的输入文本的意思是,把输入框的文本置为text,而不是在原来的文本上追加。
|
|
||||||
不加参数i则会把所有输入框的文本都置为text。例如`input("测试")`。
|
不加参数i则会把所有输入框的文本都置为text。例如`input("测试")`。
|
||||||
|
这里的输入文本的意思是,把输入框的文本置为text,而不是在原来的文本上追加。
|
||||||
|
|
||||||
|
|
||||||
### back()
|
### back()
|
||||||
模拟按下返回键
|
模拟按下返回键
|
||||||
|
|||||||
@@ -3,44 +3,52 @@
|
|||||||
var 好友验证信息 = "AutoJs自动添加群好友";
|
var 好友验证信息 = "AutoJs自动添加群好友";
|
||||||
var 延迟 = 500;
|
var 延迟 = 500;
|
||||||
|
|
||||||
toast("请打开群成员列表");
|
launchApp("QQ");
|
||||||
|
sleep(500);
|
||||||
|
if(currentActivity() != "com.tencent.mobileqq.activity.TroopMemberListActivity"){
|
||||||
|
toast("请打开要加的群的聊天窗口");
|
||||||
|
openGroupMemberList();
|
||||||
|
}
|
||||||
|
|
||||||
var added = {};
|
var added = {};
|
||||||
while(true){
|
while(true){
|
||||||
var list = className("AbsListView").findOne();
|
var list = className("AbsListView").findOne();
|
||||||
list.children().each(function(child){
|
var count = list.childCount();
|
||||||
if(child.className() != "android.widget.FrameLayout"){
|
for(var i = 0; i < count; i++){
|
||||||
return;
|
var child = list.child(i);
|
||||||
}
|
if(!child || child.className() != "android.widget.FrameLayout"){
|
||||||
if(!isGroupMember(child)){
|
continue;
|
||||||
return;
|
}
|
||||||
}
|
if(!isGroupMember(child) || isMyself(child)){
|
||||||
if(isMyself(child)){
|
continue;
|
||||||
return;
|
}
|
||||||
}
|
child.child(0).click();
|
||||||
child.child(0).click();
|
sleep(500);
|
||||||
sleep(500);
|
addAsFriend();
|
||||||
addAsFriend();
|
sleep(延迟);
|
||||||
sleep(延迟);
|
}
|
||||||
});
|
|
||||||
className("AbsListView").findOne().scrollForward();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function isGroupMember(child){
|
function isGroupMember(child){
|
||||||
if(child.childCount() != 1){
|
var tvName = child.findOne(id("tv_name"));
|
||||||
return false;
|
if(!tvName){
|
||||||
}
|
return false;
|
||||||
return child.child(0) && child.child(0).className() == "android.widget.FrameLayout";
|
}
|
||||||
|
log(tvName.text());
|
||||||
|
return tvName.text() != "Baby Q";
|
||||||
}
|
}
|
||||||
|
|
||||||
function isMyself(child){
|
function isMyself(child){
|
||||||
var l = child.findByText("我");
|
var i = child.findOne(text("我"));
|
||||||
return l && l.size() > 0;
|
if(!i){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return i.id() && !i.id().endsWith("tv_name");
|
||||||
}
|
}
|
||||||
|
|
||||||
function addAsFriend(){
|
function addAsFriend(){
|
||||||
var qq = getQQ();
|
var qq = getQQ();
|
||||||
|
toast(qq);
|
||||||
if(added[qq]){
|
if(added[qq]){
|
||||||
while(!click("返回"));
|
while(!click("返回"));
|
||||||
return;
|
return;
|
||||||
@@ -54,13 +62,21 @@ function addAsFriend(){
|
|||||||
if(click("取消")){
|
if(click("取消")){
|
||||||
sleep(400);
|
sleep(400);
|
||||||
}
|
}
|
||||||
back();
|
while(!back());
|
||||||
}else{
|
}else{
|
||||||
back();
|
while(!back());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getQQ(){
|
function getQQ(){
|
||||||
var qq = textMatches("\\d{5,12}").findOne().text();
|
var qq = textMatches("\\d{5,12}").findOne().text();
|
||||||
return qq;
|
return qq;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openGroupMemberList(){
|
||||||
|
desc("群资料卡").click();
|
||||||
|
var groupMemberCountView = textEndsWith("名成员").findOne();
|
||||||
|
var groupMemberCount = parseInt(/\d+/.exec(groupMemberCountView.text())[0]);
|
||||||
|
groupMemberCountView.parent().click();
|
||||||
|
sleep(groupMemberCount * 4);
|
||||||
}
|
}
|
||||||
@@ -5,12 +5,7 @@ function 下滑(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function 赞(){
|
function 赞(){
|
||||||
var like = className("ImageView").desc("赞").find();
|
className("ImageView").desc("赞").click();
|
||||||
if(like){
|
|
||||||
like.click();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function 显示更多(){
|
function 显示更多(){
|
||||||
@@ -21,13 +16,10 @@ function 显示更多(){
|
|||||||
|
|
||||||
toast("请打开自己的资料页,点击点赞图标");
|
toast("请打开自己的资料页,点击点赞图标");
|
||||||
sleep(100);
|
sleep(100);
|
||||||
waitForActivity("com.tencent.mobileqq.activity.VisitorsActivity");
|
|
||||||
|
|
||||||
while(notStopped()){
|
while(notStopped()){
|
||||||
var i = 0;
|
for(let i = 0; i < 10; i++){
|
||||||
while(i < 10){
|
赞();
|
||||||
i += 赞() ? 1 : 0;
|
|
||||||
click("取消");
|
|
||||||
}
|
}
|
||||||
显示更多();
|
显示更多();
|
||||||
下滑();
|
下滑();
|
||||||
|
|||||||
29
app/src/main/assets/sample/QQ与微信/QQ名片点赞(只点赞好友).js
Normal file
29
app/src/main/assets/sample/QQ与微信/QQ名片点赞(只点赞好友).js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
"auto";
|
||||||
|
|
||||||
|
var 延迟 = 100;
|
||||||
|
|
||||||
|
launchApp("QQ");
|
||||||
|
toast("请打开自己的资料页,点击点赞图标");
|
||||||
|
sleep(500);
|
||||||
|
waitForActivity("com.tencent.mobileqq.activity.VisitorsActivity");
|
||||||
|
|
||||||
|
while(notStopped()){
|
||||||
|
var list = className("AbsListView").findOne();
|
||||||
|
list.children().each(function(child){
|
||||||
|
if(!child)
|
||||||
|
return;
|
||||||
|
var l = child.findByText("(好友)");
|
||||||
|
if(l.size() > 0){
|
||||||
|
var like = child.findOne(className("ImageView").desc("赞"));
|
||||||
|
for(let i = 0; i < 10; i++){
|
||||||
|
like.click();
|
||||||
|
sleep(延迟);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
click("显示更多");
|
||||||
|
click("显示更多");
|
||||||
|
if(currentActivity() == "com.tencent.mobileqq.activity.VisitorsActivity"){
|
||||||
|
list.scrollForward();
|
||||||
|
}
|
||||||
|
}
|
||||||
11
app/src/main/assets/sample/QQ与微信/QQ批量同意好友验证.js
Normal file
11
app/src/main/assets/sample/QQ与微信/QQ批量同意好友验证.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
"auto";
|
||||||
|
|
||||||
|
launchApp("QQ");
|
||||||
|
toast("请打开新朋友界面并自行下滑");
|
||||||
|
|
||||||
|
while(true){
|
||||||
|
text("同意").clickable().findOne().click();
|
||||||
|
sleep(1500);
|
||||||
|
click("完成");
|
||||||
|
sleep(1000);
|
||||||
|
}
|
||||||
@@ -1,45 +1,53 @@
|
|||||||
"auto";
|
"auto";
|
||||||
var liked = {};
|
|
||||||
|
launchApp("QQ");
|
||||||
|
sleep(500);
|
||||||
|
if(currentActivity() != "com.tencent.mobileqq.activity.TroopMemberListActivity"){
|
||||||
|
toast("请打开要点赞的群的聊天窗口");
|
||||||
|
openGroupMemberList();
|
||||||
|
}
|
||||||
|
|
||||||
while(true){
|
while(true){
|
||||||
var list = className("AbsListView").findOne();
|
var list = className("AbsListView").findOne();
|
||||||
list.children().each(function(child){
|
var count = list.childCount();
|
||||||
if(child.className() != "android.widget.FrameLayout"){
|
for(var i = 0; i < count; i++){
|
||||||
return;
|
var child = list.child(i);
|
||||||
}
|
if(!child || child.className() != "android.widget.FrameLayout"){
|
||||||
if(!isGroupMember(child)){
|
continue;
|
||||||
return;
|
}
|
||||||
}
|
if(!isGroupMember(child) || isMyself(child)){
|
||||||
if(isMyself(child)){
|
continue;
|
||||||
return;
|
}
|
||||||
}
|
child.child(0).click();
|
||||||
child.child(0).click();
|
sleep(500);
|
||||||
like();
|
like();
|
||||||
while(!click("返回"));
|
while(!click("返回"));
|
||||||
while(!click("成员资料"));
|
while(!click("成员资料"));
|
||||||
while(!click("返回"));
|
while(!click("返回"));
|
||||||
});
|
sleep(500);
|
||||||
className("AbsListView").findOne().scrollForward();
|
}
|
||||||
|
list.scrollForward();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function isGroupMember(child){
|
function isGroupMember(child){
|
||||||
if(child.childCount() != 1){
|
var tvName = child.findOne(id("tv_name"));
|
||||||
return false;
|
if(!tvName){
|
||||||
}
|
return false;
|
||||||
return child.child(0) && child.child(0).className() == "android.widget.FrameLayout";
|
}
|
||||||
|
log(tvName.text());
|
||||||
|
return tvName.text() != "Baby Q";
|
||||||
}
|
}
|
||||||
|
|
||||||
function isMyself(child){
|
function isMyself(child){
|
||||||
var l = child.findByText("我");
|
var i = child.findOne(text("我"));
|
||||||
return l && l.size() > 0;
|
if(!i){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return i.id() && !i.id().endsWith("tv_name");
|
||||||
}
|
}
|
||||||
|
|
||||||
function like(){
|
function like(){
|
||||||
var qq = getQQ();
|
|
||||||
if(liked[qq]){
|
|
||||||
while(!click("返回"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while(!click("更多"));
|
while(!click("更多"));
|
||||||
while(!click("查看个人资料卡"));
|
while(!click("查看个人资料卡"));
|
||||||
var likeBtn = descEndsWith("点击可赞").findOne();
|
var likeBtn = descEndsWith("点击可赞").findOne();
|
||||||
@@ -52,4 +60,12 @@ function like(){
|
|||||||
function getQQ(){
|
function getQQ(){
|
||||||
var qq = textMatches("\\d{5,12}").findOne().text();
|
var qq = textMatches("\\d{5,12}").findOne().text();
|
||||||
return qq;
|
return qq;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openGroupMemberList(){
|
||||||
|
desc("群资料卡").click();
|
||||||
|
var groupMemberCountView = textEndsWith("名成员").findOne();
|
||||||
|
var groupMemberCount = parseInt(/\d+/.exec(groupMemberCountView.text())[0]);
|
||||||
|
groupMemberCountView.parent().click();
|
||||||
|
sleep(groupMemberCount * 4);
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,10 @@ var y = 180;
|
|||||||
//获取在点(x, y)处的颜色
|
//获取在点(x, y)处的颜色
|
||||||
var c = images.pixel(captureScreen(), x, y);
|
var c = images.pixel(captureScreen(), x, y);
|
||||||
//显示该颜色
|
//显示该颜色
|
||||||
toast((c >>> 0).toString(16));
|
var msg = "";
|
||||||
|
msg += "在位置(" + x + ", " + y + ")处的颜色为" + colors.toString(c);
|
||||||
|
msg += "\nR = " + colors.red(c) + ", G = " + colors.green(c) + ", B = " + colors.blue(c);
|
||||||
//检测在点(x, y)处是否有颜色0x73bdb6 (模糊比较)
|
//检测在点(x, y)处是否有颜色0x73bdb6 (模糊比较)
|
||||||
toast(images.detectsColor(captureScreen(), x, y, 0x73bdb6))
|
var isDetected = images.detectsColor(captureScreen(), "#73bdb6", x, y);
|
||||||
|
msg += "\n该位置是否匹配到颜色#73bdb6: " + isDetected;
|
||||||
|
alert(msg);
|
||||||
20
app/src/main/assets/sample/文件读写/删除所有空文件夹.js
Normal file
20
app/src/main/assets/sample/文件读写/删除所有空文件夹.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
if(confirm("该操作会删除SD卡目录及其子目录下所有空文件夹,是否继续?")){
|
||||||
|
toast("请点击右上角打开日志");
|
||||||
|
deleteAllEmptyDirs(files.getSdcardPath());
|
||||||
|
toast("全部完成!");
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteAllEmptyDirs(dir){
|
||||||
|
var list = files.listDir(dir);
|
||||||
|
var len = list.length;
|
||||||
|
if(len == 0){
|
||||||
|
log("删除目录 " + dir + " " + (files.remove(dir) ? "成功" : "失败"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(let i = 0; i < len; i++){
|
||||||
|
var child = files.join(dir, list[i]);
|
||||||
|
if(files.isDir(child)){
|
||||||
|
deleteAllEmptyDirs(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -143,6 +143,8 @@ public class Pref {
|
|||||||
}
|
}
|
||||||
def().edit().putLong(KEY_LAST_SHOW_AD_MILLIS, System.currentTimeMillis()).apply();
|
def().edit().putLong(KEY_LAST_SHOW_AD_MILLIS, System.currentTimeMillis()).apply();
|
||||||
return true;
|
return true;
|
||||||
|
case "Off":
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,12 @@ public class HoverMenuManger {
|
|||||||
if (!HoverMenuService.isServiceRunning()) {
|
if (!HoverMenuService.isServiceRunning()) {
|
||||||
if (!SettingsCompat.canDrawOverlays(App.getApp())) {
|
if (!SettingsCompat.canDrawOverlays(App.getApp())) {
|
||||||
Toast.makeText(App.getApp(), R.string.text_no_floating_window_permission, Toast.LENGTH_SHORT).show();
|
Toast.makeText(App.getApp(), R.string.text_no_floating_window_permission, Toast.LENGTH_SHORT).show();
|
||||||
SettingsCompat.manageDrawOverlays(App.getApp());
|
try {
|
||||||
|
SettingsCompat.manageDrawOverlays(App.getApp());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
IntentUtil.goToAppDetailSettings(App.getApp());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
HoverMenuService.startService(App.getApp());
|
HoverMenuService.startService(App.getApp());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.stardust.scriptdroid.external.floatingwindow.menu.view.FloatingLayout
|
|||||||
import com.stardust.scriptdroid.external.floatingwindow.menu.view.FloatingLayoutHierarchyView;
|
import com.stardust.scriptdroid.external.floatingwindow.menu.view.FloatingLayoutHierarchyView;
|
||||||
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
|
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
|
||||||
import com.stardust.theme.ThemeColorManagerCompat;
|
import com.stardust.theme.ThemeColorManagerCompat;
|
||||||
|
import com.stardust.util.IntentUtil;
|
||||||
import com.stardust.util.MessageEvent;
|
import com.stardust.util.MessageEvent;
|
||||||
import com.stardust.util.MessageIntent;
|
import com.stardust.util.MessageIntent;
|
||||||
|
|
||||||
@@ -129,7 +130,12 @@ public class HoverMenuService extends Service {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Toast.makeText(this, R.string.text_no_floating_window_permission, Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, R.string.text_no_floating_window_permission, Toast.LENGTH_SHORT).show();
|
||||||
SettingsCompat.manageDrawOverlays(this);
|
try {
|
||||||
|
SettingsCompat.manageDrawOverlays(this);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
IntentUtil.goToAppDetailSettings(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
|
|||||||
import com.stardust.scriptdroid.ui.main.MainActivity_;
|
import com.stardust.scriptdroid.ui.main.MainActivity_;
|
||||||
import com.stardust.util.ClipboardUtil;
|
import com.stardust.util.ClipboardUtil;
|
||||||
import com.stardust.util.MessageEvent;
|
import com.stardust.util.MessageEvent;
|
||||||
|
import com.stardust.view.accessibility.AccessibilityService;
|
||||||
|
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
|
|
||||||
@@ -61,10 +62,14 @@ public class MainMenuNavigatorContent implements NavigatorContent {
|
|||||||
Toast.makeText(mView.getContext(), R.string.text_layout_inspector_is_dumping, Toast.LENGTH_SHORT).show();
|
Toast.makeText(mView.getContext(), R.string.text_layout_inspector_is_dumping, Toast.LENGTH_SHORT).show();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (inspector.getCapture() == null) {
|
if (AccessibilityService.getInstance() == null) {
|
||||||
Toast.makeText(mView.getContext(), R.string.text_no_accessibility_permission_to_capture, Toast.LENGTH_SHORT).show();
|
Toast.makeText(mView.getContext(), R.string.text_no_accessibility_permission_to_capture, Toast.LENGTH_SHORT).show();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (inspector.getCapture() == null) {
|
||||||
|
Toast.makeText(mView.getContext(), R.string.text_inspect_failed, Toast.LENGTH_SHORT).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector;
|
package com.stardust.scriptdroid.external.floatingwindow.menu.layout_inspector;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.accessibility.AccessibilityNodeInfo;
|
import android.view.accessibility.AccessibilityNodeInfo;
|
||||||
|
|
||||||
@@ -43,6 +45,17 @@ public class LayoutInspector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
|
||||||
|
private void refreshChildList(AccessibilityNodeInfo root) {
|
||||||
|
if (root == null)
|
||||||
|
return;
|
||||||
|
root.refresh();
|
||||||
|
int childCount = root.getChildCount();
|
||||||
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
refreshChildList(root.getChild(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isDumping() {
|
public boolean isDumping() {
|
||||||
return mDumping;
|
return mDumping;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent;
|
package com.stardust.scriptdroid.external.floatingwindow.menu.record.inputevent;
|
||||||
|
|
||||||
|
import android.media.Image;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
@@ -33,7 +34,7 @@ public class InputEventToSendEventJsConverter extends InputEventConverter {
|
|||||||
if (mLastEventTime == 0) {
|
if (mLastEventTime == 0) {
|
||||||
mLastEventTime = event.time;
|
mLastEventTime = event.time;
|
||||||
} else if (event.time - mLastEventTime > 0.03) {
|
} else if (event.time - mLastEventTime > 0.03) {
|
||||||
mCode.append("sleep(").append((long) (1000 * (event.time - mLastEventTime))).append(");\n");
|
mCode.append("sh.usleep(").append((long) (1000000 * (event.time - mLastEventTime))).append(");\n");
|
||||||
mLastEventTime = event.time;
|
mLastEventTime = event.time;
|
||||||
}
|
}
|
||||||
int device = parseDeviceNumber(event.device);
|
int device = parseDeviceNumber(event.device);
|
||||||
@@ -122,11 +123,4 @@ public class InputEventToSendEventJsConverter extends InputEventConverter {
|
|||||||
mCode.append("sh.exitAndWaitFor();");
|
mCode.append("sh.exitAndWaitFor();");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String hex2dec(String hex) {
|
|
||||||
try {
|
|
||||||
return String.valueOf((int) Long.parseLong(hex, 16));
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
throw new EventFormatException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import com.afollestad.materialdialogs.DialogAction;
|
|||||||
import com.afollestad.materialdialogs.MaterialDialog;
|
import com.afollestad.materialdialogs.MaterialDialog;
|
||||||
import com.stardust.app.Fragment;
|
import com.stardust.app.Fragment;
|
||||||
import com.stardust.app.OperationDialogBuilder;
|
import com.stardust.app.OperationDialogBuilder;
|
||||||
|
import com.stardust.pio.UncheckedIOException;
|
||||||
import com.stardust.scriptdroid.script.ScriptFile;
|
import com.stardust.scriptdroid.script.ScriptFile;
|
||||||
import com.stardust.pio.PFile;
|
import com.stardust.pio.PFile;
|
||||||
import com.stardust.scriptdroid.R;
|
import com.stardust.scriptdroid.R;
|
||||||
@@ -137,7 +138,9 @@ public class MyScriptListFragment extends Fragment {
|
|||||||
public void createScriptFile(String path, String script) {
|
public void createScriptFile(String path, String script) {
|
||||||
if (PFile.createIfNotExists(path)) {
|
if (PFile.createIfNotExists(path)) {
|
||||||
if (script != null) {
|
if (script != null) {
|
||||||
if (!PFile.write(path, script)) {
|
try {
|
||||||
|
PFile.write(path, script);
|
||||||
|
} catch (UncheckedIOException e) {
|
||||||
Snackbar.make(getView(), R.string.text_file_write_fail, Snackbar.LENGTH_LONG).show();
|
Snackbar.make(getView(), R.string.text_file_write_fail, Snackbar.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ public class SettingsActivity extends BaseActivity {
|
|||||||
.entry(getString(R.string.text_join_qq_group), new Runnable() {
|
.entry(getString(R.string.text_join_qq_group), new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!IntentUtil.joinQQGroup(getActivity(), "vjHXzZlpGcXNe-YEWzQ85mm_z8y-curC")) {
|
if (!IntentUtil.joinQQGroup(getActivity(), "-7riBQuwFUUqdgYL5vFeIdBfH4H9m-Uj")) {
|
||||||
Toast.makeText(getActivity(), R.string.text_mobile_qq_not_installed, Toast.LENGTH_SHORT).show();
|
Toast.makeText(getActivity(), R.string.text_mobile_qq_not_installed, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.stardust.theme;
|
package com.stardust.theme;
|
||||||
|
|
||||||
|
import com.jecelyin.editor.v2.core.widget.JecEditText;
|
||||||
|
import com.jecelyin.editor.v2.ui.EditorDelegate;
|
||||||
import com.stardust.scriptdroid.*;
|
import com.stardust.scriptdroid.*;
|
||||||
import com.stardust.scriptdroid.R;
|
import com.stardust.scriptdroid.R;
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
<string name="text_recorded">录制结束</string>
|
<string name="text_recorded">录制结束</string>
|
||||||
<string name="text_copy_to_clip">复制到剪贴板</string>
|
<string name="text_copy_to_clip">复制到剪贴板</string>
|
||||||
<string name="text_file_write_fail">文件写入失败</string>
|
<string name="text_file_write_fail">文件写入失败</string>
|
||||||
<string name="text_join_qq_group">加入QQ交流群</string>
|
<string name="text_join_qq_group">加入QQ互赞&交流群</string>
|
||||||
<string name="text_copied">已复制到剪贴板</string>
|
<string name="text_copied">已复制到剪贴板</string>
|
||||||
<string name="text_use_volume_control_record">使用音量键控制</string>
|
<string name="text_use_volume_control_record">使用音量键控制</string>
|
||||||
<string name="summary_use_volume_control_record">开启后每次音量变化会开始或停止脚本录制</string>
|
<string name="summary_use_volume_control_record">开启后每次音量变化会开始或停止脚本录制</string>
|
||||||
@@ -207,6 +207,7 @@
|
|||||||
<string name="text_number_format_error">格式错误</string>
|
<string name="text_number_format_error">格式错误</string>
|
||||||
<string name="text_accessibility_settings">打开无障碍服务</string>
|
<string name="text_accessibility_settings">打开无障碍服务</string>
|
||||||
<string name="text_discard_record">放弃录制</string>
|
<string name="text_discard_record">放弃录制</string>
|
||||||
|
<string name="text_inspect_failed">布局抓取失败,请关闭悬浮窗后动一下页面重试</string>
|
||||||
|
|
||||||
<string-array name="record_control_keys">
|
<string-array name="record_control_keys">
|
||||||
<item>无</item>
|
<item>无</item>
|
||||||
@@ -225,11 +226,13 @@
|
|||||||
<string-array name="ad_showing_mode_keys">
|
<string-array name="ad_showing_mode_keys">
|
||||||
<item>默认</item>
|
<item>默认</item>
|
||||||
<item>每天显示一次</item>
|
<item>每天显示一次</item>
|
||||||
|
<item>关闭广告</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="ad_showing_mode_values">
|
<string-array name="ad_showing_mode_values">
|
||||||
<item>Default</item>
|
<item>Default</item>
|
||||||
<item>OncePerDay</item>
|
<item>OncePerDay</item>
|
||||||
|
<item>Off</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -81,6 +81,8 @@
|
|||||||
|
|
||||||
<Preference android:title="@string/text_check_update"/>
|
<Preference android:title="@string/text_check_update"/>
|
||||||
|
|
||||||
|
<Preference android:title="@string/text_join_qq_group"/>
|
||||||
|
|
||||||
<Preference android:title="@string/text_issue_report"/>
|
<Preference android:title="@string/text_issue_report"/>
|
||||||
|
|
||||||
<Preference android:title="@string/text_about_me_and_repo"/>
|
<Preference android:title="@string/text_about_me_and_repo"/>
|
||||||
|
|||||||
@@ -36,3 +36,4 @@ require("__general__")(__runtime__, this);
|
|||||||
})(__that__);
|
})(__that__);
|
||||||
|
|
||||||
__importClass__(com.stardust.autojs.runtime.api.Shell);
|
__importClass__(com.stardust.autojs.runtime.api.Shell);
|
||||||
|
__importClass__(com.stardust.autojs.runtime.api.InputEventSender);
|
||||||
|
|||||||
@@ -48,5 +48,12 @@ module.exports = function(__runtime__, scope){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope.waitForPackage = function(packageName, delay){
|
||||||
|
delay = delay || 200;
|
||||||
|
while(scope.currentPackage() != packageName){
|
||||||
|
sleep(delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scope.setScreenMetrics = __runtime__.setScreenMetrics.bind(__runtime__);
|
scope.setScreenMetrics = __runtime__.setScreenMetrics.bind(__runtime__);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
|
|
||||||
module.exports = function(__runtime__, scope){
|
module.exports = function(__runtime__, scope){
|
||||||
var images = {};
|
var images = {};
|
||||||
|
var colors = Object.create(android.graphics.Color);
|
||||||
|
colors.toString = function(color){
|
||||||
|
return '#' + (color >>> 0).toString(16);
|
||||||
|
}
|
||||||
if(android.os.Build.VERSION.SDK_INT < 19){
|
if(android.os.Build.VERSION.SDK_INT < 19){
|
||||||
return images;
|
return images;
|
||||||
}
|
}
|
||||||
@@ -17,25 +21,28 @@ module.exports = function(__runtime__, scope){
|
|||||||
|
|
||||||
images.pixel = rtImages.pixel;
|
images.pixel = rtImages.pixel;
|
||||||
|
|
||||||
images.detectsColor = rtImages.detectsColor.bind(rtImages);
|
images.detectsColor = function(img, color, x, y, threshold, algorithm){
|
||||||
|
color = parseColor(color);
|
||||||
|
algorithm = algorithm || "rgb";
|
||||||
|
threshold = threshold || 16;
|
||||||
|
var colorDetector = getColorDetector(color, algorithm, threshold);
|
||||||
|
var pixel = images.pixel(img, x, y);
|
||||||
|
return colorDetector.detectsColor(colors.red(pixel), colors.green(pixel), colors.blue(pixel));
|
||||||
|
}
|
||||||
|
|
||||||
images.findColor = function(img, color, options){
|
images.findColor = function(img, color, options){
|
||||||
if(typeof(color) == 'string'){
|
color = parseColor(color);
|
||||||
if(color.startsWith('#')){
|
|
||||||
color = parseInt('0x' + color.substring(1));
|
|
||||||
}else{
|
|
||||||
color = parseInt('0x' + color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
var region = options.region || [];
|
var region = options.region || [];
|
||||||
x = region[0] || 0;
|
var x = region[0] || 0;
|
||||||
y = region[1] || 0;
|
var y = region[1] || 0;
|
||||||
width = region[2] || (img.getWidth() - x);
|
var width = region[2] || (img.getWidth() - x);
|
||||||
height = region[3] || (img.getHeight() - y);
|
var height = region[3] || (img.getHeight() - y);
|
||||||
threads = options.threads || 2;
|
var threads = options.threads || 2;
|
||||||
if(options.threshold !== 0){
|
if(options.similarity){
|
||||||
threshold = options.threshold || 8;
|
var threshold = parseInt(255 * (1 - options.similarity));
|
||||||
|
}else{
|
||||||
|
var threshold = options.threshold || 16;
|
||||||
}
|
}
|
||||||
algorithm = options.algorithm || "rgb";
|
algorithm = options.algorithm || "rgb";
|
||||||
var rect = new android.graphics.Rect(x, y, width + x, height + y);
|
var rect = new android.graphics.Rect(x, y, width + x, height + y);
|
||||||
@@ -76,7 +83,20 @@ module.exports = function(__runtime__, scope){
|
|||||||
throw new Error("Unknown algorithm: " + algorithm);
|
throw new Error("Unknown algorithm: " + algorithm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseColor(color){
|
||||||
|
if(typeof(color) == 'string'){
|
||||||
|
if(color.startsWith('#')){
|
||||||
|
return parseInt('0x' + color.substring(1));
|
||||||
|
}else{
|
||||||
|
return parseInt('0x' + color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
scope.__asGlobal__(images, ['requestScreenCapture', 'captureScreen', 'findColor', 'findColorInRegion', 'findColorEquals']);
|
scope.__asGlobal__(images, ['requestScreenCapture', 'captureScreen', 'findColor', 'findColorInRegion', 'findColorEquals']);
|
||||||
|
|
||||||
|
scope.colors = colors;
|
||||||
|
|
||||||
return images;
|
return images;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
module.exports = function(__runtime__, scope){
|
module.exports = function(__runtime__, scope){
|
||||||
|
scope.files = com.stardust.pio.PFile;
|
||||||
scope.open = function(path, mode, encoding, bufferSize){
|
scope.open = function(path, mode, encoding, bufferSize){
|
||||||
if(arguments.length == 1){
|
if(arguments.length == 1){
|
||||||
return com.stardust.pio.PFile.open(path);
|
return com.stardust.pio.PFile.open(path);
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.stardust.autojs.execution;
|
||||||
|
|
||||||
|
import com.stardust.autojs.ScriptEngineService;
|
||||||
|
import com.stardust.autojs.engine.ScriptEngine;
|
||||||
|
import com.stardust.autojs.runtime.ScriptRuntime;
|
||||||
|
import com.stardust.autojs.runtime.api.AbstractShell;
|
||||||
|
import com.stardust.autojs.runtime.api.ProcessShell;
|
||||||
|
import com.stardust.util.IntentExtras;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Stardust on 2017/7/16.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RootedScriptExecution extends RunnableScriptExecution {
|
||||||
|
|
||||||
|
private static int count = 0;
|
||||||
|
private static Map<String, ScriptExecutionTask> arguments = new HashMap<>();
|
||||||
|
|
||||||
|
public RootedScriptExecution(ScriptEngineService service, ScriptExecutionTask task) {
|
||||||
|
super(service, task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -190,4 +190,12 @@ public abstract class AbstractShell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract void exitAndWaitFor();
|
public abstract void exitAndWaitFor();
|
||||||
|
|
||||||
|
public void sleep(long i) {
|
||||||
|
exec("sleep " + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void usleep(long l) {
|
||||||
|
exec("usleep " + l);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package com.stardust.autojs.runtime.api;
|
||||||
|
|
||||||
|
import com.stardust.util.ScreenMetrics;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Stardust on 2017/7/16.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class InputEventSender {
|
||||||
|
private DataOutputStream mDeviceFile;
|
||||||
|
private ScreenMetrics mScreenMetrics;
|
||||||
|
|
||||||
|
|
||||||
|
public InputEventSender(String devicePath) throws FileNotFoundException {
|
||||||
|
mDeviceFile = new DataOutputStream(new FileOutputStream(devicePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputEventSender(int i) throws FileNotFoundException {
|
||||||
|
this("/dev/input/event" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputEventSender() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void sendEvent(int type, int code, int value) throws IOException {
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
mDeviceFile.writeByte(0);
|
||||||
|
mDeviceFile.writeShort(type);
|
||||||
|
mDeviceFile.writeShort(code);
|
||||||
|
mDeviceFile.writeInt(value);
|
||||||
|
mDeviceFile.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setInputDevice(int i) throws IOException {
|
||||||
|
if (mDeviceFile != null) {
|
||||||
|
mDeviceFile.close();
|
||||||
|
}
|
||||||
|
mDeviceFile = new DataOutputStream(new FileOutputStream("/dev/input/event" + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Touch(int x, int y) throws IOException {
|
||||||
|
TouchX(x);
|
||||||
|
TouchY(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScreenMetrics(int width, int height) {
|
||||||
|
if (mScreenMetrics == null) {
|
||||||
|
mScreenMetrics = new ScreenMetrics();
|
||||||
|
}
|
||||||
|
mScreenMetrics.setScreenMetrics(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TouchX(int x) throws IOException {
|
||||||
|
sendEvent(3, 53, scaleX(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int scaleX(int x) {
|
||||||
|
return mScreenMetrics.scaleX(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TouchY(int y) throws IOException {
|
||||||
|
sendEvent(3, 54, scaleY(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int scaleY(int y) {
|
||||||
|
return mScreenMetrics.scaleY(y);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
mDeviceFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -40,19 +40,17 @@ public interface ColorDetector {
|
|||||||
|
|
||||||
class DifferenceDetector extends AbstractColorDetector {
|
class DifferenceDetector extends AbstractColorDetector {
|
||||||
|
|
||||||
private final int mRThreshold, mGThreshold, mBThreshold;
|
private final int mThreshold;
|
||||||
|
|
||||||
public DifferenceDetector(int color, int threshold) {
|
public DifferenceDetector(int color, int threshold) {
|
||||||
super(color);
|
super(color);
|
||||||
mRThreshold = Color.red(threshold);
|
mThreshold = threshold * 3;
|
||||||
mGThreshold = Color.green(threshold);
|
|
||||||
mBThreshold = Color.blue(threshold);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean detectsColor(int R, int G, int B) {
|
public boolean detectsColor(int R, int G, int B) {
|
||||||
return Math.abs(R - mR) <= mRThreshold && Math.abs(G - mG) <= mGThreshold &&
|
return Math.abs(R - mR) + Math.abs(G - mG) + Math.abs(B - mB) <= mThreshold;
|
||||||
Math.abs(B - mB) <= mBThreshold;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +75,7 @@ public interface ColorDetector {
|
|||||||
|
|
||||||
public RGBDistanceDetector(int color, int threshold) {
|
public RGBDistanceDetector(int color, int threshold) {
|
||||||
super(color);
|
super(color);
|
||||||
mThreshold = threshold * threshold;
|
mThreshold = threshold * threshold * 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -100,7 +98,7 @@ public interface ColorDetector {
|
|||||||
mR = (color & 0xff0000) >> 16;
|
mR = (color & 0xff0000) >> 16;
|
||||||
mG = (color & 0x00ff00) >> 8;
|
mG = (color & 0x00ff00) >> 8;
|
||||||
mB = color & 0xff;
|
mB = color & 0xff;
|
||||||
mThreshold = threshold * threshold;
|
mThreshold = threshold * threshold * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -163,7 +161,11 @@ public interface ColorDetector {
|
|||||||
long HS = getHS(mR, mG, mB);
|
long HS = getHS(mR, mG, mB);
|
||||||
mH = (int) (HS & 0xffffffffL);
|
mH = (int) (HS & 0xffffffffL);
|
||||||
mS = (int) ((HS >> 32) & 0xffffffffL);
|
mS = (int) ((HS >> 32) & 0xffffffffL);
|
||||||
mThreshold = threshold * threshold;
|
mThreshold = threshold * 3729600 / 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HSDistanceDetector(int color, float similarity) {
|
||||||
|
this(color, (int) (1.0f - similarity) * 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -191,9 +193,9 @@ public interface ColorDetector {
|
|||||||
H = 240 + (R - G) / (max - min) * 60;
|
H = 240 + (R - G) / (max - min) * 60;
|
||||||
}
|
}
|
||||||
if (H < 0) H = H + 360;
|
if (H < 0) H = H + 360;
|
||||||
int S = (max - min) / max;
|
int S = (max - min) * 100 / max;
|
||||||
return H & ((long) S << 32);
|
return H & ((long) S << 32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.stardust.autojs.runtime.api.image;
|
package com.stardust.autojs.runtime.api.image;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.media.Image;
|
import android.media.Image;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.graphics.Bitmap;
|
|||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.media.Image;
|
import android.media.Image;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.RequiresApi;
|
import android.support.annotation.RequiresApi;
|
||||||
@@ -71,6 +72,9 @@ public class Images {
|
|||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||||
public Image captureScreen() {
|
public Image captureScreen() {
|
||||||
mScriptRuntime.requiresApi(21);
|
mScriptRuntime.requiresApi(21);
|
||||||
|
if(mScreenCapturer == null){
|
||||||
|
throw new SecurityException("No screen capture permission");
|
||||||
|
}
|
||||||
colorFinder.prestartThreads();
|
colorFinder.prestartThreads();
|
||||||
return mScreenCapturer.capture();
|
return mScreenCapturer.capture();
|
||||||
}
|
}
|
||||||
@@ -122,19 +126,6 @@ public class Images {
|
|||||||
return bitmap.getPixel(x, y);
|
return bitmap.getPixel(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean detectsColor(Image image, int x, int y, int color) {
|
|
||||||
if (image == null)
|
|
||||||
return false;
|
|
||||||
int pixel = pixel(image, x, y);
|
|
||||||
return colorFinder.defaultColorDetector(color).detectsColor(Color.red(pixel), Color.green(pixel), Color.blue(pixel));
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean detectsColor(Image image, int x, int y, int color, int threshold) {
|
|
||||||
if (image == null)
|
|
||||||
return false;
|
|
||||||
int pixel = pixel(image, x, y);
|
|
||||||
return colorFinder.defaultColorDetector(color, threshold).detectsColor(Color.red(pixel), Color.green(pixel), Color.blue(pixel));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Bitmap read(String path) {
|
public static Bitmap read(String path) {
|
||||||
return BitmapFactory.decodeFile(path);
|
return BitmapFactory.decodeFile(path);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
|
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:accessibilityEventTypes="typeNotificationStateChanged|typeAnnouncement|typeAssistReadingContext|typeContextClicked|typeGestureDetectionEnd|typeGestureDetectionStart|typeTouchExplorationGestureEnd|typeTouchExplorationGestureStart|typeTouchInteractionEnd|typeTouchInteractionStart|typeViewAccessibilityFocusCleared|typeViewAccessibilityFocused|typeViewClicked|typeViewHoverEnter|typeViewHoverExit|typeViewFocused|typeViewLongClicked|typeViewScrolled|typeViewSelected|typeViewTextChanged|typeViewTextSelectionChanged|typeViewTextTraversedAtMovementGranularity|typeWindowContentChanged|typeWindowsChanged|typeWindowStateChanged"
|
android:accessibilityEventTypes="typeAllMask"
|
||||||
android:accessibilityFeedbackType="feedbackGeneric"
|
android:accessibilityFeedbackType="feedbackGeneric"
|
||||||
android:accessibilityFlags="flagIncludeNotImportantViews|flagReportViewIds|flagRetrieveInteractiveWindows|flagRequestEnhancedWebAccessibility"
|
android:accessibilityFlags="flagIncludeNotImportantViews|flagReportViewIds|flagRetrieveInteractiveWindows|flagRequestEnhancedWebAccessibility"
|
||||||
android:canPerformGestures="true"
|
android:canPerformGestures="true"
|
||||||
|
|||||||
@@ -1,22 +1,27 @@
|
|||||||
package com.stardust.view.accessibility;
|
package com.stardust.view.accessibility;
|
||||||
|
|
||||||
|
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.CallSuper;
|
import android.os.Handler;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.InputDevice;
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
import android.view.accessibility.AccessibilityNodeInfo;
|
import android.view.accessibility.AccessibilityNodeInfo;
|
||||||
import android.widget.TextView;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import com.stardust.util.ScreenMetrics;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.locks.Condition;
|
import java.util.concurrent.locks.Condition;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
@@ -27,8 +32,6 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
|
|
||||||
public class AccessibilityService extends android.accessibilityservice.AccessibilityService {
|
public class AccessibilityService extends android.accessibilityservice.AccessibilityService {
|
||||||
|
|
||||||
private AccessibilityNodeInfo mRootInActiveWindow;
|
|
||||||
|
|
||||||
|
|
||||||
private static final String TAG = "AccessibilityService";
|
private static final String TAG = "AccessibilityService";
|
||||||
|
|
||||||
@@ -38,6 +41,9 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi
|
|||||||
private static AccessibilityService instance;
|
private static AccessibilityService instance;
|
||||||
private static boolean containsAllEventTypes = false;
|
private static boolean containsAllEventTypes = false;
|
||||||
private static final Set<Integer> eventTypes = new HashSet<>();
|
private static final Set<Integer> eventTypes = new HashSet<>();
|
||||||
|
private volatile AccessibilityNodeInfo mRootInActiveWindow;
|
||||||
|
private Timer mTimer;
|
||||||
|
private Handler mHandler;
|
||||||
|
|
||||||
public static void addDelegate(int uniquePriority, AccessibilityDelegate delegate) {
|
public static void addDelegate(int uniquePriority, AccessibilityDelegate delegate) {
|
||||||
mDelegates.put(uniquePriority, delegate);
|
mDelegates.put(uniquePriority, delegate);
|
||||||
@@ -59,15 +65,6 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi
|
|||||||
@Override
|
@Override
|
||||||
public void onAccessibilityEvent(final AccessibilityEvent event) {
|
public void onAccessibilityEvent(final AccessibilityEvent event) {
|
||||||
Log.v(TAG, "onAccessibilityEvent: " + event);
|
Log.v(TAG, "onAccessibilityEvent: " + event);
|
||||||
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
|
|
||||||
|| event.getEventType() == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
|
|
||||||
|| event.getEventType() == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
|
|
||||||
AccessibilityNodeInfo root = super.getRootInActiveWindow();
|
|
||||||
if (root != null) {
|
|
||||||
mRootInActiveWindow = root;
|
|
||||||
Log.d(TAG, "rootInActiveWindow: " + mRootInActiveWindow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!containsAllEventTypes && !eventTypes.contains(event.getEventType()))
|
if (!containsAllEventTypes && !eventTypes.contains(event.getEventType()))
|
||||||
return;
|
return;
|
||||||
for (Map.Entry<Integer, AccessibilityDelegate> entry : mDelegates.entrySet()) {
|
for (Map.Entry<Integer, AccessibilityDelegate> entry : mDelegates.entrySet()) {
|
||||||
@@ -93,10 +90,10 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi
|
|||||||
return mRootInActiveWindow;
|
return mRootInActiveWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
instance = null;
|
instance = null;
|
||||||
|
mTimer.cancel();
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,9 +106,36 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi
|
|||||||
LOCK.lock();
|
LOCK.lock();
|
||||||
ENABLED.signalAll();
|
ENABLED.signalAll();
|
||||||
LOCK.unlock();
|
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???
|
// FIXME: 2017/2/12 有时在无障碍中开启服务后这里不会调用服务也不会运行,安卓的BUG???
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AccessibilityNodeInfo superGetRootInActiveWindow() {
|
||||||
|
try {
|
||||||
|
return super.getRootInActiveWindow();
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean disable() {
|
public static boolean disable() {
|
||||||
if (instance != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (instance != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
instance.disableSelf();
|
instance.disableSelf();
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.stardust.view.accessibility;
|
||||||
|
|
||||||
|
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||||
|
import android.view.accessibility.AccessibilityNodeInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Stardust on 2017/7/13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class LayoutInspectService extends AccessibilityService {
|
||||||
|
|
||||||
|
private static LayoutInspectService instance;
|
||||||
|
|
||||||
|
public static LayoutInspectService getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onServiceConnected() {
|
||||||
|
AccessibilityServiceInfo info = getServiceInfo();
|
||||||
|
info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
|
||||||
|
setServiceInfo(info);
|
||||||
|
super.onServiceConnected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -2,11 +2,16 @@ package com.stardust.pio;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
|
||||||
|
import com.stardust.util.Func1;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
@@ -47,9 +52,16 @@ public class PFile {
|
|||||||
return open(path, "r", DEFAULT_ENCODING, DEFAULT_BUFFER_SIZE);
|
return open(path, "r", DEFAULT_ENCODING, DEFAULT_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean create(String path) {
|
||||||
|
try {
|
||||||
|
return new File(path).createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean createIfNotExists(String path) {
|
public static boolean createIfNotExists(String path) {
|
||||||
ensureDirectory(path);
|
ensureDir(path);
|
||||||
File file = new File(path);
|
File file = new File(path);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
try {
|
try {
|
||||||
@@ -61,7 +73,11 @@ public class PFile {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean ensureDirectory(String path) {
|
public static boolean exists(String path) {
|
||||||
|
return new File(path).exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean ensureDir(String path) {
|
||||||
int i = path.lastIndexOf("\\");
|
int i = path.lastIndexOf("\\");
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
i = path.lastIndexOf("/");
|
i = path.lastIndexOf("/");
|
||||||
@@ -89,8 +105,7 @@ public class PFile {
|
|||||||
try {
|
try {
|
||||||
return read(new FileInputStream(file), encoding);
|
return read(new FileInputStream(file), encoding);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
e.printStackTrace();
|
throw new UncheckedIOException(e);
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,8 +119,7 @@ public class PFile {
|
|||||||
is.read(bytes);
|
is.read(bytes);
|
||||||
return new String(bytes, encoding);
|
return new String(bytes, encoding);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
throw new UncheckedIOException(e);
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +133,7 @@ public class PFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean copyStream(InputStream is, String path) {
|
public static boolean copyStream(InputStream is, String path) {
|
||||||
if (!ensureDirectory(path))
|
if (!ensureDir(path))
|
||||||
return false;
|
return false;
|
||||||
File file = new File(path);
|
File file = new File(path);
|
||||||
try {
|
try {
|
||||||
@@ -127,14 +141,15 @@ public class PFile {
|
|||||||
if (!file.createNewFile())
|
if (!file.createNewFile())
|
||||||
return false;
|
return false;
|
||||||
FileOutputStream fos = new FileOutputStream(file);
|
FileOutputStream fos = new FileOutputStream(file);
|
||||||
return write(is, fos);
|
write(is, fos);
|
||||||
} catch (IOException e) {
|
return false;
|
||||||
|
} catch (IOException | UncheckedIOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean write(InputStream is, OutputStream os) {
|
public static void write(InputStream is, OutputStream os) {
|
||||||
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
||||||
try {
|
try {
|
||||||
while (is.available() > 0) {
|
while (is.available() > 0) {
|
||||||
@@ -143,14 +158,45 @@ public class PFile {
|
|||||||
}
|
}
|
||||||
is.close();
|
is.close();
|
||||||
os.close();
|
os.close();
|
||||||
return true;
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
throw new UncheckedIOException(e);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void write(String path, String text) {
|
||||||
|
write(new File(path), text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(String path, String text, String encoding) {
|
||||||
|
try {
|
||||||
|
write(new FileOutputStream(path), text, encoding);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(File file, String text) {
|
||||||
|
try {
|
||||||
|
write(new FileOutputStream(file), text);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(FileOutputStream fileOutputStream, String text) {
|
||||||
|
write(fileOutputStream, text, "utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void write(OutputStream outputStream, String text, String encoding) {
|
||||||
|
try {
|
||||||
|
outputStream.write(text.getBytes(encoding));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean copy(String pathFrom, String pathTo) {
|
public static boolean copy(String pathFrom, String pathTo) {
|
||||||
try {
|
try {
|
||||||
return copyStream(new FileInputStream(pathFrom), pathTo);
|
return copyStream(new FileInputStream(pathFrom), pathTo);
|
||||||
@@ -183,29 +229,6 @@ public class PFile {
|
|||||||
return fileName.substring(i + 1);
|
return fileName.substring(i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean write(String path, String text) {
|
|
||||||
return write(new File(path), text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean write(File file, String text) {
|
|
||||||
try {
|
|
||||||
return write(new FileOutputStream(file), text);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean write(OutputStream outputStream, String text) {
|
|
||||||
try {
|
|
||||||
outputStream.write(text.getBytes());
|
|
||||||
return true;
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String generateNotExistingPath(String path, String extension) {
|
public static String generateNotExistingPath(String path, String extension) {
|
||||||
if (!new File(path + extension).exists())
|
if (!new File(path + extension).exists())
|
||||||
return path + extension;
|
return path + extension;
|
||||||
@@ -216,19 +239,18 @@ public class PFile {
|
|||||||
return pathI;
|
return pathI;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getNameWithoutExtension(String fileName) {
|
public static String getName(String filePath) {
|
||||||
int a = fileName.lastIndexOf('/');
|
return new File(filePath).getName();
|
||||||
if (a < 0)
|
}
|
||||||
a = fileName.lastIndexOf('\\');
|
|
||||||
if (a < 0)
|
public static String getNameWithoutExtension(String filePath) {
|
||||||
a = -1;
|
String fileName = getName(filePath);
|
||||||
int b = fileName.indexOf('.', a + 1);
|
int b = fileName.lastIndexOf('.');
|
||||||
if (b < 0)
|
if (b < 0)
|
||||||
b = fileName.length();
|
b = fileName.length();
|
||||||
fileName = fileName.substring(a + 1, b);
|
fileName = fileName.substring(0, b);
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,6 +279,18 @@ public class PFile {
|
|||||||
return file.delete();
|
return file.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean remove(String path) {
|
||||||
|
return new File(path).delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean removeDir(String path) {
|
||||||
|
return deleteRecursively(new File(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSdcardPath() {
|
||||||
|
return Environment.getExternalStorageDirectory().getPath();
|
||||||
|
}
|
||||||
|
|
||||||
public static String readAsset(AssetManager assets, String path) {
|
public static String readAsset(AssetManager assets, String path) {
|
||||||
try {
|
try {
|
||||||
return read(assets.open(path));
|
return read(assets.open(path));
|
||||||
@@ -264,4 +298,36 @@ public class PFile {
|
|||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String[] listDir(String path) {
|
||||||
|
File file = new File(path);
|
||||||
|
return file.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] listDir(String path, final Func1<String, Boolean> filter) {
|
||||||
|
final File file = new File(path);
|
||||||
|
return file.list(new FilenameFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name) {
|
||||||
|
return filter.call(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isFile(String path) {
|
||||||
|
return new File(path).isFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDir(String path) {
|
||||||
|
return new File(path).isDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDirEmpty(String path) {
|
||||||
|
File file = new File(path);
|
||||||
|
return file.isDirectory() && file.list().length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String join(String parent, String child) {
|
||||||
|
return new File(parent, child).getPath();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,4 +27,6 @@ public class PRandomAccessBinaryFile extends RandomAccessFile {
|
|||||||
return super.readLine();
|
return super.readLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
10
version.json
10
version.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"versionCode": 147,
|
"versionCode": 151,
|
||||||
"versionName": "2.0.14 Beta",
|
"versionName": "2.0.14 Beta2",
|
||||||
"releaseNotes": "* 新增 自动添加群成员脚本\n* 优化 QQ名片点赞脚本(自动取消金豆)\n* 优化 空间点赞脚本\n* 新增 桌面小部件的支持\n* 修复 低分辨率下悬浮窗显示不全的问题\n* 修复 其他一些问题",
|
"releaseNotes": "* 修复 QQ加群成员、群成员点赞脚本(完美版)\n* 新增 互赞交流群(设置页面末尾)\n* 新增 批量同意好友验证脚本\n* 新增 文件读写、图色处理、控制台、一般常用函数等部分的文档\n* 修复 无障碍授权跳转失败的问题",
|
||||||
"downloads" : [
|
"downloads" : [
|
||||||
{
|
{
|
||||||
"name": "酷安",
|
"name": "酷安",
|
||||||
@@ -10,10 +10,6 @@
|
|||||||
{
|
{
|
||||||
"name": "应用宝",
|
"name": "应用宝",
|
||||||
"url": "http://a.app.qq.com/o/simple.jsp?pkgname=com.stardust.scriptdroid"
|
"url": "http://a.app.qq.com/o/simple.jsp?pkgname=com.stardust.scriptdroid"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "百度手机助手",
|
|
||||||
"url": "https://mobile.baidu.com/item?docid=11462633"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"oldVersions": [
|
"oldVersions": [
|
||||||
|
|||||||
Reference in New Issue
Block a user