Zoptymalizuj aplikację pod kątem autouzupełniania

Aplikacje, które korzystają z widoków standardowych, współpracują z ramami automatycznego wypełniania bez konieczności specjalnej konfiguracji. Możesz też zoptymalizować działanie aplikacji za pomocą frameworka.

Konfigurowanie środowiska autouzupełniania

W tej sekcji opisano, jak skonfigurować w aplikacji podstawowe funkcje autouzupełniania.

Konfigurowanie usługi autouzupełniania

Aby aplikacja mogła korzystać z ramy autouzupełniania, musisz skonfigurować na urządzeniu usługę autouzupełniania. Chociaż większość telefonów i tabletów z Androidem 8.0 (poziom interfejsu API 26) lub nowszym ma zainstalowaną usługę autouzupełniania, podczas testowania aplikacji zalecamy użycie usługi testowej, np. usługi autouzupełniania w ramach przykładowego interfejsu autouzupełniania Androida. Jeśli używasz emulatora, wyraźnie ustaw usługę autouzupełniania, ponieważ emulator może nie mieć domyślnej usługi.

Po zainstalowaniu testowej usługi autouzupełniania z przykładowej aplikacji włącz tę usługę, wybierając Ustawienia > System > Języki i metody wprowadzania > Zaawansowane > Asystent wprowadzania > Usługa autouzupełniania.

Więcej informacji o konfigurowaniu emulatora na potrzeby testowania autouzupełniania znajdziesz w artykule Testowanie aplikacji z autouzupełnianiem.

Podawanie wskazówek dotyczących autouzupełniania

Usługa autouzupełniania określa typ każdego widoku za pomocą heurystyk. Jeśli jednak Twoja aplikacja korzysta z tych heurystycznych metod, zachowanie funkcji autouzupełniania może się nieoczekiwanie zmienić po jej zaktualizowaniu. Aby usługa autouzupełniania mogła poprawnie rozpoznawać formaty pól w aplikacji, podaj wskazówki dotyczące autouzupełniania.

Wskazówki autouzupełniania możesz ustawić za pomocą atrybutu android:autofillHints. W tym przykładzie ustawiamy podpowiedź "password" dla elementu EditText:

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:autofillHints="password" />

Możesz też skonfigurować podpowiedzi programowo za pomocą metody setAutofillHints(), jak w tym przykładzie:

Kotlin

val password = findViewById<EditText>(R.id.password)
password.setAutofillHints(View.AUTOFILL_HINT_PASSWORD)

Java

EditText password = findViewById(R.id.password);
password.setAutofillHints(View.AUTOFILL_HINT_PASSWORD);

Uwzględnij wstępnie zdefiniowane stałe wskazówki

Platforma autouzupełniania nie weryfikuje wskazówek. Są one przekazywane bez zmian i weryfikacji do usługi autouzupełniania. Możesz użyć dowolnej wartości, ale klasy ViewHintConstants w Androidzie X zawierają listy oficjalnie obsługiwanych stałych wskazówek.

Łącząc te stałe, możesz tworzyć układy na potrzeby typowych scenariuszy autouzupełniania:

Dane logowania do konta

W formularzu logowania możesz dodać wskazówki dotyczące danych logowania do konta, np. AUTOFILL_HINT_USERNAME czy AUTOFILL_HINT_PASSWORD.

Do tworzenia nowego konta lub w przypadku, gdy użytkownicy zmieniają nazwę użytkownika i hasło, możesz użyć funkcji AUTOFILL_HINT_NEW_USERNAME i AUTOFILL_HINT_NEW_PASSWORD.

Informacje o karcie kredytowej

Podczas wysyłania żądania informacji o karcie kredytowej możesz użyć wskazówek, takich jak AUTOFILL_HINT_CREDIT_CARD_NUMBERAUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE.

Aby sprawdzić daty ważności karty kredytowej, wykonaj jedną z tych czynności:

Adres pocztowy

W przypadku pól formularza adresu pocztowego możesz użyć takich podpowiedzi:

imiona i nazwiska,

Prosząc o imiona i nazwiska osób, możesz skorzystać z następujących wskazówek:

Numery telefonów

W przypadku numerów telefonów możesz użyć:

Hasło jednorazowe

Aby wyświetlić hasło jednorazowe w jednym widoku, możesz użyćAUTOFILL_HINT_SMS_OTP.

W przypadku wielu widoków, z których każdy odpowiada jednej cyfrze kodu OTP, możesz użyć metody generateSmsOtpHintForCharacterPosition() do generowania podpowiedzi dotyczących poszczególnych znaków.

Oznaczanie pól jako ważnych dla funkcji autouzupełniania

Poszczególne pola w aplikacji możesz uwzględnić w strukturze widoku na potrzeby autouzupełniania. Domyślnie widoki korzystają z trybu IMPORTANT_FOR_AUTOFILL_AUTO, który pozwala Androidowi korzystać z heurystyki, aby określić, czy widok jest ważny dla funkcji autouzupełniania.

Są jednak przypadki, w których widok, struktura widoku lub cała aktywność nie jest ważna dla funkcji autouzupełniania:

  • Pole CAPTCHA w aktywności logowania
  • Widok, w którym użytkownik tworzy treści, np. edytor tekstu lub arkusza kalkulacyjnego
  • wyświetlenia w niektórych aktywnościach w grach, np. w tych, które pokazują rozgrywkę;

Ważność widoku możesz określić za pomocą atrybutu android:importantForAutofill:

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:importantForAutofill="no" />

Wartość importantForAutofill może być dowolną z tych wartości:

auto
Pozostaw systemowi Android możliwość korzystania z heurystyki do określania, czy widok jest ważny dla autouzupełniania.
no
Ten widok nie jest ważny dla autouzupełniania.
noExcludeDescendants
Ten widok i jego elementy nie są istotne dla funkcji autouzupełniania.
yes
Ten widok jest ważny dla funkcji autouzupełniania.
yesExcludeDescendants
Ten widok jest ważny w przypadku autouzupełniania, ale jego elementy podrzędne nie są istotne.

Możesz też użyć metody setImportantForAutofill():

Kotlin

val captcha = findViewById<TextView>(R.id.captcha)
captcha.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO)

Java

TextView captcha = findViewById(R.id.captcha);
captcha.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);

Powyższe przykładowe przypadki użycia możesz zadeklarować jako nieistotne dla funkcji autouzupełniania w ten sposób:

  • Pole CAPTCHA w aktywności logowania: użyj wartości android:importantForAutofill="no" lub IMPORTANT_FOR_AUTOFILL_NO aby oznaczyć to widok jako nieważny.
  • Widok, w którym użytkownik tworzy treści: użyj opcji android:importantForAutofill="noExcludeDescendants" lub IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS, aby oznaczyć całą strukturę widoku jako nieistotną.
  • Widok w niektórych aktywnościach w grach: użyj android:importantForAutofill="noExcludeDescendants" lub IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS, aby oznaczyć całą strukturę widoku jako nieistotną.

Powiązanie danych witryny i aplikacji mobilnej

Usługi autouzupełniania, takie jak autouzupełnianie Google, mogą udostępniać dane logowania użytkownika w przeglądarkach i na urządzeniach z Androidem po powiązaniu aplikacji z witryną. Jeśli użytkownik wybierze tę samą usługę autouzupełniania na obu platformach, zalogowanie się w aplikacji internetowej spowoduje udostępnienie danych logowania do autouzupełniania w momencie logowania się w odpowiedniej aplikacji na Androida.

Aby powiązać aplikację na Androida z witryną, prześlij do niej link do zasobu cyfrowego z relacją delegate_permission/common.get_login_creds. Następnie zadeklaruj powiązanie w pliku AndroidManifest.xml aplikacji. Szczegółowe instrukcje dotyczące kojarzenia witryny z aplikacją na Androida znajdziesz w artykule Włączanie automatycznego logowania w aplikacjach i witrynach.

Dokończ przepływ pracy autouzupełniania

W tej sekcji opisaliśmy konkretne scenariusze, w których możesz podjąć działania w celu ulepszenia funkcji autouzupełniania dla użytkowników aplikacji.

Sprawdzanie, czy autouzupełnianie jest włączone

Użytkownicy mogą włączać i wyłączać autouzupełnianie oraz zmieniać usługę autouzupełniania, wybierając Ustawienia > System > Języki i metody wprowadzania > Zaawansowane > Asystent wprowadzania > Usługa autouzupełniania. Aplikacja nie może zastąpić ustawień autouzupełniania użytkownika, ale możesz wdrożyć dodatkowe funkcje autouzupełniania w aplikacji lub w określonych widokach, jeśli autouzupełnianie jest dostępne dla użytkownika.

Jeśli na przykład użytkownik ma włączone autouzupełnianie, w menu TextView będzie wyświetlany wpis autouzupełniania. Aby sprawdzić, czy autouzupełnianie jest włączone dla użytkownika, wywołaj metodę isEnabled() obiektu AutofillManager.

Aby proces rejestracji i logowania był zoptymalizowany pod kątem użytkowników, którzy nie korzystają z automatycznego wypełniania formularzy, wprowadź logowanie jednym kliknięciem.

