Export jetpack-compose code

Summary: Move the jetpack plugin to a location that will get exported to GitHub. It won't get built as part of Gradle just yet.

Reviewed By: lblasa

Differential Revision: D46932691

fbshipit-source-id: 5837bbb2f31aad4221ee745fd145b91b2783d7fe
This commit is contained in:
Pascal Hartig
2023-06-23 14:42:14 -07:00
committed by Facebook GitHub Bot
parent 0d364dc6b6
commit b35cbaae55
6 changed files with 342 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.
package com.facebook.flipper.plugins.jetpackcompose
import androidx.compose.ui.platform.ComposeView
import com.facebook.flipper.plugins.jetpackcompose.descriptors.*
import com.facebook.flipper.plugins.jetpackcompose.model.*
import com.facebook.flipper.plugins.uidebugger.core.UIDContext
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
const val JetpackComposeTag = "JetpackCompose"
object UIDebuggerComposeSupport {
fun enable(context: UIDContext) {
addDescriptors(context.descriptorRegister)
}
private fun addDescriptors(register: DescriptorRegister) {
register.register(ComposeView::class.java, ComposeViewDescriptor)
register.register(ComposeNode::class.java, ComposeNodeDescriptor)
register.register(ComposeInnerViewNode::class.java, ComposeInnerViewDescriptor)
}
}

View File

@@ -0,0 +1,72 @@
// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.
package com.facebook.flipper.plugins.jetpackcompose.descriptors
import android.graphics.Bitmap
import android.view.ViewGroup
import com.facebook.flipper.plugins.jetpackcompose.model.ComposeInnerViewNode
import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor
import com.facebook.flipper.plugins.uidebugger.descriptors.ViewDescriptor
import com.facebook.flipper.plugins.uidebugger.descriptors.ViewGroupDescriptor
import com.facebook.flipper.plugins.uidebugger.model.Bounds
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred
object ComposeInnerViewDescriptor : NodeDescriptor<ComposeInnerViewNode> {
override fun getBounds(node: ComposeInnerViewNode): Bounds {
return node.bounds
}
override fun getName(node: ComposeInnerViewNode): String {
if (node.view is ViewGroup) {
return ViewGroupDescriptor.getName(node.view)
}
return ViewDescriptor.getName(node.view)
}
override fun getQualifiedName(node: ComposeInnerViewNode): String {
if (node.view is ViewGroup) {
return ViewGroupDescriptor.getQualifiedName(node.view)
}
return ViewDescriptor.getQualifiedName(node.view)
}
override fun getChildren(node: ComposeInnerViewNode): List<Any> {
if (node.view is ViewGroup) {
return ViewGroupDescriptor.getChildren(node.view)
}
return ViewDescriptor.getChildren(node.view)
}
override fun getSnapshot(node: ComposeInnerViewNode, bitmap: Bitmap?): Bitmap? {
if (node.view is ViewGroup) {
return ViewGroupDescriptor.getSnapshot(node.view, bitmap)
}
return ViewDescriptor.getSnapshot(node.view, bitmap)
}
override fun getActiveChild(node: ComposeInnerViewNode): Any? {
if (node.view is ViewGroup) {
return ViewGroupDescriptor.getActiveChild(node.view)
}
return ViewDescriptor.getActiveChild(node.view)
}
override fun getAttributes(
node: ComposeInnerViewNode
): MaybeDeferred<Map<MetadataId, InspectableObject>> {
if (node.view is ViewGroup) {
return ViewGroupDescriptor.getAttributes(node.view)
}
return ViewDescriptor.getAttributes(node.view)
}
override fun getTags(node: ComposeInnerViewNode): Set<String> {
if (node.view is ViewGroup) {
return ViewGroupDescriptor.getTags(node.view)
}
return ViewDescriptor.getTags(node.view)
}
}

View File

