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

Commit f86cf87a authored by Daniel's avatar Daniel
Browse files

Sort inline suggestions

The sorting method of the inline suggestions will follow the sorting
method of the credman bottomsheet. There are further details to complete
the sorting, like the display limit of 4 or relative ordering of
authentication entries. These changes will be rolled out in the future
cls.

Bug: 305049603
Test: Deployed and tested locally
Change-Id: I67b594cb81083634f314b772663ec8413aeb54fa
parent dcc15f09
Loading
Loading
Loading
Loading
+44 −11
Original line number Diff line number Diff line
@@ -43,6 +43,9 @@ import org.json.JSONException
import android.widget.inline.InlinePresentationSpec
import androidx.autofill.inline.v1.InlineSuggestionUi
import com.android.credentialmanager.GetFlowUtils
import com.android.credentialmanager.getflow.CredentialEntryInfo
import com.android.credentialmanager.getflow.ProviderDisplayInfo
import com.android.credentialmanager.getflow.toProviderDisplayInfo
import org.json.JSONObject
import java.util.concurrent.Executors

@@ -114,10 +117,14 @@ class CredentialAutofillService : AutofillService() {
        val providerList = GetFlowUtils.toProviderList(
                getCredResponse.candidateProviderDataList,
                this@CredentialAutofillService)
        if (providerList.isEmpty()) {
            return null
        }
        var totalEntryCount = 0
        providerList.forEach { provider ->
            totalEntryCount += provider.credentialEntryList.size
        }
        val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerList)
        val inlineSuggestionsRequest = filLRequest.inlineSuggestionsRequest
        val inlineMaxSuggestedCount = inlineSuggestionsRequest?.maxSuggestionCount ?: 0
        val inlinePresentationSpecs = inlineSuggestionsRequest?.inlinePresentationSpecs
@@ -129,15 +136,30 @@ class CredentialAutofillService : AutofillService() {
        var i = 0
        val fillResponseBuilder = FillResponse.Builder()
        var emptyFillResponse = true
        providerList.forEach {provider ->
            // TODO(b/299321128): Before iterating the list, sort the list so that
            //  the relevant entries don't get truncated
            provider.credentialEntryList.forEach entryLoop@ {entry ->
                val autofillId: AutofillId? = entry.fillInIntent?.getParcelableExtra(

        providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@ {
            val primaryEntry = it.sortedCredentialEntryList.first()
            // In regular CredMan bottomsheet, only one primary entry per username is displayed.
            // But since the credential requests from different fields are allocated into a single
            // request for autofill, there will be duplicate primary entries, especially for
            // username/pw autofill fields. These primary entries will be the same entries except
            // their autofillIds will point to different autofill fields. Process all primary
            // fields.
            // TODO(b/307435163): Merge credential options
            it.sortedCredentialEntryList.forEach entryLoop@ { credentialEntry ->
                if (!isSameCredentialEntry(primaryEntry, credentialEntry)) {
                    // Encountering different credential entry means all the duplicate primary
                    // entries have been processed.
                    return@usernameLoop
                }
                val autofillId: AutofillId? = credentialEntry
                        .fillInIntent
                        ?.getParcelableExtra(
                                CredentialProviderService.EXTRA_AUTOFILL_ID,
                                AutofillId::class.java)
                val pendingIntent = entry.pendingIntent
                val pendingIntent = credentialEntry.pendingIntent
                if (autofillId == null || pendingIntent == null) {
                    Log.e(TAG, "AutofillId or pendingIntent was missing from the entry.")
                    return@entryLoop
                }
                var inlinePresentation: InlinePresentation? = null
@@ -151,7 +173,7 @@ class CredentialAutofillService : AutofillService() {
                    }
                    val sliceBuilder = InlineSuggestionUi
                            .newContentBuilder(pendingIntent)
                            .setTitle(entry.userName)
                            .setTitle(credentialEntry.userName)
                    inlinePresentation = InlinePresentation(
                            sliceBuilder.build().slice, spec, /* pinned= */ false)
                }
@@ -169,8 +191,8 @@ class CredentialAutofillService : AutofillService() {
                                        Field.Builder().setPresentations(
                                                presentationBuilder.build())
                                                .build())
                                .setAuthentication(entry.pendingIntent.intentSender)
                                .setAuthenticationExtras(entry.fillInIntent.extras)
                                .setAuthentication(pendingIntent.intentSender)
                                .setAuthenticationExtras(credentialEntry.fillInIntent.extras)
                                .build())
                emptyFillResponse = false
            }
@@ -292,4 +314,15 @@ class CredentialAutofillService : AutofillService() {
        }
        return result
    }

    private fun isSameCredentialEntry(
            info1: CredentialEntryInfo,
            info2: CredentialEntryInfo
    ): Boolean {
        return info1.providerId == info2.providerId &&
                info1.lastUsedTimeMillis == info2.lastUsedTimeMillis &&
                info1.credentialType == info2.credentialType &&
                info1.displayName == info2.displayName &&
                info1.userName == info2.userName
    }
}
 No newline at end of file
+8 −3
Original line number Diff line number Diff line
@@ -203,9 +203,14 @@ enum class GetScreenState {
    UNLOCKED_AUTH_ENTRIES_ONLY,
}

// IMPORTANT: new invocation should be mindful that this method will throw if more than 1 remote
// entry exists
private fun toProviderDisplayInfo(

/**
 * IMPORTANT: new invocation should be mindful that this method will throw if more than 1 remote
 * entry exists
 *
 * @hide
 */
fun toProviderDisplayInfo(
    providerInfoList: List<ProviderInfo>
): ProviderDisplayInfo {
    val userNameToCredentialEntryMap = mutableMapOf<String, MutableList<CredentialEntryInfo>>()