Added strongly typed tree for sidebar inspector
Summary: Introduced a JSON like tree structure for the sidebar insepector. Descriptors can provide their data as well as if its mutable or not. Enum mapping was simplified Reviewed By: lblasa Differential Revision: D38947238 fbshipit-source-id: cd8a6a8a752c5f626582ab8ac5efae6e9ff6a2ad
This commit is contained in:
committed by
Facebook GitHub Bot
parent
c69e737f19
commit
f123e65e8f
@@ -52,16 +52,6 @@ public class EnumMapping<T> {
|
||||
return mMapping.get(mDefaultKey);
|
||||
}
|
||||
|
||||
public InspectorValue<?> toPicker() {
|
||||
return toPicker(true);
|
||||
}
|
||||
|
||||
public InspectorValue<?> toPicker(final boolean mutable) {
|
||||
return mutable
|
||||
? InspectorValue.mutable(Picker, new InspectorValue.Picker(mMapping.keySet(), mDefaultKey))
|
||||
: InspectorValue.immutable(Enum, mDefaultKey);
|
||||
}
|
||||
|
||||
public InspectorValue<?> toPicker(final T currentValue) {
|
||||
return toPicker(currentValue, true);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
import com.facebook.flipper.plugins.uidebugger.common.Node
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
data class InitEvent(val rootId: String) {
|
||||
companion object {
|
||||
val name = "init"
|
||||
}
|
||||
}
|
||||
|
||||
// TODO flatten the tree into normalised list
|
||||
@kotlinx.serialization.Serializable
|
||||
data class NativeScanEvent(val root: Node) {
|
||||
companion object {
|
||||
val name = "nativeScan"
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,14 @@ package com.facebook.flipper.plugins.uidebugger
|
||||
|
||||
import android.app.Application
|
||||
import com.facebook.flipper.core.FlipperConnection
|
||||
import com.facebook.flipper.core.FlipperObject
|
||||
import com.facebook.flipper.core.FlipperPlugin
|
||||
import com.facebook.flipper.plugins.uidebugger.common.Node
|
||||
import com.facebook.flipper.plugins.uidebugger.core.ApplicationInspector
|
||||
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
||||
import com.facebook.flipper.plugins.uidebugger.core.Context
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
val LogTag = "FlipperUIDebugger"
|
||||
|
||||
class UIDebuggerFlipperPlugin(val application: Application) : FlipperPlugin {
|
||||
|
||||
@@ -28,14 +31,18 @@ class UIDebuggerFlipperPlugin(val application: Application) : FlipperPlugin {
|
||||
override fun onConnect(connection: FlipperConnection) {
|
||||
this.connection = connection
|
||||
// temp solution, get from descriptor
|
||||
connection.send(
|
||||
"init",
|
||||
FlipperObject.Builder()
|
||||
.put("rootId", System.identityHashCode(application).toString())
|
||||
.build())
|
||||
|
||||
val inspector = ApplicationInspector(context)
|
||||
val root = inspector.inspect()
|
||||
val root: Node = inspector.inspect()!!
|
||||
val initEvent = InitEvent(System.identityHashCode(application).toString())
|
||||
|
||||
connection.send(
|
||||
InitEvent.name,
|
||||
Json.encodeToString(
|
||||
InitEvent.serializer(), InitEvent(System.identityHashCode(application).toString())))
|
||||
|
||||
connection.send(
|
||||
NativeScanEvent.name,
|
||||
Json.encodeToString(NativeScanEvent.serializer(), NativeScanEvent(root)))
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
|
||||
@@ -7,60 +7,36 @@
|
||||
|
||||
package com.facebook.flipper.plugins.uidebugger.common
|
||||
|
||||
import androidx.collection.ArrayMap
|
||||
import androidx.collection.SimpleArrayMap
|
||||
import android.util.Log
|
||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||
|
||||
open class EnumMapping<T>(private val defaultKey: String) {
|
||||
private val map = ArrayMap<String, T>()
|
||||
// Maintains 2 way mapping between some enum value and a readable string representation
|
||||
open class EnumMapping<T>(val mapping: Map<String, T>) {
|
||||
|
||||
fun put(key: String, value: T) {
|
||||
map.put(key, value)
|
||||
fun getStringRepresentation(enumValue: T): String {
|
||||
val entry = mapping.entries.find { (_, value) -> value == enumValue }
|
||||
if (entry != null) {
|
||||
return entry.key
|
||||
} else {
|
||||
Log.w(
|
||||
LogTag,
|
||||
"Could not convert enum value ${enumValue.toString()} to string, known values ${mapping.entries}")
|
||||
return NoMapping
|
||||
}
|
||||
}
|
||||
|
||||
fun get(value: T): InspectableValue<String> {
|
||||
return get(value, true)
|
||||
fun getEnumValue(key: String): T {
|
||||
val value =
|
||||
mapping[key]
|
||||
?: throw UIDebuggerException(
|
||||
"Could not convert string ${key} to enum value, possible values ${mapping.entries} ")
|
||||
return value
|
||||
}
|
||||
|
||||
fun get(value: T, mutable: Boolean = true): InspectableValue<String> {
|
||||
val key = findKeyForValue(map, defaultKey, value)
|
||||
return if (mutable) InspectableValue.mutable(InspectableValue.Type.Enum, key)
|
||||
else InspectableValue.immutable(InspectableValue.Type.Enum, key)
|
||||
fun toInspectable(value: T, mutable: Boolean): InspectableValue.Enum {
|
||||
return InspectableValue.Enum(EnumData(mapping.keys, getStringRepresentation(value)), mutable)
|
||||
}
|
||||
|
||||
fun get(s: String): T? {
|
||||
return if (map.containsKey(s)) {
|
||||
map[s]
|
||||
} else map[defaultKey]
|
||||
}
|
||||
|
||||
fun toPicker(mutable: Boolean = true): InspectableValue<*> {
|
||||
return if (mutable)
|
||||
InspectableValue.mutable(
|
||||
InspectableValue.Type.Picker, InspectableValue.Picker(map.keys, defaultKey))
|
||||
else InspectableValue.immutable(InspectableValue.Type.Enum, defaultKey)
|
||||
}
|
||||
|
||||
fun toPicker(currentValue: T, mutable: Boolean = true): InspectableValue<*> {
|
||||
val value = findKeyForValue(map, defaultKey, currentValue)
|
||||
return if (mutable)
|
||||
InspectableValue.mutable(
|
||||
InspectableValue.Type.Picker, InspectableValue.Picker(map.keys, value))
|
||||
else InspectableValue.immutable(InspectableValue.Type.Enum, value)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <T> findKeyForValue(
|
||||
mapping: SimpleArrayMap<String, T>,
|
||||
defaultKey: String,
|
||||
currentValue: T
|
||||
): String {
|
||||
val count = mapping.size() - 1
|
||||
for (i in 0..count) {
|
||||
if (mapping.valueAt(i) == currentValue) {
|
||||
return mapping.keyAt(i)
|
||||
}
|
||||
}
|
||||
return defaultKey
|
||||
}
|
||||
val NoMapping = "__UNKNOWN_ENUM_VALUE__"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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.common
|
||||
|
||||
class UIDebuggerException(message: String) : Exception(message)
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.common
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
sealed class Inspectable {
|
||||
abstract val mutable: kotlin.Boolean
|
||||
}
|
||||
|
||||
// mutable here means you can add/remove items, for native android this should probably be false
|
||||
@SerialName("array")
|
||||
@Serializable
|
||||
data class InspectableArray(val items: List<Inspectable>, override val mutable: Boolean = false) :
|
||||
Inspectable()
|
||||
|
||||
// mutable here means you can add / remove keys, for native android this should probably be false
|
||||
@SerialName("object")
|
||||
@Serializable
|
||||
data class InspectableObject(
|
||||
val fields: Map<String, Inspectable>,
|
||||
override val mutable: Boolean = false
|
||||
) : Inspectable()
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
sealed class InspectableValue : Inspectable() {
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
@SerialName("text")
|
||||
class Text(val value: String, override val mutable: kotlin.Boolean) : InspectableValue()
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
@SerialName("boolean")
|
||||
class Boolean(val value: kotlin.Boolean, override val mutable: kotlin.Boolean) :
|
||||
InspectableValue()
|
||||
|
||||
@SerialName("number")
|
||||
@kotlinx.serialization.Serializable
|
||||
data class Number(
|
||||
@Serializable(with = NumberSerializer::class) val value: kotlin.Number,
|
||||
override val mutable: kotlin.Boolean
|
||||
) : InspectableValue()
|
||||
|
||||
@SerialName("color")
|
||||
@kotlinx.serialization.Serializable
|
||||
data class Color(val value: Int, override val mutable: kotlin.Boolean) : InspectableValue()
|
||||
|
||||
@SerialName("enum")
|
||||
@kotlinx.serialization.Serializable
|
||||
data class Enum(val value: EnumData, override val mutable: kotlin.Boolean) : InspectableValue()
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Will attempt to convert Any ref to a suitable primitive inspectable value. Only use if you
|
||||
* are dealing with an Any / object type. Prefer the specific contructors
|
||||
*/
|
||||
fun fromAny(any: Any, mutable: kotlin.Boolean): Inspectable? {
|
||||
return when (any) {
|
||||
is kotlin.Number -> InspectableValue.Number(any, mutable)
|
||||
is kotlin.Boolean -> InspectableValue.Boolean(any, mutable)
|
||||
is kotlin.String -> InspectableValue.Text(any, mutable)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@kotlinx.serialization.Serializable data class EnumData(val values: Set<String>, val value: String)
|
||||
|
||||
object NumberSerializer : KSerializer<Number> {
|
||||
override val descriptor: SerialDescriptor =
|
||||
PrimitiveSerialDescriptor("com.meta.NumberSerializer", PrimitiveKind.DOUBLE)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Number) {
|
||||
when (value) {
|
||||
is Double -> encoder.encodeDouble(value.toDouble())
|
||||
is Float -> encoder.encodeFloat(value.toFloat())
|
||||
is Long -> encoder.encodeLong(value.toLong())
|
||||
is Int -> encoder.encodeInt(value.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): Number {
|
||||
return decoder.decodeFloat()
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* 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.common
|
||||
|
||||
class InspectableValue<T>
|
||||
private constructor(val type: Type<T>, val value: T, val mutable: Boolean) {
|
||||
/**
|
||||
* Describe the type of data this value contains. This will influence how values are parsed and
|
||||
* displayed by the Flipper desktop app. For example colors will be parse as integers and
|
||||
* displayed using hex values and be editable using a color picker.
|
||||
*
|
||||
* Do not extends this list of types without adding support for the type in the desktop Inspector.
|
||||
*/
|
||||
class Type<T> internal constructor(private val name: String) {
|
||||
override fun toString(): String {
|
||||
return name
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Auto: Type<Any> = Type<Any>("auto")
|
||||
val Text = Type<String>("text")
|
||||
val Number = Type<Number>("number")
|
||||
val Boolean = Type<Boolean>("boolean")
|
||||
val Enum = Type<String>("enum")
|
||||
val Color = Type<Int>("color")
|
||||
val Picker = Type<Picker>("picker")
|
||||
}
|
||||
}
|
||||
|
||||
class Picker(val values: Set<String>, val selected: String) {}
|
||||
|
||||
companion object {
|
||||
fun <T> mutable(type: Type<T>, value: T): InspectableValue<T> {
|
||||
return InspectableValue(type, value, true)
|
||||
}
|
||||
|
||||
fun <T> immutable(type: Type<T>, value: T): InspectableValue<T> {
|
||||
return InspectableValue(type, value, false)
|
||||
}
|
||||
|
||||
fun mutable(value: Any): InspectableValue<*> {
|
||||
return InspectableValue<Any>(Type.Auto, value, true)
|
||||
}
|
||||
|
||||
fun immutable(value: Any): InspectableValue<*> {
|
||||
return InspectableValue<Any>(Type.Auto, value, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,10 @@
|
||||
|
||||
package com.facebook.flipper.plugins.uidebugger.common
|
||||
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class Node(val ref: WeakReference<Any>) {
|
||||
@kotlinx.serialization.Serializable
|
||||
class Node() {
|
||||
var id: String? = null
|
||||
var name: String? = null
|
||||
var attributes: Map<String, Any?>? = null
|
||||
var attributes: Map<String, InspectableObject> = mapOf()
|
||||
var children: List<Node>? = null
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
|
||||
package com.facebook.flipper.plugins.uidebugger.core
|
||||
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||
import com.facebook.flipper.plugins.uidebugger.common.Node
|
||||
import com.facebook.flipper.plugins.uidebugger.descriptors.Descriptor
|
||||
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class LayoutTraversal(private val descriptorRegister: DescriptorRegister) {
|
||||
class IntermediateNode(val node: Node) {
|
||||
@@ -20,7 +20,7 @@ class LayoutTraversal(private val descriptorRegister: DescriptorRegister) {
|
||||
internal inline fun Descriptor<*>.asAny(): Descriptor<Any> = this as Descriptor<Any>
|
||||
|
||||
private fun describe(obj: Any): IntermediateNode {
|
||||
var intermediate = IntermediateNode(Node(WeakReference(obj)))
|
||||
var intermediate = IntermediateNode(Node())
|
||||
|
||||
val descriptor = descriptorRegister.descriptorForClass(obj::class.java)
|
||||
descriptor?.let { descriptor ->
|
||||
@@ -29,7 +29,7 @@ class LayoutTraversal(private val descriptorRegister: DescriptorRegister) {
|
||||
intermediate.node.id = anyDescriptor.getId(obj)
|
||||
intermediate.node.name = anyDescriptor.getName(obj)
|
||||
|
||||
val attributes = mutableMapOf<String, Any?>()
|
||||
val attributes = mutableMapOf<String, InspectableObject>()
|
||||
anyDescriptor.getData(obj, attributes)
|
||||
intermediate.node.attributes = attributes
|
||||
|
||||
|
||||
@@ -7,13 +7,15 @@
|
||||
|
||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||
|
||||
/**
|
||||
* This class derives from Descriptor and provides a canonical implementation of ChainedDescriptor}.
|
||||
*/
|
||||
abstract class AbstractChainedDescriptor<T> : Descriptor<T>(), ChainedDescriptor<T> {
|
||||
private var mSuper: Descriptor<T>? = null
|
||||
|
||||
override fun setSuper(superDescriptor: Descriptor<T>) {
|
||||
final override fun setSuper(superDescriptor: Descriptor<T>) {
|
||||
if (superDescriptor !== mSuper) {
|
||||
check(mSuper == null)
|
||||
mSuper = superDescriptor
|
||||
@@ -25,7 +27,7 @@ abstract class AbstractChainedDescriptor<T> : Descriptor<T>(), ChainedDescriptor
|
||||
}
|
||||
|
||||
/** Initialize a descriptor. */
|
||||
override fun init() {
|
||||
final override fun init() {
|
||||
mSuper?.init()
|
||||
onInit()
|
||||
}
|
||||
@@ -36,7 +38,7 @@ abstract class AbstractChainedDescriptor<T> : Descriptor<T>(), ChainedDescriptor
|
||||
* A globally unique ID used to identify a node in a hierarchy. If your node does not have a
|
||||
* globally unique ID it is fine to rely on [System.identityHashCode].
|
||||
*/
|
||||
override fun getId(node: T): String {
|
||||
final override fun getId(node: T): String {
|
||||
return onGetId(node)
|
||||
}
|
||||
|
||||
@@ -46,28 +48,28 @@ abstract class AbstractChainedDescriptor<T> : Descriptor<T>(), ChainedDescriptor
|
||||
* The name used to identify this node in the inspector. Does not need to be unique. A good
|
||||
* default is to use the class name of the node.
|
||||
*/
|
||||
override fun getName(node: T): String {
|
||||
final override fun getName(node: T): String {
|
||||
return onGetName(node)
|
||||
}
|
||||
|
||||
abstract fun onGetName(node: T): String
|
||||
|
||||
/** The children this node exposes in the inspector. */
|
||||
override fun getChildren(node: T, children: MutableList<Any>) {
|
||||
final override fun getChildren(node: T, children: MutableList<Any>) {
|
||||
mSuper?.getChildren(node, children)
|
||||
onGetChildren(node, children)
|
||||
}
|
||||
|
||||
open fun onGetChildren(node: T, children: MutableList<Any>) {}
|
||||
|
||||
/**
|
||||
* Get the data to show for this node in the sidebar of the inspector. The object will be showen
|
||||
* in order and with a header matching the given name.
|
||||
*/
|
||||
override fun getData(node: T, builder: MutableMap<String, Any?>) {
|
||||
final override fun getData(node: T, builder: MutableMap<String, InspectableObject>) {
|
||||
mSuper?.getData(node, builder)
|
||||
onGetData(node, builder)
|
||||
}
|
||||
|
||||
open fun onGetData(node: T, builder: MutableMap<String, Any?>) {}
|
||||
/**
|
||||
* Get the data to show for this node in the sidebar of the inspector. Each key will be a have its
|
||||
* own section
|
||||
*/
|
||||
open fun onGetData(node: T, attributeSections: MutableMap<String, InspectableObject>) {}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||
|
||||
import android.app.Activity
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||
import com.facebook.flipper.plugins.uidebugger.stetho.FragmentCompat
|
||||
|
||||
class ActivityDescriptor : AbstractChainedDescriptor<Activity>() {
|
||||
@@ -35,7 +36,10 @@ class ActivityDescriptor : AbstractChainedDescriptor<Activity>() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetData(activity: Activity, builder: MutableMap<String, Any?>) {}
|
||||
override fun onGetData(
|
||||
activity: Activity,
|
||||
attributeSections: MutableMap<String, InspectableObject>
|
||||
) {}
|
||||
|
||||
private fun getFragments(compat: FragmentCompat<*, *, *, *>?, activity: Activity): List<Any> {
|
||||
if (compat == null) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||
|
||||
import android.app.Activity
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
||||
import com.facebook.flipper.plugins.uidebugger.core.RootViewResolver
|
||||
|
||||
@@ -48,5 +49,8 @@ class ApplicationDescriptor : AbstractChainedDescriptor<ApplicationRef>() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetData(applicationRef: ApplicationRef, builder: MutableMap<String, Any?>) {}
|
||||
override fun onGetData(
|
||||
applicationRef: ApplicationRef,
|
||||
attributeSections: MutableMap<String, InspectableObject>
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||
|
||||
import android.widget.Button
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||
|
||||
class ButtonDescriptor : AbstractChainedDescriptor<Button>() {
|
||||
override fun init() {}
|
||||
|
||||
override fun onGetId(button: Button): String {
|
||||
return Integer.toString(System.identityHashCode(button))
|
||||
@@ -20,7 +20,10 @@ class ButtonDescriptor : AbstractChainedDescriptor<Button>() {
|
||||
return button.javaClass.simpleName
|
||||
}
|
||||
|
||||
override fun onGetData(button: Button, builder: MutableMap<String, Any?>) {}
|
||||
override fun onGetData(
|
||||
button: Button,
|
||||
attributeSections: MutableMap<String, InspectableObject>
|
||||
) {}
|
||||
|
||||
override fun onGetChildren(button: Button, children: MutableList<Any>) {}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||
|
||||
interface NodeDescriptor<T> {
|
||||
/** Initialize a descriptor. */
|
||||
fun init()
|
||||
@@ -27,8 +29,8 @@ interface NodeDescriptor<T> {
|
||||
fun getChildren(node: T, children: MutableList<Any>)
|
||||
|
||||
/**
|
||||
* Get the data to show for this node in the sidebar of the inspector. The object will be showen
|
||||
* in order and with a header matching the given name.
|
||||
* Get the data to show for this node in the sidebar of the inspector. The object will be shown in
|
||||
* order and with a header matching the given name.
|
||||
*/
|
||||
fun getData(node: T, builder: MutableMap<String, Any?>)
|
||||
fun getData(node: T, builder: MutableMap<String, InspectableObject>)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||
|
||||
class ObjectDescriptor : Descriptor<Any>() {
|
||||
override fun init() {}
|
||||
|
||||
@@ -20,5 +22,5 @@ class ObjectDescriptor : Descriptor<Any>() {
|
||||
|
||||
override fun getChildren(node: Any, children: MutableList<Any>) {}
|
||||
|
||||
override fun getData(obj: Any, builder: MutableMap<String, Any?>) {}
|
||||
override fun getData(obj: Any, builder: MutableMap<String, InspectableObject>) {}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||
|
||||
import android.widget.TextView
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||
|
||||
class TextViewDescriptor : AbstractChainedDescriptor<TextView>() {
|
||||
override fun init() {}
|
||||
|
||||
override fun onGetId(textView: TextView): String {
|
||||
return Integer.toString(System.identityHashCode(textView))
|
||||
@@ -22,5 +22,8 @@ class TextViewDescriptor : AbstractChainedDescriptor<TextView>() {
|
||||
|
||||
override fun onGetChildren(textView: TextView, children: MutableList<Any>) {}
|
||||
|
||||
override fun onGetData(textView: TextView, builder: MutableMap<String, Any?>) {}
|
||||
override fun onGetData(
|
||||
textView: TextView,
|
||||
attributeSections: MutableMap<String, InspectableObject>
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -13,16 +13,16 @@ import android.util.SparseArray
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import com.facebook.flipper.plugins.uidebugger.common.EnumMapping
|
||||
import com.facebook.flipper.plugins.uidebugger.common.Inspectable
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableValue
|
||||
import com.facebook.flipper.plugins.uidebugger.stetho.ResourcesUtil
|
||||
import java.lang.reflect.Field
|
||||
|
||||
class ViewDescriptor : AbstractChainedDescriptor<View>() {
|
||||
override fun init() {}
|
||||
|
||||
override fun onGetId(view: View): String {
|
||||
return Integer.toBinaryString(System.identityHashCode(view))
|
||||
@@ -34,119 +34,133 @@ class ViewDescriptor : AbstractChainedDescriptor<View>() {
|
||||
|
||||
override fun onGetChildren(view: View, children: MutableList<Any>) {}
|
||||
|
||||
override fun onGetData(view: View, builder: MutableMap<String, Any?>) {
|
||||
override fun onGetData(view: View, attributeSections: MutableMap<String, InspectableObject>) {
|
||||
val positionOnScreen = IntArray(2)
|
||||
view.getLocationOnScreen(positionOnScreen)
|
||||
|
||||
val props = mutableMapOf<String, Any?>()
|
||||
props.put("height", InspectableValue.mutable(view.height))
|
||||
props.put("width", InspectableValue.mutable(view.width))
|
||||
props.put("alpha", InspectableValue.mutable(view.alpha))
|
||||
props.put("visibility", VisibilityMapping.toPicker(view.visibility))
|
||||
props.put("background", fromDrawable(view.background))
|
||||
// props.put("tag", InspectableValue.mutable(view.tag))
|
||||
// props.put("keyedTags", getTags(view))
|
||||
val props = mutableMapOf<String, Inspectable>()
|
||||
props.put("height", InspectableValue.Number(view.height, mutable = true))
|
||||
props.put("width", InspectableValue.Number(view.width, mutable = true))
|
||||
props.put("alpha", InspectableValue.Number(view.alpha, mutable = true))
|
||||
props.put("visibility", VisibilityMapping.toInspectable(view.visibility, mutable = false))
|
||||
|
||||
fromDrawable(view.background)?.let { props.put("background", it) }
|
||||
|
||||
view.tag?.let { InspectableValue.fromAny(it, mutable = false) }?.let { props.put("tag", it) }
|
||||
props.put("keyedTags", InspectableObject(getTags(view)))
|
||||
props.put("layoutParams", getLayoutParams(view))
|
||||
props.put(
|
||||
"state",
|
||||
mapOf<String, Any?>(
|
||||
"enabled" to InspectableValue.mutable(view.isEnabled),
|
||||
"activated" to InspectableValue.mutable(view.isActivated),
|
||||
"focused" to view.isFocused,
|
||||
"selected" to InspectableValue.mutable(view.isSelected)))
|
||||
InspectableObject(
|
||||
mapOf(
|
||||
"enabled" to InspectableValue.Boolean(view.isEnabled, mutable = false),
|
||||
"activated" to InspectableValue.Boolean(view.isActivated, mutable = false),
|
||||
"focused" to InspectableValue.Boolean(view.isFocused, mutable = false),
|
||||
"selected" to InspectableValue.Boolean(view.isSelected, mutable = false))))
|
||||
|
||||
props.put(
|
||||
"bounds",
|
||||
mapOf<String, Any?>(
|
||||
"left" to InspectableValue.mutable(view.left),
|
||||
"right" to InspectableValue.mutable(view.right),
|
||||
"top" to InspectableValue.mutable(view.top),
|
||||
"bottom" to InspectableValue.mutable(view.bottom)))
|
||||
InspectableObject(
|
||||
mapOf<String, Inspectable>(
|
||||
"left" to InspectableValue.Number(view.left, mutable = true),
|
||||
"right" to InspectableValue.Number(view.right, mutable = true),
|
||||
"top" to InspectableValue.Number(view.top, mutable = true),
|
||||
"bottom" to InspectableValue.Number(view.bottom, mutable = true))))
|
||||
props.put(
|
||||
"padding",
|
||||
mapOf<String, Any?>(
|
||||
"left" to InspectableValue.mutable(view.paddingLeft),
|
||||
"top" to InspectableValue.mutable(view.paddingTop),
|
||||
"right" to InspectableValue.mutable(view.paddingRight),
|
||||
"bottom" to InspectableValue.mutable(view.paddingBottom)))
|
||||
InspectableObject(
|
||||
mapOf<String, Inspectable>(
|
||||
"left" to InspectableValue.Number(view.paddingLeft, mutable = true),
|
||||
"right" to InspectableValue.Number(view.paddingRight, mutable = true),
|
||||
"top" to InspectableValue.Number(view.paddingTop, mutable = true),
|
||||
"bottom" to InspectableValue.Number(view.paddingBottom, mutable = true))))
|
||||
|
||||
props.put(
|
||||
"rotation",
|
||||
mapOf<String, Any?>(
|
||||
"x" to InspectableValue.mutable(view.rotationX),
|
||||
"y" to InspectableValue.mutable(view.rotationY),
|
||||
"z" to InspectableValue.mutable(view.rotation)))
|
||||
InspectableObject(
|
||||
mapOf<String, Inspectable>(
|
||||
"x" to InspectableValue.Number(view.rotationX, mutable = true),
|
||||
"y" to InspectableValue.Number(view.rotationY, mutable = true),
|
||||
"z" to InspectableValue.Number(view.rotation, mutable = true))))
|
||||
|
||||
props.put(
|
||||
"scale",
|
||||
mapOf<String, Any?>(
|
||||
"x" to InspectableValue.mutable(view.scaleX),
|
||||
"y" to InspectableValue.mutable(view.scaleY)))
|
||||
InspectableObject(
|
||||
mapOf(
|
||||
"x" to InspectableValue.Number(view.scaleX, mutable = true),
|
||||
"y" to InspectableValue.Number(view.scaleY, mutable = true))))
|
||||
props.put(
|
||||
"pivot",
|
||||
mapOf<String, Any?>(
|
||||
"x" to InspectableValue.mutable(view.pivotX),
|
||||
"y" to InspectableValue.mutable(view.pivotY)))
|
||||
props.put("positionOnScreenX", positionOnScreen[0])
|
||||
props.put("positionOnScreenY", positionOnScreen[1])
|
||||
InspectableObject(
|
||||
mapOf(
|
||||
"x" to InspectableValue.Number(view.pivotX, mutable = true),
|
||||
"y" to InspectableValue.Number(view.pivotY, mutable = true))))
|
||||
|
||||
builder.put("View", props)
|
||||
props.put(
|
||||
"globalPosition",
|
||||
InspectableObject(
|
||||
mapOf(
|
||||
"x" to InspectableValue.Number(positionOnScreen[0], mutable = false),
|
||||
"y" to InspectableValue.Number(positionOnScreen[1], mutable = false))))
|
||||
|
||||
attributeSections.put("View", InspectableObject(props.toMap()))
|
||||
}
|
||||
|
||||
fun fromDrawable(d: Drawable?): InspectableValue<*> {
|
||||
fun fromDrawable(d: Drawable?): Inspectable? {
|
||||
return if (d is ColorDrawable) {
|
||||
InspectableValue.mutable(InspectableValue.Type.Color, d.color)
|
||||
} else InspectableValue.mutable(InspectableValue.Type.Color, 0)
|
||||
InspectableValue.Color(d.color, mutable = false)
|
||||
} else null
|
||||
}
|
||||
|
||||
fun fromSize(size: Int): InspectableValue<*> {
|
||||
return when (size) {
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT ->
|
||||
InspectableValue.mutable(InspectableValue.Type.Enum, "WRAP_CONTENT")
|
||||
ViewGroup.LayoutParams.MATCH_PARENT ->
|
||||
InspectableValue.mutable(InspectableValue.Type.Enum, "MATCH_PARENT")
|
||||
else -> InspectableValue.mutable(InspectableValue.Type.Enum, Integer.toString(size))
|
||||
}
|
||||
}
|
||||
|
||||
fun getLayoutParams(node: View): MutableMap<String, Any> {
|
||||
fun getLayoutParams(node: View): InspectableObject {
|
||||
val layoutParams = node.layoutParams
|
||||
|
||||
val params = mutableMapOf<String, Any>()
|
||||
params.put("width", fromSize(layoutParams.width))
|
||||
params.put("height", fromSize(layoutParams.height))
|
||||
if (layoutParams is MarginLayoutParams) {
|
||||
val params = mutableMapOf<String, Inspectable>()
|
||||
params.put("width", LayoutParamsMapping.toInspectable(layoutParams.width, mutable = true))
|
||||
params.put("height", LayoutParamsMapping.toInspectable(layoutParams.height, mutable = true))
|
||||
|
||||
if (layoutParams is ViewGroup.MarginLayoutParams) {
|
||||
val marginLayoutParams = layoutParams
|
||||
|
||||
val margin =
|
||||
mapOf<String, Any>(
|
||||
"left" to InspectableValue.mutable(marginLayoutParams.leftMargin),
|
||||
"top" to InspectableValue.mutable(marginLayoutParams.topMargin),
|
||||
"right" to InspectableValue.mutable(marginLayoutParams.rightMargin),
|
||||
"bottom" to InspectableValue.mutable(marginLayoutParams.bottomMargin))
|
||||
InspectableObject(
|
||||
mapOf<String, Inspectable>(
|
||||
"left" to InspectableValue.Number(marginLayoutParams.leftMargin, mutable = true),
|
||||
"top" to InspectableValue.Number(marginLayoutParams.topMargin, mutable = true),
|
||||
"right" to
|
||||
InspectableValue.Number(marginLayoutParams.rightMargin, mutable = true),
|
||||
"bottom" to
|
||||
InspectableValue.Number(marginLayoutParams.bottomMargin, mutable = true)))
|
||||
|
||||
params.put("margin", margin)
|
||||
}
|
||||
if (layoutParams is FrameLayout.LayoutParams) {
|
||||
params.put("gravity", GravityMapping.toPicker(layoutParams.gravity))
|
||||
params.put("gravity", GravityMapping.toInspectable(layoutParams.gravity, mutable = true))
|
||||
}
|
||||
if (layoutParams is LinearLayout.LayoutParams) {
|
||||
val linearLayoutParams = layoutParams
|
||||
params.put("weight", InspectableValue.mutable(linearLayoutParams.weight))
|
||||
params.put("gravity", GravityMapping.toPicker(linearLayoutParams.gravity))
|
||||
params.put("weight", InspectableValue.Number(linearLayoutParams.weight, mutable = true))
|
||||
params.put(
|
||||
"gravity", GravityMapping.toInspectable(linearLayoutParams.gravity, mutable = true))
|
||||
}
|
||||
return params
|
||||
return InspectableObject(params)
|
||||
}
|
||||
|
||||
fun getTags(node: View): MutableMap<String, Any?> {
|
||||
val tags = mutableMapOf<String, Any?>()
|
||||
fun getTags(node: View): MutableMap<String, Inspectable> {
|
||||
val tags = mutableMapOf<String, Inspectable>()
|
||||
|
||||
KeyedTagsField?.let { field ->
|
||||
val keyedTags = field[node] as SparseArray<*>
|
||||
val keyedTags = field.get(node) as SparseArray<*>?
|
||||
if (keyedTags != null) {
|
||||
var i = 0
|
||||
val count = keyedTags.size()
|
||||
while (i < count) {
|
||||
val id =
|
||||
ResourcesUtil.getIdStringQuietly(node.context, node.resources, keyedTags.keyAt(i))
|
||||
tags.put(id, keyedTags.valueAt(i))
|
||||
keyedTags
|
||||
.valueAt(i)
|
||||
?.let { InspectableValue.fromAny(it, false) }
|
||||
?.let { tags.put(id, it) }
|
||||
i++
|
||||
}
|
||||
}
|
||||
@@ -155,67 +169,75 @@ class ViewDescriptor : AbstractChainedDescriptor<View>() {
|
||||
return tags
|
||||
}
|
||||
|
||||
private val LayoutParamsMapping: EnumMapping<Int> =
|
||||
object :
|
||||
EnumMapping<Int>(
|
||||
mapOf(
|
||||
"WRAP_CONTENT" to ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
"MATCH_PARENT" to ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
"FILL_PARENT" to ViewGroup.LayoutParams.FILL_PARENT,
|
||||
)) {}
|
||||
private val VisibilityMapping: EnumMapping<Int> =
|
||||
object : EnumMapping<Int>("VISIBLE") {
|
||||
init {
|
||||
put("VISIBLE", View.VISIBLE)
|
||||
put("INVISIBLE", View.INVISIBLE)
|
||||
put("GONE", View.GONE)
|
||||
}
|
||||
}
|
||||
object :
|
||||
EnumMapping<Int>(
|
||||
mapOf(
|
||||
"VISIBLE" to View.VISIBLE,
|
||||
"INVISIBLE" to View.INVISIBLE,
|
||||
"GONE" to View.GONE,
|
||||
)) {}
|
||||
|
||||
private val LayoutDirectionMapping: EnumMapping<Int> =
|
||||
object : EnumMapping<Int>("LAYOUT_DIRECTION_INHERIT") {
|
||||
init {
|
||||
put("LAYOUT_DIRECTION_INHERIT", View.LAYOUT_DIRECTION_INHERIT)
|
||||
put("LAYOUT_DIRECTION_LOCALE", View.LAYOUT_DIRECTION_LOCALE)
|
||||
put("LAYOUT_DIRECTION_LTR", View.LAYOUT_DIRECTION_LTR)
|
||||
put("LAYOUT_DIRECTION_RTL", View.LAYOUT_DIRECTION_RTL)
|
||||
}
|
||||
}
|
||||
object :
|
||||
EnumMapping<Int>(
|
||||
mapOf(
|
||||
"LAYOUT_DIRECTION_INHERIT" to View.LAYOUT_DIRECTION_INHERIT,
|
||||
"LAYOUT_DIRECTION_LOCALE" to View.LAYOUT_DIRECTION_LOCALE,
|
||||
"LAYOUT_DIRECTION_LTR" to View.LAYOUT_DIRECTION_LTR,
|
||||
"LAYOUT_DIRECTION_RTL" to View.LAYOUT_DIRECTION_RTL,
|
||||
)) {}
|
||||
|
||||
private val TextDirectionMapping: EnumMapping<Int> =
|
||||
object : EnumMapping<Int>("TEXT_DIRECTION_INHERIT") {
|
||||
init {
|
||||
put("TEXT_DIRECTION_INHERIT", View.TEXT_DIRECTION_INHERIT)
|
||||
put("TEXT_DIRECTION_FIRST_STRONG", View.TEXT_DIRECTION_FIRST_STRONG)
|
||||
put("TEXT_DIRECTION_ANY_RTL", View.TEXT_DIRECTION_ANY_RTL)
|
||||
put("TEXT_DIRECTION_LTR", View.TEXT_DIRECTION_LTR)
|
||||
put("TEXT_DIRECTION_RTL", View.TEXT_DIRECTION_RTL)
|
||||
put("TEXT_DIRECTION_LOCALE", View.TEXT_DIRECTION_LOCALE)
|
||||
put("TEXT_DIRECTION_FIRST_STRONG_LTR", View.TEXT_DIRECTION_FIRST_STRONG_LTR)
|
||||
put("TEXT_DIRECTION_FIRST_STRONG_RTL", View.TEXT_DIRECTION_FIRST_STRONG_RTL)
|
||||
}
|
||||
}
|
||||
object :
|
||||
EnumMapping<Int>(
|
||||
mapOf(
|
||||
"TEXT_DIRECTION_INHERIT" to View.TEXT_DIRECTION_INHERIT,
|
||||
"TEXT_DIRECTION_FIRST_STRONG" to View.TEXT_DIRECTION_FIRST_STRONG,
|
||||
"TEXT_DIRECTION_ANY_RTL" to View.TEXT_DIRECTION_ANY_RTL,
|
||||
"TEXT_DIRECTION_LTR" to View.TEXT_DIRECTION_LTR,
|
||||
"TEXT_DIRECTION_RTL" to View.TEXT_DIRECTION_RTL,
|
||||
"TEXT_DIRECTION_LOCALE" to View.TEXT_DIRECTION_LOCALE,
|
||||
"TEXT_DIRECTION_FIRST_STRONG_LTR" to View.TEXT_DIRECTION_FIRST_STRONG_LTR,
|
||||
"TEXT_DIRECTION_FIRST_STRONG_RTL" to View.TEXT_DIRECTION_FIRST_STRONG_RTL,
|
||||
)) {}
|
||||
|
||||
private val TextAlignmentMapping: EnumMapping<Int> =
|
||||
object : EnumMapping<Int>("TEXT_ALIGNMENT_INHERIT") {
|
||||
init {
|
||||
put("TEXT_ALIGNMENT_INHERIT", View.TEXT_ALIGNMENT_INHERIT)
|
||||
put("TEXT_ALIGNMENT_GRAVITY", View.TEXT_ALIGNMENT_GRAVITY)
|
||||
put("TEXT_ALIGNMENT_TEXT_START", View.TEXT_ALIGNMENT_TEXT_START)
|
||||
put("TEXT_ALIGNMENT_TEXT_END", View.TEXT_ALIGNMENT_TEXT_END)
|
||||
put("TEXT_ALIGNMENT_CENTER", View.TEXT_ALIGNMENT_CENTER)
|
||||
put("TEXT_ALIGNMENT_VIEW_START", View.TEXT_ALIGNMENT_VIEW_START)
|
||||
put("TEXT_ALIGNMENT_VIEW_END", View.TEXT_ALIGNMENT_VIEW_END)
|
||||
}
|
||||
}
|
||||
object :
|
||||
EnumMapping<Int>(
|
||||
mapOf(
|
||||
"TEXT_ALIGNMENT_INHERIT" to View.TEXT_ALIGNMENT_INHERIT,
|
||||
"TEXT_ALIGNMENT_GRAVITY" to View.TEXT_ALIGNMENT_GRAVITY,
|
||||
"TEXT_ALIGNMENT_TEXT_START" to View.TEXT_ALIGNMENT_TEXT_START,
|
||||
"TEXT_ALIGNMENT_TEXT_END" to View.TEXT_ALIGNMENT_TEXT_END,
|
||||
"TEXT_ALIGNMENT_CENTER" to View.TEXT_ALIGNMENT_CENTER,
|
||||
"TEXT_ALIGNMENT_VIEW_START" to View.TEXT_ALIGNMENT_VIEW_START,
|
||||
"TEXT_ALIGNMENT_VIEW_END" to View.TEXT_ALIGNMENT_VIEW_END,
|
||||
)) {}
|
||||
|
||||
private val GravityMapping: EnumMapping<Int> =
|
||||
object : EnumMapping<Int>("NO_GRAVITY") {
|
||||
init {
|
||||
put("NO_GRAVITY", Gravity.NO_GRAVITY)
|
||||
put("LEFT", Gravity.LEFT)
|
||||
put("TOP", Gravity.TOP)
|
||||
put("RIGHT", Gravity.RIGHT)
|
||||
put("BOTTOM", Gravity.BOTTOM)
|
||||
put("CENTER", Gravity.CENTER)
|
||||
put("CENTER_VERTICAL", Gravity.CENTER_VERTICAL)
|
||||
put("FILL_VERTICAL", Gravity.FILL_VERTICAL)
|
||||
put("CENTER_HORIZONTAL", Gravity.CENTER_HORIZONTAL)
|
||||
put("FILL_HORIZONTAL", Gravity.FILL_HORIZONTAL)
|
||||
}
|
||||
}
|
||||
private val GravityMapping =
|
||||
object :
|
||||
EnumMapping<Int>(
|
||||
mapOf(
|
||||
"NO_GRAVITY" to Gravity.NO_GRAVITY,
|
||||
"LEFT" to Gravity.LEFT,
|
||||
"TOP" to Gravity.TOP,
|
||||
"RIGHT" to Gravity.RIGHT,
|
||||
"BOTTOM" to Gravity.BOTTOM,
|
||||
"CENTER" to Gravity.CENTER,
|
||||
"CENTER_VERTICAL" to Gravity.CENTER_VERTICAL,
|
||||
"FILL_VERTICAL" to Gravity.FILL_VERTICAL,
|
||||
"CENTER_HORIZONTAL" to Gravity.CENTER_HORIZONTAL,
|
||||
"FILL_HORIZONTAL" to Gravity.FILL_HORIZONTAL,
|
||||
)) {}
|
||||
|
||||
companion object {
|
||||
private var KeyedTagsField: Field? = null
|
||||
|
||||
@@ -9,15 +9,16 @@ package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||
|
||||
import android.app.Fragment
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.ViewGroupCompat
|
||||
import com.facebook.flipper.plugins.uidebugger.common.EnumMapping
|
||||
import com.facebook.flipper.plugins.uidebugger.common.Inspectable
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableValue
|
||||
import com.facebook.flipper.plugins.uidebugger.stetho.FragmentCompat
|
||||
|
||||
class ViewGroupDescriptor : AbstractChainedDescriptor<ViewGroup>() {
|
||||
override fun init() {}
|
||||
|
||||
override fun onGetId(viewGroup: ViewGroup): String {
|
||||
return Integer.toString(System.identityHashCode(viewGroup))
|
||||
@@ -38,31 +39,36 @@ class ViewGroupDescriptor : AbstractChainedDescriptor<ViewGroup>() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetData(viewGroup: ViewGroup, builder: MutableMap<String, Any?>) {
|
||||
Log.d("FLIPPER_LAYOUT", "[viewgroup] onGetData")
|
||||
val groupBuilder = mutableMapOf<String, Any?>()
|
||||
override fun onGetData(
|
||||
viewGroup: ViewGroup,
|
||||
attributeSections: MutableMap<String, InspectableObject>
|
||||
) {
|
||||
val viewGroupAttrs = mutableMapOf<String, Inspectable>()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
groupBuilder.put(
|
||||
"LayoutMode",
|
||||
InspectableValue.mutable(
|
||||
InspectableValue.Type.Enum,
|
||||
if (viewGroup.getLayoutMode() == ViewGroupCompat.LAYOUT_MODE_CLIP_BOUNDS)
|
||||
"LAYOUT_MODE_CLIP_BOUNDS"
|
||||
else "LAYOUT_MODE_OPTICAL_BOUNDS"))
|
||||
groupBuilder.put(
|
||||
viewGroupAttrs.put(
|
||||
"LayoutMode", LayoutModeMapping.toInspectable(viewGroup.getLayoutMode(), true))
|
||||
viewGroupAttrs.put(
|
||||
"ClipChildren",
|
||||
InspectableValue.mutable(InspectableValue.Type.Boolean, viewGroup.getClipChildren()))
|
||||
InspectableValue.Boolean(viewGroup.getClipChildren(), true),
|
||||
)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
groupBuilder.put(
|
||||
"ClipToPadding",
|
||||
InspectableValue.mutable(InspectableValue.Type.Boolean, viewGroup.getClipToPadding()))
|
||||
viewGroupAttrs.put(
|
||||
"ClipToPadding", InspectableValue.Boolean(viewGroup.getClipToPadding(), true))
|
||||
}
|
||||
|
||||
builder.put("ViewGroup", groupBuilder)
|
||||
attributeSections.put("ViewGroup", InspectableObject(viewGroupAttrs))
|
||||
}
|
||||
|
||||
private val LayoutModeMapping: EnumMapping<Int> =
|
||||
object :
|
||||
EnumMapping<Int>(
|
||||
mapOf(
|
||||
"LAYOUT_MODE_CLIP_BOUNDS" to ViewGroupCompat.LAYOUT_MODE_CLIP_BOUNDS,
|
||||
"LAYOUT_MODE_OPTICAL_BOUNDS" to ViewGroupCompat.LAYOUT_MODE_OPTICAL_BOUNDS,
|
||||
)) {}
|
||||
|
||||
companion object {
|
||||
private fun getAttachedFragmentForView(v: View): Any? {
|
||||
return try {
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||
|
||||
import android.view.Window
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||
|
||||
class WindowDescriptor : AbstractChainedDescriptor<Window>() {
|
||||
override fun init() {}
|
||||
|
||||
override fun onGetId(window: Window): String {
|
||||
return Integer.toString(System.identityHashCode(window))
|
||||
@@ -24,5 +24,8 @@ class WindowDescriptor : AbstractChainedDescriptor<Window>() {
|
||||
children.add(window.decorView)
|
||||
}
|
||||
|
||||
override fun onGetData(window: Window, builder: MutableMap<String, Any?>) {}
|
||||
override fun onGetData(
|
||||
window: Window,
|
||||
attributeSections: MutableMap<String, InspectableObject>
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
package com.facebook.flipper.plugins.uidebugger
|
||||
|
||||
import android.view.View
|
||||
import com.facebook.flipper.plugins.uidebugger.common.EnumData
|
||||
import com.facebook.flipper.plugins.uidebugger.common.EnumMapping
|
||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableValue
|
||||
import org.hamcrest.CoreMatchers
|
||||
import org.hamcrest.CoreMatchers.*
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.junit.Test
|
||||
@@ -19,50 +19,29 @@ import org.robolectric.RobolectricTestRunner
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class EnumMappingTest {
|
||||
@Throws(Exception::class)
|
||||
@Test
|
||||
fun emptyMapping() {
|
||||
val e: EnumMapping<Int> = object : EnumMapping<Int>("k") {}
|
||||
|
||||
assertThat(e.get("j"), CoreMatchers.`is`(nullValue()))
|
||||
|
||||
var inspectable = e.get(0)
|
||||
assertThat(inspectable.mutable, equalTo(true))
|
||||
assertThat(inspectable.type, equalTo(InspectableValue.Type.Enum))
|
||||
assertThat(inspectable.value, equalTo("k"))
|
||||
|
||||
inspectable = e.get(0, true)
|
||||
assertThat(inspectable.mutable, equalTo(true))
|
||||
assertThat(inspectable.type, equalTo(InspectableValue.Type.Enum))
|
||||
assertThat(inspectable.value, equalTo("k"))
|
||||
|
||||
inspectable = e.get(0, false)
|
||||
assertThat(inspectable.mutable, equalTo(false))
|
||||
assertThat(inspectable.type, equalTo(InspectableValue.Type.Enum))
|
||||
assertThat(inspectable.value, equalTo("k"))
|
||||
|
||||
var picker = e.toPicker()
|
||||
assertThat(picker.mutable, equalTo(true))
|
||||
assertThat(picker.type, equalTo(InspectableValue.Type.Picker))
|
||||
assertThat(picker.value, CoreMatchers.`is`(notNullValue()))
|
||||
|
||||
val value: InspectableValue.Picker = picker.value as InspectableValue.Picker
|
||||
assertThat(value.selected, equalTo("k"))
|
||||
assertThat(value.values.size, equalTo(0))
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
@Test
|
||||
fun putGet() {
|
||||
val visibility: EnumMapping<Int> =
|
||||
object : EnumMapping<Int>("VISIBLE") {
|
||||
init {
|
||||
put("VISIBLE", View.VISIBLE)
|
||||
put("INVISIBLE", View.INVISIBLE)
|
||||
put("GONE", View.GONE)
|
||||
}
|
||||
object :
|
||||
EnumMapping<Int>(
|
||||
mapOf(
|
||||
"VISIBLE" to View.VISIBLE, "INVISIBLE" to View.INVISIBLE, "GONE" to View.GONE)) {}
|
||||
|
||||
@Test
|
||||
fun testTurnsEnumToString() {
|
||||
assertThat(visibility.getEnumValue("VISIBLE"), equalTo(View.VISIBLE))
|
||||
}
|
||||
|
||||
assertThat(visibility.get("VISIBLE"), equalTo(View.VISIBLE))
|
||||
@Test
|
||||
fun testTurnsStringToEnum() {
|
||||
assertThat(visibility.getStringRepresentation(View.VISIBLE), equalTo("VISIBLE"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTurnsIntoEnumInspectable() {
|
||||
assertThat(
|
||||
visibility.toInspectable(View.GONE, true),
|
||||
equalTo(
|
||||
InspectableValue.Enum(
|
||||
EnumData(setOf("VISIBLE", "INVISIBLE", "GONE"), "GONE"), mutable = true)))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user