@@ -0,0 +1,132 @@
// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.
package com.facebook.flipper.plugins.jetpackcompose.descriptors
import android.graphics.Bitmap
import com.facebook.flipper.plugins.jetpackcompose.model.ComposeNode
import com.facebook.flipper.plugins.uidebugger.descriptors.BaseTags
import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister
import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor
import com.facebook.flipper.plugins.uidebugger.model.Bounds
import com.facebook.flipper.plugins.uidebugger.model.Coordinate
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
import com.facebook.flipper.plugins.uidebugger.util.Immediate
import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred
object ComposeNodeDescriptor : NodeDescriptor<ComposeNode> {
private const val NAMESPACE = "ComposeNode"
private var SectionId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE)
private val IdAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "id")
private val KeyAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "key")
private val NameAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_IDENTITY, NAMESPACE, "name")
private val FilenameAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_IDENTITY, NAMESPACE, "filename")
private val PackageHashAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_IDENTITY, NAMESPACE, "packageHash")
private val LineNumberAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_IDENTITY, NAMESPACE, "lineNumber")
private val OffsetAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_IDENTITY, NAMESPACE, "offset")
private val LengthAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_IDENTITY, NAMESPACE, "length")
private val BoxAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "box")
private val BoundsAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "bounds")
private val Bounds0AttributeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "(x0, y0)")
private val Bounds1AttributeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "(x1, y1)")
private val Bounds2AttributeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "(x2, y2)")
private val Bounds3AttributeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "(x3, y3)")
private val ViewIdAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "viewId")
private val MergedSemanticsAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "mergedSemantics")
private val UnmergedSemanticsAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "unmergedSemantics")
override fun getName(node: ComposeNode): String = node.inspectorNode.name
override fun getChildren(node: ComposeNode): List<Any> {
return node.children
}
override fun getAttributes(node: ComposeNode): MaybeDeferred<Map<MetadataId, InspectableObject>> {
val builder = mutableMapOf<MetadataId, InspectableObject>()
val props = mutableMapOf<Int, Inspectable>()
props[IdAttributeId] = InspectableValue.Number(node.inspectorNode.id)
props[ViewIdAttributeId] = InspectableValue.Number(node.inspectorNode.viewId)
props[KeyAttributeId] = InspectableValue.Number(node.inspectorNode.key)
props[NameAttributeId] = InspectableValue.Text(node.inspectorNode.name)
props[FilenameAttributeId] = InspectableValue.Text(node.inspectorNode.fileName)
props[PackageHashAttributeId] = InspectableValue.Number(node.inspectorNode.packageHash)
props[LineNumberAttributeId] = InspectableValue.Number(node.inspectorNode.lineNumber)
props[OffsetAttributeId] = InspectableValue.Number(node.inspectorNode.offset)
props[LengthAttributeId] = InspectableValue.Number(node.inspectorNode.length)
props[BoxAttributeId] =
InspectableValue.Bounds(
Bounds(
node.inspectorNode.left,
node.inspectorNode.top,
node.inspectorNode.width,
node.inspectorNode.height))
node.inspectorNode.bounds?.let { bounds ->
val quadBounds = mutableMapOf<Int, Inspectable>()
quadBounds[Bounds0AttributeId] = InspectableValue.Coordinate(Coordinate(bounds.x0, bounds.y0))
quadBounds[Bounds1AttributeId] = InspectableValue.Coordinate(Coordinate(bounds.x1, bounds.y1))
quadBounds[Bounds2AttributeId] = InspectableValue.Coordinate(Coordinate(bounds.x2, bounds.y2))
quadBounds[Bounds3AttributeId] = InspectableValue.Coordinate(Coordinate(bounds.x3, bounds.y3))
props[BoundsAttributeId] = InspectableObject(quadBounds.toMap())
}
val mergedSemantics = mutableMapOf<Int, Inspectable>()
node.inspectorNode.mergedSemantics.forEach {
val keyAttributeId =
MetadataRegister.register(
MetadataRegister.TYPE_ATTRIBUTE, node.inspectorNode.name, it.name)
mergedSemantics[keyAttributeId] = InspectableValue.Text(it.value.toString())
}
props[MergedSemanticsAttributeId] = InspectableObject(mergedSemantics.toMap())
val unmergedSemantics = mutableMapOf<Int, Inspectable>()
node.inspectorNode.unmergedSemantics.forEach {
val keyAttributeId =
MetadataRegister.register(
MetadataRegister.TYPE_ATTRIBUTE, node.inspectorNode.name, it.name)
mergedSemantics[keyAttributeId] = InspectableValue.Text(it.value.toString())
}
props[UnmergedSemanticsAttributeId] = InspectableObject(unmergedSemantics.toMap())
builder[SectionId] = InspectableObject(props.toMap())
return Immediate(builder)
}
override fun getBounds(node: ComposeNode): Bounds {
return node.bounds
}
override fun getQualifiedName(node: ComposeNode): String = node.inspectorNode.name
override fun getSnapshot(node: ComposeNode, bitmap: Bitmap?): Bitmap? = null
override fun getActiveChild(node: ComposeNode): Any? = null
override fun getTags(node: ComposeNode): Set<String> = setOf(BaseTags.Android, "Compose")
}

