Skip to main content

Taking Screenshots Programmatically

It is possible to take a screenshot of your Android application programmatically.

Basic Usage​

There are possibly many methods that you can use to take a screenshot programmatically. In this document, we are going to take a look at 2 methods used by the wonderful Sentry team in their sentry-java open-source project:

  1. PixelCopy API. This API is added in API level 24.
  2. View.draw() method. This API is added in API level 1.

You can take a look directly at ScreenshotUtils.java file inside sentry-java repository to see how Sentry team utilizes these methods to take a screenshot programmatically. However, to simplify this document, we have recreated a simpler version of the utility file below:

SimpleScreenshotUtil.kt
package com.hanmajid.androidnotebook

import android.app.Activity
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.util.Log
import android.view.PixelCopy

/**
* Screenshot-related utility object.
*/
object SimpleScreenshotUtil {

/**
* Take a screenshot of current [Activity].
*/
fun takeScreenshot(
activity: Activity,
) {
val view = activity.window.decorView.rootView
val resultBitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val thread = HandlerThread("MyThread").apply {
start()
}
val handler = Handler(thread.looper)
PixelCopy.request(
activity.window,
resultBitmap,
{
if (it == PixelCopy.SUCCESS) {
saveScreenshot(
activity = activity,
bitmap = resultBitmap,
filename = "my-screenshot-pixel-copy.png",
)
} else {
Log.i("TAG", "ERROR $it")
// Handle error.
}
},
handler,
)
} else {
val canvas = Canvas(resultBitmap)
activity.runOnUiThread {
try {
view.draw(canvas)
saveScreenshot(
activity = activity,
bitmap = resultBitmap,
filename = "my-screenshot-canvas.png",
)
} catch (e: Throwable) {
Log.e("TAG", e.message, e)
// Handle error.
}
}
}
}

/**
* Save the taken screenshot [bitmap] to Internal Storage.
*/
private fun saveScreenshot(
activity: Activity,
bitmap: Bitmap,
filename: String,
) {
activity.openFileOutput(filename, Context.MODE_PRIVATE)
.use { outputStream ->
bitmap.compress(Bitmap.CompressFormat.PNG, 0, outputStream)
}
}
}

To use SimpleScreenshotUtil.kt object, simply call takeScreenshot method inside your Activity:

MainActivity.kt
package com.hanmajid.androidnotebook

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.hanmajid.androidnotebook.ui.theme.MyApplicationTheme

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column {
Greeting("Android")
Button(onClick = {
SimpleScreenshotUtil.takeScreenshot(this@MainActivity)
}) {
Text("Take Screenshot")
}
}
}
}
}
}
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
MyApplicationTheme {
Greeting("Android")
}
}

After takeScreenshot() method is called, the image will be saved inside the app's Internal Storage:

References​