6.7.0 - Alpha6 - 使用 LiveData 及 SharedFlow 替代已弃用的 LocalBroadcastManager
This commit is contained in:
@@ -37,6 +37,7 @@
|
||||
"文件管理器浮动按钮展开后点击菜单项时优化菜单收起时机",
|
||||
"崩溃报告页面支持双指缩放调整字体大小并添加常用功能按钮",
|
||||
"应用启动器图标支持自适应图标特性 _[`issue #405`](http://issues.autojs6.com/405)_",
|
||||
"使用 LiveData 及 SharedFlow 替代已弃用的 LocalBroadcastManager",
|
||||
"Gradle 构建脚本提升 7z 格式文件的解压效率",
|
||||
"使用版本目录 (Version Catalogs) 集中管理 Gradle 依赖和插件版本",
|
||||
"模块化 Gradle 脚本, 将共享构建逻辑迁移至 buildSrc 并抽象为约定插件",
|
||||
@@ -62,7 +63,8 @@
|
||||
"升级 Androidx AppCompat 版本 1.7.0 -> 1.7.1",
|
||||
"升级 Material Progressbar 版本 1.4.2 -> 1.6.1",
|
||||
"升级 Androidx Room 版本 2.7.1 -> 2.7.2",
|
||||
"升级 OpenCC 版本 1.2.0 -> 1.2.2"
|
||||
"升级 OpenCC 版本 1.2.0 -> 1.2.2",
|
||||
"升级 Desugar JDK Libs 版本 2.0.4 -> 2.1.5"
|
||||
]
|
||||
},
|
||||
"v6.6.4": {
|
||||
|
||||
@@ -49,9 +49,6 @@ dependencies /* Unclassified */ {
|
||||
// Androidx Core
|
||||
implementation(libs.core.ktx)
|
||||
|
||||
// Local Broadcast Manager
|
||||
implementation(libs.localbroadcastmanager)
|
||||
|
||||
// LeakCanary
|
||||
debugImplementation(libs.leakcanary)
|
||||
|
||||
@@ -427,6 +424,12 @@ dependencies /* Archived */ {
|
||||
// ! It was ever imported and used for MediaType constants.
|
||||
// ! zh-CN: 曾用于 MediaType 常量的导入及使用.
|
||||
// implementation("javax.ws.rs:javax.ws.rs-api:2.1.1")
|
||||
|
||||
// Local Broadcast Manager
|
||||
// @Comment by SuperMonster003 on Sep 27, 2025.
|
||||
// ! Deprecated. Chould by replaced with LiveData or SharedFlow.
|
||||
// ! zh-CN: 已弃用. 可用 LiveData 或 SharedFlow 替代.
|
||||
// implementation(libs.localbroadcastmanager)
|
||||
}
|
||||
|
||||
dependencies /* Reserved for auto append by IDE */ {
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.autojs.autojs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
@@ -11,7 +10,6 @@ import android.os.Build
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.multidex.MultiDexApplication
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.target.CustomViewTarget
|
||||
@@ -21,6 +19,7 @@ import com.google.mlkit.common.sdkinternal.MlKitContext
|
||||
import com.hjq.toast.Toaster
|
||||
import com.tencent.bugly.Bugly
|
||||
import com.tencent.bugly.crashreport.CrashReport
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import org.autojs.autojs.apkbuilder.ApkBuilder
|
||||
import org.autojs.autojs.app.GlobalAppContext
|
||||
import org.autojs.autojs.core.pref.Pref
|
||||
@@ -28,6 +27,7 @@ import org.autojs.autojs.core.ui.inflater.ImageLoader
|
||||
import org.autojs.autojs.core.ui.inflater.util.Drawables
|
||||
import org.autojs.autojs.event.GlobalKeyObserver
|
||||
import org.autojs.autojs.external.receiver.DynamicBroadcastReceivers
|
||||
import org.autojs.autojs.ipc.InAppEventBus
|
||||
import org.autojs.autojs.leakcanary.LeakCanarySetup
|
||||
import org.autojs.autojs.pluginclient.DevPluginService
|
||||
import org.autojs.autojs.runtime.api.augment.ocr.Ocr
|
||||
@@ -86,7 +86,7 @@ class App : MultiDexApplication() {
|
||||
if (!BuildConfig.DEBUG) {
|
||||
@Suppress("SpellCheckingInspection")
|
||||
FlurryAgent.Builder()
|
||||
.withLogEnabled(BuildConfig.DEBUG)
|
||||
.withLogEnabled(false)
|
||||
.build(this, "D42MH48ZN4PJC5TKNYZD")
|
||||
}
|
||||
}
|
||||
@@ -94,9 +94,9 @@ class App : MultiDexApplication() {
|
||||
private fun setUpDebugEnvironment() {
|
||||
Bugly.isDev = false
|
||||
val crashHandler = CrashHandler(CrashReportActivity::class.java)
|
||||
|
||||
val strategy = CrashReport.UserStrategy(applicationContext)
|
||||
strategy.setCrashHandleCallback(crashHandler)
|
||||
val strategy = CrashReport.UserStrategy(applicationContext).apply {
|
||||
crashHandleCallback = crashHandler
|
||||
}
|
||||
|
||||
CrashReport.initCrashReport(applicationContext, BUGLY_APP_ID, false, strategy)
|
||||
|
||||
@@ -128,27 +128,44 @@ class App : MultiDexApplication() {
|
||||
private fun initDynamicBroadcastReceivers() {
|
||||
dynamicBroadcastReceivers = DynamicBroadcastReceivers(this)
|
||||
val localActions = ArrayList<String>()
|
||||
val actions = ArrayList<String>()
|
||||
val systemActions = ArrayList<String>()
|
||||
TimedTaskManager.allIntentTasks
|
||||
.filter { task -> task.action != null }
|
||||
.doOnComplete {
|
||||
if (localActions.isNotEmpty()) {
|
||||
dynamicBroadcastReceivers.register(localActions, true)
|
||||
}
|
||||
if (actions.isNotEmpty()) {
|
||||
dynamicBroadcastReceivers.register(actions, false)
|
||||
if (systemActions.isNotEmpty()) {
|
||||
dynamicBroadcastReceivers.register(systemActions, false)
|
||||
}
|
||||
|
||||
// @Archived by SuperMonster003 on Sep 27, 2025.
|
||||
// ! LocalBroadcastManager is deprecated.
|
||||
// ! zh-CN: LocalBroadcastManager 已被弃用.
|
||||
// # Intent(DynamicBroadcastReceivers.ACTION_STARTUP).let {
|
||||
// # LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(it)
|
||||
// # }
|
||||
|
||||
// @Hint by JetBrains AI Assistant on Sep 27, 2025.
|
||||
// ! Why schedule on next main-loop tick?
|
||||
// ! Post the emit to the next frame on the main thread to avoid a race where
|
||||
// ! the SharedFlow collectors (registered via launchIn on Main) are not yet active
|
||||
// ! at the exact moment of emission. Emitting in the next loop ensures all
|
||||
// ! subscriptions are set up, preventing event loss when replay=0.
|
||||
// ! zh-CN:
|
||||
// ! 为什么要在主线程的下一帧再触发?
|
||||
// ! 将触发放到主线程消息队列的下一轮执行, 避免 "先发后订" 的竞态:
|
||||
// ! 订阅 (launchIn(Main)) 尚未真正激活时如果立即发送, replay=0 会丢事件.
|
||||
// ! 下一帧再发可以确保收集器已就绪, 从而可靠接收启动事件.
|
||||
AndroidSchedulers.mainThread().scheduleDirect {
|
||||
InAppEventBus.tryEmit(DynamicBroadcastReceivers.ACTION_STARTUP)
|
||||
}
|
||||
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(
|
||||
Intent(
|
||||
DynamicBroadcastReceivers.ACTION_STARTUP
|
||||
)
|
||||
)
|
||||
}
|
||||
.subscribe({
|
||||
if (it.isLocal) {
|
||||
localActions.add(it.action)
|
||||
} else {
|
||||
actions.add(it.action)
|
||||
systemActions.add(it.action)
|
||||
}
|
||||
}, { it.printStackTrace() })
|
||||
|
||||
@@ -235,6 +252,11 @@ class App : MultiDexApplication() {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
dynamicBroadcastReceivers.unregisterAll()
|
||||
InAppEventBus.clear()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val BUGLY_APP_ID = "19b3607b53"
|
||||
|
||||
@@ -2,18 +2,21 @@ package org.autojs.autojs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.util.Log
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.autojs.autojs.core.accessibility.AccessibilityTool
|
||||
import org.autojs.autojs.core.accessibility.Capture
|
||||
import org.autojs.autojs.core.accessibility.LayoutInspector.CaptureAvailableListener
|
||||
import org.autojs.autojs.core.console.GlobalConsole
|
||||
import org.autojs.autojs.execution.ScriptExecutionGlobalListener
|
||||
import org.autojs.autojs.external.fileprovider.AppFileProvider
|
||||
import org.autojs.autojs.ipc.LayoutInspectEvent
|
||||
import org.autojs.autojs.ipc.LayoutInspectEventBus
|
||||
import org.autojs.autojs.runtime.ScriptRuntime
|
||||
import org.autojs.autojs.runtime.api.AppUtils
|
||||
import org.autojs.autojs.runtime.api.AppUtils.Companion.ActivityShortForm
|
||||
@@ -39,37 +42,52 @@ open class AutoJs(appContext: Application) : AbstractAutoJs(appContext) {
|
||||
|
||||
private val mA11yTool = AccessibilityTool(appContext)
|
||||
|
||||
private val mJob = Job()
|
||||
private val mScope = CoroutineScope(Dispatchers.Main.immediate + mJob)
|
||||
|
||||
init {
|
||||
scriptEngineService.registerGlobalScriptExecutionListener(ScriptExecutionGlobalListener())
|
||||
|
||||
LocalBroadcastManager.getInstance(appContext).registerReceiver(object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
try {
|
||||
val action = intent.action ?: return
|
||||
when {
|
||||
action.equals(LayoutBoundsFloatyWindow::class.java.name, true) -> {
|
||||
mA11yTool.ensureService()
|
||||
capture(object : LayoutInspectFloatyWindow {
|
||||
override fun create(capture: Capture) = LayoutBoundsFloatyWindow(capture, context, true)
|
||||
})
|
||||
}
|
||||
action.equals(LayoutHierarchyFloatyWindow::class.java.name, true) -> {
|
||||
mA11yTool.ensureService()
|
||||
capture(object : LayoutInspectFloatyWindow {
|
||||
override fun create(capture: Capture) = LayoutHierarchyFloatyWindow(capture, context, true)
|
||||
})
|
||||
}
|
||||
// @Archived by SuperMonster003 on Sep 27, 2025.
|
||||
// ! LocalBroadcastManager is deprecated.
|
||||
// ! zh-CN: LocalBroadcastManager 已被弃用.
|
||||
// # LocalBroadcastManager.getInstance(appContext).registerReceiver(object : BroadcastReceiver() {
|
||||
// # override fun onReceive(context: Context, intent: Intent) {
|
||||
// # ... ...
|
||||
// # val action = intent.action ?: return
|
||||
// # when {
|
||||
// # action.equals(LayoutBoundsFloatyWindow::class.java.name, true) -> { ... ... }
|
||||
// # action.equals(LayoutHierarchyFloatyWindow::class.java.name, true) -> { ... ... }
|
||||
// # }
|
||||
// # ... ...
|
||||
// # }
|
||||
// # }, IntentFilter().apply {
|
||||
// # addAction(LayoutBoundsFloatyWindow::class.java.name)
|
||||
// # addAction(LayoutHierarchyFloatyWindow::class.java.name)
|
||||
// # })
|
||||
|
||||
LayoutInspectEventBus.events.onEach { event ->
|
||||
try {
|
||||
when (event) {
|
||||
LayoutInspectEvent.ShowLayoutBounds -> {
|
||||
mA11yTool.ensureService()
|
||||
capture(object : LayoutInspectFloatyWindow {
|
||||
override fun create(capture: Capture) = LayoutBoundsFloatyWindow(capture, appContext, true)
|
||||
})
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (isBackgroundThread()) {
|
||||
throw e
|
||||
LayoutInspectEvent.ShowLayoutHierarchy -> {
|
||||
mA11yTool.ensureService()
|
||||
capture(object : LayoutInspectFloatyWindow {
|
||||
override fun create(capture: Capture) = LayoutHierarchyFloatyWindow(capture, appContext, true)
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (isBackgroundThread()) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}, IntentFilter().apply {
|
||||
addAction(LayoutBoundsFloatyWindow::class.java.name)
|
||||
addAction(LayoutHierarchyFloatyWindow::class.java.name)
|
||||
})
|
||||
}.launchIn(mScope)
|
||||
}
|
||||
|
||||
private interface LayoutInspectFloatyWindow {
|
||||
@@ -131,6 +149,10 @@ open class AutoJs(appContext: Application) : AbstractAutoJs(appContext) {
|
||||
).forEach { putProperty(it.fullName, it.className) }
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
mJob.cancel()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var isInitialized = false
|
||||
private val TAG = AutoJs::class.java.simpleName
|
||||
|
||||
@@ -36,7 +36,7 @@ public class BaseBroadcastReceiver extends BroadcastReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
static void runTask(Context context, Intent intent, IntentTask task) {
|
||||
public static void runTask(Context context, Intent intent, IntentTask task) {
|
||||
Log.d(LOG_TAG, "runTask: action = " + intent.getAction() + ", script = " + task.getScriptPath());
|
||||
ScriptFile file = new ScriptFile(task.getScriptPath());
|
||||
ExecutionConfig config = new ExecutionConfig();
|
||||
|
||||
@@ -1,151 +1,182 @@
|
||||
package org.autojs.autojs.external.receiver;
|
||||
package org.autojs.autojs.external.receiver
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Build;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import android.util.Log;
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.autojs.autojs.ipc.InAppEventBus
|
||||
import org.autojs.autojs.timing.IntentTask
|
||||
import org.autojs.autojs.timing.TimedTaskManager
|
||||
|
||||
import org.autojs.autojs.timing.IntentTask;
|
||||
class DynamicBroadcastReceivers(private val context: Context) {
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
private val mActions: MutableSet<String> = LinkedHashSet()
|
||||
private val mReceiverRegistries: MutableList<ReceiverRegistry> = ArrayList<ReceiverRegistry>()
|
||||
private val mDefaultActionReceiver = BaseBroadcastReceiver()
|
||||
private val mPackageActionReceiver = BaseBroadcastReceiver()
|
||||
|
||||
public class DynamicBroadcastReceivers {
|
||||
|
||||
public static final String ACTION_STARTUP = "org.autojs.autojs.action.startup";
|
||||
|
||||
private static final String LOG_TAG = "DynBroadcastReceivers";
|
||||
|
||||
private final Set<String> mActions = new LinkedHashSet<>();
|
||||
private final List<ReceiverRegistry> mReceiverRegistries = new ArrayList<>();
|
||||
private final BaseBroadcastReceiver mDefaultActionReceiver = new BaseBroadcastReceiver();
|
||||
private final BaseBroadcastReceiver mPackageActionReceiver = new BaseBroadcastReceiver();
|
||||
private final Context mContext;
|
||||
|
||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||
public DynamicBroadcastReceivers(Context context) {
|
||||
mContext = context;
|
||||
init {
|
||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
mContext.registerReceiver(mDefaultActionReceiver, createIntentFilter(StaticBroadcastReceiver.ACTIONS), Context.RECEIVER_EXPORTED);
|
||||
context.registerReceiver(mDefaultActionReceiver, createIntentFilter(StaticBroadcastReceiver.ACTIONS), Context.RECEIVER_EXPORTED)
|
||||
} else {
|
||||
mContext.registerReceiver(mDefaultActionReceiver, createIntentFilter(StaticBroadcastReceiver.ACTIONS));
|
||||
context.registerReceiver(mDefaultActionReceiver, createIntentFilter(StaticBroadcastReceiver.ACTIONS))
|
||||
}
|
||||
IntentFilter filter = createIntentFilter(StaticBroadcastReceiver.PACKAGE_ACTIONS);
|
||||
filter.addDataScheme("package");
|
||||
val filter: IntentFilter = createIntentFilter(StaticBroadcastReceiver.PACKAGE_ACTIONS)
|
||||
filter.addDataScheme("package")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
mContext.registerReceiver(mPackageActionReceiver, filter, Context.RECEIVER_EXPORTED);
|
||||
context.registerReceiver(mPackageActionReceiver, filter, Context.RECEIVER_EXPORTED)
|
||||
} else {
|
||||
mContext.registerReceiver(mPackageActionReceiver, filter);
|
||||
context.registerReceiver(mPackageActionReceiver, filter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void registerIntent(IntentTask task) {
|
||||
register(Collections.singletonList(task.getAction()), task.isLocal());
|
||||
fun registerIntent(task: IntentTask) {
|
||||
register(mutableListOf(task.action), task.isLocal)
|
||||
}
|
||||
|
||||
public synchronized void register(List<String> actions, boolean local) {
|
||||
LinkedHashSet<String> newActions = new LinkedHashSet<>();
|
||||
for (String action : actions) {
|
||||
if (!StaticBroadcastReceiver.ACTIONS.contains(action)
|
||||
&& !StaticBroadcastReceiver.PACKAGE_ACTIONS.contains(action)
|
||||
&& !mActions.contains(action)) {
|
||||
newActions.add(action);
|
||||
@Synchronized
|
||||
fun register(actions: MutableList<String>, local: Boolean) {
|
||||
val newActions = LinkedHashSet<String>()
|
||||
for (action in actions) {
|
||||
if (!StaticBroadcastReceiver.ACTIONS.contains(action) && !StaticBroadcastReceiver.PACKAGE_ACTIONS.contains(action) && !mActions.contains(action)
|
||||
) {
|
||||
newActions.add(action)
|
||||
}
|
||||
}
|
||||
if (newActions.isEmpty()) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
ReceiverRegistry receiverRegistry = new ReceiverRegistry(newActions, local);
|
||||
receiverRegistry.register();
|
||||
mReceiverRegistries.add(receiverRegistry);
|
||||
val receiverRegistry = ReceiverRegistry(newActions, local)
|
||||
receiverRegistry.register()
|
||||
mReceiverRegistries.add(receiverRegistry)
|
||||
mActions.addAll(newActions)
|
||||
}
|
||||
|
||||
public synchronized void unregister(String action) {
|
||||
@Synchronized
|
||||
fun unregister(action: String?) {
|
||||
if (!mActions.contains(action)) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
mActions.remove(action);
|
||||
Iterator<ReceiverRegistry> iterator = mReceiverRegistries.iterator();
|
||||
mActions.remove(action)
|
||||
val iterator = mReceiverRegistries.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
ReceiverRegistry receiverRegistry = iterator.next();
|
||||
val receiverRegistry = iterator.next()
|
||||
if (!receiverRegistry.actions.contains(action)) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
receiverRegistry.actions.remove(action);
|
||||
receiverRegistry.unregister();
|
||||
receiverRegistry.actions.remove(action)
|
||||
receiverRegistry.unregister()
|
||||
if (!receiverRegistry.register()) {
|
||||
iterator.remove();
|
||||
iterator.remove()
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void unregisterAll() {
|
||||
for (ReceiverRegistry registry : mReceiverRegistries) {
|
||||
registry.unregister();
|
||||
@Synchronized
|
||||
fun unregisterAll() {
|
||||
for (registry in mReceiverRegistries) {
|
||||
registry.unregister()
|
||||
}
|
||||
mReceiverRegistries.clear();
|
||||
mReceiverRegistries.clear()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
mContext.unregisterReceiver(mDefaultActionReceiver);
|
||||
mContext.unregisterReceiver(mPackageActionReceiver);
|
||||
context.unregisterReceiver(mDefaultActionReceiver)
|
||||
context.unregisterReceiver(mPackageActionReceiver)
|
||||
}
|
||||
}
|
||||
|
||||
static IntentFilter createIntentFilter(Collection<String> actions) {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
for (String action : actions) {
|
||||
filter.addAction(action);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
private inner class ReceiverRegistry(var actions: LinkedHashSet<String>, var local: Boolean) {
|
||||
|
||||
private class ReceiverRegistry {
|
||||
BroadcastReceiver receiver;
|
||||
LinkedHashSet<String> actions;
|
||||
boolean local;
|
||||
|
||||
ReceiverRegistry(LinkedHashSet<String> actions, boolean local) {
|
||||
this.actions = actions;
|
||||
this.local = local;
|
||||
receiver = new BaseBroadcastReceiver();
|
||||
}
|
||||
|
||||
void unregister() {
|
||||
if (local) {
|
||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(mContext);
|
||||
broadcastManager.unregisterReceiver(receiver);
|
||||
} else {
|
||||
mContext.unregisterReceiver(receiver);
|
||||
}
|
||||
}
|
||||
var receiver: BroadcastReceiver = BaseBroadcastReceiver()
|
||||
var jobs: List<Job> = emptyList()
|
||||
|
||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||
boolean register() {
|
||||
if (actions.isEmpty())
|
||||
return false;
|
||||
IntentFilter intentFilter = createIntentFilter(actions);
|
||||
if (local) {
|
||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(mContext);
|
||||
broadcastManager.registerReceiver(receiver, intentFilter);
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
mContext.registerReceiver(receiver, intentFilter, Context.RECEIVER_EXPORTED);
|
||||
} else {
|
||||
mContext.registerReceiver(receiver, intentFilter);
|
||||
}
|
||||
fun register(): Boolean {
|
||||
if (actions.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
if (local) {
|
||||
|
||||
// @Archived by SuperMonster003 on Sep 27, 2025.
|
||||
// ! LocalBroadcastManager is deprecated.
|
||||
// ! zh-CN: LocalBroadcastManager 已被弃用.
|
||||
// # LocalBroadcastManager.getInstance(mContext).registerReceiver(receiver, intentFilter);
|
||||
|
||||
if (jobs.isNotEmpty()) {
|
||||
jobs.forEach { it.cancel() }
|
||||
}
|
||||
jobs = actions.map { action ->
|
||||
InAppEventBus.streamOf(action).onEach {
|
||||
try {
|
||||
TimedTaskManager.getIntentTaskOfAction(action)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ intentTask: IntentTask ->
|
||||
BaseBroadcastReceiver.runTask(context, Intent(action), intentTask)
|
||||
}, { obj: Throwable? ->
|
||||
obj?.printStackTrace()
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}.launchIn(appScope)
|
||||
}
|
||||
Log.d(LOG_TAG, "register (in-app): $actions")
|
||||
} else {
|
||||
val intentFilter: IntentFilter = createIntentFilter(actions)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
context.registerReceiver(receiver, intentFilter, Context.RECEIVER_EXPORTED)
|
||||
} else {
|
||||
context.registerReceiver(receiver, intentFilter)
|
||||
}
|
||||
Log.d(LOG_TAG, "register (system): $actions")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun unregister() {
|
||||
if (local) {
|
||||
|
||||
// @Archived by SuperMonster003 on Sep 27, 2025.
|
||||
// ! LocalBroadcastManager is deprecated.
|
||||
// ! zh-CN: LocalBroadcastManager 已被弃用.
|
||||
// # LocalBroadcastManager.getInstance(mContext).unregisterReceiver(receiver);
|
||||
|
||||
jobs.forEach { it.cancel() }
|
||||
jobs = emptyList()
|
||||
} else {
|
||||
context.unregisterReceiver(receiver)
|
||||
}
|
||||
Log.d(LOG_TAG, "register: " + actions);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val LOG_TAG = "DynBroadcastReceivers"
|
||||
private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
||||
|
||||
@JvmStatic
|
||||
val ACTION_STARTUP = "org.autojs.autojs.action.startup"
|
||||
|
||||
private fun createIntentFilter(actions: MutableCollection<String>): IntentFilter {
|
||||
return IntentFilter().apply {
|
||||
actions.forEach { addAction(it) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
36
app/src/main/java/org/autojs/autojs/ipc/InAppEventBus.kt
Normal file
36
app/src/main/java/org/autojs/autojs/ipc/InAppEventBus.kt
Normal file
@@ -0,0 +1,36 @@
|
||||
package org.autojs.autojs.ipc
|
||||
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
||||
object InAppEventBus {
|
||||
|
||||
// Map<action, eventStream>
|
||||
private val streams = mutableMapOf<String, MutableSharedFlow<Unit>>()
|
||||
|
||||
@JvmStatic
|
||||
@Synchronized
|
||||
fun streamOf(action: String): SharedFlow<Unit> {
|
||||
return streams.getOrPut(action) {
|
||||
MutableSharedFlow(
|
||||
replay = 1,
|
||||
extraBufferCapacity = 64,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Synchronized
|
||||
fun tryEmit(action: String): Boolean {
|
||||
return streams[action]?.tryEmit(Unit) == true
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Synchronized
|
||||
fun clear() {
|
||||
streams.clear()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.autojs.autojs.ipc
|
||||
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
||||
sealed interface LayoutInspectEvent {
|
||||
object ShowLayoutBounds : LayoutInspectEvent
|
||||
object ShowLayoutHierarchy : LayoutInspectEvent
|
||||
}
|
||||
|
||||
object LayoutInspectEventBus {
|
||||
// One-time event: replay=0, provide buffering
|
||||
// and discard oldest on overflow to avoid emit suspension.
|
||||
// zh-CN: 单次事件: replay=0, 提供一定缓冲并在溢出时丢弃最旧, 避免 emit 挂起.
|
||||
private val _events = MutableSharedFlow<LayoutInspectEvent>(
|
||||
replay = 0,
|
||||
extraBufferCapacity = 64,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
val events: SharedFlow<LayoutInspectEvent> get() = _events
|
||||
|
||||
suspend fun showLayoutBounds() = _events.emit(LayoutInspectEvent.ShowLayoutBounds)
|
||||
suspend fun showLayoutHierarchy() = _events.emit(LayoutInspectEvent.ShowLayoutHierarchy)
|
||||
}
|
||||
@@ -2,16 +2,26 @@ package org.autojs.autojs.runtime.api.augment.app
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.Settings
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.core.net.toUri
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.autojs.autojs.annotation.RhinoFunctionBody
|
||||
import org.autojs.autojs.annotation.RhinoRuntimeFunctionInterface
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsNullish
|
||||
import org.autojs.autojs.extension.AnyExtensions.jsBrief
|
||||
import org.autojs.autojs.extension.FlexibleArray
|
||||
import org.autojs.autojs.extension.ScriptableExtensions.defineProp
|
||||
import org.autojs.autojs.extension.ScriptableExtensions.deleteProp
|
||||
import org.autojs.autojs.extension.ScriptableExtensions.prop
|
||||
import org.autojs.autojs.extension.ScriptableObjectExtensions.inquire
|
||||
import org.autojs.autojs.external.receiver.BaseBroadcastReceiver
|
||||
import org.autojs.autojs.ipc.LayoutInspectEventBus
|
||||
import org.autojs.autojs.runtime.ScriptRuntime
|
||||
import org.autojs.autojs.runtime.api.AppUtils.Companion.ActivityShortForm
|
||||
import org.autojs.autojs.runtime.api.AppUtils.Companion.BroadcastShortForm
|
||||
@@ -20,6 +30,8 @@ import org.autojs.autojs.runtime.api.augment.Augmentable
|
||||
import org.autojs.autojs.runtime.api.augment.shell.Shell
|
||||
import org.autojs.autojs.runtime.exception.ShouldNeverHappenException
|
||||
import org.autojs.autojs.runtime.exception.WrappedIllegalArgumentException
|
||||
import org.autojs.autojs.timing.IntentTask
|
||||
import org.autojs.autojs.timing.TimedTaskManager
|
||||
import org.autojs.autojs.util.RhinoUtils
|
||||
import org.autojs.autojs.util.RhinoUtils.UNDEFINED
|
||||
import org.autojs.autojs.util.RhinoUtils.coerceBoolean
|
||||
@@ -32,13 +44,10 @@ import org.autojs.autojs.util.RootUtils
|
||||
import org.autojs.autojs6.BuildConfig
|
||||
import org.mozilla.javascript.Context
|
||||
import org.mozilla.javascript.NativeObject
|
||||
import org.mozilla.javascript.ScriptableObject.PERMANENT
|
||||
import org.mozilla.javascript.Undefined
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import org.autojs.autojs.util.App as PresetApp
|
||||
import androidx.core.net.toUri
|
||||
import org.autojs.autojs.extension.ScriptableExtensions.deleteProp
|
||||
|
||||
@Suppress("unused", "UNUSED_PARAMETER")
|
||||
class App(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
|
||||
@@ -423,15 +432,14 @@ class App(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
|
||||
fun sendBroadcast(scriptRuntime: ScriptRuntime, args: Array<out Any?>): Undefined = ensureArgumentsOnlyOne(args) { o ->
|
||||
when (o) {
|
||||
is String -> {
|
||||
runCatching {
|
||||
sendLocalBroadcastSyncInternal(intentRhinoWithRuntime(scriptRuntime, newNativeObject().also {
|
||||
val value = BroadcastShortForm.valueOf(o.uppercase()).className
|
||||
it.defineProp("action", value, PERMANENT)
|
||||
}))
|
||||
}.getOrElse {
|
||||
when (it) {
|
||||
is WrappedIllegalArgumentException -> throw WrappedIllegalArgumentException("Broadcast short form $o cannot be found")
|
||||
else -> throw it
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
when (BroadcastShortForm.valueOf(o.uppercase())) {
|
||||
BroadcastShortForm.INSPECT_LAYOUT_BOUNDS -> LayoutInspectEventBus.showLayoutBounds()
|
||||
BroadcastShortForm.LAYOUT_BOUNDS -> LayoutInspectEventBus.showLayoutBounds()
|
||||
BroadcastShortForm.BOUNDS -> LayoutInspectEventBus.showLayoutBounds()
|
||||
BroadcastShortForm.INSPECT_LAYOUT_HIERARCHY -> LayoutInspectEventBus.showLayoutHierarchy()
|
||||
BroadcastShortForm.LAYOUT_HIERARCHY -> LayoutInspectEventBus.showLayoutHierarchy()
|
||||
BroadcastShortForm.HIERARCHY -> LayoutInspectEventBus.showLayoutHierarchy()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -459,7 +467,35 @@ class App(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
|
||||
|
||||
@JvmStatic
|
||||
fun sendLocalBroadcastSyncInternal(intent: Intent?) {
|
||||
intent?.let { LocalBroadcastManager.getInstance(globalContext).sendBroadcastSync(it) }
|
||||
|
||||
// @Archived by SuperMonster003 on Sep 27, 2025.
|
||||
// ! LocalBroadcastManager is deprecated.
|
||||
// ! zh-CN: LocalBroadcastManager 已被弃用.
|
||||
// # intent?.let { LocalBroadcastManager.getInstance(globalContext).sendBroadcastSync(it) }
|
||||
|
||||
intent?.action?.let { action ->
|
||||
try {
|
||||
// Get tasks synchronously in background thread, then execute in main thread.
|
||||
// zh-CN: 在后台线程同步获取任务, 再回到主线程执行.
|
||||
val mainHandler = Handler(Looper.getMainLooper())
|
||||
Schedulers.io().scheduleDirect {
|
||||
try {
|
||||
val task = TimedTaskManager.getIntentTaskOfAction(action)
|
||||
.firstOrError()
|
||||
.blockingGet()
|
||||
task?.let {
|
||||
mainHandler.post {
|
||||
BaseBroadcastReceiver.runTask(globalContext, intent, it)
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.google.android.material.tabs.TabLayout
|
||||
import com.huaban.analysis.jieba.CharsDictionaryDatabase
|
||||
import com.huaban.analysis.jieba.PhrasesDictionaryDatabase
|
||||
import com.huaban.analysis.jieba.WordDictionaryDatabase
|
||||
import org.autojs.autojs.App
|
||||
import org.autojs.autojs.AutoJs
|
||||
import org.autojs.autojs.app.FragmentPagerAdapterBuilder
|
||||
import org.autojs.autojs.app.FragmentPagerAdapterBuilder.StoredFragmentPagerAdapter
|
||||
@@ -318,6 +319,9 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity {
|
||||
ScreenCapturerForegroundService.stopService()
|
||||
|
||||
AutoJs.instance.scriptEngineService.stopAll()
|
||||
AutoJs.instance.clear()
|
||||
|
||||
App.app.clear()
|
||||
|
||||
WordDictionaryDatabase.getInstance(applicationContext).close()
|
||||
CharsDictionaryDatabase.getInstance(applicationContext).close()
|
||||
|
||||
@@ -420,7 +420,9 @@ class TimedTaskSettingActivity : BaseActivity() {
|
||||
val task = IntentTask().apply {
|
||||
action = actionString
|
||||
scriptPath = mScriptFile.path
|
||||
isLocal = actionString == DynamicBroadcastReceivers.ACTION_STARTUP
|
||||
if (actionString == DynamicBroadcastReceivers.ACTION_STARTUP) {
|
||||
isLocal = true
|
||||
}
|
||||
}
|
||||
when (val intentTask = mIntentTask) {
|
||||
null -> {
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
[versions]
|
||||
|
||||
#deprecated : Will be removed in the future
|
||||
#substitution : LiveData or SharedFlow
|
||||
#since : Sep 25, 2025 (by SuperMonster003)
|
||||
localbroadcastmanager = "1.1.0"
|
||||
|
||||
#strictly : 4.12.0
|
||||
#purpose : Compatibility for kotlin 2.0.x (e.g. 2.0.21)
|
||||
#because : Version 5.0.0+ requires kotlin 2.2.0+
|
||||
@@ -37,7 +32,7 @@ jackson-databind = "2.13.4.2"
|
||||
log4j = "1.2.17"
|
||||
|
||||
#purpose : Compatibility of java.time.* for Android API Level < 26 (Android 8.0) [O]
|
||||
desugar = "2.0.4"
|
||||
desugar = "2.1.5"
|
||||
|
||||
analytics = "14.4.0"
|
||||
android-device-names = "2.1.1"
|
||||
@@ -151,7 +146,6 @@ junit-quickcheck-core = { module = "com.pholser:junit-quickcheck-core", version.
|
||||
junit-quickcheck-generators = { module = "com.pholser:junit-quickcheck-generators", version.ref = "junit-quickcheck-generators" }
|
||||
leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" }
|
||||
licensesdialog = { module = "de.psdev.licensesdialog:licensesdialog", version.ref = "licensesdialog" }
|
||||
localbroadcastmanager = { module = "androidx.localbroadcastmanager:localbroadcastmanager", version.ref = "localbroadcastmanager" }
|
||||
log4j = { module = "log4j:log4j", version.ref = "log4j" }
|
||||
material = { module = "com.google.android.material:material", version.ref = "material" }
|
||||
materialprogressbar = { module = "me.zhanghai.android.materialprogressbar:library", version.ref = "materialprogressbar" }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#Thu Sep 25 19:23:59 CST 2025
|
||||
BUILD_TIME=1758799439530
|
||||
#Sun Sep 28 22:30:49 CST 2025
|
||||
BUILD_TIME=1759069849955
|
||||
COMPILE_SDK_VERSION=36
|
||||
IMAGE_QUANT_CMAKE_VERSION=3.22.1
|
||||
IMAGE_QUANT_NDK_VERSION=26.1.10909125
|
||||
@@ -23,6 +23,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13
|
||||
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
|
||||
TARGET_SDK_VERSION=36
|
||||
TARGET_SDK_VERSION_INRT=29
|
||||
VERSION_BUILD=3379
|
||||
VERSION_BUILD=3386
|
||||
VERSION_NAME=6.7.0 Alpha6
|
||||
VSCODE_EXT_REQUIRED_VERSION=1.0.8
|
||||
|
||||
Reference in New Issue
Block a user