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

Commit 53270521 authored by Qinmei Du's avatar Qinmei Du
Browse files

Update the logic and storage about isFirstTimeUse and defaultProvider

1. Store isDefault provider and the first time use in local ui state
2. Fix the issue that the default provider should not just be null if
we see the passkey intro screen. (The situation for when we first time
see passkey but already set a default provider) And simply the code
structure

Test: deployed locally

Bug: 262215666
Change-Id: I9ab3752c709e528289442f8514e747bb68348a0b
parent 016f2243
Loading
Loading
Loading
Loading
+6 −30
Original line number Diff line number Diff line
@@ -41,8 +41,6 @@ import android.os.Bundle
import android.os.ResultReceiver
import android.service.credentials.CredentialProviderService
import com.android.credentialmanager.createflow.CreateCredentialUiState
import com.android.credentialmanager.createflow.EnabledProviderInfo
import com.android.credentialmanager.createflow.RemoteInfo
import com.android.credentialmanager.getflow.GetCredentialUiState
import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest.Companion.toBundle
import com.android.credentialmanager.jetpack.developer.CreatePublicKeyCredentialRequest
@@ -63,7 +61,7 @@ class CredentialManagerRepo(
    requestInfo = intent.extras?.getParcelable(
      RequestInfo.EXTRA_REQUEST_INFO,
      RequestInfo::class.java
    ) ?: testGetRequestInfo()
    ) ?: testCreatePasskeyRequestInfo()

    providerEnabledList = when (requestInfo.type) {
      RequestInfo.TYPE_CREATE ->
@@ -101,7 +99,7 @@ class CredentialManagerRepo(
  }

  fun onOptionSelected(
    providerPackageName: String,
    providerId: String,
    entryKey: String,
    entrySubkey: String,
    resultCode: Int? = null,
@@ -109,7 +107,7 @@ class CredentialManagerRepo(
  ) {
    val userSelectionDialogResult = UserSelectionDialogResult(
      requestInfo.token,
      providerPackageName,
      providerId,
      entryKey,
      entrySubkey,
      if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
@@ -138,36 +136,15 @@ class CredentialManagerRepo(
    val providerDisabledList = CreateFlowUtils.toDisabledProviderList(
      // Handle runtime cast error
      providerDisabledList, context)
    var defaultProvider: EnabledProviderInfo? = null
    var remoteEntry: RemoteInfo? = null
    var createOptionSize = 0
    var lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo? = null
    providerEnabledList.forEach{providerInfo -> providerInfo.createOptions =
      providerInfo.createOptions.sortedWith(compareBy { it.lastUsedTimeMillis }).reversed()
      if (providerInfo.isDefault) {defaultProvider = providerInfo}
      if (providerInfo.remoteEntry != null) {
        remoteEntry = providerInfo.remoteEntry!!
    }
      if (providerInfo.createOptions.isNotEmpty()) {
        createOptionSize += providerInfo.createOptions.size
        lastSeenProviderWithNonEmptyCreateOptions = providerInfo
      }
    }
    return CreateCredentialUiState(
      enabledProviders = providerEnabledList,
      disabledProviders = providerDisabledList,
      CreateFlowUtils.toCreateScreenState(
        createOptionSize, false,
        requestDisplayInfo, defaultProvider, remoteEntry),
      requestDisplayInfo,
      false,
      CreateFlowUtils.toActiveEntry(
        /*defaultProvider=*/defaultProvider, createOptionSize,
        lastSeenProviderWithNonEmptyCreateOptions, remoteEntry),
    )
    return CreateFlowUtils.toCreateCredentialUiState(
      providerEnabledList, providerDisabledList, requestDisplayInfo, false)
  }

  companion object {
    // TODO: find a way to resolve this static field leak problem
    lateinit var repo: CredentialManagerRepo

    fun setup(
@@ -198,7 +175,6 @@ class CredentialManagerRepo(
        .setRemoteEntry(
          newRemoteEntry("key2", "subkey-1")
        )
        .setIsDefaultProvider(true)
        .build(),
      CreateCredentialProviderData
        .Builder("com.dashlane")
+1 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ class CredentialSelectorActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    CredentialManagerRepo.setup(this, intent)
    UserConfigRepo.setup(this)
    val requestInfo = CredentialManagerRepo.getInstance().requestInfo
    setContent {
      CredentialSelectorTheme {
+52 −13
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ import com.android.credentialmanager.createflow.RequestDisplayInfo
import com.android.credentialmanager.createflow.EnabledProviderInfo
import com.android.credentialmanager.createflow.CreateScreenState
import com.android.credentialmanager.createflow.ActiveEntry
import com.android.credentialmanager.createflow.DisabledProviderInfo
import com.android.credentialmanager.createflow.CreateCredentialUiState
import com.android.credentialmanager.getflow.ActionEntryInfo
import com.android.credentialmanager.getflow.AuthenticationEntryInfo
import com.android.credentialmanager.getflow.CredentialEntryInfo
@@ -208,14 +210,13 @@ class CreateFlowUtils {
        val pkgInfo = packageManager
          .getPackageInfo(packageName!!,
            PackageManager.PackageInfoFlags.of(0))
        com.android.credentialmanager.createflow.EnabledProviderInfo(
        EnabledProviderInfo(
          // TODO: decide what to do when failed to load a provider icon
          icon = pkgInfo.applicationInfo.loadIcon(packageManager)!!,
          name = it.providerFlattenedComponentName,
          displayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString(),
          createOptions = toCreationOptionInfoList(
            it.providerFlattenedComponentName, it.saveEntries, requestDisplayInfo, context),
          isDefault = it.isDefaultProvider,
          remoteEntry = toRemoteInfo(it.providerFlattenedComponentName, it.remoteEntry),
        )
      }
@@ -256,8 +257,7 @@ class CreateFlowUtils {
            createCredentialRequestJetpack.password,
            createCredentialRequestJetpack.type,
            requestInfo.appPackageName,
            context.getDrawable(R.drawable.ic_password)!!,
            requestInfo.isFirstUsage
            context.getDrawable(R.drawable.ic_password)!!
          )
        }
        is CreatePublicKeyCredentialRequest -> {
@@ -275,8 +275,7 @@ class CreateFlowUtils {
            displayName,
            createCredentialRequestJetpack.type,
            requestInfo.appPackageName,
            context.getDrawable(R.drawable.ic_passkey)!!,
            requestInfo.isFirstUsage)
            context.getDrawable(R.drawable.ic_passkey)!!)
        }
        // TODO: correctly parsing for other sign-ins
        else -> {
@@ -285,20 +284,60 @@ class CreateFlowUtils {
            "Elisa Beckett",
            "other-sign-ins",
            requestInfo.appPackageName,
            context.getDrawable(R.drawable.ic_other_sign_in)!!,
            requestInfo.isFirstUsage)
            context.getDrawable(R.drawable.ic_other_sign_in)!!)
        }
      }
    }

    fun toCreateScreenState(
    fun toCreateCredentialUiState(
      enabledProviders: List<EnabledProviderInfo>,
      disabledProviders: List<DisabledProviderInfo>?,
      requestDisplayInfo: RequestDisplayInfo,
      isOnPasskeyIntroStateAlready: Boolean,
    ): CreateCredentialUiState {
      var createOptionSize = 0
      var lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo? = null
      var remoteEntry: RemoteInfo? = null
      var defaultProvider: EnabledProviderInfo? = null
      val defaultProviderId = UserConfigRepo.getInstance().getDefaultProviderId()
      enabledProviders.forEach {
          enabledProvider ->
        if (defaultProviderId != null) {
          if (enabledProvider.name == defaultProviderId) {
            defaultProvider = enabledProvider
          }
        }
        if (enabledProvider.createOptions.isNotEmpty()) {
          createOptionSize += enabledProvider.createOptions.size
          lastSeenProviderWithNonEmptyCreateOptions = enabledProvider
        }
        if (enabledProvider.remoteEntry != null) {
          remoteEntry = enabledProvider.remoteEntry!!
        }
      }
      return CreateCredentialUiState(
        enabledProviders = enabledProviders,
        disabledProviders = disabledProviders,
        toCreateScreenState(
          createOptionSize, isOnPasskeyIntroStateAlready,
          requestDisplayInfo, defaultProvider, remoteEntry),
        requestDisplayInfo,
        isOnPasskeyIntroStateAlready,
        toActiveEntry(
          /*defaultProvider=*/defaultProvider, createOptionSize,
          lastSeenProviderWithNonEmptyCreateOptions, remoteEntry),
      )
    }

    private fun toCreateScreenState(
      createOptionSize: Int,
      isOnPasskeyIntroStateAlready: Boolean,
      requestDisplayInfo: RequestDisplayInfo,
      defaultProvider: EnabledProviderInfo?,
      remoteEntry: RemoteInfo?,
    ): CreateScreenState {
      return if (requestDisplayInfo.isFirstUsage && requestDisplayInfo
      return if (
        UserConfigRepo.getInstance().getIsFirstUse() && requestDisplayInfo
          .type == TYPE_PUBLIC_KEY_CREDENTIAL && !isOnPasskeyIntroStateAlready) {
        CreateScreenState.PASSKEY_INTRO
      } else if (
@@ -318,7 +357,7 @@ class CreateFlowUtils {
      }
    }

   fun toActiveEntry(
    private fun toActiveEntry(
      defaultProvider: EnabledProviderInfo?,
      createOptionSize: Int,
      lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo?,
+70 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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

import android.content.Context
import android.content.SharedPreferences

class UserConfigRepo(context: Context) {
    val sharedPreferences: SharedPreferences = context.getSharedPreferences(
        context.packageName, Context.MODE_PRIVATE)

    fun setDefaultProvider(
        providerId: String
    ) {
        sharedPreferences.edit().apply {
            putString(DEFAULT_PROVIDER, providerId)
            apply()
        }
    }

    fun setIsFirstUse(
        isFirstUse: Boolean
    ) {
        sharedPreferences.edit().apply {
            putBoolean(IS_PASSKEY_FIRST_USE, isFirstUse)
            apply()
        }
    }

    fun getDefaultProviderId(): String? {
        return sharedPreferences.getString(DEFAULT_PROVIDER, null)
    }

    fun getIsFirstUse(): Boolean {
        return sharedPreferences.getBoolean(IS_PASSKEY_FIRST_USE, true)
    }

    companion object {
        lateinit var repo: UserConfigRepo

        const val DEFAULT_PROVIDER = "default_provider"
        // This first use value only applies to passkeys, not related with if generally
        // credential manager is first use or not
        const val IS_PASSKEY_FIRST_USE = "is_passkey_first_use"

        fun setup(
            context: Context,
        ) {
            repo = UserConfigRepo(context)
        }

        fun getInstance(): UserConfigRepo {
            return repo
        }
    }
}
+6 −4
Original line number Diff line number Diff line
@@ -108,7 +108,8 @@ fun CreateCredentialScreen(
                    )
                    CreateScreenState.MORE_OPTIONS_ROW_INTRO -> MoreOptionsRowIntroCard(
                        providerInfo = uiState.activeEntry?.activeProvider!!,
                        onDefaultOrNotSelected = viewModel::onDefaultOrNotSelected
                        onChangeDefaultSelected = viewModel::onChangeDefaultSelected,
                        onUseOnceSelected = viewModel::onUseOnceSelected,
                    )
                    CreateScreenState.EXTERNAL_ONLY_SELECTION -> ExternalOnlySelectionCard(
                        requestDisplayInfo = uiState.requestDisplayInfo,
@@ -464,7 +465,8 @@ fun MoreOptionsSelectionCard(
@Composable
fun MoreOptionsRowIntroCard(
    providerInfo: EnabledProviderInfo,
    onDefaultOrNotSelected: () -> Unit,
    onChangeDefaultSelected: () -> Unit,
    onUseOnceSelected: () -> Unit,
) {
    ContainerCard() {
        Column() {
@@ -496,11 +498,11 @@ fun MoreOptionsRowIntroCard(
            ) {
                CancelButton(
                    stringResource(R.string.use_once),
                    onClick = onDefaultOrNotSelected
                    onClick = onUseOnceSelected
                )
                ConfirmButton(
                    stringResource(R.string.set_as_default),
                    onClick = onDefaultOrNotSelected
                    onClick = onChangeDefaultSelected
                )
            }
            Divider(
Loading