Merge "Update fragment dependency" into androidx-main
diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml
index 8858e9e..09b9ddb 100644
--- a/.github/workflows/presubmit.yml
+++ b/.github/workflows/presubmit.yml
@@ -560,15 +560,6 @@
           wrapper-directory: ${{ env.project-root }}/gradle/wrapper
           wrapper-cache-enabled: true
 
-
-      - name: "upload build artifacts"
-        continue-on-error: true
-        if: always()
-        uses: actions/upload-artifact@v2
-        with:
-          name: artifacts_${{ env.artifact-id }}
-          path: ~/dist
-
       - name: Set up Cloud SDK
         uses: google-github-actions/setup-gcloud@master
         with:
@@ -588,6 +579,13 @@
           wrapper-directory: ${{ env.project-root }}/gradle/wrapper
           wrapper-cache-enabled: true
 
+      - name: "upload build artifacts"
+        continue-on-error: true
+        if: always()
+        uses: actions/upload-artifact@v2
+        with:
+          name: artifacts_${{ env.artifact-id }}
+          path: ~/dist
       - name: "Report job status"
         id: output-status
         if: always()
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index a649d44..8dd795e 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -96,7 +96,7 @@
     val PREFERENCE = Version("1.2.0-alpha01")
     val RECOMMENDATION = Version("1.1.0-alpha01")
     val RECYCLERVIEW = Version("1.3.0-alpha01")
-    val RECYCLERVIEW_SELECTION = Version("2.0.0-alpha01")
+    val RECYCLERVIEW_SELECTION = Version("1.2.0-alpha01")
     val REMOTECALLBACK = Version("1.0.0-alpha02")
     val RESOURCEINSPECTION = Version("1.0.0-alpha01")
     val ROOM = Version("2.4.0-alpha02")
diff --git a/buildSrc/src/main/kotlin/androidx/build/ftl/GCloudCLIWrapper.kt b/buildSrc/src/main/kotlin/androidx/build/ftl/GCloudCLIWrapper.kt
index 80b80c8..385e271 100644
--- a/buildSrc/src/main/kotlin/androidx/build/ftl/GCloudCLIWrapper.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/ftl/GCloudCLIWrapper.kt
@@ -16,6 +16,7 @@
 
 package androidx.build.ftl
 
+import androidx.build.ftl.GCloudCLIWrapper.RunTestParameters.Companion.TEST_OUTPUT_FILE_NAME
 import com.google.gson.Gson
 import com.google.gson.annotations.SerializedName
 import com.google.gson.reflect.TypeToken
@@ -24,6 +25,7 @@
 import java.io.ByteArrayOutputStream
 import java.io.File
 import java.util.Locale
+import java.util.UUID
 
 /**
  * Wrapper around GCloud CLI.
@@ -44,53 +46,102 @@
     /**
      * Path to the gcloud executable, derived from `which gcloud` call.
      */
