diff options
4 files changed, 174 insertions, 40 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 3ed0c9c505ba..b2812d3c245d 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 @@ -31,7 +31,7 @@ import com.google.android.horologist.compose.tools.WearPreview @Composable fun AccountRow( primaryText: String, - secondaryText: String, + secondaryText: String? = null, modifier: Modifier = Modifier, ) { Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) { @@ -42,14 +42,16 @@ fun AccountRow( maxLines = 1, style = MaterialTheme.typography.title2 ) - Text( - text = secondaryText, - modifier = Modifier.padding(top = 7.dp), - color = Color(0xFFCAC5BC), - overflow = TextOverflow.Ellipsis, - maxLines = 2, - style = MaterialTheme.typography.body1, - ) + if (secondaryText != null) { + Text( + text = secondaryText, + modifier = Modifier.padding(top = 7.dp), + color = Color(0xFFCAC5BC), + overflow = TextOverflow.Ellipsis, + maxLines = 2, + style = MaterialTheme.typography.body1, + ) + } } } diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/model/PasskeyUiModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/model/PasskeyUiModel.kt deleted file mode 100644 index a368de27867a..000000000000 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/model/PasskeyUiModel.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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.0N - * - * 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.model - -data class PasskeyUiModel( - val name: String, - val email: String, -) diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt index 2878b0b8b016..92d8a3993989 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.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. @@ -18,44 +18,119 @@ 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 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 @OptIn(ExperimentalHorologistApi::class) @Composable fun SinglePasskeyScreen( - name: String, - email: String, + credentialSelectorUiState: CredentialSelectorUiState.Get.SingleEntry, + screenIcon: Drawable?, columnState: ScalingLazyColumnState, modifier: Modifier = Modifier, + viewModel: SinglePasskeyScreenViewModel = hiltViewModel(), + navController: NavHostController = rememberNavController(), +) { + viewModel.initialize(credentialSelectorUiState.entry) + + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + when (val state = uiState) { + UiState.CredentialScreen -> { + SinglePasskeyScreen( + credentialSelectorUiState.entry, + screenIcon, + columnState, + modifier, + viewModel + ) + } + + is UiState.CredentialSelected -> { + val launcher = rememberLauncherForActivityResult( + StartBalIntentSenderForResultContract() + ) { + viewModel.onPasskeyInfoRetrieved(it.resultCode, null) + } + + SideEffect { + state.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 SinglePasskeyScreen( + entry: CredentialEntryInfo, + screenIcon: Drawable?, + columnState: ScalingLazyColumnState, + modifier: Modifier = Modifier, + viewModel: SinglePasskeyScreenViewModel, ) { SingleAccountScreen( headerContent = { SignInHeader( - icon = null, + icon = screenIcon, title = stringResource(R.string.use_passkey_title), ) }, accountContent = { - AccountRow( - primaryText = name, - secondaryText = email, - modifier = Modifier.padding(top = 10.dp), - ) + if (entry.displayName != null) { + AccountRow( + primaryText = checkNotNull(entry.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) + } } } } diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreenViewModel.kt new file mode 100644 index 000000000000..35c39f6179d9 --- /dev/null +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreenViewModel.kt @@ -0,0 +1,79 @@ +/* + * 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.passkey + +import android.content.Intent +import android.credentials.selection.UserSelectionDialogResult +import android.credentials.selection.ProviderPendingIntentResponse +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 + +@HiltViewModel +class SinglePasskeyScreenViewModel @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() { + } + + fun onPasskeyInfoRetrieved( + 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) + } +} + |