Wymuszanie żądania autouzupełniania

Czasami trzeba wymusić żądanie autouzupełniania w odpowiedzi na działanie użytkownika. Na przykład TextView oferuje opcję autouzupełniania, gdy użytkownik dotyka i przytrzymuje widok. Ten przykładowy kod pokazuje, jak wymusić żądanie automatycznego wypełniania:

Kotlin

fun eventHandler(view: View) {
    val afm = requireContext().getSystemService(AutofillManager::class.java)
    afm?.requestAutofill(view)
}

Java

public void eventHandler(View view) {
    AutofillManager afm = context.getSystemService(AutofillManager.class);
    if (afm != null) {
        afm.requestAutofill(view);
    }
}

Możesz też użyć metody cancel(), aby anulować bieżący kontekst autouzupełniania. Może to być przydatne, jeśli masz przycisk, który czyści pola na stronie logowania.

Używaj prawidłowego typu autouzupełniania danych w selektorach

Selektory mogą być przydatne w przypadku funkcji autouzupełniania, ponieważ zapewniają interfejs, który umożliwia użytkownikom zmianę wartości pola przechowującego dane o dacie lub godzinie. Na przykład w formularzu karty kredytowej selektor daty umożliwia użytkownikowi wpisanie lub zmianę daty ważności karty. Aby jednak wyświetlić dane, gdy selektor nie jest widoczny, musisz użyć innego widoku, np. EditText.

Obiekt EditText domyślnie oczekuje danych autouzupełniania typu AUTOFILL_TYPE_TEXT. Jeśli używasz innego typu danych, utwórz widok niestandardowy, który dziedziczy z EditText i implementuje metody wymagane do obsługi odpowiedniego typu danych. Jeśli na przykład masz pole daty, zaimplementuj metody z logiką, która prawidłowo obsługuje wartości typu AUTOFILL_TYPE_DATE.

Gdy określisz typ danych autouzupełniania, usługa autouzupełniania może utworzyć odpowiednią reprezentację danych wyświetlanych w widoku. Więcej informacji znajdziesz w artykule Używanie selektorów z automatycznym wypełnianiem.

Dokończ kontekst autouzupełniania

System autouzupełniania zapisuje dane wprowadzane przez użytkownika, aby można było ich użyć w przyszłości. Po zakończeniu autouzupełniania wyświetla się okno „Zapisz do autouzupełniania?”. Zazwyczaj kontekst autouzupełniania jest usuwany po zakończeniu aktywności. Są jednak sytuacje, w których musisz wyraźnie poinformować framework. Dzieje się tak na przykład, gdy używasz tej samej aktywności, ale różnych fragmentów na ekranie logowania i ekranie treści. W takich sytuacjach możesz jawnie zakończyć kontekst, wywołując funkcję AutofillManager.commit().

Obsługa widoków niestandardowych

Widoki niestandardowe mogą określać metadane udostępniane frameworkowi autouzupełniania za pomocą interfejsu AutoFill API. Niektóre widoki działają jako kontenery wirtualnych podrzędnych, na przykład widoki zawierające interfejs renderowany przez OpenGL. Zanim te widoki zaczną działać z ramami automatycznego wypełniania, muszą używać interfejsu API do określania struktury informacji używanych w aplikacji.

Jeśli aplikacja używa widoków niestandardowych, rozważ te scenariusy:

  • Widok niestandardowy zawiera standardową strukturę widoku lub domyślną strukturę widoku.
  • Widok niestandardowy ma strukturę wirtualną lub strukturę, która jest niedostępna dla platformy autouzupełniania.

Widoki niestandardowe o strukturze widoku standardowego

Widoki niestandardowe mogą definiować metadane, których autouzupełnianie wymaga do działania. Upewnij się, że widok niestandardowy zarządza metadanymi w odpowiednim sposób, aby współpracował z ramówką autouzupełniania. Widok niestandardowy musi wykonywać te działania:

  • Obsługa wartości autouzupełniania, którą framework wysyła do aplikacji.
  • Podaj typ i wartość autouzupełniania do frameworku.

Po aktywowaniu autouzupełniania platforma autouzupełniania wywołuje w widoku autofill() i wysyła wartość, której musi użyć Twój widok. Aby określić, jak widok niestandardowy ma obsługiwać wartość autouzupełniania, użyj elementu autofill().

Widok musi określać typ i wartość autouzupełniania, zastępując metody getAutofillType()getAutofillValue().

Autouzupełnianie nie może wypełniać widoku, jeśli użytkownik nie może podać wartości dla widoku w jego bieżącym stanie, np. gdy jest on wyłączony. W takich przypadkach getAutofillType() musi zwrócić AUTOFILL_TYPE_NONE, getAutofillValue() musi zwrócić null, a autofill() nie może nic robić.

