Attributes Metadata
Summary: Before this change, attributes and attribute metadata were intermingled and sent as one unit via subtree update event. This represented a few issues: - Repetitiveness. For each declared and dynamic attribute, metadata was included on each value unit. - Metadata can vary in size and thus can have a negative impact on payload size. - The attribute name which is part of metadata is a string which always overhead on processing. - Metadata instantiation is not cheap thus this also incurs in processing overhead i.e. even instantiating a single string can have an impact. The proposal is to separate metadata of attributes from the actual node reported attributes. This solves the problems mentioned above. Reviewed By: LukeDefeo Differential Revision: D40674156 fbshipit-source-id: 0788551849fbce53065f819ba503e7e4afc03cc0
This commit is contained in:
committed by
Facebook GitHub Bot
parent
27428522ce
commit
01dc22b1ab
@@ -13,8 +13,10 @@ import com.facebook.flipper.core.FlipperConnection
|
||||
import com.facebook.flipper.core.FlipperPlugin
|
||||
import com.facebook.flipper.plugins.uidebugger.core.*
|
||||
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
|
||||
import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister
|
||||
import com.facebook.flipper.plugins.uidebugger.descriptors.nodeId
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InitEvent
|
||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataUpdateEvent
|
||||
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@@ -51,6 +53,12 @@ class UIDebuggerFlipperPlugin(
|
||||
InitEvent.name,
|
||||
Json.encodeToString(InitEvent.serializer(), InitEvent(context.applicationRef.nodeId())))
|
||||
|
||||
connection.send(
|
||||
MetadataUpdateEvent.name,
|
||||
Json.encodeToString(
|
||||
MetadataUpdateEvent.serializer(),
|
||||
MetadataUpdateEvent(MetadataRegister.staticMetadata())))
|
||||
|
||||
context.treeObserverManager.start()
|
||||
}
|
||||
|
||||
@@ -59,6 +67,8 @@ class UIDebuggerFlipperPlugin(
|
||||
this.context.connectionRef.connection = null
|
||||
Log.i(LogTag, "Disconnected")
|
||||
|
||||
MetadataRegister.clear()
|
||||
|
||||
context.treeObserverManager.stop()
|
||||
context.bitmapPool.recycleAll()
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ open class EnumMapping<T>(private val mapping: Map<String, T>) {
|
||||
"Could not convert string $key to enum value, possible values ${mapping.entries} ")
|
||||
}
|
||||
|
||||
fun toInspectable(value: T, mutable: Boolean): InspectableValue.Enum {
|
||||
return InspectableValue.Enum(Enumeration(mapping.keys, getStringRepresentation(value)), mutable)
|
||||
fun toInspectable(value: T): InspectableValue.Enum {
|
||||
return InspectableValue.Enum(Enumeration(mapping.keys, getStringRepresentation(value)))
|
||||
}
|
||||
companion object {
|
||||
const val NoMapping = "__UNKNOWN_ENUM_VALUE__"
|
||||
|
||||
@@ -10,7 +10,9 @@ package com.facebook.flipper.plugins.uidebugger.core
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||
import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister
|
||||
import com.facebook.flipper.plugins.uidebugger.descriptors.nodeId
|
||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataUpdateEvent
|
||||
import com.facebook.flipper.plugins.uidebugger.model.Node
|
||||
import com.facebook.flipper.plugins.uidebugger.model.PerfStatsEvent
|
||||
import com.facebook.flipper.plugins.uidebugger.model.SubtreeUpdateEvent
|
||||
@@ -50,7 +52,16 @@ class NativeScanScheduler(val context: Context) : Scheduler.Task<ScanResult> {
|
||||
return ScanResult(txId++, start, scanEnd, nodes)
|
||||
}
|
||||
|
||||
override fun process(input: ScanResult) {
|
||||
private fun sendMetadata() {
|
||||
val metadata = MetadataRegister.dynamicMetadata()
|
||||
if (metadata.size > 0) {
|
||||
context.connectionRef.connection?.send(
|
||||
MetadataUpdateEvent.name,
|
||||
Json.encodeToString(MetadataUpdateEvent.serializer(), MetadataUpdateEvent(metadata)))
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendSubtreeUpdate(input: ScanResult) {
|
||||
val serialized =
|
||||
Json.encodeToString(
|
||||
SubtreeUpdateEvent.serializer(),
|
||||
@@ -79,4 +90,9 @@ class NativeScanScheduler(val context: Context) : Scheduler.Task<ScanResult> {
|
||||
socketEnd,
|
||||
input.nodes.size)))
|
||||
}
|
||||
|
||||
override fun process(input: ScanResult) {
|
||||
sendMetadata()
|
||||
sendSubtreeUpdate(input)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ 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
|
||||
|
||||
/**
|
||||
* A chained descriptor is a special type of descriptor that models the inheritance hierarchy in
|
||||
@@ -72,8 +73,8 @@ abstract class ChainedDescriptor<T> : NodeDescriptor<T> {
|
||||
|
||||
open fun onGetChildren(node: T): List<Any>? = null
|
||||
|
||||
final override fun getData(node: T): Map<SectionName, InspectableObject> {
|
||||
val builder = mutableMapOf<String, InspectableObject>()
|
||||
final override fun getData(node: T): Map<MetadataId, InspectableObject> {
|
||||
val builder = mutableMapOf<MetadataId, InspectableObject>()
|
||||
onGetData(node, builder)
|
||||
|
||||
var curDescriptor: ChainedDescriptor<T>? = mSuper
|
||||
@@ -90,7 +91,7 @@ abstract class ChainedDescriptor<T> : NodeDescriptor<T> {
|
||||
* Get the data to show for this node in the sidebar of the inspector. Each key will be a have its
|
||||
* own section
|
||||
*/
|
||||
open fun onGetData(node: T, attributeSections: MutableMap<SectionName, InspectableObject>) {}
|
||||
open fun onGetData(node: T, attributeSections: MutableMap<MetadataId, InspectableObject>) {}
|
||||
|
||||
/** Get a snapshot of the node. */
|
||||
final override fun getSnapshot(node: T, bitmap: Bitmap?): Bitmap? {
|
||||
|
||||
@@ -8,22 +8,25 @@
|
||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import com.facebook.flipper.plugins.uidebugger.model.Color
|
||||
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||
import com.facebook.flipper.plugins.uidebugger.model.*
|
||||
|
||||
object ColorDrawableDescriptor : ChainedDescriptor<ColorDrawable>() {
|
||||
|
||||
private const val NAMESPACE = "ColorDrawable"
|
||||
private var SectionId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE)
|
||||
private var ColorAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "color")
|
||||
|
||||
override fun onGetName(node: ColorDrawable): String = node.javaClass.simpleName
|
||||
|
||||
override fun onGetData(
|
||||
node: ColorDrawable,
|
||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
||||
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||
) {
|
||||
val props = mutableMapOf<String, Inspectable>()
|
||||
props["color"] = InspectableValue.Color(Color.fromColor(node.color), mutable = true)
|
||||
val props = mutableMapOf<Int, Inspectable>()
|
||||
props[ColorAttributeId] = InspectableValue.Color(Color.fromColor(node.color))
|
||||
|
||||
attributeSections["ColorDrawable"] = InspectableObject(props.toMap())
|
||||
attributeSections[SectionId] = InspectableObject(props.toMap())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,15 @@ import android.os.Build
|
||||
import com.facebook.flipper.plugins.uidebugger.model.*
|
||||
|
||||
object DrawableDescriptor : ChainedDescriptor<Drawable>() {
|
||||
|
||||
private const val NAMESPACE = "Drawable"
|
||||
private var SectionId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE)
|
||||
private var AlphaAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "alpha")
|
||||
private var BoundsAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "bounds")
|
||||
|
||||
override fun onGetName(node: Drawable): String = node.javaClass.simpleName
|
||||
|
||||
override fun onGetBounds(node: Drawable): Bounds =
|
||||
@@ -19,16 +28,16 @@ object DrawableDescriptor : ChainedDescriptor<Drawable>() {
|
||||
|
||||
override fun onGetData(
|
||||
node: Drawable,
|
||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
||||
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
val props = mutableMapOf<String, Inspectable>()
|
||||
props["alpha"] = InspectableValue.Number(node.alpha, true)
|
||||
val props = mutableMapOf<Int, Inspectable>()
|
||||
props[AlphaAttributeId] = InspectableValue.Number(node.alpha)
|
||||
|
||||
val bounds = node.bounds
|
||||
props["bounds"] = InspectableValue.Bounds(Bounds.fromRect(bounds))
|
||||
props[BoundsAttributeId] = InspectableValue.Bounds(Bounds.fromRect(bounds))
|
||||
|
||||
attributeSections["Drawable"] = InspectableObject(props.toMap())
|
||||
attributeSections[SectionId] = InspectableObject(props.toMap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,14 @@ import android.os.Bundle
|
||||
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||
|
||||
object FragmentFrameworkDescriptor : ChainedDescriptor<android.app.Fragment>() {
|
||||
|
||||
private const val NAMESPACE = "Fragment"
|
||||
private var SectionId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE)
|
||||
|
||||
override fun onGetName(node: android.app.Fragment): String {
|
||||
return node.javaClass.simpleName
|
||||
}
|
||||
@@ -23,19 +28,24 @@ object FragmentFrameworkDescriptor : ChainedDescriptor<android.app.Fragment>() {
|
||||
|
||||
override fun onGetData(
|
||||
node: android.app.Fragment,
|
||||
attributeSections: MutableMap<String, InspectableObject>
|
||||
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||
) {
|
||||
val args: Bundle = node.arguments
|
||||
|
||||
val props = mutableMapOf<String, Inspectable>()
|
||||
val props = mutableMapOf<Int, Inspectable>()
|
||||
for (key in args.keySet()) {
|
||||
val metadata = MetadataRegister.get(NAMESPACE, key)
|
||||
val identifier =
|
||||
metadata?.id
|
||||
?: MetadataRegister.registerDynamic(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, key)
|
||||
|
||||
when (val value = args[key]) {
|
||||
is Number -> props[key] = InspectableValue.Number(value)
|
||||
is Boolean -> props[key] = InspectableValue.Boolean(value)
|
||||
is String -> props[key] = InspectableValue.Text(value)
|
||||
is Number -> props[identifier] = InspectableValue.Number(value)
|
||||
is Boolean -> props[identifier] = InspectableValue.Boolean(value)
|
||||
is String -> props[identifier] = InspectableValue.Text(value)
|
||||
}
|
||||
}
|
||||
|
||||
attributeSections["Fragment"] = InspectableObject(props.toMap())
|
||||
attributeSections[SectionId] = InspectableObject(props.toMap())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,14 @@ package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||
|
||||
object FragmentSupportDescriptor : ChainedDescriptor<androidx.fragment.app.Fragment>() {
|
||||
|
||||
private const val NAMESPACE = "Fragment"
|
||||
private var SectionId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE)
|
||||
|
||||
override fun onGetName(node: androidx.fragment.app.Fragment): String {
|
||||
return node.javaClass.simpleName
|
||||
}
|
||||
@@ -22,19 +27,23 @@ object FragmentSupportDescriptor : ChainedDescriptor<androidx.fragment.app.Fragm
|
||||
|
||||
override fun onGetData(
|
||||
node: androidx.fragment.app.Fragment,
|
||||
attributeSections: MutableMap<String, InspectableObject>
|
||||
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||
) {
|
||||
val args = node.arguments
|
||||
args?.let { bundle ->
|
||||
val props = mutableMapOf<String, Inspectable>()
|
||||
val props = mutableMapOf<Int, Inspectable>()
|
||||
for (key in bundle.keySet()) {
|
||||
val metadata = MetadataRegister.get(NAMESPACE, key)
|
||||
val identifier =
|
||||
metadata?.id
|
||||
?: MetadataRegister.registerDynamic(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, key)
|
||||
when (val value = bundle[key]) {
|
||||
is Number -> props[key] = InspectableValue.Number(value)
|
||||
is Boolean -> props[key] = InspectableValue.Boolean(value)
|
||||
is String -> props[key] = InspectableValue.Text(value)
|
||||
is Number -> props[identifier] = InspectableValue.Number(value)
|
||||
is Boolean -> props[identifier] = InspectableValue.Boolean(value)
|
||||
is String -> props[identifier] = InspectableValue.Text(value)
|
||||
}
|
||||
}
|
||||
attributeSections["Fragment"] = InspectableObject(props.toMap())
|
||||
attributeSections[SectionId] = InspectableObject(props.toMap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,18 +12,27 @@ import android.widget.ImageView.ScaleType
|
||||
import com.facebook.flipper.plugins.uidebugger.common.EnumMapping
|
||||
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||
|
||||
object ImageViewDescriptor : ChainedDescriptor<ImageView>() {
|
||||
|
||||
private const val NAMESPACE = "ImageView"
|
||||
|
||||
private var SectionId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE)
|
||||
private var ScaleTypeAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "scaleType")
|
||||
|
||||
override fun onGetName(node: ImageView): String = node.javaClass.simpleName
|
||||
|
||||
override fun onGetData(
|
||||
node: ImageView,
|
||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
||||
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||
) {
|
||||
val props = mutableMapOf<String, Inspectable>()
|
||||
props["scaleType"] = scaleTypeMapping.toInspectable(node.scaleType, true)
|
||||
val props = mutableMapOf<Int, Inspectable>()
|
||||
props[ScaleTypeAttributeId] = scaleTypeMapping.toInspectable(node.scaleType)
|
||||
|
||||
attributeSections["ImageView"] = InspectableObject(props)
|
||||
attributeSections[SectionId] = InspectableObject(props)
|
||||
}
|
||||
|
||||
private val scaleTypeMapping: EnumMapping<ScaleType> =
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.descriptors
|
||||
|
||||
import com.facebook.flipper.plugins.uidebugger.model.Metadata
|
||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||
|
||||
/**
|
||||
* Registry of attribute metadata. There's two types of attributes:
|
||||
* - Static attributes. Those that are known at build time.
|
||||
* - Dynamic attributes. Those that are known at runtime.
|
||||
*/
|
||||
object MetadataRegister {
|
||||
|
||||
const val TYPE_IDENTITY = "identity"
|
||||
const val TYPE_ATTRIBUTE = "attribute"
|
||||
const val TYPE_LAYOUT = "layout"
|
||||
const val TYPE_DOCUMENTATION = "documentation"
|
||||
|
||||
private var generator: MetadataId = 0
|
||||
private val staticMetadata: MutableMap<String, Metadata> = mutableMapOf()
|
||||
private val dynamicMetadata: MutableMap<String, Metadata> = mutableMapOf()
|
||||
private val queried: MutableSet<MetadataId> = mutableSetOf()
|
||||
|
||||
fun key(namespace: String, name: String): String = "${namespace}_$name"
|
||||
|
||||
private fun register(
|
||||
metadata: MutableMap<String, Metadata>,
|
||||
type: String,
|
||||
namespace: String,
|
||||
name: String,
|
||||
mutable: Boolean
|
||||
): MetadataId {
|
||||
val key = key(namespace, name)
|
||||
metadata[key]?.let { m ->
|
||||
return m.id
|
||||
}
|
||||
|
||||
val identifier = ++generator
|
||||
metadata[key] = Metadata(identifier, type, namespace, name, mutable)
|
||||
return identifier
|
||||
}
|
||||
|
||||
fun register(
|
||||
type: String,
|
||||
namespace: String,
|
||||
name: String,
|
||||
mutable: Boolean = false
|
||||
): MetadataId {
|
||||
return register(staticMetadata, type, namespace, name, mutable)
|
||||
}
|
||||
|
||||
fun registerDynamic(
|
||||
type: String,
|
||||
namespace: String,
|
||||
name: String,
|
||||
mutable: Boolean = false
|
||||
): MetadataId {
|
||||
return register(dynamicMetadata, type, namespace, name, mutable)
|
||||
}
|
||||
|
||||
fun get(namespace: String, name: String): Metadata? {
|
||||
val key = key(namespace, name)
|
||||
staticMetadata[key]?.let {
|
||||
return it
|
||||
}
|
||||
return dynamicMetadata[key]
|
||||
}
|
||||
|
||||
fun staticMetadata(): Map<MetadataId, Metadata> {
|
||||
val metadata: MutableMap<MetadataId, Metadata> = mutableMapOf()
|
||||
staticMetadata.forEach { entry -> metadata[entry.value.id] = entry.value }
|
||||
return metadata
|
||||
}
|
||||
|
||||
fun dynamicMetadata(): Map<MetadataId, Metadata> {
|
||||
val metadata: MutableMap<MetadataId, Metadata> = mutableMapOf()
|
||||
dynamicMetadata.forEach { entry ->
|
||||
if (!queried.contains(entry.value.id)) {
|
||||
metadata[entry.value.id] = entry.value
|
||||
queried.add(entry.value.id)
|
||||
}
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
queried.clear()
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ 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
|
||||
|
||||
/*
|
||||
Descriptors are an extension point used during traversal to extract data out of arbitrary
|
||||
@@ -19,8 +20,6 @@ import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||
Descriptors should be stateless and each descriptor should be a singleton
|
||||
*/
|
||||
|
||||
typealias SectionName = String
|
||||
|
||||
object BaseTags {
|
||||
|
||||
const val Declarative = "Declarative"
|
||||
@@ -63,7 +62,7 @@ interface NodeDescriptor<T> {
|
||||
* Get the data to show for this node in the sidebar of the inspector. The object will be shown in
|
||||
* order and with a header matching the given name.
|
||||
*/
|
||||
fun getData(node: T): Map<SectionName, InspectableObject>
|
||||
fun getData(node: T): Map<MetadataId, InspectableObject>
|
||||
|
||||
/**
|
||||
* Set of tags to describe this node in an abstract way for the UI Unfortunately this can't be an
|
||||
|
||||
@@ -10,6 +10,7 @@ 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
|
||||
|
||||
object ObjectDescriptor : NodeDescriptor<Any> {
|
||||
|
||||
@@ -21,7 +22,7 @@ object ObjectDescriptor : NodeDescriptor<Any> {
|
||||
|
||||
override fun getChildren(node: Any) = listOf<Any>()
|
||||
|
||||
override fun getData(node: Any) = mutableMapOf<SectionName, InspectableObject>()
|
||||
override fun getData(node: Any) = mutableMapOf<MetadataId, InspectableObject>()
|
||||
|
||||
override fun getBounds(node: Any): Bounds? = null
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ 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
|
||||
|
||||
/** a drawable or view that is mounted, along with the correct descriptor */
|
||||
class OffsetChild(val child: Any, val descriptor: NodeDescriptor<Any>, val x: Int, val y: Int) {
|
||||
@@ -34,7 +35,7 @@ object OffsetChildDescriptor : NodeDescriptor<OffsetChild> {
|
||||
|
||||
override fun getActiveChild(node: OffsetChild): Any? = node.descriptor.getActiveChild(node.child)
|
||||
|
||||
override fun getData(node: OffsetChild): Map<SectionName, InspectableObject> =
|
||||
override fun getData(node: OffsetChild): Map<MetadataId, InspectableObject> =
|
||||
node.descriptor.getData(node.child)
|
||||
|
||||
override fun getTags(node: OffsetChild): Set<String> = node.descriptor.getTags(node.child)
|
||||
|
||||
@@ -13,45 +13,73 @@ import com.facebook.flipper.plugins.uidebugger.model.Color
|
||||
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||
|
||||
object TextViewDescriptor : ChainedDescriptor<TextView>() {
|
||||
|
||||
private const val NAMESPACE = "TextView"
|
||||
|
||||
private var SectionId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE)
|
||||
private val TextAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "text")
|
||||
private val TextSizeAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "textSize")
|
||||
private val TextColorAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "textColor")
|
||||
private val IsBoldAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "isBold")
|
||||
private val IsItalicAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "isItalic")
|
||||
private val WeightAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "weight")
|
||||
private val TypefaceAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "typeface")
|
||||
private val MinLinesAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "minLines")
|
||||
private val MaxLinesAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "maxLines")
|
||||
private val MinWidthAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "minWidth")
|
||||
private val MaxWidthAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "maxWidth")
|
||||
|
||||
override fun onGetName(node: TextView): String = node.javaClass.simpleName
|
||||
|
||||
override fun onGetData(
|
||||
node: TextView,
|
||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
||||
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||
) {
|
||||
|
||||
val props =
|
||||
mutableMapOf<String, Inspectable>(
|
||||
"text" to InspectableValue.Text(node.text.toString(), false),
|
||||
"textSize" to InspectableValue.Number(node.textSize, false),
|
||||
"textColor" to
|
||||
InspectableValue.Color(Color.fromColor(node.textColors.defaultColor), false))
|
||||
mutableMapOf<Int, Inspectable>(
|
||||
TextAttributeId to InspectableValue.Text(node.text.toString()),
|
||||
TextSizeAttributeId to InspectableValue.Number(node.textSize),
|
||||
TextColorAttributeId to
|
||||
InspectableValue.Color(Color.fromColor(node.textColors.defaultColor)))
|
||||
|
||||
val typeface = node.typeface
|
||||
if (typeface != null) {
|
||||
val typeFaceProp =
|
||||
mutableMapOf<String, InspectableValue>(
|
||||
"isBold" to InspectableValue.Boolean(typeface.isBold, false),
|
||||
"isItalic" to InspectableValue.Boolean(typeface.isItalic, false),
|
||||
mutableMapOf<Int, InspectableValue>(
|
||||
IsBoldAttributeId to InspectableValue.Boolean(typeface.isBold),
|
||||
IsItalicAttributeId to InspectableValue.Boolean(typeface.isItalic),
|
||||
)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
typeFaceProp["weight"] = InspectableValue.Number(typeface.weight, false)
|
||||
typeFaceProp[WeightAttributeId] = InspectableValue.Number(typeface.weight)
|
||||
}
|
||||
|
||||
props["typeface"] = InspectableObject(typeFaceProp)
|
||||
props[TypefaceAttributeId] = InspectableObject(typeFaceProp)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
props["minLines"] = InspectableValue.Number(node.minLines, false)
|
||||
props["maxLines"] = InspectableValue.Number(node.maxLines, false)
|
||||
props["minWidth"] = InspectableValue.Number(node.minWidth, false)
|
||||
props["maxWidth"] = InspectableValue.Number(node.maxWidth, false)
|
||||
props[MinLinesAttributeId] = InspectableValue.Number(node.minLines)
|
||||
props[MaxLinesAttributeId] = InspectableValue.Number(node.maxLines)
|
||||
props[MinWidthAttributeId] = InspectableValue.Number(node.minWidth)
|
||||
props[MaxWidthAttributeId] = InspectableValue.Number(node.maxWidth)
|
||||
}
|
||||
|
||||
attributeSections["TextView"] = InspectableObject(props)
|
||||
attributeSections[SectionId] = InspectableObject(props)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,15 +23,86 @@ import android.widget.LinearLayout
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.facebook.flipper.plugins.uidebugger.common.*
|
||||
import com.facebook.flipper.plugins.uidebugger.common.BitmapPool
|
||||
import com.facebook.flipper.plugins.uidebugger.common.EnumMapping
|
||||
import com.facebook.flipper.plugins.uidebugger.model.*
|
||||
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
||||
import com.facebook.flipper.plugins.uidebugger.util.ResourcesUtil
|
||||
import java.lang.reflect.Field
|
||||
|
||||
object ViewDescriptor : ChainedDescriptor<View>() {
|
||||
|
||||
private const val NAMESPACE = "View"
|
||||
|
||||
private var SectionId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE)
|
||||
private val PositionAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "position")
|
||||
private val GlobalPositionAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "globalPosition")
|
||||
private val SizeAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "size")
|
||||
private val BoundsAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "bounds")
|
||||
private val PaddingAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "padding")
|
||||
private val LocalVisibleRectAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "localVisibleRect")
|
||||
private val RotationAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "rotation")
|
||||
private val ScaleAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "scale")
|
||||
private val PivotAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "pivot")
|
||||
private val LayoutParamsAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "layoutParams")
|
||||
private val LayoutDirectionAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "layoutDirection")
|
||||
private val TranslationAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "translation")
|
||||
private val ElevationAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "elevation")
|
||||
private val VisibilityAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "visibility")
|
||||
|
||||
private val BackgroundAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "background")
|
||||
private val ForegroundAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "foreground")
|
||||
|
||||
private val AlphaAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "alpha")
|
||||
private val StateAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "state")
|
||||
|
||||
private val StateEnabledAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "enabled")
|
||||
private val StateActivatedAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "activated")
|
||||
private val StateFocusedAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "focused")
|
||||
private val StateSelectedAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "selected")
|
||||
|
||||
private val TextDirectionAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "textDirection")
|
||||
private val TextAlignmentAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "textAlignment")
|
||||
|
||||
private val TagAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "tag")
|
||||
private val KeyedTagsAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "keyedTags")
|
||||
|
||||
private val WidthAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "width")
|
||||
private val HeightAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "height")
|
||||
|
||||
private val MarginAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "margin")
|
||||
private val WeightAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "weight")
|
||||
private val GravityAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "gravity")
|
||||
|
||||
override fun onGetName(node: View): String = node.javaClass.simpleName
|
||||
|
||||
override fun onGetBounds(node: View): Bounds {
|
||||
@@ -64,12 +135,9 @@ object ViewDescriptor : ChainedDescriptor<View>() {
|
||||
|
||||
override fun onGetTags(node: View): Set<String> = BaseTags.NativeAndroid
|
||||
|
||||
override fun onGetData(
|
||||
node: View,
|
||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
||||
) {
|
||||
override fun onGetData(node: View, attributeSections: MutableMap<MetadataId, InspectableObject>) {
|
||||
|
||||
val props = mutableMapOf<String, Inspectable>()
|
||||
val props = mutableMapOf<Int, Inspectable>()
|
||||
|
||||
val positionOnScreen = IntArray(2)
|
||||
node.getLocationOnScreen(positionOnScreen)
|
||||
@@ -78,80 +146,83 @@ object ViewDescriptor : ChainedDescriptor<View>() {
|
||||
node.getLocalVisibleRect(localVisible)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
props["position"] = InspectableValue.Coordinate3D(Coordinate3D(node.x, node.y, node.z))
|
||||
props[PositionAttributeId] =
|
||||
InspectableValue.Coordinate3D(Coordinate3D(node.x, node.y, node.z))
|
||||
} else {
|
||||
props["position"] = InspectableValue.Coordinate(Coordinate(node.x, node.y))
|
||||
props[PositionAttributeId] = InspectableValue.Coordinate(Coordinate(node.x, node.y))
|
||||
}
|
||||
|
||||
props["globalPosition"] =
|
||||
props[GlobalPositionAttributeId] =
|
||||
InspectableValue.Coordinate(Coordinate(positionOnScreen[0], positionOnScreen[1]))
|
||||
|
||||
props["size"] = InspectableValue.Size(Size(node.width, node.height), mutable = true)
|
||||
props[SizeAttributeId] = InspectableValue.Size(Size(node.width, node.height))
|
||||
|
||||
props["bounds"] = InspectableValue.Bounds(Bounds(node.left, node.top, node.right, node.bottom))
|
||||
props["padding"] =
|
||||
props[BoundsAttributeId] =
|
||||
InspectableValue.Bounds(Bounds(node.left, node.top, node.right, node.bottom))
|
||||
props[PaddingAttributeId] =
|
||||
InspectableValue.SpaceBox(
|
||||
SpaceBox(node.paddingTop, node.paddingRight, node.paddingBottom, node.paddingLeft))
|
||||
|
||||
props["localVisibleRect"] =
|
||||
props[LocalVisibleRectAttributeId] =
|
||||
InspectableObject(
|
||||
mapOf(
|
||||
"position" to
|
||||
PositionAttributeId to
|
||||
InspectableValue.Coordinate(Coordinate(localVisible.left, localVisible.top)),
|
||||
"size" to InspectableValue.Size(Size(localVisible.width(), localVisible.height()))),
|
||||
SizeAttributeId to
|
||||
InspectableValue.Size(Size(localVisible.width(), localVisible.height()))),
|
||||
)
|
||||
|
||||
props["rotation"] =
|
||||
props[RotationAttributeId] =
|
||||
InspectableValue.Coordinate3D(Coordinate3D(node.rotationX, node.rotationY, node.rotation))
|
||||
props["scale"] = InspectableValue.Coordinate(Coordinate(node.scaleX, node.scaleY))
|
||||
props["pivot"] = InspectableValue.Coordinate(Coordinate(node.pivotX, node.pivotY))
|
||||
props[ScaleAttributeId] = InspectableValue.Coordinate(Coordinate(node.scaleX, node.scaleY))
|
||||
props[PivotAttributeId] = InspectableValue.Coordinate(Coordinate(node.pivotX, node.pivotY))
|
||||
|
||||
props["layoutParams"] = getLayoutParams(node)
|
||||
props[LayoutParamsAttributeId] = getLayoutParams(node)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
props["layoutDirection"] = LayoutDirectionMapping.toInspectable(node.layoutDirection, false)
|
||||
props[LayoutDirectionAttributeId] = LayoutDirectionMapping.toInspectable(node.layoutDirection)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
props["translation"] =
|
||||
props[TranslationAttributeId] =
|
||||
InspectableValue.Coordinate3D(
|
||||
Coordinate3D(node.translationX, node.translationY, node.translationZ))
|
||||
} else {
|
||||
props["translation"] =
|
||||
props[TranslationAttributeId] =
|
||||
InspectableValue.Coordinate(Coordinate(node.translationX, node.translationY))
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
props["elevation"] = InspectableValue.Number(node.elevation)
|
||||
props[ElevationAttributeId] = InspectableValue.Number(node.elevation)
|
||||
}
|
||||
|
||||
props["visibility"] = VisibilityMapping.toInspectable(node.visibility, mutable = false)
|
||||
props[VisibilityAttributeId] = VisibilityMapping.toInspectable(node.visibility)
|
||||
|
||||
fromDrawable(node.background)?.let { background -> props["background"] = background }
|
||||
fromDrawable(node.background)?.let { background -> props[BackgroundAttributeId] = background }
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
fromDrawable(node.foreground)?.let { foreground -> props["foreground"] = foreground }
|
||||
fromDrawable(node.foreground)?.let { foreground -> props[ForegroundAttributeId] = foreground }
|
||||
}
|
||||
|
||||
props["alpha"] = InspectableValue.Number(node.alpha, mutable = true)
|
||||
props["state"] =
|
||||
props[AlphaAttributeId] = InspectableValue.Number(node.alpha)
|
||||
props[StateAttributeId] =
|
||||
InspectableObject(
|
||||
mapOf(
|
||||
"enabled" to InspectableValue.Boolean(node.isEnabled, mutable = false),
|
||||
"activated" to InspectableValue.Boolean(node.isActivated, mutable = false),
|
||||
"focused" to InspectableValue.Boolean(node.isFocused, mutable = false),
|
||||
"selected" to InspectableValue.Boolean(node.isSelected, mutable = false)))
|
||||
StateEnabledAttributeId to InspectableValue.Boolean(node.isEnabled),
|
||||
StateActivatedAttributeId to InspectableValue.Boolean(node.isActivated),
|
||||
StateFocusedAttributeId to InspectableValue.Boolean(node.isFocused),
|
||||
StateSelectedAttributeId to InspectableValue.Boolean(node.isSelected)))
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
props["textDirection"] = TextDirectionMapping.toInspectable(node.textDirection, false)
|
||||
props["textAlignment"] = TextAlignmentMapping.toInspectable(node.textAlignment, false)
|
||||
props[TextDirectionAttributeId] = TextDirectionMapping.toInspectable(node.textDirection)
|
||||
props[TextAlignmentAttributeId] = TextAlignmentMapping.toInspectable(node.textAlignment)
|
||||
}
|
||||
|
||||
node.tag
|
||||
?.let { InspectableValue.fromAny(it, mutable = false) }
|
||||
?.let { tag -> props.put("tag", tag) }
|
||||
?.let { tag -> props.put(TagAttributeId, tag) }
|
||||
|
||||
props["keyedTags"] = InspectableObject(getViewTags(node))
|
||||
props[KeyedTagsAttributeId] = InspectableObject(getViewTags(node))
|
||||
|
||||
attributeSections["View"] = InspectableObject(props.toMap())
|
||||
attributeSections[SectionId] = InspectableObject(props.toMap())
|
||||
}
|
||||
|
||||
override fun onGetSnapshot(node: View, bitmap: Bitmap?): Bitmap? {
|
||||
@@ -180,19 +251,19 @@ object ViewDescriptor : ChainedDescriptor<View>() {
|
||||
|
||||
private fun fromDrawable(d: Drawable?): Inspectable? {
|
||||
return if (d is ColorDrawable) {
|
||||
InspectableValue.Color(Color.fromColor(d.color), mutable = false)
|
||||
InspectableValue.Color(Color.fromColor(d.color))
|
||||
} else null
|
||||
}
|
||||
|
||||
private fun getLayoutParams(node: View): InspectableObject {
|
||||
val layoutParams = node.layoutParams
|
||||
|
||||
val params = mutableMapOf<String, Inspectable>()
|
||||
params["width"] = LayoutParamsMapping.toInspectable(layoutParams.width, mutable = true)
|
||||
params["height"] = LayoutParamsMapping.toInspectable(layoutParams.height, mutable = true)
|
||||
val params = mutableMapOf<Int, Inspectable>()
|
||||
params[WidthAttributeId] = LayoutParamsMapping.toInspectable(layoutParams.width)
|
||||
params[HeightAttributeId] = LayoutParamsMapping.toInspectable(layoutParams.height)
|
||||
|
||||
if (layoutParams is ViewGroup.MarginLayoutParams) {
|
||||
params["margin"] =
|
||||
params[MarginAttributeId] =
|
||||
InspectableValue.SpaceBox(
|
||||
SpaceBox(
|
||||
layoutParams.topMargin,
|
||||
@@ -201,17 +272,17 @@ object ViewDescriptor : ChainedDescriptor<View>() {
|
||||
layoutParams.leftMargin))
|
||||
}
|
||||
if (layoutParams is FrameLayout.LayoutParams) {
|
||||
params["gravity"] = GravityMapping.toInspectable(layoutParams.gravity, mutable = true)
|
||||
params[GravityAttributeId] = GravityMapping.toInspectable(layoutParams.gravity)
|
||||
}
|
||||
if (layoutParams is LinearLayout.LayoutParams) {
|
||||
params["weight"] = InspectableValue.Number(layoutParams.weight, mutable = true)
|
||||
params["gravity"] = GravityMapping.toInspectable(layoutParams.gravity, mutable = true)
|
||||
params[WeightAttributeId] = InspectableValue.Number(layoutParams.weight)
|
||||
params[GravityAttributeId] = GravityMapping.toInspectable(layoutParams.gravity)
|
||||
}
|
||||
return InspectableObject(params)
|
||||
}
|
||||
|
||||
private fun getViewTags(node: View): MutableMap<String, Inspectable> {
|
||||
val tags = mutableMapOf<String, Inspectable>()
|
||||
private fun getViewTags(node: View): MutableMap<Int, Inspectable> {
|
||||
val tags = mutableMapOf<Int, Inspectable>()
|
||||
|
||||
KeyedTagsField?.let { field ->
|
||||
val keyedTags = field.get(node) as SparseArray<*>?
|
||||
@@ -224,7 +295,13 @@ object ViewDescriptor : ChainedDescriptor<View>() {
|
||||
keyedTags
|
||||
.valueAt(i)
|
||||
?.let { InspectableValue.fromAny(it, false) }
|
||||
?.let { tags.put(id, it) }
|
||||
?.let {
|
||||
val metadata = MetadataRegister.get(NAMESPACE, id)
|
||||
val identifier =
|
||||
metadata?.id
|
||||
?: MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, id)
|
||||
tags.put(identifier, it)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ import com.facebook.flipper.plugins.uidebugger.model.*
|
||||
|
||||
object ViewGroupDescriptor : ChainedDescriptor<ViewGroup>() {
|
||||
|
||||
private const val NAMESPACE = "ViewGroup"
|
||||
private var SectionId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE)
|
||||
override fun onGetName(node: ViewGroup): String {
|
||||
return node.javaClass.simpleName
|
||||
}
|
||||
@@ -37,21 +40,28 @@ object ViewGroupDescriptor : ChainedDescriptor<ViewGroup>() {
|
||||
return children
|
||||
}
|
||||
|
||||
private val LayoutModeAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "layoutMode")
|
||||
private val ClipChildrenAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "layoutMode")
|
||||
private val ClipToPaddingAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "clipToPadding")
|
||||
|
||||
override fun onGetData(
|
||||
node: ViewGroup,
|
||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
||||
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||
) {
|
||||
val viewGroupAttrs = mutableMapOf<String, Inspectable>()
|
||||
val props = mutableMapOf<Int, Inspectable>()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
viewGroupAttrs["LayoutMode"] = LayoutModeMapping.toInspectable(node.layoutMode, true)
|
||||
viewGroupAttrs["ClipChildren"] = InspectableValue.Boolean(node.clipChildren, true)
|
||||
props[LayoutModeAttributeId] = LayoutModeMapping.toInspectable(node.layoutMode)
|
||||
props[ClipChildrenAttributeId] = InspectableValue.Boolean(node.clipChildren)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
viewGroupAttrs["ClipToPadding"] = InspectableValue.Boolean(node.clipToPadding, true)
|
||||
props[ClipToPaddingAttributeId] = InspectableValue.Boolean(node.clipToPadding)
|
||||
}
|
||||
|
||||
attributeSections["ViewGroup"] = InspectableObject(viewGroupAttrs)
|
||||
attributeSections[SectionId] = InspectableObject(props)
|
||||
}
|
||||
|
||||
private val LayoutModeMapping: EnumMapping<Int> =
|
||||
|
||||
@@ -11,9 +11,13 @@ import androidx.viewpager.widget.ViewPager
|
||||
import com.facebook.flipper.plugins.uidebugger.core.FragmentTracker
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||
|
||||
object ViewPagerDescriptor : ChainedDescriptor<ViewPager>() {
|
||||
|
||||
private const val NAMESPACE = "ViewPager"
|
||||
private var SectionId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE)
|
||||
override fun onGetName(node: ViewPager): String = node.javaClass.simpleName
|
||||
|
||||
override fun onGetActiveChild(node: ViewPager): Any? {
|
||||
@@ -25,14 +29,16 @@ object ViewPagerDescriptor : ChainedDescriptor<ViewPager>() {
|
||||
return child
|
||||
}
|
||||
|
||||
private val CurrentItemIndexAttributeId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "currentItemIndex")
|
||||
override fun onGetData(
|
||||
node: ViewPager,
|
||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
||||
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||
) {
|
||||
val props =
|
||||
InspectableObject(
|
||||
mapOf("currentItemIndex" to InspectableValue.Number(node.currentItem, false)))
|
||||
mapOf(CurrentItemIndexAttributeId to InspectableValue.Number(node.currentItem)))
|
||||
|
||||
attributeSections["ViewPager"] = props
|
||||
attributeSections[SectionId] = props
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,14 @@ import com.facebook.flipper.plugins.uidebugger.model.Color
|
||||
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||
import java.lang.reflect.Field
|
||||
|
||||
object WindowDescriptor : ChainedDescriptor<Window>() {
|
||||
|
||||
private const val NAMESPACE = "Window"
|
||||
private var SectionId =
|
||||
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE)
|
||||
private var internalRStyleableClass: Class<*>? = null
|
||||
private var internalRStyleableFields: Array<Field>? = null
|
||||
private var internalRStyleableWindowField: Field? = null
|
||||
@@ -32,7 +36,7 @@ object WindowDescriptor : ChainedDescriptor<Window>() {
|
||||
@SuppressLint("PrivateApi")
|
||||
override fun onGetData(
|
||||
node: Window,
|
||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
||||
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||
) {
|
||||
try {
|
||||
if (internalRStyleableClass == null) {
|
||||
@@ -58,7 +62,7 @@ object WindowDescriptor : ChainedDescriptor<Window>() {
|
||||
}
|
||||
}
|
||||
|
||||
val props = mutableMapOf<String, Inspectable>()
|
||||
val props = mutableMapOf<Int, Inspectable>()
|
||||
|
||||
val typedValue = TypedValue()
|
||||
for ((index, attr) in windowStyleable.withIndex()) {
|
||||
@@ -68,20 +72,28 @@ object WindowDescriptor : ChainedDescriptor<Window>() {
|
||||
// Strip 'Windows_' (length: 7) from the name.
|
||||
val name = fieldName.substring(7)
|
||||
|
||||
val metadata = MetadataRegister.get(NAMESPACE, name)
|
||||
val identifier =
|
||||
metadata?.id
|
||||
?: MetadataRegister.registerDynamic(
|
||||
MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, name)
|
||||
|
||||
when (typedValue.type) {
|
||||
TypedValue.TYPE_STRING ->
|
||||
props[name] = InspectableValue.Text(typedValue.string.toString())
|
||||
props[identifier] = InspectableValue.Text(typedValue.string.toString())
|
||||
TypedValue.TYPE_INT_BOOLEAN ->
|
||||
props[name] = InspectableValue.Boolean(typedValue.data != 0)
|
||||
props[identifier] = InspectableValue.Boolean(typedValue.data != 0)
|
||||
TypedValue.TYPE_INT_HEX ->
|
||||
props[name] = InspectableValue.Text("0x" + Integer.toHexString(typedValue.data))
|
||||
props[identifier] =
|
||||
InspectableValue.Text("0x" + Integer.toHexString(typedValue.data))
|
||||
TypedValue.TYPE_FLOAT ->
|
||||
props[name] =
|
||||
props[identifier] =
|
||||
InspectableValue.Number(java.lang.Float.intBitsToFloat(typedValue.data))
|
||||
TypedValue.TYPE_REFERENCE -> {
|
||||
val resId = typedValue.data
|
||||
if (resId != 0) {
|
||||
props[name] = InspectableValue.Text(node.context.resources.getResourceName(resId))
|
||||
props[identifier] =
|
||||
InspectableValue.Text(node.context.resources.getResourceName(resId))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
@@ -90,18 +102,18 @@ object WindowDescriptor : ChainedDescriptor<Window>() {
|
||||
try {
|
||||
val hexColor = "#" + Integer.toHexString(typedValue.data)
|
||||
val color = android.graphics.Color.parseColor(hexColor)
|
||||
props[name] = InspectableValue.Color(Color.fromColor(color))
|
||||
props[identifier] = InspectableValue.Color(Color.fromColor(color))
|
||||
} catch (e: Exception) {}
|
||||
} else if (typedValue.type >= TypedValue.TYPE_FIRST_INT &&
|
||||
typedValue.type <= TypedValue.TYPE_LAST_INT) {
|
||||
props[name] = InspectableValue.Number(typedValue.data)
|
||||
props[identifier] = InspectableValue.Number(typedValue.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attributeSections["Window"] = InspectableObject(props.toMap())
|
||||
attributeSections[SectionId] = InspectableObject(props.toMap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,21 @@ package com.facebook.flipper.plugins.uidebugger.model
|
||||
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
data class InitEvent(val rootId: Id) {
|
||||
data class InitEvent(
|
||||
val rootId: Id,
|
||||
) {
|
||||
companion object {
|
||||
const val name = "init"
|
||||
}
|
||||
}
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
data class MetadataUpdateEvent(val attributeMetadata: Map<MetadataId, Metadata> = emptyMap()) {
|
||||
companion object {
|
||||
const val name = "metadataUpdate"
|
||||
}
|
||||
}
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
data class SubtreeUpdateEvent(
|
||||
val txId: Long,
|
||||
|
||||
@@ -16,93 +16,77 @@ import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
sealed class Inspectable {
|
||||
abstract val mutable: kotlin.Boolean
|
||||
}
|
||||
@kotlinx.serialization.Serializable sealed class Inspectable {}
|
||||
|
||||
// In this context, mutable means you can add/remove items,
|
||||
// for native android this should probably be false.
|
||||
@SerialName("array")
|
||||
@Serializable
|
||||
data class InspectableArray(val items: List<Inspectable>, override val mutable: Boolean = false) :
|
||||
Inspectable()
|
||||
data class InspectableArray(val id: Int, val items: List<Inspectable>) : Inspectable()
|
||||
|
||||
// In this context, mutable means you can add / remove keys,
|
||||
// for native android this should probably be false.
|
||||
@SerialName("object")
|
||||
@Serializable
|
||||
data class InspectableObject(
|
||||
val fields: Map<String, Inspectable>,
|
||||
override val mutable: Boolean = false
|
||||
) : Inspectable()
|
||||
data class InspectableObject(val fields: Map<Int, Inspectable>) : Inspectable()
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
sealed class InspectableValue : Inspectable() {
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
@SerialName("text")
|
||||
class Text(val value: String, override val mutable: kotlin.Boolean = false) : InspectableValue()
|
||||
class Text(val value: String) : InspectableValue()
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
@SerialName("boolean")
|
||||
class Boolean(val value: kotlin.Boolean, override val mutable: kotlin.Boolean = false) :
|
||||
InspectableValue()
|
||||
class Boolean(val value: kotlin.Boolean) : InspectableValue()
|
||||
|
||||
@SerialName("number")
|
||||
@kotlinx.serialization.Serializable
|
||||
data class Number(
|
||||
@Serializable(with = NumberSerializer::class) val value: kotlin.Number,
|
||||
override val mutable: kotlin.Boolean = false
|
||||
) : InspectableValue()
|
||||
|
||||
@SerialName("color")
|
||||
@kotlinx.serialization.Serializable
|
||||
data class Color(
|
||||
val value: com.facebook.flipper.plugins.uidebugger.model.Color,
|
||||
override val mutable: kotlin.Boolean = false
|
||||
) : InspectableValue()
|
||||
|
||||
@SerialName("coordinate")
|
||||
@kotlinx.serialization.Serializable
|
||||
data class Coordinate(
|
||||
val value: com.facebook.flipper.plugins.uidebugger.model.Coordinate,
|
||||
override val mutable: kotlin.Boolean = false
|
||||
) : InspectableValue()
|
||||
|
||||
@SerialName("coordinate3d")
|
||||
@kotlinx.serialization.Serializable
|
||||
data class Coordinate3D(
|
||||
val value: com.facebook.flipper.plugins.uidebugger.model.Coordinate3D,
|
||||
override val mutable: kotlin.Boolean = false
|
||||
) : InspectableValue()
|
||||
|
||||
@SerialName("size")
|
||||
@kotlinx.serialization.Serializable
|
||||
data class Size(
|
||||
val value: com.facebook.flipper.plugins.uidebugger.model.Size,
|
||||
override val mutable: kotlin.Boolean = false
|
||||
) : InspectableValue()
|
||||
|
||||
@SerialName("bounds")
|
||||
@kotlinx.serialization.Serializable
|
||||
data class Bounds(
|
||||
val value: com.facebook.flipper.plugins.uidebugger.model.Bounds,
|
||||
override val mutable: kotlin.Boolean = false
|
||||
) : InspectableValue()
|
||||
|
||||
@SerialName("space")
|
||||
@kotlinx.serialization.Serializable
|
||||
data class SpaceBox(
|
||||
val value: com.facebook.flipper.plugins.uidebugger.model.SpaceBox,
|
||||
override val mutable: kotlin.Boolean = false
|
||||
) : InspectableValue()
|
||||
|
||||
@SerialName("enum")
|
||||
@kotlinx.serialization.Serializable
|
||||
data class Enum(
|
||||
val value: com.facebook.flipper.plugins.uidebugger.model.Enumeration,
|
||||
override val mutable: kotlin.Boolean = false
|
||||
) : InspectableValue()
|
||||
|
||||
companion object {
|
||||
@@ -112,9 +96,9 @@ sealed class InspectableValue : Inspectable() {
|
||||
*/
|
||||
fun fromAny(any: Any, mutable: kotlin.Boolean = false): Inspectable? {
|
||||
return when (any) {
|
||||
is kotlin.Number -> InspectableValue.Number(any, mutable)
|
||||
is kotlin.Boolean -> InspectableValue.Boolean(any, mutable)
|
||||
is kotlin.String -> InspectableValue.Text(any, mutable)
|
||||
is kotlin.Number -> InspectableValue.Number(any)
|
||||
is kotlin.Boolean -> InspectableValue.Boolean(any)
|
||||
is kotlin.String -> InspectableValue.Text(any)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.model
|
||||
|
||||
typealias MetadataId = Int
|
||||
|
||||
/**
|
||||
* Represent metadata associated for an attribute. MetadataId is a unique identifier used by
|
||||
* attributes to refer to its metadata. Type refers to attribute semantics. It can represent:
|
||||
* identity, attributes, layout, documentation, or a custom type.
|
||||
*/
|
||||
@kotlinx.serialization.Serializable
|
||||
data class Metadata(
|
||||
val id: MetadataId,
|
||||
val type: String,
|
||||
val namespace: String,
|
||||
val name: String,
|
||||
val mutable: kotlin.Boolean,
|
||||
val tags: List<String>? = emptyList()
|
||||
) {}
|
||||
@@ -13,7 +13,7 @@ import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
||||
data class Node(
|
||||
val id: Id,
|
||||
val name: String,
|
||||
val attributes: Map<String, InspectableObject>,
|
||||
val attributes: Map<MetadataId, InspectableObject>,
|
||||
val bounds: Bounds?,
|
||||
val tags: Set<String>,
|
||||
val children: List<Id>,
|
||||
|
||||
@@ -16,8 +16,10 @@ import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||
import com.facebook.flipper.plugins.uidebugger.common.BitmapPool
|
||||
import com.facebook.flipper.plugins.uidebugger.core.Context
|
||||
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
||||
import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister
|
||||
import com.facebook.flipper.plugins.uidebugger.model.Coordinate
|
||||
import com.facebook.flipper.plugins.uidebugger.model.CoordinateUpdateEvent
|
||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataUpdateEvent
|
||||
import com.facebook.flipper.plugins.uidebugger.model.Node
|
||||
import com.facebook.flipper.plugins.uidebugger.model.PerfStatsEvent
|
||||
import com.facebook.flipper.plugins.uidebugger.model.SubtreeUpdateEvent
|
||||
@@ -69,9 +71,7 @@ class TreeObserverManager(val context: Context) {
|
||||
workerScope.launch {
|
||||
while (isActive) {
|
||||
try {
|
||||
|
||||
val update = updates.receive()
|
||||
when (update) {
|
||||
when (val update = updates.receive()) {
|
||||
is SubtreeUpdate -> sendSubtreeUpdate(update)
|
||||
is CoordinateUpdate -> {
|
||||
val event =
|
||||
@@ -88,11 +88,22 @@ class TreeObserverManager(val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
fun sendSubtreeUpdate(treeUpdate: SubtreeUpdate) {
|
||||
private fun sendMetadata() {
|
||||
val metadata = MetadataRegister.dynamicMetadata()
|
||||
if (metadata.size > 0) {
|
||||
context.connectionRef.connection?.send(
|
||||
MetadataUpdateEvent.name,
|
||||
Json.encodeToString(MetadataUpdateEvent.serializer(), MetadataUpdateEvent(metadata)))
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendSubtreeUpdate(treeUpdate: SubtreeUpdate) {
|
||||
val onWorkerThread = System.currentTimeMillis()
|
||||
val txId = txId.getAndIncrement().toLong()
|
||||
|
||||
var serialized: String?
|
||||
sendMetadata()
|
||||
|
||||
val serialized: String?
|
||||
if (treeUpdate.snapshot == null) {
|
||||
serialized =
|
||||
Json.encodeToString(
|
||||
|
||||
@@ -39,9 +39,7 @@ class EnumMappingTest {
|
||||
@Test
|
||||
fun testTurnsIntoEnumInspectable() {
|
||||
assertThat(
|
||||
visibility.toInspectable(View.GONE, true),
|
||||
equalTo(
|
||||
InspectableValue.Enum(
|
||||
Enumeration(setOf("VISIBLE", "INVISIBLE", "GONE"), "GONE"), mutable = true)))
|
||||
visibility.toInspectable(View.GONE),
|
||||
equalTo(InspectableValue.Enum(Enumeration(setOf("VISIBLE", "INVISIBLE", "GONE"), "GONE"))))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user