This repository has been archived by the owner on Nov 22, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 954
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Summary: Introducing descriptors. Taken from the existing inspector, refreshed with a few things from Stetho, and translated to Kotlin. Note: doesn't use FlipperObject or FlipperArray. This is a very rough draft for them all, but can be used as basis for experimentation. Reviewed By: LukeDefeo Differential Revision: D38860763 fbshipit-source-id: 9f6bf4ad0e61fc40b0a773dfb8bfa80b1f397b3a
- Loading branch information
1 parent
b5bdd56
commit 6adf1d6
Showing
13 changed files
with
794 additions
and
0 deletions.
There are no files selected for viewing
73 changes: 73 additions & 0 deletions
73
...ain/java/com/facebook/flipper/plugins/uidebugger/descriptors/AbstractChainedDescriptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* | ||
* 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 | ||
|
||
/** | ||
* 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>) { | ||
if (superDescriptor !== mSuper) { | ||
check(mSuper == null) | ||
mSuper = superDescriptor | ||
} | ||
} | ||
|
||
fun getSuper(): Descriptor<T>? { | ||
return mSuper | ||
} | ||
|
||
/** Initialize a descriptor. */ | ||
override fun init() { | ||
mSuper?.init() | ||
onInit() | ||
} | ||
|
||
open fun onInit() {} | ||
|
||
/** | ||
* 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? { | ||
return onGetId(node) | ||
} | ||
|
||
abstract fun onGetId(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. | ||
*/ | ||
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>) { | ||
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?>) { | ||
mSuper?.getData(node, builder) | ||
onGetData(node, builder) | ||
} | ||
|
||
open fun onGetData(node: T, builder: MutableMap<String, Any?>) {} | ||
} |
47 changes: 47 additions & 0 deletions
47
...d/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ActivityDescriptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* 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 android.app.Activity | ||
import com.facebook.flipper.plugins.uidebugger.stetho.FragmentCompat | ||
|
||
class ActivityDescriptor : AbstractChainedDescriptor<Activity>() { | ||
override fun onInit() {} | ||
|
||
override fun onGetId(activity: Activity): String { | ||
return Integer.toString(System.identityHashCode(activity)) | ||
} | ||
|
||
override fun onGetName(activity: Activity): String { | ||
return activity.javaClass.simpleName | ||
} | ||
|
||
override fun onGetChildren(activity: Activity, children: MutableList<Any>) { | ||
activity.window?.let { window -> children.add(activity.window) } | ||
|
||
var fragments = getFragments(FragmentCompat.supportInstance, activity) | ||
for (fragment in fragments) { | ||
children.add(fragment) | ||
} | ||
|
||
fragments = getFragments(FragmentCompat.frameworkInstance, activity) | ||
for (fragment in fragments) { | ||
children.add(fragment) | ||
} | ||
} | ||
|
||
override fun onGetData(activity: Activity, builder: MutableMap<String, Any?>) {} | ||
|
||
private fun getFragments(compat: FragmentCompat<*, *, *, *>?, activity: Activity): List<Any> { | ||
if (compat == null) { | ||
return emptyList() | ||
} | ||
|
||
return compat?.getFragments(activity) | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
...rc/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ApplicationDescriptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
* 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 android.app.Activity | ||
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef | ||
import com.facebook.flipper.plugins.uidebugger.core.RootViewResolver | ||
|
||
class ApplicationDescriptor : AbstractChainedDescriptor<ApplicationRef>() { | ||
val rootResolver = RootViewResolver() | ||
|
||
override fun onInit() {} | ||
|
||
override fun onGetId(applicationRef: ApplicationRef): String { | ||
return applicationRef.application.packageName | ||
} | ||
|
||
override fun onGetName(applicationRef: ApplicationRef): String { | ||
val applicationInfo = applicationRef.application.getApplicationInfo() | ||
val stringId = applicationInfo.labelRes | ||
return if (stringId == 0) applicationInfo.nonLocalizedLabel.toString() | ||
else applicationRef.application.getString(stringId) | ||
} | ||
|
||
override fun onGetChildren(applicationRef: ApplicationRef, children: MutableList<Any>) { | ||
val activeRoots = rootResolver.listActiveRootViews() | ||
|
||
activeRoots?.let { roots -> | ||
for (root: RootViewResolver.RootView in roots) { | ||
var added = false | ||
for (activity: Activity in applicationRef.activitiesStack) { | ||
if (activity.window.decorView == root.view) { | ||
children.add(activity) | ||
added = true | ||
|
||
break | ||
} | ||
} | ||
if (!added) { | ||
children.add(root.view) | ||
} | ||
} | ||
} | ||
} | ||
|
||
override fun onGetData(applicationRef: ApplicationRef, builder: MutableMap<String, Any?>) {} | ||
} |
26 changes: 26 additions & 0 deletions
26
...oid/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ButtonDescriptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* 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 android.widget.Button | ||
|
||
class ButtonDescriptor : AbstractChainedDescriptor<Button>() { | ||
override fun init() {} | ||
|
||
override fun onGetId(button: Button): String { | ||
return Integer.toString(System.identityHashCode(button)) | ||
} | ||
|
||
override fun onGetName(button: Button): String { | ||
return button.javaClass.simpleName | ||
} | ||
|
||
override fun onGetData(button: Button, builder: MutableMap<String, Any?>) {} | ||
|
||
override fun onGetChildren(button: Button, children: MutableList<Any>) {} | ||
} |
42 changes: 42 additions & 0 deletions
42
...id/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ChainedDescriptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* 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 | ||
|
||
/** | ||
* This interface marks a Descriptor in a way that is specially understood by the register. When | ||
* registered for a particular class 'T', a Descriptor that implements this interface will be | ||
* chained (via ChainedDescriptor.setSuper) to the Descriptor that is registered for the super class | ||
* of 'T'. If the super class of 'T' doesn't have a registration, then the super-super class will be | ||
* used (and so on). This allows you to implement Descriptor for any class in an inheritance | ||
* hierarchy without having to couple it (via direct inheritance) to the super-class' Descriptor. | ||
* | ||
* To understand why this is useful, let's say you wanted to write a Descriptor for ListView. You | ||
* have three options: | ||
* | ||
* The first option is to derive directly from Descriptor and write code to describe everything | ||
* about instances of ListView, including details that are exposed by super classes such as | ||
* ViewGroup, View, and even Object. This isn't generally a very good choice because it would | ||
* require a lot of duplicated code amongst many descriptor implementations. | ||
* | ||
* The second option is to derive your ListViewDescriptor from ViewGroupDescriptor and only | ||
* implement code to describe how ListView differs from ViewGroup. This will result in a class | ||
* hierarchy that is parallel to the one that you are describing, but is also not a good choice for | ||
* two reasons (let's assume for the moment that ViewGroupDescriptor is deriving from | ||
* ViewDescriptor). The first problem is that you will need to write code for aggregating results | ||
* from the super-class in methods such as Descriptor.getChildren and Descriptor.getAttributes. The | ||
* second problem is that you'd end up with a log of fragility if you ever want to implement a | ||
* descriptor for classes that are in-between ViewGroup and ListView, e.g. AbsListView. Any | ||
* descriptor that derived from ViewGroupDescriptor and described a class deriving from AbsListView | ||
* would have to be modified to now derive from AbsListViewDescriptor. | ||
* | ||
* The third option is to implement ChainedDescriptor (e.g. by deriving from | ||
* AbstractChainedDescriptor) which solves all of these issues for you. | ||
*/ | ||
interface ChainedDescriptor<T> { | ||
fun setSuper(superDescriptor: Descriptor<T>) | ||
} |
56 changes: 56 additions & 0 deletions
56
android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/Descriptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* 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 | ||
|
||
abstract class Descriptor<T> : NodeDescriptor<T> { | ||
var descritorRegister: DescriptorRegister? = null | ||
fun setDescriptorRegister(descriptorMapping: DescriptorRegister) { | ||
this.descritorRegister = descriptorMapping | ||
} | ||
|
||
protected fun descriptorForClass(clazz: Class<*>): Descriptor<*>? { | ||
descritorRegister?.let { register -> | ||
return register.descriptorForClass(clazz) | ||
} | ||
return null | ||
} | ||
|
||
protected fun descriptorForObject(obj: Any): Descriptor<*>? { | ||
descritorRegister?.let { register -> | ||
return register.descriptorForClass(obj::class.java) | ||
} | ||
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() | ||
// } | ||
} |
67 changes: 67 additions & 0 deletions
67
...d/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/DescriptorRegister.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
* 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 android.app.Activity | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import android.view.Window | ||
import android.widget.Button | ||
import android.widget.TextView | ||
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef | ||
|
||
class DescriptorRegister { | ||
private val register: MutableMap<Class<*>, Descriptor<*>> = HashMap() | ||
|
||
companion object { | ||
|
||
fun withDefaults(): DescriptorRegister { | ||
val mapping = DescriptorRegister() | ||
mapping.register(Any::class.java, ObjectDescriptor()) | ||
mapping.register(ApplicationRef::class.java, ApplicationDescriptor()) | ||
mapping.register(Activity::class.java, ActivityDescriptor()) | ||
mapping.register(Window::class.java, WindowDescriptor()) | ||
mapping.register(ViewGroup::class.java, ViewGroupDescriptor()) | ||
mapping.register(View::class.java, ViewDescriptor()) | ||
mapping.register(TextView::class.java, TextViewDescriptor()) | ||
mapping.register(Button::class.java, ButtonDescriptor()) | ||
|
||
for (clazz in mapping.register.keys) { | ||
val descriptor: Descriptor<*>? = mapping.register[clazz] | ||
descriptor?.let { descriptor -> | ||
if (descriptor is ChainedDescriptor<*>) { | ||
val chainedDescriptor = descriptor as ChainedDescriptor<Any> | ||
val superClass: Class<*> = clazz.getSuperclass() | ||
val superDescriptor: Descriptor<*>? = mapping.descriptorForClass(superClass) | ||
superDescriptor?.let { superDescriptor -> | ||
chainedDescriptor.setSuper(superDescriptor as Descriptor<Any>) | ||
} | ||
} | ||
} | ||
} | ||
|
||
for (descriptor in mapping.register.values) { | ||
descriptor.setDescriptorRegister(mapping) | ||
} | ||
|
||
return mapping | ||
} | ||
} | ||
|
||
fun <T> register(clazz: Class<T>, descriptor: Descriptor<T>) { | ||
register[clazz] = descriptor | ||
} | ||
|
||
fun descriptorForClass(clazz: Class<*>): Descriptor<*>? { | ||
var clazz = clazz | ||
while (!register.containsKey(clazz)) { | ||
clazz = clazz.superclass | ||
} | ||
return register[clazz] | ||
} | ||
} |
Oops, something went wrong.