Merge "Prevent accidentally removing active fragments" into androidx-main
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentManagerTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentManagerTest.kt
index 72dcbb3..9dcbe26 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentManagerTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentManagerTest.kt
@@ -296,4 +296,113 @@
             assertThat(popped).isTrue()
         }
     }
+
+    @Test
+    fun navigatePopNavigateRestore() {
+        withUse(ActivityScenario.launch(FragmentTestActivity::class.java)) {
+            val fm = withActivity { supportFragmentManager }
+            val fragment2 = StrictViewFragment()
+
+            fm.beginTransaction()
+                .setCustomAnimations(
+                    androidx.fragment.R.animator.fragment_fade_enter,
+                    androidx.fragment.R.animator.fragment_fade_exit,
+                    androidx.fragment.R.animator.fragment_fade_enter,
+                    androidx.fragment.R.animator.fragment_fade_exit
+                )
+                .replace(R.id.content, fragment2, "fragment2")
+                .setReorderingAllowed(true)
+                .addToBackStack("stack2")
+                .commit()
+            executePendingTransactions()
+
+            val fragment3 = StrictViewFragment()
+
+            fm.saveBackStack("stack2")
+            fm.beginTransaction()
+                .setCustomAnimations(
+                    androidx.fragment.R.animator.fragment_fade_enter,
+                    androidx.fragment.R.animator.fragment_fade_exit,
+                    androidx.fragment.R.animator.fragment_fade_enter,
+                    androidx.fragment.R.animator.fragment_fade_exit
+                )
+                .replace(R.id.content, fragment3, "fragment3")
+                .setReorderingAllowed(true)
+                .addToBackStack("stack3")
+                .commit()
+
+            fm.saveBackStack("stack3")
+            fm.restoreBackStack("stack2")
+            executePendingTransactions()
+
+            recreate()
+
+            val restoredFragmentManager = withActivity { supportFragmentManager }
+
+            assertThat(restoredFragmentManager.findFragmentByTag("fragment2"))
+                .isNotNull()
+        }
+    }
+
+    @Test
+    fun replacePopReplaceWithRestored() {
+        withUse(ActivityScenario.launch(FragmentTestActivity::class.java)) {
+            val fm = withActivity { supportFragmentManager }
+            val fragment2 = StrictViewFragment()
+
+            fm.beginTransaction()
+                .setCustomAnimations(
+                    androidx.fragment.R.animator.fragment_fade_enter,
+                    androidx.fragment.R.animator.fragment_fade_exit,
+                    androidx.fragment.R.animator.fragment_fade_enter,
+                    androidx.fragment.R.animator.fragment_fade_exit
+                )
+                .replace(R.id.content, fragment2, "fragment2")
+                .setReorderingAllowed(true)
+                .addToBackStack("stack2")
+                .commit()
+            executePendingTransactions()
+
+            val fragment3 = StrictViewFragment()
+
+            fm.popBackStack("stack2", 0)
+            fm.beginTransaction()
+                .setCustomAnimations(
+                    androidx.fragment.R.animator.fragment_fade_enter,
+                    androidx.fragment.R.animator.fragment_fade_exit,
+                    androidx.fragment.R.animator.fragment_fade_enter,
+                    androidx.fragment.R.animator.fragment_fade_exit
+                )
+                .replace(R.id.content, fragment3, "fragment3")
+                .setReorderingAllowed(true)
+                .addToBackStack("stack3")
+                .commit()
+
+            fm.popBackStack("stack3", 0)
+
+            val restoreFragment2 = StrictViewFragment()
+            withActivity {
+                restoreFragment2.setInitialSavedState(fm.saveFragmentInstanceState(fragment2))
+            }
+            fm.beginTransaction()
+                .setCustomAnimations(
+                    androidx.fragment.R.animator.fragment_fade_enter,
+                    androidx.fragment.R.animator.fragment_fade_exit,
+                    androidx.fragment.R.animator.fragment_fade_enter,
+                    androidx.fragment.R.animator.fragment_fade_exit
+                )
+                .replace(R.id.content, restoreFragment2, "fragment2")
+                .setReorderingAllowed(true)
+                .addToBackStack("stack2")
+                .commit()
+            executePendingTransactions()
+
+            recreate()
+
+            val restoredFragmentManager = withActivity { supportFragmentManager }
+
+            assertThat(restoredFragmentManager.findFragmentByTag("fragment2"))
+                .isNotNull()
+        }
+    }
 }
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStore.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStore.java
index f7b5fb9..bcf9942 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStore.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStore.java
@@ -151,6 +151,10 @@
             mNonConfig.removeRetainedFragment(f);
         }
 
+        if (mActive.get(f.mWho) != newlyInactive) {
+            return;
+        }
+
         // Don't remove yet. That happens in burpActive(). This prevents
         // concurrent modification while iterating over mActive
         FragmentStateManager removedStateManager = mActive.put(f.mWho, null);