-    private val executable: String by lazy {
-        val output = ByteArrayOutputStream()
-        val result = execOperations.exec {
-            it.commandLine("which", "gcloud")
-            it.standardOutput = output
-            it.isIgnoreExitValue = true
-        }
-        if (result.exitValue != 0) {
-            throw GradleException(
-                """
-                Unable to find gcloud CLI executable.
-                `which gcloud` returned exit code ${result.exitValue}.
-                Make sure gcloud CLI is installed, authenticated and is part of your PATH.
-                See https://cloud.google.com/sdk/gcloud for installation instructions.
-                """.trimIndent()
-            )
-        }
-        output.toString(Charsets.UTF_8).trim()
+    private val gcloud: String by lazy {
+        findExecutable("gcloud")
+    }
+
+    /**
+     * Path to the gsutil executable, derived from `which gsutil` call.
+     */
+    private val gsutil: String by lazy {
+        findExecutable("gsutil")
     }
 
     private inline fun <reified T> executeGcloud(
         vararg params: String
     ): T {
         val output = ByteArrayOutputStream()
-        execOperations.exec {
-            it.executable = executable
+        val errorOutput = ByteArrayOutputStream()
+        val execResult = execOperations.exec {
+            it.executable = gcloud
             it.args = params.toList() + "--format=json"
             it.standardOutput = output
+            it.errorOutput = errorOutput
+            it.isIgnoreExitValue = true
         }
+        if (execResult.exitValue != 0) {
+            System.err.println("GCloud command failed: ${errorOutput.toString(Charsets.UTF_8)}")
+        }
+        // still try to parse the because when it fails (e.g. test failure), it returns a non-0
+        // exit code but still prints the output. We are interested in the output.
         val commandOutput = output.toString(Charsets.UTF_8)
         return gson.parse(commandOutput)
     }
 
+    private fun execGsUtil(
+        vararg params: String
+    ): String {
+        val output = ByteArrayOutputStream()
+        execOperations.exec {
+            it.executable = gsutil
+            it.args = params.toList()
+            it.standardOutput = output
+        }
+        return output.toString(Charsets.UTF_8)
+    }
+
     /**
      * https://cloud.google.com/sdk/gcloud/reference/firebase/test/android/run
      */
     fun runTest(
-        testedApk: File,
-        testApk: File
+        params: RunTestParameters
     ): List<TestResult> {
-        return executeGcloud(
+        val testResults = executeGcloud<List<TestResult>>(
             "firebase", "test", "android", "run",
             "--type", "instrumentation",
-            "--test", testApk.canonicalPath,
-            "--app", testedApk.canonicalPath,
-            "--num-flaky-test-attempts", "3",
+            "--test", params.testApk.canonicalPath,
+            "--app", params.testedApk.canonicalPath,
+            "--num-flaky-test-attempts", "2",
+            "--results-bucket=${params.bucketName}",
+            "--results-dir=${params.resultsBucketDir}",
+            "--results-history-name=${params.projectPath}"
         )
+        // copy the test results from the bucket to the build directory
+        execGsUtil(
+            "cp", "-r", params.cloudBucketPath() + "/*", params.resultsLocalDir.canonicalPath
+        )
+        // finally, write the command response into the directory as well
+        val testResultOutput = params.resultsLocalDir.resolve(TEST_OUTPUT_FILE_NAME)
+        testResultOutput.bufferedWriter(Charsets.UTF_8).use {
+            gson.toJson(
+                testResults,
+                it
+            )
+        }
+        return testResults
+    }
+
+    /**
+     * find the given executable's path in the PATH via `which` command.
+     */
+    private fun findExecutable(name: String): String {
+        val output = ByteArrayOutputStream()
+        val result = execOperations.exec {
+            it.commandLine("which", name)
+            it.standardOutput = output
+            it.isIgnoreExitValue = true
+        }
+        if (result.exitValue != 0) {
+            throw GradleException(
+                """
+                Unable to find $name CLI executable.
+                `which $name` returned exit code ${result.exitValue}.
+                Make sure gcloud CLI is installed, authenticated and is part of your PATH.
+                See https://cloud.google.com/sdk/gcloud for installation instructions.
+                """.trimIndent()
+            )
+        }
+        return output.toString(Charsets.UTF_8).trim()
     }
 
     /**
@@ -110,6 +161,95 @@
             private val SUCCESS_OUTCOMES = listOf("passed", "flaky")
         }
     }
+
+    /**
+     * Parameters for invoking a test on the Firebase Test Lab
+     */
+    internal data class RunTestParameters(
+        /**
+         * The project path for which we are executing the tests for.
+         */
+        val projectPath: String,
+        /**
+         * The tested APK file
+         */
+        val testedApk: File,
+        /**
+         * The test APK file which includes the instrumentation tests
+         */
+        val testApk: File,
+        /**
+         * The name of the GS bucket to save the results
+         */
+        val bucketName: String = DEFAULT_BUCKET_NAME,
+        /**
+         * The GS Bucket directory where the results will be saved
+         */
+        val resultsBucketDir: String = buildRelativeResultDirPath(projectPath),
+        /**
+         * The local directory where we will download the test results
+         */
+        val resultsLocalDir: File,
+    ) {
+
+        /**
+         * Returns the path to the Google Cloud bucket where the test run results are saved
+         */
+        fun cloudBucketPath(): String {
+            return "gs://$bucketName/$resultsBucketDir"
+        }
+
+        companion object {
+            const val DEFAULT_BUCKET_NAME = "androidx-ftl-test-results"
+
+            /**
+             * The file into which the result of the gcloud command will be written.
+             */
+            const val TEST_OUTPUT_FILE_NAME = "testResults.json"
+
+            /**
+             * Generates a relative path for test results.
+             *
+             * If run on Github Actions CI, this method will use the environment variables to
+             * create a unique path for the action.
+             * If run locally, this will create a random UUID for the directory.
+             */
+            private fun buildRelativeResultDirPath(
+                projectPath: String
+            ): String {
+                // github action env variables:
+                // https://docs.github.com/en/actions/reference/environment-variables
+                val inGithubActions = System.getenv().containsKey("GITHUB_ACTIONS")
+                val pathValues = if (inGithubActions) {
+                    val workflowName = requireEnvValue("GITHUB_WORKFLOW")
+                    val runNumber = requireEnvValue("GITHUB_RUN_NUMBER")
+                    val runId = requireEnvValue("GITHUB_RUN_ID")
+                    val ref = System.getenv("GITHUB_REF")
+                    listOfNotNull(
+                        "github",
+                        projectPath,
+                        ref,
+                        workflowName,
+                        runNumber,
+                        runId,
+                    )
+                } else {
+                    listOf(
+                        "local",
+                        projectPath,
+                        UUID.randomUUID().toString()
+                    )
+                }
+                return pathValues.joinToString("/")
+            }
+
+            private fun requireEnvValue(name: String): String {
+                return System.getenv(name) ?: throw GradleException(
+                    "Cannot find required environment variable: $name"
+                )
+            }
+        }
+    }
 }
 
 private inline fun <reified T> Gson.parse(
diff --git a/buildSrc/src/main/kotlin/androidx/build/ftl/RunTestOnFTLTask.kt b/buildSrc/src/main/kotlin/androidx/build/ftl/RunTestOnFTLTask.kt
index 0d4f9a3..8ec207d 100644
--- a/buildSrc/src/main/kotlin/androidx/build/ftl/RunTestOnFTLTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/ftl/RunTestOnFTLTask.kt
@@ -16,16 +16,21 @@
 
 package androidx.build.ftl
 
+import androidx.build.getDistributionDirectory
+import androidx.build.getSupportRootFolder
 import com.android.build.gradle.api.ApkVariant
 import com.android.build.gradle.api.ApkVariantOutput
 import com.android.build.gradle.api.TestVariant
-import com.google.gson.Gson
 import org.gradle.api.DefaultTask
 import org.gradle.api.GradleException
 import org.gradle.api.Project
+import org.gradle.api.file.DirectoryProperty
 import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.Copy
 import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.OutputDirectory
 import org.gradle.api.tasks.PathSensitive
 import org.gradle.api.tasks.PathSensitivity
 import org.gradle.api.tasks.TaskAction
@@ -45,6 +50,7 @@
  * Due to the limitations of FTL, this task only support application instrumentation tests for now.
  */
 @Suppress("UnstableApiUsage") // for gradle property APIs
+@CacheableTask
 abstract class RunTestOnFTLTask @Inject constructor(
     private val workerExecutor: WorkerExecutor
 ) : DefaultTask() {
@@ -63,8 +69,8 @@
     /**
      * Output file to write the results
      */
-    @get:OutputFile
-    abstract val testResults: RegularFileProperty
+    @get:OutputDirectory
+    abstract val testResults: DirectoryProperty
 
     @TaskAction
     fun executeTest() {
@@ -74,35 +80,37 @@
             it.testApk.set(testApk)
             it.testedApk.set(testedApk)
             it.testResults.set(testResults)
+            it.projectPath.set(project.relativeResultPath())
         }
     }
 
     interface RunFTLTestParams : WorkParameters {
+        val projectPath: Property<String>
         val testApk: RegularFileProperty
         val testedApk: RegularFileProperty
-        val testResults: RegularFileProperty
+        val testResults: DirectoryProperty
     }
 
     abstract class RunFTLTestWorkAction @Inject constructor(
         private val execOperations: ExecOperations
     ) : WorkAction<RunFTLTestParams> {
         override fun execute() {
+            val localTestResultDir = parameters.testResults.asFile.get()
+            localTestResultDir.apply {
+                deleteRecursively()
+                mkdirs()
+            }
             val testApk = parameters.testApk.asFile.get()
             val testedApk = parameters.testedApk.asFile.get()
             val gcloud = GCloudCLIWrapper(execOperations)
-            val result = gcloud.runTest(
+            val params = GCloudCLIWrapper.RunTestParameters(
                 testedApk = testedApk,
-                testApk = testApk
+                testApk = testApk,
+                projectPath = parameters.projectPath.get(),
+                resultsLocalDir = localTestResultDir
+
             )
-            val outFile = parameters.testResults.asFile.get()
-            outFile.parentFile.mkdirs()
-            val gson = Gson()
-            outFile.bufferedWriter(Charsets.UTF_8).use {
-                gson.toJson(
-                    result,
-                    it
-                )
-            }
+            val result = gcloud.runTest(params)
             val failed = result.filterNot {
                 it.passed
             }
@@ -114,7 +122,6 @@
 
     companion object {
         private const val TASK_SUFFIX = "OnFirebaseTestLab"
-        private const val TEST_OUTPUT_FILE_NAME = "testResults.json"
 
         /**
          * Creates an FTL test runner task and returns it.
@@ -128,16 +135,26 @@
             val testedVariant = testVariant.testedVariant as? ApkVariant
                 ?: return null
             val taskName = testVariant.name + TASK_SUFFIX
+            val testResultDir = project.layout.buildDirectory.dir(
+                "ftl-results"
+            )
+            // create task to copy results into dist directory
+            val copyToDistTask = project.tasks.register(
+                "copyResultsOf${taskName}ToDist",
+                Copy::class.java
+            ) {
+                it.description = "Copy test results from $taskName into DIST folder"
+                it.group = "build"
+                it.from(testResultDir)
+                it.into(
+                    project.getDistributionDirectory()
+                        .resolve("ftl-results/${project.relativeResultPath()}/$taskName")
+                )
+            }
             return project.tasks.register(taskName, RunTestOnFTLTask::class.java) { task ->
                 task.description = "Run ${testVariant.name} tests on Firebase Test Lab"
                 task.group = "Verification"
-                task.testResults.set(
-                    project.layout.buildDirectory.dir(
-                        "ftl-results"
-                    ).map {
-                        it.file(TEST_OUTPUT_FILE_NAME)
-                    }
-                )
+                task.testResults.set(testResultDir)
                 task.dependsOn(testVariant.packageApplicationProvider)
                 task.dependsOn(testedVariant.packageApplicationProvider)
 
@@ -153,7 +170,16 @@
                         .firstOrNull()
                         ?.outputFile
                 )
+                task.finalizedBy(copyToDistTask)
             }
         }
     }
-}
\ No newline at end of file
+}
+
+/**
+ * Returns the relative path of the project wrt the support root. This path is used for both
+ * local dist path and cloud bucket paths.
+ */
+private fun Project.relativeResultPath() = projectDir.relativeTo(
+    project.getSupportRootFolder()
+).path
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
index 737d89a..ec34900 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
@@ -33,6 +33,9 @@
 import androidx.compose.ui.draw.clipToBounds
 import androidx.compose.ui.layout.SubcomposeLayout
 import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.semantics.CollectionInfo
+import androidx.compose.ui.semantics.collectionInfo
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.LayoutDirection
 
@@ -85,6 +88,12 @@
             .clipToBounds()
             .padding(contentPadding)
             .then(state.remeasurementModifier)
+            .semantics {
+                collectionInfo = CollectionInfo(
+                    rowCount = if (isVertical) -1 else 1,
+                    columnCount = if (isVertical) 1 else -1
+                )
+            }
     ) { constraints ->
         constraints.assertNotNestingScrollableContainers(isVertical)
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/SelectableGroup.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/SelectableGroup.kt
index e433162..1ff8402 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/SelectableGroup.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/SelectableGroup.kt
@@ -26,6 +26,6 @@
  *
  * @see selectableGroup
  */
-fun Modifier.selectableGroup() = this.semantics(true) {
+fun Modifier.selectableGroup() = this.semantics {
     selectableGroup()
 }
\ No newline at end of file
diff --git a/compose/integration-tests/demos/src/androidTest/java/androidx/compose/integration/demos/test/DemoTest.kt b/compose/integration-tests/demos/src/androidTest/java/androidx/compose/integration/demos/test/DemoTest.kt
index b6b215f..e90c1cf 100644
--- a/compose/integration-tests/demos/src/androidTest/java/androidx/compose/integration/demos/test/DemoTest.kt
+++ b/compose/integration-tests/demos/src/androidTest/java/androidx/compose/integration/demos/test/DemoTest.kt
@@ -182,6 +182,10 @@
             Espresso.pressBack()
             rule.waitForIdle()
         }
+
+        clearFocusFromDemo()
+        rule.waitForIdle()
+
         Espresso.pressBack()
         rule.waitForIdle()
 
@@ -212,6 +216,23 @@
     private fun SemanticsNodeInteractionCollection.isNotEmpty(): Boolean {
         return fetchSemanticsNodes(atLeastOneRootRequired = false).isNotEmpty()
     }
+
+    private fun clearFocusFromDemo() {
+        with(rule.activity) {
+            if (hostView.hasFocus()) {
+                if (hostView.isFocused) {
+                    // One of the Compose components has focus.
+                    focusManager.clearFocus(forcedClear = true)
+                } else {
+                    // A child view has focus. (View interop scenario).
+                    // We could also use hostViewGroup.focusedChild?.clearFocus(), but the
+                    // interop views might end up being focused if one of them is marked as
+                    // focusedByDefault. So we clear focus by requesting focus on the owner.
+                    rule.runOnUiThread { hostView.requestFocus() }
+                }
+            }
+        }
+    }
 }
 
 private val AllButIgnoredDemos =
diff --git a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoActivity.kt b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoActivity.kt
index d858192..8f59333 100644
--- a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoActivity.kt
+++ b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoActivity.kt
@@ -21,37 +21,43 @@
 import android.content.Intent
 import android.content.SharedPreferences
 import android.os.Bundle
+import android.view.View
 import android.view.Window
 import androidx.activity.ComponentActivity
 import androidx.activity.OnBackPressedCallback
 import androidx.activity.OnBackPressedDispatcher
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleEventObserver
-import androidx.preference.PreferenceManager
 import androidx.compose.integration.demos.common.ActivityDemo
 import androidx.compose.integration.demos.common.Demo
 import androidx.compose.integration.demos.common.DemoCategory
-import androidx.compose.ui.graphics.toArgb
 import androidx.compose.material.Colors
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.darkColors
 import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.Saver
 import androidx.compose.runtime.saveable.listSaver
 import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.platform.LocalView
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.preference.PreferenceManager
 
 /**
  * Main [Activity] containing all Compose related demos.
  */
 class DemoActivity : ComponentActivity() {
+    lateinit var hostView: View
+    lateinit var focusManager: FocusManager
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
@@ -59,6 +65,8 @@
         ComposeView(this).also {
             setContentView(it)
         }.setContent {
+            hostView = LocalView.current
+            focusManager = LocalFocusManager.current
             val activityStarter = fun(demo: ActivityDemo<*>) {
                 startActivity(Intent(this, demo.activityClass.java))
             }
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/BottomNavigationDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/BottomNavigationDemo.kt
index 50d8776..02ff422 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/BottomNavigationDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/BottomNavigationDemo.kt
@@ -43,42 +43,44 @@
 fun BottomNavigationDemo() {
     var alwaysShowLabels by remember { mutableStateOf(false) }
     Column(
-        modifier = Modifier.fillMaxHeight().selectableGroup(),
+        modifier = Modifier.fillMaxHeight(),
         verticalArrangement = Arrangement.Bottom
     ) {
-        Row(
-            modifier = Modifier
-                .fillMaxWidth()
-                .height(56.dp)
-                .selectable(
+        Column(Modifier.selectableGroup()) {
+            Row(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .height(56.dp)
+                    .selectable(
+                        selected = !alwaysShowLabels,
+                        onClick = { alwaysShowLabels = false }
+                    ),
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+                RadioButton(
                     selected = !alwaysShowLabels,
-                    onClick = { alwaysShowLabels = false }
-                ),
-            verticalAlignment = Alignment.CenterVertically
-        ) {
-            RadioButton(
-                selected = !alwaysShowLabels,
-                onClick = null
-            )
-            Spacer(Modifier.requiredWidth(16.dp))
-            Text("Only show labels when selected")
-        }
-        Row(
-            modifier = Modifier
-                .fillMaxWidth()
-                .height(56.dp)
-                .selectable(
+                    onClick = null
+                )
+                Spacer(Modifier.requiredWidth(16.dp))
+                Text("Only show labels when selected")
+            }
+            Row(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .height(56.dp)
+                    .selectable(
+                        selected = alwaysShowLabels,
+                        onClick = { alwaysShowLabels = true }
+                    ),
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+                RadioButton(
                     selected = alwaysShowLabels,
-                    onClick = { alwaysShowLabels = true }
-                ),
-            verticalAlignment = Alignment.CenterVertically
-        ) {
-            RadioButton(
-                selected = alwaysShowLabels,
-                onClick = null
-            )
-            Spacer(Modifier.requiredWidth(16.dp))
-            Text("Always show labels")
+                    onClick = null
+                )
+                Spacer(Modifier.requiredWidth(16.dp))
+                Text("Always show labels")
+            }
         }
 
         Spacer(Modifier.height(50.dp))
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt
index 604dd36..eda5587 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt
@@ -221,7 +221,7 @@
     tabs: @Composable () -> Unit
 ) {
     Surface(
-        modifier = modifier.selectableGroup(),
+        modifier = modifier,
         color = backgroundColor,
         contentColor = contentColor
     ) {
@@ -237,6 +237,7 @@
             Modifier.fillMaxWidth()
                 .wrapContentSize(align = Alignment.CenterStart)
                 .horizontalScroll(scrollState)
+                .selectableGroup()
                 .clipToBounds()
         ) { constraints ->
             val minTabWidth = ScrollableTabRowMinimumTabWidth.roundToPx()
diff --git a/compose/ui/ui/api/1.0.0-beta06.txt b/compose/ui/ui/api/1.0.0-beta06.txt
index b6757ba..40f9f97 100644
--- a/compose/ui/ui/api/1.0.0-beta06.txt
+++ b/compose/ui/ui/api/1.0.0-beta06.txt
@@ -2058,6 +2058,13 @@
 
 }
 
+package androidx.compose.ui.platform.accessibility {
+
+  public final class CollectionInfoKt {
+  }
+
+}
+
 package androidx.compose.ui.platform.actionmodecallback {
 
   public final class TextActionModeCallback_androidKt {
@@ -2120,6 +2127,26 @@
     property public final String? label;
   }
 
+  public final class CollectionInfo {
+    ctor public CollectionInfo(int rowCount, int columnCount);
+    method public int getColumnCount();
+    method public int getRowCount();
+    property public final int columnCount;
+    property public final int rowCount;
+  }
+
+  public final class CollectionItemInfo {
+    ctor public CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan);
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    property public final int columnIndex;
+    property public final int columnSpan;
+    property public final int rowIndex;
+    property public final int rowSpan;
+  }
+
   public final class CustomAccessibilityAction {
     ctor public CustomAccessibilityAction(String label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public kotlin.jvm.functions.Function0<java.lang.Boolean> getAction();
@@ -2280,6 +2307,8 @@
   }
 
   public final class SemanticsProperties {
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> getCollectionInfo();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> getCollectionItemInfo();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getContentDescription();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getDisabled();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> getEditableText();
@@ -2305,6 +2334,8 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> getTextSelectionRange();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.state.ToggleableState> getToggleableState();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ScrollAxisRange> getVerticalScrollAxisRange();
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> CollectionInfo;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> CollectionItemInfo;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> ContentDescription;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Disabled;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> EditableText;
@@ -2342,6 +2373,8 @@
     method public static void dismiss(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
     method public static void error(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String description);
     method public static void expand(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+    method public static androidx.compose.ui.semantics.CollectionInfo getCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static androidx.compose.ui.semantics.CollectionItemInfo getCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static String getContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> getCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.text.AnnotatedString getEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
@@ -2370,6 +2403,8 @@
     method public static void scrollBy(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Boolean>? action);
     method public static void scrollToIndex(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Boolean> action);
     method public static void selectableGroup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static void setCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionInfo p);
+    method public static void setCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionItemInfo p);
     method public static void setContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
     method public static void setCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver, java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> p);
     method public static void setEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.text.AnnotatedString p);
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index b6757ba..40f9f97 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -2058,6 +2058,13 @@
 
 }
 
+package androidx.compose.ui.platform.accessibility {
+
+  public final class CollectionInfoKt {
+  }
+
+}
+
 package androidx.compose.ui.platform.actionmodecallback {
 
   public final class TextActionModeCallback_androidKt {
@@ -2120,6 +2127,26 @@
     property public final String? label;
   }
 
+  public final class CollectionInfo {
+    ctor public CollectionInfo(int rowCount, int columnCount);
+    method public int getColumnCount();
+    method public int getRowCount();
+    property public final int columnCount;
+    property public final int rowCount;
+  }
+
+  public final class CollectionItemInfo {
+    ctor public CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan);
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    property public final int columnIndex;
+    property public final int columnSpan;
+    property public final int rowIndex;
+    property public final int rowSpan;
+  }
+
   public final class CustomAccessibilityAction {
     ctor public CustomAccessibilityAction(String label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public kotlin.jvm.functions.Function0<java.lang.Boolean> getAction();
@@ -2280,6 +2307,8 @@
   }
 
   public final class SemanticsProperties {
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> getCollectionInfo();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> getCollectionItemInfo();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getContentDescription();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getDisabled();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> getEditableText();
@@ -2305,6 +2334,8 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> getTextSelectionRange();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.state.ToggleableState> getToggleableState();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ScrollAxisRange> getVerticalScrollAxisRange();
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> CollectionInfo;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> CollectionItemInfo;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> ContentDescription;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Disabled;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> EditableText;
@@ -2342,6 +2373,8 @@
     method public static void dismiss(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
     method public static void error(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String description);
     method public static void expand(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+    method public static androidx.compose.ui.semantics.CollectionInfo getCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static androidx.compose.ui.semantics.CollectionItemInfo getCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static String getContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> getCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.text.AnnotatedString getEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
@@ -2370,6 +2403,8 @@
     method public static void scrollBy(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Boolean>? action);
     method public static void scrollToIndex(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Boolean> action);
     method public static void selectableGroup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static void setCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionInfo p);
+    method public static void setCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionItemInfo p);
     method public static void setContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
     method public static void setCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver, java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> p);
     method public static void setEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.text.AnnotatedString p);
diff --git a/compose/ui/ui/api/public_plus_experimental_1.0.0-beta06.txt b/compose/ui/ui/api/public_plus_experimental_1.0.0-beta06.txt
index 81cc288..535178e 100644
--- a/compose/ui/ui/api/public_plus_experimental_1.0.0-beta06.txt
+++ b/compose/ui/ui/api/public_plus_experimental_1.0.0-beta06.txt
@@ -2197,6 +2197,13 @@
 
 }
 
+package androidx.compose.ui.platform.accessibility {
+
+  public final class CollectionInfoKt {
+  }
+
+}
+
 package androidx.compose.ui.platform.actionmodecallback {
 
   public final class TextActionModeCallback_androidKt {
@@ -2260,6 +2267,26 @@
     property public final String? label;
   }
 
+  public final class CollectionInfo {
+    ctor public CollectionInfo(int rowCount, int columnCount);
+    method public int getColumnCount();
+    method public int getRowCount();
+    property public final int columnCount;
+    property public final int rowCount;
+  }
+
+  public final class CollectionItemInfo {
+    ctor public CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan);
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    property public final int columnIndex;
+    property public final int columnSpan;
+    property public final int rowIndex;
+    property public final int rowSpan;
+  }
+
   public final class CustomAccessibilityAction {
     ctor public CustomAccessibilityAction(String label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public kotlin.jvm.functions.Function0<java.lang.Boolean> getAction();
@@ -2420,6 +2447,8 @@
   }
 
   public final class SemanticsProperties {
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> getCollectionInfo();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> getCollectionItemInfo();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getContentDescription();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getDisabled();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> getEditableText();
@@ -2445,6 +2474,8 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> getTextSelectionRange();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.state.ToggleableState> getToggleableState();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ScrollAxisRange> getVerticalScrollAxisRange();
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> CollectionInfo;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> CollectionItemInfo;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> ContentDescription;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Disabled;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> EditableText;
@@ -2482,6 +2513,8 @@
     method public static void dismiss(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
     method public static void error(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String description);
     method public static void expand(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+    method public static androidx.compose.ui.semantics.CollectionInfo getCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static androidx.compose.ui.semantics.CollectionItemInfo getCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static String getContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> getCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.text.AnnotatedString getEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
@@ -2511,6 +2544,8 @@
     method public static void scrollBy(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Boolean>? action);
     method public static void scrollToIndex(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Boolean> action);
     method public static void selectableGroup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static void setCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionInfo p);
+    method public static void setCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionItemInfo p);
     method public static void setContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
     method public static void setCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver, java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> p);
     method public static void setEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.text.AnnotatedString p);
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 81cc288..535178e 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -2197,6 +2197,13 @@
 
 }
 
+package androidx.compose.ui.platform.accessibility {
+
+  public final class CollectionInfoKt {
+  }
+
+}
+
 package androidx.compose.ui.platform.actionmodecallback {
 
   public final class TextActionModeCallback_androidKt {
@@ -2260,6 +2267,26 @@
     property public final String? label;
   }
 
+  public final class CollectionInfo {
+    ctor public CollectionInfo(int rowCount, int columnCount);
+    method public int getColumnCount();
+    method public int getRowCount();
+    property public final int columnCount;
+    property public final int rowCount;
+  }
+
+  public final class CollectionItemInfo {
+    ctor public CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan);
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    property public final int columnIndex;
+    property public final int columnSpan;
+    property public final int rowIndex;
+    property public final int rowSpan;
+  }
+
   public final class CustomAccessibilityAction {
     ctor public CustomAccessibilityAction(String label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public kotlin.jvm.functions.Function0<java.lang.Boolean> getAction();
@@ -2420,6 +2447,8 @@
   }
 
   public final class SemanticsProperties {
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> getCollectionInfo();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> getCollectionItemInfo();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getContentDescription();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getDisabled();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> getEditableText();
@@ -2445,6 +2474,8 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> getTextSelectionRange();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.state.ToggleableState> getToggleableState();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ScrollAxisRange> getVerticalScrollAxisRange();
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> CollectionInfo;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> CollectionItemInfo;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> ContentDescription;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Disabled;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> EditableText;
@@ -2482,6 +2513,8 @@
     method public static void dismiss(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
     method public static void error(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String description);
     method public static void expand(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+    method public static androidx.compose.ui.semantics.CollectionInfo getCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static androidx.compose.ui.semantics.CollectionItemInfo getCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static String getContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> getCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.text.AnnotatedString getEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
@@ -2511,6 +2544,8 @@
     method public static void scrollBy(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Boolean>? action);
     method public static void scrollToIndex(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Boolean> action);
     method public static void selectableGroup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static void setCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionInfo p);
+    method public static void setCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionItemInfo p);
     method public static void setContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
     method public static void setCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver, java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> p);
     method public static void setEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.text.AnnotatedString p);
diff --git a/compose/ui/ui/api/restricted_1.0.0-beta06.txt b/compose/ui/ui/api/restricted_1.0.0-beta06.txt
index 4760263..84c7fa3 100644
--- a/compose/ui/ui/api/restricted_1.0.0-beta06.txt
+++ b/compose/ui/ui/api/restricted_1.0.0-beta06.txt
@@ -2088,6 +2088,13 @@
 
 }
 
+package androidx.compose.ui.platform.accessibility {
+
+  public final class CollectionInfoKt {
+  }
+
+}
+
 package androidx.compose.ui.platform.actionmodecallback {
 
   public final class TextActionModeCallback_androidKt {
@@ -2150,6 +2157,26 @@
     property public final String? label;
   }
 
+  public final class CollectionInfo {
+    ctor public CollectionInfo(int rowCount, int columnCount);
+    method public int getColumnCount();
+    method public int getRowCount();
+    property public final int columnCount;
+    property public final int rowCount;
+  }
+
+  public final class CollectionItemInfo {
+    ctor public CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan);
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    property public final int columnIndex;
+    property public final int columnSpan;
+    property public final int rowIndex;
+    property public final int rowSpan;
+  }
+
   public final class CustomAccessibilityAction {
     ctor public CustomAccessibilityAction(String label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public kotlin.jvm.functions.Function0<java.lang.Boolean> getAction();
@@ -2310,6 +2337,8 @@
   }
 
   public final class SemanticsProperties {
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> getCollectionInfo();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> getCollectionItemInfo();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getContentDescription();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getDisabled();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> getEditableText();
@@ -2335,6 +2364,8 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> getTextSelectionRange();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.state.ToggleableState> getToggleableState();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ScrollAxisRange> getVerticalScrollAxisRange();
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> CollectionInfo;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> CollectionItemInfo;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> ContentDescription;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Disabled;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> EditableText;
@@ -2372,6 +2403,8 @@
     method public static void dismiss(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
     method public static void error(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String description);
     method public static void expand(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+    method public static androidx.compose.ui.semantics.CollectionInfo getCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static androidx.compose.ui.semantics.CollectionItemInfo getCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static String getContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> getCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.text.AnnotatedString getEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
@@ -2400,6 +2433,8 @@
     method public static void scrollBy(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Boolean>? action);
     method public static void scrollToIndex(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Boolean> action);
     method public static void selectableGroup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static void setCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionInfo p);
+    method public static void setCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionItemInfo p);
     method public static void setContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
     method public static void setCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver, java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> p);
     method public static void setEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.text.AnnotatedString p);
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 4760263..84c7fa3 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -2088,6 +2088,13 @@
 
 }
 
+package androidx.compose.ui.platform.accessibility {
+
+  public final class CollectionInfoKt {
+  }
+
+}
+
 package androidx.compose.ui.platform.actionmodecallback {
 
   public final class TextActionModeCallback_androidKt {
@@ -2150,6 +2157,26 @@
     property public final String? label;
   }
 
+  public final class CollectionInfo {
+    ctor public CollectionInfo(int rowCount, int columnCount);
+    method public int getColumnCount();
+    method public int getRowCount();
+    property public final int columnCount;
+    property public final int rowCount;
+  }
+
+  public final class CollectionItemInfo {
+    ctor public CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan);
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    property public final int columnIndex;
+    property public final int columnSpan;
+    property public final int rowIndex;
+    property public final int rowSpan;
+  }
+
   public final class CustomAccessibilityAction {
     ctor public CustomAccessibilityAction(String label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public kotlin.jvm.functions.Function0<java.lang.Boolean> getAction();
@@ -2310,6 +2337,8 @@
   }
 
   public final class SemanticsProperties {
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> getCollectionInfo();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> getCollectionItemInfo();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getContentDescription();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getDisabled();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> getEditableText();
@@ -2335,6 +2364,8 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> getTextSelectionRange();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.state.ToggleableState> getToggleableState();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ScrollAxisRange> getVerticalScrollAxisRange();
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> CollectionInfo;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> CollectionItemInfo;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> ContentDescription;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Disabled;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> EditableText;
@@ -2372,6 +2403,8 @@
     method public static void dismiss(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
     method public static void error(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String description);
     method public static void expand(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+    method public static androidx.compose.ui.semantics.CollectionInfo getCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static androidx.compose.ui.semantics.CollectionItemInfo getCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static String getContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> getCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.text.AnnotatedString getEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
@@ -2400,6 +2433,8 @@
     method public static void scrollBy(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Boolean>? action);
     method public static void scrollToIndex(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Boolean> action);
     method public static void selectableGroup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static void setCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionInfo p);
+    method public static void setCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionItemInfo p);
     method public static void setContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
     method public static void setCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver, java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> p);
     method public static void setEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.text.AnnotatedString p);
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
index 6a41110..653c6ca 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
@@ -24,10 +24,7 @@
 import android.view.accessibility.AccessibilityNodeInfo
 import android.widget.FrameLayout
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.selection.selectable
-import androidx.compose.foundation.selection.selectableGroup
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -848,46 +845,6 @@
     }
 
     @Test
-    fun testCollectionItemInfo() {
-        rule.setContent {
-            Column(Modifier.selectableGroup()) {
-                Box(Modifier.selectable(selected = true, onClick = {}).testTag("item"))
-                Box(Modifier.selectable(selected = false, onClick = {}))
-            }
-        }
-        val itemNode = rule.onNodeWithTag("item").fetchSemanticsNode()
-        accessibilityDelegate.populateAccessibilityNodeInfoProperties(1, info, itemNode)
-
-        val resultCollectionItemInfo = info.collectionItemInfo
-        assertEquals(0, resultCollectionItemInfo.rowIndex)
-        assertEquals(1, resultCollectionItemInfo.rowSpan)
-        assertEquals(0, resultCollectionItemInfo.columnIndex)
-        assertEquals(1, resultCollectionItemInfo.columnSpan)
-        assertEquals(true, resultCollectionItemInfo.isSelected)
-    }
-
-    @Test
-    fun testCollectionInfo() {
-        rule.setContent {
-            Column(Modifier.selectableGroup().testTag("collection")) {
-                Box(Modifier.size(50.dp).selectable(selected = true, onClick = {}))
-                Box(Modifier.size(50.dp).selectable(selected = false, onClick = {}))
-            }
-        }
-        val collectionNode = rule.onNodeWithTag("collection").fetchSemanticsNode()
-        accessibilityDelegate.populateAccessibilityNodeInfoProperties(1, info, collectionNode)
-
-        val resultCollectionInfo = info.collectionInfo
-        assertEquals(2, resultCollectionInfo.rowCount)
-        assertEquals(1, resultCollectionInfo.columnCount)
-        assertEquals(false, resultCollectionInfo.isHierarchical)
-        assertEquals(
-            AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_SINGLE,
-            resultCollectionInfo.selectionMode
-        )
-    }
-
-    @Test
     fun testSemanticsNodePositionAndBounds_doesNotThrow_whenLayoutNodeNotAttached() {
         var emitNode by mutableStateOf(true)
         rule.setContent {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/accessibility/CollectionInfoTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/accessibility/CollectionInfoTest.kt
new file mode 100644
index 0000000..d14771c
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/accessibility/CollectionInfoTest.kt
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.accessibility
+
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.OpenComposeView
+import androidx.compose.ui.platform.AndroidComposeView
+import androidx.compose.ui.platform.AndroidComposeViewAccessibilityDelegateCompat
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.CollectionInfo
+import androidx.compose.ui.semantics.CollectionItemInfo
+import androidx.compose.ui.semantics.collectionInfo
+import androidx.compose.ui.semantics.collectionItemInfo
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.TestActivity
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.unit.dp
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.After
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class CollectionInfoTest {
+    @get:Rule
+    val rule = createAndroidComposeRule<TestActivity>()
+
+    private lateinit var accessibilityDelegate: AndroidComposeViewAccessibilityDelegateCompat
+    private lateinit var info: AccessibilityNodeInfoCompat
+
+    @Before
+    fun setup() {
+        val container = OpenComposeView(rule.activity)
+
+        rule.runOnUiThread {
+            rule.activity.setContentView(
+                container,
+                ViewGroup.LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT
+                )
+            )
+        }
+
+        val composeView = container.getChildAt(0) as AndroidComposeView
+        accessibilityDelegate = AndroidComposeViewAccessibilityDelegateCompat(composeView).apply {
+            accessibilityForceEnabledForTesting = true
+        }
+        info = AccessibilityNodeInfoCompat.obtain()
+    }
+
+    @After
+    fun cleanup() {
+        info.recycle()
+    }
+
+    // Collection Info tests
+    @Test
+    fun testCollectionInfo_withSelectableGroup() {
+        rule.setContent {
+            Column(Modifier.selectableGroup().testTag("collection")) {
+                Box(Modifier.size(50.dp).selectable(selected = true, onClick = {}))
+                Box(Modifier.size(50.dp).selectable(selected = false, onClick = {}))
+            }
+        }
+        val collectionNode = rule.onNodeWithTag("collection").fetchSemanticsNode()
+        accessibilityDelegate
+            .populateAccessibilityNodeInfoProperties(collectionNode.id, info, collectionNode)
+
+        val resultCollectionInfo = info.collectionInfo
+        Assert.assertEquals(2, resultCollectionInfo.rowCount)
+        Assert.assertEquals(1, resultCollectionInfo.columnCount)
+        Assert.assertEquals(false, resultCollectionInfo.isHierarchical)
+    }
+
+    @Test
+    fun testDefaultCollectionInfo_lazyList() {
+        val tag = "LazyColumn"
+        rule.setContent {
+            LazyColumn(Modifier.testTag(tag)) {
+                items(2) { BasicText("Text") }
+            }
+        }
+
+        val itemNode = rule.onNodeWithTag(tag).fetchSemanticsNode()
+        accessibilityDelegate.populateAccessibilityNodeInfoProperties(itemNode.id, info, itemNode)
+
+        val resultCollectionInfo = info.collectionInfo
+        Assert.assertEquals(-1, resultCollectionInfo.rowCount)
+        Assert.assertEquals(1, resultCollectionInfo.columnCount)
+        Assert.assertEquals(false, resultCollectionInfo.isHierarchical)
+    }
+
+    @Test
+    fun testCollectionInfo_lazyList() {
+        val tag = "LazyColumn"
+        rule.setContent {
+            LazyColumn(
+                Modifier
+                    .testTag(tag)
+                    .semantics { collectionInfo = CollectionInfo(2, 1) }
+            ) {
+                items(2) { BasicText("Text") }
+            }
+        }
+
+        val itemNode = rule.onNodeWithTag(tag).fetchSemanticsNode()
+        accessibilityDelegate.populateAccessibilityNodeInfoProperties(itemNode.id, info, itemNode)
+
+        val resultCollectionInfo = info.collectionInfo
+        Assert.assertEquals(2, resultCollectionInfo.rowCount)
+        Assert.assertEquals(1, resultCollectionInfo.columnCount)
+        Assert.assertEquals(false, resultCollectionInfo.isHierarchical)
+    }
+
+    @Test
+    fun testCollectionInfo_withSelectableGroup_andDefaultLazyListSemantics() {
+        val tag = "LazyColumn"
+        rule.setContent {
+            LazyColumn(Modifier.testTag(tag).selectableGroup()) {
+                items(2) { BasicText("Text") }
+            }
+        }
+
+        val itemNode = rule.onNodeWithTag(tag).fetchSemanticsNode()
+        accessibilityDelegate.populateAccessibilityNodeInfoProperties(itemNode.id, info, itemNode)
+
+        val resultCollectionInfo = info.collectionInfo
+        Assert.assertEquals(-1, resultCollectionInfo.rowCount)
+        Assert.assertEquals(1, resultCollectionInfo.columnCount)
+        Assert.assertEquals(false, resultCollectionInfo.isHierarchical)
+    }
+
+    @Test
+    fun testCollectionInfo_withSelectableGroup_andLazyListSemantics() {
+        val tag = "LazyColumn"
+        rule.setContent {
+            LazyColumn(
+                Modifier
+                    .testTag(tag)
+                    .selectableGroup()
+                    .semantics { collectionInfo = CollectionInfo(2, 1) }
+            ) {
+                items(2) { BasicText("Text") }
+            }
+        }
+
+        val itemNode = rule.onNodeWithTag(tag).fetchSemanticsNode()
+        accessibilityDelegate.populateAccessibilityNodeInfoProperties(itemNode.id, info, itemNode)
+
+        val resultCollectionInfo = info.collectionInfo
+        Assert.assertEquals(2, resultCollectionInfo.rowCount)
+        Assert.assertEquals(1, resultCollectionInfo.columnCount)
+        Assert.assertEquals(false, resultCollectionInfo.isHierarchical)
+    }
+
+    // Collection Item Info tests
+    @Test
+    fun testCollectionItemInfo_withSelectableGroup() {
+        rule.setContent {
+            Column(Modifier.selectableGroup()) {
+                Box(Modifier.selectable(selected = true, onClick = {}).testTag("item"))
+                Box(Modifier.selectable(selected = false, onClick = {}))
+            }
+        }
+
+        val itemNode = rule.onNodeWithTag("item").fetchSemanticsNode()
+        accessibilityDelegate.populateAccessibilityNodeInfoProperties(itemNode.id, info, itemNode)
+
+        val resultCollectionItemInfo = info.collectionItemInfo
+        Assert.assertEquals(0, resultCollectionItemInfo.rowIndex)
+        Assert.assertEquals(1, resultCollectionItemInfo.rowSpan)
+        Assert.assertEquals(0, resultCollectionItemInfo.columnIndex)
+        Assert.assertEquals(1, resultCollectionItemInfo.columnSpan)
+        Assert.assertEquals(true, resultCollectionItemInfo.isSelected)
+    }
+
+    @Test
+    fun testNoCollectionItemInfo_lazyList() {
+        rule.setContent {
+            LazyColumn {
+                itemsIndexed(listOf("Text", "Text")) { index, item -> BasicText(item + index) }
+            }
+        }
+
+        val itemNode = rule.onNodeWithText("Text0").fetchSemanticsNode()
+        accessibilityDelegate.populateAccessibilityNodeInfoProperties(itemNode.id, info, itemNode)
+
+        Assert.assertNull(info.collectionItemInfo)
+    }
+
+    @Test
+    fun testCollectionItemInfo_defaultLazyListSemantics() {
+        rule.setContent {
+            LazyColumn {
+                itemsIndexed(listOf("Text", "Text")) { index, item ->
+                    BasicText(
+                        item + index,
+                        Modifier.semantics {
+                            collectionItemInfo = CollectionItemInfo(index, 1, 0, 1)
+                        }
+                    )
+                }
+            }
+        }
+
+        val itemNode = rule.onNodeWithText("Text0").fetchSemanticsNode()
+        accessibilityDelegate.populateAccessibilityNodeInfoProperties(itemNode.id, info, itemNode)
+
+        val resultCollectionItemInfo = info.collectionItemInfo
+        Assert.assertEquals(0, resultCollectionItemInfo.rowIndex)
+        Assert.assertEquals(1, resultCollectionItemInfo.rowSpan)
+        Assert.assertEquals(0, resultCollectionItemInfo.columnIndex)
+        Assert.assertEquals(1, resultCollectionItemInfo.columnSpan)
+    }
+
+    @Test
+    fun testCollectionItemInfo_lazyList() {
+        rule.setContent {
+            LazyColumn(Modifier.semantics { collectionInfo = CollectionInfo(2, 1) }) {
+                itemsIndexed(listOf("Text", "Text")) { index, item ->
+                    BasicText(
+                        item + index,
+                        Modifier.semantics {
+                            collectionItemInfo = CollectionItemInfo(index, 1, 0, 1)
+                        }
+                    )
+                }
+            }
+        }
+
+        val itemNode = rule.onNodeWithText("Text0").fetchSemanticsNode()
+        accessibilityDelegate.populateAccessibilityNodeInfoProperties(itemNode.id, info, itemNode)
+
+        val resultCollectionItemInfo = info.collectionItemInfo
+        Assert.assertEquals(0, resultCollectionItemInfo.rowIndex)
+        Assert.assertEquals(1, resultCollectionItemInfo.rowSpan)
+        Assert.assertEquals(0, resultCollectionItemInfo.columnIndex)
+        Assert.assertEquals(1, resultCollectionItemInfo.columnSpan)
+    }
+
+    @Test
+    fun testCollectionItemInfo_withSelectableGroup_andDefaultLazyListSemantics() {
+        rule.setContent {
+            LazyColumn(Modifier.selectableGroup()) {
+                itemsIndexed(listOf("Text", "Text")) { index, item ->
+                    BasicText(
+                        item + index,
+                        Modifier.semantics {
+                            collectionItemInfo = CollectionItemInfo(index, 1, 0, 1)
+                        }
+                    )
+                }
+            }
+        }
+
+        val itemNode = rule.onNodeWithText("Text0").fetchSemanticsNode()
+        accessibilityDelegate.populateAccessibilityNodeInfoProperties(itemNode.id, info, itemNode)
+
+        val resultCollectionItemInfo = info.collectionItemInfo
+        Assert.assertEquals(0, resultCollectionItemInfo.rowIndex)
+        Assert.assertEquals(1, resultCollectionItemInfo.rowSpan)
+        Assert.assertEquals(0, resultCollectionItemInfo.columnIndex)
+        Assert.assertEquals(1, resultCollectionItemInfo.columnSpan)
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/KeyEventToFocusDirectionTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/KeyEventToFocusDirectionTest.kt
index 0e10a62..d389b61 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/KeyEventToFocusDirectionTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/KeyEventToFocusDirectionTest.kt
@@ -34,7 +34,6 @@
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -141,7 +140,6 @@
         assertThat(focusDirection).isEqualTo(In)
     }
 
-    @Ignore("Disabled due to b/185211677")
     @Test
     fun back_out() {
         // Arrange.
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
index a75c428..ab7fbac 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
@@ -68,6 +68,7 @@
 import androidx.compose.ui.graphics.CanvasHolder
 import androidx.compose.ui.hapticfeedback.AndroidHapticFeedback
 import androidx.compose.ui.hapticfeedback.HapticFeedback
+import androidx.compose.ui.input.key.Key.Companion.Back
 import androidx.compose.ui.input.key.Key.Companion.DirectionCenter
 import androidx.compose.ui.input.key.Key.Companion.DirectionDown
 import androidx.compose.ui.input.key.Key.Companion.DirectionLeft
@@ -640,11 +641,7 @@
             DirectionUp -> Up
             DirectionDown -> Down
             DirectionCenter -> In
-            // TODO(b/183746743): Enable Back after fixing issue with DemoTests (b/185211677).
-            // If we use the back button to clear focus, then the demo tests need two Back
-            // events when an item is focused. Either remove initial focus from the affected
-            // Demos or call clearFocus() before sending the Back key event.
-            // Back -> Out
+            Back -> Out
             else -> null
         }
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
index 984ea09..5f2d9a5 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
@@ -59,9 +59,9 @@
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.fastJoinToString
-import androidx.compose.ui.fastReduce
-import androidx.compose.ui.fastZipWithNext
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.accessibility.setCollectionInfo
+import androidx.compose.ui.platform.accessibility.setCollectionItemInfo
 import androidx.compose.ui.semantics.AccessibilityAction
 import androidx.compose.ui.semantics.LiveRegionMode
 import androidx.compose.ui.semantics.ProgressBarRangeInfo
@@ -76,7 +76,6 @@
 import androidx.core.view.accessibility.AccessibilityNodeProviderCompat
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.delay
-import kotlin.math.abs
 import kotlin.math.ceil
 import kotlin.math.floor
 
@@ -2045,104 +2044,6 @@
         return null
     }
 
-    private fun setCollectionInfo(node: SemanticsNode, info: AccessibilityNodeInfoCompat) {
-        val groupedChildren = mutableListOf<SemanticsNode>()
-
-        if (node.config.getOrNull(SemanticsProperties.SelectableGroup) != null) {
-            node.children.fastForEach { childNode ->
-                // we assume that Tabs and RadioButtons are not mixed under a single group
-                if (childNode.config.contains(SemanticsProperties.Selected)) {
-                    groupedChildren.add(childNode)
-                }
-            }
-        }
-
-        if (groupedChildren.isNotEmpty()) {
-            /* When we provide a more complex CollectionInfo object, we will use it to determine
-            the number of rows, columns, and selection mode. Currently we assume mutual
-            exclusivity and liner layout (aka Column or Row). We determine if the layout is
-            horizontal or vertical by checking the bounds of the children
-            */
-            val isHorizontal = calculateIfHorizontallyStacked(groupedChildren)
-            info.setCollectionInfo(
-                AccessibilityNodeInfoCompat.CollectionInfoCompat.obtain(
-                    if (isHorizontal) 1 else groupedChildren.count(),
-                    if (isHorizontal) groupedChildren.count() else 1,
-                    false,
-                    getSelectionMode(groupedChildren)
-                )
-            )
-        }
-    }
-
-    private fun setCollectionItemInfo(node: SemanticsNode, info: AccessibilityNodeInfoCompat) {
-        if (!node.config.contains(SemanticsProperties.Selected)) return
-
-        val groupedChildren = mutableListOf<SemanticsNode>()
-
-        // for "tab" item find all siblings to calculate the index
-        val parentNode = node.parent ?: return
-        if (parentNode.config.getOrNull(SemanticsProperties.SelectableGroup) != null) {
-            // find all siblings to calculate the index
-            parentNode.children.fastForEach { childNode ->
-                if (childNode.config.contains(SemanticsProperties.Selected)) {
-                    groupedChildren.add(childNode)
-                }
-            }
-        }
-
-        if (groupedChildren.isNotEmpty()) {
-            val isHorizontal = calculateIfHorizontallyStacked(groupedChildren)
-
-            groupedChildren.fastForEachIndexed { index, tabNode ->
-                if (tabNode.id == node.id) {
-                    val itemInfo = AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
-                        if (isHorizontal) 0 else index,
-                        1,
-                        if (isHorizontal) index else 0,
-                        1,
-                        false,
-                        tabNode.config.getOrElse(SemanticsProperties.Selected) { false }
-                    )
-                    if (itemInfo != null) {
-                        info.setCollectionItemInfo(itemInfo)
-                    }
-                }
-            }
-        }
-    }
-
-    /** A naïve algorithm to determine if elements are stacked vertically or horizontally */
-    private fun calculateIfHorizontallyStacked(items: List<SemanticsNode>): Boolean {
-        if (items.count() < 2) return true
-
-        val deltas = items.fastZipWithNext { el1, el2 ->
-            Offset(
-                abs(el1.boundsInRoot.center.x - el2.boundsInRoot.center.x),
-                abs(el1.boundsInRoot.center.y - el2.boundsInRoot.center.y)
-            )
-        }
-        val (deltaX, deltaY) = when (deltas.count()) {
-            1 -> deltas.first()
-            else -> deltas.fastReduce { result, element -> result + element }
-        }
-        return deltaY < deltaX
-    }
-
-    private fun getSelectionMode(items: List<SemanticsNode>): Int {
-        var numberOfSelectedItems = 0
-        items.fastForEach {
-            if (it.config.getOrElse(SemanticsProperties.Selected) { false }) {
-                numberOfSelectedItems += 1
-            }
-        }
-        return when (numberOfSelectedItems) {
-            0 -> AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE
-            1 -> AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_SINGLE
-            else -> AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_MULTIPLE
-        }
-    }
-
     // TODO(b/160820721): use AccessibilityNodeProviderCompat instead of AccessibilityNodeProvider
     inner class MyNodeProvider : AccessibilityNodeProvider() {
         override fun createAccessibilityNodeInfo(virtualViewId: Int):
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/accessibility/CollectionInfo.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/accessibility/CollectionInfo.kt
new file mode 100644
index 0000000..7bfbdb9
--- /dev/null
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/accessibility/CollectionInfo.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.platform.accessibility
+
+import androidx.compose.ui.fastReduce
+import androidx.compose.ui.fastZipWithNext
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.semantics.CollectionInfo
+import androidx.compose.ui.semantics.CollectionItemInfo
+import androidx.compose.ui.semantics.SemanticsNode
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.getOrNull
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastForEachIndexed
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import kotlin.math.abs
+
+internal fun setCollectionInfo(node: SemanticsNode, info: AccessibilityNodeInfoCompat) {
+    // prioritise collection info provided by developer
+    val collectionInfo = node.config.getOrNull(SemanticsProperties.CollectionInfo)
+    if (collectionInfo != null) {
+        info.setCollectionInfo(collectionInfo.toAccessibilityCollectionInfo())
+        return
+    }
+
+    // if no collection info is provided, we'll check the 'SelectableGroup'
+    val groupedChildren = mutableListOf<SemanticsNode>()
+
+    if (node.config.getOrNull(SemanticsProperties.SelectableGroup) != null) {
+        node.children.fastForEach { childNode ->
+            // we assume that Tabs and RadioButtons are not mixed under a single group
+            if (childNode.config.contains(SemanticsProperties.Selected)) {
+                groupedChildren.add(childNode)
+            }
+        }
+    }
+
+    if (groupedChildren.isNotEmpty()) {
+        val isHorizontal = calculateIfHorizontallyStacked(groupedChildren)
+        info.setCollectionInfo(
+            AccessibilityNodeInfoCompat.CollectionInfoCompat.obtain(
+                if (isHorizontal) 1 else groupedChildren.count(),
+                if (isHorizontal) groupedChildren.count() else 1,
+                false,
+                AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE
+            )
+        )
+    }
+}
+
+internal fun setCollectionItemInfo(node: SemanticsNode, info: AccessibilityNodeInfoCompat) {
+    // prioritise collection item info provided by developer
+    val collectionItemInfo = node.config.getOrNull(SemanticsProperties.CollectionItemInfo)
+    if (collectionItemInfo != null) {
+        info.setCollectionItemInfo(collectionItemInfo.toAccessibilityCollectionItemInfo(node))
+    }
+
+    // if no collection item info is provided, we'll check the 'SelectableGroup'
+    val parentNode = node.parent ?: return
+    if (parentNode.config.getOrNull(SemanticsProperties.SelectableGroup) != null) {
+        // first check if parent has a CollectionInfo. If it does and any of the counters is
+        // unknown, then we assume that it is a lazy collection so we won't provide
+        // collectionItemInfo using `SelectableGroup`
+        val collectionInfo = parentNode.config.getOrNull(SemanticsProperties.CollectionInfo)
+        if (collectionInfo != null && collectionInfo.isLazyCollection) return
+
+        // `SelectableGroup` designed for selectable elements
+        if (!node.config.contains(SemanticsProperties.Selected)) return
+
+        val groupedChildren = mutableListOf<SemanticsNode>()
+
+        // find all siblings to calculate the index
+        parentNode.children.fastForEach { childNode ->
+            if (childNode.config.contains(SemanticsProperties.Selected)) {
+                groupedChildren.add(childNode)
+            }
+        }
+
+        if (groupedChildren.isNotEmpty()) {
+            val isHorizontal = calculateIfHorizontallyStacked(groupedChildren)
+
+            groupedChildren.fastForEachIndexed { index, tabNode ->
+                if (tabNode.id == node.id) {
+                    val itemInfo = AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
+                        if (isHorizontal) 0 else index,
+                        1,
+                        if (isHorizontal) index else 0,
+                        1,
+                        false,
+                        tabNode.config.getOrElse(SemanticsProperties.Selected) { false }
+                    )
+                    if (itemInfo != null) {
+                        info.setCollectionItemInfo(itemInfo)
+                    }
+                }
+            }
+        }
+    }
+}
+
+/** A naïve algorithm to determine if elements are stacked vertically or horizontally */
+private fun calculateIfHorizontallyStacked(items: List<SemanticsNode>): Boolean {
+    if (items.count() < 2) return true
+
+    val deltas = items.fastZipWithNext { el1, el2 ->
+        Offset(
+            abs(el1.boundsInRoot.center.x - el2.boundsInRoot.center.x),
+            abs(el1.boundsInRoot.center.y - el2.boundsInRoot.center.y)
+        )
+    }
+    val (deltaX, deltaY) = when (deltas.count()) {
+        1 -> deltas.first()
+        else -> deltas.fastReduce { result, element -> result + element }
+    }
+    return deltaY < deltaX
+}
+
+private val CollectionInfo.isLazyCollection get() = rowCount < 0 || columnCount < 0
+
+private fun CollectionInfo.toAccessibilityCollectionInfo() =
+    AccessibilityNodeInfoCompat.CollectionInfoCompat.obtain(
+        rowCount,
+        columnCount,
+        false,
+        AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE
+    )
+
+private fun CollectionItemInfo.toAccessibilityCollectionItemInfo(itemNode: SemanticsNode) =
+    AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
+        rowIndex,
+        rowSpan,
+        columnIndex,
+        columnSpan,
+        false,
+        itemNode.config.getOrElse(SemanticsProperties.Selected) { false }
+    )
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
index 81ae47e..d99109b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
@@ -67,6 +67,12 @@
     /** @see SemanticsPropertyReceiver.selectableGroup */
     val SelectableGroup = SemanticsPropertyKey<Unit>("SelectableGroup")
 
+    /** @see SemanticsPropertyReceiver.collectionInfo */
+    val CollectionInfo = SemanticsPropertyKey<CollectionInfo>("CollectionInfo")
+
+    /** @see SemanticsPropertyReceiver.collectionItemInfo */
+    val CollectionItemInfo = SemanticsPropertyKey<CollectionItemInfo>("CollectionItemInfo")
+
     /**
      * @see SemanticsPropertyReceiver.heading
      */
@@ -481,6 +487,39 @@
 }
 
 /**
+ * Information about the collection.
+ *
+ * A collection of items has [rowCount] rows and [columnCount] columns.
+ * For example, a vertical list is a collection with one column, as many rows as the list items
+ * that are important for accessibility; A table is a collection with several rows and several
+ * columns.
+ *
+ * @param rowCount the number of rows in the collection, or -1 if unknown
+ * @param columnCount the number of columns in the collection, or -1 if unknown
+ */
+class CollectionInfo(val rowCount: Int, val columnCount: Int)
+
+/**
+ * Information about the item of a collection.
+ *
+ * A collection item is contained in a collection, it starts at a given [rowIndex] and
+ * [columnIndex] in the collection, and spans one or more rows and columns. For example, a header
+ * of two related table columns starts at the first row and the first column, spans one row and
+ * two columns.
+ *
+ * @param rowIndex the index of the row at which item is located
+ * @param rowSpan the number of rows the item spans
+ * @param columnIndex the index of the column at which item is located
+ * @param columnSpan the number of columns the item spans
+ */
+class CollectionItemInfo(
+    val rowIndex: Int,
+    val rowSpan: Int,
+    val columnIndex: Int,
+    val columnSpan: Int
+)
+
+/**
  * The scroll state of one axis if this node is scrollable.
  *
  * @param value current 0-based scroll position value (either in pixels, or lazy-item count)
@@ -693,8 +732,6 @@
  * properties of this element. But some elements with subtle differences need an exact role. If
  * an exact role is not listed in [Role], this property should not be set and the framework will
  * automatically resolve it.
- *
- * @see SemanticsProperties.Role
  */
 var SemanticsPropertyReceiver.role by SemanticsProperties.Role
 
@@ -737,6 +774,21 @@
 var SemanticsPropertyReceiver.selected by SemanticsProperties.Selected
 
 /**
+ * This semantics marks node as a collection and provides the required information.
+ *
+ * @see collectionItemInfo
+ */
+var SemanticsPropertyReceiver.collectionInfo by SemanticsProperties.CollectionInfo
+
+/**
+ * This semantics marks node as an items of a collection and provides the required information.
+ *
+ * If you mark items of a collection, you should also be marking the collection with
+ * [collectionInfo].
+ */
+var SemanticsPropertyReceiver.collectionItemInfo by SemanticsProperties.CollectionItemInfo
+
+/**
  * The state of a toggleable component.
  *
  * The presence of this property indicates that the element is toggleable.
@@ -771,6 +823,11 @@
 /**
  * The node is marked as a collection of horizontally or vertically stacked selectable elements.
  *
+ * Unlike [collectionInfo] which marks a collection of any elements and asks developer to
+ * provide all the required information like number of elements etc., this semantics will
+ * populate the number of selectable elements automatically. Note that if you use this semantics
+ * with lazy collections, it won't get the number of elements in the collection.
+ *
  * @see SemanticsPropertyReceiver.selected
 */
 fun SemanticsPropertyReceiver.selectableGroup() {
diff --git a/fragment/fragment/api/public_plus_experimental_current.txt b/fragment/fragment/api/public_plus_experimental_current.txt
index adc45fa..c4a2d4d 100644
--- a/fragment/fragment/api/public_plus_experimental_current.txt
+++ b/fragment/fragment/api/public_plus_experimental_current.txt
@@ -276,7 +276,6 @@
     method public final void clearFragmentResultListener(String);
     method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
     method @Deprecated public static void enableDebugLogging(boolean);
-    method @androidx.fragment.app.FragmentStateManagerControl public static void enableNewStateManager(boolean);
     method public boolean executePendingTransactions();
     method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
     method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
@@ -370,9 +369,6 @@
     method public void setFragmentResultListener(String, androidx.lifecycle.LifecycleOwner, androidx.fragment.app.FragmentResultListener);
   }
 
-  @experimental.Experimental(level=androidx.annotation.experimental.Experimental.Level.WARNING) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface FragmentStateManagerControl {
-  }
-
   @Deprecated public abstract class FragmentStatePagerAdapter extends androidx.viewpager.widget.PagerAdapter {
     ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager);
     ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager, int);
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReorderingTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReorderingTest.kt
index 85860a6..fde5c4d 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReorderingTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReorderingTest.kt
@@ -23,26 +23,19 @@
 import androidx.fragment.test.R
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleEventObserver
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
-import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
 
 @SmallTest
-@RunWith(Parameterized::class)
-class FragmentReorderingTest(private val stateManager: StateManager) {
-
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "stateManager={0}")
-        fun data() = arrayOf(NewStateManager, OldStateManager)
-    }
+@RunWith(AndroidJUnit4::class)
+class FragmentReorderingTest() {
 
     @Suppress("DEPRECATION")
     @get:Rule
@@ -54,25 +47,16 @@
 
     @Before
     fun setup() {
-        stateManager.setup()
         activityRule.setContentView(R.layout.simple_container)
         container = activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
         fm = activityRule.activity.supportFragmentManager
         instrumentation = InstrumentationRegistry.getInstrumentation()
     }
 
-    @After
-    fun teardown() {
-        stateManager.teardown()
-    }
-
     // Ensure that a replaced fragment is stopped before its replacement is started
     // and vice versa when popped
     @Test
     fun stopBeforeStart() {
-        if (stateManager is OldStateManager) {
-            return
-        }
         val fragment1 = StrictViewFragment()
         fm.beginTransaction()
             .add(R.id.fragmentContainer, fragment1)
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTestUtil.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTestUtil.kt
index 790569f..800e19e 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTestUtil.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTestUtil.kt
@@ -40,27 +40,6 @@
 object Reordered : ReorderingAllowed()
 object Ordered : ReorderingAllowed()
 
-sealed class StateManager {
-    abstract fun setup()
-
-    override fun toString(): String = this.javaClass.simpleName
-
-    fun teardown() {
-        // Reset it back to the default
-        FragmentManager.enableNewStateManager(true)
-    }
-}
-object NewStateManager : StateManager() {
-    override fun setup() {
-        FragmentManager.enableNewStateManager(true)
-    }
-}
-object OldStateManager : StateManager() {
-    override fun setup() {
-        FragmentManager.enableNewStateManager(false)
-    }
-}
-
 @Suppress("DEPRECATION")
 fun androidx.test.rule.ActivityTestRule<out FragmentActivity>.executePendingTransactions(
     fm: FragmentManager = activity.supportFragmentManager
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionAnimTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionAnimTest.kt
index ab7bdd1..1ad1ba5 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionAnimTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionAnimTest.kt
@@ -31,7 +31,6 @@
 import androidx.test.filters.SdkSuppress
 import androidx.testutils.withActivity
 import com.google.common.truth.Truth.assertThat
-import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -44,21 +43,14 @@
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
 class FragmentTransitionAnimTest(
     private val reorderingAllowed: ReorderingAllowed,
-    private val stateManager: StateManager
 ) {
     private var onBackStackChangedTimes: Int = 0
 
     @Before
     fun setup() {
-        stateManager.setup()
         onBackStackChangedTimes = 0
     }
 
-    @After
-    fun teardown() {
-        stateManager.teardown()
-    }
-
     // Ensure when transition duration is shorter than animation duration, we will get both end
     // callbacks
     @Test
@@ -109,13 +101,13 @@
                 TIMEOUT,
                 TimeUnit.MILLISECONDS
             )
-            assertThat(startAnimationRan).isEqualTo(stateManager == OldStateManager)
+            assertThat(startAnimationRan).isFalse()
             fragment.waitForTransition()
             val exitAnimationRan = fragment.exitAnimationLatch.await(
                 TIMEOUT,
                 TimeUnit.MILLISECONDS
             )
-            assertThat(exitAnimationRan).isEqualTo(stateManager == OldStateManager)
+            assertThat(exitAnimationRan).isFalse()
             assertThat(onBackStackChangedTimes).isEqualTo(2)
         }
     }
@@ -170,13 +162,13 @@
                 TIMEOUT,
                 TimeUnit.MILLISECONDS
             )
-            assertThat(startAnimationRan).isEqualTo(stateManager == OldStateManager)
+            assertThat(startAnimationRan).isFalse()
             fragment.waitForTransition()
             val exitAnimationRan = fragment.exitAnimationLatch.await(
                 TIMEOUT,
                 TimeUnit.MILLISECONDS
             )
-            assertThat(exitAnimationRan).isEqualTo(stateManager == OldStateManager)
+            assertThat(exitAnimationRan).isFalse()
             assertThat(onBackStackChangedTimes).isEqualTo(2)
         }
     }
