Use pixel copy on activities
Summary: Pixel copy is a more reliable and consistent way to take a snapshot rather than drawing into a canvas. It accepts either: Surface SurfaceView Window For root views that belong to an activity its easy to get the window so we do that here. In the next diff we solve this for other root views Reviewed By: lblasa Differential Revision: D50845282 fbshipit-source-id: 3968828dedd1e96a854b907e0fd152ad64993d95
This commit is contained in:
committed by
Facebook GitHub Bot
parent
6bf93347ee
commit
d85adc030f
@@ -10,7 +10,6 @@ package com.facebook.flipper.plugins.uidebugger.common
|
||||
import android.graphics.Bitmap
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/** BitmapPool is intended to be used on the main thread. In other words, it is not thread-safe. */
|
||||
class BitmapPool(private val config: Bitmap.Config = Bitmap.Config.RGB_565) {
|
||||
@@ -57,7 +56,7 @@ class BitmapPool(private val config: Bitmap.Config = Bitmap.Config.RGB_565) {
|
||||
override fun readyForReuse() {
|
||||
val key = generateKey(bitmap.width, bitmap.height)
|
||||
|
||||
mainScope.launch {
|
||||
synchronized(this@BitmapPool) {
|
||||
if (isRecycled) {
|
||||
bitmap.recycle()
|
||||
} else {
|
||||
|
||||
@@ -7,9 +7,15 @@
|
||||
|
||||
package com.facebook.flipper.plugins.uidebugger.core
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Canvas
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.PixelCopy
|
||||
import android.view.View
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||
import com.facebook.flipper.plugins.uidebugger.common.BitmapPool
|
||||
|
||||
@@ -41,3 +47,50 @@ class CanvasSnapshotter(private val bitmapPool: BitmapPool) : Snapshotter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
class PixelCopySnapshotter(
|
||||
private val bitmapPool: BitmapPool,
|
||||
private val applicationRef: ApplicationRef,
|
||||
private val fallback: Snapshotter
|
||||
) : Snapshotter {
|
||||
|
||||
override fun takeSnapshot(view: View): BitmapPool.ReusableBitmap? {
|
||||
|
||||
if (view.width <= 0 || view.height <= 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
val bitmap = bitmapPool.getBitmap(view.width, view.height)
|
||||
try {
|
||||
|
||||
val decorViewToActivity: Map<View, Activity> =
|
||||
applicationRef.activitiesStack.toList().associateBy { it.window.decorView }
|
||||
|
||||
val activity = decorViewToActivity[view]
|
||||
|
||||
// if this view belongs to an activity prefer this as it doesn't require private api hacks
|
||||
if (activity != null) {
|
||||
PixelCopy.request(
|
||||
activity.window,
|
||||
bitmap.bitmap,
|
||||
{
|
||||
// no-op this this api is actually synchronous despite how it looks
|
||||
},
|
||||
Handler(Looper.getMainLooper()))
|
||||
return bitmap
|
||||
}
|
||||
} catch (e: OutOfMemoryError) {
|
||||
Log.e(LogTag, "OOM when taking snapshot")
|
||||
null
|
||||
} catch (e: Exception) {
|
||||
// there was some problem with the pixel copy, fall back to canvas impl
|
||||
Log.e(LogTag, "Exception when taking snapshot", e)
|
||||
Log.i(LogTag, "Using fallback snapshot", e)
|
||||
bitmap.readyForReuse()
|
||||
fallback.takeSnapshot(view)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package com.facebook.flipper.plugins.uidebugger.core
|
||||
|
||||
import android.app.Application
|
||||
import android.os.Build
|
||||
import com.facebook.flipper.core.FlipperConnection
|
||||
import com.facebook.flipper.plugins.uidebugger.common.BitmapPool
|
||||
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
|
||||
@@ -32,7 +33,16 @@ class UIDContext(
|
||||
) {
|
||||
|
||||
val bitmapPool = BitmapPool()
|
||||
val decorViewTracker = DecorViewTracker(this, CanvasSnapshotter(bitmapPool))
|
||||
private val canvasSnapshotter = CanvasSnapshotter(bitmapPool)
|
||||
|
||||
val snapshotter =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
PixelCopySnapshotter(bitmapPool, applicationRef, canvasSnapshotter)
|
||||
} else {
|
||||
canvasSnapshotter
|
||||
}
|
||||
|
||||
val decorViewTracker = DecorViewTracker(this, snapshotter)
|
||||
val updateQueue = UpdateQueue(this)
|
||||
val layoutTraversal: LayoutTraversal = LayoutTraversal(this)
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ object ApplicationRefDescriptor : ChainedDescriptor<ApplicationRef>() {
|
||||
val activeRoots = node.rootsResolver.rootViews()
|
||||
|
||||
val decorViewToActivity: Map<View, Activity> =
|
||||
node.activitiesStack.toList().map { it.window.decorView to it }.toMap()
|
||||
node.activitiesStack.toList().associateBy { it.window.decorView }
|
||||
|
||||
for (root in activeRoots) {
|
||||
// if there is an activity for this root view use that,
|
||||
|
||||
Reference in New Issue
Block a user