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 Original line Diff line number Diff line
@@ -43,6 +43,9 @@ import org.json.JSONException
import android.widget.inline.InlinePresentationSpec
import android.widget.inline.InlinePresentationSpec
import androidx.autofill.inline.v1.InlineSuggestionUi
import androidx.autofill.inline.v1.InlineSuggestionUi
import com.android.credentialmanager.GetFlowUtils
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 org.json.JSONObject
import java.util.concurrent.Executors
import java.util.concurrent.Executors


@@ -114,10 +117,14 @@ class CredentialAutofillService : AutofillService() {
        val providerList = GetFlowUtils.toProviderList(
        val providerList = GetFlowUtils.toProviderList(
                getCredResponse.candidateProviderDataList,
                getCredResponse.candidateProviderDataList,
                this@CredentialAutofillService)
                this@CredentialAutofillService)
        if (providerList.isEmpty()) {
            return null
        }
        var totalEntryCount = 0
        var totalEntryCount = 0
        providerList.forEach { provider ->
        providerList.forEach { provider ->
            totalEntryCount += provider.credentialEntryList.size
            totalEntryCount += provider.credentialEntryList.size
        }
        }
        val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerList)
        val inlineSuggestionsRequest = filLRequest.inlineSuggestionsRequest
        val inlineSuggestionsRequest = filLRequest.inlineSuggestionsRequest
        val inlineMaxSuggestedCount = inlineSuggestionsRequest?.maxSuggestionCount ?: 0
        val inlineMaxSuggestedCount = inlineSuggestionsRequest?.maxSuggestionCount ?: 0
        val inlinePresentationSpecs = inlineSuggestionsRequest?.inlinePresentationSpecs
        val inlinePresentationSpecs = inlineSuggestionsRequest?.inlinePresentationSpecs
@@ -129,15 +136,30 @@ class CredentialAutofillService : AutofillService() {
        var i = 0
        var i = 0
        val fillResponseBuilder = FillResponse.Builder()
        val fillResponseBuilder = FillResponse.Builder()
        var emptyFillResponse = true
        var emptyFillResponse = true
        providerList.forEach {provider ->

            // TODO(b/299321128): Before iterating the list, sort the list so that
        providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@ {
            //  the relevant entries don't get truncated
            val primaryEntry = it.sortedCredentialEntryList.first()
            provider.credentialEntryList.forEach entryLoop@ {entry ->
            // In regular CredMan bottomsheet, only one primary entry per username is displayed.
                val autofillId: AutofillId? = entry.fillInIntent?.getParcelableExtra(
            // 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,
                                CredentialProviderService.EXTRA_AUTOFILL_ID,
                                AutofillId::class.java)
                                AutofillId::class.java)
                val pendingIntent = entry.pendingIntent
                val pendingIntent = credentialEntry.pendingIntent
                if (autofillId == null || pendingIntent == null) {
                if (autofillId == null || pendingIntent == null) {
                    Log.e(TAG, "AutofillId or pendingIntent was missing from the entry.")
                    return@entryLoop
                    return@entryLoop
                }
                }
                var inlinePresentation: InlinePresentation? = null
                var inlinePresentation: InlinePresentation? = null
@@ -151,7 +173,7 @@ class CredentialAutofillService : AutofillService() {
                    }
                    }
                    val sliceBuilder = InlineSuggestionUi
                    val sliceBuilder = InlineSuggestionUi
                            .newContentBuilder(pendingIntent)
                            .newContentBuilder(pendingIntent)
                            .setTitle(entry.userName)
                            .setTitle(credentialEntry.userName)
                    inlinePresentation = InlinePresentation(
                    inlinePresentation = InlinePresentation(
                            sliceBuilder.build().slice, spec, /* pinned= */ false)
                            sliceBuilder.build().slice, spec, /* pinned= */ false)
                }
                }
@@ -169,8 +191,8 @@ class CredentialAutofillService : AutofillService() {
                                        Field.Builder().setPresentations(
                                        Field.Builder().setPresentations(
                                                presentationBuilder.build())
                                                presentationBuilder.build())
                                                .build())
                                                .build())
                                .setAuthentication(entry.pendingIntent.intentSender)
                                .setAuthentication(pendingIntent.intentSender)
                                .setAuthenticationExtras(entry.fillInIntent.extras)
                                .setAuthenticationExtras(credentialEntry.fillInIntent.extras)
                                .build())
                                .build())
                emptyFillResponse = false
                emptyFillResponse = false
            }
            }
@@ -292,4 +314,15 @@ class CredentialAutofillService : AutofillService() {
        }
        }
        return result
        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 Original line Diff line number Diff line
@@ -203,9 +203,14 @@ enum class GetScreenState {
    UNLOCKED_AUTH_ENTRIES_ONLY,
    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>
    providerInfoList: List<ProviderInfo>
): ProviderDisplayInfo {
): ProviderDisplayInfo {
    val userNameToCredentialEntryMap = mutableMapOf<String, MutableList<CredentialEntryInfo>>()
    val userNameToCredentialEntryMap = mutableMapOf<String, MutableList<CredentialEntryInfo>>()