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 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.observers.TreeObserver
|
||||
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverBuilder
|
||||
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.Application
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class ApplicationRef(val application: Application) : Application.ActivityLifecycleCallbacks {
|
||||
interface ActivityStackChangedListener {
|
||||
fun onActivityAdded(activity: Activity, stack: List<Activity>)
|
||||
fun onActivityStackChanged(stack: List<Activity>)
|
||||
fun onActivityDestroyed(activity: Activity, stack: List<Activity>)
|
||||
class ApplicationRef(val application: Application) {
|
||||
init {
|
||||
ActivityTracker.start(application)
|
||||
}
|
||||
|
||||
val rootsResolver: 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 rootsResolver: RootViewResolver = RootViewResolver()
|
||||
|
||||
val activitiesStack: List<Activity>
|
||||
get() {
|
||||
val stack: MutableList<Activity> = ArrayList<Activity>(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
|
||||
return ActivityTracker.activitiesStack
|
||||
}
|
||||
|
||||
val rootViews: List<View>
|
||||
get() {
|
||||
val roots = rootsResolver.listActiveRootViews()
|
||||
roots?.let { roots ->
|
||||
val viewRoots: MutableList<View> = ArrayList<View>(roots.size)
|
||||
val activeRootViews = rootsResolver.listActiveRootViews()
|
||||
activeRootViews?.let { roots ->
|
||||
val viewRoots: MutableList<View> = ArrayList(roots.size)
|
||||
for (root in roots) {
|
||||
viewRoots.add(root.view)
|
||||
}
|
||||
@@ -79,10 +36,4 @@ class ApplicationRef(val application: Application) : Application.ActivityLifecyc
|
||||
|
||||
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 =
|
||||
descriptorRegister.descriptorForClassUnsafe(child::class.java).asAny()
|
||||
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) {
|
||||
stack.add(child)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
||||
/** Layout Visitor traverses the entire view hierarchy from a given root. */
|
||||
class LayoutVisitor(val visitor: Visitor) {
|
||||
class LayoutVisitor(private val visitor: Visitor) {
|
||||
interface Visitor {
|
||||
fun visit(view: View)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import android.view.WindowManager
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.lang.reflect.Modifier
|
||||
import java.util.ArrayList
|
||||
|
||||
/**
|
||||
* Provides access to all root views in an application.
|
||||
@@ -125,21 +124,23 @@ class RootViewResolver {
|
||||
var params: List<WindowManager.LayoutParams>? = null
|
||||
try {
|
||||
viewsField?.let { field ->
|
||||
if (Build.VERSION.SDK_INT < 19) {
|
||||
val arr = field[windowManagerObj] as Array<View>
|
||||
views = arr.toList()
|
||||
} else {
|
||||
views = field[windowManagerObj] as List<View>
|
||||
}
|
||||
views =
|
||||
if (Build.VERSION.SDK_INT < 19) {
|
||||
val arr = field[windowManagerObj] as Array<View>
|
||||
arr.toList()
|
||||
} else {
|
||||
field[windowManagerObj] as List<View>
|
||||
}
|
||||
}
|
||||
|
||||
paramsField?.let { field ->
|
||||
if (Build.VERSION.SDK_INT < 19) {
|
||||
val arr = field[windowManagerObj] as Array<WindowManager.LayoutParams>
|
||||
params = arr.toList() as List<WindowManager.LayoutParams>
|
||||
} else {
|
||||
params = field[windowManagerObj] as List<WindowManager.LayoutParams>
|
||||
}
|
||||
params =
|
||||
if (Build.VERSION.SDK_INT < 19) {
|
||||
val arr = field[windowManagerObj] as Array<WindowManager.LayoutParams>
|
||||
arr.toList() as List<WindowManager.LayoutParams>
|
||||
} else {
|
||||
field[windowManagerObj] as List<WindowManager.LayoutParams>
|
||||
}
|
||||
}
|
||||
} catch (re: RuntimeException) {
|
||||
return null
|
||||
@@ -150,12 +151,12 @@ class RootViewResolver {
|
||||
val roots = mutableListOf<RootView>()
|
||||
views?.let { views ->
|
||||
params?.let { params ->
|
||||
for (i in views.indices) {
|
||||
val view = views[i]
|
||||
// TODO FIX, len(param) is not always the same as len(views) For now just use first
|
||||
|
||||
// params
|
||||
roots.add(RootView(view, null))
|
||||
if (views.size == params.size) {
|
||||
for (i in views.indices) {
|
||||
val view = views[i]
|
||||
val param = params[i]
|
||||
roots.add(RootView(view, param))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||
|
||||
import android.app.Activity
|
||||
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>() {
|
||||
|
||||
@@ -24,30 +24,12 @@ object ActivityDescriptor : ChainedDescriptor<Activity>() {
|
||||
override fun onGetChildren(node: Activity, children: MutableList<Any>) {
|
||||
node.window?.let { window -> children.add(window) }
|
||||
|
||||
var fragments = getDialogFragments(FragmentCompat.supportInstance, node)
|
||||
for (fragment in fragments) {
|
||||
children.add(fragment)
|
||||
}
|
||||
|
||||
fragments = getDialogFragments(FragmentCompat.frameworkInstance, node)
|
||||
for (fragment in fragments) {
|
||||
children.add(fragment)
|
||||
}
|
||||
val fragments = FragmentTracker.getDialogFragments(node)
|
||||
fragments.forEach { fragment -> children.add(fragment) }
|
||||
}
|
||||
|
||||
override fun onGetData(
|
||||
node: Activity,
|
||||
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
|
||||
|
||||
import android.app.Activity
|
||||
import android.util.Log
|
||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||
import android.view.View
|
||||
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
||||
import com.facebook.flipper.plugins.uidebugger.core.RootViewResolver
|
||||
|
||||
object ApplicationRefDescriptor : ChainedDescriptor<ApplicationRef>() {
|
||||
|
||||
val rootsLocal = RootViewResolver()
|
||||
override fun onGetActiveChild(node: ApplicationRef): Any? {
|
||||
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>) {
|
||||
val activeRoots = node.rootViews
|
||||
|
||||
Log.i(LogTag, rootsLocal.toString())
|
||||
activeRoots.let { roots ->
|
||||
for (root in roots) {
|
||||
var added = false
|
||||
/**
|
||||
* 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
|
||||
* view resolver is able to (usually ) get the root view regardless, with this we insert the
|
||||
* root decor view without the activity. Ideally we wouldn't rely on this behaviour and find
|
||||
* a better way to track activities
|
||||
*/
|
||||
for (activity: Activity in node.activitiesStack) {
|
||||
if (activity.window.decorView == root) {
|
||||
children.add(activity)
|
||||
added = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
children.add(root)
|
||||
}
|
||||
val added = mutableSetOf<View>()
|
||||
for (activity: Activity in node.activitiesStack) {
|
||||
children.add(activity)
|
||||
added.add(activity.window.decorView)
|
||||
}
|
||||
|
||||
// Picks up root views not tied to an activity (dialogs)
|
||||
for (root in activeRoots) {
|
||||
if (!added.contains(root)) {
|
||||
children.add(root)
|
||||
added.add(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,17 +33,22 @@ class DescriptorRegister {
|
||||
mapping.register(TextView::class.java, TextViewDescriptor)
|
||||
mapping.register(Button::class.java, ButtonDescriptor)
|
||||
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) {
|
||||
val descriptor: NodeDescriptor<*>? = mapping.register[clazz]
|
||||
descriptor?.let { descriptor ->
|
||||
val maybeDescriptor: NodeDescriptor<*>? = mapping.register[clazz]
|
||||
maybeDescriptor?.let { descriptor ->
|
||||
if (descriptor is ChainedDescriptor<*>) {
|
||||
val chainedDescriptor = descriptor as ChainedDescriptor<Any>
|
||||
val superClass: Class<*> = clazz.getSuperclass()
|
||||
val superDescriptor: NodeDescriptor<*>? = mapping.descriptorForClass(superClass)
|
||||
// todo we should walk all the way up the superclass hierarchy?
|
||||
if (superDescriptor is ChainedDescriptor<*>) {
|
||||
chainedDescriptor.setSuper(superDescriptor as ChainedDescriptor<Any>)
|
||||
val superClass: Class<*> = clazz.superclass
|
||||
val maybeSuperDescriptor: NodeDescriptor<*>? = mapping.descriptorForClass(superClass)
|
||||
|
||||
maybeSuperDescriptor?.let { superDescriptor ->
|
||||
if (superDescriptor is ChainedDescriptor<*>) {
|
||||
chainedDescriptor.setSuper(superDescriptor as ChainedDescriptor<Any>)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,11 +63,17 @@ class DescriptorRegister {
|
||||
}
|
||||
|
||||
fun <T> descriptorForClass(clazz: Class<T>): NodeDescriptor<T>? {
|
||||
var clazz: Class<*> = clazz
|
||||
while (!register.containsKey(clazz)) {
|
||||
clazz = clazz.superclass
|
||||
var mutableClass: Class<*> = clazz
|
||||
while (!register.containsKey(mutableClass)) {
|
||||
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> {
|
||||
|
||||
@@ -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
|
||||
|
||||
import android.app.Fragment
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
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.InspectableObject
|
||||
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>() {
|
||||
|
||||
@@ -32,8 +31,8 @@ object ViewGroupDescriptor : ChainedDescriptor<ViewGroup>() {
|
||||
val count = node.childCount - 1
|
||||
for (i in 0..count) {
|
||||
val child: View = node.getChildAt(i)
|
||||
val fragment = getAttachedFragmentForView(child)
|
||||
if (fragment != null && !FragmentCompat.isDialogFragment(fragment)) {
|
||||
val fragment = FragmentTracker.getFragment(child)
|
||||
if (fragment != null) {
|
||||
children.add(fragment)
|
||||
} else children.add(child)
|
||||
}
|
||||
@@ -63,19 +62,4 @@ object ViewGroupDescriptor : ChainedDescriptor<ViewGroup>() {
|
||||
"LAYOUT_MODE_CLIP_BOUNDS" to ViewGroupCompat.LAYOUT_MODE_CLIP_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.view.View
|
||||
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.Context
|
||||
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)
|
||||
*/
|
||||
class ApplicationTreeObserver(val context: Context) : TreeObserver<ApplicationRef>() {
|
||||
@@ -48,6 +47,7 @@ class ApplicationTreeObserver(val context: Context) : TreeObserver<ApplicationRe
|
||||
}
|
||||
|
||||
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.ViewTreeObserver
|
||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||
import com.facebook.flipper.plugins.uidebugger.TreeObserver
|
||||
import com.facebook.flipper.plugins.uidebugger.core.Context
|
||||
|
||||
typealias DecorView = View
|
||||
|
||||
@@ -7,11 +7,9 @@
|
||||
|
||||
package com.facebook.flipper.plugins.uidebugger.observers
|
||||
|
||||
import com.facebook.flipper.plugins.uidebugger.TreeObserver
|
||||
import com.facebook.flipper.plugins.uidebugger.core.Context
|
||||
|
||||
interface TreeObserverBuilder<T> {
|
||||
|
||||
fun canBuildFor(node: Any): Boolean
|
||||
fun build(context: Context): TreeObserver<T>
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
* 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 com.facebook.flipper.plugins.uidebugger.LogTag
|
||||
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.
|
||||
@@ -75,7 +75,7 @@ abstract class TreeObserver<T> {
|
||||
}
|
||||
|
||||
fun cleanUpRecursive() {
|
||||
Log.i(LogTag, "Cleaning up observer ${this}")
|
||||
Log.i(LogTag, "Cleaning up observer $this")
|
||||
children.values.forEach { it.cleanUpRecursive() }
|
||||
unsubscribe()
|
||||
children.clear()
|
||||
|
||||
@@ -44,7 +44,7 @@ object AccessibilityUtil {
|
||||
* @param node The [AccessibilityNodeInfoCompat] to evaluate
|
||||
* @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) {
|
||||
return false
|
||||
}
|
||||
@@ -69,7 +69,7 @@ object AccessibilityUtil {
|
||||
* @param node The [AccessibilityNodeInfoCompat] to evaluate
|
||||
* @return `true` if it has any non-actionable speaking descendants within its subtree
|
||||
*/
|
||||
fun hasNonActionableSpeakingDescendants(
|
||||
private fun hasNonActionableSpeakingDescendants(
|
||||
node: AccessibilityNodeInfoCompat?,
|
||||
view: View?
|
||||
): Boolean {
|
||||
@@ -79,10 +79,7 @@ object AccessibilityUtil {
|
||||
val viewGroup = view as ViewGroup
|
||||
val count = viewGroup.childCount - 1
|
||||
for (i in 0..count) {
|
||||
val childView = viewGroup.getChildAt(i)
|
||||
if (childView == null) {
|
||||
continue
|
||||
}
|
||||
val childView = viewGroup.getChildAt(i) ?: continue
|
||||
val childNode = AccessibilityNodeInfoCompat.obtain()
|
||||
try {
|
||||
ViewCompat.onInitializeAccessibilityNodeInfo(childView, childNode)
|
||||
@@ -107,7 +104,7 @@ object AccessibilityUtil {
|
||||
* @param node The [AccessibilityNodeInfoCompat] to evaluate
|
||||
* @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) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ abstract class FragmentCompat<
|
||||
}
|
||||
return field
|
||||
}
|
||||
|
||||
private var hasSupportFragment = false
|
||||
init {
|
||||
hasSupportFragment = tryGetClassForName("androidx.fragment.app.Fragment") != null
|
||||
|
||||
@@ -56,7 +56,7 @@ class FragmentCompatFramework :
|
||||
}
|
||||
|
||||
override fun forFragmentManager():
|
||||
FragmentManagerAccessorViaReflection<FragmentManager, Fragment>? {
|
||||
FragmentManagerAccessorViaReflection<FragmentManager, Fragment> {
|
||||
return fragmentManagerAccessor
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class FragmentCompatFramework :
|
||||
val fragmentManagerAccessor = forFragmentManager()
|
||||
var addedFragments: List<Any?>? = null
|
||||
try {
|
||||
addedFragments = fragmentManagerAccessor?.getAddedFragments(fragmentManager)
|
||||
addedFragments = fragmentManagerAccessor.getAddedFragments(fragmentManager)
|
||||
} catch (e: Exception) {}
|
||||
|
||||
if (addedFragments != null) {
|
||||
@@ -99,7 +99,7 @@ class FragmentCompatFramework :
|
||||
}
|
||||
|
||||
val activityAccessor = forFragmentActivity()
|
||||
val fragmentManager = activityAccessor?.getFragmentManager(activity) ?: return null
|
||||
val fragmentManager = activityAccessor.getFragmentManager(activity) ?: return null
|
||||
|
||||
return findFragmentForViewInFragmentManager(fragmentManager, view)
|
||||
}
|
||||
@@ -111,7 +111,7 @@ class FragmentCompatFramework :
|
||||
val fragmentManagerAccessor = forFragmentManager()
|
||||
var fragments: List<Any>? = null
|
||||
try {
|
||||
fragments = fragmentManagerAccessor?.getAddedFragments(fragmentManager)
|
||||
fragments = fragmentManagerAccessor.getAddedFragments(fragmentManager)
|
||||
} catch (e: Exception) {}
|
||||
|
||||
if (fragments != null) {
|
||||
|
||||
@@ -25,19 +25,19 @@ class FragmentCompatSupportLib :
|
||||
override val fragmentActivityClass: Class<FragmentActivity>
|
||||
get() = FragmentActivity::class.java
|
||||
|
||||
override fun forFragment(): FragmentAccessorSupportLib? {
|
||||
override fun forFragment(): FragmentAccessorSupportLib {
|
||||
return fragmentAccessor
|
||||
}
|
||||
|
||||
override fun forDialogFragment(): DialogFragmentAccessorSupportLib? {
|
||||
override fun forDialogFragment(): DialogFragmentAccessorSupportLib {
|
||||
return dialogFragmentAccessor
|
||||
}
|
||||
|
||||
override fun forFragmentManager(): FragmentManagerAccessor<FragmentManager, Fragment>? {
|
||||
override fun forFragmentManager(): FragmentManagerAccessor<FragmentManager, Fragment> {
|
||||
return fragmentManagerAccessor
|
||||
}
|
||||
|
||||
override fun forFragmentActivity(): FragmentActivityAccessorSupportLib? {
|
||||
override fun forFragmentActivity(): FragmentActivityAccessorSupportLib {
|
||||
return fragmentActivityAccessor
|
||||
}
|
||||
|
||||
@@ -48,12 +48,12 @@ class FragmentCompatSupportLib :
|
||||
|
||||
val activityAccessor = forFragmentActivity()
|
||||
val fragmentManager =
|
||||
activityAccessor?.getFragmentManager(activity as FragmentActivity) ?: return emptyList()
|
||||
activityAccessor.getFragmentManager(activity as FragmentActivity) ?: return emptyList()
|
||||
|
||||
val fragmentManagerAccessor = forFragmentManager()
|
||||
var addedFragments: List<Any?>? = null
|
||||
try {
|
||||
addedFragments = fragmentManagerAccessor?.getAddedFragments(fragmentManager)
|
||||
addedFragments = fragmentManagerAccessor.getAddedFragments(fragmentManager)
|
||||
} catch (e: Exception) {}
|
||||
|
||||
if (addedFragments != null) {
|
||||
@@ -78,7 +78,7 @@ class FragmentCompatSupportLib :
|
||||
|
||||
val activityAccessor = forFragmentActivity()
|
||||
val fragmentManager =
|
||||
activityAccessor?.getFragmentManager(activity as FragmentActivity) ?: return null
|
||||
activityAccessor.getFragmentManager(activity as FragmentActivity) ?: return null
|
||||
|
||||
return findFragmentForViewInFragmentManager(fragmentManager, view)
|
||||
}
|
||||
@@ -90,7 +90,7 @@ class FragmentCompatSupportLib :
|
||||
val fragmentManagerAccessor = forFragmentManager()
|
||||
var fragments: List<Any>? = null
|
||||
try {
|
||||
fragments = fragmentManagerAccessor?.getAddedFragments(fragmentManager)
|
||||
fragments = fragmentManagerAccessor.getAddedFragments(fragmentManager)
|
||||
} catch (e: Exception) {}
|
||||
|
||||
if (fragments != null) {
|
||||
@@ -112,17 +112,13 @@ class FragmentCompatSupportLib :
|
||||
}
|
||||
|
||||
val fragmentAccessor = forFragment()
|
||||
fragmentAccessor?.let { accessor ->
|
||||
if (accessor.getView(fragment as Fragment) === view) {
|
||||
return fragment
|
||||
}
|
||||
val childFragmentManager = accessor.getChildFragmentManager(fragment as Fragment)
|
||||
return if (childFragmentManager != null) {
|
||||
findFragmentForViewInFragmentManager(childFragmentManager, view)
|
||||
} else null
|
||||
if (fragmentAccessor.getView(fragment as Fragment) === view) {
|
||||
return fragment
|
||||
}
|
||||
|
||||
return null
|
||||
val childFragmentManager = fragmentAccessor.getChildFragmentManager(fragment as Fragment)
|
||||
return if (childFragmentManager != null) {
|
||||
findFragmentForViewInFragmentManager(childFragmentManager, view)
|
||||
} else null
|
||||
}
|
||||
|
||||
open class FragmentAccessorSupportLib : FragmentAccessor<Fragment, FragmentManager> {
|
||||
@@ -161,7 +157,7 @@ class FragmentCompatSupportLib :
|
||||
|
||||
class FragmentActivityAccessorSupportLib :
|
||||
FragmentActivityAccessor<FragmentActivity, FragmentManager> {
|
||||
override fun getFragmentManager(activity: FragmentActivity): FragmentManager? {
|
||||
override fun getFragmentManager(activity: FragmentActivity): FragmentManager {
|
||||
return activity.supportFragmentManager
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user