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
@@ -8,16 +8,15 @@
|
|||||||
package com.facebook.flipper.plugins.uidebugger.litho.descriptors
|
package com.facebook.flipper.plugins.uidebugger.litho.descriptors
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.BaseTags
|
import com.facebook.flipper.plugins.uidebugger.descriptors.*
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.OffsetChild
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.litho.LithoTag
|
import com.facebook.flipper.plugins.uidebugger.litho.LithoTag
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
import com.facebook.litho.DebugComponent
|
import com.facebook.litho.DebugComponent
|
||||||
|
|
||||||
class DebugComponentDescriptor(val register: DescriptorRegister) : NodeDescriptor<DebugComponent> {
|
class DebugComponentDescriptor(val register: DescriptorRegister) : NodeDescriptor<DebugComponent> {
|
||||||
|
private val NAMESPACE = "DebugComponent"
|
||||||
|
|
||||||
override fun getName(node: DebugComponent): String {
|
override fun getName(node: DebugComponent): String {
|
||||||
return node.component.simpleName
|
return node.component.simpleName
|
||||||
@@ -54,7 +53,15 @@ class DebugComponentDescriptor(val register: DescriptorRegister) : NodeDescripto
|
|||||||
|
|
||||||
override fun getActiveChild(node: DebugComponent): Any? = null
|
override fun getActiveChild(node: DebugComponent): Any? = null
|
||||||
|
|
||||||
override fun getData(node: DebugComponent) = mapOf<String, InspectableObject>()
|
private val LayoutId =
|
||||||
|
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "Litho Layout")
|
||||||
|
private val UserPropsId =
|
||||||
|
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "User Props")
|
||||||
|
override fun getData(node: DebugComponent): Map<MetadataId, InspectableObject> {
|
||||||
|
val attributeSections = mutableMapOf<MetadataId, InspectableObject>()
|
||||||
|
|
||||||
|
return attributeSections
|
||||||
|
}
|
||||||
|
|
||||||
override fun getBounds(node: DebugComponent): Bounds =
|
override fun getBounds(node: DebugComponent): Bounds =
|
||||||
Bounds.fromRect(node.boundsInParentDebugComponent)
|
Bounds.fromRect(node.boundsInParentDebugComponent)
|
||||||
|
|||||||
@@ -8,15 +8,19 @@
|
|||||||
package com.facebook.flipper.plugins.uidebugger.litho.descriptors
|
package com.facebook.flipper.plugins.uidebugger.litho.descriptors
|
||||||
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.ChainedDescriptor
|
import com.facebook.flipper.plugins.uidebugger.descriptors.ChainedDescriptor
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.SectionName
|
import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
import com.facebook.litho.DebugComponent
|
import com.facebook.litho.DebugComponent
|
||||||
import com.facebook.litho.LithoView
|
import com.facebook.litho.LithoView
|
||||||
|
|
||||||
object LithoViewDescriptor : ChainedDescriptor<LithoView>() {
|
object LithoViewDescriptor : ChainedDescriptor<LithoView>() {
|
||||||
|
|
||||||
|
private const val NAMESPACE = "LithoView"
|
||||||
|
private val SectionId =
|
||||||
|
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE)
|
||||||
|
|
||||||
override fun onGetName(node: LithoView): String = node.javaClass.simpleName
|
override fun onGetName(node: LithoView): String = node.javaClass.simpleName
|
||||||
|
|
||||||
override fun onGetChildren(node: LithoView): List<Any> {
|
override fun onGetChildren(node: LithoView): List<Any> {
|
||||||
@@ -28,14 +32,18 @@ object LithoViewDescriptor : ChainedDescriptor<LithoView>() {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val IsIncrementalMountEnabledAttributeId =
|
||||||
|
MetadataRegister.register(
|
||||||
|
MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "isIncrementalMountEnabled")
|
||||||
|
|
||||||
override fun onGetData(
|
override fun onGetData(
|
||||||
node: LithoView,
|
node: LithoView,
|
||||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||||
) {
|
) {
|
||||||
attributeSections["Litho"] =
|
attributeSections[SectionId] =
|
||||||
InspectableObject(
|
InspectableObject(
|
||||||
mapOf<String, Inspectable>(
|
mapOf(
|
||||||
"isIncrementalMountEnabled" to
|
IsIncrementalMountEnabledAttributeId to
|
||||||
InspectableValue.Boolean(node.isIncrementalMountEnabled, false)))
|
InspectableValue.Boolean(node.isIncrementalMountEnabled)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import android.graphics.Bitmap
|
|||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.*
|
import com.facebook.flipper.plugins.uidebugger.descriptors.*
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
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 */
|
/** a drawable or view that is mounted, along with the correct descriptor */
|
||||||
class MountedObject(val obj: Any, val descriptor: NodeDescriptor<Any>)
|
class MountedObject(val obj: Any, val descriptor: NodeDescriptor<Any>)
|
||||||
@@ -37,7 +38,7 @@ object MountedObjectDescriptor : NodeDescriptor<MountedObject> {
|
|||||||
|
|
||||||
override fun getActiveChild(node: MountedObject): Any? = node.descriptor.getActiveChild(node.obj)
|
override fun getActiveChild(node: MountedObject): Any? = node.descriptor.getActiveChild(node.obj)
|
||||||
|
|
||||||
override fun getData(node: MountedObject): Map<SectionName, InspectableObject> =
|
override fun getData(node: MountedObject): Map<MetadataId, InspectableObject> =
|
||||||
node.descriptor.getData(node.obj)
|
node.descriptor.getData(node.obj)
|
||||||
|
|
||||||
override fun getTags(node: MountedObject): Set<String> = node.descriptor.getTags(node.obj)
|
override fun getTags(node: MountedObject): Set<String> = node.descriptor.getTags(node.obj)
|
||||||
|
|||||||
@@ -8,23 +8,30 @@
|
|||||||
package com.facebook.flipper.plugins.uidebugger.litho.descriptors
|
package com.facebook.flipper.plugins.uidebugger.litho.descriptors
|
||||||
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.ChainedDescriptor
|
import com.facebook.flipper.plugins.uidebugger.descriptors.ChainedDescriptor
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.SectionName
|
import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
import com.facebook.litho.widget.TextDrawable
|
import com.facebook.litho.widget.TextDrawable
|
||||||
|
|
||||||
object TextDrawableDescriptor : ChainedDescriptor<TextDrawable>() {
|
object TextDrawableDescriptor : ChainedDescriptor<TextDrawable>() {
|
||||||
|
|
||||||
|
private const val NAMESPACE = "TextDrawable"
|
||||||
|
private val SectionId =
|
||||||
|
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE)
|
||||||
|
|
||||||
override fun onGetName(node: TextDrawable): String = node.javaClass.simpleName
|
override fun onGetName(node: TextDrawable): String = node.javaClass.simpleName
|
||||||
|
|
||||||
|
private val TextAttributeId =
|
||||||
|
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "text")
|
||||||
override fun onGetData(
|
override fun onGetData(
|
||||||
node: TextDrawable,
|
node: TextDrawable,
|
||||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val props =
|
val props =
|
||||||
mapOf<String, Inspectable>("text" to InspectableValue.Text(node.text.toString(), false))
|
mapOf<Int, Inspectable>(TextAttributeId to InspectableValue.Text(node.text.toString()))
|
||||||
|
|
||||||
attributeSections["TextDrawable"] = InspectableObject(props)
|
attributeSections[SectionId] = InspectableObject(props)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ import com.facebook.flipper.core.FlipperConnection
|
|||||||
import com.facebook.flipper.core.FlipperPlugin
|
import com.facebook.flipper.core.FlipperPlugin
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.*
|
import com.facebook.flipper.plugins.uidebugger.core.*
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
|
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.descriptors.nodeId
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InitEvent
|
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 com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
@@ -51,6 +53,12 @@ class UIDebuggerFlipperPlugin(
|
|||||||
InitEvent.name,
|
InitEvent.name,
|
||||||
Json.encodeToString(InitEvent.serializer(), InitEvent(context.applicationRef.nodeId())))
|
Json.encodeToString(InitEvent.serializer(), InitEvent(context.applicationRef.nodeId())))
|
||||||
|
|
||||||
|
connection.send(
|
||||||
|
MetadataUpdateEvent.name,
|
||||||
|
Json.encodeToString(
|
||||||
|
MetadataUpdateEvent.serializer(),
|
||||||
|
MetadataUpdateEvent(MetadataRegister.staticMetadata())))
|
||||||
|
|
||||||
context.treeObserverManager.start()
|
context.treeObserverManager.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +67,8 @@ class UIDebuggerFlipperPlugin(
|
|||||||
this.context.connectionRef.connection = null
|
this.context.connectionRef.connection = null
|
||||||
Log.i(LogTag, "Disconnected")
|
Log.i(LogTag, "Disconnected")
|
||||||
|
|
||||||
|
MetadataRegister.clear()
|
||||||
|
|
||||||
context.treeObserverManager.stop()
|
context.treeObserverManager.stop()
|
||||||
context.bitmapPool.recycleAll()
|
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} ")
|
"Could not convert string $key to enum value, possible values ${mapping.entries} ")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toInspectable(value: T, mutable: Boolean): InspectableValue.Enum {
|
fun toInspectable(value: T): InspectableValue.Enum {
|
||||||
return InspectableValue.Enum(Enumeration(mapping.keys, getStringRepresentation(value)), mutable)
|
return InspectableValue.Enum(Enumeration(mapping.keys, getStringRepresentation(value)))
|
||||||
}
|
}
|
||||||
companion object {
|
companion object {
|
||||||
const val NoMapping = "__UNKNOWN_ENUM_VALUE__"
|
const val NoMapping = "__UNKNOWN_ENUM_VALUE__"
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ package com.facebook.flipper.plugins.uidebugger.core
|
|||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
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.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.Node
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.PerfStatsEvent
|
import com.facebook.flipper.plugins.uidebugger.model.PerfStatsEvent
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.SubtreeUpdateEvent
|
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)
|
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 =
|
val serialized =
|
||||||
Json.encodeToString(
|
Json.encodeToString(
|
||||||
SubtreeUpdateEvent.serializer(),
|
SubtreeUpdateEvent.serializer(),
|
||||||
@@ -79,4 +90,9 @@ class NativeScanScheduler(val context: Context) : Scheduler.Task<ScanResult> {
|
|||||||
socketEnd,
|
socketEnd,
|
||||||
input.nodes.size)))
|
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 android.graphics.Bitmap
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
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
|
* 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
|
open fun onGetChildren(node: T): List<Any>? = null
|
||||||
|
|
||||||
final override fun getData(node: T): Map<SectionName, InspectableObject> {
|
final override fun getData(node: T): Map<MetadataId, InspectableObject> {
|
||||||
val builder = mutableMapOf<String, InspectableObject>()
|
val builder = mutableMapOf<MetadataId, InspectableObject>()
|
||||||
onGetData(node, builder)
|
onGetData(node, builder)
|
||||||
|
|
||||||
var curDescriptor: ChainedDescriptor<T>? = mSuper
|
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
|
* Get the data to show for this node in the sidebar of the inspector. Each key will be a have its
|
||||||
* own section
|
* 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. */
|
/** Get a snapshot of the node. */
|
||||||
final override fun getSnapshot(node: T, bitmap: Bitmap?): Bitmap? {
|
final override fun getSnapshot(node: T, bitmap: Bitmap?): Bitmap? {
|
||||||
|
|||||||
@@ -8,22 +8,25 @@
|
|||||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Color
|
import com.facebook.flipper.plugins.uidebugger.model.*
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
|
||||||
|
|
||||||
object ColorDrawableDescriptor : ChainedDescriptor<ColorDrawable>() {
|
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 onGetName(node: ColorDrawable): String = node.javaClass.simpleName
|
||||||
|
|
||||||
override fun onGetData(
|
override fun onGetData(
|
||||||
node: ColorDrawable,
|
node: ColorDrawable,
|
||||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||||
) {
|
) {
|
||||||
val props = mutableMapOf<String, Inspectable>()
|
val props = mutableMapOf<Int, Inspectable>()
|
||||||
props["color"] = InspectableValue.Color(Color.fromColor(node.color), mutable = true)
|
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.*
|
import com.facebook.flipper.plugins.uidebugger.model.*
|
||||||
|
|
||||||
object DrawableDescriptor : ChainedDescriptor<Drawable>() {
|
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 onGetName(node: Drawable): String = node.javaClass.simpleName
|
||||||
|
|
||||||
override fun onGetBounds(node: Drawable): Bounds =
|
override fun onGetBounds(node: Drawable): Bounds =
|
||||||
@@ -19,16 +28,16 @@ object DrawableDescriptor : ChainedDescriptor<Drawable>() {
|
|||||||
|
|
||||||
override fun onGetData(
|
override fun onGetData(
|
||||||
node: Drawable,
|
node: Drawable,
|
||||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||||
) {
|
) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
val props = mutableMapOf<String, Inspectable>()
|
val props = mutableMapOf<Int, Inspectable>()
|
||||||
props["alpha"] = InspectableValue.Number(node.alpha, true)
|
props[AlphaAttributeId] = InspectableValue.Number(node.alpha)
|
||||||
|
|
||||||
val bounds = node.bounds
|
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.Inspectable
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
|
|
||||||
object FragmentFrameworkDescriptor : ChainedDescriptor<android.app.Fragment>() {
|
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 {
|
override fun onGetName(node: android.app.Fragment): String {
|
||||||
return node.javaClass.simpleName
|
return node.javaClass.simpleName
|
||||||
}
|
}
|
||||||
@@ -23,19 +28,24 @@ object FragmentFrameworkDescriptor : ChainedDescriptor<android.app.Fragment>() {
|
|||||||
|
|
||||||
override fun onGetData(
|
override fun onGetData(
|
||||||
node: android.app.Fragment,
|
node: android.app.Fragment,
|
||||||
attributeSections: MutableMap<String, InspectableObject>
|
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||||
) {
|
) {
|
||||||
val args: Bundle = node.arguments
|
val args: Bundle = node.arguments
|
||||||
|
|
||||||
val props = mutableMapOf<String, Inspectable>()
|
val props = mutableMapOf<Int, Inspectable>()
|
||||||
for (key in args.keySet()) {
|
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]) {
|
when (val value = args[key]) {
|
||||||
is Number -> props[key] = InspectableValue.Number(value)
|
is Number -> props[identifier] = InspectableValue.Number(value)
|
||||||
is Boolean -> props[key] = InspectableValue.Boolean(value)
|
is Boolean -> props[identifier] = InspectableValue.Boolean(value)
|
||||||
is String -> props[key] = InspectableValue.Text(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.Inspectable
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
|
|
||||||
object FragmentSupportDescriptor : ChainedDescriptor<androidx.fragment.app.Fragment>() {
|
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 {
|
override fun onGetName(node: androidx.fragment.app.Fragment): String {
|
||||||
return node.javaClass.simpleName
|
return node.javaClass.simpleName
|
||||||
}
|
}
|
||||||
@@ -22,19 +27,23 @@ object FragmentSupportDescriptor : ChainedDescriptor<androidx.fragment.app.Fragm
|
|||||||
|
|
||||||
override fun onGetData(
|
override fun onGetData(
|
||||||
node: androidx.fragment.app.Fragment,
|
node: androidx.fragment.app.Fragment,
|
||||||
attributeSections: MutableMap<String, InspectableObject>
|
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||||
) {
|
) {
|
||||||
val args = node.arguments
|
val args = node.arguments
|
||||||
args?.let { bundle ->
|
args?.let { bundle ->
|
||||||
val props = mutableMapOf<String, Inspectable>()
|
val props = mutableMapOf<Int, Inspectable>()
|
||||||
for (key in bundle.keySet()) {
|
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]) {
|
when (val value = bundle[key]) {
|
||||||
is Number -> props[key] = InspectableValue.Number(value)
|
is Number -> props[identifier] = InspectableValue.Number(value)
|
||||||
is Boolean -> props[key] = InspectableValue.Boolean(value)
|
is Boolean -> props[identifier] = InspectableValue.Boolean(value)
|
||||||
is String -> props[key] = InspectableValue.Text(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.common.EnumMapping
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
|
|
||||||
object ImageViewDescriptor : ChainedDescriptor<ImageView>() {
|
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 onGetName(node: ImageView): String = node.javaClass.simpleName
|
||||||
|
|
||||||
override fun onGetData(
|
override fun onGetData(
|
||||||
node: ImageView,
|
node: ImageView,
|
||||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||||
) {
|
) {
|
||||||
val props = mutableMapOf<String, Inspectable>()
|
val props = mutableMapOf<Int, Inspectable>()
|
||||||
props["scaleType"] = scaleTypeMapping.toInspectable(node.scaleType, true)
|
props[ScaleTypeAttributeId] = scaleTypeMapping.toInspectable(node.scaleType)
|
||||||
|
|
||||||
attributeSections["ImageView"] = InspectableObject(props)
|
attributeSections[SectionId] = InspectableObject(props)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val scaleTypeMapping: EnumMapping<ScaleType> =
|
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 android.graphics.Bitmap
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
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
|
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
|
Descriptors should be stateless and each descriptor should be a singleton
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typealias SectionName = String
|
|
||||||
|
|
||||||
object BaseTags {
|
object BaseTags {
|
||||||
|
|
||||||
const val Declarative = "Declarative"
|
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
|
* 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.
|
* 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
|
* 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 android.graphics.Bitmap
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
|
|
||||||
object ObjectDescriptor : NodeDescriptor<Any> {
|
object ObjectDescriptor : NodeDescriptor<Any> {
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ object ObjectDescriptor : NodeDescriptor<Any> {
|
|||||||
|
|
||||||
override fun getChildren(node: Any) = listOf<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
|
override fun getBounds(node: Any): Bounds? = null
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ package com.facebook.flipper.plugins.uidebugger.descriptors
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
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 */
|
/** 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) {
|
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 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)
|
node.descriptor.getData(node.child)
|
||||||
|
|
||||||
override fun getTags(node: OffsetChild): Set<String> = node.descriptor.getTags(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.Inspectable
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
|
|
||||||
object TextViewDescriptor : ChainedDescriptor<TextView>() {
|
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 onGetName(node: TextView): String = node.javaClass.simpleName
|
||||||
|
|
||||||
override fun onGetData(
|
override fun onGetData(
|
||||||
node: TextView,
|
node: TextView,
|
||||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val props =
|
val props =
|
||||||
mutableMapOf<String, Inspectable>(
|
mutableMapOf<Int, Inspectable>(
|
||||||
"text" to InspectableValue.Text(node.text.toString(), false),
|
TextAttributeId to InspectableValue.Text(node.text.toString()),
|
||||||
"textSize" to InspectableValue.Number(node.textSize, false),
|
TextSizeAttributeId to InspectableValue.Number(node.textSize),
|
||||||
"textColor" to
|
TextColorAttributeId to
|
||||||
InspectableValue.Color(Color.fromColor(node.textColors.defaultColor), false))
|
InspectableValue.Color(Color.fromColor(node.textColors.defaultColor)))
|
||||||
|
|
||||||
val typeface = node.typeface
|
val typeface = node.typeface
|
||||||
if (typeface != null) {
|
if (typeface != null) {
|
||||||
val typeFaceProp =
|
val typeFaceProp =
|
||||||
mutableMapOf<String, InspectableValue>(
|
mutableMapOf<Int, InspectableValue>(
|
||||||
"isBold" to InspectableValue.Boolean(typeface.isBold, false),
|
IsBoldAttributeId to InspectableValue.Boolean(typeface.isBold),
|
||||||
"isItalic" to InspectableValue.Boolean(typeface.isItalic, false),
|
IsItalicAttributeId to InspectableValue.Boolean(typeface.isItalic),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
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) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
props["minLines"] = InspectableValue.Number(node.minLines, false)
|
props[MinLinesAttributeId] = InspectableValue.Number(node.minLines)
|
||||||
props["maxLines"] = InspectableValue.Number(node.maxLines, false)
|
props[MaxLinesAttributeId] = InspectableValue.Number(node.maxLines)
|
||||||
props["minWidth"] = InspectableValue.Number(node.minWidth, false)
|
props[MinWidthAttributeId] = InspectableValue.Number(node.minWidth)
|
||||||
props["maxWidth"] = InspectableValue.Number(node.maxWidth, false)
|
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.core.widget.NestedScrollView
|
||||||
import androidx.viewpager.widget.ViewPager
|
import androidx.viewpager.widget.ViewPager
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.*
|
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.*
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.util.ResourcesUtil
|
import com.facebook.flipper.plugins.uidebugger.util.ResourcesUtil
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
|
|
||||||
object ViewDescriptor : ChainedDescriptor<View>() {
|
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 onGetName(node: View): String = node.javaClass.simpleName
|
||||||
|
|
||||||
override fun onGetBounds(node: View): Bounds {
|
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 onGetTags(node: View): Set<String> = BaseTags.NativeAndroid
|
||||||
|
|
||||||
override fun onGetData(
|
override fun onGetData(node: View, attributeSections: MutableMap<MetadataId, InspectableObject>) {
|
||||||
node: View,
|
|
||||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
|
||||||
) {
|
|
||||||
|
|
||||||
val props = mutableMapOf<String, Inspectable>()
|
val props = mutableMapOf<Int, Inspectable>()
|
||||||
|
|
||||||
val positionOnScreen = IntArray(2)
|
val positionOnScreen = IntArray(2)
|
||||||
node.getLocationOnScreen(positionOnScreen)
|
node.getLocationOnScreen(positionOnScreen)
|
||||||
@@ -78,80 +146,83 @@ object ViewDescriptor : ChainedDescriptor<View>() {
|
|||||||
node.getLocalVisibleRect(localVisible)
|
node.getLocalVisibleRect(localVisible)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
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 {
|
} 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]))
|
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[BoundsAttributeId] =
|
||||||
props["padding"] =
|
InspectableValue.Bounds(Bounds(node.left, node.top, node.right, node.bottom))
|
||||||
|
props[PaddingAttributeId] =
|
||||||
InspectableValue.SpaceBox(
|
InspectableValue.SpaceBox(
|
||||||
SpaceBox(node.paddingTop, node.paddingRight, node.paddingBottom, node.paddingLeft))
|
SpaceBox(node.paddingTop, node.paddingRight, node.paddingBottom, node.paddingLeft))
|
||||||
|
|
||||||
props["localVisibleRect"] =
|
props[LocalVisibleRectAttributeId] =
|
||||||
InspectableObject(
|
InspectableObject(
|
||||||
mapOf(
|
mapOf(
|
||||||
"position" to
|
PositionAttributeId to
|
||||||
InspectableValue.Coordinate(Coordinate(localVisible.left, localVisible.top)),
|
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))
|
InspectableValue.Coordinate3D(Coordinate3D(node.rotationX, node.rotationY, node.rotation))
|
||||||
props["scale"] = InspectableValue.Coordinate(Coordinate(node.scaleX, node.scaleY))
|
props[ScaleAttributeId] = InspectableValue.Coordinate(Coordinate(node.scaleX, node.scaleY))
|
||||||
props["pivot"] = InspectableValue.Coordinate(Coordinate(node.pivotX, node.pivotY))
|
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) {
|
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) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
props["translation"] =
|
props[TranslationAttributeId] =
|
||||||
InspectableValue.Coordinate3D(
|
InspectableValue.Coordinate3D(
|
||||||
Coordinate3D(node.translationX, node.translationY, node.translationZ))
|
Coordinate3D(node.translationX, node.translationY, node.translationZ))
|
||||||
} else {
|
} else {
|
||||||
props["translation"] =
|
props[TranslationAttributeId] =
|
||||||
InspectableValue.Coordinate(Coordinate(node.translationX, node.translationY))
|
InspectableValue.Coordinate(Coordinate(node.translationX, node.translationY))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
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) {
|
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[AlphaAttributeId] = InspectableValue.Number(node.alpha)
|
||||||
props["state"] =
|
props[StateAttributeId] =
|
||||||
InspectableObject(
|
InspectableObject(
|
||||||
mapOf(
|
mapOf(
|
||||||
"enabled" to InspectableValue.Boolean(node.isEnabled, mutable = false),
|
StateEnabledAttributeId to InspectableValue.Boolean(node.isEnabled),
|
||||||
"activated" to InspectableValue.Boolean(node.isActivated, mutable = false),
|
StateActivatedAttributeId to InspectableValue.Boolean(node.isActivated),
|
||||||
"focused" to InspectableValue.Boolean(node.isFocused, mutable = false),
|
StateFocusedAttributeId to InspectableValue.Boolean(node.isFocused),
|
||||||
"selected" to InspectableValue.Boolean(node.isSelected, mutable = false)))
|
StateSelectedAttributeId to InspectableValue.Boolean(node.isSelected)))
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
props["textDirection"] = TextDirectionMapping.toInspectable(node.textDirection, false)
|
props[TextDirectionAttributeId] = TextDirectionMapping.toInspectable(node.textDirection)
|
||||||
props["textAlignment"] = TextAlignmentMapping.toInspectable(node.textAlignment, false)
|
props[TextAlignmentAttributeId] = TextAlignmentMapping.toInspectable(node.textAlignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
node.tag
|
node.tag
|
||||||
?.let { InspectableValue.fromAny(it, mutable = false) }
|
?.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? {
|
override fun onGetSnapshot(node: View, bitmap: Bitmap?): Bitmap? {
|
||||||
@@ -180,19 +251,19 @@ object ViewDescriptor : ChainedDescriptor<View>() {
|
|||||||
|
|
||||||
private fun fromDrawable(d: Drawable?): Inspectable? {
|
private fun fromDrawable(d: Drawable?): Inspectable? {
|
||||||
return if (d is ColorDrawable) {
|
return if (d is ColorDrawable) {
|
||||||
InspectableValue.Color(Color.fromColor(d.color), mutable = false)
|
InspectableValue.Color(Color.fromColor(d.color))
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getLayoutParams(node: View): InspectableObject {
|
private fun getLayoutParams(node: View): InspectableObject {
|
||||||
val layoutParams = node.layoutParams
|
val layoutParams = node.layoutParams
|
||||||
|
|
||||||
val params = mutableMapOf<String, Inspectable>()
|
val params = mutableMapOf<Int, Inspectable>()
|
||||||
params["width"] = LayoutParamsMapping.toInspectable(layoutParams.width, mutable = true)
|
params[WidthAttributeId] = LayoutParamsMapping.toInspectable(layoutParams.width)
|
||||||
params["height"] = LayoutParamsMapping.toInspectable(layoutParams.height, mutable = true)
|
params[HeightAttributeId] = LayoutParamsMapping.toInspectable(layoutParams.height)
|
||||||
|
|
||||||
if (layoutParams is ViewGroup.MarginLayoutParams) {
|
if (layoutParams is ViewGroup.MarginLayoutParams) {
|
||||||
params["margin"] =
|
params[MarginAttributeId] =
|
||||||
InspectableValue.SpaceBox(
|
InspectableValue.SpaceBox(
|
||||||
SpaceBox(
|
SpaceBox(
|
||||||
layoutParams.topMargin,
|
layoutParams.topMargin,
|
||||||
@@ -201,17 +272,17 @@ object ViewDescriptor : ChainedDescriptor<View>() {
|
|||||||
layoutParams.leftMargin))
|
layoutParams.leftMargin))
|
||||||
}
|
}
|
||||||
if (layoutParams is FrameLayout.LayoutParams) {
|
if (layoutParams is FrameLayout.LayoutParams) {
|
||||||
params["gravity"] = GravityMapping.toInspectable(layoutParams.gravity, mutable = true)
|
params[GravityAttributeId] = GravityMapping.toInspectable(layoutParams.gravity)
|
||||||
}
|
}
|
||||||
if (layoutParams is LinearLayout.LayoutParams) {
|
if (layoutParams is LinearLayout.LayoutParams) {
|
||||||
params["weight"] = InspectableValue.Number(layoutParams.weight, mutable = true)
|
params[WeightAttributeId] = InspectableValue.Number(layoutParams.weight)
|
||||||
params["gravity"] = GravityMapping.toInspectable(layoutParams.gravity, mutable = true)
|
params[GravityAttributeId] = GravityMapping.toInspectable(layoutParams.gravity)
|
||||||
}
|
}
|
||||||
return InspectableObject(params)
|
return InspectableObject(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getViewTags(node: View): MutableMap<String, Inspectable> {
|
private fun getViewTags(node: View): MutableMap<Int, Inspectable> {
|
||||||
val tags = mutableMapOf<String, Inspectable>()
|
val tags = mutableMapOf<Int, Inspectable>()
|
||||||
|
|
||||||
KeyedTagsField?.let { field ->
|
KeyedTagsField?.let { field ->
|
||||||
val keyedTags = field.get(node) as SparseArray<*>?
|
val keyedTags = field.get(node) as SparseArray<*>?
|
||||||
@@ -224,7 +295,13 @@ object ViewDescriptor : ChainedDescriptor<View>() {
|
|||||||
keyedTags
|
keyedTags
|
||||||
.valueAt(i)
|
.valueAt(i)
|
||||||
?.let { InspectableValue.fromAny(it, false) }
|
?.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++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ import com.facebook.flipper.plugins.uidebugger.model.*
|
|||||||
|
|
||||||
object ViewGroupDescriptor : ChainedDescriptor<ViewGroup>() {
|
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 {
|
override fun onGetName(node: ViewGroup): String {
|
||||||
return node.javaClass.simpleName
|
return node.javaClass.simpleName
|
||||||
}
|
}
|
||||||
@@ -37,21 +40,28 @@ object ViewGroupDescriptor : ChainedDescriptor<ViewGroup>() {
|
|||||||
return children
|
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(
|
override fun onGetData(
|
||||||
node: ViewGroup,
|
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) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||||
viewGroupAttrs["LayoutMode"] = LayoutModeMapping.toInspectable(node.layoutMode, true)
|
props[LayoutModeAttributeId] = LayoutModeMapping.toInspectable(node.layoutMode)
|
||||||
viewGroupAttrs["ClipChildren"] = InspectableValue.Boolean(node.clipChildren, true)
|
props[ClipChildrenAttributeId] = InspectableValue.Boolean(node.clipChildren)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
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> =
|
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.core.FragmentTracker
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
|
|
||||||
object ViewPagerDescriptor : ChainedDescriptor<ViewPager>() {
|
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 onGetName(node: ViewPager): String = node.javaClass.simpleName
|
||||||
|
|
||||||
override fun onGetActiveChild(node: ViewPager): Any? {
|
override fun onGetActiveChild(node: ViewPager): Any? {
|
||||||
@@ -25,14 +29,16 @@ object ViewPagerDescriptor : ChainedDescriptor<ViewPager>() {
|
|||||||
return child
|
return child
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val CurrentItemIndexAttributeId =
|
||||||
|
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "currentItemIndex")
|
||||||
override fun onGetData(
|
override fun onGetData(
|
||||||
node: ViewPager,
|
node: ViewPager,
|
||||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||||
) {
|
) {
|
||||||
val props =
|
val props =
|
||||||
InspectableObject(
|
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.Inspectable
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
|
|
||||||
object WindowDescriptor : ChainedDescriptor<Window>() {
|
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 internalRStyleableClass: Class<*>? = null
|
||||||
private var internalRStyleableFields: Array<Field>? = null
|
private var internalRStyleableFields: Array<Field>? = null
|
||||||
private var internalRStyleableWindowField: Field? = null
|
private var internalRStyleableWindowField: Field? = null
|
||||||
@@ -32,7 +36,7 @@ object WindowDescriptor : ChainedDescriptor<Window>() {
|
|||||||
@SuppressLint("PrivateApi")
|
@SuppressLint("PrivateApi")
|
||||||
override fun onGetData(
|
override fun onGetData(
|
||||||
node: Window,
|
node: Window,
|
||||||
attributeSections: MutableMap<SectionName, InspectableObject>
|
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
if (internalRStyleableClass == null) {
|
if (internalRStyleableClass == null) {
|
||||||
@@ -58,7 +62,7 @@ object WindowDescriptor : ChainedDescriptor<Window>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val props = mutableMapOf<String, Inspectable>()
|
val props = mutableMapOf<Int, Inspectable>()
|
||||||
|
|
||||||
val typedValue = TypedValue()
|
val typedValue = TypedValue()
|
||||||
for ((index, attr) in windowStyleable.withIndex()) {
|
for ((index, attr) in windowStyleable.withIndex()) {
|
||||||
@@ -68,20 +72,28 @@ object WindowDescriptor : ChainedDescriptor<Window>() {
|
|||||||
// Strip 'Windows_' (length: 7) from the name.
|
// Strip 'Windows_' (length: 7) from the name.
|
||||||
val name = fieldName.substring(7)
|
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) {
|
when (typedValue.type) {
|
||||||
TypedValue.TYPE_STRING ->
|
TypedValue.TYPE_STRING ->
|
||||||
props[name] = InspectableValue.Text(typedValue.string.toString())
|
props[identifier] = InspectableValue.Text(typedValue.string.toString())
|
||||||
TypedValue.TYPE_INT_BOOLEAN ->
|
TypedValue.TYPE_INT_BOOLEAN ->
|
||||||
props[name] = InspectableValue.Boolean(typedValue.data != 0)
|
props[identifier] = InspectableValue.Boolean(typedValue.data != 0)
|
||||||
TypedValue.TYPE_INT_HEX ->
|
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 ->
|
TypedValue.TYPE_FLOAT ->
|
||||||
props[name] =
|
props[identifier] =
|
||||||
InspectableValue.Number(java.lang.Float.intBitsToFloat(typedValue.data))
|
InspectableValue.Number(java.lang.Float.intBitsToFloat(typedValue.data))
|
||||||
TypedValue.TYPE_REFERENCE -> {
|
TypedValue.TYPE_REFERENCE -> {
|
||||||
val resId = typedValue.data
|
val resId = typedValue.data
|
||||||
if (resId != 0) {
|
if (resId != 0) {
|
||||||
props[name] = InspectableValue.Text(node.context.resources.getResourceName(resId))
|
props[identifier] =
|
||||||
|
InspectableValue.Text(node.context.resources.getResourceName(resId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@@ -90,18 +102,18 @@ object WindowDescriptor : ChainedDescriptor<Window>() {
|
|||||||
try {
|
try {
|
||||||
val hexColor = "#" + Integer.toHexString(typedValue.data)
|
val hexColor = "#" + Integer.toHexString(typedValue.data)
|
||||||
val color = android.graphics.Color.parseColor(hexColor)
|
val color = android.graphics.Color.parseColor(hexColor)
|
||||||
props[name] = InspectableValue.Color(Color.fromColor(color))
|
props[identifier] = InspectableValue.Color(Color.fromColor(color))
|
||||||
} catch (e: Exception) {}
|
} catch (e: Exception) {}
|
||||||
} else if (typedValue.type >= TypedValue.TYPE_FIRST_INT &&
|
} else if (typedValue.type >= TypedValue.TYPE_FIRST_INT &&
|
||||||
typedValue.type <= TypedValue.TYPE_LAST_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
|
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
||||||
|
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
data class InitEvent(val rootId: Id) {
|
data class InitEvent(
|
||||||
|
val rootId: Id,
|
||||||
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
const val name = "init"
|
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
|
@kotlinx.serialization.Serializable
|
||||||
data class SubtreeUpdateEvent(
|
data class SubtreeUpdateEvent(
|
||||||
val txId: Long,
|
val txId: Long,
|
||||||
|
|||||||
@@ -16,93 +16,77 @@ import kotlinx.serialization.descriptors.SerialDescriptor
|
|||||||
import kotlinx.serialization.encoding.Decoder
|
import kotlinx.serialization.encoding.Decoder
|
||||||
import kotlinx.serialization.encoding.Encoder
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable sealed class Inspectable {}
|
||||||
sealed class Inspectable {
|
|
||||||
abstract val mutable: kotlin.Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this context, mutable means you can add/remove items,
|
// In this context, mutable means you can add/remove items,
|
||||||
// for native android this should probably be false.
|
// for native android this should probably be false.
|
||||||
@SerialName("array")
|
@SerialName("array")
|
||||||
@Serializable
|
@Serializable
|
||||||
data class InspectableArray(val items: List<Inspectable>, override val mutable: Boolean = false) :
|
data class InspectableArray(val id: Int, val items: List<Inspectable>) : Inspectable()
|
||||||
Inspectable()
|
|
||||||
|
|
||||||
// In this context, mutable means you can add / remove keys,
|
// In this context, mutable means you can add / remove keys,
|
||||||
// for native android this should probably be false.
|
// for native android this should probably be false.
|
||||||
@SerialName("object")
|
@SerialName("object")
|
||||||
@Serializable
|
@Serializable
|
||||||
data class InspectableObject(
|
data class InspectableObject(val fields: Map<Int, Inspectable>) : Inspectable()
|
||||||
val fields: Map<String, Inspectable>,
|
|
||||||
override val mutable: Boolean = false
|
|
||||||
) : Inspectable()
|
|
||||||
|
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
sealed class InspectableValue : Inspectable() {
|
sealed class InspectableValue : Inspectable() {
|
||||||
|
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
@SerialName("text")
|
@SerialName("text")
|
||||||
class Text(val value: String, override val mutable: kotlin.Boolean = false) : InspectableValue()
|
class Text(val value: String) : InspectableValue()
|
||||||
|
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
@SerialName("boolean")
|
@SerialName("boolean")
|
||||||
class Boolean(val value: kotlin.Boolean, override val mutable: kotlin.Boolean = false) :
|
class Boolean(val value: kotlin.Boolean) : InspectableValue()
|
||||||
InspectableValue()
|
|
||||||
|
|
||||||
@SerialName("number")
|
@SerialName("number")
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
data class Number(
|
data class Number(
|
||||||
@Serializable(with = NumberSerializer::class) val value: kotlin.Number,
|
@Serializable(with = NumberSerializer::class) val value: kotlin.Number,
|
||||||
override val mutable: kotlin.Boolean = false
|
|
||||||
) : InspectableValue()
|
) : InspectableValue()
|
||||||
|
|
||||||
@SerialName("color")
|
@SerialName("color")
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
data class Color(
|
data class Color(
|
||||||
val value: com.facebook.flipper.plugins.uidebugger.model.Color,
|
val value: com.facebook.flipper.plugins.uidebugger.model.Color,
|
||||||
override val mutable: kotlin.Boolean = false
|
|
||||||
) : InspectableValue()
|
) : InspectableValue()
|
||||||
|
|
||||||
@SerialName("coordinate")
|
@SerialName("coordinate")
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
data class Coordinate(
|
data class Coordinate(
|
||||||
val value: com.facebook.flipper.plugins.uidebugger.model.Coordinate,
|
val value: com.facebook.flipper.plugins.uidebugger.model.Coordinate,
|
||||||
override val mutable: kotlin.Boolean = false
|
|
||||||
) : InspectableValue()
|
) : InspectableValue()
|
||||||
|
|
||||||
@SerialName("coordinate3d")
|
@SerialName("coordinate3d")
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
data class Coordinate3D(
|
data class Coordinate3D(
|
||||||
val value: com.facebook.flipper.plugins.uidebugger.model.Coordinate3D,
|
val value: com.facebook.flipper.plugins.uidebugger.model.Coordinate3D,
|
||||||
override val mutable: kotlin.Boolean = false
|
|
||||||
) : InspectableValue()
|
) : InspectableValue()
|
||||||
|
|
||||||
@SerialName("size")
|
@SerialName("size")
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
data class Size(
|
data class Size(
|
||||||
val value: com.facebook.flipper.plugins.uidebugger.model.Size,
|
val value: com.facebook.flipper.plugins.uidebugger.model.Size,
|
||||||
override val mutable: kotlin.Boolean = false
|
|
||||||
) : InspectableValue()
|
) : InspectableValue()
|
||||||
|
|
||||||
@SerialName("bounds")
|
@SerialName("bounds")
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
data class Bounds(
|
data class Bounds(
|
||||||
val value: com.facebook.flipper.plugins.uidebugger.model.Bounds,
|
val value: com.facebook.flipper.plugins.uidebugger.model.Bounds,
|
||||||
override val mutable: kotlin.Boolean = false
|
|
||||||
) : InspectableValue()
|
) : InspectableValue()
|
||||||
|
|
||||||
@SerialName("space")
|
@SerialName("space")
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
data class SpaceBox(
|
data class SpaceBox(
|
||||||
val value: com.facebook.flipper.plugins.uidebugger.model.SpaceBox,
|
val value: com.facebook.flipper.plugins.uidebugger.model.SpaceBox,
|
||||||
override val mutable: kotlin.Boolean = false
|
|
||||||
) : InspectableValue()
|
) : InspectableValue()
|
||||||
|
|
||||||
@SerialName("enum")
|
@SerialName("enum")
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
data class Enum(
|
data class Enum(
|
||||||
val value: com.facebook.flipper.plugins.uidebugger.model.Enumeration,
|
val value: com.facebook.flipper.plugins.uidebugger.model.Enumeration,
|
||||||
override val mutable: kotlin.Boolean = false
|
|
||||||
) : InspectableValue()
|
) : InspectableValue()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -112,9 +96,9 @@ sealed class InspectableValue : Inspectable() {
|
|||||||
*/
|
*/
|
||||||
fun fromAny(any: Any, mutable: kotlin.Boolean = false): Inspectable? {
|
fun fromAny(any: Any, mutable: kotlin.Boolean = false): Inspectable? {
|
||||||
return when (any) {
|
return when (any) {
|
||||||
is kotlin.Number -> InspectableValue.Number(any, mutable)
|
is kotlin.Number -> InspectableValue.Number(any)
|
||||||
is kotlin.Boolean -> InspectableValue.Boolean(any, mutable)
|
is kotlin.Boolean -> InspectableValue.Boolean(any)
|
||||||
is kotlin.String -> InspectableValue.Text(any, mutable)
|
is kotlin.String -> InspectableValue.Text(any)
|
||||||
else -> null
|
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(
|
data class Node(
|
||||||
val id: Id,
|
val id: Id,
|
||||||
val name: String,
|
val name: String,
|
||||||
val attributes: Map<String, InspectableObject>,
|
val attributes: Map<MetadataId, InspectableObject>,
|
||||||
val bounds: Bounds?,
|
val bounds: Bounds?,
|
||||||
val tags: Set<String>,
|
val tags: Set<String>,
|
||||||
val children: List<Id>,
|
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.common.BitmapPool
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.Context
|
import com.facebook.flipper.plugins.uidebugger.core.Context
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
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.Coordinate
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.CoordinateUpdateEvent
|
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.Node
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.PerfStatsEvent
|
import com.facebook.flipper.plugins.uidebugger.model.PerfStatsEvent
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.SubtreeUpdateEvent
|
import com.facebook.flipper.plugins.uidebugger.model.SubtreeUpdateEvent
|
||||||
@@ -69,9 +71,7 @@ class TreeObserverManager(val context: Context) {
|
|||||||
workerScope.launch {
|
workerScope.launch {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
try {
|
try {
|
||||||
|
when (val update = updates.receive()) {
|
||||||
val update = updates.receive()
|
|
||||||
when (update) {
|
|
||||||
is SubtreeUpdate -> sendSubtreeUpdate(update)
|
is SubtreeUpdate -> sendSubtreeUpdate(update)
|
||||||
is CoordinateUpdate -> {
|
is CoordinateUpdate -> {
|
||||||
val event =
|
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 onWorkerThread = System.currentTimeMillis()
|
||||||
val txId = txId.getAndIncrement().toLong()
|
val txId = txId.getAndIncrement().toLong()
|
||||||
|
|
||||||
var serialized: String?
|
sendMetadata()
|
||||||
|
|
||||||
|
val serialized: String?
|
||||||
if (treeUpdate.snapshot == null) {
|
if (treeUpdate.snapshot == null) {
|
||||||
serialized =
|
serialized =
|
||||||
Json.encodeToString(
|
Json.encodeToString(
|
||||||
|
|||||||
@@ -39,9 +39,7 @@ class EnumMappingTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testTurnsIntoEnumInspectable() {
|
fun testTurnsIntoEnumInspectable() {
|
||||||
assertThat(
|
assertThat(
|
||||||
visibility.toInspectable(View.GONE, true),
|
visibility.toInspectable(View.GONE),
|
||||||
equalTo(
|
equalTo(InspectableValue.Enum(Enumeration(setOf("VISIBLE", "INVISIBLE", "GONE"), "GONE"))))
|
||||||
InspectableValue.Enum(
|
|
||||||
Enumeration(setOf("VISIBLE", "INVISIBLE", "GONE"), "GONE"), mutable = true)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import React, {useState} from 'react';
|
|||||||
import {plugin} from '../index';
|
import {plugin} from '../index';
|
||||||
import {DetailSidebar, Layout, usePlugin, useValue} from 'flipper-plugin';
|
import {DetailSidebar, Layout, usePlugin, useValue} from 'flipper-plugin';
|
||||||
import {useHotkeys} from 'react-hotkeys-hook';
|
import {useHotkeys} from 'react-hotkeys-hook';
|
||||||
import {Id, Snapshot, UINode} from '../types';
|
import {Id, Metadata, MetadataId, Snapshot, UINode} from '../types';
|
||||||
import {PerfStats} from './PerfStats';
|
import {PerfStats} from './PerfStats';
|
||||||
import {Tree} from './Tree';
|
import {Tree} from './Tree';
|
||||||
import {Visualization2D} from './Visualization2D';
|
import {Visualization2D} from './Visualization2D';
|
||||||
@@ -22,6 +22,7 @@ export function Component() {
|
|||||||
const instance = usePlugin(plugin);
|
const instance = usePlugin(plugin);
|
||||||
const rootId = useValue(instance.rootId);
|
const rootId = useValue(instance.rootId);
|
||||||
const nodes: Map<Id, UINode> = useValue(instance.nodes);
|
const nodes: Map<Id, UINode> = useValue(instance.nodes);
|
||||||
|
const metadata: Map<MetadataId, Metadata> = useValue(instance.metadata);
|
||||||
const snapshots: Map<Id, Snapshot> = useValue(instance.snapshots);
|
const snapshots: Map<Id, Snapshot> = useValue(instance.snapshots);
|
||||||
|
|
||||||
const [showPerfStats, setShowPerfStats] = useState(false);
|
const [showPerfStats, setShowPerfStats] = useState(false);
|
||||||
@@ -32,13 +33,16 @@ export function Component() {
|
|||||||
|
|
||||||
const {ctrlPressed} = useKeyboardModifiers();
|
const {ctrlPressed} = useKeyboardModifiers();
|
||||||
|
|
||||||
function renderSidebar(node: UINode | undefined) {
|
function renderSidebar(
|
||||||
|
node: UINode | undefined,
|
||||||
|
metadata: Map<MetadataId, Metadata>,
|
||||||
|
) {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<DetailSidebar width={350}>
|
<DetailSidebar width={350}>
|
||||||
<Inspector node={node} />
|
<Inspector metadata={metadata} node={node} />
|
||||||
</DetailSidebar>
|
</DetailSidebar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -68,7 +72,7 @@ export function Component() {
|
|||||||
onSelectNode={setSelectedNode}
|
onSelectNode={setSelectedNode}
|
||||||
modifierPressed={ctrlPressed}
|
modifierPressed={ctrlPressed}
|
||||||
/>
|
/>
|
||||||
{selectedNode && renderSidebar(nodes.get(selectedNode))}
|
{selectedNode && renderSidebar(nodes.get(selectedNode), metadata)}
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,16 +11,17 @@ import React from 'react';
|
|||||||
// eslint-disable-next-line rulesdir/no-restricted-imports-clone
|
// eslint-disable-next-line rulesdir/no-restricted-imports-clone
|
||||||
import {Glyph} from 'flipper';
|
import {Glyph} from 'flipper';
|
||||||
import {Layout, Tab, Tabs} from 'flipper-plugin';
|
import {Layout, Tab, Tabs} from 'flipper-plugin';
|
||||||
import {UINode} from '../../types';
|
import {Metadata, MetadataId, UINode} from '../../types';
|
||||||
import {IdentityInspector} from './inspector/IdentityInspector';
|
import {IdentityInspector} from './inspector/IdentityInspector';
|
||||||
import {AttributesInspector} from './inspector/AttributesInspector';
|
import {AttributesInspector} from './inspector/AttributesInspector';
|
||||||
import {DocumentationInspector} from './inspector/DocumentationInspector';
|
import {DocumentationInspector} from './inspector/DocumentationInspector';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
node: UINode;
|
node: UINode;
|
||||||
|
metadata: Map<MetadataId, Metadata>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Inspector: React.FC<Props> = ({node}) => {
|
export const Inspector: React.FC<Props> = ({node, metadata}) => {
|
||||||
return (
|
return (
|
||||||
<Layout.Container gap pad>
|
<Layout.Container gap pad>
|
||||||
<Tabs grow centered>
|
<Tabs grow centered>
|
||||||
@@ -38,7 +39,11 @@ export const Inspector: React.FC<Props> = ({node}) => {
|
|||||||
<Glyph name="data-table" size={16} />
|
<Glyph name="data-table" size={16} />
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
}>
|
}>
|
||||||
<AttributesInspector mode="attributes" node={node} />
|
<AttributesInspector
|
||||||
|
mode="attribute"
|
||||||
|
node={node}
|
||||||
|
metadata={metadata}
|
||||||
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab
|
<Tab
|
||||||
tab={
|
tab={
|
||||||
@@ -46,7 +51,7 @@ export const Inspector: React.FC<Props> = ({node}) => {
|
|||||||
<Glyph name="square-ruler" size={16} />
|
<Glyph name="square-ruler" size={16} />
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
}>
|
}>
|
||||||
<AttributesInspector mode="layout" node={node} />
|
<AttributesInspector mode="layout" node={node} metadata={metadata} />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab
|
<Tab
|
||||||
tab={
|
tab={
|
||||||
|
|||||||
@@ -8,7 +8,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Inspectable, InspectableObject, UINode} from '../../../types';
|
import {
|
||||||
|
Inspectable,
|
||||||
|
InspectableObject,
|
||||||
|
Metadata,
|
||||||
|
MetadataId,
|
||||||
|
UINode,
|
||||||
|
} from '../../../types';
|
||||||
import {DataInspector, Panel, styled} from 'flipper-plugin';
|
import {DataInspector, Panel, styled} from 'flipper-plugin';
|
||||||
import {Checkbox, Col, Row} from 'antd';
|
import {Checkbox, Col, Row} from 'antd';
|
||||||
import {displayableName} from '../utilities/displayableName';
|
import {displayableName} from '../utilities/displayableName';
|
||||||
@@ -73,21 +79,25 @@ const NamedAttributeInspector: React.FC<NamedAttributeInspectorProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ObjectAttributeInspector: React.FC<{
|
const ObjectAttributeInspector: React.FC<{
|
||||||
|
metadata: Map<MetadataId, Metadata>;
|
||||||
name: string;
|
name: string;
|
||||||
value: Record<string, Inspectable>;
|
fields: Record<MetadataId, Inspectable>;
|
||||||
level: number;
|
level: number;
|
||||||
}> = ({name, value, level}) => {
|
}> = ({metadata, name, fields, level}) => {
|
||||||
return (
|
return (
|
||||||
<div style={ContainerStyle}>
|
<div style={ContainerStyle}>
|
||||||
{name}
|
{name}
|
||||||
{Object.keys(value).map(function (key, _) {
|
{Object.keys(fields).map(function (key, _) {
|
||||||
|
const metadataId: number = Number(key);
|
||||||
|
const inspectableValue = fields[metadataId];
|
||||||
|
const attributeName = metadata.get(metadataId)?.name ?? '';
|
||||||
return (
|
return (
|
||||||
<ObjectContainer
|
<ObjectContainer
|
||||||
key={key}
|
key={metadataId}
|
||||||
style={{
|
style={{
|
||||||
paddingLeft: level,
|
paddingLeft: level,
|
||||||
}}>
|
}}>
|
||||||
{create(key, value[key], level + 2)}
|
{create(metadata, attributeName, inspectableValue, level + 2)}
|
||||||
</ObjectContainer>
|
</ObjectContainer>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -95,145 +105,143 @@ const ObjectAttributeInspector: React.FC<{
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function create(key: string, inspectable: Inspectable, level: number = 2) {
|
function create(
|
||||||
|
metadata: Map<MetadataId, Metadata>,
|
||||||
|
name: string,
|
||||||
|
inspectable: Inspectable,
|
||||||
|
level: number = 2,
|
||||||
|
) {
|
||||||
switch (inspectable.type) {
|
switch (inspectable.type) {
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
return (
|
return (
|
||||||
<NamedAttributeInspector name={displayableName(key)}>
|
<NamedAttributeInspector name={displayableName(name)}>
|
||||||
<Checkbox checked={inspectable.value} disabled />
|
<Checkbox checked={inspectable.value} disabled />
|
||||||
</NamedAttributeInspector>
|
</NamedAttributeInspector>
|
||||||
);
|
);
|
||||||
case 'enum':
|
case 'enum':
|
||||||
return (
|
return (
|
||||||
<NamedAttributeInspector name={displayableName(key)}>
|
<NamedAttributeInspector name={displayableName(name)}>
|
||||||
<EnumValue>{inspectable.value.value}</EnumValue>
|
<EnumValue>{inspectable.value.value}</EnumValue>
|
||||||
</NamedAttributeInspector>
|
</NamedAttributeInspector>
|
||||||
);
|
);
|
||||||
case 'text':
|
case 'text':
|
||||||
return (
|
return (
|
||||||
<NamedAttributeInspector name={displayableName(key)}>
|
<NamedAttributeInspector name={displayableName(name)}>
|
||||||
<TextValue>{inspectable.value}</TextValue>
|
<TextValue>{inspectable.value}</TextValue>
|
||||||
</NamedAttributeInspector>
|
</NamedAttributeInspector>
|
||||||
);
|
);
|
||||||
case 'number':
|
case 'number':
|
||||||
return (
|
return (
|
||||||
<NamedAttributeInspector name={displayableName(key)}>
|
<NamedAttributeInspector name={displayableName(name)}>
|
||||||
<NumberValue>{inspectable.value}</NumberValue>
|
<NumberValue>{inspectable.value}</NumberValue>
|
||||||
</NamedAttributeInspector>
|
</NamedAttributeInspector>
|
||||||
);
|
);
|
||||||
case 'color':
|
case 'color':
|
||||||
return (
|
return (
|
||||||
<NamedAttributeInspector name={displayableName(key)}>
|
<NamedAttributeInspector name={displayableName(name)}>
|
||||||
<ColorInspector color={inspectable.value} />
|
<ColorInspector color={inspectable.value} />
|
||||||
</NamedAttributeInspector>
|
</NamedAttributeInspector>
|
||||||
);
|
);
|
||||||
case 'size':
|
case 'size':
|
||||||
return (
|
return (
|
||||||
<NamedAttributeInspector name={displayableName(key)}>
|
<NamedAttributeInspector name={displayableName(name)}>
|
||||||
<SizeInspector value={inspectable.value} />
|
<SizeInspector value={inspectable.value} />
|
||||||
</NamedAttributeInspector>
|
</NamedAttributeInspector>
|
||||||
);
|
);
|
||||||
case 'bounds':
|
case 'bounds':
|
||||||
return (
|
return (
|
||||||
<NamedAttributeInspector name={displayableName(key)}>
|
<NamedAttributeInspector name={displayableName(name)}>
|
||||||
<BoundsInspector value={inspectable.value} />
|
<BoundsInspector value={inspectable.value} />
|
||||||
</NamedAttributeInspector>
|
</NamedAttributeInspector>
|
||||||
);
|
);
|
||||||
case 'coordinate':
|
case 'coordinate':
|
||||||
return (
|
return (
|
||||||
<NamedAttributeInspector name={displayableName(key)}>
|
<NamedAttributeInspector name={displayableName(name)}>
|
||||||
<CoordinateInspector value={inspectable.value} />
|
<CoordinateInspector value={inspectable.value} />
|
||||||
</NamedAttributeInspector>
|
</NamedAttributeInspector>
|
||||||
);
|
);
|
||||||
case 'coordinate3d':
|
case 'coordinate3d':
|
||||||
return (
|
return (
|
||||||
<NamedAttributeInspector name={displayableName(key)}>
|
<NamedAttributeInspector name={displayableName(name)}>
|
||||||
<Coordinate3DInspector value={inspectable.value} />
|
<Coordinate3DInspector value={inspectable.value} />
|
||||||
</NamedAttributeInspector>
|
</NamedAttributeInspector>
|
||||||
);
|
);
|
||||||
case 'space':
|
case 'space':
|
||||||
return (
|
return (
|
||||||
<NamedAttributeInspector name={displayableName(key)}>
|
<NamedAttributeInspector name={displayableName(name)}>
|
||||||
<SpaceBoxInspector value={inspectable.value} />
|
<SpaceBoxInspector value={inspectable.value} />
|
||||||
</NamedAttributeInspector>
|
</NamedAttributeInspector>
|
||||||
);
|
);
|
||||||
case 'object':
|
case 'object':
|
||||||
return (
|
return (
|
||||||
<ObjectAttributeInspector
|
<ObjectAttributeInspector
|
||||||
name={displayableName(key)}
|
metadata={metadata}
|
||||||
value={inspectable.fields}
|
name={displayableName(name)}
|
||||||
|
fields={inspectable.fields}
|
||||||
level={level}
|
level={level}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<NamedAttributeInspector name={displayableName(key)}>
|
<NamedAttributeInspector name={displayableName(name)}>
|
||||||
<TextValue>{JSON.stringify(inspectable)}</TextValue>
|
<TextValue>{JSON.stringify(inspectable)}</TextValue>
|
||||||
</NamedAttributeInspector>
|
</NamedAttributeInspector>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter out those inspectables that affect sizing, positioning, and
|
|
||||||
* overall layout of elements.
|
|
||||||
*/
|
|
||||||
const layoutFilter = new Set([
|
|
||||||
'size',
|
|
||||||
'padding',
|
|
||||||
'margin',
|
|
||||||
'bounds',
|
|
||||||
'position',
|
|
||||||
'globalPosition',
|
|
||||||
'localVisibleRect',
|
|
||||||
'rotation',
|
|
||||||
'scale',
|
|
||||||
'pivot',
|
|
||||||
'layoutParams',
|
|
||||||
'layoutDirection',
|
|
||||||
'translation',
|
|
||||||
'elevation',
|
|
||||||
]);
|
|
||||||
function createSection(
|
function createSection(
|
||||||
mode: InspectorMode,
|
mode: InspectorMode,
|
||||||
|
metadata: Map<MetadataId, Metadata>,
|
||||||
name: string,
|
name: string,
|
||||||
inspectable: InspectableObject,
|
inspectable: InspectableObject,
|
||||||
) {
|
) {
|
||||||
const fields = Object.keys(inspectable.fields).filter(
|
const children: any[] = [];
|
||||||
(key) =>
|
Object.keys(inspectable.fields).forEach((key, _index) => {
|
||||||
(mode === 'attributes' && !layoutFilter.has(key)) ||
|
const metadataId: number = Number(key);
|
||||||
(mode === 'layout' && layoutFilter.has(key)),
|
const attributeMetadata = metadata.get(metadataId);
|
||||||
);
|
if (attributeMetadata && attributeMetadata.type === mode) {
|
||||||
if (!fields || fields.length === 0) {
|
const attributeValue = inspectable.fields[metadataId];
|
||||||
return;
|
children.push(create(metadata, attributeMetadata.name, attributeValue));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (children.length > 0) {
|
||||||
return (
|
return (
|
||||||
<Panel key={name} title={name}>
|
<Panel key={mode.concat(name)} title={name}>
|
||||||
{fields.map(function (key, _) {
|
{...children}
|
||||||
return create(key, inspectable.fields[key]);
|
|
||||||
})}
|
|
||||||
</Panel>
|
</Panel>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type InspectorMode = 'layout' | 'attributes';
|
type InspectorMode = 'layout' | 'attribute';
|
||||||
type Props = {
|
type Props = {
|
||||||
node: UINode;
|
node: UINode;
|
||||||
|
metadata: Map<MetadataId, Metadata>;
|
||||||
mode: InspectorMode;
|
mode: InspectorMode;
|
||||||
rawDisplayEnabled?: boolean;
|
rawDisplayEnabled?: boolean;
|
||||||
};
|
};
|
||||||
export const AttributesInspector: React.FC<Props> = ({
|
export const AttributesInspector: React.FC<Props> = ({
|
||||||
node,
|
node,
|
||||||
|
metadata,
|
||||||
mode,
|
mode,
|
||||||
rawDisplayEnabled = false,
|
rawDisplayEnabled = false,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Object.keys(node.attributes).map(function (key, _) {
|
{Object.keys(node.attributes).map(function (key, _) {
|
||||||
|
const metadataId: number = Number(key);
|
||||||
|
/**
|
||||||
|
* The node top-level attributes refer to the displayable panels.
|
||||||
|
* The panel name is obtained by querying the metadata.
|
||||||
|
* The inspectable contains the actual attributes belonging to each panel.
|
||||||
|
*/
|
||||||
return createSection(
|
return createSection(
|
||||||
mode,
|
mode,
|
||||||
key,
|
metadata,
|
||||||
node.attributes[key] as InspectableObject,
|
metadata.get(metadataId)?.name ?? '',
|
||||||
|
node.attributes[metadataId] as InspectableObject,
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{rawDisplayEnabled ?? (
|
{rawDisplayEnabled ?? (
|
||||||
|
|||||||
@@ -8,12 +8,36 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {PluginClient, createState, createDataSource} from 'flipper-plugin';
|
import {PluginClient, createState, createDataSource} from 'flipper-plugin';
|
||||||
import {Events, Id, PerfStatsEvent, Snapshot, TreeState, UINode} from './types';
|
import {
|
||||||
|
Events,
|
||||||
|
Id,
|
||||||
|
Metadata,
|
||||||
|
MetadataId,
|
||||||
|
PerfStatsEvent,
|
||||||
|
Snapshot,
|
||||||
|
TreeState,
|
||||||
|
UINode,
|
||||||
|
} from './types';
|
||||||
import './node_modules/react-complex-tree/lib/style.css';
|
import './node_modules/react-complex-tree/lib/style.css';
|
||||||
|
|
||||||
export function plugin(client: PluginClient<Events>) {
|
export function plugin(client: PluginClient<Events>) {
|
||||||
const rootId = createState<Id | undefined>(undefined);
|
const rootId = createState<Id | undefined>(undefined);
|
||||||
client.onMessage('init', (root) => rootId.set(root.rootId));
|
const metadata = createState<Map<MetadataId, Metadata>>(new Map());
|
||||||
|
|
||||||
|
client.onMessage('init', (event) => {
|
||||||
|
rootId.set(event.rootId);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.onMessage('metadataUpdate', (event) => {
|
||||||
|
if (!event.attributeMetadata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
metadata.update((draft) => {
|
||||||
|
for (const [_key, value] of Object.entries(event.attributeMetadata)) {
|
||||||
|
draft.set(value.id, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const perfEvents = createDataSource<PerfStatsEvent, 'txId'>([], {
|
const perfEvents = createDataSource<PerfStatsEvent, 'txId'>([], {
|
||||||
key: 'txId',
|
key: 'txId',
|
||||||
@@ -23,13 +47,13 @@ export function plugin(client: PluginClient<Events>) {
|
|||||||
perfEvents.append(event);
|
perfEvents.append(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
const nodesAtom = createState<Map<Id, UINode>>(new Map());
|
const nodes = createState<Map<Id, UINode>>(new Map());
|
||||||
const snapshotsAtom = createState<Map<Id, Snapshot>>(new Map());
|
const snapshots = createState<Map<Id, Snapshot>>(new Map());
|
||||||
|
|
||||||
const treeState = createState<TreeState>({expandedNodes: []});
|
const treeState = createState<TreeState>({expandedNodes: []});
|
||||||
|
|
||||||
client.onMessage('coordinateUpdate', (event) => {
|
client.onMessage('coordinateUpdate', (event) => {
|
||||||
nodesAtom.update((draft) => {
|
nodes.update((draft) => {
|
||||||
const node = draft.get(event.nodeId);
|
const node = draft.get(event.nodeId);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
console.warn(`Coordinate update for non existing node `, event);
|
console.warn(`Coordinate update for non existing node `, event);
|
||||||
@@ -42,13 +66,13 @@ export function plugin(client: PluginClient<Events>) {
|
|||||||
|
|
||||||
const seenNodes = new Set<Id>();
|
const seenNodes = new Set<Id>();
|
||||||
client.onMessage('subtreeUpdate', (event) => {
|
client.onMessage('subtreeUpdate', (event) => {
|
||||||
snapshotsAtom.update((draft) => {
|
snapshots.update((draft) => {
|
||||||
draft.set(event.rootId, event.snapshot);
|
draft.set(event.rootId, event.snapshot);
|
||||||
});
|
});
|
||||||
nodesAtom.update((draft) => {
|
nodes.update((draft) => {
|
||||||
for (const node of event.nodes) {
|
event.nodes.forEach((node) => {
|
||||||
draft.set(node.id, node);
|
draft.set(node.id, node);
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
treeState.update((draft) => {
|
treeState.update((draft) => {
|
||||||
@@ -74,8 +98,9 @@ export function plugin(client: PluginClient<Events>) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
rootId,
|
rootId,
|
||||||
snapshots: snapshotsAtom,
|
nodes,
|
||||||
nodes: nodesAtom,
|
metadata,
|
||||||
|
snapshots,
|
||||||
perfEvents,
|
perfEvents,
|
||||||
treeState,
|
treeState,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export type Events = {
|
|||||||
subtreeUpdate: SubtreeUpdateEvent;
|
subtreeUpdate: SubtreeUpdateEvent;
|
||||||
coordinateUpdate: CoordinateUpdateEvent;
|
coordinateUpdate: CoordinateUpdateEvent;
|
||||||
perfStats: PerfStatsEvent;
|
perfStats: PerfStatsEvent;
|
||||||
|
metadataUpdate: UpdateMetadataEvent;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CoordinateUpdateEvent = {
|
export type CoordinateUpdateEvent = {
|
||||||
@@ -29,7 +30,9 @@ export type SubtreeUpdateEvent = {
|
|||||||
snapshot: Snapshot;
|
snapshot: Snapshot;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InitEvent = {rootId: Id};
|
export type InitEvent = {
|
||||||
|
rootId: Id;
|
||||||
|
};
|
||||||
|
|
||||||
export type PerfStatsEvent = {
|
export type PerfStatsEvent = {
|
||||||
txId: number;
|
txId: number;
|
||||||
@@ -43,16 +46,29 @@ export type PerfStatsEvent = {
|
|||||||
nodesCount: number;
|
nodesCount: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UpdateMetadataEvent = {
|
||||||
|
attributeMetadata: Record<MetadataId, Metadata>;
|
||||||
|
};
|
||||||
|
|
||||||
export type UINode = {
|
export type UINode = {
|
||||||
id: Id;
|
id: Id;
|
||||||
name: string;
|
name: string;
|
||||||
attributes: Record<string, Inspectable>;
|
attributes: Record<MetadataId, Inspectable>;
|
||||||
children: Id[];
|
children: Id[];
|
||||||
bounds: Bounds;
|
bounds: Bounds;
|
||||||
tags: Tag[];
|
tags: Tag[];
|
||||||
activeChild?: Id;
|
activeChild?: Id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Metadata = {
|
||||||
|
id: MetadataId;
|
||||||
|
type: string;
|
||||||
|
namespace: string;
|
||||||
|
name: string;
|
||||||
|
mutable: boolean;
|
||||||
|
tags?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
export type Bounds = {
|
export type Bounds = {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
@@ -93,6 +109,7 @@ export type Color = {
|
|||||||
export type Snapshot = string;
|
export type Snapshot = string;
|
||||||
export type Id = number | TreeItemIndex;
|
export type Id = number | TreeItemIndex;
|
||||||
|
|
||||||
|
export type MetadataId = number;
|
||||||
export type TreeState = {expandedNodes: Id[]};
|
export type TreeState = {expandedNodes: Id[]};
|
||||||
|
|
||||||
export type Tag = 'Native' | 'Declarative' | 'Android' | 'Litho ';
|
export type Tag = 'Native' | 'Declarative' | 'Android' | 'Litho ';
|
||||||
@@ -113,64 +130,54 @@ export type Inspectable =
|
|||||||
export type InspectableText = {
|
export type InspectableText = {
|
||||||
type: 'text';
|
type: 'text';
|
||||||
value: string;
|
value: string;
|
||||||
mutable: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InspectableNumber = {
|
export type InspectableNumber = {
|
||||||
type: 'number';
|
type: 'number';
|
||||||
value: number;
|
value: number;
|
||||||
mutable: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InspectableBoolean = {
|
export type InspectableBoolean = {
|
||||||
type: 'boolean';
|
type: 'boolean';
|
||||||
value: boolean;
|
value: boolean;
|
||||||
mutable: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InspectableEnum = {
|
export type InspectableEnum = {
|
||||||
type: 'enum';
|
type: 'enum';
|
||||||
value: {value: string; values: string[]};
|
value: {value: string; values: string[]};
|
||||||
mutable: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InspectableColor = {
|
export type InspectableColor = {
|
||||||
type: 'color';
|
type: 'color';
|
||||||
value: Color;
|
value: Color;
|
||||||
mutable: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InspectableBounds = {
|
export type InspectableBounds = {
|
||||||
type: 'bounds';
|
type: 'bounds';
|
||||||
value: Bounds;
|
value: Bounds;
|
||||||
mutable: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InspectableSize = {
|
export type InspectableSize = {
|
||||||
type: 'size';
|
type: 'size';
|
||||||
value: Size;
|
value: Size;
|
||||||
mutable: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InspectableCoordinate = {
|
export type InspectableCoordinate = {
|
||||||
type: 'coordinate';
|
type: 'coordinate';
|
||||||
value: Coordinate;
|
value: Coordinate;
|
||||||
mutable: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InspectableCoordinate3D = {
|
export type InspectableCoordinate3D = {
|
||||||
type: 'coordinate3d';
|
type: 'coordinate3d';
|
||||||
value: Coordinate3D;
|
value: Coordinate3D;
|
||||||
mutable: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InspectableSpaceBox = {
|
export type InspectableSpaceBox = {
|
||||||
type: 'space';
|
type: 'space';
|
||||||
value: SpaceBox;
|
value: SpaceBox;
|
||||||
mutable: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InspectableObject = {
|
export type InspectableObject = {
|
||||||
type: 'object';
|
type: 'object';
|
||||||
fields: Record<string, Inspectable>;
|
fields: Record<MetadataId, Inspectable>;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user