Loading packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt +14 −6 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ import androidx.compose.runtime.remember import androidx.navigation.NavHostController interface NavControllerWrapper { fun navigate(route: String) fun navigate(route: String, popUpCurrent: Boolean = false) fun navigateBack() val highlightEntryId: String? Loading @@ -48,17 +48,17 @@ fun NavHostController.localNavController(): ProvidedValue<NavControllerWrapper> val LocalNavController = compositionLocalOf<NavControllerWrapper> { object : NavControllerWrapper { override fun navigate(route: String) {} override fun navigate(route: String, popUpCurrent: Boolean) {} override fun navigateBack() {} } } @Composable fun navigator(route: String?): () -> Unit { fun navigator(route: String?, popUpCurrent: Boolean = false): () -> Unit { if (route == null) return {} val navController = LocalNavController.current return { navController.navigate(route) } return { navController.navigate(route, popUpCurrent) } } internal class NavControllerWrapperImpl( Loading @@ -68,8 +68,16 @@ internal class NavControllerWrapperImpl( var highlightId: String? = null var sessionName: String? = null override fun navigate(route: String) { navController.navigate(route) override fun navigate(route: String, popUpCurrent: Boolean) { navController.navigate(route) { if (popUpCurrent) { navController.currentDestination?.let { currentDestination -> popUpTo(currentDestination.id) { inclusive = true } } } } } override fun navigateBack() { Loading packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/NavControllerWrapperTest.kt 0 → 100644 +105 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 com.android.settingslib.spa.framework.compose import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settingslib.spa.testutils.waitUntilExists import kotlinx.coroutines.delay import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class NavControllerWrapperTest { @get:Rule val composeTestRule = createComposeRule() @Test fun navigate_canNavigate() { composeTestRule.setContent { TestNavHost { LocalNavController.current.navigate(ROUTE_B) } } composeTestRule.onNodeWithText(ROUTE_B).assertIsDisplayed() } @Test fun navigate_canNavigateBack() { composeTestRule.setContent { TestNavHost { val navController = LocalNavController.current LaunchedEffect(Unit) { navController.navigate(ROUTE_B) delay(100) navController.navigateBack() } } } composeTestRule.waitUntilExists(hasText(ROUTE_A)) } @Test fun navigate_canNavigateAndPopUpCurrent() { composeTestRule.setContent { TestNavHost { val navController = LocalNavController.current LaunchedEffect(Unit) { navController.navigate(ROUTE_B) delay(100) navController.navigate(ROUTE_C, popUpCurrent = true) delay(100) navController.navigateBack() } } } composeTestRule.waitUntilExists(hasText(ROUTE_A)) } private companion object { @Composable fun TestNavHost(content: @Composable () -> Unit) { val navController = rememberNavController() CompositionLocalProvider(navController.localNavController()) { NavHost(navController, ROUTE_A) { composable(route = ROUTE_A) { Text(ROUTE_A) } composable(route = ROUTE_B) { Text(ROUTE_B) } composable(route = ROUTE_C) { Text(ROUTE_C) } } content() } } const val ROUTE_A = "RouteA" const val ROUTE_B = "RouteB" const val ROUTE_C = "RouteC" } } packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FakeNavControllerWrapper.kt +1 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ class FakeNavControllerWrapper : NavControllerWrapper { var navigateCalledWith: String? = null var navigateBackIsCalled = false override fun navigate(route: String) { override fun navigate(route: String, popUpCurrent: Boolean) { navigateCalledWith = route } Loading Loading
packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt +14 −6 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ import androidx.compose.runtime.remember import androidx.navigation.NavHostController interface NavControllerWrapper { fun navigate(route: String) fun navigate(route: String, popUpCurrent: Boolean = false) fun navigateBack() val highlightEntryId: String? Loading @@ -48,17 +48,17 @@ fun NavHostController.localNavController(): ProvidedValue<NavControllerWrapper> val LocalNavController = compositionLocalOf<NavControllerWrapper> { object : NavControllerWrapper { override fun navigate(route: String) {} override fun navigate(route: String, popUpCurrent: Boolean) {} override fun navigateBack() {} } } @Composable fun navigator(route: String?): () -> Unit { fun navigator(route: String?, popUpCurrent: Boolean = false): () -> Unit { if (route == null) return {} val navController = LocalNavController.current return { navController.navigate(route) } return { navController.navigate(route, popUpCurrent) } } internal class NavControllerWrapperImpl( Loading @@ -68,8 +68,16 @@ internal class NavControllerWrapperImpl( var highlightId: String? = null var sessionName: String? = null override fun navigate(route: String) { navController.navigate(route) override fun navigate(route: String, popUpCurrent: Boolean) { navController.navigate(route) { if (popUpCurrent) { navController.currentDestination?.let { currentDestination -> popUpTo(currentDestination.id) { inclusive = true } } } } } override fun navigateBack() { Loading
packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/NavControllerWrapperTest.kt 0 → 100644 +105 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 com.android.settingslib.spa.framework.compose import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settingslib.spa.testutils.waitUntilExists import kotlinx.coroutines.delay import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class NavControllerWrapperTest { @get:Rule val composeTestRule = createComposeRule() @Test fun navigate_canNavigate() { composeTestRule.setContent { TestNavHost { LocalNavController.current.navigate(ROUTE_B) } } composeTestRule.onNodeWithText(ROUTE_B).assertIsDisplayed() } @Test fun navigate_canNavigateBack() { composeTestRule.setContent { TestNavHost { val navController = LocalNavController.current LaunchedEffect(Unit) { navController.navigate(ROUTE_B) delay(100) navController.navigateBack() } } } composeTestRule.waitUntilExists(hasText(ROUTE_A)) } @Test fun navigate_canNavigateAndPopUpCurrent() { composeTestRule.setContent { TestNavHost { val navController = LocalNavController.current LaunchedEffect(Unit) { navController.navigate(ROUTE_B) delay(100) navController.navigate(ROUTE_C, popUpCurrent = true) delay(100) navController.navigateBack() } } } composeTestRule.waitUntilExists(hasText(ROUTE_A)) } private companion object { @Composable fun TestNavHost(content: @Composable () -> Unit) { val navController = rememberNavController() CompositionLocalProvider(navController.localNavController()) { NavHost(navController, ROUTE_A) { composable(route = ROUTE_A) { Text(ROUTE_A) } composable(route = ROUTE_B) { Text(ROUTE_B) } composable(route = ROUTE_C) { Text(ROUTE_C) } } content() } } const val ROUTE_A = "RouteA" const val ROUTE_B = "RouteB" const val ROUTE_C = "RouteC" } }
packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FakeNavControllerWrapper.kt +1 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ class FakeNavControllerWrapper : NavControllerWrapper { var navigateCalledWith: String? = null var navigateBackIsCalled = false override fun navigate(route: String) { override fun navigate(route: String, popUpCurrent: Boolean) { navigateCalledWith = route } Loading