AutoFill-Dienste erstellen

Ein Autofill-Service ist eine App, die Nutzern das Ausfüllen von Formularen erleichtert, indem sie Daten in die Ansichten anderer Apps einfügt. Autofill-Services können auch Nutzerdaten aus den Ansichten in einer App abrufen und zur späteren Verwendung speichern. Autofill-Services werden in der Regel von Apps bereitgestellt, die Nutzerdaten verwalten, z. B. Passwortmanager.

Android erleichtert das Ausfüllen von Formularen mit dem Autofill-Framework, das ab Android 8.0 (API-Level 26) verfügbar ist. Nutzer können Autofill-Funktionen nur dann nutzen, wenn auf ihrem Gerät eine App vorhanden ist, die Autofill-Dienste bereitstellt.

Auf dieser Seite wird beschrieben, wie Sie einen Autofill-Service in Ihrer App implementieren. Ein Codebeispiel für die Implementierung eines Dienstes finden Sie im AutofillFramework-Beispiel in Java oder Kotlin. Weitere Informationen zur Funktionsweise von Autofill-Services finden Sie auf den Referenzseiten für die Klassen AutofillService und AutofillManager.

Manifestdeklarationen und Berechtigungen

Apps, die Autofill-Services anbieten, müssen eine Erklärung enthalten, in der die Implementierung des Dienstes beschrieben wird. Wenn Sie die Deklaration angeben möchten, fügen Sie ein <service>-Element in das App-Manifest ein. Das <service>-Element muss die folgenden Attribute und Elemente enthalten:

  • android:name-Attribut, das auf die Unterklasse von AutofillService in der App verweist, die den Dienst implementiert.
  • Das Attribut android:permission, das die Berechtigung BIND_AUTOFILL_SERVICE deklariert.
  • <intent-filter>-Element, dessen obligatorisches untergeordnetes <action>-Element die Aktion android.service.autofill.AutofillService angibt.
  • Optionales <meta-data>-Element, mit dem Sie zusätzliche Konfigurationsparameter für den Dienst angeben können.

Das folgende Beispiel zeigt die Deklaration eines Autofill-Service:

<service
    android:name=".MyAutofillService"
    android:label="My Autofill Service"
    android:permission="android.permissi>on.BI<ND_AUTOFILL_S>ERVICE&qu<ot;
    intent-filter
        action android:name="android.>servi<ce.autofill.Au>tofil<lService" /
    /intent-filter
    meta-data
        android:name="android.autofill"
     > <  android:resource="@xml/service_configuration" /
/service

Das Element <meta-data> enthält ein android:resource-Attribut, das auf eine XML-Ressource mit weiteren Details zum Dienst verweist. Die Ressource service_configuration im vorherigen Beispiel gibt eine Aktivität an, mit der Nutzer den Dienst konfigurieren können. Das folgende Beispiel zeigt die XML-Ressource service_configuration:

<autofill-service
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:settingsActivity="com.example.android.Sett>ingsActivity" /

Weitere Informationen zu XML-Ressourcen finden Sie unter App-Ressourcen – Übersicht.

Aufforderung zum Aktivieren des Dienstes

Eine App wird als Autofill-Service verwendet, nachdem sie die Berechtigung BIND_AUTOFILL_SERVICE deklariert hat und der Nutzer sie in den Geräteeinstellungen aktiviert hat. Eine App kann prüfen, ob sie der aktivierte Dienst ist, indem sie die Methode hasEnabledAutofillServices() der Klasse AutofillManager aufruft.

Wenn die App nicht der aktuelle Autofill-Service ist, kann sie den Nutzer auffordern, die Autofill-Einstellungen mit dem Intent ACTION_REQUEST_SET_AUTOFILL_SERVICE zu ändern. Der Intent gibt den Wert RESULT_OK zurück, wenn der Nutzer einen Autofill-Service auswählt, der mit dem Paket des Aufrufers übereinstimmt.

Kundenansichten ausfüllen

Der Autofill-Service empfängt Anfragen zum Ausfüllen von Clientansichten, wenn der Nutzer mit anderen Apps interagiert. Wenn der Autofill-Service Nutzerdaten hat, die der Anfrage entsprechen, sendet er die Daten in der Antwort. Das Android-System zeigt eine Autofill-Benutzeroberfläche mit den verfügbaren Daten an, wie in Abbildung 1 dargestellt:

Drop-down-Menü mit Autofill-Vorschlägen, in dem ein verfügbares Dataset angezeigt wird
Abbildung 1. Autofill-Benutzeroberfläche mit einem Dataset

Das Autofill-Framework definiert einen Workflow zum Ausfüllen von Ansichten, der so konzipiert ist, dass die Zeit, die das Android-System an den Autofill-Service gebunden ist, minimiert wird. Bei jeder Anfrage sendet das Android-System ein AssistStructure-Objekt an den Dienst, indem es die Methode onFillRequest() aufruft.

Der Autofill-Service prüft, ob er die Anfrage mit zuvor gespeicherten Nutzerdaten erfüllen kann. Wenn die Anfrage erfüllt werden kann, verpackt der Dienst die Daten in Dataset-Objekte. Der Dienst ruft die Methode onSuccess() auf und übergibt ein FillResponse-Objekt, das die Dataset-Objekte enthält. Wenn der Dienst keine Daten hat, um die Anfrage zu erfüllen, übergibt er null an die Methode onSuccess(). Der Dienst ruft stattdessen die Methode onFailure() auf, wenn bei der Verarbeitung der Anfrage ein Fehler auftritt. Eine ausführliche Erläuterung des Workflows finden Sie in der Beschreibung auf der Referenzseite zu AutofillService.

Der folgende Code zeigt ein Beispiel für die Methode onFillRequest():

Kotlin

