Skip to content

Commit

Permalink
Replace root view resolver with curtains
Browse files Browse the repository at this point in the history
Summary:
Curtains allows a common entrypoint into accessing the root views, this means multiple tools can access it without clobbering each others observable array list

it also means we can get rid of some nasty reflection code that we don't maintain as well as curtains

Differential Revision: D56015851
  • Loading branch information
Luke De Feo authored and facebook-github-bot committed Apr 15, 2024
1 parent fa4ceea commit 302a9a9
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 184 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ class ApplicationRef(val application: Application) {
// the root view resolver will at least find the decor view, this is the case for various
// kinds of custom overlays
// 2. Dialog fragments
val rootsResolver: RootViewResolver = RootViewResolver()
val windowManagerUtility = WindowManagerUtility()
val activitiesStack: List<Activity>
get() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import com.facebook.flipper.plugins.uidebugger.descriptors.ViewDescriptor
import com.facebook.flipper.plugins.uidebugger.util.StopWatch
import com.facebook.flipper.plugins.uidebugger.util.Throttler
import com.facebook.flipper.plugins.uidebugger.util.objectIdentity
import curtains.Curtains
import curtains.OnRootViewsChangedListener

/**
* The UIDebugger does 3 things:
Expand Down Expand Up @@ -44,61 +46,66 @@ class DecorViewTracker(private val context: UIDContext, private val snapshotter:

fun start() {

val applicationRef = context.applicationRef

val rootViewListener =
object : RootViewResolver.Listener {
override fun onRootViewAdded(rootView: View) {}

override fun onRootViewRemoved(rootView: View) {}

override fun onRootViewsChanged(rootViews: List<View>) {
// remove predraw listen from current view as its going away or will be covered
Log.i(LogTag, "Removing pre draw listener from ${currentDecorView?.objectIdentity()}")
currentDecorView?.viewTreeObserver?.removeOnPreDrawListener(preDrawListener)
val rootViewChangedListener = OnRootViewsChangedListener { view, added ->
if (currentDecorView != null) {
// remove predraw listen from current view as its going away or will be covered
Log.d(LogTag, "Removing pre draw listener from ${currentDecorView?.objectIdentity()}")
currentDecorView?.viewTreeObserver?.removeOnPreDrawListener(preDrawListener)
}

val decorViewToActivity: Map<View, Activity> = ActivityTracker.decorViewToActivityMap

// at the time of this callback curtains.rootViews is not updated yet, so we need to use the
// 'view' and 'added' params to the callback to see any new root views
val topView =
if (added && ApplicationRefDescriptor.isUsefulRoot(decorViewToActivity[view] ?: view)) {
view
} else {
// this is technically the preview set of root view but this is the branch where the new
// root view is not 'useful' or we are popping a view off the stack so the old roots are
// fine here
Curtains.rootViews.lastOrNull {
ApplicationRefDescriptor.isUsefulRoot(decorViewToActivity[it] ?: it)
}
}

// setup new listener on top most view, that will be the active child in traversal
if (topView != null) {
val throttler = Throttler(500) { currentDecorView?.let { traverseSnapshotAndSend(it) } }

val decorViewToActivity: Map<View, Activity> = ActivityTracker.decorViewToActivityMap
preDrawListener =
ViewTreeObserver.OnPreDrawListener {
throttler.trigger()
true
}

val topView =
rootViews.lastOrNull { view ->
val activityOrView = decorViewToActivity[view] ?: view
ApplicationRefDescriptor.isUsefulRoot(activityOrView)
}
topView.viewTreeObserver.addOnPreDrawListener(preDrawListener)
currentDecorView = topView

if (topView != null) {
val throttler =
Throttler(500) { currentDecorView?.let { traverseSnapshotAndSend(it) } }
Log.i(LogTag, "Added pre draw listener to ${topView.objectIdentity()}")

preDrawListener =
ViewTreeObserver.OnPreDrawListener {
throttler.trigger()
true
}
// schedule traversal immediately when we detect a new decor view
throttler.trigger()
}
}

topView.viewTreeObserver.addOnPreDrawListener(preDrawListener)
currentDecorView = topView
Curtains.onRootViewsChangedListeners.add(rootViewChangedListener)

Log.i(LogTag, "Added pre draw listener to ${topView.objectIdentity()}")
// On subscribe, trigger a traversal on whatever roots we have
val decorViewToActivity: Map<View, Activity> = ActivityTracker.decorViewToActivityMap

// schedule traversal immediately when we detect a new decor view
throttler.trigger()
}
}
val topView =
Curtains.rootViews.lastOrNull {
ApplicationRefDescriptor.isUsefulRoot(decorViewToActivity[it] ?: it)
}
if (topView != null) {
rootViewChangedListener.onRootViewsChanged(topView, true)
}

context.applicationRef.rootsResolver.attachListener(rootViewListener)
// On subscribe, trigger a traversal on whatever roots we have
rootViewListener.onRootViewsChanged(applicationRef.rootsResolver.rootViews())

Log.i(
LogTag,
"Starting tracking root views, currently ${context.applicationRef.rootsResolver.rootViews().size} root views")
Log.i(LogTag, "Starting tracking root views, currently ${Curtains.rootViews.size} root views")
}

fun stop() {
context.applicationRef.rootsResolver.attachListener(null)
Curtains.onRootViewsChangedListeners.clear()
currentDecorView?.viewTreeObserver?.removeOnPreDrawListener(preDrawListener)
currentDecorView = null
preDrawListener = null
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ internal object SnapshotCommon {
}

// something went wrong, use fallback, make sure to give bitmap back to pool first
Log.i(LogTag, "Using fallback snapshot method")
Log.d(LogTag, "Using fallback snapshot method")
reusableBitmap?.readyForReuse()
return fallback?.takeSnapshot(view)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.facebook.flipper.plugins.uidebugger.core.ActivityTracker
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
import com.facebook.flipper.plugins.uidebugger.model.Bounds
import com.facebook.flipper.plugins.uidebugger.util.DisplayMetrics
import curtains.Curtains

object ApplicationRefDescriptor : ChainedDescriptor<ApplicationRef>() {

Expand All @@ -34,7 +35,7 @@ object ApplicationRefDescriptor : ChainedDescriptor<ApplicationRef>() {
override fun onGetChildren(node: ApplicationRef): List<Any> {
val children = mutableListOf<Any>()

val rootViews = node.rootsResolver.rootViews()
val rootViews = Curtains.rootViews

val decorViewToActivity: Map<View, Activity> = ActivityTracker.decorViewToActivityMap

Expand Down

0 comments on commit 302a9a9

Please sign in to comment.