@@ -232,7 +224,7 @@
                 TIMEOUT,
                 TimeUnit.MILLISECONDS
             )
-            assertThat(exitAnimatorRan).isEqualTo(stateManager == OldStateManager)
+            assertThat(exitAnimatorRan).isFalse()
             assertThat(onBackStackChangedTimes).isEqualTo(2)
         }
     }
@@ -288,7 +280,7 @@
                 TIMEOUT,
                 TimeUnit.MILLISECONDS
             )
-            assertThat(exitAnimatorRan).isEqualTo(stateManager == OldStateManager)
+            assertThat(exitAnimatorRan).isFalse()
             assertThat(onBackStackChangedTimes).isEqualTo(2)
         }
     }
@@ -342,17 +334,12 @@
 
     companion object {
         @JvmStatic
-        @Parameterized.Parameters(name = "ordering={0}, stateManager={1}")
+        @Parameterized.Parameters(name = "ordering={0}")
         fun data() = mutableListOf<Array<Any>>().apply {
             arrayOf(
                 Ordered,
                 Reordered
-            ).forEach { ordering ->
-                // Run the test with the new state manager
-                add(arrayOf(ordering, NewStateManager))
-                // Run the test with the old state manager
-                add(arrayOf(ordering, OldStateManager))
-            }
+            )
         }
 
         @AnimRes
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionTest.kt
index 4ccfe08..d92c9bf 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionTest.kt
@@ -15,7 +15,6 @@
  */
 package androidx.fragment.app
 
-import android.graphics.Rect
 import android.os.Build
 import android.os.Bundle
 import android.transition.Transition
