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

Commit 7c9550ab authored by Helen Qin's avatar Helen Qin
Browse files

Handle empty input gracefully.

Bug: 268209738
Test: local verification
Change-Id: I425f3bd979145c742b6c32ee965417510e0f6431
parent 8a7ef108
Loading
Loading
Loading
Loading
+22 −17
Original line number Original line Diff line number Diff line
@@ -73,7 +73,7 @@ class CredentialManagerRepo(
        requestInfo = intent.extras?.getParcelable(
        requestInfo = intent.extras?.getParcelable(
            RequestInfo.EXTRA_REQUEST_INFO,
            RequestInfo.EXTRA_REQUEST_INFO,
            RequestInfo::class.java
            RequestInfo::class.java
        ) ?: testGetRequestInfo()
        ) ?: testCreatePasskeyRequestInfo()


        providerEnabledList = when (requestInfo.type) {
        providerEnabledList = when (requestInfo.type) {
            RequestInfo.TYPE_CREATE ->
            RequestInfo.TYPE_CREATE ->
@@ -115,8 +115,10 @@ class CredentialManagerRepo(
                        providerDisableListUiState,
                        providerDisableListUiState,
                        defaultProviderId,
                        defaultProviderId,
                        requestDisplayInfoUiState,
                        requestDisplayInfoUiState,
                        /** isOnPasskeyIntroStateAlready = */ false,
                        /** isOnPasskeyIntroStateAlready = */
                        isPasskeyFirstUse)!!,
                        false,
                        isPasskeyFirstUse
                    )!!,
                    getCredentialUiState = null,
                    getCredentialUiState = null,
                )
                )
            }
            }
