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.

217 lines
6.2 KiB

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<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
}
}