Skip to content

Commit

Permalink
Set view tree owners (#606)
Browse files Browse the repository at this point in the history
* Set view tree owners in RibActivity#onCreate
* Cleanup duplicate setting of view tree owners
* Add more assertions
  • Loading branch information
jbarr21 authored Aug 7, 2023
1 parent 0d87f61 commit 9f6a215
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
package com.uber.rib.compose.root

import android.view.ViewGroup
import androidx.lifecycle.setViewTreeLifecycleOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import com.uber.rib.compose.root.main.MainScope
import com.uber.rib.compose.util.AnalyticsClient
import com.uber.rib.compose.util.AnalyticsClientImpl
Expand All @@ -43,11 +41,8 @@ interface RootScope {

abstract fun presenter(): EmptyPresenter

fun view(parentViewGroup: ViewGroup, activity: RibActivity): RootView {
return RootView(parentViewGroup.context).apply {
setViewTreeLifecycleOwner(activity)
setViewTreeSavedStateRegistryOwner(activity)
}
fun view(parentViewGroup: ViewGroup): RootView {
return RootView(parentViewGroup.context)
}

@Expose
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,12 @@ import android.view.ViewGroup
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.setViewTreeLifecycleOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import com.uber.rib.compose.root.main.loggedin.LoggedInScope
import com.uber.rib.compose.root.main.loggedout.LoggedOutScope
import com.uber.rib.compose.util.AnalyticsClient
import com.uber.rib.compose.util.ExperimentClient
import com.uber.rib.compose.util.LoggerClient
import com.uber.rib.core.ComposePresenter
import com.uber.rib.core.RibActivity
import motif.Expose

@motif.Scope
Expand Down Expand Up @@ -55,15 +52,8 @@ interface MainScope {
}
}

fun view(
parentViewGroup: ViewGroup,
activity: RibActivity,
presenter: ComposePresenter,
): ComposeView {
return ComposeView(parentViewGroup.context).apply {
setViewTreeLifecycleOwner(activity)
setViewTreeSavedStateRegistryOwner(activity)
}
fun view(parentViewGroup: ViewGroup): ComposeView {
return ComposeView(parentViewGroup.context)
}

abstract fun childContent(): MainRouter.ChildContent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@
package com.uber.rib.workers.root

import android.view.ViewGroup
import androidx.lifecycle.setViewTreeLifecycleOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import com.uber.rib.core.EmptyPresenter
import com.uber.rib.core.RibActivity
import com.uber.rib.workers.root.main.MainScope

@motif.Scope
Expand All @@ -36,11 +33,8 @@ interface RootScope {

abstract fun presenter(): EmptyPresenter

fun view(parentViewGroup: ViewGroup, activity: RibActivity): RootView {
return RootView(parentViewGroup.context).apply {
setViewTreeLifecycleOwner(activity)
setViewTreeSavedStateRegistryOwner(activity)
}
fun view(parentViewGroup: ViewGroup): RootView {
return RootView(parentViewGroup.context)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ import android.view.ViewGroup
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.setViewTreeLifecycleOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import com.uber.rib.core.ComposePresenter
import com.uber.rib.core.RibActivity
import com.uber.rib.workers.root.main.ribworkerselection.RibWorkerSelectionScope

@motif.Scope
Expand All @@ -45,15 +42,8 @@ interface MainScope {
}
}

fun view(
parentViewGroup: ViewGroup,
activity: RibActivity,
presenter: ComposePresenter,
): ComposeView {
return ComposeView(parentViewGroup.context).apply {
setViewTreeLifecycleOwner(activity)
setViewTreeSavedStateRegistryOwner(activity)
}
fun view(parentViewGroup: ViewGroup): ComposeView {
return ComposeView(parentViewGroup.context)
}

abstract fun childContent(): MainRouter.ChildContent
Expand Down
3 changes: 2 additions & 1 deletion android/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ android-api = "4.1.1.4"
androidx-activity = "1.7.0"
androidx-annotations = "1.1.0"
androidx-appcompat = "1.3.0"
androidx-lifecycle = "2.5.1"
androidx-lifecycle = "2.6.1"
autocommon = "0.8"
autodispose = "1.4.0"
autoservice = "1.0-rc4"
Expand Down Expand Up @@ -95,6 +95,7 @@ javax-inject = { group = "javax.inject", name = "javax.inject", version = "1" }
jsr250 = { group = "javax.annotation", name = "jsr250-api", version.ref = "jsr250" }
kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" }
leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "leakcanary" }
lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime", version.ref = "androidx-lifecycle" }
motif-compiler = { group = "com.uber.motif", name = "motif-compiler", version.ref = "motif" }
motif-library = { group = "com.uber.motif", name = "motif", version.ref = "motif" }
percent = { group = "androidx.percentlayout", name = "percentlayout", version.ref = "percent" }
Expand Down
1 change: 1 addition & 0 deletions android/libraries/rib-android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies {
implementation(libs.coroutines.android)
implementation(libs.coroutines.rx2)
testImplementation(testLibs.robolectric)
testImplementation(libs.lifecycle.runtime)
testImplementation(libs.appcompat)
testImplementation(testLibs.mockitoKotlin)
testImplementation(project(":libraries:rib-test"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import android.content.res.Configuration
import android.os.Build
import android.view.ViewGroup
import androidx.annotation.CallSuper
import androidx.lifecycle.ViewTreeLifecycleOwner
import androidx.lifecycle.ViewTreeViewModelStoreOwner
import androidx.savedstate.ViewTreeSavedStateRegistryOwner
import com.uber.autodispose.lifecycle.CorrespondingEventsFunction
import com.uber.autodispose.lifecycle.LifecycleEndedException
import com.uber.autodispose.lifecycle.LifecycleNotStartedException
Expand Down Expand Up @@ -90,6 +93,7 @@ abstract class RibActivity :
@CallSuper
override fun onCreate(savedInstanceState: android.os.Bundle?) {
super.onCreate(savedInstanceState)
initViewTreeOwners()
val rootViewGroup = findViewById<ViewGroup>(android.R.id.content)
_lifecycleFlow.tryEmit(createOnCreateEvent(savedInstanceState))
val wrappedBundle: Bundle? =
Expand Down Expand Up @@ -232,6 +236,18 @@ abstract class RibActivity :
*/
protected abstract fun createRouter(parentViewGroup: ViewGroup): ViewRouter<*, *>

/**
* [RibActivity] must call this since it does not use [ComponentActivity.setContentView] which
* already handles this.
*/
private fun initViewTreeOwners() {
// Set the view tree owners before setting the content view so that the inflation process
// and attach listeners will see them already present
ViewTreeLifecycleOwner.set(window.decorView, this)
ViewTreeViewModelStoreOwner.set(window.decorView, this)
ViewTreeSavedStateRegistryOwner.set(window.decorView, this)
}

companion object {
/**
* Figures out which corresponding next lifecycle event in which to unsubscribe, for Activities.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import android.content.Intent
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
import com.google.common.truth.Truth.assertThat
import com.uber.autodispose.AutoDispose
import com.uber.autodispose.lifecycle.LifecycleEndedException
Expand Down Expand Up @@ -189,6 +192,26 @@ class RibActivityTest {
assertThat(activity.interactor).isNotNull()
}

@Test
fun onCreate_setsViewTreeOwners_forDecorView() {
val activity = Robolectric.buildActivity(EmptyActivity::class.java).create(null).get()
val decorView = activity.window.decorView
assertThat(decorView.findViewTreeLifecycleOwner()).isNotNull()
assertThat(decorView.findViewTreeSavedStateRegistryOwner()).isNotNull()
assertThat(decorView.findViewTreeViewModelStoreOwner()).isNotNull()
}

@Test
fun onCreate_setsViewTreeOwners_forViewAddedToDecorView() {
val activity = Robolectric.buildActivity(EmptyActivity::class.java).create(null).get()
val contentView = activity.findViewById<FrameLayout>(android.R.id.content)
val rootView = View(RuntimeEnvironment.application)
contentView.addView(rootView)
assertThat(rootView.findViewTreeLifecycleOwner()).isNotNull()
assertThat(rootView.findViewTreeSavedStateRegistryOwner()).isNotNull()
assertThat(rootView.findViewTreeViewModelStoreOwner()).isNotNull()
}

@Test(expected = IllegalArgumentException::class)
fun createEvent_withIllegalType_shouldFail() {
create(ActivityLifecycleEvent.Type.CREATE)
Expand Down

0 comments on commit 9f6a215

Please sign in to comment.