Aby prawidłowo korzystać z ramy, musisz wykonać dodatkowe czynności w tych przypadkach:

  • Widok niestandardowy można edytować.
  • Widok niestandardowy zawiera dane wrażliwe.

Widok niestandardowy można edytować.

Jeśli widok jest edytowalny, poinformuj framework autouzupełniania o zmianach, wywołując funkcję notifyValueChanged() obiektu AutofillManager.

Widok niestandardowy zawiera dane wrażliwe

Jeśli widok zawiera informacje umożliwiające identyfikację, takie jak adresy e-mail, numery kart kredytowych i hasła, musi być oznaczony jako poufny.

Widoki, których treści pochodzą z zasobów statycznych, nie zawierają danych wrażliwych, a widoki, których zawartość jest ustalana dynamicznie, mogą zawierać dane wrażliwe. Na przykład etykieta zawierająca tekst Wpisz swoją nazwę użytkownika nie zawiera danych wrażliwych, podczas gdy etykieta zawierająca tekst Cześć, Janie już tak.

System autouzupełniania zakłada, że wszystkie dane są domyślnie wrażliwe. Możesz oznaczać dane, które nie są poufne.

Aby oznaczyć, czy widok zawiera dane wrażliwe, zaimplementuj funkcję onProvideAutofillStructure() i wywołaj ją w obiekcie ViewStructure.setDataIsSensitive()

Poniższy przykład kodu pokazuje, jak oznaczyć dane w strukturze widoku jako niepoufne:

Kotlin

override fun onProvideAutofillStructure(structure: ViewStructure, flags: Int) {
    super.onProvideAutofillStructure(structure, flags)

    structure.setDataIsSensitive(false)
}

Java

@Override
public void onProvideAutofillStructure(ViewStructure structure, int flags) {
    super.onProvideAutofillStructure(structure, flags);

    structure.setDataIsSensitive(false);
}

Jeśli widok akceptuje tylko wstępnie zdefiniowane wartości, możesz użyć metody setAutofillOptions(), aby ustawić opcje, które można wykorzystać do automatycznego wypełniania widoku. W szczególności widoki, których typ autouzupełniania to AUTOFILL_TYPE_LIST, muszą używać tej metody, ponieważ usługa autouzupełniania może lepiej działać, jeśli zna opcje dostępne do wypełnienia widoku.

Podobnie jest w przypadku widoków, które korzystają z przejściówki, np. Spinner. Na przykład spinner, który dynamicznie generuje lata na podstawie bieżącego roku, aby użyć ich w polach wygaśnięcia karty kredytowej, może implementować metodę getAutofillOptions() interfejsu Adapter, aby podać listę lat.

Widoki, które używają parametru ArrayAdapter, mogą też zawierać listy wartości. ArrayAdapter automatycznie ustawia opcje autouzupełniania w przypadku zasobów statycznych. Jeśli podajesz wartości dynamicznie, zastąp parametr getAutofillOptions().

Widoki niestandardowe z wirtualną strukturą

Platforma autouzupełniania wymaga struktury widoku, aby móc edytować i zapisywać informacje w interfejsie aplikacji. Struktura widoku jest niedostępna dla frameworka w tych sytuacjach:

  • Do renderowania UI aplikacja wykorzystuje silnik renderowania niskiego poziomu, taki jak OpenGL.
  • Aplikacja używa instancji Canvas do wyświetlania interfejsu.

W takich przypadkach możesz określić strukturę widoku, wdrażając onProvideAutofillVirtualStructure()i wykonując te czynności:

  1. Zwiększ liczbę elementów podrzędnych w strukturze widoku, wywołując funkcję addChildCount().
  2. Dodaj dziecko, dzwoniąc na numernewChild().
  3. Aby ustawić identyfikator automatycznego wypełniania dla dziecka, wywołaj funkcję setAutofillId().
  4. Ustaw odpowiednie właściwości, takie jak wartość i typ autouzupełniania.
  5. Jeśli dane wirtualnego dziecka są wrażliwe, prześlij true do setDataIsSensitive(); w przeciwnym razie prześlij false.

Ten fragment kodu pokazuje, jak utworzyć nowy element podrzędny w strukturze wirtualnej:

Kotlin

