Merge "Make DrawerSheet a FocusGroup" into androidx-main
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/ModalNavigationDrawerTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/ModalNavigationDrawerTest.kt
index ba9bc9b..a522dc83 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/ModalNavigationDrawerTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/ModalNavigationDrawerTest.kt
@@ -109,8 +109,8 @@
                 drawerState = navigationDrawerValue,
                 drawerContent = {
                     BasicText(
-                        text =
-                        if (it == DrawerValue.Open) "Opened" else "Closed"
+                        modifier = Modifier.focusable(),
+                        text = if (it == DrawerValue.Open) "Opened" else "Closed"
                     )
                 }) { BasicText("other content") }
         }
@@ -138,7 +138,10 @@
                         .focusable(false),
                     drawerState = navigationDrawerValue,
                     drawerContent = {
-                        BasicText(text = if (it == DrawerValue.Open) "Opened" else "Closed")
+                        BasicText(
+                            modifier = Modifier.focusable(),
+                            text = if (it == DrawerValue.Open) "Opened" else "Closed"
+                        )
                     }) {
                     Box(modifier = Modifier.focusable()) {
                         BasicText("Button")
@@ -154,7 +157,7 @@
         rule.onAllNodesWithText("Closed").assertAnyAreDisplayed()
     }
 
-    @OptIn(ExperimentalComposeUiApi::class, ExperimentalTestApi::class)
+    @OptIn(ExperimentalTestApi::class)
     @Test
     fun modalNavigationDrawer_focusMovesIntoDrawer_openStateComposableDisplayed() {
         InstrumentationRegistry.getInstrumentation().setInTouchMode(false)
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/NavigationDrawerTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/NavigationDrawerTest.kt
index d1aedfa..c7d8840 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/NavigationDrawerTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/NavigationDrawerTest.kt
@@ -105,8 +105,8 @@
                 drawerState = navigationDrawerValue,
                 drawerContent = {
                     BasicText(
-                        text =
-                        if (it == DrawerValue.Open) "Opened" else "Closed"
+                        modifier = Modifier.focusable(),
+                        text = if (it == DrawerValue.Open) "Opened" else "Closed"
                     )
                 }) { BasicText("other content") }
         }
@@ -132,7 +132,10 @@
                     modifier = Modifier.focusRequester(drawerFocusRequester),
                     drawerState = navigationDrawerValue,
                     drawerContent = {
-                        BasicText(text = if (it == DrawerValue.Open) "Opened" else "Closed")
+                        BasicText(
+                            text = if (it == DrawerValue.Open) "Opened" else "Closed",
+                            modifier = Modifier.focusable()
+                        )
                     }) {
                     Box(modifier = Modifier.focusable()) {
                         BasicText("Button")
@@ -335,7 +338,10 @@
                 modifier = Modifier.focusRequester(drawerFocusRequester),
                 drawerState = navigationDrawerValue,
                 drawerContent = {
-                    BasicText(text = if (it == DrawerValue.Open) "Opened" else "Closed")
+                    BasicText(
+                        modifier = Modifier.focusable(),
+                        text = if (it == DrawerValue.Open) "Opened" else "Closed"
+                    )
                 }) { BasicText("other content") }
         }
 
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/NavigationDrawer.kt b/tv/tv-material/src/main/java/androidx/tv/material3/NavigationDrawer.kt
index 4d00127..ac34cff 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/NavigationDrawer.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/NavigationDrawer.kt
@@ -16,10 +16,10 @@
 
 package androidx.tv.material3
 
-import android.view.KeyEvent.KEYCODE_BACK
 import androidx.compose.animation.animateContentSize
 import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.focusable
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.focusGroup
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxHeight
@@ -35,28 +35,16 @@
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusDirection
-import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.FocusState
-import androidx.compose.ui.focus.focusProperties
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.input.key.KeyEventType.Companion.KeyDown
-import androidx.compose.ui.input.key.key
-import androidx.compose.ui.input.key.nativeKeyCode
-import androidx.compose.ui.input.key.onKeyEvent
-import androidx.compose.ui.input.key.type
 import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalFocusManager
-import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection.Ltr
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.zIndex
 
@@ -96,20 +84,10 @@
     scrimColor: Color = LocalColorScheme.current.scrim.copy(alpha = 0.5f),
     content: @Composable () -> Unit
 ) {
-    val layoutDirection = LocalLayoutDirection.current
     val localDensity = LocalDensity.current
-    val exitDirection =
-        if (layoutDirection == Ltr) FocusDirection.Right else FocusDirection.Left
-    val drawerFocusRequester = remember { FocusRequester() }
     val closedDrawerWidth: MutableState<Dp?> = remember { mutableStateOf(null) }
     val internalDrawerModifier =
         Modifier
-            .modalDrawerNavigation(
-                drawerFocusRequester = drawerFocusRequester,
-                exitDirection = exitDirection,
-                drawerState = drawerState,
-                focusManager = LocalFocusManager.current
-            )
             .zIndex(Float.MAX_VALUE)
             .onSizeChanged {
                 if (closedDrawerWidth.value == null &&
@@ -252,31 +230,7 @@
 }
 
 @Suppress("IllegalExperimentalApiUsage") // TODO (b/233188423): Address before moving to beta
-@OptIn(ExperimentalComposeUiApi::class, ExperimentalTvMaterial3Api::class)
-private fun Modifier.modalDrawerNavigation(
-    drawerFocusRequester: FocusRequester,
-    exitDirection: FocusDirection,
-    drawerState: DrawerState,
-    focusManager: FocusManager
-): Modifier {
-    return this
-        .focusRequester(drawerFocusRequester)
-        .focusProperties {
-            exit = {
-                if (it == exitDirection) {
-                    drawerFocusRequester.requestFocus()
-                    drawerState.setValue(DrawerValue.Closed)
-                    focusManager.moveFocus(it)
-                    FocusRequester.Cancel
-                } else {
-                    FocusRequester.Default
-                }
-            }
-        }
-}
-
-@Suppress("IllegalExperimentalApiUsage") // TODO (b/233188423): Address before moving to beta
-@OptIn(ExperimentalComposeUiApi::class, ExperimentalTvMaterial3Api::class)
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalFoundationApi::class)
 @Composable
 private fun DrawerSheet(
     modifier: Modifier = Modifier,
@@ -287,12 +241,7 @@
     // indicates that the drawer has been set to its initial state and has grabbed focus if
     // necessary. Controls whether focus is used to decide the state of the drawer going forward.
     var initializationComplete: Boolean by remember { mutableStateOf(false) }
-    val focusManager = LocalFocusManager.current
     var focusState by remember { mutableStateOf<FocusState?>(null) }
-
-    val isDrawerOpen = drawerState.currentValue == DrawerValue.Open
-    val isDrawerClosed = drawerState.currentValue == DrawerValue.Closed
-
     val focusRequester = remember { FocusRequester() }
     LaunchedEffect(key1 = drawerState.currentValue) {
         if (drawerState.currentValue == DrawerValue.Open && focusState?.hasFocus == false) {
@@ -312,25 +261,12 @@
             .then(modifier)
             .onFocusChanged {
                 focusState = it
-                when {
-                    it.isFocused && isDrawerClosed -> {
-                        drawerState.setValue(DrawerValue.Open)
-                        focusManager.moveFocus(FocusDirection.Enter)
-                    }
 
-                    !it.hasFocus && isDrawerOpen && initializationComplete -> {
-                        drawerState.setValue(DrawerValue.Closed)
-                    }
+                if (initializationComplete) {
+                    drawerState.setValue(if (it.hasFocus) DrawerValue.Open else DrawerValue.Closed)
                 }
             }
-            .onKeyEvent {
-                // Handle back press key event
-                if (it.key.nativeKeyCode == KEYCODE_BACK && it.type == KeyDown) {
-                    focusManager.moveFocus(FocusDirection.Exit)
-                }
-                KeyEventPropagation.ContinuePropagation
-            }
-            .focusable()
+            .focusGroup()
 
     Box(modifier = internalModifier) { content.invoke(drawerState.currentValue) }
 }