Integrate ViewTreeViewModelStoreOwner into Activity+Fragment
Ensure that ComponentActivity and Fragment return
the correct result from ViewTreeViewModelStoreOwner.get()
Test: updated Activity and Fragment tests
Change-Id: I29203201d1bdc7a6fba0a16e062d02563ba6bef1
diff --git a/activity/activity-ktx/build.gradle b/activity/activity-ktx/build.gradle
index ce1eb88..c728b82 100644
--- a/activity/activity-ktx/build.gradle
+++ b/activity/activity-ktx/build.gradle
@@ -38,10 +38,10 @@
api("androidx.core:core-ktx:1.1.0") {
because 'Mirror activity dependency graph for -ktx artifacts'
}
- api("androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha01") {
+ api(project(":lifecycle:lifecycle-runtime-ktx")) {
because 'Mirror activity dependency graph for -ktx artifacts'
}
- api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-alpha01")
+ api(project(":lifecycle:lifecycle-viewmodel-ktx"))
api(KOTLIN_STDLIB)
androidTestImplementation(project(":lifecycle:lifecycle-runtime-testing"))
diff --git a/activity/activity/build.gradle b/activity/activity/build.gradle
index 6571d19..327871c 100644
--- a/activity/activity/build.gradle
+++ b/activity/activity/build.gradle
@@ -23,10 +23,10 @@
api("androidx.annotation:annotation:1.1.0")
implementation("androidx.collection:collection:1.0.0")
api("androidx.core:core:1.1.0")
- api("androidx.lifecycle:lifecycle-runtime:2.3.0-alpha01")
- api("androidx.lifecycle:lifecycle-viewmodel:2.3.0-alpha01")
+ api(project(":lifecycle:lifecycle-runtime"))
+ api(project(":lifecycle:lifecycle-viewmodel"))
api("androidx.savedstate:savedstate:1.0.0")
- api("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0-alpha01")
+ api(project(":lifecycle:lifecycle-viewmodel-savedstate"))
androidTestImplementation(project(":lifecycle:lifecycle-runtime-testing"))
androidTestImplementation(KOTLIN_STDLIB)
diff --git a/activity/activity/src/androidTest/java/androidx/activity/ContentViewTest.kt b/activity/activity/src/androidTest/java/androidx/activity/ContentViewTest.kt
index 356b6da..1ca8f2c 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/ContentViewTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/ContentViewTest.kt
@@ -22,6 +22,7 @@
import android.widget.TextView
import androidx.activity.test.R
import androidx.lifecycle.ViewTreeLifecycleOwner
+import androidx.lifecycle.ViewTreeViewModelStoreOwner
import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
@@ -45,7 +46,7 @@
}
@Test
- fun testViewTreeLifecycleOwnerInflation() {
+ fun testViewTreeInflation() {
with(ActivityScenario.launch(ContentViewActivity::class.java)) {
val inflatedTextView: TextView = withActivity { findViewById(R.id.inflated_text_view) }
@@ -53,12 +54,15 @@
assertWithMessage("inflated view has correct ViewTreeLifecycleOwner")
.that(ViewTreeLifecycleOwner.get(inflatedTextView))
.isSameInstanceAs(this@withActivity)
+ assertWithMessage("inflated view has correct ViewTreeViewModelStoreOwner")
+ .that(ViewTreeViewModelStoreOwner.get(inflatedTextView))
+ .isSameInstanceAs(this@withActivity)
}
}
}
@Test
- fun testViewTreeLifecycleAttachment() {
+ fun testViewTreeAttachment() {
runAttachTest("setContentView view only") { setContentView(it) }
runAttachTest("setContentView with LayoutParams") {
setContentView(it, ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT))
@@ -76,19 +80,23 @@
withActivity {
val view = View(this)
- var attachedResult: Any? = "did not attach"
+ var attachedLifecycleOwner: Any? = "did not attach"
+ var attachedViewModelStoreOwner: Any? = "did not attach"
view.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewDetachedFromWindow(v: View?) {
// Do nothing
}
override fun onViewAttachedToWindow(v: View?) {
- attachedResult = ViewTreeLifecycleOwner.get(view)
+ attachedLifecycleOwner = ViewTreeLifecycleOwner.get(view)
+ attachedViewModelStoreOwner = ViewTreeViewModelStoreOwner.get(view)
}
})
attach(view)
assertWithMessage("$message: ViewTreeLifecycleOwner was set correctly")
- .that(attachedResult).isSameInstanceAs(this)
+ .that(attachedLifecycleOwner).isSameInstanceAs(this)
+ assertWithMessage("$message: ViewTreeViewModelStoreOwner was set correctly")
+ .that(attachedViewModelStoreOwner).isSameInstanceAs(this)
}
}
}
diff --git a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
index 58b8ab1..6620d21 100644
--- a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
+++ b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
@@ -67,6 +67,7 @@
import androidx.lifecycle.ViewModelStore;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.lifecycle.ViewTreeLifecycleOwner;
+import androidx.lifecycle.ViewTreeViewModelStoreOwner;
import androidx.savedstate.SavedStateRegistry;
import androidx.savedstate.SavedStateRegistryController;
import androidx.savedstate.SavedStateRegistryOwner;
@@ -354,6 +355,7 @@
// Set the VTLO before setting the content view so that the inflation process
// and attach listeners will see it already present
ViewTreeLifecycleOwner.set(getWindow().getDecorView(), this);
+ ViewTreeViewModelStoreOwner.set(getWindow().getDecorView(), this);
super.setContentView(layoutResID);
}
@@ -362,6 +364,7 @@
// Set the VTLO before setting the content view so that attach listeners
// will see it already present
ViewTreeLifecycleOwner.set(getWindow().getDecorView(), this);
+ ViewTreeViewModelStoreOwner.set(getWindow().getDecorView(), this);
super.setContentView(view);
}
@@ -372,6 +375,7 @@
// Set the VTLO before setting the content view so that attach listeners
// will see it already present
ViewTreeLifecycleOwner.set(getWindow().getDecorView(), this);
+ ViewTreeViewModelStoreOwner.set(getWindow().getDecorView(), this);
super.setContentView(view, params);
}
@@ -382,6 +386,7 @@
// Set the VTLO before setting the content view so that attach listeners
// will see it already present.
ViewTreeLifecycleOwner.set(getWindow().getDecorView(), this);
+ ViewTreeViewModelStoreOwner.set(getWindow().getDecorView(), this);
super.addContentView(view, params);
}
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewLifecycleTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewLifecycleTest.kt
index 9d2a203..9362743 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewLifecycleTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewLifecycleTest.kt
@@ -29,6 +29,7 @@
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewTreeLifecycleOwner
+import androidx.lifecycle.ViewTreeViewModelStoreOwner
import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
@@ -244,18 +245,18 @@
}
/**
- * Test that the ViewTreeLifecycleOwner for a fragment's view is the fragment's own
- * viewLifecycleOwner.
+ * Test that the ViewTree get() methods for a fragment's view work correctly.
*/
@Test
- fun testFragmentViewTreeLifecycleOwner() {
+ fun testFragmentViewTree() {
val activity = activityRule.activity
val fm = activity.supportFragmentManager
- val fragment = ViewTreeLifecycleOwnerCheckFragment()
+ val fragment = ViewTreeCheckFragment()
var observedLifecycleOwner: Any? = "not set"
var observedTreeLifecycleOwner: Any? = "not set"
+ var observedTreeViewModelStoreOwner: Any? = "not set"
val latch = CountDownLatch(1)
activity.runOnUiThread {
@@ -266,6 +267,9 @@
observedLifecycleOwner = owner
observedTreeLifecycleOwner = fragment.view?.let { ViewTreeLifecycleOwner.get(it) }
+ observedTreeViewModelStoreOwner = fragment.view?.let {
+ ViewTreeViewModelStoreOwner.get(it)
+ }
}
fm.beginTransaction().add(R.id.content, fragment).commitNow()
@@ -277,11 +281,19 @@
assertWithMessage("ViewTreeLifecycleOwner should match viewLifecycleOwner after commitNow")
.that(ViewTreeLifecycleOwner.get(fragment.view ?: error("no fragment view created")))
.isSameInstanceAs(fragment.viewLifecycleOwner)
+ assertWithMessage("ViewTreeViewModelStoreOwner should match fragment after commitNow")
+ .that(ViewTreeViewModelStoreOwner.get(fragment.view
+ ?: error("no fragment view created")))
+ .isSameInstanceAs(fragment)
assertWithMessage("ViewTreeLifecycleOwner should match viewLifecycleOwner in " +
"viewLifecycleOwnerLiveData observer")
.that(observedTreeLifecycleOwner)
.isSameInstanceAs(fragment.viewLifecycleOwner)
+ assertWithMessage("ViewTreeViewModelStoreOwner should match fragment in " +
+ "viewLifecycleOwnerLiveData observer")
+ .that(observedTreeViewModelStoreOwner)
+ .isSameInstanceAs(fragment)
assertWithMessage("ViewTreeLifecycleOwner should match observed LifecycleOwner in " +
"viewLifecycleOwnerLiveData observer")
@@ -292,10 +304,15 @@
"onViewCreated")
.that(fragment.onViewCreatedLifecycleOwner)
.isSameInstanceAs(fragment.viewLifecycleOwner)
+ assertWithMessage("ViewTreeViewModelStoreOwner should match fragment in " +
+ "onViewCreated")
+ .that(fragment.onViewCreatedViewModelStoreOwner)
+ .isSameInstanceAs(fragment)
}
- class ViewTreeLifecycleOwnerCheckFragment : Fragment() {
+ class ViewTreeCheckFragment : Fragment() {
var onViewCreatedLifecycleOwner: Any? = "not set"
+ var onViewCreatedViewModelStoreOwner: Any? = "not set"
override fun onCreateView(
inflater: LayoutInflater,
@@ -305,6 +322,7 @@
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
onViewCreatedLifecycleOwner = ViewTreeLifecycleOwner.get(view)
+ onViewCreatedViewModelStoreOwner = ViewTreeViewModelStoreOwner.get(view)
}
}
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
index 2d594f9..0da7ba0 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
@@ -84,6 +84,7 @@
import androidx.lifecycle.ViewModelStore;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.lifecycle.ViewTreeLifecycleOwner;
+import androidx.lifecycle.ViewTreeViewModelStoreOwner;
import androidx.loader.app.LoaderManager;
import androidx.savedstate.SavedStateRegistry;
import androidx.savedstate.SavedStateRegistryController;
@@ -2894,8 +2895,9 @@
mViewLifecycleOwner.initialize();
// Tell the fragment's new view about it before we tell anyone listening
// to mViewLifecycleOwnerLiveData and before onViewCreated, so that calls to
- // ViewTreeLifecycleOwner.get() return something meaningful
+ // ViewTree get() methods return something meaningful
ViewTreeLifecycleOwner.set(mView, mViewLifecycleOwner);
+ ViewTreeViewModelStoreOwner.set(mView, this);
// Then inform any Observers of the new LifecycleOwner
mViewLifecycleOwnerLiveData.setValue(mViewLifecycleOwner);
} else {