override fun onFillRequest(
    request: FillRequest,
    cancellationSignal: CancellationSignal,
    callback: FillCallback
) {
    // Get the structure from the request
    val context: List<FillContext> = request.fillContexts
    val structure: AssistStructure = context[context.size - 1].structure

    // Traverse the structure looking for nodes to fill out
    val parsedStructure: ParsedStructure = parseStructure(structure)

    // Fetch user data that matches the fields
    val (username: String, password: String) = fetchUserData(parsedStructure)

    // Build the presentation of the datasets
    val usernamePresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1)
    usernamePresentation.setTextViewText(android.R.id.text1, "my_username")
    val passwordPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1)
    passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username")

    // Add a dataset to the response
    val fillResponse: FillResponse = FillResponse.Builder()
            .addDataset(Dataset.Builder()
                    .setValue(
                            parsedStructure.usernameId,
                            AutofillValue.forText(username),
                            usernamePresentation
                    )
                    .setValue(
                            parsedStructure.passwordId,
                            AutofillValue.forText(password),
                            passwordPresentation
                    )
                    .build())
            .build()

    // If there are no errors, call onSuccess() and pass the response
    callback.onSuccess(fillResponse)
}

data class ParsedStructure(var usernameId: AutofillId, var passwordId: AutofillId)

data class UserData(var username: String, var password: String)

Java

@Override
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {
    // Get the structure from the request
    List<FillContext> context = request.getFillContexts();
    AssistStructure structure = context.get(context.size() - 1).getStructure();

    // Traverse the structure looking for nodes to fill out
    ParsedStructure parsedStructure = parseStructure(structure);

    // Fetch user data that matches the fields
    UserData userData = fetchUserData(parsedStructure);

    // Build the presentation of the datasets
    RemoteViews usernamePresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
    usernamePresentation.setTextViewText(android.R.id.text1, "my_username");
    RemoteViews passwordPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
    passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username");

    // Add a dataset to the response
    FillResponse fillResponse = new FillResponse.Builder()
            .addDataset(new Dataset.Builder()
                    .setValue(parsedStructure.usernameId,
                            AutofillValue.forText(userData.username), usernamePresentation)
                    .setValue(parsedStructure.passwordId,
                            AutofillValue.forText(userData.password), passwordPresentation)
                    .build())
            .build();

    // If there are no errors, call onSuccess() and pass the response
    callback.onSuccess(fillResponse);
}

class ParsedStructure {
    AutofillId usernameId;
    AutofillId passwordId;
}

class UserData {
    String username;
    String password;
}

Ein Dienst kann mehr als einen Datensatz haben, der die Anfrage erfüllt. In diesem Fall zeigt das Android-System mehrere Optionen in der Autofill-Benutzeroberfläche an – eine für jeden Datensatz. Im folgenden Codebeispiel wird gezeigt, wie mehrere Datasets in einer Antwort bereitgestellt werden:

Kotlin

// Add multiple datasets to the response
val fillResponse: FillResponse = FillResponse.Builder()
        .addDataset(Dataset.Builder()
                .setValue(parsedStructure.usernameId,
                        AutofillValue.forText(user1Data.username), username1Presentation)
                .setValue(parsedStructure.passwordId,
                        AutofillValue.forText(user1Data.password), password1Presentation)
                .build())
        .addDataset(Dataset.Builder()
                .setValue(parsedStructure.usernameId,
                        AutofillValue.forText(user2Data.username), username2Presentation)
                .setValue(parsedStructure.passwordId,
                        AutofillValue.forText(user2Data.password), password2Presentation)
                .build())
        .build()

Java

// Add multiple datasets to the response
FillResponse fillResponse = new FillResponse.Builder()
        .addDataset(new Dataset.Builder()
                .setValue(parsedStructure.usernameId,
                        AutofillValue.forText(user1Data.username), username1Presentation)
                .setValue(parsedStructure.passwordId,
                        AutofillValue.forText(user1Data.password), password1Presentation)
                .build())
        .addDataset(new Dataset.Builder()
                .setValue(parsedStructure.usernameId,
                        AutofillValue.forText(user2Data.username), username2Presentation)
                .setValue(parsedStructure.passwordId,
                        AutofillValue.forText(user2Data.password), password2Presentation)
                .build())
        .build();

Autofill-Services können die ViewNode-Objekte in der AssistStructure durchsuchen, um die für die Anfrage erforderlichen Autofill-Daten abzurufen. Ein Dienst kann Autofill-Daten mithilfe von Methoden der Klasse ViewNode abrufen, z. B. getAutofillId().

Ein Dienst muss in der Lage sein, den Inhalt einer Ansicht zu beschreiben, um zu prüfen, ob er die Anfrage erfüllen kann. Das Attribut autofillHints ist der erste Ansatz, den ein Dienst verwenden muss, um den Inhalt einer Ansicht zu beschreiben. Clientanwendungen müssen das Attribut jedoch explizit in ihren Ansichten angeben, bevor es für den Dienst verfügbar ist.

Wenn eine Client-Anwendung das Attribut autofillHints nicht bereitstellt, muss ein Dienst seine eigenen Heuristiken verwenden, um den Inhalt zu beschreiben. Der Dienst kann Methoden aus anderen Klassen wie getText() oder getHint() verwenden, um Informationen über den Inhalt der Ansicht abzurufen. Weitere Informationen finden Sie unter Hinweise für Autofill bereitstellen.

Im folgenden Beispiel wird gezeigt, wie Sie die AssistStructure durchlaufen und Daten für automatisches Ausfüllen aus einem ViewNode-Objekt abrufen:

Kotlin

fun traverseStructure(structure: AssistStructure) {
    val windowNodes: List<AssistStructure.WindowNode> =
            structure.run {
                (0 until windowNodeCount).map { getWindowNodeAt(it) }
            }

    windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
        val viewNode: ViewNode? = windowNode.rootViewNode
        traverseNode(viewNode)
    }
}

fun traverseNode(viewNode: ViewNode?) {
    if (viewNode?.autofillHints?.isNotEmpty() == true) {
        // If the client app provides autofill hints, you can obtain them using
        // viewNode.getAutofillHints();
    } else {
        // Or use your own heuristics to describe the contents of a view
        // using methods such as getText() or getHint()
    }

    val children: List<ViewNode>? =
            viewNode?.run {
                (0 until childCount).map { getChildAt(it) }
            }

    children?.forEach { childNode: ViewNode ->
        traverseNode(childNode)
    }
}

Java

public void traverseStructure(AssistStructure structure) {
    int nodes = structure.getWindowNodeCount();

    for (int i = 0; i < nodes; i++) {
        WindowNode windowNode = structure.getWindowNodeAt(i);
        ViewNode viewNode = windowNode.getRootViewNode();
        traverseNode(viewNode);
    }
}

