Ein Autofill-Service ist eine App, mit der Nutzer Formulare leichter ausfüllen können indem Daten in die Ansichten anderer Apps eingebracht werden. Autofill-Dienste können auch Nutzerdaten aus den Ansichten in einer App abrufen und zur späteren Verwendung speichern. Autofill-Dienste 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 unter Android 8.0 (API-Level 26) und höher. Nutzer können die Autofill-Funktionen nur dann nutzen, wenn auf ihrem Gerät eine App installiert ist, die Autofill-Dienste bereitstellt.
Auf dieser Seite erfahren Sie, wie Sie einen Autofill-Dienst in Ihrer App implementieren. Wenn Sie
nach einem Codebeispiel suchen, das zeigt, wie ein Dienst implementiert wird,
AutofillFramework-Beispiel in Java
oder
Kotlin
Weitere Informationen zur Funktionsweise von Autofill-Diensten findest du in der Referenz
Seiten für AutofillService
und AutofillManager
Klassen.
Manifestdeklarationen und Berechtigungen
Apps, die Autofill-Dienste anbieten, müssen eine Erklärung enthalten, in der Folgendes beschrieben wird:
die Implementierung des Dienstes. Fügen Sie zum Angeben der Deklaration einen
<service>
-Element im
App-Manifest. Die
<service>
-Element muss
enthalten die folgenden Attribute und Elemente:
- Attribut
android:name
die auf die abgeleitete Klasse vonAutofillService
in der App verweist, in der für den Dienst. android:permission
Attribut, das dieBIND_AUTOFILL_SERVICE
deklariert Berechtigung.<intent-filter>
-Element, dessen obligatorisches untergeordnetes Element<action>
dieandroid.service.autofill.AutofillService
-Aktion angibt.- Optional
<meta-data>
-Element, mit dem Sie zusätzliche Konfigurationsparameter für für den Dienst.
Das folgende Beispiel zeigt eine Deklaration für einen Autofill-Service:
<service
android:name=".MyAutofillService"
android:label="My Autofill Service"
android:permission="android.permission.BIND_AUTOFILL_SERVICE">
<intent-filter>
<action android:name="android.service.autofill.AutofillService" />
</intent-filter>
<meta-data
android:name="android.autofill"
android:resource="@xml/service_configuration" />
</service>
Das <meta-data>
-Element enthält das Attribut android:resource
, das auf eine XML-Ressource mit weiteren Details zum Dienst verweist.
Die Ressource service_configuration
im vorherigen Beispiel gibt ein
Aktivität, mit der Nutzer den Dienst konfigurieren können. Im folgenden Beispiel
zeigt die XML-Ressource service_configuration
an:
<autofill-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.example.android.SettingsActivity" />
Weitere Informationen zu XML-Ressourcen finden Sie unter App-Ressourcen – Übersicht.
Aufforderung zum Aktivieren des Dienstes
Eine App wird als Autofill-Dienst verwendet, nachdem sie die BIND_AUTOFILL_SERVICE
-Berechtigung erklärt und der Nutzer sie in den Geräteeinstellungen aktiviert hat. So kann eine App prüfen, ob es sich um den derzeit aktivierten Dienst handelt:
das Aufrufen der
hasEnabledAutofillServices()
der AutofillManager
-Klasse.
Wenn die App nicht der aktuelle Autofill-Dienst ist, kann sie den Nutzer bitten, die Autofill-Einstellungen mithilfe der ACTION_REQUEST_SET_AUTOFILL_SERVICE
-Intent zu ändern. Der Intent gibt den Wert RESULT_OK
zurück, wenn der Nutzer einen Autofill-Dienst auswählt, der dem Paket des Aufrufers entspricht.
Kundenansichten ausfüllen
Der Autofill-Dienst erhält Anfragen zum Ausfüllen von Kundenansichten, wenn der Nutzer mit anderen Apps interagiert. Wenn der Autofill-Service Nutzerdaten hat, der Anfrage, dann sendet er die Daten in der Antwort. Das Android-System zeigt eine Autofill-Benutzeroberfläche mit den verfügbaren Daten, wie in Abbildung 1 dargestellt:
Das Autofill-Framework definiert einen Workflow zum Ausfüllen von Ansichten, der die Zeit minimiert, in der das Android-System an den Autofill-Dienst gebunden ist. In
bei jeder Anfrage sendet das Android-System ein AssistStructure
-Objekt an den Dienst,
onFillRequest()
wird aufgerufen
.
Der Autofill-Dienst prüft, ob der Anfrage mit Nutzerdaten entsprochen werden kann, die er
die zuvor gespeichert wurden. Wenn die Anfrage erfüllt werden kann, verpackt der Dienst die Daten in Dataset
-Objekten. Der Dienst ruft die Methode onSuccess()
auf und übergibt ein FillResponse
-Objekt, das die Dataset
-Objekte enthält. Wenn der Dienst nicht
Daten zur Erfüllung der Anfrage haben, wird null
an die Methode onSuccess()
übergeben.
Dienst
ruft die Methode onFailure()
auf
wenn bei der Verarbeitung der Anfrage ein Fehler auftritt. Eine detaillierte
Erläuterung des Workflows, siehe Beschreibung auf der AutofillService
Referenzseite.
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 mehrere Datensätze haben, die der Anfrage entsprechen. In dieser zeigt das Android-System mehrere Optionen an – eine für jede in der Autofill-Benutzeroberfläche. Im folgenden Codebeispiel wird gezeigt, wie mehrere Datensätze 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-Dienste können zwischen den ViewNode
-Objekten im
AssistStructure
zum Abrufen der Autofill-Daten
die zur Ausführung der Anfrage erforderlich sind. Ein Dienst kann Autofill-Daten mithilfe von Methoden der Klasse ViewNode
abrufen, z. B. getAutofillId()
.
Ein Dienst muss den Inhalt einer Ansicht beschreiben können, um zu prüfen, ob er die Anfrage erfüllen kann. Die Verwendung des Attributs autofillHints
ist der erste Ansatz, den ein Dienst zur Beschreibung des Inhalts einer Ansicht verwenden muss. Sie können jedoch
Client-Apps müssen das Attribut explizit in ihren Ansichten angeben, bevor es
die für den Dienst zur Verfügung stehen.
Wenn eine Client-App das autofillHints
-Attribut nicht angibt, 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 zum Inhalt der Ansicht abzurufen.
Weitere Informationen finden Sie unter Hinweise für das automatische Ausfüllen bereitstellen.
Im folgenden Beispiel wird gezeigt, wie Sie AssistStructure
durchlaufen und Autofill-Daten 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-Dienst benötigt Nutzerdaten, um Ansichten in Apps auszufüllen. Wenn Nutzende eine Ansicht manuell ausfüllen, werden sie aufgefordert, die Daten im aktuellen Autofill-Service, wie in Abbildung 2 dargestellt.
Damit die Daten gespeichert werden können, 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,
Es gibt eine Ausführungsanfrage, bei der der Dienst die
Aufrufe. Um anzugeben, dass er an der Speicherung der Daten interessiert ist, fügt der Dienst in der Antwort auf die Anfrage zum Ausfüllen ein SaveInfo
-Objekt ein. SaveInfo
-Objekt
enthält mindestens die folgenden Daten:
- Der Typ der gespeicherten Nutzerdaten. Eine Liste der verfügbaren
SAVE_DATA
-Werte finden Sie unterSaveInfo
. - Die Mindestanzahl an 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
undpassword
aktualisieren, um eine Speicheranfrage auszulösen.
Ein SaveInfo
-Objekt ist mit einem FillResponse
-Objekt verknüpft, wie im
folgenden Codebeispiel:
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 eine Logik implementieren, um die Nutzerdaten im
onSaveRequest()
, die normalerweise aufgerufen wird, nachdem die Client-Aktivität beendet ist, oder wenn die
Client-App-Aufrufe commit()
.
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-Dienste müssen sensible Daten verschlüsseln, bevor sie gespeichert werden. Sie können jedoch Nutzerdaten können Labels oder nicht vertrauliche Daten enthalten. Beispiel: Ein Nutzer Konto kann ein Label enthalten, das die Daten als geschäftlich oder privat kennzeichnet. Konto. Dienste dürfen Labels nicht verschlüsseln. Wenn Labels nicht verschlüsselt werden, können Dienste die Labels in Präsentationsansichten verwenden, auch wenn sich der Nutzer nicht authentifiziert hat. Anschließend können Dienste die Labels nach der Authentifizierung des Nutzers durch die tatsächlichen Daten ersetzen.
Benutzeroberfläche zum Speichern von Autofill-Daten verschieben
Ab Android 10, wenn Sie die Autofill-Funktion auf mehreren Bildschirmen implementieren
Workflow, z. B. ein Bildschirm für das Feld für den Nutzernamen und ein weiterer für das Feld
Passwort eingeben. Sie können die Benutzeroberfläche zum Speichern von Autofill-Daten verschieben, indem Sie die
SaveInfo.FLAG_DELAY_SAVE
melden.
Wenn dieses Flag festgelegt ist, wird die Benutzeroberfläche zum Speichern von Autofill-Daten nicht ausgelöst, wenn die Autofill-
Kontext, der mit der SaveInfo
-Antwort verknüpft ist, wird festgeschrieben. Stattdessen können Sie eine separate Aktivität innerhalb derselben Aufgabe verwenden, um zukünftige Ausfüllanfragen zu senden und dann die Benutzeroberfläche über eine Speicheranfrage anzuzeigen. Weitere Informationen finden Sie unter SaveInfo.FLAG_DELAY_SAVE
.
Nutzerauthentifizierung erforderlich
Autofill-Dienste können für zusätzliche Sicherheit sorgen, indem der Nutzer authentifiziert werden muss, bevor er Ansichten ausfüllen kann. Folgende Szenarien sind gute Kandidaten für die Implementierung der Nutzerauthentifizierung:
- Die Nutzerdaten in der App müssen mit einem primären Passwort oder einem Fingerabdruck gescannt werden, um entsperrt zu werden.
- Ein bestimmter Datensatz muss entsperrt werden, etwa Kreditkartendetails durch mit einer Kartenprüfnummer (CVC).
Wenn für den Dienst eine Nutzerauthentifizierung erforderlich ist, bevor die Daten entsperrt werden, kann der Dienst Standarddaten oder ein Label anzeigen und die Intent
angeben, die für die Authentifizierung zuständig ist. Wenn Sie nach dem
Authentifizierungsvorgang abgeschlossen ist, können Sie diese Daten dem Intent hinzufügen. Ihr
Authentifizierungsaktivität können die Daten dann an die Klasse AutofillService
zurückgeben.
in Ihrer App.
Das folgende Codebeispiel zeigt, wie angegeben wird, dass die Anfrage Authentifizierung erforderlich:
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()
-Methode,
Übergeben Sie einen RESULT_OK
-Wert und legen Sie die EXTRA_AUTHENTICATION_RESULT
fest.
zum FillResponse
-Objekt hinzu, das das gefüllte Dataset enthält. Die
Der folgende Code zeigt ein Beispiel für die Rückgabe des Ergebnisses
Authentifizierungsabläufe abgeschlossen:
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, auf der nach der CVC gefragt wird. Sie können die Daten ausblenden, bis der Datensatz entsperrt wird, indem Sie Standarddaten wie den Namen der Bank und die letzten vier Ziffern der Kreditkartennummer präsentieren. Das folgende Beispiel zeigt, wie Sie Eine Authentifizierung für ein Dataset ist erforderlich und die Daten werden ausgeblendet, bis der Nutzer der CVC:
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_AUTHENTICATION_RESULT
-Extra auf ein Dataset
-Objekt festlegen, das die Kreditkartennummer und das Ablaufdatum enthält. Der neue Datensatz ersetzt den Datensatz, für den eine Authentifizierung erforderlich ist, und die Ansichten werden sofort ausgefüllt. Im folgenden Code wird gezeigt, wie der Datensatz zurückgegeben wird, sobald der Nutzer die 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-Dienste müssen die Daten in logischen Gruppen organisieren, die die Daten von Konzepten aus verschiedenen Bereichen. Auf dieser Seite werden diese logischen Gruppen als Partitionen bezeichnet. Die folgende Liste enthält typische Beispiele für Partitionen und Felder:
- Anmeldedaten, einschließlich der Felder für Nutzername und Passwort.
- Adresse, einschließlich der Felder „Straße“, „Ort“, „Bundesland“ und „Postleitzahl“.
- Zahlungsinformationen, wie Kreditkartennummer, Ablaufdatum und Bestätigungscodes ein.
Ein Autofill-Service, der Daten korrekt partitioniert, kann den der Daten seiner Nutzer, indem sie keine Daten aus mehr als einer Partition in einer Dataset. Beispielsweise muss ein Dataset mit Anmeldedaten Zahlungsinformationen enthalten. Wenn Sie Daten in Partitionen organisieren, können Sie -Dienst, um die Mindestmenge an relevanten Informationen bereitzustellen, die für eine Anfrage erfüllen.
Das Organisieren von Daten in Partitionen ermöglicht es Diensten, Aktivitäten zu füllen, Ansichten aus mehreren Partitionen erstellen und gleichzeitig die Mindestmenge an relevanten an die Client-App übertragen. Stellen Sie sich z. B. eine Aktivität vor, die Aufrufe umfasst. für Nutzername, Passwort, Straße und Stadt sowie einen Autofill-Service mit folgende Daten:
Partition | Feld 1 | Feld 2 |
---|---|---|
Anmeldedaten | Geschäftlicher_Nutzername | work_password |
personal_username | persönliches_passwort | |
Adresse | work_street | work_city |
personal_street | personal_city |
Der Dienst kann ein Dataset vorbereiten, das die Partition der Anmeldedaten für beruflichen und privaten Konten. Wenn der Nutzer einen Datensatz auswählt, kann eine nachfolgende Autofill-Antwort je nach der ersten Auswahl des Nutzers entweder die geschäftliche oder die private Adresse enthalten.
Ein Dienst kann das Feld ermitteln, von dem die Anfrage stammt, indem er die Methode isFocused()
aufruft, während er das AssistStructure
-Objekt durchläuft. Dadurch können die
-Dienst, um eine FillResponse
mit den entsprechenden Partitionsdaten vorzubereiten.
SMS-Einmalcodes automatisch ausfüllen
Ihr Autofill-Dienst kann Nutzern mithilfe der SMS Retriever API dabei helfen, per SMS gesendete Einmalcodes einzugeben.
Damit Sie diese Funktion verwenden können, müssen die folgenden Anforderungen erfüllt sein:
- Der Autofill-Dienst wird unter Android 9 (API-Level 28) oder höher ausgeführt.
- Der Nutzer willigt ein, dass Ihr Autofill-Service Einmalcodes auslesen darf SMS.
- Die Anwendung, für die Sie die Autofill-Funktion bereitstellen, verwendet noch nicht die SMS Retriever API zum Lesen von Einmalcodes.
Ihr Autofill-Dienst kann SmsCodeAutofillClient
,
verfügbar durch Anruf bei SmsCodeRetriever.getAutofillClient()
über Google Play
Dienste 19.0.56 oder höher.
Die wichtigsten Schritte für die Verwendung dieser API in einem Autofill-Service sind:
- Im Autofill-Service
hasOngoingSmsRequest
verwenden vonSmsCodeAutofillClient
, um zu ermitteln, ob es Anfragen gibt für den Paketnamen der App, die automatisch ausgefüllt wird. Ihr Autofill-Dienst darf nur dann eine Vorschlagsaufforderung anzeigen, wennfalse
zurückgegeben wird. - Verwenden Sie im Autofill-Dienst
checkPermissionState
vonSmsCodeAutofillClient
, um zu prüfen, ob der Autofill-Dienst zum automatischen Ausfüllen von Einmalcodes berechtigt ist. Dieser Berechtigungsstatus kannNONE
sein,GRANTED
oderDENIED
. Der Autofill-Dienst muss für die BundesstaatenNONE
undGRANTED
einen Vorschlag anzeigen. - Verwende in der Autofill-Authentifizierungsaktivität die
Berechtigung
SmsRetriever.SEND_PERMISSION
, umBroadcastReceiver
zu registrieren Warten aufSmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION
, um die SMS zu erhalten wenn es verfügbar ist. Rufen Sie
startSmsCodeRetriever
aufSmsCodeAutofillClient
, um nach einmaligen Codes zu suchen, die per SMS gesendet werden. Wenn der Nutzer Ihrem AutoFill-Dienst Berechtigungen zum Abrufen von Einmalcodes aus SMS erteilt, wird nach SMS-Nachrichten gesucht, die in den letzten ein bis fünf Minuten eingegangen sind.Wenn Ihr Autofill-Dienst die Nutzerberechtigung zum Lesen von Einmalcodes anfordern muss, schlägt die von
startSmsCodeRetriever
zurückgegebeneTask
möglicherweise fehl und es wirdResolvableApiException
zurückgegeben. In diesem Fall müssen Sie die MethodeResolvableApiException.startResolutionForResult()
-Methode zum Anzeigen einer Dialog zur Einholung von Einwilligungen für die Berechtigungsanfrage.Empfangen Sie das Ergebnis des SMS-Codes von der Intent-Aktion und geben Sie den SMS-Code dann als Autofill-Antwort zurück.
Erweiterte Autofill-Szenarien
- In Tastatur einbinden
- Ab Android 11 können Tastaturen und andere Eingabemethoden-Editoren (IMEs) Vorschläge für die automatische Vervollständigung inline anzeigen, anstatt ein Drop-down-Menü zu verwenden. Weitere Informationen dazu, wie der Autofill-Dienst dies unterstützt finden Sie unter Autofill mit Tastaturen.
- Datasets paginatieren
- Eine große Autofill-Antwort kann die zulässige Transaktionsgröße des
Binder
-Objekts überschreiten, das das Remote-Objekt darstellt, das zur Verarbeitung der Anfrage erforderlich ist. Um zu verhindern, dass das Android-System in diesen Fällen eine Ausnahme auslöst, können SieFillResponse
klein halten, indem Sie jeweils nicht mehr als 20Dataset
-Objekte hinzufügen. Wenn Ihre Antwort mehr Datasets benötigt, können Sie ein Dataset hinzufügen, wissen Nutzende, dass es mehr Informationen gibt, und ruft die nächste Datasets. Weitere Informationen finden Sie unteraddDataset(Dataset)
. - Auf mehrere Bildschirme verteilte Daten speichern
In Apps werden Nutzerdaten häufig in mehreren Bildschirmen derselben Aktivität aufgeteilt, insbesondere bei Aktivitäten, mit denen ein neues Nutzerkonto erstellt wird. Auf dem ersten Bildschirm wird beispielsweise nach einem Nutzernamen gefragt. Wenn der Nutzername verfügbar ist, wird auf dem zweiten Bildschirm nach einem Passwort gefragt. In diesen Fällen muss der Autofill-Dienst warten, bis der Nutzer beide Felder ausgefüllt hat, bevor die Benutzeroberfläche zum Speichern von Autofill-Daten angezeigt werden kann. Gehen Sie so vor, um mit solchen Szenarien umzugehen:
- Geben Sie in der ersten Anfrage ausfüllen Clientstatus-Bundle hinzufügen in der Antwort, die die Autofill-IDs der Teilfelder enthält auf dem Bildschirm angezeigt wird.
- Rufen Sie in der zweiten Fill-Anfrage das Client-Status-Bundle ab, rufen Sie die in der vorherigen Anfrage im Clientstatus festgelegten Autofill-IDs ab und fügen Sie diese IDs und das Flag
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
demSaveInfo
-Objekt hinzu, das in der zweiten Antwort verwendet wird. - Verwende in der Speicheranfrage die entsprechenden
FillContext
-Objekte, um den Wert jedes Felds abzurufen. Es gibt einen Füllkontext pro Anfrage ausführen.
Weitere Informationen finden Sie unter Speichern, wenn Daten auf mehrere Bildschirme verteilt sind.
- Initialisierungs- und Teardown-Logik für jede Anfrage angeben
Bei jeder Autofill-Anfrage stellt das Android-System eine Verbindung zum und ruft die zugehörige
onConnected()
-Methode auf. Sobald der Dienst die Anfrage verarbeitet hat, ruft das Android-System die MethodeonDisconnected()
auf und trennt die Bindung zum Dienst. Sie könnenonConnected()
implementieren, der vor der Verarbeitung einer Anfrage ausgeführt wird, undonDisconnected()
, der nach der Verarbeitung einer Anfrage ausgeführt wird.- Benutzeroberfläche für das Speichern von Autofill-Informationen anpassen
Autofill-Dienste können die Benutzeroberfläche zum Speichern von Autofill-Daten anpassen, damit Nutzer besser entscheiden können, möchten, dass der Dienst ihre Daten speichert. Mögliche Leistungen zusätzliche Informationen darüber, was entweder durch einen einfachen Text oder benutzerdefinierte Ansicht anpassen. Dienste können auch das Aussehen der Schaltfläche ändern, mit der der Speichervorgang 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-Dienste die virtuelle Struktur für Bedienungshilfen für das automatische Ausfüllen verwenden. Sie ist besonders nützlich, um Autofill-Funktionen in Browsern bereitzustellen, die die Autofill APIs nicht explizit implementieren.
Wenn Sie den Autofill-Dienst im Kompatibilitätsmodus testen möchten, müssen Sie Setzen Sie den Browser oder die App, für die der Kompatibilitätsmodus erforderlich ist, auf die Zulassungsliste. Sie können die welche Pakete bereits auf die Zulassungsliste gesetzt wurden, indem Sie den folgenden Befehl ausführen:
$ adb shell settings get global autofill_compat_mode_allowed_packages
Wenn das zu testende Paket nicht aufgeführt ist, fügen Sie es mit dem folgenden Befehl hinzu: dem folgenden Befehl, wobei
pkgX
das Paket der Anwendung ist:$ adb shell settings put global autofill_compat_mode_allowed_packages pkg1[resId1]:pkg2[resId1,resId2]
Wenn es sich bei der App um einen Browser handelt, Verwenden Sie
resIdx
, um die Ressourcen-ID des Eingabefelds anzugeben, das die URL enthält. der gerenderten Seite.
Der Kompatibilitätsmodus hat folgende Einschränkungen:
- Eine Speicheranfrage wird ausgelöst, wenn der Dienst die
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
oder dersetTrigger()
aufgerufen wird.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
ist standardmäßig bei Verwendung des Kompatibilitätsmodus festgelegt. - Der Textwert der Knoten ist möglicherweise nicht in der Methode
onSaveRequest(SaveRequest, SaveCallback)
verfügbar.
Weitere Informationen zum Kompatibilitätsmodus, einschließlich der Einschränkungen
finden Sie in der
AutofillService
Klassenreferenz.