Extracted snapshot out of descriptor

Summary: Snapshot never made much sense in the descriptor since we only snapshot the decor views. Additionally in the next diff i will introduce a new way to snapshot so this will make it easier

Reviewed By: lblasa

Differential Revision: D50845280

fbshipit-source-id: c2eac351b72786e7b66951d0fa09cea52a6dcc69
This commit is contained in:
Luke De Feo
2023-11-02 12:29:07 -07:00
committed by Facebook GitHub Bot
parent c93c494ef4
commit 6bf93347ee
11 changed files with 50 additions and 82 deletions

View File

@@ -7,7 +7,6 @@
package com.facebook.flipper.plugins.jetpackcompose.descriptors
import android.graphics.Bitmap
import android.view.ViewGroup
import com.facebook.flipper.plugins.jetpackcompose.model.ComposeInnerViewNode
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
@@ -49,13 +48,6 @@ object ComposeInnerViewDescriptor : NodeDescriptor<ComposeInnerViewNode> {
return ViewDescriptor.getChildren(node.view)
}
override fun getSnapshot(node: ComposeInnerViewNode, bitmap: Bitmap?): Bitmap? {
if (node.view is ViewGroup) {
return ViewGroupDescriptor.getSnapshot(node.view, bitmap)
}
return ViewDescriptor.getSnapshot(node.view, bitmap)
}
override fun getActiveChild(node: ComposeInnerViewNode): Any? {
if (node.view is ViewGroup) {
return ViewGroupDescriptor.getActiveChild(node.view)

View File

@@ -7,7 +7,6 @@
package com.facebook.flipper.plugins.jetpackcompose.descriptors
import android.graphics.Bitmap
import com.facebook.flipper.plugins.jetpackcompose.model.ComposeNode
import com.facebook.flipper.plugins.uidebugger.descriptors.BaseTags
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
@@ -130,8 +129,6 @@ object ComposeNodeDescriptor : NodeDescriptor<ComposeNode> {
override fun getQualifiedName(node: ComposeNode): String = node.inspectorNode.name
override fun getSnapshot(node: ComposeNode, bitmap: Bitmap?): Bitmap? = null
override fun getActiveChild(node: ComposeNode): Any? = null
override fun getTags(node: ComposeNode): Set<String> = setOf(BaseTags.Android, "Compose")

View File

@@ -16,7 +16,7 @@ import kotlinx.coroutines.launch
class BitmapPool(private val config: Bitmap.Config = Bitmap.Config.RGB_565) {
interface ReusableBitmap {
val bitmap: Bitmap?
val bitmap: Bitmap
fun readyForReuse()
}

View File

@@ -11,7 +11,6 @@ import android.util.Log
import android.view.View
import android.view.ViewTreeObserver
import com.facebook.flipper.plugins.uidebugger.LogTag
import com.facebook.flipper.plugins.uidebugger.common.BitmapPool
import com.facebook.flipper.plugins.uidebugger.descriptors.ViewDescriptor
import com.facebook.flipper.plugins.uidebugger.util.StopWatch
import com.facebook.flipper.plugins.uidebugger.util.Throttler
@@ -22,7 +21,7 @@ import com.facebook.flipper.plugins.uidebugger.util.objectIdentity
* to it This predraw observer triggers a full traversal of the UI. There should only ever be one
* active predraw listener at once
*/
class DecorViewTracker(val context: UIDContext) {
class DecorViewTracker(private val context: UIDContext, private val snapshotter: Snapshotter) {
private var currentDecorView: View? = null
private var preDrawListener: ViewTreeObserver.OnPreDrawListener? = null
@@ -88,18 +87,7 @@ class DecorViewTracker(val context: UIDContext) {
val (nodes, traversalTime) =
StopWatch.time { context.layoutTraversal.traverse(context.applicationRef) }
mStopWatch.start()
var snapshotBitmap: BitmapPool.ReusableBitmap? = null
if (decorView.width > 0 && decorView.height > 0) {
snapshotBitmap = context.bitmapPool.getBitmap(decorView.width, decorView.height)
context.bitmapPool.getBitmap(decorView.width, decorView.height)
Log.i(
LogTag,
"Snapshotting view ${ViewDescriptor.getId(decorView)}",
)
ViewDescriptor.getSnapshot(decorView, snapshotBitmap.bitmap)
}
val snapshotTime = mStopWatch.stop()
val (reusableBitmap, snapshotMs) = StopWatch.time { snapshotter.takeSnapshot(decorView) }
context.updateQueue.enqueueUpdate(
Update(
@@ -107,8 +95,8 @@ class DecorViewTracker(val context: UIDContext) {
nodes,
startTimestamp,
traversalTime,
snapshotTime,
snapshotMs,
System.currentTimeMillis(),
snapshotBitmap))
reusableBitmap))
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.flipper.plugins.uidebugger.core
import android.graphics.Canvas
import android.util.Log
import android.view.View
import com.facebook.flipper.plugins.uidebugger.LogTag
import com.facebook.flipper.plugins.uidebugger.common.BitmapPool
interface Snapshotter {
fun takeSnapshot(view: View): BitmapPool.ReusableBitmap?
}
/**
* Takes a snapshot by redrawing the view into a bitmap backed canvas, Since this is software
* rendering there can be discrepancies between the real image and the snapshot:
* 1. It can be unreliable when snapshotting views that are added directly to window manager
* 2. It doesnt include certain types of content (video / images)
*/
class CanvasSnapshotter(private val bitmapPool: BitmapPool) : Snapshotter {
override fun takeSnapshot(view: View): BitmapPool.ReusableBitmap? {
if (view.width <= 0 || view.height <= 0) {
return null
}
return try {
val reuseAbleBitmap = bitmapPool.getBitmap(view.width, view.height)
val canvas = Canvas(reuseAbleBitmap.bitmap)
view.draw(canvas)
reuseAbleBitmap
} catch (e: OutOfMemoryError) {
Log.e(LogTag, "OOM when taking snapshot")
null
}
}
}

View File

@@ -31,12 +31,11 @@ class UIDContext(
private val pendingFrameworkEvents: MutableList<FrameworkEvent>
) {
val decorViewTracker = DecorViewTracker(this)
val bitmapPool = BitmapPool()
val decorViewTracker = DecorViewTracker(this, CanvasSnapshotter(bitmapPool))
val updateQueue = UpdateQueue(this)
val layoutTraversal: LayoutTraversal = LayoutTraversal(this)
val bitmapPool = BitmapPool()
fun addFrameworkEvent(frameworkEvent: FrameworkEvent) {
synchronized(pendingFrameworkEvents) { pendingFrameworkEvents.add(frameworkEvent) }
}

View File

@@ -7,7 +7,6 @@
package com.facebook.flipper.plugins.uidebugger.descriptors
import android.graphics.Bitmap
import com.facebook.flipper.plugins.uidebugger.model.Bounds
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
@@ -133,15 +132,6 @@ abstract class ChainedDescriptor<T> : NodeDescriptor<T> {
*/
open fun onGetAttributes(node: T, attributeSections: MutableMap<MetadataId, InspectableObject>) {}
/** Get a snapshot of the node. */
final override fun getSnapshot(node: T, bitmap: Bitmap?): Bitmap? {
return onGetSnapshot(node, bitmap) ?: mSuper?.onGetSnapshot(node, bitmap)
}
open fun onGetSnapshot(node: T, bitmap: Bitmap?): Bitmap? {
return null
}
final override fun getInlineAttributes(node: T): Map<String, String> {
val builder = mutableMapOf<String, String>()

View File

@@ -7,7 +7,6 @@
package com.facebook.flipper.plugins.uidebugger.descriptors
import android.graphics.Bitmap
import com.facebook.flipper.plugins.uidebugger.model.Bounds
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
@@ -58,13 +57,6 @@ interface NodeDescriptor<T> {
/** The children this node exposes in the inspector. */
fun getChildren(node: T): List<Any>
/**
* Get a snapshot of the node. Bitmaps are not cheap to create, so accept one as an optional
* parameter. If a bitmap is provided, it will be used by the canvas to draw on it. Otherwise, a
* bitmap will be created.
*/
fun getSnapshot(node: T, bitmap: Bitmap?): Bitmap? = null
/**
* If you have overlapping children this indicates which child is active / on top, we will only
* listen to / traverse this child. If return null we assume all children are 'active'

View File

@@ -7,7 +7,6 @@
package com.facebook.flipper.plugins.uidebugger.descriptors
import android.graphics.Bitmap
import com.facebook.flipper.plugins.uidebugger.model.Bounds
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
@@ -34,6 +33,4 @@ object ObjectDescriptor : NodeDescriptor<Any> {
override fun getBounds(node: Any): Bounds = Bounds(0, 0, 0, 0)
override fun getTags(node: Any): Set<String> = setOf(BaseTags.Unknown)
override fun getSnapshot(node: Any, bitmap: Bitmap?): Bitmap? = null
}

View File

@@ -7,7 +7,6 @@
package com.facebook.flipper.plugins.uidebugger.descriptors
import android.graphics.Bitmap
import com.facebook.flipper.plugins.uidebugger.model.Bounds
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
@@ -42,7 +41,4 @@ object OffsetChildDescriptor : NodeDescriptor<OffsetChild> {
node.descriptor.getAttributes(node.child)
override fun getTags(node: OffsetChild): Set<String> = node.descriptor.getTags(node.child)
override fun getSnapshot(node: OffsetChild, bitmap: Bitmap?): Bitmap? =
node.descriptor.getSnapshot(node.child, bitmap)
}

View File

@@ -8,8 +8,6 @@
package com.facebook.flipper.plugins.uidebugger.descriptors
import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
@@ -380,30 +378,6 @@ object ViewDescriptor : ChainedDescriptor<View>() {
attributes["id"] = value
}
override fun onGetSnapshot(node: View, bitmap: Bitmap?): Bitmap? {
if (node.width <= 0 || node.height <= 0) {
return null
}
var workingBitmap = bitmap
try {
val differentSize =
if (bitmap != null) (node.width != bitmap.width || node.height != bitmap.height)
else false
if (workingBitmap == null || differentSize) {
val viewWidth: Int = node.width
val viewHeight: Int = node.height
workingBitmap = BitmapPool.createBitmapWithDefaultConfig(viewWidth, viewHeight)
}
val canvas = Canvas(workingBitmap)
node.draw(canvas)
} catch (e: OutOfMemoryError) {}
return workingBitmap
}
private fun fromDrawable(d: Drawable?): Inspectable? {
return if (d is ColorDrawable) {
InspectableValue.Color(Color.fromColor(d.color))