summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/AccountRow.kt20
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/model/PasskeyUiModel.kt22
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt93
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreenViewModel.kt79
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)
+ }
+}
+