6.3.1 - 新增发布通知权限 / WS 实例创建; 修复 floaty 模块异常 / 安卓 7.x 异常; UI 模式增强; 迁移至 View Binding

This commit is contained in:
SuperMonster003
2023-05-26 20:58:38 +08:00
parent 31ebd5b854
commit 1755ae3271
511 changed files with 12567 additions and 11063 deletions

View File

@@ -110,6 +110,38 @@
[comment]: <> "Version history only shows last 3 versions"
# v6.3.1
###### 2023/05/26
* `新增` 发布通知权限及主页抽屉开关 _[`issue #55`](http://issues.autojs6.com/55)_
* `新增` UI 模式支持简单的 Android 布局解析 (参阅 示例代码 > 布局 > 简单安卓布局)
* `新增` UI 模式增加 console / imagebutton / ratingbar / switch / textclock / togglebutton 等控件
* `新增` UI 模式控件的颜色色值支持 [OmniColor](https://docs.autojs6.com/#/omniTypes?id=omnicolor) 类型 (如 color="orange")
* `新增` UI 模式的控件完全支持 attr 方法设置控件属性 (如 ui.text.attr('color', 'blue'))
* `新增` UI 模式控件支持布尔类型属性值的缺省形式 (如 clickable="true" 可简写为 clickable 或 isClickable)
* `新增` button 控件支持 isColored 及 isBorderless 布尔类型属性
* `新增` console.resetGlobalLogConfig 方法用于重置全局日志配置
* `新增` web.newWebSocket 方法用于创建 Web Socket 实例 (参阅 项目文档 > [万维网](https://docs.autojs6.com/#/web?id=m-newwebsocket))
* `修复` 文件管理器的文件夹排序异常
* `修复` floaty 模块构建的浮动窗口无法调节样式及位置的问题 _[`issue #60`](http://issues.autojs6.com/60)_
* `修复` floaty 模块构建的浮动窗口与系统状态栏重叠的问题
* `修复` http.postMultipart 方法功能异常 _[`issue #56`](http://issues.autojs6.com/56)_
* `修复` Android 7.x 无法运行任何脚本的问题 _[`issue #61`](http://issues.autojs6.com/61)_
* `修复` sign.property 文件不存在时无法构建项目的问题
* `修复` 高版本系统 AutoJs6 置于后台时可能因无前台通知权限而崩溃的问题 (API >= 33)
* `修复` 调用 console.show 方法后日志窗口点击 FAB 按钮无法清空日志的问题
* `修复` 脚本编辑器调试时出现的 prototype 空指针异常
* `修复` 脚本编辑器运行脚本时在缓存文件夹运行临时脚本而非先保存再在原始位置运行以避免可能的脚本内容丢失问题
* `修复` 调整布局层次分析的层级色条宽度避免层级过多时控件名称无法显示的问题 _[`issue #46`](http://issues.autojs6.com/46)_
* `优化` 布局分析浮动窗口增加退出按钮以关闭窗口 _[`issue #63`](http://issues.autojs6.com/63)_
* `优化` 脚本绝对路径使用简称形式以缩减文本长度并增加可读性
* `优化` 将 Error 替换为 Exception 避免出现异常时 AutoJs6 应用崩溃
* `优化` 视图 (View) 绑定方式由 ButterKnife 迁移至 View Binding _[`issue #48`](http://issues.autojs6.com/48)_
* `优化` 服务端模式非正常关闭时将于 AutoJs6 启动时自动开启 _[`issue #64`](http://issues.autojs6.com/64)_
* `优化` 客户端模式非正常关闭时将于 AutoJs6 启动时按最近一次的历史地址自动连接
* `优化` 部分依赖或本地库版本调整 _[`CHANGELOG.md`](http://project.autojs6.com/blob/master/app/src/main/assets/doc/CHANGELOG.md#v631)_
# v6.3.0
###### 2023/04/29
@@ -259,19 +291,6 @@
* `优化` 重新设计 colors 全局对象并增加 HSV / HSL 等色彩模式支持 (参阅 项目文档 > [颜色](https://docs.autojs6.com/#/color))
* `优化` 部分依赖或本地库版本调整 _[`CHANGELOG.md`](http://project.autojs6.com/blob/master/app/src/main/assets/doc/CHANGELOG.md#v620)_
# v6.1.1
###### 2022/05/31
* `新增` 检查更新/下载更新/更新提示功能 (参阅 设置页面) (暂不支持安卓 7.x 系统)
* `修复` 应用在安卓 10 系统无法读写外部存储的问题 _[`issue #17`](http://issues.autojs6.com/17)_
* `修复` 编辑器页面长按时可能导致应用崩溃的问题 _[`issue #18`](http://issues.autojs6.com/18)_
* `修复` 编辑器页面长按菜单 "删除行" 和 "复制行" 功能无效的问题
* `修复` 编辑器页面选项菜单中 "粘贴" 功能缺失的问题
* `优化` 部分异常消息字符串资源化 (en / zh)
* `优化` 调整内容未保存对话框的按钮布局并增加颜色区分
* `优化` 部分依赖或本地库版本调整 _[`CHANGELOG.md`](http://project.autojs6.com/blob/master/app/src/main/assets/doc/CHANGELOG.md#v611)_
##### 更多版本历史可参阅
* [CHANGELOG.md](http://changelog.autojs6.com)
@@ -297,12 +316,12 @@
#### Android Studio 准备
下载 `Android Studio Flamingo | 2022.2.1` 版本 (按需选择其一):
下载 `Android Studio Flamingo | 2022.2.1 Patch 2` 版本 (按需选择其一):
- [android-studio-2022.2.1.18-windows.exe](https://redirector.gvt1.com/edgedl/android/studio/install/2022.2.1.18/android-studio-2022.2.1.18-windows.exe) (1.0 GB)
- [android-studio-2022.2.1.18-windows.zip](https://redirector.gvt1.com/edgedl/android/studio/ide-zips/2022.2.1.18/android-studio-2022.2.1.18-windows.zip) (1.0 GB)
- [android-studio-2022.2.1.20-windows.exe](https://redirector.gvt1.com/edgedl/android/studio/install/2022.2.1.20/android-studio-2022.2.1.20-windows.exe) (1.0 GB)
- [android-studio-2022.2.1.20-windows.zip](https://redirector.gvt1.com/edgedl/android/studio/ide-zips/2022.2.1.20/android-studio-2022.2.1.20-windows.zip) (1.0 GB)
> 注: 如需下载其他版本, 或上述链接已失效, 可前往 [Android Studio 发行版本归档](https://developer.android.com/studio/archive) 页面.
> 注: 上述版本发布时间为 2023 年 5 月 24 日. 如需下载其他版本, 或上述链接已失效, 可访问 [Android Studio 发行版本归档](https://developer.android.com/studio/archive) 页面.
安装或解压上述文件, 运行 Android Studio 软件 (如 `"D:\android-studio\bin\studio64.exe"`).

View File

@@ -21,12 +21,12 @@ plugins {
dependencies /* Unclassified */ {
// LeakCanary
debugImplementation("com.squareup.leakcanary:leakcanary-android:2.10")
debugImplementation("com.squareup.leakcanary:leakcanary-android:2.11")
// Android supports
implementation("androidx.cardview:cardview:1.0.0")
implementation("androidx.multidex:multidex:2.0.1")
implementation("com.google.android.material:material:1.8.0")
implementation("com.google.android.material:material:1.9.0")
// SwipeRefreshLayout
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
@@ -60,10 +60,10 @@ dependencies /* Unclassified */ {
implementation(project(":libs:com.tencent.bugly.crashreport-4.0.4"))
// OkHttp
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.9")
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.11")
// Webkit
implementation("androidx.webkit:webkit:1.6.1")
implementation("androidx.webkit:webkit:1.7.0")
// Gson
implementation("com.google.code.gson:gson:2.10.1")
@@ -76,6 +76,7 @@ dependencies /* Unclassified */ {
implementation("log4j:log4j:1.2.17")
// Preference
implementation("androidx.preference:preference:1.2.0")
implementation("androidx.preference:preference-ktx:1.2.0")
// RootShell
@@ -138,10 +139,6 @@ dependencies /* Annotations */ {
// JCIP Annotations
implementation("net.jcip:jcip-annotations:1.0")
// ButterKnife
kapt("com.jakewharton:butterknife-compiler:10.2.3")
implementation("com.jakewharton:butterknife:10.2.3")
// EventBus
implementation("org.greenrobot:eventbus:3.3.1")
}
@@ -218,7 +215,7 @@ dependencies /* GitHub API */ {
}
dependencies /* MLKit */ {
implementation("com.google.mlkit:text-recognition-chinese:16.0.0-beta6")
implementation("com.google.mlkit:text-recognition-chinese:16.0.0")
}
dependencies /* Auto.js Extensions */ {
@@ -319,21 +316,27 @@ android {
}
buildTypes {
arrayOf(
val proguardFiles = arrayOf(
getDefaultProguardFile("proguard-android.txt"),
"proguard-rules.pro",
).let { proguardFiles ->
getByName("release") {
isMinifyEnabled = false
proguardFiles(*proguardFiles)
signingConfig = takeIf { sign.isValid }?.let { signingConfigs.getByName("release") }
}
getByName("debug") {
isMinifyEnabled = getByName("release").isMinifyEnabled
proguardFiles(*proguardFiles)
signingConfig = getByName("release").signingConfig
}
)
val niceSigningConfig = takeIf { sign.isValid }?.let {
signingConfigs.getByName("release")
}
getByName("release") {
isMinifyEnabled = false
proguardFiles(*proguardFiles)
niceSigningConfig?.let { signingConfig = it }
}
getByName("debug") {
isMinifyEnabled = getByName("release").isMinifyEnabled
proguardFiles(*proguardFiles)
niceSigningConfig?.let { signingConfig = it }
}
}
buildFeatures {
viewBinding = true
}
defaultConfig {
@@ -348,7 +351,7 @@ android {
).let { arguments(it) }
}
}
buildConfigField("String", "VERSION_DATE", "\"${Utils.getDateString("MMM dd, yyyy", "GMT+08:00")}\"")
buildConfigField("String", "VERSION_DATE", "\"${Utils.getDateString("MMM d, yyyy", "GMT+08:00")}\"")
}
applicationVariants.all {

View File

@@ -35,6 +35,7 @@
-keep class org.greenrobot.eventbus.** { *; }
-keep class * extends c
-keepattributes *Annotation*
-keepattributes SourceFile,LineNumberTable
# Event bus
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
@@ -108,6 +109,7 @@
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
public <init>(android.content.Context, android.util.AttributeSet, int, int);
}
# Google Play Services library
-keep class * extends java.util.ListResourceBundle {

View File

@@ -61,6 +61,9 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- To allow posting notifications on Android 13. -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission
@@ -122,7 +125,7 @@
</activity>
<activity
android:name="org.autojs.autojs.ui.main.MainActivity_"
android:name="org.autojs.autojs.ui.main.MainActivity"
android:launchMode="singleTask"
android:theme="@style/AppTheme.FullScreen" />
@@ -151,7 +154,7 @@
</provider>
<activity
android:name="org.autojs.autojs.ui.edit.EditActivity_"
android:name="org.autojs.autojs.ui.edit.EditActivity"
android:configChanges="orientation|keyboardHidden|screenSize|locale"
android:launchMode="standard"
android:multiprocess="true"
@@ -159,36 +162,36 @@
android:theme="@style/EditorTheme" />
<activity
android:name="org.autojs.autojs.ui.settings.AboutActivity_"
android:name="org.autojs.autojs.ui.settings.AboutActivity"
android:theme="@style/AppTheme.Settings" />
<activity
android:name="org.autojs.autojs.ui.settings.SettingsActivity_"
android:name="org.autojs.autojs.ui.settings.PreferencesActivity"
android:theme="@style/AppTheme.Settings" />
<activity
android:name="org.autojs.autojs.ui.settings.DeveloperOptionsActivity_"
android:name="org.autojs.autojs.ui.settings.DeveloperOptionsActivity"
android:theme="@style/AppTheme.Settings" />
<activity android:name="org.autojs.autojs.ui.error.ErrorReportActivity" />
<activity
android:name="org.autojs.autojs.external.tasker.TaskerScriptEditActivity_"
android:name="org.autojs.autojs.external.tasker.TaskerScriptEditActivity"
android:configChanges="orientation|keyboardHidden|screenSize|locale" />
<activity
android:name="org.autojs.autojs.ui.project.BuildActivity_"
android:name="org.autojs.autojs.ui.project.BuildActivity"
android:configChanges="orientation|keyboardHidden|screenSize|locale" />
<activity android:name="org.autojs.autojs.ui.project.ProjectConfigActivity_" />
<activity android:name="org.autojs.autojs.ui.project.ProjectConfigActivity" />
<activity android:name="org.autojs.autojs.ui.log.LogActivity_" />
<activity android:name="org.autojs.autojs.ui.log.LogActivity" />
<activity
android:name="org.autojs.autojs.ui.doc.DocumentationActivity_"
android:name="org.autojs.autojs.ui.doc.DocumentationActivity"
android:label="@string/docs_app_name"
android:icon="@drawable/ic_docs_app"
android:taskAffinity="org.autojs.autojs.ui.doc.DocumentationActivity_"
android:taskAffinity="org.autojs.autojs.ui.doc.DocumentationActivity"
android:theme="@style/AppTheme.FullScreen"
android:exported="true">
@@ -202,16 +205,16 @@
</activity>
<activity android:name="org.autojs.autojs.ui.shortcut.AppsIconSelectActivity_" />
<activity android:name="org.autojs.autojs.ui.shortcut.AppsIconSelectActivity" />
<activity android:name="org.autojs.autojs.ui.timing.TimedTaskSettingActivity_" />
<activity android:name="org.autojs.autojs.ui.timing.TimedTaskSettingActivity" />
<activity
android:name="org.autojs.autojs.ui.shortcut.ShortcutCreateActivity"
android:theme="@style/AppTheme.Transparent" />
<activity
android:name="org.autojs.autojs.external.widget.ScriptWidgetSettingsActivity_"
android:name="org.autojs.autojs.external.widget.ScriptWidgetSettingsActivity"
android:exported="false">
<intent-filter>
@@ -234,7 +237,7 @@
<service android:name="org.autojs.autojs.external.ScriptExecutionIntentService" />
<activity android:name="org.autojs.autojs.external.tasker.TaskPrefEditActivity_" />
<activity android:name="org.autojs.autojs.external.tasker.TaskPrefEditActivity" />
<service
android:name="org.autojs.autojs.external.tile.LayoutBoundsTile"
@@ -275,7 +278,7 @@
android:exported="true"
android:icon="@drawable/autojs6_material"
android:label="@string/app_name"
android:targetActivity="org.autojs.autojs.external.tasker.TaskPrefEditActivity_"
android:targetActivity="org.autojs.autojs.external.tasker.TaskPrefEditActivity"
tools:ignore="ExportedActivity">
<intent-filter>

View File

@@ -4,6 +4,42 @@
******
# v6.3.1
###### 2023/05/26
* `新增` 发布通知权限及主页抽屉开关 _[`issue #55`](http://issues.autojs6.com/55)_
* `新增` UI 模式支持简单的 Android 布局解析 (参阅 示例代码 > 布局 > 简单安卓布局)
* `新增` UI 模式增加 console / imagebutton / ratingbar / switch / textclock / togglebutton 等控件
* `新增` UI 模式控件的颜色色值支持 [OmniColor](https://docs.autojs6.com/#/omniTypes?id=omnicolor) 类型 (如 color="orange")
* `新增` UI 模式的控件完全支持 attr 方法设置控件属性 (如 ui.text.attr('color', 'blue'))
* `新增` UI 模式控件支持布尔类型属性值的缺省形式 (如 clickable="true" 可简写为 clickable 或 isClickable)
* `新增` button 控件支持 isColored 及 isBorderless 布尔类型属性
* `新增` console.resetGlobalLogConfig 方法用于重置全局日志配置
* `新增` web.newWebSocket 方法用于创建 Web Socket 实例 (参阅 项目文档 > [万维网](https://docs.autojs6.com/#/web?id=m-newwebsocket))
* `修复` 文件管理器的文件夹排序异常
* `修复` floaty 模块构建的浮动窗口无法调节样式及位置的问题 _[`issue #60`](http://issues.autojs6.com/60)_
* `修复` floaty 模块构建的浮动窗口与系统状态栏重叠的问题
* `修复` http.postMultipart 方法功能异常 _[`issue #56`](http://issues.autojs6.com/56)_
* `修复` Android 7.x 无法运行任何脚本的问题 _[`issue #61`](http://issues.autojs6.com/61)_
* `修复` sign.property 文件不存在时无法构建项目的问题
* `修复` 高版本系统 AutoJs6 置于后台时可能因无前台通知权限而崩溃的问题 (API >= 33)
* `修复` 调用 console.show 方法后日志窗口点击 FAB 按钮无法清空日志的问题
* `修复` 脚本编辑器调试时出现的 prototype 空指针异常
* `修复` 脚本编辑器运行脚本时在缓存文件夹运行临时脚本而非先保存再在原始位置运行以避免可能的脚本内容丢失问题
* `修复` 调整布局层次分析的层级色条宽度避免层级过多时控件名称无法显示的问题 _[`issue #46`](http://issues.autojs6.com/46)_
* `优化` 布局分析浮动窗口增加退出按钮以关闭窗口 _[`issue #63`](http://issues.autojs6.com/63)_
* `优化` 脚本绝对路径使用简称形式以缩减文本长度并增加可读性
* `优化` 将 Error 替换为 Exception 避免出现异常时 AutoJs6 应用崩溃
* `优化` 视图 (View) 绑定方式由 ButterKnife 迁移至 View Binding _[`issue #48`](http://issues.autojs6.com/48)_
* `优化` 服务端模式非正常关闭时将于 AutoJs6 启动时自动开启 _[`issue #64`](http://issues.autojs6.com/64)_
* `优化` 客户端模式非正常关闭时将于 AutoJs6 启动时按最近一次的历史地址自动连接
* `优化` 升级 Leakcanary 版本 2.10 -> 2.11
* `优化` 升级 Android Material 版本 1.8.0 -> 1.9.0
* `优化` 升级 Androidx WebKit 版本 1.6.1 -> 1.7.0
* `优化` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.9 -> 5.0.0-alpha.11
* `优化` 升级 MLKit Text Recognition Chinese 版本 16.0.0-beta6 -> 16.0.0
# v6.3.0
###### 2023/04/29
@@ -347,7 +383,7 @@
* `新增` 主页抽屉底部增加重启应用按钮
* `新增` 主页抽屉增加忽略电池优化/显示在其他应用上层等开关
* `修复` 应用初始安装后部分区域主题颜色渲染异常的问题
* `修复` sign.property 不存在时无法 build 的问题
* `修复` sign.property 文件不存在时无法构建项目的问题
* `修复` 定时任务面板一次性任务的月份存取错误
* `修复` 应用设置页面开关颜色不随主题变更的问题
* `修复` 无法识别打包插件及打包插件下载地址无效的问题

View File

@@ -1532,6 +1532,10 @@
<li><span class="stability_undefined"><a href="#console_setgloballogconfig_config">setGlobalLogConfig(config)</a></span></li>
</ul>
</li>
<li><span class="stability_undefined"><a href="#console_m_resetgloballogconfig">[m] resetGlobalLogConfig</a></span><ul>
<li><span class="stability_undefined"><a href="#console_resetgloballogconfig">resetGlobalLogConfig()</a></span></li>
</ul>
</li>
<li><span class="stability_undefined"><a href="#console_m_input">[m] input</a></span><ul>
<li><span class="stability_undefined"><a href="#console_input_data_args">input(data, ...args)</a></span></li>
</ul>
@@ -2017,6 +2021,10 @@
<li><span class="stability_undefined"><a href="#web_newinjectablewebclient">newInjectableWebClient()</a></span></li>
</ul>
</li>
<li><span class="stability_undefined"><a href="#web_m_newwebsocket">[m] newWebSocket</a></span><ul>
<li><span class="stability_undefined"><a href="#web_newwebsocket_url">newWebSocket(url)</a></span></li>
</ul>
</li>
</ul>
</li>
<li><span class="stability_undefined"><a href="#http_http">HTTP</a></span><ul>
@@ -2541,7 +2549,7 @@ console.log(UiObject.bounds()); /* 访问的是类 UiObject 的静态方法 boun
<h3>OCR 特性<span><a class="mark" href="#qa_ocr" id="qa_ocr">#</a></span></h3>
<p>AutoJs6 的 OCR 特性是基于 <a href="https://developers.google.com/ml-kit?hl=zh-cn">Google ML Kit</a> 的 <a href="https://developers.google.com/ml-kit/vision/text-recognition/android?hl=zh-cn">文字识别 API</a> 实现的.</p>
<blockquote>
<p>注: AutoJs6 的 <a href="http://project.autojs6.com/blob/master/app/src/main/java/org/autojs/autojs/runtime/api/MlKitOCR.kt">OCR 实现源码</a> 参考自 <a href="https://github.com/TonyJiangWJ">TonyJiangWJ</a> 的开源项目.</p>
<p>注: AutoJs6 的 <a href="http://project.autojs6.com/blob/master/app/src/main/java/org/autojs/autojs/runtime/api/MlKitOCR.kt">OCR 实现源码</a> 参考自 <a href="https://github.com/TonyJiangWJ">TonyJiangWJ</a> 的 <a href="https://github.com/TonyJiangWJ/Auto.js">开源项目</a>.</p>
</blockquote>
<blockquote>
<p>参阅: <a href="ocr.html">光学字符识别 (OCR)</a> 模块</p>
@@ -7615,7 +7623,7 @@ ocr.recognizeText(img, [ 0, 0, 100, 150 ]).filter(text =&gt; text.includes(&#39;
<pre><code class="lang-js">ocr.recognizeText(&#39;./picture.jpg&#39;); /* 获取本地图像文件中的所有文本. */
</code></pre>
<h3>recognizeText(imgPath, region)<span><a class="mark" href="#ocr_recognizetext_imgpath_region" id="ocr_recognizetext_imgpath_region">#</a></span></h3>
<p><strong><code>6.3.0</code></strong> <strong><code>Overload [4-5]/6</code></strong></p>
<p><strong><code>6.3.0</code></strong> <strong><code>Overload 6/6</code></strong></p>
<ul>
<li><strong>imgPath</strong> { <span class="type"><a href="dataTypes.html#datatypes_string">string</a></span> } - 图像路径</li>
<li><strong>region</strong> { <span class="type"><a href="omniTypes.html#omnitypes_omniregion">OmniRegion</a></span> } - OCR 识别区域</li>
@@ -10278,6 +10286,14 @@ console.timeEnd(&#39;fruit&#39;);
maxFileSize: 384 &lt;&lt; 10, /* 384 KB. */
});
</code></pre>
<h2>[m] resetGlobalLogConfig<span><a class="mark" href="#console_m_resetgloballogconfig" id="console_m_resetgloballogconfig">#</a></span></h2>
<h3>resetGlobalLogConfig()<span><a class="mark" href="#console_resetgloballogconfig" id="console_resetgloballogconfig">#</a></span></h3>
<p><strong><code>6.3.1</code></strong></p>
<ul>
<li><ins><strong>returns</strong></ins> { <span class="type"><a href="dataTypes.html#datatypes_void">void</a></span> }</li>
</ul>
<p>重置全局日志写入配置.</p>
<p>此方法可重置 <a href="#all_m_setgloballogconfig">setGlobalLogConfig</a> 的全部选项配置.</p>
<h2>[m] input<span><a class="mark" href="#console_m_input" id="console_m_input">#</a></span></h2>
<h3>input(data, ...args)<span><a class="mark" href="#console_input_data_args" id="console_input_data_args">#</a></span></h3>
<p><strong><code>ABANDONED</code></strong></p>
@@ -10286,7 +10302,7 @@ console.timeEnd(&#39;fruit&#39;);
<li><strong>args</strong> { <span class="type"><a href="documentation.html#documentation_可变参数">...</a><a href="dataTypes.html#datatypes_any">any</a><a href="documentation.html#documentation_可变参数">[]</a></span> } - <a href="glossaries.html#glossaries_占位符替换参数">占位符替换参数</a></li>
<li><ins><strong>returns</strong></ins> { <span class="type"><a href="dataTypes.html#datatypes_void">void</a></span> }</li>
</ul>
<p>此方法已于 <code>6.3.0</code> 版本被废弃, 使用后将无任何效果.</p>
<p>此方法已于 <code>6.3.1</code> 版本被废弃, 使用后将无任何效果.</p>
<h2>[m] rawInput<span><a class="mark" href="#console_m_rawinput" id="console_m_rawinput">#</a></span></h2>
<h3>rawInput(data, ...args)<span><a class="mark" href="#console_rawinput_data_args" id="console_rawinput_data_args">#</a></span></h3>
<p><strong><code>ABANDONED</code></strong></p>
@@ -10295,7 +10311,7 @@ console.timeEnd(&#39;fruit&#39;);
<li><strong>args</strong> { <span class="type"><a href="documentation.html#documentation_可变参数">...</a><a href="dataTypes.html#datatypes_any">any</a><a href="documentation.html#documentation_可变参数">[]</a></span> } - <a href="glossaries.html#glossaries_占位符替换参数">占位符替换参数</a></li>
<li><ins><strong>returns</strong></ins> { <span class="type"><a href="dataTypes.html#datatypes_void">void</a></span> }</li>
</ul>
<p>此方法已于 <code>6.3.0</code> 版本被废弃, 使用后将无任何效果.
<p>此方法已于 <code>6.3.1</code> 版本被废弃, 使用后将无任何效果.
<!-- [end-include:console.md] --></p>
<!-- [start-include:shell.md] -->
<h1>Shell<span><a class="mark" href="#shell_shell" id="shell_shell">#</a></span></h1>
@@ -13433,6 +13449,73 @@ settings.setDisplayZoomControls(false);
<pre><code class="lang-js">/* 为 webView 对象重新设置一个新的客户端. */
webView.setWebViewClient(newInjectableWebClient());
</code></pre>
<h2>[m] newWebSocket<span><a class="mark" href="#web_m_newwebsocket" id="web_m_newwebsocket">#</a></span></h2>
<h3>newWebSocket(url)<span><a class="mark" href="#web_newwebsocket_url" id="web_newwebsocket_url">#</a></span></h3>
<p><strong><code>6.3.1</code></strong> <strong><code>Global</code></strong></p>
<ul>
<li><strong>url</strong> { <span class="type"><a href="dataTypes.html#datatypes_string">string</a></span> }</li>
<li><ins><strong>returns</strong></ins> { <span class="type"><a href="webSocketType.html">WebSocket</a></span> }</li>
</ul>
<p>新建并返回一个 <a href="webSocketType.html">WebSocket</a> 实例.</p>
<pre><code class="lang-js">console.setExitOnClose(7e3).show();
let ws = web.newWebSocket(&#39;wss://echo.websocket.events&#39;);
ws
.on(&#39;open&#39;, (res, ws) =&gt; {
console.log(&#39;WebSocket 已连接&#39;);
})
.on(&#39;message&#39;, (message, ws) =&gt; {
console.log(&#39;接收到消息&#39;);
// if (message instanceof okio.ByteString) {
// console.log(`消息类型: ByteString`);
// } else if (typeof message === &#39;string&#39;) {
// console.log(`消息类型: String`);
// } else {
// throw TypeError(&#39;Should never happen&#39;);
// }
})
.on(&#39;text&#39;, (text, ws) =&gt; {
console.info(&#39;接收到文本消息:&#39;);
console.info(`text: ${text}`);
})
.on(&#39;bytes&#39;, (bytes, ws) =&gt; {
console.info(&#39;接收到字节数组消息:&#39;);
console.info(`utf8: ${bytes.utf8()}`);
console.info(`base64: ${bytes.base64()}`);
console.info(`md5: ${bytes.md5()}`);
console.info(`hex: ${bytes.hex()}`);
})
.on(&#39;closing&#39;, (code, reason, ws) =&gt; {
console.log(&#39;WebSocket 关闭中&#39;);
})
.on(&#39;closed&#39;, (code, reason, ws) =&gt; {
console.log(&#39;WebSocket 已关闭&#39;);
console.log(`code: ${code}`);
if (reason) console.log(`reason: ${reason}`);
})
.on(&#39;failure&#39;, (err, res, ws) =&gt; {
console.error(&#39;WebSocket 连接失败&#39;);
console.error(err);
});
/* 发送文本消息. */
ws.send(&#39;Hello WebSocket&#39;);
/* 发送字节数组消息. */
ws.send(new okio.ByteString(new java.lang.String(&#39;Hello WebSocket&#39;).getBytes()));
setTimeout(() =&gt; {
console.log(&#39;断开 WebSocket&#39;);
/* code 支持的范围 [1000,5000). */
/* 1000 表示成功操作或常规的 Socket 关闭操作. */
/* 参阅 https://github.com/Luka967/websocket-close-codes */
ws.close(1000, &#39;Closed by user&#39;);
}, 8e3);
setTimeout(() =&gt; console.log(&#39;Exit&#39;), 9e3);
</code></pre>
<!-- [end-include:web.md] -->
<!-- [start-include:http.md] -->
<h1>HTTP<span><a class="mark" href="#http_http" id="http_http">#</a></span></h1>
@@ -13441,7 +13524,7 @@ webView.setWebViewClient(newInjectableWebClient());
<p style="font: italic 1em sans-serif; color: #78909C">Marked by SuperMonster003 on Mar 21, 2023.</p>
<hr>
<p>http模块提供一些进行http请求的函数.</p>
<p>http 模块主要用于发送 HTTP 请求, 获取并解析 HTTP 响应.</p>
<blockquote>
<p>注: 与 <a href="web.html">web</a> 模块不同, web 模块主要用于 WebView 网页的注入及客户端构建.</p>
</blockquote>
@@ -13578,11 +13661,11 @@ log(res.body.string());
<p>选项options可以包含以下属性:</p>
<ul>
<li><code>headers</code> { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object" class="type">Object</a> } 键值对形式的HTTP头部信息. 有关HTTP头部信息, 参见<a href="http://www.runoob.com/http/http-header-fields.html">菜鸟教程:HTTP响应头信息</a>.</li>
<li><code>method</code> { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type">string</a> } HTTP请求方法. 包括&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELET&quot;, &quot;PATCH&quot;.</li>
<li><code>method</code> { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type">string</a> } HTTP请求方法. 包括&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;, &quot;PATCH&quot;.</li>
<li><code>contentType</code> { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type">string</a> } HTTP头部信息中的&quot;Content-Type&quot;, 表示HTTP请求的内容类型. 例如&quot;text/plain&quot;, &quot;application/json&quot;. 更多信息参见<a href="http://www.runoob.com/http/http-content-type.html">菜鸟教程:HTTP contentType</a>.</li>
<li><code>body</code> { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type">string</a> } | { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array" class="type">Array</a> } | { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function" class="type">Function</a> } HTTP请求的内容. 可以是一个字符串, 也可以是一个字节数组;或者是一个以<a href="https://github.com/square/okio/blob/master/okio/src/main/java/okio/BufferedSink.java/">BufferedSink</a>为参数的函数.</li>
</ul>
<p>该函数是get, post, postJson等函数的基础函数. 因此除非是PUT, DELET等请求, 或者需要更高定制的HTTP请求, 否则直接使用get, post, postJson等函数会更加方便.</p>
<p>该函数是get, post, postJson等函数的基础函数. 因此除非是PUT, DELETE等请求, 或者需要更高定制的HTTP请求, 否则直接使用get, post, postJson等函数会更加方便.</p>
<h2>[m] buildRequest<span><a class="mark" href="#http_m_buildrequest" id="http_m_buildrequest">#</a></span></h2>
<h3>buildRequest(url, options)<span><a class="mark" href="#http_buildrequest_url_options" id="http_buildrequest_url_options">#</a></span></h3>
<p>... ...

View File

@@ -189,12 +189,20 @@
</thead>
<tbody>
<tr>
<td style="text-align:left">35 (?)</td>
<td style="text-align:left">Android 15</td>
<td style="text-align:left">VANILLA_ICE_CREAM</td>
<td style="text-align:left">15</td>
<td style="text-align:left">Vanilla Ice Cream</td>
<td style="text-align:left">Q3, 2024 (?)</td>
</tr>
<tr>
<td style="text-align:left">34</td>
<td style="text-align:left">Android 14</td>
<td style="text-align:left">?</td>
<td style="text-align:left">UPSIDE_DOWN_CAKE</td>
<td style="text-align:left">14</td>
<td style="text-align:left">Upside Down Cake</td>
<td style="text-align:left">Feb 8, 2023</td>
<td style="text-align:left">Q3, 2023 (?)</td>
</tr>
<tr>
<td style="text-align:left">33</td>
@@ -475,6 +483,7 @@
<p>要求设备 API 级别不低于指定值:</p>
<pre><code class="lang-js">/* 在 API 级别低于 30 的设备上将抛出异常. */
runtime.requiresApi(30);
runtime.requiresApi(util.versionCodes.R); /* 效果同上. */
</code></pre>
<blockquote>
<p>注: AutoJs6 安装及使用需满足的最低 API 级别为 24 (7.0) [N].</p>

View File

@@ -166,6 +166,7 @@
<h2>目录</h2>
<ul>
<li><span class="stability_undefined"><a href="#changelog_changelog">文档更新日志 (Changelog)</a></span><ul>
<li><span class="stability_undefined"><a href="#changelog_v1_1_4">v1.1.4</a></span></li>
<li><span class="stability_undefined"><a href="#changelog_v1_1_3">v1.1.3</a></span></li>
<li><span class="stability_undefined"><a href="#changelog_v1_1_2">v1.1.2</a></span></li>
<li><span class="stability_undefined"><a href="#changelog_v1_1_1">v1.1.1</a></span></li>
@@ -183,6 +184,15 @@
<div id="apicontent">
<h1>文档更新日志 (Changelog)<span><a class="mark" href="#changelog_changelog" id="changelog_changelog">#</a></span></h1>
<h2>v1.1.4<span><a class="mark" href="#changelog_v1_1_4" id="changelog_v1_1_4">#</a></span></h2>
<p style="font: bold 0.8em sans-serif; color: #888888">2023/05/26</p>
<ul>
<li><code>新增</code> <a href="https://docs.autojs6.com/#/console?id=m-resetgloballogconfig">console.resetGlobalLogConfig</a> 文档</li>
<li><code>新增</code> <a href="https://docs.autojs6.com/#/web?id=m-newwebsocket">web.newWebSocket</a> 文档</li>
<li><code>优化</code> 完善 <a href="https://docs.autojs6.com/#/omniTypes">全能类型 (Omnipotent Types)</a> 章节</li>
<li><code>优化</code> 完善 <a href="https://docs.autojs6.com/#/apiLevel">安卓 API 级别 (Android API Level)</a> 章节</li>
</ul>
<h2>v1.1.3<span><a class="mark" href="#changelog_v1_1_3" id="changelog_v1_1_3">#</a></span></h2>
<p style="font: bold 0.8em sans-serif; color: #888888">2023/04/29</p>
@@ -211,7 +221,7 @@
<li><code>新增</code> <a href="https://docs.autojs6.com/#/notice">消息通知 (Notice)</a> 文档</li>
<li><code>新增</code> <a href="https://docs.autojs6.com/#/httpRequestHeadersType">HttpRequestHeaders</a> / <a href="https://docs.autojs6.com/#/httpResponseHeadersType">HttpResponseHeaders</a> / <a href="https://docs.autojs6.com/#/opencvRectType">OpencvRect</a> 等类型</li>
<li><code>新增</code> <a href="https://docs.autojs6.com/#/glossaries?id=通知渠道">通知渠道</a> / <a href="https://docs.autojs6.com/#/glossaries?id=HTTP-标头">HTTP 标头</a> / <a href="https://docs.autojs6.com/#/glossaries?id=MIME-类型">MIME 类型</a> / <a href="https://docs.autojs6.com/#/glossaries?id=HTTP-请求方法">HTTP 请求方法</a> 等术语</li>
<li><code>新增</code> <a href="https://docs.autojs6.com/#/color">颜色 (Color)</a> 章节增加 <a href="https://docs.autojs6.com/#/color?id=m-toColorStateList">toColorStateList</a> 及 <a href="https://docs.autojs6.com/#/color?id=m-setPaintColor">setPaintColor</a> 条目</li>
<li><code>新增</code> <a href="https://docs.autojs6.com/#/color">颜色 (Color)</a> 章节增加 <a href="https://docs.autojs6.com/#/color?id=m-tocolorstatelist">toColorStateList</a> 及 <a href="https://docs.autojs6.com/#/color?id=m-setpaintcolor">setPaintColor</a> 条目</li>
<li><code>修复</code> 文档更新日志条目中的链接无效的问题</li>
<li><code>优化</code> 完善 <a href="https://docs.autojs6.com/#/qa">疑难解答 (Q &amp; A)</a> 章节</li>
</ul>

View File

@@ -404,7 +404,7 @@ c.getRed(); // 255
c.getRedDouble(); // 1
</code></pre>
<p>Color 类是对 <a href="color.html">colors</a> 模块的一种变式封装, 用于解决 colors 模块冗余嵌套的难题.</p>
<p>如需对颜色 <code>hsv(174,100,59)</code> 设置 <code>80%</code> 透明度然后返回其 Hex 代码:</p>
<p>例如需要对颜色 <code>hsv(174,100,59)</code> 设置 <code>80%</code> 透明度然后返回其 Hex 代码:</p>
<pre><code class="lang-js">colors.toHex(colors.setAlpha(colors.hsv(174, 100, 59), 0.8));
</code></pre>
<p>或使用变量拆写形式以增加可读性:</p>
@@ -422,7 +422,7 @@ colors.toHex(colorWithAlpha);
.toHex();
</code></pre>
<blockquote>
<p>注: 上述示例仅用于演示, 否则可使用 colors.hsva 或 Color#setHsva 同时设置 HSV 分量与 A 分量.</p>
<p>注: 上述示例仅用于演示, 实际可使用 colors.hsva 或 Color#setHsva 同时设置 HSV 分量与 A 分量.</p>
</blockquote>
<p>Color 实例方法的使用方式与 colors 模块对应方法多数情况是类似的, 因此某些情况下可用于替代 colors 模块.</p>
<p>以 <code>set</code> 或 <code>remove</code> 为前缀的方法, 通常都会返回 Color 自身类型, 从而支持链式调用, 如 <a href="#colortype_m_setalpha">setAlpha</a>, <a href="#colortype_m_removealpha">removeAlpha</a>, <a href="#colortype_m_setred">setRed</a>, <a href="#colortype_m_removered">removeRed</a>, <a href="#colortype_m_sethsv">setHsv</a>, <a href="#colortype_m_setrgba">setRgba</a> 等.</p>

View File

@@ -324,6 +324,10 @@
<li><span class="stability_undefined"><a href="#console_setgloballogconfig_config">setGlobalLogConfig(config)</a></span></li>
</ul>
</li>
<li><span class="stability_undefined"><a href="#console_m_resetgloballogconfig">[m] resetGlobalLogConfig</a></span><ul>
<li><span class="stability_undefined"><a href="#console_resetgloballogconfig">resetGlobalLogConfig()</a></span></li>
</ul>
</li>
<li><span class="stability_undefined"><a href="#console_m_input">[m] input</a></span><ul>
<li><span class="stability_undefined"><a href="#console_input_data_args">input(data, ...args)</a></span></li>
</ul>
@@ -1174,6 +1178,14 @@ console.timeEnd(&#39;fruit&#39;);
maxFileSize: 384 &lt;&lt; 10, /* 384 KB. */
});
</code></pre>
<h2>[m] resetGlobalLogConfig<span><a class="mark" href="#console_m_resetgloballogconfig" id="console_m_resetgloballogconfig">#</a></span></h2>
<h3>resetGlobalLogConfig()<span><a class="mark" href="#console_resetgloballogconfig" id="console_resetgloballogconfig">#</a></span></h3>
<p><strong><code>6.3.1</code></strong></p>
<ul>
<li><ins><strong>returns</strong></ins> { <span class="type"><a href="dataTypes.html#datatypes_void">void</a></span> }</li>
</ul>
<p>重置全局日志写入配置.</p>
<p>此方法可重置 <a href="#console_m_setgloballogconfig">setGlobalLogConfig</a> 的全部选项配置.</p>
<h2>[m] input<span><a class="mark" href="#console_m_input" id="console_m_input">#</a></span></h2>
<h3>input(data, ...args)<span><a class="mark" href="#console_input_data_args" id="console_input_data_args">#</a></span></h3>
<p><strong><code>ABANDONED</code></strong></p>
@@ -1182,7 +1194,7 @@ console.timeEnd(&#39;fruit&#39;);
<li><strong>args</strong> { <span class="type"><a href="documentation.html#documentation_可变参数">...</a><a href="dataTypes.html#datatypes_any">any</a><a href="documentation.html#documentation_可变参数">[]</a></span> } - <a href="glossaries.html#glossaries_占位符替换参数">占位符替换参数</a></li>
<li><ins><strong>returns</strong></ins> { <span class="type"><a href="dataTypes.html#datatypes_void">void</a></span> }</li>
</ul>
<p>此方法已于 <code>6.3.0</code> 版本被废弃, 使用后将无任何效果.</p>
<p>此方法已于 <code>6.3.1</code> 版本被废弃, 使用后将无任何效果.</p>
<h2>[m] rawInput<span><a class="mark" href="#console_m_rawinput" id="console_m_rawinput">#</a></span></h2>
<h3>rawInput(data, ...args)<span><a class="mark" href="#console_rawinput_data_args" id="console_rawinput_data_args">#</a></span></h3>
<p><strong><code>ABANDONED</code></strong></p>
@@ -1191,7 +1203,7 @@ console.timeEnd(&#39;fruit&#39;);
<li><strong>args</strong> { <span class="type"><a href="documentation.html#documentation_可变参数">...</a><a href="dataTypes.html#datatypes_any">any</a><a href="documentation.html#documentation_可变参数">[]</a></span> } - <a href="glossaries.html#glossaries_占位符替换参数">占位符替换参数</a></li>
<li><ins><strong>returns</strong></ins> { <span class="type"><a href="dataTypes.html#datatypes_void">void</a></span> }</li>
</ul>
<p>此方法已于 <code>6.3.0</code> 版本被废弃, 使用后将无任何效果.</p>
<p>此方法已于 <code>6.3.1</code> 版本被废弃, 使用后将无任何效果.</p>
</div>
</div>

View File

@@ -309,7 +309,8 @@ console.build({ titleIconsTint: &#39;green&#39; }).show();
console.build({ contentTextSize: 18 }).show();
</code></pre>
<h2>[p?] contentTextColor<span><a class="mark" href="#consolebuildoptionstype_p_contenttextcolor" id="consolebuildoptionstype_p_contenttextcolor">#</a></span></h2>
<div class="signature"><ul>
<p><strong><code>Overload 1/2</code></strong></p>
<ul>
<li>{{<ul>
<li>verbose?: <a href="omniTypes.html#omnitypes_omnicolor">OmniColor</a>;</li>
<li>log?: <a href="omniTypes.html#omnitypes_omnicolor">OmniColor</a>;</li>
@@ -319,9 +320,9 @@ console.build({ contentTextSize: 18 }).show();
<li>assert?: <a href="omniTypes.html#omnitypes_omnicolor">OmniColor</a>;</li>
</ul>
</li>
<li>} | <a href="omniTypes.html#omnitypes_omnicolor">OmniColor</a> } - 浮动窗口日志文本字体颜色</li>
<li>}} - 浮动窗口日志文本字体颜色</li>
</ul>
</div><p>设置控制台浮动窗口的日志文本字体颜色, 按日志等级设置一个或多个不同的字体颜色.</p>
<p>设置控制台浮动窗口的日志文本字体颜色, 按日志等级设置一个或多个不同的字体颜色.</p>
<pre><code class="lang-js">/* 设置 LOG 等级日志字体颜色为深橙色. */
console.build({ contentTextColor: { log: &#39;dark-orange&#39; } }).show();
console.log(&#39;content text color test for console.log&#39;);
@@ -344,7 +345,11 @@ console.build({
console[fName].call(console, `content text color test for console.${fName}`);
});
</code></pre>
<p>如果希望不区分日志等级, 统一设置所有日志的文本颜色, 可参考下述示例:</p>
<p><strong><code>Overload 2/2</code></strong></p>
<ul>
<li>{ <span class="type"><a href="omniTypes.html#omnitypes_omnicolor">OmniColor</a></span> } - 浮动窗口日志文本统一字体颜色</li>
</ul>
<p>使用 <code>{ contentTextColor: OmniColor }</code> 时, 不区分日志等级, 统一设置所有日志的文本颜色:</p>
<pre><code class="lang-js">/* 所有日志本文的颜色统一设置为深绿色. */
console.build({
contentTextColor: &#39;dark-green&#39;,

View File

@@ -203,7 +203,7 @@
<p style="font: italic 1em sans-serif; color: #78909C">Marked by SuperMonster003 on Mar 21, 2023.</p>
<hr>
<p>http模块提供一些进行http请求的函数.</p>
<p>http 模块主要用于发送 HTTP 请求, 获取并解析 HTTP 响应.</p>
<blockquote>
<p>注: 与 <a href="web.html">web</a> 模块不同, web 模块主要用于 WebView 网页的注入及客户端构建.</p>
</blockquote>
@@ -340,11 +340,11 @@ log(res.body.string());
<p>选项options可以包含以下属性:</p>
<ul>
<li><code>headers</code> { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object" class="type">Object</a> } 键值对形式的HTTP头部信息. 有关HTTP头部信息, 参见<a href="http://www.runoob.com/http/http-header-fields.html">菜鸟教程:HTTP响应头信息</a>.</li>
<li><code>method</code> { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type">string</a> } HTTP请求方法. 包括&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELET&quot;, &quot;PATCH&quot;.</li>
<li><code>method</code> { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type">string</a> } HTTP请求方法. 包括&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;, &quot;PATCH&quot;.</li>
<li><code>contentType</code> { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type">string</a> } HTTP头部信息中的&quot;Content-Type&quot;, 表示HTTP请求的内容类型. 例如&quot;text/plain&quot;, &quot;application/json&quot;. 更多信息参见<a href="http://www.runoob.com/http/http-content-type.html">菜鸟教程:HTTP contentType</a>.</li>
<li><code>body</code> { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type">string</a> } | { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array" class="type">Array</a> } | { <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function" class="type">Function</a> } HTTP请求的内容. 可以是一个字符串, 也可以是一个字节数组;或者是一个以<a href="https://github.com/square/okio/blob/master/okio/src/main/java/okio/BufferedSink.java/">BufferedSink</a>为参数的函数.</li>
</ul>
<p>该函数是get, post, postJson等函数的基础函数. 因此除非是PUT, DELET等请求, 或者需要更高定制的HTTP请求, 否则直接使用get, post, postJson等函数会更加方便.</p>
<p>该函数是get, post, postJson等函数的基础函数. 因此除非是PUT, DELETE等请求, 或者需要更高定制的HTTP请求, 否则直接使用get, post, postJson等函数会更加方便.</p>
<h2>[m] buildRequest<span><a class="mark" href="#http_m_buildrequest" id="http_m_buildrequest">#</a></span></h2>
<h3>buildRequest(url, options)<span><a class="mark" href="#http_buildrequest_url_options" id="http_buildrequest_url_options">#</a></span></h3>
<p>... ...</p>

View File

@@ -316,7 +316,7 @@ ocr.recognizeText(img, [ 0, 0, 100, 150 ]).filter(text =&gt; text.includes(&#39;
<pre><code class="lang-js">ocr.recognizeText(&#39;./picture.jpg&#39;); /* 获取本地图像文件中的所有文本. */
</code></pre>
<h3>recognizeText(imgPath, region)<span><a class="mark" href="#ocr_recognizetext_imgpath_region" id="ocr_recognizetext_imgpath_region">#</a></span></h3>
<p><strong><code>6.3.0</code></strong> <strong><code>Overload [4-5]/6</code></strong></p>
<p><strong><code>6.3.0</code></strong> <strong><code>Overload 6/6</code></strong></p>
<ul>
<li><strong>imgPath</strong> { <span class="type"><a href="dataTypes.html#datatypes_string">string</a></span> } - 图像路径</li>
<li><strong>region</strong> { <span class="type"><a href="omniTypes.html#omnitypes_omniregion">OmniRegion</a></span> } - OCR 识别区域</li>

View File

@@ -240,6 +240,35 @@ console.show(); /* 显示控制台悬浮窗. */
</tr>
</tbody>
</table>
<p><code>OmniColor</code> 不仅可用于参数传入方法中, 还可以作为 XML 元素的属性值:</p>
<pre><code class="lang-js">&#39;ui&#39;;
ui.layout(&lt;vertical&gt;
&lt;button text=&quot;click to start&quot; color=&quot;#006400&quot;/&gt;
&lt;/vertical&gt;);
</code></pre>
<p>上述示例设置按钮布局的文字颜色为深绿色, 使用了 <a href="dataTypes.html#datatypes_colorhex">ColorHex</a> 作为颜色值.</p>
<p>使用 <a href="dataTypes.html#datatypes_colorname">ColorName</a> 作为颜色值也可达到相同的效果:</p>
<pre><code class="lang-js">&#39;ui&#39;;
ui.layout(&lt;vertical&gt;
&lt;button text=&quot;click to start&quot; color=&quot;dark-green&quot;/&gt;
&lt;/vertical&gt;);
</code></pre>
<p>使用 <a href="dataTypes.html#datatypes_colorint">ColorInt</a> 作为颜色值同样可以达到相同的效果:</p>
<pre><code class="lang-js">&#39;ui&#39;;
ui.layout(&lt;vertical&gt;
&lt;button text=&quot;click to start&quot; color=&quot;-16751616&quot;/&gt;
&lt;/vertical&gt;);
</code></pre>
<p>如需使用主题色作为颜色值, 需要使用一对花括号以及绑定全局作用域的表达式:</p>
<pre><code class="lang-js">&#39;ui&#39;;
ui.layout(&lt;vertical&gt;
&lt;button text=&quot;click to start&quot; color=&quot;{{autojs.themeColor}}&quot;/&gt;
&lt;/vertical&gt;);
</code></pre>
<h2>OmniIntent<span><a class="mark" href="#omnitypes_omniintent" id="omnitypes_omniintent">#</a></span></h2>
<p>Intent 聚合类型.</p>
<table>

View File

@@ -275,7 +275,7 @@
<h3>OCR 特性<span><a class="mark" href="#qa_ocr" id="qa_ocr">#</a></span></h3>
<p>AutoJs6 的 OCR 特性是基于 <a href="https://developers.google.com/ml-kit?hl=zh-cn">Google ML Kit</a> 的 <a href="https://developers.google.com/ml-kit/vision/text-recognition/android?hl=zh-cn">文字识别 API</a> 实现的.</p>
<blockquote>
<p>注: AutoJs6 的 <a href="http://project.autojs6.com/blob/master/app/src/main/java/org/autojs/autojs/runtime/api/MlKitOCR.kt">OCR 实现源码</a> 参考自 <a href="https://github.com/TonyJiangWJ">TonyJiangWJ</a> 的开源项目.</p>
<p>注: AutoJs6 的 <a href="http://project.autojs6.com/blob/master/app/src/main/java/org/autojs/autojs/runtime/api/MlKitOCR.kt">OCR 实现源码</a> 参考自 <a href="https://github.com/TonyJiangWJ">TonyJiangWJ</a> 的 <a href="https://github.com/TonyJiangWJ/Auto.js">开源项目</a>.</p>
</blockquote>
<blockquote>
<p>参阅: <a href="ocr.html">光学字符识别 (OCR)</a> 模块</p>

View File

@@ -175,6 +175,10 @@
<li><span class="stability_undefined"><a href="#web_newinjectablewebclient">newInjectableWebClient()</a></span></li>
</ul>
</li>
<li><span class="stability_undefined"><a href="#web_m_newwebsocket">[m] newWebSocket</a></span><ul>
<li><span class="stability_undefined"><a href="#web_newwebsocket_url">newWebSocket(url)</a></span></li>
</ul>
</li>
</ul>
</li>
</ul>
@@ -287,6 +291,73 @@ settings.setDisplayZoomControls(false);
<p>新建并返回一个 <a href="injectableWebClientType.html">InjectableWebClient</a> (可 <a href="glossaries.html#glossaries_注入">注入</a> 的 <a href="https://developer.android.com/reference/android/webkit/WebViewClient">WebViewClient</a>) 实例.</p>
<pre><code class="lang-js">/* 为 webView 对象重新设置一个新的客户端. */
webView.setWebViewClient(newInjectableWebClient());
</code></pre>
<h2>[m] newWebSocket<span><a class="mark" href="#web_m_newwebsocket" id="web_m_newwebsocket">#</a></span></h2>
<h3>newWebSocket(url)<span><a class="mark" href="#web_newwebsocket_url" id="web_newwebsocket_url">#</a></span></h3>
<p><strong><code>6.3.1</code></strong> <strong><code>Global</code></strong></p>
<ul>
<li><strong>url</strong> { <span class="type"><a href="dataTypes.html#datatypes_string">string</a></span> }</li>
<li><ins><strong>returns</strong></ins> { <span class="type"><a href="webSocketType.html">WebSocket</a></span> }</li>
</ul>
<p>新建并返回一个 <a href="webSocketType.html">WebSocket</a> 实例.</p>
<pre><code class="lang-js">console.setExitOnClose(7e3).show();
let ws = web.newWebSocket(&#39;wss://echo.websocket.events&#39;);
ws
.on(&#39;open&#39;, (res, ws) =&gt; {
console.log(&#39;WebSocket 已连接&#39;);
})
.on(&#39;message&#39;, (message, ws) =&gt; {
console.log(&#39;接收到消息&#39;);
// if (message instanceof okio.ByteString) {
// console.log(`消息类型: ByteString`);
// } else if (typeof message === &#39;string&#39;) {
// console.log(`消息类型: String`);
// } else {
// throw TypeError(&#39;Should never happen&#39;);
// }
})
.on(&#39;text&#39;, (text, ws) =&gt; {
console.info(&#39;接收到文本消息:&#39;);
console.info(`text: ${text}`);
})
.on(&#39;bytes&#39;, (bytes, ws) =&gt; {
console.info(&#39;接收到字节数组消息:&#39;);
console.info(`utf8: ${bytes.utf8()}`);
console.info(`base64: ${bytes.base64()}`);
console.info(`md5: ${bytes.md5()}`);
console.info(`hex: ${bytes.hex()}`);
})
.on(&#39;closing&#39;, (code, reason, ws) =&gt; {
console.log(&#39;WebSocket 关闭中&#39;);
})
.on(&#39;closed&#39;, (code, reason, ws) =&gt; {
console.log(&#39;WebSocket 已关闭&#39;);
console.log(`code: ${code}`);
if (reason) console.log(`reason: ${reason}`);
})
.on(&#39;failure&#39;, (err, res, ws) =&gt; {
console.error(&#39;WebSocket 连接失败&#39;);
console.error(err);
});
/* 发送文本消息. */
ws.send(&#39;Hello WebSocket&#39;);
/* 发送字节数组消息. */
ws.send(new okio.ByteString(new java.lang.String(&#39;Hello WebSocket&#39;).getBytes()));
setTimeout(() =&gt; {
console.log(&#39;断开 WebSocket&#39;);
/* code 支持的范围 [1000,5000). */
/* 1000 表示成功操作或常规的 Socket 关闭操作. */
/* 参阅 https://github.com/Luka967/websocket-close-codes */
ws.close(1000, &#39;Closed by user&#39;);
}, 8e3);
setTimeout(() =&gt; console.log(&#39;Exit&#39;), 9e3);
</code></pre>
</div>

View File

@@ -1,7 +1,5 @@
// noinspection JSUnusedGlobalSymbols,NpmUsedModulesInstalled,JSUnusedLocalSymbols
'use strict';
// @Caution by SuperMonster003 on Apr 19, 2022.
// ! Do not declare with const because variable has already
// ! declared globally in RhinoJavaScriptEngine.kt.
@@ -15,9 +13,11 @@ let RootMode = org.autojs.autojs.util.RootUtils.RootMode;
let Pref = org.autojs.autojs.pref.Pref;
let Shell = org.autojs.autojs.runtime.api.Shell;
let Intent = global.Intent = android.content.Intent;
let Context = global.Context = android.content.Context;
let PendingIntent = android.app.PendingIntent;
let Toast = android.widget.Toast;
let KeyEvent = android.view.KeyEvent;
let MotionEvent = android.view.MotionEvent;
let Version = Packages.io.github.g00fy2.versioncompare.Version;
let Image = org.autojs.autojs.core.image.ImageWrapper;
let ColorTable = org.autojs.autojs.core.image.ColorTable;
@@ -40,17 +40,59 @@ let GlobalAppContext = org.autojs.autojs.app.GlobalAppContext;
let DisplayUtils = org.autojs.autojs.util.DisplayUtils;
let StringUtils = org.autojs.autojs.util.StringUtils;
let ColorUtils = org.autojs.autojs.util.ColorUtils;
let TextUtils = org.autojs.autojs.util.TextUtils;
let ProxyObject = org.autojs.autojs.rhino.ProxyObject;
let ProxyJavaObject = org.autojs.autojs.rhino.ProxyJavaObject;
let NotificationManager = android.app.NotificationManager;
let NotificationCompat = Packages.androidx.core.app.NotificationCompat;
let SecurityException = java.lang.SecurityException;
let Locale = java.util.Locale;
let URI = java.net.URI;
let File = java.io.File;
/* Global View classes. */
let JsAppBarLayout = org.autojs.autojs.core.ui.widget.JsAppBarLayout;
let JsButton = org.autojs.autojs.core.ui.widget.JsButton;
let JsCanvasView = org.autojs.autojs.core.ui.widget.JsCanvasView;
let JsCardView = org.autojs.autojs.core.ui.widget.JsCardView;
let JsCheckBox = org.autojs.autojs.core.ui.widget.JsCheckBox;
let JsConsoleView = org.autojs.autojs.core.ui.widget.JsConsoleView;
let JsDatePicker = org.autojs.autojs.core.ui.widget.JsDatePicker;
let JsDrawerLayout = org.autojs.autojs.core.ui.widget.JsDrawerLayout;
let JsEditText = org.autojs.autojs.core.ui.widget.JsEditText;
let JsFloatingActionButton = org.autojs.autojs.core.ui.widget.JsFloatingActionButton;
let JsFrameLayout = org.autojs.autojs.core.ui.widget.JsFrameLayout;
let JsGridView = org.autojs.autojs.core.ui.widget.JsGridView;
let JsImageButton = org.autojs.autojs.core.ui.widget.JsImageButton;
let JsImageView = org.autojs.autojs.core.ui.widget.JsImageView;
let JsLinearLayout = org.autojs.autojs.core.ui.widget.JsLinearLayout;
let JsListView = org.autojs.autojs.core.ui.widget.JsListView;
let JsProgressBar = org.autojs.autojs.core.ui.widget.JsProgressBar;
let JsRadioButton = org.autojs.autojs.core.ui.widget.JsRadioButton;
let JsRadioGroup = org.autojs.autojs.core.ui.widget.JsRadioGroup;
let JsRatingBar = org.autojs.autojs.core.ui.widget.JsRatingBar;
let JsRelativeLayout = org.autojs.autojs.core.ui.widget.JsRelativeLayout;
let JsScrollView = org.autojs.autojs.core.ui.widget.JsScrollView;
let JsSeekBar = org.autojs.autojs.core.ui.widget.JsSeekBar;
let JsSpinner = org.autojs.autojs.core.ui.widget.JsSpinner;
let JsSwitch = org.autojs.autojs.core.ui.widget.JsSwitch;
let JsTabLayout = org.autojs.autojs.core.ui.widget.JsTabLayout;
let JsTextClock = org.autojs.autojs.core.ui.widget.JsTextClock;
let JsTextView = android.os.Build.VERSION.SDK_INT < 26 /* Android API 26 (8.0) [O] */
? org.autojs.autojs.core.ui.widget.JsTextViewLegacy
: org.autojs.autojs.core.ui.widget.JsTextView;
let JsTimePicker = org.autojs.autojs.core.ui.widget.JsTimePicker;
let JsToggleButton = org.autojs.autojs.core.ui.widget.JsToggleButton;
let JsToolbar = org.autojs.autojs.core.ui.widget.JsToolbar;
let JsViewPager = org.autojs.autojs.core.ui.widget.JsViewPager;
let JsWebView = org.autojs.autojs.core.ui.widget.JsWebView;
/* Global assignment. */
Object.assign(this, {
io: Packages.io,
okio: Packages.okio,
de: Packages.de,
ezy: Packages.ezy,
kotlin: Packages.kotlin,
@@ -264,8 +306,11 @@ Object.assign(this, {
if (typeof global[module] === 'object') {
if (!(Object.hasOwn(global[module], 'toString'))) {
// noinspection JSPotentiallyInvalidConstructorUsage
if (typeof global[module].constructor.prototype.toString !== 'function') {
global[module].toString = () => module;
let prototype = global[module].constructor.prototype;
if (prototype !== undefined) /* For AutoJs6 debugger. */ {
if (typeof prototype.toString !== 'function') {
global[module].toString = () => module;
}
}
}
}
@@ -415,6 +460,9 @@ Object.assign(this, {
[ 'engines', 'continuation', 'http' ],
[ 'images', 'colors', 'dialogs' ],
/* ! http < web */
[ 'web' ],
/* ! files < i18n */
/* ! i18n < selector */
[ 'i18n', 'selector' ],
@@ -438,7 +486,7 @@ Object.assign(this, {
/* Safe to put last regardless of the order, no guarantee ;). */
[ 'floaty', 'storages', 'device', 'recorder', 'toast' ],
[ 'media', 'sensors', 'web', 'events', 'base64', 'notice' ],
[ 'media', 'sensors', 'events', 'base64', 'notice' ],
/* Last but not the least */
[ 'globals' ],

View File

@@ -350,7 +350,6 @@ var MODE = {
};
function Beautifier(js_source_text, options) {
"use strict";
var output;
var tokens = [],
token_pos;

View File

@@ -62,6 +62,9 @@ module.exports = function (scriptRuntime, scope) {
Color.prototype = {
constructor: Color,
toString() {
return this.digest();
},
digest() {
return colors.digest.apply(colors, [ this.color ].concat(Array.from(arguments)));
},
@@ -78,6 +81,9 @@ module.exports = function (scriptRuntime, scope) {
this.color = colors.setAlpha(this.color, alpha);
return this;
},
setAlphaRelative(percentage) {
return this.setAlpha(this.getAlpha() * _.parseRelativePercentage(percentage));
},
getAlpha() {
return colors.alpha(this.color);
},
@@ -98,6 +104,9 @@ module.exports = function (scriptRuntime, scope) {
this.color = colors.setRed(this.color, red);
return this;
},
setRedRelative(percentage) {
return this.setRed(this.getRed() * _.parseRelativePercentage(percentage));
},
getRed() {
return colors.red(this.color);
},
@@ -118,6 +127,9 @@ module.exports = function (scriptRuntime, scope) {
this.color = colors.setGreen(this.color, green);
return this;
},
setGreenRelative(percentage) {
return this.setGreen(this.getGreen() * _.parseRelativePercentage(percentage));
},
getGreen() {
return colors.green(this.color);
},
@@ -138,6 +150,9 @@ module.exports = function (scriptRuntime, scope) {
this.color = colors.setBlue(this.color, blue);
return this;
},
setBlueRelative(percentage) {
return this.setBlue(this.getBlue() * _.parseRelativePercentage(percentage));
},
getBlue() {
return colors.blue(this.color);
},
@@ -267,6 +282,9 @@ module.exports = function (scriptRuntime, scope) {
let [ r, g, b ] = this.toRgb(color);
return this.toInt(this.argb(alpha, r, g, b));
},
setAlphaRelative(color, percentage) {
return this.setAlpha(color, this.getAlpha(color) * _.parseRelativePercentage(percentage));
},
removeAlpha(color) {
return this.setAlpha(color, 0);
},
@@ -293,6 +311,9 @@ module.exports = function (scriptRuntime, scope) {
let [ a, , g, b ] = this.toArgb(color);
return this.toInt(this.argb(a, red, g, b));
},
setRedRelative(color, percentage) {
return this.setRed(color, this.getRed(color) * _.parseRelativePercentage(percentage));
},
removeRed(color) {
return this.setRed(color, 0);
},
@@ -319,6 +340,9 @@ module.exports = function (scriptRuntime, scope) {
let [ a, r, , b ] = this.toArgb(color);
return this.toInt(this.argb(a, r, green, b));
},
setGreenRelative(color, percentage) {
return this.getGreen(color, this.getGreen(color) * _.parseRelativePercentage(percentage));
},
removeGreen(color) {
return this.setGreen(color, 0);
},
@@ -345,75 +369,27 @@ module.exports = function (scriptRuntime, scope) {
let [ a, r, g ] = this.toArgb(color);
return this.toInt(this.argb(a, r, g, blue));
},
setBlueRelative(color, percentage) {
return this.setBlue(color, this.getBlue(color) * _.parseRelativePercentage(percentage));
},
removeBlue(color) {
return this.setBlue(color, 0);
},
toInt(color) {
try {
return _.parseColor(typeof color === 'number' ? _.toJavaIntegerRange(color) : this.toFullHex(color));
} catch (e) {
scriptRuntime.console.error(`Passed color: ${color}`);
throw Error(e + '\n' + e.stack);
if (color instanceof _.Color) {
color = color.color;
}
return ColorUtils.toInt.apply(ColorUtils, [ color ]);
},
toHex(color, alphaOrLength) {
let [ _ignoredArg0, arg1 /* alpha | length */ ] = arguments;
if (color instanceof _.Color) {
return this.toHex.apply(this, [ color.color ].concat(Array.from(arguments).slice(1)));
}
if (color instanceof ThemeColor) {
return this.toHex.apply(this, [ color.getColorPrimary() ].concat(Array.from(arguments).slice(1)));
}
if (typeof color === 'number') {
color = rtColors.toString(_.toJavaIntegerRange(color));
if (isNullish(alphaOrLength)) {
return ColorUtils.toHex.apply(ColorUtils, [ color ]);
} else {
color = String(color);
return ColorUtils.toHex.apply(ColorUtils, [ color, alphaOrLength ]);
}
if (color.startsWith('#')) {
if (color.length === 4) {
color = color.replace(/(#)(\w)(\w)(\w)/, '$1$2$2$3$3$4$4');
}
} else {
if (color === parseInt(color).toString()) {
return this.toHex.apply(this, [ parseInt(color) ].concat(Array.from(arguments).slice(1)));
}
let colorByName = ColorTable.getColorByName(color, true);
if (colorByName !== null) {
return this.toHex.apply(this, [ colorByName.intValue() ].concat(Array.from(arguments).slice(1)));
}
}
if (!/^#[A-F\d]{3}([A-F\d]{3}([A-F\d]{2})?)?$/i.test(color)) {
throw TypeError(`Invalid color string format: ${color}`);
}
return ( /* @IIFE(toColorHex) */ () => {
if (arg1 /* alpha */ === true || arg1 /* alpha */ === 'keep' || arg1 /* length */ === 8) {
if (color.length === 7) {
color = `#FF${color.slice(1)}`;
}
return color;
}
if (arg1 /* alpha */ === false || arg1 /* alpha */ === 'none' || arg1 /* length */ === 6) {
return `#${color.slice(-6)}`;
}
if (arg1 /* length */ === 3) {
if (!/^#(?:([A-F\d]){2})?([A-F\d])\2([A-F\d])\3([A-F\d])\4$/i.test(color)) {
throw TypeError(`Can't convert color ${color} to #RGB with unexpected color format.`);
}
let [ r, g, b ] = [ color.slice(-6, -5), color.slice(-4, -3), color.slice(-2, -1) ];
return `#${r}${g}${b}`;
}
if (arg1 /* alpha */ === undefined || arg1 /* alpha */ === 'auto') {
return /^#FF([A-F\d]){6}$/i.test(color) ? `#${color.slice(3)}` : color;
}
throw TypeError('Unknown type of alpha for colors.toString()');
})().toUpperCase();
},
/**
* Color to full hex like '#BF110523'.
*/
toFullHex(color) {
return this.toHex(color, 8);
return ColorUtils.toFullHex.apply(ColorUtils, [ color ]);
},
/**
* Get hex code string of a color.
@@ -870,15 +846,12 @@ module.exports = function (scriptRuntime, scope) {
parseNumber(num, def) {
return typeof num === 'number' ? num : typeof def === 'function' ? def() : def || 0;
},
/**
* @param {number|string} color
* @returns {number}
*/
parseColor(color) {
if (typeof color === 'string') {
return rtColors.parseColor(color);
parseRelativePercentage(percentage) {
let p = Numberx.parseAny(percentage);
if (isNaN(p) || p < 0) {
throw TypeError(`Relative percentage must be in range 0..255, instead of ${percentage}`)
}
return color;
return p;
},
};

View File

@@ -11,8 +11,9 @@ let { files, util, s13n } = global;
module.exports = function (scriptRuntime, scope) {
const Log = android.util.Log;
const Level = org.apache.log4j.Level;
const ConsoleUtils = org.autojs.autojs.util.ConsoleUtils;
const LogManager = org.apache.log4j.LogManager;
const LogConfigurator = de.mindpipe.android.logging.log4j.LogConfigurator;
const ConsoleUtils = org.autojs.autojs.util.ConsoleUtils;
// noinspection JSValidateTypes
/** @type {org.autojs.autojs.core.console.GlobalConsole} */
@@ -92,8 +93,14 @@ module.exports = function (scriptRuntime, scope) {
Boolean(typeof value === 'function' ? value() : value),
message || util.getClassName(java.lang.AssertionError));
},
input(data, param) {
return eval(String(this.rawInput(data, param)));
input() {
// @Abandoned by SuperMonster003 as of May 3, 2023.
// return eval(String(this.rawInput(data, param)));
throw Error(context.getString(R.strings.error_abandoned_method, 'console.input'));
},
rawInput() {
// @Abandoned by SuperMonster003 as of May 3, 2023.
throw Error(context.getString(R.strings.error_abandoned_method, 'console.rawInput'));
},
log() {
rtConsole.log(util.format.apply(util, arguments));
@@ -247,6 +254,9 @@ module.exports = function (scriptRuntime, scope) {
configurator.setResetConfiguration(_.parseOption(config.resetConfiguration, true));
configurator.configure();
},
resetGlobalLogConfig() {
LogManager.getLoggerRepository().resetConfiguration();
},
launch() {
ConsoleUtils.launch();
},

View File

@@ -484,6 +484,11 @@ module.exports = function (runtime, scope) {
delete this.$appropriateProtect;
},
},
legacies: {
isObjectSpecies(o) {
return species.isObject(o);
},
},
ensureNonUiThread() {
if (ui.isUiThread()) {
throw Error('不能在ui线程执行阻塞操作,请在子线程或子脚本执行,或者使用setInterval循环检测当前activity和package.');
@@ -533,7 +538,7 @@ module.exports = function (runtime, scope) {
},
};
Object.assign(scope, _.extensions);
Object.assign(scope, _.extensions, _.legacies);
// Object.keys(_.extensions)
// .filter(key => !key.startsWith('$'))

View File

@@ -1,4 +1,4 @@
// noinspection JSUnusedGlobalSymbols
// noinspection JSUnusedGlobalSymbols,JSUnusedLocalSymbols,UnnecessaryLocalVariableJS
/* Overwritten protection. */
@@ -10,7 +10,8 @@ let { ui } = global;
* @return {Internal.Http}
*/
module.exports = function (scriptRuntime, scope) {
const PFile = org.autojs.pio.PFile;
const PFile = org.autojs.autojs.pio.PFile;
const Request = okhttp3.Request;
const RequestBody = okhttp3.RequestBody;
const MultipartBody = okhttp3.MultipartBody;
@@ -31,6 +32,13 @@ module.exports = function (scriptRuntime, scope) {
Http.prototype = {
constructor: Http,
__okhttp__: new MutableOkHttp(),
/**
* @example
* http.client() === http.client(); // true
*/
client() {
return this.__okhttp__.client();
},
/**
* @param {string} url
* @param {Http.RequestBuilderOptions} [options]
@@ -83,6 +91,7 @@ module.exports = function (scriptRuntime, scope) {
return body;
}
if (typeof body === 'string') {
// noinspection JSDeprecatedSymbols
return RequestBody.create(MediaType.parse(this.options.contentType), body);
}
if (typeof body === 'function') {
@@ -119,6 +128,7 @@ module.exports = function (scriptRuntime, scope) {
let file = new PFile(path);
fileName = fileName || file.getName();
mimeType = mimeType || this.parseMimeType(file.getExtension());
// noinspection JSDeprecatedSymbols
let requestBody = RequestBody.create(MediaType.parse(mimeType), file);
builder.addFormDataPart(key, fileName, requestBody);
});

View File

@@ -14,7 +14,13 @@ module.exports = function (scriptRuntime, scope) {
const BigTextStyle = androidx.core.app.NotificationCompat.BigTextStyle;
const Notification = android.app.Notification;
const NotificationManager = android.app.NotificationManager;
const NotificationManagerCompat = androidx.core.app.NotificationManagerCompat;
// @Caution by SuperMonster003 on May 6, 2023.
// ! On device running with Android 7.x,
// ! importing NotificationManagerCompat will cause java.lang.ClassNotFoundException:
// ! Didn't find class "android.app.NotificationChannel" on path: DexPathList ...
// const NotificationManagerCompat = androidx.core.app.NotificationManagerCompat;
const configDefaults = {
/**
@@ -511,7 +517,9 @@ module.exports = function (scriptRuntime, scope) {
if (!isNullish(id)) {
let niceId = Number(id);
if (!isNaN(niceId)) {
NotificationManagerCompat.from(context).cancel(niceId);
if (util.version.sdkInt >= util.versionCodes.O) {
androidx.core.app.NotificationManagerCompat.from(context).cancel(niceId);
}
}
}
},

View File

@@ -65,11 +65,11 @@ module.exports = function (scriptRuntime, scope) {
__asGlobal__(scriptRuntime.timers, methods, scope);
Object.assign(scope, {
/**
* @global
*/
/** @global */
loop() {
return scriptRuntime.console.warn('Method loop() is deprecated and has no effect.');
// @Abandoned by SuperMonster003 as of May 3, 2023.
// scriptRuntime.console.warn('Method loop() is deprecated and has no effect.');
throw Error(context.getString(R.strings.error_abandoned_method, 'global.loop'));
},
});
},

View File

@@ -12,17 +12,20 @@ let { files } = global;
module.exports = function (scriptRuntime, scope) {
const Looper = android.os.Looper;
const Runnable = java.lang.Runnable;
const Color = android.graphics.Color;
const ContextThemeWrapper = android.view.ContextThemeWrapper;
const ViewExtras = org.autojs.autojs.core.ui.ViewExtras;
const JsListView = org.autojs.autojs.core.ui.widget.JsListView;
const JsGridView = org.autojs.autojs.core.ui.widget.JsGridView;
const JsViewHelper = org.autojs.autojs.core.ui.JsViewHelper;
const ThemeColor = org.autojs.autojs.theme.ThemeColor;
const DynamicLayoutInflater = org.autojs.autojs.core.ui.inflater.DynamicLayoutInflater;
const ColorDrawable = android.graphics.drawable.ColorDrawable;
require('object-observe-lite.min').call(scope);
require('array-observe.min').call(scope);
let isAndroidLayout = null;
// noinspection JSValidateTypes
let _ = {
// @Coerce by SuperMonster003 on Nov 9, 2022.
@@ -142,6 +145,15 @@ module.exports = function (scriptRuntime, scope) {
: activity);
return layoutInflater.inflate(_.toXMLString(xml), parent || null, Boolean(isAttachedToParent));
},
useAndroidLayout(b) {
if (typeof b === 'boolean') {
isAndroidLayout = b;
} else if (b === undefined) {
isAndroidLayout = true;
} else {
isAndroidLayout = null;
}
},
run(action) {
if (this.isUiThread()) {
return action();
@@ -175,11 +187,16 @@ module.exports = function (scriptRuntime, scope) {
scriptRuntime.getUiHandler().postDelayed(_.wrapUiAction(action), delay);
}
},
layout(layout) {
layout(xml) {
_.ensureActivity();
layoutInflater.setContext(activity);
// noinspection JSCheckFunctionSignatures
this.setContentView(layoutInflater.inflate(layout, activity.window.decorView, false));
layoutInflater.setContext(activity);
// noinspection JSCheckFunctionSignatures,JSTypeOfValues
this.setContentView(layoutInflater.inflate(
typeof xml === 'xml' ? xml.toXMLString() : String(xml),
activity.window.decorView,
false,
));
},
layoutFile(path) {
this.layout(files.read(path));
@@ -200,18 +217,13 @@ module.exports = function (scriptRuntime, scope) {
},
statusBarColor(color) {
_.ensureActivity();
let colorInt = (/* @IIFE */ () => {
if (typeof color === 'string') {
if (Number(color).toString() === color) {
color = Number(color);
}
}
if (typeof color === 'number') {
return color;
}
return scriptRuntime.colors.parseColor(String(color));
})();
this.run(() => activity.window.setStatusBarColor(colorInt));
this.run(() => activity.window.setStatusBarColor(colors.toInt(color)));
},
backgroundColor(color) {
_.ensureActivity();
this.run(() => activity.window.setBackgroundDrawable(new ColorDrawable(
Color(color).setAlpha(1.0).toInt(),
)));
},
findById(id) {
return this.view ? this.findByStringId(this.view, id) : null;
@@ -235,28 +247,19 @@ module.exports = function (scriptRuntime, scope) {
*/
layoutInflaterDelegate: {
beforeConvertXml(context, xml) {
if (isAndroidLayout === true || isAndroidLayout === null && /\bxmlns:\w+="\w+:\/\/|\b(android|app):\w+=".+"/.test(xml)) {
// noinspection JSTypeOfValues
return typeof xml === 'xml' ? xml.toXMLString() : String(xml);
}
return null;
},
afterConvertXml(context, xml) {
return xml;
},
beforeInflation(context, xml, parent) {
return null;
},
afterInflation(context, result, xml, parent) {
return result;
},
beforeInflateView(context, node, parent, attachToParent) {
return null;
},
afterInflateView(context, view, node, parent, attachToParent) {
let { widget } = view;
if (widget && context.get('root') !== widget) {
widget.notifyAfterInflation(view);
}
return view;
},
beforeCreateView(context, node, viewName, parent, attrs) {
beforeCreateView(context, node, viewName, parent) {
if (uiProxy.__widgets__.hasOwnProperty(viewName)) {
let Widget = uiProxy.__widgets__[viewName];
let widget = new Widget();
@@ -267,7 +270,13 @@ module.exports = function (scriptRuntime, scope) {
}
return null;
},
afterCreateView(context, view, node, viewName, parent, attrs) {
beforeInflation(context, xml, parent) {
return null;
},
afterInflation(context, result, xml, parent) {
return result;
},
afterCreateView(context, view, node, viewName, parent) {
if (view instanceof JsListView || view instanceof JsGridView) {
_.initListView(view);
}
@@ -275,11 +284,13 @@ module.exports = function (scriptRuntime, scope) {
if (widget !== null) {
widget.view = view;
view.widget = widget;
ViewExtras.getViewAttributes(view, layoutInflater.getResourceParser()).setViewAttributeDelegate({
has: name => widget.hasAttr(name),
get: (view, name, getter) => widget.getAttr(view, name, getter),
set: (view, name, value, setter) => widget.setAttr(view, name, value, setter),
});
ViewExtras
.getViewAttributes(view, layoutInflater.getResourceParser())
.setViewAttributeDelegate({
has: name => widget.hasAttr(name),
get: (view, name, getter) => widget.getAttr(view, name, getter),
set: (view, name, value, setter) => widget.setAttr(view, name, value, setter),
});
widget.notifyViewCreated(view);
}
return view;
@@ -287,6 +298,27 @@ module.exports = function (scriptRuntime, scope) {
beforeApplyAttributes(context, view, inflater, attrs, parent) {
return false;
},
beforeApplyAttribute(context, inflater, view, ns, attrName, value, parent) {
let isDynamic = layoutInflater.isDynamicValue(value);
return isDynamic && layoutInflater.getInflateFlags() === DynamicLayoutInflater.FLAG_IGNORES_DYNAMIC_ATTRS
|| !isDynamic && layoutInflater.getInflateFlags() === DynamicLayoutInflater.FLAG_JUST_DYNAMIC_ATTRS
|| (/* @IIFE */ () => {
value = _.bind(value);
let widget = context.get('widget');
if (widget !== null && widget.hasAttr(attrName)) {
widget.setAttr(view, attrName, value, (view, attrName, value) => {
inflater.setAttr(view, ns, attrName, value, parent);
});
} else {
inflater.setAttr(view, ns, attrName, value, parent);
}
this.afterApplyAttribute(context, inflater, view, ns, attrName, value, parent);
return true;
})();
},
afterApplyAttribute(context, inflater, view, ns, attrName, value, parent) {
// Empty method body
},
afterApplyAttributes(context, view, inflater, attrs, parent) {
context.remove('widget');
},
@@ -302,26 +334,12 @@ module.exports = function (scriptRuntime, scope) {
afterApplyPendingAttributesOfChildren(context, inflater, view) {
// Empty method body
},
beforeApplyAttribute(context, inflater, view, ns, attrName, value, parent, attrs) {
let isDynamic = layoutInflater.isDynamicValue(value);
return isDynamic && layoutInflater.getInflateFlags() === DynamicLayoutInflater.FLAG_IGNORES_DYNAMIC_ATTRS
|| !isDynamic && layoutInflater.getInflateFlags() === DynamicLayoutInflater.FLAG_JUST_DYNAMIC_ATTRS
|| (/* @IIFE */ () => {
value = _.bind(value);
let widget = context.get('widget');
if (widget !== null && widget.hasAttr(attrName)) {
widget.setAttr(view, attrName, value, (view, attrName, value) => {
inflater.setAttr(view, ns, attrName, value, parent, attrs);
});
} else {
inflater.setAttr(view, ns, attrName, value, parent, attrs);
}
this.afterApplyAttribute(context, inflater, view, ns, attrName, value, parent, attrs);
return true;
})();
},
afterApplyAttribute(context, inflater, view, ns, attrName, value, parent, attrs) {
// Empty method body
afterInflateView(context, view, node, parent, attachToParent) {
let { widget } = view;
if (widget && context.get('root') !== widget) {
widget.notifyAfterInflation(view);
}
return view;
},
},
bind(value) {
@@ -333,7 +351,8 @@ module.exports = function (scriptRuntime, scope) {
if (j < 0) {
return value;
}
value = value.slice(0, i) + _.evalInContext(value.slice(i + 2, j), ctx) + value.slice(j + 2);
let evaluated = _.evalInContext(value.slice(i + 2, j), ctx);
value = value.slice(0, i) + _.attrValueConvert(evaluated) + value.slice(j + 2);
i = j + 1;
}
return value;
@@ -401,6 +420,18 @@ module.exports = function (scriptRuntime, scope) {
setLayoutInflaterDelegate() {
layoutInflater.setLayoutInflaterDelegate(this.layoutInflaterDelegate);
},
attrValueConvert(o) {
if (typeof o === 'string') {
return o;
}
if (o instanceof scope.Color) {
return o.toHex();
}
if (o instanceof ThemeColor) {
return o.getColorPrimary();
}
return o;
},
};
/**

View File

@@ -1,5 +1,9 @@
// noinspection JSUnusedGlobalSymbols
/* Overwritten protection. */
let { http } = global;
/**
* @param {org.autojs.autojs.runtime.ScriptRuntime} scriptRuntime
* @param {org.mozilla.javascript.Scriptable | global} scope
@@ -38,6 +42,9 @@ module.exports = function (scriptRuntime, scope) {
newInjectableWebClient() {
return new InjectableWebClient(Context.getCurrentContext(), scope);
},
newWebSocket(url) {
return new org.autojs.autojs.core.web.WebSocket(http.__okhttp__, url);
},
};
return Web;
@@ -46,7 +53,7 @@ module.exports = function (scriptRuntime, scope) {
/**
* @type {(keyof Internal.Web)[]}
*/
let methods = [ 'newInjectableWebView', 'newInjectableWebClient' ];
let methods = [ 'newInjectableWebView', 'newInjectableWebClient', 'newWebSocket' ];
__asGlobal__(web, methods, scope);
},
};

View File

@@ -1,3 +1,3 @@
module.exports = function(){
Object.observe&&!Array.observe&&function(t,e){"use strict";var n=t.getNotifier,r="performChange",i="_original",o="splice";var u={push:function h(t){var e=arguments,u=h[i].apply(this,e);n(this)[r](o,function(){return{index:u-e.length,addedCount:e.length,removed:[]}});return u},unshift:function d(t){var e=arguments,u=d[i].apply(this,e);n(this)[r](o,function(){return{index:0,addedCount:e.length,removed:[]}});return u},pop:function a(){var t=this.length,e=a[i].call(this);if(this.length!==t)n(this)[r](o,function(){return{index:this.length,addedCount:0,removed:[e]}},this);return e},shift:function l(){var t=this.length,e=l[i].call(this);if(this.length!==t)n(this)[r](o,function(){return{index:0,addedCount:0,removed:[e]}},this);return e},splice:function f(t,e){var u=arguments,s=f[i].apply(this,u);if(s.length||u.length>2)n(this)[r](o,function(){return{index:t,addedCount:u.length-2,removed:s}},this);return s}};for(var s in u){u[s][i]=e.prototype[s];e.prototype[s]=u[s]}e.observe=function(e,n){return t.observe(e,n,["add","update","delete",o])};e.unobserve=t.unobserve}(Object,Array);
Object.observe&&!Array.observe&&function(t,e){var n=t.getNotifier,r="performChange",i="_original",o="splice";var u={push:function h(t){var e=arguments,u=h[i].apply(this,e);n(this)[r](o,function(){return{index:u-e.length,addedCount:e.length,removed:[]}});return u},unshift:function d(t){var e=arguments,u=d[i].apply(this,e);n(this)[r](o,function(){return{index:0,addedCount:e.length,removed:[]}});return u},pop:function a(){var t=this.length,e=a[i].call(this);if(this.length!==t)n(this)[r](o,function(){return{index:this.length,addedCount:0,removed:[e]}},this);return e},shift:function l(){var t=this.length,e=l[i].call(this);if(this.length!==t)n(this)[r](o,function(){return{index:0,addedCount:0,removed:[e]}},this);return e},splice:function f(t,e){var u=arguments,s=f[i].apply(this,u);if(s.length||u.length>2)n(this)[r](o,function(){return{index:t,addedCount:u.length-2,removed:s}},this);return s}};for(var s in u){u[s][i]=e.prototype[s];e.prototype[s]=u[s]}e.observe=function(e,n){return t.observe(e,n,["add","update","delete",o])};e.unobserve=t.unobserve}(Object,Array);
}

View File

@@ -1,7 +1,5 @@
( /* @ModuleIIFE */ () => {
"use strict";
var _createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {

View File

@@ -5,8 +5,6 @@
* @see https://raw.githubusercontent.com/taylorhakes/promise-polyfill/master/dist/polyfill.js
*/
'use strict';
/**
* @type {PromiseConstructorLike}
*/

View File

@@ -0,0 +1,142 @@
'ui';
let themeColor = 'orange-800';
let materialColors = [
'amber-500',
'blue-500',
'blue-grey-500',
'brown-500',
'cyan-500',
'deep-orange-500',
'deep-purple-500',
'green-500',
'grey-500',
'indigo-500',
'light-blue-500',
'light-green-500',
'lime-500',
'orange-500',
'pink-500',
'purple-500',
'red-500',
'teal-500',
'yellow-500',
];
let storage = storages.create('todoList');
storage.clear();
ui.layout(
<frame>
<vertical>
<appbar>
<toolbar id="toolbar" title="TODO" />
</appbar>
<button id="selectAll" text="全部完成" isColored margin="6 4" />
<list id="todoList">
<card w="*" h="70" margin="10 5" cardCornerRadius="2dp"
cardElevation="1dp" foreground="?selectableItemBackground">
<horizontal gravity="center_vertical">
<view bg="{{this.color}}" h="*" w="10" />
<vertical padding="10 8" h="auto" w="0" layout_weight="1">
<text id="title" text="{{this.title}}" textColor="blue-grey-900" textSize="16sp" maxLines="1" paddingBottom="4dp" />
<text text="{{this.summary}}" textColor="blue-grey-300" textSize="14sp" maxLines="1" />
</vertical>
<checkbox id="done" marginLeft="4" marginRight="6" checked="{{this.done}}" tint="blue-grey-500,pink-300" />
</horizontal>
</card>
</list>
</vertical>
<fab id="add" w="auto" h="auto" src="@drawable/ic_add_black_48dp"
margin="16" layout_gravity="bottom|right" tint="white" backgroundTint="orange-800" />
</frame>,
);
// 从 storage 获取待办事项列表
let todoList = storage.get('items', [{
title: '写操作系统作业',
summary: '明天第 1 - 2 节',
color: 'red-500',
done: false,
}, {
title: '给 ui 模式增加若干 bug',
summary: '无限期',
color: 'orange-500',
done: false,
}, {
title: '发布 AutoJs6 v6.6.6',
summary: '2066 年 6 月',
color: 'teal-500',
done: false,
}, {
title: '完成 AutoJs6 文档撰写',
summary: '2031 年 5 月',
color: '#2196f3',
done: false,
}]);
ui.statusBarColor(themeColor);
ui['toolbar'].attr('bg', themeColor);
ui['selectAll'].attr('backgroundTint', 'pink-300');
ui['todoList'].setDataSource(todoList);
ui['selectAll'].on('click', function () {
todoList.forEach(item => {
item.done = true;
});
// 通知数据全部更新
ui['todoList'].adapter.notifyDataSetChanged();
});
ui['todoList'].on('item_bind', function (itemView, itemHolder) {
// 绑定勾选框事件
itemView.done.on('check', function (checked) {
let item = itemHolder.item;
item.done = checked;
let paint = itemView.title.paint;
// 设置或取消中划线效果
if (checked) {
paint.flags |= Paint.STRIKE_THRU_TEXT_FLAG;
} else {
paint.flags &= ~Paint.STRIKE_THRU_TEXT_FLAG;
}
itemView.title.invalidate();
});
});
ui['todoList'].on('item_click', function (item, i, itemView) {
itemView.done.checked = !itemView.done.checked;
});
ui['todoList'].on('item_long_click', function (e, item, i) {
confirm(`确定要删除 "${item.title}" 吗?`)
.then(ok => {
if (ok) {
todoList.splice(i, 1);
}
});
e.consumed = true;
});
// 当离开本界面时保存 todoList
ui.emitter.on('pause', () => storage.put('items', todoList));
ui['add'].on('click', () => {
dialogs.rawInput('请输入标题')
.then((title) => {
if (!title) {
return;
}
dialogs.rawInput('请输入期限', '明天')
.then(summary => {
todoList.push({
title: title,
summary: summary,
color: materialColors[random(0, materialColors.length - 1)],
});
});
});
});

View File

@@ -0,0 +1,34 @@
'ui';
const themeColor = Color(autojs.themeColor);
const colorRelativeRate = 1.8;
ui.layout(
<org.autojs.autojs.core.ui.widget.JsTextClock
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text_clock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:format24Hour="HH:mm"
android:textSize="36sp"/>,
);
/* 1 秒钟后应用自定义样式. */
setTimeout(() => {
ui.statusBarColor(themeColor);
ui.backgroundColor(
Color(themeColor)
.setRedRelative(colorRelativeRate)
.setGreenRelative(colorRelativeRate)
.setBlueRelative(colorRelativeRate)
.toInt(),
);
/** @type {JsTextClock} */
let textClockView = ui.text_clock;
textClockView.attr('color', themeColor.toInt());
textClockView.attr('size', '56');
textClockView.attr('layout_gravity', 'center');
textClockView.attr('format24Hour', 'HH:mm:ss');
}, 1e3);

View File

@@ -4,12 +4,12 @@ ui.layout(
<vertical padding="16">
<checkbox id="cb1" text="复选框"/>
<checkbox id="cb2" checked="true" text="勾选的复选框"/>
<radiogroup>
<radiogroup marginTop="16">
<radio text="单选框1"/>
<radio text="单选框2"/>
<radio text="单选框3"/>
</radiogroup>
<radiogroup mariginTop="16">
<radiogroup marginTop="16">
<radio text="单选框1"/>
<radio text="单选框2"/>
<radio text="勾选的单选框3" checked="true"/>

View File

@@ -0,0 +1,24 @@
'ui';
ui.layout(<vertical gravity="center">
<horizontal width="-1" gravity="center" margin="16">
<text text="滑块单色/轨道单色" size="16" marginEnd="32"/>
<switch checked="false" thumbTint="orange-800" trackTint="orange-200" marginEnd="16"></switch>
<switch checked="true" thumbTint="orange-800" trackTint="orange-200"></switch>
</horizontal>
<horizontal width="-1" gravity="center" margin="16">
<text text="滑块单色/轨道双色" size="16" marginEnd="32"/>
<switch checked="false" thumbTint="orange-800" trackTint="light-gray/orange-200" marginEnd="16"></switch>
<switch checked="true" thumbTint="orange-800" trackTint="light-gray/orange-200"></switch>
</horizontal>
<horizontal width="-1" gravity="center" margin="16">
<text text="滑块双色/轨道单色" size="16" marginEnd="32"/>
<switch checked="false" thumbTint="gray/orange-800" trackTint="light-gray" marginEnd="16"></switch>
<switch checked="true" thumbTint="gray/orange-800" trackTint="light-gray"></switch>
</horizontal>
<horizontal width="-1" gravity="center" margin="16">
<text text="滑块双色/轨道双色" size="16" marginEnd="32"/>
<switch checked="false" thumbTint="gray/orange-800" trackTint="light-gray/orange-200" marginEnd="16"></switch>
<switch checked="true" thumbTint="gray/orange-800" trackTint="light-gray/orange-200"></switch>
</horizontal>
</vertical>);

View File

@@ -13,10 +13,8 @@ const assert = (value, message) => {
message && console.verbose(`${message} passed`);
};
const SecurityException = java.lang.SecurityException;
const AtomicLong = java.util.concurrent.atomic.AtomicLong;
const JavaScriptEngine = org.autojs.autojs.engine.JavaScriptEngine;
const JsLinearLayout = org.autojs.autojs.core.ui.widget.JsLinearLayout;
// @Test Rhino basic

View File

@@ -16,7 +16,6 @@ const assert = (value, message) => {
};
const JavaScriptEngine = org.autojs.autojs.engine.JavaScriptEngine;
const JsLinearLayout = org.autojs.autojs.core.ui.widget.JsLinearLayout;
// @Test auto

View File

@@ -0,0 +1,46 @@
// noinspection HtmlUnknownAttribute
let win = floaty.window(
<vertical bg="white" padding="24">
<input id="input" hint="请输入任意内容" textSize="16sp" margin="0 12"/>
<view h="1" w="180" layout_weight="1"/>
<button id="ok" text="确定" bgTint="light-blue-600" color="white" layout_gravity="bottom"/>
</vertical>,
);
win.exitOnClose();
/** @type {JsButton} */
let buttonView = win['ok'];
/** @type {JsEditText} */
let inputView = win['input'];
toast(`长按 "${buttonView.attr('text')}" 键可调整浮动窗口样式`, 'long');
inputView.on('click', () => {
win.requestFocus();
});
inputView.on('key_up', function (keyCode, event) {
if (keyCode === keys.back) {
win.disableFocus();
event.consumed = true;
}
});
buttonView.on('click', () => {
let text = inputView.text();
text
? toast('输入内容: ' + text)
: toast('未输入任何内容');
win.disableFocus();
});
buttonView.on('long_click', (event) => {
toast.dismissAll();
win.setAdjustEnabled(!win.isAdjustEnabled());
event.consumed = true;
});
setInterval(() => null, 1000);

View File

@@ -20,16 +20,16 @@ import org.autojs.autojs.runtime.api.AppUtils.Companion.ActivityShortForm
import org.autojs.autojs.runtime.api.AppUtils.Companion.BroadcastShortForm
import org.autojs.autojs.runtime.exception.ScriptException
import org.autojs.autojs.runtime.exception.ScriptInterruptedException
import org.autojs.autojs.ui.doc.DocumentationActivity_
import org.autojs.autojs.ui.doc.DocumentationActivity
import org.autojs.autojs.ui.floating.FloatyWindowManger
import org.autojs.autojs.ui.floating.FullScreenFloatyWindow
import org.autojs.autojs.ui.floating.layoutinspector.LayoutBoundsFloatyWindow
import org.autojs.autojs.ui.floating.layoutinspector.LayoutHierarchyFloatyWindow
import org.autojs.autojs.ui.log.LogActivity_
import org.autojs.autojs.ui.main.MainActivity_
import org.autojs.autojs.ui.project.BuildActivity_
import org.autojs.autojs.ui.settings.AboutActivity_
import org.autojs.autojs.ui.settings.SettingsActivity_
import org.autojs.autojs.ui.log.LogActivity
import org.autojs.autojs.ui.main.MainActivity
import org.autojs.autojs.ui.project.BuildActivity
import org.autojs.autojs.ui.settings.AboutActivity
import org.autojs.autojs.ui.settings.PreferencesActivity
import org.autojs.autojs6.R
import java.util.concurrent.Executors
@@ -141,23 +141,23 @@ class AutoJs private constructor(private val appContext: Application) : Abstract
/* Activities. */
putProperty(ActivityShortForm.SETTINGS.fullName, SettingsActivity_::class.java)
putProperty(ActivityShortForm.PREFERENCES.fullName, SettingsActivity_::class.java)
putProperty(ActivityShortForm.PREF.fullName, SettingsActivity_::class.java)
putProperty(ActivityShortForm.SETTINGS.fullName, PreferencesActivity::class.java)
putProperty(ActivityShortForm.PREFERENCES.fullName, PreferencesActivity::class.java)
putProperty(ActivityShortForm.PREF.fullName, PreferencesActivity::class.java)
putProperty(ActivityShortForm.CONSOLE.fullName, LogActivity_::class.java)
putProperty(ActivityShortForm.LOG.fullName, LogActivity_::class.java)
putProperty(ActivityShortForm.CONSOLE.fullName, LogActivity::class.java)
putProperty(ActivityShortForm.LOG.fullName, LogActivity::class.java)
putProperty(ActivityShortForm.HOMEPAGE.fullName, MainActivity_::class.java)
putProperty(ActivityShortForm.HOME.fullName, MainActivity_::class.java)
putProperty(ActivityShortForm.HOMEPAGE.fullName, MainActivity::class.java)
putProperty(ActivityShortForm.HOME.fullName, MainActivity::class.java)
putProperty(ActivityShortForm.ABOUT.fullName, AboutActivity_::class.java)
putProperty(ActivityShortForm.ABOUT.fullName, AboutActivity::class.java)
putProperty(ActivityShortForm.BUILD.fullName, BuildActivity_::class.java)
putProperty(ActivityShortForm.BUILD.fullName, BuildActivity::class.java)
putProperty(ActivityShortForm.DOCUMENTATION.fullName, DocumentationActivity_::class.java)
putProperty(ActivityShortForm.DOC.fullName, DocumentationActivity_::class.java)
putProperty(ActivityShortForm.DOCS.fullName, DocumentationActivity_::class.java)
putProperty(ActivityShortForm.DOCUMENTATION.fullName, DocumentationActivity::class.java)
putProperty(ActivityShortForm.DOC.fullName, DocumentationActivity::class.java)
putProperty(ActivityShortForm.DOCS.fullName, DocumentationActivity::class.java)
/* Broadcasts. */

View File

@@ -6,36 +6,30 @@ import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.autojs.autojs6.R;
import org.autojs.autojs6.databinding.OperationDialogItemBinding;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Created by Stardust on 2017/6/26.
*/
public class CircularMenuOperationDialogBuilder extends AppLevelThemeDialogBuilder {
private final RecyclerView mOperations;
private final ArrayList<Integer> mIds = new ArrayList<>();
private final ArrayList<View.OnClickListener> mOnClickListeners = new ArrayList<>();
private final ArrayList<Integer> mIcons = new ArrayList<>();
private final ArrayList<String> mTexts = new ArrayList<>();
private Object mOnItemClickTarget;
public CircularMenuOperationDialogBuilder(@NonNull Context context) {
super(context);
mOperations = new RecyclerView(context);
mOperations.setLayoutManager(new LinearLayoutManager(context));
mOperations.setAdapter(new RecyclerView.Adapter<ViewHolder>() {
RecyclerView operations = new RecyclerView(context);
operations.setLayoutManager(new LinearLayoutManager(context));
operations.setAdapter(new RecyclerView.Adapter<ViewHolder>() {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -44,51 +38,48 @@ public class CircularMenuOperationDialogBuilder extends AppLevelThemeDialogBuild
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.itemView.setId(mIds.get(position));
holder.text.setText(mTexts.get(position));
holder.text.setTextColor(context.getColor(R.color.day_night));
holder.icon.setImageResource(mIcons.get(position));
holder.icon.setImageTintList(ColorStateList.valueOf(context.getColor(R.color.day_night)));
if (mOnItemClickTarget != null) {
//// TODO: 2017/6/26 效率
ButterKnife.bind(mOnItemClickTarget, holder.itemView);
}
holder.itemView.setOnClickListener(mOnClickListeners.get(position));
holder.binding.text.setText(mTexts.get(position));
holder.binding.text.setTextColor(context.getColor(R.color.day_night));
holder.binding.icon.setImageResource(mIcons.get(position));
holder.binding.icon.setImageTintList(ColorStateList.valueOf(context.getColor(R.color.day_night)));
}
@Override
public int getItemCount() {
return mIds.size();
return mOnClickListeners.size();
}
});
customView(mOperations, false);
customView(operations, false);
}
public CircularMenuOperationDialogBuilder item(int id, int iconRes, int textRes) {
return item(id, iconRes, getContext().getString(textRes));
public CircularMenuOperationDialogBuilder item(int iconRes, int textRes) {
return item(iconRes, textRes, null);
}
public CircularMenuOperationDialogBuilder item(int id, int iconRes, String text) {
mIds.add(id);
public CircularMenuOperationDialogBuilder item(int iconRes, int textRes, View.OnClickListener l) {
return item(iconRes, getContext().getString(textRes), l);
}
public CircularMenuOperationDialogBuilder item(int iconRes, String text) {
return item(iconRes, text, null);
}
public CircularMenuOperationDialogBuilder item(int iconRes, String text, View.OnClickListener l) {
mOnClickListeners.add(l);
mIcons.add(iconRes);
mTexts.add(text);
return this;
}
public CircularMenuOperationDialogBuilder bindItemClick(Object target) {
mOnItemClickTarget = target;
return this;
}
static class ViewHolder extends RecyclerView.ViewHolder {
class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.icon)
ImageView icon;
@BindView(R.id.text)
TextView text;
@NonNull
public final OperationDialogItemBinding binding;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
binding = OperationDialogItemBinding.bind(itemView);
}
}

View File

@@ -14,19 +14,40 @@ class JsonSocketClientTool(context: Context) : AbstractJsonSocketTool(context) {
override val isConnected
get() = devPlugin.isJsonSocketClientConnected
private var isNormallyClosed
get() = devPlugin.isClientSocketNormallyClosed
set(state) {
devPlugin.isClientSocketNormallyClosed = state
}
override val isInMainThread = true
override fun connect() = inputRemoteHost()
override fun connect() {
inputRemoteHost(isAutoConnect = !isNormallyClosed)
}
override fun disconnect() = devPlugin.disconnectJsonSocketClient()
internal fun connectIfNotNormallyClosed() {
if (!isNormallyClosed) connect()
}
override fun disconnect() {
devPlugin.disconnectJsonSocketClient()
isNormallyClosed = true
}
override fun dispose() {
stateDisposable?.dispose()
}
@SuppressLint("CheckResult")
private fun inputRemoteHost() {
private fun inputRemoteHost(isAutoConnect: Boolean) {
val host = Pref.getServerAddress()
if (isAutoConnect) {
devPlugin
.connectToRemoteServer(host, true)
.subscribe(Observers.emptyConsumer(), Observers.emptyConsumer())
return
}
MaterialDialog.Builder(context)
.title(R.string.text_pc_server_address)
.input(context.getString(R.string.text_pc_server_address), host) { _, input ->

View File

@@ -9,14 +9,28 @@ class JsonSocketServerTool(context: Context) : AbstractJsonSocketTool(context) {
override val isConnected
get() = devPlugin.isServerSocketConnected
private var isNormallyClosed
get() = devPlugin.isServerSocketNormallyClosed
set(state) {
devPlugin.isServerSocketNormallyClosed = state
}
@SuppressLint("CheckResult")
override fun connect() {
devPlugin
.enableLocalServer()
.subscribe(Observers.emptyConsumer(), onConnectionException)
isNormallyClosed = false
}
override fun disconnect() = devPlugin.disconnectJsonSocketServer()
internal fun connectIfNotNormallyClosed() {
if (!isNormallyClosed) connect()
}
override fun disconnect() {
devPlugin.disconnectJsonSocketServer()
isNormallyClosed = true
}
override fun dispose() {
stateDisposable?.dispose()

View File

@@ -54,9 +54,6 @@ import org.mozilla.javascript.BaseFunction
import org.mozilla.javascript.NativeArray
import org.mozilla.javascript.NativeJavaMethod
import org.mozilla.javascript.regexp.NativeRegExp
import java.lang.Boolean.parseBoolean
import java.lang.Float.parseFloat
import java.lang.Integer.parseInt
import java.math.BigInteger
/**
@@ -993,43 +990,43 @@ open class UiSelector : UiObjectActions {
doubleTypes.containsKey(key) -> when (val types = doubleTypes.getValue(key)) {
cDouble -> tmp::class.java
.getMethod(key, cDouble)
.invoke(tmp, *value.map { parseFloat(it.toString()).toDouble() }.toTypedArray())
.invoke(tmp, *value.map { it.toString().toDouble() }.toTypedArray())
is Array<*> -> when ( /* isOverloaded */ types.any { it is Array<*> }) {
true -> when (value.size) {
1 -> types.find { it == cDouble }?.let {
tmp::class.java
.getMethod(key, cDouble)
.invoke(tmp, parseFloat(value[0].toString()).toDouble())
} ?: throw Error()
.invoke(tmp, value[0].toString().toDouble())
} ?: throw Exception("UiSelector: $key($value)")
2 -> types.find { it is Array<*> }?.let { type: Any ->
val t = type as Array<*>
when {
t[0] == cDouble && t[1] == cDouble -> tmp::class.java
.getMethod(key, cDouble, cDouble)
.invoke(tmp, *value.map { parseFloat(it.toString()).toDouble() }.toTypedArray())
.invoke(tmp, *value.map { it.toString().toDouble() }.toTypedArray())
t[0] == cBoolean && t[1] == cDouble -> tmp::class.java
.getMethod(key, cBoolean, cDouble)
.invoke(tmp, *value.mapIndexed { index, any ->
when (index) {
0 -> parseBoolean(any.toString())
1 -> parseFloat(any.toString()).toDouble()
else -> throw Error()
0 -> any.toString().toBoolean()
1 -> any.toString().toDouble()
else -> throw Exception("UiSelector: $key($value)")
}
}.toTypedArray())
else -> throw Error()
else -> throw Exception("UiSelector: $key($value)")
}
} ?: throw Error()
else -> throw Error()
} ?: throw Exception("UiSelector: $key($value)")
else -> throw Exception("UiSelector: $key($value)")
}
else -> @Suppress("UNCHECKED_CAST") tmp::class.java
.getMethod(key, *(types as Array<out Class<*>>))
.invoke(tmp, *value.map { parseFloat(it.toString()).toDouble() }.toTypedArray())
.invoke(tmp, *value.map { it.toString().toDouble() }.toTypedArray())
}
else -> throw Error()
else -> throw Exception("UiSelector: $key($value)")
}
intTypes.contains(key) -> tmp::class.java
.getMethod(key, cInt)
.invoke(tmp, parseInt(value[0].toString()))
.invoke(tmp, value[0].toString().toInt())
stringTypes.contains(key) -> tmp::class.java
.getMethod(key, cString)
.invoke(tmp, value[0])
@@ -1040,7 +1037,7 @@ open class UiSelector : UiObjectActions {
is String -> tmp::class.java
.getMethod(key, cString)
.invoke(tmp, value[0])
else -> throw Error()
else -> throw Exception("UiSelector: $key($value)")
}
appTypes.contains(key) -> when (value[0]) {
is App -> tmp::class.java
@@ -1049,7 +1046,7 @@ open class UiSelector : UiObjectActions {
is String -> tmp::class.java
.getMethod(key, cString)
.invoke(tmp, value[0])
else -> throw Error()
else -> throw Exception("UiSelector: $key($value)")
}
nullableBoolTypes.contains(key) -> when (value.size) {
0 -> tmp::class.java
@@ -1057,12 +1054,12 @@ open class UiSelector : UiObjectActions {
.invoke(tmp)
1 -> tmp::class.java
.getMethod(key, cBoolean)
.invoke(tmp, parseBoolean(value[0].toString()))
else -> throw Error()
.invoke(tmp, value[0].toString().toBoolean())
else -> throw Exception("UiSelector: $key($value)")
}
else -> throw Error()
else -> throw Exception("UiSelector: $key($value)")
}
else -> throw Error()
else -> throw Exception("UiSelector: $key($value)")
}
}
} catch (e: NoSuchMethodException) {

View File

@@ -4,6 +4,7 @@ import android.content.Context
import android.content.ContextWrapper
import android.content.res.ColorStateList
import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
@@ -17,12 +18,17 @@ import org.autojs.autojs.ui.enhancedfloaty.ResizableExpandableFloaty.AbstractRes
import org.autojs.autojs.ui.enhancedfloaty.ResizableExpandableFloatyWindow
import org.autojs.autojs.util.ViewUtils.setViewMeasure
import org.autojs.autojs6.R
import org.autojs.autojs6.databinding.FloatingConsoleExpandBinding
import org.autojs.autojs6.databinding.FloatingWindowCollapseBinding
/**
* Created by Stardust on 2017/4/20.
*/
class ConsoleFloaty(private val mConsole: ConsoleImpl) : AbstractResizableExpandableFloaty() {
private lateinit var expandedBinding: FloatingConsoleExpandBinding
private lateinit var collapsedBinding: FloatingWindowCollapseBinding
private var mContextWrapper: ContextWrapper? = null
private var mResizer: View? = null
private var mMoveCursor: View? = null
@@ -58,7 +64,7 @@ class ConsoleFloaty(private val mConsole: ConsoleImpl) : AbstractResizableExpand
override fun inflateCollapsedView(service: FloatyService, window: ResizableExpandableFloatyWindow): View {
ensureContextWrapper(service)
return View.inflate(mContextWrapper, R.layout.floating_window_collapse, null)
return FloatingWindowCollapseBinding.inflate(LayoutInflater.from(mContextWrapper)).also { collapsedBinding = it }.root
}
private fun ensureContextWrapper(context: Context) {
@@ -67,9 +73,9 @@ class ConsoleFloaty(private val mConsole: ConsoleImpl) : AbstractResizableExpand
override fun inflateExpandedView(service: FloatyService, window: ResizableExpandableFloatyWindow): View {
ensureContextWrapper(service)
return View.inflate(mContextWrapper, R.layout.floating_console_expand, null).also { view ->
return FloatingConsoleExpandBinding.inflate(LayoutInflater.from(mContextWrapper)).also { expandedBinding = it }.root.also { view ->
expandedView = view
setUpConsole(view, window)
setUpConsole(window)
setInitialMeasure(service)
}
}
@@ -94,20 +100,20 @@ class ConsoleFloaty(private val mConsole: ConsoleImpl) : AbstractResizableExpand
}
}
private fun initConsoleTitleBar(view: View, window: ResizableExpandableFloatyWindow) {
mTitleView = view.findViewById<TextView?>(R.id.title)?.apply {
private fun initConsoleTitleBar(window: ResizableExpandableFloatyWindow) {
mTitleView = expandedBinding.title.apply {
text = mTitleText
}
titleBarView = view.findViewById<LinearLayout?>(R.id.title_bar).apply {
titleBarView = expandedBinding.titleBar.apply {
mTitleBackgroundColor?.let { setBackgroundColor(it) }
mTitleBackgroundAlpha?.let { background.alpha = it }
}
view.findViewById<ImageView>(R.id.close).let {
expandedBinding.close.let {
it.setOnClickListener { mConsole.hide() }
mtTitleIconsTint?.let { color -> it.imageTintList = ColorStateList.valueOf(color) }
mCloseButton = it
}
view.findViewById<ImageView>(R.id.move_or_resize).let {
expandedBinding.moveOrResize.let {
it.setOnClickListener {
mMoveCursor?.run {
if (visibility == View.VISIBLE) {
@@ -122,27 +128,27 @@ class ConsoleFloaty(private val mConsole: ConsoleImpl) : AbstractResizableExpand
mtTitleIconsTint?.let { color -> it.imageTintList = ColorStateList.valueOf(color) }
mControllingButton = it
}
view.findViewById<ImageView>(R.id.minimize).let {
expandedBinding.minimize.let {
it.setOnClickListener { window.collapse() }
mtTitleIconsTint?.let { color -> it.imageTintList = ColorStateList.valueOf(color) }
mMinimizeButton = it
}
}
private fun setUpConsole(view: View, window: ResizableExpandableFloatyWindow) {
view.findViewById<FloatingConsoleView>(R.id.console).apply {
private fun setUpConsole(window: ResizableExpandableFloatyWindow) {
expandedBinding.console.apply {
setConsole(mConsole)
setWindow(window)
}
initConsoleTitleBar(view, window)
initConsoleTitleBar(window)
}
override fun getResizerView(expandedView: View): View? {
return expandedView.findViewById<View?>(R.id.resizer).also { mResizer = it }
override fun getResizerView(expandedView: View): View {
return expandedBinding.resizer.also { mResizer = it }
}
override fun getMoveCursorView(expandedView: View): View? {
return expandedView.findViewById<View?>(R.id.move_cursor).also { mMoveCursor = it }
override fun getMoveCursorView(expandedView: View): View {
return expandedBinding.moveCursor.also { mMoveCursor = it }
}
fun setTitle(title: CharSequence?) {

View File

@@ -35,7 +35,7 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
private val mDefaultSafeDelay: Long = 360
private var mLogListener: WeakReference<LogListener?>? = null
private var mLogListeners = ArrayList<WeakReference<LogListener?>>()
private var mConsoleView: WeakReference<ConsoleView?>? = null
@get:Synchronized
@@ -67,7 +67,6 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
init {
synchronized(mLockWindowCreated) {
startFloatyService()
mConsoleFloaty = ConsoleFloaty(this)
mFloatyWindow = ResizableExpandableFloatyWindow(mConsoleFloaty)
mLockWindowCreated.notify()
@@ -86,13 +85,13 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
fun setConsoleView(consoleView: ConsoleView?) {
consoleView.let {
mConsoleView = WeakReference(it)
setLogListener(it)
addLogListener(it)
}
synchronized(mLockConsoleView) { mLockConsoleView.notify() }
}
private fun setLogListener(logListener: LogListener?) {
mLogListener = WeakReference(logListener)
private fun addLogListener(logListener: LogListener?) {
mLogListeners.add(WeakReference(logListener))
}
fun printAllStackTrace(t: Throwable?) {
@@ -104,7 +103,7 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
override fun println(level: Int, charSequence: CharSequence): String? {
val logEntry = LogEntry(mIdCounter.getAndIncrement(), level, charSequence)
synchronized(logEntries) { logEntries.add(logEntry) }
mLogListener?.get()?.onNewLog(logEntry)
mLogListeners.forEach { it.get()?.onNewLog(logEntry) }
return null
}
@@ -115,7 +114,7 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
@ScriptInterface
override fun clear() {
synchronized(logEntries) { logEntries.clear() }
mLogListener?.get()?.onLogClear()
mLogListeners.forEach { it.get()?.onLogClear() }
}
override fun show() = show(false)

View File

@@ -51,6 +51,7 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener
private final float mMinTextSize = 8.0f;
private final float mMaxTextSize = 56.0f;
private boolean mIsPinchToZoomEnabled;
public ConsoleView(Context context) {
super(context);
@@ -84,6 +85,9 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener
mLogListRecyclerView.setAdapter(new Adapter());
mLogListRecyclerView.setOnTouchListener((v, event) -> {
if (!mIsPinchToZoomEnabled) {
return super.onTouchEvent(event);
}
mScaleGestureDetector.onTouchEvent(event);
return !mScaleGestureDetector.isInProgress() && super.onTouchEvent(event);
});
@@ -147,6 +151,48 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener
}
}
public void setVerboseTextColor(int color) {
Adapter adapter = (Adapter) mLogListRecyclerView.getAdapter();
if (adapter != null) {
adapter.setVerboseTextColor(color);
}
}
public void setDebugTextColor(int color) {
Adapter adapter = (Adapter) mLogListRecyclerView.getAdapter();
if (adapter != null) {
adapter.setDebugTextColor(color);
}
}
public void setInfoTextColor(int color) {
Adapter adapter = (Adapter) mLogListRecyclerView.getAdapter();
if (adapter != null) {
adapter.setInfoTextColor(color);
}
}
public void setWarnTextColor(int color) {
Adapter adapter = (Adapter) mLogListRecyclerView.getAdapter();
if (adapter != null) {
adapter.setWarnTextColor(color);
}
}
public void setErrorTextColor(int color) {
Adapter adapter = (Adapter) mLogListRecyclerView.getAdapter();
if (adapter != null) {
adapter.setErrorTextColor(color);
}
}
public void setAssertTextColor(int color) {
Adapter adapter = (Adapter) mLogListRecyclerView.getAdapter();
if (adapter != null) {
adapter.setAssertTextColor(color);
}
}
public void setPinchToZoomEnabled(boolean enabled) {
mIsPinchToZoomEnabled = enabled;
}
protected Map<Integer, Integer> getLogLevelMap() {
return new MapBuilder<Integer, Integer>()
.put(Log.VERBOSE, R.color.console_view_verbose)
@@ -331,6 +377,36 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener
}
}
public void setVerboseTextColor(int color) {
mColors.replace(Log.VERBOSE, color);
notifyDataSetChanged();
}
public void setDebugTextColor(int color) {
mColors.replace(Log.DEBUG, color);
notifyDataSetChanged();
}
public void setInfoTextColor(int color) {
mColors.replace(Log.INFO, color);
notifyDataSetChanged();
}
public void setWarnTextColor(int color) {
mColors.replace(Log.WARN, color);
notifyDataSetChanged();
}
public void setErrorTextColor(int color) {
mColors.replace(Log.ERROR, color);
notifyDataSetChanged();
}
public void setAssertTextColor(int color) {
mColors.replace(Log.ASSERT, color);
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return mLogEntries.size();

View File

@@ -14,6 +14,10 @@ class FloatingConsoleView : ConsoleView {
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
init {
setPinchToZoomEnabled(true)
}
override fun getLogLevelMap(): MutableMap<Int, Int> = MapBuilder<Int, Int>()
.put(Log.VERBOSE,R.color.floating_console_view_verbose)
.put(Log.DEBUG,R.color.floating_console_view_debug)

View File

@@ -0,0 +1,19 @@
package org.autojs.autojs.core.console
import android.content.Context
import android.util.AttributeSet
import org.autojs.autojs.AutoJs
class JsConsoleView : ConsoleView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
init {
setConsole(AutoJs.instance.globalConsole)
}
}

View File

@@ -3,20 +3,26 @@ package org.autojs.autojs.core.floaty
import android.content.Context
import android.graphics.PixelFormat
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.FrameLayout
import android.view.WindowManager.LayoutParams
import android.widget.ImageView
import org.autojs.autojs.concurrent.VolatileDispose
import org.autojs.autojs.core.ui.inflater.Exceptions
import org.autojs.autojs.ui.enhancedfloaty.FloatyService
import org.autojs.autojs.ui.enhancedfloaty.FloatyWindow
import org.autojs.autojs.ui.enhancedfloaty.ResizableFloaty
import org.autojs.autojs.ui.enhancedfloaty.ResizableFloatyWindow
import org.autojs.autojs.ui.enhancedfloaty.WindowBridge
import org.autojs.autojs.ui.enhancedfloaty.WindowBridge.DefaultImpl
import org.autojs.autojs.ui.enhancedfloaty.gesture.DragGesture
import org.autojs.autojs.ui.enhancedfloaty.gesture.ResizeGesture
import org.autojs.autojs.ui.enhancedfloaty.util.WindowTypeCompat
import org.autojs.autojs.concurrent.VolatileDispose
import org.autojs.autojs.core.ui.inflater.inflaters.Exceptions
import org.autojs.autojs6.R
import org.autojs.autojs6.databinding.FloatyWindowBinding
import org.autojs.autojs6.databinding.RawWindowBinding
/**
@@ -30,47 +36,45 @@ class BaseResizableFloatyWindow(context: Context, viewSupplier: ViewSupplier) :
fun inflate(context: Context?, parent: ViewGroup?): View
}
private var rawWindowBinding: RawWindowBinding
private val mInflateException = VolatileDispose<RuntimeException>()
private var mCloseButton: View
private var mOffset = context.resources.getDimensionPixelSize(R.dimen.floaty_window_offset)
private val mFloaty = MyFloaty(context, viewSupplier)
val rootView = mFloaty.rootView
private var mMoveCursor = mFloaty.getMoveCursorView(rootView)!!
private var mResizer = mFloaty.getResizerView(rootView)!!
private var mCloseButton = mFloaty.getCloseButtonView(rootView)
private var mMoveCursor = mFloaty.getMoveCursorView(rootView)
private var mResizer = mFloaty.getResizerView(rootView)
// @Reference to aiselp (https://github.com/aiselp) on Mar 27, 2023.
// ! https://github.com/kkevsekk1/AutoX/pull/529/commits/782f1c3c12dee64d9b1ad70aba462afaf60313a4#diff-b8e544bc658140f04a7fa7171cc938032f18c92495ca25bb08e2d2eb546b70f5
init {
val layoutParams = createWindowLayoutParams()
val windowView = View.inflate(context, R.layout.raw_window, null as ViewGroup?) as ViewGroup
rawWindowBinding = RawWindowBinding.inflate(LayoutInflater.from(context))
val windowView = rawWindowBinding.root
val params = ViewGroup.LayoutParams(-2, -2)
windowView.addView(rootView, params)
windowView.isFocusableInTouchMode = true
mCloseButton = windowView.findViewById(R.id.close)
super.setWindowLayoutParams(layoutParams)
super.setWindowView(windowView)
super.setWindowManager(context.getSystemService(FloatyService.WINDOW_SERVICE) as WindowManager)
super.setWindowBridge(super.onCreateWindowBridge(layoutParams))
}
@Suppress("DEPRECATION")
private fun createWindowLayoutParams() = WindowManager
.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowTypeCompat.getWindowType(),
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
or WindowManager.LayoutParams.FLAG_FULLSCREEN
or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
or WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
PixelFormat.TRANSLUCENT
).apply { gravity = Gravity.TOP or Gravity.START }
private fun createWindowLayoutParams() = LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT,
WindowTypeCompat.getPhoneWindowType(),
LayoutParams.FLAG_LAYOUT_NO_LIMITS or LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
).apply { gravity = Gravity.TOP or Gravity.START }
// fun waitForCreation(): RuntimeException = mInflateException.blockedGetOrThrow(ScriptInterruptedException::class.java)
override fun onCreateWindowBridge(params: WindowManager.LayoutParams): WindowBridge {
override fun onCreateWindowBridge(params: LayoutParams): WindowBridge {
return object : DefaultImpl(params, windowManager, windowView) {
override fun getX() = super.getX() + mOffset
@@ -80,7 +84,7 @@ class BaseResizableFloatyWindow(context: Context, viewSupplier: ViewSupplier) :
}
}
override fun onCreateWindowLayoutParams(): WindowManager.LayoutParams {
override fun onCreateWindowLayoutParams(): LayoutParams {
return super.getWindowLayoutParams()
}
@@ -94,6 +98,24 @@ class BaseResizableFloatyWindow(context: Context, viewSupplier: ViewSupplier) :
mInflateException.setAndNotify(Exceptions.NO_EXCEPTION)
}
override fun onViewCreated(view: View?) {
super.onViewCreated(view)
initGesture()
}
private fun initGesture() {
enableResize()
enableMove()
}
private fun enableResize() {
ResizeGesture.enableResize(mResizer, rootView, windowBridge)
}
private fun enableMove() {
DragGesture(windowBridge, mMoveCursor).apply { pressedAlpha = 1.0f }
}
fun setOnCloseButtonClickListener(listener: View.OnClickListener?) {
mCloseButton.setOnClickListener(listener)
}
@@ -115,34 +137,37 @@ class BaseResizableFloatyWindow(context: Context, viewSupplier: ViewSupplier) :
override fun onCreateView(floatyService: FloatyService?): View = super.getWindowView()
fun disableWindowFocus() {
val windowLayoutParams = windowLayoutParams
windowLayoutParams.flags = windowLayoutParams.flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
updateWindowLayoutParams(windowLayoutParams)
windowLayoutParams.apply {
flags = flags or LayoutParams.FLAG_NOT_FOCUSABLE
updateWindowLayoutParams(this)
}
}
fun requestWindowFocus() {
val windowLayoutParams = windowLayoutParams
windowLayoutParams.flags = windowLayoutParams.flags and WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE.inv()
updateWindowLayoutParams(windowLayoutParams)
windowLayoutParams.apply {
flags = flags and LayoutParams.FLAG_NOT_FOCUSABLE.inv()
updateWindowLayoutParams(this)
}
windowView.requestLayout()
}
private class MyFloaty(context: Context, contentViewSupplier: ViewSupplier) : ResizableFloaty {
val rootView: View = View.inflate(context, R.layout.floaty_window, null)
val container: FrameLayout = rootView.findViewById(R.id.container)
private val floatyWindowBinding = FloatyWindowBinding.inflate(LayoutInflater.from(context))
val rootView = floatyWindowBinding.root
init {
contentViewSupplier.inflate(context, container)
contentViewSupplier.inflate(context, floatyWindowBinding.container)
}
override fun inflateView(floatyService: FloatyService, resizableFloatyWindow: ResizableFloatyWindow) = rootView
override fun getResizerView(view: View): View? {
return view.findViewById(R.id.resizer)
}
override fun getResizerView(view: View): ImageView = floatyWindowBinding.resizer
override fun getMoveCursorView(view: View): ImageView = floatyWindowBinding.moveCursor
override fun getCloseButtonView(view: View): ImageView = floatyWindowBinding.close
override fun getMoveCursorView(view: View): View? {
return view.findViewById(R.id.move_cursor)
}
}
}

View File

@@ -11,7 +11,7 @@ import org.autojs.autojs.ui.enhancedfloaty.FloatyService
import org.autojs.autojs.ui.enhancedfloaty.FloatyWindow
import org.autojs.autojs.ui.enhancedfloaty.util.WindowTypeCompat
import org.autojs.autojs.concurrent.VolatileDispose
import org.autojs.autojs.core.ui.inflater.inflaters.Exceptions
import org.autojs.autojs.core.ui.inflater.Exceptions
import org.autojs.autojs6.R
/**
@@ -57,39 +57,37 @@ class RawWindow(rawFloaty: RawFloaty, context: Context) : FloatyWindow() {
// fun waitForCreation(): RuntimeException = mInflateException.blockedGetOrThrow(ScriptInterruptedException::class.java)
@Suppress("DEPRECATION")
private fun createWindowLayoutParams() = LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT,
WindowTypeCompat.getWindowType(),
LayoutParams.FLAG_LAYOUT_NO_LIMITS
or LayoutParams.FLAG_NOT_TOUCH_MODAL
or LayoutParams.FLAG_FULLSCREEN
or LayoutParams.FLAG_NOT_FOCUSABLE
or LayoutParams.FLAG_TRANSLUCENT_STATUS,
LayoutParams.FLAG_LAYOUT_NO_LIMITS or LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
).apply { gravity = Gravity.TOP or Gravity.START }
fun disableWindowFocus() {
val windowLayoutParams = windowLayoutParams
windowLayoutParams.flags = windowLayoutParams.flags or LayoutParams.FLAG_NOT_FOCUSABLE
updateWindowLayoutParams(windowLayoutParams)
windowLayoutParams.apply {
flags = flags or LayoutParams.FLAG_NOT_FOCUSABLE
updateWindowLayoutParams(this)
}
}
fun requestWindowFocus() {
val windowLayoutParams = windowLayoutParams
windowLayoutParams.flags = windowLayoutParams.flags and LayoutParams.FLAG_NOT_FOCUSABLE.inv()
updateWindowLayoutParams(windowLayoutParams)
windowLayoutParams.apply {
flags = flags and LayoutParams.FLAG_NOT_FOCUSABLE.inv()
updateWindowLayoutParams(this)
}
windowView.requestLayout()
}
fun setTouchable(touchable: Boolean) {
val windowLayoutParams = windowLayoutParams
if (touchable) {
windowLayoutParams.flags = windowLayoutParams.flags and LayoutParams.FLAG_NOT_TOUCHABLE.inv()
} else {
windowLayoutParams.flags = windowLayoutParams.flags or LayoutParams.FLAG_NOT_TOUCHABLE
windowLayoutParams.apply {
flags = if (touchable) {
flags and LayoutParams.FLAG_NOT_TOUCHABLE.inv()
} else {
flags or LayoutParams.FLAG_NOT_TOUCHABLE
}
updateWindowLayoutParams(this)
}
updateWindowLayoutParams(windowLayoutParams)
}
}

View File

@@ -5,11 +5,12 @@ import android.content.Context
import android.graphics.Canvas
import android.graphics.SurfaceTexture
import android.os.SystemClock
import android.util.AttributeSet
import android.util.Log
import android.view.TextureView
import android.view.View
import org.autojs.autojs.AutoJs
import org.autojs.autojs.core.eventloop.EventEmitter
import org.autojs.autojs.runtime.ScriptRuntime
import org.autojs.autojs.runtime.exception.ScriptInterruptedException
import org.autojs.autojs.util.KotlinUtils.ifNull
import java.util.concurrent.ExecutorService
@@ -19,27 +20,31 @@ import java.util.concurrent.Executors
* Created by Stardust on 2018/3/16.
*/
@SuppressLint("ViewConstructor")
class ScriptCanvasView(context: Context, private val mScriptRuntime: ScriptRuntime) : TextureView(context), TextureView.SurfaceTextureListener {
class JsCanvasView : TextureView, TextureView.SurfaceTextureListener {
@Volatile
private var mDrawing = true
private val mEventEmitter: EventEmitter = EventEmitter(mScriptRuntime.bridges)
private val mRuntime by lazy { AutoJs.instance.runtime }
private val mEventEmitter: EventEmitter by lazy { EventEmitter(mRuntime.bridges) }
private var mDrawingThreadPool: ExecutorService? = null
@Volatile
private var mTimePerDraw = (1000 / 30).toLong()
val maxListeners: Int
get() = mEventEmitter.maxListeners
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
init {
surfaceTextureListener = this
}
fun setMaxFps(maxFps: Int) {
mTimePerDraw = if (maxFps <= 0) {
0
} else {
(100 / maxFps).toLong()
}
mTimePerDraw = (if (maxFps <= 0) 0L else 100L / maxFps)
}
@Synchronized
@@ -55,7 +60,7 @@ class ScriptCanvasView(context: Context, private val mScriptRuntime: ScriptRunti
while (mDrawing) {
canvas = lockCanvas()
scriptCanvas.setCanvas(canvas)
emit("draw", scriptCanvas, this@ScriptCanvasView)
emit("draw", scriptCanvas, this@JsCanvasView)
if (canvas != null) {
unlockCanvasAndPost(canvas)
canvas = null
@@ -67,7 +72,7 @@ class ScriptCanvasView(context: Context, private val mScriptRuntime: ScriptRunti
time = SystemClock.uptimeMillis()
}
} catch (e: Exception) {
mScriptRuntime.exit(e)
mRuntime.exit(e)
mDrawing = false
} finally {
if (canvas != null) {

View File

@@ -147,7 +147,7 @@ interface ColorDetector {
"hs" -> HSDistanceDetector(color, threshold)
"h" -> HDistanceDetector(color, threshold)
"equal" -> EqualityDetector(color)
else -> throw Error("Failed to get a color detector")
else -> throw Exception("Failed to get a color detector")
}
}
}

View File

@@ -1,243 +0,0 @@
package org.autojs.autojs.core.image;
import static org.autojs.autojs.util.StringUtils.str;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.media.Image;
import androidx.annotation.NonNull;
import org.autojs.autojs.AutoJs;
import org.autojs.autojs.core.opencv.Mat;
import org.autojs.autojs.core.opencv.OpenCVHelper;
import org.autojs.autojs.pio.UncheckedIOException;
import org.autojs.autojs.runtime.ScriptRuntime;
import org.autojs.autojs6.R;
import org.opencv.android.Utils;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.ArrayList;
/**
* Created by Stardust on 2017/11/25.
*/
public class ImageWrapper implements Recyclable {
private Mat mMat;
private Bitmap mBitmap;
private final int mWidth;
private final int mHeight;
private final ScriptRuntime mScriptRuntime = AutoJs.getInstance().getRuntime();
private boolean isRecycled;
private final static ArrayList<WeakReference<Object>> imageList = new ArrayList<>();
private boolean mOneShot;
private void addToList(Object image) {
imageList.add(new WeakReference<>(image));
}
public static synchronized void recycleAll() {
for (WeakReference<Object> reference : imageList) {
Object image = reference.get();
if (image instanceof Recyclable) {
((Recyclable) image).recycle();
} else if (image instanceof Bitmap) {
((Bitmap) image).recycle();
}
}
imageList.clear();
}
protected ImageWrapper(Mat mat) {
this(null, mat);
}
protected ImageWrapper(Bitmap bitmap) {
this(bitmap, null);
}
protected ImageWrapper(int width, int height) {
this(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888));
}
protected ImageWrapper(Bitmap bitmap, Mat mat) {
if (mat != null) {
mMat = mat;
}
if (bitmap != null) {
mBitmap = bitmap;
mWidth = bitmap.getWidth();
mHeight = bitmap.getHeight();
} else {
if (mat == null) {
throw new Error(str(R.string.error_both_bitmap_and_mat_are_null));
}
mWidth = mat.cols();
mHeight = mat.rows();
}
addToList(this);
}
public static ImageWrapper ofImage(Image image) {
if (image == null) {
return null;
}
return new ImageWrapper(toBitmap(image));
}
public static ImageWrapper ofMat(Mat mat) {
if (mat == null) {
return null;
}
return new ImageWrapper(mat);
}
public static ImageWrapper ofBitmap(Bitmap bitmap) {
if (bitmap == null) {
return null;
}
return new ImageWrapper(bitmap);
}
public static Bitmap toBitmap(Image image) {
Image.Plane plane = image.getPlanes()[0];
ByteBuffer buffer = plane.getBuffer();
buffer.position(0);
int pixelStride = plane.getPixelStride();
int rowPadding = plane.getRowStride() - pixelStride * image.getWidth();
Bitmap bitmap = Bitmap.createBitmap(image.getWidth() + rowPadding / pixelStride, image.getHeight(), Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
if (rowPadding == 0) {
return bitmap;
}
try {
return Bitmap.createBitmap(bitmap, 0, 0, image.getWidth(), image.getHeight());
} catch (IllegalStateException e) {
// Wrapped java.lang.IllegalStateException: Image is already closed
e.printStackTrace();
return null;
}
}
public int getWidth() {
ensureNotRecycled();
return mWidth;
}
public int getHeight() {
ensureNotRecycled();
return mHeight;
}
public Size getSize() {
ensureNotRecycled();
return new Size(mWidth, mHeight);
}
public Mat getMat() {
ensureNotRecycled();
if (mMat == null && mBitmap != null) {
mMat = new Mat();
Utils.bitmapToMat(mBitmap, mMat);
}
return mMat;
}
public void saveTo(String path) {
ensureNotRecycled();
path = mScriptRuntime.files.path(path);
if (mBitmap != null) {
try {
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(path));
} catch (FileNotFoundException e) {
throw new UncheckedIOException(e);
}
} else {
Imgcodecs.imwrite(path, mMat);
}
}
public int pixel(int x, int y) {
ensureNotRecycled();
int result;
if (mBitmap != null) {
result = mBitmap.getPixel(x, y);
} else {
double[] channels = mMat.get(x, y);
result = Color.argb((int) channels[3], (int) channels[0], (int) channels[1], (int) channels[2]);
}
return result;
}
public Bitmap getBitmap() {
ensureNotRecycled();
if (mBitmap == null && mMat != null) {
mBitmap = Bitmap.createBitmap(mMat.width(), mMat.height(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mMat, mBitmap);
}
return mBitmap;
}
@Override
public void recycle() {
if (mBitmap != null) {
mBitmap.recycle();
mBitmap = null;
}
if (mMat != null) {
OpenCVHelper.release(mMat);
mMat = null;
}
isRecycled = true;
}
@Override
public ImageWrapper setOneShot(boolean b) {
mOneShot = b;
return this;
}
@Override
public void shoot() {
if (mOneShot) {
recycle();
}
}
@Override
public boolean isRecycled() {
return isRecycled;
}
public void ensureNotRecycled() {
if (isRecycled()) {
throw new IllegalStateException(str(R.string.error_image_has_been_recycled));
}
}
@SuppressWarnings("MethodDoesntCallSuperMethod")
@NonNull
public ImageWrapper clone() {
ensureNotRecycled();
ImageWrapper imageWrapper;
if (mBitmap == null) {
imageWrapper = ImageWrapper.ofMat(mMat.clone());
} else if (mMat == null) {
imageWrapper = ImageWrapper.ofBitmap(mBitmap.copy(mBitmap.getConfig(), true));
} else {
imageWrapper = new ImageWrapper(mBitmap.copy(mBitmap.getConfig(), true), mMat.clone());
}
return imageWrapper;
}
}

View File

@@ -0,0 +1,196 @@
package org.autojs.autojs.core.image
import android.graphics.Bitmap
import android.graphics.Color
import android.media.Image
import org.autojs.autojs.AutoJs.Companion.instance
import org.autojs.autojs.annotation.ScriptInterface
import org.autojs.autojs.core.opencv.Mat
import org.autojs.autojs.core.opencv.OpenCVHelper
import org.autojs.autojs.pio.UncheckedIOException
import org.autojs.autojs.util.StringUtils.str
import org.autojs.autojs6.R
import org.opencv.android.Utils
import org.opencv.core.Size
import org.opencv.imgcodecs.Imgcodecs
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.lang.ref.WeakReference
/**
* Created by Stardust on 2017/11/25.
* Modified by SuperMonster003 as of May 16, 2023.
* Transformed by SuperMonster003 on May 16, 2023.
*/
class ImageWrapper constructor(bitmap: Bitmap?, mat: Mat?) : Recyclable {
// @Hint by SuperMonster003
// ! It is ensured that bitmap and mat will not be null at the same time.
private var mMat: Mat? = null
private var mBitmap: Bitmap? = null
private var mWidth = 0
private var mHeight = 0
private val mScriptRuntime = instance.runtime
private var mIsRecycled = false
private var mIsOneShot = false
constructor(mat: Mat) : this(null, mat)
constructor(bitmap: Bitmap) : this(bitmap, null)
constructor(width: Int, height: Int) : this(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888))
init {
if (mat != null) {
mMat = mat
}
if (bitmap != null) {
mBitmap = bitmap
mWidth = bitmap.width
mHeight = bitmap.height
} else {
if (mat != null) {
mWidth = mat.cols()
mHeight = mat.rows()
} else {
throw Exception(str(R.string.error_both_bitmap_and_mat_are_null))
}
}
addToList(this)
}
val width
get() = mWidth.also { ensureNotRecycled() }
val height
get() = mHeight.also { ensureNotRecycled() }
val size
get() = Size(mWidth.toDouble(), mHeight.toDouble()).also { ensureNotRecycled() }
val bitmap: Bitmap
get() {
ensureNotRecycled()
if (mBitmap == null && mMat != null) {
mBitmap = Bitmap.createBitmap(mMat!!.width(), mMat!!.height(), Bitmap.Config.ARGB_8888)
Utils.matToBitmap(mMat, mBitmap)
}
return mBitmap!!
}
val mat: Mat
get() {
ensureNotRecycled()
if (mMat == null && mBitmap != null) {
mMat = Mat()
Utils.bitmapToMat(mBitmap, mMat)
}
return mMat!!
}
private fun addToList(image: Any) {
imageList.add(WeakReference(image))
}
fun saveTo(path: String?) {
ensureNotRecycled()
val nicePath = mScriptRuntime.files.path(path)
if (mBitmap != null) {
try {
mBitmap!!.compress(Bitmap.CompressFormat.PNG, 100, FileOutputStream(nicePath))
} catch (e: FileNotFoundException) {
throw UncheckedIOException(e)
}
} else {
Imgcodecs.imwrite(nicePath, mMat)
}
}
fun pixel(x: Int, y: Int): Int {
ensureNotRecycled()
val result = if (mBitmap != null) {
mBitmap!!.getPixel(x, y)
} else {
val channels = mat[x, y]
Color.argb(channels[3].toInt(), channels[0].toInt(), channels[1].toInt(), channels[2].toInt())
}
return result
}
override fun recycle() {
mBitmap?.run { recycle().also { mBitmap = null } }
mMat?.run { OpenCVHelper.release(mMat).also { mMat = null } }
mIsRecycled = true
}
override fun setOneShot(b: Boolean) = also { mIsOneShot = b }
override fun shoot() {
if (mIsOneShot) recycle()
}
override fun isRecycled() = mIsRecycled
@ScriptInterface
fun ensureNotRecycled() {
check(!isRecycled) { str(R.string.error_image_has_been_recycled) }
}
fun clone(): ImageWrapper {
ensureNotRecycled()
return if (mBitmap == null) {
ofMat(mMat!!.clone())!!
} else if (mMat == null) {
ofBitmap(mBitmap!!.copy(mBitmap!!.config, true))!!
} else {
ImageWrapper(mBitmap!!.copy(mBitmap!!.config, true), mMat!!.clone())
}
}
companion object {
private val imageList = ArrayList<WeakReference<Any>>()
@JvmStatic
@Synchronized
fun recycleAll() {
imageList.forEach {
when (val i = it.get()) {
is Recyclable -> i.recycle()
is Bitmap -> i.recycle()
}
}
imageList.clear()
}
@JvmStatic
fun ofImage(image: Image?) = image?.let { ImageWrapper(toBitmap(it)) }
@JvmStatic
fun ofMat(mat: Mat?) = mat?.let { ImageWrapper(it) }
@JvmStatic
fun ofBitmap(bitmap: Bitmap?) = bitmap?.let { ImageWrapper(it) }
@ScriptInterface
fun toBitmap(image: Image): Bitmap {
val plane = image.planes[0]
val buffer = plane.buffer.apply { position(0) }
val pixelStride = plane.pixelStride
val rowPadding = plane.rowStride - pixelStride * image.width
val bitmap = Bitmap.createBitmap(
image.width + rowPadding / pixelStride,
image.height,
Bitmap.Config.ARGB_8888,
).apply { copyPixelsFromBuffer(buffer) }
return when (rowPadding == 0) {
true -> bitmap
else -> Bitmap.createBitmap(bitmap, 0, 0, image.width, image.height)
}
}
}
}

View File

@@ -1,25 +1,37 @@
package org.autojs.autojs.core.inputevent;
import static org.autojs.autojs.core.inputevent.InputEventCodes.ABS_MT_POSITION_X;
import static org.autojs.autojs.core.inputevent.InputEventCodes.ABS_MT_POSITION_Y;
import static org.autojs.autojs.core.inputevent.InputEventCodes.ABS_MT_SLOT;
import static org.autojs.autojs.core.inputevent.InputEventCodes.ABS_MT_TOUCH_MAJOR;
import static org.autojs.autojs.core.inputevent.InputEventCodes.ABS_MT_TRACKING_ID;
import static org.autojs.autojs.core.inputevent.InputEventCodes.ABS_MT_WIDTH_MAJOR;
import static org.autojs.autojs.core.inputevent.InputEventCodes.BTN_TOUCH;
import static org.autojs.autojs.core.inputevent.InputEventCodes.DOWN;
import static org.autojs.autojs.core.inputevent.InputEventCodes.EV_ABS;
import static org.autojs.autojs.core.inputevent.InputEventCodes.EV_KEY;
import static org.autojs.autojs.core.inputevent.InputEventCodes.EV_SYN;
import static org.autojs.autojs.core.inputevent.InputEventCodes.SYN_MT_REPORT;
import static org.autojs.autojs.core.inputevent.InputEventCodes.SYN_REPORT;
import static org.autojs.autojs.core.inputevent.InputEventCodes.UP;
import android.content.Context;
import android.os.SystemClock;
import androidx.annotation.Nullable;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.ViewConfiguration;
import org.autojs.autojs.runtime.api.Shell;
import androidx.annotation.Nullable;
import org.autojs.autojs.engine.RootAutomatorEngine;
import org.autojs.autojs.runtime.exception.ScriptInterruptedException;
import org.autojs.autojs.runtime.api.ScreenMetrics;
import org.autojs.autojs.runtime.api.Shell;
import org.autojs.autojs.runtime.exception.ScriptInterruptedException;
import java.io.IOException;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
import static org.autojs.autojs.core.inputevent.InputEventCodes.*;
/**
* Created by Stardust on 2017/7/16.
* Modified by SuperMonster003 as of May 12, 2022.

View File

@@ -31,7 +31,6 @@ public class ViewExtras {
return extras;
}
public static ViewAttributes getViewAttributes(View view, ResourceParser parser) {
ViewExtras extras = get(view);
ViewAttributes attributes = extras.getViewAttributes();
@@ -42,7 +41,6 @@ public class ViewExtras {
return attributes;
}
public static NativeView getNativeView(Scriptable scope, View view, Class<?> staticType, ScriptRuntime runtime) {
ViewExtras extras = get(view);
NativeView nativeView = extras.getNativeView();
@@ -73,4 +71,5 @@ public class ViewExtras {
public final void setViewAttributes(ViewAttributes viewAttributes) {
mViewAttributes = viewAttributes;
}
}

View File

@@ -0,0 +1,24 @@
package org.autojs.autojs.core.ui.attribute
import android.view.View
import android.widget.AbsSeekBar
import org.autojs.autojs.core.ui.inflater.ResourceParser
import org.autojs.autojs.util.ColorUtils
open class AbsSeekBarAttributes<V : AbsSeekBar>(resourceParser: ResourceParser, view: View) : ProgressBarAttributes<V>(resourceParser, view) {
override val view = super.view as AbsSeekBar
override fun onRegisterAttrs() {
super.onRegisterAttrs()
registerAttr("keyProgressIncrement") { view.keyProgressIncrement = it.toInt() }
registerAttr("splitTrack") { view.splitTrack = it.toBoolean() }
registerAttr("thumb") { view.thumb = drawables.parse(view, it) }
registerAttr("thumbOffset") { view.thumbOffset = it.toInt() }
registerAttr("thumbTintList") { view.thumbTintList = ColorUtils.toColorStateList(view, it) }
registerAttr("thumbTintList") { view.thumbTintList = ColorUtils.toColorStateList(view, it) }
registerAttr("tickMark") { view.tickMark = drawables.parse(view, it) }
registerAttr("tickMarkTintList") { view.tickMarkTintList = ColorUtils.toColorStateList(view, it) }
}
}

View File

@@ -0,0 +1,11 @@
package org.autojs.autojs.core.ui.attribute
import android.view.View
import android.widget.AbsSpinner
import org.autojs.autojs.core.ui.inflater.ResourceParser
open class AbsSpinnerAttributes(resourceParser: ResourceParser, view: View) : AdapterViewAttributes(resourceParser, view) {
override val view = super.view as AbsSpinner
}

View File

@@ -0,0 +1,17 @@
package org.autojs.autojs.core.ui.attribute
import android.view.View
import android.widget.AdapterView
import org.autojs.autojs.core.ui.inflater.ResourceParser
open class AdapterViewAttributes(resourceParser: ResourceParser, view: View) : ViewGroupAttributes(resourceParser, view) {
override val view = super.view as AdapterView<*>
override fun onRegisterAttrs() {
super.onRegisterAttrs()
registerAttr("selection") { view.setSelection(it.toInt()) }
}
}

View File

@@ -0,0 +1,23 @@
package org.autojs.autojs.core.ui.attribute
import android.view.View
import com.google.android.material.appbar.AppBarLayout
import org.autojs.autojs.core.ui.inflater.ResourceParser
import org.autojs.autojs.core.ui.inflater.util.Dimensions
import org.autojs.autojs.util.ColorUtils
open class AppBarLayoutAttributes(resourceParser: ResourceParser, view: View) : LinearLayoutAttributes(resourceParser, view) {
override val view = super.view as AppBarLayout
override fun onRegisterAttrs() {
super.onRegisterAttrs()
registerAttr("expanded") { view.setExpanded(it.toBoolean()) }
registerAttrs(arrayOf("isLifted", "lifted")) { view.isLifted = it.toBoolean() }
registerAttrs(arrayOf("statusBarForegroundColor", "statusBarFgColor")) { view.setStatusBarForegroundColor(ColorUtils.parse(view, it)) }
@Suppress("DEPRECATION")
registerAttr("elevation") { view.targetElevation = Dimensions.parseToPixel(it, view) }
}
}

View File

@@ -0,0 +1,11 @@
package org.autojs.autojs.core.ui.attribute
import android.view.View
import androidx.appcompat.widget.AppCompatCheckBox
import org.autojs.autojs.core.ui.inflater.ResourceParser
open class AppCompatCheckBoxAttributes(resourceParser: ResourceParser, view: View) : CheckBoxAttributes(resourceParser, view) {
override val view = super.view as AppCompatCheckBox
}

View File

@@ -0,0 +1,11 @@
package org.autojs.autojs.core.ui.attribute
import android.view.View
import androidx.appcompat.widget.AppCompatSpinner
import org.autojs.autojs.core.ui.inflater.ResourceParser
open class AppCompatSpinnerAttributes(resourceParser: ResourceParser, view: View) : SpinnerAttributes(resourceParser, view) {
override val view = super.view as AppCompatSpinner
}

View File

@@ -0,0 +1,11 @@
package org.autojs.autojs.core.ui.attribute
import android.view.View
import androidx.appcompat.widget.AppCompatTextView
import org.autojs.autojs.core.ui.inflater.ResourceParser
open class AppCompatTextViewAttributes(resourceParser: ResourceParser, view: View): TextViewAttributes(resourceParser, view) {
override val view = super.view as AppCompatTextView
}

View File

@@ -0,0 +1,11 @@
package org.autojs.autojs.core.ui.attribute
import android.view.View
import org.autojs.autojs.core.ui.inflater.ResourceParser
import androidx.appcompatlegacy.widget.AppCompatTextView
open class AppCompatTextViewLegacyAttributes(resourceParser: ResourceParser, view: View) : TextViewAttributes(resourceParser, view) {
override val view = super.view as AppCompatTextView
}

View File

@@ -1,24 +0,0 @@
package org.autojs.autojs.core.ui.attribute;
import android.view.View;
import com.google.android.material.appbar.AppBarLayout;
import org.autojs.autojs.core.ui.inflater.ResourceParser;
public class AppbarAttributes extends ViewAttributes {
public AppbarAttributes(ResourceParser resourceParser, View view) {
super(resourceParser, view);
}
@Override
protected void onRegisterAttrs() {
super.onRegisterAttrs();
registerPixelAttr("elevation", getView()::setTargetElevation);
registerBooleanAttr("expanded", getView()::setExpanded);
}
@Override
public AppBarLayout getView() {
return (AppBarLayout) super.getView();
}
}

View File

@@ -0,0 +1,11 @@
package org.autojs.autojs.core.ui.attribute
import android.view.View
import android.widget.Button
import org.autojs.autojs.core.ui.inflater.ResourceParser
open class ButtonAttributes(resourceParser: ResourceParser, view: View) : TextViewAttributes(resourceParser, view) {
override val view = super.view as Button
}

View File

@@ -1,73 +0,0 @@
package org.autojs.autojs.core.ui.attribute;
import android.graphics.Color;
import android.view.View;
import org.autojs.autojs.core.ui.inflater.ResourceParser;
import androidx.cardview.widget.CardView;
import org.autojs.autojs.core.ui.inflater.util.Dimensions;
public class CardAttributes extends ViewAttributes {
public CardAttributes(ResourceParser resourceParser, View view) {
super(resourceParser, view);
}
@Override
protected void onRegisterAttrs() {
super.onRegisterAttrs();
registerAttr("cardBackgroundColor", Color::parseColor, getView()::setCardBackgroundColor);
registerPixelAttr("cardCornerRadius", getView()::setRadius);
registerPixelAttr("cardElevation", getView()::setCardElevation);
registerPixelAttr("cardMaxElevation", getView()::setMaxCardElevation);
registerBooleanAttr("cardPreventCornerOverlap", getView()::setPreventCornerOverlap);
registerBooleanAttr("cardUseCompatPadding", getView()::setUseCompatPadding);
registerAttr("contentPadding", this::setContentPadding);
registerIntPixelAttr("contentPaddingBottom", this::setContentPaddingBottom);
registerIntPixelAttr("contentPaddingLeft", this::setContentPaddingLeft);
registerIntPixelAttr("contentPaddingTop", this::setContentPaddingTop);
registerIntPixelAttr("contentPaddingRight", this::setContentPaddingRight);
}
private void setContentPadding(String value) {
int[] pixels = Dimensions.parseToIntPixelArray(getView(), value);
getView().setContentPadding(pixels[0], pixels[1], pixels[2], pixels[3]);
}
private void setContentPaddingBottom(int value) {
CardView cardView = getView();
cardView.setContentPadding(cardView.getContentPaddingLeft(), cardView.getContentPaddingTop(),
cardView.getContentPaddingRight(), value);
}
private void setContentPaddingLeft(int value) {
CardView cardView = getView();
cardView.setContentPadding(value, cardView.getContentPaddingTop(),
cardView.getContentPaddingRight(), cardView.getContentPaddingBottom());
}
private void setContentPaddingTop(int value) {
CardView cardView = getView();
cardView.setContentPadding(cardView.getContentPaddingLeft(), value,
cardView.getContentPaddingRight(), cardView.getContentPaddingBottom());
}
private void setContentPaddingRight(int value) {
CardView cardView = getView();
cardView.setContentPadding(cardView.getContentPaddingLeft(), cardView.getContentPaddingTop(),
value, cardView.getContentPaddingBottom());
}
@Override
public CardView getView() {
return (CardView) super.getView();
}
}

View File

@@ -0,0 +1,51 @@
package org.autojs.autojs.core.ui.attribute
import android.view.View
import androidx.cardview.widget.CardView
import org.autojs.autojs.core.ui.inflater.ResourceParser
import org.autojs.autojs.core.ui.inflater.util.Dimensions
import org.autojs.autojs.util.ColorUtils
open class CardViewAttributes(resourceParser: ResourceParser, view: View) : FrameLayoutAttributes(resourceParser, view) {
override val view = super.view as CardView
override fun onRegisterAttrs() {
super.onRegisterAttrs()
registerAttrs(arrayOf("cardBackgroundColor", "cardBgColor", "cardBg")) { view.setCardBackgroundColor(ColorUtils.parse(view, it)) }
registerAttrs(arrayOf("radius", "cardCornerRadius", "cornerRadius")) { view.radius = parseDimensionToPixel(it) }
registerPixelAttr("cardElevation") { view.cardElevation = it }
registerAttrs(arrayOf("maxCardElevation", "cardMaxElevation")) { view.maxCardElevation = parseDimensionToPixel(it) }
registerAttrs(arrayOf("preventCornerOverlap", "cardPreventCornerOverlap")) { view.preventCornerOverlap = it.toBoolean() }
registerAttrs(arrayOf("useCompatPadding", "cardUseCompatPadding")) { view.useCompatPadding = it.toBoolean() }
registerAttr("contentPadding") { setContentPadding(it) }
registerIntPixelAttr("contentPaddingBottom") { setContentPaddingBottom(it) }
registerIntPixelAttr("contentPaddingLeft") { setContentPaddingLeft(it) }
registerIntPixelAttr("contentPaddingTop") { setContentPaddingTop(it) }
registerIntPixelAttr("contentPaddingRight") { setContentPaddingRight(it) }
}
private fun setContentPadding(value: String) {
Dimensions.parseToIntPixelArray(view, value).let { (left, top, right, bottom) ->
view.setContentPadding(left, top, right, bottom)
}
}
private fun setContentPaddingBottom(value: Int) {
view.apply { setContentPadding(contentPaddingLeft, contentPaddingTop, contentPaddingRight, value) }
}
private fun setContentPaddingLeft(value: Int) {
view.apply { setContentPadding(value, contentPaddingTop, contentPaddingRight, contentPaddingBottom) }
}
private fun setContentPaddingTop(value: Int) {
view.apply { setContentPadding(contentPaddingLeft, value, contentPaddingRight, contentPaddingBottom) }
}
private fun setContentPaddingRight(value: Int) {
view.apply { setContentPadding(contentPaddingLeft, contentPaddingTop, value, contentPaddingBottom) }
}
}

View File

@@ -0,0 +1,11 @@
package org.autojs.autojs.core.ui.attribute
import android.view.View
import android.widget.CheckBox
import org.autojs.autojs.core.ui.inflater.ResourceParser
open class CheckBoxAttributes(resourceParser: ResourceParser, view: View) : CompoundButtonAttributes(resourceParser, view) {
override val view = super.view as CheckBox
}

View File

@@ -0,0 +1,42 @@
package org.autojs.autojs.core.ui.attribute
import android.content.res.ColorStateList
import android.view.View
import android.widget.CompoundButton
import org.autojs.autojs.core.ui.inflater.ResourceParser
import org.autojs.autojs.util.ColorUtils
open class CompoundButtonAttributes(resourceParser: ResourceParser, view: View) : ButtonAttributes(resourceParser, view) {
override val view = super.view as CompoundButton
override fun onRegisterAttrs() {
super.onRegisterAttrs()
registerAttrs(arrayOf("checked", "check", "isChecked")) { setChecked(it.toBoolean()) }
registerAttr("buttonDrawable") { view.buttonDrawable = drawables.parse(view, it) }
registerAttrs(arrayOf("buttonTint", "tint")) { view.buttonTintList = parseTintList(view, it) }
}
private fun setChecked(checked: Boolean) {
view.isChecked = checked
}
companion object {
private val switchStates = arrayOf(
intArrayOf(-android.R.attr.state_checked),
intArrayOf(android.R.attr.state_checked),
)
fun parseTintList(view: View, value: String): ColorStateList {
val colors = parseAttrValue(value)
return when (colors.size) {
2 -> ColorStateList(switchStates, colors.map { ColorUtils.parse(view, it) }.toIntArray())
else -> ColorUtils.parse(view, colors[0]).let { ColorStateList(switchStates, intArrayOf(it, it)) }
}
}
}
}

View File

@@ -0,0 +1,26 @@
package org.autojs.autojs.core.ui.attribute
import android.view.View
import org.autojs.autojs.core.console.ConsoleView
import org.autojs.autojs.core.ui.inflater.ResourceParser
import org.autojs.autojs.util.ColorUtils
open class ConsoleViewAttributes(resourceParser: ResourceParser, view: View) : FrameLayoutAttributes(resourceParser, view) {
override val view = super.view as ConsoleView
override fun onRegisterAttrs() {
super.onRegisterAttrs()
registerAttr("textSize") { view.textSize = it.toFloat() }
registerAttr("textColors") { value -> view.setTextColors(parseAttrValue(value).map { ColorUtils.parse(view, it) }.toTypedArray()) }
registerAttrs(arrayOf("verboseTextColor", "verboseColor")) { view.setVerboseTextColor(ColorUtils.parse(view, it)) }
registerAttrs(arrayOf("debugTextColor", "debugColor")) { view.setDebugTextColor(ColorUtils.parse(view, it)) }
registerAttrs(arrayOf("infoTextColor", "infoColor")) { view.setInfoTextColor(ColorUtils.parse(view, it)) }
registerAttrs(arrayOf("warnTextColor", "warnColor")) { view.setWarnTextColor(ColorUtils.parse(view, it)) }
registerAttrs(arrayOf("errorTextColor", "errorColor")) { view.setErrorTextColor(ColorUtils.parse(view, it)) }
registerAttrs(arrayOf("assertTextColor", "assertColor")) { view.setAssertTextColor(ColorUtils.parse(view, it)) }
registerAttrs(arrayOf("isPinchToZoomEnabled", "pinchToZoomEnabled", "enablePinchToZoom")) { view.setPinchToZoomEnabled(it.toBoolean()) }
}
}

Some files were not shown because too many files have changed in this diff Show More