Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 293e456a authored by Shuang Hao's avatar Shuang Hao Committed by Android (Google) Code Review
Browse files

Merge "Add auto selection support. Move entry selection logic to flow Engine...

Merge "Add auto selection support. Move entry selection logic to flow Engine to be shared between screens. demo: http://shortn/_GYz5GifqrK" into main
parents 39d31754 79fa94fb
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -47,8 +47,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)
+25 −0
Original line number Diff line number Diff line
@@ -29,10 +29,14 @@ import com.android.credentialmanager.model.get.AuthenticationEntryInfo
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
@@ -47,6 +51,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,
@@ -108,6 +114,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 {
+12 −0
Original line number Diff line number Diff line
@@ -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
+8 −0
Original line number Diff line number Diff line
@@ -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()
+2 −15
Original line number Diff line number Diff line
@@ -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() }
            }