1

I would like to capture the UI emitted by Jetpack compose as a Bitmap. In XML this was done like this:

Basically takes a view as an input parameter and returns it as a Bitmap.

//take screenshot of the view added as an input argument
fun takeScreenShot(view: View) : Bitmap {
    val bitmap = Bitmap.createBitmap(
        view.width,
        view.height,
        Bitmap.Config.ARGB_8888
    )
    val canvas = Canvas(bitmap)
    view.draw(canvas)
    return bitmap
}

What is the equivalent of this in Jetpack compose?

Mahozad
  • 18,032
  • 13
  • 118
  • 133
Anudeep Ananth
  • 955
  • 1
  • 8
  • 30
  • See also https://github.com/JohannBlake/bitmap-from-composable for an example of using a `ComposeView` for this. – CommonsWare Jul 18 '21 at 15:20

2 Answers2

0

I would look at how JP-Compose testing does this.

A good starting point could be the android-compose-codelab, see:

/**
 * Simple on-device screenshot comparator that uses golden images present in
 * `androidTest/assets`. It's used to showcase the [AnimationClockTestRule] used in
 * [AnimatingCircleTests].
 *
 * Minimum SDK is O. Densities between devices must match.
 *
 * Screenshots are saved on device in `/data/data/{package}/files`.
 */
@RequiresApi(Build.VERSION_CODES.O)
fun assertScreenshotMatchesGolden(
    goldenName: String,
    node: SemanticsNodeInteraction
) {
    val bitmap = node.captureToImage().asAndroidBitmap()
}

from ScreenshotComparator.kt. You can find captureToImage() here in the AndroidHelpers.kt.

Also you can find here the ImageBitmap.kt, where asAndroidBitmap() only makes sure that, the underlying "common" version of the ImageBitmap is actually an android.graphics.Bitmap on Android (this is to make the code more platform agnostic, so that it can be run on the JVM/desktop as well)

Róbert Nagy
  • 6,720
  • 26
  • 45
0

Taking screenshots from a composable is possible in tests.
For taking screenshots in production code see this question and this issue.

First, make sure you have the following dependency in your build script (along with other required Compose dependencies):

debugImplementation("androidx.compose.ui:ui-test-manifest:<version>")

Note: Instead of the above dependency, you can simply add AndroidManifest.xml in androidTest directory and in the manifest>application element add this: <activity android:name="androidx.activity.ComponentActivity"/>.
Refer to this answer.

Here is a complete example for saving, reading, and comparing screenshots:
(Please refer to this post for setting up write permissions and so on for the tests)

class ScreenshotTest {

    @get:Rule val composeTestRule = createComposeRule()

    @Test fun takeAndSaveScreenshot() {
        composeTestRule.setContent { MyComposableFunction() }
        val node = composeTestRule.onRoot()
        val screenshot = node.captureToImage().asAndroidBitmap()
        saveScreenshot("screenshot.png", screenshot)
    }

    @Test fun readAndCompareScreenshots() {
        composeTestRule.setContent { MyComposableFunction() }
        val node = composeTestRule.onRoot()
        val screenshot = node.captureToImage().asAndroidBitmap()

        val context = InstrumentationRegistry.getInstrumentation().targetContext
        val path = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        val file = File(path, "screenshot.png")
        val saved = readScreenshot(file)

        println("Are screenshots the same: ${screenshot.sameAs(saved)}")
    }

    private fun readScreenshot(file: File) = BitmapFactory.decodeFile(file.path)

    private fun saveScreenshot(filename: String, screenshot: Bitmap) {
        val context = InstrumentationRegistry.getInstrumentation().targetContext
        // Saves in /Android/data/your.package.name.test/files/Pictures on external storage
        val path = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        val file = File(path, filename)
        file.outputStream().use { stream ->
            screenshot.compress(Bitmap.CompressFormat.PNG, 100, stream)
        }
    }
}

Thanks to Google Codelabs for this.

Mahozad
  • 18,032
  • 13
  • 118
  • 133