Merge "Revert "Fix several focus issues"" into androidx-main
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 9631cd1..a12b66a 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -135,12 +135,10 @@
     property public final boolean NewNestedScrollFlingDispatchingEnabled;
     property public final boolean isRectTrackingEnabled;
     property public final boolean isSemanticAutofillEnabled;
-    property public final boolean isViewFocusFixEnabled;
     field public static final androidx.compose.ui.ComposeUiFlags INSTANCE;
     field public static boolean NewNestedScrollFlingDispatchingEnabled;
     field public static boolean isRectTrackingEnabled;
     field public static boolean isSemanticAutofillEnabled;
-    field public static boolean isViewFocusFixEnabled;
   }
 
   public final class ComposedModifierKt {
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index f614763c..258aa94 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -135,12 +135,10 @@
     property public final boolean NewNestedScrollFlingDispatchingEnabled;
     property public final boolean isRectTrackingEnabled;
     property public final boolean isSemanticAutofillEnabled;
-    property public final boolean isViewFocusFixEnabled;
     field public static final androidx.compose.ui.ComposeUiFlags INSTANCE;
     field public static boolean NewNestedScrollFlingDispatchingEnabled;
     field public static boolean isRectTrackingEnabled;
     field public static boolean isSemanticAutofillEnabled;
-    field public static boolean isViewFocusFixEnabled;
   }
 
   public final class ComposedModifierKt {
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/FocusViewInteropTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/FocusViewInteropTest.kt
index baafca3..8d7bed4 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/FocusViewInteropTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/FocusViewInteropTest.kt
@@ -19,9 +19,7 @@
 import android.content.Context
 import android.graphics.Rect as AndroidRect
 import android.view.View
-import android.widget.Button
 import android.widget.EditText
-import android.widget.LinearLayout
 import androidx.compose.foundation.focusGroup
 import androidx.compose.foundation.focusable
 import androidx.compose.foundation.layout.Box
@@ -35,8 +33,6 @@
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.LazyListState
-import androidx.compose.material.Button
-import androidx.compose.material.Text
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
@@ -45,24 +41,14 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusDirection.Companion.Down
-import androidx.compose.ui.focus.FocusDirection.Companion.Left
-import androidx.compose.ui.focus.FocusDirection.Companion.Next
-import androidx.compose.ui.focus.FocusDirection.Companion.Previous
-import androidx.compose.ui.focus.FocusDirection.Companion.Right
-import androidx.compose.ui.focus.FocusDirection.Companion.Up
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.assertIsFocused
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performTouchInput
-import androidx.compose.ui.test.requestFocus
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntRect
 import androidx.compose.ui.unit.dp
@@ -225,268 +211,6 @@
         assertThat(thirdFocused).isTrue()
     }
 
