diff options
5 files changed, 249 insertions, 2 deletions
diff --git a/packages/CredentialManager/wear/res/values/strings.xml b/packages/CredentialManager/wear/res/values/strings.xml index 4a92936db85f..80b1b30dc26a 100644 --- a/packages/CredentialManager/wear/res/values/strings.xml +++ b/packages/CredentialManager/wear/res/values/strings.xml @@ -33,6 +33,8 @@ <string name="dialog_continue_button">Continue</string> <!-- Content description for the sign in options button of a screen. [CHAR LIMIT=NONE] --> <string name="dialog_sign_in_options_button">Sign-in Options</string> + <!-- Title for multiple credentials folded screen. [CHAR LIMIT=NONE] --> + <string name="sign_in_options_title">Sign-in Options</string> <!-- Title for multiple credentials flattened screen. [CHAR LIMIT=NONE] --> <string name="choose_sign_in_title">Choose a sign in</string> </resources>
\ No newline at end of file diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt new file mode 100644 index 000000000000..11188b436a05 --- /dev/null +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt @@ -0,0 +1,170 @@ +/* + * Copyright (C) 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 com.android.credentialmanager.ui.screens.multiple + +import android.graphics.drawable.Drawable +import com.android.credentialmanager.ui.screens.UiState +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavHostController +import androidx.navigation.compose.rememberNavController +import androidx.wear.compose.material.MaterialTheme +import androidx.wear.compose.material.Text +import com.android.credentialmanager.ui.components.SignInHeader +import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntry +import com.android.credentialmanager.R +import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract +import com.android.credentialmanager.model.get.ActionEntryInfo +import com.android.credentialmanager.model.get.CredentialEntryInfo +import com.android.credentialmanager.ui.components.CredentialsScreenChip +import com.google.android.horologist.annotations.ExperimentalHorologistApi +import com.google.android.horologist.compose.layout.ScalingLazyColumn +import com.google.android.horologist.compose.layout.ScalingLazyColumnState + + +/** + * Screen that shows multiple credentials to select from, grouped by accounts + * + * @param credentialSelectorUiState The app bar view model. + * @param screenIcon The view model corresponding to the home page. + * @param columnState ScalingLazyColumn configuration to be be applied + * @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 MultiCredentialsFlattenScreen( + credentialSelectorUiState: MultipleEntry, + screenIcon: Drawable?, + columnState: ScalingLazyColumnState, + modifier: Modifier = Modifier, + viewModel: MultiCredentialsFlattenViewModel = hiltViewModel(), + navController: NavHostController = rememberNavController(), +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + when (val state = uiState) { + UiState.CredentialScreen -> { + MultiCredentialsFlattenScreen( + state = credentialSelectorUiState, + columnState = columnState, + screenIcon = screenIcon, + onActionEntryClicked = viewModel::onActionEntryClicked, + onCredentialClicked = viewModel::onCredentialClicked, + modifier = modifier, + ) + } + + is UiState.CredentialSelected -> { + val launcher = rememberLauncherForActivityResult( + StartBalIntentSenderForResultContract() + ) { + viewModel.onInfoRetrieved(it.resultCode, null) + } + + SideEffect { + state.intentSenderRequest?.let { + launcher.launch(it) + } + } + } + + UiState.Cancel -> { + navController.popBackStack() + } + } +} + +@OptIn(ExperimentalHorologistApi::class) +@Composable +fun MultiCredentialsFlattenScreen( + state: MultipleEntry, + columnState: ScalingLazyColumnState, + screenIcon: Drawable?, + onActionEntryClicked: (entryInfo: ActionEntryInfo) -> Unit, + onCredentialClicked: (entryInfo: CredentialEntryInfo) -> Unit, + modifier: Modifier, +) { + ScalingLazyColumn( + columnState = columnState, + modifier = modifier.fillMaxSize(), + ) { + item { + // make this credential specific if all credentials are same + SignInHeader( + icon = screenIcon, + title = stringResource(R.string.sign_in_options_title), + ) + } + + state.accounts.forEach { userNameEntries -> + item { + Text( + text = userNameEntries.userName, + modifier = Modifier + .padding(top = 6.dp) + .padding(horizontal = 10.dp), + style = MaterialTheme.typography.title3 + ) + } + + userNameEntries.sortedCredentialEntryList.forEach { credential: CredentialEntryInfo -> + item { + CredentialsScreenChip( + label = credential.userName, + onClick = { onCredentialClicked(credential) }, + secondaryLabel = credential.userName, + icon = credential.icon, + modifier = modifier, + ) + } + } + } + item { + Text( + text = "Manage Sign-ins", + modifier = Modifier + .padding(top = 6.dp) + .padding(horizontal = 10.dp), + style = MaterialTheme.typography.title3 + ) + } + + state.actionEntryList.forEach { + item { + CredentialsScreenChip( + label = it.title, + onClick = { onActionEntryClicked(it) }, + secondaryLabel = null, + icon = it.icon, + modifier = modifier, + ) + } + } + } +} + + diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenViewModel.kt new file mode 100644 index 000000000000..ee5f3f4799d6 --- /dev/null +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenViewModel.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 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 com.android.credentialmanager.ui.screens.multiple + +import android.content.Intent +import android.credentials.selection.ProviderPendingIntentResponse +import android.credentials.selection.UserSelectionDialogResult +import androidx.lifecycle.ViewModel +import com.android.credentialmanager.client.CredentialManagerClient +import com.android.credentialmanager.ktx.getIntentSenderRequest +import com.android.credentialmanager.model.Request +import com.android.credentialmanager.model.get.ActionEntryInfo +import com.android.credentialmanager.model.get.CredentialEntryInfo +import com.android.credentialmanager.ui.screens.UiState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject + +/** ViewModel for [MultiCredentialsFlattenScreen].*/ +@HiltViewModel +class MultiCredentialsFlattenViewModel @Inject constructor( + private val credentialManagerClient: CredentialManagerClient, +) : ViewModel() { + + private lateinit var requestGet: Request.Get + private lateinit var entryInfo: CredentialEntryInfo + + private val _uiState = + MutableStateFlow<UiState>(UiState.CredentialScreen) + val uiState: StateFlow<UiState> = _uiState + + fun onCredentialClicked(entryInfo: CredentialEntryInfo) { + this.entryInfo = entryInfo + _uiState.value = UiState.CredentialSelected( + intentSenderRequest = entryInfo.getIntentSenderRequest() + ) + } + + fun onCancelClicked() { + _uiState.value = UiState.Cancel + } + + fun onInfoRetrieved( + resultCode: Int? = null, + resultData: Intent? = null, + ) { + val userSelectionDialogResult = UserSelectionDialogResult( + requestGet.token, + entryInfo.providerId, + entryInfo.entryKey, + entryInfo.entrySubkey, + if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null + ) + credentialManagerClient.sendResult(userSelectionDialogResult) + } + + fun onActionEntryClicked(actionEntryInfo: ActionEntryInfo) { + // TODO(b/322797032)to be filled out + } +}
\ No newline at end of file diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt index a8be94466a2e..29b9572f7d95 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt index 3f841b8af146..8debecbac599 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 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. |