public void traverseNode(ViewNode viewNode) {
    if(viewNode.getAutofillHints() != null && viewNode.getAutofillHints().length > 0) {
        // If the client app provides autofill hints, you can obtain them using
        // viewNode.getAutofillHints();
    } else {
        // Or use your own heuristics to describe the contents of a view
        // using methods such as getText() or getHint()
    }

    for(int i = 0; i < viewNode.getChildCount(); i++) {
        ViewNode childNode = viewNode.getChildAt(i);
        traverseNode(childNode);
    }
}

Nutzerdaten speichern

Ein Autofill-Service benötigt Nutzerdaten, um Ansichten in Apps auszufüllen. Wenn Nutzer eine Ansicht manuell ausfüllen, werden sie aufgefordert, die Daten im aktuellen Autofill-Service zu speichern (siehe Abbildung 2).

Systemdialogfeld, in dem der Nutzer aufgefordert wird, Autofill-Daten im Dienst zu speichern
Abbildung 2: UI zum Speichern von Autofill-Daten.

Damit die Daten gespeichert werden, muss der Dienst angeben, dass er die Daten für die zukünftige Verwendung speichern möchte. Bevor das Android-System eine Anfrage zum Speichern der Daten sendet, gibt es eine Ausfüllanfrage, bei der der Dienst die Möglichkeit hat, die Ansichten auszufüllen. Um anzugeben, dass der Dienst am Speichern der Daten interessiert ist, fügt er der Antwort auf die Anfrage zum Ausfüllen ein SaveInfo-Objekt hinzu. Das Objekt SaveInfo enthält mindestens die folgenden Daten:

  • Die Art der gespeicherten Nutzerdaten. Eine Liste der verfügbaren SAVE_DATA-Werte finden Sie unter SaveInfo.
  • Die Mindestanzahl von Ansichten, die geändert werden müssen, um eine Speicheranfrage auszulösen. Bei einem Anmeldeformular muss der Nutzer beispielsweise in der Regel die Ansichten username und password aktualisieren, um eine Speicheranfrage auszulösen.

Ein SaveInfo-Objekt ist mit einem FillResponse-Objekt verknüpft, wie im folgenden Codebeispiel gezeigt:

Kotlin

override fun onFillRequest(
    request: FillRequest,
    cancellationSignal: CancellationSignal,
    callback: FillCallback
) {
    // ...
    // Builder object requires a non-null presentation
    val notUsed = RemoteViews(packageName, android.R.layout.simple_list_item_1)

    val fillResponse: FillResponse = FillResponse.Builder()
            .addDataset(
                    Dataset.Builder()
                            .setValue(parsedStructure.usernameId, null, notUsed)
                            .setValue(parsedStructure.passwordId, null, notUsed)
                            .build()
            )
            .setSaveInfo(
                    SaveInfo.Builder(
                            SaveInfo.SAVE_DATA_TYPE_USERNAME or SaveInfo.SAVE_DATA_TYPE_PASSWORD,
                            arrayOf(parsedStructure.usernameId, parsedStructure.passwordId)
                    ).build()
            )
            .build()
    // ...
}

Java

@Override
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {
    // ...
    // Builder object requires a non-null presentation
    RemoteViews notUsed = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);

    FillResponse fillResponse = new FillResponse.Builder()
            .addDataset(new Dataset.Builder()
                    .setValue(parsedStructure.usernameId, null, notUsed)
                    .setValue(parsedStructure.passwordId, null, notUsed)
                    .build())
            .setSaveInfo(new SaveInfo.Builder(
                    SaveInfo.SAVE_DATA_TYPE_USERNAME | SaveInfo.SAVE_DATA_TYPE_PASSWORD,
                    new AutofillId[] {parsedStructure.usernameId, parsedStructure.passwordId})
                    .build())
            .build();
    // ...
}

Der Autofill-Service kann Logik implementieren, um die Nutzerdaten in der Methode onSaveRequest() beizubehalten. Diese wird normalerweise aufgerufen, nachdem die Clientaktivität abgeschlossen ist oder wenn die Client-App commit() aufruft. Der folgende Code zeigt ein Beispiel für die Methode onSaveRequest():

Kotlin

override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
    // Get the structure from the request
    val context: List<FillContext> = request.fillContexts
    val structure: AssistStructure = context[context.size - 1].structure

    // Traverse the structure looking for data to save
    traverseStructure(structure)

    // Persist the data - if there are no errors, call onSuccess()
    callback.onSuccess()
}

Java

@Override
public void onSaveRequest(SaveRequest request, SaveCallback callback) {
    // Get the structure from the request
    List<FillContext> context = request.getFillContexts();
    AssistStructure structure = context.get(context.size() - 1).getStructure();

    // Traverse the structure looking for data to save
    traverseStructure(structure);

    // Persist the data - if there are no errors, call onSuccess()
    callback.onSuccess();
}

Autofill-Services müssen sensible Daten verschlüsseln, bevor sie sie speichern. Nutzerdaten können jedoch Labels oder Daten enthalten, die nicht sensibel sind. Ein Nutzerkonto kann beispielsweise ein Label enthalten, das die Daten als Arbeitskonto oder privates Konto kennzeichnet. Dienste dürfen Labels nicht verschlüsseln. Wenn Labels nicht verschlüsselt werden, können Dienste die Labels in Präsentationsansichten verwenden, wenn sich der Nutzer nicht authentifiziert hat. Anschließend können Dienste die Labels durch die tatsächlichen Daten ersetzen, nachdem sich der Nutzer authentifiziert hat.

Benutzeroberfläche zum Speichern von Autofill-Daten später anzeigen

Wenn Sie ab Android 10 mehrere Bildschirme verwenden, um einen Autofill-Workflow zu implementieren, z. B. einen Bildschirm für das Feld für den Nutzernamen und einen anderen für das Passwort, können Sie die Benutzeroberfläche zum Speichern von Autofill-Daten mit dem Flag SaveInfo.FLAG_DELAY_SAVE verschieben.