@@ -55,8 +54,7 @@
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
 class FragmentTransitionTest(
-    private val reorderingAllowed: ReorderingAllowed,
-    private val stateManager: StateManager
+    private val reorderingAllowed: ReorderingAllowed
 ) {
 
     @Suppress("DEPRECATION")
@@ -71,7 +69,6 @@
 
     @Before
     fun setup() {
-        stateManager.setup()
         activityRule.setContentView(R.layout.simple_container)
         onBackStackChangedTimes = 0
         fragmentManager = activityRule.activity.supportFragmentManager
@@ -81,7 +78,6 @@
     @After
     fun teardown() {
         fragmentManager.removeOnBackStackChangedListener(onBackStackChangedListener)
-        stateManager.teardown()
     }
 
     // Test that normal view transitions (enter, exit, reenter, return) run with
@@ -183,8 +179,6 @@
     fun removeThenAddBeforeTransitionFinishes() {
         // enter transition
         val fragment = setupInitialFragment()
-        val blue = activityRule.findBlue()
-        val green = activityRule.findGreen()
 
         val view1 = fragment.view
 
@@ -208,23 +202,10 @@
         // back stack
         if (reorderingAllowed is Reordered) {
             assertThat(onBackStackChangedTimes).isEqualTo(2)
-            assertThat(fragment.requireView()).isEqualTo(view1)
         } else {
             assertThat(onBackStackChangedTimes).isEqualTo(3)
-            if (stateManager is NewStateManager) {
-                // When using FragmentStateManager, the transition gets cancelled and the
-                // Fragment  does not go all the way through to destroying the view before
-                // coming back up, so the view instances will still match
-                assertThat(fragment.requireView()).isEqualTo(view1)
-            } else {
-                // If reorder is not allowed we will get the exit Transition
-                fragment.waitForTransition()
-                fragment.exitTransition.verifyAndClearTransition {
-                    exitingViews += listOf(green, blue)
-                }
-                assertThat(fragment.requireView()).isNotEqualTo(view1)
-            }
         }
+        assertThat(fragment.requireView()).isEqualTo(view1)
         verifyNoOtherTransitions(fragment)
     }
 
@@ -784,9 +765,7 @@
         fragment1.waitForTransition()
         fragment2.waitForTransition()
         fragment1.exitTransition.endAnimatorCountDownLatch.await(1000, TimeUnit.MILLISECONDS)
-        if (stateManager is NewStateManager) {
-            assertThat(listener.isGoneAtTransitionStart).isFalse()
-        }
+        assertThat(listener.isGoneAtTransitionStart).isFalse()
 
         val endGreen = findViewById(fragment2, R.id.greenSquare)
         val endBlue = findViewById(fragment2, R.id.blueSquare)
@@ -946,7 +925,6 @@
 
         val startBlue = activityRule.findBlue()
         val startGreen = activityRule.findGreen()
-        val startBlueBounds = startBlue.boundsOnScreen
 
         fragmentManager.beginTransaction()
             .addSharedElement(startBlue, "fooSquare")
@@ -962,22 +940,10 @@
         val endBlue = activityRule.findBlue()
         val endGreen = activityRule.findGreen()
 
-        // FragmentStateManager is able to build the correct transition
-        // whether you use reordering or not
-        if (stateManager is NewStateManager || reorderingAllowed is Reordered) {
-            fragment1.exitTransition.verifyAndClearTransition {
-                exitingViews += listOf(startGreen, startBlue)
-            }
-        } else {
-            fragment1.exitTransition.verifyAndClearTransition {
-                epicenter = startBlueBounds
-                exitingViews += startGreen
-            }
-            fragment2.sharedElementEnter.verifyAndClearTransition {
-                epicenter = startBlueBounds
-                exitingViews += startBlue
-            }
+        fragment1.exitTransition.verifyAndClearTransition {
+            exitingViews += listOf(startGreen, startBlue)
         }
+
         verifyNoOtherTransitions(fragment1)
 
         fragment2.enterTransition.verifyAndClearTransition {
@@ -1116,16 +1082,10 @@
         if (reorderingAllowed is Reordered) {
             // reordering allowed fragment3 to get a transition so we should wait for it to finish
             fragment3.waitForTransition()
-            if (stateManager is NewStateManager) {
-                // When using the NewStateManager, the last operation sets the direction.
-                // In this case, the forward direction since we did a replace() after the pop
-                fragment2.exitTransition.verifyAndClearTransition {
-                    exitingViews += listOf(midGreen, midBlue)
-                }
-            } else {
-                fragment2.returnTransition.verifyAndClearTransition {
-                    exitingViews += listOf(midGreen, midBlue)
-                }
+            // The last operation (in this case a replace()) sets the direction of
+            // the transition, so the popped fragment runs its exit transition
+            fragment2.exitTransition.verifyAndClearTransition {
+                exitingViews += listOf(midGreen, midBlue)
             }
             val endGreen = activityRule.findGreen()
             val endBlue = activityRule.findBlue()
@@ -1181,29 +1141,18 @@
             val endBlue = activityRule.findBlue()
             val endGreenBounds = endGreen.boundsOnScreen
 
-            if (stateManager is NewStateManager) {
-                // When using the NewStateManager, the last operation sets the direction.
-                // In this case, the forward direction since we did a replace() after the pop
-                fragment1.exitTransition.verifyAndClearTransition {
-                    epicenter = endGreenBounds
-                    exitingViews += startGreen
-                }
-            } else {
-                fragment1.returnTransition.verifyAndClearTransition {
-                    exitingViews += startGreen
-                }
+            // The last operation (in this case a replace()) sets the direction of
+            // the transition, so the popped fragment runs its exit transition
+            fragment1.exitTransition.verifyAndClearTransition {
+                epicenter = endGreenBounds
+                exitingViews += startGreen
             }
             fragment2.enterTransition.verifyAndClearTransition {
                 epicenter = startGreenBounds
                 enteringViews += endGreen
             }
             fragment2.sharedElementEnter.verifyAndClearTransition {
-                epicenter = if (stateManager is NewStateManager) {
-                    endGreenBounds
-                } else {
-                    // In this case, we can't find an epicenter
-                    Rect()
-                }
+                epicenter = endGreenBounds
                 exitingViews += startBlue
                 enteringViews += endBlue
             }
@@ -1242,20 +1191,10 @@
         val midRed = activityRule.findRed()
         val midGreenBounds = midGreen.boundsOnScreen
 
-        // FragmentStateManager is able to build the correct transition
-        // whether you use reordering or not
-        if (stateManager is NewStateManager || reorderingAllowed is Reordered) {
-            fragment2.sharedElementEnter.verifyAndClearTransition {
-                epicenter = startGreenBounds
-                exitingViews += startGreen
-                enteringViews += midGreen
-            }
-        } else {
-            fragment2.sharedElementEnter.verifyAndClearTransition {
-                epicenter = startGreenBounds
-                exitingViews += listOf(startGreen, startBlue)
-                enteringViews += midGreen
-            }
+        fragment2.sharedElementEnter.verifyAndClearTransition {
+            epicenter = startGreenBounds
+            exitingViews += startGreen
+            enteringViews += midGreen
         }
         fragment2.enterTransition.verifyAndClearTransition {
             epicenter = midGreenBounds
@@ -1685,18 +1624,8 @@
 
     companion object {
         @JvmStatic
-        @Parameterized.Parameters(name = "ordering={0}, stateManager={1}")
-        fun data() = mutableListOf<Array<Any>>().apply {
-            arrayOf(
-                Ordered,
-                Reordered
-            ).forEach { ordering ->
-                // Run the test with the new state manager
-                add(arrayOf(ordering, NewStateManager))
-                // Run the test with the old state manager
-                add(arrayOf(ordering, OldStateManager))
-            }
-        }
+        @Parameterized.Parameters(name = "ordering={0}")
+        fun data() = arrayOf(Ordered, Reordered)
     }
 }
 
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/PostponedTransitionTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/PostponedTransitionTest.kt
index 0948f8e..157c6c7 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/PostponedTransitionTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/PostponedTransitionTest.kt
@@ -25,6 +25,7 @@
 import androidx.fragment.test.R
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.ViewModelStore
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.platform.app.InstrumentationRegistry
@@ -32,26 +33,17 @@
 import androidx.testutils.waitForExecution
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
-import org.junit.After
-import org.junit.Assert.fail
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
 
 @LargeTest
-@RunWith(Parameterized::class)
+@RunWith(AndroidJUnit4::class)
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
-class PostponedTransitionTest(private val stateManager: StateManager) {
-
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "stateManager={0}")
-        fun data() = arrayOf(NewStateManager, OldStateManager)
-    }
+class PostponedTransitionTest() {
 
     @Suppress("DEPRECATION")
     @get:Rule
@@ -62,7 +54,6 @@
 
     @Before
     fun setupContainer() {
-        stateManager.setup()
         activityRule.setContentView(R.layout.simple_container)
         val fm = activityRule.activity.supportFragmentManager
 
@@ -94,11 +85,6 @@
         verifyNoOtherTransitions(beginningFragment)
     }
 
-    @After
-    fun teardown() {
-        stateManager.teardown()
-    }
-
     // Ensure that replacing with a fragment that has a postponed transition
     // will properly postpone it, both adding and popping.
     @Test
@@ -151,9 +137,6 @@
 
     @Test
     fun changePostponedFragmentVisibility() {
-        if (stateManager == OldStateManager) {
-            return
-        }
         val fm = activityRule.activity.supportFragmentManager
         val startBlue = activityRule.findBlue()
 
@@ -290,44 +273,20 @@
         }
         activityRule.waitForExecution()
 
-        if (stateManager is NewStateManager) {
-            // fragment2 should have been put on the back stack without any transitions
-            verifyNoOtherTransitions(fragment2)
+        // fragment2 should have been put on the back stack without any transitions
+        verifyNoOtherTransitions(fragment2)
 
-            // fragment3 should be postponed
-            assertPostponedTransition(beginningFragment, fragment3)
+        // fragment3 should be postponed
+        assertPostponedTransition(beginningFragment, fragment3)
 
-            // start the postponed transition
-            fragment3.startPostponedEnterTransition()
+        // start the postponed transition
+        fragment3.startPostponedEnterTransition()
 
-            // make sure it ran
-            assertForwardTransition(
-                startBlue, startBlueBounds, startGreen,
-                beginningFragment, fragment3
-            )
-        } else {
-            // transition to fragment2 should be started
-            assertForwardTransition(
-                startBlue, startBlueBounds, startGreen,
-                beginningFragment, fragment2
-            )
-
-            // fragment3 should be postponed, but fragment2 should be executed with no transition.
-            assertPostponedTransition(fragment2, fragment3, beginningFragment)
-
-            val startBlue2 = fragment2.requireView().findViewById<View>(R.id.blueSquare)
-            val startGreen2 = fragment2.requireView().findViewById<View>(R.id.greenSquare)
-            val startBlueBounds2 = startBlue2.boundsOnScreen
-
-            // start the postponed transition
-            fragment3.startPostponedEnterTransition()
-
-            // make sure it ran
-            assertForwardTransition(
-                startBlue2, startBlueBounds2, startGreen2,
-                fragment2, fragment3
-            )
-        }
+        // make sure it ran
+        assertForwardTransition(
+            startBlue, startBlueBounds, startGreen,
+            beginningFragment, fragment3
+        )
 
         val startBlue3 = fragment3.requireView().findViewById<View>(R.id.blueSquare)
         val startGreen3 = fragment3.requireView().findViewById<View>(R.id.greenSquare)
@@ -335,37 +294,18 @@
 
         activityRule.popBackStackImmediate(commit, FragmentManager.POP_BACK_STACK_INCLUSIVE)
 
-        if (stateManager is NewStateManager) {
-            // The transition back to beginningFragment should be postponed
-            // and fragment2 should be removed without any transitions
-            assertPostponedTransition(fragment3, beginningFragment, fragment2)
+        // The transition back to beginningFragment should be postponed
+        // and fragment2 should be removed without any transitions
+        assertPostponedTransition(fragment3, beginningFragment, fragment2)
 
-            // start the postponed transition
-            beginningFragment.startPostponedEnterTransition()
+        // start the postponed transition
+        beginningFragment.startPostponedEnterTransition()
 
-            // make sure it ran
-            assertBackTransition(
-                startBlue3, startBlueBounds3, startGreen3,
-                fragment3, beginningFragment
-            )
-        } else {
-            assertBackTransition(startBlue3, startBlueBounds3, startGreen3, fragment3, fragment2)
-
-            assertPostponedTransition(fragment2, beginningFragment, fragment3)
-
-            val endBlue2 = fragment2.requireView().findViewById<View>(R.id.blueSquare)
-            val endGreen2 = fragment2.requireView().findViewById<View>(R.id.greenSquare)
-            val endBlueBounds2 = endBlue2.boundsOnScreen
-
-            // start the postponed transition
-            beginningFragment.startPostponedEnterTransition()
-
-            // make sure it ran
-            assertBackTransition(
-                endBlue2, endBlueBounds2, endGreen2,
-                fragment2, beginningFragment
-            )
-        }
+        // make sure it ran
+        assertBackTransition(
+            startBlue3, startBlueBounds3, startGreen3,
+            fragment3, beginningFragment
+        )
     }
 
     // Ensure that postponed transition is forced after another has been committed.
@@ -402,35 +342,20 @@
         // and start fragment2 -> fragment3 transition postponed
         activityRule.waitForExecution()
 
-        if (stateManager is NewStateManager) {
-            // fragment2 should have been put on the back stack without any transitions
-            verifyNoOtherTransitions(fragment2)
+        // fragment2 should have been put on the back stack without any transitions
+        verifyNoOtherTransitions(fragment2)
 
-            // fragment3 should be postponed
-            assertPostponedTransition(beginningFragment, fragment3)
+        // fragment3 should be postponed
+        assertPostponedTransition(beginningFragment, fragment3)
 
-            // start the postponed transition
-            fragment3.startPostponedEnterTransition()
+        // start the postponed transition
+        fragment3.startPostponedEnterTransition()
 
-            // make sure it ran
-            assertForwardTransition(
-                startBlue, startBlueBounds, startGreen,
-                beginningFragment, fragment3
-            )
-        } else {
-            // fragment3 should be postponed, but fragment2 should be executed with no transition.
-            assertPostponedTransition(fragment2, fragment3, beginningFragment)
-
-            val startBlue2 = activityRule.findBlue()
-            val startGreen2 = activityRule.findGreen()
-            val startBlueBounds2 = startBlue2.boundsOnScreen
-
-            // start the postponed transition
-            fragment3.startPostponedEnterTransition()
-
-            // make sure it ran
-            assertForwardTransition(startBlue2, startBlueBounds2, startGreen2, fragment2, fragment3)
-        }
+        // make sure it ran
+        assertForwardTransition(
+            startBlue, startBlueBounds, startGreen,
+            beginningFragment, fragment3
+        )
 
         val startBlue3 = fragment3.requireView().findViewById<View>(R.id.blueSquare)
         val startGreen3 = fragment3.requireView().findViewById<View>(R.id.greenSquare)
@@ -445,37 +370,20 @@
         // start the beginningFragment transaction postponed
         activityRule.popBackStackImmediate()
 
-        if (stateManager is NewStateManager) {
-            // The transition back to beginningFragment should be postponed
-            // and no transitions should be done on fragment2
-            assertPostponedTransition(fragment3, beginningFragment)
-            verifyNoOtherTransitions(fragment2)
+        // The transition back to beginningFragment should be postponed
+        // and no transitions should be done on fragment2
+        assertPostponedTransition(fragment3, beginningFragment)
+        verifyNoOtherTransitions(fragment2)
 
-            // start the postponed transition
-            beginningFragment.startPostponedEnterTransition()
+        // start the postponed transition
+        beginningFragment.startPostponedEnterTransition()
 
-            // make sure it ran
-            assertBackTransition(
-                startBlue3, startBlueBounds3, startGreen3,
-                fragment3, beginningFragment
-            )
-            verifyNoOtherTransitions(fragment2)
-        } else {
-            assertPostponedTransition(fragment2, beginningFragment, fragment3)
-
-            val endBlue2 = fragment2.requireView().findViewById<View>(R.id.blueSquare)
-            val endGreen2 = fragment2.requireView().findViewById<View>(R.id.greenSquare)
-            val endBlueBounds2 = endBlue2.boundsOnScreen
-
-            // start the postponed transition
-            beginningFragment.startPostponedEnterTransition()
-
-            // make sure it ran
-            assertBackTransition(
-                endBlue2, endBlueBounds2, endGreen2,
-                fragment2, beginningFragment
-            )
-        }
+        // make sure it ran
+        assertBackTransition(
+            startBlue3, startBlueBounds3, startGreen3,
+            fragment3, beginningFragment
+        )
+        verifyNoOtherTransitions(fragment2)
     }
 
     // Do a bunch of things to one fragment in a transaction and see if it can screw things up.
@@ -852,8 +760,6 @@
         activityRule.waitForExecution()
 
         val startBlue2 = fragment2.requireView().findViewById<View>(R.id.blueSquare)
-        val startGreen2 = fragment2.requireView().findViewById<View>(R.id.greenSquare)
-        val startBlueBounds2 = startBlue2.boundsOnScreen
 
         instrumentation.runOnMainSync {
             fm.beginTransaction()
@@ -863,31 +769,19 @@
                 .commitNow()
         }
 
-        if (stateManager is NewStateManager) {
-            // fragment2 should have been put on the back stack without any transitions
-            verifyNoOtherTransitions(fragment2)
+        // fragment2 should have been put on the back stack without any transitions
+        verifyNoOtherTransitions(fragment2)
 
-            // fragment3 should be postponed
-            assertPostponedTransition(beginningFragment, fragment3)
+        // fragment3 should be postponed
+        assertPostponedTransition(beginningFragment, fragment3)
 
-            // start the postponed transition
-            fragment3.startPostponedEnterTransition()
+        // start the postponed transition
+        fragment3.startPostponedEnterTransition()
 
-            assertForwardTransition(
-                startBlue, startBlueBounds, startGreen,
-                beginningFragment, fragment3
-            )
-        } else {
-            assertPostponedTransition(fragment2, fragment3, beginningFragment)
-
-            // start the postponed transition
-            fragment3.startPostponedEnterTransition()
-
-            assertForwardTransition(
-                startBlue2, startBlueBounds2, startGreen2,
-                fragment2, fragment3
-            )
-        }
+        assertForwardTransition(
+            startBlue, startBlueBounds, startGreen,
+            beginningFragment, fragment3
+        )
     }
 
     // Make sure that when a transaction that removes a view is postponed that
@@ -1034,25 +928,9 @@
         activityRule.runOnUiThread {
             // start the postponed transition
             fragment.startPostponedEnterTransition()
-
-            if (stateManager is NewStateManager) {
-                // This should succeed since onResume() is called outside of the
-                // transaction completing
-                fm.executePendingTransactions()
-            } else {
-                try {
-                    // This should trigger an IllegalStateException
-                    fm.executePendingTransactions()
-                    fail(
-                        "commitNow() while executing a transaction should cause an" +
-                            " IllegalStateException"
-                    )
-                } catch (e: IllegalStateException) {
-                    assertThat(e)
-                        .hasMessageThat()
-                        .contains("FragmentManager is already executing transactions")
-                }
-            }
+            // This should succeed since onResume() is called outside of the
+            // transaction completing
+            fm.executePendingTransactions()
         }
     }
 
@@ -1241,13 +1119,8 @@
 
         verifyNoOtherTransitions(fromFragment)
         verifyNoOtherTransitions(toFragment)
-        if (stateManager is NewStateManager) {
-            assertThat(fromFragment.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED)
-            assertThat(toFragment.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
-        } else {
-            assertThat(fromFragment.isResumed).isTrue()
-            assertThat(toFragment.isResumed).isFalse()
-        }
+        assertThat(fromFragment.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED)
+        assertThat(toFragment.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
     }
 
     private fun clearTargets(fragment: TransitionFragment) {
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/PrimaryNavFragmentTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/PrimaryNavFragmentTest.kt
index 8bd7499..4255529 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/PrimaryNavFragmentTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/PrimaryNavFragmentTest.kt
@@ -300,29 +300,11 @@
         activityRule.waitForExecution()
 
         assertThat(navigations.drain()).isEqualTo(
-            if (FragmentManager.USE_STATE_MANAGER) {
-                listOf(
-                    trackingFragment to false,
-                    postponedFragment to true
-                )
-            } else {
-                listOf(
-                    trackingFragment to false,
-                    postponedFragment to true,
-                    postponedFragment to false,
-                    trackingFragment to true
-                )
-            }
+            listOf(trackingFragment to false, postponedFragment to true)
         )
         assertWithMessage("primary nav fragment not set correctly after replace")
             .that(fm.primaryNavigationFragment)
-            .isSameInstanceAs(
-                if (FragmentManager.USE_STATE_MANAGER) {
-                    postponedFragment
-                } else {
-                    trackingFragment
-                }
-            )
+            .isSameInstanceAs(postponedFragment)
 
         // Now pop the back stack and also add a replacement Fragment
         fm.popBackStack()
@@ -335,23 +317,12 @@
         activityRule.waitForExecution()
 
         assertThat(navigations.drain()).isEqualTo(
-            if (FragmentManager.USE_STATE_MANAGER) {
-                listOf(
-                    postponedFragment to false,
-                    trackingFragment to true,
-                    trackingFragment to false,
-                    replacementFragment to true
-                )
-            } else {
-                listOf(
-                    trackingFragment to false,
-                    postponedFragment to true,
-                    postponedFragment to false,
-                    trackingFragment to true,
-                    trackingFragment to false,
-                    replacementFragment to true
-                )
-            }
+            listOf(
+                postponedFragment to false,
+                trackingFragment to true,
+                trackingFragment to false,
+                replacementFragment to true
+            )
         )
 
         assertWithMessage("primary nav fragment not set correctly after replace")
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/SpecialEffectsControllerTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/SpecialEffectsControllerTest.kt
index 6fa9e48..bdda9ab 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/SpecialEffectsControllerTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/SpecialEffectsControllerTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.fragment.app
 
-import android.view.View
 import android.view.ViewGroup
 import android.widget.FrameLayout
 import androidx.fragment.app.test.EmptyFragmentTestActivity
@@ -417,11 +416,6 @@
                 // This moves the Fragment up to STARTED,
                 // calling enqueueAdd() under the hood
                 fragmentStateManager.moveToExpectedState()
-                // Normally this would be done for us in the USE_STATE_MANAGER world
-                // but we need to do it manually if that isn't the case yet.
-                if (!FragmentManager.USE_STATE_MANAGER) {
-                    fragment.mView.visibility = View.INVISIBLE
-                }
             }
             assertThat(controller.operationsToExecute)
                 .isEmpty()
@@ -442,17 +436,8 @@
             onActivity {
                 fragment.startPostponedEnterTransition()
             }
-            onActivity {
-                // When USE_STATE_MANAGER is true, this second onActivity is enough
-                // to handle the post() that startPostponedEnterTransition() does.
-                // Otherwise, we need to do a little more work at this point
-                if (!FragmentManager.USE_STATE_MANAGER) {
-                    // These are called automatically when USE_STATE_MANAGER is true
-                    // but we need to call them manually if it is false
-                    controller.markPostponedState()
-                    controller.executePendingOperations()
-                }
-            }
+            // Wait for idle thread to handle the post() that startPostponedEnterTransition() does.
+            InstrumentationRegistry.getInstrumentation().waitForIdleSync()
 
             // Verify that the operation was sent for execution
             assertThat(controller.operationsToExecute)
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/StrictViewFragment.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/StrictViewFragment.kt
index 21201eb..00b0c2e 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/StrictViewFragment.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/StrictViewFragment.kt
@@ -73,10 +73,7 @@
         assertWithMessage("getView returned null in onDestroyView")
             .that(view)
             .isNotNull()
-        if (requireView().parent != null &&
-            requireView().animation != null &&
-            FragmentManager.USE_STATE_MANAGER
-        ) {
+        if (requireView().parent != null && requireView().animation != null) {
             assertWithMessage("View should be removed from parent if there is no animation")
                 .that((requireView().parent as ViewGroup).layoutTransition)
                 .isNotNull()
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java b/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
index c67ee5a..e691044 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
@@ -364,49 +364,8 @@
         return true;
     }
 
-    boolean interactsWith(int containerId) {
-        final int numOps = mOps.size();
-        for (int opNum = 0; opNum < numOps; opNum++) {
-            final Op op = mOps.get(opNum);
-            final int fragContainer = op.mFragment != null ? op.mFragment.mContainerId : 0;
-            if (fragContainer != 0 && fragContainer == containerId) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean interactsWith(ArrayList<BackStackRecord> records, int startIndex, int endIndex) {
-        if (endIndex == startIndex) {
-            return false;
-        }
-        final int numOps = mOps.size();
-        int lastContainer = -1;
-        for (int opNum = 0; opNum < numOps; opNum++) {
-            final Op op = mOps.get(opNum);
-            final int container = op.mFragment != null ? op.mFragment.mContainerId : 0;
-            if (container != 0 && container != lastContainer) {
-                lastContainer = container;
-                for (int i = startIndex; i < endIndex; i++) {
-                    BackStackRecord record = records.get(i);
-                    final int numThoseOps = record.mOps.size();
-                    for (int thoseOpIndex = 0; thoseOpIndex < numThoseOps; thoseOpIndex++) {
-                        final Op thatOp = record.mOps.get(thoseOpIndex);
-                        final int thatContainer = thatOp.mFragment != null
-                                ? thatOp.mFragment.mContainerId : 0;
-                        if (thatContainer == container) {
-                            return true;
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
     /**
-     * Executes the operations contained within this transaction. The Fragment states will only
-     * be modified if optimizations are not allowed.
+     * Executes the operations contained within this transaction.
      */
     void executeOps() {
         final int numOps = mOps.size();
@@ -459,26 +418,13 @@
                 default:
                     throw new IllegalArgumentException("Unknown cmd: " + op.mCmd);
             }
-            if (!mReorderingAllowed && op.mCmd != OP_ADD && f != null) {
-                if (!FragmentManager.USE_STATE_MANAGER) {
-                    mManager.moveFragmentToExpectedState(f);
-                }
-            }
-        }
-        if (!mReorderingAllowed && !FragmentManager.USE_STATE_MANAGER) {
-            // Added fragments are added at the end to comply with prior behavior.
-            mManager.moveToState(mManager.mCurState, true);
         }
     }
 
     /**
-     * Reverses the execution of the operations within this transaction. The Fragment states will
-     * only be modified if reordering is not allowed.
-     *
-     * @param moveToState {@code true} if added fragments should be moved to their final state
-     *                    in ordered transactions
+     * Reverses the execution of the operations within this transaction.
      */
-    void executePopOps(boolean moveToState) {
+    void executePopOps() {
         for (int opNum = mOps.size() - 1; opNum >= 0; opNum--) {
             final Op op = mOps.get(opNum);
             Fragment f = op.mFragment;
@@ -529,14 +475,6 @@
                 default:
                     throw new IllegalArgumentException("Unknown cmd: " + op.mCmd);
             }
-            if (!mReorderingAllowed && op.mCmd != OP_REMOVE && f != null) {
-                if (!FragmentManager.USE_STATE_MANAGER) {
-                    mManager.moveFragmentToExpectedState(f);
-                }
-            }
-        }
-        if (!mReorderingAllowed && moveToState && !FragmentManager.USE_STATE_MANAGER) {
-            mManager.moveToState(mManager.mCurState, true);
         }
     }
 
@@ -708,31 +646,6 @@
         }
     }
 
-    boolean isPostponed() {
-        for (int opNum = 0; opNum < mOps.size(); opNum++) {
-            final Op op = mOps.get(opNum);
-            if (isFragmentPostponed(op)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    void setOnStartPostponedListener(Fragment.OnStartEnterTransitionListener listener) {
-        for (int opNum = 0; opNum < mOps.size(); opNum++) {
-            final Op op = mOps.get(opNum);
-            if (isFragmentPostponed(op)) {
-                op.mFragment.setOnStartEnterTransitionListener(listener);
-            }
-        }
-    }
-
-    private static boolean isFragmentPostponed(Op op) {
-        final Fragment fragment = op.mFragment;
-        return fragment != null && fragment.mAdded && fragment.mView != null && !fragment.mDetached
-                && !fragment.mHidden && fragment.isPostponed();
-    }
-
     @Override
     @Nullable
     public String getName() {
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 6a65314..c33fffe 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
@@ -263,19 +263,9 @@
         }
     };
 
-    // True if the View was added, and its animation has yet to be run. This could
-    // also indicate that the fragment view hasn't been made visible, even if there is no
-    // animation for this fragment.
-    boolean mIsNewlyAdded;
-
     // True if mHidden has been changed and the animation should be scheduled.
     boolean mHiddenChanged;
 
-    // The alpha of the view when the view was added and then postponed. If the value is less
-    // than zero, this means that the view's add was canceled and should not participate in
-    // removal animations.
-    float mPostponedAlpha;
-
     // The cached value from onGetLayoutInflater(Bundle) that will be returned from
     // getLayoutInflater()
     LayoutInflater mLayoutInflater;
@@ -1145,16 +1135,6 @@
     }
 
     /**
-     * Return <code>true</code> if this fragment or any of its ancestors are currently being removed
-     * from its activity.  This is <em>not</em> whether its activity is finishing, but rather
-     * whether it, or its ancestors are in the process of being removed from its activity.
-     */
-    final boolean isRemovingParent() {
-        Fragment parent = getParentFragment();
-        return parent != null && (parent.isRemoving() || parent.isRemovingParent());
-    }
-
-    /**
      * Return true if the layout is included as part of an activity view
      * hierarchy via the &lt;fragment&gt; tag.  This will always be true when
      * fragments are created through the &lt;fragment&gt; tag, <em>except</em>
@@ -2676,10 +2656,7 @@
      * independent containers will not interfere with each other's postponement.
      * <p>
      * Calling postponeEnterTransition on Fragments with a null View will not postpone the
-     * transition. Likewise, postponement only works if
-     * {@link FragmentTransaction#setReorderingAllowed(boolean) FragmentTransaction reordering} is
-     * enabled if you have called {@link FragmentManager#enableNewStateManager(boolean)} with
-     * <code>false</code>.
+     * transition.
      *
      * @see Activity#postponeEnterTransition()
      * @see FragmentTransaction#setReorderingAllowed(boolean)
@@ -2709,10 +2686,7 @@
      * independent containers will not interfere with each other's postponement.
      * <p>
      * Calling postponeEnterTransition on Fragments with a null View will not postpone the
-     * transition. Likewise, postponement only works if
-     * {@link FragmentTransaction#setReorderingAllowed(boolean) FragmentTransaction reordering} is
-     * enabled if you have called {@link FragmentManager#enableNewStateManager(boolean)} with
-     * <code>false</code>.
+     * transition.
      *
      * @param duration The length of the delay in {@code timeUnit} units
      * @param timeUnit The units of time for {@code duration}
@@ -2768,18 +2742,10 @@
      */
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     void callStartTransitionListener(boolean calledDirectly) {
-        final OnStartEnterTransitionListener listener;
-        if (mAnimationInfo == null) {
-            listener = null;
-        } else {
+        if (mAnimationInfo != null) {
             mAnimationInfo.mEnterTransitionPostponed = false;
-            listener = mAnimationInfo.mStartEnterTransitionListener;
-            mAnimationInfo.mStartEnterTransitionListener = null;
         }
-        if (listener != null) {
-            listener.onStartEnterTransition();
-        } else if (FragmentManager.USE_STATE_MANAGER && mView != null
-                && mContainer != null && mFragmentManager != null) {
+        if (mView != null && mContainer != null && mFragmentManager != null) {
             // Mark the updated postponed state with the SpecialEffectsController immediately
             final SpecialEffectsController controller = SpecialEffectsController
                     .getOrCreateController(mContainer, mFragmentManager);
@@ -3256,23 +3222,6 @@
         }
     }
 
-    void setOnStartEnterTransitionListener(OnStartEnterTransitionListener listener) {
-        ensureAnimationInfo();
-        if (listener == mAnimationInfo.mStartEnterTransitionListener) {
-            return;
-        }
-        if (listener != null && mAnimationInfo.mStartEnterTransitionListener != null) {
-            throw new IllegalStateException("Trying to set a replacement "
-                    + "startPostponedEnterTransition on " + this);
-        }
-        if (mAnimationInfo.mEnterTransitionPostponed) {
-            mAnimationInfo.mStartEnterTransitionListener = listener;
-        }
-        if (listener != null) {
-            listener.startListening();
-        }
-    }
-
     private AnimationInfo ensureAnimationInfo() {
         if (mAnimationInfo == null) {
             mAnimationInfo = new AnimationInfo();
@@ -3391,21 +3340,6 @@
         return mAnimationInfo.mAnimatingAway;
     }
 
-    void setAnimatingAway(View view) {
-        ensureAnimationInfo().mAnimatingAway = view;
-    }
-
-    void setAnimator(Animator animator) {
-        ensureAnimationInfo().mAnimator = animator;
-    }
-
-    Animator getAnimator() {
-        if (mAnimationInfo == null) {
-            return null;
-        }
-        return mAnimationInfo.mAnimator;
-    }
-
     void setPostOnViewCreatedAlpha(float alpha) {
         ensureAnimationInfo().mPostOnViewCreatedAlpha = alpha;
     }
@@ -3435,17 +3369,6 @@
         return mAnimationInfo.mEnterTransitionPostponed;
     }
 
-    boolean isHideReplaced() {
-        if (mAnimationInfo == null) {
-            return false;
-        }
-        return mAnimationInfo.mIsHideReplaced;
-    }
-
-    void setHideReplaced(boolean replaced) {
-        ensureAnimationInfo().mIsHideReplaced = replaced;
-    }
-
     /**
      * {@inheritDoc}
      *
@@ -3556,16 +3479,6 @@
     }
 
     /**
-     * Used internally to be notified when {@link #startPostponedEnterTransition()} has
-     * been called. This listener will only be called once and then be removed from the
-     * listeners.
-     */
-    interface OnStartEnterTransitionListener {
-        void onStartEnterTransition();
-        void startListening();
-    }
-
-    /**
      * Contains all the animation and transition information for a fragment. This will only
      * be instantiated for Fragments that have Views.
      */
@@ -3575,10 +3488,6 @@
         // view that is animating.
         View mAnimatingAway;
 
-        // Non-null if the fragment's view hierarchy is currently animating away with an
-        // animator instead of an animation.
-        Animator mAnimator;
-
         // If app requests the animation direction, this is what to use
         boolean mIsPop;
 
@@ -3613,12 +3522,5 @@
         // True when postponeEnterTransition has been called and startPostponeEnterTransition
         // hasn't been called yet.
         boolean mEnterTransitionPostponed;
-
-        // Listener to wait for startPostponeEnterTransition. After being called, it will
-        // be set to null
-        OnStartEnterTransitionListener mStartEnterTransitionListener;
-
-        // True if the View was hidden, but the transition is handling the hide
-        boolean mIsHideReplaced;
     }
 }
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentAnim.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentAnim.java
index 28116cf..aa6272c 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentAnim.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentAnim.java
@@ -18,7 +18,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
-import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -31,7 +30,6 @@
 
 import androidx.annotation.AnimRes;
 import androidx.annotation.NonNull;
-import androidx.core.os.CancellationSignal;
 import androidx.core.view.OneShotPreDrawListener;
 import androidx.fragment.R;
 
@@ -134,83 +132,6 @@
         }
     }
 
-    /**
-     * Animates the removal of a fragment with the given animator or animation. After animating,
-     * the fragment's view will be removed from the hierarchy.
-     *
-     * @param fragment The fragment to animate out
-     * @param anim The animator or animation to run on the fragment's view
-     */
-    static void animateRemoveFragment(@NonNull final Fragment fragment,
-            @NonNull AnimationOrAnimator anim,
-            @NonNull final FragmentTransition.Callback callback) {
-        final View viewToAnimate = fragment.mView;
-        final ViewGroup container = fragment.mContainer;
-        container.startViewTransition(viewToAnimate);
-        final CancellationSignal signal = new CancellationSignal();
-        signal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
-            @Override
-            public void onCancel() {
-                if (fragment.getAnimatingAway() != null) {
-                    View v = fragment.getAnimatingAway();
-                    fragment.setAnimatingAway(null);
-                    v.clearAnimation();
-                }
-                fragment.setAnimator(null);
-            }
-        });
-        callback.onStart(fragment, signal);
-        if (anim.animation != null) {
-            Animation animation =
-                    new EndViewTransitionAnimation(anim.animation, container, viewToAnimate);
-            fragment.setAnimatingAway(fragment.mView);
-            animation.setAnimationListener(new Animation.AnimationListener() {
-                @Override
-                public void onAnimationStart(Animation animation) {
-                }
-
-                @Override
-                public void onAnimationEnd(Animation animation) {
-                    // onAnimationEnd() comes during draw(), so there can still be some
-                    // draw events happening after this call. We don't want to detach
-                    // the view until after the onAnimationEnd()
-                    container.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            if (fragment.getAnimatingAway() != null) {
-                                fragment.setAnimatingAway(null);
-                                callback.onComplete(fragment, signal);
-                            }
-                        }
-                    });
-                }
-
-                @Override
-                public void onAnimationRepeat(Animation animation) {
-                }
-            });
-            fragment.mView.startAnimation(animation);
-        } else {
-            Animator animator = anim.animator;
-            fragment.setAnimator(anim.animator);
-            animator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator anim) {
-                    container.endViewTransition(viewToAnimate);
-                    // If an animator ends immediately, we can just pretend there is no animation.
-                    // When that happens the the fragment's view won't have been removed yet.
-                    Animator animator = fragment.getAnimator();
-                    fragment.setAnimator(null);
-                    if (animator != null && container.indexOfChild(viewToAnimate) < 0) {
-                        callback.onComplete(fragment, signal);
-                    }
-                }
-            });
-            animator.setTarget(fragment.mView);
-            animator.start();
-        }
-    }
-
     @AnimRes
     private static int transitToAnimResourceId(@NonNull Context context, int transit,
             boolean enter) {
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
index 244bbce..0ff6ea8 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -21,8 +21,6 @@
 import static androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult.EXTRA_INTENT_SENDER_REQUEST;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.Context;
@@ -61,8 +59,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.StringRes;
-import androidx.collection.ArraySet;
-import androidx.core.os.CancellationSignal;
 import androidx.fragment.R;
 import androidx.fragment.app.strictmode.FragmentStrictMode;
 import androidx.lifecycle.Lifecycle;
@@ -98,33 +94,6 @@
 public abstract class FragmentManager implements FragmentResultOwner {
     private static boolean DEBUG = false;
     static final String TAG = "FragmentManager";
-    static boolean USE_STATE_MANAGER = true;
-
-    /**
-     * Control whether FragmentManager uses the new state manager that is responsible for:
-     * <ul>
-     *     <li>Moving Fragments through their lifecycle methods</li>
-     *     <li>Running animations and transitions</li>
-     *     <li>Handling postponed transactions</li>
-     * </ul>
-     *
-     * This must only be changed <strong>before</strong> any fragment transactions are done
-     * (i.e., in your <code>Application</code> class or prior to <code>super.onCreate()</code>
-     * in every activity with the same value for all activities). Changing it after that point
-     * is <strong>not</strong> supported and can result in fragments not moving to their
-     * expected state.
-     * <p>
-     * This is <strong>enabled</strong> by default. Disabling it should only be used in
-     * cases where you are debugging a potential regression and as part of
-     * <a href="https://issuetracker.google.com/issues/new?component=460964">filing
-     * an issue</a> to verify and fix the regression.
-     *
-     * @param enabled Whether the new state manager should be enabled.
-     */
-    @FragmentStateManagerControl
-    public static void enableNewStateManager(boolean enabled) {
-        FragmentManager.USE_STATE_MANAGER = enabled;
-    }
 
     /**
      * Control whether the framework's internal fragment manager debugging
@@ -450,23 +419,6 @@
             Collections.synchronizedMap(new HashMap<String, LifecycleAwareResultListener>());
 
     private ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
-    private Map<Fragment, HashSet<CancellationSignal>> mExitAnimationCancellationSignals =
-            Collections.synchronizedMap(new HashMap<Fragment, HashSet<CancellationSignal>>());
-    private final FragmentTransition.Callback mFragmentTransitionCallback =
-            new FragmentTransition.Callback() {
-                @Override
-                public void onStart(@NonNull Fragment fragment,
-                        @NonNull CancellationSignal signal) {
-                    addCancellationSignal(fragment, signal);
-                }
-
-                @Override
-                public void onComplete(@NonNull Fragment f, @NonNull CancellationSignal signal) {
-                    if (!signal.isCanceled()) {
-                        removeCancellationSignal(f, signal);
-                    }
-                }
-            };
     private final FragmentLifecycleCallbacksDispatcher mLifecycleCallbacksDispatcher =
             new FragmentLifecycleCallbacksDispatcher(this);
     private final CopyOnWriteArrayList<FragmentOnAttachListener> mOnAttachListeners =
@@ -518,9 +470,6 @@
     private ArrayList<Boolean> mTmpIsPop;
     private ArrayList<Fragment> mTmpAddedFragments;
 
-    // Postponed transactions.
-    private ArrayList<StartEnterTransitionListener> mPostponedTransactions;
-
     private FragmentManagerViewModel mNonConfig;
 
     private FragmentStrictMode.Policy mStrictModePolicy;
@@ -866,36 +815,6 @@
         }
     }
 
-    /**
-     * Add new {@link CancellationSignal} for exit animation cancel callbacks
-     */
-    void addCancellationSignal(@NonNull Fragment f, @NonNull CancellationSignal signal) {
-        if (mExitAnimationCancellationSignals.get(f) == null) {
-            mExitAnimationCancellationSignals.put(f, new HashSet<CancellationSignal>());
-        }
-        mExitAnimationCancellationSignals.get(f).add(signal);
-    }
-
-    /**
-     * Remove a {@link CancellationSignal} that was previously added with
-     * {@link #addCancellationSignal(Fragment, CancellationSignal)}.
-     *
-     * Destroy the view of the Fragment associated with that listener and move it to the proper
-     * state.
-     */
-    void removeCancellationSignal(@NonNull Fragment f, @NonNull CancellationSignal signal) {
-        HashSet<CancellationSignal> signals = mExitAnimationCancellationSignals.get(f);
-        if (signals != null && signals.remove(signal) && signals.isEmpty()) {
-            mExitAnimationCancellationSignals.remove(f);
-            // The Fragment state must be below STARTED before destroying the view to ensure we
-            // support hide/show
-            if (f.mState < Fragment.STARTED) {
-                destroyFragmentView(f);
-                moveToState(f);
-            }
-        }
-    }
-
     @Override
     public final void setFragmentResult(@NonNull String requestKey, @NonNull Bundle result) {
         // Check if there is a listener waiting for a result with this key
@@ -1356,11 +1275,7 @@
                 return;
             }
             f.mDeferStart = false;
-            if (USE_STATE_MANAGER) {
-                fragmentStateManager.moveToExpectedState();
-            } else {
-                moveToState(f);
-            }
+            fragmentStateManager.moveToExpectedState();
         }
     }
 
@@ -1368,190 +1283,6 @@
         return mCurState >= state;
     }
 
