You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
5.9 KiB
186 lines
5.9 KiB
|
5 months ago
|
package com.quillstudios.pokegoalshelper.capture
|
||
|
|
|
||
|
|
import android.app.Activity
|
||
|
|
import android.content.Context
|
||
|
|
import android.content.Intent
|
||
|
|
import android.graphics.PixelFormat
|
||
|
|
import android.hardware.display.DisplayManager
|
||
|
|
import android.hardware.display.VirtualDisplay
|
||
|
|
import android.media.Image
|
||
|
|
import android.media.ImageReader
|
||
|
|
import android.media.projection.MediaProjection
|
||
|
|
import android.media.projection.MediaProjectionManager
|
||
|
|
import android.os.Handler
|
||
|
|
import android.util.DisplayMetrics
|
||
|
|
import android.util.Log
|
||
|
|
import android.view.WindowManager
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Implementation of ScreenCaptureManager that handles MediaProjection-based screen capture.
|
||
|
|
* Extracted from ScreenCaptureService for better separation of concerns.
|
||
|
|
*/
|
||
|
|
class ScreenCaptureManagerImpl(
|
||
|
|
private val context: Context,
|
||
|
|
private val handler: Handler
|
||
|
|
) : ScreenCaptureManager {
|
||
|
|
|
||
|
|
companion object {
|
||
|
|
private const val TAG = "ScreenCaptureManager"
|
||
|
|
private const val BUFFER_COUNT = 3
|
||
|
|
}
|
||
|
|
|
||
|
|
private var mediaProjectionManager: MediaProjectionManager? = null
|
||
|
|
private var mediaProjection: MediaProjection? = null
|
||
|
|
private var virtualDisplay: VirtualDisplay? = null
|
||
|
|
private var imageReader: ImageReader? = null
|
||
|
|
|
||
|
|
private var screenWidth = 0
|
||
|
|
private var screenHeight = 0
|
||
|
|
private var screenDensity = 0
|
||
|
|
|
||
|
|
private var imageCallback: ((Image) -> Unit)? = null
|
||
|
|
private var isActive = false
|
||
|
|
|
||
|
|
init {
|
||
|
|
mediaProjectionManager = context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
||
|
|
initializeScreenMetrics()
|
||
|
|
}
|
||
|
|
|
||
|
|
private fun initializeScreenMetrics() {
|
||
|
|
val displayMetrics = DisplayMetrics()
|
||
|
|
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||
|
|
windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||
|
|
|
||
|
|
screenWidth = displayMetrics.widthPixels
|
||
|
|
screenHeight = displayMetrics.heightPixels
|
||
|
|
screenDensity = displayMetrics.densityDpi
|
||
|
|
|
||
|
|
Log.d(TAG, "Screen metrics initialized: ${screenWidth}x${screenHeight}, density: $screenDensity")
|
||
|
|
}
|
||
|
|
|
||
|
|
private val mediaProjectionCallback = object : MediaProjection.Callback() {
|
||
|
|
override fun onStop() {
|
||
|
|
Log.d(TAG, "MediaProjection stopped")
|
||
|
|
stopCapture()
|
||
|
|
}
|
||
|
|
|
||
|
|
override fun onCapturedContentResize(width: Int, height: Int) {
|
||
|
|
Log.d(TAG, "Screen size changed: ${width}x${height}")
|
||
|
|
}
|
||
|
|
|
||
|
|
override fun onCapturedContentVisibilityChanged(isVisible: Boolean) {
|
||
|
|
Log.d(TAG, "Content visibility changed: $isVisible")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private val onImageAvailableListener = ImageReader.OnImageAvailableListener { reader ->
|
||
|
|
try {
|
||
|
|
val image = reader.acquireLatestImage()
|
||
|
|
if (image != null) {
|
||
|
|
imageCallback?.invoke(image)
|
||
|
|
}
|
||
|
|
} catch (e: Exception) {
|
||
|
|
Log.e(TAG, "Error in onImageAvailableListener", e)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
override fun startCapture(resultData: Intent): Boolean {
|
||
|
|
if (isActive) {
|
||
|
|
Log.w(TAG, "Capture already active")
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
Log.d(TAG, "Starting screen capture")
|
||
|
|
|
||
|
|
mediaProjection = mediaProjectionManager?.getMediaProjection(Activity.RESULT_OK, resultData)
|
||
|
|
if (mediaProjection == null) {
|
||
|
|
Log.e(TAG, "Failed to get MediaProjection")
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
Log.d(TAG, "Registering MediaProjection callback")
|
||
|
|
mediaProjection?.registerCallback(mediaProjectionCallback, handler)
|
||
|
|
|
||
|
|
Log.d(TAG, "Creating ImageReader: ${screenWidth}x${screenHeight}")
|
||
|
|
imageReader = ImageReader.newInstance(screenWidth, screenHeight, PixelFormat.RGBA_8888, BUFFER_COUNT)
|
||
|
|
imageReader?.setOnImageAvailableListener(onImageAvailableListener, handler)
|
||
|
|
|
||
|
|
Log.d(TAG, "Creating VirtualDisplay")
|
||
|
|
virtualDisplay = mediaProjection?.createVirtualDisplay(
|
||
|
|
"ScreenCapture",
|
||
|
|
screenWidth,
|
||
|
|
screenHeight,
|
||
|
|
screenDensity,
|
||
|
|
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
|
||
|
|
imageReader?.surface,
|
||
|
|
null,
|
||
|
|
null
|
||
|
|
)
|
||
|
|
|
||
|
|
if (virtualDisplay == null) {
|
||
|
|
Log.e(TAG, "Failed to create VirtualDisplay")
|
||
|
|
cleanupResources()
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
isActive = true
|
||
|
|
Log.d(TAG, "Screen capture started successfully")
|
||
|
|
return true
|
||
|
|
|
||
|
|
} catch (e: Exception) {
|
||
|
|
Log.e(TAG, "Error starting screen capture", e)
|
||
|
|
cleanupResources()
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
override fun stopCapture() {
|
||
|
|
if (!isActive) {
|
||
|
|
Log.d(TAG, "Capture not active, nothing to stop")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
Log.d(TAG, "Stopping screen capture")
|
||
|
|
cleanupResources()
|
||
|
|
isActive = false
|
||
|
|
Log.d(TAG, "Screen capture stopped")
|
||
|
|
}
|
||
|
|
|
||
|
|
private fun cleanupResources() {
|
||
|
|
try {
|
||
|
|
virtualDisplay?.release()
|
||
|
|
imageReader?.close()
|
||
|
|
mediaProjection?.unregisterCallback(mediaProjectionCallback)
|
||
|
|
mediaProjection?.stop()
|
||
|
|
} catch (e: Exception) {
|
||
|
|
Log.e(TAG, "Error during cleanup", e)
|
||
|
|
} finally {
|
||
|
|
virtualDisplay = null
|
||
|
|
imageReader = null
|
||
|
|
mediaProjection = null
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
override fun setImageCallback(callback: (Image) -> Unit) {
|
||
|
|
this.imageCallback = callback
|
||
|
|
Log.d(TAG, "Image callback set")
|
||
|
|
}
|
||
|
|
|
||
|
|
override fun isCapturing(): Boolean = isActive
|
||
|
|
|
||
|
|
override fun getScreenDimensions(): Pair<Int, Int>? {
|
||
|
|
return if (screenWidth > 0 && screenHeight > 0) {
|
||
|
|
Pair(screenWidth, screenHeight)
|
||
|
|
} else {
|
||
|
|
null
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
override fun release() {
|
||
|
|
Log.d(TAG, "Releasing ScreenCaptureManager")
|
||
|
|
stopCapture()
|
||
|
|
imageCallback = null
|
||
|
|
mediaProjectionManager = null
|
||
|
|
}
|
||
|
|
}
|