Wenn dieses Flag festgelegt ist, wird die Benutzeroberfläche zum Speichern von Autofill-Daten nicht ausgelöst, wenn der Autofill-Kontext, der mit der SaveInfo-Antwort verknüpft ist, bestätigt wird. Stattdessen können Sie eine separate Aktivität innerhalb derselben Aufgabe verwenden, um zukünftige Anfragen zum Ausfüllen zu senden, und dann die Benutzeroberfläche mit einer Speicheranfrage anzeigen. Weitere Informationen finden Sie unter SaveInfo.FLAG_DELAY_SAVE.

Nutzerauthentifizierung erforderlich machen

Autofill-Services können eine zusätzliche Sicherheitsebene bieten, indem sie den Nutzer zur Authentifizierung auffordern, bevor Ansichten ausgefüllt werden können. Die folgenden Szenarien sind gute Kandidaten für die Implementierung der Nutzerauthentifizierung:

  • Die Nutzerdaten in der App müssen mit einem primären Passwort oder einem Fingerabdruckscan entsperrt werden.
  • Ein bestimmter Datensatz muss entsperrt werden, z. B. Kreditkartendetails mithilfe eines Kartenprüfcodes (CVC).

Wenn der Dienst eine Nutzerauthentifizierung erfordert, bevor die Daten freigegeben werden, kann er Standarddaten oder ein Label präsentieren und die Intent angeben, die die Authentifizierung verarbeitet. Wenn Sie nach Abschluss des Authentifizierungsablaufs zusätzliche Daten zur Verarbeitung der Anfrage benötigen, können Sie diese Daten dem Intent hinzufügen. Ihre Authentifizierungsaktivität kann die Daten dann an die Klasse AutofillService in Ihrer App zurückgeben.

Im folgenden Codebeispiel wird gezeigt, wie Sie angeben, dass für die Anfrage eine Authentifizierung erforderlich ist:

Kotlin

val authPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply {
    setTextViewText(android.R.id.text1, "requires authentication")
}
val authIntent = Intent(this, AuthActivity::class.java).apply {
    // Send any additional data required to complete the request
    putExtra(MY_EXTRA_DATASET_NAME, "my_dataset")
}

val intentSender: IntentSender = PendingIntent.getActivity(
        this,
        1001,
        authIntent,
        PendingIntent.FLAG_CANCEL_CURRENT
).intentSender

// Build a FillResponse object that requires authentication
val fillResponse: FillResponse = FillResponse.Builder()
        .setAuthentication(autofillIds, intentSender, authPresentation)
        .build()

Java

RemoteViews authPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
authPresentation.setTextViewText(android.R.id.text1, "requires authentication");
Intent authIntent = new Intent(this, AuthActivity.class);

// Send any additional data required to complete the request
authIntent.putExtra(MY_EXTRA_DATASET_NAME, "my_dataset");
IntentSender intentSender = PendingIntent.getActivity(
                this,
                1001,
                authIntent,
                PendingIntent.FLAG_CANCEL_CURRENT
        ).getIntentSender();

// Build a FillResponse object that requires authentication
FillResponse fillResponse = new FillResponse.Builder()
        .setAuthentication(autofillIds, intentSender, authPresentation)
        .build();

Sobald die Aktivität den Authentifizierungsvorgang abgeschlossen hat, muss sie die Methode setResult() aufrufen, einen RESULT_OK-Wert übergeben und das Extra EXTRA_AUTHENTICATION_RESULT auf das FillResponse-Objekt festlegen, das das ausgefüllte Dataset enthält. Der folgende Code zeigt ein Beispiel dafür, wie das Ergebnis nach Abschluss des Authentifizierungsablaufs zurückgegeben wird:

Kotlin

// The data sent by the service and the structure are included in the intent
val datasetName: String? = intent.getStringExtra(MY_EXTRA_DATASET_NAME)
val structure: AssistStructure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE)
val parsedStructure: ParsedStructure = parseStructure(structure)
val (username, password) = fetchUserData(parsedStructure)

// Build the presentation of the datasets
val usernamePresentation =
        RemoteViews(packageName, android.R.layout.simple_list_item_1).apply {
            setTextViewText(android.R.id.text1, "my_username")
        }
val passwordPresentation =
        RemoteViews(packageName, android.R.layout.simple_list_item_1).apply {
            setTextViewText(android.R.id.text1, "Password for my_username")
        }

// Add the dataset to the response
val fillResponse: FillResponse = FillResponse.Builder()
        .addDataset(Dataset.Builder()
                .setValue(
                        parsedStructure.usernameId,
                        AutofillValue.forText(username),
                        usernamePresentation
                )
                .setValue(
                        parsedStructure.passwordId,
                        AutofillValue.forText(password),
                        passwordPresentation
                )
                .build()
        ).build()

val replyIntent = Intent().apply {
    // Send the data back to the service
    putExtra(MY_EXTRA_DATASET_NAME, datasetName)
    putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse)
}

setResult(Activity.RESULT_OK, replyIntent)

Java

Intent intent = getIntent();

// The data sent by the service and the structure are included in the intent
String datasetName = intent.getStringExtra(MY_EXTRA_DATASET_NAME);
AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE);
ParsedStructure parsedStructure = parseStructure(structure);
UserData userData = fetchUserData(parsedStructure);

// Build the presentation of the datasets
RemoteViews usernamePresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
usernamePresentation.setTextViewText(android.R.id.text1, "my_username");
RemoteViews passwordPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username");

// Add the dataset to the response
FillResponse fillResponse = new FillResponse.Builder()
        .addDataset(new Dataset.Builder()
                .setValue(parsedStructure.usernameId,
                        AutofillValue.forText(userData.username), usernamePresentation)
                .setValue(parsedStructure.passwordId,
                        AutofillValue.forText(userData.password), passwordPresentation)
                .build())
        .build();

Intent replyIntent = new Intent();

// Send the data back to the service
replyIntent.putExtra(MY_EXTRA_DATASET_NAME, datasetName);
replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse);

setResult(RESULT_OK, replyIntent);

Wenn ein Kreditkartendatensatz entsperrt werden muss, kann der Dienst eine Benutzeroberfläche anzeigen, in der der CVC abgefragt wird. Sie können die Daten ausblenden, bis das Dataset entsperrt wird, indem Sie Standarddaten wie den Namen der Bank und die letzten vier Ziffern der Kartennummer anzeigen. Im folgenden Beispiel wird gezeigt, wie Sie die Authentifizierung für einen Datensatz erzwingen und die Daten ausblenden, bis der Nutzer den CVC angibt:

