diff options
| author | 2024-02-13 17:43:00 +0000 | |
|---|---|---|
| committer | 2024-02-13 17:43:00 +0000 | |
| commit | aaedac7a48be9c824b3701359e5f0ef2978bc6a9 (patch) | |
| tree | 53e79922213e7a1762a590723e0df59a4d301cf6 | |
| parent | e9d16f3d27e84cb434e806c1059ed07dfcb50bc5 (diff) | |
| parent | fe58cf93c303c2b6af47074cfaa545e8b1e75306 (diff) | |
Merge "Single credential sign in with provider screen changes" into main
3 files changed, 228 insertions, 1 deletions
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/AccountRow.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/AccountRow.kt index b2812d3c245d..8b19e1b659d2 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/AccountRow.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/AccountRow.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/signInWithProvider/SignInWithProviderScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/signInWithProvider/SignInWithProviderScreen.kt new file mode 100644 index 000000000000..c33a863600de --- /dev/null +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/signInWithProvider/SignInWithProviderScreen.kt @@ -0,0 +1,146 @@ +/* + * 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.single.signInWithProvider + +import android.graphics.drawable.Drawable +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.compose.foundation.layout.Column +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 com.android.credentialmanager.CredentialSelectorUiState +import com.android.credentialmanager.model.get.CredentialEntryInfo +import com.android.credentialmanager.R +import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract +import com.android.credentialmanager.ui.components.AccountRow +import com.android.credentialmanager.ui.components.ContinueChip +import com.android.credentialmanager.ui.components.DismissChip +import com.android.credentialmanager.ui.components.SignInHeader +import com.android.credentialmanager.ui.components.SignInOptionsChip +import com.android.credentialmanager.ui.screens.single.SingleAccountScreen +import com.android.credentialmanager.ui.screens.single.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 screenIcon The view model corresponding to the home page. + * @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 SignInWithProviderScreen( + credentialSelectorUiState: CredentialSelectorUiState.Get.SingleEntry, + screenIcon: Drawable?, + columnState: ScalingLazyColumnState, + modifier: Modifier = Modifier, + viewModel: SignInWithProviderViewModel = hiltViewModel(), + navController: NavHostController = rememberNavController(), +) { + viewModel.initialize(credentialSelectorUiState.entry) + + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + when (uiState) { + UiState.CredentialScreen -> { + SignInWithProviderScreen( + credentialSelectorUiState.entry, + screenIcon, + columnState, + modifier, + viewModel + ) + } + + is UiState.CredentialSelected -> { + val launcher = rememberLauncherForActivityResult( + StartBalIntentSenderForResultContract() + ) { + viewModel.onInfoRetrieved(it.resultCode, null) + } + + SideEffect { + (uiState as UiState.CredentialSelected).intentSenderRequest?.let { + launcher.launch(it) + } + } + } + + UiState.Cancel -> { + // TODO(b/322797032) add valid navigation path here for going back + navController.popBackStack() + } + } +} + +@OptIn(ExperimentalHorologistApi::class) +@Composable +fun SignInWithProviderScreen( + entry: CredentialEntryInfo, + screenIcon: Drawable?, + columnState: ScalingLazyColumnState, + modifier: Modifier = Modifier, + viewModel: SignInWithProviderViewModel, +) { + SingleAccountScreen( + headerContent = { + SignInHeader( + icon = screenIcon, + title = stringResource(R.string.use_sign_in_with_provider_title, + entry.providerDisplayName), + ) + }, + accountContent = { + val displayName = entry.displayName + if (displayName != null) { + AccountRow( + primaryText = displayName, + secondaryText = entry.userName, + modifier = Modifier.padding(top = 10.dp), + ) + } else { + AccountRow( + primaryText = entry.userName, + modifier = Modifier.padding(top = 10.dp), + ) + } + }, + columnState = columnState, + modifier = modifier.padding(horizontal = 10.dp) + ) { + item { + Column { + ContinueChip(viewModel::onContinueClick) + SignInOptionsChip(viewModel::onSignInOptionsClick) + DismissChip(viewModel::onDismissClick) + } + } + } +}
\ No newline at end of file diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/signInWithProvider/SignInWithProviderViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/signInWithProvider/SignInWithProviderViewModel.kt new file mode 100644 index 000000000000..2f9e2b1004fe --- /dev/null +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/signInWithProvider/SignInWithProviderViewModel.kt @@ -0,0 +1,81 @@ +/* + * 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.single.signInWithProvider + +import android.content.Intent +import android.credentials.selection.ProviderPendingIntentResponse +import android.credentials.selection.UserSelectionDialogResult +import androidx.annotation.MainThread +import androidx.lifecycle.ViewModel +import com.android.credentialmanager.ktx.getIntentSenderRequest +import com.android.credentialmanager.model.Request +import com.android.credentialmanager.client.CredentialManagerClient +import com.android.credentialmanager.model.get.CredentialEntryInfo +import dagger.hilt.android.lifecycle.HiltViewModel +import com.android.credentialmanager.ui.screens.single.UiState +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject + +/** ViewModel for [SignInWithProviderScreen].*/ +@HiltViewModel +class SignInWithProviderViewModel @Inject constructor( + private val credentialManagerClient: CredentialManagerClient, +) : ViewModel() { + + private val _uiState = + MutableStateFlow<UiState>(UiState.CredentialScreen) + val uiState: StateFlow<UiState> = _uiState + + private lateinit var requestGet: Request.Get + private lateinit var entryInfo: CredentialEntryInfo + + @MainThread + fun initialize(entry: CredentialEntryInfo) { + this.entryInfo = entry + } + + fun onDismissClick() { + _uiState.value = UiState.Cancel + } + + fun onContinueClick() { + _uiState.value = UiState.CredentialSelected( + intentSenderRequest = entryInfo.getIntentSenderRequest() + ) + } + + fun onSignInOptionsClick() { + // TODO(b/322797032) Implement navigation route for single credential screen to multiple + // credentials + } + + 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) + } +} + |