@@ -224,12 +226,14 @@ class CredentialManagerRepo(
                .Builder("io.enpass.app")
                .Builder("io.enpass.app")
                .setSaveEntries(
                .setSaveEntries(
                    listOf<Entry>(
                    listOf<Entry>(
                        CreateTestUtils.newCreateEntry(context,
                        CreateTestUtils.newCreateEntry(
                            context,
                            "key1", "subkey-1", "elisa.beckett@gmail.com",
                            "key1", "subkey-1", "elisa.beckett@gmail.com",
                            20, 7, 27, Instant.ofEpochSecond(10L),
                            20, 7, 27, Instant.ofEpochSecond(10L),
                            "Legal note"
                            "Legal note"
                        ),
                        ),
                        CreateTestUtils.newCreateEntry(context,
                        CreateTestUtils.newCreateEntry(
                            context,
                            "key1", "subkey-2", "elisa.work@google.com",
                            "key1", "subkey-2", "elisa.work@google.com",
                            20, 7, 27, Instant.ofEpochSecond(12L),
                            20, 7, 27, Instant.ofEpochSecond(12L),
                            null
                            null
@@ -244,12 +248,14 @@ class CredentialManagerRepo(
                .Builder("com.dashlane")
                .Builder("com.dashlane")
                .setSaveEntries(
                .setSaveEntries(
                    listOf<Entry>(
                    listOf<Entry>(
                        CreateTestUtils.newCreateEntry(context,
                        CreateTestUtils.newCreateEntry(
                            context,
                            "key1", "subkey-3", "elisa.beckett@dashlane.com",
                            "key1", "subkey-3", "elisa.beckett@dashlane.com",
                            20, 7, 27, Instant.ofEpochSecond(11L),
                            20, 7, 27, Instant.ofEpochSecond(11L),
                            null
                            null
                        ),
                        ),
                        CreateTestUtils.newCreateEntry(context,
                        CreateTestUtils.newCreateEntry(
                            context,
                            "key1", "subkey-4", "elisa.work@dashlane.com",
                            "key1", "subkey-4", "elisa.work@dashlane.com",
                            20, 7, 27, Instant.ofEpochSecond(14L),
                            20, 7, 27, Instant.ofEpochSecond(14L),
                            null
                            null
@@ -343,7 +349,6 @@ class CredentialManagerRepo(
    }
    }





    private fun newRemoteEntry(
    private fun newRemoteEntry(
        key: String,
        key: String,
        subkey: String,
        subkey: String,
@@ -427,7 +432,8 @@ class CredentialManagerRepo(
        val displayInfo = DisplayInfo("my-username00", "Joe")
        val displayInfo = DisplayInfo("my-username00", "Joe")
        data.putBundle(
        data.putBundle(
            "androidx.credentials.BUNDLE_KEY_REQUEST_DISPLAY_INFO",
            "androidx.credentials.BUNDLE_KEY_REQUEST_DISPLAY_INFO",
            displayInfo.toBundle())
            displayInfo.toBundle()
        )
        return RequestInfo.newCreateRequestInfo(
        return RequestInfo.newCreateRequestInfo(
            Binder(),
            Binder(),
            CreateCredentialRequest(
            CreateCredentialRequest(
@@ -445,8 +451,7 @@ class CredentialManagerRepo(
            Binder(),
            Binder(),
            GetCredentialRequest.Builder(
            GetCredentialRequest.Builder(
                Bundle()
                Bundle()
            )
            ).addCredentialOption(
                .addCredentialOption(
                CredentialOption(
                CredentialOption(
                    "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL",
                    "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL",
                    Bundle(),
                    Bundle(),
+5 −3
Original line number Original line Diff line number Diff line
@@ -36,7 +36,9 @@ import com.android.credentialmanager.common.DialogState
import com.android.credentialmanager.common.ProviderActivityResult
import com.android.credentialmanager.common.ProviderActivityResult
import com.android.credentialmanager.common.StartBalIntentSenderForResultContract
import com.android.credentialmanager.common.StartBalIntentSenderForResultContract
import com.android.credentialmanager.createflow.CreateCredentialScreen
import com.android.credentialmanager.createflow.CreateCredentialScreen
import com.android.credentialmanager.createflow.hasContentToDisplay
import com.android.credentialmanager.getflow.GetCredentialScreen
import com.android.credentialmanager.getflow.GetCredentialScreen
import com.android.credentialmanager.getflow.hasContentToDisplay
import com.android.credentialmanager.ui.theme.CredentialSelectorTheme
import com.android.credentialmanager.ui.theme.CredentialSelectorTheme


@ExperimentalMaterialApi
@ExperimentalMaterialApi
@@ -94,13 +96,13 @@ class CredentialSelectorActivity : ComponentActivity() {


        val createCredentialUiState = viewModel.uiState.createCredentialUiState
        val createCredentialUiState = viewModel.uiState.createCredentialUiState
        val getCredentialUiState = viewModel.uiState.getCredentialUiState
        val getCredentialUiState = viewModel.uiState.getCredentialUiState
        if (createCredentialUiState != null) {
        if (createCredentialUiState != null && hasContentToDisplay(createCredentialUiState)) {
            CreateCredentialScreen(
            CreateCredentialScreen(
                viewModel = viewModel,
                viewModel = viewModel,
                createCredentialUiState = createCredentialUiState,
                createCredentialUiState = createCredentialUiState,
                providerActivityLauncher = launcher
                providerActivityLauncher = launcher
            )
            )
        } else if (getCredentialUiState != null) {
        } else if (getCredentialUiState != null && hasContentToDisplay(getCredentialUiState)) {
            GetCredentialScreen(
            GetCredentialScreen(
                viewModel = viewModel,
                viewModel = viewModel,
                getCredentialUiState = getCredentialUiState,
                getCredentialUiState = getCredentialUiState,
@@ -130,7 +132,7 @@ class CredentialSelectorActivity : ComponentActivity() {
    }
    }


    private fun onInitializationError(e: Exception, intent: Intent) {
    private fun onInitializationError(e: Exception, intent: Intent) {
        Log.e(Constants.LOG_TAG, "Failed to show the credential selector", e)
        Log.e(Constants.LOG_TAG, "Failed to show the credential selector; closing the activity", e)
        val resultReceiver = intent.getParcelableExtra(
        val resultReceiver = intent.getParcelableExtra(
            android.credentials.ui.Constants.EXTRA_RESULT_RECEIVER,
            android.credentials.ui.Constants.EXTRA_RESULT_RECEIVER,
            ResultReceiver::class.java
            ResultReceiver::class.java
+9 −2
Original line number Original line Diff line number Diff line
@@ -473,8 +473,14 @@ class CreateFlowUtils {
                        createOptionsPairs.add(Pair(it, enabledProvider))
                        createOptionsPairs.add(Pair(it, enabledProvider))
                    }
                    }
                }
                }
                if (enabledProvider.remoteEntry != null) {
                val currRemoteEntry = enabledProvider.remoteEntry
                    remoteEntry = enabledProvider.remoteEntry!!
                if (currRemoteEntry != null) {
                    if (remoteEntry != null) {
                        // There can only be at most one remote entry
                        Log.d(Constants.LOG_TAG, "Found more than one remote entry.")
                        return null
                    }
                    remoteEntry = currRemoteEntry
                }
                }
            }
            }
            val initialScreenState = toCreateScreenState(
            val initialScreenState = toCreateScreenState(
@@ -500,6 +506,7 @@ class CreateFlowUtils {
                    lastSeenProviderWithNonEmptyCreateOptions,
                    lastSeenProviderWithNonEmptyCreateOptions,
                    /*remoteEntry=*/remoteEntry
                    /*remoteEntry=*/remoteEntry
                ),
                ),
                remoteEntry = remoteEntry,
            )
            )
        }
        }


+41 −37
Original line number Original line Diff line number Diff line
@@ -59,7 +59,6 @@ import com.android.credentialmanager.common.ui.ContainerCard
import com.android.credentialmanager.common.ui.ToggleVisibilityButton
import com.android.credentialmanager.common.ui.ToggleVisibilityButton
import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme


@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Composable
fun CreateCredentialScreen(
fun CreateCredentialScreen(
    viewModel: CredentialSelectorViewModel,
    viewModel: CredentialSelectorViewModel,
@@ -80,10 +79,10 @@ fun CreateCredentialScreen(
                        )
                        )
                        CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard(
                        CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard(
                            requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
                            requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
                            enabledProviderList = createCredentialUiState.enabledProviders,
                            disabledProviderList = createCredentialUiState.disabledProviders,
                            disabledProviderList = createCredentialUiState.disabledProviders,
                            sortedCreateOptionsPairs =
                            sortedCreateOptionsPairs =
                            createCredentialUiState.sortedCreateOptionsPairs,
                            createCredentialUiState.sortedCreateOptionsPairs,
                            hasRemoteEntry = createCredentialUiState.remoteEntry != null,
                            onOptionSelected =
                            onOptionSelected =
                            viewModel::createFlowOnEntrySelectedFromFirstUseScreen,
                            viewModel::createFlowOnEntrySelectedFromFirstUseScreen,
                            onDisabledProvidersSelected =
                            onDisabledProvidersSelected =
@@ -277,9 +276,9 @@ fun ConfirmationCard(
@Composable
@Composable
fun ProviderSelectionCard(
fun ProviderSelectionCard(
    requestDisplayInfo: RequestDisplayInfo,
    requestDisplayInfo: RequestDisplayInfo,
    enabledProviderList: List<EnabledProviderInfo>,
    disabledProviderList: List<DisabledProviderInfo>?,
    disabledProviderList: List<DisabledProviderInfo>?,
    sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
    sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
    hasRemoteEntry: Boolean,
    onOptionSelected: (ActiveEntry) -> Unit,
    onOptionSelected: (ActiveEntry) -> Unit,
    onDisabledProvidersSelected: () -> Unit,
    onDisabledProvidersSelected: () -> Unit,
    onMoreOptionsSelected: () -> Unit,
    onMoreOptionsSelected: () -> Unit,
@@ -302,7 +301,8 @@ fun ProviderSelectionCard(
                        CredentialType.PASSWORD ->
                        CredentialType.PASSWORD ->
                            stringResource(R.string.passwords)
                            stringResource(R.string.passwords)
                        CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info)
                        CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info)
                    }),
                    }
                ),
                style = MaterialTheme.typography.titleMedium,
                style = MaterialTheme.typography.titleMedium,
                modifier = Modifier.padding(horizontal = 24.dp)
                modifier = Modifier.padding(horizontal = 24.dp)
                    .align(alignment = Alignment.CenterHorizontally),
                    .align(alignment = Alignment.CenterHorizontally),
@@ -317,15 +317,14 @@ fun ProviderSelectionCard(
                style = MaterialTheme.typography.bodyLarge,
                style = MaterialTheme.typography.bodyLarge,
                modifier = Modifier.padding(horizontal = 28.dp),
                modifier = Modifier.padding(horizontal = 28.dp),
            )
            )
            Divider(
                thickness = 18.dp,
                color = Color.Transparent
            )
            ContainerCard(
            ContainerCard(
                shape = MaterialTheme.shapes.medium,
                shape = MaterialTheme.shapes.medium,
                modifier = Modifier
                modifier = Modifier.padding(
                    .padding(horizontal = 24.dp)
                    start = 24.dp,
                    .align(alignment = Alignment.CenterHorizontally),
                    end = 24.dp,
                    top = 24.dp,
                    bottom = if (hasRemoteEntry) 24.dp else 16.dp
                ).align(alignment = Alignment.CenterHorizontally),
            ) {
            ) {
                LazyColumn(
                LazyColumn(
                    verticalArrangement = Arrangement.spacedBy(2.dp)
                    verticalArrangement = Arrangement.spacedBy(2.dp)
@@ -356,12 +355,11 @@ fun ProviderSelectionCard(
                    }
                    }
                }
                }
            }
            }
            if (hasRemoteEntry) {
                Divider(
                Divider(
                    thickness = 24.dp,
                    thickness = 24.dp,
                    color = Color.Transparent
                    color = Color.Transparent
                )
                )
            enabledProviderList.forEach { enabledProvider ->
                if (enabledProvider.remoteEntry != null) {
                Row(
                Row(
                    horizontalArrangement = Arrangement.Start,
                    horizontalArrangement = Arrangement.Start,
                    modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
                    modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
@@ -371,13 +369,10 @@ fun ProviderSelectionCard(
                        onMoreOptionsSelected
                        onMoreOptionsSelected
                    )
                    )
                }
                }
                    return@forEach
                }
            }
            }
            Divider(
            Divider(
                thickness = 18.dp,
                thickness = 24.dp,
                color = Color.Transparent,
                color = Color.Transparent,
                modifier = Modifier.padding(bottom = 16.dp)
            )
            )
        }
        }
    }
    }
@@ -412,7 +407,8 @@ fun MoreOptionsSelectionCard(
                                CredentialType.PASSWORD ->
                                CredentialType.PASSWORD ->
                                    stringResource(R.string.password)
                                    stringResource(R.string.password)
                                CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info)
                                CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info)
                            }),
                            }
                        ),
                        style = MaterialTheme.typography.titleMedium,
                        style = MaterialTheme.typography.titleMedium,
                    )
                    )
                },
                },
@@ -653,7 +649,8 @@ fun CreationSelectionCard(
                    text = createOptionInfo.footerDescription,
                    text = createOptionInfo.footerDescription,
                    style = MaterialTheme.typography.bodyLarge,
                    style = MaterialTheme.typography.bodyLarge,
                    modifier = Modifier.padding(
                    modifier = Modifier.padding(
                        start = 29.dp, top = 8.dp, bottom = 18.dp, end = 28.dp)
                        start = 29.dp, top = 8.dp, bottom = 18.dp, end = 28.dp
                    )
                )
                )
            }
            }
            Divider(
            Divider(
@@ -739,7 +736,8 @@ fun MoreAboutPasskeysIntroCard(
                    TextOnSurface(
                    TextOnSurface(
                        text =
                        text =
                        stringResource(
                        stringResource(
                            R.string.more_about_passkeys_title),
                            R.string.more_about_passkeys_title
                        ),
                        style = MaterialTheme.typography.titleMedium,
                        style = MaterialTheme.typography.titleMedium,
                    )
                    )
                },
                },
@@ -865,9 +863,13 @@ fun PrimaryCreateOptionRow(
                            style = MaterialTheme.typography.titleLarge,
                            style = MaterialTheme.typography.titleLarge,
                            modifier = Modifier.padding(top = 16.dp, start = 5.dp),
                            modifier = Modifier.padding(top = 16.dp, start = 5.dp),
                        )
                        )
                        Row(modifier = Modifier.fillMaxWidth().padding(top = 4.dp, bottom = 16.dp,
                        Row(
                                                                       start = 5.dp),
                            modifier = Modifier.fillMaxWidth().padding(
                            verticalAlignment = Alignment.CenterVertically) {
                                top = 4.dp, bottom = 16.dp,
                                start = 5.dp
                            ),
                            verticalAlignment = Alignment.CenterVertically
                        ) {
                            val visualTransformation = remember { PasswordVisualTransformation() }
                            val visualTransformation = remember { PasswordVisualTransformation() }
                            // This subtitle would never be null for create password
                            // This subtitle would never be null for create password
                            val originalPassword by remember {
                            val originalPassword by remember {
@@ -915,7 +917,8 @@ fun PrimaryCreateOptionRow(
                                text = requestDisplayInfo.title,
                                text = requestDisplayInfo.title,
                                style = MaterialTheme.typography.titleLarge,
                                style = MaterialTheme.typography.titleLarge,
                                modifier = Modifier.padding(
                                modifier = Modifier.padding(
                                    top = 16.dp, bottom = 16.dp, start = 5.dp),
                                    top = 16.dp, bottom = 16.dp, start = 5.dp
                                ),
                            )
                            )
                        }
                        }
                    }
                    }
@@ -957,7 +960,8 @@ fun MoreOptionsInfoRow(
                    )
                    )
                }
                }
                if (requestDisplayInfo.type == CredentialType.PASSKEY ||
                if (requestDisplayInfo.type == CredentialType.PASSKEY ||
                    requestDisplayInfo.type == CredentialType.PASSWORD) {
                    requestDisplayInfo.type == CredentialType.PASSWORD
                ) {
                    if (createOptionInfo.passwordCount != null &&
                    if (createOptionInfo.passwordCount != null &&
                        createOptionInfo.passkeyCount != null
                        createOptionInfo.passkeyCount != null
                    ) {
                    ) {
+5 −0
Original line number Original line Diff line number Diff line
@@ -33,9 +33,14 @@ data class CreateCredentialUiState(
  // we're showing provider selection page at the beginning
  // we're showing provider selection page at the beginning
  val hasDefaultProvider: Boolean,
  val hasDefaultProvider: Boolean,
  val activeEntry: ActiveEntry? = null,
  val activeEntry: ActiveEntry? = null,
  val remoteEntry: RemoteInfo? = null,
  val isFromProviderSelection: Boolean? = null,
  val isFromProviderSelection: Boolean? = null,
)
)


internal fun hasContentToDisplay(state: CreateCredentialUiState): Boolean {
    return state.sortedCreateOptionsPairs.isNotEmpty()
}

open class ProviderInfo(
open class ProviderInfo(
  val icon: Drawable,
  val icon: Drawable,
  val id: String,
  val id: String,
Loading