Kotlin

// Parse the structure and fetch payment data
val parsedStructure: ParsedStructure = parseStructure(structure)
val paymentData: Payment = fetchPaymentData(parsedStructure)

// Build the presentation that shows the bank and the last four digits of the
// credit card number, such as 'Bank-1234'
val maskedPresentation: String = "${paymentData.bank}-" +
        paymentData.creditCardNumber.substring(paymentData.creditCardNumber.length - 4)
val authPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply {
    setTextViewText(android.R.id.text1, maskedPresentation)
}

// Prepare an intent that displays the UI that asks for the CVC
val cvcIntent = Intent(this, CvcActivity::class.java)
val cvcIntentSender: IntentSender = PendingIntent.getActivity(
        this,
        1001,
        cvcIntent,
        PendingIntent.FLAG_CANCEL_CURRENT
).intentSender

// Build a FillResponse object that includes a Dataset that requires authentication
val fillResponse: FillResponse = FillResponse.Builder()
        .addDataset(
                Dataset.Builder()
                        // The values in the dataset are replaced by the actual
                        // data once the user provides the CVC
                        .setValue(parsedStructure.creditCardId, null, authPresentation)
                        .setValue(parsedStructure.expDateId, null, authPresentation)
                        .setAuthentication(cvcIntentSender)
                        .build()
        ).build()

Java

// Parse the structure and fetch payment data
ParsedStructure parsedStructure = parseStructure(structure);
Payment paymentData = fetchPaymentData(parsedStructure);

// Build the presentation that shows the bank and the last four digits of the
// credit card number, such as 'Bank-1234'
String maskedPresentation = paymentData.bank + "-" +
    paymentData.creditCardNumber.subString(paymentData.creditCardNumber.length - 4);
RemoteViews authPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
authPresentation.setTextViewText(android.R.id.text1, maskedPresentation);

// Prepare an intent that displays the UI that asks for the CVC
Intent cvcIntent = new Intent(this, CvcActivity.class);
IntentSender cvcIntentSender = PendingIntent.getActivity(
        this,
        1001,
        cvcIntent,
        PendingIntent.FLAG_CANCEL_CURRENT
).getIntentSender();

// Build a FillResponse object that includes a Dataset that requires authentication
FillResponse fillResponse = new FillResponse.Builder()
        .addDataset(new Dataset.Builder()
                // The values in the dataset are replaced by the actual
                // data once the user provides the CVC
                .setValue(parsedStructure.creditCardId, null, authPresentation)
                .setValue(parsedStructure.expDateId, null, authPresentation)
                .setAuthentication(cvcIntentSender)
                .build())
        .build();

Sobald die Aktivität den CVC validiert hat, sollte sie die Methode setResult() aufrufen, einen RESULT_OK-Wert übergeben und das Extra EXTRA_AUTHENTICATION_RESULT auf ein Dataset-Objekt festlegen, das die Kartennummer und das Ablaufdatum enthält. Das neue Dataset ersetzt das Dataset, für das eine Authentifizierung erforderlich ist, und die Ansichten werden sofort ausgefüllt. Der folgende Code zeigt ein Beispiel dafür, wie der Datensatz zurückgegeben wird, nachdem der Nutzer den CVC angegeben hat:

Kotlin

// Parse the structure and fetch payment data.
val parsedStructure: ParsedStructure = parseStructure(structure)
val paymentData: Payment = fetchPaymentData(parsedStructure)

// Build a non-null RemoteViews object to use as the presentation when
// creating the Dataset object. This presentation isn't actually used, but the
// Builder object requires a non-null presentation.
val notUsed = RemoteViews(packageName, android.R.layout.simple_list_item_1)

// Create a dataset with the credit card number and expiration date.
val responseDataset: Dataset = Dataset.Builder()
        .setValue(
                parsedStructure.creditCardId,
                AutofillValue.forText(paymentData.creditCardNumber),
                notUsed
        )
        .setValue(
                parsedStructure.expDateId,
                AutofillValue.forText(paymentData.expirationDate),
                notUsed
        )
        .build()

val replyIntent = Intent().apply {
    putExtra(EXTRA_AUTHENTICATION_RESULT, responseDataset)
}

Java

// Parse the structure and fetch payment data.
ParsedStructure parsedStructure = parseStructure(structure);
Payment paymentData = fetchPaymentData(parsedStructure);

// Build a non-null RemoteViews object to use as the presentation when
// creating the Dataset object. This presentation isn't actually used, but the
// Builder object requires a non-null presentation.
RemoteViews notUsed = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);

// Create a dataset with the credit card number and expiration date.
Dataset responseDataset = new Dataset.Builder()
        .setValue(parsedStructure.creditCardId,
                AutofillValue.forText(paymentData.creditCardNumber), notUsed)
        .setValue(parsedStructure.expDateId,
                AutofillValue.forText(paymentData.expirationDate), notUsed)
        .build();

Intent replyIntent = new Intent();
replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, responseDataset);

Daten in logischen Gruppen organisieren

Autofill-Services müssen die Daten in logischen Gruppen organisieren, die Konzepte aus verschiedenen Domains isolieren. Auf dieser Seite werden diese logischen Gruppen als Partitionen bezeichnet. Die folgende Liste enthält typische Beispiele für Partitionen und Felder:

  • „Anmeldedaten“ mit den Feldern für Nutzernamen und Passwort.
  • Adresse, einschließlich Straße, Ort, Bundesland und Postleitzahl.
  • Zahlungsinformationen, einschließlich Kartennummer, Ablaufdatum und Bestätigungscode.

Ein Autofill-Service, der Daten korrekt partitioniert, kann die Daten seiner Nutzer besser schützen, da nicht mehr als eine Partition in einem Datensatz offengelegt wird. Ein Dataset, das Anmeldedaten enthält, muss beispielsweise keine Zahlungsinformationen enthalten. Wenn Sie Daten in Partitionen organisieren, kann Ihr Dienst die Mindestmenge an relevanten Informationen bereitstellen, die für die Erfüllung einer Anfrage erforderlich sind.