-    @Test
-    fun moveFocusThroughUnfocusableComposeViewNext() {
-        lateinit var topEditText: EditText
-        lateinit var composeView: ComposeView
-        lateinit var bottomEditText: EditText
-        lateinit var focusManager: FocusManager
-
-        rule.setContent {
-            focusManager = LocalFocusManager.current
-            AndroidView(
-                modifier = Modifier.fillMaxSize(),
-                factory = { context ->
-                    LinearLayout(context).also { linearLayout ->
-                        linearLayout.orientation = LinearLayout.VERTICAL
-                        EditText(context).also {
-                            linearLayout.addView(it)
-                            topEditText = it
-                        }
-                        ComposeView(context).also {
-                            it.setContent { Box(Modifier.size(10.dp)) }
-                            linearLayout.addView(it)
-                            composeView = it
-                        }
-                        EditText(context).also {
-                            linearLayout.addView(it)
-                            bottomEditText = it
-                        }
-                    }
-                }
-            )
-        }
-
-        rule.runOnIdle { topEditText.requestFocus() }
-
-        rule.runOnIdle { focusManager.moveFocus(Next) }
-
-        rule.runOnIdle {
-            assertThat(topEditText.isFocused).isFalse()
-            assertThat(composeView.isFocused).isFalse()
-            assertThat(bottomEditText.isFocused).isTrue()
-        }
-    }
-
-    @Test
-    fun moveFocusThroughUnfocusableComposeViewDown() {
-        lateinit var topEditText: EditText
-        lateinit var composeView: ComposeView
-        lateinit var bottomEditText: EditText
-        lateinit var focusManager: FocusManager
-
-        rule.setContent {
-            focusManager = LocalFocusManager.current
-            AndroidView(
-                modifier = Modifier.fillMaxSize(),
-                factory = { context ->
-                    LinearLayout(context).also { linearLayout ->
-                        linearLayout.orientation = LinearLayout.VERTICAL
-                        EditText(context).also {
-                            linearLayout.addView(it)
-                            topEditText = it
-                        }
-                        ComposeView(context).also {
-                            it.setContent { Box(Modifier.size(10.dp)) }
-                            linearLayout.addView(it)
-                            composeView = it
-                        }
-                        EditText(context).also {
-                            linearLayout.addView(it)
-                            bottomEditText = it
-                        }
-                    }
-                }
-            )
-        }
-
-        rule.runOnIdle { topEditText.requestFocus() }
-
-        rule.runOnIdle { focusManager.moveFocus(Down) }
-
-        rule.runOnIdle {
-            assertThat(topEditText.isFocused).isFalse()
-            assertThat(composeView.isFocused).isFalse()
-            assertThat(bottomEditText.isFocused).isTrue()
-        }
-    }
-
-    @Test
-    fun focusBetweenComposeViews_NextPrevious() {
-        lateinit var focusManager: FocusManager
-
-        rule.setContent {
-            focusManager = LocalFocusManager.current
-            AndroidView(
-                modifier = Modifier.fillMaxSize(),
-                factory = { context ->
-                    LinearLayout(context).also { linearLayout ->
-                        linearLayout.orientation = LinearLayout.VERTICAL
-                        ComposeView(context).also {
-                            it.setContent {
-                                Box(
-                                    Modifier.size(10.dp)
-                                        .focusProperties { canFocus = true }
-                                        .focusable()
-                                        .testTag("button1")
-                                )
-                            }
-                            linearLayout.addView(it)
-                        }
-                        ComposeView(context).also {
-                            it.setContent {
-                                Box(
-                                    Modifier.size(10.dp)
-                                        .focusProperties { canFocus = true }
-                                        .focusable()
-                                        .testTag("button2")
-                                )
-                            }
-                            linearLayout.addView(it)
-                        }
-                        ComposeView(context).also {
-                            it.setContent {
-                                Box(
-                                    Modifier.size(10.dp)
-                                        .focusProperties { canFocus = true }
-                                        .focusable()
-                                        .testTag("button3")
-                                )
-                            }
-                            linearLayout.addView(it)
-                        }
-                    }
-                }
-            )
-        }
-        rule.onNodeWithTag("button1").requestFocus()
-        rule.onNodeWithTag("button1").assertIsFocused()
-        rule.runOnIdle { focusManager.moveFocus(Next) }
-        rule.onNodeWithTag("button2").assertIsFocused()
-        rule.runOnIdle { focusManager.moveFocus(Next) }
-        rule.onNodeWithTag("button3").assertIsFocused()
-        rule.runOnIdle { focusManager.moveFocus(Previous) }
-        rule.onNodeWithTag("button2").assertIsFocused()
-        rule.runOnIdle { focusManager.moveFocus(Previous) }
-        rule.onNodeWithTag("button1").assertIsFocused()
-    }
-
-    @Test
-    fun focusBetweenComposeViews_DownUp() {
-        lateinit var focusManager: FocusManager
-
-        rule.setContent {
-            focusManager = LocalFocusManager.current
-            AndroidView(
-                modifier = Modifier.fillMaxSize(),
-                factory = { context ->
-                    LinearLayout(context).also { linearLayout ->
-                        linearLayout.orientation = LinearLayout.VERTICAL
-                        ComposeView(context).also {
-                            it.setContent {
-                                Box(
-                                    Modifier.size(10.dp)
-                                        .focusProperties { canFocus = true }
-                                        .focusable()
-                                        .testTag("button1")
-                                )
-                            }
-                            linearLayout.addView(it)
-                        }
-                        ComposeView(context).also {
-                            it.setContent {
-                                Box(
-                                    Modifier.size(10.dp)
-                                        .focusProperties { canFocus = true }
-                                        .focusable()
-                                        .testTag("button2")
-                                )
-                            }
-                            linearLayout.addView(it)
-                        }
-                        ComposeView(context).also {
-                            it.setContent {
-                                Box(
-                                    Modifier.size(10.dp)
-                                        .focusProperties { canFocus = true }
-                                        .focusable()
-                                        .testTag("button3")
-                                )
-                            }
-                            linearLayout.addView(it)
-                        }
-                    }
-                }
-            )
-        }
-        rule.onNodeWithTag("button1").requestFocus()
-        rule.onNodeWithTag("button1").assertIsFocused()
-        rule.runOnIdle { focusManager.moveFocus(Down) }
-        rule.onNodeWithTag("button2").assertIsFocused()
-        rule.runOnIdle { focusManager.moveFocus(Down) }
-        rule.onNodeWithTag("button3").assertIsFocused()
-        rule.runOnIdle { focusManager.moveFocus(Up) }
-        rule.onNodeWithTag("button2").assertIsFocused()
-        rule.runOnIdle { focusManager.moveFocus(Up) }
-        rule.onNodeWithTag("button1").assertIsFocused()
-    }
-
-    @Test
-    fun requestFocusFromViewMovesToComposeView() {
-        lateinit var androidButton1: Button
-        lateinit var composeView: View
-        val composeButton = FocusRequester()
-        rule.setContent {
-            composeView = LocalView.current
-            Column(Modifier.fillMaxSize()) {
-                Button(
-                    onClick = {},
-                    Modifier.testTag("button")
-                        .focusProperties { canFocus = true }
-                        .focusRequester(composeButton)
-                ) {
-                    Text("Compose Button")
-                }
-                AndroidView(
-                    factory = { context ->
-                        LinearLayout(context).also { linearLayout ->
-                            linearLayout.orientation = LinearLayout.VERTICAL
-                            linearLayout.addView(
-                                Button(context).apply {
-                                    setText("Android Button")
-                                    isFocusableInTouchMode = true
-                                    androidButton1 = this
-                                }
-                            )
-                            linearLayout.addView(
-                                Button(context).apply {
-                                    setText("Android Button 2")
-                                    isFocusableInTouchMode = true
-                                }
-                            )
-                        }
-                    }
-                )
-            }
-        }
-
-        for (direction in arrayOf(Left, Up, Right, Down, Next, Previous)) {
-            rule.runOnIdle { androidButton1.requestFocus() }
-
-            rule.runOnIdle {
-                assertThat(androidButton1.isFocused).isTrue()
-                composeButton.requestFocus(direction)
-            }
-
-            rule.onNodeWithTag("button").assertIsFocused()
-
-            rule.runOnIdle {
-                assertThat(composeView.isFocused).isTrue()
-                assertThat(androidButton1.isFocused).isFalse()
-            }
-        }
-    }
-
     private fun View.getFocusedRect() =
         AndroidRect().run {
             rule.runOnIdle { getFocusedRect(this) }
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/OwnerFocusTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/OwnerFocusTest.kt
index 334d095..71f1d7e 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/OwnerFocusTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/OwnerFocusTest.kt
@@ -248,8 +248,7 @@
 
         // Assert.
         rule.runOnIdle {
-            // b/369256395 we must return true when we advertise that we're focusable
-            assertThat(success).isTrue()
+            assertThat(success).isFalse()
             assertThat(ownerView.isFocused).isFalse()
         }
     }
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 ad5617d..5b17e1c3 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
@@ -107,6 +107,7 @@
 import androidx.compose.ui.focus.focusRect
 import androidx.compose.ui.focus.is1dFocusSearch
 import androidx.compose.ui.focus.isBetterCandidate
