@@ -52,6 +52,7 @@ class ScreenCaptureService : Service() {
5252 private var virtualDisplay: VirtualDisplay ? = null
5353 private var imageReader: ImageReader ? = null
5454 private var isReady = false // Flag to indicate if MediaProjection is set up and active
55+ private var shouldTakeScreenshot = false // Flag to control when to actually capture
5556
5657 // Callback for MediaProjection
5758 private val mediaProjectionCallback = object : MediaProjection .Callback () {
@@ -187,120 +188,133 @@ class ScreenCaptureService : Service() {
187188 }
188189 }
189190
190- private fun takeScreenshot () {
191- if (! isReady || mediaProjection == null ) {
192- Log .e(TAG , " Cannot take screenshot - service not ready or mediaProjection is null. isReady=$isReady , mediaProjectionIsNull=${mediaProjection == null } " )
193- return
194- }
195- Log .d(TAG , " takeScreenshot: Preparing to capture." )
196-
197- try {
198- // Check if we need to initialize VirtualDisplay and ImageReader
199- if (virtualDisplay == null || imageReader == null ) {
200- val windowManager = getSystemService(Context .WINDOW_SERVICE ) as WindowManager
201- val displayMetrics = DisplayMetrics ()
202-
203- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
204- val defaultDisplay = windowManager.defaultDisplay
205- if (defaultDisplay != null ) {
206- defaultDisplay.getRealMetrics(displayMetrics)
191+ private fun takeScreenshot () {
192+ if (! isReady || mediaProjection == null ) {
193+ Log .e(TAG , " Cannot take screenshot - service not ready or mediaProjection is null. isReady=$isReady , mediaProjectionIsNull=${mediaProjection == null } " )
194+ return
195+ }
196+ Log .d(TAG , " takeScreenshot: Preparing to capture." )
197+
198+ // Set flag to capture on next frame
199+ shouldTakeScreenshot = true
200+
201+ try {
202+ // Check if we need to initialize VirtualDisplay and ImageReader
203+ if (virtualDisplay == null || imageReader == null ) {
204+ val windowManager = getSystemService(Context .WINDOW_SERVICE ) as WindowManager
205+ val displayMetrics = DisplayMetrics ()
206+
207+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
208+ val defaultDisplay = windowManager.defaultDisplay
209+ if (defaultDisplay != null ) {
210+ defaultDisplay.getRealMetrics(displayMetrics)
211+ } else {
212+ val bounds = windowManager.currentWindowMetrics.bounds
213+ displayMetrics.widthPixels = bounds.width()
214+ displayMetrics.heightPixels = bounds.height()
215+ displayMetrics.densityDpi = resources.displayMetrics.densityDpi
216+ }
207217 } else {
208- val bounds = windowManager.currentWindowMetrics.bounds
209- displayMetrics.widthPixels = bounds.width()
210- displayMetrics.heightPixels = bounds.height()
211- displayMetrics.densityDpi = resources.displayMetrics.densityDpi
218+ @Suppress(" DEPRECATION" )
219+ windowManager.defaultDisplay.getMetrics(displayMetrics)
212220 }
213- } else {
214- @Suppress(" DEPRECATION" )
215- windowManager.defaultDisplay.getMetrics(displayMetrics)
216- }
217221
218- val width = displayMetrics.widthPixels
219- val height = displayMetrics.heightPixels
220- val density = displayMetrics.densityDpi
222+ val width = displayMetrics.widthPixels
223+ val height = displayMetrics.heightPixels
224+ val density = displayMetrics.densityDpi
221225
222- if (width <= 0 || height <= 0 ) {
223- Log .e(TAG , " Invalid display dimensions: ${width} x${height} . Cannot create ImageReader." )
224- return
225- }
226- Log .d(TAG , " Display dimensions: ${width} x${height} , density: $density " )
226+ if (width <= 0 || height <= 0 ) {
227+ Log .e(TAG , " Invalid display dimensions: ${width} x${height} . Cannot create ImageReader." )
228+ shouldTakeScreenshot = false
229+ return
230+ }
231+ Log .d(TAG , " Display dimensions: ${width} x${height} , density: $density " )
227232
228- imageReader?.close() // Close previous reader if any
229- virtualDisplay?.release() // Release previous display if any
233+ imageReader?.close() // Close previous reader if any
234+ virtualDisplay?.release() // Release previous display if any
230235
231- imageReader = ImageReader .newInstance(width, height, PixelFormat .RGBA_8888 , 1 )
232- val localImageReader = imageReader ? : run {
233- Log .e(TAG , " ImageReader is null after creation attempt." )
234- return
235- }
236+ imageReader = ImageReader .newInstance(width, height, PixelFormat .RGBA_8888 , 1 )
237+ val localImageReader = imageReader ? : run {
238+ Log .e(TAG , " ImageReader is null after creation attempt." )
239+ shouldTakeScreenshot = false
240+ return
241+ }
236242
237- localImageReader.setOnImageAvailableListener({ reader ->
238- var image: android.media.Image ? = null
239- try {
240- image = reader.acquireLatestImage()
241- if (image != null ) {
242- val planes = image.planes
243- val buffer = planes[0 ].buffer
244- val pixelStride = planes[0 ].pixelStride
245- val rowStride = planes[0 ].rowStride
246- val rowPadding = rowStride - pixelStride * width
247-
248- val bitmap = Bitmap .createBitmap(
249- width + rowPadding / pixelStride,
250- height,
251- Bitmap .Config .ARGB_8888
252- )
253- bitmap.copyPixelsFromBuffer(buffer)
254- Log .d(TAG , " Bitmap created, proceeding to save." )
255- saveScreenshot(bitmap)
256- } else {
257- Log .w(TAG , " acquireLatestImage returned null." )
243+ localImageReader.setOnImageAvailableListener({ reader ->
244+ // Only process image if screenshot was requested
245+ if (! shouldTakeScreenshot) {
246+ reader.acquireLatestImage()?.close()
247+ return @setOnImageAvailableListener
248+ }
249+
250+ var image: android.media.Image ? = null
251+ try {
252+ image = reader.acquireLatestImage()
253+ if (image != null ) {
254+ val planes = image.planes
255+ val buffer = planes[0 ].buffer
256+ val pixelStride = planes[0 ].pixelStride
257+ val rowStride = planes[0 ].rowStride
258+ val rowPadding = rowStride - pixelStride * width
259+
260+ val bitmap = Bitmap .createBitmap(
261+ width + rowPadding / pixelStride,
262+ height,
263+ Bitmap .Config .ARGB_8888
264+ )
265+ bitmap.copyPixelsFromBuffer(buffer)
266+ Log .d(TAG , " Bitmap created, proceeding to save." )
267+ saveScreenshot(bitmap)
268+
269+ // Reset flag after capturing
270+ shouldTakeScreenshot = false
271+ } else {
272+ Log .w(TAG , " acquireLatestImage returned null." )
273+ }
274+ } catch (e: Exception ) {
275+ Log .e(TAG , " Error processing image" , e)
276+ shouldTakeScreenshot = false
277+ } finally {
278+ image?.close()
279+ Log .d(TAG , " Screenshot captured, keeping resources for reuse." )
258280 }
259- } catch (e: Exception ) {
260- Log .e(TAG , " Error processing image" , e)
261- } finally {
262- image?.close()
263- // Do NOT release VirtualDisplay or ImageReader here
264- // They will be reused for the next screenshot
265- Log .d(TAG , " Screenshot captured, keeping resources for reuse." )
281+ }, Handler (Looper .getMainLooper()))
282+
283+ virtualDisplay = mediaProjection?.createVirtualDisplay(
284+ " ScreenCapture" ,
285+ width, height, density,
286+ DisplayManager .VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR ,
287+ localImageReader.surface,
288+ object : VirtualDisplay .Callback () {
289+ override fun onPaused () { Log .d(TAG , " VirtualDisplay paused" ) }
290+ override fun onResumed () { Log .d(TAG , " VirtualDisplay resumed" ) }
291+ override fun onStopped () { Log .d(TAG , " VirtualDisplay stopped" ) }
292+ },
293+ Handler (Looper .getMainLooper())
294+ )
295+
296+ if (virtualDisplay == null ) {
297+ Log .e(TAG , " Failed to create VirtualDisplay." )
298+ localImageReader.close()
299+ this .imageReader = null
300+ shouldTakeScreenshot = false
301+ return
266302 }
267- }, Handler (Looper .getMainLooper()))
268-
269- virtualDisplay = mediaProjection?.createVirtualDisplay(
270- " ScreenCapture" ,
271- width, height, density,
272- DisplayManager .VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR ,
273- localImageReader.surface,
274- object : VirtualDisplay .Callback () {
275- override fun onPaused () { Log .d(TAG , " VirtualDisplay paused" ) }
276- override fun onResumed () { Log .d(TAG , " VirtualDisplay resumed" ) }
277- override fun onStopped () { Log .d(TAG , " VirtualDisplay stopped" ) }
278- },
279- Handler (Looper .getMainLooper())
280- )
281-
282- if (virtualDisplay == null ) {
283- Log .e(TAG , " Failed to create VirtualDisplay." )
284- localImageReader.close() // Clean up the reader we just created
285- this .imageReader = null
286- return
303+ Log .d(TAG , " VirtualDisplay and ImageReader initialized for reuse." )
304+ } else {
305+ // Resources already exist, flag is set, listener will capture on next frame
306+ Log .d(TAG , " Using existing VirtualDisplay and ImageReader, screenshot will be captured on next frame." )
287307 }
288- Log .d(TAG , " VirtualDisplay and ImageReader initialized for reuse." )
289- } else {
290- // Resources already exist, just trigger a new capture
291- Log .d(TAG , " Using existing VirtualDisplay and ImageReader." )
292- // Force the ImageReader to capture a new frame
293- // The listener is already set up and will handle the new image
294- }
295308
296- } catch (e: Exception ) {
297- Log .e(TAG , " Error in takeScreenshot setup" , e)
298- virtualDisplay?.release()
299- virtualDisplay = null
300- imageReader?.close()
301- imageReader = null
309+ } catch (e: Exception ) {
310+ Log .e(TAG , " Error in takeScreenshot setup" , e)
311+ shouldTakeScreenshot = false
312+ virtualDisplay?.release()
313+ virtualDisplay = null
314+ imageReader?.close()
315+ imageReader = null
316+ }
302317 }
303- }
304318
305319 private fun saveScreenshot (bitmap : Bitmap ) {
306320 try {
0 commit comments