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 display_metrics = DisplayMetrics() val window_manager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager window_manager.defaultDisplay.getMetrics(display_metrics) screenWidth = display_metrics.widthPixels screenHeight = display_metrics.heightPixels screenDensity = display_metrics.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 captured_image = reader.acquireLatestImage() if (captured_image != null) { imageCallback?.invoke(captured_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? { return if (screenWidth > 0 && screenHeight > 0) { Pair(screenWidth, screenHeight) } else { null } } override fun release() { Log.d(TAG, "Releasing ScreenCaptureManager") stopCapture() imageCallback = null mediaProjectionManager = null } }