- 根因:多处代码重复调用getMediaProjection()创建新实例,系统自动stop旧实例触发onStop回调,形成权限丢失恢复再丢失的死循环 - ScreenCaptureManager.ensureMediaProjection():禁止重复创建,只从Holder获取已有对象 - ScreenCaptureManager.captureWithMediaProjection():移除重复创建逻辑 - ScreenCaptureManager.triggerPermissionRecovery():优先从Holder和SmartManager获取已有对象 - ScreenCaptureManager.regenerateMediaProjectionForAndroid15():优先复用Holder中已有对象 - SmartMediaProjectionManager.attemptSilentRecovery():先检查Holder是否已有有效对象 - Android15MediaProjectionManager.attemptSilentRecovery():先检查Holder复用已有对象 - Android15MediaProjectionManager.determineStopReason():修复误判逻辑,Holder中仍有有效对象时识别为旧实例被替换而非用户主动停止 - AccessibilityRemoteService.attemptAndroid15SilentRecovery():优先复用Holder中已有对象 - AccessibilityRemoteService.handleMediaProjectionGranted():Android 11+优先从Holder获取 - RemoteControlForegroundService.handleStartMediaProjection():优先检查Holder避免重复创建
668 lines
22 KiB
Kotlin
668 lines
22 KiB
Kotlin
package com.hikoncont.manager
|
||
|
||
import android.content.Context
|
||
import android.content.Intent
|
||
import android.content.IntentFilter
|
||
import android.content.BroadcastReceiver
|
||
import android.media.projection.MediaProjection
|
||
import android.media.projection.MediaProjectionManager
|
||
import android.os.Build
|
||
import android.os.Handler
|
||
import android.os.Looper
|
||
import android.util.Log
|
||
import com.hikoncont.MediaProjectionHolder
|
||
import com.hikoncont.service.AccessibilityRemoteService
|
||
import kotlinx.coroutines.*
|
||
import java.util.concurrent.atomic.AtomicBoolean
|
||
import java.util.concurrent.atomic.AtomicInteger
|
||
|
||
/**
|
||
* 智能MediaProjection管理器
|
||
*
|
||
* 专门解决Android 15投屏权限丢失问题:
|
||
* 1. 智能区分用户主动停止和系统自动停止
|
||
* 2. 多层级权限保持策略
|
||
* 3. 非侵入式权限恢复机制
|
||
* 4. 向下兼容所有Android版本
|
||
*/
|
||
class SmartMediaProjectionManager(
|
||
private val context: Context
|
||
) {
|
||
|
||
companion object {
|
||
private const val TAG = "SmartMediaProjection"
|
||
|
||
// 恢复策略配置
|
||
private const val MAX_SILENT_RECOVERY_ATTEMPTS = 5 // 最大静默恢复次数
|
||
private const val MAX_USER_RECOVERY_ATTEMPTS = 3 // 最大用户确认恢复次数
|
||
private const val SILENT_RECOVERY_DELAY = 3000L // 静默恢复延迟(3秒)
|
||
private const val USER_RECOVERY_DELAY = 30000L // 用户恢复延迟(30秒)
|
||
private const val PERMISSION_CHECK_INTERVAL = 10000L // 权限检查间隔(10秒)
|
||
|
||
// Android版本特定配置
|
||
private const val ANDROID_15_MIN_RECOVERY_INTERVAL = 120000L // Android 15最小恢复间隔(2分钟)
|
||
|
||
@Volatile
|
||
private var instance: SmartMediaProjectionManager? = null
|
||
|
||
fun getInstance(context: Context): SmartMediaProjectionManager {
|
||
return instance ?: synchronized(this) {
|
||
instance ?: SmartMediaProjectionManager(context.applicationContext).also { instance = it }
|
||
}
|
||
}
|
||
}
|
||
|
||
// 状态管理
|
||
private var mediaProjection: MediaProjection? = null
|
||
private var mediaProjectionManager: MediaProjectionManager? = null
|
||
private val isInitialized = AtomicBoolean(false)
|
||
private val isRecovering = AtomicBoolean(false)
|
||
private val silentRecoveryAttempts = AtomicInteger(0)
|
||
private val userRecoveryAttempts = AtomicInteger(0)
|
||
|
||
// 时间记录
|
||
@Volatile private var lastPermissionLostTime = 0L
|
||
@Volatile private var lastRecoveryTime = 0L
|
||
@Volatile private var lastUserInteractionTime = 0L
|
||
|
||
// 协程管理
|
||
private val managerScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
||
private var monitoringJob: Job? = null
|
||
|
||
// 回调接口
|
||
interface PermissionStateListener {
|
||
fun onPermissionLost(reason: LossReason)
|
||
fun onPermissionRecovered()
|
||
fun onPermissionFailedToRecover()
|
||
}
|
||
|
||
enum class LossReason {
|
||
USER_STOPPED, // 用户主动停止
|
||
SYSTEM_LOCK_SCREEN, // 系统锁屏停止
|
||
SYSTEM_AUTO_STOP, // 系统其他原因停止
|
||
APP_BACKGROUNDED, // 应用后台化
|
||
UNKNOWN // 未知原因
|
||
}
|
||
|
||
private val listeners = mutableSetOf<PermissionStateListener>()
|
||
|
||
// 系统状态监听
|
||
private val systemStateReceiver = object : BroadcastReceiver() {
|
||
override fun onReceive(context: Context?, intent: Intent?) {
|
||
when (intent?.action) {
|
||
Intent.ACTION_SCREEN_OFF -> {
|
||
Log.i(TAG, "📱 设备锁屏,标记可能的权限丢失原因")
|
||
lastUserInteractionTime = System.currentTimeMillis()
|
||
}
|
||
Intent.ACTION_USER_PRESENT -> {
|
||
Log.i(TAG, "📱 用户解锁,检查权限状态")
|
||
checkPermissionStatusAfterUnlock()
|
||
}
|
||
"android.mycustrecev.USER_STOPPED_PROJECTION" -> {
|
||
Log.i(TAG, "👤 收到用户主动停止投屏信号")
|
||
handleUserStoppedProjection()
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 智能MediaProjection回调
|
||
* 能够智能判断权限丢失的原因
|
||
*/
|
||
private val smartCallback = object : MediaProjection.Callback() {
|
||
override fun onStop() {
|
||
Log.w(TAG, "🛑 MediaProjection权限丢失")
|
||
|
||
val currentTime = System.currentTimeMillis()
|
||
lastPermissionLostTime = currentTime
|
||
|
||
// 智能判断丢失原因
|
||
val lossReason = determineLossReason(currentTime)
|
||
|
||
// 清理当前MediaProjection引用
|
||
cleanupCurrentProjection()
|
||
|
||
// 通知监听器
|
||
notifyPermissionLost(lossReason)
|
||
|
||
// 根据丢失原因选择恢复策略
|
||
selectRecoveryStrategy(lossReason)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 初始化智能管理器
|
||
*/
|
||
fun initialize(): Boolean {
|
||
return try {
|
||
if (isInitialized.get()) {
|
||
Log.d(TAG, "管理器已初始化")
|
||
return true
|
||
}
|
||
|
||
Log.i(TAG, "🚀 初始化智能MediaProjection管理器")
|
||
|
||
// 初始化MediaProjectionManager
|
||
mediaProjectionManager = context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as? MediaProjectionManager
|
||
|
||
if (mediaProjectionManager == null) {
|
||
Log.e(TAG, "❌ 无法获取MediaProjectionManager")
|
||
return false
|
||
}
|
||
|
||
// 注册系统状态监听
|
||
registerSystemStateReceiver()
|
||
|
||
// 检查现有权限
|
||
checkAndRestoreExistingPermission()
|
||
|
||
// 启动权限状态监控
|
||
startPermissionMonitoring()
|
||
|
||
isInitialized.set(true)
|
||
Log.i(TAG, "✅ 智能MediaProjection管理器初始化成功")
|
||
|
||
true
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 初始化失败", e)
|
||
false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 智能判断权限丢失原因
|
||
*/
|
||
private fun determineLossReason(lossTime: Long): LossReason {
|
||
return when {
|
||
// 用户最近与状态栏交互(可能点击了停止按钮)
|
||
lossTime - lastUserInteractionTime < 5000 -> {
|
||
Log.i(TAG, "🤔 判断为用户主动停止(最近有用户交互)")
|
||
LossReason.USER_STOPPED
|
||
}
|
||
|
||
// 锁屏相关的权限丢失
|
||
lossTime - lastUserInteractionTime < 2000 -> {
|
||
Log.i(TAG, "🔒 判断为系统锁屏自动停止")
|
||
LossReason.SYSTEM_LOCK_SCREEN
|
||
}
|
||
|
||
// Android 15特有的系统自动停止
|
||
Build.VERSION.SDK_INT >= 35 -> {
|
||
Log.i(TAG, "🤖 Android 15系统自动停止")
|
||
LossReason.SYSTEM_AUTO_STOP
|
||
}
|
||
|
||
else -> {
|
||
Log.i(TAG, "❓ 未知原因的权限丢失")
|
||
LossReason.UNKNOWN
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据丢失原因选择恢复策略
|
||
*/
|
||
private fun selectRecoveryStrategy(reason: LossReason) {
|
||
when (reason) {
|
||
LossReason.USER_STOPPED -> {
|
||
Log.i(TAG, "👤 用户主动停止,不进行自动恢复")
|
||
// 用户主动停止,重置恢复计数器但不自动恢复
|
||
resetRecoveryCounters()
|
||
}
|
||
|
||
LossReason.SYSTEM_LOCK_SCREEN -> {
|
||
Log.i(TAG, "🔒 锁屏导致的权限丢失,等待解锁后恢复")
|
||
// 等待用户解锁后再恢复
|
||
scheduleUnlockRecovery()
|
||
}
|
||
|
||
LossReason.SYSTEM_AUTO_STOP,
|
||
LossReason.APP_BACKGROUNDED,
|
||
LossReason.UNKNOWN -> {
|
||
Log.i(TAG, "🤖 系统自动停止,启动智能恢复")
|
||
// 系统原因,启动智能恢复
|
||
startSmartRecovery()
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 启动智能恢复机制
|
||
*/
|
||
private fun startSmartRecovery() {
|
||
if (isRecovering.get()) {
|
||
Log.d(TAG, "🔄 恢复进程已在进行中")
|
||
return
|
||
}
|
||
|
||
managerScope.launch {
|
||
try {
|
||
isRecovering.set(true)
|
||
|
||
// 首先尝试静默恢复
|
||
if (attemptSilentRecovery()) {
|
||
Log.i(TAG, "✅ 静默恢复成功")
|
||
return@launch
|
||
}
|
||
|
||
// 静默恢复失败,根据情况决定是否提示用户
|
||
if (shouldPromptUserRecovery()) {
|
||
scheduleUserPromptedRecovery()
|
||
} else {
|
||
Log.i(TAG, "🤐 不满足用户提示条件,等待下次机会")
|
||
}
|
||
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 智能恢复失败", e)
|
||
} finally {
|
||
isRecovering.set(false)
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 尝试静默恢复
|
||
*
|
||
* 🚨 核心修复:优先检查 Holder 中是否已有有效对象,
|
||
* 避免重复调用 getMediaProjection() 创建新实例导致旧实例被 stop
|
||
*/
|
||
private suspend fun attemptSilentRecovery(): Boolean {
|
||
val currentAttempts = silentRecoveryAttempts.get()
|
||
|
||
if (currentAttempts >= MAX_SILENT_RECOVERY_ATTEMPTS) {
|
||
Log.w(TAG, "⚠️ 已达到最大静默恢复次数($MAX_SILENT_RECOVERY_ATTEMPTS)")
|
||
return false
|
||
}
|
||
|
||
silentRecoveryAttempts.incrementAndGet()
|
||
Log.i(TAG, "🤫 尝试静默恢复 (${silentRecoveryAttempts.get()}/$MAX_SILENT_RECOVERY_ATTEMPTS)")
|
||
|
||
try {
|
||
// 延迟恢复,避免与系统操作冲突
|
||
delay(SILENT_RECOVERY_DELAY)
|
||
|
||
// ✅ 优先检查 Holder 中是否已有有效的 MediaProjection 对象
|
||
val existingProjection = MediaProjectionHolder.getMediaProjection()
|
||
if (existingProjection != null) {
|
||
Log.i(TAG, "✅ Holder中已有有效MediaProjection,直接复用")
|
||
mediaProjection = existingProjection
|
||
notifyPermissionRecovered()
|
||
resetRecoveryCounters()
|
||
return true
|
||
}
|
||
|
||
// Holder 中无有效对象,尝试从权限数据重新创建(仅一次)
|
||
val permissionData = MediaProjectionHolder.getPermissionData()
|
||
if (permissionData != null) {
|
||
val (resultCode, resultData) = permissionData
|
||
if (resultData != null) {
|
||
val newProjection = createMediaProjectionSafely(resultCode, resultData)
|
||
if (newProjection != null) {
|
||
Log.i(TAG, "✅ 静默恢复成功")
|
||
notifyPermissionRecovered()
|
||
resetRecoveryCounters()
|
||
return true
|
||
}
|
||
}
|
||
}
|
||
|
||
Log.w(TAG, "❌ 静默恢复失败,权限数据不可用")
|
||
return false
|
||
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 静默恢复异常", e)
|
||
return false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 安全地创建MediaProjection
|
||
*/
|
||
private fun createMediaProjectionSafely(resultCode: Int, resultData: Intent): MediaProjection? {
|
||
return try {
|
||
val projection = mediaProjectionManager?.getMediaProjection(resultCode, resultData)
|
||
|
||
if (projection != null) {
|
||
// 注册智能回调
|
||
projection.registerCallback(smartCallback, Handler(Looper.getMainLooper()))
|
||
|
||
mediaProjection = projection
|
||
MediaProjectionHolder.setMediaProjection(projection)
|
||
|
||
Log.i(TAG, "✅ MediaProjection创建成功")
|
||
}
|
||
|
||
projection
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 创建MediaProjection失败", e)
|
||
null
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 判断是否应该提示用户恢复
|
||
*/
|
||
private fun shouldPromptUserRecovery(): Boolean {
|
||
val currentTime = System.currentTimeMillis()
|
||
|
||
// 检查用户恢复次数
|
||
if (userRecoveryAttempts.get() >= MAX_USER_RECOVERY_ATTEMPTS) {
|
||
Log.w(TAG, "⚠️ 已达到最大用户恢复次数")
|
||
return false
|
||
}
|
||
|
||
// 检查最小恢复间隔(Android 15需要更长间隔)
|
||
val minInterval = if (Build.VERSION.SDK_INT >= 35) {
|
||
ANDROID_15_MIN_RECOVERY_INTERVAL
|
||
} else {
|
||
USER_RECOVERY_DELAY
|
||
}
|
||
|
||
if (currentTime - lastRecoveryTime < minInterval) {
|
||
Log.d(TAG, "🕐 未达到最小恢复间隔")
|
||
return false
|
||
}
|
||
|
||
// 检查是否有正在运行的AccessibilityService
|
||
if (!AccessibilityRemoteService.isServiceRunning()) {
|
||
Log.w(TAG, "⚠️ AccessibilityService未运行,不提示用户恢复")
|
||
return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
/**
|
||
* 安排用户提示恢复
|
||
*/
|
||
private fun scheduleUserPromptedRecovery() {
|
||
managerScope.launch {
|
||
try {
|
||
delay(USER_RECOVERY_DELAY)
|
||
|
||
userRecoveryAttempts.incrementAndGet()
|
||
lastRecoveryTime = System.currentTimeMillis()
|
||
|
||
Log.i(TAG, "🔔 启动用户提示恢复")
|
||
|
||
// 发送恢复请求广播(非侵入式)
|
||
val intent = Intent("android.mycustrecev.SMART_PERMISSION_RECOVERY").apply {
|
||
putExtra("recovery_type", "user_prompted")
|
||
putExtra("attempt", userRecoveryAttempts.get())
|
||
putExtra("max_attempts", MAX_USER_RECOVERY_ATTEMPTS)
|
||
}
|
||
context.sendBroadcast(intent)
|
||
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 用户提示恢复失败", e)
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理解锁后的权限检查
|
||
*/
|
||
private fun checkPermissionStatusAfterUnlock() {
|
||
managerScope.launch {
|
||
try {
|
||
delay(2000) // 等待系统稳定
|
||
|
||
if (mediaProjection == null) {
|
||
Log.i(TAG, "🔓 解锁后检测到权限丢失,启动恢复")
|
||
startSmartRecovery()
|
||
}
|
||
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 解锁后权限检查失败", e)
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 安排解锁恢复
|
||
*/
|
||
private fun scheduleUnlockRecovery() {
|
||
Log.i(TAG, "⏰ 安排解锁后恢复")
|
||
// 解锁恢复会在系统状态监听器中触发
|
||
}
|
||
|
||
/**
|
||
* 启动权限状态监控
|
||
*/
|
||
private fun startPermissionMonitoring() {
|
||
monitoringJob?.cancel()
|
||
monitoringJob = managerScope.launch {
|
||
while (isActive) {
|
||
try {
|
||
delay(PERMISSION_CHECK_INTERVAL)
|
||
|
||
// 检查MediaProjection是否仍然有效
|
||
val currentProjection = mediaProjection
|
||
if (currentProjection != null) {
|
||
// 执行轻量级状态检查
|
||
try {
|
||
// 这里可以添加一些检查逻辑来验证MediaProjection是否仍然有效
|
||
Log.v(TAG, "📊 权限状态检查:正常")
|
||
} catch (e: Exception) {
|
||
Log.w(TAG, "⚠️ 权限状态检查异常,可能已失效", e)
|
||
if (!isRecovering.get()) {
|
||
startSmartRecovery()
|
||
}
|
||
}
|
||
}
|
||
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 权限监控异常", e)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 注册系统状态监听
|
||
*/
|
||
private fun registerSystemStateReceiver() {
|
||
val filter = IntentFilter().apply {
|
||
addAction(Intent.ACTION_SCREEN_OFF)
|
||
addAction(Intent.ACTION_USER_PRESENT)
|
||
addAction("android.mycustrecev.USER_STOPPED_PROJECTION")
|
||
}
|
||
context.registerReceiver(systemStateReceiver, filter)
|
||
Log.i(TAG, "✅ 已注册系统状态监听")
|
||
}
|
||
|
||
/**
|
||
* 检查并恢复现有权限
|
||
*/
|
||
private fun checkAndRestoreExistingPermission() {
|
||
try {
|
||
val permissionData = MediaProjectionHolder.getPermissionData()
|
||
if (permissionData != null) {
|
||
val (resultCode, resultData) = permissionData
|
||
if (resultData != null) {
|
||
Log.i(TAG, "📱 发现现有权限数据,尝试恢复")
|
||
val projection = createMediaProjectionSafely(resultCode, resultData)
|
||
if (projection != null) {
|
||
Log.i(TAG, "✅ 现有权限恢复成功")
|
||
notifyPermissionRecovered()
|
||
}
|
||
}
|
||
}
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 检查现有权限失败", e)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理用户主动停止投屏
|
||
*/
|
||
private fun handleUserStoppedProjection() {
|
||
Log.i(TAG, "👤 处理用户主动停止投屏")
|
||
lastUserInteractionTime = System.currentTimeMillis()
|
||
|
||
// 停止所有恢复机制
|
||
isRecovering.set(false)
|
||
resetRecoveryCounters()
|
||
|
||
// 清理MediaProjection
|
||
cleanupCurrentProjection()
|
||
}
|
||
|
||
/**
|
||
* 清理当前MediaProjection
|
||
*/
|
||
private fun cleanupCurrentProjection() {
|
||
try {
|
||
mediaProjection?.unregisterCallback(smartCallback)
|
||
mediaProjection = null
|
||
|
||
// 🚨 重要修复:Android 15设备不要清理权限数据!
|
||
if (android.os.Build.VERSION.SDK_INT >= 35) {
|
||
Log.i(TAG, "🛡️ Android 15设备:仅清理MediaProjection引用,保留权限数据")
|
||
// 不清理权限数据,避免Android 15权限丢失
|
||
} else {
|
||
// 其他版本可以选择性清理,但建议保留
|
||
MediaProjectionHolder.clearMediaProjection()
|
||
Log.i(TAG, "MediaProjection引用和权限数据已清理")
|
||
}
|
||
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 清理MediaProjection失败", e)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 重置恢复计数器
|
||
*/
|
||
private fun resetRecoveryCounters() {
|
||
silentRecoveryAttempts.set(0)
|
||
userRecoveryAttempts.set(0)
|
||
Log.i(TAG, "🔄 恢复计数器已重置")
|
||
}
|
||
|
||
/**
|
||
* 添加权限状态监听器
|
||
*/
|
||
fun addPermissionStateListener(listener: PermissionStateListener) {
|
||
listeners.add(listener)
|
||
}
|
||
|
||
/**
|
||
* 移除权限状态监听器
|
||
*/
|
||
fun removePermissionStateListener(listener: PermissionStateListener) {
|
||
listeners.remove(listener)
|
||
}
|
||
|
||
/**
|
||
* 通知权限丢失
|
||
*/
|
||
private fun notifyPermissionLost(reason: LossReason) {
|
||
listeners.forEach { listener ->
|
||
try {
|
||
listener.onPermissionLost(reason)
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 通知权限丢失失败", e)
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 通知权限恢复
|
||
*/
|
||
private fun notifyPermissionRecovered() {
|
||
listeners.forEach { listener ->
|
||
try {
|
||
listener.onPermissionRecovered()
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 通知权限恢复失败", e)
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取当前MediaProjection
|
||
*/
|
||
fun getCurrentMediaProjection(): MediaProjection? {
|
||
return mediaProjection
|
||
}
|
||
|
||
/**
|
||
* 手动设置新的MediaProjection
|
||
*/
|
||
fun setMediaProjection(resultCode: Int, resultData: Intent): Boolean {
|
||
return try {
|
||
val projection = createMediaProjectionSafely(resultCode, resultData)
|
||
if (projection != null) {
|
||
resetRecoveryCounters()
|
||
notifyPermissionRecovered()
|
||
true
|
||
} else {
|
||
false
|
||
}
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 设置MediaProjection失败", e)
|
||
false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 手动停止MediaProjection
|
||
*/
|
||
fun stopMediaProjection() {
|
||
try {
|
||
Log.i(TAG, "🛑 用户手动停止MediaProjection")
|
||
|
||
// 发送用户停止信号
|
||
val intent = Intent("android.mycustrecev.USER_STOPPED_PROJECTION")
|
||
context.sendBroadcast(intent)
|
||
|
||
// 清理资源
|
||
mediaProjection?.stop()
|
||
cleanupCurrentProjection()
|
||
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 停止MediaProjection失败", e)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取管理器状态信息
|
||
*/
|
||
fun getStatusInfo(): Map<String, Any> {
|
||
return mapOf(
|
||
"initialized" to isInitialized.get(),
|
||
"hasPermission" to (mediaProjection != null),
|
||
"isRecovering" to isRecovering.get(),
|
||
"silentRecoveryAttempts" to silentRecoveryAttempts.get(),
|
||
"userRecoveryAttempts" to userRecoveryAttempts.get(),
|
||
"lastPermissionLostTime" to lastPermissionLostTime,
|
||
"lastRecoveryTime" to lastRecoveryTime,
|
||
"androidVersion" to Build.VERSION.SDK_INT
|
||
)
|
||
}
|
||
|
||
/**
|
||
* 清理资源
|
||
*/
|
||
fun cleanup() {
|
||
try {
|
||
Log.i(TAG, "🧹 清理智能MediaProjection管理器")
|
||
|
||
isInitialized.set(false)
|
||
monitoringJob?.cancel()
|
||
managerScope.cancel()
|
||
|
||
cleanupCurrentProjection()
|
||
|
||
context.unregisterReceiver(systemStateReceiver)
|
||
listeners.clear()
|
||
|
||
Log.i(TAG, "✅ 清理完成")
|
||
|
||
} catch (e: Exception) {
|
||
Log.e(TAG, "❌ 清理失败", e)
|
||
}
|
||
}
|
||
} |