diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt index a5f227a3adc92d611ed745f263f21f11c0883289..f1d31ba9fbc1fd7c8d4dc657732b5811dff44e4d 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt @@ -46,8 +46,9 @@ import com.android.credentialmanager.model.CredentialType import com.android.credentialmanager.model.get.ProviderInfo import com.android.credentialmanager.model.get.RemoteEntryInfo import com.android.credentialmanager.TAG +import com.android.credentialmanager.model.EntryInfo -fun CredentialEntryInfo.getIntentSenderRequest( +fun EntryInfo.getIntentSenderRequest( isAutoSelected: Boolean = false ): IntentSenderRequest? { val entryIntent = fillInIntent?.putExtra(IS_AUTO_SELECTED_KEY, isAutoSelected) diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt index 88bdc3339db4c65ed38b432b66357ff48a7a1a6c..880038553d63b89e9eaea8572a45eea94465d05b 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt @@ -28,10 +28,14 @@ import com.android.credentialmanager.model.get.ActionEntryInfo import com.android.credentialmanager.model.get.CredentialEntryInfo import com.android.credentialmanager.ui.mappers.toGet import android.util.Log +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.compose.runtime.Composable import com.android.credentialmanager.CredentialSelectorUiState.Cancel import com.android.credentialmanager.CredentialSelectorUiState.Close import com.android.credentialmanager.CredentialSelectorUiState.Create import com.android.credentialmanager.CredentialSelectorUiState.Idle +import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract +import com.android.credentialmanager.ktx.getIntentSenderRequest import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -46,6 +50,8 @@ class CredentialSelectorViewModel @Inject constructor( ) : FlowEngine, ViewModel() { private val isPrimaryScreen = MutableStateFlow(true) private val shouldClose = MutableStateFlow(false) + private lateinit var selectedEntry: EntryInfo + private var isAutoSelected: Boolean = false val uiState: StateFlow<CredentialSelectorUiState> = combine( credentialManagerClient.requests, @@ -107,6 +113,25 @@ class CredentialSelectorViewModel @Inject constructor( ) shouldClose.value = result } + + @Composable + override fun getEntrySelector(): (entry: EntryInfo, isAutoSelected: Boolean) -> Unit { + val launcher = rememberLauncherForActivityResult( + StartBalIntentSenderForResultContract() + ) { + sendSelectionResult(entryInfo = selectedEntry, + resultCode = it.resultCode, + resultData = it.data, + isAutoSelected = isAutoSelected) + } + return { selected, autoSelect -> + selectedEntry = selected + isAutoSelected = autoSelect + selected.getIntentSenderRequest()?.let { + launcher.launch(it) + } ?: Log.w(TAG, "Cannot parse IntentSenderRequest") + } + } } sealed class CredentialSelectorUiState { diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt index e4216446772ba9b9aae9f2725bc6bd0054b3c40d..2e80a7c672f4290d2b88cc5bfbc09a0a2226073b 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt @@ -17,6 +17,8 @@ package com.android.credentialmanager import android.content.Intent +import androidx.activity.result.IntentSenderRequest +import androidx.compose.runtime.Composable import com.android.credentialmanager.model.EntryInfo /** Engine of the credential selecting flow. */ @@ -42,4 +44,14 @@ interface FlowEngine { resultData: Intent? = null, isAutoSelected: Boolean = false, ) + + /** + * Helper function to get an entry selector. + * + * @return selector fun consumes selected [EntryInfo]. Once invoked, [IntentSenderRequest] would + * be launched and invocation of [sendSelectionResult] would happen right after launching result + * coming back. + */ + @Composable + fun getEntrySelector(): (entry: EntryInfo, isAutoSelected: Boolean) -> Unit } \ No newline at end of file diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt index f9a5887158ebd64a1e2172177904ab885cf12c84..405de1d3acc6f619e471202c254d1c234253b4ea 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt @@ -43,6 +43,7 @@ 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.model.EntryInfo import com.android.credentialmanager.ui.screens.multiple.MultiCredentialsFoldScreen @OptIn(ExperimentalHorologistApi::class) @@ -56,6 +57,7 @@ fun WearApp( val swipeToDismissBoxState = rememberSwipeToDismissBoxState() val navHostState = rememberSwipeDismissableNavHostState(swipeToDismissBoxState = swipeToDismissBoxState) + val selectEntry = flowEngine.getEntrySelector() val uiState by viewModel.uiState.collectAsStateWithLifecycle() WearNavScaffold( @@ -111,6 +113,7 @@ fun WearApp( navController = navController, state = state, onCloseApp = onCloseApp, + selectEntry = selectEntry ) } @@ -133,9 +136,14 @@ private fun handleGetNavigation( navController: NavController, state: CredentialSelectorUiState.Get, onCloseApp: () -> Unit, + selectEntry: (entry: EntryInfo, isAutoSelected: Boolean) -> Unit, ) { when (state) { is SingleEntry -> { + if (state.entry.isAutoSelectable) { + selectEntry(state.entry, true) + return + } when (state.entry.credentialType) { CredentialType.UNKNOWN -> { navController.navigateToSignInWithProviderScreen() 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 1f1a296dca9b1c59eeda61c71b57693a5d9f04e2..2ca8ef13c0cf091bb9d23b697d01fe97d52a0352 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 @@ -18,8 +18,6 @@ package com.android.credentialmanager.ui.screens.single.password -import android.util.Log -import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable @@ -28,9 +26,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.android.credentialmanager.FlowEngine import com.android.credentialmanager.R -import com.android.credentialmanager.TAG -import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract -import com.android.credentialmanager.ktx.getIntentSenderRequest import com.android.credentialmanager.ui.components.PasswordRow import com.android.credentialmanager.ui.components.ContinueChip import com.android.credentialmanager.ui.components.DismissChip @@ -57,11 +52,7 @@ fun SinglePasswordScreen( modifier: Modifier = Modifier, flowEngine: FlowEngine, ) { - val launcher = rememberLauncherForActivityResult( - StartBalIntentSenderForResultContract() - ) { - flowEngine.sendSelectionResult(entry, it.resultCode, it.data) - } + val selectEntry = flowEngine.getEntrySelector() SingleAccountScreen( headerContent = { SignInHeader( @@ -80,11 +71,7 @@ fun SinglePasswordScreen( ) { item { Column { - ContinueChip { - entry.getIntentSenderRequest()?.let { - launcher.launch(it) - } ?: Log.w(TAG, "Cannot parse IntentSenderRequest") - } + ContinueChip { selectEntry(entry, false) } SignInOptionsChip{ flowEngine.openSecondaryScreen() } DismissChip { flowEngine.cancel() } }