-    @SuppressWarnings("deprecation")
-    void moveToState(@NonNull Fragment f, int newState) {
-        FragmentStateManager fragmentStateManager = mFragmentStore.getFragmentStateManager(f.mWho);
-        if (fragmentStateManager == null) {
-            // Ideally, we only call moveToState() on active Fragments. However,
-            // in restoreSaveState() we can call moveToState() on retained Fragments
-            // just to clean them up without them ever being added to mActive.
-            // For these cases, a brand new FragmentStateManager is enough.
-            fragmentStateManager = new FragmentStateManager(mLifecycleCallbacksDispatcher,
-                    mFragmentStore, f);
-            // Only allow this FragmentStateManager to go up to CREATED at the most
-            fragmentStateManager.setFragmentManagerState(Fragment.CREATED);
-        }
-        // When inflating an Activity view with a resource instead of using setContentView(), and
-        // that resource adds a fragment using the <fragment> tag (i.e. from layout and in layout),
-        // the fragment will move to the VIEW_CREATED state before the fragment manager
-        // moves to CREATED. So when moving the fragment manager moves to CREATED and the
-        // inflated fragment is already in VIEW_CREATED we need to move new state up from CREATED
-        // to VIEW_CREATED. This avoids accidentally moving the fragment back down to CREATED
-        // which would immediately destroy the Fragment's view. We rely on computeExpectedState()
-        // to pull the state back down if needed.
-        if (f.mFromLayout && f.mInLayout && f.mState == Fragment.VIEW_CREATED) {
-            newState = Math.max(newState, Fragment.VIEW_CREATED);
-        }
-        newState = Math.min(newState, fragmentStateManager.computeExpectedState());
-        if (f.mState <= newState) {
-            // If we are moving to the same state, we do not need to give up on the animation.
-            if (f.mState < newState && !mExitAnimationCancellationSignals.isEmpty()) {
-                // The fragment is currently being animated...  but!  Now we
-                // want to move our state back up.  Give up on waiting for the
-                // animation and proceed from where we are.
-                cancelExitAnimation(f);
-            }
-            switch (f.mState) {
-                case Fragment.INITIALIZING:
-                    if (newState > Fragment.INITIALIZING) {
-                        fragmentStateManager.attach();
-                    }
-                    // fall through
-                case Fragment.ATTACHED:
-                    if (newState > Fragment.ATTACHED) {
-                        fragmentStateManager.create();
-                    }
-                    // fall through
-                case Fragment.CREATED:
-                    // We want to unconditionally run this anytime we do a moveToState that
-                    // moves the Fragment above INITIALIZING, including cases such as when
-                    // we move from CREATED => CREATED as part of the case fall through above.
-                    if (newState > Fragment.INITIALIZING) {
-                        fragmentStateManager.ensureInflatedView();
-                    }
-
-                    if (newState > Fragment.CREATED) {
-                        fragmentStateManager.createView();
-                    }
-                    // fall through
-                case Fragment.VIEW_CREATED:
-                    if (newState > Fragment.VIEW_CREATED) {
-                        fragmentStateManager.activityCreated();
-                    }
-                    // fall through
-                case Fragment.ACTIVITY_CREATED:
-                    if (newState > Fragment.ACTIVITY_CREATED) {
-                        fragmentStateManager.start();
-                    }
-                    // fall through
-                case Fragment.STARTED:
-                    if (newState > Fragment.STARTED) {
-                        fragmentStateManager.resume();
-                    }
-            }
-        } else if (f.mState > newState) {
-            switch (f.mState) {
-                case Fragment.RESUMED:
-                    if (newState < Fragment.RESUMED) {
-                        fragmentStateManager.pause();
-                    }
-                    // fall through
-                case Fragment.STARTED:
-                    if (newState < Fragment.STARTED) {
-                        fragmentStateManager.stop();
-                    }
-                    // fall through
-                case Fragment.ACTIVITY_CREATED:
-                    if (newState < Fragment.ACTIVITY_CREATED) {
-                        if (isLoggingEnabled(Log.DEBUG)) {
-                            Log.d(TAG, "movefrom ACTIVITY_CREATED: " + f);
-                        }
-                        if (f.mView != null) {
-                            // Need to save the current view state if not
-                            // done already.
-                            if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
-                                fragmentStateManager.saveViewState();
-                            }
-                        }
-                    }
-                    // fall through
-                case Fragment.VIEW_CREATED:
-                    if (newState < Fragment.VIEW_CREATED) {
-                        FragmentAnim.AnimationOrAnimator anim = null;
-                        if (f.mView != null && f.mContainer != null) {
-                            // Stop any current animations:
-                            f.mContainer.endViewTransition(f.mView);
-                            f.mView.clearAnimation();
-                            // If parent is being removed, no need to handle child animations.
-                            if (!f.isRemovingParent()) {
-                                if (mCurState > Fragment.INITIALIZING && !mDestroyed
-                                        && f.mView.getVisibility() == View.VISIBLE
-                                        && f.mPostponedAlpha >= 0) {
-                                    anim = FragmentAnim.loadAnimation(mHost.getContext(),
-                                            f, false, f.getPopDirection());
-                                }
-                                f.mPostponedAlpha = 0;
-                                // Robolectric tests do not post the animation like a real device
-                                // so we should keep up with the container and view in case the
-                                // fragment view is destroyed before we can remove it.
-                                ViewGroup container = f.mContainer;
-                                View view = f.mView;
-                                if (anim != null) {
-                                    FragmentAnim.animateRemoveFragment(f, anim,
-                                            mFragmentTransitionCallback);
-                                }
-                                container.removeView(view);
-                                if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                                    Log.v(FragmentManager.TAG, "Removing view " + view + " for "
-                                            + "fragment " + f + " from container " + container);
-                                }
-                                // If the local container is different from the fragment
-                                // container, that means onAnimationEnd was called, onDestroyView
-                                // was dispatched and the fragment was already moved to state, so
-                                // we should early return here instead of attempting to move to
-                                // state again.
-                                if (container != f.mContainer) {
-                                    return;
-                                }
-                            }
-                        }
-                        // If a fragment has an exit animation (or transition), do not destroy
-                        // its view immediately and set the state after animating
-                        if (mExitAnimationCancellationSignals.get(f) == null) {
-                            fragmentStateManager.destroyFragmentView();
-                        }
-                    }
-                    // fall through
-                case Fragment.CREATED:
-                    if (newState < Fragment.CREATED) {
-                        if (mExitAnimationCancellationSignals.get(f) != null) {
-                            // We are waiting for the fragment's view to finish animating away.
-                            newState = Fragment.CREATED;
-                        } else {
-                            fragmentStateManager.destroy();
-                        }
-                    }
-                    // fall through
-                case Fragment.ATTACHED:
-                    if (newState < Fragment.ATTACHED) {
-                        fragmentStateManager.detach();
-                    }
-            }
-        }
-
-        if (f.mState != newState) {
-            if (isLoggingEnabled(Log.DEBUG)) {
-                Log.d(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
-                        + "expected state " + newState + " found " + f.mState);
-            }
-            f.mState = newState;
-        }
-    }
-
-    // If there is a listener associated with the given fragment, remove that listener and
-    // destroy the fragment's view.
-    private void cancelExitAnimation(@NonNull Fragment f) {
-        HashSet<CancellationSignal> signals = mExitAnimationCancellationSignals.get(f);
-        if (signals != null) {
-            for (CancellationSignal signal: signals) {
-                signal.cancel();
-            }
-            signals.clear();
-            destroyFragmentView(f);
-            mExitAnimationCancellationSignals.remove(f);
-        }
-    }
-
     /**
      * Allows for changing the draw order on a container, if the container is a
      * FragmentContainerView.
@@ -1565,123 +1296,6 @@
         }
     }
 
-    private void destroyFragmentView(@NonNull Fragment fragment) {
-        fragment.performDestroyView();
-        mLifecycleCallbacksDispatcher.dispatchOnFragmentViewDestroyed(fragment, false);
-        fragment.mContainer = null;
-        fragment.mView = null;
-        // Set here to ensure that Observers are called after
-        // the Fragment's view is set to null
-        fragment.mViewLifecycleOwner = null;
-        fragment.mViewLifecycleOwnerLiveData.setValue(null);
-        fragment.mInLayout = false;
-    }
-
-    void moveToState(@NonNull Fragment f) {
-        moveToState(f, mCurState);
-    }
-
-    /**
-     * Fragments that have been shown or hidden don't have their visibility changed or
-     * animations run during the {@link #showFragment(Fragment)} or {@link #hideFragment(Fragment)}
-     * calls. After fragments are brought to their final state in
-     * {@link #moveFragmentToExpectedState(Fragment)} the fragments that have been shown or
-     * hidden must have their visibility changed and their animations started here.
-     *
-     * @param fragment The fragment with mHiddenChanged = true that should change its View's
-     *                 visibility and start the show or hide animation.
-     */
-    private void completeShowHideFragment(@NonNull final Fragment fragment) {
-        if (fragment.mView != null) {
-            FragmentAnim.AnimationOrAnimator anim = FragmentAnim.loadAnimation(
-                    mHost.getContext(), fragment, !fragment.mHidden, fragment.getPopDirection());
-            if (anim != null && anim.animator != null) {
-                anim.animator.setTarget(fragment.mView);
-                if (fragment.mHidden) {
-                    if (fragment.isHideReplaced()) {
-                        fragment.setHideReplaced(false);
-                    } else {
-                        final ViewGroup container = fragment.mContainer;
-                        final View animatingView = fragment.mView;
-                        container.startViewTransition(animatingView);
-                        // Delay the actual hide operation until the animation finishes,
-                        // otherwise the fragment will just immediately disappear
-                        anim.animator.addListener(new AnimatorListenerAdapter() {
-                            @Override
-                            public void onAnimationEnd(Animator animation) {
-                                container.endViewTransition(animatingView);
-                                animation.removeListener(this);
-                                if (fragment.mView != null && fragment.mHidden) {
-                                    fragment.mView.setVisibility(View.GONE);
-                                }
-                            }
-                        });
-                    }
-                } else {
-                    fragment.mView.setVisibility(View.VISIBLE);
-                }
-                anim.animator.start();
-            } else {
-                if (anim != null) {
-                    fragment.mView.startAnimation(anim.animation);
-                    anim.animation.start();
-                }
-                final int visibility = fragment.mHidden && !fragment.isHideReplaced()
-                        ? View.GONE
-                        : View.VISIBLE;
-                fragment.mView.setVisibility(visibility);
-                if (fragment.isHideReplaced()) {
-                    fragment.setHideReplaced(false);
-                }
-            }
-        }
-        invalidateMenuForFragment(fragment);
-        fragment.mHiddenChanged = false;
-        fragment.onHiddenChanged(fragment.mHidden);
-    }
-
-    /**
-     * Moves a fragment to its expected final state or the fragment manager's state, depending
-     * on whether the fragment manager's state is raised properly.
-     *
-     * @param f The fragment to change.
-     */
-    void moveFragmentToExpectedState(@NonNull Fragment f) {
-        if (!mFragmentStore.containsActiveFragment(f.mWho)) {
-            if (isLoggingEnabled(Log.DEBUG)) {
-                Log.d(TAG, "Ignoring moving " + f + " to state " + mCurState
-                        + "since it is not added to " + this);
-            }
-            return;
-        }
-        moveToState(f);
-
-        if (f.mView != null) {
-            if (f.mIsNewlyAdded && f.mContainer != null) {
-                // Make it visible and run the animations
-                if (f.mPostponedAlpha > 0f) {
-                    f.mView.setAlpha(f.mPostponedAlpha);
-                }
-                f.mPostponedAlpha = 0f;
-                f.mIsNewlyAdded = false;
-                // run animations:
-                FragmentAnim.AnimationOrAnimator anim = FragmentAnim.loadAnimation(
-                        mHost.getContext(), f, true, f.getPopDirection());
-                if (anim != null) {
-                    if (anim.animation != null) {
-                        f.mView.startAnimation(anim.animation);
-                    } else {
-                        anim.animator.setTarget(f.mView);
-                        anim.animator.start();
-                    }
-                }
-            }
-        }
-        if (f.mHiddenChanged) {
-            completeShowHideFragment(f);
-        }
-    }
-
     /**
      * Changes the state of the fragment manager to {@code newState}. If the fragment manager
      * changes state or {@code always} is {@code true}, any fragments within it have their
@@ -1701,30 +1315,7 @@
         }
 
         mCurState = newState;
-
-        if (USE_STATE_MANAGER) {
-            mFragmentStore.moveToExpectedState();
-        } else {
-            // Must add them in the proper order. mActive fragments may be out of order
-            for (Fragment f : mFragmentStore.getFragments()) {
-                moveFragmentToExpectedState(f);
-            }
-
-            // Now iterate through all active fragments. These will include those that are removed
-            // and detached.
-            for (FragmentStateManager fragmentStateManager :
-                    mFragmentStore.getActiveFragmentStateManagers()) {
-                Fragment f = fragmentStateManager.getFragment();
-                if (!f.mIsNewlyAdded) {
-                    moveFragmentToExpectedState(f);
-                }
-                boolean beingRemoved = f.mRemoving && !f.isInBackStack();
-                if (beingRemoved) {
-                    mFragmentStore.makeInactive(fragmentStateManager);
-                }
-            }
-        }
-
+        mFragmentStore.moveToExpectedState();
         startPendingDeferredFragments();
 
         if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
@@ -1800,8 +1391,7 @@
     }
 
     /**
-     * Marks a fragment as hidden to be later animated in with
-     * {@link #completeShowHideFragment(Fragment)}.
+     * Marks a fragment as hidden to be later animated.
      *
      * @param fragment The fragment to be shown.
      */
@@ -1817,8 +1407,7 @@
     }
 
     /**
-     * Marks a fragment as shown to be later animated in with
-     * {@link #completeShowHideFragment(Fragment)}.
+     * Marks a fragment as shown to be later animated.
      *
      * @param fragment The fragment to be shown.
      */
