Loading packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt +16 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,22 @@ fun NavController.navigateToSinglePasswordScreen() { navigateToAsRoot(Screen.SinglePasswordScreen.route) } fun NavController.navigateToSinglePasskeyScreen() { navigateToAsRoot(Screen.SinglePasskeyScreen.route) } fun NavController.navigateToSignInWithProviderScreen() { navigateToAsRoot(Screen.SignInWithProviderScreen.route) } fun NavController.navigateToMultipleCredentialsFoldScreen() { navigateToAsRoot(Screen.MultipleCredentialsScreenFold.route) } fun NavController.navigateToMultipleCredentialsFlattenScreen() { navigateToAsRoot(Screen.MultipleCredentialsScreenFlatten.route) } fun NavController.navigateToAsRoot(route: String) { popBackStack() navigate(route) Loading packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt +8 −0 Original line number Diff line number Diff line Loading @@ -22,4 +22,12 @@ sealed class Screen( data object Loading : Screen("loading") data object SinglePasswordScreen : Screen("singlePasswordScreen") data object SinglePasskeyScreen : Screen("singlePasskeyScreen") data object SignInWithProviderScreen : Screen("signInWithProviderScreen") data object MultipleCredentialsScreenFold : Screen("multipleCredentialsScreenFold") data object MultipleCredentialsScreenFlatten : Screen("multipleCredentialsScreenFlatten") } packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt +43 −3 Original line number Diff line number Diff line Loading @@ -27,14 +27,20 @@ import androidx.wear.compose.navigation.rememberSwipeDismissableNavController import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState import com.android.credentialmanager.CredentialSelectorUiState import com.android.credentialmanager.CredentialSelectorUiState.Get.SingleEntry import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntry import com.android.credentialmanager.CredentialSelectorViewModel import com.android.credentialmanager.ui.screens.LoadingScreen import com.android.credentialmanager.ui.screens.single.passkey.SinglePasskeyScreen import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen import com.android.credentialmanager.ui.screens.single.signInWithProvider.SignInWithProviderScreen import com.google.android.horologist.annotations.ExperimentalHorologistApi import com.google.android.horologist.compose.navscaffold.WearNavScaffold import com.google.android.horologist.compose.navscaffold.composable import com.google.android.horologist.compose.navscaffold.scrollable import com.android.credentialmanager.model.CredentialType import com.android.credentialmanager.ui.screens.multiple.MultiCredentialsFoldScreen @OptIn(ExperimentalHorologistApi::class) @Composable fun WearApp( viewModel: CredentialSelectorViewModel, Loading @@ -59,6 +65,27 @@ fun WearApp( scrollable(Screen.SinglePasswordScreen.route) { SinglePasswordScreen( credentialSelectorUiState = viewModel.uiState.value as SingleEntry, columnState = it.columnState, ) } scrollable(Screen.SinglePasskeyScreen.route) { SinglePasskeyScreen( credentialSelectorUiState = viewModel.uiState.value as SingleEntry, columnState = it.columnState, ) } scrollable(Screen.SignInWithProviderScreen.route) { SignInWithProviderScreen( credentialSelectorUiState = viewModel.uiState.value as SingleEntry, columnState = it.columnState, ) } scrollable(Screen.MultipleCredentialsScreenFold.route) { MultiCredentialsFoldScreen( credentialSelectorUiState = viewModel.uiState.value as MultipleEntry, screenIcon = null, columnState = it.columnState, ) Loading @@ -71,7 +98,6 @@ fun WearApp( navController.navigateToLoading() } } is CredentialSelectorUiState.Get -> { handleGetNavigation( navController = navController, Loading Loading @@ -103,8 +129,22 @@ private fun handleGetNavigation( ) { when (state) { is SingleEntry -> { when (state.entry.credentialType) { CredentialType.UNKNOWN -> { navController.navigateToSignInWithProviderScreen() } CredentialType.PASSKEY -> { navController.navigateToSinglePasskeyScreen() } CredentialType.PASSWORD -> { navController.navigateToSinglePasswordScreen() } } } is CredentialSelectorUiState.Get.MultipleEntry -> { navController.navigateToMultipleCredentialsFoldScreen() } else -> { // TODO: b/301206470 - Implement other get flows Loading packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt +3 −9 Original line number Diff line number Diff line Loading @@ -23,17 +23,12 @@ import com.android.credentialmanager.model.CredentialType import com.android.credentialmanager.model.get.CredentialEntryInfo fun Request.Get.toGet(isPrimary: Boolean): CredentialSelectorUiState.Get { // TODO: b/301206470 returning a hard coded state for MVP if (true) return CredentialSelectorUiState.Get.SingleEntry( providerInfos .flatMap { it.credentialEntryList } .first { it.credentialType == CredentialType.PASSWORD } ) val accounts = providerInfos .flatMap { it.credentialEntryList } .groupBy { it.userName} .entries .toList() return if (isPrimary) { if (accounts.size == 1) { CredentialSelectorUiState.Get.SingleEntry( Loading @@ -58,5 +53,4 @@ fun Request.Get.toGet(isPrimary: Boolean): CredentialSelectorUiState.Get { val comparator = compareBy<CredentialEntryInfo> { entryInfo -> // Passkey type always go first entryInfo.credentialType.let { if (it == CredentialType.PASSKEY) 0 else 1 } } .thenByDescending{ it.lastUsedTimeMillis } }.thenByDescending { it.lastUsedTimeMillis ?: 0 } packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt +10 −5 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.credentialmanager.ui.screens.single.passkey import android.graphics.drawable.Drawable import androidx.compose.foundation.layout.Column import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.layout.padding Loading Loading @@ -46,11 +45,19 @@ import com.android.credentialmanager.ui.screens.UiState import com.google.android.horologist.annotations.ExperimentalHorologistApi import com.google.android.horologist.compose.layout.ScalingLazyColumnState /** * Screen that shows sign in with provider credential. * * @param credentialSelectorUiState The app bar view model. * @param columnState ScalingLazyColumn configuration to be be applied to SingleAccountScreen * @param modifier styling for composable * @param viewModel ViewModel that updates ui state for this screen * @param navController handles navigation events from this screen */ @OptIn(ExperimentalHorologistApi::class) @Composable fun SinglePasskeyScreen( credentialSelectorUiState: CredentialSelectorUiState.Get.SingleEntry, screenIcon: Drawable?, columnState: ScalingLazyColumnState, modifier: Modifier = Modifier, viewModel: SinglePasskeyScreenViewModel = hiltViewModel(), Loading @@ -64,7 +71,6 @@ fun SinglePasskeyScreen( UiState.CredentialScreen -> { SinglePasskeyScreen( credentialSelectorUiState.entry, screenIcon, columnState, modifier, viewModel Loading Loading @@ -96,7 +102,6 @@ fun SinglePasskeyScreen( @Composable fun SinglePasskeyScreen( entry: CredentialEntryInfo, screenIcon: Drawable?, columnState: ScalingLazyColumnState, modifier: Modifier = Modifier, viewModel: SinglePasskeyScreenViewModel, Loading @@ -104,7 +109,7 @@ fun SinglePasskeyScreen( SingleAccountScreen( headerContent = { SignInHeader( icon = screenIcon, icon = entry.icon, title = stringResource(R.string.use_passkey_title), ) }, Loading Loading
packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt +16 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,22 @@ fun NavController.navigateToSinglePasswordScreen() { navigateToAsRoot(Screen.SinglePasswordScreen.route) } fun NavController.navigateToSinglePasskeyScreen() { navigateToAsRoot(Screen.SinglePasskeyScreen.route) } fun NavController.navigateToSignInWithProviderScreen() { navigateToAsRoot(Screen.SignInWithProviderScreen.route) } fun NavController.navigateToMultipleCredentialsFoldScreen() { navigateToAsRoot(Screen.MultipleCredentialsScreenFold.route) } fun NavController.navigateToMultipleCredentialsFlattenScreen() { navigateToAsRoot(Screen.MultipleCredentialsScreenFlatten.route) } fun NavController.navigateToAsRoot(route: String) { popBackStack() navigate(route) Loading
packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt +8 −0 Original line number Diff line number Diff line Loading @@ -22,4 +22,12 @@ sealed class Screen( data object Loading : Screen("loading") data object SinglePasswordScreen : Screen("singlePasswordScreen") data object SinglePasskeyScreen : Screen("singlePasskeyScreen") data object SignInWithProviderScreen : Screen("signInWithProviderScreen") data object MultipleCredentialsScreenFold : Screen("multipleCredentialsScreenFold") data object MultipleCredentialsScreenFlatten : Screen("multipleCredentialsScreenFlatten") }
packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt +43 −3 Original line number Diff line number Diff line Loading @@ -27,14 +27,20 @@ import androidx.wear.compose.navigation.rememberSwipeDismissableNavController import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState import com.android.credentialmanager.CredentialSelectorUiState import com.android.credentialmanager.CredentialSelectorUiState.Get.SingleEntry import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntry import com.android.credentialmanager.CredentialSelectorViewModel import com.android.credentialmanager.ui.screens.LoadingScreen import com.android.credentialmanager.ui.screens.single.passkey.SinglePasskeyScreen import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen import com.android.credentialmanager.ui.screens.single.signInWithProvider.SignInWithProviderScreen import com.google.android.horologist.annotations.ExperimentalHorologistApi import com.google.android.horologist.compose.navscaffold.WearNavScaffold import com.google.android.horologist.compose.navscaffold.composable import com.google.android.horologist.compose.navscaffold.scrollable import com.android.credentialmanager.model.CredentialType import com.android.credentialmanager.ui.screens.multiple.MultiCredentialsFoldScreen @OptIn(ExperimentalHorologistApi::class) @Composable fun WearApp( viewModel: CredentialSelectorViewModel, Loading @@ -59,6 +65,27 @@ fun WearApp( scrollable(Screen.SinglePasswordScreen.route) { SinglePasswordScreen( credentialSelectorUiState = viewModel.uiState.value as SingleEntry, columnState = it.columnState, ) } scrollable(Screen.SinglePasskeyScreen.route) { SinglePasskeyScreen( credentialSelectorUiState = viewModel.uiState.value as SingleEntry, columnState = it.columnState, ) } scrollable(Screen.SignInWithProviderScreen.route) { SignInWithProviderScreen( credentialSelectorUiState = viewModel.uiState.value as SingleEntry, columnState = it.columnState, ) } scrollable(Screen.MultipleCredentialsScreenFold.route) { MultiCredentialsFoldScreen( credentialSelectorUiState = viewModel.uiState.value as MultipleEntry, screenIcon = null, columnState = it.columnState, ) Loading @@ -71,7 +98,6 @@ fun WearApp( navController.navigateToLoading() } } is CredentialSelectorUiState.Get -> { handleGetNavigation( navController = navController, Loading Loading @@ -103,8 +129,22 @@ private fun handleGetNavigation( ) { when (state) { is SingleEntry -> { when (state.entry.credentialType) { CredentialType.UNKNOWN -> { navController.navigateToSignInWithProviderScreen() } CredentialType.PASSKEY -> { navController.navigateToSinglePasskeyScreen() } CredentialType.PASSWORD -> { navController.navigateToSinglePasswordScreen() } } } is CredentialSelectorUiState.Get.MultipleEntry -> { navController.navigateToMultipleCredentialsFoldScreen() } else -> { // TODO: b/301206470 - Implement other get flows Loading
packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt +3 −9 Original line number Diff line number Diff line Loading @@ -23,17 +23,12 @@ import com.android.credentialmanager.model.CredentialType import com.android.credentialmanager.model.get.CredentialEntryInfo fun Request.Get.toGet(isPrimary: Boolean): CredentialSelectorUiState.Get { // TODO: b/301206470 returning a hard coded state for MVP if (true) return CredentialSelectorUiState.Get.SingleEntry( providerInfos .flatMap { it.credentialEntryList } .first { it.credentialType == CredentialType.PASSWORD } ) val accounts = providerInfos .flatMap { it.credentialEntryList } .groupBy { it.userName} .entries .toList() return if (isPrimary) { if (accounts.size == 1) { CredentialSelectorUiState.Get.SingleEntry( Loading @@ -58,5 +53,4 @@ fun Request.Get.toGet(isPrimary: Boolean): CredentialSelectorUiState.Get { val comparator = compareBy<CredentialEntryInfo> { entryInfo -> // Passkey type always go first entryInfo.credentialType.let { if (it == CredentialType.PASSKEY) 0 else 1 } } .thenByDescending{ it.lastUsedTimeMillis } }.thenByDescending { it.lastUsedTimeMillis ?: 0 }
packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt +10 −5 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.credentialmanager.ui.screens.single.passkey import android.graphics.drawable.Drawable import androidx.compose.foundation.layout.Column import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.layout.padding Loading Loading @@ -46,11 +45,19 @@ import com.android.credentialmanager.ui.screens.UiState import com.google.android.horologist.annotations.ExperimentalHorologistApi import com.google.android.horologist.compose.layout.ScalingLazyColumnState /** * Screen that shows sign in with provider credential. * * @param credentialSelectorUiState The app bar view model. * @param columnState ScalingLazyColumn configuration to be be applied to SingleAccountScreen * @param modifier styling for composable * @param viewModel ViewModel that updates ui state for this screen * @param navController handles navigation events from this screen */ @OptIn(ExperimentalHorologistApi::class) @Composable fun SinglePasskeyScreen( credentialSelectorUiState: CredentialSelectorUiState.Get.SingleEntry, screenIcon: Drawable?, columnState: ScalingLazyColumnState, modifier: Modifier = Modifier, viewModel: SinglePasskeyScreenViewModel = hiltViewModel(), Loading @@ -64,7 +71,6 @@ fun SinglePasskeyScreen( UiState.CredentialScreen -> { SinglePasskeyScreen( credentialSelectorUiState.entry, screenIcon, columnState, modifier, viewModel Loading Loading @@ -96,7 +102,6 @@ fun SinglePasskeyScreen( @Composable fun SinglePasskeyScreen( entry: CredentialEntryInfo, screenIcon: Drawable?, columnState: ScalingLazyColumnState, modifier: Modifier = Modifier, viewModel: SinglePasskeyScreenViewModel, Loading @@ -104,7 +109,7 @@ fun SinglePasskeyScreen( SingleAccountScreen( headerContent = { SignInHeader( icon = screenIcon, icon = entry.icon, title = stringResource(R.string.use_passkey_title), ) }, Loading