View File

@@ -0,0 +1,43 @@
// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.
package com.facebook.flipper.plugins.jetpackcompose.descriptors
import android.os.Build
import android.view.View
import androidx.compose.ui.platform.ComposeView
import com.facebook.flipper.plugins.jetpackcompose.model.ComposeNode
import com.facebook.flipper.plugins.uidebugger.descriptors.ChainedDescriptor
import facebook.internal.androidx.compose.ui.inspection.inspector.InspectorNode
import facebook.internal.androidx.compose.ui.inspection.inspector.LayoutInspectorTree
object ComposeViewDescriptor : ChainedDescriptor<ComposeView>() {
override fun onGetName(node: ComposeView): String = node.javaClass.simpleName
private fun transform(view: View, nodes: List<InspectorNode>): List<ComposeNode> {
val positionOnScreen = IntArray(2)
view.getLocationOnScreen(positionOnScreen)
val xOffset = positionOnScreen[0]
val yOffset = positionOnScreen[1]
return nodes.map { node -> ComposeNode(view, node, xOffset, yOffset) }
}
override fun onGetChildren(node: ComposeView): List<Any> {
val children = mutableListOf<Any>()
val count = node.childCount - 1
for (i in 0..count) {
val child: View = node.getChildAt(i)
children.add(child)
if (child.javaClass.simpleName.contains("AndroidComposeView") &&
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)) {
val layoutInspector = LayoutInspectorTree()
layoutInspector.hideSystemNodes = false
return transform(child, layoutInspector.convert(child))
}
}
return children
}
}

View File

@@ -0,0 +1,12 @@
// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.
package com.facebook.flipper.plugins.jetpackcompose.model
import android.view.View
import com.facebook.flipper.plugins.uidebugger.model.Bounds
class ComposeInnerViewNode(
val view: View,
) {
val bounds: Bounds = Bounds(0, 0, view.width, view.height)
}

View File

@@ -0,0 +1,59 @@
// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.
package com.facebook.flipper.plugins.jetpackcompose.model
import android.os.Build
import android.view.View
import android.view.ViewGroup
import androidx.annotation.RequiresApi
import com.facebook.flipper.plugins.uidebugger.model.*
import facebook.internal.androidx.compose.ui.inspection.inspector.InspectorNode
class ComposeNode(
private val parentComposeView: View,
val inspectorNode: InspectorNode,
xOffset: Int,
yOffset: Int
) {
val bounds: Bounds =
Bounds(
inspectorNode.left - xOffset,
inspectorNode.top - yOffset,
inspectorNode.width,
inspectorNode.height)
val children: List<Any> = collectChildren()
private fun collectChildren(): List<Any> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val viewId = inspectorNode.viewId
if (viewId != 0L) {
val view = parentComposeView.findViewByDrawingId(viewId)
if (view != null) {
return listOf(ComposeInnerViewNode(view))
}
}
}
return inspectorNode.children.map { child ->
ComposeNode(parentComposeView, child, inspectorNode.left, inspectorNode.top)
}
}
@RequiresApi(Build.VERSION_CODES.Q)
private fun View.findViewByDrawingId(drawingId: Long): View? {
if (this.uniqueDrawingId == drawingId) {
return this
}
if (this is ViewGroup) {
for (i in 0 until this.childCount) {
val child = this.getChildAt(i)
val foundView = child.findViewByDrawingId(drawingId)
if (foundView != null) {
return foundView
}
}
}
return null
}
}