@@ -1967,10 +1556,8 @@
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     void scheduleCommit() {
         synchronized (mPendingActions) {
-            boolean postponeReady =
-                    mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
             boolean pendingReady = mPendingActions.size() == 1;
-            if (postponeReady || pendingReady) {
+            if (pendingReady) {
                 mHost.getHandler().removeCallbacks(mExecCommit);
                 mHost.getHandler().post(mExecCommit);
                 updateOnBackPressedCallbackEnabled();
@@ -2013,12 +1600,6 @@
             mTmpRecords = new ArrayList<>();
             mTmpIsPop = new ArrayList<>();
         }
-        mExecutingActions = true;
-        try {
-            executePostponedTransaction(null, null);
-        } finally {
-            mExecutingActions = false;
-        }
     }
 
     void execSingleAction(@NonNull OpGenerator action, boolean allowStateLoss) {
@@ -2076,44 +1657,6 @@
     }
 
     /**
-     * Complete the execution of transactions that have previously been postponed, but are
-     * now ready.
-     */
-    private void executePostponedTransaction(@Nullable ArrayList<BackStackRecord> records,
-            @Nullable ArrayList<Boolean> isRecordPop) {
-        int numPostponed = mPostponedTransactions == null ? 0 : mPostponedTransactions.size();
-        for (int i = 0; i < numPostponed; i++) {
-            StartEnterTransitionListener listener = mPostponedTransactions.get(i);
-            if (records != null && !listener.mIsBack) {
-                int index = records.indexOf(listener.mRecord);
-                if (index != -1 && isRecordPop != null && isRecordPop.get(index)) {
-                    mPostponedTransactions.remove(i);
-                    i--;
-                    numPostponed--;
-                    listener.cancelTransaction();
-                    continue;
-                }
-            }
-            if (listener.isReady() || (records != null
-                    && listener.mRecord.interactsWith(records, 0, records.size()))) {
-                mPostponedTransactions.remove(i);
-                i--;
-                numPostponed--;
-                int index;
-                if (records != null && !listener.mIsBack
-                        && (index = records.indexOf(listener.mRecord)) != -1
-                        && isRecordPop != null
-                        && isRecordPop.get(index)) {
-                    // This is popping a postponed transaction
-                    listener.cancelTransaction();
-                } else {
-                    listener.completeTransaction();
-                }
-            }
-        }
-    }
-
-    /**
      * Remove redundant BackStackRecord operations and executes them. This method merges operations
      * of proximate records that allow reordering. See
      * {@link FragmentTransaction#setReorderingAllowed(boolean)}.
@@ -2137,9 +1680,6 @@
             throw new IllegalStateException("Internal error with the back stack records");
         }
 
-        // Force start of any postponed transactions that interact with scheduled transactions:
-        executePostponedTransaction(records, isRecordPop);
-
         final int numRecords = records.size();
         int startIndex = 0;
         for (int recordNum = 0; recordNum < numRecords; recordNum++) {
@@ -2201,24 +1741,18 @@
         mTmpAddedFragments.clear();
 
         if (!allowReordering && mCurState >= Fragment.CREATED) {
-            if (USE_STATE_MANAGER) {
-                // When reordering isn't allowed, we may be operating on Fragments that haven't
-                // been made active
-                for (int index = startIndex; index < endIndex; index++) {
-                    BackStackRecord record = records.get(index);
-                    for (FragmentTransaction.Op op : record.mOps) {
-                        Fragment fragment = op.mFragment;
-                        if (fragment != null && fragment.mFragmentManager != null) {
-                            FragmentStateManager fragmentStateManager =
-                                    createOrGetFragmentStateManager(fragment);
-                            mFragmentStore.makeActive(fragmentStateManager);
-                        }
+            // When reordering isn't allowed, we may be operating on Fragments that haven't
+            // been made active
+            for (int index = startIndex; index < endIndex; index++) {
+                BackStackRecord record = records.get(index);
+                for (FragmentTransaction.Op op : record.mOps) {
+                    Fragment fragment = op.mFragment;
+                    if (fragment != null && fragment.mFragmentManager != null) {
+                        FragmentStateManager fragmentStateManager =
+                                createOrGetFragmentStateManager(fragment);
+                        mFragmentStore.makeActive(fragmentStateManager);
                     }
                 }
-            } else {
-                FragmentTransition.startTransitions(mHost.getContext(), mContainer,
-                        records, isRecordPop, startIndex, endIndex,
-                        false, mFragmentTransitionCallback);
             }
         }
         executeOps(records, isRecordPop, startIndex, endIndex);
@@ -2228,70 +1762,49 @@
             record.runOnExecuteRunnables();
         }
 
-        if (USE_STATE_MANAGER) {
-            // The last operation determines the overall direction, this ensures that operations
-            // such as push, push, pop, push are correctly considered a push
-            boolean isPop = isRecordPop.get(endIndex - 1);
-            // Ensure that Fragments directly affected by operations
-            // are moved to their expected state in operation order
-            for (int index = startIndex; index < endIndex; index++) {
-                BackStackRecord record = records.get(index);
-                if (isPop) {
-                    // Pop operations get applied in reverse order
-                    for (int opIndex = record.mOps.size() - 1; opIndex >= 0; opIndex--) {
-                        FragmentTransaction.Op op = record.mOps.get(opIndex);
-                        Fragment fragment = op.mFragment;
-                        if (fragment != null) {
-                            FragmentStateManager fragmentStateManager =
-                                    createOrGetFragmentStateManager(fragment);
-                            fragmentStateManager.moveToExpectedState();
-                        }
-                    }
-                } else {
-                    for (FragmentTransaction.Op op : record.mOps) {
-                        Fragment fragment = op.mFragment;
-                        if (fragment != null) {
-                            FragmentStateManager fragmentStateManager =
-                                    createOrGetFragmentStateManager(fragment);
-                            fragmentStateManager.moveToExpectedState();
-                        }
+        // The last operation determines the overall direction, this ensures that operations
+        // such as push, push, pop, push are correctly considered a push
+        boolean isPop = isRecordPop.get(endIndex - 1);
+        // Ensure that Fragments directly affected by operations
+        // are moved to their expected state in operation order
+        for (int index = startIndex; index < endIndex; index++) {
+            BackStackRecord record = records.get(index);
+            if (isPop) {
+                // Pop operations get applied in reverse order
+                for (int opIndex = record.mOps.size() - 1; opIndex >= 0; opIndex--) {
+                    FragmentTransaction.Op op = record.mOps.get(opIndex);
+                    Fragment fragment = op.mFragment;
+                    if (fragment != null) {
+                        FragmentStateManager fragmentStateManager =
+                                createOrGetFragmentStateManager(fragment);
+                        fragmentStateManager.moveToExpectedState();
                     }
                 }
-
-            }
-            // And only then do we move all other fragments to the current state
-            moveToState(mCurState, true);
-            Set<SpecialEffectsController> changedControllers = collectChangedControllers(
-                    records, startIndex, endIndex);
-            for (SpecialEffectsController controller : changedControllers) {
-                controller.updateOperationDirection(isPop);
-                controller.markPostponedState();
-                controller.executePendingOperations();
-            }
-        } else {
-            int postponeIndex = endIndex;
-            if (allowReordering) {
-                ArraySet<Fragment> addedFragments = new ArraySet<>();
-                addAddedFragments(addedFragments);
-                postponeIndex = postponePostponableTransactions(records, isRecordPop,
-                        startIndex, endIndex, addedFragments);
-                makeRemovedFragmentsInvisible(addedFragments);
-            }
-
-            if (postponeIndex != startIndex && allowReordering) {
-                // need to run something now
-                if (mCurState >= Fragment.CREATED) {
-                    FragmentTransition.startTransitions(mHost.getContext(), mContainer,
-                            records, isRecordPop, startIndex,
-                            postponeIndex, true, mFragmentTransitionCallback);
+            } else {
+                for (FragmentTransaction.Op op : record.mOps) {
+                    Fragment fragment = op.mFragment;
+                    if (fragment != null) {
+                        FragmentStateManager fragmentStateManager =
+                                createOrGetFragmentStateManager(fragment);
+                        fragmentStateManager.moveToExpectedState();
+                    }
                 }
-                moveToState(mCurState, true);
             }
+
+        }
+        // And only then do we move all other fragments to the current state
+        moveToState(mCurState, true);
+        Set<SpecialEffectsController> changedControllers = collectChangedControllers(
+                records, startIndex, endIndex);
+        for (SpecialEffectsController controller : changedControllers) {
+            controller.updateOperationDirection(isPop);
+            controller.markPostponedState();
+            controller.executePendingOperations();
         }
 
         for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
             final BackStackRecord record = records.get(recordNum);
-            final boolean isPop = isRecordPop.get(recordNum);
+            isPop = isRecordPop.get(recordNum);
             if (isPop && record.mIndex >= 0) {
                 record.mIndex = -1;
             }
@@ -2322,132 +1835,6 @@
     }
 
     /**
-     * Any fragments that were removed because they have been postponed should have their views
-     * made invisible by setting their alpha to 0.
-     *
-     * @param fragments The fragments that were added during operation execution. Only the ones
-     *                  that are no longer added will have their alpha changed.
-     */
-    private void makeRemovedFragmentsInvisible(@NonNull ArraySet<Fragment> fragments) {
-        final int numAdded = fragments.size();
-        for (int i = 0; i < numAdded; i++) {
-            final Fragment fragment = fragments.valueAt(i);
-            if (!fragment.mAdded) {
-                final View view = fragment.requireView();
-                fragment.mPostponedAlpha = view.getAlpha();
-                view.setAlpha(0f);
-            }
-        }
-    }
-
-    /**
-     * Examine all transactions and determine which ones are marked as postponed. Those will
-     * have their operations rolled back and moved to the end of the record list (up to endIndex).
-     * It will also add the postponed transaction to the queue.
-     *
-     * @param records A list of BackStackRecords that should be checked.
-     * @param isRecordPop The direction that these records are being run.
-     * @param startIndex The index of the first record in <code>records</code> to be checked
-     * @param endIndex One more than the final record index in <code>records</code> to be checked.
-     * @return The index of the first postponed transaction or endIndex if no transaction was
-     * postponed.
-     */
-    private int postponePostponableTransactions(@NonNull ArrayList<BackStackRecord> records,
-            @NonNull ArrayList<Boolean> isRecordPop, int startIndex, int endIndex,
-            @NonNull ArraySet<Fragment> added) {
-        int postponeIndex = endIndex;
-        for (int i = endIndex - 1; i >= startIndex; i--) {
-            final BackStackRecord record = records.get(i);
-            final boolean isPop = isRecordPop.get(i);
-            boolean isPostponed = record.isPostponed()
-                    && !record.interactsWith(records, i + 1, endIndex);
-            if (isPostponed) {
-                if (mPostponedTransactions == null) {
-                    mPostponedTransactions = new ArrayList<>();
-                }
-                StartEnterTransitionListener listener =
-                        new StartEnterTransitionListener(record, isPop);
-                mPostponedTransactions.add(listener);
-                record.setOnStartPostponedListener(listener);
-
-                // roll back the transaction
-                if (isPop) {
-                    record.executeOps();
-                } else {
-                    record.executePopOps(false);
-                }
-
-                // move to the end
-                postponeIndex--;
-                if (i != postponeIndex) {
-                    records.remove(i);
-                    records.add(postponeIndex, record);
-                }
-
-                // different views may be visible now
-                addAddedFragments(added);
-            }
-        }
-        return postponeIndex;
-    }
-
-    /**
-     * When a postponed transaction is ready to be started, this completes the transaction,
-     * removing, hiding, or showing views as well as starting the animations and transitions.
-     * <p>
-     * {@code runtransitions} is set to false when the transaction postponement was interrupted
-     * abnormally -- normally by a new transaction being started that affects the postponed
-     * transaction.
-     *
-     * @param record The transaction to run
-     * @param isPop true if record is popping or false if it is adding
-     * @param runTransitions true if the fragment transition should be run or false otherwise.
-     * @param moveToState true if the state should be changed after executing the operations.
-     *                    This is false when the transaction is canceled when a postponed
-     *                    transaction is popped.
-     */
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    void completeExecute(@NonNull BackStackRecord record, boolean isPop, boolean runTransitions,
-            boolean moveToState) {
-        if (isPop) {
-            record.executePopOps(moveToState);
-        } else {
-            record.executeOps();
-        }
-        ArrayList<BackStackRecord> records = new ArrayList<>(1);
-        ArrayList<Boolean> isRecordPop = new ArrayList<>(1);
-        records.add(record);
-        isRecordPop.add(isPop);
-        if (runTransitions && mCurState >= Fragment.CREATED) {
-            FragmentTransition.startTransitions(mHost.getContext(), mContainer,
-                    records, isRecordPop, 0, 1, true,
-                    mFragmentTransitionCallback);
-        }
-        if (moveToState) {
-            moveToState(mCurState, true);
-        }
-
-        for (Fragment fragment : mFragmentStore.getActiveFragments()) {
-            // Allow added fragments to be removed during the pop since we aren't going
-            // to move them to the final state with moveToState(mCurState).
-            if (fragment != null) {
-                if (fragment.mView != null && fragment.mIsNewlyAdded
-                        && record.interactsWith(fragment.mContainerId)) {
-                    if (fragment.mPostponedAlpha > 0) {
-                        fragment.mView.setAlpha(fragment.mPostponedAlpha);
-                    }
-                    if (moveToState) {
-                        fragment.mPostponedAlpha = 0;
-                    } else {
-                        fragment.mPostponedAlpha = -1;
-                        fragment.mIsNewlyAdded = false;
-                    }
-                }
-            }
-        }
-    }
-
-    /**
      * Run the operations in the BackStackRecords, either to push or pop.
      *
      * @param records The list of records whose operations should be run.
@@ -2462,10 +1849,7 @@
             final boolean isPop = isRecordPop.get(i);
             if (isPop) {
                 record.bumpBackStackNesting(-1);
-                // Only execute the add operations at the end of
-                // all transactions.
-                boolean moveToState = i == (endIndex - 1);
-                record.executePopOps(moveToState);
+                record.executePopOps();
             } else {
                 record.bumpBackStackNesting(1);
                 record.executeOps();
@@ -2516,42 +1900,12 @@
     }
 
     /**
-     * Ensure that fragments that are added are moved to at least the CREATED state.
-     * Any newly-added Views are inserted into {@code added} so that the Transaction can be
-     * postponed with {@link Fragment#postponeEnterTransition()}. They will later be made
-     * invisible (by setting their alpha to 0) if they have been removed when postponed.
-     */
-    private void addAddedFragments(@NonNull ArraySet<Fragment> added) {
-        if (mCurState < Fragment.CREATED) {
-            return;
-        }
-        // We want to leave the fragment in the started state
-        final int state = Math.min(mCurState, Fragment.STARTED);
-        for (Fragment fragment : mFragmentStore.getFragments()) {
-            if (fragment.mState < state) {
-                moveToState(fragment, state);
-                if (fragment.mView != null && !fragment.mHidden && fragment.mIsNewlyAdded) {
-                    added.add(fragment);
-                }
-            }
-        }
-    }
-
-    /**
      * Starts all postponed transactions regardless of whether they are ready or not.
      */
     private void forcePostponedTransactions() {
-        if (USE_STATE_MANAGER) {
-            Set<SpecialEffectsController> controllers = collectAllSpecialEffectsController();
-            for (SpecialEffectsController controller : controllers) {
-                controller.forcePostponedExecutePendingOperations();
-            }
-        } else {
-            if (mPostponedTransactions != null) {
-                while (!mPostponedTransactions.isEmpty()) {
-                    mPostponedTransactions.remove(0).completeTransaction();
-                }
-            }
+        Set<SpecialEffectsController> controllers = collectAllSpecialEffectsController();
+        for (SpecialEffectsController controller : controllers) {
+            controller.forcePostponedExecutePendingOperations();
         }
     }
 
@@ -2560,18 +1914,9 @@
      * This is used prior to saving the state so that the correct state is saved.
      */
     private void endAnimatingAwayFragments() {
-        if (USE_STATE_MANAGER) {
-            Set<SpecialEffectsController> controllers = collectAllSpecialEffectsController();
-            for (SpecialEffectsController controller : controllers) {
-                controller.forceCompleteAllOperations();
-            }
-        } else {
-            if (!mExitAnimationCancellationSignals.isEmpty()) {
-                for (Fragment fragment: mExitAnimationCancellationSignals.keySet()) {
-                    cancelExitAnimation(fragment);
-                    moveToState(fragment);
-                }
-            }
+        Set<SpecialEffectsController> controllers = collectAllSpecialEffectsController();
+        for (SpecialEffectsController controller : controllers) {
+            controller.forceCompleteAllOperations();
         }
     }
 
@@ -3368,11 +2713,9 @@
             mExecutingActions = true;
             mFragmentStore.dispatchStateChange(nextState);
             moveToState(nextState, false);
-            if (USE_STATE_MANAGER) {
-                Set<SpecialEffectsController> controllers = collectAllSpecialEffectsController();
-                for (SpecialEffectsController controller : controllers) {
-                    controller.forceCompleteAllOperations();
-                }
+            Set<SpecialEffectsController> controllers = collectAllSpecialEffectsController();
+            for (SpecialEffectsController controller : controllers) {
+                controller.forceCompleteAllOperations();
             }
         } finally {
             mExecutingActions = false;
@@ -3835,80 +3178,6 @@
         }
     }
 
-    /**
-     * A listener for a postponed transaction. This waits until
-     * {@link Fragment#startPostponedEnterTransition()} is called or a transaction is started
-     * that interacts with this one, based on interactions with the fragment container.
-     */
-    static class StartEnterTransitionListener
-            implements Fragment.OnStartEnterTransitionListener {
-        final boolean mIsBack;
-        final BackStackRecord mRecord;
-        private int mNumPostponed;
-
-        StartEnterTransitionListener(@NonNull BackStackRecord record, boolean isBack) {
-            mIsBack = isBack;
-            mRecord = record;
-        }
-
-        /**
-         * Called from {@link Fragment#startPostponedEnterTransition()}, this decreases the
-         * number of Fragments that are postponed. This may cause the transaction to schedule
-         * to finish running and run transitions and animations.
-         */
-        @Override
-        public void onStartEnterTransition() {
-            mNumPostponed--;
-            if (mNumPostponed != 0) {
-                return;
-            }
-            mRecord.mManager.scheduleCommit();
-        }
-
-        /**
-         * Called from {@link Fragment#
-         * setOnStartEnterTransitionListener(Fragment.OnStartEnterTransitionListener)}, this
-         * increases the number of fragments that are postponed as part of this transaction.
-         */
-        @Override
-        public void startListening() {
-            mNumPostponed++;
-        }
-
-        /**
-         * @return true if there are no more postponed fragments as part of the transaction.
-         */
-        public boolean isReady() {
-            return mNumPostponed == 0;
-        }
-
-        /**
-         * Completes the transaction and start the animations and transitions. This may skip
-         * the transitions if this is called before all fragments have called
-         * {@link Fragment#startPostponedEnterTransition()}.
-         */
-        void completeTransaction() {
-            final boolean canceled;
-            canceled = mNumPostponed > 0;
-            FragmentManager manager = mRecord.mManager;
-            for (Fragment fragment : manager.getFragments()) {
-                fragment.setOnStartEnterTransitionListener(null);
-                if (canceled && fragment.isPostponed()) {
-                    fragment.startPostponedEnterTransition();
-                }
-            }
-            mRecord.mManager.completeExecute(mRecord, mIsBack, !canceled, true);
-        }
-
-        /**
-         * Cancels this transaction instead of completing it. That means that the state isn't
-         * changed, so the pop results in no change to the state.
-         */
-        void cancelTransaction() {
-            mRecord.mManager.completeExecute(mRecord, mIsBack, false, false);
-        }
-    }
-
     @SuppressLint("BanParcelableUsage")
     static class LaunchedFragmentInfo implements Parcelable {
         String mWho;
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
index 7d24a8c..5ebc691 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
@@ -199,7 +199,7 @@
             maxState = Math.min(maxState, Fragment.CREATED);
         }
         SpecialEffectsController.Operation.LifecycleImpact awaitingEffect = null;
-        if (FragmentManager.USE_STATE_MANAGER && mFragment.mContainer != null) {
+        if (mFragment.mContainer != null) {
             SpecialEffectsController controller = SpecialEffectsController.getOrCreateController(
                     mFragment.mContainer, mFragment.getParentFragmentManager());
             awaitingEffect = controller.getAwaitingCompletionLifecycleImpact(this);
@@ -338,7 +338,7 @@
                     }
                 }
             }
-            if (FragmentManager.USE_STATE_MANAGER && mFragment.mHiddenChanged) {
+            if (mFragment.mHiddenChanged) {
                 if (mFragment.mView != null && mFragment.mContainer != null) {
                     // Get the controller and enqueue the show/hide
                     SpecialEffectsController controller = SpecialEffectsController
@@ -436,10 +436,7 @@
             targetFragmentStateManager = null;
         }
         if (targetFragmentStateManager != null) {
-            if (FragmentManager.USE_STATE_MANAGER
-                    || targetFragmentStateManager.getFragment().mState < Fragment.CREATED) {
-                targetFragmentStateManager.moveToExpectedState();
-            }
+            targetFragmentStateManager.moveToExpectedState();
         }
         mFragment.mHost = mFragment.mFragmentManager.getHost();
         mFragment.mParentFragment = mFragment.mFragmentManager.getParent();
@@ -534,26 +531,19 @@
                     mFragment, mFragment.mView, mFragment.mSavedFragmentState, false);
             int postOnViewCreatedVisibility = mFragment.mView.getVisibility();
             float postOnViewCreatedAlpha = mFragment.mView.getAlpha();
-            if (FragmentManager.USE_STATE_MANAGER) {
-                mFragment.setPostOnViewCreatedAlpha(postOnViewCreatedAlpha);
-                if (mFragment.mContainer != null && postOnViewCreatedVisibility == View.VISIBLE) {
-                    // Save the focused view if one was set via requestFocus()
-                    View focusedView = mFragment.mView.findFocus();
-                    if (focusedView != null) {
-                        mFragment.setFocusedView(focusedView);
-                        if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                            Log.v(TAG, "requestFocus: Saved focused view " + focusedView
-                                    + " for Fragment " + mFragment);
-                        }
+            mFragment.setPostOnViewCreatedAlpha(postOnViewCreatedAlpha);
+            if (mFragment.mContainer != null && postOnViewCreatedVisibility == View.VISIBLE) {
+                // Save the focused view if one was set via requestFocus()
+                View focusedView = mFragment.mView.findFocus();
+                if (focusedView != null) {
+                    mFragment.setFocusedView(focusedView);
+                    if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                        Log.v(TAG, "requestFocus: Saved focused view " + focusedView
+                                + " for Fragment " + mFragment);
                     }
-                    // Set the view alpha to 0
-                    mFragment.mView.setAlpha(0f);
                 }
-            } else {
-                // Only animate the view if it is visible. This is done after
-                // dispatchOnFragmentViewCreated in case visibility is changed
-                mFragment.mIsNewlyAdded = (postOnViewCreatedVisibility == View.VISIBLE)
-                        && mFragment.mContainer != null;
+                // Set the view alpha to 0
+                mFragment.mView.setAlpha(0f);
             }
         }
         mFragment.mState = Fragment.VIEW_CREATED;
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManagerControl.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManagerControl.java
deleted file mode 100644
index de8bdbe..0000000
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManagerControl.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.fragment.app;
-
-import androidx.annotation.experimental.Experimental;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * @see FragmentManager#enableNewStateManager(boolean)
- */
-@Retention(RetentionPolicy.CLASS)
-@Target({ElementType.METHOD})
-@Experimental(level = Experimental.Level.WARNING)
-public @interface FragmentStateManagerControl {
-}
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransition.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransition.java
index c4729ca..6ee0e076 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransition.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransition.java
@@ -15,65 +15,21 @@
  */
 package androidx.fragment.app;
 
-import android.content.Context;
-import android.graphics.Rect;
 import android.os.Build;
-import android.util.SparseArray;
 import android.view.View;
-import android.view.ViewGroup;
 
 import androidx.annotation.NonNull;
 import androidx.collection.ArrayMap;
 import androidx.core.app.SharedElementCallback;
-import androidx.core.os.CancellationSignal;
-import androidx.core.view.OneShotPreDrawListener;
-import androidx.core.view.ViewCompat;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import java.util.Map;
 
 /**
- * Contains the Fragment Transition functionality for both ordered and reordered
- * Fragment Transactions. With reordered fragment transactions, all Views have been
- * added to the View hierarchy prior to calling startTransitions. With ordered
- * fragment transactions, Views will be removed and added after calling startTransitions.
+ * Contains the Fragment Transition functionality.
  */
 class FragmentTransition {
-    /**
-     * The inverse of all BackStackRecord operation commands. This assumes that
-     * REPLACE operations have already been replaced by add/remove operations.
-     */
-    private static final int[] INVERSE_OPS = {
-            BackStackRecord.OP_NULL,                // inverse of OP_NULL (error)
-            BackStackRecord.OP_REMOVE,              // inverse of OP_ADD
-            BackStackRecord.OP_NULL,                // inverse of OP_REPLACE (error)
-            BackStackRecord.OP_ADD,                 // inverse of OP_REMOVE
-            BackStackRecord.OP_SHOW,                // inverse of OP_HIDE
-            BackStackRecord.OP_HIDE,                // inverse of OP_SHOW
-            BackStackRecord.OP_ATTACH,              // inverse of OP_DETACH
-            BackStackRecord.OP_DETACH,              // inverse of OP_ATTACH
-            BackStackRecord.OP_UNSET_PRIMARY_NAV,   // inverse of OP_SET_PRIMARY_NAV
-            BackStackRecord.OP_SET_PRIMARY_NAV,     // inverse of OP_UNSET_PRIMARY_NAV
-            BackStackRecord.OP_SET_MAX_LIFECYCLE
-    };
-
-    /**
-     * Interface to watch Fragment Transitions
-     */
-    interface Callback {
-        /**
-         * Called whenever an transition started
-         */
-        void onStart(@NonNull Fragment fragment, @NonNull CancellationSignal signal);
-
-        /**
-         * Called whenever an transition is complete
-         */
-        void onComplete(@NonNull Fragment fragment, @NonNull CancellationSignal signal);
-    }
-
     static final FragmentTransitionImpl PLATFORM_IMPL = Build.VERSION.SDK_INT >= 21
             ? new FragmentTransitionCompat21()
             : null;
@@ -93,847 +49,6 @@
     }
 
     /**
-     * The main entry point for Fragment Transitions, this starts the transitions
-     * set on the leaving Fragment's {@link Fragment#getExitTransition()}, the
-     * entering Fragment's {@link Fragment#getEnterTransition()} and
-     * {@link Fragment#getSharedElementEnterTransition()}. When popping,
-     * the leaving Fragment's {@link Fragment#getReturnTransition()} and
-     * {@link Fragment#getSharedElementReturnTransition()} and the entering
-     * {@link Fragment#getReenterTransition()} will be run.
-     * <p>
-     * With reordered Fragment Transitions, all Views have been added to the
-     * View hierarchy prior to calling this method. The incoming Fragment's Views
-     * will be INVISIBLE. With ordered Fragment Transitions, this method
-     * is called before any change has been made to the hierarchy. That means
-     * that the added Fragments have not created their Views yet and the hierarchy
-     * is unknown.
-     *
-     * @param context The hosting context
-     * @param fragmentContainer the FragmentContainer for finding the container for each Fragment
-     * @param records The list of transactions being executed.
-     * @param isRecordPop For each transaction, whether it is a pop transaction or not.
-     * @param startIndex The first index into records and isRecordPop to execute as
-     *                   part of this transition.
-     * @param endIndex One past the last index into records and isRecordPop to execute
-     *                 as part of this transition.
-     * @param isReordered true if this is a reordered transaction, meaning that the
-     *                    Views of incoming fragments have been added. false if the
-     *                    transaction has yet to be run and Views haven't been created.
-     */
-    static void startTransitions(@NonNull Context context,
-            @NonNull FragmentContainer fragmentContainer,
-            ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
-            int startIndex, int endIndex, boolean isReordered, Callback callback) {
-        SparseArray<FragmentContainerTransition> transitioningFragments =
-                new SparseArray<>();
-        for (int i = startIndex; i < endIndex; i++) {
-            final BackStackRecord record = records.get(i);
-            final boolean isPop = isRecordPop.get(i);
-            if (isPop) {
-                calculatePopFragments(record, transitioningFragments, isReordered);
-            } else {
-                calculateFragments(record, transitioningFragments, isReordered);
-            }
-        }
-
-        if (transitioningFragments.size() != 0) {
-            final View nonExistentView = new View(context);
-            final int numContainers = transitioningFragments.size();
-            for (int i = 0; i < numContainers; i++) {
-                int containerId = transitioningFragments.keyAt(i);
-                ArrayMap<String, String> nameOverrides = calculateNameOverrides(containerId,
-                        records, isRecordPop, startIndex, endIndex);
-
-                FragmentContainerTransition containerTransition =
-                        transitioningFragments.valueAt(i);
-
-                if (fragmentContainer.onHasView()) {
-                    ViewGroup container = (ViewGroup) fragmentContainer.onFindViewById(
-                            containerId);
-                    if (container == null) {
-                        // No container means no transitions
-                        continue;
-                    }
-                    if (isReordered) {
-                        configureTransitionsReordered(container,
-                                containerTransition, nonExistentView, nameOverrides, callback);
-                    } else {
-                        configureTransitionsOrdered(container,
-                                containerTransition, nonExistentView, nameOverrides, callback);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Iterates through the transactions that affect a given fragment container
-     * and tracks the shared element names across transactions. This is most useful
-     * in pop transactions where the names of shared elements are known.
-     *
-     * @param containerId The container ID that is executing the transition.
-     * @param records The list of transactions being executed.
-     * @param isRecordPop For each transaction, whether it is a pop transaction or not.
-     * @param startIndex The first index into records and isRecordPop to execute as
-     *                   part of this transition.
-     * @param endIndex One past the last index into records and isRecordPop to execute
-     *                 as part of this transition.
-     * @return A map from the initial shared element name to the final shared element name
-     * before any onMapSharedElements is run.
-     */
-    private static ArrayMap<String, String> calculateNameOverrides(int containerId,
-            ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
-            int startIndex, int endIndex) {
-        ArrayMap<String, String> nameOverrides = new ArrayMap<>();
-        for (int recordNum = endIndex - 1; recordNum >= startIndex; recordNum--) {
-            final BackStackRecord record = records.get(recordNum);
-            if (!record.interactsWith(containerId)) {
-                continue;
-            }
-            final boolean isPop = isRecordPop.get(recordNum);
-            if (record.mSharedElementSourceNames != null) {
-                final int numSharedElements = record.mSharedElementSourceNames.size();
-                final ArrayList<String> sources;
-                final ArrayList<String> targets;
-                if (isPop) {
-                    targets = record.mSharedElementSourceNames;
-                    sources = record.mSharedElementTargetNames;
-                } else {
-                    sources = record.mSharedElementSourceNames;
-                    targets = record.mSharedElementTargetNames;
-                }
-                for (int i = 0; i < numSharedElements; i++) {
-                    String sourceName = sources.get(i);
-                    String targetName = targets.get(i);
-                    String previousTarget = nameOverrides.remove(targetName);
-                    if (previousTarget != null) {
-                        nameOverrides.put(sourceName, previousTarget);
-                    } else {
-                        nameOverrides.put(sourceName, targetName);
-                    }
-                }
-            }
-        }
-        return nameOverrides;
-    }
-
-    /**
-     * Configures a transition for a single fragment container for which the transaction was
-     * reordered. That means that all Fragment Views have been added and incoming fragment
-     * Views are marked invisible.
-     *
-     * @param container the container that the transitioning fragments are within
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @param nonExistentView A View that does not exist in the hierarchy. This is used to
-     *                        prevent transitions from acting on other Views when there is no
-     *                        other target.
-     * @param nameOverrides A map of the shared element names from the starting fragment to
-     *                      the final fragment's Views as given in
-     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
-     */
-    private static void configureTransitionsReordered(@NonNull ViewGroup container,
-            FragmentContainerTransition fragments,
-            View nonExistentView, ArrayMap<String, String> nameOverrides, final Callback callback) {
-        final Fragment inFragment = fragments.lastIn;
-        final Fragment outFragment = fragments.firstOut;
-        final FragmentTransitionImpl impl = chooseImpl(outFragment, inFragment);
-        if (impl == null) {
-            return;
-        }
-        final boolean inIsPop = fragments.lastInIsPop;
-        final boolean outIsPop = fragments.firstOutIsPop;
-
-        ArrayList<View> sharedElementsIn = new ArrayList<>();
-        ArrayList<View> sharedElementsOut = new ArrayList<>();
-        Object enterTransition = getEnterTransition(impl, inFragment, inIsPop);
-        Object exitTransition = getExitTransition(impl, outFragment, outIsPop);
-
-        Object sharedElementTransition = configureSharedElementsReordered(impl, container,
-                nonExistentView, nameOverrides, fragments, sharedElementsOut, sharedElementsIn,
-                enterTransition, exitTransition);
-
-        if (enterTransition == null && sharedElementTransition == null
-                && exitTransition == null) {
-            return; // no transitions!
-        }
-
-        ArrayList<View> exitingViews = configureEnteringExitingViews(impl, exitTransition,
-                outFragment, sharedElementsOut, nonExistentView);
-
-        ArrayList<View> enteringViews = configureEnteringExitingViews(impl, enterTransition,
-                inFragment, sharedElementsIn, nonExistentView);
-
-        setViewVisibility(enteringViews, View.INVISIBLE);
-
-        Object transition = mergeTransitions(impl, enterTransition, exitTransition,
-                sharedElementTransition, inFragment, inIsPop);
-
-        if (outFragment != null && exitingViews != null
-                && (exitingViews.size() > 0 || sharedElementsOut.size() > 0)) {
-            final CancellationSignal signal = new CancellationSignal();
-            callback.onStart(outFragment, signal);
-            impl.setListenerForTransitionEnd(outFragment, transition, signal, new Runnable() {
-                @Override
-                public void run() {
-                    callback.onComplete(outFragment, signal);
-                }
-            });
-        }
-
-        if (transition != null) {
-            replaceHide(impl, exitTransition, outFragment, exitingViews);
-            ArrayList<String> inNames =
-                    impl.prepareSetNameOverridesReordered(sharedElementsIn);
-            impl.scheduleRemoveTargets(transition,
-                    enterTransition, enteringViews, exitTransition, exitingViews,
-                    sharedElementTransition, sharedElementsIn);
-            impl.beginDelayedTransition(container, transition);
-            impl.setNameOverridesReordered(container, sharedElementsOut,
-                    sharedElementsIn, inNames, nameOverrides);
-            setViewVisibility(enteringViews, View.VISIBLE);
-            impl.swapSharedElementTargets(sharedElementTransition,
-                    sharedElementsOut, sharedElementsIn);
-        }
-    }
-
-    /**
-     * Replace hide operations with visibility changes on the exiting views. Instead of making
-     * the entire fragment's view GONE, make each exiting view INVISIBLE. At the end of the
-     * transition, make the fragment's view GONE.
-     */
-    private static void replaceHide(FragmentTransitionImpl impl,
-            Object exitTransition, Fragment exitingFragment,
-            final ArrayList<View> exitingViews) {
-        if (exitingFragment != null && exitTransition != null && exitingFragment.mAdded
-                && exitingFragment.mHidden && exitingFragment.mHiddenChanged) {
-            exitingFragment.setHideReplaced(true);
-            impl.scheduleHideFragmentView(exitTransition,
-                    exitingFragment.getView(), exitingViews);
-            /* This is required to indicate to the TransitionManager the desired end state of the
-             scene when a hide is used. In the replace case, the exiting Fragment's view is
-             removed from the sceneRoot during the delay, and the TransitionManager is able to
-             calculate the difference between the two switching views. Because we can have
-             exiting child views with transitions, we cannot just mark the entire exiting
-             Fragment view as INVISIBLE or the TransitionManager will not consider the views
-             individually.
-
-            This OneShotPreDrawListener gets fired before the delayed start of the Transition and
-             changes the visibility of any exiting child views that *ARE NOT* shared element
-             transitions. The TransitionManager then properly considers exiting views and marks
-             them as disappearing, applying a transition and a listener to take proper actions
-             once the transition is complete.
-            */
-
-            final ViewGroup container = exitingFragment.mContainer;
-            OneShotPreDrawListener.add(container, new Runnable() {
-                @Override
-                public void run() {
-                    setViewVisibility(exitingViews, View.INVISIBLE);
-                }
-            });
-        }
-    }
-
-    /**
-     * Configures a transition for a single fragment container for which the transaction was
-     * ordered. That means that the transaction has not been executed yet, so incoming
-     * Views are not yet known.
-     *
-     * @param container the container that the transitioning fragments are within
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @param nonExistentView A View that does not exist in the hierarchy. This is used to
-     *                        prevent transitions from acting on other Views when there is no
-     *                        other target.
-     * @param nameOverrides A map of the shared element names from the starting fragment to
-     *                      the final fragment's Views as given in
-     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
-     */
-    private static void configureTransitionsOrdered(@NonNull ViewGroup container,
-            FragmentContainerTransition fragments,
-            View nonExistentView, ArrayMap<String, String> nameOverrides, final Callback callback) {
-        final Fragment inFragment = fragments.lastIn;
-        final Fragment outFragment = fragments.firstOut;
-        final FragmentTransitionImpl impl = chooseImpl(outFragment, inFragment);
-        if (impl == null) {
-            return;
-        }
-        final boolean inIsPop = fragments.lastInIsPop;
-        final boolean outIsPop = fragments.firstOutIsPop;
-
-        Object enterTransition = getEnterTransition(impl, inFragment, inIsPop);
-        Object exitTransition = getExitTransition(impl, outFragment, outIsPop);
-
-        ArrayList<View> sharedElementsOut = new ArrayList<>();
-        ArrayList<View> sharedElementsIn = new ArrayList<>();
-
-        Object sharedElementTransition = configureSharedElementsOrdered(impl, container,
-                nonExistentView, nameOverrides, fragments, sharedElementsOut, sharedElementsIn,
-                enterTransition, exitTransition);
-
-        if (enterTransition == null && sharedElementTransition == null
-                && exitTransition == null) {
-            return; // no transitions!
-        }
-
-        ArrayList<View> exitingViews = configureEnteringExitingViews(impl, exitTransition,
-                outFragment, sharedElementsOut, nonExistentView);
-
-        if (exitingViews == null || exitingViews.isEmpty()) {
-            exitTransition = null;
-        }
-
-        // Ensure the entering transition doesn't target anything until the views are made
-        // visible
-        impl.addTarget(enterTransition, nonExistentView);
-
-        Object transition = mergeTransitions(impl, enterTransition, exitTransition,
-                sharedElementTransition, inFragment, fragments.lastInIsPop);
-
-        if (outFragment != null && exitingViews != null
-                && (exitingViews.size() > 0 || sharedElementsOut.size() > 0)) {
-            final CancellationSignal signal = new CancellationSignal();
-            callback.onStart(outFragment, signal);
-            impl.setListenerForTransitionEnd(outFragment, transition, signal, new Runnable() {
-                @Override
-                public void run() {
-                    callback.onComplete(outFragment, signal);
-                }
-            });
-        }
-
-        if (transition != null) {
-            final ArrayList<View> enteringViews = new ArrayList<>();
-            impl.scheduleRemoveTargets(transition,
-                    enterTransition, enteringViews, exitTransition, exitingViews,
-                    sharedElementTransition, sharedElementsIn);
-            scheduleTargetChange(impl, container, inFragment, nonExistentView, sharedElementsIn,
-                    enterTransition, enteringViews, exitTransition, exitingViews);
-            impl.setNameOverridesOrdered(container, sharedElementsIn, nameOverrides);
-
-            impl.beginDelayedTransition(container, transition);
-            impl.scheduleNameReset(container, sharedElementsIn, nameOverrides);
-        }
-    }
-
-    /**
-     * This method is used for fragment transitions for ordrered transactions to change the
-     * enter and exit transition targets after the call to
-     * {@link FragmentTransitionCompat21#beginDelayedTransition(ViewGroup, Object)}. The exit
-     * transition must ensure that it does not target any Views and the enter transition must start
-     * targeting the Views of the incoming Fragment.
-     *
-     * @param sceneRoot The fragment container View
-     * @param inFragment The last fragment that is entering
-     * @param nonExistentView A view that does not exist in the hierarchy that is used as a
-     *                        transition target to ensure no View is targeted.
-     * @param sharedElementsIn The shared element Views of the incoming fragment
-     * @param enterTransition The enter transition of the incoming fragment
-     * @param enteringViews The entering Views of the incoming fragment
-     * @param exitTransition The exit transition of the outgoing fragment
-     * @param exitingViews The exiting views of the outgoing fragment
-     */
-    private static void scheduleTargetChange(final FragmentTransitionImpl impl,
-            final ViewGroup sceneRoot,
-            final Fragment inFragment, final View nonExistentView,
-            final ArrayList<View> sharedElementsIn,
-            final Object enterTransition, final ArrayList<View> enteringViews,
-            final Object exitTransition, final ArrayList<View> exitingViews) {
-        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                if (enterTransition != null) {
-                    impl.removeTarget(enterTransition,
-                            nonExistentView);
-                    ArrayList<View> views = configureEnteringExitingViews(impl,
-                            enterTransition, inFragment, sharedElementsIn, nonExistentView);
-                    enteringViews.addAll(views);
-                }
-
-                if (exitingViews != null) {
-                    if (exitTransition != null) {
-                        ArrayList<View> tempExiting = new ArrayList<>();
-                        tempExiting.add(nonExistentView);
-                        impl.replaceTargets(exitTransition, exitingViews,
-                                tempExiting);
-                    }
-                    exitingViews.clear();
-                    exitingViews.add(nonExistentView);
-                }
-            }
-        });
-    }
-
-    /**
-     * Chooses the appropriate implementation depending on the Transition instances hold by the
-     * Fragments.
-     */
-    private static FragmentTransitionImpl chooseImpl(Fragment outFragment, Fragment inFragment) {
-        // Collect all transition instances
-        final ArrayList<Object> transitions = new ArrayList<>();
-        if (outFragment != null) {
-            final Object exitTransition = outFragment.getExitTransition();
-            if (exitTransition != null) {
-                transitions.add(exitTransition);
-            }
-            final Object returnTransition = outFragment.getReturnTransition();
-            if (returnTransition != null) {
-                transitions.add(returnTransition);
-            }
-            final Object sharedReturnTransition = outFragment.getSharedElementReturnTransition();
-            if (sharedReturnTransition != null) {
-                transitions.add(sharedReturnTransition);
-            }
-        }
-        if (inFragment != null) {
-            final Object enterTransition = inFragment.getEnterTransition();
-            if (enterTransition != null) {
-                transitions.add(enterTransition);
-            }
-            final Object reenterTransition = inFragment.getReenterTransition();
-            if (reenterTransition != null) {
-                transitions.add(reenterTransition);
-            }
-            final Object sharedEnterTransition = inFragment.getSharedElementEnterTransition();
-            if (sharedEnterTransition != null) {
-                transitions.add(sharedEnterTransition);
-            }
-        }
-        if (transitions.isEmpty()) {
-            return null; // No transition to run
-        }
-        // Pick the implementation that can handle all the transitions
-        if (PLATFORM_IMPL != null && canHandleAll(PLATFORM_IMPL, transitions)) {
-            return PLATFORM_IMPL;
-        }
-        if (SUPPORT_IMPL != null && canHandleAll(SUPPORT_IMPL, transitions)) {
-            return SUPPORT_IMPL;
-        }
-        if (PLATFORM_IMPL != null || SUPPORT_IMPL != null) {
-            throw new IllegalArgumentException("Invalid Transition types");
-        }
-        return null;
-    }
-
-    private static boolean canHandleAll(FragmentTransitionImpl impl, List<Object> transitions) {
-        for (int i = 0, size = transitions.size(); i < size; i++) {
-            if (!impl.canHandle(transitions.get(i))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Returns a TransitionSet containing the shared element transition. The wrapping TransitionSet
-     * targets all shared elements to ensure that no other Views are targeted. The shared element
-     * transition can then target any or all shared elements without worrying about accidentally
-     * targeting entering or exiting Views.
-     *
-     * @param inFragment The incoming fragment
-     * @param outFragment the outgoing fragment
-     * @param isPop True if this is a pop transaction or false if it is a normal (add) transaction.
-     * @return A TransitionSet wrapping the shared element transition or null if no such transition
-     * exists.
-     */
-    private static Object getSharedElementTransition(FragmentTransitionImpl impl,
-            Fragment inFragment, Fragment outFragment, boolean isPop) {
-        if (inFragment == null || outFragment == null) {
-            return null;
-        }
-        Object transition = impl.cloneTransition(isPop
-                ? outFragment.getSharedElementReturnTransition()
-                : inFragment.getSharedElementEnterTransition());
-        return impl.wrapTransitionInSet(transition);
-    }
-
-    /**
-     * Returns a clone of the enter transition or null if no such transition exists.
-     */
-    private static Object getEnterTransition(FragmentTransitionImpl impl,
-            Fragment inFragment, boolean isPop) {
-        if (inFragment == null) {
-            return null;
-        }
-        return impl.cloneTransition(isPop
-                ? inFragment.getReenterTransition()
-                : inFragment.getEnterTransition());
-    }
-
-    /**
-     * Returns a clone of the exit transition or null if no such transition exists.
-     */
-    private static Object getExitTransition(FragmentTransitionImpl impl,
-            Fragment outFragment, boolean isPop) {
-        if (outFragment == null) {
-            return null;
-        }
-        return impl.cloneTransition(isPop
-                ? outFragment.getReturnTransition()
-                : outFragment.getExitTransition());
-    }
-
-    /**
-     * Configures the shared elements of a reordered fragment transaction's transition.
-     * This retrieves the shared elements of the outgoing and incoming fragments, maps the
-     * views, and sets up the epicenter on the transitions.
-     * <p>
-     * The epicenter of exit and shared element transitions is the first shared element
-     * in the outgoing fragment. The epicenter of the entering transition is the first shared
-     * element in the incoming fragment.
-     *
-     * @param sceneRoot The fragment container View
-     * @param nonExistentView A View that does not exist in the hierarchy. This is used to
-     *                        prevent transitions from acting on other Views when there is no
-     *                        other target.
-     * @param nameOverrides A map of the shared element names from the starting fragment to
-     *                      the final fragment's Views as given in
-     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @param sharedElementsOut A list modified to contain the shared elements in the outgoing
-     *                          fragment
-     * @param sharedElementsIn A list modified to contain the shared elements in the incoming
-     *                         fragment
-     * @param enterTransition The transition used for entering Views, modified by applying the
-     *                        epicenter
-     * @param exitTransition The transition used for exiting Views, modified by applying the
-     *                       epicenter
-     * @return The shared element transition or null if no shared elements exist
-     */
-    private static Object configureSharedElementsReordered(final FragmentTransitionImpl impl,
-            final ViewGroup sceneRoot,
-            final View nonExistentView, final ArrayMap<String, String> nameOverrides,
-            final FragmentContainerTransition fragments,
-            final ArrayList<View> sharedElementsOut,
-            final ArrayList<View> sharedElementsIn,
-            final Object enterTransition, final Object exitTransition) {
-        final Fragment inFragment = fragments.lastIn;
-        final Fragment outFragment = fragments.firstOut;
-        if (inFragment != null) {
-            inFragment.requireView().setVisibility(View.VISIBLE);
-        }
-        if (inFragment == null || outFragment == null) {
-            return null; // no shared element without a fragment
-        }
-
-        final boolean inIsPop = fragments.lastInIsPop;
-        Object sharedElementTransition = nameOverrides.isEmpty() ? null
-                : getSharedElementTransition(impl, inFragment, outFragment, inIsPop);
-
-        final ArrayMap<String, View> outSharedElements = captureOutSharedElements(impl,
-                nameOverrides, sharedElementTransition, fragments);
-
-        final ArrayMap<String, View> inSharedElements = captureInSharedElements(impl,
-                nameOverrides, sharedElementTransition, fragments);
-
-        if (nameOverrides.isEmpty()) {
-            sharedElementTransition = null;
-            if (outSharedElements != null) {
-                outSharedElements.clear();
-            }
-            if (inSharedElements != null) {
-                inSharedElements.clear();
-            }
-        } else {
-            addSharedElementsWithMatchingNames(sharedElementsOut, outSharedElements,
-                    nameOverrides.keySet());
-            addSharedElementsWithMatchingNames(sharedElementsIn, inSharedElements,
-                    nameOverrides.values());
-        }
-
-        if (enterTransition == null && exitTransition == null && sharedElementTransition == null) {
-            // don't call onSharedElementStart/End since there is no transition
-            return null;
-        }
-
-        callSharedElementStartEnd(inFragment, outFragment, inIsPop, outSharedElements, true);
-
-        final Rect epicenter;
-        final View epicenterView;
-        if (sharedElementTransition != null) {
-            sharedElementsIn.add(nonExistentView);
-            impl.setSharedElementTargets(sharedElementTransition,
-                    nonExistentView, sharedElementsOut);
-            final boolean outIsPop = fragments.firstOutIsPop;
-            final BackStackRecord outTransaction = fragments.firstOutTransaction;
-            setOutEpicenter(impl, sharedElementTransition, exitTransition, outSharedElements,
-                    outIsPop, outTransaction);
-            epicenter = new Rect();
-            epicenterView = getInEpicenterView(inSharedElements, fragments,
-                    enterTransition, inIsPop);
-            if (epicenterView != null) {
-                impl.setEpicenter(enterTransition, epicenter);
-            }
-        } else {
-            epicenter = null;
-            epicenterView = null;
-        }
-
-        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                callSharedElementStartEnd(inFragment, outFragment, inIsPop,
-                        inSharedElements, false);
-                if (epicenterView != null) {
-                    impl.getBoundsOnScreen(epicenterView, epicenter);
-                }
-            }
-        });
-        return sharedElementTransition;
-    }
-
-    /**
-     * Add Views from sharedElements into views that have the transitionName in the
-     * nameOverridesSet.
-     *
-     * @param views               Views list to add shared elements to
-     * @param sharedElements      List of shared elements
-     * @param nameOverridesSet    The transition names for all views to be copied from
-     *                            sharedElements to views.
-     */
-    private static void addSharedElementsWithMatchingNames(ArrayList<View> views,
-            ArrayMap<String, View> sharedElements, Collection<String> nameOverridesSet) {
-        for (int i = sharedElements.size() - 1; i >= 0; i--) {
-            View view = sharedElements.valueAt(i);
-            if (nameOverridesSet.contains(ViewCompat.getTransitionName(view))) {
-                views.add(view);
-            }
-        }
-    }
-
-    /**
-     * Configures the shared elements of an ordered fragment transaction's transition.
-     * This retrieves the shared elements of the incoming fragments, and schedules capturing
-     * the incoming fragment's shared elements. It also maps the views, and sets up the epicenter
-     * on the transitions.
-     * <p>
-     * The epicenter of exit and shared element transitions is the first shared element
-     * in the outgoing fragment. The epicenter of the entering transition is the first shared
-     * element in the incoming fragment.
-     *
-     * @param sceneRoot The fragment container View
-     * @param nonExistentView A View that does not exist in the hierarchy. This is used to
-     *                        prevent transitions from acting on other Views when there is no
-     *                        other target.
-     * @param nameOverrides A map of the shared element names from the starting fragment to
-     *                      the final fragment's Views as given in
-     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @param sharedElementsOut A list modified to contain the shared elements in the outgoing
-     *                          fragment
-     * @param sharedElementsIn A list modified to contain the shared elements in the incoming
-     *                         fragment
-     * @param enterTransition The transition used for entering Views, modified by applying the
-     *                        epicenter
-     * @param exitTransition The transition used for exiting Views, modified by applying the
-     *                       epicenter
-     * @return The shared element transition or null if no shared elements exist
-     */
-    private static Object configureSharedElementsOrdered(final FragmentTransitionImpl impl,
-            final ViewGroup sceneRoot,
-            final View nonExistentView, final ArrayMap<String, String> nameOverrides,
-            final FragmentContainerTransition fragments,
-            final ArrayList<View> sharedElementsOut,
-            final ArrayList<View> sharedElementsIn,
-            final Object enterTransition, final Object exitTransition) {
-        final Fragment inFragment = fragments.lastIn;
-        final Fragment outFragment = fragments.firstOut;
-
-        if (inFragment == null || outFragment == null) {
-            return null; // no transition
-        }
-
-        final boolean inIsPop = fragments.lastInIsPop;
-        Object sharedElementTransition = nameOverrides.isEmpty() ? null
-                : getSharedElementTransition(impl, inFragment, outFragment, inIsPop);
-
-        ArrayMap<String, View> outSharedElements = captureOutSharedElements(impl, nameOverrides,
-                sharedElementTransition, fragments);
-
-        if (nameOverrides.isEmpty()) {
-            sharedElementTransition = null;
-        } else {
-            sharedElementsOut.addAll(outSharedElements.values());
-        }
-
-        if (enterTransition == null && exitTransition == null && sharedElementTransition == null) {
-            // don't call onSharedElementStart/End since there is no transition
-            return null;
-        }
-
-        callSharedElementStartEnd(inFragment, outFragment, inIsPop, outSharedElements, true);
-
-        final Rect inEpicenter;
-        if (sharedElementTransition != null) {
-            inEpicenter = new Rect();
-            impl.setSharedElementTargets(sharedElementTransition,
-                    nonExistentView, sharedElementsOut);
-            final boolean outIsPop = fragments.firstOutIsPop;
-            final BackStackRecord outTransaction = fragments.firstOutTransaction;
-            setOutEpicenter(impl, sharedElementTransition, exitTransition, outSharedElements,
-                    outIsPop, outTransaction);
-            if (enterTransition != null) {
-                impl.setEpicenter(enterTransition, inEpicenter);
-            }
-        } else {
-            inEpicenter = null;
-        }
-
-
-        final Object finalSharedElementTransition = sharedElementTransition;
-        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                ArrayMap<String, View> inSharedElements = captureInSharedElements(impl,
-                        nameOverrides, finalSharedElementTransition, fragments);
-
-                if (inSharedElements != null) {
-                    sharedElementsIn.addAll(inSharedElements.values());
-                    sharedElementsIn.add(nonExistentView);
-                }
-
-                callSharedElementStartEnd(inFragment, outFragment, inIsPop,
-                        inSharedElements, false);
-                if (finalSharedElementTransition != null) {
-                    impl.swapSharedElementTargets(
-                            finalSharedElementTransition, sharedElementsOut,
-                            sharedElementsIn);
-
-                    final View inEpicenterView = getInEpicenterView(inSharedElements,
-                            fragments, enterTransition, inIsPop);
-                    if (inEpicenterView != null) {
-                        impl.getBoundsOnScreen(inEpicenterView,
-                                inEpicenter);
-                    }
-                }
-            }
-        });
-
-        return sharedElementTransition;
-    }
-
-    /**
-     * Finds the shared elements in the outgoing fragment. It also calls
-     * {@link SharedElementCallback#onMapSharedElements(List, Map)} to allow more control
-     * of the shared element mapping. {@code nameOverrides} is updated to match the
-     * actual transition name of the mapped shared elements.
-     *
-     * @param nameOverrides A map of the shared element names from the starting fragment to
-     *                      the final fragment's Views as given in
-     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
-     * @param sharedElementTransition The shared element transition
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @return The mapping of shared element names to the Views in the hierarchy or null
-     * if there is no shared element transition.
-     */
-    private static ArrayMap<String, View> captureOutSharedElements(FragmentTransitionImpl impl,
-            ArrayMap<String, String> nameOverrides, Object sharedElementTransition,
-            FragmentContainerTransition fragments) {
-        if (nameOverrides.isEmpty() || sharedElementTransition == null) {
-            nameOverrides.clear();
-            return null;
-        }
-        final Fragment outFragment = fragments.firstOut;
-        final ArrayMap<String, View> outSharedElements = new ArrayMap<>();
-        impl.findNamedViews(outSharedElements, outFragment.requireView());
-
-        final SharedElementCallback sharedElementCallback;
-        final ArrayList<String> names;
-        final BackStackRecord outTransaction = fragments.firstOutTransaction;
-        if (fragments.firstOutIsPop) {
-            sharedElementCallback = outFragment.getEnterTransitionCallback();
-            names = outTransaction.mSharedElementTargetNames;
-        } else {
-            sharedElementCallback = outFragment.getExitTransitionCallback();
-            names = outTransaction.mSharedElementSourceNames;
-        }
-
-        if (names != null) {
-            outSharedElements.retainAll(names);
-        }
-        if (sharedElementCallback != null) {
-            sharedElementCallback.onMapSharedElements(names, outSharedElements);
-            for (int i = names.size() - 1; i >= 0; i--) {
-                String name = names.get(i);
-                View view = outSharedElements.get(name);
-                if (view == null) {
-                    nameOverrides.remove(name);
-                } else if (!name.equals(ViewCompat.getTransitionName(view))) {
-                    String targetValue = nameOverrides.remove(name);
-                    nameOverrides.put(ViewCompat.getTransitionName(view), targetValue);
-                }
-            }
-        } else {
-            nameOverrides.retainAll(outSharedElements.keySet());
-        }
-        return outSharedElements;
-    }
-
-    /**
-     * Finds the shared elements in the incoming fragment. It also calls
-     * {@link SharedElementCallback#onMapSharedElements(List, Map)} to allow more control
-     * of the shared element mapping. {@code nameOverrides} is updated to match the
-     * actual transition name of the mapped shared elements.
-     *
-     * @param nameOverrides A map of the shared element names from the starting fragment to
-     *                      the final fragment's Views as given in
-     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
-     * @param sharedElementTransition The shared element transition
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @return The mapping of shared element names to the Views in the hierarchy or null
-     * if there is no shared element transition.
-     */
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    static ArrayMap<String, View> captureInSharedElements(FragmentTransitionImpl impl,
-            ArrayMap<String, String> nameOverrides, Object sharedElementTransition,
-            FragmentContainerTransition fragments) {
-        Fragment inFragment = fragments.lastIn;
-        final View fragmentView = inFragment.getView();
-        if (nameOverrides.isEmpty() || sharedElementTransition == null || fragmentView == null) {
-            nameOverrides.clear();
-            return null;
-        }
-        final ArrayMap<String, View> inSharedElements = new ArrayMap<>();
-        impl.findNamedViews(inSharedElements, fragmentView);
-
-        final SharedElementCallback sharedElementCallback;
-        final ArrayList<String> names;
-        final BackStackRecord inTransaction = fragments.lastInTransaction;
-        if (fragments.lastInIsPop) {
-            sharedElementCallback = inFragment.getExitTransitionCallback();
-            names = inTransaction.mSharedElementSourceNames;
-        } else {
-            sharedElementCallback = inFragment.getEnterTransitionCallback();
-            names = inTransaction.mSharedElementTargetNames;
-        }
-
-        if (names != null) {
-            inSharedElements.retainAll(names);
-            inSharedElements.retainAll(nameOverrides.values());
-        }
-        if (sharedElementCallback != null) {
-            sharedElementCallback.onMapSharedElements(names, inSharedElements);
-            for (int i = names.size() - 1; i >= 0; i--) {
-                String name = names.get(i);
-                View view = inSharedElements.get(name);
-                if (view == null) {
-                    String key = findKeyForValue(nameOverrides, name);
-                    if (key != null) {
-                        nameOverrides.remove(key);
-                    }
-                } else if (!name.equals(ViewCompat.getTransitionName(view))) {
-                    String key = findKeyForValue(nameOverrides, name);
-                    if (key != null) {
-                        nameOverrides.put(key, ViewCompat.getTransitionName(view));
-                    }
-                }
-            }
-        } else {
-            retainValues(nameOverrides, inSharedElements);
-        }
-        return inSharedElements;
-    }
-
-    /**
      * Utility to find the String key in {@code map} that maps to {@code value}.
      */
     static String findKeyForValue(ArrayMap<String, String> map, String value) {
@@ -947,57 +62,6 @@
     }
 
     /**
-     * Returns the View in the incoming Fragment that should be used as the epicenter.
-     *
-     * @param inSharedElements The mapping of shared element names to Views in the
-     *                         incoming fragment.
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @param enterTransition The transition used for the incoming Fragment's views
-     * @param inIsPop Is the incoming fragment being added as a pop transaction?
-     */
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    static View getInEpicenterView(ArrayMap<String, View> inSharedElements,
-            FragmentContainerTransition fragments,
-            Object enterTransition, boolean inIsPop) {
-        BackStackRecord inTransaction = fragments.lastInTransaction;
-        if (enterTransition != null && inSharedElements != null
-                && inTransaction.mSharedElementSourceNames != null
-                && !inTransaction.mSharedElementSourceNames.isEmpty()) {
-            final String targetName = inIsPop
-                    ? inTransaction.mSharedElementSourceNames.get(0)
-                    : inTransaction.mSharedElementTargetNames.get(0);
-            return inSharedElements.get(targetName);
-        }
-        return null;
-    }
-
-    /**
-     * Sets the epicenter for the exit transition.
-     *
-     * @param sharedElementTransition The shared element transition
-     * @param exitTransition The transition for the outgoing fragment's views
-     * @param outSharedElements Shared elements in the outgoing fragment
-     * @param outIsPop Is the outgoing fragment being removed as a pop transaction?
-     * @param outTransaction The transaction that caused the fragment to be removed.
-     */
-    private static void setOutEpicenter(FragmentTransitionImpl impl, Object sharedElementTransition,
-            Object exitTransition, ArrayMap<String, View> outSharedElements, boolean outIsPop,
-            BackStackRecord outTransaction) {
-        if (outTransaction.mSharedElementSourceNames != null
-                && !outTransaction.mSharedElementSourceNames.isEmpty()) {
-            final String sourceName = outIsPop
-                    ? outTransaction.mSharedElementTargetNames.get(0)
-                    : outTransaction.mSharedElementSourceNames.get(0);
-            final View outEpicenterView = outSharedElements.get(sourceName);
-            impl.setEpicenter(sharedElementTransition, outEpicenterView);
-
-            if (exitTransition != null) {
-                impl.setEpicenter(exitTransition, outEpicenterView);
-            }
-        }
-    }
-
-    /**
      * A utility to retain only the mappings in {@code nameOverrides} that have a value
      * that has a key in {@code namedViews}. This is a useful equivalent to
      * {@link ArrayMap#retainAll(Collection)} for values.
@@ -1045,28 +109,6 @@
         }
     }
 
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    static ArrayList<View> configureEnteringExitingViews(FragmentTransitionImpl impl,
-            Object transition,
-            Fragment fragment, ArrayList<View> sharedElements, View nonExistentView) {
-        ArrayList<View> viewList = null;
-        if (transition != null) {
-            viewList = new ArrayList<>();
-            View root = fragment.getView();
-            if (root != null) {
-                impl.captureTransitioningViews(viewList, root);
-            }
-            if (sharedElements != null) {
-                viewList.removeAll(sharedElements);
-            }
-            if (!viewList.isEmpty()) {
-                viewList.add(nonExistentView);
-                impl.addTargets(transition, viewList);
-            }
-        }
-        return viewList;
-    }
-
     /**
      * Sets the visibility of all Views in {@code views} to {@code visibility}.
      */
@@ -1080,237 +122,10 @@
         }
     }
 
