Introduced concept of active child
Summary: A node can have an active child, if present we assume all others are inactive and we don't traverse them. This means the activities not on top and view pager views not active will not be scanned. Additionally on the desktop we are automatically collapsing these views. The net result is a lot less work done on the main thread Reviewed By: lblasa Differential Revision: D39310126 fbshipit-source-id: ebd0c69d46f2d42fe42e678c8327fcdc73d08385
This commit is contained in:
committed by
Facebook GitHub Bot
parent
a9fe381076
commit
c76c993ce4
@@ -40,19 +40,37 @@ class LayoutTraversal(
|
|||||||
descriptor.getChildren(node, children)
|
descriptor.getChildren(node, children)
|
||||||
|
|
||||||
val childrenIds = mutableListOf<String>()
|
val childrenIds = mutableListOf<String>()
|
||||||
|
|
||||||
|
val activeChild = descriptor.getActiveChild(node)
|
||||||
for (child in children) {
|
for (child in children) {
|
||||||
// it might make sense one day to remove id from the descriptor since its always the
|
// it might make sense one day to remove id from the descriptor since its always the
|
||||||
// hash code
|
// hash code
|
||||||
val childDescriptor =
|
val childDescriptor =
|
||||||
descriptorRegister.descriptorForClassUnsafe(child::class.java).asAny()
|
descriptorRegister.descriptorForClassUnsafe(child::class.java).asAny()
|
||||||
childrenIds.add(childDescriptor.getId(child))
|
childrenIds.add(childDescriptor.getId(child))
|
||||||
stack.add(child)
|
// if there is an active child then dont traverse it
|
||||||
|
if (activeChild == null) {
|
||||||
|
stack.add(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var activeChildId: String? = null
|
||||||
|
if (activeChild != null) {
|
||||||
|
stack.add(activeChild)
|
||||||
|
activeChildId =
|
||||||
|
descriptorRegister.descriptorForClassUnsafe(activeChild.javaClass).getId(activeChild)
|
||||||
}
|
}
|
||||||
|
|
||||||
val attributes = mutableMapOf<String, InspectableObject>()
|
val attributes = mutableMapOf<String, InspectableObject>()
|
||||||
descriptor.getData(node, attributes)
|
descriptor.getData(node, attributes)
|
||||||
|
|
||||||
result.add(Node(descriptor.getId(node), descriptor.getName(node), attributes, childrenIds))
|
result.add(
|
||||||
|
Node(
|
||||||
|
descriptor.getId(node),
|
||||||
|
descriptor.getName(node),
|
||||||
|
attributes,
|
||||||
|
childrenIds,
|
||||||
|
activeChildId))
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e(LogTag, "Error while processing node ${node.javaClass.name} ${node} ", exception)
|
Log.e(LogTag, "Error while processing node ${node.javaClass.name} ${node} ", exception)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,13 +33,7 @@ class NativeScanScheduler(val context: Context) : Scheduler.Task<ScanResult> {
|
|||||||
|
|
||||||
Log.d(
|
Log.d(
|
||||||
"LAYOUT_SCHEDULER",
|
"LAYOUT_SCHEDULER",
|
||||||
Thread.currentThread().name +
|
"${Thread.currentThread().name}${Looper.myLooper()} produced: ${nodes.count()} nodes")
|
||||||
Looper.myLooper() +
|
|
||||||
", produced: " +
|
|
||||||
{
|
|
||||||
nodes.count()
|
|
||||||
} +
|
|
||||||
" nodes")
|
|
||||||
|
|
||||||
return ScanResult(txId++, start, scanEnd, nodes)
|
return ScanResult(txId++, start, scanEnd, nodes)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ abstract class AbstractChainedDescriptor<T> : Descriptor<T>(), ChainedDescriptor
|
|||||||
|
|
||||||
open fun onInit() {}
|
open fun onInit() {}
|
||||||
|
|
||||||
|
final override fun getActiveChild(node: T): Any? {
|
||||||
|
// ask each descriptor in the chain for an active child, if none available look up the chain
|
||||||
|
// until no more super descriptors
|
||||||
|
return onGetActiveChild(node) ?: mSuper?.getActiveChild(node)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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].
|
||||||
@@ -52,6 +58,8 @@ abstract class AbstractChainedDescriptor<T> : Descriptor<T>(), ChainedDescriptor
|
|||||||
return onGetName(node)
|
return onGetName(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract fun onGetActiveChild(node: T): Any?
|
||||||
|
|
||||||
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. */
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ import com.facebook.flipper.plugins.uidebugger.stetho.FragmentCompat
|
|||||||
|
|
||||||
class ActivityDescriptor : AbstractChainedDescriptor<Activity>() {
|
class ActivityDescriptor : AbstractChainedDescriptor<Activity>() {
|
||||||
override fun onInit() {}
|
override fun onInit() {}
|
||||||
|
override fun onGetActiveChild(node: Activity): Any? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
override fun onGetId(activity: Activity): String {
|
override fun onGetId(activity: Activity): String {
|
||||||
return Integer.toString(System.identityHashCode(activity))
|
return Integer.toString(System.identityHashCode(activity))
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ class ApplicationRefDescriptor : AbstractChainedDescriptor<ApplicationRef>() {
|
|||||||
val rootResolver = RootViewResolver()
|
val rootResolver = RootViewResolver()
|
||||||
|
|
||||||
override fun onInit() {}
|
override fun onInit() {}
|
||||||
|
override fun onGetActiveChild(node: ApplicationRef): Any? {
|
||||||
|
return if (node.activitiesStack.size > 0) node.activitiesStack.last() else null
|
||||||
|
}
|
||||||
|
|
||||||
override fun onGetId(applicationRef: ApplicationRef): String {
|
override fun onGetId(applicationRef: ApplicationRef): String {
|
||||||
return applicationRef.application.packageName
|
return applicationRef.application.packageName
|
||||||
@@ -38,7 +41,6 @@ class ApplicationRefDescriptor : AbstractChainedDescriptor<ApplicationRef>() {
|
|||||||
if (activity.window.decorView == root.view) {
|
if (activity.window.decorView == root.view) {
|
||||||
children.add(activity)
|
children.add(activity)
|
||||||
added = true
|
added = true
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,4 +26,7 @@ class ButtonDescriptor : AbstractChainedDescriptor<Button>() {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
override fun onGetChildren(button: Button, children: MutableList<Any>) {}
|
override fun onGetChildren(button: Button, children: MutableList<Any>) {}
|
||||||
|
override fun onGetActiveChild(node: Button): Any? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,31 +26,4 @@ abstract class Descriptor<T> : NodeDescriptor<T> {
|
|||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// override fun inspect(obj: Any): FlipperObject {
|
|
||||||
// val descriptor = descriptorForObject(obj)
|
|
||||||
// descriptor?.let { descriptor ->
|
|
||||||
// return (descriptor as Descriptor<Any>).get(obj)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return FlipperObject.Builder().build()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun get(node: T): FlipperObject {
|
|
||||||
// val builder = FlipperObject.Builder()
|
|
||||||
//
|
|
||||||
// val propsBuilder = FlipperObject.Builder()
|
|
||||||
// getData(node, propsBuilder)
|
|
||||||
//
|
|
||||||
// val childrenBuilder = FlipperArray.Builder()
|
|
||||||
// getChildren(node, childrenBuilder)
|
|
||||||
//
|
|
||||||
// builder
|
|
||||||
// .put("key", getId(node))
|
|
||||||
// .put("title", getName(node))
|
|
||||||
// .put("data", propsBuilder)
|
|
||||||
// .put("children", childrenBuilder)
|
|
||||||
//
|
|
||||||
// return builder.build()
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import android.view.ViewGroup
|
|||||||
import android.view.Window
|
import android.view.Window
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.viewpager.widget.ViewPager
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.UIDebuggerException
|
import com.facebook.flipper.plugins.uidebugger.common.UIDebuggerException
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ class DescriptorRegister {
|
|||||||
mapping.register(View::class.java, ViewDescriptor())
|
mapping.register(View::class.java, ViewDescriptor())
|
||||||
mapping.register(TextView::class.java, TextViewDescriptor())
|
mapping.register(TextView::class.java, TextViewDescriptor())
|
||||||
mapping.register(Button::class.java, ButtonDescriptor())
|
mapping.register(Button::class.java, ButtonDescriptor())
|
||||||
|
mapping.register(ViewPager::class.java, ViewPagerDescriptor())
|
||||||
|
|
||||||
for (clazz in mapping.register.keys) {
|
for (clazz in mapping.register.keys) {
|
||||||
val descriptor: Descriptor<*>? = mapping.register[clazz]
|
val descriptor: Descriptor<*>? = mapping.register[clazz]
|
||||||
|
|||||||
@@ -28,6 +28,12 @@ interface NodeDescriptor<T> {
|
|||||||
/** The children this node exposes in the inspector. */
|
/** The children this node exposes in the inspector. */
|
||||||
fun getChildren(node: T, children: MutableList<Any>)
|
fun getChildren(node: T, children: MutableList<Any>)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you have overlapping children this indicates which child is active / on top, we will only
|
||||||
|
* listen to / traverse this child. If return null we assume all children are 'active'
|
||||||
|
*/
|
||||||
|
fun getActiveChild(node: T): Any?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the data to show for this node in the sidebar of the inspector. The object will be shown in
|
* Get the data to show for this node in the sidebar of the inspector. The object will be shown in
|
||||||
* order and with a header matching the given name.
|
* order and with a header matching the given name.
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
|||||||
|
|
||||||
class ObjectDescriptor : Descriptor<Any>() {
|
class ObjectDescriptor : Descriptor<Any>() {
|
||||||
override fun init() {}
|
override fun init() {}
|
||||||
|
override fun getActiveChild(node: Any): Any? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
override fun getId(obj: Any): String {
|
override fun getId(obj: Any): String {
|
||||||
return Integer.toString(System.identityHashCode(obj))
|
return Integer.toString(System.identityHashCode(obj))
|
||||||
|
|||||||
@@ -26,4 +26,8 @@ class TextViewDescriptor : AbstractChainedDescriptor<TextView>() {
|
|||||||
textView: TextView,
|
textView: TextView,
|
||||||
attributeSections: MutableMap<String, InspectableObject>
|
attributeSections: MutableMap<String, InspectableObject>
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
override fun onGetActiveChild(node: TextView): Any? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,4 +256,8 @@ class ViewDescriptor : AbstractChainedDescriptor<View>() {
|
|||||||
} catch (ignored: Exception) {}
|
} catch (ignored: Exception) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onGetActiveChild(node: View): Any? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,4 +85,8 @@ class ViewGroupDescriptor : AbstractChainedDescriptor<ViewGroup>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onGetActiveChild(node: ViewGroup): Any? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
|
import androidx.viewpager.widget.ViewPager
|
||||||
|
|
||||||
|
class ViewPagerDescriptor : AbstractChainedDescriptor<ViewPager>() {
|
||||||
|
|
||||||
|
override fun onGetId(node: ViewPager): String = System.identityHashCode(node).toString()
|
||||||
|
|
||||||
|
override fun onGetName(node: ViewPager): String = node.javaClass.simpleName
|
||||||
|
|
||||||
|
override fun onGetActiveChild(node: ViewPager): Any? = node.getChildAt(node.currentItem)
|
||||||
|
}
|
||||||
@@ -28,4 +28,8 @@ class WindowDescriptor : AbstractChainedDescriptor<Window>() {
|
|||||||
window: Window,
|
window: Window,
|
||||||
attributeSections: MutableMap<String, InspectableObject>
|
attributeSections: MutableMap<String, InspectableObject>
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
override fun onGetActiveChild(node: Window): Any? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ data class Node(
|
|||||||
val id: String,
|
val id: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
val attributes: Map<String, InspectableObject>,
|
val attributes: Map<String, InspectableObject>,
|
||||||
val children: List<String>
|
val children: List<String>,
|
||||||
|
val activeChild: String?,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,9 +24,20 @@ import {DownOutlined} from '@ant-design/icons';
|
|||||||
import {useHotkeys} from 'react-hotkeys-hook';
|
import {useHotkeys} from 'react-hotkeys-hook';
|
||||||
import {Id, UINode} from '../types';
|
import {Id, UINode} from '../types';
|
||||||
|
|
||||||
function nodesToAntTree(root: Id, nodes: Map<Id, UINode>): DataNode {
|
function nodesToAntTree(root: Id, nodes: Map<Id, UINode>): [DataNode, Id[]] {
|
||||||
|
const inactive: Id[] = [];
|
||||||
|
|
||||||
function uiNodeToAntNode(id: Id): DataNode {
|
function uiNodeToAntNode(id: Id): DataNode {
|
||||||
const node = nodes.get(id);
|
const node = nodes.get(id);
|
||||||
|
|
||||||
|
if (node?.activeChild) {
|
||||||
|
for (const child of node.children) {
|
||||||
|
if (child !== node?.activeChild) {
|
||||||
|
inactive.push(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: id,
|
key: id,
|
||||||
title: node?.name,
|
title: node?.name,
|
||||||
@@ -34,7 +45,7 @@ function nodesToAntTree(root: Id, nodes: Map<Id, UINode>): DataNode {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return uiNodeToAntNode(root);
|
return [uiNodeToAntNode(root), inactive];
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDiff(start: number, end: number): string {
|
function formatDiff(start: number, end: number): string {
|
||||||
@@ -119,7 +130,7 @@ export function Component() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (rootId) {
|
if (rootId) {
|
||||||
const antTree = nodesToAntTree(rootId, nodes);
|
const [antTree, inactive] = nodesToAntTree(rootId, nodes);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Layout.ScrollContainer>
|
<Layout.ScrollContainer>
|
||||||
@@ -130,7 +141,9 @@ export function Component() {
|
|||||||
setSelectedNode(selected[0] as string);
|
setSelectedNode(selected[0] as string);
|
||||||
}}
|
}}
|
||||||
defaultExpandAll
|
defaultExpandAll
|
||||||
expandedKeys={[...nodes.keys()]}
|
expandedKeys={[...nodes.keys()].filter(
|
||||||
|
(key) => !inactive.includes(key),
|
||||||
|
)}
|
||||||
switcherIcon={<DownOutlined />}
|
switcherIcon={<DownOutlined />}
|
||||||
treeData={[antTree]}
|
treeData={[antTree]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -42,4 +42,5 @@ export type UINode = {
|
|||||||
name: string;
|
name: string;
|
||||||
attributes: Record<string, Inspectable>;
|
attributes: Record<string, Inspectable>;
|
||||||
children: Id[];
|
children: Id[];
|
||||||
|
activeChild?: Id;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user