Usługa autouzupełniania to aplikacja, która ułatwia użytkownikom wypełnianie formularzy wstrzykiwanie danych w widokach innych aplikacji. Usługi autouzupełniania mogą też pobieranie danych użytkownika z widoków w aplikacji i przechowywanie ich do późniejszego wykorzystania obecnie się znajdujesz. Usługi autouzupełniania są zwykle udostępniane przez aplikacje, które zarządzają danymi użytkownika, takie jak: jako menedżerów haseł.
Android ułatwia wypełnianie formularzy dzięki platformie autouzupełniania dostępnej w językach: Android 8.0 (poziom interfejsu API 26) lub nowszy. Użytkownicy mogą korzystać z funkcji autouzupełniania tylko wtedy, gdy na ich urządzeniu jest aplikacja oferująca usługi autouzupełniania.
Na tej stronie znajdziesz informacje o wdrażaniu usługi autouzupełniania w aplikacji. Jeśli szukasz przykładowego kodu, który pokazuje, jak zaimplementować usługę, zapoznaj się z przykładem AutofillFramework w Javie lub Kotlinie.
Więcej informacji o działaniu usług autouzupełniania znajdziesz w dokumentacji
strony witryny AutofillService
i AutofillManager
zajęcia.
Deklaracje i uprawnienia w pliku manifestu
Aplikacje udostępniające usługi autouzupełniania muszą zawierać deklarację dotyczącą
wdrożenia usługi. Aby określić deklarację, dodaj element <service>
do pliku manifestu aplikacji. Element <service>
musi zawierać te atrybuty i elementy:
- Atrybut
android:name
wskazujący podklasęAutofillService
w aplikacji, która implementuje i z usługami. - atrybut
android:permission
, który deklaruje uprawnienieBIND_AUTOFILL_SERVICE
. <intent-filter>
element, którego wymagany element podrzędny<action>
określa działanieandroid.service.autofill.AutofillService
.- Opcjonalny element
<meta-data>
, który umożliwia podanie dodatkowych parametrów konfiguracji usługi.
Ten przykład przedstawia deklarację usługi autouzupełniania:
<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>
Element <meta-data>
zawiera element
android:resource
wskazujący zasób XML z dodatkowymi informacjami o usłudze.
Zasób service_configuration
w poprzednim przykładzie określa aktywność, która umożliwia użytkownikom skonfigurowanie usługi. Przykład poniżej
pokazuje zasób XML service_configuration
:
<autofill-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.example.android.SettingsActivity" />
Więcej informacji o zasobach XML znajdziesz w artykule Omówienie zasobów aplikacji.
Prośba o włączenie usługi
Aplikacja jest używana jako usługa autouzupełniania po zadeklarowaniu parametru
BIND_AUTOFILL_SERVICE
, a użytkownik włączy je na urządzeniu
ustawieniach. Aplikacja może sprawdzić, czy jest to obecnie włączona usługa, wywołując metodę hasEnabledAutofillServices()
klasy AutofillManager
.
Jeśli aplikacja nie jest bieżącą usługą autouzupełniania, może poprosić użytkownika o zmianę ustawień autouzupełniania za pomocą intencji ACTION_REQUEST_SET_AUTOFILL_SERVICE
. Jeśli użytkownik wybierze usługę autouzupełniania, która pasuje do pakietu wywołującego, intencja zwraca wartość RESULT_OK
.
Wypełnij dane o widokach klientów
Usługa autouzupełniania otrzymuje żądania dotyczące wypełniania widoków klienta, gdy użytkownik wchodzi w interakcję z innymi aplikacjami. Jeśli usługa autouzupełniania ma dane użytkownika, które odpowiadają żądaniu, wysyła je w odpowiedzi. System Android pokazuje interfejs autouzupełniania dostępnymi danymi, tak jak na ilustracji 1:
System autouzupełniania definiuje przepływ pracy służący do wypełniania widoków, który ma na celu zminimalizowanie czasu, przez jaki system Android jest połączony z usługą autouzupełniania. W
każdego żądania system Android wysyła do usługi obiekt AssistStructure
przez
Wywołuję onFillRequest()
.
Usługa autouzupełniania sprawdza, czy może spełnić żądanie danymi użytkownika, które
zapisane wcześniej. Jeśli jest w stanie spełnić żądanie, pakiety usług
dane w: Dataset
obiektów. Usługa wywołuje metodę onSuccess()
, przekazując obiekt FillResponse
, który zawiera obiekty Dataset
. Jeśli usługa nie ma danych, które mogłyby spełnić żądanie, przekazuje null
do metody onSuccess()
.
Usługa
wywołuje metodę onFailure()
, jeśli podczas przetwarzania żądania wystąpi błąd. Szczegółowe informacje
na temat procedury, zapoznaj się z opisem na stronie AutofillService
stronie referencyjnej.
Poniższy kod pokazuje przykład metody 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; }
Usługa może zawierać więcej niż 1 zbiór danych, który spełnia kryteria zapytania. W takim przypadku system Android wyświetla w interfejsie automatycznego wypełniania kilka opcji – po jednej dla każdego zbioru danych. Poniższy przykładowy kod pokazuje, jak przesyłać wiele zbiorów danych w odpowiedzi:
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();
Usługi autouzupełniania mogą przeszukiwać obiekty ViewNode
w AssistStructure
AssistStructure
, aby pobrać dane autouzupełniania wymagane do wykonania żądania. Usługa może pobierać dane autouzupełniania za pomocą metod klasy ViewNode
, takich jak getAutofillId()
.
Usługa musi być w stanie opisywać zawartość widoku, aby sprawdzić, czy
mogą zrealizować to żądanie. Użycie atrybutu autofillHints
jest pierwszym podejściem, którego usługa musi użyć do opisania zawartości widoku. Pamiętaj jednak:
aplikacje klienckie muszą wyraźnie podać atrybut w swoich widokach, zanim będzie on
dostępnych w usłudze.
Jeśli aplikacja klienta nie udostępnia atrybutu autofillHints
, usługa musi użyć własnej heurystyki do opisu treści.
Usługa może używać metod z innych klas, takich jak getText()
lub getHint()
, aby uzyskać informacje o treści widoku.
Więcej informacji znajdziesz w sekcji Przekazywanie wskazówek dla
autouzupełnianie
Ten przykład pokazuje, jak przeglądać AssistStructure
i pobrać
autouzupełnianie danych z obiektu ViewNode
:
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); } }
Zapisywanie danych użytkowników
Usługa autouzupełniania potrzebuje danych użytkownika, aby wypełniać widoki w aplikacjach. Gdy użytkownicy ręcznie wypełni widok, wyświetla się prośba o zapisanie danych z usługi autouzupełniania, jak widać na rysunku 2.
Aby zapisać dane, usługa musi wskazać, że jest zainteresowana przechowywaniem
do wykorzystania w przyszłości. Zanim system Android wyśle żądanie zapisania danych,
pojawi się prośba, w ramach której usługa może wypełnić
wyświetleń. Aby wskazać, że jest zainteresowana zapisaniem danych, usługa zawiera obiekt SaveInfo
w odpowiedzi na żądanie wypełnienia. Obiekt SaveInfo
zawiera co najmniej te dane:
- Typ zapisywanych danych użytkownika. Aby zobaczyć listę dostępnych
SAVE_DATA
wartości, patrzSaveInfo
. - Minimalna liczba widoków, które trzeba zmienić, aby wywołać prośbę o zapisanie.
Na przykład w przypadku formularza logowania użytkownik zwykle musi zaktualizować
username
ipassword
widoków, aby wywołać żądanie zapisania.
Obiekt SaveInfo
jest powiązany z obiektem FillResponse
, jak pokazano w
następujący przykładowy kod:
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(); ... }
Usługa autouzupełniania może stosować logikę, która przechowuje dane użytkownika w metodzie onSaveRequest()
, która jest zwykle wywoływana po zakończeniu aktywności klienta lub gdy aplikacja klienta wywołuje funkcję commit()
.
Poniższy kod pokazuje przykład metody 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(); }
Usługi autouzupełniania muszą zaszyfrować dane wrażliwe przed ich zapisaniem. Dane użytkownika mogą jednak zawierać etykiety lub dane, które nie są poufne. Na przykład konto użytkownika może zawierać etykietę, która oznacza dane jako należące do konta służbowego lub osobistego. Usługi nie mogą szyfrować etykiet. Brak szyfrowania etykiet usługi mogą używać etykiet w widokach prezentacji, jeśli użytkownik nie uwierzytelniono. Usługi mogą wtedy zastąpić etykiety rzeczywistymi danymi. po uwierzytelnieniu użytkownika.
Przełóż interfejs zapisywania autouzupełniania
Począwszy od Androida 10, jeśli do implementacji przepływu pracy autouzupełniania używasz wielu ekranów (np. jednego ekranu dla pola nazwy użytkownika i drugiego dla hasła), możesz opóźnić wyświetlenie interfejsu zapisywania autouzupełniania, używając flagi SaveInfo.FLAG_DELAY_SAVE
.
Jeśli ten parametr jest ustawiony, interfejs zapisywania danych autouzupełniania nie jest uruchamiany, gdy kontekst autouzupełniania powiązany z odpowiedzią SaveInfo
zostanie zapisany. Zamiast tego możesz użyć osobnego działania w ramach tego samego zadania, aby przesyłać przyszłe żądania wypełniania, a potem wyświetlać interfejs za pomocą żądania zapisywania. Więcej informacji znajdziesz w sekcji SaveInfo.FLAG_DELAY_SAVE
.
Wymagaj uwierzytelniania użytkownika
Usługi autouzupełniania mogą zapewnić dodatkowy poziom bezpieczeństwa, wymagając uwierzytelnić użytkownika, zanim będzie mógł wypełniać widoki. Poniższe scenariusze którzy warto wdrożyć uwierzytelnianie użytkowników:
- Dane użytkownika w aplikacji muszą być odblokowywane za pomocą hasła głównego lub skanu linii papilarnych.
- Należy odblokować określony zbiór danych, na przykład dane karty kredytowej, za pomocą kodu weryfikacyjnego karty (CVC).
Jeśli usługa wymaga uwierzytelnienia użytkownika przed odblokowaniem danych, może wyświetlić dane szablonowe lub etykietę i wskazywać Intent
, który odpowiada za uwierzytelnienie. Jeśli potrzebujesz dodatkowych danych do przetworzenia żądania po
uwierzytelniania, możesz dodać takie dane do intencji. Twoje
aktywność uwierzytelniania może następnie zwrócić dane do klasy AutofillService
w aplikacji.
Poniższy przykładowy kod pokazuje, jak określić, że żądanie wymaga uwierzytelnienia:
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();
Po zakończeniu procesu uwierzytelniania aktywność musi wywołać metodę
setResult()
,
przekazując wartość RESULT_OK
i ustawić EXTRA_AUTHENTICATION_RESULT
do obiektu FillResponse
, który zawiera wypełniony zbiór danych.
Poniższy kod pokazuje przykład zwracania wyniku, gdy
przebiegi uwierzytelniania:
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);
W sytuacji, gdy trzeba odblokować zbiór danych karty kredytowej, usługa może wyświetlić interfejs z prośbą o podanie kodu CVC. Możesz ukryć dane, dopóki nie zostaną odblokowane, prezentując dane szablonowe, takie jak nazwa banku i ostatnie 4 cyfry numeru karty kredytowej. Ten przykład pokazuje, jak wymaga uwierzytelniania zbioru danych i ukrywa dane, dopóki użytkownik ich nie poda kod 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();
Po zweryfikowaniu kodu CVC aktywność powinna wywołać metodę setResult()
, podając wartość RESULT_OK
i ustawiając dodatkowy parametr EXTRA_AUTHENTICATION_RESULT
na obiekt Dataset
, który zawiera numer karty kredytowej i datę ważności.
zastąpił zbiór danych wymagający uwierzytelnienia, a widoki danych są
można go wypełnić natychmiast. Poniższy kod pokazuje, jak zwrócić zbiór danych po podaniu przez użytkownika kodu weryfikacyjnego CVC:
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);
Uporządkuj dane w grupy logiczne
Usługi autouzupełniania muszą organizować dane w grupy logiczne, które odizolują koncepcjach z różnych domen. Na tej stronie te grupy logiczne są nazywane partycjami. Poniżej znajdziesz typowe przykłady partycji i pol:
- Dane logowania, które obejmują pola nazwy użytkownika i hasła.
- Adres obejmujący ulicę, miasto, województwo i kod pocztowy.
- dane karty, w tym numer karty kredytowej, datę ważności pola kodu weryfikacyjnego.
Usługa autouzupełniania, która prawidłowo dzieli dane, może lepiej chronić dane użytkowników, nie ujawniając danych z więcej niż 1 partycji w danym zbiorze danych. Na przykład w zbiorze danych, który zawiera dane logowania, zawierają dane do płatności. Organizowanie danych w partycjach pozwala usłudze udostępniać minimalną ilość odpowiednich informacji wymaganych do zaspokojenia żądania.
Organizowanie danych w partycjach umożliwia usługom wypełnianie aktywności, które mają widoki z wielu partycji, przy jednoczesnym wysyłaniu do aplikacji klienckiej minimalnej ilości odpowiednich danych. Rozważmy na przykład aktywność, która obejmuje widoki dotyczące nazwy użytkownika, hasła, ulicy i miasta, oraz usługę autouzupełniania, która zawiera te dane:
Partycja | Pole 1 | Pole 2 |
---|---|---|
Dane logowania | work_username | hasło_służbowe |
osobista_nazwa_użytkownika | osobiste_hasło | |
Adres | ulica_pracy | work_city |
personal_street | osobiste_miasto |
Usługa może przygotować zbiór danych zawierający partycję danych logowania dla zarówno na koncie służbowym, jak i osobistym. Gdy użytkownik wybiera zbiór danych, kolejna odpowiedź autouzupełniania może zawierać służbowy lub prywatny adres e-mail w zależności od pierwszego wyboru użytkownika.
Usługa może zidentyfikować pole, które jest źródłem żądania, wywołując
isFocused()
podczas przemierzania obiektu AssistStructure
. Dzięki temu funkcja
aby przygotować FillResponse
z odpowiednimi danymi partycji.
Autouzupełnianie kodów jednorazowych z SMS-ów
Twoja usługa autouzupełniania może pomóc użytkownikowi w uzupełnieniu jednorazowych kodów wysłanych przez SMS-y z użyciem interfejsu SMS retriever API;
Aby korzystać z tej funkcji, musisz spełniać poniższe wymagania:
- Usługa autouzupełniania działa na Androidzie 9 (poziom interfejsu API 28) lub nowszym.
- Użytkownik wyraża zgodę na odczytywanie przez Twoją usługę autouzupełniania jednorazowych kodów z SMS-ów.
- Aplikacja, dla której udostępniasz autouzupełnianie, nie korzysta jeszcze z pola SMS retriever API do odczytu kodów jednorazowych.
Usługa autouzupełniania może korzystać z usługi SmsCodeAutofillClient
, do której można się dostać, wywołując SmsCodeRetriever.getAutofillClient()
w Usługach Google Play w wersji 19.0.56 lub nowszej.
Podstawowe kroki, które należy wykonać, aby użyć tego interfejsu API w usłudze autouzupełniania:
- W usłudze autouzupełniania użyj
hasOngoingSmsRequest
zSmsCodeAutofillClient
, aby sprawdzić, czy są aktywne jakieś żądania dotyczące nazwy pakietu aplikacji, której dotyczy autouzupełnianie. Twoje autouzupełnianie usługa może wyświetlać prompt z sugestią tylko wtedy, gdy zwraca ono wartośćfalse
. - W usłudze autouzupełniania użyj adresu
checkPermissionState
. odSmsCodeAutofillClient
, aby sprawdzić, czy usługa autouzupełniania uprawnienia do autouzupełniania kodów jednorazowych. Stan uprawnień może byćNONE
,GRANTED
lubDENIED
. Usługa autouzupełniania musi wyświetlać prośbę o propozycję w przypadku stanówNONE
iGRANTED
. - W aktywności uwierzytelniania autouzupełniania użyj uprawnienia
SmsRetriever.SEND_PERMISSION
, aby zarejestrowaćBroadcastReceiver
, które nasłuchujeSmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION
, aby otrzymać kod SMS-em, gdy będzie dostępny. Zadzwoń pod numer
startSmsCodeRetriever
na urządzeniuSmsCodeAutofillClient
, aby zacząć nasłuchiwać kodów jednorazowych wysyłanych SMS-em. Jeśli użytkownik przyzna uprawnienia usłudze autouzupełniania do pobierania jednorazowych kodów z SMS-ów, usługa będzie szukać SMS-ów otrzymanych w ciągu ostatnich 1–5 minut.Jeśli usługa autouzupełniania musi poprosić użytkownika o jednorazowy odczyt kod
Task
zwrócony przezstartSmsCodeRetriever
może spowodować błąd z błędemResolvableApiException
. W takim przypadku musisz wywołać metodęResolvableApiException.startResolutionForResult()
, aby wyświetlić okno zgody dotyczące prośby o dostęp.Otrzymaj kod SMS-a z intencji, a potem zwracaj kod SMS-a jako odpowiedź autouzupełniania.
Zaawansowane scenariusze autouzupełniania
- Integracja z klawiaturą
- Od Androida 11 platforma zezwala na korzystanie z klawiatur i innych edytorów metody wprowadzania (IME), , aby wyświetlać sugestie autouzupełniania w tekście, zamiast używać menu. Więcej informacji o tym, jak usługa autouzupełniania może obsługiwać tę funkcję, znajdziesz w artykule Integracja autouzupełniania z klawiaturami.
- Podział zbiorów danych na strony
- Duża odpowiedź autouzupełniania może przekroczyć dozwolony rozmiar transakcji
Binder
, który reprezentuje Obiekt z możliwością usunięcia wymagany do przetworzenia żądania. Aby zapobiec wyrzucaniu wyjątków przez system Androida w takich sytuacjach, możesz utrzymaćFillResponse
na niskim poziomie, dodając nie więcej niż 20 obiektówDataset
naraz. Jeśli Twoja odpowiedź wymaga dodatkowych zbiorów danych, możesz dodać zbiór danych, który informuje użytkowników, że są dostępne dodatkowe informacje, i pobiera kolejną grupę zbiorów danych po jej wybraniu. Więcej informacji znajdziesz w artykuleaddDataset(Dataset)
. - Zapisz podział danych na kilku ekranach
Aplikacje często dzielą dane użytkownika na kilka ekranów w ramach tego samego działania, zwłaszcza w przypadku działań służących do utworzenia nowego konta użytkownika. Na przykład na pierwszym ekranie wyświetla się prośba o podanie nazwy użytkownika, a jeśli jest ona dostępna, na drugim ekranie wyświetla się prośba o podanie hasła. W takich sytuacjach usługa autouzupełniania musi poczekać, aż użytkownik wypełni oba pola, zanim wyświetli interfejs zapisywania autouzupełniania. Obserwuj te czynności, jakie należy wykonać, aby poradzić sobie w takich sytuacjach:
- W pierwszym żądaniu uzupełnienia dodaj w odpowiedzi pakiet stanu klienta, który zawiera identyfikatory automatycznego wypełniania pól częściowych widocznych na ekranie.
- W drugim żądaniu wypełniania pobierz pakiet stanu klienta, pobierz identyfikatory automatycznego wypełniania ustawione w poprzednim żądaniu ze stanu klienta i dodaj te identyfikatory oraz flagę
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
do obiektuSaveInfo
użytego w drugiej odpowiedzi. - W prośbie o zapisanie użyj odpowiednich obiektów
FillContext
, aby uzyskać wartość każdego pola. Istnieje jeden kontekst wypełnienia na żądanie wypełnienia.
Więcej informacji znajdziesz w artykule Zapisywanie danych podzielonych na kilka ekranów.
- Zapewnij logikę inicjowania i zamykania dla każdego żądania.
Za każdym razem, gdy pojawi się żądanie automatycznego wypełniania, system Android łączy się z usługą i wywołuje jej metodę
onConnected()
. Gdy usługa przetworzy żądanie, system Android wywoła metodęonDisconnected()
i rozwiąże powiązanie z usługą. Możesz zaimplementowaćonConnected()
, aby podać kod, który jest wykonywany przed przetworzeniem żądania, orazonDisconnected()
, aby podać kod, który jest wykonywany po przetworzeniu żądania.- Dostosowywanie interfejsu zapisywania autouzupełniania
Usługi autouzupełniania mogą dostosować interfejs zapisywania autouzupełniania, aby ułatwić użytkownikom i chce, aby usługa zachowała ich dane. Usługi mogą udostępniać dodatkowe informacje o tym, co zostało zapisane, za pomocą prostego tekstu lub widoku niestandardowego. Usługi mogą też zmienić wygląd przycisku anulowania prośby o zapisanie i otrzymać powiadomienie, gdy użytkownik go kliknie. Więcej informacji znajdziesz na stronie referencyjnej
SaveInfo
.- Tryb zgodności
Tryb zgodności umożliwia usługom autouzupełniania korzystanie z ułatwień dostępu wirtualna struktura na potrzeby autouzupełniania. Jest to szczególnie przydatne w przypadku funkcji autouzupełniania w przeglądarkach, które nie implementują jawnie interfejsów API autouzupełniania.
Aby przetestować usługę autouzupełniania za pomocą trybu zgodności, wyraźnie dodać do listy dozwolonych przeglądarkę lub aplikację, która wymaga trybu zgodności. Aby sprawdzić, które pakiety są już uwzględnione na liście dozwolonych, uruchom to polecenie:
$ adb shell settings get global autofill_compat_mode_allowed_packages
Jeśli pakietu, który testujesz, nie ma na liście, dodaj go, uruchamiając to polecenie, gdzie
pkgX
to pakiet aplikacji:$ adb shell settings put global autofill_compat_mode_allowed_packages pkg1[resId1]:pkg2[resId1,resId2]
Jeśli aplikacja jest przeglądarką, użyj atrybutu
resIdx
, aby określić identyfikator zasobu pola wejściowego zawierającego adres URL wyrenderowanej strony.
Tryb zgodności ma te ograniczenia:
- Prośba o zapisanie jest wywoływana, gdy usługa używa flagi
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
lub wywołuje metodęsetTrigger()
.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
jest domyślnie ustawiony podczas korzystania z trybu zgodności. - Wartość tekstowa węzłów może być niedostępna w
onSaveRequest(SaveRequest, SaveCallback)
.
Więcej informacji o trybie zgodności oraz o ograniczeniach
, patrz sekcja
AutofillService
odniesienie do klasy.