-    /**
-     * Merges exit, shared element, and enter transitions so that they act together or
-     * sequentially as defined in the fragments.
-     */
-    private static Object mergeTransitions(FragmentTransitionImpl impl, Object enterTransition,
-            Object exitTransition, Object sharedElementTransition, Fragment inFragment,
-            boolean isPop) {
-        boolean overlap = true;
-        if (enterTransition != null && exitTransition != null && inFragment != null) {
-            overlap = isPop ? inFragment.getAllowReturnTransitionOverlap() :
-                    inFragment.getAllowEnterTransitionOverlap();
-        }
-
-        // Wrap the transitions. Explicit targets like in enter and exit will cause the
-        // views to be targeted regardless of excluded views. If that happens, then the
-        // excluded fragments views (hidden fragments) will still be in the transition.
-
-        Object transition;
-        if (overlap) {
-            // Regular transition -- do it all together
-            transition = impl.mergeTransitionsTogether(exitTransition,
-                    enterTransition, sharedElementTransition);
-        } else {
-            // First do exit, then enter, but allow shared element transition to happen
-            // during both.
-            transition = impl.mergeTransitionsInSequence(exitTransition,
-                    enterTransition, sharedElementTransition);
-        }
-        return transition;
-    }
-
-    /**
-     * Finds the first removed fragment and last added fragments when going forward.
-     * If none of the fragments have transitions, then both lists will be empty.
-     *
-     * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
-     *                               and last fragments to be added. This will be modified by
-     *                               this method.
-     */
-    public static void calculateFragments(BackStackRecord transaction,
-            SparseArray<FragmentContainerTransition> transitioningFragments,
-            boolean isReordered) {
-        final int numOps = transaction.mOps.size();
-        for (int opNum = 0; opNum < numOps; opNum++) {
-            final BackStackRecord.Op op = transaction.mOps.get(opNum);
-            addToFirstInLastOut(transaction, op, transitioningFragments, false, isReordered);
-        }
-    }
-
-    /**
-     * Finds the first removed fragment and last added fragments when popping the back stack.
-     * If none of the fragments have transitions, then both lists will be empty.
-     *
-     * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
-     *                               and last fragments to be added. This will be modified by
-     *                               this method.
-     */
-    public static void calculatePopFragments(BackStackRecord transaction,
-            SparseArray<FragmentContainerTransition> transitioningFragments, boolean isReordered) {
-        if (!transaction.mManager.getContainer().onHasView()) {
-            return; // nothing to see, so no transitions
-        }
-        final int numOps = transaction.mOps.size();
-        for (int opNum = numOps - 1; opNum >= 0; opNum--) {
-            final BackStackRecord.Op op = transaction.mOps.get(opNum);
-            addToFirstInLastOut(transaction, op, transitioningFragments, true, isReordered);
-        }
-    }
-
     static boolean supportsTransition() {
         return PLATFORM_IMPL != null || SUPPORT_IMPL != null;
     }
 
-    /**
-     * Examines the {@code command} and may set the first out or last in fragment for the fragment's
-     * container.
-     *
-     * @param transaction The executing transaction
-     * @param op The operation being run.
-     * @param transitioningFragments A structure holding the first in and last out fragments
-     *                               for each fragment container.
-     * @param isPop Is the operation a pop?
-     * @param isReorderedTransaction True if the operations have been partially executed and the
-     *                               added fragments have Views in the hierarchy or false if the
-     *                               operations haven't been executed yet.
-     */
-    @SuppressWarnings("ReferenceEquality")
-    private static void addToFirstInLastOut(BackStackRecord transaction, BackStackRecord.Op op,
-            SparseArray<FragmentContainerTransition> transitioningFragments, boolean isPop,
-            boolean isReorderedTransaction) {
-        final Fragment fragment = op.mFragment;
-        if (fragment == null) {
-            return; // no fragment, no transition
-        }
-        final int containerId = fragment.mContainerId;
-        if (containerId == 0) {
-            return; // no container, no transition
-        }
-        final int command = isPop ? INVERSE_OPS[op.mCmd] : op.mCmd;
-        boolean setLastIn = false;
-        boolean wasRemoved = false;
-        boolean setFirstOut = false;
-        boolean wasAdded = false;
-        switch (command) {
-            case BackStackRecord.OP_SHOW:
-                if (isReorderedTransaction) {
-                    setLastIn = fragment.mHiddenChanged && !fragment.mHidden && fragment.mAdded;
-                } else {
-                    setLastIn = fragment.mHidden;
-                }
-                wasAdded = true;
-                break;
-            case BackStackRecord.OP_ADD:
-            case BackStackRecord.OP_ATTACH:
-                if (isReorderedTransaction) {
-                    setLastIn = fragment.mIsNewlyAdded;
-                } else {
-                    setLastIn = !fragment.mAdded && !fragment.mHidden;
-                }
-                wasAdded = true;
-                break;
-            case BackStackRecord.OP_HIDE:
-                if (isReorderedTransaction) {
-                    setFirstOut = fragment.mHiddenChanged && fragment.mAdded && fragment.mHidden;
-                } else {
-                    setFirstOut = fragment.mAdded && !fragment.mHidden;
-                }
-                wasRemoved = true;
-                break;
-            case BackStackRecord.OP_REMOVE:
-            case BackStackRecord.OP_DETACH:
-                if (isReorderedTransaction) {
-                    setFirstOut = !fragment.mAdded && fragment.mView != null
-                            && fragment.mView.getVisibility() == View.VISIBLE
-                            && fragment.mPostponedAlpha >= 0;
-                } else {
-                    setFirstOut = fragment.mAdded && !fragment.mHidden;
-                }
-                wasRemoved = true;
-                break;
-        }
-        FragmentContainerTransition containerTransition = transitioningFragments.get(containerId);
-        if (setLastIn) {
-            containerTransition =
-                    ensureContainer(containerTransition, transitioningFragments, containerId);
-            containerTransition.lastIn = fragment;
-            containerTransition.lastInIsPop = isPop;
-            containerTransition.lastInTransaction = transaction;
-        }
-        if (!isReorderedTransaction && wasAdded) {
-            if (containerTransition != null && containerTransition.firstOut == fragment) {
-                containerTransition.firstOut = null;
-            }
-
-            if (!transaction.mReorderingAllowed) {
-                // When reordering isn't allowed, we may be starting Transitions before
-                // the Fragment operation is actually executed so we move any new Fragments
-                // to created here first so that they have a Context, etc. when they
-                // are asked to load their Transitions
-                FragmentManager manager = transaction.mManager;
-                FragmentStateManager fragmentStateManager =
-                        manager.createOrGetFragmentStateManager(fragment);
-                manager.getFragmentStore().makeActive(fragmentStateManager);
-                manager.moveToState(fragment);
-            }
-        }
-        if (setFirstOut && (containerTransition == null || containerTransition.firstOut == null)) {
-            containerTransition =
-                    ensureContainer(containerTransition, transitioningFragments, containerId);
-            containerTransition.firstOut = fragment;
-            containerTransition.firstOutIsPop = isPop;
-            containerTransition.firstOutTransaction = transaction;
-        }
-
-        if (!isReorderedTransaction && wasRemoved
-                && (containerTransition != null && containerTransition.lastIn == fragment)) {
-            containerTransition.lastIn = null;
-        }
-    }
-
-    /**
-     * Ensures that a FragmentContainerTransition has been added to the SparseArray. If so,
-     * it returns the existing one. If not, one is created and added to the SparseArray and
-     * returned.
-     */
-    private static FragmentContainerTransition ensureContainer(
-            FragmentContainerTransition containerTransition,
-            SparseArray<FragmentContainerTransition> transitioningFragments, int containerId) {
-        if (containerTransition == null) {
-            containerTransition = new FragmentContainerTransition();
-            transitioningFragments.put(containerId, containerTransition);
-        }
-        return containerTransition;
-    }
-
-    /**
-     * Tracks the last fragment added and first fragment removed for fragment transitions.
-     * This also tracks which fragments are changed by push or pop transactions.
-     */
-    static class FragmentContainerTransition {
-        /**
-         * The last fragment added/attached/shown in its container
-         */
-        public Fragment lastIn;
-
-        /**
-         * true when lastIn was added during a pop transaction or false if added with a push
-         */
-        public boolean lastInIsPop;
-
-        /**
-         * The transaction that included the last in fragment
-         */
-        public BackStackRecord lastInTransaction;
-
-        /**
-         * The first fragment with a View that was removed/detached/hidden in its container.
-         */
-        public Fragment firstOut;
-
-        /**
-         * true when firstOut was removed during a pop transaction or false otherwise
-         */
-        public boolean firstOutIsPop;
-
-        /**
-         * The transaction that included the first out fragment
-         */
-        public BackStackRecord firstOutTransaction;
-    }
-
     private FragmentTransition() {
     }
 }
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransitionImpl.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransitionImpl.java
index 4e4a7f7..651df8b 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransitionImpl.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransitionImpl.java
@@ -30,7 +30,6 @@
 import androidx.core.os.CancellationSignal;
 import androidx.core.view.OneShotPreDrawListener;
 import androidx.core.view.ViewCompat;
-import androidx.core.view.ViewGroupCompat;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -208,75 +207,6 @@
     }
 
     /**
-     * Gets the Views in the hierarchy affected by entering and exiting Activity Scene transitions.
-     *
-     * @param transitioningViews This View will be added to transitioningViews if it is VISIBLE and
-     *                           a normal View or a ViewGroup with
-     *                           {@link android.view.ViewGroup#isTransitionGroup()} true.
-     * @param view               The base of the view hierarchy to look in.
-     */
-    void captureTransitioningViews(ArrayList<View> transitioningViews, View view) {
-        if (view.getVisibility() == View.VISIBLE) {
-            if (view instanceof ViewGroup) {
-                ViewGroup viewGroup = (ViewGroup) view;
-                if (ViewGroupCompat.isTransitionGroup(viewGroup)) {
-                    transitioningViews.add(viewGroup);
-                } else {
-                    int count = viewGroup.getChildCount();
-                    for (int i = 0; i < count; i++) {
-                        View child = viewGroup.getChildAt(i);
-                        captureTransitioningViews(transitioningViews, child);
-                    }
-                }
-            } else {
-                transitioningViews.add(view);
-            }
-        }
-    }
-
-    /**
-     * Finds all views that have transition names in the hierarchy under the given view and
-     * stores them in {@code namedViews} map with the name as the key.
-     */
-    void findNamedViews(Map<String, View> namedViews, @NonNull View view) {
-        if (view.getVisibility() == View.VISIBLE) {
-            String transitionName = ViewCompat.getTransitionName(view);
-            if (transitionName != null) {
-                namedViews.put(transitionName, view);
-            }
-            if (view instanceof ViewGroup) {
-                ViewGroup viewGroup = (ViewGroup) view;
-                int count = viewGroup.getChildCount();
-                for (int i = 0; i < count; i++) {
-                    View child = viewGroup.getChildAt(i);
-                    findNamedViews(namedViews, child);
-                }
-            }
-        }
-    }
-
-    /**
-     *Applies the prepared {@code nameOverrides} to the view hierarchy.
-     */
-    void setNameOverridesOrdered(final View sceneRoot,
-            final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
-        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                final int numSharedElements = sharedElementsIn.size();
-                for (int i = 0; i < numSharedElements; i++) {
-                    View view = sharedElementsIn.get(i);
-                    String name = ViewCompat.getTransitionName(view);
-                    if (name != null) {
-                        String inName = findKeyForValue(nameOverrides, name);
-                        ViewCompat.setTransitionName(view, inName);
-                    }
-                }
-            }
-        });
-    }
-
-    /**
      * After the transition has started, remove all targets that we added to the transitions
      * so that the transitions are left in a clean state.
      */
@@ -334,22 +264,6 @@
      */
     public abstract void setEpicenter(Object transitionObj, Rect epicenter);
 
-    void scheduleNameReset(final ViewGroup sceneRoot,
-            final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
-        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                final int numSharedElements = sharedElementsIn.size();
-                for (int i = 0; i < numSharedElements; i++) {
-                    final View view = sharedElementsIn.get(i);
-                    final String name = ViewCompat.getTransitionName(view);
-                    final String inName = nameOverrides.get(name);
-                    ViewCompat.setTransitionName(view, inName);
-                }
-            }
-        });
-    }
-
     /**
      * Uses a breadth-first scheme to add startView and all of its children to views.
      * It won't add a child if it is already in views.
@@ -395,17 +309,4 @@
         return list == null || list.isEmpty();
     }
 
-    /**
-     * Utility to find the String key in {@code map} that maps to {@code value}.
-     */
-    @SuppressWarnings("WeakerAccess")
-    static String findKeyForValue(Map<String, String> map, String value) {
-        for (Map.Entry<String, String> entry : map.entrySet()) {
-            if (value.equals(entry.getValue())) {
-                return entry.getKey();
-            }
-        }
-        return null;
-    }
-
 }
diff --git a/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavTypeTest.kt b/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavTypeTest.kt
index f01b81f..4659a2b 100644
--- a/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavTypeTest.kt
+++ b/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavTypeTest.kt
@@ -48,6 +48,8 @@
         private val parcelable = ActivityInfo()
         private val parcelables = arrayOf(parcelable)
         private val en = Bitmap.Config.ALPHA_8
+        private val enString = "ALPHA_8"
+        private val enStringCasing = "alpha_8"
         private val serializable = Person()
         private val serializables = arrayOf(Bitmap.Config.ALPHA_8)
         private val parcelableNavType = NavType.ParcelableType(ActivityInfo::class.java)
@@ -242,4 +244,13 @@
         assertThat(NavType.ReferenceType.parseValue(referenceHex))
             .isEqualTo(reference)
     }