override fun onProvideAutofillVirtualStructure(structure: ViewStructure, flags: Int) {

    super.onProvideAutofillVirtualStructure(structure, flags)

    // Create a new child in the virtual structure.
    structure.addChildCount(1)
    val child = structure.newChild(childIndex)

    // Set the autofill ID for the child.
    child.setAutofillId(structure.autofillId!!, childVirtualId)

    // Populate the child by providing properties such as value and type.
    child.setAutofillValue(childAutofillValue)
    child.setAutofillType(childAutofillType)

    // Some children can provide a list of values, such as when the child is
    // a spinner.
    val childAutofillOptions = arrayOf<CharSequence>("option1", "option2")
    child.setAutofillOptions(childAutofillOptions)

    // Just like other types of views, mark the data as sensitive when
    // appropriate.
    val sensitive = !contentIsSetFromResources()
    child.setDataIsSensitive(sensitive)
}

Java

@Override
public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {

    super.onProvideAutofillVirtualStructure(structure, flags);

    // Create a new child in the virtual structure.
    structure.addChildCount(1);
    ViewStructure child =
            structure.newChild(childIndex);

    // Set the autofill ID for the child.
    child.setAutofillId(structure.getAutofillId(), childVirtualId);

    // Populate the child by providing properties such as value and type.
    child.setAutofillValue(childAutofillValue);
    child.setAutofillType(childAutofillType);

    // Some children can provide a list of values, such as when the child is
    // a spinner.
    CharSequence childAutofillOptions[] = { "option1", "option2" };
    child.setAutofillOptions(childAutofillOptions);

    // Just like other types of views, mark the data as sensitive when
    // appropriate.
    boolean sensitive = !contentIsSetFromResources();
    child.setDataIsSensitive(sensitive);
}

Gdy elementy wirtualnej struktury ulegną zmianie, poinformuj o tym framework, wykonując te czynności:

  • Jeśli punkt skupienia wewnątrz elementów podrzędnych ulegnie zmianie, wywołaj funkcję notifyViewEntered() i notifyViewExited() na obiekcie AutofillManager.
  • Jeśli wartość elementu podrzędnego ulegnie zmianie, wywołaj funkcję notifyValueChanged() obiektu AutofillManager.
  • Jeśli hierarchia widoku nie jest już dostępna, ponieważ użytkownik ukończył krok w przepływie pracy, np. zalogował się za pomocą formularza logowania, wywołaj funkcję commit() obiektu AutofillManager.
  • Jeśli hierarchia widoków jest nieprawidłowa, ponieważ użytkownik anulował krok w przepływie pracy, na przykład kliknięcie przycisku otwierającego formularz logowania, wywołaj cancel() w obiekcie AutofillManager.

Używanie wywołań zwrotnych w przypadku zdarzeń autouzupełniania

Jeśli aplikacja udostępnia własne widoki autouzupełniania, musisz użyć mechanizmu, który powoduje w niej włączenie lub wyłączenie tych widoków w reakcji na zmiany w interfejsie autouzupełniania. Platforma autouzupełniania udostępnia ten mechanizm w postaci AutofillCallback.

Ta klasa udostępnia metodę onAutofillEvent(View, int), którą aplikacja wywołuje po zmianie stanu autouzupełniania powiązanego z widokiem. Istnieje też przeciążona wersja tej metody, która zawiera parametr childId, którego Twoja aplikacja może używać w przypadku wyświetleń wirtualnych. Dostępne stany są zdefiniowane jako stałe w połączeniu zwrotnym.

Możesz zarejestrować wywołanie zwrotne za pomocą metody registerCallback() klasy AutofillManager. Poniższy przykładowy kod pokazuje, jak zadeklarować wywołanie zwrotne dla zdarzeń autouzupełniania:

Kotlin

val afm = context.getSystemService(AutofillManager::class.java)

afm?.registerCallback(object : AutofillManager.AutofillCallback() {
    // For virtual structures, override
    // onAutofillEvent(View view, int childId, int event) instead.
    override fun onAutofillEvent(view: View, event: Int) {
        super.onAutofillEvent(view, event)
        when (event) {
            EVENT_INPUT_HIDDEN -> {
                // The autofill affordance associated with the view was hidden.
            }
            EVENT_INPUT_SHOWN -> {
                // The autofill affordance associated with the view was shown.
            }
            EVENT_INPUT_UNAVAILABLE -> {
                // Autofill isn't available.
            }
        }

    }
})

Java

AutofillManager afm = getContext().getSystemService(AutofillManager.class);

afm.registerCallback(new AutofillManager.AutofillCallback() {
    // For virtual structures, override
    // onAutofillEvent(View view, int childId, int event) instead.
    @Override
    public void onAutofillEvent(@NonNull View view, int event) {
        super.onAutofillEvent(view, event);
        switch (event) {
            case EVENT_INPUT_HIDDEN:
                // The autofill affordance associated with the view was hidden.
                break;
            case EVENT_INPUT_SHOWN:
                // The autofill affordance associated with the view was shown.
                break;
            case EVENT_INPUT_UNAVAILABLE:
                // Autofill isn't available.
                break;
        }
    }
});

Gdy nadejdzie czas na usunięcie wywołania zwrotnego, użyj metody unregisterCallback().

Dostosowywanie wyróżnionego obiektu do wypełniania

Gdy widok danych jest uzupełniony automatycznie, platforma renderuje w nim obiekt Drawable, aby wskazać, że jego zawartość jest wypełniana automatycznie. Domyślnie ten element jest wypełnionym prostokątem w przezroczystym kolorze, który jest nieco ciemniejszy niż kolor motywu używany do rysowania tła. Nie musisz tego zmieniać, ale możesz go dostosować, zastępując element android:autofilledHighlight motywu używanego przez aplikację lub działanie, jak w tym przykładzie:

res/values/styles.xml

<resources>
    <style name="MyAutofilledHighlight" parent="...">
        <item name="android:autofilledHighlight">@drawable/my_drawable</item>
    </style>
</resources>

res/drawable/my_drawable.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#4DFF0000" />
</shape>

AndroidManifest.xml

<application ...
    android:theme="@style/MyAutofilledHighlight">
<!-- or -->
<activity ...
    android:theme="@style/MyAutofilledHighlight">

Uwierzytelnij na potrzeby autouzupełniania

Usługa autouzupełniania może wymagać uwierzytelnienia użytkownika, zanim będzie mogła wypełnić pola w aplikacji. W takim przypadku system Android uruchamia aktywność uwierzytelniania usługi jako część stosu aktywności.

Nie musisz aktualizować aplikacji, aby obsługiwała uwierzytelnianie, ponieważ uwierzytelnianie odbywa się w ramach usługi. Musisz jednak zadbać o to, by struktura widoku aktywności została zachowywana po ponownym uruchomieniu, np. po utworzeniu struktury widoku w onCreate(), a nie w onStart() czy onResume().

Aby sprawdzić, jak aplikacja zachowuje się, gdy usługa autouzupełniania wymaga uwierzytelnienia, użyj HeuristicsService z przykładowej aplikacji AutofillFramework i skonfiguruj ją tak, aby wymagała uwierzytelniania odpowiedzi autouzupełniania. Aby emulować ten problem, możesz też użyć przykładu BadViewStructureCreationSignInActivity.

Przypisywanie identyfikatorów autouzupełniania do odzyskanych wyświetleń

Kontenery, które odświeżają widoki, takie jak klasa RecyclerView, są przydatne w aplikacjach, które muszą wyświetlać przewijane listy elementów na podstawie dużych zbiorów danych. Podczas przewijania kontenera system ponownie wykorzystuje widoki w układzie, ale widoki zawierają nowe treści.

Jeśli początkowe treści w ponowtórzonym widoku są wypełnione, usługa autouzupełniania zachowuje logiczne znaczenie widoków za pomocą ich identyfikatorów autouzupełniania. Problem pojawia się, gdy system ponownie używa widoków w układzie, a ich identyfikatory logiczne pozostają takie same, co powoduje, że z identyfikatorem autouzupełniania powiązane są niewłaściwe dane użytkownika.

Aby rozwiązać ten problem na urządzeniach z Androidem 9 (poziom interfejsu API 28) lub nowszym, bezpośrednio zarządzaj identyfikatorem autouzupełniania widoków, których używa RecyclerView, korzystając z tych metod:

  • Metoda getNextAutofillId() otrzymuje nowy identyfikator autouzupełniania, który jest unikalny dla danej aktywności.
  • Metoda setAutofillId() ustawia w działaniu unikalny, logiczny identyfikator automatycznego wypełniania tego widoku.

Rozwiązywanie znanych problemów

W tej sekcji znajdziesz obejścia znanych problemów w ramach systemu autouzupełniania.

Autouzupełnianie powoduje awarię aplikacji na Androidzie 8.0 i 8.1

W Androidzie 8.0 (poziom interfejsu API 26) i 8.1 (poziom interfejsu API 27) autouzupełnianie może w niektórych sytuacjach spowodować awarię aplikacji. Aby uniknąć potencjalnych problemów, otaguj widoki, które nie są wypełniane automatycznie, wartością importantForAutofill=no. Możesz też oznaczyć cały trening tagiem importantForAutofill=noExcludeDescendants.

Dialogi o zmienionym rozmiarze nie są uwzględniane w autouzupełnianiu

