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

Commit 9c2bd2ee authored by Daniel's avatar Daniel
Browse files

Return inline presentations of credential entries

Create inline presentations from the credential entries and return to
Autofill Session.

Test: Built locally
Bug: 299321128
Change-Id: I7641cc92a4e10a1bcce9c7652a039bdd6324f38b
parent 1ade2aac
Loading
Loading
Loading
Loading
+82 −3
Original line number Diff line number Diff line
@@ -27,14 +27,21 @@ import android.os.Bundle
import android.os.CancellationSignal
import android.os.OutcomeReceiver
import android.service.autofill.AutofillService
import android.service.autofill.Dataset
import android.service.autofill.Field
import android.service.autofill.FillCallback
import android.service.autofill.FillRequest
import android.service.autofill.FillResponse
import android.service.autofill.InlinePresentation
import android.service.autofill.Presentations
import android.service.autofill.SaveCallback
import android.service.autofill.SaveRequest
import android.service.credentials.CredentialProviderService
import android.util.Log
import android.view.autofill.AutofillId
import android.widget.inline.InlinePresentationSpec
import androidx.autofill.inline.v1.InlineSuggestionUi
import com.android.credentialmanager.GetFlowUtils
import org.json.JSONObject
import java.util.concurrent.Executors

@@ -74,8 +81,13 @@ class CredentialAutofillService : AutofillService() {
                GetCandidateCredentialsException> {
            override fun onResult(result: GetCandidateCredentialsResponse) {
                Log.i(TAG, "getCandidateCredentials onResponse")
                val fillResponse: FillResponse? = convertToFillResponse(result, request)
                val fillResponse = convertToFillResponse(result, request)
                if (fillResponse != null) {
                    callback.onSuccess(fillResponse)
                } else {
                    Log.e(TAG, "Failed to create a FillResponse from the CredentialResponse.")
                    callback.onFailure("No dataset was created from the CredentialResponse")
                }
            }

            override fun onError(error: GetCandidateCredentialsException) {
@@ -97,7 +109,74 @@ class CredentialAutofillService : AutofillService() {
            getCredResponse: GetCandidateCredentialsResponse,
            filLRequest: FillRequest
    ): FillResponse? {
        TODO("Not yet implemented")
        val providerList = GetFlowUtils.toProviderList(
                getCredResponse.candidateProviderDataList,
                this@CredentialAutofillService)
        var totalEntryCount = 0
        providerList.forEach { provider ->
            totalEntryCount += provider.credentialEntryList.size
        }
        val inlineSuggestionsRequest = filLRequest.inlineSuggestionsRequest
        val inlineMaxSuggestedCount = inlineSuggestionsRequest?.maxSuggestionCount ?: 0
        val inlinePresentationSpecs = inlineSuggestionsRequest?.inlinePresentationSpecs
        val inlinePresentationSpecsCount = inlinePresentationSpecs?.size ?: 0
        var maxItemCount = totalEntryCount
        if (inlineMaxSuggestedCount > 0) {
            maxItemCount = maxItemCount.coerceAtMost(inlineMaxSuggestedCount)
        }
        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(
                        CredentialProviderService.EXTRA_AUTOFILL_ID,
                        AutofillId::class.java)
                val pendingIntent = entry.pendingIntent
                if (autofillId == null || pendingIntent == null) {
                    return@entryLoop
                }
                var inlinePresentation: InlinePresentation? = null
                // Create inline presentation
                if (inlinePresentationSpecs != null && i < maxItemCount) {
                    val spec: InlinePresentationSpec
                    if (i < inlinePresentationSpecsCount) {
                        spec = inlinePresentationSpecs[i]
                    } else {
                        spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
                    }
                    val sliceBuilder = InlineSuggestionUi
                            .newContentBuilder(pendingIntent)
                            .setTitle(entry.userName)
                    inlinePresentation = InlinePresentation(
                            sliceBuilder.build().slice, spec, /* pinned= */ false)
                }
                i++

                val dataSetBuilder = Dataset.Builder()
                val presentationBuilder = Presentations.Builder()
                if (inlinePresentation != null) {
                    presentationBuilder.setInlinePresentation(inlinePresentation)
                }
                fillResponseBuilder.addDataset(
                        dataSetBuilder
                                .setField(
                                        autofillId,
                                        Field.Builder().setPresentations(
                                                presentationBuilder.build())
                                                .build())
                                .setAuthentication(entry.pendingIntent.intentSender)
                                .setAuthenticationExtras(entry.fillInIntent.extras)
                                .build())
                emptyFillResponse = false
            }
        }
        if (emptyFillResponse) {
            return null
        }
        return fillResponseBuilder.build()
    }

    override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {