Addresses fragments tracking and different bugs/warnings
Summary: ^ After this change lands, it is safe to remove most of the Stetho fragment support types. Reviewed By: LukeDefeo Differential Revision: D39460121 fbshipit-source-id: 0e7d4ce71e828ee7bc9c6e945b8fe27dbd6f08f8
This commit is contained in:
committed by
Facebook GitHub Bot
parent
e8392bdceb
commit
86364cbd40
@@ -9,8 +9,8 @@ package com.facebook.flipper.plugins.uidebugger.litho
|
|||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||||
import com.facebook.flipper.plugins.uidebugger.TreeObserver
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.Context
|
import com.facebook.flipper.plugins.uidebugger.core.Context
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserver
|
||||||
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverBuilder
|
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverBuilder
|
||||||
import com.facebook.litho.LithoView
|
import com.facebook.litho.LithoView
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Application
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
|
||||||
|
object ActivityTracker : Application.ActivityLifecycleCallbacks {
|
||||||
|
interface ActivityStackChangedListener {
|
||||||
|
fun onActivityAdded(activity: Activity, stack: List<Activity>)
|
||||||
|
fun onActivityStackChanged(stack: List<Activity>)
|
||||||
|
fun onActivityDestroyed(activity: Activity, stack: List<Activity>)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val activities: MutableList<WeakReference<Activity>> = mutableListOf()
|
||||||
|
private val trackedActivities: MutableSet<Int> = mutableSetOf()
|
||||||
|
private var activityStackChangedListener: ActivityStackChangedListener? = null
|
||||||
|
|
||||||
|
fun setActivityStackChangedListener(listener: ActivityStackChangedListener?) {
|
||||||
|
activityStackChangedListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
fun start(application: Application) {
|
||||||
|
initialiseActivities()
|
||||||
|
application.registerActivityLifecycleCallbacks(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun trackActivity(activity: Activity) {
|
||||||
|
if (trackedActivities.contains(System.identityHashCode(activity))) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
trackedActivities.add(System.identityHashCode(activity))
|
||||||
|
activities.add(WeakReference<Activity>(activity))
|
||||||
|
|
||||||
|
FragmentTracker.trackFragmentsOfActivity(activity)
|
||||||
|
|
||||||
|
activityStackChangedListener?.onActivityAdded(activity, this.activitiesStack)
|
||||||
|
activityStackChangedListener?.onActivityStackChanged(this.activitiesStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun untrackActivity(activity: Activity) {
|
||||||
|
trackedActivities.remove(System.identityHashCode(activity))
|
||||||
|
val activityIterator: MutableIterator<WeakReference<Activity>> = activities.iterator()
|
||||||
|
|
||||||
|
while (activityIterator.hasNext()) {
|
||||||
|
if (activityIterator.next().get() === activity) {
|
||||||
|
activityIterator.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FragmentTracker.untrackFragmentsOfActivity(activity)
|
||||||
|
|
||||||
|
activityStackChangedListener?.onActivityDestroyed(activity, this.activitiesStack)
|
||||||
|
activityStackChangedListener?.onActivityStackChanged(this.activitiesStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
|
||||||
|
trackActivity(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityStarted(activity: Activity) {
|
||||||
|
trackActivity(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResumed(activity: Activity) {}
|
||||||
|
|
||||||
|
override fun onActivityPaused(activity: Activity) {}
|
||||||
|
|
||||||
|
override fun onActivityStopped(activity: Activity) {}
|
||||||
|
|
||||||
|
override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {}
|
||||||
|
|
||||||
|
override fun onActivityDestroyed(activity: Activity) {
|
||||||
|
untrackActivity(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
val activitiesStack: List<Activity>
|
||||||
|
get() {
|
||||||
|
val stack: MutableList<Activity> = ArrayList(activities.size)
|
||||||
|
val activityIterator: MutableIterator<WeakReference<Activity>> = activities.iterator()
|
||||||
|
while (activityIterator.hasNext()) {
|
||||||
|
val activity: Activity? = activityIterator.next().get()
|
||||||
|
if (activity == null) {
|
||||||
|
activityIterator.remove()
|
||||||
|
} else {
|
||||||
|
stack.add(activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("PrivateApi", "DiscouragedPrivateApi")
|
||||||
|
fun initialiseActivities() {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val activityThreadClass: Class<*> = Class.forName("android.app.ActivityThread")
|
||||||
|
val currentActivityThreadMethod: Method =
|
||||||
|
activityThreadClass.getMethod("currentActivityThread")
|
||||||
|
val currentActivityThread: Any? = currentActivityThreadMethod.invoke(null)
|
||||||
|
|
||||||
|
currentActivityThread?.let { activityThread ->
|
||||||
|
val mActivitiesField: Field = activityThreadClass.getDeclaredField("mActivities")
|
||||||
|
mActivitiesField.isAccessible = true
|
||||||
|
val mActivities = mActivitiesField.get(activityThread) as android.util.ArrayMap<*, *>
|
||||||
|
for (record in mActivities.values) {
|
||||||
|
val recordClass: Class<*> = record.javaClass
|
||||||
|
val activityField: Field = recordClass.getDeclaredField("activity")
|
||||||
|
activityField.isAccessible = true
|
||||||
|
|
||||||
|
val activity = activityField.get(record)
|
||||||
|
if (activity != null && activity is Activity) {
|
||||||
|
trackActivity(activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,68 +9,25 @@ package com.facebook.flipper.plugins.uidebugger.core
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
|
|
||||||
class ApplicationRef(val application: Application) : Application.ActivityLifecycleCallbacks {
|
class ApplicationRef(val application: Application) {
|
||||||
interface ActivityStackChangedListener {
|
init {
|
||||||
fun onActivityAdded(activity: Activity, stack: List<Activity>)
|
ActivityTracker.start(application)
|
||||||
fun onActivityStackChanged(stack: List<Activity>)
|
|
||||||
fun onActivityDestroyed(activity: Activity, stack: List<Activity>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootsResolver: RootViewResolver
|
val rootsResolver: RootViewResolver = RootViewResolver()
|
||||||
private val activities: MutableList<WeakReference<Activity>>
|
|
||||||
private var activityStackChangedlistener: ActivityStackChangedListener? = null
|
|
||||||
|
|
||||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
|
||||||
activities.add(WeakReference<Activity>(activity))
|
|
||||||
activityStackChangedlistener?.onActivityAdded(activity, this.activitiesStack)
|
|
||||||
activityStackChangedlistener?.onActivityStackChanged(this.activitiesStack)
|
|
||||||
}
|
|
||||||
override fun onActivityStarted(activity: Activity) {}
|
|
||||||
override fun onActivityResumed(activity: Activity) {}
|
|
||||||
override fun onActivityPaused(activity: Activity) {}
|
|
||||||
override fun onActivityStopped(activity: Activity) {}
|
|
||||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
|
|
||||||
override fun onActivityDestroyed(activity: Activity) {
|
|
||||||
val activityIterator: MutableIterator<WeakReference<Activity>> = activities.iterator()
|
|
||||||
|
|
||||||
while (activityIterator.hasNext()) {
|
|
||||||
if (activityIterator.next().get() === activity) {
|
|
||||||
activityIterator.remove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
activityStackChangedlistener?.onActivityDestroyed(activity, this.activitiesStack)
|
|
||||||
activityStackChangedlistener?.onActivityStackChanged(this.activitiesStack)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setActivityStackChangedListener(listener: ActivityStackChangedListener?) {
|
|
||||||
activityStackChangedlistener = listener
|
|
||||||
}
|
|
||||||
|
|
||||||
val activitiesStack: List<Activity>
|
val activitiesStack: List<Activity>
|
||||||
get() {
|
get() {
|
||||||
val stack: MutableList<Activity> = ArrayList<Activity>(activities.size)
|
return ActivityTracker.activitiesStack
|
||||||
val activityIterator: MutableIterator<WeakReference<Activity>> = activities.iterator()
|
|
||||||
while (activityIterator.hasNext()) {
|
|
||||||
val activity: Activity? = activityIterator.next().get()
|
|
||||||
if (activity == null) {
|
|
||||||
activityIterator.remove()
|
|
||||||
} else {
|
|
||||||
stack.add(activity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stack
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootViews: List<View>
|
val rootViews: List<View>
|
||||||
get() {
|
get() {
|
||||||
val roots = rootsResolver.listActiveRootViews()
|
val activeRootViews = rootsResolver.listActiveRootViews()
|
||||||
roots?.let { roots ->
|
activeRootViews?.let { roots ->
|
||||||
val viewRoots: MutableList<View> = ArrayList<View>(roots.size)
|
val viewRoots: MutableList<View> = ArrayList(roots.size)
|
||||||
for (root in roots) {
|
for (root in roots) {
|
||||||
viewRoots.add(root.view)
|
viewRoots.add(root.view)
|
||||||
}
|
}
|
||||||
@@ -79,10 +36,4 @@ class ApplicationRef(val application: Application) : Application.ActivityLifecyc
|
|||||||
|
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
|
||||||
rootsResolver = RootViewResolver()
|
|
||||||
application.registerActivityLifecycleCallbacks(this)
|
|
||||||
activities = ArrayList<WeakReference<Activity>>()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,271 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.app.Activity
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
object FragmentTracker {
|
||||||
|
|
||||||
|
class FragmentRef {
|
||||||
|
val supportFragment: WeakReference<androidx.fragment.app.Fragment>?
|
||||||
|
val frameworkFragment: WeakReference<android.app.Fragment>?
|
||||||
|
val isSupportFragment: Boolean
|
||||||
|
|
||||||
|
constructor(supportFragment: androidx.fragment.app.Fragment) {
|
||||||
|
this.supportFragment = WeakReference(supportFragment)
|
||||||
|
this.frameworkFragment = null
|
||||||
|
this.isSupportFragment = true
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(frameworkFragment: android.app.Fragment) {
|
||||||
|
this.supportFragment = null
|
||||||
|
this.frameworkFragment = WeakReference(frameworkFragment)
|
||||||
|
this.isSupportFragment = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
if (isSupportFragment) {
|
||||||
|
val fragment = supportFragment?.get()
|
||||||
|
fragment?.let { f ->
|
||||||
|
return System.identityHashCode(f)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val fragment = frameworkFragment?.get()
|
||||||
|
fragment?.let { f ->
|
||||||
|
return System.identityHashCode(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as FragmentRef
|
||||||
|
|
||||||
|
if (isSupportFragment != other.isSupportFragment) return false
|
||||||
|
if (isSupportFragment && (supportFragment != other.supportFragment)) return false
|
||||||
|
else if (frameworkFragment != other.frameworkFragment) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val view: View?
|
||||||
|
get() {
|
||||||
|
if (isSupportFragment) {
|
||||||
|
val fragment = supportFragment?.get()
|
||||||
|
fragment?.let { f ->
|
||||||
|
return f.view
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val fragment = frameworkFragment?.get()
|
||||||
|
fragment?.let { f ->
|
||||||
|
return f.view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
if (isSupportFragment) {
|
||||||
|
val fragment = supportFragment?.get()
|
||||||
|
fragment?.let { f ->
|
||||||
|
return "$f"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val fragment = frameworkFragment?.get()
|
||||||
|
fragment?.let { f ->
|
||||||
|
return "$f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val activityFragments: MutableMap<Int, MutableSet<FragmentRef>> = mutableMapOf()
|
||||||
|
private val viewFragment: MutableMap<Int, FragmentRef> = mutableMapOf()
|
||||||
|
|
||||||
|
private val supportLibraryLifecycleTracker =
|
||||||
|
object : androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks() {
|
||||||
|
override fun onFragmentAttached(
|
||||||
|
fm: androidx.fragment.app.FragmentManager,
|
||||||
|
f: androidx.fragment.app.Fragment,
|
||||||
|
context: android.content.Context
|
||||||
|
) {
|
||||||
|
super.onFragmentAttached(fm, f, context)
|
||||||
|
|
||||||
|
f.activity?.let { activity ->
|
||||||
|
val fragmentRef = FragmentRef(f)
|
||||||
|
activityFragments[System.identityHashCode(activity)]?.add(fragmentRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFragmentViewCreated(
|
||||||
|
fm: androidx.fragment.app.FragmentManager,
|
||||||
|
f: androidx.fragment.app.Fragment,
|
||||||
|
v: View,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
) {
|
||||||
|
super.onFragmentViewCreated(fm, f, v, savedInstanceState)
|
||||||
|
|
||||||
|
val fragmentRef = FragmentRef(f)
|
||||||
|
viewFragment[System.identityHashCode(v)] = fragmentRef
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFragmentViewDestroyed(
|
||||||
|
fm: androidx.fragment.app.FragmentManager,
|
||||||
|
f: androidx.fragment.app.Fragment
|
||||||
|
) {
|
||||||
|
super.onFragmentViewDestroyed(fm, f)
|
||||||
|
for (entry in viewFragment) {
|
||||||
|
if (entry.value.supportFragment == f) {
|
||||||
|
viewFragment.remove(entry.key)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFragmentDetached(
|
||||||
|
fm: androidx.fragment.app.FragmentManager,
|
||||||
|
f: androidx.fragment.app.Fragment
|
||||||
|
) {
|
||||||
|
super.onFragmentDetached(fm, f)
|
||||||
|
|
||||||
|
f.activity?.let { activity ->
|
||||||
|
val fragmentRef = FragmentRef(f)
|
||||||
|
activityFragments[System.identityHashCode(activity)]?.remove(fragmentRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.view?.let { view -> viewFragment.remove(System.identityHashCode(view)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var frameworkLifecycleTracker: Any? = null
|
||||||
|
|
||||||
|
fun trackFragmentsOfActivity(activity: Activity) {
|
||||||
|
activityFragments[System.identityHashCode(activity)] = mutableSetOf()
|
||||||
|
if (activity is androidx.fragment.app.FragmentActivity) {
|
||||||
|
activity.supportFragmentManager.registerFragmentLifecycleCallbacks(
|
||||||
|
supportLibraryLifecycleTracker, true)
|
||||||
|
} else {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && frameworkLifecycleTracker != null) {
|
||||||
|
activity.fragmentManager.registerFragmentLifecycleCallbacks(
|
||||||
|
frameworkLifecycleTracker as android.app.FragmentManager.FragmentLifecycleCallbacks,
|
||||||
|
true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun untrackFragmentsOfActivity(activity: Activity) {
|
||||||
|
activityFragments[System.identityHashCode(activity)]?.let { fragments ->
|
||||||
|
fragments.forEach { f ->
|
||||||
|
f.view?.let { view -> viewFragment.remove(System.identityHashCode(view)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activityFragments.remove(System.identityHashCode(activity))
|
||||||
|
|
||||||
|
if (activity is androidx.fragment.app.FragmentActivity) {
|
||||||
|
activity.supportFragmentManager.unregisterFragmentLifecycleCallbacks(
|
||||||
|
supportLibraryLifecycleTracker)
|
||||||
|
} else {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && frameworkLifecycleTracker != null) {
|
||||||
|
activity.fragmentManager.unregisterFragmentLifecycleCallbacks(
|
||||||
|
frameworkLifecycleTracker as android.app.FragmentManager.FragmentLifecycleCallbacks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFragment(view: View): Any? {
|
||||||
|
val key = System.identityHashCode(view)
|
||||||
|
val fragmentRef = viewFragment[key]
|
||||||
|
|
||||||
|
fragmentRef?.let { fragment ->
|
||||||
|
fragment.supportFragment?.get()?.let { f ->
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment.frameworkFragment?.get()?.let { f ->
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDialogFragments(activity: Activity): List<Any> {
|
||||||
|
val key = System.identityHashCode(activity)
|
||||||
|
val fragments = mutableListOf<Any>()
|
||||||
|
|
||||||
|
activityFragments[key]?.forEach { fragmentRef ->
|
||||||
|
fragmentRef.supportFragment?.get()?.let { fragment ->
|
||||||
|
if (androidx.fragment.app.DialogFragment::class.java.isInstance(fragment)) {
|
||||||
|
fragments.add(fragmentRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fragmentRef.frameworkFragment?.get()?.let { fragment ->
|
||||||
|
if (android.app.DialogFragment::class.java.isInstance(fragment)) {
|
||||||
|
fragments.add(fragmentRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fragments
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
||||||
|
Class.forName("android.app.FragmentManager\$FragmentLifecycleCallbacks") != null) {
|
||||||
|
frameworkLifecycleTracker =
|
||||||
|
object : android.app.FragmentManager.FragmentLifecycleCallbacks() {
|
||||||
|
override fun onFragmentAttached(
|
||||||
|
fm: android.app.FragmentManager?,
|
||||||
|
f: android.app.Fragment?,
|
||||||
|
context: android.content.Context?
|
||||||
|
) {
|
||||||
|
super.onFragmentAttached(fm, f, context)
|
||||||
|
f?.let { fragment ->
|
||||||
|
val fragmentRef = FragmentRef(fragment)
|
||||||
|
activityFragments[System.identityHashCode(fragment.activity)]?.add(fragmentRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFragmentViewCreated(
|
||||||
|
fm: android.app.FragmentManager?,
|
||||||
|
f: android.app.Fragment?,
|
||||||
|
v: View?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
) {
|
||||||
|
super.onFragmentViewCreated(fm, f, v, savedInstanceState)
|
||||||
|
if (f != null && v != null) {
|
||||||
|
val fragmentRef = FragmentRef(f)
|
||||||
|
viewFragment[System.identityHashCode(v)] = fragmentRef
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFragmentDetached(
|
||||||
|
fm: android.app.FragmentManager?,
|
||||||
|
f: android.app.Fragment?
|
||||||
|
) {
|
||||||
|
super.onFragmentDetached(fm, f)
|
||||||
|
f?.let { fragment ->
|
||||||
|
val fragmentRef = FragmentRef(fragment)
|
||||||
|
activityFragments[System.identityHashCode(fragment.activity)]?.remove(fragmentRef)
|
||||||
|
|
||||||
|
fragment.view?.let { view -> viewFragment.remove(System.identityHashCode(view)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,7 +46,7 @@ class LayoutTraversal(
|
|||||||
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))
|
||||||
// if there is an active child then dont traverse it
|
// if there is an active child then don't traverse it
|
||||||
if (activeChild == null) {
|
if (activeChild == null) {
|
||||||
stack.add(child)
|
stack.add(child)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
|
||||||
/** Layout Visitor traverses the entire view hierarchy from a given root. */
|
/** Layout Visitor traverses the entire view hierarchy from a given root. */
|
||||||
class LayoutVisitor(val visitor: Visitor) {
|
class LayoutVisitor(private val visitor: Visitor) {
|
||||||
interface Visitor {
|
interface Visitor {
|
||||||
fun visit(view: View)
|
fun visit(view: View)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import android.view.WindowManager
|
|||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
import java.util.ArrayList
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides access to all root views in an application.
|
* Provides access to all root views in an application.
|
||||||
@@ -125,21 +124,23 @@ class RootViewResolver {
|
|||||||
var params: List<WindowManager.LayoutParams>? = null
|
var params: List<WindowManager.LayoutParams>? = null
|
||||||
try {
|
try {
|
||||||
viewsField?.let { field ->
|
viewsField?.let { field ->
|
||||||
if (Build.VERSION.SDK_INT < 19) {
|
views =
|
||||||
val arr = field[windowManagerObj] as Array<View>
|
if (Build.VERSION.SDK_INT < 19) {
|
||||||
views = arr.toList()
|
val arr = field[windowManagerObj] as Array<View>
|
||||||
} else {
|
arr.toList()
|
||||||
views = field[windowManagerObj] as List<View>
|
} else {
|
||||||
}
|
field[windowManagerObj] as List<View>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
paramsField?.let { field ->
|
paramsField?.let { field ->
|
||||||
if (Build.VERSION.SDK_INT < 19) {
|
params =
|
||||||
val arr = field[windowManagerObj] as Array<WindowManager.LayoutParams>
|
if (Build.VERSION.SDK_INT < 19) {
|
||||||
params = arr.toList() as List<WindowManager.LayoutParams>
|
val arr = field[windowManagerObj] as Array<WindowManager.LayoutParams>
|
||||||
} else {
|
arr.toList() as List<WindowManager.LayoutParams>
|
||||||
params = field[windowManagerObj] as List<WindowManager.LayoutParams>
|
} else {
|
||||||
}
|
field[windowManagerObj] as List<WindowManager.LayoutParams>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (re: RuntimeException) {
|
} catch (re: RuntimeException) {
|
||||||
return null
|
return null
|
||||||
@@ -150,12 +151,12 @@ class RootViewResolver {
|
|||||||
val roots = mutableListOf<RootView>()
|
val roots = mutableListOf<RootView>()
|
||||||
views?.let { views ->
|
views?.let { views ->
|
||||||
params?.let { params ->
|
params?.let { params ->
|
||||||
for (i in views.indices) {
|
if (views.size == params.size) {
|
||||||
val view = views[i]
|
for (i in views.indices) {
|
||||||
// TODO FIX, len(param) is not always the same as len(views) For now just use first
|
val view = views[i]
|
||||||
|
val param = params[i]
|
||||||
// params
|
roots.add(RootView(view, param))
|
||||||
roots.add(RootView(view, null))
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ 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.common.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.stetho.FragmentCompat
|
import com.facebook.flipper.plugins.uidebugger.core.FragmentTracker
|
||||||
|
|
||||||
object ActivityDescriptor : ChainedDescriptor<Activity>() {
|
object ActivityDescriptor : ChainedDescriptor<Activity>() {
|
||||||
|
|
||||||
@@ -24,30 +24,12 @@ object ActivityDescriptor : ChainedDescriptor<Activity>() {
|
|||||||
override fun onGetChildren(node: Activity, children: MutableList<Any>) {
|
override fun onGetChildren(node: Activity, children: MutableList<Any>) {
|
||||||
node.window?.let { window -> children.add(window) }
|
node.window?.let { window -> children.add(window) }
|
||||||
|
|
||||||
var fragments = getDialogFragments(FragmentCompat.supportInstance, node)
|
val fragments = FragmentTracker.getDialogFragments(node)
|
||||||
for (fragment in fragments) {
|
fragments.forEach { fragment -> children.add(fragment) }
|
||||||
children.add(fragment)
|
|
||||||
}
|
|
||||||
|
|
||||||
fragments = getDialogFragments(FragmentCompat.frameworkInstance, node)
|
|
||||||
for (fragment in fragments) {
|
|
||||||
children.add(fragment)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetData(
|
override fun onGetData(
|
||||||
node: Activity,
|
node: Activity,
|
||||||
attributeSections: MutableMap<String, InspectableObject>
|
attributeSections: MutableMap<String, InspectableObject>
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private fun getDialogFragments(
|
|
||||||
compat: FragmentCompat<*, *, *, *>?,
|
|
||||||
activity: Activity
|
|
||||||
): List<Any> {
|
|
||||||
if (compat == null) {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
return compat.getDialogFragments(activity)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,11 @@
|
|||||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.util.Log
|
import android.view.View
|
||||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.RootViewResolver
|
|
||||||
|
|
||||||
object ApplicationRefDescriptor : ChainedDescriptor<ApplicationRef>() {
|
object ApplicationRefDescriptor : ChainedDescriptor<ApplicationRef>() {
|
||||||
|
|
||||||
val rootsLocal = RootViewResolver()
|
|
||||||
override fun onGetActiveChild(node: ApplicationRef): Any? {
|
override fun onGetActiveChild(node: ApplicationRef): Any? {
|
||||||
return if (node.activitiesStack.isNotEmpty()) node.activitiesStack.last() else null
|
return if (node.activitiesStack.isNotEmpty()) node.activitiesStack.last() else null
|
||||||
}
|
}
|
||||||
@@ -34,27 +31,17 @@ object ApplicationRefDescriptor : ChainedDescriptor<ApplicationRef>() {
|
|||||||
override fun onGetChildren(node: ApplicationRef, children: MutableList<Any>) {
|
override fun onGetChildren(node: ApplicationRef, children: MutableList<Any>) {
|
||||||
val activeRoots = node.rootViews
|
val activeRoots = node.rootViews
|
||||||
|
|
||||||
Log.i(LogTag, rootsLocal.toString())
|
val added = mutableSetOf<View>()
|
||||||
activeRoots.let { roots ->
|
for (activity: Activity in node.activitiesStack) {
|
||||||
for (root in roots) {
|
children.add(activity)
|
||||||
var added = false
|
added.add(activity.window.decorView)
|
||||||
/**
|
}
|
||||||
* This code serves 2 purposes: 1.it picks up root views not tied to an activity (dialogs)
|
|
||||||
* 2. We can get initialized late and miss the first activity, it does seem that the root
|
// Picks up root views not tied to an activity (dialogs)
|
||||||
* view resolver is able to (usually ) get the root view regardless, with this we insert the
|
for (root in activeRoots) {
|
||||||
* root decor view without the activity. Ideally we wouldn't rely on this behaviour and find
|
if (!added.contains(root)) {
|
||||||
* a better way to track activities
|
children.add(root)
|
||||||
*/
|
added.add(root)
|
||||||
for (activity: Activity in node.activitiesStack) {
|
|
||||||
if (activity.window.decorView == root) {
|
|
||||||
children.add(activity)
|
|
||||||
added = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!added) {
|
|
||||||
children.add(root)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,17 +33,22 @@ class DescriptorRegister {
|
|||||||
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)
|
mapping.register(ViewPager::class.java, ViewPagerDescriptor)
|
||||||
|
mapping.register(android.app.Fragment::class.java, FragmentFrameworkDescriptor)
|
||||||
|
mapping.register(androidx.fragment.app.Fragment::class.java, FragmentSupportDescriptor)
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
for (clazz in mapping.register.keys) {
|
for (clazz in mapping.register.keys) {
|
||||||
val descriptor: NodeDescriptor<*>? = mapping.register[clazz]
|
val maybeDescriptor: NodeDescriptor<*>? = mapping.register[clazz]
|
||||||
descriptor?.let { descriptor ->
|
maybeDescriptor?.let { descriptor ->
|
||||||
if (descriptor is ChainedDescriptor<*>) {
|
if (descriptor is ChainedDescriptor<*>) {
|
||||||
val chainedDescriptor = descriptor as ChainedDescriptor<Any>
|
val chainedDescriptor = descriptor as ChainedDescriptor<Any>
|
||||||
val superClass: Class<*> = clazz.getSuperclass()
|
val superClass: Class<*> = clazz.superclass
|
||||||
val superDescriptor: NodeDescriptor<*>? = mapping.descriptorForClass(superClass)
|
val maybeSuperDescriptor: NodeDescriptor<*>? = mapping.descriptorForClass(superClass)
|
||||||
// todo we should walk all the way up the superclass hierarchy?
|
|
||||||
if (superDescriptor is ChainedDescriptor<*>) {
|
maybeSuperDescriptor?.let { superDescriptor ->
|
||||||
chainedDescriptor.setSuper(superDescriptor as ChainedDescriptor<Any>)
|
if (superDescriptor is ChainedDescriptor<*>) {
|
||||||
|
chainedDescriptor.setSuper(superDescriptor as ChainedDescriptor<Any>)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,11 +63,17 @@ class DescriptorRegister {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <T> descriptorForClass(clazz: Class<T>): NodeDescriptor<T>? {
|
fun <T> descriptorForClass(clazz: Class<T>): NodeDescriptor<T>? {
|
||||||
var clazz: Class<*> = clazz
|
var mutableClass: Class<*> = clazz
|
||||||
while (!register.containsKey(clazz)) {
|
while (!register.containsKey(mutableClass)) {
|
||||||
clazz = clazz.superclass
|
mutableClass = mutableClass.superclass
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (register[clazz] != null) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
register[clazz] as NodeDescriptor<T>
|
||||||
|
} else {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
return register[clazz] as NodeDescriptor<T>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> descriptorForClassUnsafe(clazz: Class<T>): NodeDescriptor<T> {
|
fun <T> descriptorForClassUnsafe(clazz: Class<T>): NodeDescriptor<T> {
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||||
|
|
||||||
|
object FragmentFrameworkDescriptor : ChainedDescriptor<android.app.Fragment>() {
|
||||||
|
|
||||||
|
override fun onGetId(node: android.app.Fragment): String {
|
||||||
|
return System.identityHashCode(node).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onGetName(node: android.app.Fragment): String {
|
||||||
|
return node.javaClass.simpleName
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onGetChildren(node: android.app.Fragment, children: MutableList<Any>) {
|
||||||
|
node.view?.let { view -> children.add(view) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onGetData(
|
||||||
|
node: android.app.Fragment,
|
||||||
|
attributeSections: MutableMap<String, InspectableObject>
|
||||||
|
) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
||||||
|
|
||||||
|
object FragmentSupportDescriptor : ChainedDescriptor<androidx.fragment.app.Fragment>() {
|
||||||
|
|
||||||
|
override fun onGetId(node: androidx.fragment.app.Fragment): String {
|
||||||
|
return System.identityHashCode(node).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onGetName(node: androidx.fragment.app.Fragment): String {
|
||||||
|
return node.javaClass.simpleName
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onGetChildren(node: androidx.fragment.app.Fragment, children: MutableList<Any>) {
|
||||||
|
node.view?.let { view -> children.add(view) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onGetData(
|
||||||
|
node: androidx.fragment.app.Fragment,
|
||||||
|
attributeSections: MutableMap<String, InspectableObject>
|
||||||
|
) {}
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
import android.app.Fragment
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@@ -16,7 +15,7 @@ import com.facebook.flipper.plugins.uidebugger.common.EnumMapping
|
|||||||
import com.facebook.flipper.plugins.uidebugger.common.Inspectable
|
import com.facebook.flipper.plugins.uidebugger.common.Inspectable
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
|
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.core.FragmentTracker
|
||||||
|
|
||||||
object ViewGroupDescriptor : ChainedDescriptor<ViewGroup>() {
|
object ViewGroupDescriptor : ChainedDescriptor<ViewGroup>() {
|
||||||
|
|
||||||
@@ -32,8 +31,8 @@ object ViewGroupDescriptor : ChainedDescriptor<ViewGroup>() {
|
|||||||
val count = node.childCount - 1
|
val count = node.childCount - 1
|
||||||
for (i in 0..count) {
|
for (i in 0..count) {
|
||||||
val child: View = node.getChildAt(i)
|
val child: View = node.getChildAt(i)
|
||||||
val fragment = getAttachedFragmentForView(child)
|
val fragment = FragmentTracker.getFragment(child)
|
||||||
if (fragment != null && !FragmentCompat.isDialogFragment(fragment)) {
|
if (fragment != null) {
|
||||||
children.add(fragment)
|
children.add(fragment)
|
||||||
} else children.add(child)
|
} else children.add(child)
|
||||||
}
|
}
|
||||||
@@ -63,19 +62,4 @@ object ViewGroupDescriptor : ChainedDescriptor<ViewGroup>() {
|
|||||||
"LAYOUT_MODE_CLIP_BOUNDS" to ViewGroupCompat.LAYOUT_MODE_CLIP_BOUNDS,
|
"LAYOUT_MODE_CLIP_BOUNDS" to ViewGroupCompat.LAYOUT_MODE_CLIP_BOUNDS,
|
||||||
"LAYOUT_MODE_OPTICAL_BOUNDS" to ViewGroupCompat.LAYOUT_MODE_OPTICAL_BOUNDS,
|
"LAYOUT_MODE_OPTICAL_BOUNDS" to ViewGroupCompat.LAYOUT_MODE_OPTICAL_BOUNDS,
|
||||||
)) {}
|
)) {}
|
||||||
|
|
||||||
private fun getAttachedFragmentForView(v: View): Any? {
|
|
||||||
return try {
|
|
||||||
val fragment = FragmentCompat.findFragmentForView(v)
|
|
||||||
var added = false
|
|
||||||
if (fragment is Fragment) {
|
|
||||||
added = fragment.isAdded
|
|
||||||
} else if (fragment is androidx.fragment.app.Fragment) {
|
|
||||||
added = fragment.isAdded
|
|
||||||
}
|
|
||||||
if (added) fragment else null
|
|
||||||
} catch (e: RuntimeException) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,12 @@ package com.facebook.flipper.plugins.uidebugger.observers
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||||
import com.facebook.flipper.plugins.uidebugger.TreeObserver
|
|
||||||
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 com.facebook.flipper.plugins.uidebugger.core.RootViewResolver
|
import com.facebook.flipper.plugins.uidebugger.core.RootViewResolver
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* responsible for observing the activity stack and managing the subscription to the top most
|
* Responsible for observing the activity stack and managing the subscription to the top most
|
||||||
* content view (decor view)
|
* content view (decor view)
|
||||||
*/
|
*/
|
||||||
class ApplicationTreeObserver(val context: Context) : TreeObserver<ApplicationRef>() {
|
class ApplicationTreeObserver(val context: Context) : TreeObserver<ApplicationRef>() {
|
||||||
@@ -48,6 +47,7 @@ class ApplicationTreeObserver(val context: Context) : TreeObserver<ApplicationRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun unsubscribe() {
|
override fun unsubscribe() {
|
||||||
context.applicationRef.setActivityStackChangedListener(null)
|
// Not entirely sure this will ever happen or be needed.
|
||||||
|
// context.applicationRef.setActivityStackChangedListener(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import android.util.Log
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||||
import com.facebook.flipper.plugins.uidebugger.TreeObserver
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.Context
|
import com.facebook.flipper.plugins.uidebugger.core.Context
|
||||||
|
|
||||||
typealias DecorView = View
|
typealias DecorView = View
|
||||||
|
|||||||
@@ -7,11 +7,9 @@
|
|||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.observers
|
package com.facebook.flipper.plugins.uidebugger.observers
|
||||||
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.TreeObserver
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.Context
|
import com.facebook.flipper.plugins.uidebugger.core.Context
|
||||||
|
|
||||||
interface TreeObserverBuilder<T> {
|
interface TreeObserverBuilder<T> {
|
||||||
|
|
||||||
fun canBuildFor(node: Any): Boolean
|
fun canBuildFor(node: Any): Boolean
|
||||||
fun build(context: Context): TreeObserver<T>
|
fun build(context: Context): TreeObserver<T>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger
|
package com.facebook.flipper.plugins.uidebugger.observers
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.Context
|
import com.facebook.flipper.plugins.uidebugger.core.Context
|
||||||
import com.facebook.flipper.plugins.uidebugger.observers.SubtreeUpdate
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Stateful class that manages some subtree in the UI Hierarchy.
|
Stateful class that manages some subtree in the UI Hierarchy.
|
||||||
@@ -75,7 +75,7 @@ abstract class TreeObserver<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun cleanUpRecursive() {
|
fun cleanUpRecursive() {
|
||||||
Log.i(LogTag, "Cleaning up observer ${this}")
|
Log.i(LogTag, "Cleaning up observer $this")
|
||||||
children.values.forEach { it.cleanUpRecursive() }
|
children.values.forEach { it.cleanUpRecursive() }
|
||||||
unsubscribe()
|
unsubscribe()
|
||||||
children.clear()
|
children.clear()
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ object AccessibilityUtil {
|
|||||||
* @param node The [AccessibilityNodeInfoCompat] to evaluate
|
* @param node The [AccessibilityNodeInfoCompat] to evaluate
|
||||||
* @return `true` if it meets the criterion for producing spoken feedback
|
* @return `true` if it meets the criterion for producing spoken feedback
|
||||||
*/
|
*/
|
||||||
fun isSpeakingNode(node: AccessibilityNodeInfoCompat?, view: View?): Boolean {
|
private fun isSpeakingNode(node: AccessibilityNodeInfoCompat?, view: View?): Boolean {
|
||||||
if (node == null || view == null) {
|
if (node == null || view == null) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ object AccessibilityUtil {
|
|||||||
* @param node The [AccessibilityNodeInfoCompat] to evaluate
|
* @param node The [AccessibilityNodeInfoCompat] to evaluate
|
||||||
* @return `true` if it has any non-actionable speaking descendants within its subtree
|
* @return `true` if it has any non-actionable speaking descendants within its subtree
|
||||||
*/
|
*/
|
||||||
fun hasNonActionableSpeakingDescendants(
|
private fun hasNonActionableSpeakingDescendants(
|
||||||
node: AccessibilityNodeInfoCompat?,
|
node: AccessibilityNodeInfoCompat?,
|
||||||
view: View?
|
view: View?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
@@ -79,10 +79,7 @@ object AccessibilityUtil {
|
|||||||
val viewGroup = view as ViewGroup
|
val viewGroup = view as ViewGroup
|
||||||
val count = viewGroup.childCount - 1
|
val count = viewGroup.childCount - 1
|
||||||
for (i in 0..count) {
|
for (i in 0..count) {
|
||||||
val childView = viewGroup.getChildAt(i)
|
val childView = viewGroup.getChildAt(i) ?: continue
|
||||||
if (childView == null) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val childNode = AccessibilityNodeInfoCompat.obtain()
|
val childNode = AccessibilityNodeInfoCompat.obtain()
|
||||||
try {
|
try {
|
||||||
ViewCompat.onInitializeAccessibilityNodeInfo(childView, childNode)
|
ViewCompat.onInitializeAccessibilityNodeInfo(childView, childNode)
|
||||||
@@ -107,7 +104,7 @@ object AccessibilityUtil {
|
|||||||
* @param node The [AccessibilityNodeInfoCompat] to evaluate
|
* @param node The [AccessibilityNodeInfoCompat] to evaluate
|
||||||
* @return `true` if it is possible to gain accessibility focus
|
* @return `true` if it is possible to gain accessibility focus
|
||||||
*/
|
*/
|
||||||
fun isAccessibilityFocusable(node: AccessibilityNodeInfoCompat?, view: View?): Boolean {
|
private fun isAccessibilityFocusable(node: AccessibilityNodeInfoCompat?, view: View?): Boolean {
|
||||||
if (node == null || view == null) {
|
if (node == null || view == null) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ abstract class FragmentCompat<
|
|||||||
}
|
}
|
||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
private var hasSupportFragment = false
|
private var hasSupportFragment = false
|
||||||
init {
|
init {
|
||||||
hasSupportFragment = tryGetClassForName("androidx.fragment.app.Fragment") != null
|
hasSupportFragment = tryGetClassForName("androidx.fragment.app.Fragment") != null
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class FragmentCompatFramework :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun forFragmentManager():
|
override fun forFragmentManager():
|
||||||
FragmentManagerAccessorViaReflection<FragmentManager, Fragment>? {
|
FragmentManagerAccessorViaReflection<FragmentManager, Fragment> {
|
||||||
return fragmentManagerAccessor
|
return fragmentManagerAccessor
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ class FragmentCompatFramework :
|
|||||||
val fragmentManagerAccessor = forFragmentManager()
|
val fragmentManagerAccessor = forFragmentManager()
|
||||||
var addedFragments: List<Any?>? = null
|
var addedFragments: List<Any?>? = null
|
||||||
try {
|
try {
|
||||||
addedFragments = fragmentManagerAccessor?.getAddedFragments(fragmentManager)
|
addedFragments = fragmentManagerAccessor.getAddedFragments(fragmentManager)
|
||||||
} catch (e: Exception) {}
|
} catch (e: Exception) {}
|
||||||
|
|
||||||
if (addedFragments != null) {
|
if (addedFragments != null) {
|
||||||
@@ -99,7 +99,7 @@ class FragmentCompatFramework :
|
|||||||
}
|
}
|
||||||
|
|
||||||
val activityAccessor = forFragmentActivity()
|
val activityAccessor = forFragmentActivity()
|
||||||
val fragmentManager = activityAccessor?.getFragmentManager(activity) ?: return null
|
val fragmentManager = activityAccessor.getFragmentManager(activity) ?: return null
|
||||||
|
|
||||||
return findFragmentForViewInFragmentManager(fragmentManager, view)
|
return findFragmentForViewInFragmentManager(fragmentManager, view)
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ class FragmentCompatFramework :
|
|||||||
val fragmentManagerAccessor = forFragmentManager()
|
val fragmentManagerAccessor = forFragmentManager()
|
||||||
var fragments: List<Any>? = null
|
var fragments: List<Any>? = null
|
||||||
try {
|
try {
|
||||||
fragments = fragmentManagerAccessor?.getAddedFragments(fragmentManager)
|
fragments = fragmentManagerAccessor.getAddedFragments(fragmentManager)
|
||||||
} catch (e: Exception) {}
|
} catch (e: Exception) {}
|
||||||
|
|
||||||
if (fragments != null) {
|
if (fragments != null) {
|
||||||
|
|||||||
@@ -25,19 +25,19 @@ class FragmentCompatSupportLib :
|
|||||||
override val fragmentActivityClass: Class<FragmentActivity>
|
override val fragmentActivityClass: Class<FragmentActivity>
|
||||||
get() = FragmentActivity::class.java
|
get() = FragmentActivity::class.java
|
||||||
|
|
||||||
override fun forFragment(): FragmentAccessorSupportLib? {
|
override fun forFragment(): FragmentAccessorSupportLib {
|
||||||
return fragmentAccessor
|
return fragmentAccessor
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun forDialogFragment(): DialogFragmentAccessorSupportLib? {
|
override fun forDialogFragment(): DialogFragmentAccessorSupportLib {
|
||||||
return dialogFragmentAccessor
|
return dialogFragmentAccessor
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun forFragmentManager(): FragmentManagerAccessor<FragmentManager, Fragment>? {
|
override fun forFragmentManager(): FragmentManagerAccessor<FragmentManager, Fragment> {
|
||||||
return fragmentManagerAccessor
|
return fragmentManagerAccessor
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun forFragmentActivity(): FragmentActivityAccessorSupportLib? {
|
override fun forFragmentActivity(): FragmentActivityAccessorSupportLib {
|
||||||
return fragmentActivityAccessor
|
return fragmentActivityAccessor
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,12 +48,12 @@ class FragmentCompatSupportLib :
|
|||||||
|
|
||||||
val activityAccessor = forFragmentActivity()
|
val activityAccessor = forFragmentActivity()
|
||||||
val fragmentManager =
|
val fragmentManager =
|
||||||
activityAccessor?.getFragmentManager(activity as FragmentActivity) ?: return emptyList()
|
activityAccessor.getFragmentManager(activity as FragmentActivity) ?: return emptyList()
|
||||||
|
|
||||||
val fragmentManagerAccessor = forFragmentManager()
|
val fragmentManagerAccessor = forFragmentManager()
|
||||||
var addedFragments: List<Any?>? = null
|
var addedFragments: List<Any?>? = null
|
||||||
try {
|
try {
|
||||||
addedFragments = fragmentManagerAccessor?.getAddedFragments(fragmentManager)
|
addedFragments = fragmentManagerAccessor.getAddedFragments(fragmentManager)
|
||||||
} catch (e: Exception) {}
|
} catch (e: Exception) {}
|
||||||
|
|
||||||
if (addedFragments != null) {
|
if (addedFragments != null) {
|
||||||
@@ -78,7 +78,7 @@ class FragmentCompatSupportLib :
|
|||||||
|
|
||||||
val activityAccessor = forFragmentActivity()
|
val activityAccessor = forFragmentActivity()
|
||||||
val fragmentManager =
|
val fragmentManager =
|
||||||
activityAccessor?.getFragmentManager(activity as FragmentActivity) ?: return null
|
activityAccessor.getFragmentManager(activity as FragmentActivity) ?: return null
|
||||||
|
|
||||||
return findFragmentForViewInFragmentManager(fragmentManager, view)
|
return findFragmentForViewInFragmentManager(fragmentManager, view)
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ class FragmentCompatSupportLib :
|
|||||||
val fragmentManagerAccessor = forFragmentManager()
|
val fragmentManagerAccessor = forFragmentManager()
|
||||||
var fragments: List<Any>? = null
|
var fragments: List<Any>? = null
|
||||||
try {
|
try {
|
||||||
fragments = fragmentManagerAccessor?.getAddedFragments(fragmentManager)
|
fragments = fragmentManagerAccessor.getAddedFragments(fragmentManager)
|
||||||
} catch (e: Exception) {}
|
} catch (e: Exception) {}
|
||||||
|
|
||||||
if (fragments != null) {
|
if (fragments != null) {
|
||||||
@@ -112,17 +112,13 @@ class FragmentCompatSupportLib :
|
|||||||
}
|
}
|
||||||
|
|
||||||
val fragmentAccessor = forFragment()
|
val fragmentAccessor = forFragment()
|
||||||
fragmentAccessor?.let { accessor ->
|
if (fragmentAccessor.getView(fragment as Fragment) === view) {
|
||||||
if (accessor.getView(fragment as Fragment) === view) {
|
return fragment
|
||||||
return fragment
|
|
||||||
}
|
|
||||||
val childFragmentManager = accessor.getChildFragmentManager(fragment as Fragment)
|
|
||||||
return if (childFragmentManager != null) {
|
|
||||||
findFragmentForViewInFragmentManager(childFragmentManager, view)
|
|
||||||
} else null
|
|
||||||
}
|
}
|
||||||
|
val childFragmentManager = fragmentAccessor.getChildFragmentManager(fragment as Fragment)
|
||||||
return null
|
return if (childFragmentManager != null) {
|
||||||
|
findFragmentForViewInFragmentManager(childFragmentManager, view)
|
||||||
|
} else null
|
||||||
}
|
}
|
||||||
|
|
||||||
open class FragmentAccessorSupportLib : FragmentAccessor<Fragment, FragmentManager> {
|
open class FragmentAccessorSupportLib : FragmentAccessor<Fragment, FragmentManager> {
|
||||||
@@ -161,7 +157,7 @@ class FragmentCompatSupportLib :
|
|||||||
|
|
||||||
class FragmentActivityAccessorSupportLib :
|
class FragmentActivityAccessorSupportLib :
|
||||||
FragmentActivityAccessor<FragmentActivity, FragmentManager> {
|
FragmentActivityAccessor<FragmentActivity, FragmentManager> {
|
||||||
override fun getFragmentManager(activity: FragmentActivity): FragmentManager? {
|
override fun getFragmentManager(activity: FragmentActivity): FragmentManager {
|
||||||
return activity.supportFragmentManager
|
return activity.supportFragmentManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user