traversal

Summary: This adds a simple traversal to the UI. As it stands, it will most likely be updated in future iterations.

Reviewed By: LukeDefeo

Differential Revision: D38907292

fbshipit-source-id: 98cea1f971a4ef37ba2d1c75b882dd44df29bb3a
This commit is contained in:
Lorenzo Blasa
2022-08-23 05:45:29 -07:00
committed by Facebook GitHub Bot
parent f1e80b18b1
commit 5aff69d1c9
6 changed files with 102 additions and 18 deletions

View File

@@ -11,6 +11,7 @@ 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.core.ApplicationInspector
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
import com.facebook.flipper.plugins.uidebugger.core.Context
@@ -32,6 +33,9 @@ class UIDebuggerFlipperPlugin(val application: Application) : FlipperPlugin {
FlipperObject.Builder()
.put("rootId", System.identityHashCode(application).toString())
.build())
val inspector = ApplicationInspector(context)
val root = inspector.inspect()
}
@Throws(Exception::class)

View File

@@ -0,0 +1,17 @@
/*
* 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 java.lang.ref.WeakReference
class Node(val ref: WeakReference<Any>) {
var id: String? = null
var name: String? = null
var attributes: Map<String, Any?>? = null
var children: List<Node>? = null
}

View File

@@ -9,17 +9,15 @@ package com.facebook.flipper.plugins.uidebugger.core
import android.view.View
import android.view.ViewTreeObserver
import java.util.List
import com.facebook.flipper.plugins.uidebugger.common.Node
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
class ApplicationInspector(val context: Context) {
val descriptorRegister = DescriptorRegister.withDefaults()
val traversal = LayoutTraversal(descriptorRegister)
fun traverse(view: View) {
val inspector =
LayoutVisitor.create(
object : LayoutVisitor.Visitor {
override fun visit(view: View) {}
})
inspector.traverse(view)
fun inspect(): Node? {
return traversal.inspect(context.application)
}
fun attachListeners(view: View) {
@@ -52,14 +50,10 @@ class ApplicationInspector(val context: Context) {
override fun onRootViewRemoved(view: View) {}
override fun onRootViewsChanged(views: List<View>) {}
override fun onRootViewsChanged(views: java.util.List<View>) {}
})
val activeRoots = rootResolver.listActiveRootViews()
activeRoots?.let { roots ->
for (root: RootViewResolver.RootView in roots) {
traverse(root.view)
}
}
activeRoots?.let { roots -> for (root: RootViewResolver.RootView in roots) {} }
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.flipper.plugins.uidebugger.core
import 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) {
var children: List<Any>? = null
}
internal inline fun Descriptor<*>.asAny(): Descriptor<Any> = this as Descriptor<Any>
private fun describe(obj: Any): IntermediateNode {
var intermediate = IntermediateNode(Node(WeakReference(obj)))
val descriptor = descriptorRegister.descriptorForClass(obj::class.java)
descriptor?.let { descriptor ->
val anyDescriptor = descriptor.asAny()
intermediate.node.id = anyDescriptor.getId(obj)
intermediate.node.name = anyDescriptor.getName(obj)
val attributes = mutableMapOf<String, Any?>()
anyDescriptor.getData(obj, attributes)
intermediate.node.attributes = attributes
val children = mutableListOf<Any>()
anyDescriptor.getChildren(obj, children)
intermediate.children = children
}
return intermediate
}
private fun traverse(entry: Any): Node? {
val root = describe(entry)
root?.let { intermediate ->
val queue = mutableListOf<IntermediateNode>()
queue.add(intermediate)
while (queue.isNotEmpty()) {
val intermediateNode = queue.removeFirst()
val children = mutableListOf<Node>()
intermediateNode.children?.forEach {
val intermediateChild = describe(it)
children.add(intermediateChild.node)
queue.add(intermediateChild)
}
intermediateNode.node.children = children
}
}
return root?.node
}
fun inspect(applicationRef: ApplicationRef): Node? {
return traverse(applicationRef)
}
}

View File

@@ -36,7 +36,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? {
override fun getId(node: T): String {
return onGetId(node)
}
@@ -46,7 +46,7 @@ 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? {
override fun getName(node: T): String {
return onGetName(node)
}

View File

@@ -15,13 +15,13 @@ interface NodeDescriptor<T> {
* 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].
*/
fun getId(node: T): String?
fun getId(node: T): String
/**
* 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.
*/
fun getName(node: T): String?
fun getName(node: T): String
/** The children this node exposes in the inspector. */
fun getChildren(node: T, children: MutableList<Any>)