Durch die Organisation von Daten in Partitionen können Dienste Aktivitäten mit Ansichten aus mehreren Partitionen ausführen und gleichzeitig die minimale Menge an relevanten Daten an die Client-App senden. Angenommen, eine Aktivität enthält Ansichten für Nutzername, Passwort, Straße und Ort und ein Autofill-Service hat die folgenden Daten:

Partition Feld 1 Feld 2
Anmeldedaten work_username work_password
personal_username personal_password
Adresse work_street work_city
personal_street personal_city

Der Dienst kann ein Dataset vorbereiten, das die Partition mit den Anmeldedaten für das Arbeits- und das private Konto enthält. Wenn der Nutzer einen Datensatz auswählt, kann eine nachfolgende Autofill-Antwort entweder die geschäftliche oder die private Adresse enthalten, je nachdem, was der Nutzer zuerst ausgewählt hat.

Ein Dienst kann das Feld, von dem die Anfrage stammt, identifizieren, indem er die Methode isFocused() aufruft, während er das Objekt AssistStructure durchläuft. So kann der Dienst eine FillResponse mit den entsprechenden Partitionsdaten vorbereiten.

Automatisches Ausfüllen von Einmalcodes aus SMS

Dein Autofill-Service kann den Nutzer beim Ausfüllen von Einmalcodes unterstützen, die über die SMS Retriever API gesendet werden.

Damit du diese Funktion verwenden kannst, müssen die folgenden Anforderungen erfüllt sein:

  • Der Autofill-Service wird unter Android 9 (API-Level 28) oder höher ausgeführt.
  • Der Nutzer erteilt Ihrem Autofill-Service die Berechtigung, Einmalcodes aus SMS zu lesen.
  • Die Anwendung, für die Sie das automatische Ausfüllen bereitstellen, verwendet die SMS Retriever API nicht bereits zum Lesen von Einmalcodes.

Dein Autofill-Service kann SmsCodeAutofillClient verwenden, das durch Aufrufen von SmsCodeRetriever.getAutofillClient() in den Google Play-Diensten 19.0.56 oder höher verfügbar ist.

Die wichtigsten Schritte zur Verwendung dieser API in einem Autofill-Service sind:

  1. Verwende im Autofill-Service hasOngoingSmsRequest aus SmsCodeAutofillClient, um zu ermitteln, ob für den Paketnamen der App, die du automatisch ausfüllst, aktive Anfragen vorliegen. Der Autofill-Service darf nur dann eine Aufforderung mit Vorschlägen anzeigen, wenn false zurückgegeben wird.
  2. Verwenden Sie im Autofill-Service checkPermissionState aus SmsCodeAutofillClient, um zu prüfen, ob der Autofill-Service die Berechtigung zum automatischen Ausfüllen von Einmalcodes hat. Dieser Berechtigungsstatus kann NONE, GRANTED oder DENIED sein. Der Autofill-Service muss eine Vorschlagsaufforderung für die Status NONE und GRANTED anzeigen.
  3. Verwenden Sie in der Aktivität für die automatische Bestätigung die Berechtigung SmsRetriever.SEND_PERMISSION, um einen BroadcastReceiver zu registrieren, der auf SmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION wartet, um das SMS-Code-Ergebnis zu erhalten, sobald es verfügbar ist.
  4. Rufen Sie startSmsCodeRetriever für SmsCodeAutofillClient auf, um auf per SMS gesendete Einmalcodes zu warten. Wenn der Nutzer Ihrem Autofill-Service die Berechtigung erteilt, Einmalcodes aus SMS abzurufen, wird nach SMS gesucht, die in den letzten ein bis fünf Minuten empfangen wurden.

    Wenn Ihr Autofill-Service die Nutzerberechtigung zum Lesen von Einmalcodes anfordern muss, schlägt der von startSmsCodeRetriever zurückgegebene Task möglicherweise fehl und es wird ein ResolvableApiException zurückgegeben. In diesem Fall müssen Sie die Methode ResolvableApiException.startResolutionForResult() aufrufen, um einen Dialog zur Einholung von Einwilligungen für die Berechtigungsanfrage anzuzeigen.

  5. Empfangen des SMS-Code-Ergebnisses aus dem Intent und anschließendes Zurückgeben des SMS-Codes als Autofill-Antwort.

Autofill in Chrome aktivieren

Chrome ermöglicht es Drittanbieter-Autofill-Services, Formulare nativ automatisch auszufüllen, was die Nutzerfreundlichkeit verbessert. Wenn Nutzer Autofill-Services von Drittanbietern verwenden möchten, um Passwörter, Passkeys und andere Informationen wie Adressen und Zahlungsdaten automatisch auszufüllen, müssen sie in den Chrome-Einstellungen Autofill mit einem anderen Dienst auswählen.

Chrome-Einstellungen mit aktivierter Option „Autofill mit einem anderen Dienst“
Abbildung 3: Chrome-Einstellungen, in denen die Option „Autofill mit einem anderen Dienst“ aktiviert ist.

Damit Nutzer mit Ihrem Autofill-Service und Chrome auf Android-Geräten die bestmögliche Autofill-Erfahrung haben, sollten Autofill-Dienstanbieter ihre Nutzer dazu anregen, ihren bevorzugten Dienstanbieter in den Chrome-Einstellungen anzugeben.

So können Entwickler Nutzern helfen, die Einstellung zu aktivieren:

  • Chrome-Einstellungen abfragen und prüfen, ob der Nutzer einen Autofill-Service eines Drittanbieters verwenden möchte.
  • Deeplink zur Chrome-Einstellungsseite, auf der Nutzer Autofill-Dienste von Drittanbietern aktivieren können.

Maximale Chrome-Versionen für den Kompatibilitätsmodus angeben

Chrome unterstützt den Kompatibilitätsmodus ab Version 137 nicht mehr und setzt stattdessen auf Android Autofill. Wenn Sie den Kompatibilitätsmodus beibehalten, kann dies zu Stabilitätsproblemen führen. Geben Sie die maximale Version von Chrome-Paketen an, die den Kompatibilitätsmodus für Stabilität unterstützen.

<autofill-service>
  ...
  <compatibility-package android:name="com.android.chrome" android:maxLongVersionCode=&qu>ot;<711900039" /
  compatibility-package android:name="com.chrome.beta" android:ma>xLo<ngVersionCode="711900039" /
  compatibility-package android:name="com.chrome.>dev<" android:maxLongVersionCode="711900039" /
  compatibility-package android:name=>"c<om.chrome.canary&>quot; android:maxLongVersionCode="711900039" /
  ...
