@@ -50,26 +50,33 @@ class ScreenCaptureService : Service() {
5050 }
5151
5252 override fun onStartCommand (intent : Intent ? , flags : Int , startId : Int ): Int {
53+ // IMPORTANT: Call startForeground immediately
54+ startForegroundImmediately()
55+
5356 if (intent?.action == ACTION_START_CAPTURE ) {
5457 val resultCode = intent.getIntExtra(EXTRA_RESULT_CODE , - 1 )
5558 val resultData = intent.getParcelableExtra<Intent >(EXTRA_RESULT_DATA )
5659
5760 if (resultCode != - 1 && resultData != null ) {
58- startForeground()
5961 startCapture(resultCode, resultData)
6062 } else {
63+ Log .e(TAG , " Invalid result code or data" )
6164 stopSelf()
6265 }
66+ } else {
67+ Log .e(TAG , " Invalid action" )
68+ stopSelf()
6369 }
6470 return START_NOT_STICKY
6571 }
6672
67- private fun startForeground () {
73+ private fun startForegroundImmediately () {
6874 val notification = NotificationCompat .Builder (this , CHANNEL_ID )
6975 .setContentTitle(" Taking Screenshot" )
7076 .setContentText(" Processing..." )
7177 .setSmallIcon(android.R .drawable.ic_menu_camera)
7278 .setPriority(NotificationCompat .PRIORITY_LOW )
79+ .setOngoing(true )
7380 .build()
7481
7582 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .Q ) {
@@ -87,61 +94,84 @@ class ScreenCaptureService : Service() {
8794 NotificationManager .IMPORTANCE_LOW
8895 ).apply {
8996 description = " Used for taking screenshots"
97+ setShowBadge(false )
9098 }
9199 val notificationManager = getSystemService(NotificationManager ::class .java)
92100 notificationManager.createNotificationChannel(channel)
93101 }
94102 }
95103
96104 private fun startCapture (resultCode : Int , data : Intent ) {
97- val mediaProjectionManager = getSystemService(Context .MEDIA_PROJECTION_SERVICE ) as MediaProjectionManager
98- mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data)
105+ try {
106+ val mediaProjectionManager = getSystemService(Context .MEDIA_PROJECTION_SERVICE ) as MediaProjectionManager
107+ mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data)
99108
100- Handler (Looper .getMainLooper()).postDelayed({
101- takeScreenshot()
102- }, 100 )
109+ Handler (Looper .getMainLooper()).postDelayed({
110+ takeScreenshot()
111+ }, 100 )
112+ } catch (e: Exception ) {
113+ Log .e(TAG , " Error starting capture" , e)
114+ cleanup()
115+ }
103116 }
104117
105118 private fun takeScreenshot () {
106- val windowManager = getSystemService(Context .WINDOW_SERVICE ) as WindowManager
107- val displayMetrics = DisplayMetrics ()
108- windowManager.defaultDisplay.getMetrics(displayMetrics)
109-
110- val width = displayMetrics.widthPixels
111- val height = displayMetrics.heightPixels
112- val density = displayMetrics.densityDpi
113-
114- imageReader = ImageReader .newInstance(width, height, PixelFormat .RGBA_8888 , 1 )
115-
116- virtualDisplay = mediaProjection?.createVirtualDisplay(
117- " ScreenCapture" ,
118- width, height, density,
119- DisplayManager .VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR ,
120- imageReader!! .surface, null , null
121- )
122-
123- imageReader!! .setOnImageAvailableListener({ reader ->
124- val image = reader.acquireLatestImage()
125- if (image != null ) {
126- val planes = image.planes
127- val buffer = planes[0 ].buffer
128- val pixelStride = planes[0 ].pixelStride
129- val rowStride = planes[0 ].rowStride
130- val rowPadding = rowStride - pixelStride * width
131-
132- val bitmap = Bitmap .createBitmap(
133- width + rowPadding / pixelStride,
134- height,
135- Bitmap .Config .ARGB_8888
136- )
137- bitmap.copyPixelsFromBuffer(buffer)
138-
139- saveScreenshot(bitmap)
140-
141- image.close()
142- cleanup()
119+ try {
120+ val windowManager = getSystemService(Context .WINDOW_SERVICE ) as WindowManager
121+ val displayMetrics = DisplayMetrics ()
122+
123+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
124+ val display = display
125+ display?.getRealMetrics(displayMetrics)
126+ } else {
127+ @Suppress(" DEPRECATION" )
128+ windowManager.defaultDisplay.getMetrics(displayMetrics)
143129 }
144- }, Handler (Looper .getMainLooper()))
130+
131+ val width = displayMetrics.widthPixels
132+ val height = displayMetrics.heightPixels
133+ val density = displayMetrics.densityDpi
134+
135+ imageReader = ImageReader .newInstance(width, height, PixelFormat .RGBA_8888 , 1 )
136+
137+ virtualDisplay = mediaProjection?.createVirtualDisplay(
138+ " ScreenCapture" ,
139+ width, height, density,
140+ DisplayManager .VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR ,
141+ imageReader!! .surface, null , null
142+ )
143+
144+ imageReader!! .setOnImageAvailableListener({ reader ->
145+ var image: android.media.Image ? = null
146+ try {
147+ image = reader.acquireLatestImage()
148+ if (image != null ) {
149+ val planes = image.planes
150+ val buffer = planes[0 ].buffer
151+ val pixelStride = planes[0 ].pixelStride
152+ val rowStride = planes[0 ].rowStride
153+ val rowPadding = rowStride - pixelStride * width
154+
155+ val bitmap = Bitmap .createBitmap(
156+ width + rowPadding / pixelStride,
157+ height,
158+ Bitmap .Config .ARGB_8888
159+ )
160+ bitmap.copyPixelsFromBuffer(buffer)
161+
162+ saveScreenshot(bitmap)
163+ }
164+ } catch (e: Exception ) {
165+ Log .e(TAG , " Error processing image" , e)
166+ } finally {
167+ image?.close()
168+ cleanup()
169+ }
170+ }, Handler (Looper .getMainLooper()))
171+ } catch (e: Exception ) {
172+ Log .e(TAG , " Error taking screenshot" , e)
173+ cleanup()
174+ }
145175 }
146176
147177 private fun saveScreenshot (bitmap : Bitmap ) {
@@ -160,26 +190,36 @@ class ScreenCaptureService : Service() {
160190 outputStream.close()
161191
162192 Log .i(TAG , " Screenshot saved to: ${file.absolutePath} " )
163- Toast .makeText(
164- this ,
165- " Screenshot saved to: Android/data/com.google.ai.sample/files/Pictures/Screenshots/" ,
166- Toast .LENGTH_LONG
167- ).show()
193+
194+ Handler (Looper .getMainLooper()).post {
195+ Toast .makeText(
196+ applicationContext,
197+ " Screenshot saved to: Android/data/com.google.ai.sample/files/Pictures/Screenshots/" ,
198+ Toast .LENGTH_LONG
199+ ).show()
200+ }
168201 } catch (e: Exception ) {
169202 Log .e(TAG , " Failed to save screenshot" , e)
170- Toast .makeText(this , " Failed to save screenshot: ${e.message} " , Toast .LENGTH_LONG ).show()
203+ Handler (Looper .getMainLooper()).post {
204+ Toast .makeText(applicationContext, " Failed to save screenshot: ${e.message} " , Toast .LENGTH_LONG ).show()
205+ }
171206 }
172207 }
173208
174209 private fun cleanup () {
175- virtualDisplay?.release()
176- virtualDisplay = null
177- imageReader?.close()
178- imageReader = null
179- mediaProjection?.stop()
180- mediaProjection = null
181- stopForeground(true )
182- stopSelf()
210+ try {
211+ virtualDisplay?.release()
212+ virtualDisplay = null
213+ imageReader?.close()
214+ imageReader = null
215+ mediaProjection?.stop()
216+ mediaProjection = null
217+ } catch (e: Exception ) {
218+ Log .e(TAG , " Error during cleanup" , e)
219+ } finally {
220+ stopForeground(STOP_FOREGROUND_REMOVE )
221+ stopSelf()
222+ }
183223 }
184224
185225 override fun onDestroy () {
0 commit comments