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);
|
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) {
|
public InspectorValue<?> toPicker(final T currentValue) {
|
||||||
return toPicker(currentValue, true);
|
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 android.app.Application
|
||||||
import com.facebook.flipper.core.FlipperConnection
|
import com.facebook.flipper.core.FlipperConnection
|
||||||
import com.facebook.flipper.core.FlipperObject
|
|
||||||
import com.facebook.flipper.core.FlipperPlugin
|
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.ApplicationInspector
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.Context
|
import com.facebook.flipper.plugins.uidebugger.core.Context
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
val LogTag = "FlipperUIDebugger"
|
||||||
|
|
||||||
class UIDebuggerFlipperPlugin(val application: Application) : FlipperPlugin {
|
class UIDebuggerFlipperPlugin(val application: Application) : FlipperPlugin {
|
||||||
|
|
||||||
@@ -28,14 +31,18 @@ class UIDebuggerFlipperPlugin(val application: Application) : FlipperPlugin {
|
|||||||
override fun onConnect(connection: FlipperConnection) {
|
override fun onConnect(connection: FlipperConnection) {
|
||||||
this.connection = connection
|
this.connection = connection
|
||||||
// temp solution, get from descriptor
|
// temp solution, get from descriptor
|
||||||
connection.send(
|
|
||||||
"init",
|
|
||||||
FlipperObject.Builder()
|
|
||||||
.put("rootId", System.identityHashCode(application).toString())
|
|
||||||
.build())
|
|
||||||
|
|
||||||
val inspector = ApplicationInspector(context)
|
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)
|
@Throws(Exception::class)
|
||||||
|
|||||||
@@ -7,60 +7,36 @@
|
|||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.common
|
package com.facebook.flipper.plugins.uidebugger.common
|
||||||
|
|
||||||
import androidx.collection.ArrayMap
|
import android.util.Log
|
||||||
import androidx.collection.SimpleArrayMap
|
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||||
|
|
||||||
open class EnumMapping<T>(private val defaultKey: String) {
|
// Maintains 2 way mapping between some enum value and a readable string representation
|
||||||
private val map = ArrayMap<String, T>()
|
open class EnumMapping<T>(val mapping: Map<String, T>) {
|
||||||
|
|
||||||
fun put(key: String, value: T) {
|
fun getStringRepresentation(enumValue: T): String {
|
||||||
map.put(key, value)
|
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> {
|
fun getEnumValue(key: String): T {
|
||||||
return get(value, true)
|
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> {
|
fun toInspectable(value: T, mutable: Boolean): InspectableValue.Enum {
|
||||||
val key = findKeyForValue(map, defaultKey, value)
|
return InspectableValue.Enum(EnumData(mapping.keys, getStringRepresentation(value)), mutable)
|
||||||
return if (mutable) InspectableValue.mutable(InspectableValue.Type.Enum, key)
|
|
||||||
else InspectableValue.immutable(InspectableValue.Type.Enum, key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
companion object {
|
||||||
fun <T> findKeyForValue(
|
val NoMapping = "__UNKNOWN_ENUM_VALUE__"
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
package com.facebook.flipper.plugins.uidebugger.common
|
||||||
|
|
||||||
import java.lang.ref.WeakReference
|
@kotlinx.serialization.Serializable
|
||||||
|
class Node() {
|
||||||
class Node(val ref: WeakReference<Any>) {
|
|
||||||
var id: String? = null
|
var id: String? = null
|
||||||
var name: String? = null
|
var name: String? = null
|
||||||
var attributes: Map<String, Any?>? = null
|
var attributes: Map<String, InspectableObject> = mapOf()
|
||||||
var children: List<Node>? = null
|
var children: List<Node>? = null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.core
|
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.common.Node
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.Descriptor
|
import com.facebook.flipper.plugins.uidebugger.descriptors.Descriptor
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
|
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
|
|
||||||
class LayoutTraversal(private val descriptorRegister: DescriptorRegister) {
|
class LayoutTraversal(private val descriptorRegister: DescriptorRegister) {
|
||||||
class IntermediateNode(val node: Node) {
|
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>
|
internal inline fun Descriptor<*>.asAny(): Descriptor<Any> = this as Descriptor<Any>
|
||||||
|
|
||||||
private fun describe(obj: Any): IntermediateNode {
|
private fun describe(obj: Any): IntermediateNode {
|
||||||
var intermediate = IntermediateNode(Node(WeakReference(obj)))
|
var intermediate = IntermediateNode(Node())
|
||||||
|
|
||||||
val descriptor = descriptorRegister.descriptorForClass(obj::class.java)
|
val descriptor = descriptorRegister.descriptorForClass(obj::class.java)
|
||||||
descriptor?.let { descriptor ->
|
descriptor?.let { descriptor ->
|
||||||
@@ -29,7 +29,7 @@ class LayoutTraversal(private val descriptorRegister: DescriptorRegister) {
|
|||||||
intermediate.node.id = anyDescriptor.getId(obj)
|
intermediate.node.id = anyDescriptor.getId(obj)
|
||||||
intermediate.node.name = anyDescriptor.getName(obj)
|
intermediate.node.name = anyDescriptor.getName(obj)
|
||||||
|
|
||||||
val attributes = mutableMapOf<String, Any?>()
|
val attributes = mutableMapOf<String, InspectableObject>()
|
||||||
anyDescriptor.getData(obj, attributes)
|
anyDescriptor.getData(obj, attributes)
|
||||||
intermediate.node.attributes = attributes
|
intermediate.node.attributes = attributes
|
||||||
|
|
||||||
|
|||||||
@@ -7,13 +7,15 @@
|
|||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
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}.
|
* This class derives from Descriptor and provides a canonical implementation of ChainedDescriptor}.
|
||||||
*/
|
*/
|
||||||
abstract class AbstractChainedDescriptor<T> : Descriptor<T>(), ChainedDescriptor<T> {
|
abstract class AbstractChainedDescriptor<T> : Descriptor<T>(), ChainedDescriptor<T> {
|
||||||
private var mSuper: Descriptor<T>? = null
|
private var mSuper: Descriptor<T>? = null
|
||||||
|
|
||||||
override fun setSuper(superDescriptor: Descriptor<T>) {
|
final override fun setSuper(superDescriptor: Descriptor<T>) {
|
||||||
if (superDescriptor !== mSuper) {
|
if (superDescriptor !== mSuper) {
|
||||||
check(mSuper == null)
|
check(mSuper == null)
|
||||||
mSuper = superDescriptor
|
mSuper = superDescriptor
|
||||||
@@ -25,7 +27,7 @@ abstract class AbstractChainedDescriptor<T> : Descriptor<T>(), ChainedDescriptor
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Initialize a descriptor. */
|
/** Initialize a descriptor. */
|
||||||
override fun init() {
|
final override fun init() {
|
||||||
mSuper?.init()
|
mSuper?.init()
|
||||||
onInit()
|
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
|
* 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].
|
* 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)
|
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
|
* 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.
|
* 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)
|
return onGetName(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun onGetName(node: T): String
|
abstract fun onGetName(node: T): String
|
||||||
|
|
||||||
/** The children this node exposes in the inspector. */
|
/** 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)
|
mSuper?.getChildren(node, children)
|
||||||
onGetChildren(node, children)
|
onGetChildren(node, children)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun onGetChildren(node: T, children: MutableList<Any>) {}
|
open fun onGetChildren(node: T, children: MutableList<Any>) {}
|
||||||
|
|
||||||
/**
|
final override fun getData(node: T, builder: MutableMap<String, InspectableObject>) {
|
||||||
* 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?>) {
|
|
||||||
mSuper?.getData(node, builder)
|
mSuper?.getData(node, builder)
|
||||||
onGetData(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
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.stetho.FragmentCompat
|
import com.facebook.flipper.plugins.uidebugger.stetho.FragmentCompat
|
||||||
|
|
||||||
class ActivityDescriptor : AbstractChainedDescriptor<Activity>() {
|
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> {
|
private fun getFragments(compat: FragmentCompat<*, *, *, *>?, activity: Activity): List<Any> {
|
||||||
if (compat == null) {
|
if (compat == null) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
import android.app.Activity
|
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.ApplicationRef
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.RootViewResolver
|
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
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||||
|
|
||||||
class ButtonDescriptor : AbstractChainedDescriptor<Button>() {
|
class ButtonDescriptor : AbstractChainedDescriptor<Button>() {
|
||||||
override fun init() {}
|
|
||||||
|
|
||||||
override fun onGetId(button: Button): String {
|
override fun onGetId(button: Button): String {
|
||||||
return Integer.toString(System.identityHashCode(button))
|
return Integer.toString(System.identityHashCode(button))
|
||||||
@@ -20,7 +20,10 @@ class ButtonDescriptor : AbstractChainedDescriptor<Button>() {
|
|||||||
return button.javaClass.simpleName
|
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>) {}
|
override fun onGetChildren(button: Button, children: MutableList<Any>) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||||
|
|
||||||
interface NodeDescriptor<T> {
|
interface NodeDescriptor<T> {
|
||||||
/** Initialize a descriptor. */
|
/** Initialize a descriptor. */
|
||||||
fun init()
|
fun init()
|
||||||
@@ -27,8 +29,8 @@ interface NodeDescriptor<T> {
|
|||||||
fun getChildren(node: T, children: MutableList<Any>)
|
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
|
* Get the data to show for this node in the sidebar of the inspector. The object will be shown in
|
||||||
* in order and with a header matching the given name.
|
* 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
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||||
|
|
||||||
class ObjectDescriptor : Descriptor<Any>() {
|
class ObjectDescriptor : Descriptor<Any>() {
|
||||||
override fun init() {}
|
override fun init() {}
|
||||||
|
|
||||||
@@ -20,5 +22,5 @@ class ObjectDescriptor : Descriptor<Any>() {
|
|||||||
|
|
||||||
override fun getChildren(node: Any, children: MutableList<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
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||||
|
|
||||||
class TextViewDescriptor : AbstractChainedDescriptor<TextView>() {
|
class TextViewDescriptor : AbstractChainedDescriptor<TextView>() {
|
||||||
override fun init() {}
|
|
||||||
|
|
||||||
override fun onGetId(textView: TextView): String {
|
override fun onGetId(textView: TextView): String {
|
||||||
return Integer.toString(System.identityHashCode(textView))
|
return Integer.toString(System.identityHashCode(textView))
|
||||||
@@ -22,5 +22,8 @@ class TextViewDescriptor : AbstractChainedDescriptor<TextView>() {
|
|||||||
|
|
||||||
override fun onGetChildren(textView: TextView, children: MutableList<Any>) {}
|
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.Gravity
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewGroup.MarginLayoutParams
|
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.EnumMapping
|
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.common.InspectableValue
|
||||||
import com.facebook.flipper.plugins.uidebugger.stetho.ResourcesUtil
|
import com.facebook.flipper.plugins.uidebugger.stetho.ResourcesUtil
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
|
|
||||||
class ViewDescriptor : AbstractChainedDescriptor<View>() {
|
class ViewDescriptor : AbstractChainedDescriptor<View>() {
|
||||||
override fun init() {}
|
|
||||||
|
|
||||||
override fun onGetId(view: View): String {
|
override fun onGetId(view: View): String {
|
||||||
return Integer.toBinaryString(System.identityHashCode(view))
|
return Integer.toBinaryString(System.identityHashCode(view))
|
||||||
@@ -34,119 +34,133 @@ class ViewDescriptor : AbstractChainedDescriptor<View>() {
|
|||||||
|
|
||||||
override fun onGetChildren(view: View, children: MutableList<Any>) {}
|
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)
|
val positionOnScreen = IntArray(2)
|
||||||
view.getLocationOnScreen(positionOnScreen)
|
view.getLocationOnScreen(positionOnScreen)
|
||||||
|
|
||||||
val props = mutableMapOf<String, Any?>()
|
val props = mutableMapOf<String, Inspectable>()
|
||||||
props.put("height", InspectableValue.mutable(view.height))
|
props.put("height", InspectableValue.Number(view.height, mutable = true))
|
||||||
props.put("width", InspectableValue.mutable(view.width))
|
props.put("width", InspectableValue.Number(view.width, mutable = true))
|
||||||
props.put("alpha", InspectableValue.mutable(view.alpha))
|
props.put("alpha", InspectableValue.Number(view.alpha, mutable = true))
|
||||||
props.put("visibility", VisibilityMapping.toPicker(view.visibility))
|
props.put("visibility", VisibilityMapping.toInspectable(view.visibility, mutable = false))
|
||||||
props.put("background", fromDrawable(view.background))
|
|
||||||
// props.put("tag", InspectableValue.mutable(view.tag))
|
fromDrawable(view.background)?.let { props.put("background", it) }
|
||||||
// props.put("keyedTags", getTags(view))
|
|
||||||
|
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("layoutParams", getLayoutParams(view))
|
||||||
props.put(
|
props.put(
|
||||||
"state",
|
"state",
|
||||||
mapOf<String, Any?>(
|
InspectableObject(
|
||||||
"enabled" to InspectableValue.mutable(view.isEnabled),
|
mapOf(
|
||||||
"activated" to InspectableValue.mutable(view.isActivated),
|
"enabled" to InspectableValue.Boolean(view.isEnabled, mutable = false),
|
||||||
"focused" to view.isFocused,
|
"activated" to InspectableValue.Boolean(view.isActivated, mutable = false),
|
||||||
"selected" to InspectableValue.mutable(view.isSelected)))
|
"focused" to InspectableValue.Boolean(view.isFocused, mutable = false),
|
||||||
|
"selected" to InspectableValue.Boolean(view.isSelected, mutable = false))))
|
||||||
|
|
||||||
props.put(
|
props.put(
|
||||||
"bounds",
|
"bounds",
|
||||||
mapOf<String, Any?>(
|
InspectableObject(
|
||||||
"left" to InspectableValue.mutable(view.left),
|
mapOf<String, Inspectable>(
|
||||||
"right" to InspectableValue.mutable(view.right),
|
"left" to InspectableValue.Number(view.left, mutable = true),
|
||||||
"top" to InspectableValue.mutable(view.top),
|
"right" to InspectableValue.Number(view.right, mutable = true),
|
||||||
"bottom" to InspectableValue.mutable(view.bottom)))
|
"top" to InspectableValue.Number(view.top, mutable = true),
|
||||||
|
"bottom" to InspectableValue.Number(view.bottom, mutable = true))))
|
||||||
props.put(
|
props.put(
|
||||||
"padding",
|
"padding",
|
||||||
mapOf<String, Any?>(
|
InspectableObject(
|
||||||
"left" to InspectableValue.mutable(view.paddingLeft),
|
mapOf<String, Inspectable>(
|
||||||
"top" to InspectableValue.mutable(view.paddingTop),
|
"left" to InspectableValue.Number(view.paddingLeft, mutable = true),
|
||||||
"right" to InspectableValue.mutable(view.paddingRight),
|
"right" to InspectableValue.Number(view.paddingRight, mutable = true),
|
||||||
"bottom" to InspectableValue.mutable(view.paddingBottom)))
|
"top" to InspectableValue.Number(view.paddingTop, mutable = true),
|
||||||
|
"bottom" to InspectableValue.Number(view.paddingBottom, mutable = true))))
|
||||||
|
|
||||||
props.put(
|
props.put(
|
||||||
"rotation",
|
"rotation",
|
||||||
mapOf<String, Any?>(
|
InspectableObject(
|
||||||
"x" to InspectableValue.mutable(view.rotationX),
|
mapOf<String, Inspectable>(
|
||||||
"y" to InspectableValue.mutable(view.rotationY),
|
"x" to InspectableValue.Number(view.rotationX, mutable = true),
|
||||||
"z" to InspectableValue.mutable(view.rotation)))
|
"y" to InspectableValue.Number(view.rotationY, mutable = true),
|
||||||
|
"z" to InspectableValue.Number(view.rotation, mutable = true))))
|
||||||
|
|
||||||
props.put(
|
props.put(
|
||||||
"scale",
|
"scale",
|
||||||
mapOf<String, Any?>(
|
InspectableObject(
|
||||||
"x" to InspectableValue.mutable(view.scaleX),
|
mapOf(
|
||||||
"y" to InspectableValue.mutable(view.scaleY)))
|
"x" to InspectableValue.Number(view.scaleX, mutable = true),
|
||||||
|
"y" to InspectableValue.Number(view.scaleY, mutable = true))))
|
||||||
props.put(
|
props.put(
|
||||||
"pivot",
|
"pivot",
|
||||||
mapOf<String, Any?>(
|
InspectableObject(
|
||||||
"x" to InspectableValue.mutable(view.pivotX),
|
mapOf(
|
||||||
"y" to InspectableValue.mutable(view.pivotY)))
|
"x" to InspectableValue.Number(view.pivotX, mutable = true),
|
||||||
props.put("positionOnScreenX", positionOnScreen[0])
|
"y" to InspectableValue.Number(view.pivotY, mutable = true))))
|
||||||
props.put("positionOnScreenY", positionOnScreen[1])
|
|
||||||
|
|
||||||
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) {
|
return if (d is ColorDrawable) {
|
||||||
InspectableValue.mutable(InspectableValue.Type.Color, d.color)
|
InspectableValue.Color(d.color, mutable = false)
|
||||||
} else InspectableValue.mutable(InspectableValue.Type.Color, 0)
|
} else null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromSize(size: Int): InspectableValue<*> {
|
fun getLayoutParams(node: View): InspectableObject {
|
||||||
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> {
|
|
||||||
val layoutParams = node.layoutParams
|
val layoutParams = node.layoutParams
|
||||||
|
|
||||||
val params = mutableMapOf<String, Any>()
|
val params = mutableMapOf<String, Inspectable>()
|
||||||
params.put("width", fromSize(layoutParams.width))
|
params.put("width", LayoutParamsMapping.toInspectable(layoutParams.width, mutable = true))
|
||||||
params.put("height", fromSize(layoutParams.height))
|
params.put("height", LayoutParamsMapping.toInspectable(layoutParams.height, mutable = true))
|
||||||
if (layoutParams is MarginLayoutParams) {
|
|
||||||
|
if (layoutParams is ViewGroup.MarginLayoutParams) {
|
||||||
val marginLayoutParams = layoutParams
|
val marginLayoutParams = layoutParams
|
||||||
|
|
||||||
val margin =
|
val margin =
|
||||||
mapOf<String, Any>(
|
InspectableObject(
|
||||||
"left" to InspectableValue.mutable(marginLayoutParams.leftMargin),
|
mapOf<String, Inspectable>(
|
||||||
"top" to InspectableValue.mutable(marginLayoutParams.topMargin),
|
"left" to InspectableValue.Number(marginLayoutParams.leftMargin, mutable = true),
|
||||||
"right" to InspectableValue.mutable(marginLayoutParams.rightMargin),
|
"top" to InspectableValue.Number(marginLayoutParams.topMargin, mutable = true),
|
||||||
"bottom" to InspectableValue.mutable(marginLayoutParams.bottomMargin))
|
"right" to
|
||||||
|
InspectableValue.Number(marginLayoutParams.rightMargin, mutable = true),
|
||||||
|
"bottom" to
|
||||||
|
InspectableValue.Number(marginLayoutParams.bottomMargin, mutable = true)))
|
||||||
|
|
||||||
params.put("margin", margin)
|
params.put("margin", margin)
|
||||||
}
|
}
|
||||||
if (layoutParams is FrameLayout.LayoutParams) {
|
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) {
|
if (layoutParams is LinearLayout.LayoutParams) {
|
||||||
val linearLayoutParams = layoutParams
|
val linearLayoutParams = layoutParams
|
||||||
params.put("weight", InspectableValue.mutable(linearLayoutParams.weight))
|
params.put("weight", InspectableValue.Number(linearLayoutParams.weight, mutable = true))
|
||||||
params.put("gravity", GravityMapping.toPicker(linearLayoutParams.gravity))
|
params.put(
|
||||||
|
"gravity", GravityMapping.toInspectable(linearLayoutParams.gravity, mutable = true))
|
||||||
}
|
}
|
||||||
return params
|
return InspectableObject(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTags(node: View): MutableMap<String, Any?> {
|
fun getTags(node: View): MutableMap<String, Inspectable> {
|
||||||
val tags = mutableMapOf<String, Any?>()
|
val tags = mutableMapOf<String, Inspectable>()
|
||||||
|
|
||||||
KeyedTagsField?.let { field ->
|
KeyedTagsField?.let { field ->
|
||||||
val keyedTags = field[node] as SparseArray<*>
|
val keyedTags = field.get(node) as SparseArray<*>?
|
||||||
if (keyedTags != null) {
|
if (keyedTags != null) {
|
||||||
var i = 0
|
var i = 0
|
||||||
val count = keyedTags.size()
|
val count = keyedTags.size()
|
||||||
while (i < count) {
|
while (i < count) {
|
||||||
val id =
|
val id =
|
||||||
ResourcesUtil.getIdStringQuietly(node.context, node.resources, keyedTags.keyAt(i))
|
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++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,67 +169,75 @@ class ViewDescriptor : AbstractChainedDescriptor<View>() {
|
|||||||
return tags
|
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> =
|
private val VisibilityMapping: EnumMapping<Int> =
|
||||||
object : EnumMapping<Int>("VISIBLE") {
|
object :
|
||||||
init {
|
EnumMapping<Int>(
|
||||||
put("VISIBLE", View.VISIBLE)
|
mapOf(
|
||||||
put("INVISIBLE", View.INVISIBLE)
|
"VISIBLE" to View.VISIBLE,
|
||||||
put("GONE", View.GONE)
|
"INVISIBLE" to View.INVISIBLE,
|
||||||
}
|
"GONE" to View.GONE,
|
||||||
}
|
)) {}
|
||||||
|
|
||||||
private val LayoutDirectionMapping: EnumMapping<Int> =
|
private val LayoutDirectionMapping: EnumMapping<Int> =
|
||||||
object : EnumMapping<Int>("LAYOUT_DIRECTION_INHERIT") {
|
object :
|
||||||
init {
|
EnumMapping<Int>(
|
||||||
put("LAYOUT_DIRECTION_INHERIT", View.LAYOUT_DIRECTION_INHERIT)
|
mapOf(
|
||||||
put("LAYOUT_DIRECTION_LOCALE", View.LAYOUT_DIRECTION_LOCALE)
|
"LAYOUT_DIRECTION_INHERIT" to View.LAYOUT_DIRECTION_INHERIT,
|
||||||
put("LAYOUT_DIRECTION_LTR", View.LAYOUT_DIRECTION_LTR)
|
"LAYOUT_DIRECTION_LOCALE" to View.LAYOUT_DIRECTION_LOCALE,
|
||||||
put("LAYOUT_DIRECTION_RTL", View.LAYOUT_DIRECTION_RTL)
|
"LAYOUT_DIRECTION_LTR" to View.LAYOUT_DIRECTION_LTR,
|
||||||
}
|
"LAYOUT_DIRECTION_RTL" to View.LAYOUT_DIRECTION_RTL,
|
||||||
}
|
)) {}
|
||||||
|
|
||||||
private val TextDirectionMapping: EnumMapping<Int> =
|
private val TextDirectionMapping: EnumMapping<Int> =
|
||||||
object : EnumMapping<Int>("TEXT_DIRECTION_INHERIT") {
|
object :
|
||||||
init {
|
EnumMapping<Int>(
|
||||||
put("TEXT_DIRECTION_INHERIT", View.TEXT_DIRECTION_INHERIT)
|
mapOf(
|
||||||
put("TEXT_DIRECTION_FIRST_STRONG", View.TEXT_DIRECTION_FIRST_STRONG)
|
"TEXT_DIRECTION_INHERIT" to View.TEXT_DIRECTION_INHERIT,
|
||||||
put("TEXT_DIRECTION_ANY_RTL", View.TEXT_DIRECTION_ANY_RTL)
|
"TEXT_DIRECTION_FIRST_STRONG" to View.TEXT_DIRECTION_FIRST_STRONG,
|
||||||
put("TEXT_DIRECTION_LTR", View.TEXT_DIRECTION_LTR)
|
"TEXT_DIRECTION_ANY_RTL" to View.TEXT_DIRECTION_ANY_RTL,
|
||||||
put("TEXT_DIRECTION_RTL", View.TEXT_DIRECTION_RTL)
|
"TEXT_DIRECTION_LTR" to View.TEXT_DIRECTION_LTR,
|
||||||
put("TEXT_DIRECTION_LOCALE", View.TEXT_DIRECTION_LOCALE)
|
"TEXT_DIRECTION_RTL" to View.TEXT_DIRECTION_RTL,
|
||||||
put("TEXT_DIRECTION_FIRST_STRONG_LTR", View.TEXT_DIRECTION_FIRST_STRONG_LTR)
|
"TEXT_DIRECTION_LOCALE" to View.TEXT_DIRECTION_LOCALE,
|
||||||
put("TEXT_DIRECTION_FIRST_STRONG_RTL", View.TEXT_DIRECTION_FIRST_STRONG_RTL)
|
"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> =
|
private val TextAlignmentMapping: EnumMapping<Int> =
|
||||||
object : EnumMapping<Int>("TEXT_ALIGNMENT_INHERIT") {
|
object :
|
||||||
init {
|
EnumMapping<Int>(
|
||||||
put("TEXT_ALIGNMENT_INHERIT", View.TEXT_ALIGNMENT_INHERIT)
|
mapOf(
|
||||||
put("TEXT_ALIGNMENT_GRAVITY", View.TEXT_ALIGNMENT_GRAVITY)
|
"TEXT_ALIGNMENT_INHERIT" to View.TEXT_ALIGNMENT_INHERIT,
|
||||||
put("TEXT_ALIGNMENT_TEXT_START", View.TEXT_ALIGNMENT_TEXT_START)
|
"TEXT_ALIGNMENT_GRAVITY" to View.TEXT_ALIGNMENT_GRAVITY,
|
||||||
put("TEXT_ALIGNMENT_TEXT_END", View.TEXT_ALIGNMENT_TEXT_END)
|
"TEXT_ALIGNMENT_TEXT_START" to View.TEXT_ALIGNMENT_TEXT_START,
|
||||||
put("TEXT_ALIGNMENT_CENTER", View.TEXT_ALIGNMENT_CENTER)
|
"TEXT_ALIGNMENT_TEXT_END" to View.TEXT_ALIGNMENT_TEXT_END,
|
||||||
put("TEXT_ALIGNMENT_VIEW_START", View.TEXT_ALIGNMENT_VIEW_START)
|
"TEXT_ALIGNMENT_CENTER" to View.TEXT_ALIGNMENT_CENTER,
|
||||||
put("TEXT_ALIGNMENT_VIEW_END", View.TEXT_ALIGNMENT_VIEW_END)
|
"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> =
|
private val GravityMapping =
|
||||||
object : EnumMapping<Int>("NO_GRAVITY") {
|
object :
|
||||||
init {
|
EnumMapping<Int>(
|
||||||
put("NO_GRAVITY", Gravity.NO_GRAVITY)
|
mapOf(
|
||||||
put("LEFT", Gravity.LEFT)
|
"NO_GRAVITY" to Gravity.NO_GRAVITY,
|
||||||
put("TOP", Gravity.TOP)
|
"LEFT" to Gravity.LEFT,
|
||||||
put("RIGHT", Gravity.RIGHT)
|
"TOP" to Gravity.TOP,
|
||||||
put("BOTTOM", Gravity.BOTTOM)
|
"RIGHT" to Gravity.RIGHT,
|
||||||
put("CENTER", Gravity.CENTER)
|
"BOTTOM" to Gravity.BOTTOM,
|
||||||
put("CENTER_VERTICAL", Gravity.CENTER_VERTICAL)
|
"CENTER" to Gravity.CENTER,
|
||||||
put("FILL_VERTICAL", Gravity.FILL_VERTICAL)
|
"CENTER_VERTICAL" to Gravity.CENTER_VERTICAL,
|
||||||
put("CENTER_HORIZONTAL", Gravity.CENTER_HORIZONTAL)
|
"FILL_VERTICAL" to Gravity.FILL_VERTICAL,
|
||||||
put("FILL_HORIZONTAL", Gravity.FILL_HORIZONTAL)
|
"CENTER_HORIZONTAL" to Gravity.CENTER_HORIZONTAL,
|
||||||
}
|
"FILL_HORIZONTAL" to Gravity.FILL_HORIZONTAL,
|
||||||
}
|
)) {}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var KeyedTagsField: Field? = null
|
private var KeyedTagsField: Field? = null
|
||||||
|
|||||||
@@ -9,15 +9,16 @@ package com.facebook.flipper.plugins.uidebugger.descriptors
|
|||||||
|
|
||||||
import android.app.Fragment
|
import android.app.Fragment
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.view.ViewGroupCompat
|
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.common.InspectableValue
|
||||||
import com.facebook.flipper.plugins.uidebugger.stetho.FragmentCompat
|
import com.facebook.flipper.plugins.uidebugger.stetho.FragmentCompat
|
||||||
|
|
||||||
class ViewGroupDescriptor : AbstractChainedDescriptor<ViewGroup>() {
|
class ViewGroupDescriptor : AbstractChainedDescriptor<ViewGroup>() {
|
||||||
override fun init() {}
|
|
||||||
|
|
||||||
override fun onGetId(viewGroup: ViewGroup): String {
|
override fun onGetId(viewGroup: ViewGroup): String {
|
||||||
return Integer.toString(System.identityHashCode(viewGroup))
|
return Integer.toString(System.identityHashCode(viewGroup))
|
||||||
@@ -38,31 +39,36 @@ class ViewGroupDescriptor : AbstractChainedDescriptor<ViewGroup>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetData(viewGroup: ViewGroup, builder: MutableMap<String, Any?>) {
|
override fun onGetData(
|
||||||
Log.d("FLIPPER_LAYOUT", "[viewgroup] onGetData")
|
viewGroup: ViewGroup,
|
||||||
val groupBuilder = mutableMapOf<String, Any?>()
|
attributeSections: MutableMap<String, InspectableObject>
|
||||||
|
) {
|
||||||
|
val viewGroupAttrs = mutableMapOf<String, Inspectable>()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||||
groupBuilder.put(
|
viewGroupAttrs.put(
|
||||||
"LayoutMode",
|
"LayoutMode", LayoutModeMapping.toInspectable(viewGroup.getLayoutMode(), true))
|
||||||
InspectableValue.mutable(
|
viewGroupAttrs.put(
|
||||||
InspectableValue.Type.Enum,
|
|
||||||
if (viewGroup.getLayoutMode() == ViewGroupCompat.LAYOUT_MODE_CLIP_BOUNDS)
|
|
||||||
"LAYOUT_MODE_CLIP_BOUNDS"
|
|
||||||
else "LAYOUT_MODE_OPTICAL_BOUNDS"))
|
|
||||||
groupBuilder.put(
|
|
||||||
"ClipChildren",
|
"ClipChildren",
|
||||||
InspectableValue.mutable(InspectableValue.Type.Boolean, viewGroup.getClipChildren()))
|
InspectableValue.Boolean(viewGroup.getClipChildren(), true),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
groupBuilder.put(
|
viewGroupAttrs.put(
|
||||||
"ClipToPadding",
|
"ClipToPadding", InspectableValue.Boolean(viewGroup.getClipToPadding(), true))
|
||||||
InspectableValue.mutable(InspectableValue.Type.Boolean, viewGroup.getClipToPadding()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
companion object {
|
||||||
private fun getAttachedFragmentForView(v: View): Any? {
|
private fun getAttachedFragmentForView(v: View): Any? {
|
||||||
return try {
|
return try {
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||||
|
|
||||||
class WindowDescriptor : AbstractChainedDescriptor<Window>() {
|
class WindowDescriptor : AbstractChainedDescriptor<Window>() {
|
||||||
override fun init() {}
|
|
||||||
|
|
||||||
override fun onGetId(window: Window): String {
|
override fun onGetId(window: Window): String {
|
||||||
return Integer.toString(System.identityHashCode(window))
|
return Integer.toString(System.identityHashCode(window))
|
||||||
@@ -24,5 +24,8 @@ class WindowDescriptor : AbstractChainedDescriptor<Window>() {
|
|||||||
children.add(window.decorView)
|
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
|
package com.facebook.flipper.plugins.uidebugger
|
||||||
|
|
||||||
import android.view.View
|
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.EnumMapping
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableValue
|
import com.facebook.flipper.plugins.uidebugger.common.InspectableValue
|
||||||
import org.hamcrest.CoreMatchers
|
|
||||||
import org.hamcrest.CoreMatchers.*
|
import org.hamcrest.CoreMatchers.*
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -19,50 +19,29 @@ import org.robolectric.RobolectricTestRunner
|
|||||||
|
|
||||||
@RunWith(RobolectricTestRunner::class)
|
@RunWith(RobolectricTestRunner::class)
|
||||||
class EnumMappingTest {
|
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> =
|
val visibility: EnumMapping<Int> =
|
||||||
object : EnumMapping<Int>("VISIBLE") {
|
object :
|
||||||
init {
|
EnumMapping<Int>(
|
||||||
put("VISIBLE", View.VISIBLE)
|
mapOf(
|
||||||
put("INVISIBLE", View.INVISIBLE)
|
"VISIBLE" to View.VISIBLE, "INVISIBLE" to View.INVISIBLE, "GONE" to View.GONE)) {}
|
||||||
put("GONE", 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