/autofill-service

Chrome-Einstellungen lesen

Jede App kann auslesen, ob Chrome den Autofill-Modus von Drittanbietern verwendet, der die Verwendung von Android Autofill ermöglicht. Chrome verwendet den ContentProvider von Android, um diese Informationen zu übertragen. Deklarieren Sie in Ihrem Android-Manifest, von welchen Kanälen Sie Einstellungen lesen möchten:

<uses-permission android:name="android.permission.READ_USER_DICTION>A<RY">;/<
queries
 !-- To Query Chrom>e <Beta: --
 package android:name="com>.ch<rome.beta" /

 !-- To Que>ry< Chrome Stable: --
 package android:name=&q>u<ot;com.a>ndroid.chrome" /
/queries

Verwende dann den ContentResolver von Android, um diese Informationen anzufordern, indem du den Inhalts-URI erstellst:

Kotlin

val CHROME_CHANNEL_PACKAGE = "com.android.chrome" // Chrome Stable.
val CONTENT_PROVIDER_NAME = ".AutofillThirdPartyModeContentProvider"
val THIRD_PARTY_MODE_COLUMN = "autofill_third_party_state"
val THIRD_PARTY_MODE_ACTIONS_URI_PATH = "autofill_third_party_mode"

val uri = Uri.Builder()
    .scheme(ContentResolver.SCHEME_CONTENT)
    .authority(CHROME_CHANNEL_PACKAGE + CONTENT_PROVIDER_NAME)
    .path(THIRD_PARTY_MODE_ACTIONS_URI_PATH)
    .build()

val cursor = contentResolver.query(
    uri,
    arrayOf(THIRD_PARTY_MODE_COLUMN), // projection
    null, // selection
    null, // selectionArgs
    null  // sortOrder
)

if (cursor == null) {
  // Terminate now! Chromium versions older than this don't provide this information.
}

cursor?.use { // Use the safe call operator and the use function for auto-closing
    if (it.moveToFirst()) { // Check if the cursor has any rows
        val index = it.getColumnIndex(THIRD_PARTY_MODE_COLUMN)
        if (index != -1) { // Check if the column exists
          val value = it.getInt(index)
          if (0 == value) {
              // 0 means that the third party mode is turned off. Chrome uses its built-in
              // password manager. This is the default for new users.
          } else {
              // 1 means that the third party mode is turned on. Chrome forwards all
              // autofill requests to Android Autofill. Users have to opt-in for this.
          }
        } else {
          // Handle the case where the column doesn't exist.  Log a warning, perhaps.
          Log.w("Autofill", "Column $THIRD_PARTY_MODE_COLUMN not found in cursor&quot;)
        }
    }
} // The cursor is automatically closed here

Java

final String CHROME_CHANNEL_PACKAGE = "com.android.chrome";  // Chrome Stable.
final String CONTENT_PROVIDER_NAME = ".AutofillThirdPartyModeContentProvider";
final String THIRD_PARTY_MODE_COLUMN = "autofill_third_party_state";
final String THIRD_PARTY_MODE_ACTIONS_URI_PATH = "autofill_third_party_mode";

final Uri uri = new Uri.Builder()
                  .scheme(ContentResolver.SCHEME_CONTENT)
                  .authority(CHROME_CHANNEL_PACKAGE + CONTENT_PROVIDER_NAME)
                  .path(THIRD_PARTY_MODE_ACTIONS_URI_PATH)
                  .build();

final Cursor cursor = getContentResolver().query(
                  uri,
                  /*projection=*/new String[] {THIRD_PARTY_MODE_COLUMN},
                  /*selection=*/ null,
                  /*selectionArgs=*/ null,
                  /*sortOrder=*/ null);

if (cursor == null) {
  // Terminate now! Chromium versions older than this don't provide this information.
}

cursor.moveToFirst(); // Retrieve the result;

int index = cursor.getColumnIndex(THIRD_PARTY_MODE_COLUMN);

if (0 == cursor.getInt(index)) {
  // 0 means that the third party mode is turned off. Chrome uses its built-in
  // password manager. This is the default for new users.
} else {
  // 1 means that the third party mode is turned on. Chrome forwards all
  // autofill requests to Android Autofill. Users have to opt-in for this.
}

Wenn du einen Deeplink zur Chrome-Einstellungsseite erstellen möchtest, auf der Nutzer Autofill-Services von Drittanbietern aktivieren können, verwende einen Android-Intent. Konfigurieren Sie die Aktion und die Kategorien wie in diesem Beispiel gezeigt:

Kotlin

val autofillSettingsIntent = Intent(Intent.ACTION_APPLICATION_PREFERENCES)
autofillSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT)
autofillSettingsIntent.addCategory(Intent.CATEGORY_APP_BROWSER)
autofillSettingsIntent.addCategory(Intent.CATEGORY_PREFERENCE)

// Invoking the intent with a chooser allows users to select the channel they
// want to configure. If only one browser reacts to the intent, the chooser is
// skipped.
val chooser = Intent.createChooser(autofillSettingsIntent, "Pick Chrome Channel")
startActivity(chooser)

// If the caller knows which Chrome channel they want to configure,
// they can instead add a package hint to the intent, e.g.
val specificChromeIntent = Intent(Intent.ACTION_APPLICATION_PREFERENCES) // Create a *new* intent
specificChromeIntent.addCategory(Intent.CATEGORY_DEFAULT)
specificChromeIntent.addCategory(Intent.CATEGORY_APP_BROWSER)
specificChromeIntent.addCategory(Intent.CATEGORY_PREFERENCE)
specificChromeIntent.setPackage("com.android.chrome") // Set the package on the *new* intent
startActivity(specificChromeIntent) // Start the *new* intent

Java

Intent autofillSettingsIntent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES);
autofillSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
autofillSettingsIntent.addCategory(Intent.CATEGORY_APP_BROWSER);
autofillSettingsIntent.addCategory(Intent.CATEGORY_PREFERENCE);

// Invoking the intent with a chooser allows users to select the channel they
// want to configure. If only one browser reacts to the intent, the chooser is
// skipped.
Intent chooser = Intent.createChooser(autofillSettingsIntent, "Pick Chrome Channel");
startActivity(chooser);