+import androidx.compose.ui.focus.requestFocus
 import androidx.compose.ui.focus.requestInteropFocus
 import androidx.compose.ui.focus.toAndroidFocusDirection
 import androidx.compose.ui.focus.toFocusDirection
@@ -325,90 +326,28 @@
     override val windowInfo: WindowInfo
         get() = _windowInfo
 
-    /**
-     * Because AndroidComposeView always accepts focus, we have to divert focus to another View if
-     * there is nothing focusable within. However, if there are only nonfocusable ComposeViews, then
-     * the redirection can recurse infinitely. This makes sure that if that happens, then it can
-     * bail when it is detected
-     */
-    private var processingRequestFocusForNextNonChildView = false
-
     // When move focus is triggered by a key event, and move focus does not cause any focus change,
     // we return the key event to the view system if focus search finds a suitable view which is not
     // a compose sub-view. However if move focus is triggered programmatically, we have to manually
     // implement this behavior because the view system does not have a moveFocus API.
     private fun onMoveFocusInChildren(focusDirection: FocusDirection): Boolean {
-        @OptIn(ExperimentalComposeUiApi::class)
-        if (!ComposeUiFlags.isViewFocusFixEnabled) {
-            // The view system does not have an API corresponding to Enter/Exit.
-            if (focusDirection == Enter || focusDirection == Exit) return false
 
-            val direction =
-                checkNotNull(focusDirection.toAndroidFocusDirection()) { "Invalid focus direction" }
-            val focusedRect = onFetchFocusRect()?.toAndroidRect()
-
-            val nextView =
-                FocusFinder.getInstance().let {
-                    if (focusedRect == null) {
-                        it.findNextFocus(this, findFocus(), direction)
-                    } else {
-                        it.findNextFocusFromRect(this, focusedRect, direction)
-                    }
-                }
-            return nextView?.requestInteropFocus(direction, focusedRect) ?: false
-        }
         // The view system does not have an API corresponding to Enter/Exit.
-        if (focusDirection == Enter || focusDirection == Exit || !hasFocus()) return false
-
-        val androidViewsHandler = _androidViewsHandler ?: return false
+        if (focusDirection == Enter || focusDirection == Exit) return false
 
         val direction =
             checkNotNull(focusDirection.toAndroidFocusDirection()) { "Invalid focus direction" }
+        val focusedRect = onFetchFocusRect()?.toAndroidRect()
 
-        val root = rootView as ViewGroup
-
-        val currentFocus = root.findFocus() ?: error("view hasFocus but root can't find it")
-
-        val focusFinder = FocusFinder.getInstance()
-        val nextView: View?
-        val focusedRect: Rect?
-        if (focusDirection.is1dFocusSearch() && androidViewsHandler.hasFocus()) {
-            focusedRect = null
-            if (SDK_INT >= O) {
-                // On newer devices, the focus is normal and we can expect forward/next to work
-                nextView = focusFinder.findNextFocus(root, currentFocus, direction)
-            } else {
-                // On older devices, FocusFinder doesn't properly order Views, so we have to use
-                // a copy of the focus finder the corrects the order
-                nextView = FocusFinderCompat.instance.findNextFocus1d(root, currentFocus, direction)
+        val nextView =
+            FocusFinder.getInstance().let {
+                if (focusedRect == null) {
+                    it.findNextFocus(this, findFocus(), direction)
+                } else {
+                    it.findNextFocusFromRect(this, focusedRect, direction)
+                }
             }
-        } else {
-            focusedRect = onFetchFocusRect()?.toAndroidRect()
-            nextView = focusFinder.findNextFocusFromRect(root, focusedRect, direction)
-            nextView?.getLocationInWindow(tmpPositionArray)
-            val nextPositionX = tmpPositionArray[0]
-            val nextPositionY = tmpPositionArray[1]
-            getLocationInWindow(tmpPositionArray)
-            focusedRect?.offset(
-                tmpPositionArray[0] - nextPositionX,
-                tmpPositionArray[1] - nextPositionY
-            )
-        }
-
-        // is it part of the compose hierarchy?
-        if (nextView == null || nextView === currentFocus) {
-            return false
-        }
-
-        val focusedChild = androidViewsHandler.focusedChild
-        var nextParent = nextView.parent
-        while (nextParent != null && nextParent !== focusedChild) {
-            nextParent = nextParent.parent
-        }
-        if (nextParent == null) {
-            return false // not a part of the compose hierarchy
-        }
-        return nextView.requestInteropFocus(direction, focusedRect)
+        return nextView?.requestInteropFocus(direction, focusedRect) ?: false
     }
 
     // If this root view is focused, we can get the focus rect from focusOwner. But if a sub-view
@@ -429,16 +368,6 @@
             val focusDirection = getFocusDirection(keyEvent)
             if (focusDirection == null || keyEvent.type != KeyDown) return@onKeyEvent false
 
-            val androidDirection = focusDirection.toAndroidFocusDirection()
-
-            @OptIn(ExperimentalComposeUiApi::class)
-            if (ComposeUiFlags.isViewFocusFixEnabled) {
-                if (hasFocus() && androidDirection != null) {
-                    // A child AndroidView is focused. See if the view has a child that should be
-                    // focused next.
-                    if (onMoveFocusInChildren(focusDirection)) return@onKeyEvent true
-                }
-            }
             val focusedRect = onFetchFocusRect()
 
             // Consume the key event if we moved focus or if focus search or requestFocus is
@@ -459,22 +388,13 @@
             // this view. We don't return false because we don't want to re-visit sub-views. They
             // will
             // instead be visited when the AndroidView around them gets a moveFocus(Enter)).
+            val androidDirection =
+                checkNotNull(focusDirection.toAndroidFocusDirection()) { "Invalid focus direction" }
+            val androidRect = checkNotNull(focusedRect?.toAndroidRect()) { "Invalid rect" }
 
-            if (androidDirection != null) {
-                val nextView = findNextNonChildView(androidDirection).takeIf { it != this }
-                if (nextView != null) {
-                    val androidRect = checkNotNull(focusedRect?.toAndroidRect()) { "Invalid rect" }
-                    nextView.getLocationInWindow(tmpPositionArray)
-                    val nextX = tmpPositionArray[0]
-                    val nextY = tmpPositionArray[1]
-                    getLocationInWindow(tmpPositionArray)
-                    val currentX = tmpPositionArray[0]
-                    val currentY = tmpPositionArray[1]
-                    androidRect.offset(currentX - nextX, currentY - nextY)
-                    if (nextView.requestInteropFocus(androidDirection, androidRect)) {
-                        return@onKeyEvent true
-                    }
-                }
+            val nextView = findNextNonChildView(androidDirection).takeIf { it != this }
+            if (nextView != null && nextView.requestInteropFocus(androidDirection, androidRect)) {
+                return@onKeyEvent true
             }
 
             // Focus finder couldn't find another view. We manually wrap around since focus remained
@@ -498,9 +418,10 @@
 
     private fun findNextNonChildView(direction: Int): View? {
         var currentView: View? = this
-        val focusFinder = FocusFinder.getInstance()
         while (currentView != null) {
-            currentView = focusFinder.findNextFocus(rootView as ViewGroup, currentView, direction)
+            currentView =
+                FocusFinder.getInstance()
+                    .findNextFocus(rootView as ViewGroup, currentView, direction)
             if (currentView != null && !containsDescendant(currentView)) return currentView
         }
         return null
@@ -1014,73 +935,22 @@
     }
 
     override fun requestFocus(direction: Int, previouslyFocusedRect: Rect?): Boolean {
-        @OptIn(ExperimentalComposeUiApi::class)
-        if (!ComposeUiFlags.isViewFocusFixEnabled) {
-            // This view is already focused.
-            if (isFocused) return true
-
-            // If the root has focus, it means a sub-view is focused,
-            // and is trying to move focus within itself.
-            if (focusOwner.rootState.hasFocus) {
-                return super.requestFocus(direction, previouslyFocusedRect)
-            }
-
-            val focusDirection = toFocusDirection(direction) ?: Enter
-            return focusOwner.focusSearch(
-                focusDirection = focusDirection,
-                focusedRect = previouslyFocusedRect?.toComposeRect()
-            ) {
-                it.requestFocus(focusDirection)
-            } ?: false
-        }
         // This view is already focused.
         if (isFocused) return true
 
-        // There is nothing focusable and we've looped around all Views back to this one, so
-        // we just return false to indicate that nothing can be focused.
-        if (processingRequestFocusForNextNonChildView) return false
-
-        val focusDirection = toFocusDirection(direction) ?: Enter
-
         // If the root has focus, it means a sub-view is focused,
         // and is trying to move focus within itself.
-        if (hasFocus() && onMoveFocusInChildren(focusDirection)) return true
-
-        var foundFocusable = false
-        val focusSearchResult =
-            focusOwner.focusSearch(
-                focusDirection = focusDirection,
-                focusedRect = previouslyFocusedRect?.toComposeRect()
-            ) {
-                foundFocusable = true
-                it.requestFocus(focusDirection)
-            }
-        if (focusSearchResult == null) {
-            return false // The focus search was canceled
-        }
-        if (focusSearchResult) {
-            return true // We found something to focus on
-        }
-        if (foundFocusable) {
-            return false // The requestFocus() from within the focusSearch was canceled
+        if (focusOwner.rootState.hasFocus) {
+            return super.requestFocus(direction, previouslyFocusedRect)
         }
 
-        // We advertised ourselves as focusable, but we aren't. Try to just move the focus to the
-        // next item.
-        val nextFocusedView = findNextNonChildView(direction)
-
-        // Can crash if we return false when we've advertised ourselves as focusable and we aren't
-        // b/369256395
-        if (nextFocusedView == null || nextFocusedView === this) {
-            // There is no next View, so just return true so we don't cause a crash
-            return true
-        }
-
-        // Try to focus on the next focusable View
-        processingRequestFocusForNextNonChildView = true
-        val requestFocusResult = nextFocusedView.requestFocus(direction)
-        processingRequestFocusForNextNonChildView = false
-        return requestFocusResult
+        val focusDirection = toFocusDirection(direction) ?: Enter
+        return focusOwner.focusSearch(
+            focusDirection = focusDirection,
+            focusedRect = previouslyFocusedRect?.toComposeRect()
+        ) {
+            it.requestFocus(focusDirection)
+        } ?: false
     }
 
     private fun onRequestFocusForOwner(
@@ -1098,12 +968,7 @@
     }
 
     private fun onClearFocusForOwner() {
-        @OptIn(ExperimentalComposeUiApi::class)
-        if (isFocused || (!ComposeUiFlags.isViewFocusFixEnabled && hasFocus())) {
-            super.clearFocus()
-        } else if (hasFocus()) {
-            // Call clearFocus() on the child that has focus
-            findFocus()?.clearFocus()
+        if (isFocused || hasFocus()) {
             super.clearFocus()
         }
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/FocusFinderCompat.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/FocusFinderCompat.android.kt
deleted file mode 100644
index 9a42b02..0000000
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/FocusFinderCompat.android.kt
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * Copyright 2024 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
-
-import android.annotation.SuppressLint
-import android.content.pm.PackageManager
-import android.graphics.Rect
-import android.os.Build
-import android.view.View
-import android.view.View.FOCUS_BACKWARD
-import android.view.View.FOCUS_FORWARD
-import android.view.ViewGroup
-import androidx.collection.MutableObjectList
-import androidx.collection.ObjectList
-import androidx.collection.mutableObjectIntMapOf
-import androidx.collection.mutableScatterMapOf
-import androidx.collection.mutableScatterSetOf
-import androidx.core.view.isVisible
-import java.util.Collections
-
-/**
- * On devices before [Build.VERSION_CODES.O], [FocusFinder] orders Views incorrectly. This
- * implementation uses the current [FocusFinder] behavior, ordering Views correctly for
- * one-dimensional focus searches.
- *
- * This is copied and simplified from FocusFinder's source. There may be some code that doesn't look
- * quite right in Kotlin as it was copy/pasted with auto-translation.
- */
-internal class FocusFinderCompat {
-    companion object {
-        private val FocusFinderThreadLocal =
-            object : ThreadLocal<FocusFinderCompat>() {
-                override fun initialValue(): FocusFinderCompat {
-                    return FocusFinderCompat()
-                }
-            }
-
-        /** Get the focus finder for this thread. */
-        val instance: FocusFinderCompat
-            get() = FocusFinderThreadLocal.get()!!
-    }
-
-    private val focusedRect: Rect = Rect()
-
-    private val userSpecifiedFocusComparator =
-        UserSpecifiedFocusComparator({ r, v ->
-            if (isValidId(v.nextFocusForwardId)) v.findUserSetNextFocus(r, FOCUS_FORWARD) else null
-        })
-
-    private val tmpList = MutableObjectList<View>()
-
-    // enforce thread local access
-    private fun FocusFinder() {}
-
-    /**
-     * Find the next view to take focus in root's descendants, starting from the view that currently
-     * is focused.
-     *
-     * @param root Contains focused. Cannot be null.
-     * @param focused Has focus now.
-     * @param direction Direction to look.
-     * @return The next focusable view, or null if none exists.
-     */
-    fun findNextFocus1d(root: ViewGroup, focused: View, direction: Int): View? {
-        val effectiveRoot = getEffectiveRoot(root, focused)
-        var next = findNextUserSpecifiedFocus(effectiveRoot, focused, direction)
-        if (next != null) {
-            return next
-        }
-        val focusables = tmpList
-        try {
-            focusables.clear()
-            effectiveRoot.addFocusableViews(focusables, direction)
-            if (!focusables.isEmpty()) {
-                next = findNextFocus(effectiveRoot, focused, direction, focusables)
-            }
-        } finally {
-            focusables.clear()
-        }
-        return next
-    }
-
-    /**
-     * Returns the "effective" root of a view. The "effective" root is the closest ancestor
-     * within-which focus should cycle.
-     *
-     * For example: normal focus navigation would stay within a ViewGroup marked as
-     * touchscreenBlocksFocus and keyboardNavigationCluster until a cluster-jump out.
-     *
-     * @return the "effective" root of {@param focused}
-     */
-    private fun getEffectiveRoot(root: ViewGroup, focused: View?): ViewGroup {
-        if (focused == null || focused === root) {
-            return root
-        }
-        var effective: ViewGroup? = null
-        var nextParent = focused.parent
-        while (nextParent is ViewGroup) {
-            if (nextParent === root) {
-                return effective ?: root
-            }
-            val vg = nextParent
-            if (
-                vg.touchscreenBlocksFocus &&
-                    focused.context.packageManager.hasSystemFeature(
-                        PackageManager.FEATURE_TOUCHSCREEN
-                    )
-            ) {
-                // Don't stop and return here because the cluster could be nested and we only
-                // care about the top-most one.
-                effective = vg
-            }
-            nextParent = nextParent.parent
-        }
-        return root
-    }
-
-    private fun findNextUserSpecifiedFocus(root: ViewGroup, focused: View, direction: Int): View? {
-        // check for user specified next focus
-        var userSetNextFocus: View? = focused.findUserSetNextFocus(root, direction)
-        var cycleCheck = userSetNextFocus
-        var cycleStep = true // we want the first toggle to yield false
-        while (userSetNextFocus != null) {
-            if (
-                userSetNextFocus.isFocusable &&
-                    userSetNextFocus.visibility == View.VISIBLE &&
-                    (!userSetNextFocus.isInTouchMode || userSetNextFocus.isFocusableInTouchMode)
-            ) {
-                return userSetNextFocus
-            }
-            userSetNextFocus = userSetNextFocus.findUserSetNextFocus(root, direction)
-            if ((!cycleStep).also { cycleStep = it }) {
-                cycleCheck = cycleCheck?.findUserSetNextFocus(root, direction)
-                if (cycleCheck === userSetNextFocus) {
-                    // found a cycle, user-specified focus forms a loop and none of the views
-                    // are currently focusable.
-                    break
-                }
-            }
-        }
-        return null
-    }
-
-    private fun findNextFocus(
-        root: ViewGroup,
-        focused: View,
-        direction: Int,
-        focusables: MutableObjectList<View>
-    ): View? {
-        val focusedRect = focusedRect
-        // fill in interesting rect from focused
-        focused.getFocusedRect(focusedRect)
-        root.offsetDescendantRectToMyCoords(focused, focusedRect)
-
-        return findNextFocusInRelativeDirection(focusables, root, focused, direction)
-    }
-
-    @SuppressLint("AsCollectionCall")
-    private fun findNextFocusInRelativeDirection(
-        focusables: MutableObjectList<View>,
-        root: ViewGroup?,
-        focused: View,
-        direction: Int
-    ): View? {
-        try {
-            // Note: This sort is stable.
-            userSpecifiedFocusComparator.setFocusables(focusables, root!!)
-            Collections.sort(focusables.asMutableList(), userSpecifiedFocusComparator)
-        } finally {
-            userSpecifiedFocusComparator.recycle()
-        }
-
-        val count = focusables.size
-        if (count < 2) {
-            return null
-        }
-        var next: View? = null
-        val looped = BooleanArray(1)
-        when (direction) {
-            FOCUS_FORWARD -> next = getNextFocusable(focused, focusables, count, looped)
-            FOCUS_BACKWARD -> next = getPreviousFocusable(focused, focusables, count, looped)
-        }
-        return next ?: focusables[count - 1]
-    }
-
-    private fun getNextFocusable(
-        focused: View,
-        focusables: ObjectList<View>,
-        count: Int,
-        outLooped: BooleanArray
-    ): View? {
-        if (count < 2) {
-            return null
-        }
-        val position = focusables.lastIndexOf(focused)
-        if (position >= 0 && position + 1 < count) {
-            return focusables[position + 1]
-        }
-        outLooped[0] = true
-        return focusables[0]
-    }
-
-    private fun getPreviousFocusable(
-        focused: View?,
-        focusables: ObjectList<View>,
-        count: Int,
-        outLooped: BooleanArray
-    ): View? {
-        if (count < 2) {
-            return null
-        }
-        if (focused != null) {
-            val position = focusables.indexOf(focused)
-            if (position > 0) {
-                return focusables[position - 1]
-            }
-        }
-        outLooped[0] = true
-        return focusables[count - 1]
-    }
-
-    private fun isValidId(id: Int): Boolean {
-        return id != 0 && id != View.NO_ID
-    }
-
-    /**
-     * Sorts views according to any explicitly-specified focus-chains. If there are no explicitly
-     * specified focus chains (eg. no nextFocusForward attributes defined), this should be a no-op.
-     */
-    private class UserSpecifiedFocusComparator(private val mNextFocusGetter: NextFocusGetter) :
-        Comparator<View?> {
-        private val nextFoci = mutableScatterMapOf<View, View>()
-        private val isConnectedTo = mutableScatterSetOf<View>()
-        private val headsOfChains = mutableScatterMapOf<View, View>()
-        private val originalOrdinal = mutableObjectIntMapOf<View>()
-        private var root: View? = null
-
-        fun interface NextFocusGetter {
-            fun get(root: View, view: View): View?
-        }
-
-        fun recycle() {
-            root = null
-            headsOfChains.clear()
-            isConnectedTo.clear()
-            originalOrdinal.clear()
-            nextFoci.clear()
-        }
-
-        fun setFocusables(focusables: ObjectList<View>, root: View) {
-            this.root = root
-            focusables.forEachIndexed { index, view -> originalOrdinal[view] = index }
-
-            for (i in focusables.indices.reversed()) {
-                val view = focusables[i]
-                val next = mNextFocusGetter.get(root, view)
-                if (next != null && originalOrdinal.containsKey(next)) {
-                    nextFoci[view] = next
-                    isConnectedTo.add(next)
-                }
-            }
-
-            for (i in focusables.indices.reversed()) {
-                val view = focusables[i]
-                val next = nextFoci[view]
-                if (next != null && !isConnectedTo.contains(view)) {
-                    setHeadOfChain(view)
-                }
-            }
-        }
-
-        fun setHeadOfChain(head: View) {
-            var newHead = head
-            var view: View? = newHead
-            while (view != null) {
-                val otherHead = headsOfChains[view]
-                if (otherHead != null) {
-                    if (otherHead === newHead) {
-                        return // This view has already had its head set properly
-                    }
-                    // A hydra -- multi-headed focus chain (e.g. A->C and B->C)
-                    // Use the one we've already chosen instead and reset this chain.
-                    view = newHead
-                    newHead = otherHead
-                }
-                headsOfChains[view] = newHead
-                view = nextFoci[view]
-            }
-        }
-
-        override fun compare(first: View?, second: View?): Int {
-            if (first === second) {
-                return 0
-            }
-            if (first == null) {
-                return -1
-            }
-            if (second == null) {
-                return 1
-            }
-            // Order between views within a chain is immaterial -- next/previous is
-            // within a chain is handled elsewhere.
-            val firstHead = headsOfChains[first]
-            val secondHead = headsOfChains[second]
-            if (firstHead === secondHead && firstHead != null) {
-                return if (first === firstHead) {
-                    -1 // first is the head, it should be first
-                } else if (second === firstHead) {
-                    1 // second is the head, it should be first
-                } else if (nextFoci[first] != null) {
-                    -1 // first is not the end of the chain
-                } else {
-                    1 // first is end of chain
-                }
-            }
-            val chainedFirst = firstHead ?: first
-            val chainedSecond = secondHead ?: second
-
-            return if (firstHead != null || secondHead != null) {
-                // keep original order between chains
-                if (originalOrdinal[chainedFirst] < originalOrdinal[chainedSecond]) -1 else 1
-            } else {
-                0
-            }
-        }
-    }
-}
-
-/**
- * If a user manually specified the next view id for a particular direction, use the root to look up
- * the view.
- *
- * @param root The root view of the hierarchy containing this view.
- * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT, FOCUS_FORWARD, or
- *   FOCUS_BACKWARD.
- * @return The user specified next view, or null if there is none.
- */
-private fun View.findUserSetNextFocus(root: View, direction: Int): View? {
-    when (direction) {
-        FOCUS_FORWARD -> {
-            val next = nextFocusForwardId
-            if (next == View.NO_ID) return null
-            return findViewInsideOutShouldExist(root, this, next)
-        }
-        FOCUS_BACKWARD -> {
-            if (id == View.NO_ID) return null
-            val startView: View = this
-            // Since we have forward links but no backward links, we need to find the view that
-            // forward links to this view. We can't just find the view with the specified ID
-            // because view IDs need not be unique throughout the tree.
-            return root.findViewByPredicateInsideOut(startView) { t ->
-                (findViewInsideOutShouldExist(root, t, t.nextFocusForwardId) === startView)
-            }
-        }
-    }
-    return null
-}
-
-private fun findViewInsideOutShouldExist(root: View, start: View, id: Int): View? {
-    return root.findViewByPredicateInsideOut(start) { it.id == id }
-}
-
-/**
- * Look for a child view that matches the specified predicate, starting with the specified view and
- * its descendants and then recursively searching the ancestors and siblings of that view until this
- * view is reached.
- *
- * This method is useful in cases where the predicate does not match a single unique view (perhaps
- * multiple views use the same id) and we are trying to find the view that is "closest" in scope to
- * the starting view.
- *
- * @param start The view to start from.
- * @param predicate The predicate to evaluate.
- * @return The first view that matches the predicate or null.
- */
-private fun View.findViewByPredicateInsideOut(start: View, predicate: (View) -> Boolean): View? {
-    var next = start
-    var childToSkip: View? = null
-    while (true) {
-        val view = next.findViewByPredicateTraversal(predicate, childToSkip)
-        if (view != null || next === this) {
-            return view
-        }
-
-        val parent = next.parent
-        if (parent == null || parent !is View) {
-            return null
-        }
-
-        childToSkip = next
-        next = parent
-    }
-}
-
-/**
- * @param predicate The predicate to evaluate.
- * @param childToSkip If not null, ignores this child during the recursive traversal.
- * @return The first view that matches the predicate or null.
- */
-private fun View.findViewByPredicateTraversal(
-    predicate: (View) -> Boolean,
-    childToSkip: View?
-): View? {
-    if (predicate(this)) {
-        return this
-    }
-    if (this is ViewGroup) {
-        for (i in 0 until childCount) {
-            val child = getChildAt(i)
-            if (child !== childToSkip) {
-                val v = child.findViewByPredicateTraversal(predicate, childToSkip)
-                if (v != null) {
-                    return v
-                }
-            }
-        }
-    }
-    return null
-}
-
-private fun View.addFocusableViews(views: MutableObjectList<View>, direction: Int) {
-    addFocusableViews(views, direction, isInTouchMode)
-}
-
-/**
- * Older versions of View don't add focusable Views in order. This is a corrected version that adds
- * them in the right order.
- */
-private fun View.addFocusableViews(
-    views: MutableObjectList<View>,
-    direction: Int,
-    inTouchMode: Boolean
-) {
-    if (
-        isVisible &&
-            isFocusable &&
-            isEnabled &&
-            width > 0 &&
-            height > 0 &&
-            (!inTouchMode || isFocusableInTouchMode)
-    ) {
-        views += this
-    }
-    if (this is ViewGroup) {
-        for (i in 0 until childCount) {
-            getChildAt(i).addFocusableViews(views, direction, inTouchMode)
-        }
-    }
-}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
index 9c9835e..8b1d1dc9 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
@@ -25,8 +25,6 @@
 import android.view.ViewParent
 import androidx.compose.runtime.ComposeNodeLifecycleCallback
 import androidx.compose.runtime.CompositionContext
-import androidx.compose.ui.ComposeUiFlags
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.InternalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
@@ -420,10 +418,6 @@
             if (view.parent !== this) addView(view)
         }
         layoutNode.onDetach = { owner ->
-            @OptIn(ExperimentalComposeUiApi::class)
-            if (ComposeUiFlags.isViewFocusFixEnabled && hasFocus()) {
-                owner.focusOwner.clearFocus(true)
-            }
             (owner as? AndroidComposeView)?.removeAndroidView(this)
             removeAllViewsInLayout()
         }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/FocusGroupNode.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/FocusGroupNode.android.kt
index 8082b9a..0d25269 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/FocusGroupNode.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/FocusGroupNode.android.kt
@@ -22,8 +22,6 @@
 import android.view.ViewGroup
 import android.view.ViewGroup.FOCUS_DOWN
 import android.view.ViewTreeObserver
-import androidx.compose.ui.ComposeUiFlags
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.InternalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusDirection.Companion.Exit
@@ -88,12 +86,7 @@
 
     val onExit: FocusEnterExitScope.() -> Unit = {
         val embeddedView = getEmbeddedView()
-        @OptIn(ExperimentalComposeUiApi::class)
-        if (ComposeUiFlags.isViewFocusFixEnabled) {
-            if (embeddedView.hasFocus() || embeddedView.isFocused) {
-                embeddedView.clearFocus()
-            }
-        } else if (embeddedView.hasFocus()) {
+        if (embeddedView.hasFocus()) {
             val focusOwner = requireOwner().focusOwner
             val hostView = requireView()
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposeUiFlags.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposeUiFlags.kt
index d518673..728a1bb 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposeUiFlags.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposeUiFlags.kt
@@ -80,10 +80,4 @@
      * the new Autofill APIs and features introduced.
      */
     @Suppress("MutableBareField") @JvmField var isSemanticAutofillEnabled: Boolean = false
-
-    /**
-     * This enables fixes for View focus. The changes are large enough to require a flag to allow
-     * disabling them.
-     */
-    @Suppress("MutableBareField") @JvmField var isViewFocusFixEnabled: Boolean = true
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
index d86a6bc..8741918 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
@@ -17,8 +17,6 @@
 package androidx.compose.ui.focus
 
 import androidx.collection.MutableLongSet
-import androidx.compose.ui.ComposeUiFlags
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.CustomDestinationResult.Cancelled
 import androidx.compose.ui.focus.CustomDestinationResult.None
@@ -204,11 +202,6 @@
      * @return true if focus was moved successfully. false if the focused item is unchanged.
      */
     override fun moveFocus(focusDirection: FocusDirection): Boolean {
-        // First check to see if the focus should move within child Views
-        @OptIn(ExperimentalComposeUiApi::class)
-        if (ComposeUiFlags.isViewFocusFixEnabled && onMoveFocusInterop(focusDirection)) {
-            return true
-        }
         var requestFocusSuccess: Boolean? = false
         val generationBefore = focusTransactionManager.generation
         val focusSearchSuccess =
@@ -244,8 +237,7 @@
         // If we couldn't move focus within compose, we attempt to move focus within embedded views.
         // We don't need this for 1D focus search because the wrap-around logic triggers a
         // focus exit which will perform a focus search among the subviews.
-        @OptIn(ExperimentalComposeUiApi::class)
-        return !ComposeUiFlags.isViewFocusFixEnabled && onMoveFocusInterop(focusDirection)
+        return onMoveFocusInterop(focusDirection)
     }
 
     override fun focusSearch(
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTransactions.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTransactions.kt
index fe97805..7d1b2d6 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTransactions.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTransactions.kt
@@ -16,9 +16,6 @@
 
 package androidx.compose.ui.focus
 
-import androidx.compose.ui.ComposeUiFlags
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.InternalComposeUiApi
 import androidx.compose.ui.focus.CustomDestinationResult.Cancelled
 import androidx.compose.ui.focus.CustomDestinationResult.None
 import androidx.compose.ui.focus.CustomDestinationResult.RedirectCancelled
@@ -32,7 +29,6 @@
 import androidx.compose.ui.node.Nodes.FocusTarget
 import androidx.compose.ui.node.nearestAncestor
 import androidx.compose.ui.node.observeReads
-import androidx.compose.ui.node.requireLayoutNode
 import androidx.compose.ui.node.requireOwner
 
 /**
@@ -62,14 +58,7 @@
                 }
             }
         }
-    if (success) {
-        @OptIn(ExperimentalComposeUiApi::class, InternalComposeUiApi::class)
-        if (ComposeUiFlags.isViewFocusFixEnabled && requireLayoutNode().getInteropView() == null) {
-            // This isn't an AndroidView, so we should be focused on this ComposeView
-            requireOwner().focusOwner.requestFocusForOwner(FocusDirection.Next, null)
-        }
-        dispatchFocusCallbacks()
-    }
+    if (success) dispatchFocusCallbacks()
     return success
 }