• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<lambda>null1 package platform.test.screenshot
2 
3 import android.graphics.Bitmap
4 import android.graphics.Rect
5 import android.os.Build
6 import android.os.Handler
7 import android.os.Looper
8 import android.view.PixelCopy
9 import android.view.Window
10 import androidx.concurrent.futures.SuspendToFutureAdapter
11 import androidx.test.platform.graphics.HardwareRendererCompat
12 import com.google.common.util.concurrent.ListenableFuture
13 import kotlinx.coroutines.Dispatchers
14 import kotlinx.coroutines.Job
15 
16 /**
17  * Suspend function that captures an image of the underlying window into a [Bitmap].
18  *
19  * For devices below [Build.VERSION_CODES#O] the image is obtained using [View#draw] on the windows
20  * decorView. Otherwise, [PixelCopy] is used.
21  *
22  * This method will also enable [HardwareRendererCompat#setDrawingEnabled(boolean)] if required.
23  *
24  * This API is primarily intended for use in lower layer libraries or frameworks. For test authors,
25  * its recommended to use espresso or compose's captureToImage.
26  *
27  * This API must be called from the UI thread.
28  *
29  * This API is currently experimental and subject to change or removal.
30  */
31 suspend fun Window.captureRegionToBitmap(boundsInWindow: Rect? = null): Bitmap {
32     var bitmap: Bitmap? = null
33 
34     val hardwareDrawingEnabled = HardwareRendererCompat.isDrawingEnabled()
35     HardwareRendererCompat.setDrawingEnabled(true)
36     try {
37         decorView.forceRedraw()
38         bitmap = generateBitmap(boundsInWindow)
39     } finally {
40         HardwareRendererCompat.setDrawingEnabled(hardwareDrawingEnabled)
41     }
42 
43     return bitmap!!
44 }
45 
46 /** A ListenableFuture variant of captureRegionToBitmap intended for use from Java. */
Windownull47 fun Window.captureRegionToBitmapAsync(boundsInWindow: Rect? = null): ListenableFuture<Bitmap> {
48     return SuspendToFutureAdapter.launchFuture(Dispatchers.Default + Job()) {
49         captureRegionToBitmap(boundsInWindow)
50     }
51 }
52 
generateBitmapnull53 internal suspend fun Window.generateBitmap(boundsInWindow: Rect? = null): Bitmap {
54     val destBitmap =
55         Bitmap.createBitmap(
56             boundsInWindow?.width() ?: decorView.width,
57             boundsInWindow?.height() ?: decorView.height,
58             Bitmap.Config.ARGB_8888,
59         )
60     when {
61         Build.VERSION.SDK_INT < 26 ->
62             // TODO: handle boundsInWindow
63             decorView.generateBitmapFromDraw(destBitmap, boundsInWindow)
64         else -> generateBitmapFromPixelCopy(boundsInWindow, destBitmap)
65     }
66 
67     return destBitmap
68 }
69 
70 @SuppressWarnings("NewApi")
generateBitmapFromPixelCopynull71 internal suspend fun Window.generateBitmapFromPixelCopy(
72     boundsInWindow: Rect? = null,
73     destBitmap: Bitmap,
74 ) {
75     val job = Job()
76     var exception: Exception? = null
77     val onCopyFinished =
78         PixelCopy.OnPixelCopyFinishedListener { result ->
79             if (result != PixelCopy.SUCCESS) {
80                 exception = RuntimeException(String.format("PixelCopy failed: %d", result))
81             }
82             job.complete()
83         }
84 
85     PixelCopy.request(
86         this,
87         boundsInWindow,
88         destBitmap,
89         onCopyFinished,
90         Handler(Looper.getMainLooper()),
91     )
92 
93     job.join()
94     exception?.let { throw it }
95 }
96