// If the caller knows which Chrome channel they want to configure,
// they can instead add a package hint to the intent, e.g.
autofillSettingsIntent.setPackage("com.android.chrome");
startActivity(autofillSettingsIntent);

Erweiterte Autofill-Szenarien

Verwenden Sie Autofill in den folgenden Fällen:

In Tastatur einbinden

Ab Android 11 können Tastaturen und andere Eingabemethoden-Editoren (IMEs) Vorschläge für das automatische Ausfüllen inline statt in einem Drop-down-Menü anzeigen. Weitere Informationen dazu, wie dein Autofill-Service diese Funktion unterstützen kann, findest du unter Autofill in Tastaturen einbinden.

Datasets paginieren

Eine große Autofill-Antwort kann die zulässige Transaktionsgröße des Binder-Objekts überschreiten, das das remotefähige Objekt darstellt, das zur Verarbeitung der Anfrage erforderlich ist. Damit das Android-System in diesen Szenarien keine Ausnahme auslöst, können Sie die FillResponse klein halten, indem Sie jeweils maximal 20 Dataset-Objekte hinzufügen. Wenn für Ihre Antwort weitere Datasets erforderlich sind, können Sie ein Dataset hinzufügen, das Nutzer darüber informiert, dass weitere Informationen verfügbar sind, und die nächste Gruppe von Datasets abruft, wenn es ausgewählt wird. Weitere Informationen finden Sie unter addDataset(Dataset).

Daten auf mehrere Bildschirme aufteilen

In Apps werden Nutzerdaten häufig auf mehrere Bildschirme innerhalb derselben Aktivität aufgeteilt, z. B. beim Erstellen neuer Nutzerkonten. Auf dem ersten Bildschirm wird beispielsweise nach einem Nutzernamen und auf dem zweiten nach einem Passwort gefragt. In diesen Fällen muss der Autofill-Service warten, bis der Nutzer Daten in alle relevanten Felder eingegeben hat, bevor die Benutzeroberfläche zum Speichern der Autofill-Daten angezeigt wird. Gehen Sie so vor, um solche Szenarien zu handhaben:

  1. Füge in der ersten Ausfüllanfrage ein Clientstatus-Bundle in die Antwort ein, das die Autofill-IDs der auf dem Bildschirm vorhandenen Teilfelder enthält.
  2. Rufe in der zweiten Anfrage zum automatischen Ausfüllen das Clientstatus-Bundle ab, rufe die in der vorherigen Anfrage festgelegten Autofill-IDs aus dem Clientstatus ab und füge diese IDs und das Flag FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE dem SaveInfo-Objekt hinzu, das in der zweiten Antwort verwendet wird.
  3. Verwenden Sie in der Speicheranfrage die richtigen FillContext-Objekte, um den Wert der einzelnen Felder abzurufen. Pro Füllanfrage gibt es einen Füllkontext.

    Weitere Informationen finden Sie unter Daten speichern, wenn sie auf mehrere Bildschirme aufgeteilt sind.

Initialisierungs- und Teardown-Logik für jede Anfrage bereitstellen

Bei jeder Anfrage zum automatischen Ausfüllen bindet das Android-System den Dienst ein und ruft die Methode onConnected() auf. Sobald der Dienst die Anfrage verarbeitet hat, ruft das Android-System die Methode onDisconnected() auf und hebt die Bindung an den Dienst auf. Sie können onConnected() implementieren, um Code bereitzustellen, der vor der Verarbeitung einer Anfrage ausgeführt wird, und onDisconnected(), um Code bereitzustellen, der nach der Verarbeitung einer Anfrage ausgeführt wird.

Benutzeroberfläche zum Speichern von Autofill-Daten anpassen

Autofill-Dienste können die Benutzeroberfläche zum Speichern von Autofill-Daten anpassen, damit Nutzer entscheiden können, ob sie dem Dienst erlauben möchten, ihre Daten zu speichern. Dienste können zusätzliche Informationen zu den gespeicherten Daten entweder über Text oder über eine benutzerdefinierte Ansicht bereitstellen. Dienste können auch das Erscheinungsbild der Schaltfläche ändern, mit der die Speicheranfrage abgebrochen wird, und eine Benachrichtigung erhalten, wenn der Nutzer auf diese Schaltfläche tippt. Weitere Informationen finden Sie auf der Referenzseite zu SaveInfo.

Kompatibilitätsmodus

Im Kompatibilitätsmodus können Autofill-Services die virtuelle Struktur für Bedienungshilfen für Autofill-Zwecke verwenden. Sie ist besonders nützlich, um Autofill-Funktionen in Browsern bereitzustellen, die die Autofill APIs nicht explizit implementieren.

Wenn du deinen Autofill-Service im Kompatibilitätsmodus testen möchtest, füge den Browser oder die App, für die der Kompatibilitätsmodus erforderlich ist, explizit der Zulassungsliste hinzu. Mit dem folgenden Befehl können Sie prüfen, welche Pakete bereits auf der Zulassungsliste stehen:

$ adb shell settings get global autofill_compat_mode_allowed_packages

Wenn das Paket, das Sie testen möchten, nicht aufgeführt ist, fügen Sie es mit dem folgenden Befehl hinzu, wobei pkgX das Paket der App ist:

$ adb shell settings put global autofill_compat_mode_allowed_packages pkg1[resId1]:pkg2[resId1,resId2]

Wenn die App ein Browser ist, geben Sie mit resIdx die Ressourcen-ID des Eingabefelds an, das die URL der gerenderten Seite enthält.

Für den Kompatibilitätsmodus gelten die folgenden Einschränkungen:

  • Eine Speicheranfrage wird ausgelöst, wenn der Dienst das Flag FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE verwendet oder die Methode setTrigger() aufgerufen wird. FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE wird standardmäßig festgelegt, wenn der Kompatibilitätsmodus verwendet wird.
  • Der Textwert der Knoten ist in der Methode onSaveRequest(SaveRequest, SaveCallback) möglicherweise nicht verfügbar.

Weitere Informationen zum Kompatibilitätsmodus, einschließlich der damit verbundenen Einschränkungen, finden Sie in der Referenz zur Klasse AutofillService.