Revert "Update .gitignore to exclude app/build/ except res"

This reverts commit fccae746f0.
This commit is contained in:
wdvipa
2026-02-14 14:44:10 +08:00
parent fccae746f0
commit fe18e7eab0
3132 changed files with 77489 additions and 327 deletions

View File

@@ -88,13 +88,6 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
// 🔑 新增AccessibilityService截图模式开关
private var useAccessibilityScreenshot = false
// 🔑 无障碍截图间隔自适应系统takeScreenshot有最小间隔限制通常≥1秒
// 当收到ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT时动态增加间隔
@Volatile private var accessibilityMinInterval = 1100L // 初始1100msAndroid系统takeScreenshot最小间隔约1秒
private val ACCESSIBILITY_INTERVAL_STEP = 200L // 每次错误码3增加200ms
private val ACCESSIBILITY_INTERVAL_MAX = 3000L // 最大间隔3秒
private val ACCESSIBILITY_INTERVAL_MIN = 800L // 最小间隔800ms成功时逐步回落的下限
// 📊 自适应画质运行时可调参数覆盖companion object中的常量
@Volatile private var dynamicFps: Int = CAPTURE_FPS
@Volatile private var dynamicQuality: Int = CAPTURE_QUALITY
@@ -414,18 +407,13 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
}
// 发送缓存的有效帧保持画面连续(如果有的话)
val cachedBitmap = lastValidBitmap
if (cachedBitmap != null && !cachedBitmap.isRecycled) {
if (lastValidBitmap != null && !lastValidBitmap!!.isRecycled) {
val cacheAge = System.currentTimeMillis() - lastCaptureTime
if (cacheAge < 10000) {
try {
val cachedJpeg = compressBitmap(cachedBitmap)
if (cachedJpeg.size >= MIN_VALID_FRAME_SIZE) {
sendFrameToServer(cachedJpeg)
Log.d(TAG, "📸 使用缓存帧替代黑屏帧 (${cacheAge}ms前)")
}
} catch (e: Exception) {
Log.w(TAG, "⚠️ 压缩缓存帧失败(可能已被回收)", e)
val cachedJpeg = compressBitmap(lastValidBitmap!!)
if (cachedJpeg.size >= MIN_VALID_FRAME_SIZE) {
sendFrameToServer(cachedJpeg)
Log.d(TAG, "📸 使用缓存帧替代黑屏帧 (${cacheAge}ms前)")
}
}
}
@@ -440,18 +428,13 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
consecutiveFailures++
// ✅ 无新帧时发送缓存帧,保持画面连续
val cachedBitmap2 = lastValidBitmap
if (cachedBitmap2 != null && !cachedBitmap2.isRecycled) {
if (lastValidBitmap != null && !lastValidBitmap!!.isRecycled) {
val cacheAge = System.currentTimeMillis() - lastCaptureTime
if (cacheAge < 10000) {
try {
val cachedJpeg = compressBitmap(cachedBitmap2)
if (cachedJpeg.size >= MIN_VALID_FRAME_SIZE) {
sendFrameToServer(cachedJpeg)
Log.d(TAG, "📸 无新帧,发送缓存帧 (${cacheAge}ms前)")
}
} catch (e: Exception) {
Log.w(TAG, "⚠️ 压缩缓存帧失败(可能已被回收)", e)
val cachedJpeg = compressBitmap(lastValidBitmap!!)
if (cachedJpeg.size >= MIN_VALID_FRAME_SIZE) {
sendFrameToServer(cachedJpeg)
Log.d(TAG, "📸 无新帧,发送缓存帧 (${cacheAge}ms前)")
}
}
}
@@ -579,20 +562,17 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
Log.d(TAG, "📱 无障碍截图失败(${consecutiveFailures}次),跳过本帧等待下次")
}
// ✅ 截图成功时按帧率延迟,失败时按自适应间隔延迟
// 无障碍截图模式下,成功延迟也不能低于系统最小间隔
// ✅ 截图成功时按帧率延迟,失败时短延迟后立即重试
if (consecutiveFailures == 0) {
val fpsDelay = 1000 / dynamicFps.toLong()
delay(maxOf(fpsDelay, accessibilityMinInterval))
delay(1000 / dynamicFps.toLong())
} else {
// 失败时等待自适应间隔,避免疯狂触发系统截图间隔限制
delay(accessibilityMinInterval)
delay(50) // 失败时50ms后重试系统截图间隔限制自己控制节奏
}
} catch (e: Exception) {
Log.e(TAG, "屏幕捕获失败", e)
consecutiveFailures++
delay(accessibilityMinInterval) // 异常时也按自适应间隔等待
delay(100) // ✅ 出错时进一步缩短间隔保持流畅度从200ms改为100ms
}
}
}
@@ -700,12 +680,12 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
*/
private fun captureWithAccessibilityService(): Bitmap? {
return try {
// ✅ 修复:无障碍截图始终使用自适应间隔系统takeScreenshot有最小间隔限制通常≥1秒
// ✅ 修复:检查截图间隔,防止截图间隔太短
val currentTime = System.currentTimeMillis()
val timeSinceLastScreenshot = currentTime - lastScreenshotTime
if (timeSinceLastScreenshot < accessibilityMinInterval) {
Log.d(TAG, "📱 截图间隔太短,跳过本次截图: ${timeSinceLastScreenshot}ms < ${accessibilityMinInterval}ms")
if (timeSinceLastScreenshot < MIN_CAPTURE_INTERVAL) {
Log.d(TAG, "📱 截图间隔太短,跳过本次截图: ${timeSinceLastScreenshot}ms < ${MIN_CAPTURE_INTERVAL}ms")
return null
}
@@ -724,10 +704,6 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
// 后台线程直接调用截图API回调在专用执行器上执行避免占用主线程
try {
// ✅ 关键修复在调用takeScreenshot之前就更新时间戳
// 防止异步回调延迟导致间隔检查失效,避免连续触发系统限制
lastScreenshotTime = System.currentTimeMillis()
service.takeScreenshot(
android.view.Display.DEFAULT_DISPLAY,
screenshotExecutor,
@@ -739,11 +715,6 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
// ✅ 修复:更新截图时间戳,防止截图间隔太短
lastScreenshotTime = System.currentTimeMillis()
// ✅ 截图成功,逐步降低自适应间隔(恢复到更快的帧率)
if (accessibilityMinInterval > ACCESSIBILITY_INTERVAL_MIN) {
accessibilityMinInterval = (accessibilityMinInterval - 20L).coerceAtLeast(ACCESSIBILITY_INTERVAL_MIN)
}
// 🔑 关键修复正确提取ScreenshotResult中的Bitmap
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val hardwareBuffer = screenshotResult.hardwareBuffer
@@ -791,39 +762,19 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
}
override fun onFailure(failureErrorCode: Int) {
Log.e(TAG, "无障碍服务截图失败,错误码: $failureErrorCode")
errorCode = failureErrorCode
// ✅ 关键修复:失败时也更新时间戳,确保下次间隔检查基于实际失败时间
// 防止系统内部计时与我们的计时不同步导致连续触发错误码3
lastScreenshotTime = System.currentTimeMillis()
// 🚨 Android 11+特殊处理:详细分析错误码
if (Build.VERSION.SDK_INT >= 30) {
val errorMessage = when (failureErrorCode) {
android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR -> "内部错误"
android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT -> {
// ✅ 错误码3截图间隔太短动态增加间隔
val newInterval = (accessibilityMinInterval + ACCESSIBILITY_INTERVAL_STEP).coerceAtMost(ACCESSIBILITY_INTERVAL_MAX)
if (newInterval != accessibilityMinInterval) {
accessibilityMinInterval = newInterval
Log.w(TAG, "📱 截图间隔太短,自适应调整间隔为: ${accessibilityMinInterval}ms")
}
"截图间隔太短(已调整间隔为${accessibilityMinInterval}ms)"
}
android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT -> "截图间隔太短"
android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY -> "无效显示"
android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS -> "无障碍权限不足"
else -> "未知错误($failureErrorCode)"
}
// 错误码3是预期的限流行为用WARN级别其他错误用ERROR级别
if (failureErrorCode == android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT) {
Log.w(TAG, "📱 Android 11+设备:截图失败详情 - $errorMessage")
} else {
Log.e(TAG, "无障碍服务截图失败,错误码: $failureErrorCode")
Log.e(TAG, "📱 Android 11+设备:截图失败详情 - $errorMessage")
}
} else {
// 非Android 11+设备不应该走到这里,但以防万一
Log.w(TAG, "无障碍服务截图失败,错误码: $failureErrorCode")
Log.e(TAG, "📱 Android 11+设备:截图失败详情 - $errorMessage")
}
latch.countDown()
@@ -845,8 +796,6 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
if (!success) {
Log.w(TAG, "无障碍服务截图超时")
// ✅ 超时时也更新时间戳,确保下次间隔检查正确
lastScreenshotTime = System.currentTimeMillis()
// 🚨 Android 11+特殊处理返回null让主循环处理失败逻辑
if (Build.VERSION.SDK_INT >= 30) {
Log.w(TAG, "📱 Android 11+设备无障碍截图超时返回null让主循环处理")
@@ -2190,27 +2139,13 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
*/
private fun cleanupVirtualDisplayOnly() {
try {
// ✅ 修复释放顺序:先释放 VirtualDisplay(断开对 Surface 的引用),
// 等待系统 WindowManager 完成挂起的布局操作,再关闭 ImageReader释放 Surface
// 原来的顺序会导致 system_server 的 WindowManager 在异步镜像操作中
// 访问已释放的 Surface抛出 "Surface has already been released" 异常。
// 1. 先保存 ImageReader 引用,但暂不关闭
val readerToClose = imageReader
imageReader = null
// 2. 释放 VirtualDisplay断开它对 ImageReader.surface 的引用
// 清理VirtualDisplay
virtualDisplay?.release()
virtualDisplay = null
// 3. 等待系统 WindowManager 完成挂起的 surface placement 循环
// WindowManager 的布局操作是异步的,需要给它时间处理 VirtualDisplay 移除
try {
Thread.sleep(100)
} catch (_: InterruptedException) {}
// 4. 现在安全关闭 ImageReader释放底层 Surface
readerToClose?.close()
// 清理ImageReader
imageReader?.close()
imageReader = null
// 清理缓存的图像
lastValidBitmap?.recycle()
@@ -2391,18 +2326,13 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
Log.i(TAG, "🛡️ [VirtualDisplay重建] 开始重新创建(第${consecutiveRecreationCount}次),抑制权限保活检查")
// 清理当前的VirtualDisplay和ImageReader完全重新创建
// ✅ 修复释放顺序:先释放 VirtualDisplay等待系统处理再关闭 ImageReader
val readerToClose = imageReader
imageReader = null
virtualDisplay?.release()
virtualDisplay = null
// 等待系统 WindowManager 完成挂起的布局操作后再释放 Surface
Thread.sleep(200)
readerToClose?.close()
imageReader?.close()
imageReader = null
// 等待系统完全清理
Thread.sleep(300)
Thread.sleep(500)
Log.i(TAG, "🔧 Android 15完全重新创建ImageReader和VirtualDisplay")
@@ -2661,15 +2591,23 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
/**
* 为Android 15强制刷新Surface
* ✅ 修复:不能直接 release Surface必须先断开 VirtualDisplay 的引用
*/
private suspend fun refreshSurfaceForAndroid15() {
try {
Log.d(TAG, "🔄 Android 15强制刷新Surface — 通过重建 VirtualDisplay + ImageReader")
// 正确做法:通过 cleanupVirtualDisplayOnly 安全释放,再重建
cleanupVirtualDisplayOnly()
delay(300)
setupMediaProjectionResources()
imageReader?.let { reader ->
val surface = reader.surface
if (surface.isValid) {
Log.d(TAG, "🔄 Android 15强制刷新Surface")
// 通过重设Surface的方式强制刷新
surface.release()
delay(500)
// 重新创建ImageReader如果需要
if (isVirtualDisplayCreated()) {
createImageReader()
}
}
}
} catch (e: Exception) {
Log.e(TAG, "❌ Android 15 Surface刷新失败", e)
}
@@ -2677,39 +2615,27 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
/**
* 为Android 15重新初始化ImageReader
* ✅ 修复:不能在 VirtualDisplay 还持有 Surface 引用时释放 ImageReader
* 改为先创建新 ImageReader再切换 VirtualDisplay 的 Surface最后释放旧 ImageReader
*/
private suspend fun reinitializeImageReaderForAndroid15() {
try {
Log.d(TAG, "🔄 Android 15重新初始化ImageReader")
// 保存旧 ImageReader 引用
val oldReader = imageReader
// 释放现有ImageReader
releaseImageReader()
delay(1000)
// 先创建新 ImageReader
// 重新创建ImageReader
createImageReader()
delay(300)
delay(1000)
// 如果VirtualDisplay存在先切换到新 Surface,再释放旧的
// 如果VirtualDisplay存在重新关联Surface
virtualDisplay?.let { display ->
imageReader?.let { newReader ->
display.surface = newReader.surface
Log.d(TAG, "✅ Android 15已重新关联ImageReader和VirtualDisplay")
imageReader?.let { reader ->
display.surface = reader.surface
Log.d(TAG, "✅ Android 15已重新关联ImageReader和VirtualDisplay")
}
}
// 等待系统完成 Surface 切换
delay(200)
// 现在安全释放旧 ImageReader
try {
oldReader?.close()
Log.d(TAG, "✅ 旧 ImageReader 已安全释放")
} catch (e: Exception) {
Log.w(TAG, "⚠️ 释放旧 ImageReader 异常", e)
}
} catch (e: Exception) {
Log.e(TAG, "❌ Android 15 ImageReader重新初始化失败", e)
}