Skip to content

Instantly share code, notes, and snippets.

@nosix
Created September 18, 2016 08:40
Show Gist options
  • Save nosix/fcf1923f0c7cc42aa8c2ee70ff457b9c to your computer and use it in GitHub Desktop.
Save nosix/fcf1923f0c7cc42aa8c2ee70ff457b9c to your computer and use it in GitHub Desktop.
Taking a snapshot of the screen for Android (SDK 21) in Kotlin 1.0.3
package xxx
import android.content.Context
import android.graphics.Bitmap
import android.graphics.PixelFormat
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.media.ImageReader
import android.media.projection.MediaProjection
import android.util.Log
class Capture(val context: Context) : ImageReader.OnImageAvailableListener {
companion object {
private val TAG = Capture::class.qualifiedName
}
private var display: VirtualDisplay? = null
private var onCaptureListener: ((Bitmap) -> Unit)? = null
fun run(mediaProjection: MediaProjection, onCaptureListener: (Bitmap) -> Unit) {
this.onCaptureListener = onCaptureListener
if (display == null) {
display = createDisplay(mediaProjection)
}
}
private fun createDisplay(mediaProjection: MediaProjection): VirtualDisplay {
context.resources.displayMetrics.run {
val maxImages = 2
val reader = ImageReader.newInstance(
widthPixels, heightPixels, PixelFormat.RGBA_8888, maxImages)
reader.setOnImageAvailableListener(this@Capture, null)
val display = mediaProjection.createVirtualDisplay(
"Capture Display", widthPixels, heightPixels, densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
reader.surface, null, null)
Log.d(TAG, "createVirtualDisplay")
return display
}
}
override fun onImageAvailable(reader: ImageReader) {
if (display != null) {
onCaptureListener?.invoke(captureImage(reader))
}
}
private fun captureImage(reader: ImageReader): Bitmap {
Log.d(TAG, "captureImage")
val image = reader.acquireLatestImage()
context.resources.displayMetrics.run {
image.planes[0].run {
val bitmap = Bitmap.createBitmap(
rowStride / pixelStride, heightPixels, Bitmap.Config.ARGB_8888)
bitmap.copyPixelsFromBuffer(buffer)
image.close()
return bitmap
}
}
}
fun stop() {
display?.release()
display = null
onCaptureListener = null
}
}
package xxx
import android.app.Activity
import android.app.Service
import android.content.Intent
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
import android.os.Bundle
import android.widget.Toast
class CaptureActivity : Activity() {
companion object {
private const val REQUEST_CAPTURE = 1
var projection: MediaProjection? = null
}
private lateinit var mediaProjectionManager: MediaProjectionManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mediaProjectionManager = getSystemService(Service.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), REQUEST_CAPTURE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CAPTURE) {
if (resultCode == RESULT_OK) {
projection = mediaProjectionManager.getMediaProjection(resultCode, data)
val intent = Intent(this, CaptureService::class.java)
.setAction(CaptureService.ACTION_ENABLE_CAPTURE)
startService(intent)
} else {
projection = null
Toast.makeText(this, R.string.capture_error, Toast.LENGTH_SHORT).show()
}
}
finish()
}
}
package xxx
import android.app.Notification
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import java.util.*
import xxx.Capture
class CaptureService : Service() {
companion object {
private val TAG = CaptureService::class.qualifiedName
val ACTION_ENABLE_CAPTURE = "enable_capture"
}
private val notificationId = Random().nextInt()
private val capture = Capture(this)
// ... snip ...
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent != null) {
when (intent.action) {
ACTION_ENABLE_CAPTURE -> onEnableCapture()
}
}
return Service.START_STICKY
}
private fun enableCapture() {
if (CaptureActivity.projection == null) {
Log.d(TAG, "startActivity(CaptureActivity)")
val intent = Intent(this, CaptureActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
} else {
onEnableCapture()
}
}
private fun onEnableCapture() {
CaptureActivity.projection?.run {
capture.run(this) {
capture.stop()
// save bitmap
}
}
}
private fun disableCapture() {
capture.stop()
CaptureActivity.projection = null
}
override fun onDestroy() {
super.onDestroy()
disableCapture()
}
}
@nosix
Copy link
Author

nosix commented Apr 7, 2021

Please read the following about the service.

https://developer.android.com/guide/components/services

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment