6.3.2 - 新增 crypto 模块 / 日志复制及导出; 修复 runtime.loadDex 及 loadJar; 优化客户端及服务端连接体验

This commit is contained in:
SuperMonster003
2023-07-07 01:02:28 +08:00
parent f754e880d3
commit 29c2f4a03a
651 changed files with 20786 additions and 3753 deletions

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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()) {

View File

@@ -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();
}

View File

@@ -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 {

View File

@@ -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
/**

View File

@@ -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)+")
}
}

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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))
}

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View 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()
}
}

View File

@@ -47,7 +47,7 @@ public class ScriptCanvas {
return mCanvas;
}
void setCanvas(Canvas canvas) {
public void setCanvas(Canvas canvas) {
mCanvas = canvas;
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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) {

View 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);
}
}

View File

@@ -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 {}
}
}

View 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);
}
}

View File

@@ -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 {}
}
}

View 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() + "]";
}
}

View File

@@ -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())
}
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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) }
}
}

View File

@@ -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) }
}
}

View File

@@ -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() }
}
}

View File

@@ -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()
}
}
}

View File

@@ -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))) }
}
}

View File

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

View File

@@ -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) {

View File

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

View File

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

View File

@@ -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) {

View File

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

View File

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

View File

@@ -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

View File

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

View File

@@ -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

View File

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

View File

@@ -0,0 +1,11 @@
package org.autojs.autojs.core.ui.attribute
import android.view.View
import org.autojs.autojs.core.ui.inflater.ResourceParser
import org.autojs.autojs.core.ui.widget.JsSeekBar
class JsSeekbarAttributes(resourceParser: ResourceParser, view: View) : SeekBarAttributes<JsSeekBar>(resourceParser, view) {
override val view = super.view as JsSeekBar
}

View File

@@ -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

View File

@@ -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) }
}
}
}

View File

@@ -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()
}
}
}
}

View File

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

View File

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

View File

@@ -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
}
}
}

View File

@@ -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

View File

@@ -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()
}
}

View File

@@ -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

View File

@@ -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() }
}
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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()) }
}
}

View File

@@ -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)) }
}
}

View File

@@ -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)) }

View File

@@ -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) }
}
}

View File

@@ -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() }
}
}

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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() }
}
}

View File

@@ -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
}

View File

@@ -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();
}

View File

@@ -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() {

View File

@@ -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

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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
}

View File

@@ -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.
}

View File

@@ -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.

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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
}
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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)
}

View File

@@ -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)
}
}
}

View File

@@ -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