6.3.2 - 新增 crypto 模块 / 日志复制及导出; 修复 runtime.loadDex 及 loadJar; 优化客户端及服务端连接体验
This commit is contained in:
@@ -4,6 +4,8 @@ import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.autojs.autojs.pref.Language;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -113,11 +115,11 @@ public class RomUtil {
|
||||
sName = ROM_SMARTISAN;
|
||||
} else {
|
||||
sVersion = Build.DISPLAY;
|
||||
if (sVersion.toUpperCase().contains(ROM_FLYME)) {
|
||||
if (sVersion.toUpperCase(Language.getPrefLanguage().getLocale()).contains(ROM_FLYME)) {
|
||||
sName = ROM_FLYME;
|
||||
} else {
|
||||
sVersion = Build.UNKNOWN;
|
||||
sName = Build.MANUFACTURER.toUpperCase();
|
||||
sName = Build.MANUFACTURER.toUpperCase(Language.getPrefLanguage().getLocale());
|
||||
}
|
||||
}
|
||||
return sName.equals(rom);
|
||||
|
||||
@@ -145,7 +145,7 @@ class App : MultiDexApplication() {
|
||||
}
|
||||
|
||||
private fun setupDrawableImageLoader() {
|
||||
Drawables.setDefaultImageLoader(object : ImageLoader {
|
||||
Drawables.defaultImageLoader = object : ImageLoader {
|
||||
override fun loadInto(imageView: ImageView, uri: Uri) {
|
||||
Glide.with(imageView)
|
||||
.load(uri)
|
||||
@@ -210,7 +210,7 @@ class App : MultiDexApplication() {
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
|
||||
@@ -52,14 +52,19 @@ class AutoJs private constructor(private val appContext: Application) : Abstract
|
||||
LocalBroadcastManager.getInstance(appContext).registerReceiver(object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
try {
|
||||
val action = intent.action ?: return
|
||||
ensureAccessibilityServiceEnabled()
|
||||
when (intent.action) {
|
||||
LayoutBoundsFloatyWindow::class.java.name -> capture(object : LayoutInspectFloatyWindow {
|
||||
override fun create(nodeInfo: NodeInfo?) = LayoutBoundsFloatyWindow(nodeInfo, context)
|
||||
})
|
||||
LayoutHierarchyFloatyWindow::class.java.name -> capture(object : LayoutInspectFloatyWindow {
|
||||
override fun create(nodeInfo: NodeInfo?) = LayoutHierarchyFloatyWindow(nodeInfo, context)
|
||||
})
|
||||
when {
|
||||
action.equals(LayoutBoundsFloatyWindow::class.java.name, true) -> {
|
||||
capture(object : LayoutInspectFloatyWindow {
|
||||
override fun create(nodeInfo: NodeInfo?) = LayoutBoundsFloatyWindow(nodeInfo, context)
|
||||
})
|
||||
}
|
||||
action.equals(LayoutHierarchyFloatyWindow::class.java.name, true) -> {
|
||||
capture(object : LayoutInspectFloatyWindow {
|
||||
override fun create(nodeInfo: NodeInfo?) = LayoutHierarchyFloatyWindow(nodeInfo, context)
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.autojs.autojs.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Created by SuperMonster003 on Jun 3, 2023.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target({ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.FIELD})
|
||||
public @interface CodeAuthor {
|
||||
|
||||
String name();
|
||||
|
||||
String homepage();
|
||||
|
||||
}
|
||||
@@ -23,69 +23,19 @@ import java.util.concurrent.Callable
|
||||
*/
|
||||
open class ApkBuilder(apkInputStream: InputStream?, private val mOutApkFile: File, private val mWorkspacePath: String) {
|
||||
|
||||
interface ProgressCallback {
|
||||
fun onPrepare(builder: ApkBuilder?)
|
||||
fun onBuild(builder: ApkBuilder?)
|
||||
fun onSign(builder: ApkBuilder?)
|
||||
fun onClean(builder: ApkBuilder?)
|
||||
}
|
||||
|
||||
class AppConfig {
|
||||
|
||||
var appName: String? = null
|
||||
private set
|
||||
var versionName: String? = null
|
||||
private set
|
||||
var versionCode = 0
|
||||
private set
|
||||
var sourcePath: String? = null
|
||||
private set
|
||||
var packageName: String? = null
|
||||
private set
|
||||
var ignoredDirs = ArrayList<File>()
|
||||
var icon: Callable<Bitmap>? = null
|
||||
private set
|
||||
|
||||
fun ignoreDir(dir: File) = also { ignoredDirs.add(dir) }
|
||||
|
||||
fun setAppName(appName: String?) = also { appName?.let { this.appName = it } }
|
||||
|
||||
fun setVersionName(versionName: String?) = also { versionName?.let { this.versionName = it } }
|
||||
|
||||
fun setVersionCode(versionCode: Int?) = also { versionCode?.let { this.versionCode = it } }
|
||||
|
||||
fun setSourcePath(sourcePath: String?) = also { sourcePath?.let { this.sourcePath = it } }
|
||||
|
||||
fun setPackageName(packageName: String?) = also { packageName?.let { this.packageName = it } }
|
||||
|
||||
fun setIcon(icon: Callable<Bitmap>?) = also { icon?.let { this.icon = it } }
|
||||
|
||||
fun setIcon(iconPath: String?) = also { iconPath?.let { this.icon = Callable { BitmapFactory.decodeFile(it) } } }
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun fromProjectConfig(projectDir: String?, projectConfig: ProjectConfig) = AppConfig()
|
||||
.setAppName(projectConfig.name)
|
||||
.setPackageName(projectConfig.packageName)
|
||||
.ignoreDir(File(projectDir, projectConfig.buildDir))
|
||||
.setVersionCode(projectConfig.versionCode)
|
||||
.setVersionName(projectConfig.versionName)
|
||||
.setSourcePath(projectDir)
|
||||
.setIcon(projectConfig.icon?.let { File(projectDir, it).path })
|
||||
}
|
||||
}
|
||||
|
||||
private var mProgressCallback: ProgressCallback? = null
|
||||
private val mApkPackager: ApkPackager
|
||||
private val mApkPackager = ApkPackager(apkInputStream, mWorkspacePath)
|
||||
private var mArscPackageName: String? = null
|
||||
private var mManifestEditor: ManifestEditor? = null
|
||||
private var mInitVector: String? = null
|
||||
private var mKey: String? = null
|
||||
|
||||
private val mManifestFile
|
||||
get() = File(mWorkspacePath, "AndroidManifest.xml")
|
||||
|
||||
private lateinit var mAppConfig: AppConfig
|
||||
|
||||
init {
|
||||
mApkPackager = ApkPackager(apkInputStream, mWorkspacePath)
|
||||
PFiles.ensureDir(mOutApkFile.path)
|
||||
}
|
||||
|
||||
@@ -167,10 +117,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val mOutApkFile: Fil
|
||||
}
|
||||
|
||||
@Throws(FileNotFoundException::class)
|
||||
fun editManifest(): ManifestEditor = ManifestEditorWithAuthorities(FileInputStream(manifestFile)).also { mManifestEditor = it }
|
||||
|
||||
protected val manifestFile: File
|
||||
get() = File(mWorkspacePath, "AndroidManifest.xml")
|
||||
fun editManifest(): ManifestEditor = ManifestEditorWithAuthorities(FileInputStream(mManifestFile)).also { mManifestEditor = it }
|
||||
|
||||
private fun updateProjectConfig(appConfig: AppConfig) {
|
||||
let {
|
||||
@@ -211,7 +158,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val mOutApkFile: Fil
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
mManifestEditor?.apply { commit() }?.run { writeTo(FileOutputStream(manifestFile)) }
|
||||
mManifestEditor?.apply { commit() }?.run { writeTo(FileOutputStream(mManifestFile)) }
|
||||
mArscPackageName?.let { buildArsc() }
|
||||
}
|
||||
|
||||
@@ -245,6 +192,60 @@ open class ApkBuilder(apkInputStream: InputStream?, private val mOutApkFile: Fil
|
||||
file.apply { if (isDirectory) listFiles()?.forEach { delete(it) } }.also { it.delete() }
|
||||
}
|
||||
|
||||
interface ProgressCallback {
|
||||
|
||||
fun onPrepare(builder: ApkBuilder)
|
||||
fun onBuild(builder: ApkBuilder)
|
||||
fun onSign(builder: ApkBuilder)
|
||||
fun onClean(builder: ApkBuilder)
|
||||
|
||||
}
|
||||
|
||||
class AppConfig {
|
||||
|
||||
var appName: String? = null
|
||||
private set
|
||||
var versionName: String? = null
|
||||
private set
|
||||
var versionCode = 0
|
||||
private set
|
||||
var sourcePath: String? = null
|
||||
private set
|
||||
var packageName: String? = null
|
||||
private set
|
||||
var ignoredDirs = ArrayList<File>()
|
||||
var icon: Callable<Bitmap>? = null
|
||||
private set
|
||||
|
||||
fun ignoreDir(dir: File) = also { ignoredDirs.add(dir) }
|
||||
|
||||
fun setAppName(appName: String?) = also { appName?.let { this.appName = it } }
|
||||
|
||||
fun setVersionName(versionName: String?) = also { versionName?.let { this.versionName = it } }
|
||||
|
||||
fun setVersionCode(versionCode: Int?) = also { versionCode?.let { this.versionCode = it } }
|
||||
|
||||
fun setSourcePath(sourcePath: String?) = also { sourcePath?.let { this.sourcePath = it } }
|
||||
|
||||
fun setPackageName(packageName: String?) = also { packageName?.let { this.packageName = it } }
|
||||
|
||||
fun setIcon(icon: Callable<Bitmap>?) = also { icon?.let { this.icon = it } }
|
||||
|
||||
fun setIcon(iconPath: String?) = also { iconPath?.let { this.icon = Callable { BitmapFactory.decodeFile(it) } } }
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun fromProjectConfig(projectDir: String?, projectConfig: ProjectConfig) = AppConfig()
|
||||
.setAppName(projectConfig.name)
|
||||
.setPackageName(projectConfig.packageName)
|
||||
.ignoreDir(File(projectDir, projectConfig.buildDir))
|
||||
.setVersionCode(projectConfig.versionCode)
|
||||
.setVersionName(projectConfig.versionName)
|
||||
.setSourcePath(projectDir)
|
||||
.setIcon(projectConfig.icon?.let { File(projectDir, it).path })
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ManifestEditorWithAuthorities(manifestInputStream: InputStream?) : ManifestEditor(manifestInputStream) {
|
||||
override fun onAttr(attr: AxmlWriter.Attr) {
|
||||
attr.apply {
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.content.DialogInterface
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.functions.Consumer
|
||||
import org.autojs.autojs.App
|
||||
import org.autojs.autojs.pluginclient.DevPluginService
|
||||
import org.autojs.autojs.ui.main.drawer.SocketItemHelper
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,14 +3,20 @@ package org.autojs.autojs.app.tool
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.text.InputFilter
|
||||
import android.view.KeyEvent
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import org.autojs.autojs.app.DialogUtils
|
||||
import org.autojs.autojs.pluginclient.JsonSocketClient
|
||||
import org.autojs.autojs.pref.Pref
|
||||
import org.autojs.autojs.util.IntentUtils
|
||||
import org.autojs.autojs.ui.main.drawer.DrawerMenuDisposableItem
|
||||
import org.autojs.autojs.util.Observers
|
||||
import org.autojs.autojs.util.ViewUtils
|
||||
import org.autojs.autojs6.R
|
||||
|
||||
class JsonSocketClientTool(context: Context) : AbstractJsonSocketTool(context) {
|
||||
|
||||
private var mClientModeItem: DrawerMenuDisposableItem? = null
|
||||
|
||||
override val isConnected
|
||||
get() = devPlugin.isJsonSocketClientConnected
|
||||
|
||||
@@ -30,7 +36,12 @@ class JsonSocketClientTool(context: Context) : AbstractJsonSocketTool(context) {
|
||||
if (!isNormallyClosed) connect()
|
||||
}
|
||||
|
||||
internal fun setClientModeItem(clientModeItem: DrawerMenuDisposableItem) {
|
||||
mClientModeItem = clientModeItem
|
||||
}
|
||||
|
||||
override fun disconnect() {
|
||||
mClientModeItem?.subtitle = null
|
||||
devPlugin.disconnectJsonSocketClient()
|
||||
isNormallyClosed = true
|
||||
}
|
||||
@@ -44,44 +55,161 @@ class JsonSocketClientTool(context: Context) : AbstractJsonSocketTool(context) {
|
||||
val host = Pref.getServerAddress()
|
||||
if (isAutoConnect) {
|
||||
devPlugin
|
||||
.connectToRemoteServer(host, true)
|
||||
.connectToRemoteServer(host, mClientModeItem, 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 ->
|
||||
Pref.setServerAddress(input.toString())
|
||||
devPlugin
|
||||
.connectToRemoteServer(input.toString())
|
||||
.subscribe(Observers.emptyConsumer(), onConnectionException)
|
||||
.input(context.getString(R.string.text_pc_server_address), host) { dialog, _ ->
|
||||
connectToRemoteServer(dialog)
|
||||
}
|
||||
.neutralText(R.string.dialog_button_history)
|
||||
.neutralColorRes(R.color.dialog_button_hint)
|
||||
.onNeutral { dialog, _ ->
|
||||
MaterialDialog.Builder(context)
|
||||
.title(R.string.text_histories)
|
||||
.content(R.string.text_no_histories)
|
||||
.items(JsonSocketClient.serverAddressHistories)
|
||||
.itemsCallback { dHistories, _, _, text ->
|
||||
dHistories.dismiss()
|
||||
dialog.inputEditText?.setText(text)
|
||||
connectToRemoteServer(dialog)
|
||||
}
|
||||
.itemsLongCallback { dHistories, _, _, text ->
|
||||
false.also {
|
||||
MaterialDialog.Builder(context)
|
||||
.title(R.string.text_prompt)
|
||||
.content(R.string.text_confirm_to_delete)
|
||||
.negativeText(R.string.dialog_button_cancel)
|
||||
.positiveText(R.string.dialog_button_confirm)
|
||||
.positiveColorRes(R.color.dialog_button_caution)
|
||||
.onPositive { ds, _ ->
|
||||
ds.dismiss()
|
||||
JsonSocketClient.removeFromHistories(text.toString())
|
||||
dHistories.items?.let {
|
||||
it.remove(text)
|
||||
dHistories.notifyItemsChanged()
|
||||
DialogUtils.toggleContentViewByItems(dHistories)
|
||||
}
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
.negativeText(R.string.dialog_button_back)
|
||||
.negativeColorRes(R.color.dialog_button_default)
|
||||
.onNegative { dHistories, _ -> dHistories.dismiss() }
|
||||
.autoDismiss(false)
|
||||
.show()
|
||||
.also { DialogUtils.toggleContentViewByItems(it) }
|
||||
}
|
||||
.neutralText(R.string.text_help)
|
||||
.negativeText(R.string.text_back)
|
||||
.onNeutral { _, _ -> IntentUtils.browse(context, context.getString(R.string.url_github_autojs6_vscode_extension_usage)) }
|
||||
.onNegative { dialog, _ -> dialog.dismiss() }
|
||||
.autoDismiss(false)
|
||||
.dismissListener(onConnectionDialogDismissed)
|
||||
.show()
|
||||
.also { dialog: MaterialDialog ->
|
||||
dialog.setOnKeyListener { _, keyCode, _ ->
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER) {
|
||||
connectToRemoteServer(dialog)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
dialog.inputEditText!!.filters += InputFilter { source, start, end, dest, dstart, dend ->
|
||||
val rexDot = "[, .,。]"
|
||||
val rexNum = "\\d{1,3}"
|
||||
val rexIp = Regex("^${rexNum}(${rexDot}(${rexNum}(${rexDot}(${rexNum}(${rexDot}(${rexNum})?)?)?)?)?)?")
|
||||
if (end > start) {
|
||||
val fullText = dest.substring(0, dstart) +
|
||||
source.subSequence(start, end) +
|
||||
dest.substring(dend)
|
||||
if (!fullText.contains(rexIp)) {
|
||||
return@InputFilter ""
|
||||
}
|
||||
fullText.split(rexDot.toRegex()).dropLastWhile { it.isEmpty() }.forEach { s ->
|
||||
if (Integer.valueOf(s) > 255) {
|
||||
if (dstart > 0) {
|
||||
val prevNearest = dest[dstart - 1]
|
||||
if (Regex(rexDot).matches(prevNearest.toString()) && Regex(rexDot).matches(source)) {
|
||||
showSnack(dialog, R.string.error_repeated_dot_symbol)
|
||||
return@InputFilter ""
|
||||
}
|
||||
if (Regex(rexColon).matches(prevNearest.toString()) && Regex(rexColon).matches(source)) {
|
||||
showSnack(dialog, R.string.error_repeated_colon_symbol)
|
||||
return@InputFilter ""
|
||||
}
|
||||
}
|
||||
if (!rexAcceptable.matches(fullText)) {
|
||||
showSnack(dialog, R.string.error_unacceptable_character)
|
||||
return@InputFilter ""
|
||||
}
|
||||
if (!fullText.contains(rexPartialIp)) {
|
||||
showSnack(dialog, R.string.error_invalid_ip_address)
|
||||
return@InputFilter ""
|
||||
}
|
||||
if (!fullText.contains(Regex(rexColon))) {
|
||||
fullText.split(Regex(rexDot)).dropLastWhile { it.isEmpty() }.forEach { s ->
|
||||
if (s.toIntOrNull()?.let { it <= 255 } != true) {
|
||||
showSnack(dialog, R.string.error_dot_decimal_notation_num_over_255)
|
||||
return@InputFilter ""
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!fullText.matches(rexFullIpWithColon)) {
|
||||
if (!dest.substring(0, dstart).contains(Regex(rexColon)) && dend == dest.length) {
|
||||
showSnack(dialog, R.string.error_colon_must_follow_a_valid_ip_address)
|
||||
} else {
|
||||
showSnack(dialog, R.string.error_invalid_ip_address)
|
||||
}
|
||||
return@InputFilter ""
|
||||
}
|
||||
fullText.split(Regex("$rexDot|$rexColon")).dropLastWhile { it.isEmpty() }.forEachIndexed { index, s ->
|
||||
if (index < 4 && s.toIntOrNull()?.let { it <= 255 } != true) {
|
||||
showSnack(dialog, R.string.error_dot_decimal_notation_num_over_255)
|
||||
return@InputFilter ""
|
||||
}
|
||||
if (index >= 4 && s.toIntOrNull()?.let { it <= 65535 } != true) {
|
||||
showSnack(dialog, R.string.error_port_num_over_65535)
|
||||
return@InputFilter ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return@InputFilter source.replace(rexDot.toRegex(), ".")
|
||||
return@InputFilter source
|
||||
.replace(Regex("$rexDot+"), ".")
|
||||
.replace(Regex("$rexColon+"), ":")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSnack(dialog: MaterialDialog, strRes: Int) {
|
||||
ViewUtils.showSnack(dialog.view, dialog.context.getString(strRes))
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun connectToRemoteServer(dialog: MaterialDialog) {
|
||||
val input = dialog.inputEditText?.text?.toString() ?: ""
|
||||
if (!rexValidIp.matches(input)) {
|
||||
if (input.isEmpty()) {
|
||||
ViewUtils.showSnack(dialog.view, dialog.context.getString(R.string.error_ip_address_should_not_be_empty))
|
||||
} else {
|
||||
ViewUtils.showSnack(dialog.view, dialog.context.getString(R.string.error_invalid_ip_address))
|
||||
}
|
||||
return
|
||||
}
|
||||
dialog.dismiss()
|
||||
devPlugin
|
||||
.connectToRemoteServer(input, mClientModeItem)
|
||||
.subscribe({ Pref.setServerAddress(input) }, onConnectionException)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val rexDot = "[,.,。\\u0020]"
|
||||
const val rexColon = "[::]"
|
||||
|
||||
private const val rexIpDec = "\\d{1,3}"
|
||||
private const val rexPort = "\\d{1,5}"
|
||||
|
||||
val rexPartialIp = Regex("^$rexIpDec($rexDot($rexIpDec($rexDot($rexIpDec($rexDot($rexIpDec)?)?)?)?)?)?")
|
||||
val rexFullIpWithColon = Regex("\\d+$rexDot\\d+$rexDot\\d+$rexDot\\d+$rexColon\\d*")
|
||||
val rexValidIp = Regex("$rexIpDec$rexDot$rexIpDec$rexDot$rexIpDec$rexDot$rexIpDec($rexColon$rexPort)?")
|
||||
val rexAcceptable = Regex("($rexDot|$rexColon|\\d)+")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package org.autojs.autojs.app.tool
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import org.autojs.autojs.util.Observers
|
||||
import org.autojs.autojs.util.ViewUtils
|
||||
|
||||
class JsonSocketServerTool(context: Context) : AbstractJsonSocketTool(context) {
|
||||
|
||||
@@ -19,7 +20,11 @@ class JsonSocketServerTool(context: Context) : AbstractJsonSocketTool(context) {
|
||||
override fun connect() {
|
||||
devPlugin
|
||||
.enableLocalServer()
|
||||
.subscribe(Observers.emptyConsumer(), onConnectionException)
|
||||
.subscribe(Observers.emptyConsumer()) {
|
||||
disconnect()
|
||||
ViewUtils.showToast(context, it.message)
|
||||
onConnectionException.accept(it)
|
||||
}
|
||||
isNormallyClosed = false
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package org.autojs.autojs.core.accessibility
|
||||
|
||||
import android.accessibilityservice.AccessibilityService
|
||||
@@ -32,7 +34,7 @@ import org.autojs.autojs.util.DeveloperUtils
|
||||
*/
|
||||
class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge, private val scriptRuntime: ScriptRuntime) {
|
||||
|
||||
private val globalActionAutomatorRaw: GlobalActionAutomator by lazy {
|
||||
private val globalActionAutomatorRaw by lazy {
|
||||
GlobalActionAutomator(Handler(scriptRuntime.loopers.servantLooper)) {
|
||||
ensureService()
|
||||
accessibilityBridge.service!!
|
||||
@@ -101,96 +103,78 @@ class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge
|
||||
fun appendText(target: ActionTarget, text: String) = performAction(target.createAction(UiObject.ACTION_APPEND_TEXT, text))
|
||||
|
||||
@ScriptInterface
|
||||
fun back(): Boolean = globalActionAutomator.back()
|
||||
fun back() = globalActionAutomator.back()
|
||||
|
||||
@ScriptInterface
|
||||
fun home(): Boolean = globalActionAutomator.home()
|
||||
fun home() = globalActionAutomator.home()
|
||||
|
||||
@ScriptInterface
|
||||
fun recents(): Boolean = globalActionAutomator.recents()
|
||||
fun recents() = globalActionAutomator.recents()
|
||||
|
||||
@ScriptInterface
|
||||
fun notifications(): Boolean = globalActionAutomator.notifications()
|
||||
fun notifications() = globalActionAutomator.notifications()
|
||||
|
||||
@ScriptInterface
|
||||
fun quickSettings(): Boolean = globalActionAutomator.quickSettings()
|
||||
fun quickSettings() = globalActionAutomator.quickSettings()
|
||||
|
||||
@ScriptInterface
|
||||
fun powerDialog(): Boolean = globalActionAutomator.powerDialog()
|
||||
fun powerDialog() = globalActionAutomator.powerDialog()
|
||||
|
||||
@ScriptInterface
|
||||
fun splitScreen(): Boolean = globalActionAutomator.splitScreen()
|
||||
fun splitScreen() = globalActionAutomator.splitScreen()
|
||||
|
||||
@ScriptInterface
|
||||
fun lockScreen(): Boolean = globalActionAutomator.lockScreen()
|
||||
fun lockScreen() = globalActionAutomator.lockScreen()
|
||||
|
||||
@ScriptInterface
|
||||
fun takeScreenshot(): Boolean = globalActionAutomator.takeScreenshot()
|
||||
fun takeScreenshot() = globalActionAutomator.takeScreenshot()
|
||||
|
||||
@ScriptInterface
|
||||
fun headsethook(): Boolean = globalActionAutomator.headsethook()
|
||||
fun headsethook() = globalActionAutomator.headsethook()
|
||||
|
||||
@ScriptInterface
|
||||
fun accessibilityButton(): Boolean = globalActionAutomator.accessibilityButton()
|
||||
fun accessibilityButton() = globalActionAutomator.accessibilityButton()
|
||||
|
||||
@ScriptInterface
|
||||
fun accessibilityButtonChooser(): Boolean = globalActionAutomator.accessibilityButtonChooser()
|
||||
fun accessibilityButtonChooser() = globalActionAutomator.accessibilityButtonChooser()
|
||||
|
||||
@ScriptInterface
|
||||
fun accessibilityShortcut(): Boolean = globalActionAutomator.accessibilityShortcut()
|
||||
fun accessibilityShortcut() = globalActionAutomator.accessibilityShortcut()
|
||||
|
||||
@ScriptInterface
|
||||
fun accessibilityAllApps(): Boolean = globalActionAutomator.accessibilityAllApps()
|
||||
fun accessibilityAllApps() = globalActionAutomator.accessibilityAllApps()
|
||||
|
||||
@ScriptInterface
|
||||
fun dismissNotificationShade(): Boolean = globalActionAutomator.dismissNotificationShade()
|
||||
fun dismissNotificationShade() = globalActionAutomator.dismissNotificationShade()
|
||||
|
||||
@ScriptInterface
|
||||
fun gesture(start: Long, duration: Long, vararg points: IntArray): Boolean {
|
||||
return globalActionAutomatorForGesture.gesture(start, duration, *points)
|
||||
}
|
||||
fun gesture(start: Long, duration: Long, vararg points: IntArray) = globalActionAutomatorForGesture.gesture(start, duration, *points)
|
||||
|
||||
@ScriptInterface
|
||||
fun gestureAsync(start: Long, duration: Long, vararg points: IntArray) {
|
||||
globalActionAutomatorForGesture.gestureAsync(start, duration, *points)
|
||||
}
|
||||
fun gestureAsync(start: Long, duration: Long, vararg points: IntArray) = globalActionAutomatorForGesture.gestureAsync(start, duration, *points)
|
||||
|
||||
@ScriptInterface
|
||||
fun gestures(strokes: Any): Boolean {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return globalActionAutomatorForGesture.gestures(*strokes as Array<GestureDescription.StrokeDescription>)
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun gestures(strokes: Any) = globalActionAutomatorForGesture.gestures(*strokes as Array<GestureDescription.StrokeDescription>)
|
||||
|
||||
@ScriptInterface
|
||||
fun gesturesAsync(strokes: Any) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
globalActionAutomatorForGesture.gesturesAsync(*strokes as Array<GestureDescription.StrokeDescription>)
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun gesturesAsync(strokes: Any) = globalActionAutomatorForGesture.gesturesAsync(*strokes as Array<GestureDescription.StrokeDescription>)
|
||||
|
||||
@ScriptInterface
|
||||
fun click(x: Int, y: Int): Boolean {
|
||||
return globalActionAutomatorForGesture.click(x, y)
|
||||
}
|
||||
fun click(x: Int, y: Int) = globalActionAutomatorForGesture.click(x, y)
|
||||
|
||||
@ScriptInterface
|
||||
fun press(x: Int, y: Int, delay: Int): Boolean {
|
||||
return globalActionAutomatorForGesture.press(x, y, delay)
|
||||
}
|
||||
fun press(x: Int, y: Int, delay: Int) = globalActionAutomatorForGesture.press(x, y, delay)
|
||||
|
||||
@ScriptInterface
|
||||
fun longClick(x: Int, y: Int): Boolean {
|
||||
return globalActionAutomatorForGesture.longClick(x, y)
|
||||
}
|
||||
fun longClick(x: Int, y: Int) = globalActionAutomatorForGesture.longClick(x, y)
|
||||
|
||||
@ScriptInterface
|
||||
fun swipe(x1: Int, y1: Int, x2: Int, y2: Int, delay: Int): Boolean {
|
||||
return globalActionAutomatorForGesture.swipe(x1, y1, x2, y2, delay.toLong())
|
||||
}
|
||||
fun swipe(x1: Int, y1: Int, x2: Int, y2: Int, delay: Int) = globalActionAutomatorForGesture.swipe(x1, y1, x2, y2, delay.toLong())
|
||||
|
||||
@ScriptInterface
|
||||
fun paste(target: ActionTarget): Boolean {
|
||||
return performAction(target.createAction(AccessibilityNodeInfo.ACTION_PASTE))
|
||||
}
|
||||
fun paste(target: ActionTarget) = performAction(target.createAction(AccessibilityNodeInfo.ACTION_PASTE))
|
||||
|
||||
@ScriptInterface
|
||||
fun isServiceRunning() = isRunning()
|
||||
@@ -204,16 +188,7 @@ class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge
|
||||
return false
|
||||
}
|
||||
return accessibilityBridge.windowRoots().filterNotNull().let { roots ->
|
||||
when {
|
||||
roots.isEmpty() -> false
|
||||
else -> {
|
||||
var succeed = true
|
||||
roots.forEach { root ->
|
||||
simpleAction.perform(UiObject.createRoot(root)).also { succeed = succeed and it }
|
||||
}
|
||||
succeed
|
||||
}
|
||||
}
|
||||
roots.isNotEmpty() && roots.map { root -> simpleAction.perform(UiObject.createRoot(root)) }.all { it /* == true */ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,16 +201,23 @@ class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge
|
||||
ScriptRuntime.requiresApi(Build.VERSION_CODES.R)
|
||||
ensureService()
|
||||
|
||||
val promiseAdapter = mPromiseAdapter ?: ScriptPromiseAdapter()
|
||||
mPromiseAdapter = promiseAdapter
|
||||
|
||||
val promiseAdapter = mPromiseAdapter ?: ScriptPromiseAdapter().also { mPromiseAdapter = it }
|
||||
val service = accessibilityBridge.service!!
|
||||
val executor = service.mainExecutor
|
||||
val callback = object : AccessibilityService.TakeScreenshotCallback {
|
||||
override fun onSuccess(screenshot: AccessibilityService.ScreenshotResult) {
|
||||
val bitmap = Bitmap.wrapHardwareBuffer(screenshot.hardwareBuffer, screenshot.colorSpace)
|
||||
val imageWrapper = ImageWrapper.ofBitmap(bitmap)
|
||||
promiseAdapter.resolve(imageWrapper)
|
||||
val hardwareBuffer = Bitmap.wrapHardwareBuffer(screenshot.hardwareBuffer, screenshot.colorSpace)
|
||||
|
||||
// @Hint by SuperMonster003 on Jun 9, 2023.
|
||||
// ! To avoid the exception as below.
|
||||
// !
|
||||
// ! Wrapped java.lang.IllegalStateException: unable to getPixel(), pixel access is not supported on Config#HARDWARE bitmaps.
|
||||
// !
|
||||
// ! Reference: https://stackoverflow.com/questions/60462841/
|
||||
val bitmap = hardwareBuffer?.copy(Bitmap.Config.ARGB_8888, true)
|
||||
|
||||
hardwareBuffer?.recycle()
|
||||
promiseAdapter.resolve(ImageWrapper.ofBitmap(bitmap))
|
||||
mPromiseAdapter = null
|
||||
}
|
||||
|
||||
@@ -245,7 +227,7 @@ class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge
|
||||
captureScreen()
|
||||
}, 50)
|
||||
} else {
|
||||
Log.w(SimpleActionAutomator::class.java.name, "onFailure: $errorCode")
|
||||
Log.w(TAG, "onFailure: $errorCode")
|
||||
promiseAdapter.resolve(null)
|
||||
}
|
||||
}
|
||||
@@ -256,4 +238,10 @@ class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge
|
||||
return promiseAdapter
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val TAG: String = SimpleActionAutomator::class.java.name
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.autojs.autojs.runtime.api.ScreenMetrics
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/5/16.
|
||||
* Modified by SuperMonster003 as of Dec 1, 2021.
|
||||
*/
|
||||
class GlobalActionAutomator(private val mHandler: Handler?, private val serviceProvider: () -> AccessibilityService) {
|
||||
|
||||
@@ -102,10 +103,7 @@ class GlobalActionAutomator(private val mHandler: Handler?, private val serviceP
|
||||
false
|
||||
}
|
||||
|
||||
fun gesture(start: Long, duration: Long, vararg points: IntArray): Boolean {
|
||||
val path = pointsToPath(points)
|
||||
return gestures(GestureDescription.StrokeDescription(path, start, duration))
|
||||
}
|
||||
fun gesture(start: Long, duration: Long, vararg points: IntArray) = gestures(GestureDescription.StrokeDescription(pointsToPath(points), start, duration))
|
||||
|
||||
private fun pointsToPath(points: Array<out IntArray>): Path {
|
||||
val path = Path()
|
||||
@@ -122,17 +120,9 @@ class GlobalActionAutomator(private val mHandler: Handler?, private val serviceP
|
||||
gesturesAsync(GestureDescription.StrokeDescription(path, start, duration))
|
||||
}
|
||||
|
||||
fun gestures(vararg strokes: GestureDescription.StrokeDescription): Boolean {
|
||||
val builder = GestureDescription.Builder()
|
||||
for (stroke in strokes) {
|
||||
builder.addStroke(stroke)
|
||||
}
|
||||
val handler = mHandler
|
||||
return if (handler == null) {
|
||||
gesturesWithoutHandler(builder.build())
|
||||
} else {
|
||||
gesturesWithHandler(handler, builder.build())
|
||||
}
|
||||
fun gestures(vararg strokes: GestureDescription.StrokeDescription) = GestureDescription.Builder().let { builder ->
|
||||
val built = strokes.forEach { builder.addStroke(it) }.let { builder.build() }
|
||||
mHandler?.let { gesturesWithHandler(it, built) } ?: gesturesWithoutHandler(built)
|
||||
}
|
||||
|
||||
private fun gesturesWithHandler(handler: Handler, description: GestureDescription): Boolean {
|
||||
@@ -152,9 +142,7 @@ class GlobalActionAutomator(private val mHandler: Handler?, private val serviceP
|
||||
private fun gesturesWithoutHandler(description: GestureDescription): Boolean {
|
||||
prepareLooperIfNeeded()
|
||||
val result = VolatileBox(false)
|
||||
val myLooper = Looper.myLooper()
|
||||
if (myLooper != null) {
|
||||
val handler = Handler(myLooper)
|
||||
Looper.myLooper()?.let { myLooper ->
|
||||
service.dispatchGesture(description, object : GestureResultCallback() {
|
||||
override fun onCompleted(gestureDescription: GestureDescription) {
|
||||
result.set(true)
|
||||
@@ -165,41 +153,37 @@ class GlobalActionAutomator(private val mHandler: Handler?, private val serviceP
|
||||
result.set(false)
|
||||
quitLoop()
|
||||
}
|
||||
}, handler)
|
||||
}, Handler(myLooper))
|
||||
}
|
||||
Looper.loop()
|
||||
return result.get()
|
||||
}
|
||||
|
||||
fun gesturesAsync(vararg strokes: GestureDescription.StrokeDescription) {
|
||||
val builder = GestureDescription.Builder()
|
||||
for (stroke in strokes) {
|
||||
builder.addStroke(stroke)
|
||||
GestureDescription.Builder().let { builder ->
|
||||
val built = strokes.forEach { builder.addStroke(it) }.let { builder.build() }
|
||||
service.dispatchGesture(built, null, null)
|
||||
}
|
||||
service.dispatchGesture(builder.build(), null, null)
|
||||
}
|
||||
|
||||
private fun quitLoop() {
|
||||
val looper = Looper.myLooper()
|
||||
looper?.quit()
|
||||
Looper.myLooper()?.quit()
|
||||
}
|
||||
|
||||
private fun prepareLooperIfNeeded() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare()
|
||||
}
|
||||
Looper.myLooper() ?: Looper.prepare()
|
||||
}
|
||||
|
||||
fun click(x: Int, y: Int): Boolean = press(x, y, ViewConfiguration.getTapTimeout() + 50)
|
||||
fun click(x: Int, y: Int) = press(x, y, ViewConfiguration.getTapTimeout() + 50)
|
||||
|
||||
fun press(x: Int, y: Int, delay: Int): Boolean = gesture(0, delay.toLong(), intArrayOf(x, y))
|
||||
fun press(x: Int, y: Int, delay: Int) = gesture(0, delay.toLong(), intArrayOf(x, y))
|
||||
|
||||
fun longClick(x: Int, y: Int): Boolean = gesture(0, (ViewConfiguration.getLongPressTimeout() + 200).toLong(), intArrayOf(x, y))
|
||||
fun longClick(x: Int, y: Int) = gesture(0, (ViewConfiguration.getLongPressTimeout() + 200).toLong(), intArrayOf(x, y))
|
||||
|
||||
private fun scaleX(x: Int): Int = mScreenMetrics?.scaleX(x) ?: x
|
||||
private fun scaleX(x: Int) = mScreenMetrics?.scaleX(x) ?: x
|
||||
|
||||
private fun scaleY(y: Int): Int = mScreenMetrics?.scaleX(y) ?: y
|
||||
private fun scaleY(y: Int) = mScreenMetrics?.scaleX(y) ?: y
|
||||
|
||||
fun swipe(x1: Int, y1: Int, x2: Int, y2: Int, delay: Long): Boolean = gesture(0, delay, intArrayOf(x1, y1), intArrayOf(x2, y2))
|
||||
fun swipe(x1: Int, y1: Int, x2: Int, y2: Int, delay: Long) = gesture(0, delay, intArrayOf(x1, y1), intArrayOf(x2, y2))
|
||||
|
||||
}
|
||||
|
||||
@@ -411,6 +411,7 @@ open class UiObject constructor(info: Any?, private val allocator: Accessibility
|
||||
// @Hint by SuperMonster003 on May 12, 2022.
|
||||
// ! Param root should be nullable because an exception
|
||||
// ! will happen on devices with API Level >= 31 (Android 12) [S].
|
||||
// !
|
||||
// ! Wrapped java.lang.NullPointerException: Parameter specified as non-null is null:
|
||||
// ! method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter root.
|
||||
@JvmStatic
|
||||
|
||||
@@ -14,6 +14,8 @@ import org.autojs.autojs.tool.UiHandler
|
||||
import org.autojs.autojs.ui.enhancedfloaty.FloatyService
|
||||
import org.autojs.autojs.ui.enhancedfloaty.ResizableExpandableFloatyWindow
|
||||
import org.autojs.autojs.ui.enhancedfloaty.gesture.DragGesture
|
||||
import org.autojs.autojs.util.ClipboardUtils
|
||||
import org.autojs.autojs.util.ViewUtils
|
||||
import org.autojs.autojs.util.ViewUtils.setViewMeasure
|
||||
import org.autojs.autojs6.R
|
||||
import org.opencv.core.Point
|
||||
@@ -41,6 +43,7 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
|
||||
@get:Synchronized
|
||||
private var mCountDownTimer: CountDownTimer? = null
|
||||
|
||||
private val context = uiHandler.context
|
||||
private val mLockWindowShow = Object()
|
||||
private val mLockWindowCreated = Object()
|
||||
private val mLockConsoleView = Object()
|
||||
@@ -48,7 +51,7 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
|
||||
private val mFloatyWindow: ResizableExpandableFloatyWindow
|
||||
private val mConsoleFloaty: ConsoleFloaty
|
||||
private val mInput: BlockingQueue<String> = ArrayBlockingQueue(1)
|
||||
private val mDisplayOverOtherAppsPerm = DisplayOverOtherAppsPermission(uiHandler.context)
|
||||
private val mDisplayOverOtherAppsPerm = DisplayOverOtherAppsPermission(context)
|
||||
|
||||
val logEntries = ArrayList<LogEntry>()
|
||||
|
||||
@@ -56,6 +59,9 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
|
||||
var isShowing = false
|
||||
private set
|
||||
|
||||
private val logEntriesJoint
|
||||
get() = logEntries.joinToString("\n") { it.content }
|
||||
|
||||
// val size: Size
|
||||
// get() = configurator.size ?: Size()
|
||||
//
|
||||
@@ -117,6 +123,26 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
|
||||
mLogListeners.forEach { it.get()?.onLogClear() }
|
||||
}
|
||||
|
||||
fun copyAll() {
|
||||
try {
|
||||
ClipboardUtils.setClip(context, logEntriesJoint)
|
||||
ViewUtils.showToast(context, R.string.text_already_copied_to_clip)
|
||||
} catch (_: Exception) {
|
||||
ViewUtils.showToast(context, R.string.text_failed)
|
||||
}
|
||||
}
|
||||
|
||||
fun export() {
|
||||
val sendIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(Intent.EXTRA_TEXT, logEntriesJoint)
|
||||
type = "text/plain"
|
||||
}
|
||||
context.startActivity(Intent.createChooser(sendIntent, null).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
})
|
||||
}
|
||||
|
||||
override fun show() = show(false)
|
||||
|
||||
@ScriptInterface
|
||||
@@ -188,7 +214,7 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
|
||||
}
|
||||
|
||||
private fun startFloatyService() {
|
||||
uiHandler.context.startService(Intent(uiHandler.context, FloatyService::class.java))
|
||||
context.startService(Intent(context, FloatyService::class.java))
|
||||
}
|
||||
|
||||
@ScriptInterface
|
||||
|
||||
@@ -8,11 +8,11 @@ import org.autojs.autojs6.R
|
||||
|
||||
class FloatingConsoleView : ConsoleView {
|
||||
|
||||
constructor(context: Context?) : super(context)
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
|
||||
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) : super(context, attrs, defStyleAttr)
|
||||
|
||||
init {
|
||||
setPinchToZoomEnabled(true)
|
||||
|
||||
317
app/src/main/java/org/autojs/autojs/core/crypto/Crypto.kt
Normal file
317
app/src/main/java/org/autojs/autojs/core/crypto/Crypto.kt
Normal file
@@ -0,0 +1,317 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package org.autojs.autojs.core.crypto
|
||||
|
||||
import android.util.Base64
|
||||
import org.autojs.autojs.AutoJs
|
||||
import org.autojs.autojs.annotation.ScriptInterface
|
||||
import org.autojs.autojs.util.ArrayUtils
|
||||
import org.mozilla.javascript.NativeArray
|
||||
import org.mozilla.javascript.NativeObject
|
||||
import org.mozilla.javascript.Undefined
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.OutputStream
|
||||
import java.io.Serializable
|
||||
import java.nio.charset.Charset
|
||||
import java.security.KeyFactory
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.MessageDigest
|
||||
import java.security.spec.AlgorithmParameterSpec
|
||||
import java.security.spec.PKCS8EncodedKeySpec
|
||||
import java.security.spec.X509EncodedKeySpec
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.CipherOutputStream
|
||||
import javax.crypto.KeyAgreement
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
/**
|
||||
* Created by SuperMonster003 on Jun 15, 2023.
|
||||
*/
|
||||
// @Reference to com.stardust.autojs.core.cypto.Crypto.class on Jun 15, 2023.
|
||||
// ! There is a strong possibility that "cypto" is a typo.
|
||||
// @Reference to Auto.js Pro 9.3.11 module __$crypto__.js on Jun 15, 2023.
|
||||
object Crypto {
|
||||
|
||||
private val scriptRuntime by lazy { AutoJs.instance.runtime }
|
||||
|
||||
private const val A = 97
|
||||
private const val F = 102
|
||||
private const val NINE = 57
|
||||
private const val ZERO = 48
|
||||
|
||||
private const val DEFAULT_DIGEST_ALGORITHM = "MD5"
|
||||
|
||||
private val HEX_DIGITS = "0123456789abcdef".toCharArray()
|
||||
|
||||
private fun singleHexToNumber(paramChar: Char): Byte {
|
||||
val byte = paramChar.lowercaseChar().code.toByte()
|
||||
if (byte in ZERO..NINE) {
|
||||
return (byte - ZERO).toByte()
|
||||
}
|
||||
if (byte in A..F) {
|
||||
return (byte - A + 10).toByte()
|
||||
}
|
||||
throw IllegalArgumentException("char: $paramChar")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromHex(hex: String): ByteArray {
|
||||
if (hex.length % 2 != 0) {
|
||||
throw IllegalArgumentException("The length of hex string is required to be even.")
|
||||
}
|
||||
val max = hex.length / 2
|
||||
val arrayOfByte = ByteArray(max)
|
||||
for (i in 0 until max) {
|
||||
val j = i * 2
|
||||
arrayOfByte[i] = (singleHexToNumber(hex[j]) * 16 + singleHexToNumber(hex[j + 1])).toByte()
|
||||
}
|
||||
return arrayOfByte
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toHex(bytes: ByteArray): String {
|
||||
val stringBuilder = StringBuilder(bytes.size * 2)
|
||||
bytes.indices.forEach { i ->
|
||||
stringBuilder.append(HEX_DIGITS[bytes[i].toInt() and 0xF0 ushr 4])
|
||||
stringBuilder.append(HEX_DIGITS[bytes[i].toInt() and 0xF])
|
||||
}
|
||||
return stringBuilder.toString()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun digest(message: String, algorithm: String = DEFAULT_DIGEST_ALGORITHM, options: NativeObject = NativeObject()): Serializable {
|
||||
val messageDigest = MessageDigest.getInstance(algorithm)
|
||||
input(message, options) { bytes: ByteArray, start: Int, length: Int ->
|
||||
messageDigest.update(/* input = */ bytes, /* offset = */ start, /* len = */ length)
|
||||
}
|
||||
return output(messageDigest.digest(), options, "hex")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun digest(message: String, options: NativeObject) = digest(message, DEFAULT_DIGEST_ALGORITHM, options)
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun encrypt(data: Any, key: Any, transformation: String, options: NativeObject = NativeObject()): Serializable {
|
||||
return cipher(data, Cipher.ENCRYPT_MODE, key, transformation, options)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun decrypt(data: Any, key: Any, transformation: String, options: NativeObject = NativeObject()): Serializable {
|
||||
return cipher(data, Cipher.DECRYPT_MODE, key, transformation, options)
|
||||
}
|
||||
|
||||
private fun cipher(data: Any, mode: Int, key: Any, transformation: String, options: NativeObject): Serializable {
|
||||
val niceKey = when (key) {
|
||||
is Key -> key
|
||||
is java.security.Key -> Key(key.encoded)
|
||||
else -> throw Exception("Unknown type of key: ${key::class.java}")
|
||||
}
|
||||
val cipher = Cipher.getInstance(transformation)
|
||||
when (val iv = options["iv"]) {
|
||||
is String -> cipher.init(mode, niceKey.toKeySpec(transformation), IvParameterSpec(iv.toByteArray()))
|
||||
is ByteArray -> cipher.init(mode, niceKey.toKeySpec(transformation), IvParameterSpec(iv))
|
||||
is NativeArray -> cipher.init(mode, niceKey.toKeySpec(transformation), IvParameterSpec(ArrayUtils.jsBytesToByteArray(iv)))
|
||||
is AlgorithmParameterSpec -> cipher.init(mode, niceKey.toKeySpec(transformation), iv)
|
||||
else -> cipher.init(mode, niceKey.toKeySpec(transformation))
|
||||
}
|
||||
if (options["output"] == "file") {
|
||||
val dest = options["dest"] as? String ?: throw IllegalArgumentException(
|
||||
"Property \"dest\" is required when writing output to a file"
|
||||
)
|
||||
val fos = FileOutputStream(scriptRuntime.files.path(dest))
|
||||
writeInputData(fos, cipher, data, options)
|
||||
}
|
||||
val bos = ByteArrayOutputStream()
|
||||
writeInputData(bos, cipher, data, options)
|
||||
return output(bos.toByteArray(), options, "bytes").also { bos.close() }
|
||||
}
|
||||
|
||||
private fun writeInputData(os: OutputStream, cipher: Cipher, data: Any, options: NativeObject) {
|
||||
val cos = CipherOutputStream(os, cipher)
|
||||
input(data, options) { bytes, start, length ->
|
||||
cos.write(bytes, start, length)
|
||||
}
|
||||
cos.close()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun generateKeyPair(algorithm: String, length: Int = 256): KeyPair {
|
||||
return KeyPairGenerator.getInstance(algorithm).apply {
|
||||
initialize(length)
|
||||
}.generateKeyPair().let {
|
||||
KeyPair(it.public.encoded, it.private.encoded).apply {
|
||||
keyPairGeneratorAlgorithm = algorithm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param input { ByteArray | NativeArray | String }
|
||||
*/
|
||||
private fun input(input: Any, options: NativeObject, callback: (bytes: ByteArray, start: Int, length: Int) -> Unit) {
|
||||
when (input) {
|
||||
is ByteArray -> callback(input, 0, input.size)
|
||||
is NativeArray -> callback(ArrayUtils.jsBytesToByteArray(input), 0, input.size)
|
||||
is String -> when (options["input"]) {
|
||||
"file" -> {
|
||||
val fis = FileInputStream(scriptRuntime.files.path(input))
|
||||
val buffer = ByteArray(4096)
|
||||
var read: Int
|
||||
while (fis.read(buffer).also { read = it } != -1) {
|
||||
callback(buffer, 0, read)
|
||||
}
|
||||
}
|
||||
"base64" -> Base64.decode(input, Base64.NO_WRAP).also {
|
||||
callback(it, 0, it.size)
|
||||
}
|
||||
"hex" -> fromHex(input).also {
|
||||
callback(it, 0, it.size)
|
||||
}
|
||||
else -> {
|
||||
val encoding = when (val optEncoding = options["encoding"]) {
|
||||
is String -> Charset.forName(optEncoding)
|
||||
else -> Charsets.UTF_8
|
||||
}
|
||||
input.toByteArray(encoding).also {
|
||||
callback(it, 0, it.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw Exception("Unknown type of input (${input.javaClass})")
|
||||
}
|
||||
}
|
||||
|
||||
private fun output(bytes: ByteArray, options: NativeObject, defaultFormat: String): Serializable {
|
||||
return when (options["output"]?.takeUnless { it is Undefined } ?: defaultFormat) {
|
||||
"bytes" -> bytes
|
||||
"base64" -> Base64.encodeToString(bytes, Base64.NO_WRAP)
|
||||
"string" -> {
|
||||
val encoding = when (val optEncoding = options["encoding"]) {
|
||||
is String -> Charset.forName(optEncoding)
|
||||
else -> Charsets.UTF_8
|
||||
}
|
||||
String(bytes, encoding)
|
||||
}
|
||||
else -> toHex(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param data { ByteArray | NativeArray | String }
|
||||
*/
|
||||
class Key internal constructor(data: Any, options: NativeObject, isPublic: Boolean?) {
|
||||
|
||||
val data: ByteArray
|
||||
|
||||
val keyPair: String?
|
||||
|
||||
init {
|
||||
this.keyPair = isPublic?.let {
|
||||
if (it) KEY_PAIR_PUBLIC else KEY_PAIR_PRIVATE
|
||||
} ?: (options["keyPair"] as? String) ?.also {
|
||||
if (it != KEY_PAIR_PUBLIC && it != KEY_PAIR_PRIVATE) {
|
||||
throw Exception("Unknown keyPair ($it)")
|
||||
}
|
||||
}
|
||||
val bos = ByteArrayOutputStream()
|
||||
input(data, options) { bytes, start, length ->
|
||||
bos.write(bytes, start, length)
|
||||
}
|
||||
this.data = bos.toByteArray()
|
||||
}
|
||||
|
||||
constructor(data: Any) : this(data, NativeObject())
|
||||
|
||||
constructor(data: Any, options: NativeObject) : this(data, options, null)
|
||||
|
||||
fun toKeySpec(transformation: String): java.security.Key {
|
||||
val i = transformation.indexOf('/')
|
||||
val algorithm = if (i >= 0) transformation.substring(0, i) else transformation
|
||||
if (algorithm == "RSA") {
|
||||
if (keyPair == KEY_PAIR_PUBLIC) {
|
||||
return KeyFactory.getInstance(algorithm).generatePublic(X509EncodedKeySpec(this.data))
|
||||
}
|
||||
if (keyPair == KEY_PAIR_PRIVATE) {
|
||||
return KeyFactory.getInstance(algorithm).generatePrivate(PKCS8EncodedKeySpec(this.data))
|
||||
}
|
||||
throw Exception("Unknown keyPair (${keyPair})")
|
||||
}
|
||||
return SecretKeySpec(this.data, algorithm)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val data = Base64.encodeToString(this.data, Base64.NO_WRAP)
|
||||
return this.keyPair?.let { "Key[${this.keyPair}]{data=\'$data\'}" } ?: "Key{data=\'$data\'}"
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val KEY_PAIR_PUBLIC = "public"
|
||||
private const val KEY_PAIR_PRIVATE = "private"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param publicKeyData { String | ByteArray | NativeArray }
|
||||
* @param privateKeyData { String | ByteArray | NativeArray }
|
||||
*/
|
||||
class KeyPair(publicKeyData: Any, privateKeyData: Any, options: NativeObject) {
|
||||
|
||||
@get:ScriptInterface
|
||||
val publicKey: Key
|
||||
|
||||
@get:ScriptInterface
|
||||
val privateKey: Key
|
||||
|
||||
internal var keyPairGeneratorAlgorithm: String? = null
|
||||
|
||||
/**
|
||||
* @param publicKeyData { String | ByteArray | NativeArray }
|
||||
* @param privateKeyData { String | ByteArray | NativeArray }
|
||||
*/
|
||||
constructor(publicKeyData: Any, privateKeyData: Any) : this(publicKeyData, privateKeyData, NativeObject())
|
||||
|
||||
init {
|
||||
this.publicKey = Key(publicKeyData, options, true)
|
||||
this.privateKey = Key(privateKeyData, options, false)
|
||||
}
|
||||
|
||||
fun toKeySpec(transformation: String): java.security.Key {
|
||||
val keyFactory = KeyFactory.getInstance(
|
||||
keyPairGeneratorAlgorithm
|
||||
?: throw Exception("keyPairGeneratorAlgorithm must be defined first")
|
||||
)
|
||||
|
||||
val x509KeySpec = X509EncodedKeySpec(publicKey.data)
|
||||
val pubKey = keyFactory.generatePublic(x509KeySpec)
|
||||
|
||||
val pkcs8KeySpec = PKCS8EncodedKeySpec(privateKey.data)
|
||||
val priKey = keyFactory.generatePrivate(pkcs8KeySpec)
|
||||
|
||||
val keyAgreement = KeyAgreement.getInstance(keyFactory.algorithm).apply {
|
||||
init(priKey)
|
||||
doPhase(pubKey, true)
|
||||
}
|
||||
|
||||
val i = transformation.indexOf('/')
|
||||
val cipherAlgorithm = if (i >= 0) transformation.substring(0, i) else transformation
|
||||
return keyAgreement.generateSecret(cipherAlgorithm)
|
||||
}
|
||||
|
||||
override fun toString() = """{
|
||||
| publicKey: $publicKey,
|
||||
| privateKey: $privateKey,
|
||||
|}""".trimMargin()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public class ScriptCanvas {
|
||||
return mCanvas;
|
||||
}
|
||||
|
||||
void setCanvas(Canvas canvas) {
|
||||
public void setCanvas(Canvas canvas) {
|
||||
mCanvas = canvas;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import kotlin.math.sqrt
|
||||
*/
|
||||
interface ColorDetector {
|
||||
|
||||
fun detectsColor(r: Int, g: Int, b: Int): Boolean
|
||||
fun detectColor(r: Int, g: Int, b: Int): Boolean
|
||||
|
||||
abstract class AbstractColorDetector(color: Int) : ColorDetector {
|
||||
protected open val colorR: Int = Color.red(color)
|
||||
@@ -26,19 +26,19 @@ interface ColorDetector {
|
||||
}
|
||||
|
||||
class EqualityDetector(color: Int) : AbstractColorDetector(color) {
|
||||
override fun detectsColor(r: Int, g: Int, b: Int): Boolean {
|
||||
override fun detectColor(r: Int, g: Int, b: Int): Boolean {
|
||||
return colorR == r && colorG == g && colorB == b
|
||||
}
|
||||
}
|
||||
|
||||
class DifferenceDetector(color: Int, private val threshold: Int) : AbstractColorDetector(color) {
|
||||
override fun detectsColor(r: Int, g: Int, b: Int): Boolean {
|
||||
override fun detectColor(r: Int, g: Int, b: Int): Boolean {
|
||||
return (abs(r - colorR) + abs(g - colorG) + abs(b - colorB)) / 3.0 <= threshold
|
||||
}
|
||||
}
|
||||
|
||||
class RGBDistanceDetector(color: Int, private val threshold: Int) : AbstractColorDetector(color) {
|
||||
override fun detectsColor(r: Int, g: Int, b: Int): Boolean {
|
||||
override fun detectColor(r: Int, g: Int, b: Int): Boolean {
|
||||
val dR = (r - colorR).toDouble()
|
||||
val dG = (g - colorG).toDouble()
|
||||
val dB = (b - colorB).toDouble()
|
||||
@@ -51,7 +51,7 @@ interface ColorDetector {
|
||||
override val colorG = color and 0x00ff00 shr 8
|
||||
override val colorB = color and 0xff
|
||||
|
||||
override fun detectsColor(r: Int, g: Int, b: Int): Boolean {
|
||||
override fun detectColor(r: Int, g: Int, b: Int): Boolean {
|
||||
val dR = (r - colorR).toDouble()
|
||||
val dG = (g - colorG).toDouble()
|
||||
val dB = (b - colorB).toDouble()
|
||||
@@ -73,7 +73,7 @@ interface ColorDetector {
|
||||
}
|
||||
|
||||
class HDistanceDetector(color: Int, private val threshold: Int) : AbstractColorDetector(color) {
|
||||
override fun detectsColor(r: Int, g: Int, b: Int): Boolean {
|
||||
override fun detectColor(r: Int, g: Int, b: Int): Boolean {
|
||||
// @Hint by SuperMonster003 on Feb 17, 2023.
|
||||
// ! Code snippet in Auto.js 4.1.1 alpha2:
|
||||
// !
|
||||
@@ -98,7 +98,7 @@ interface ColorDetector {
|
||||
|
||||
constructor(color: Int, similarity: Float) : this(color, ((1.0f - similarity) * 255).roundToInt())
|
||||
|
||||
override fun detectsColor(r: Int, g: Int, b: Int): Boolean {
|
||||
override fun detectColor(r: Int, g: Int, b: Int): Boolean {
|
||||
val hs = getHnS(r, g, b)
|
||||
val dH = (hs and 0xffffffffL) - h
|
||||
val dS = (hs shr 32 and 0xffffffffL) - s
|
||||
|
||||
@@ -2,24 +2,27 @@ package org.autojs.autojs.core.image;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
import org.autojs.autojs.annotation.CodeAuthor;
|
||||
import org.autojs.autojs.annotation.ScriptInterface;
|
||||
import org.autojs.autojs.core.opencv.Mat;
|
||||
import org.autojs.autojs.core.opencv.MatOfPoint;
|
||||
import org.autojs.autojs.core.opencv.OpenCVHelper;
|
||||
import org.autojs.autojs.runtime.api.ScreenMetrics;
|
||||
|
||||
import org.autojs.autojs.core.opencv.Mat;
|
||||
import org.opencv.core.Core;
|
||||
|
||||
import org.opencv.core.Point;
|
||||
import org.opencv.core.Rect;
|
||||
import org.opencv.core.Scalar;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/5/18.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ColorFinder {
|
||||
|
||||
private final ScreenMetrics mScreenMetrics;
|
||||
@@ -28,19 +31,23 @@ public class ColorFinder {
|
||||
mScreenMetrics = screenMetrics;
|
||||
}
|
||||
|
||||
public Point findColorEquals(ImageWrapper imageWrapper, int color) {
|
||||
return findColorEquals(imageWrapper, color, null);
|
||||
@ScriptInterface
|
||||
public Point findPointByColor(ImageWrapper imageWrapper, int color) {
|
||||
return findPointByColor(imageWrapper, color, null);
|
||||
}
|
||||
|
||||
public Point findColorEquals(ImageWrapper imageWrapper, int color, Rect region) {
|
||||
return findColor(imageWrapper, color, 0, region);
|
||||
@ScriptInterface
|
||||
public Point findPointByColor(ImageWrapper imageWrapper, int color, Rect region) {
|
||||
return findPointByColor(imageWrapper, color, 0, region);
|
||||
}
|
||||
|
||||
public Point findColor(ImageWrapper imageWrapper, int color, int threshold) {
|
||||
return findColor(imageWrapper, color, threshold, null);
|
||||
@ScriptInterface
|
||||
public Point findPointByColor(ImageWrapper imageWrapper, int color, int threshold) {
|
||||
return findPointByColor(imageWrapper, color, threshold, null);
|
||||
}
|
||||
|
||||
public Point findColor(ImageWrapper image, int color, int threshold, Rect rect) {
|
||||
@ScriptInterface
|
||||
public Point findPointByColor(ImageWrapper image, int color, int threshold, Rect rect) {
|
||||
MatOfPoint matOfPoint = findColorInner(image, color, threshold, rect);
|
||||
image.shoot();
|
||||
if (matOfPoint == null) {
|
||||
@@ -55,7 +62,8 @@ public class ColorFinder {
|
||||
return point;
|
||||
}
|
||||
|
||||
public Point[] findAllPointsForColor(ImageWrapper image, int color, int threshold, Rect rect) {
|
||||
@ScriptInterface
|
||||
public Point[] findPointsByColor(ImageWrapper image, int color, int threshold, Rect rect) {
|
||||
MatOfPoint matOfPoint = findColorInner(image, color, threshold, rect);
|
||||
image.shoot();
|
||||
if (matOfPoint == null) {
|
||||
@@ -98,35 +106,44 @@ public class ColorFinder {
|
||||
return result;
|
||||
}
|
||||
|
||||
public Point findMultiColors(ImageWrapper image, int firstColor, int threshold, Rect rect, int[] points) {
|
||||
@ScriptInterface
|
||||
public Point findPointByColors(ImageWrapper image, int firstColor, int threshold, Rect rect, int[] points) {
|
||||
Point[] firstPoints = findPointsByColor(image, firstColor, threshold, rect);
|
||||
image.shoot();
|
||||
return Arrays.stream(firstPoints)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(firstPoint -> checksPath(image, firstPoint, threshold, points))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@ScriptInterface
|
||||
public Point[] findPointsByColors(ImageWrapper image, int firstColor, int threshold, Rect rect, int[] points) {
|
||||
Point[] firstPoints = findPointsByColor(image, firstColor, threshold, rect);
|
||||
image.shoot();
|
||||
return Arrays.stream(firstPoints)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(firstPoint -> checksPath(image, firstPoint, threshold, points))
|
||||
.toArray(Point[]::new);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ScriptInterface
|
||||
@SuppressWarnings("deprecation")
|
||||
@CodeAuthor(name = "LYS", homepage = "https://github.com/LYS86")
|
||||
public Point[] findAllMultiColors(ImageWrapper image, int firstColor, int threshold, Rect rect, int[] points) {
|
||||
Point[] firstPoints = findAllPointsForColor(image, firstColor, threshold, rect);
|
||||
Point result = null;
|
||||
List<Point> resultPoints = new ArrayList<>();
|
||||
for (Point firstPoint : firstPoints) {
|
||||
if (firstPoint != null) {
|
||||
if (checksPath(image, firstPoint, threshold, points)) {
|
||||
result = firstPoint;
|
||||
break;
|
||||
resultPoints.add(firstPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
image.shoot();
|
||||
return result;
|
||||
return resultPoints.toArray(new Point[0]);
|
||||
}
|
||||
|
||||
public Point[] findAllMultiColors(ImageWrapper image, int firstColor, int threshold, Rect rect, int[] points) {
|
||||
Point[] firstPoints = findAllPointsForColor(image, firstColor, threshold, rect);
|
||||
List<Point> resultPoints = new ArrayList<>();
|
||||
for (Point firstPoint : firstPoints) {
|
||||
if (firstPoint != null) {
|
||||
if (checksPath(image, firstPoint, threshold, points)) {
|
||||
resultPoints.add(firstPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
image.shoot();
|
||||
return resultPoints.toArray(new Point[0]);
|
||||
}
|
||||
|
||||
|
||||
private boolean checksPath(ImageWrapper image, Point startingPoint, int threshold, int[] points) {
|
||||
for (int i = 0; i < points.length; i += 3) {
|
||||
@@ -140,10 +157,49 @@ public class ColorFinder {
|
||||
return false;
|
||||
}
|
||||
int c = image.pixel(x, y);
|
||||
if (!colorDetector.detectsColor(Color.red(c), Color.green(c), Color.blue(c))) {
|
||||
if (!colorDetector.detectColor(Color.red(c), Color.green(c), Color.blue(c))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ScriptInterface
|
||||
@SuppressWarnings("deprecation")
|
||||
public Point findColorEquals(ImageWrapper imageWrapper, int color) {
|
||||
return findColorEquals(imageWrapper, color, null);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ScriptInterface
|
||||
@SuppressWarnings("deprecation")
|
||||
public Point findColorEquals(ImageWrapper imageWrapper, int color, Rect region) {
|
||||
return findColor(imageWrapper, color, 0, region);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ScriptInterface
|
||||
public Point findColor(ImageWrapper imageWrapper, int color, int threshold) {
|
||||
return findPointByColor(imageWrapper, color, threshold);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ScriptInterface
|
||||
public Point findColor(ImageWrapper image, int color, int threshold, Rect rect) {
|
||||
return findPointByColor(image, color, threshold, rect);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ScriptInterface
|
||||
public Point[] findAllPointsForColor(ImageWrapper image, int color, int threshold, Rect rect) {
|
||||
return findPointsByColor(image, color, threshold, rect);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ScriptInterface
|
||||
public Point findMultiColors(ImageWrapper image, int firstColor, int threshold, Rect rect, int[] points) {
|
||||
return findPointByColors(image, firstColor, threshold, rect, points);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,19 +12,23 @@ public class LooperHelper {
|
||||
private static final ConcurrentHashMap<Thread, Looper> sLoopers = new ConcurrentHashMap<>();
|
||||
|
||||
public static void prepare() {
|
||||
if (Looper.myLooper() == Looper.getMainLooper())
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
return;
|
||||
if (Looper.myLooper() == null)
|
||||
}
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
Looper l = Looper.myLooper();
|
||||
if (l != null)
|
||||
if (l != null) {
|
||||
sLoopers.put(Thread.currentThread(), l);
|
||||
}
|
||||
}
|
||||
|
||||
public static void quitForThread(Thread thread) {
|
||||
Looper looper = sLoopers.remove(thread);
|
||||
if (looper != null && looper != Looper.getMainLooper())
|
||||
looper.quit();
|
||||
if (looper != null && looper != Looper.getMainLooper()) {
|
||||
looper.quitSafely();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean contains(Thread thread) {
|
||||
|
||||
211
app/src/main/java/org/autojs/autojs/core/looper/Loopers.java
Normal file
211
app/src/main/java/org/autojs/autojs/core/looper/Loopers.java
Normal file
@@ -0,0 +1,211 @@
|
||||
package org.autojs.autojs.core.looper;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.MessageQueue;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.autojs.autojs.lang.ThreadCompat;
|
||||
import org.autojs.autojs.rhino.AutoJsContext;
|
||||
import org.autojs.autojs.runtime.ScriptRuntime;
|
||||
import org.autojs.autojs.runtime.api.Threads;
|
||||
import org.autojs.autojs.runtime.api.Timers;
|
||||
import org.autojs.autojs.runtime.exception.ScriptInterruptedException;
|
||||
import org.mozilla.javascript.Context;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/7/29.
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public class Loopers implements MessageQueue.IdleHandler {
|
||||
|
||||
private static final String LOG_TAG = "Loopers";
|
||||
|
||||
public interface LooperQuitHandler {
|
||||
boolean shouldQuit();
|
||||
}
|
||||
|
||||
private static final Runnable EMPTY_RUNNABLE = () -> {
|
||||
};
|
||||
|
||||
private final ThreadLocal<Boolean> waitWhenIdle = new ThreadLocal<>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected Boolean initialValue() {
|
||||
return Looper.myLooper() == Looper.getMainLooper();
|
||||
}
|
||||
};
|
||||
private final ThreadLocal<HashSet<Integer>> waitIds = new ThreadLocal<>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected HashSet<Integer> initialValue() {
|
||||
return new HashSet<>();
|
||||
}
|
||||
};
|
||||
private final ThreadLocal<Integer> maxWaitId = new ThreadLocal<>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected Integer initialValue() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
private final ThreadLocal<CopyOnWriteArrayList<LooperQuitHandler>> looperQuitHandlers = new ThreadLocal<>();
|
||||
private volatile Looper mServantLooper;
|
||||
private final Timers mTimers;
|
||||
private LooperQuitHandler mMainLooperQuitHandler;
|
||||
private final Handler mMainHandler;
|
||||
private final Looper mMainLooper;
|
||||
private final Threads mThreads;
|
||||
private final MessageQueue mMainMessageQueue;
|
||||
|
||||
public Loopers(ScriptRuntime runtime) {
|
||||
mTimers = runtime.timers;
|
||||
mThreads = runtime.threads;
|
||||
prepare();
|
||||
mMainLooper = Looper.myLooper();
|
||||
mMainHandler = new Handler();
|
||||
mMainMessageQueue = Looper.myQueue();
|
||||
}
|
||||
|
||||
|
||||
public Looper getMainLooper() {
|
||||
return mMainLooper;
|
||||
}
|
||||
|
||||
public void addLooperQuitHandler(LooperQuitHandler handler) {
|
||||
CopyOnWriteArrayList<LooperQuitHandler> handlers = looperQuitHandlers.get();
|
||||
if (handlers == null) {
|
||||
handlers = new CopyOnWriteArrayList<>();
|
||||
looperQuitHandlers.set(handlers);
|
||||
}
|
||||
handlers.add(handler);
|
||||
}
|
||||
|
||||
public boolean removeLooperQuitHandler(LooperQuitHandler handler) {
|
||||
CopyOnWriteArrayList<LooperQuitHandler> handlers = looperQuitHandlers.get();
|
||||
return handlers != null && handlers.remove(handler);
|
||||
}
|
||||
|
||||
private boolean shouldQuitLooper() {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
return true;
|
||||
}
|
||||
if (mTimers.hasPendingCallbacks()) {
|
||||
return false;
|
||||
}
|
||||
if (waitWhenIdle.get() || !waitIds.get().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (((AutoJsContext) Context.getCurrentContext()).hasPendingContinuation()) {
|
||||
return false;
|
||||
}
|
||||
CopyOnWriteArrayList<LooperQuitHandler> handlers = looperQuitHandlers.get();
|
||||
if (handlers == null) {
|
||||
return true;
|
||||
}
|
||||
for (LooperQuitHandler handler : handlers) {
|
||||
if (!handler.shouldQuit()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private void initServantThread() {
|
||||
final Object lock = Loopers.this;
|
||||
new ThreadCompat(() -> {
|
||||
Looper.prepare();
|
||||
mServantLooper = Looper.myLooper();
|
||||
synchronized (lock) {
|
||||
lock.notifyAll();
|
||||
}
|
||||
Looper.loop();
|
||||
}).start();
|
||||
}
|
||||
|
||||
public Looper getServantLooper() {
|
||||
if (mServantLooper == null) {
|
||||
initServantThread();
|
||||
synchronized (this) {
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new ScriptInterruptedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mServantLooper;
|
||||
}
|
||||
|
||||
private void quitServantLooper() {
|
||||
if (mServantLooper == null)
|
||||
return;
|
||||
mServantLooper.quit();
|
||||
}
|
||||
|
||||
public int waitWhenIdle() {
|
||||
int id = maxWaitId.get();
|
||||
Log.d(LOG_TAG, "waitWhenIdle: " + id);
|
||||
maxWaitId.set(id + 1);
|
||||
waitIds.get().add(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
public void doNotWaitWhenIdle(int waitId) {
|
||||
Log.d(LOG_TAG, "doNotWaitWhenIdle: " + waitId);
|
||||
waitIds.get().remove(waitId);
|
||||
}
|
||||
|
||||
public void waitWhenIdle(boolean b) {
|
||||
waitWhenIdle.set(b);
|
||||
}
|
||||
|
||||
public void recycle() {
|
||||
quitServantLooper();
|
||||
mMainMessageQueue.removeIdleHandler(this);
|
||||
}
|
||||
|
||||
public void setMainLooperQuitHandler(LooperQuitHandler mainLooperQuitHandler) {
|
||||
mMainLooperQuitHandler = mainLooperQuitHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean queueIdle() {
|
||||
Looper l = Looper.myLooper();
|
||||
if (l == null)
|
||||
return true;
|
||||
if (l == mMainLooper) {
|
||||
Log.d(LOG_TAG, "main looper queueIdle");
|
||||
if (shouldQuitLooper() && !mThreads.hasRunningThreads() &&
|
||||
mMainLooperQuitHandler != null && mMainLooperQuitHandler.shouldQuit()) {
|
||||
Log.d(LOG_TAG, "main looper quit");
|
||||
l.quit();
|
||||
}
|
||||
} else {
|
||||
Log.d(LOG_TAG, "looper queueIdle: " + l);
|
||||
if (shouldQuitLooper()) {
|
||||
l.quit();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void prepare() {
|
||||
if (Looper.myLooper() == null)
|
||||
LooperHelper.prepare();
|
||||
Looper.myQueue().addIdleHandler(this);
|
||||
}
|
||||
|
||||
public void notifyThreadExit(TimerThread thread) {
|
||||
Log.d(LOG_TAG, "notifyThreadExit: " + thread);
|
||||
//当子线程退成时,主线程需要检查自身是否退出(主线程在所有子线程执行完成后才能退出,如果主线程已经执行完任务仍然要等待所有子线程),
|
||||
//此时通过向主线程发送一个空的Runnable,主线程执行完这个Runnable后会触发IdleHandler,从而检查自身是否退出
|
||||
mMainHandler.post(EMPTY_RUNNABLE);
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
package org.autojs.autojs.core.looper
|
||||
|
||||
import android.os.Looper
|
||||
import android.os.MessageQueue
|
||||
import android.util.Log
|
||||
import org.autojs.autojs.lang.ThreadCompat
|
||||
import org.autojs.autojs.rhino.AutoJsContext
|
||||
import org.autojs.autojs.runtime.ScriptRuntime
|
||||
import org.autojs.autojs.runtime.exception.ScriptInterruptedException
|
||||
import org.mozilla.javascript.Context
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/7/29.
|
||||
*/
|
||||
/**
|
||||
* update by aiselp on 2023/6/4
|
||||
* 调整内容:
|
||||
* 使此类只负责单loop线程生命周期管理,移除繁琐的调用链
|
||||
* 调整timer由此类创建
|
||||
* 通过向此类添加AsyncTask以监听线程退出事件
|
||||
*/
|
||||
class Loopers(val runtime: ScriptRuntime) {
|
||||
@Deprecated("使用AsyncTask代替")
|
||||
interface LooperQuitHandler {
|
||||
fun shouldQuit(): Boolean
|
||||
}
|
||||
|
||||
open class AsyncTask(private val describe: String) {
|
||||
private val allBind = ConcurrentLinkedQueue<Loopers>()
|
||||
var isEnd: Boolean = false
|
||||
private set
|
||||
|
||||
//线程即将退出时调用,返回true阻止线程退出,只要有一个task返回true线程就不会退出
|
||||
open fun onFinish(loopers: Loopers): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
fun end() {
|
||||
isEnd = true
|
||||
}
|
||||
|
||||
//线程正在退出,这里应该结束任务的执行,回收资源
|
||||
open fun onStop(loopers: Loopers) {}
|
||||
override fun toString(): String {
|
||||
return "AsyncTask: $describe"
|
||||
}
|
||||
}
|
||||
|
||||
private var waitWhenIdle: Boolean
|
||||
|
||||
@Volatile
|
||||
private var mServantLooper: Looper? = null
|
||||
private var mMainLooperQuitHandler: LooperQuitHandler? = null
|
||||
private val allTasks = ConcurrentLinkedQueue<AsyncTask>()
|
||||
val mTimer: Timer
|
||||
val myLooper: Looper
|
||||
|
||||
init {
|
||||
prepare()
|
||||
myLooper = Looper.myLooper()!!
|
||||
mTimer = Timer(runtime, myLooper)
|
||||
waitWhenIdle = myLooper == Looper.getMainLooper()
|
||||
}
|
||||
|
||||
fun createAndAddAsyncTask(describe: String): AsyncTask {
|
||||
val task = AsyncTask(describe)
|
||||
allTasks.add(task)
|
||||
return task
|
||||
}
|
||||
|
||||
fun addAsyncTask(task: AsyncTask) {
|
||||
synchronized(myLooper) {
|
||||
allTasks.add(task)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeAsyncTask(task: AsyncTask) {
|
||||
synchronized(myLooper) {
|
||||
allTasks.remove(task)
|
||||
mTimer.post(EMPTY_RUNNABLE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkTask(): Boolean {
|
||||
allTasks.removeAll(allTasks.filter { it.isEnd }.toSet())
|
||||
for (task in allTasks) {
|
||||
if (task.onFinish(this)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun shouldQuitLooper(): Boolean {
|
||||
synchronized(myLooper) {
|
||||
if (Thread.currentThread().isInterrupted) return true
|
||||
if (mTimer.hasPendingCallbacks()) return false
|
||||
//检查是否有运行中的线程
|
||||
if (checkTask()) return false
|
||||
if (waitWhenIdle) return false
|
||||
if ((Context.getCurrentContext() as AutoJsContext).hasPendingContinuation()) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private fun initServantThread() {
|
||||
ThreadCompat {
|
||||
Looper.prepare()
|
||||
val lock = this@Loopers as Object
|
||||
mServantLooper = Looper.myLooper()
|
||||
synchronized(lock) { lock.notifyAll() }
|
||||
Looper.loop()
|
||||
}.start()
|
||||
}
|
||||
|
||||
val servantLooper: Looper
|
||||
get() {
|
||||
if (mServantLooper == null) {
|
||||
initServantThread()
|
||||
val lock = this as java.lang.Object
|
||||
synchronized(lock) {
|
||||
try {
|
||||
lock.wait()
|
||||
} catch (e: InterruptedException) {
|
||||
throw ScriptInterruptedException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return mServantLooper!!
|
||||
}
|
||||
|
||||
@Deprecated("使用AsyncTask代替")
|
||||
fun waitWhenIdle(b: Boolean) {
|
||||
waitWhenIdle = b
|
||||
}
|
||||
|
||||
fun recycle() {
|
||||
Log.d(LOG_TAG, "recycle")
|
||||
for (task in allTasks.filter { !it.isEnd }) {
|
||||
try {
|
||||
task.onStop(this)
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, e)
|
||||
}
|
||||
}
|
||||
mServantLooper?.quit()
|
||||
}
|
||||
|
||||
@Deprecated("使用AsyncTask代替")
|
||||
fun setMainLooperQuitHandler(mainLooperQuitHandler: LooperQuitHandler?) {
|
||||
mMainLooperQuitHandler = mainLooperQuitHandler
|
||||
}
|
||||
|
||||
private fun prepare() {
|
||||
if (Looper.myLooper() == null) LooperHelper.prepare()
|
||||
Looper.myQueue().addIdleHandler(MessageQueue.IdleHandler {
|
||||
if (this == runtime.loopers) {
|
||||
Log.d(LOG_TAG, "main looper queueIdle")
|
||||
if (shouldQuitLooper() &&
|
||||
mMainLooperQuitHandler != null &&
|
||||
mMainLooperQuitHandler!!.shouldQuit()
|
||||
) {
|
||||
Log.d(LOG_TAG, "main looper quit")
|
||||
Looper.myLooper()!!.quitSafely()
|
||||
}
|
||||
}else {
|
||||
Log.d(LOG_TAG, "looper queueIdle $this")
|
||||
if (shouldQuitLooper()) {
|
||||
Log.d(LOG_TAG, "looper quit $this")
|
||||
Looper.myLooper()!!.quitSafely()
|
||||
}
|
||||
}
|
||||
return@IdleHandler true
|
||||
})
|
||||
}
|
||||
|
||||
fun notifyThreadExit(thread: TimerThread) {
|
||||
Log.d(LOG_TAG, "notifyThreadExit: $thread")
|
||||
//当子线程退成时,主线程需要检查自身是否退出(主线程在所有子线程执行完成后才能退出,如果主线程已经执行完任务仍然要等待所有子线程),
|
||||
//此时通过向主线程发送一个空的Runnable,主线程执行完这个Runnable后会触发IdleHandler,从而检查自身是否退出
|
||||
//mHandler.post(EMPTY_RUNNABLE)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val LOG_TAG = "Loopers"
|
||||
private val EMPTY_RUNNABLE = Runnable {}
|
||||
}
|
||||
}
|
||||
131
app/src/main/java/org/autojs/autojs/core/looper/Timer.java
Normal file
131
app/src/main/java/org/autojs/autojs/core/looper/Timer.java
Normal file
@@ -0,0 +1,131 @@
|
||||
package org.autojs.autojs.core.looper;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import org.autojs.autojs.runtime.ScriptRuntime;
|
||||
import org.autojs.autojs.concurrent.VolatileBox;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/27.
|
||||
*/
|
||||
public class Timer {
|
||||
|
||||
private final SparseArray<Runnable> mHandlerCallbacks = new SparseArray<>();
|
||||
private int mCallbackMaxId = 0;
|
||||
private final ScriptRuntime mRuntime;
|
||||
private final Handler mHandler;
|
||||
private long mMaxCallbackUptimeMillis = 0;
|
||||
private final VolatileBox<Long> mMaxCallbackMillisForAllThread;
|
||||
|
||||
public Timer(ScriptRuntime runtime, VolatileBox<Long> maxCallbackMillisForAllThread) {
|
||||
mRuntime = runtime;
|
||||
mMaxCallbackMillisForAllThread = maxCallbackMillisForAllThread;
|
||||
mHandler = new Handler();
|
||||
}
|
||||
|
||||
public Timer(ScriptRuntime runtime, VolatileBox<Long> maxCallbackMillisForAllThread, Looper looper) {
|
||||
mRuntime = runtime;
|
||||
mMaxCallbackMillisForAllThread = maxCallbackMillisForAllThread;
|
||||
mHandler = new Handler(looper);
|
||||
}
|
||||
|
||||
public int setTimeout(final Object callback, final long delay, final Object... args) {
|
||||
mCallbackMaxId++;
|
||||
final int id = mCallbackMaxId;
|
||||
Runnable r = () -> {
|
||||
callFunction(callback, args);
|
||||
mHandlerCallbacks.remove(id);
|
||||
};
|
||||
mHandlerCallbacks.put(id, r);
|
||||
postDelayed(r, delay);
|
||||
return id;
|
||||
}
|
||||
|
||||
private void callFunction(Object callback, Object[] args) {
|
||||
if(Looper.myLooper() == Looper.getMainLooper()){
|
||||
try {
|
||||
mRuntime.bridges.callFunction(callback, null, args);
|
||||
}catch (Exception e){
|
||||
mRuntime.exit(e);
|
||||
}
|
||||
}else {
|
||||
mRuntime.bridges.callFunction(callback, null, args);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean clearTimeout(int id) {
|
||||
return clearCallback(id);
|
||||
}
|
||||
|
||||
public int setInterval(final Object listener, final long interval, final Object... args) {
|
||||
mCallbackMaxId++;
|
||||
final int id = mCallbackMaxId;
|
||||
final Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mHandlerCallbacks.get(id) == null)
|
||||
return;
|
||||
callFunction(listener, args);
|
||||
postDelayed(this, interval);
|
||||
}
|
||||
};
|
||||
mHandlerCallbacks.put(id, r);
|
||||
postDelayed(r, interval);
|
||||
return id;
|
||||
}
|
||||
|
||||
public void postDelayed(Runnable r, long interval) {
|
||||
long uptime = SystemClock.uptimeMillis() + interval;
|
||||
mHandler.postAtTime(r, uptime);
|
||||
mMaxCallbackUptimeMillis = Math.max(mMaxCallbackUptimeMillis, uptime);
|
||||
synchronized (mMaxCallbackMillisForAllThread) {
|
||||
mMaxCallbackMillisForAllThread.set(Math.max(mMaxCallbackMillisForAllThread.get(), uptime));
|
||||
}
|
||||
}
|
||||
|
||||
public void post(Runnable r) {
|
||||
|
||||
}
|
||||
|
||||
public boolean clearInterval(int id) {
|
||||
return clearCallback(id);
|
||||
}
|
||||
|
||||
public int setImmediate(final Object listener, final Object... args) {
|
||||
mCallbackMaxId++;
|
||||
final int id = mCallbackMaxId;
|
||||
Runnable r = () -> {
|
||||
callFunction(listener, args);
|
||||
mHandlerCallbacks.remove(id);
|
||||
};
|
||||
mHandlerCallbacks.put(id, r);
|
||||
postDelayed(r, 0);
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean clearImmediate(int id) {
|
||||
return clearCallback(id);
|
||||
}
|
||||
|
||||
private boolean clearCallback(int id) {
|
||||
Runnable callback = mHandlerCallbacks.get(id);
|
||||
if (callback != null) {
|
||||
mHandler.removeCallbacks(callback);
|
||||
mHandlerCallbacks.remove(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasPendingCallbacks() {
|
||||
return mMaxCallbackUptimeMillis > SystemClock.uptimeMillis();
|
||||
}
|
||||
|
||||
public void removeAllCallbacks() {
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package org.autojs.autojs.core.looper
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.SystemClock
|
||||
import org.autojs.autojs.runtime.ScriptRuntime
|
||||
import org.mozilla.javascript.BaseFunction
|
||||
import org.mozilla.javascript.Context
|
||||
import org.mozilla.javascript.Scriptable
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/27.
|
||||
*/
|
||||
class Timer(
|
||||
runtime: ScriptRuntime,
|
||||
looper: Looper
|
||||
) {
|
||||
private val myLooper: Looper = looper
|
||||
private val mHandlerCallbacks = ConcurrentHashMap<Int, Runnable?>()
|
||||
private val mRuntime: ScriptRuntime = runtime
|
||||
private val mHandler: Handler = Handler(looper)
|
||||
private val isUiLoop: Boolean = looper == Looper.getMainLooper()
|
||||
|
||||
constructor(runtime: ScriptRuntime, ) : this(runtime, Looper.myLooper()!!)
|
||||
|
||||
fun setTimeout(callback: Any, delay: Long, vararg args: Any?): Int {
|
||||
val id = createTimerId()
|
||||
val r = Runnable {
|
||||
callFunction(callback, null, args)
|
||||
mHandlerCallbacks.remove(id)
|
||||
}
|
||||
mHandlerCallbacks[id] = r
|
||||
postDelayed(r, delay)
|
||||
return id
|
||||
}
|
||||
|
||||
private fun callFunction(callback: Any, thiz: Any?, args :Any?) {
|
||||
val myArgs = args?.let { args as Array<*> }?: emptyArray<Any>()
|
||||
val map = myArgs.map { Context.javaToJS(it, callback as BaseFunction) }
|
||||
try {
|
||||
(callback as BaseFunction).call(Context.getCurrentContext(),callback.parentScope,
|
||||
thiz as? Scriptable?, map.toTypedArray()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
if (isUiLoop) {
|
||||
mRuntime.exit(e)
|
||||
} else throw e
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun createTimerId(): Int {
|
||||
var id: Int
|
||||
do {
|
||||
id = Random.nextInt()
|
||||
} while (mHandlerCallbacks.containsKey(id))
|
||||
mHandlerCallbacks[id] = EMPTY_RUNNABLE
|
||||
return id
|
||||
}
|
||||
|
||||
fun setInterval(listener: Any, interval: Long, vararg args: Any?): Int {
|
||||
val id = createTimerId()
|
||||
val r: Runnable = object : Runnable {
|
||||
override fun run() {
|
||||
if (mHandlerCallbacks[id] == null) return
|
||||
callFunction(listener, null, args)
|
||||
postDelayed(this, interval)
|
||||
}
|
||||
}
|
||||
mHandlerCallbacks[id] = r
|
||||
postDelayed(r, interval)
|
||||
return id
|
||||
}
|
||||
|
||||
fun postDelayed(r: Runnable, interval: Long) {
|
||||
synchronized(myLooper) {
|
||||
val uptime = SystemClock.uptimeMillis() + interval
|
||||
mHandler.postAtTime(r, uptime)
|
||||
}
|
||||
}
|
||||
|
||||
fun post(r: Runnable) {
|
||||
synchronized(myLooper) {
|
||||
mHandler.post(r)
|
||||
}
|
||||
}
|
||||
|
||||
fun clearInterval(id: Int): Boolean = clearCallback(id)
|
||||
fun clearImmediate(id: Int): Boolean = clearCallback(id)
|
||||
fun clearTimeout(id: Int): Boolean = clearCallback(id)
|
||||
|
||||
fun setImmediate(listener: Any, vararg args: Any?): Int {
|
||||
val id = createTimerId()
|
||||
val r = Runnable {
|
||||
callFunction(listener, null, args)
|
||||
mHandlerCallbacks.remove(id)
|
||||
}
|
||||
mHandlerCallbacks[id] = r
|
||||
post(r)
|
||||
return id
|
||||
}
|
||||
|
||||
|
||||
private fun clearCallback(id: Int): Boolean {
|
||||
val callback = mHandlerCallbacks[id]
|
||||
if (callback != null) {
|
||||
mHandler.removeCallbacks(callback)
|
||||
mHandlerCallbacks.remove(id)
|
||||
if (mHandlerCallbacks.isEmpty()) mHandler.post(EMPTY_RUNNABLE)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun hasPendingCallbacks(): Boolean {
|
||||
return mHandlerCallbacks.size > 0
|
||||
}
|
||||
|
||||
fun removeAllCallbacks() {
|
||||
mHandler.removeCallbacksAndMessages(null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val LOG_TAG = "Timer"
|
||||
private val EMPTY_RUNNABLE = Runnable {}
|
||||
}
|
||||
}
|
||||
134
app/src/main/java/org/autojs/autojs/core/looper/TimerThread.java
Normal file
134
app/src/main/java/org/autojs/autojs/core/looper/TimerThread.java
Normal file
@@ -0,0 +1,134 @@
|
||||
package org.autojs.autojs.core.looper;
|
||||
|
||||
import static org.autojs.autojs.util.StringUtils.str;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.autojs.autojs.concurrent.VolatileBox;
|
||||
import org.autojs.autojs.engine.RhinoJavaScriptEngine;
|
||||
import org.autojs.autojs.lang.ThreadCompat;
|
||||
import org.autojs.autojs.runtime.ScriptRuntime;
|
||||
import org.autojs.autojs.runtime.exception.ScriptInterruptedException;
|
||||
import org.autojs.autojs6.R;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/27.
|
||||
*/
|
||||
public class TimerThread extends ThreadCompat {
|
||||
|
||||
private static final ConcurrentHashMap<Thread, Timer> sTimerMap = new ConcurrentHashMap<>();
|
||||
|
||||
private Timer mTimer;
|
||||
private final VolatileBox<Long> mMaxCallbackUptimeMillisForAllThreads;
|
||||
private final ScriptRuntime mRuntime;
|
||||
private final Runnable mTarget;
|
||||
private boolean mRunning = false;
|
||||
private final Object mRunningLock = new Object();
|
||||
|
||||
public TimerThread(ScriptRuntime runtime, VolatileBox<Long> maxCallbackUptimeMillisForAllThreads, Runnable target) {
|
||||
super(target);
|
||||
mRuntime = runtime;
|
||||
mTarget = target;
|
||||
mMaxCallbackUptimeMillisForAllThreads = maxCallbackUptimeMillisForAllThreads;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mRuntime.loopers.prepare();
|
||||
mTimer = new Timer(mRuntime, mMaxCallbackUptimeMillisForAllThreads);
|
||||
sTimerMap.put(Thread.currentThread(), mTimer);
|
||||
((RhinoJavaScriptEngine) mRuntime.engines.myEngine()).enterContext();
|
||||
notifyRunning();
|
||||
new Handler().post(mTarget);
|
||||
try {
|
||||
Looper.loop();
|
||||
} catch (Throwable e) {
|
||||
if (!ScriptInterruptedException.causedByInterrupted(e)) {
|
||||
mRuntime.console.error(Thread.currentThread() + ": ", e);
|
||||
}
|
||||
} finally {
|
||||
onExit();
|
||||
mTimer = null;
|
||||
org.mozilla.javascript.Context.exit();
|
||||
sTimerMap.remove(Thread.currentThread(), mTimer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interrupt() {
|
||||
LooperHelper.quitForThread(this);
|
||||
super.interrupt();
|
||||
}
|
||||
|
||||
private void notifyRunning() {
|
||||
synchronized (mRunningLock) {
|
||||
mRunning = true;
|
||||
mRunningLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected void onExit() {
|
||||
mRuntime.loopers.notifyThreadExit(this);
|
||||
}
|
||||
|
||||
public static Timer getTimerForThread(Thread thread) {
|
||||
return sTimerMap.get(thread);
|
||||
}
|
||||
|
||||
public static Timer getTimerForCurrentThread() {
|
||||
return getTimerForThread(Thread.currentThread());
|
||||
}
|
||||
|
||||
public int setTimeout(Object callback, long delay, Object... args) {
|
||||
return getTimer().setTimeout(callback, delay, args);
|
||||
}
|
||||
|
||||
public Timer getTimer() {
|
||||
if (mTimer == null) {
|
||||
throw new IllegalStateException(str(R.string.error_thread_is_not_alive));
|
||||
}
|
||||
return mTimer;
|
||||
}
|
||||
|
||||
public boolean clearTimeout(int id) {
|
||||
return getTimer().clearTimeout(id);
|
||||
}
|
||||
|
||||
public int setInterval(Object listener, long interval, Object... args) {
|
||||
return getTimer().setInterval(listener, interval, args);
|
||||
}
|
||||
|
||||
public boolean clearInterval(int id) {
|
||||
return getTimer().clearInterval(id);
|
||||
}
|
||||
|
||||
public int setImmediate(Object listener, Object... args) {
|
||||
return getTimer().setImmediate(listener, args);
|
||||
}
|
||||
|
||||
public boolean clearImmediate(int id) {
|
||||
return getTimer().clearImmediate(id);
|
||||
}
|
||||
|
||||
public void waitFor() throws InterruptedException {
|
||||
synchronized (mRunningLock) {
|
||||
if (!mRunning) {
|
||||
mRunningLock.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Thread[" + getName() + "," + getPriority() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package org.autojs.autojs.core.looper
|
||||
|
||||
import android.os.Looper
|
||||
import androidx.annotation.CallSuper
|
||||
import org.autojs.autojs.engine.RhinoJavaScriptEngine
|
||||
import org.autojs.autojs.lang.ThreadCompat
|
||||
import org.autojs.autojs.runtime.ScriptRuntime
|
||||
import org.autojs.autojs.runtime.exception.ScriptInterruptedException
|
||||
import org.mozilla.javascript.Context
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/27.
|
||||
*/
|
||||
open class TimerThread(private val mRuntime: ScriptRuntime, private val mTarget: Runnable) :
|
||||
ThreadCompat(mTarget) {
|
||||
private var mTimer: Timer? = null
|
||||
private var mRunning = false
|
||||
private val mRunningLock = Object()
|
||||
private val mAsyncTask = Loopers.AsyncTask("TimerThread")
|
||||
var loopers: Loopers? = null
|
||||
init {
|
||||
mRuntime.loopers.addAsyncTask(mAsyncTask)
|
||||
}
|
||||
override fun run() {
|
||||
loopers = Loopers(mRuntime)
|
||||
mTimer = loopers!!.mTimer
|
||||
sTimerMap[currentThread()] = mTimer!!
|
||||
(mRuntime.engines.myEngine() as RhinoJavaScriptEngine).enterContext()
|
||||
notifyRunning()
|
||||
mTimer!!.post(mTarget)
|
||||
try {
|
||||
Looper.loop()
|
||||
} catch (e: Throwable) {
|
||||
if (!ScriptInterruptedException.causedByInterrupted(e)) {
|
||||
mRuntime.console.error(currentThread().toString() + ": ", e)
|
||||
}
|
||||
} finally {
|
||||
//mRuntime.console.log("TimerThread exit");
|
||||
onExit()
|
||||
mTimer = null
|
||||
Context.exit()
|
||||
sTimerMap.remove(currentThread(), mTimer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun interrupt() {
|
||||
LooperHelper.quitForThread(this)
|
||||
super.interrupt()
|
||||
}
|
||||
|
||||
private fun notifyRunning() {
|
||||
synchronized(mRunningLock) {
|
||||
mRunning = true
|
||||
mRunningLock.notifyAll()
|
||||
}
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun onExit() {
|
||||
mRuntime.loopers.removeAsyncTask(mAsyncTask)
|
||||
mRuntime.loopers.notifyThreadExit(this)
|
||||
}
|
||||
|
||||
fun setTimeout(callback: Any, delay: Long, vararg args: Any?): Int {
|
||||
return timer.setTimeout(callback, delay, *args as Array<out Any>)
|
||||
}
|
||||
|
||||
fun setTimeout(callback: Any): Int {
|
||||
return setTimeout(callback, 1)
|
||||
}
|
||||
|
||||
val timer: Timer
|
||||
get() {
|
||||
checkNotNull(mTimer) { "thread is not alive" }
|
||||
return mTimer as Timer
|
||||
}
|
||||
|
||||
fun clearTimeout(id: Int): Boolean {
|
||||
return timer.clearTimeout(id)
|
||||
}
|
||||
|
||||
fun setInterval(listener: Any?, interval: Long, vararg args: Any?): Int {
|
||||
return timer.setInterval(listener!!, interval, *args as Array<out Any>)
|
||||
}
|
||||
|
||||
fun setInterval(listener: Any?): Int {
|
||||
return setInterval(listener, 1)
|
||||
}
|
||||
|
||||
fun clearInterval(id: Int): Boolean {
|
||||
return timer.clearInterval(id)
|
||||
}
|
||||
|
||||
fun setImmediate(listener: Any, vararg args: Any?): Int {
|
||||
return timer.setImmediate(listener, *args as Array<out Any>)
|
||||
}
|
||||
|
||||
fun clearImmediate(id: Int): Boolean {
|
||||
return timer.clearImmediate(id)
|
||||
}
|
||||
|
||||
@Throws(InterruptedException::class)
|
||||
fun waitFor() {
|
||||
synchronized(mRunningLock) {
|
||||
if (mRunning) return
|
||||
mRunningLock.wait()
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Thread[$name,$priority]"
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val sTimerMap = ConcurrentHashMap<Thread, Timer?>()
|
||||
|
||||
@JvmStatic
|
||||
fun getTimerForThread(thread: Thread): Timer? {
|
||||
return sTimerMap[thread]
|
||||
}
|
||||
|
||||
val timerForCurrentThread: Timer?
|
||||
get() = getTimerForThread(currentThread())
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ 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) {
|
||||
open class AbsSeekBarAttributes(resourceParser: ResourceParser, view: View) : ProgressBarAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as AbsSeekBar
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import android.widget.AutoCompleteTextView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
|
||||
open class AutoCompleteTextViewAttributes(resourceParser: ResourceParser, view: View) : EditTextAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as AutoCompleteTextView
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import android.widget.CalendarView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
|
||||
open class CalendarViewAttributes(resourceParser: ResourceParser, view: View) : FrameLayoutAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as CalendarView
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttr("date") { view.date = it.toLong() }
|
||||
registerAttr("firstDayOfWeek") { view.firstDayOfWeek = parseDayOfWeek(it) }
|
||||
registerAttr("minDate") { setMinDate(view, it) }
|
||||
registerAttr("maxDate") { setMaxDate(view, it) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import android.widget.CheckedTextView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.util.ColorUtils
|
||||
|
||||
open class CheckedTextViewAttributes(resourceParser: ResourceParser, view: View) : TextViewAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as CheckedTextView
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttr("checkMarkDrawable") { view.checkMarkDrawable = drawables.parse(view, it) }
|
||||
registerAttrs(arrayOf("checkMarkTintList", "checkMarkTint")) { view.checkMarkTintList = ColorUtils.toColorStateList(view, it) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import android.widget.Chronometer
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.util.Strings
|
||||
|
||||
open class ChronometerAttributes(resourceParser: ResourceParser, view: View) : TextViewAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as Chronometer
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttr("base") { view.base = it.toLong() }
|
||||
registerAttrs(arrayOf("isCountDown", "countDown")) { view.isCountDown = it.toBoolean() }
|
||||
registerAttr("format") { view.format = Strings.parse(view, it) }
|
||||
registerAttrs(arrayOf("autoStart", "isAutoStart")) { if (it.toBoolean()) view.start() }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,9 +6,7 @@ import android.widget.DatePicker
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import kotlin.text.RegexOption.IGNORE_CASE
|
||||
|
||||
open class DatePickerAttributes(resourceParser: ResourceParser, view: View) : FrameLayoutAttributes(resourceParser, view) {
|
||||
|
||||
@@ -17,8 +15,8 @@ open class DatePickerAttributes(resourceParser: ResourceParser, view: View) : Fr
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttr("minDate") { setMinDate(it) }
|
||||
registerAttr("maxDate") { setMaxDate(it) }
|
||||
registerAttr("minDate") { setMinDate(view, it) }
|
||||
registerAttr("maxDate") { setMaxDate(view, it) }
|
||||
registerAttr("firstDayOfWeek") { view.firstDayOfWeek = parseDayOfWeek(it) }
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@@ -44,52 +42,4 @@ open class DatePickerAttributes(resourceParser: ResourceParser, view: View) : Fr
|
||||
)
|
||||
}
|
||||
|
||||
private fun setMaxDate(value: String) {
|
||||
try {
|
||||
parseDate(value)?.time?.let { view.maxDate = it }
|
||||
} catch (e: ParseException) {
|
||||
throw InflateException(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setMinDate(value: String) {
|
||||
try {
|
||||
parseDate(value)?.time?.let { view.minDate = it }
|
||||
} catch (e: ParseException) {
|
||||
throw InflateException(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseDate(value: String) = when {
|
||||
value.matches(Regex("^\\d{4}/.+")) -> {
|
||||
SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).parse(value)
|
||||
}
|
||||
else -> {
|
||||
SimpleDateFormat("MM/dd/yyyy", Locale.getDefault()).parse(value)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
private fun parseDayOfWeek(value: String) = when {
|
||||
value.matches(Regex("MON(DAY)?", IGNORE_CASE)) -> Calendar.MONDAY
|
||||
value.matches(Regex("TUE(SDAY)?", IGNORE_CASE)) -> Calendar.TUESDAY
|
||||
value.matches(Regex("WED(NESDAY)?", IGNORE_CASE)) -> Calendar.WEDNESDAY
|
||||
value.matches(Regex("THU(RSDAY)?", IGNORE_CASE)) -> Calendar.THURSDAY
|
||||
value.matches(Regex("FRI(DAY)?", IGNORE_CASE)) -> Calendar.FRIDAY
|
||||
value.matches(Regex("SAT(URDAY)?", IGNORE_CASE)) -> Calendar.SATURDAY
|
||||
value.matches(Regex("SUN(DAY)?", IGNORE_CASE)) -> Calendar.SUNDAY
|
||||
else -> {
|
||||
// @Caution by SuperMonster003 on May 19, 2023.
|
||||
// ! Calendar.XXX is not as same as JavaScript Date.
|
||||
// ! Take Tuesday as an example,
|
||||
// ! for Java, Calendar.TUESDAY is 3,
|
||||
// ! for JavaScript, Date#getDay() is 2.
|
||||
|
||||
// Compatibility for 0 is not necessary.
|
||||
// (value.toInt() + 6).mod(7) + 1
|
||||
|
||||
value.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.net.Uri
|
||||
import android.view.View
|
||||
import android.widget.ImageSwitcher
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.util.Strings
|
||||
|
||||
open class ImageSwitcherAttributes(resourceParser: ResourceParser, view: View) : ViewSwitcherAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as ImageSwitcher
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttrs(arrayOf("imageDrawable", "drawable")) { view.setImageDrawable(drawables.parse(view, it)) }
|
||||
registerAttrs(arrayOf("imageResource", "resource")) { view.setImageResource(it.toInt()) }
|
||||
registerAttrs(arrayOf("imageURI", "uri")) { view.setImageURI(Uri.parse(Strings.parse(view, it))) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsCalendarView
|
||||
|
||||
class JsCalendarViewAttributes(resourceParser: ResourceParser, view: View) : CalendarViewAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsCalendarView
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.graphics.JsCanvasView
|
||||
import org.autojs.autojs.core.ui.widget.JsCanvasView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
|
||||
class JsCanvasViewAttributes(resourceParser: ResourceParser, view: View) : TextureViewAttributes(resourceParser, view) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsCheckedTextView
|
||||
|
||||
class JsCheckedTextViewAttributes(resourceParser: ResourceParser, view: View) : CheckedTextViewAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsCheckedTextView
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsChronometer
|
||||
|
||||
class JsChronometerAttributes(resourceParser: ResourceParser, view: View) : ChronometerAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsChronometer
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.console.JsConsoleView
|
||||
import org.autojs.autojs.core.ui.widget.JsConsoleView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
|
||||
class JsConsoleViewAttributes(resourceParser: ResourceParser, view: View) : ConsoleViewAttributes(resourceParser, view) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsImageSwitcher
|
||||
|
||||
class JsImageSwitcherAttributes(resourceParser: ResourceParser, view: View) : ImageSwitcherAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsImageSwitcher
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsNumberPicker
|
||||
|
||||
open class JsNumberPickerAttributes(resourceParser: ResourceParser, view: View) : NumberPickerAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsNumberPicker
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsProgressBar
|
||||
|
||||
class JsProgressBarAttributes(resourceParser: ResourceParser, view: View) : ProgressBarAttributes<JsProgressBar>(resourceParser, view) {
|
||||
class JsProgressBarAttributes(resourceParser: ResourceParser, view: View) : ProgressBarAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsProgressBar
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsQuickContactBadge
|
||||
|
||||
class JsQuickContactBadgeAttributes(resourceParser: ResourceParser, view: View) : QuickContactBadgeAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsQuickContactBadge
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsRatingBar
|
||||
|
||||
class JsRatingBarAttributes(resourceParser: ResourceParser, view: View) : RatingBarAttributes<JsRatingBar>(resourceParser, view) {
|
||||
class JsRatingBarAttributes(resourceParser: ResourceParser, view: View) : RatingBarAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsRatingBar
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsSearchView
|
||||
|
||||
class JsSearchViewAttributes(resourceParser: ResourceParser, view: View) : SearchViewAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsSearchView
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsSeekBar
|
||||
|
||||
class JsSeekbarAttributes(resourceParser: ResourceParser, view: View) : SeekBarAttributes<JsSeekBar>(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsSeekBar
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsSeekBar
|
||||
|
||||
class JsSeekbarAttributes(resourceParser: ResourceParser, view: View) : SeekBarAttributes<JsSeekBar>(resourceParser, view) {
|
||||
class JsSeekBarAttributes(resourceParser: ResourceParser, view: View) : SeekBarAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsSeekBar
|
||||
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.InputFilter
|
||||
import android.text.InputType
|
||||
import android.text.method.DigitsKeyListener
|
||||
import android.text.method.TextKeyListener
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.animation.AnimationUtils
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.inflaters.TextViewInflater
|
||||
import org.autojs.autojs.core.ui.inflater.util.Dimensions
|
||||
import org.autojs.autojs.core.ui.inflater.util.Gravities
|
||||
import org.autojs.autojs.core.ui.inflater.util.Res
|
||||
import org.autojs.autojs.core.ui.inflater.util.Strings
|
||||
import org.autojs.autojs.core.ui.widget.JsTextSwitcher
|
||||
import org.autojs.autojs.core.ui.widget.JsTextView
|
||||
import org.autojs.autojs.util.ColorUtils
|
||||
import org.autojs.autojs6.R
|
||||
import kotlin.text.RegexOption.IGNORE_CASE
|
||||
|
||||
class JsTextSwitcherAttributes(resourceParser: ResourceParser, view: View) : TextSwitcherAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsTextSwitcher
|
||||
|
||||
private var mDrawableLeft: Drawable? = null
|
||||
private var mDrawableTop: Drawable? = null
|
||||
private var mDrawableRight: Drawable? = null
|
||||
private var mDrawableBottom: Drawable? = null
|
||||
private var mCapitalize: TextKeyListener.Capitalize? = null
|
||||
private var mAutoText = false
|
||||
private var mFontFamily: String? = null
|
||||
private var mTypeface: String? = null
|
||||
private var mTextStyle: Int? = null
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
val textViews by lazy { view.textViews }
|
||||
|
||||
registerAttr("autoLink") { value -> textViews.forEach { it.autoLinkMask = TextViewInflater.AUTO_LINK_MASKS[value] } }
|
||||
registerAttr("autoText") { value -> mAutoText = value.toBoolean(); setKeyListener(textViews) }
|
||||
registerAttr("capitalize") { value -> mCapitalize = TextViewInflater.CAPITALIZE[value]; setKeyListener(textViews) }
|
||||
registerAttr("digit") { value -> setDigit(textViews, value) }
|
||||
registerAttr("drawableBottom") { value -> mDrawableBottom = drawables.parse(view, value); setDrawables(textViews) }
|
||||
registerAttr("drawableLeft") { value -> mDrawableLeft = drawables.parse(view, value); setDrawables(textViews) }
|
||||
registerAttr("drawablePadding") { value -> textViews.forEach { it.compoundDrawablePadding = Dimensions.parseToIntPixel(value, view) } }
|
||||
registerAttr("drawableRight") { value -> mDrawableRight = drawables.parse(view, value); setDrawables(textViews) }
|
||||
registerAttr("drawableTop") { value -> mDrawableTop = drawables.parse(view, value); setDrawables(textViews) }
|
||||
registerAttr("drawables") { value -> setDrawables(textViews, value) }
|
||||
registerAttr("ellipsize") { value -> TextViewInflater.ELLIPSIZE[value]?.let { ellipsize -> textViews.forEach { it.ellipsize = ellipsize } } }
|
||||
registerAttr("ems") { value -> textViews.forEach { it.setEms(value.toInt()) } }
|
||||
registerAttr("fontFamily") { value -> mFontFamily = value; textViews.forEach { setTypeface(it) } }
|
||||
registerAttr("fontFeatureSettings") { value -> textViews.forEach { it.fontFeatureSettings = Strings.parse(view, value) } }
|
||||
registerAttr("freezesText") { value -> textViews.forEach { it.freezesText = value.toBoolean() } }
|
||||
registerAttr("gravity") { value -> textViews.forEach { it.gravity = Gravities.parse(value) } }
|
||||
registerAttr("hint") { value -> textViews.forEach { it.hint = Strings.parse(view, value) } }
|
||||
registerAttr("hyphenationFrequency") { value -> textViews.forEach { it.hyphenationFrequency = TextViewInflater.HYPHENATION_FREQUENCY[value] } }
|
||||
registerAttr("imeActionId") { value -> textViews.forEach { it.setImeActionLabel(it.imeActionLabel, value.toInt()) } }
|
||||
registerAttr("imeActionLabel") { value -> textViews.forEach { it.setImeActionLabel(value, it.imeActionId) } }
|
||||
registerAttr("imeOptions") { value -> textViews.forEach { it.imeOptions = TextViewInflater.IME_OPTIONS.split(value) } }
|
||||
registerAttr("includeFontPadding") { value -> textViews.forEach { it.includeFontPadding = value.toBoolean() } }
|
||||
registerAttr("inputType") { value -> textViews.forEach { it.inputType = TextViewInflater.INPUT_TYPES.split(value) } }
|
||||
registerAttr("letterSpacing") { value -> textViews.forEach { it.letterSpacing = value.toFloat() } }
|
||||
registerAttr("lineSpacingExtra") { value -> textViews.forEach { it.setLineSpacing(Dimensions.parseToIntPixel(value, it).toFloat(), it.lineSpacingMultiplier) } }
|
||||
registerAttr("lineSpacingMultiplier") { value -> textViews.forEach { it.setLineSpacing(it.lineSpacingExtra, Dimensions.parseToIntPixel(value, it).toFloat()) } }
|
||||
registerAttr("lines") { value -> textViews.forEach { it.setLines(value.toInt()) } }
|
||||
registerAttr("linksClickable") { value -> textViews.forEach { it.linksClickable = value.toBoolean() } }
|
||||
registerAttr("marqueeRepeatLimit") { value -> textViews.forEach { it.marqueeRepeatLimit = if (value == "marquee_forever") Int.MAX_VALUE else value.toInt() } }
|
||||
registerAttr("maxEms") { value -> textViews.forEach { it.maxEms = value.toInt() } }
|
||||
registerAttr("maxHeight") { value -> textViews.forEach { it.maxHeight = Dimensions.parseToIntPixel(value, it) } }
|
||||
registerAttr("maxLength") { value -> textViews.forEach { it.filters = arrayOf<InputFilter>(InputFilter.LengthFilter(value.toInt())) } }
|
||||
registerAttr("maxLines") { value -> textViews.forEach { it.maxLines = value.toInt() } }
|
||||
registerAttr("maxWidth") { value -> textViews.forEach { it.maxWidth = Dimensions.parseToIntPixel(value, view) } }
|
||||
registerAttr("minEms") { value -> textViews.forEach { it.minEms = value.toInt() } }
|
||||
registerAttr("minHeight") { value -> textViews.forEach { it.minHeight = Dimensions.parseToIntPixel(value, it) } }
|
||||
registerAttr("minLines") { value -> textViews.forEach { it.minLines = value.toInt() } }
|
||||
registerAttr("minWidth") { value -> textViews.forEach { it.minWidth = Dimensions.parseToIntPixel(value, view) } }
|
||||
registerAttr("numeric") { value -> textViews.forEach { it.inputType = TextViewInflater.INPUT_TYPE_NUMERIC.split(value) or InputType.TYPE_CLASS_NUMBER } }
|
||||
registerAttr("password") { value -> if (value == "true") textViews.forEach { it.inputType = it.inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD } }
|
||||
registerAttr("phoneNumber") { value -> if (value == "true") textViews.forEach { it.inputType = it.inputType or InputType.TYPE_TEXT_VARIATION_PHONETIC } }
|
||||
registerAttr("privateImeOptions") { value -> textViews.forEach { it.privateImeOptions = Strings.parse(view, value) } }
|
||||
registerAttr("scrollHorizontally") { value -> textViews.forEach { it.setHorizontallyScrolling(value.toBoolean()) } }
|
||||
registerAttr("selectAllOnFocus") { value -> textViews.forEach { it.setSelectAllOnFocus(value.toBoolean()) } }
|
||||
registerAttr("shadowColor") { value -> textViews.forEach { it.setShadowLayer(it.shadowRadius, it.shadowDx, it.shadowDy, ColorUtils.parse(it, value)) } }
|
||||
registerAttr("shadowDx") { value -> textViews.forEach { it.setShadowLayer(it.shadowRadius, Dimensions.parseToPixel(value, it), it.shadowDy, it.shadowColor) } }
|
||||
registerAttr("shadowDy") { value -> textViews.forEach { it.setShadowLayer(it.shadowRadius, it.shadowDx, Dimensions.parseToPixel(value, it), it.shadowColor) } }
|
||||
registerAttr("shadowRadius") { value -> textViews.forEach { it.setShadowLayer(Dimensions.parseToPixel(value, it), it.shadowDx, it.shadowDy, it.shadowColor) } }
|
||||
registerAttr("text") { value -> textViews.forEach { it.text = Strings.parse(it, value) } }
|
||||
registerAttr("textAppearance") { value -> textViews.forEach { it.setTextAppearance(Res.parseStyle(it, value)) } }
|
||||
registerAttr("textIsSelectable") { value -> textViews.forEach { it.setTextIsSelectable(value.toBoolean()) } }
|
||||
registerAttr("textScaleX") { value -> textViews.forEach { it.textScaleX = Dimensions.parseToPixel(value, it) } }
|
||||
registerAttr("textStyle") { value -> mTextStyle = TextViewInflater.TEXT_STYLES.split(value); textViews.forEach { setTypeface(it) } }
|
||||
registerAttr("typeface") { value -> mTypeface = value; textViews.forEach { setTypeface(it) } }
|
||||
registerAttrs(arrayOf("highlightTextColor", "textColorHighlight")) { value -> textViews.forEach { it.highlightColor = ColorUtils.parse(view, value) } }
|
||||
registerAttrs(arrayOf("hintTextColor", "textColorHint")) { value -> textViews.forEach { it.setHintTextColor(ColorUtils.parse(view, value)) } }
|
||||
registerAttrs(arrayOf("isAllCaps", "allCaps", "textAllCaps")) { value -> textViews.forEach { it.isAllCaps = value.toBoolean() } }
|
||||
registerAttrs(arrayOf("isCursorVisible", "cursorVisible")) { value -> textViews.forEach { it.isCursorVisible = value.toBoolean() } }
|
||||
registerAttrs(arrayOf("isElegantTextHeight", "elegantTextHeight")) { value -> textViews.forEach { it.isElegantTextHeight = value.toBoolean() } }
|
||||
registerAttrs(arrayOf("isSingleLine", "singleLine")) { value -> textViews.forEach { it.isSingleLine = value.toBoolean() } }
|
||||
registerAttrs(arrayOf("linkTextColor", "textColorLink")) { value -> textViews.forEach { it.setLinkTextColor(ColorUtils.parse(view, value)) } }
|
||||
registerAttrs(arrayOf("textColor", "color")) { value -> textViews.forEach { it.setTextColor(ColorUtils.parse(it.context, value)) } }
|
||||
registerAttrs(arrayOf("textSize", "size")) { value -> textViews.forEach { it.setTextSize(TypedValue.COMPLEX_UNIT_PX, Dimensions.parseToPixel(value, it)) } }
|
||||
|
||||
registerAttrs(arrayOf("anim", "animation")) { setAnimations(it) }
|
||||
registerAttrs(arrayOf("animIn", "inAnim", "inAnimation")) { setInAnimation(Strings.parseAnimation(view, it)) }
|
||||
registerAttrs(arrayOf("animOut", "outAnim", "outAnimation")) { setOutAnimation(Strings.parseAnimation(view, it)) }
|
||||
|
||||
registerAttrUnsupported(
|
||||
arrayOf(
|
||||
"drawableStart",
|
||||
"drawableEnd",
|
||||
"editable",
|
||||
"editorExtras",
|
||||
"inputMethod",
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
private fun setAnimations(value: String) {
|
||||
mapOf(
|
||||
Regex("from.*left|(to.*)?right|from.*left.*to.*right", IGNORE_CASE) to { setAnimations(R.anim.slide_in_left, R.anim.slide_out_right) },
|
||||
Regex("from.*(top|up)|(to.*)?(bottom|down)|from.*(top|up).*to.*(bottom|down)", IGNORE_CASE) to { setAnimations(R.anim.slide_in_top, R.anim.slide_out_bottom) },
|
||||
Regex("from.*right|(to.*)?left|from.*right.*to.*left", IGNORE_CASE) to { setAnimations(R.anim.slide_in_right, R.anim.slide_out_left) },
|
||||
Regex("from.*(bottom|down)|(to.*)?(top|up)|from.*(bottom|down).*to.*(top|up)", IGNORE_CASE) to { setAnimations(R.anim.slide_in_bottom, R.anim.slide_out_top) },
|
||||
Regex("micro", IGNORE_CASE) to { setAnimations(R.anim.slide_in_micro, R.anim.slide_out_micro) },
|
||||
Regex("fade", IGNORE_CASE) to { setAnimations(R.anim.fade_in, R.anim.fade_out) },
|
||||
Regex("fast.*Fade|fade.*Fast", IGNORE_CASE) to { setAnimations(R.anim.fast_fade_in, R.anim.fast_fade_out) },
|
||||
Regex("shrink", IGNORE_CASE) to { setAnimations(R.anim.grow_fade_in, R.anim.shrink_fade_out) },
|
||||
).forEach { entry ->
|
||||
if (entry.key.matches(value)) {
|
||||
entry.value.invoke().also { return }
|
||||
}
|
||||
}
|
||||
throw Exception("Can't parse animations for $value")
|
||||
}
|
||||
|
||||
private fun setAnimations(`in`: Int, out: Int) {
|
||||
setInAnimation(`in`)
|
||||
setOutAnimation(out)
|
||||
}
|
||||
|
||||
private fun setInAnimation(`in`: Int) {
|
||||
view.inAnimation = AnimationUtils.loadAnimation(view.context, `in`)
|
||||
}
|
||||
|
||||
private fun setOutAnimation(out: Int) {
|
||||
view.outAnimation = AnimationUtils.loadAnimation(view.context, out)
|
||||
}
|
||||
|
||||
private fun setDigit(textViews: MutableList<JsTextView>, value: String) {
|
||||
if (value == "true") {
|
||||
@Suppress("DEPRECATION")
|
||||
textViews.forEach { it.keyListener = DigitsKeyListener.getInstance() }
|
||||
} else if (value != "false") {
|
||||
textViews.forEach { it.keyListener = DigitsKeyListener.getInstance(value) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun setDrawables(textViews: MutableList<JsTextView>, value: String) {
|
||||
val values = parseAttrValue(value)
|
||||
when (values.size) {
|
||||
1 -> {
|
||||
mDrawableLeft = drawables.parse(view, values[0])
|
||||
mDrawableTop = drawables.parse(view, values[0])
|
||||
mDrawableRight = drawables.parse(view, values[0])
|
||||
mDrawableBottom = drawables.parse(view, values[0])
|
||||
}
|
||||
2 -> {
|
||||
mDrawableLeft = drawables.parse(view, values[0])
|
||||
mDrawableTop = drawables.parse(view, values[1])
|
||||
mDrawableRight = drawables.parse(view, values[0])
|
||||
mDrawableBottom = drawables.parse(view, values[1])
|
||||
}
|
||||
3 -> {
|
||||
mDrawableLeft = drawables.parse(view, values[0])
|
||||
mDrawableTop = drawables.parse(view, values[1])
|
||||
mDrawableRight = drawables.parse(view, values[2])
|
||||
mDrawableBottom = drawables.parse(view, values[1])
|
||||
}
|
||||
4 -> {
|
||||
mDrawableLeft = drawables.parse(view, values[0])
|
||||
mDrawableTop = drawables.parse(view, values[1])
|
||||
mDrawableRight = drawables.parse(view, values[2])
|
||||
mDrawableBottom = drawables.parse(view, values[3])
|
||||
}
|
||||
}
|
||||
setDrawables(textViews)
|
||||
}
|
||||
|
||||
private fun setKeyListener(textViews: MutableList<JsTextView>) {
|
||||
mCapitalize?.let { value -> textViews.forEach { it.keyListener = TextKeyListener.getInstance(mAutoText, value) } }
|
||||
}
|
||||
|
||||
private fun setDrawables(textViews: MutableList<JsTextView>) {
|
||||
textViews.forEach { view ->
|
||||
view.compoundDrawables.let {
|
||||
view.setCompoundDrawables(
|
||||
mDrawableLeft ?: it[TextViewInflater.LEFT],
|
||||
mDrawableTop ?: it[TextViewInflater.TOP],
|
||||
mDrawableRight ?: it[TextViewInflater.RIGHT],
|
||||
mDrawableBottom ?: it[TextViewInflater.BOTTOM],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setTypeface(view: JsTextView) {
|
||||
if (mFontFamily != null) {
|
||||
//ignore typeface as android does
|
||||
mTypeface = mFontFamily
|
||||
}
|
||||
if (mTypeface != null) {
|
||||
view.typeface = Typeface.create(mTypeface, mTextStyle ?: view.typeface.style)
|
||||
} else {
|
||||
mTextStyle?.let { view.setTypeface(view.typeface, it) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsVideoView
|
||||
|
||||
class JsVideoViewAttributes(resourceParser: ResourceParser, view: View) : VideoViewAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsVideoView
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttrs(arrayOf("controller", "mediaController", "isControllerEnabled", "controllerEnabled", "enableController", "isMediaControllerEnabled", "mediaControllerEnabled", "enableMediaController")) {
|
||||
when (it) {
|
||||
"null", "false" -> view.clearMediaController()
|
||||
"true" -> view.resetMediaController()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsViewFlipper
|
||||
|
||||
class JsViewFlipperAttributes(resourceParser: ResourceParser, view: View) : ViewFlipperAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsViewFlipper
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.JsViewSwitcher
|
||||
|
||||
class JsViewSwitcherAttributes(resourceParser: ResourceParser, view: View) : ViewSwitcherAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as JsViewSwitcher
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.widget.NumberPicker
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.util.Dimensions
|
||||
import org.autojs.autojs.core.ui.inflater.util.Strings
|
||||
import org.autojs.autojs.util.ColorUtils
|
||||
|
||||
open class NumberPickerAttributes(resourceParser: ResourceParser, view: View) : LinearLayoutAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as NumberPicker
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttrs(arrayOf("maxValue", "max")) { view.maxValue = it.toInt() }
|
||||
registerAttrs(arrayOf("minValue", "min")) { view.minValue = it.toInt() }
|
||||
registerAttrs(arrayOf("onLongPressUpdateInterval", "longPressUpdateInterval")) { view.setOnLongPressUpdateInterval(it.toLong()) }
|
||||
registerAttr("wrapSelectorWheel") { view.wrapSelectorWheel = it.toBoolean() }
|
||||
registerAttrs(arrayOf("value", "selectedIndex", "currentIndex")) { view.value = it.toInt() }
|
||||
|
||||
registerAttrs(arrayOf("textColor", "color")) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
view.textColor = ColorUtils.parse(view, it)
|
||||
}
|
||||
}
|
||||
|
||||
registerAttrs(arrayOf("textSize", "size")) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
view.textSize = Dimensions.parseToPixel(it, view)
|
||||
}
|
||||
}
|
||||
|
||||
registerAttr("selectionDividerHeight") {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
view.selectionDividerHeight = it.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
registerAttrs(arrayOf("displayedValues", "values")) {
|
||||
val strings = parseAttrValue(Strings.parse(view, it)).toTypedArray()
|
||||
val actualLength = strings.size
|
||||
if (actualLength == 0) {
|
||||
return@registerAttrs
|
||||
}
|
||||
val expectedLength = view.maxValue - view.minValue + 1
|
||||
if (expectedLength != actualLength) {
|
||||
view.minValue = 0
|
||||
view.maxValue = actualLength - 1
|
||||
}
|
||||
view.displayedValues = strings
|
||||
}
|
||||
|
||||
registerAttrs(arrayOf("distinctDisplayedValues", "distinctValues")) {
|
||||
val strings = parseAttrValue(Strings.parse(view, it)).toSet().toTypedArray()
|
||||
val actualLength = strings.size
|
||||
if (actualLength == 0) {
|
||||
return@registerAttrs
|
||||
}
|
||||
val expectedLength = view.maxValue - view.minValue + 1
|
||||
if (expectedLength != actualLength) {
|
||||
view.minValue = 0
|
||||
view.maxValue = actualLength - 1
|
||||
}
|
||||
view.displayedValues = strings
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import org.autojs.autojs.util.ColorUtils
|
||||
/**
|
||||
* Created by SuperMonster003 on May 20, 2023.
|
||||
*/
|
||||
open class ProgressBarAttributes<V: ProgressBar>(resourceParser: ResourceParser, view: View) : ViewAttributes(resourceParser, view) {
|
||||
open class ProgressBarAttributes(resourceParser: ResourceParser, view: View) : ViewAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as ProgressBar
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.provider.ContactsContract
|
||||
import android.provider.ContactsContract.CommonDataKinds
|
||||
import android.view.View
|
||||
import android.widget.QuickContactBadge
|
||||
import org.autojs.autojs.core.ui.BiMaps
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
|
||||
open class QuickContactBadgeAttributes(resourceParser: ResourceParser, view: View) : ImageViewAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as QuickContactBadge
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttr("overlay") { view.setOverlay(drawables.parse(view, it)) }
|
||||
registerAttr("prioritizedMimeType") { view.setPrioritizedMimeType(PRIORITIZED_MIME_TYPES[it]) }
|
||||
|
||||
registerAttr("phone") { view.assignContactFromPhone(it, true) }
|
||||
registerAttr("email") { view.assignContactFromEmail(it, true) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val PRIORITIZED_MIME_TYPES = BiMaps.newBuilder<String, String>()
|
||||
.put("aggregationExceptions", ContactsContract.AggregationExceptions.CONTENT_ITEM_TYPE)
|
||||
.put("contacts", ContactsContract.Contacts.CONTENT_ITEM_TYPE)
|
||||
.put("directory", ContactsContract.Directory.CONTENT_ITEM_TYPE)
|
||||
.put("email", CommonDataKinds.Email.CONTENT_ITEM_TYPE)
|
||||
.put("event", CommonDataKinds.Event.CONTENT_ITEM_TYPE)
|
||||
.put("groupMembership", CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE)
|
||||
.put("groups", ContactsContract.Groups.CONTENT_ITEM_TYPE)
|
||||
.put("identity", CommonDataKinds.Identity.CONTENT_ITEM_TYPE)
|
||||
.put("im", CommonDataKinds.Im.CONTENT_ITEM_TYPE)
|
||||
.put("nickname", CommonDataKinds.Nickname.CONTENT_ITEM_TYPE)
|
||||
.put("note", CommonDataKinds.Note.CONTENT_ITEM_TYPE)
|
||||
.put("organization", CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
|
||||
.put("phone", CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
.put("photo", CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
||||
.put("rawContacts", ContactsContract.RawContacts.CONTENT_ITEM_TYPE)
|
||||
.put("relation", CommonDataKinds.Relation.CONTENT_ITEM_TYPE)
|
||||
.put("settings", ContactsContract.Settings.CONTENT_ITEM_TYPE)
|
||||
.put("sipAddress", CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
|
||||
.put("statusUpdates", ContactsContract.StatusUpdates.CONTENT_ITEM_TYPE)
|
||||
.put("structuredName", CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put("structuredPostal", CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put("website", CommonDataKinds.Website.CONTENT_ITEM_TYPE)
|
||||
.build()
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import android.view.View
|
||||
import android.widget.RatingBar
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
|
||||
open class RatingBarAttributes<V : RatingBar>(resourceParser: ResourceParser, view: View) : AbsSeekBarAttributes<V>(resourceParser, view) {
|
||||
open class RatingBarAttributes(resourceParser: ResourceParser, view: View) : AbsSeekBarAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as RatingBar
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import android.widget.SearchView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.inflaters.TextViewInflater
|
||||
import org.autojs.autojs.core.ui.inflater.util.Dimensions
|
||||
import org.autojs.autojs.core.ui.inflater.util.Strings
|
||||
|
||||
open class SearchViewAttributes(resourceParser: ResourceParser, view: View) : LinearLayoutAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as SearchView
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttr("imeOptions") { view.imeOptions = TextViewInflater.IME_OPTIONS.split(it) }
|
||||
registerAttr("inputType") { view.inputType = TextViewInflater.INPUT_TYPES.split(it) }
|
||||
registerAttr("maxWidth") { view.maxWidth = Dimensions.parseToIntPixel(it, view) }
|
||||
registerAttr("queryHint") { view.queryHint = Strings.parse(view, it) }
|
||||
registerAttrs(arrayOf("isIconified", "iconified")) { view.isIconified = it.toBoolean() }
|
||||
registerAttrs(arrayOf("isIconifiedByDefault", "iconifiedByDefault")) { view.isIconifiedByDefault = it.toBoolean() }
|
||||
registerAttrs(arrayOf("isQueryRefinementEnabled", "queryRefinementEnabled", "isQueryRefinement", "enableQueryRefinement")) { view.isQueryRefinementEnabled = it.toBoolean() }
|
||||
registerAttrs(arrayOf("isSubmitButtonEnabled", "submitButtonEnabled", "isSubmitButton", "enableSubmitButton")) { view.isSubmitButtonEnabled = it.toBoolean() }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import android.view.View
|
||||
import android.widget.SeekBar
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
|
||||
open class SeekBarAttributes<V : SeekBar>(resourceParser: ResourceParser, view: View) : AbsSeekBarAttributes<V>(resourceParser, view) {
|
||||
open class SeekBarAttributes(resourceParser: ResourceParser, view: View) : AbsSeekBarAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as SeekBar
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import android.widget.Space
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
|
||||
open class SpaceAttributes(resourceParser: ResourceParser, view: View) : ViewAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as Space
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.SurfaceView
|
||||
import android.view.View
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
|
||||
open class SurfaceViewAttributes(resourceParser: ResourceParser, view: View) : ViewAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as SurfaceView
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttr("clipBounds") { value ->
|
||||
val (left, top, right, bottom) = parseAttrValue(value).map { it.toInt() }
|
||||
view.clipBounds = Rect(left, top, right, bottom)
|
||||
}
|
||||
registerAttr("visibility") { view.visibility = VISIBILITY[it] }
|
||||
registerAttrs(arrayOf("secure", "isSecure")) { view.setSecure(it.toBoolean()) }
|
||||
registerAttrs(arrayOf("zOrderOnTop", "isZOrderOnTop")) { view.setZOrderOnTop(it.toBoolean()) }
|
||||
registerAttrs(arrayOf("zOrderMediaOverlay", "isZOrderMediaOverlay")) { view.setZOrderMediaOverlay(it.toBoolean()) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextSwitcher
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.util.Strings
|
||||
|
||||
open class TextSwitcherAttributes(resourceParser: ResourceParser, view: View) : ViewSwitcherAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as TextSwitcher
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttrs(arrayOf("text", "nextText")) { view.setText(Strings.parse(view, it)) }
|
||||
registerAttr("currentText") { view.setCurrentText(Strings.parse(view, it)) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,20 +42,16 @@ open class TextViewAttributes(resourceParser: ResourceParser, view: View) : View
|
||||
registerAttr("autoLink") { view.autoLinkMask = TextViewInflater.AUTO_LINK_MASKS[it] }
|
||||
registerAttr("autoText") { mAutoText = it.toBoolean(); setKeyListener() }
|
||||
registerAttr("capitalize") { mCapitalize = TextViewInflater.CAPITALIZE[it]; setKeyListener() }
|
||||
registerAttr("drawableLeft") { mDrawableLeft = drawables.parse(view, it); setDrawables() }
|
||||
registerAttr("drawableTop") { mDrawableTop = drawables.parse(view, it); setDrawables() }
|
||||
registerAttr("drawableRight") { mDrawableRight = drawables.parse(view, it); setDrawables() }
|
||||
registerAttr("drawableBottom") { mDrawableBottom = drawables.parse(view, it); setDrawables() }
|
||||
registerAttr("drawables") { setDrawables(it) }
|
||||
registerAttrs(arrayOf("isCursorVisible", "cursorVisible")) { view.isCursorVisible = it.toBoolean() }
|
||||
registerAttr("digit") { setDigit(it) }
|
||||
registerAttr("drawableBottom") { mDrawableBottom = drawables.parse(view, it); setDrawables() }
|
||||
registerAttr("drawableLeft") { mDrawableLeft = drawables.parse(view, it); setDrawables() }
|
||||
registerAttr("drawablePadding") { view.compoundDrawablePadding = Dimensions.parseToIntPixel(it, view) }
|
||||
registerAttrs(arrayOf("isElegantTextHeight", "elegantTextHeight")) { view.isElegantTextHeight = it.toBoolean() }
|
||||
registerAttr("drawableRight") { mDrawableRight = drawables.parse(view, it); setDrawables() }
|
||||
registerAttr("drawableTop") { mDrawableTop = drawables.parse(view, it); setDrawables() }
|
||||
registerAttr("drawables") { setDrawables(it) }
|
||||
registerAttr("ellipsize") { it -> TextViewInflater.ELLIPSIZE[it]?.let { view.ellipsize = it } }
|
||||
registerAttr("ems") { view.setEms(it.toInt()) }
|
||||
registerAttr("fontFamily") { mFontFamily = it; setTypeface() }
|
||||
registerAttr("textStyle") { mTextStyle = TextViewInflater.TEXT_STYLES.split(it); setTypeface() }
|
||||
registerAttr("typeface") { mTypeface = it; setTypeface() }
|
||||
registerAttr("fontFeatureSettings") { view.fontFeatureSettings = Strings.parse(view, it) }
|
||||
registerAttr("freezesText") { view.freezesText = it.toBoolean() }
|
||||
registerAttr("gravity") { view.gravity = Gravities.parse(it) }
|
||||
@@ -91,15 +87,19 @@ open class TextViewAttributes(resourceParser: ResourceParser, view: View) : View
|
||||
registerAttr("shadowDx") { view.setShadowLayer(view.shadowRadius, Dimensions.parseToPixel(it, view), view.shadowDy, view.shadowColor) }
|
||||
registerAttr("shadowDy") { view.setShadowLayer(view.shadowRadius, view.shadowDx, Dimensions.parseToPixel(it, view), view.shadowColor) }
|
||||
registerAttr("shadowRadius") { view.setShadowLayer(Dimensions.parseToPixel(it, view), view.shadowDx, view.shadowDy, view.shadowColor) }
|
||||
registerAttrs(arrayOf("isSingleLine", "singleLine")) { view.isSingleLine = it.toBoolean() }
|
||||
registerAttrs(arrayOf("isAllCaps", "allCaps", "textAllCaps")) { view.isAllCaps = it.toBoolean() }
|
||||
registerAttr("text") { view.text = Strings.parse(view, it) }
|
||||
registerAttr("textAppearance") { view.setTextAppearance(Res.parseStyle(view, it)) }
|
||||
registerAttrs(arrayOf("highlightTextColor", "textColorHighlight")) { view.highlightColor = parse(view, it) }
|
||||
registerAttrs(arrayOf("hintTextColor", "textColorHint")) { view.setHintTextColor(parse(view, it)) }
|
||||
registerAttrs(arrayOf("linkTextColor", "textColorLink")) { view.setLinkTextColor(parse(view, it)) }
|
||||
registerAttr("textIsSelectable") { view.setTextIsSelectable(it.toBoolean()) }
|
||||
registerAttr("textScaleX") { view.textScaleX = Dimensions.parseToPixel(it, view) }
|
||||
registerAttr("text") { view.text = Strings.parse(view, it) }
|
||||
registerAttr("textStyle") { mTextStyle = TextViewInflater.TEXT_STYLES.split(it); setTypeface() }
|
||||
registerAttr("typeface") { mTypeface = it; setTypeface() }
|
||||
registerAttrs(arrayOf("highlightTextColor", "textColorHighlight")) { view.highlightColor = parse(view, it) }
|
||||
registerAttrs(arrayOf("hintTextColor", "textColorHint")) { view.setHintTextColor(parse(view, it)) }
|
||||
registerAttrs(arrayOf("isAllCaps", "allCaps", "textAllCaps")) { view.isAllCaps = it.toBoolean() }
|
||||
registerAttrs(arrayOf("isCursorVisible", "cursorVisible")) { view.isCursorVisible = it.toBoolean() }
|
||||
registerAttrs(arrayOf("isElegantTextHeight", "elegantTextHeight")) { view.isElegantTextHeight = it.toBoolean() }
|
||||
registerAttrs(arrayOf("isSingleLine", "singleLine")) { view.isSingleLine = it.toBoolean() }
|
||||
registerAttrs(arrayOf("linkTextColor", "textColorLink")) { view.setLinkTextColor(parse(view, it)) }
|
||||
registerAttrs(arrayOf("textColor", "color")) { view.setTextColor(parse(view.context, it)) }
|
||||
registerAttrs(arrayOf("textSize", "size")) { view.setTextSize(TypedValue.COMPLEX_UNIT_PX, Dimensions.parseToPixel(it, view)) }
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import android.widget.VideoView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.util.Strings
|
||||
|
||||
open class VideoViewAttributes(resourceParser: ResourceParser, view: View) : SurfaceViewAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as VideoView
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttrs(arrayOf("videoPath", "path", "src")) { view.setVideoPath(Strings.parsePath(view, it) ?: return@registerAttrs) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ViewAnimator
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
|
||||
open class ViewAnimatorAttributes(resourceParser: ResourceParser, view: View) : FrameLayoutAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as ViewAnimator
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttr("animateFirstView") { view.animateFirstView = it.toBoolean() }
|
||||
registerAttr("displayedChild") { view.displayedChild = it.toInt() }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,10 +5,13 @@ package org.autojs.autojs.core.ui.attribute
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.InflateException
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.widget.CalendarView
|
||||
import android.widget.DatePicker
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
@@ -25,6 +28,11 @@ import org.autojs.autojs.core.ui.inflater.util.Ids
|
||||
import org.autojs.autojs.core.ui.inflater.util.Strings
|
||||
import org.autojs.autojs.core.ui.inflater.util.ValueMapper
|
||||
import org.autojs.autojs.util.ColorUtils
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
open class ViewAttributes(resourceParser: ResourceParser, open val view: View) {
|
||||
|
||||
@@ -133,6 +141,8 @@ open class ViewAttributes(resourceParser: ResourceParser, open val view: View) {
|
||||
registerAttrs(arrayOf("layout_marginBottom", "layoutMarginBottom"), { parseDimension(it, false) }, ::setMarginBottom)
|
||||
registerAttrs(arrayOf("layout_marginStart", "layoutMarginStart"), { parseDimension(it, true) }, ::setMarginStart)
|
||||
registerAttrs(arrayOf("layout_marginEnd", "layoutMarginEnd"), { parseDimension(it, true) }, ::setMarginEnd)
|
||||
registerAttrs(arrayOf("layout_marginVertical", "layoutMarginVertical"), { parseDimension(it, true) }, ::setMarginVertical)
|
||||
registerAttrs(arrayOf("layout_marginHorizontal", "layoutMarginHorizontal"), { parseDimension(it, true) }, ::setMarginHorizontal)
|
||||
registerAttrs(arrayOf("layout_alignParentBottom", "layoutAlignParentBottom")) { setLayoutRule(RelativeLayout.ALIGN_PARENT_BOTTOM, false, it) }
|
||||
registerAttrs(arrayOf("layout_alignParentTop", "layoutAlignParentTop")) { setLayoutRule(RelativeLayout.ALIGN_PARENT_TOP, false, it) }
|
||||
registerAttrs(arrayOf("layout_alignParentLeft", "layoutAlignParentLeft")) { setLayoutRule(RelativeLayout.ALIGN_PARENT_LEFT, false, it) }
|
||||
@@ -159,6 +169,8 @@ open class ViewAttributes(resourceParser: ResourceParser, open val view: View) {
|
||||
registerAttr("paddingBottom", { parseDimension(it, false) }, ::setPaddingBottom)
|
||||
registerAttr("paddingStart", { parseDimension(it, true) }, ::setPaddingStart)
|
||||
registerAttr("paddingEnd", { parseDimension(it, true) }, ::setPaddingEnd)
|
||||
registerAttr("paddingVertical", { parseDimension(it, true) }, ::setPaddingVertical)
|
||||
registerAttr("paddingHorizontal", { parseDimension(it, true) }, ::setPaddingHorizontal)
|
||||
registerAttr("alpha") { view.alpha = it.toFloat() }
|
||||
registerAttrs(arrayOf("isClickable", "clickable")) { view.isClickable = it.toBoolean() }
|
||||
registerAttr("contentDescription") { view.contentDescription = parseString(it) }
|
||||
@@ -379,7 +391,7 @@ open class ViewAttributes(resourceParser: ResourceParser, open val view: View) {
|
||||
registerAttr(name) { applier(it.toBoolean()) }
|
||||
}
|
||||
|
||||
protected fun parseDrawable(value: String?): Drawable = drawables.parse(view, value)
|
||||
protected fun parseDrawable(value: String): Drawable? = drawables.parse(view, value)
|
||||
|
||||
protected fun setGravity(g: Int) = try {
|
||||
val setGravity = view.javaClass.getMethod("setGravity", Int::class.javaPrimitiveType)
|
||||
@@ -411,27 +423,41 @@ open class ViewAttributes(resourceParser: ResourceParser, open val view: View) {
|
||||
}
|
||||
|
||||
private fun setMarginLeft(margin: Int) {
|
||||
(view.layoutParams as? MarginLayoutParams)?.run { leftMargin = margin }
|
||||
(view.layoutParams as? MarginLayoutParams)?.let { it.leftMargin = margin }
|
||||
}
|
||||
|
||||
private fun setMarginRight(margin: Int) {
|
||||
(view.layoutParams as? MarginLayoutParams)?.run { rightMargin = margin }
|
||||
(view.layoutParams as? MarginLayoutParams)?.let { it.rightMargin = margin }
|
||||
}
|
||||
|
||||
private fun setMarginTop(margin: Int) {
|
||||
(view.layoutParams as? MarginLayoutParams)?.run { topMargin = margin }
|
||||
(view.layoutParams as? MarginLayoutParams)?.let { it.topMargin = margin }
|
||||
}
|
||||
|
||||
private fun setMarginBottom(margin: Int) {
|
||||
(view.layoutParams as? MarginLayoutParams)?.run { bottomMargin = margin }
|
||||
(view.layoutParams as? MarginLayoutParams)?.let { it.bottomMargin = margin }
|
||||
}
|
||||
|
||||
protected fun setMarginStart(margin: Int) {
|
||||
(view.layoutParams as? MarginLayoutParams)?.run { marginStart = margin }
|
||||
(view.layoutParams as? MarginLayoutParams)?.let { it.marginStart = margin }
|
||||
}
|
||||
|
||||
protected fun setMarginEnd(margin: Int) {
|
||||
(view.layoutParams as? MarginLayoutParams)?.run { marginEnd = margin }
|
||||
(view.layoutParams as? MarginLayoutParams)?.let { it.marginEnd = margin }
|
||||
}
|
||||
|
||||
protected fun setMarginVertical(margin: Int) {
|
||||
(view.layoutParams as? MarginLayoutParams)?.let {
|
||||
it.topMargin = margin
|
||||
it.bottomMargin = margin
|
||||
}
|
||||
}
|
||||
|
||||
protected fun setMarginHorizontal(margin: Int) {
|
||||
(view.layoutParams as? MarginLayoutParams)?.let {
|
||||
it.marginStart = margin
|
||||
it.marginEnd = margin
|
||||
}
|
||||
}
|
||||
|
||||
protected fun setPadding(padding: String) {
|
||||
@@ -463,6 +489,14 @@ open class ViewAttributes(resourceParser: ResourceParser, open val view: View) {
|
||||
view.setPaddingRelative(view.paddingStart, view.paddingTop, padding, view.paddingBottom)
|
||||
}
|
||||
|
||||
private fun setPaddingVertical(padding: Int) {
|
||||
view.setPaddingRelative(view.paddingStart, padding, view.paddingEnd, padding)
|
||||
}
|
||||
|
||||
private fun setPaddingHorizontal(padding: Int) {
|
||||
view.setPaddingRelative(padding, view.paddingTop, padding, view.paddingBottom)
|
||||
}
|
||||
|
||||
protected fun setBackgroundTint(color: Int) {
|
||||
ViewCompat.setBackgroundTintList(view, ColorStateList.valueOf(color))
|
||||
}
|
||||
@@ -536,6 +570,71 @@ open class ViewAttributes(resourceParser: ResourceParser, open val view: View) {
|
||||
return result
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Suppress("SpellCheckingInspection")
|
||||
fun parseDayOfWeek(value: String) = when {
|
||||
value.matches(Regex("MON(DAY)?", RegexOption.IGNORE_CASE)) -> Calendar.MONDAY
|
||||
value.matches(Regex("TUE(SDAY)?", RegexOption.IGNORE_CASE)) -> Calendar.TUESDAY
|
||||
value.matches(Regex("WED(NESDAY)?", RegexOption.IGNORE_CASE)) -> Calendar.WEDNESDAY
|
||||
value.matches(Regex("THU(RSDAY)?", RegexOption.IGNORE_CASE)) -> Calendar.THURSDAY
|
||||
value.matches(Regex("FRI(DAY)?", RegexOption.IGNORE_CASE)) -> Calendar.FRIDAY
|
||||
value.matches(Regex("SAT(URDAY)?", RegexOption.IGNORE_CASE)) -> Calendar.SATURDAY
|
||||
value.matches(Regex("SUN(DAY)?", RegexOption.IGNORE_CASE)) -> Calendar.SUNDAY
|
||||
else -> {
|
||||
// @Caution by SuperMonster003 on May 19, 2023.
|
||||
// ! Calendar.XXX is not as same as JavaScript Date.
|
||||
// ! Take Tuesday as an example,
|
||||
// ! for Java, Calendar.TUESDAY is 3,
|
||||
// ! for JavaScript, Date#getDay() is 2.
|
||||
|
||||
// Compatibility for 0 is not necessary.
|
||||
// (value.toInt() + 6).mod(7) + 1
|
||||
|
||||
value.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
fun setMaxDate(view: CalendarView, value: String) {
|
||||
try {
|
||||
parseDate(value)?.time?.let { view.maxDate = it }
|
||||
} catch (e: ParseException) {
|
||||
throw InflateException(e)
|
||||
}
|
||||
}
|
||||
|
||||
fun setMaxDate(view: DatePicker, value: String) {
|
||||
try {
|
||||
parseDate(value)?.time?.let { view.maxDate = it }
|
||||
} catch (e: ParseException) {
|
||||
throw InflateException(e)
|
||||
}
|
||||
}
|
||||
|
||||
fun setMinDate(view: CalendarView, value: String) {
|
||||
try {
|
||||
parseDate(value)?.time?.let { view.minDate = it }
|
||||
} catch (e: ParseException) {
|
||||
throw InflateException(e)
|
||||
}
|
||||
}
|
||||
|
||||
fun setMinDate(view: DatePicker, value: String) {
|
||||
try {
|
||||
parseDate(value)?.time?.let { view.minDate = it }
|
||||
} catch (e: ParseException) {
|
||||
throw InflateException(e)
|
||||
}
|
||||
}
|
||||
|
||||
fun parseDate(value: String): Date? = when {
|
||||
value.matches(Regex("^\\d{4}/.+")) -> {
|
||||
SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).parse(value)
|
||||
}
|
||||
else -> {
|
||||
SimpleDateFormat("MM/dd/yyyy", Locale.getDefault()).parse(value)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmField
|
||||
val TINT_MODES: ValueMapper<PorterDuff.Mode> = ValueMapper<PorterDuff.Mode>("tintMode")
|
||||
.map("add", PorterDuff.Mode.ADD)
|
||||
|
||||
@@ -1,12 +1,31 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.os.Build
|
||||
import android.view.SurfaceView
|
||||
import android.view.TextureView
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import org.autojs.autojs.core.console.JsConsoleView
|
||||
import org.autojs.autojs.core.graphics.JsCanvasView
|
||||
import android.webkit.WebView
|
||||
import android.widget.*
|
||||
import androidx.appcompat.widget.AppCompatCheckBox
|
||||
import androidx.appcompat.widget.AppCompatSpinner
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.makeramen.roundedimageview.RoundedImageView
|
||||
import org.autojs.autojs.core.console.ConsoleView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.widget.*
|
||||
import org.autojs.autojs.core.ui.widget.JsCanvasView
|
||||
import org.autojs.autojs.core.ui.widget.JsCheckedTextView
|
||||
import org.autojs.autojs.core.ui.widget.JsConsoleView
|
||||
|
||||
/**
|
||||
* Modified by SuperMonster003 as of May 26, 2022.
|
||||
@@ -17,46 +36,238 @@ object ViewAttributesFactory {
|
||||
private val sViewAttributesCreators = HashMap<Class<out View>, ViewAttributesCreator>()
|
||||
|
||||
init {
|
||||
put(JsAppBarLayout::class.java, ::JsAppBarLayoutAttributes)
|
||||
put(JsButton::class.java, ::JsButtonAttributes)
|
||||
// @Hint by SuperMonster003 on Jun 12, 2023.
|
||||
// ! ASCII Tree for Views with attributes appended to.
|
||||
// !
|
||||
// ! android.view.View
|
||||
// ! ├─ android.view.SurfaceView
|
||||
// ! │ ├─ android.widget.VideoView
|
||||
// ! │ │ ├─ JsVideoView
|
||||
// ! ├─ android.view.TextureView
|
||||
// ! │ ├─ JsCanvasView
|
||||
// ! ├─ android.view.ViewGroup
|
||||
// ! │ ├─ android.widget.AbsoluteLayout
|
||||
// ! │ │ ├─ android.webkit.WebView
|
||||
// ! │ │ │ ├─ JsWebView
|
||||
// ! │ ├─ android.widget.AdapterView
|
||||
// ! │ │ ├─ android.widget.AbsSpinner
|
||||
// ! │ │ │ ├─ android.widget.Spinner
|
||||
// ! │ │ │ │ ├─ androidx.appcompat.widget.AppCompatSpinner
|
||||
// ! │ │ │ │ │ ├─ JsSpinner
|
||||
// ! │ ├─ android.widget.FrameLayout
|
||||
// ! │ │ ├─ android.widget.CalendarView
|
||||
// ! │ │ │ ├─ JsCalendarView
|
||||
// ! │ │ ├─ android.widget.DatePicker
|
||||
// ! │ │ │ ├─ JsDatePicker
|
||||
// ! │ │ ├─ android.widget.HorizontalScrollView
|
||||
// ! │ │ │ ├─ com.google.android.material.tabs.TabLayout
|
||||
// ! │ │ │ │ ├─ JsTabLayout
|
||||
// ! │ │ ├─ android.widget.ScrollView
|
||||
// ! │ │ │ ├─ JsScrollView
|
||||
// ! │ │ ├─ android.widget.TimePicker
|
||||
// ! │ │ │ ├─ JsTimePicker
|
||||
// ! │ │ ├─ android.widget.ViewAnimator
|
||||
// ! │ │ │ ├─ android.widget.ViewFlipper
|
||||
// ! │ │ │ │ ├─ JsViewFlipper
|
||||
// ! │ │ │ ├─ android.widget.ViewSwitcher
|
||||
// ! │ │ │ │ ├─ android.widget.ImageSwitcher
|
||||
// ! │ │ │ │ │ ├─ JsImageSwitcher
|
||||
// ! │ │ │ │ ├─ android.widget.TextSwitcher
|
||||
// ! │ │ │ │ │ ├─ JsTextSwitcher
|
||||
// ! │ │ │ │ ├─ JsViewSwitcher
|
||||
// ! │ │ ├─ androidx.cardview.widget.CardView
|
||||
// ! │ │ │ ├─ JsCardView
|
||||
// ! │ │ ├─ org.autojs.autojs.core.console.ConsoleView
|
||||
// ! │ │ │ ├─ JsConsoleView
|
||||
// ! │ │ ├─ JsFrameLayout
|
||||
// ! │ ├─ android.widget.LinearLayout
|
||||
// ! │ │ ├─ android.widget.NumberPicker
|
||||
// ! │ │ │ ├─ JsNumberPicker
|
||||
// ! │ │ ├─ android.widget.RadioGroup
|
||||
// ! │ │ │ ├─ JsRadioGroup
|
||||
// ! │ │ ├─ android.widget.SearchView
|
||||
// ! │ │ │ ├─ JsSearchView
|
||||
// ! │ │ ├─ com.google.android.material.appbar.AppBarLayout
|
||||
// ! │ │ │ ├─ JsAppBarLayout
|
||||
// ! │ │ ├─ JsLinearLayout
|
||||
// ! │ ├─ android.widget.RelativeLayout
|
||||
// ! │ │ ├─ JsRelativeLayout
|
||||
// ! │ ├─ androidx.appcompat.widget.Toolbar
|
||||
// ! │ │ ├─ JsToolbar
|
||||
// ! │ ├─ androidx.drawerlayout.widget.DrawerLayout
|
||||
// ! │ │ ├─ JsDrawerLayout
|
||||
// ! │ ├─ androidx.recyclerview.widget.RecyclerView
|
||||
// ! │ │ ├─ JsListView
|
||||
// ! │ │ │ ├─ JsGridView
|
||||
// ! │ ├─ androidx.viewpager.widget.ViewPager
|
||||
// ! │ │ ├─ JsViewPager
|
||||
// ! ├─ android.widget.ImageView
|
||||
// ! │ ├─ android.widget.ImageButton
|
||||
// ! │ │ ├─ com.google.android.material.internal.VisibilityAwareImageButton
|
||||
// ! │ │ │ ├─ com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
// ! │ │ │ │ ├─ JsFloatingActionButton
|
||||
// ! │ │ ├─ JsImageButton
|
||||
// ! │ ├─ android.widget.QuickContactBadge
|
||||
// ! │ │ ├─ JsQuickContactBadge
|
||||
// ! │ ├─ com.makeramen.roundedimageview.RoundedImageView
|
||||
// ! │ │ ├─ JsImageView
|
||||
// ! ├─ android.widget.ProgressBar
|
||||
// ! │ ├─ android.widget.AbsSeekBar
|
||||
// ! │ │ ├─ android.widget.RatingBar
|
||||
// ! │ │ │ ├─ JsRatingBar
|
||||
// ! │ │ ├─ android.widget.SeekBar
|
||||
// ! │ │ │ ├─ JsSeekBar
|
||||
// ! │ ├─ JsProgressBar
|
||||
// ! ├─ android.widget.Space
|
||||
// ! ├─ android.widget.TextView
|
||||
// ! │ ├─ android.widget.Button
|
||||
// ! │ │ ├─ android.widget.CompoundButton
|
||||
// ! │ │ │ ├─ android.widget.RadioButton
|
||||
// ! │ │ │ │ ├─ JsRadioButton
|
||||
// ! │ │ │ ├─ android.widget.CheckBox
|
||||
// ! │ │ │ │ ├─ androidx.appcompat.widget.AppCompatCheckBox
|
||||
// ! │ │ │ │ │ ├─ JsCheckBox
|
||||
// ! │ │ │ ├─ android.widget.ToggleButton
|
||||
// ! │ │ │ │ │ ├─ JsToggleButton
|
||||
// ! │ │ │ ├─ androidx.appcompat.widget.SwitchCompat
|
||||
// ! │ │ │ │ ├─ JsSwitch
|
||||
// ! │ │ ├─ JsButton
|
||||
// ! │ ├─ android.widget.CheckedTextView
|
||||
// ! │ │ ├─ JsCheckedTextView
|
||||
// ! │ ├─ android.widget.Chronometer
|
||||
// ! │ │ ├─ JsChronometer
|
||||
// ! │ ├─ android.widget.EditText
|
||||
// ! │ │ ├─ JsEditText
|
||||
// ! │ ├─ android.widget.TextClock
|
||||
// ! │ │ ├─ JsTextClock
|
||||
// ! │ ├─ androidx.appcompat.widget.AppCompatTextView
|
||||
// ! │ │ ├─ JsTextView
|
||||
|
||||
/* Level 0. */
|
||||
|
||||
put(View::class.java, ::ViewAttributes)
|
||||
|
||||
/* Level 1. */
|
||||
|
||||
put(ImageView::class.java, ::ImageViewAttributes)
|
||||
put(ProgressBar::class.java, ::ProgressBarAttributes)
|
||||
put(Space::class.java, ::SpaceAttributes)
|
||||
put(SurfaceView::class.java, ::SurfaceViewAttributes)
|
||||
put(TextView::class.java, ::TextViewAttributes)
|
||||
put(TextureView::class.java, ::TextureViewAttributes)
|
||||
put(ViewGroup::class.java, ::ViewGroupAttributes)
|
||||
|
||||
/* Level 2. */
|
||||
|
||||
put(AbsSeekBar::class.java, ::AbsSeekBarAttributes)
|
||||
put(AdapterView::class.java, ::AdapterViewAttributes)
|
||||
put(AppCompatTextView::class.java, ::AppCompatTextViewAttributes)
|
||||
put(Button::class.java, ::ButtonAttributes)
|
||||
put(CheckedTextView::class.java, ::CheckedTextViewAttributes)
|
||||
put(Chronometer::class.java, ::ChronometerAttributes)
|
||||
put(DrawerLayout::class.java, ::DrawerLayoutAttributes)
|
||||
put(EditText::class.java, ::EditTextAttributes)
|
||||
put(FrameLayout::class.java, ::FrameLayoutAttributes)
|
||||
put(ImageButton::class.java, ::ImageButtonAttributes)
|
||||
put(JsCanvasView::class.java, ::JsCanvasViewAttributes)
|
||||
put(JsCardView::class.java, ::JsCardViewAttributes)
|
||||
put(JsCheckBox::class.java, ::JsCheckBoxAttributes)
|
||||
put(JsConsoleView::class.java, ::JsConsoleViewAttributes)
|
||||
put(JsDatePicker::class.java, ::JsDatePickerAttributes)
|
||||
put(JsProgressBar::class.java, ::JsProgressBarAttributes)
|
||||
put(LinearLayout::class.java, ::LinearLayoutAttributes)
|
||||
put(QuickContactBadge::class.java, ::QuickContactBadgeAttributes)
|
||||
put(RecyclerView::class.java, ::RecyclerViewAttributes)
|
||||
put(RelativeLayout::class.java, ::RelativeLayoutAttributes)
|
||||
put(RoundedImageView::class.java, ::RoundedImageViewAttributes)
|
||||
put(TextClock::class.java, ::TextClockAttributes)
|
||||
put(Toolbar::class.java, ::ToolbarAttributes)
|
||||
put(VideoView::class.java, ::VideoViewAttributes)
|
||||
put(ViewPager::class.java, ::ViewPagerAttributes)
|
||||
|
||||
/* Level 3. */
|
||||
|
||||
put(AbsSpinner::class.java, ::AbsSpinnerAttributes)
|
||||
put(AppBarLayout::class.java, ::AppBarLayoutAttributes)
|
||||
put(CalendarView::class.java, ::CalendarViewAttributes)
|
||||
put(CardView::class.java, ::CardViewAttributes)
|
||||
put(CompoundButton::class.java, ::CompoundButtonAttributes)
|
||||
put(ConsoleView::class.java, ::ConsoleViewAttributes)
|
||||
put(DatePicker::class.java, ::DatePickerAttributes)
|
||||
put(HorizontalScrollView::class.java, ::HorizontalScrollViewAttributes)
|
||||
put(JsButton::class.java, ::JsButtonAttributes)
|
||||
put(JsCheckedTextView::class.java, ::JsCheckedTextViewAttributes)
|
||||
put(JsChronometer::class.java, ::JsChronometerAttributes)
|
||||
put(JsDrawerLayout::class.java, ::JsDrawerLayoutAttributes)
|
||||
put(JsEditText::class.java, ::JsEditTextAttributes)
|
||||
put(JsFloatingActionButton::class.java, ::JsFloatingActionButtonAttributes)
|
||||
put(JsFrameLayout::class.java, ::JsFrameLayoutAttributes)
|
||||
put(JsGridView::class.java, ::JsGridViewAttributes)
|
||||
put(JsImageButton::class.java, ::JsImageButtonAttributes)
|
||||
put(JsImageView::class.java, ::JsImageViewAttributes)
|
||||
put(JsLinearLayout::class.java, ::JsLinearLayoutAttributes)
|
||||
put(JsListView::class.java, ::JsListViewAttributes)
|
||||
put(JsProgressBar::class.java, ::JsProgressBarAttributes)
|
||||
put(JsRadioButton::class.java, ::JsRadioButtonAttributes)
|
||||
put(JsRadioGroup::class.java, ::JsRadioGroupAttributes)
|
||||
put(JsRatingBar::class.java, ::JsRatingBarAttributes)
|
||||
put(JsQuickContactBadge::class.java, ::JsQuickContactBadgeAttributes)
|
||||
put(JsRelativeLayout::class.java, ::JsRelativeLayoutAttributes)
|
||||
put(JsScrollView::class.java, ::JsScrollViewAttributes)
|
||||
put(JsSeekBar::class.java, ::JsSeekbarAttributes)
|
||||
put(JsSpinner::class.java, ::JsSpinnerAttributes)
|
||||
put(JsSwitch::class.java, ::JsSwitchAttributes)
|
||||
put(JsTabLayout::class.java, ::JsTabLayoutAttributes)
|
||||
put(JsTextClock::class.java, ::JsTextClockAttributes)
|
||||
put(JsTimePicker::class.java, ::JsTimePickerAttributes)
|
||||
put(JsToggleButton::class.java, ::JsToggleButtonAttributes)
|
||||
put(JsToolbar::class.java, ::JsToolbarAttributes)
|
||||
put(JsVideoView::class.java, ::JsVideoViewAttributes)
|
||||
put(JsViewPager::class.java, ::JsViewPagerAttributes)
|
||||
put(JsWebView::class.java, ::JsWebViewAttributes)
|
||||
|
||||
put(ViewGroup::class.java, ::ViewGroupAttributes)
|
||||
put(View::class.java, ::ViewAttributes)
|
||||
put(NumberPicker::class.java, ::NumberPickerAttributes)
|
||||
put(RadioGroup::class.java, ::RadioGroupAttributes)
|
||||
put(RatingBar::class.java, ::RatingBarAttributes)
|
||||
put(ScrollView::class.java, ::ScrollViewAttributes)
|
||||
put(SearchView::class.java, ::SearchViewAttributes)
|
||||
put(SeekBar::class.java, ::SeekBarAttributes)
|
||||
put(TimePicker::class.java, ::TimePickerAttributes)
|
||||
put(ViewAnimator::class.java, ::ViewAnimatorAttributes)
|
||||
put(WebView::class.java, ::WebViewAttributes)
|
||||
|
||||
when (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
true -> put(JsTextViewLegacy::class.java, ::JsTextViewLegacyAttributes)
|
||||
else -> put(JsTextView::class.java, ::JsTextViewAttributes)
|
||||
}
|
||||
|
||||
/* Level 4. */
|
||||
|
||||
put(CheckBox::class.java, ::CheckBoxAttributes)
|
||||
put(FloatingActionButton::class.java, ::FloatingActionButtonAttributes)
|
||||
put(JsAppBarLayout::class.java, ::JsAppBarLayoutAttributes)
|
||||
put(JsCalendarView::class.java, ::JsCalendarViewAttributes)
|
||||
put(JsCardView::class.java, ::JsCardViewAttributes)
|
||||
put(JsConsoleView::class.java, ::JsConsoleViewAttributes)
|
||||
put(JsDatePicker::class.java, ::JsDatePickerAttributes)
|
||||
put(JsGridView::class.java, ::JsGridViewAttributes)
|
||||
put(JsNumberPicker::class.java, ::JsNumberPickerAttributes)
|
||||
put(JsRadioGroup::class.java, ::JsRadioGroupAttributes)
|
||||
put(JsRatingBar::class.java, ::JsRatingBarAttributes)
|
||||
put(JsScrollView::class.java, ::JsScrollViewAttributes)
|
||||
put(JsSearchView::class.java, ::JsSearchViewAttributes)
|
||||
put(JsSeekBar::class.java, ::JsSeekBarAttributes)
|
||||
put(JsTimePicker::class.java, ::JsTimePickerAttributes)
|
||||
put(JsWebView::class.java, ::JsWebViewAttributes)
|
||||
put(RadioButton::class.java, ::RadioButtonAttributes)
|
||||
put(Spinner::class.java, ::SpinnerAttributes)
|
||||
put(SwitchCompat::class.java, ::SwitchCompatAttributes)
|
||||
put(TabLayout::class.java, ::TabLayoutAttributes)
|
||||
put(ToggleButton::class.java, ::ToggleButtonAttributes)
|
||||
put(ViewFlipper::class.java, ::ViewFlipperAttributes)
|
||||
put(ViewSwitcher::class.java, ::ViewSwitcherAttributes)
|
||||
|
||||
/* Level 5. */
|
||||
|
||||
put(AppCompatCheckBox::class.java, ::AppCompatCheckBoxAttributes)
|
||||
put(AppCompatSpinner::class.java, ::AppCompatSpinnerAttributes)
|
||||
put(ImageSwitcher::class.java, ::ImageSwitcherAttributes)
|
||||
put(JsFloatingActionButton::class.java, ::JsFloatingActionButtonAttributes)
|
||||
put(JsRadioButton::class.java, ::JsRadioButtonAttributes)
|
||||
put(JsSwitch::class.java, ::JsSwitchAttributes)
|
||||
put(JsTabLayout::class.java, ::JsTabLayoutAttributes)
|
||||
put(JsViewFlipper::class.java, ::JsViewFlipperAttributes)
|
||||
put(JsViewSwitcher::class.java, ::JsViewSwitcherAttributes)
|
||||
put(TextSwitcher::class.java, ::TextSwitcherAttributes)
|
||||
|
||||
/* Level 6. */
|
||||
|
||||
put(JsCheckBox::class.java, ::JsCheckBoxAttributes)
|
||||
put(JsImageSwitcher::class.java, ::JsImageSwitcherAttributes)
|
||||
put(JsSpinner::class.java, ::JsSpinnerAttributes)
|
||||
put(JsTextSwitcher::class.java, ::JsTextSwitcherAttributes)
|
||||
put(JsToggleButton::class.java, ::JsToggleButtonAttributes)
|
||||
}
|
||||
|
||||
fun put(clazz: Class<out View>, creator: (ResourceParser, View) -> ViewAttributes) {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ViewFlipper
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
|
||||
open class ViewFlipperAttributes(resourceParser: ResourceParser, view: View) : ViewAnimatorAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as ViewFlipper
|
||||
|
||||
override fun onRegisterAttrs() {
|
||||
super.onRegisterAttrs()
|
||||
|
||||
registerAttrs(arrayOf("isAutoStart", "autoStart")) { view.isAutoStart = it.toBoolean() }
|
||||
registerAttr("flipInterval") { view.flipInterval = it.toInt() }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.autojs.autojs.core.ui.attribute
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ViewSwitcher
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
|
||||
open class ViewSwitcherAttributes(resourceParser: ResourceParser, view: View): ViewAnimatorAttributes(resourceParser, view) {
|
||||
|
||||
override val view = super.view as ViewSwitcher
|
||||
|
||||
}
|
||||
@@ -112,6 +112,10 @@ public class JsDialog {
|
||||
return getActionButton(getDialogAction(action)).getText().toString();
|
||||
}
|
||||
|
||||
public MDButton getActionButton(@NonNull DialogAction which) {
|
||||
return mDialog.getActionButton(which);
|
||||
}
|
||||
|
||||
public void setActionButton(String action, String text) {
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
setActionButton(getDialogAction(action), text);
|
||||
@@ -120,6 +124,18 @@ public class JsDialog {
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void setActionButton(@NonNull DialogAction which, CharSequence title) {
|
||||
mDialog.setActionButton(which, title);
|
||||
}
|
||||
|
||||
public void setActionButton(DialogAction which, int titleRes) {
|
||||
mDialog.setActionButton(which, titleRes);
|
||||
}
|
||||
|
||||
public boolean hasActionButtons() {
|
||||
return mDialog.hasActionButtons();
|
||||
}
|
||||
|
||||
public MaterialDialog.Builder getBuilder() {
|
||||
return mDialog.getBuilder();
|
||||
@@ -154,10 +170,6 @@ public class JsDialog {
|
||||
mDialog.onClick(v);
|
||||
}
|
||||
|
||||
public MDButton getActionButton(@NonNull DialogAction which) {
|
||||
return mDialog.getActionButton(which);
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
return mDialog.getView();
|
||||
}
|
||||
@@ -185,19 +197,6 @@ public class JsDialog {
|
||||
return mDialog.getCustomView();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void setActionButton(@NonNull DialogAction which, CharSequence title) {
|
||||
mDialog.setActionButton(which, title);
|
||||
}
|
||||
|
||||
public void setActionButton(DialogAction which, int titleRes) {
|
||||
mDialog.setActionButton(which, titleRes);
|
||||
}
|
||||
|
||||
public boolean hasActionButtons() {
|
||||
return mDialog.hasActionButtons();
|
||||
}
|
||||
|
||||
public int numberOfActionButtons() {
|
||||
return mDialog.numberOfActionButtons();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class JsDialogBuilder extends MaterialDialog.Builder {
|
||||
private final Timer mTimer;
|
||||
private final Loopers mLoopers;
|
||||
private JsDialog mDialog;
|
||||
private volatile Loopers.AsyncTask task;
|
||||
private volatile int mWaitId = -1;
|
||||
|
||||
|
||||
public JsDialogBuilder(Context context, ScriptRuntime runtime) {
|
||||
@@ -55,14 +55,14 @@ public class JsDialogBuilder extends MaterialDialog.Builder {
|
||||
}
|
||||
});
|
||||
dismissListener(dialog -> {
|
||||
mTimer.postDelayed(() -> mLoopers.removeAsyncTask(task), 0);
|
||||
mTimer.postDelayed(() -> mLoopers.doNotWaitWhenIdle(mWaitId), 0);
|
||||
emit("dismiss", dialog);
|
||||
});
|
||||
cancelListener(dialog -> emit("cancel", dialog));
|
||||
}
|
||||
|
||||
public void onShowCalled() {
|
||||
mTimer.postDelayed(() -> task = mLoopers.createAndAddAsyncTask("js-dialog"), 0);
|
||||
mTimer.postDelayed(() -> mWaitId = mLoopers.waitWhenIdle(), 0);
|
||||
}
|
||||
|
||||
public JsDialog getDialog() {
|
||||
|
||||
@@ -9,8 +9,12 @@ import android.util.Log
|
||||
import android.view.InflateException
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import org.autojs.autojs.core.console.JsConsoleView
|
||||
import org.autojs.autojs.core.graphics.JsCanvasView
|
||||
import org.autojs.autojs.core.ui.widget.JsCheckedTextView
|
||||
import android.widget.Space
|
||||
import org.autojs.autojs.annotation.ScriptInterface
|
||||
import org.autojs.autojs.app.GlobalAppContext
|
||||
import org.autojs.autojs.core.ui.widget.JsConsoleView
|
||||
import org.autojs.autojs.core.ui.widget.JsCanvasView
|
||||
import org.autojs.autojs.core.ui.inflater.inflaters.*
|
||||
import org.autojs.autojs.core.ui.inflater.util.Res
|
||||
import org.autojs.autojs.core.ui.widget.*
|
||||
@@ -23,9 +27,12 @@ open class DynamicLayoutInflater {
|
||||
|
||||
private var mViewAttrSetters: MutableMap<String, ViewInflater<*>> = HashMap()
|
||||
private var mViewCreators: MutableMap<String, ViewCreator<*>> = HashMap()
|
||||
private var mLayoutInflaterDelegate = LayoutInflaterDelegate.NO_OP
|
||||
|
||||
var context: Context? = null
|
||||
@get:ScriptInterface
|
||||
@set:ScriptInterface
|
||||
var layoutInflaterDelegate: LayoutInflaterDelegate = LayoutInflaterDelegate.NO_OP
|
||||
|
||||
var context: Context? = GlobalAppContext.get()
|
||||
val resourceParser: ResourceParser
|
||||
var inflateFlags = 0
|
||||
|
||||
@@ -41,62 +48,75 @@ open class DynamicLayoutInflater {
|
||||
mViewCreators = HashMap(inflater.mViewCreators)
|
||||
}
|
||||
|
||||
var layoutInflaterDelegate: LayoutInflaterDelegate?
|
||||
get() = mLayoutInflaterDelegate
|
||||
set(layoutInflaterDelegate) {
|
||||
var niceLayoutInflaterDelegate = layoutInflaterDelegate
|
||||
if (niceLayoutInflaterDelegate == null) {
|
||||
niceLayoutInflaterDelegate = LayoutInflaterDelegate.NO_OP
|
||||
}
|
||||
mLayoutInflaterDelegate = niceLayoutInflaterDelegate!!
|
||||
}
|
||||
|
||||
protected fun registerViewAttrSetters() {
|
||||
registerViewAttrSetter(JsActionMenuView::class.java, JsActionMenuViewInflater(resourceParser))
|
||||
registerViewAttrSetter(JsAppBarLayout::class.java, JsAppBarLayoutInflater(resourceParser))
|
||||
registerViewAttrSetter(JsButton::class.java, JsButtonInflater(resourceParser))
|
||||
registerViewAttrSetter(JsCanvasView::class.java, JsCanvasViewInflater(resourceParser))
|
||||
registerViewAttrSetter(JsCardView::class.java, JsCardViewInflater(resourceParser))
|
||||
registerViewAttrSetter(JsCalendarView::class.java, JsCalendarViewInflater(resourceParser))
|
||||
registerViewAttrSetter(JsCheckBox::class.java, JsCheckBoxInflater(resourceParser))
|
||||
registerViewAttrSetter(JsCheckedTextView::class.java, JsCheckedTextViewInflater(resourceParser))
|
||||
registerViewAttrSetter(JsChronometer::class.java, JsChronometerInflater(resourceParser))
|
||||
registerViewAttrSetter(JsConsoleView::class.java, JsConsoleViewInflater(resourceParser))
|
||||
registerViewAttrSetter(JsDatePicker::class.java, JsDatePickerInflater(resourceParser))
|
||||
registerViewAttrSetter(JsDrawerLayout::class.java, JsDrawerLayoutInflater(resourceParser))
|
||||
registerViewAttrSetter(JsEditText::class.java, JsEditTextViewInflater(resourceParser))
|
||||
registerViewAttrSetter(JsEditText::class.java, JsEditTextInflater(resourceParser))
|
||||
registerViewAttrSetter(JsFloatingActionButton::class.java, JsFloatingActionButtonInflater(resourceParser))
|
||||
registerViewAttrSetter(JsFrameLayout::class.java, JsFrameLayoutInflater(resourceParser))
|
||||
registerViewAttrSetter(JsGridView::class.java, JsGridViewInflater<JsGridView>(resourceParser))
|
||||
registerViewAttrSetter(JsImageButton::class.java, JsImageButtonInflater(resourceParser))
|
||||
registerViewAttrSetter(JsImageView::class.java, JsImageViewInflater(resourceParser))
|
||||
registerViewAttrSetter(JsImageSwitcher::class.java, JsImageSwitcherInflater(resourceParser))
|
||||
registerViewAttrSetter(JsLinearLayout::class.java, JsLinearLayoutInflater(resourceParser))
|
||||
registerViewAttrSetter(JsListView::class.java, JsListViewInflater<JsListView>(resourceParser))
|
||||
registerViewAttrSetter(JsNumberPicker::class.java, JsNumberPickerInflater(resourceParser))
|
||||
registerViewAttrSetter(JsProgressBar::class.java, JsProgressBarInflater(resourceParser))
|
||||
registerViewAttrSetter(JsQuickContactBadge::class.java, JsQuickContactBadgeInflater(resourceParser))
|
||||
registerViewAttrSetter(JsRadioButton::class.java, JsRadioButtonInflater(resourceParser))
|
||||
registerViewAttrSetter(JsRadioGroup::class.java, JsRadioGroupInflater(resourceParser))
|
||||
registerViewAttrSetter(JsRatingBar::class.java, JsRatingBarInflater(resourceParser))
|
||||
registerViewAttrSetter(JsRelativeLayout::class.java, JsRelativeLayoutInflater(resourceParser))
|
||||
registerViewAttrSetter(JsScrollView::class.java, JsScrollViewInflater(resourceParser))
|
||||
registerViewAttrSetter(JsSearchView::class.java, JsSearchViewInflater(resourceParser))
|
||||
registerViewAttrSetter(JsSeekBar::class.java, JsSeekBarInflater(resourceParser))
|
||||
registerViewAttrSetter(JsSpinner::class.java, JsSpinnerInflater(resourceParser))
|
||||
registerViewAttrSetter(JsSwitch::class.java, JsSwitchInflater(resourceParser))
|
||||
registerViewAttrSetter(JsTabLayout::class.java, JsTabLayoutInflater(resourceParser))
|
||||
registerViewAttrSetter(JsTextClock::class.java, JsTextClockInflater(resourceParser))
|
||||
registerViewAttrSetter(JsTextSwitcher::class.java, JsTextSwitcherInflater(resourceParser))
|
||||
registerViewAttrSetter(JsTimePicker::class.java, JsTimePickerInflater(resourceParser))
|
||||
registerViewAttrSetter(JsToggleButton::class.java, JsToggleButtonInflater(resourceParser))
|
||||
registerViewAttrSetter(JsToolbar::class.java, JsToolbarInflater(resourceParser))
|
||||
registerViewAttrSetter(JsVideoView::class.java, JsVideoViewInflater(resourceParser))
|
||||
registerViewAttrSetter(JsViewFlipper::class.java, JsViewFlipperInflater(resourceParser))
|
||||
registerViewAttrSetter(JsViewPager::class.java, JsViewPagerInflater(resourceParser))
|
||||
registerViewAttrSetter(JsViewSwitcher::class.java, JsViewSwitcherInflater(resourceParser))
|
||||
registerViewAttrSetter(JsWebView::class.java, JsWebViewInflater(resourceParser))
|
||||
|
||||
registerViewAttrSetter(ViewGroup::class.java, ViewGroupInflater<ViewGroup>(resourceParser))
|
||||
registerViewAttrSetter(View::class.java, BaseViewInflater<View>(resourceParser))
|
||||
|
||||
when (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
true -> registerViewAttrSetter(JsTextViewLegacy::class.java, JsTextViewLegacyInflater(resourceParser))
|
||||
else -> registerViewAttrSetter(JsTextView::class.java, JsTextViewInflater(resourceParser))
|
||||
}
|
||||
|
||||
// TODO by SuperMonster003 on Jun 8, 2023.
|
||||
// ! Android XML like menu, shape, paths and so forth.
|
||||
// ! Not easy as expected.
|
||||
|
||||
// registerViewAttrSetter("menu", JsMenuInflater(resourceParser))
|
||||
|
||||
registerViewAttrSetter(Space::class.java, SpaceInflater(resourceParser))
|
||||
registerViewAttrSetter(ViewGroup::class.java, ViewGroupInflater<ViewGroup>(resourceParser))
|
||||
registerViewAttrSetter(View::class.java, BaseViewInflater<View>(resourceParser))
|
||||
}
|
||||
|
||||
fun registerViewAttrSetter(clazz: Class<*>, inflater: ViewInflater<*>) {
|
||||
mViewAttrSetters[clazz.name] = inflater
|
||||
inflater.getCreator()?.let { mViewCreators[clazz.name] = it }
|
||||
registerViewAttrSetter(clazz.name, inflater)
|
||||
}
|
||||
|
||||
fun registerViewAttrSetter(className: String, inflater: ViewInflater<*>) {
|
||||
mViewAttrSetters[className] = inflater
|
||||
inflater.getCreator()?.let { mViewCreators[className] = it }
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
@@ -106,9 +126,9 @@ open class DynamicLayoutInflater {
|
||||
}
|
||||
|
||||
fun inflate(context: InflateContext, xml: String, parent: ViewGroup?, attachToParent: Boolean): View {
|
||||
mLayoutInflaterDelegate.beforeInflation(context, xml, parent)?.let { return it }
|
||||
layoutInflaterDelegate.beforeInflation(context, xml, parent)?.let { return it }
|
||||
val niceXml = convertXml(context, xml)
|
||||
return mLayoutInflaterDelegate.afterInflation(context, doInflation(context, niceXml, parent, attachToParent), niceXml, parent)
|
||||
return layoutInflaterDelegate.afterInflation(context, doInflation(context, niceXml, parent, attachToParent), niceXml, parent)
|
||||
}
|
||||
|
||||
fun newInflateContext() = InflateContext()
|
||||
@@ -127,8 +147,8 @@ open class DynamicLayoutInflater {
|
||||
}
|
||||
|
||||
protected fun convertXml(context: InflateContext?, xml: String?): String {
|
||||
return mLayoutInflaterDelegate.beforeConvertXml(context, xml) ?: try {
|
||||
mLayoutInflaterDelegate.afterConvertXml(context, XmlConverter.convertToAndroidLayout(xml))
|
||||
return layoutInflaterDelegate.beforeConvertXml(context, xml) ?: try {
|
||||
layoutInflaterDelegate.afterConvertXml(context, XmlConverter.convertToAndroidLayout(xml))
|
||||
} catch (e: Exception) {
|
||||
throw InflateException(e)
|
||||
}
|
||||
@@ -141,12 +161,15 @@ open class DynamicLayoutInflater {
|
||||
}
|
||||
|
||||
protected fun doInflation(context: InflateContext, node: Node, parent: ViewGroup?, attachToParent: Boolean): View {
|
||||
var view = mLayoutInflaterDelegate.beforeInflateView(context, node, parent, attachToParent)
|
||||
var view = layoutInflaterDelegate.beforeInflateView(context, node, parent, attachToParent)
|
||||
if (view != null) {
|
||||
return view
|
||||
}
|
||||
val attrs = getAttributesMap(node)
|
||||
view = doCreateView(context, node, node.nodeName, parent, attrs)
|
||||
if (view is EmptyView) {
|
||||
return view
|
||||
}
|
||||
if (parent != null) {
|
||||
parent.addView(view) // have to add to parent to generate layout params
|
||||
if (!attachToParent) {
|
||||
@@ -161,21 +184,21 @@ open class DynamicLayoutInflater {
|
||||
applyPendingAttributesOfChildren(context, inflater as ViewGroupInflater<ViewGroup>, view)
|
||||
}
|
||||
}
|
||||
return mLayoutInflaterDelegate.afterInflateView(context, view, node, parent, attachToParent)
|
||||
return layoutInflaterDelegate.afterInflateView(context, view, node, parent, attachToParent)
|
||||
}
|
||||
|
||||
protected fun applyPendingAttributesOfChildren(context: InflateContext, inflater: ViewGroupInflater<ViewGroup>, view: ViewGroup?) {
|
||||
if (!mLayoutInflaterDelegate.beforeApplyPendingAttributesOfChildren(context, inflater, view)) {
|
||||
if (!layoutInflaterDelegate.beforeApplyPendingAttributesOfChildren(context, inflater, view)) {
|
||||
view?.let { inflater.applyPendingAttributesOfChildren(it) }
|
||||
mLayoutInflaterDelegate.afterApplyPendingAttributesOfChildren(context, inflater, view)
|
||||
layoutInflaterDelegate.afterApplyPendingAttributesOfChildren(context, inflater, view)
|
||||
}
|
||||
}
|
||||
|
||||
fun applyAttributes(context: InflateContext, view: View, attrs: HashMap<String, String>, parent: ViewGroup?): ViewInflater<View> {
|
||||
val inflater = getViewInflater(view)
|
||||
if (!mLayoutInflaterDelegate.beforeApplyAttributes(context, view, inflater, attrs, parent)) {
|
||||
if (!layoutInflaterDelegate.beforeApplyAttributes(context, view, inflater, attrs, parent)) {
|
||||
applyAttributes(context, view, inflater, attrs, parent)
|
||||
mLayoutInflaterDelegate.afterApplyAttributes(context, view, inflater, attrs, parent)
|
||||
layoutInflaterDelegate.afterApplyAttributes(context, view, inflater, attrs, parent)
|
||||
}
|
||||
return inflater
|
||||
}
|
||||
@@ -192,14 +215,14 @@ open class DynamicLayoutInflater {
|
||||
}
|
||||
|
||||
protected fun inflateChildren(context: InflateContext, inflater: ViewInflater<View>, node: Node, parent: ViewGroup?) {
|
||||
if (mLayoutInflaterDelegate.beforeInflateChildren(context, inflater, node, parent)) {
|
||||
if (layoutInflaterDelegate.beforeInflateChildren(context, inflater, node, parent)) {
|
||||
return
|
||||
}
|
||||
if (inflater.inflateChildren(this, node, parent)) {
|
||||
return
|
||||
}
|
||||
inflateChildren(context, node, parent)
|
||||
mLayoutInflaterDelegate.afterInflateChildren(context, inflater, node, parent)
|
||||
layoutInflaterDelegate.afterInflateChildren(context, inflater, node, parent)
|
||||
}
|
||||
|
||||
fun inflateChildren(context: InflateContext, node: Node, parent: ViewGroup?) {
|
||||
@@ -214,22 +237,25 @@ open class DynamicLayoutInflater {
|
||||
}
|
||||
|
||||
protected fun doCreateView(context: InflateContext?, node: Node?, viewName: String, parent: ViewGroup?, attrs: HashMap<String, String>): View {
|
||||
val view = mLayoutInflaterDelegate.beforeCreateView(context, node, viewName, parent)
|
||||
return view ?: mLayoutInflaterDelegate.afterCreateView(context, createViewForName(viewName, attrs), node, viewName, parent)
|
||||
val view = layoutInflaterDelegate.beforeCreateView(context, node, viewName, parent)
|
||||
return view ?: layoutInflaterDelegate.afterCreateView(context, createViewForName(viewName, attrs, parent), node, viewName, parent)
|
||||
}
|
||||
|
||||
fun createViewForName(name: String, attrs: HashMap<String, String>): View {
|
||||
fun createViewForName(name: String, attrs: HashMap<String, String>, parent: ViewGroup?): View {
|
||||
var niceName = name
|
||||
val androidWidgetPrefixBlacklist = listOf(
|
||||
"menu", "item", "shape", "paths", "set", "selector", "merge", "view",
|
||||
)
|
||||
return try {
|
||||
if (niceName == "View") {
|
||||
return View(context)
|
||||
}
|
||||
if (!niceName.contains(".")) {
|
||||
if (!niceName.contains(".") && !androidWidgetPrefixBlacklist.contains(niceName)) {
|
||||
niceName = "android.widget.$niceName"
|
||||
}
|
||||
val creator = mViewCreators[niceName]
|
||||
if (creator != null) {
|
||||
return creator.create(context, attrs)
|
||||
context?.let { ctx -> return creator.create(ctx, attrs, parent) }
|
||||
}
|
||||
val clazz = Class.forName(niceName)
|
||||
val style = attrs["style"]
|
||||
@@ -273,7 +299,7 @@ open class DynamicLayoutInflater {
|
||||
}
|
||||
|
||||
protected fun applyAttribute(context: InflateContext, inflater: ViewInflater<View>, view: View, ns: String?, attrName: String, value: String, parent: ViewGroup?) {
|
||||
if (mLayoutInflaterDelegate.beforeApplyAttribute(context, inflater, view, ns, attrName, value, parent)) {
|
||||
if (layoutInflaterDelegate.beforeApplyAttribute(context, inflater, view, ns, attrName, value, parent)) {
|
||||
return
|
||||
}
|
||||
val isDynamic = isDynamicValue(value)
|
||||
@@ -281,7 +307,7 @@ open class DynamicLayoutInflater {
|
||||
return
|
||||
}
|
||||
inflater.setAttr(view, ns, attrName, value, parent)
|
||||
mLayoutInflaterDelegate.afterApplyAttribute(context, inflater, view, ns, attrName, value, parent)
|
||||
layoutInflaterDelegate.afterApplyAttribute(context, inflater, view, ns, attrName, value, parent)
|
||||
}
|
||||
|
||||
private val isJustDynamicFlags: Boolean
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.autojs.autojs.core.ui.inflater
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
|
||||
class EmptyView(context: Context) : View(context)
|
||||
@@ -6,6 +6,8 @@ import android.net.Uri;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/11/3.
|
||||
*/
|
||||
@@ -23,7 +25,7 @@ public interface ImageLoader {
|
||||
|
||||
void loadIntoBackground(View view, Uri uri);
|
||||
|
||||
Drawable load(View view, Uri uri);
|
||||
@Nullable Drawable load(View view, Uri uri);
|
||||
|
||||
void load(View view, Uri uri, DrawableCallback callback);
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package org.autojs.autojs.core.ui.inflater;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/11/29.
|
||||
*/
|
||||
public interface ViewCreator<V extends View> {
|
||||
|
||||
V create(Context context, Map<String, String> attrs);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.autojs.autojs.core.ui.inflater
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/11/29.
|
||||
* Transformed by SuperMonster003 on Jun 8, 2023.
|
||||
*/
|
||||
interface ViewCreator<V : View> {
|
||||
|
||||
fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): V
|
||||
|
||||
}
|
||||
@@ -3,6 +3,6 @@ package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
import android.widget.AbsSeekBar
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
|
||||
open class AbsSeekbarInflater<V : AbsSeekBar>(resourceParser: ResourceParser) : ProgressBarInflater<V>(resourceParser) {
|
||||
open class AbsSeekBarInflater<V : AbsSeekBar>(resourceParser: ResourceParser) : ProgressBarInflater<V>(resourceParser) {
|
||||
// Empty inflater.
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.widget.AbsSpinner
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class AbsSpinnerInflater<V : AbsSpinner>(resourceParser: ResourceParser) : AdapterViewInflater<V>(resourceParser) {
|
||||
// Empty inflater.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ActionMenuView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class ActionMenuViewInflater<V : ActionMenuView>(resourceParser: ResourceParser) : LinearLayoutInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<ActionMenuView> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): ActionMenuView {
|
||||
return ActionMenuView(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class AppBarLayoutInflater<V : AppBarLayout>(resourceParser: ResourceParser) : LinearLayoutInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> AppBarLayout(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<AppBarLayout> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): AppBarLayout {
|
||||
return AppBarLayout(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.AppCompatCheckBox
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class AppCompatCheckBoxInflater<V : AppCompatCheckBox>(resourceParser: ResourceParser) : CheckBoxInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> AppCompatCheckBox(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<AppCompatCheckBox> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): AppCompatCheckBox {
|
||||
return AppCompatCheckBox(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.AppCompatSpinner
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class AppCompatSpinnerInflater<V : AppCompatSpinner>(resourceParser: ResourceParser) : SpinnerInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> AppCompatSpinner(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<AppCompatSpinner> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): AppCompatSpinner {
|
||||
return AppCompatSpinner(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class AppCompatTextViewInflater<V : AppCompatTextView>(resourceParser: ResourceParser) : TextViewInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in AppCompatTextView> = ViewCreator { context, _ -> AppCompatTextView(context) }
|
||||
override fun getCreator(): ViewCreator<in AppCompatTextView> = object : ViewCreator<AppCompatTextView> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): AppCompatTextView {
|
||||
return AppCompatTextView(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AutoCompleteTextView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class AutoCompleteTextViewInflater<V : AutoCompleteTextView>(resourceParser: ResourceParser) : EditTextInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<AutoCompleteTextView> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): AutoCompleteTextView {
|
||||
return AutoCompleteTextView(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class ButtonInflater<V : Button>(resourceParser: ResourceParser) : TextViewInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> Button(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<Button> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): Button {
|
||||
return Button(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CalendarView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class CalendarViewInflater<V : CalendarView>(resourceParser: ResourceParser) : FrameLayoutInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<CalendarView> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): CalendarView {
|
||||
return CalendarView(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.cardview.widget.CardView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
@@ -10,6 +12,10 @@ import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
*/
|
||||
open class CardViewInflater<V : CardView>(resourceParser: ResourceParser) : FrameLayoutInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> CardView(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<CardView> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): CardView {
|
||||
return CardView(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CheckBox
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class CheckBoxInflater<V: CheckBox>(resourceParser: ResourceParser): CompoundButtonInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> CheckBox(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<CheckBox> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): CheckBox {
|
||||
return CheckBox(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CheckedTextView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class CheckedTextViewInflater<V : CheckedTextView>(resourceParser: ResourceParser) : TextViewInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<CheckedTextView> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): CheckedTextView {
|
||||
return CheckedTextView(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Chronometer
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class ChronometerInflater<V : Chronometer>(resourceParser: ResourceParser) : TextViewInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<Chronometer> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): Chronometer {
|
||||
return Chronometer(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import org.autojs.autojs.core.console.ConsoleView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class ConsoleViewInflater<V: ConsoleView>(resourceParser: ResourceParser): FrameLayoutInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> ConsoleView(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<ConsoleView> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): ConsoleView {
|
||||
return ConsoleView(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.DatePicker
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
@@ -15,14 +16,16 @@ import org.autojs.autojs6.R
|
||||
open class DatePickerInflater<V: DatePicker>(resourceParser: ResourceParser) : FrameLayoutInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> {
|
||||
return ViewCreator { context: Context?, attrs: MutableMap<String?, String?> ->
|
||||
val datePickerMode = attrs.remove("android:datePickerMode")
|
||||
if (datePickerMode == null || datePickerMode != "spinner") {
|
||||
DatePicker(context)
|
||||
} else {
|
||||
(View.inflate(context, R.layout.date_picker_spinner, null) as DatePicker).apply {
|
||||
@Suppress("DEPRECATION")
|
||||
calendarViewShown = false
|
||||
return object : ViewCreator<DatePicker> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): DatePicker {
|
||||
val datePickerMode = attrs.remove("android:datePickerMode")
|
||||
return if (datePickerMode == null || datePickerMode != "spinner") {
|
||||
DatePicker(context)
|
||||
} else {
|
||||
(View.inflate(context, R.layout.date_picker_spinner, null) as DatePicker).apply {
|
||||
@Suppress("DEPRECATION")
|
||||
calendarViewShown = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class DrawerLayoutInflater<V : DrawerLayout>(resourceParser: ResourceParser) : ViewGroupInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> DrawerLayout(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<DrawerLayout> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): DrawerLayout {
|
||||
return DrawerLayout(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class EditTextInflater<V : EditText>(resourceParser: ResourceParser) : TextViewInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> EditText(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<EditText> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): EditText {
|
||||
return EditText(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
@@ -10,6 +12,10 @@ import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
*/
|
||||
open class FloatingActionButtonInflater<V : FloatingActionButton>(resourceParser: ResourceParser) : ImageViewInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> FloatingActionButton(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<FloatingActionButton> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): FloatingActionButton {
|
||||
return FloatingActionButton(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class FrameLayoutInflater<V : FrameLayout>(resourceParser: ResourceParser) : ViewGroupInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> FrameLayout(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<FrameLayout> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): FrameLayout {
|
||||
return FrameLayout(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import android.widget.HorizontalScrollView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class HorizontalScrollViewInflater<V : HorizontalScrollView>(resourceParser: ResourceParser) : FrameLayoutInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> HorizontalScrollView(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<HorizontalScrollView> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): HorizontalScrollView {
|
||||
return HorizontalScrollView(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class ImageButtonInflater<V : ImageButton>(resourceParser: ResourceParser) : ImageViewInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> ImageButton(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<ImageButton> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): ImageButton {
|
||||
return ImageButton(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageSwitcher
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
open class ImageSwitcherInflater<V : ImageSwitcher>(resourceParser: ResourceParser) : ViewSwitcherInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<ImageSwitcher> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): ImageSwitcher {
|
||||
return ImageSwitcher(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
@@ -11,6 +13,10 @@ import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
*/
|
||||
open class ImageViewInflater<V : ImageView>(resourceParser: ResourceParser) : BaseViewInflater<V>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in V> = ViewCreator { context, _ -> ImageView(context) }
|
||||
override fun getCreator(): ViewCreator<in V> = object : ViewCreator<ImageView> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): ImageView {
|
||||
return ImageView(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
import org.autojs.autojs.core.ui.widget.JsActionMenuView
|
||||
import org.autojs.autojs6.R
|
||||
import org.autojs.autojs6.databinding.JsToolbarBinding
|
||||
|
||||
|
||||
class JsActionMenuViewInflater(resourceParser: ResourceParser) : ActionMenuViewInflater<JsActionMenuView>(resourceParser) {
|
||||
|
||||
override fun getCreator() = object : ViewCreator<JsActionMenuView> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): JsActionMenuView {
|
||||
val amv = View.inflate(context, R.layout.js_actionmenuview, null) as JsActionMenuView
|
||||
|
||||
val toolbar = JsToolbarBinding.inflate(LayoutInflater.from(context)).toolbar
|
||||
|
||||
// FIXME by SuperMonster003 on Jun 7, 2023.
|
||||
// ! Doesn't work.
|
||||
@Suppress("DEPRECATION")
|
||||
toolbar.overflowIcon?.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)
|
||||
|
||||
return amv
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
import org.autojs.autojs.core.ui.widget.JsAppBarLayout
|
||||
@@ -8,8 +10,10 @@ import org.autojs.autojs6.R
|
||||
|
||||
class JsAppBarLayoutInflater(resourceParser: ResourceParser) : AppBarLayoutInflater<JsAppBarLayout>(resourceParser) {
|
||||
|
||||
override fun getCreator() = ViewCreator { context, _ ->
|
||||
View.inflate(context, R.layout.js_appbar, null) as JsAppBarLayout
|
||||
override fun getCreator() = object : ViewCreator<JsAppBarLayout> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): JsAppBarLayout {
|
||||
return View.inflate(context, R.layout.js_appbar, null) as JsAppBarLayout
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.R
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
import org.autojs.autojs.core.ui.inflater.util.Res
|
||||
@@ -7,25 +10,26 @@ import org.autojs.autojs.core.ui.widget.JsButton
|
||||
|
||||
class JsButtonInflater(resourceParser: ResourceParser) : ButtonInflater<JsButton>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in JsButton> = ViewCreator { context, attrs ->
|
||||
override fun getCreator(): ViewCreator<in JsButton> = object : ViewCreator<JsButton> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): JsButton {
|
||||
fun hasTrueAttr(name: String) = attrs["android:$name"] == "true"
|
||||
|
||||
fun hasTrueAttr(name: String) = attrs["android:$name"] == "true"
|
||||
attrs["style"]?.let { return JsButton(context, null, 0, Res.parseStyle(context, it)) }
|
||||
|
||||
attrs["style"]?.let { return@ViewCreator JsButton(context, null, 0, Res.parseStyle(context, it)) }
|
||||
|
||||
if (hasTrueAttr("isBorderlessColored") || hasTrueAttr("isColoredBorderless")) {
|
||||
return@ViewCreator JsButton(context, null, 0, androidx.appcompat.R.style.Widget_AppCompat_Button_Borderless_Colored)
|
||||
}
|
||||
if (hasTrueAttr("isColored")) {
|
||||
if (hasTrueAttr("isBorderless")) {
|
||||
return@ViewCreator JsButton(context, null, 0, androidx.appcompat.R.style.Widget_AppCompat_Button_Borderless_Colored)
|
||||
if (hasTrueAttr("isBorderlessColored") || hasTrueAttr("isColoredBorderless")) {
|
||||
return JsButton(context, null, 0, R.style.Widget_AppCompat_Button_Borderless_Colored)
|
||||
}
|
||||
return@ViewCreator JsButton(context, null, 0, androidx.appcompat.R.style.Widget_AppCompat_Button_Colored)
|
||||
if (hasTrueAttr("isColored")) {
|
||||
if (hasTrueAttr("isBorderless")) {
|
||||
return JsButton(context, null, 0, R.style.Widget_AppCompat_Button_Borderless_Colored)
|
||||
}
|
||||
return JsButton(context, null, 0, R.style.Widget_AppCompat_Button_Colored)
|
||||
}
|
||||
if (hasTrueAttr("isBorderless")) {
|
||||
return JsButton(context, null, 0, R.style.Widget_AppCompat_Button_Borderless)
|
||||
}
|
||||
return JsButton(context)
|
||||
}
|
||||
if (hasTrueAttr("isBorderless")) {
|
||||
return@ViewCreator JsButton(context, null, 0, androidx.appcompat.R.style.Widget_AppCompat_Button_Borderless)
|
||||
}
|
||||
return@ViewCreator JsButton(context)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
import org.autojs.autojs.core.ui.widget.JsCalendarView
|
||||
|
||||
class JsCalendarViewInflater(resourceParser: ResourceParser) : CalendarViewInflater<JsCalendarView>(resourceParser) {
|
||||
|
||||
override fun getCreator(): ViewCreator<in JsCalendarView> = object : ViewCreator<JsCalendarView> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): JsCalendarView {
|
||||
return JsCalendarView(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.autojs.autojs.core.ui.inflater.inflaters
|
||||
|
||||
import org.autojs.autojs.core.graphics.JsCanvasView
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import org.autojs.autojs.core.ui.widget.JsCanvasView
|
||||
import org.autojs.autojs.core.ui.inflater.ResourceParser
|
||||
import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
|
||||
@@ -9,6 +11,10 @@ import org.autojs.autojs.core.ui.inflater.ViewCreator
|
||||
*/
|
||||
class JsCanvasViewInflater(resourceParser: ResourceParser) : TextureViewInflater<JsCanvasView>(resourceParser) {
|
||||
|
||||
override fun getCreator() = ViewCreator { context, _ -> JsCanvasView(context) }
|
||||
override fun getCreator() = object : ViewCreator<JsCanvasView> {
|
||||
override fun create(context: Context, attrs: HashMap<String, String>, parent: ViewGroup?): JsCanvasView {
|
||||
return JsCanvasView(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user