W Androidzie 8.1 (poziom interfejsu API 27) i starszych, jeśli rozmiar widoku w dialogu zostanie zmieniony po wyświetleniu, widok nie jest uwzględniany w autouzupełnianiu. Te widoki nie są uwzględniane w obiekcie AssistStructure, który system Android wysyła do usługi autouzupełniania. W związku z tym usługa nie może wypełniać widoków.

Aby obejść ten problem, zastąp atrybut token parametrów okna dialogowego atrybutem token aktywności, która tworzy okno dialogowe. Po sprawdzeniu, czy autouzupełnianie jest włączone, zapisz parametry okna w metodzie onWindowAttributesChanged() klasy, która dziedziczy z Dialog. Następnie zastąp właściwość token zapisanych parametrów właściwością token aktywności nadrzędnej w metodzie onAttachedToWindow().

Fragment kodu poniżej pokazuje klasę, w której zastosowano to obejście:

Kotlin

class MyDialog(context: Context) : Dialog(context) {

    // Used to store the dialog window parameters.
    private var token: IBinder? = null

    private val isDialogResizedWorkaroundRequired: Boolean
        get() {
            if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O || Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1) {
                return false
            }
            val autofillManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                context.getSystemService(AutofillManager::class.java)
            } else {
                null
            }
            return autofillManager?.isEnabled ?: false
        }

    override fun onWindowAttributesChanged(params: WindowManager.LayoutParams) {
        if (params.token == null && token != null) {
            params.token = token
        }

        super.onWindowAttributesChanged(params)
    }

    override fun onAttachedToWindow() {
        if (isDialogResizedWorkaroundRequired) {
            token = ownerActivity!!.window.attributes.token
        }

        super.onAttachedToWindow()
    }

}

Java

public class MyDialog extends Dialog {

    public MyDialog(Context context) {
        super(context);
    }

    // Used to store the dialog window parameters.
    private IBinder token;

    @Override
    public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
        if (params.token == null && token != null) {
            params.token = token;
        }

        super.onWindowAttributesChanged(params);
    }

    @Override
    public void onAttachedToWindow() {
        if (isDialogResizedWorkaroundRequired()) {
            token = getOwnerActivity().getWindow().getAttributes().token;
        }

        super.onAttachedToWindow();
    }

    private boolean isDialogResizedWorkaroundRequired() {
        if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O
                || Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1) {
            return false;
        }
        AutofillManager autofillManager =
                null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            autofillManager = getContext().getSystemService(AutofillManager.class);
        }
        return autofillManager != null && autofillManager.isEnabled();
    }

}

Aby uniknąć niepotrzebnych operacji, w tym fragmencie kodu pokazano, jak sprawdzić, czy automatyczne wypełnianie jest obsługiwane na urządzeniu i włączone dla bieżącego użytkownika oraz czy to obejście jest wymagane:

Kotlin

// AutofillExtensions.kt

fun Context.isDialogResizedWorkaroundRequired(): Boolean {
    // After the issue is resolved on Android, check whether the
    // workaround is still required for the current device.
    return isAutofillAvailable()
}

fun Context.isAutofillAvailable(): Boolean {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
        // The autofill framework is available on Android 8.0
        // or higher.
        return false
    }

    val afm = getSystemService(AutofillManager::class.java)
    // Return true if autofill is supported by the device and enabled
    // for the current user.
    return afm != null && afm.isEnabled
}

Java

public class AutofillHelper {

    public static boolean isDialogResizedWorkaroundRequired(Context context) {
        // After the issue is resolved on Android, check whether the
        // workaround is still required for the current device.
        return isAutofillAvailable(context);
    }

    public static boolean isAutofillAvailable(Context context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            // The autofill framework is available on Android 8.0
            // or higher.
            return false;
        }

        AutofillManager afm = context.getSystemService(AutofillManager.class);
        // Return true if autofill is supported by the device and enabled
        // for the current user.
        return afm != null && afm.isEnabled();
    }
}

Testowanie aplikacji za pomocą autouzupełniania

Po zoptymalizowaniu aplikacji pod kątem współpracy z usługami autouzupełniania sprawdź, czy działa ona prawidłowo z tymi usługami.

Aby przetestować aplikację, użyj emulatora lub fizycznego urządzenia z Androidem 8.0 (poziom interfejsu API 26) lub nowszym. Więcej informacji o tworzeniu emulatora znajdziesz w artykule Tworzenie urządzeń wirtualnych i zarządzanie nimi.

Instalowanie usługi autouzupełniania