+
+    @Test
+    fun parseEnumValue() {
+        assertThat(enumNavType.parseValue(enString))
+            .isEqualTo(en)
+
+        assertThat(enumNavType.parseValue(enStringCasing))
+            .isEqualTo(en)
+    }
 }
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavType.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavType.kt
index 7b11712..ef64348 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavType.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavType.kt
@@ -749,12 +749,9 @@
          */
         @Suppress("RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
         public override fun parseValue(value: String): D {
-            for (constant in type.enumConstants) {
-                if ((constant as Enum<*>).name == value) {
-                    return constant
-                }
-            }
-            throw IllegalArgumentException(
+            return type.enumConstants.firstOrNull { constant ->
+                constant.name.equals(value, ignoreCase = true)
+            } ?: throw IllegalArgumentException(
                 "Enum value $value not found for type ${type.name}."
             )
         }
diff --git a/samples/Support4Demos/src/main/java/com/example/android/supportv4/widget/SlidingPaneLayoutActivity.java b/samples/Support4Demos/src/main/java/com/example/android/supportv4/widget/SlidingPaneLayoutActivity.java
index 0e03ec8..c330ff7 100644
--- a/samples/Support4Demos/src/main/java/com/example/android/supportv4/widget/SlidingPaneLayoutActivity.java
+++ b/samples/Support4Demos/src/main/java/com/example/android/supportv4/widget/SlidingPaneLayoutActivity.java
@@ -18,8 +18,11 @@
 package com.example.android.supportv4.widget;
 
 import android.annotation.SuppressLint;
+import android.app.ActionBar;
 import android.os.Build;
 import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewTreeObserver;
@@ -98,15 +101,27 @@
     }
 
     @Override
+    public boolean onCreateOptionsMenu(@NonNull Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.sliding_pane_layout_menu, menu);
+        return true;
+    }
+
+    @Override
     public boolean onOptionsItemSelected(@NonNull MenuItem item) {
-        /*
-         * The action bar up action should close the detail view if it is
-         * currently open, as the left pane contains content one level up in the navigation
-         * hierarchy.
-         */
-        if (item.getItemId() == android.R.id.home && mSlidingLayout.isOpen()) {
-            mSlidingLayout.closePane();
-            return true;
+        switch (item.getItemId()) {
+            case R.id.lock_mode_unlocked:
+                mSlidingLayout.setLockMode(SlidingPaneLayout.LOCK_MODE_UNLOCKED);
+                return true;
+            case R.id.lock_mode_locked_open:
+                mSlidingLayout.setLockMode(SlidingPaneLayout.LOCK_MODE_LOCKED_OPEN);
+                return true;
+            case R.id.lock_mode_locked_closed:
+                mSlidingLayout.setLockMode(SlidingPaneLayout.LOCK_MODE_LOCKED_CLOSED);
+                return true;
+            case R.id.lock_mode_locked:
+                mSlidingLayout.setLockMode(SlidingPaneLayout.LOCK_MODE_LOCKED);
+                return true;
         }
         return super.onOptionsItemSelected(item);
     }
@@ -204,12 +219,40 @@
     /**
      * Stub action bar helper; this does nothing.
      */
-    static class ActionBarHelper {
-        public void init() {}
-        public void onPanelClosed() {}
-        public void onPanelOpened() {}
-        public void onFirstLayout() {}
-        public void setTitle(CharSequence title) {}
+    private class ActionBarHelper {
+        private final ActionBar mActionBar;
+        private CharSequence mListTitle;
+        private CharSequence mDetailTitle;
+
+        ActionBarHelper() {
+            mActionBar = getActionBar();
+        }
+
+        public void init() {
+            mListTitle = mDetailTitle = getTitle();
+        }
+
+        public void onPanelClosed() {
+            mActionBar.setTitle(mListTitle);
+        }
+
+        public void onPanelOpened() {
+            mActionBar.setTitle(mDetailTitle);
+        }
+
+        public void onFirstLayout() {
+            if (mSlidingLayout.isSlideable() && !mSlidingLayout.isOpen()) {
+                onPanelClosed();
+            } else {
+                onPanelOpened();
+            }
+        }
+
+        public void setTitle(CharSequence title) {
+            mListTitle = title;
+        }
     }
 
+
+
 }
diff --git a/samples/Support4Demos/src/main/res/menu/sliding_pane_layout_menu.xml b/samples/Support4Demos/src/main/res/menu/sliding_pane_layout_menu.xml
new file mode 100644
index 0000000..a967f08
--- /dev/null
+++ b/samples/Support4Demos/src/main/res/menu/sliding_pane_layout_menu.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/lock_mode_unlocked"
+        android:title="UNLOCK" />
+    <item android:id="@+id/lock_mode_locked_open"
+        android:title="LOCKED_OPEN" />
+    <item android:id="@+id/lock_mode_locked_closed"
+        android:title="LOCKED_CLOSED" />
+    <item android:id="@+id/lock_mode_locked"
+        android:title="LOCKED" />
+</menu>
\ No newline at end of file
diff --git a/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/LockModeTest.kt b/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/LockModeTest.kt
index 6b5ab24..faf82fa 100644
--- a/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/LockModeTest.kt
+++ b/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/LockModeTest.kt
@@ -22,11 +22,12 @@
 import androidx.slidingpanelayout.widget.SlidingPaneLayout.LOCK_MODE_LOCKED
 import androidx.slidingpanelayout.widget.SlidingPaneLayout.LOCK_MODE_UNLOCKED
 import androidx.slidingpanelayout.widget.helpers.TestActivity
+import androidx.slidingpanelayout.widget.helpers.addWaitForCloseLatch
 import androidx.slidingpanelayout.widget.helpers.addWaitForOpenLatch
-import androidx.slidingpanelayout.widget.helpers.dragLeft
-import androidx.slidingpanelayout.widget.helpers.dragRight
-import androidx.slidingpanelayout.widget.helpers.findViewX
+import androidx.slidingpanelayout.widget.helpers.addWaitForSlideLatch
 import androidx.slidingpanelayout.widget.helpers.openPane
+import androidx.slidingpanelayout.widget.helpers.slideClose
+import androidx.slidingpanelayout.widget.helpers.slideOpen
 import androidx.test.core.app.ActivityScenario
 import androidx.test.espresso.Espresso.onView
 import androidx.test.espresso.assertion.ViewAssertions
@@ -36,6 +37,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
+import androidx.testutils.withActivity
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Test
@@ -64,11 +66,15 @@
                     ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)
                 )
             )
+            val slidingPaneLayout =
+                withActivity { findViewById<SlidingPaneLayout>(R.id.sliding_pane_layout) }
+            assertThat(slidingPaneLayout.isOpen).isFalse()
+            assertThat(slidingPaneLayout.isSlideable).isTrue()
         }
     }
 
     /**
-     * Test users can freely swipe right between list and detail panes when lock mode set to
+     * Test users can swipe right between list and detail panes when lock mode set to
      * LOCK_MODE_UNLOCKED.
      */
     @SdkSuppress(maxSdkVersion = 28) // TODO: Fix flaky test issues on API 30 Cuttlefish devices.
@@ -81,18 +87,19 @@
         }
 
         with(ActivityScenario.launch(TestActivity::class.java)) {
-            val latch = addWaitForOpenLatch(R.id.sliding_pane_layout)
+            val panelOpenCountDownLatch = addWaitForOpenLatch(R.id.sliding_pane_layout)
             onView(withId(R.id.sliding_pane_layout)).perform(openPane())
-            assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue()
-            latch.await(2, TimeUnit.SECONDS)
-            val detailPaneOpenX = findViewX(R.id.detail_pane)
-            onView(withId(R.id.sliding_pane_layout)).perform(dragRight())
-            assertThat(findViewX(R.id.detail_pane)).isGreaterThan(detailPaneOpenX)
+            // wait for detail pane open
+            assertThat(panelOpenCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue()
+            val panelSlideCountDownLatch = addWaitForSlideLatch(R.id.sliding_pane_layout)
+            onView(withId(R.id.sliding_pane_layout)).perform(slideClose())
+            // wait for detail pane sliding
+            assertThat(panelSlideCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue()
         }
     }
 
     /**
-     * Test users can freely swipe left between list and detail panes when lock mode set to
+     * Test users can swipe left between list and detail panes when lock mode set to
      * LOCK_MODE_UNLOCKED.
      */
     @SdkSuppress(maxSdkVersion = 28) // TODO: Fix flaky test issues on API 30 Cuttlefish devices.
@@ -105,19 +112,20 @@
         }
 
         with(ActivityScenario.launch(TestActivity::class.java)) {
-            val detailPaneOpenX = findViewX(R.id.detail_pane)
-            onView(withId(R.id.sliding_pane_layout)).perform(dragLeft())
-            assertThat(findViewX(R.id.detail_pane)).isLessThan(detailPaneOpenX)
+            val panelSlideCountDownLatch = addWaitForSlideLatch(R.id.sliding_pane_layout)
+            onView(withId(R.id.sliding_pane_layout)).perform(slideOpen())
+            // wait for detail pane sliding
+            assertThat(panelSlideCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue()
         }
     }
 
     /**
-     * Test users cannot swipe from list to detail, but can swipe from detail to list when lock
-     * mode set to LOCK_MODE_LOCKED_OPEN
+     * Test users can swipe to open detail pane in lock mode LOCK_MODE_LOCKED_OPEN when
+     * detail view is in closed state. Otherwise, users cannot swipe it.
      */
     @SdkSuppress(maxSdkVersion = 28) // TODO: Fix flaky test issues on API 30 Cuttlefish devices.
     @Test
-    public fun testCanSlideListToDetailWhenLockModeLockedOpen() {
+    public fun testSwipeWhenLockModeLockedOpen() {
         TestActivity.onActivityCreated = { activity ->
             val slidingPaneLayout =
                 activity.findViewById<SlidingPaneLayout>(R.id.sliding_pane_layout)
@@ -125,21 +133,24 @@
         }
 
         with(ActivityScenario.launch(TestActivity::class.java)) {
-            val detailPaneClosedX = findViewX(R.id.detail_pane)
-            onView(withId(R.id.sliding_pane_layout)).perform(dragRight())
-            assertThat(findViewX(R.id.detail_pane)).isEqualTo(detailPaneClosedX)
-            onView(withId(R.id.sliding_pane_layout)).perform(dragLeft())
-            assertThat(findViewX(R.id.detail_pane)).isLessThan(detailPaneClosedX)
+            var panelSlideCountDownLatch = addWaitForSlideLatch(R.id.sliding_pane_layout)
+            onView(withId(R.id.sliding_pane_layout)).perform(slideOpen())
+            // can slide to open
+            assertThat(panelSlideCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue()
+            panelSlideCountDownLatch = addWaitForCloseLatch(R.id.sliding_pane_layout)
+            onView(withId(R.id.sliding_pane_layout)).perform(slideClose())
+            // cannot slide to close
+            assertThat(panelSlideCountDownLatch.await(2, TimeUnit.SECONDS)).isFalse()
         }
     }
 
     /**
-     * Test users cannot swipe from detail to list, but can swipe from list to detail when lock
-     * mode set to LOCK_MODE_LOCKED_CLOSED
+     * Test users can swipe to close the detail pane in lock mode LOCK_MODE_LOCKED_CLOSED when
+     * detail view is in open state. Otherwise, users cannot swipe it.
      */
     @SdkSuppress(maxSdkVersion = 28) // TODO: Fix flaky test issues on API 30 Cuttlefish devices.
     @Test
-    public fun testSwipeWhenLockModeClosed() {
+    public fun testSwipeWhenLockModeLockedClosed() {
         TestActivity.onActivityCreated = { activity ->
             val slidingPaneLayout =
                 activity.findViewById<SlidingPaneLayout>(R.id.sliding_pane_layout)
@@ -147,15 +158,17 @@
         }
 
         with(ActivityScenario.launch(TestActivity::class.java)) {
-            var detailPaneClosedX = findViewX(R.id.detail_pane)
-            onView(withId(R.id.sliding_pane_layout)).perform(dragLeft())
-            assertThat(findViewX(R.id.detail_pane)).isEqualTo(detailPaneClosedX)
+            var panelSlideCountDownLatch = addWaitForSlideLatch(R.id.sliding_pane_layout)
+            onView(withId(R.id.sliding_pane_layout)).perform(slideOpen())
+            // cannot slide to open
+            assertThat(panelSlideCountDownLatch.await(2, TimeUnit.SECONDS)).isFalse()
             val latch = addWaitForOpenLatch(R.id.sliding_pane_layout)
             onView(withId(R.id.sliding_pane_layout)).perform(openPane())
             assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue()
-            detailPaneClosedX = findViewX(R.id.detail_pane)
-            onView(withId(R.id.sliding_pane_layout)).perform(dragRight())
-            assertThat(findViewX(R.id.detail_pane)).isGreaterThan(detailPaneClosedX)
+            panelSlideCountDownLatch = addWaitForSlideLatch(R.id.sliding_pane_layout)
+            onView(withId(R.id.sliding_pane_layout)).perform(slideClose())
+            // can slide to close
+            assertThat(panelSlideCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue()
         }
     }
 
@@ -173,15 +186,17 @@
         }
 
         with(ActivityScenario.launch(TestActivity::class.java)) {
-            val detailPaneClosedX = findViewX(R.id.detail_pane)
-            onView(withId(R.id.sliding_pane_layout)).perform(dragLeft())
-            assertThat(findViewX(R.id.detail_pane)).isEqualTo(detailPaneClosedX)
+            var panelSlideCountDownLatch = addWaitForSlideLatch(R.id.sliding_pane_layout)
+            onView(withId(R.id.sliding_pane_layout)).perform(slideOpen())
+            // cannot slide to open
+            assertThat(panelSlideCountDownLatch.await(2, TimeUnit.SECONDS)).isFalse()
             val latch = addWaitForOpenLatch(R.id.sliding_pane_layout)
             onView(withId(R.id.sliding_pane_layout)).perform(openPane())
             assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue()
-            val detailPaneOpenX = findViewX(R.id.detail_pane)
-            onView(withId(R.id.sliding_pane_layout)).perform(dragRight())
-            assertThat(findViewX(R.id.detail_pane)).isEqualTo(detailPaneOpenX)
+            panelSlideCountDownLatch = addWaitForSlideLatch(R.id.sliding_pane_layout)
+            onView(withId(R.id.sliding_pane_layout)).perform(slideClose())
+            // cannot slide to close
+            assertThat(panelSlideCountDownLatch.await(2, TimeUnit.SECONDS)).isFalse()
         }
     }
 }
\ No newline at end of file
diff --git a/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/helpers/ActivityScenario.kt b/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/helpers/ActivityScenario.kt
index 2d5a251..4d54fe3 100644
--- a/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/helpers/ActivityScenario.kt
+++ b/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/helpers/ActivityScenario.kt
@@ -51,3 +51,39 @@
     }
     return latch
 }
+
+public inline fun <reified A : Activity> ActivityScenario<A>.addWaitForCloseLatch(
+    @IdRes resId: Int
+): CountDownLatch {
+    val latch = CountDownLatch(1)
+    withActivity {
+        val slidingPaneLayout = findViewById<SlidingPaneLayout>(resId)
+        slidingPaneLayout.addPanelSlideListener(object : SlidingPaneLayout.PanelSlideListener {
+            override fun onPanelSlide(panel: View, slideOffset: Float) {}
+            override fun onPanelOpened(panel: View) {}
+            override fun onPanelClosed(panel: View) {
+                latch.countDown()
+                slidingPaneLayout.removePanelSlideListener(this)
+            }
+        })
+    }
+    return latch
+}
+
+public inline fun <reified A : Activity> ActivityScenario<A>.addWaitForSlideLatch(
+    @IdRes resId: Int
+): CountDownLatch {
+    val latch = CountDownLatch(1)
+    withActivity {
+        val slidingPaneLayout = findViewById<SlidingPaneLayout>(resId)
+        slidingPaneLayout.addPanelSlideListener(object : SlidingPaneLayout.PanelSlideListener {
+            override fun onPanelSlide(panel: View, slideOffset: Float) {
+                latch.countDown()
+                slidingPaneLayout.removePanelSlideListener(this)
+            }
+            override fun onPanelOpened(panel: View) {}
+            override fun onPanelClosed(panel: View) {}
+        })
+    }
+    return latch
+}
\ No newline at end of file
diff --git a/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/helpers/Espresso.kt b/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/helpers/Espresso.kt
index 80b7429..b55f81e 100644
--- a/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/helpers/Espresso.kt
+++ b/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/helpers/Espresso.kt
@@ -28,10 +28,10 @@
 import androidx.test.espresso.matcher.ViewMatchers
 import org.hamcrest.Matcher
 
-public fun dragRight(): ViewAction? {
+public fun slideClose(): ViewAction? {
     return ViewActions.actionWithAssertions(
         GeneralSwipeAction(
-            Swipe.SLOW,
+            Swipe.FAST,
             GeneralLocation.CENTER_LEFT,
             GeneralLocation.CENTER_RIGHT,
             Press.FINGER
@@ -39,10 +39,10 @@
     )
 }
 
-public fun dragLeft(): ViewAction? {
+public fun slideOpen(): ViewAction? {
     return ViewActions.actionWithAssertions(
         GeneralSwipeAction(
-            Swipe.SLOW,
+            Swipe.FAST,
             GeneralLocation.CENTER_RIGHT,
             GeneralLocation.CENTER_LEFT,
             Press.FINGER
diff --git a/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java b/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java
index 3138c10..024d6964 100644
--- a/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java
+++ b/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java
@@ -233,6 +233,8 @@
 
     /**
      * Set the lock mode that controls how the user can swipe between the panes.
+     *
+     * @param lockMode The new lock mode for the detail pane.
      */
     public final void setLockMode(@LockMode int lockMode) {
         mLockMode = lockMode;
@@ -1013,6 +1015,9 @@
     }
 
     private boolean closePane(int initialVelocity) {
+        if (!mCanSlide) {
+            mPreservedOpenState = false;
+        }
         if (mFirstLayout || smoothSlideTo(1.f, initialVelocity)) {
             mPreservedOpenState = false;
             return true;
@@ -1021,6 +1026,9 @@
     }
 
     private boolean openPane(int initialVelocity) {
+        if (!mCanSlide) {
+            mPreservedOpenState = true;
+        }
         if (mFirstLayout || smoothSlideTo(0.f, initialVelocity)) {
             mPreservedOpenState = true;
             return true;
@@ -1475,6 +1483,7 @@
 
         SavedState ss = new SavedState(superState);
         ss.isOpen = isSlideable() ? isOpen() : mPreservedOpenState;
+        ss.mLockMode = mLockMode;
 
         return ss;
     }
@@ -1495,6 +1504,8 @@
             closePane();
         }
         mPreservedOpenState = ss.isOpen;
+
+        setLockMode(ss.mLockMode);
     }
 
     private class DragHelperCallback extends ViewDragHelper.Callback {
@@ -1504,7 +1515,7 @@
 
         @Override
         public boolean tryCaptureView(View child, int pointerId) {
-            if (mIsUnableToDrag) {
+            if (!isDraggable()) {
                 return false;
             }
 
@@ -1566,20 +1577,7 @@
 
         @Override
         public int clampViewPositionHorizontal(View child, int left, int dx) {
-            boolean slidingDetailToList = isLayoutRtlSupport() ? dx > 0 : dx < 0;
             int newLeft = left;
-            if (slidingDetailToList) {
-                if (getLockMode() == LOCK_MODE_LOCKED_CLOSED
-                        || getLockMode() == LOCK_MODE_LOCKED) {
-                    newLeft -= dx;
-                }
-            } else {
-                if (getLockMode() == LOCK_MODE_LOCKED_OPEN
-                        || getLockMode() == LOCK_MODE_LOCKED) {
-                    newLeft -= dx;
-                }
-            }
-
             final LayoutParams lp = (LayoutParams) mSlideableView.getLayoutParams();
 
             if (isLayoutRtlSupport()) {
@@ -1604,15 +1602,39 @@
 
         @Override
         public void onEdgeTouched(int edgeFlags, int pointerId) {
+            if (!isDraggable()) {
+                return;
+            }
             mDragHelper.captureChildView(mSlideableView, pointerId);
         }
 
         @Override
         public void onEdgeDragStarted(int edgeFlags, int pointerId) {
+            if (!isDraggable()) {
+                return;
+            }
             mDragHelper.captureChildView(mSlideableView, pointerId);
         }
+
+        private boolean isDraggable() {
+            if (mIsUnableToDrag) {
+                return false;
+            }
+            if (getLockMode() == LOCK_MODE_LOCKED) {
+                return false;
+            }
+            if (isOpen() && getLockMode() == LOCK_MODE_LOCKED_OPEN) {
+                return false;
+            }
+            if (!isOpen() && getLockMode() == LOCK_MODE_LOCKED_CLOSED) {
+                return false;
+            }
+            return true;
+        }
     }
 
+
+
     public static class LayoutParams extends ViewGroup.MarginLayoutParams {
         private static final int[] ATTRS = new int[]{
                 android.R.attr.layout_weight
@@ -1670,6 +1692,7 @@
 
     static class SavedState extends AbsSavedState {
         boolean isOpen;
+        @LockMode int mLockMode;
 
         SavedState(Parcelable superState) {
             super(superState);
@@ -1678,12 +1701,14 @@
         SavedState(Parcel in, ClassLoader loader) {
             super(in, loader);
             isOpen = in.readInt() != 0;
+            mLockMode = in.readInt();
         }
 
         @Override
         public void writeToParcel(Parcel out, int flags) {
             super.writeToParcel(out, flags);
             out.writeInt(isOpen ? 1 : 0);
+            out.writeInt(mLockMode);
         }
 
         public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
diff --git a/transition/transition/src/androidTest/java/androidx/transition/FragmentTestUtil.kt b/transition/transition/src/androidTest/java/androidx/transition/FragmentTestUtil.kt
index 7f5a337..1579cd7b 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/FragmentTestUtil.kt
+++ b/transition/transition/src/androidTest/java/androidx/transition/FragmentTestUtil.kt
@@ -25,9 +25,9 @@
 import androidx.fragment.app.FragmentManager
 import androidx.fragment.app.FragmentTransaction
 import androidx.fragment.app.TargetTracking
-import androidx.transition.test.R
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.testutils.runOnUiThreadRethrow
+import androidx.transition.test.R
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import java.lang.ref.WeakReference
@@ -43,27 +43,6 @@
 object Reordered : ReorderingAllowed()
 object Ordered : ReorderingAllowed()
 
-sealed class StateManager {
-    abstract fun setup()
-
-    override fun toString(): String = this.javaClass.simpleName
-
-    fun teardown() {
-        // Reset it back to the default
-        FragmentManager.enableNewStateManager(true)
-    }
-}
-object NewStateManager : StateManager() {
-    override fun setup() {
-        FragmentManager.enableNewStateManager(true)
-    }
-}
-object OldStateManager : StateManager() {
-    override fun setup() {
-        FragmentManager.enableNewStateManager(false)
-    }
-}
-
 @Suppress("DEPRECATION")
 fun androidx.test.rule.ActivityTestRule<out FragmentActivity>.executePendingTransactions(
     fm: FragmentManager = activity.supportFragmentManager
diff --git a/transition/transition/src/androidTest/java/androidx/transition/FragmentTransitionTest.kt b/transition/transition/src/androidTest/java/androidx/transition/FragmentTransitionTest.kt
index c45abaf..db5d385 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/FragmentTransitionTest.kt
+++ b/transition/transition/src/androidTest/java/androidx/transition/FragmentTransitionTest.kt
@@ -15,7 +15,6 @@
  */
 package androidx.transition
 
-import android.graphics.Rect
 import android.os.Build
 import android.os.Bundle
 import android.view.View
@@ -50,8 +49,7 @@
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
 class FragmentTransitionTest(
-    private val reorderingAllowed: ReorderingAllowed,
-    private val stateManager: StateManager
+    private val reorderingAllowed: ReorderingAllowed
 ) {
 
     @Suppress("DEPRECATION")
@@ -68,7 +66,6 @@
 
     @Before
     fun setup() {
-        stateManager.setup()
         onBackStackChangedTimes = 0
         fragmentManager = activityRule.activity.supportFragmentManager
         fragmentManager.addOnBackStackChangedListener(onBackStackChangedListener)
@@ -77,7 +74,6 @@
     @After
     fun teardown() {
         fragmentManager.removeOnBackStackChangedListener(onBackStackChangedListener)
-        stateManager.teardown()
     }
 
     // Test that normal view transitions (enter, exit, reenter, return) run with
@@ -130,8 +126,6 @@
     fun removeThenAddBeforeTransitionFinishes() {
         // enter transition
         val fragment = setupInitialFragment()
-        val blue = activityRule.findBlue()
-        val green = activityRule.findGreen()
 
         val view1 = fragment.view
 
@@ -155,23 +149,10 @@
         // back stack
         if (reorderingAllowed is Reordered) {
             assertThat(onBackStackChangedTimes).isEqualTo(2)
-            assertThat(fragment.requireView()).isEqualTo(view1)
         } else {
             assertThat(onBackStackChangedTimes).isEqualTo(3)
-            if (stateManager is NewStateManager) {
-                // When using FragmentStateManager, the transition gets cancelled and the
-                // Fragment  does not go all the way through to destroying the view before
-                // coming back up, so the view instances will still match
-                assertThat(fragment.requireView()).isEqualTo(view1)
-            } else {
-                // If reorder is not allowed we will get the exit Transition
-                fragment.waitForTransition()
-                fragment.exitTransition.verifyAndClearTransition {
-                    exitingViews += listOf(green, blue)
-                }
-                assertThat(fragment.requireView()).isNotEqualTo(view1)
-            }
         }
+        assertThat(fragment.requireView()).isEqualTo(view1)
         verifyNoOtherTransitions(fragment)
     }
     @Test
@@ -860,7 +841,6 @@
 
         val startBlue = activityRule.findBlue()
         val startGreen = activityRule.findGreen()
-        val startBlueBounds = startBlue.boundsOnScreen
 
         fragmentManager.beginTransaction()
             .addSharedElement(startBlue, "fooSquare")
@@ -876,21 +856,8 @@
         val endBlue = activityRule.findBlue()
         val endGreen = activityRule.findGreen()
 
-        // FragmentStateManager is able to build the correct transition
-        // whether you use reordering or not
-        if (stateManager is NewStateManager || reorderingAllowed is Reordered) {
-            fragment1.exitTransition.verifyAndClearTransition {
-                exitingViews += listOf(startGreen, startBlue)
-            }
-        } else {
-            fragment1.exitTransition.verifyAndClearTransition {
-                epicenter = startBlueBounds
-                exitingViews += startGreen
-            }
-            fragment2.sharedElementEnter.verifyAndClearTransition {
-                epicenter = startBlueBounds
-                exitingViews += startBlue
-            }
+        fragment1.exitTransition.verifyAndClearTransition {
+            exitingViews += listOf(startGreen, startBlue)
         }
         verifyNoOtherTransitions(fragment1)
 
@@ -1030,16 +997,10 @@
         if (reorderingAllowed is Reordered) {
             // reordering allowed fragment3 to get a transition so we should wait for it to finish
             fragment3.waitForTransition()
-            if (stateManager is NewStateManager) {
-                // When using the NewStateManager, the last operation sets the direction.
-                // In this case, the forward direction since we did a replace() after the pop
-                fragment2.exitTransition.verifyAndClearTransition {
-                    exitingViews += listOf(midGreen, midBlue)
-                }
-            } else {
-                fragment2.returnTransition.verifyAndClearTransition {
-                    exitingViews += listOf(midGreen, midBlue)
-                }
+            // The last operation (in this case a replace()) sets the direction of
+            // the transition, so the popped fragment runs its exit transition
+            fragment2.exitTransition.verifyAndClearTransition {
+                exitingViews += listOf(midGreen, midBlue)
             }
             val endGreen = activityRule.findGreen()
             val endBlue = activityRule.findBlue()
@@ -1094,30 +1055,18 @@
             val endGreen = activityRule.findGreen()
             val endBlue = activityRule.findBlue()
             val endGreenBounds = endGreen.boundsOnScreen
-
-            if (stateManager is NewStateManager) {
-                // When using the NewStateManager, the last operation sets the direction.
-                // In this case, the forward direction since we did a replace() after the pop
-                fragment1.exitTransition.verifyAndClearTransition {
-                    epicenter = endGreenBounds
-                    exitingViews += startGreen
-                }
-            } else {
-                fragment1.returnTransition.verifyAndClearTransition {
-                    exitingViews += startGreen
-                }
+            // The last operation (in this case a replace()) sets the direction of
+            // the transition, so the popped fragment runs its exit transition
+            fragment1.exitTransition.verifyAndClearTransition {
+                epicenter = endGreenBounds
+                exitingViews += startGreen
             }
             fragment2.enterTransition.verifyAndClearTransition {
                 epicenter = startGreenBounds
                 enteringViews += endGreen
             }
             fragment2.sharedElementEnter.verifyAndClearTransition {
-                epicenter = if (stateManager is NewStateManager) {
-                    endGreenBounds
-                } else {
-                    // In this case, we can't find an epicenter
-                    Rect()
-                }
+                epicenter = endGreenBounds
                 exitingViews += startBlue
                 enteringViews += endBlue
             }
@@ -1156,20 +1105,10 @@
         val midRed = activityRule.findRed()
         val midGreenBounds = midGreen.boundsOnScreen
 
-        // FragmentStateManager is able to build the correct transition
-        // whether you use reordering or not
-        if (stateManager is NewStateManager || reorderingAllowed is Reordered) {
-            fragment2.sharedElementEnter.verifyAndClearTransition {
-                epicenter = startGreenBounds
-                exitingViews += startGreen
-                enteringViews += midGreen
-            }
-        } else {
-            fragment2.sharedElementEnter.verifyAndClearTransition {
-                epicenter = startGreenBounds
-                exitingViews += listOf(startGreen, startBlue)
-                enteringViews += midGreen
-            }
+        fragment2.sharedElementEnter.verifyAndClearTransition {
+            epicenter = startGreenBounds
+            exitingViews += startGreen
+            enteringViews += midGreen
         }
         fragment2.enterTransition.verifyAndClearTransition {
             epicenter = midGreenBounds
@@ -1535,18 +1474,8 @@
 
     companion object {
         @JvmStatic
-        @Parameterized.Parameters(name = "ordering={0}, stateManager={1}")
-        fun data() = mutableListOf<Array<Any>>().apply {
-            arrayOf(
-                Ordered,
-                Reordered
-            ).forEach { ordering ->
-                // Run the test with the new state manager
-                add(arrayOf(ordering, NewStateManager))
-                // Run the test with the old state manager
-                add(arrayOf(ordering, OldStateManager))
-            }
-        }
+        @Parameterized.Parameters(name = "ordering={0}")
+        fun data() = arrayOf(Ordered, Reordered)
     }
 }