Zanim przetestujesz aplikację z autouzupełnianiem, musisz zainstalować inną aplikację, która udostępnia usługi autouzupełniania. Do tego celu możesz użyć aplikacji innej firmy, ale łatwiej jest skorzystać z przykładowej usługi autouzupełniania, dzięki czemu nie musisz rejestrować się w usługach innych firm.

Aby przetestować aplikację z usługami autouzupełniania, możesz użyć przykładowego frameworku autouzupełniania na Androida w języku Java. Przykładowa aplikacja udostępnia usługę autouzupełniania i klasy Activity klienta, których możesz użyć do przetestowania przepływu pracy przed użyciem go w swojej aplikacji. Ta strona odwołuje się do przykładowej aplikacji android-AutofillFramework.

Po zainstalowaniu aplikacji włącz usługę autouzupełniania w ustawieniach systemu emulatora. Aby to zrobić, kliknij Ustawienia > System > Języki i metody wprowadzania > Zaawansowane > Asystent wprowadzania > Usługa autouzupełniania.

Analiza wymagań dotyczących danych

Aby przetestować aplikację za pomocą usługi autouzupełniania, usługa musi mieć dane, których może użyć do wypełnienia aplikacji. Usługa musi też wiedzieć, jakiego typu danych oczekuje się w widokach aplikacji. Jeśli na przykład aplikacja ma widok, który wymaga nazwy użytkownika, usługa musi mieć zbiór danych zawierający nazwę użytkownika i jakiś mechanizm, który pozwoli jej rozpoznać, że widok oczekuje takich danych.

Poinformuj usługę, jakiego typu dane są oczekiwane w widokach, ustawiając atrybut android:autofillHints. Niektóre usługi używają zaawansowanych metod heurystycznych do określania typu danych, ale inne, takie jak przykładowa aplikacja, polegają na tym, że deweloper podaje te informacje. Aplikacja będzie lepiej współpracować z usługami autouzupełniania, jeśli w widokach istotnych dla autouzupełniania ustawisz atrybut android:autofillHints.

Przeprowadź test

Po przeanalizowaniu wymagań dotyczących danych możesz uruchomić test, który obejmuje zapisanie danych testowych w usłudze autouzupełniania i aktywowanie autouzupełniania w aplikacji.

Zapisywanie danych w usłudze

Aby zapisać dane w obecnie aktywnej usłudze autouzupełniania:

  1. Otwórz aplikację zawierającą widok, który wymaga typu danych, których chcesz użyć podczas testu. Przykładowa aplikacja android-AutofillFramework udostępnia interfejs użytkownika z widokami, które wymagają kilku typów danych, takich jak numery kart kredytowych i nazwy użytkowników.
  2. Kliknij widok zawierający potrzebny typ danych.
  3. Wpisz wartość w widoku.
  4. Kliknij przycisk potwierdzenia, np. Zaloguj się lub Prześlij. Zwykle formularz musisz przesłać, zanim usługa zapisze dane.
  5. Sprawdź prośbę o uprawnienia w oknie systemowym. W oknie systemowym wyświetla się nazwa usługi, która jest obecnie aktywna, i pytanie, czy chcesz jej użyć w teście. Jeśli chcesz skorzystać z usługi, kliknij Zapisz.

Jeśli Android nie wyświetla okna uprawnień lub usługa nie jest tą, której chcesz używać w teście, sprawdź w ustawieniach systemu, czy jest obecnie aktywna.

Aktywowanie autouzupełniania w aplikacji

Aby aktywować automatyczne wypełnianie w aplikacji:

  1. Otwórz aplikację i otwórz aktywność, która zawiera widoki, które chcesz przetestować.
  2. Kliknij widok, który chcesz wypełnić.
  3. System wyświetla interfejs autouzupełniania, który zawiera zbiory danych, które mogą wypełnić widok, jak pokazano na rysunku 1.
  4. Kliknij zbiór danych zawierający dane, których chcesz użyć. Widok ten wyświetla dane wcześniej zapisane w usłudze.
Interfejs autouzupełniania wyświetlający „dataset-2” jako dostępny zbiór danych
Rysunek 1. Interfejs autouzupełniania z dostępnymi zbiorami danych

Jeśli Android nie wyświetla interfejsu autouzupełniania, wypróbuj te sposoby rozwiązania problemu:

  • Sprawdź, czy widoki w aplikacji używają prawidłowej wartości w atributach android:autofillHints. Listę możliwych wartości tego atrybutu znajdziesz w klasie View w konstantach z przedrostkiem AUTOFILL_HINT.
  • Sprawdź, czy atrybut android:importantForAutofill ma ustawioną wartość inną niż no w widoku, który wymaga wypełnienia, lub inną niż no w widoku lub w jednym z jego elementów nadrzędnych.noExcludeDescendants