Zoptymalizuj aplikację pod kątem autouzupełniania

Aplikacje, które korzystają z widoków standardowych, współpracują z platformą autouzupełniania i nie wymagają specjalnej konfiguracji. Możesz też zoptymalizować sposób, w jaki aplikacja współpracuje z platformą.

Konfigurowanie środowiska autouzupełniania

Z tej sekcji dowiesz się, jak skonfigurować podstawowe funkcje autouzupełniania w aplikacji.

Konfigurowanie usługi autouzupełniania

Aby aplikacja mogła korzystać z platformy 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 jest wyposażona w usługę autouzupełniania, zalecamy jednak korzystanie z usługi testowej podczas testowania aplikacji, np. z usługi autouzupełniania w przykładzie platformy autouzupełniania na Androidzie. Jeśli używasz emulatora, jawnie skonfiguruj usługę autouzupełniania, ponieważ emulator może nie zawierać usługi domyślnej.

Po zainstalowaniu testowej usługi autouzupełniania z przykładowej aplikacji włącz ją. Aby to zrobić, kliknij Ustawienia > System > Języki i metody wprowadzania > Zaawansowane > Pomoc przy wpisywaniu > Usługa autouzupełniania.

Więcej informacji o konfigurowaniu emulatora do testowania autouzupełniania znajdziesz w artykule o testowaniu aplikacji z autouzupełnianiem.

Podawanie wskazówek dotyczących autouzupełniania

Usługa autouzupełniania określa typ każdego widoku za pomocą heurystyki. Jeśli jednak Twoja aplikacja korzysta z tych algorytmów, autouzupełnianie może nieoczekiwanie zmienić się podczas jej aktualizowania. Aby mieć pewność, że usługa autouzupełniania prawidłowo identyfikuje formaty reklam w aplikacji, podaj wskazówki autouzupełniania.

Wskazówki autouzupełniania możesz ustawić za pomocą atrybutu android:autofillHints. Ten przykład ustawia wskazówkę "password" dotyczącą elementu EditText:

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

Wskazówki możesz też ustawiać automatycznie za pomocą metody setAutofillHints(), jak pokazano 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 do usługi autouzupełniania bez wprowadzania zmian ani weryfikacji. Możesz użyć dowolnej wartości, ale klasy View i AndroidX HintConstants zawierają listy oficjalnie obsługiwanych stałych wskazówek.

Korzystając z kombinacji tych stałych, możesz tworzyć układy do typowych scenariuszy autouzupełniania:

Dane logowania do konta

W formularzu logowania możesz umieścić wskazówki dotyczące danych logowania na konto, takie jak AUTOFILL_HINT_USERNAME i AUTOFILL_HINT_PASSWORD.

Aby utworzyć nowe konto lub zmienić nazwę użytkownika i hasło, możesz użyć AUTOFILL_HINT_NEW_USERNAME i AUTOFILL_HINT_NEW_PASSWORD.

Informacje o karcie kredytowej

Gdy prosisz o podanie danych karty kredytowej, możesz skorzystać z wskazówek takich jak AUTOFILL_HINT_CREDIT_CARD_NUMBER i AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE.

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

Adres pocztowy

W przypadku pól adresu fizycznego możesz skorzystać z takich wskazówek:

Imiona i nazwiska

Prosząc o podanie imion i nazwisk, możesz skorzystać z tych wskazówek:

Numery telefonów

W przypadku numerów telefonu możesz użyć tych znaków:

Hasło jednorazowe

Aby zobaczyć hasło jednorazowe w jednym widoku, możesz użyć adresu AUTOFILL_HINT_SMS_OTP.

W przypadku wielu widoków, w których każdy z nich jest mapowany na jedną cyfrę hasła jednorazowego, możesz korzystać z metody generateSmsOtpHintForCharacterPosition(), aby generować wskazówki dla poszczególnych znaków.

Oznacz pola jako ważne na potrzeby autouzupełniania

Na potrzeby autouzupełniania możesz uwzględnić poszczególne pola w aplikacji w strukturze widoku danych. Domyślnie widoki korzystają z trybu IMPORTANT_FOR_AUTOFILL_AUTO, w którym Android za pomocą heurystyki określa, czy dany widok jest ważny dla autouzupełniania.

W pewnych przypadkach widok, struktura widoku danych lub cała aktywność nie są ważne na potrzeby autouzupełniania:

  • Pole CAPTCHA w aktywności związanej z logowaniem
  • Widok, w którym użytkownik tworzy treści, np. edytor tekstu lub arkuszy kalkulacyjnych.
  • Wyświetlenia w przypadku niektórych aktywności w grach, np. przedstawiających rozgrywkę

Znaczenie widoku dla autouzupełniania możesz określić za pomocą atrybutu android:importantForAutofill:

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

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

auto
Pozwól systemowi Android użyć własnych metod heurystycznych, aby określić, czy dane wyświetlenie jest istotne dla autouzupełniania.
no
Ten widok nie jest istotny dla autouzupełniania.
noExcludeDescendants
Ten widok danych i jego elementy podrzędne nie są ważne dla autouzupełniania.
yes
Ten widok jest ważny dla autouzupełniania.
yesExcludeDescendants
Ten widok jest ważny dla autouzupełniania, ale jego elementy podrzędne nie są ważne dla autouzupełniania.

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);

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

  • Pole CAPTCHA w aktywności związanej z logowaniem: użyj android:importantForAutofill="no" lub IMPORTANT_FOR_AUTOFILL_NO, aby oznaczyć ten widok jako mało ważny.
  • Widok, w którym użytkownik tworzy treści: użyj android:importantForAutofill="noExcludeDescendants" lub IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS, aby oznaczyć całą strukturę widoku jako nieważną.
  • Widoki 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 nieważną.

Powiąż dane witryny i aplikacji mobilnej

Usługi autouzupełniania takie jak Autouzupełnianie z Google mogą udostępniać dane logowania użytkownika między przeglądarkami i urządzeniami z Androidem po powiązaniu aplikacji z witryną. Jeśli użytkownik wybierze tę samą usługę autouzupełniania na obu platformach, po zalogowaniu się w aplikacji internetowej jego dane logowania będą dostępne do autouzupełniania, gdy zalogują się w odpowiedniej aplikacji na Androida.

Aby powiązać aplikację na Androida ze swoją witryną, hostuj Digital Asset Link za pomocą relacji delegate_permission/common.get_login_creds w witrynie. Następnie zadeklaruj powiązanie w pliku AndroidManifest.xml aplikacji. Szczegółowe instrukcje łączenia witryny z aplikacją na Androida znajdziesz w artykule Włączanie automatycznego logowania w aplikacjach i witrynach.

Wykonywanie przepływu pracy autouzupełniania

W tej sekcji omawiamy konkretne scenariusze, w których możesz podjąć działania, aby poprawić działanie autouzupełniania użytkowników aplikacji.

Sprawdzanie, czy autouzupełnianie jest włączone

Użytkownicy mogą włączyć lub wyłączyć autouzupełnianie oraz zmienić usługę autouzupełniania. Aby to zrobić, kliknij Ustawienia > System > Języki i metody wprowadzania > Zaawansowane > Pomoc przy wpisywaniu > Usługa autouzupełniania. Twoja aplikacja nie może zastąpić ustawień autouzupełniania wybranych przez użytkownika, ale możesz zaimplementować dodatkowe funkcje autouzupełniania w aplikacji lub w poszczególnych widokach aplikacji, jeśli użytkownik może korzystać z autouzupełniania.

Jeśli na przykład użytkownik ma włączone autouzupełnianie, TextView wyświetla w rozszerzonym menu 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ł zoptymalizowana pod kątem użytkowników bez autouzupełniania, zaimplementuj logowanie jednym dotknięciem.

Wymuś żądanie autouzupełniania

Czasami trzeba wymusić wysłanie żądania autouzupełniania w odpowiedzi na działanie użytkownika. Na przykład TextView udostępnia pozycję menu autouzupełniania, gdy użytkownik kliknie i przytrzyma widok. Poniższy przykładowy kod pokazuje, jak wymusić żądanie autouzupeł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. Jest to przydatne, jeśli masz przycisk, który czyści pola na stronie logowania.

Używaj prawidłowego typu autouzupełniania danych w elementach sterujących selektora

Selektory mogą być przydatne w przypadku autouzupełniania, ponieważ mają interfejs umożliwiający użytkownikom zmianę wartości pola z danymi o dacie lub godzinie. Na przykład w formularzu karty kredytowej selektor daty pozwala użytkownikom wpisać lub zmienić datę ważności karty kredytowej. Aby wyświetlić dane, gdy selektor nie jest widoczny, musisz jednak użyć innego widoku danych, np. EditText.

Obiekt EditText natywnie oczekuje danych autouzupełniania typu AUTOFILL_TYPE_TEXT. Jeśli używasz danych innego typu, utwórz widok niestandardowy, który dziedziczy dane z EditText i implementuje metody wymagane do obsługi danych odpowiedniego typu. Jeśli na przykład masz pole daty, zaimplementuj metody za pomocą logiki, 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 autouzupełnianiem.

Zakończ wyświetlanie kontekstu autouzupełniania

Platforma autouzupełniania zapisuje dane wejściowe użytkownika do wykorzystania w przyszłości. Po zakończeniu kontekstu autouzupełniania wyświetla się okno „Zapisać do autouzupełniania?”. Zwykle kontekst autouzupełniania kończy się po zakończeniu działania. Istnieją jednak sytuacje, w których musisz wyraźnie powiadomić platformę, na przykład gdy używasz tej samej aktywności, ale innych fragmentów na ekranie logowania i na ekranie treści. W takich sytuacjach możesz zamknąć kontekst, wywołując funkcję AutofillManager.commit().

Obsługa widoków niestandardowych

Widoki niestandardowe mogą określać metadane widoczne na platformie autouzupełniania za pomocą interfejsu API autouzupełniania. Niektóre widoki pełnią rolę kontenera wirtualnych elementów podrzędnych, np. widoki zawierające interfejs renderowany przez OpenGL. Aby widoki danych mogły korzystać z platformy autouzupełniania, muszą one korzystać z interfejsu API do określania struktury informacji używanych w aplikacji.

Jeśli Twoja aplikacja korzysta z widoków niestandardowych, weź pod uwagę te scenariusze:

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

Widoki niestandardowe o standardowej strukturze widoku

Widoki niestandardowe mogą definiować metadane niezbędne do działania autouzupełniania. Upewnij się, że widok niestandardowy odpowiednio zarządza metadanymi, aby współpracował z platformą autouzupełniania. Widok niestandardowy musi wykonywać następujące działania:

  • Przetwarzaj wartość autouzupełniania wysyłaną przez platformę do Twojej aplikacji.
  • Podaj typ i wartość autouzupełniania platformy.

Po uruchomieniu autouzupełniania platforma autouzupełniania wywołuje w Twoim widoku danych autofill() i wysyła wartość, której musi on używać. Aby określić, jak widok niestandardowy ma obsługiwać wartości autouzupełniania, zaimplementuj funkcję autofill().

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

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

W tych przypadkach prawidłowe działanie w ramach platformy wymaga wykonania dodatkowych czynności:

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

Widok niestandardowy można edytować

Jeśli widok da się edytować, poinformuj platformę autouzupełniania o zmianach, wywołując notifyValueChanged() w obiekcie AutofillManager.

Widok niestandardowy zawiera dane wrażliwe

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

Ogólnie widoki, których treść pochodzi z zasobów statycznych, nie zawierają danych wrażliwych, a wyświetlenia, których zawartość jest ustawiana dynamicznie, mogą zawierać dane wrażliwe. Na przykład etykieta z tekstem Wpisz swoją nazwę użytkownika nie zawiera danych wrażliwych, a etykieta z tekstem Cześć, Jan – je.

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

Aby zaznaczyć, czy widok zawiera dane wrażliwe, zaimplementuj onProvideAutofillStructure() i wywołaj setDataIsSensitive() w obiekcie ViewStructure.

Na tym przykładowym kodzie widać, jak oznaczyć dane w strukturze widoku jako niewrażliwe:

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 mogą być używane do autouzupełniania tego widoku. Tej metody muszą używać widoki danych z typem autouzupełniania AUTOFILL_TYPE_LIST, ponieważ usługa autouzupełniania może radzić sobie lepiej, jeśli zna dostępne opcje wypełnienia widoku.

Podobnym przypadkiem są widoki danych, które korzystają z przejściówek, np. Spinner. Na przykład wskaźnik postępu, który pokazuje lata generowane dynamicznie na podstawie bieżącego roku i użyty w polach daty ważności karty kredytowej, może zaimplementować metodę getAutofillOptions() w interfejsie Adapter, aby wyświetlić listę lat.

Widoki danych korzystające z elementu ArrayAdapter też mogą zawierać listy wartości. ArrayAdapter automatycznie ustawia opcje autouzupełniania zasobów statycznych. Jeśli podajesz wartości dynamicznie, zastąp getAutofillOptions().

Widoki niestandardowe ze strukturą wirtualną

Platforma autouzupełniania wymaga struktury widoku, aby mogła edytować i zapisywać informacje w interfejsie aplikacji. Struktura widoku jest niedostępna w przypadku tej platformy w tych sytuacjach:

  • Do renderowania UI aplikacja wykorzystuje silnik renderowania niskopoziomowego, taki jak OpenGL.
  • Aplikacja wykorzystuje wystąpienie Canvas do rysowania UI.

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

  1. Zwiększ liczbę elementów podrzędnych struktury widoku, wywołując metodę addChildCount().
  2. Dodaj dziecko, wywołując stronę newChild().
  3. Ustaw identyfikator autouzupełniania dla konta podrzędnego, wywołując metodę setAutofillId().
  4. Ustaw odpowiednie właściwości, takie jak wartość i typ autouzupełniania.
  5. Jeśli dane w wirtualnym środowisku podrzędnym są poufne, przekaż instrukcje true do setDataIsSensitive(). W przeciwnym razie przekaż 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 w strukturze wirtualnej ulegną zmianie, powiadom platformę, wykonując te czynności:

  • Jeśli zaznaczenie wewnątrz elementów podrzędnych zmieni się, wywołaj notifyViewEntered() i notifyViewExited() na obiekcie AutofillManager.
  • Jeśli wartość obiektu podrzędnego zmieni się, wywołaj notifyValueChanged() w obiekcie AutofillManager.
  • Jeśli hierarchia widoków danych jest już niedostępna, ponieważ użytkownik wykonał krok w przepływie pracy, np. loguje się za pomocą formularza logowania, wywołaj commit() w obiekcie AutofillManager.
  • Jeśli hierarchia widoków danych jest nieprawidłowa, ponieważ użytkownik anulował krok w przepływie pracy (np. kliknięcie przycisku, który powoduje wyczyszczenie formularza logowania), wywołaj cancel() w obiekcie AutofillManager.

Używanie wywołań zwrotnych w zdarzeniach autouzupełniania

Jeśli aplikacja ma własne widoki autouzupełniania, potrzebujesz mechanizmu, który będzie informować aplikację, że ma je włączać lub wyłączać w odpowiedzi na zmiany w atrybucji autouzupełniania interfejsu. Platforma autouzupełniania udostępnia ten mechanizm w postaci AutofillCallback.

Ta klasa udostępnia metodę onAutofillEvent(View, int), która jest wywoływana przez aplikację po zmianie stanu autouzupełniania powiązanego z widokiem danych. Istnieje też przeciążona wersja tej metody zawierająca parametr childId, którego aplikacja może używać z widokami wirtualnymi. Dostępne stany w wywołaniu zwrotnym są zdefiniowane jako stałe.

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().

Dostosuj wyróżniony element autouzupełniania z elementami, które można rysować

Gdy widok jest wypełniany automatycznie, platforma renderuje nim element Drawable, co wskazuje, że jego zawartość jest wypełniana automatycznie. Domyślnie ten element do rysowania to ciągły prostokąt z półprzezroczystym kolorem, który jest nieco ciemniejszy od koloru motywu używanego do rysowania tła. Elementu rysowalnego nie trzeba zmieniać, ale można go dostosować, zastępując element android:autofilledHighlight theme używany przez aplikację lub działanie, jak pokazano 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ć od użytkownika uwierzytelnienia, aby usługa mogła wypełniać pola w aplikacji. W takim przypadku system Android uruchamia działanie uwierzytelniania w ramach stosu aktywności.

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

Aby sprawdzić, jak zachowuje się aplikacja, gdy usługa autouzupełniania wymaga uwierzytelniania, za pomocą usługi HeuristysService z przykładowego AutofillFramework i skonfiguruj ją tak, aby wymagała uwierzytelniania odpowiedzi wypełnienia. Do emulacji tego problemu możesz też użyć przykładu BadViewStructuredCreationSignInActivity.

Przypisz identyfikatory autouzupełniania do widoków użytych podczas recyklingu

Kontenery, które wykorzystują widoki danych, np. klasa RecyclerView, są przydatne w aplikacjach, które muszą wyświetlać przewijane listy elementów oparte na dużych zbiorach danych. Gdy kontener jest przewijany, system wykorzystuje widoki danych w układzie, ale zawierają one nowe treści.

Jeśli początkowa zawartość widoku przetworzonego zostanie uzupełniona, usługa autouzupełniania zachowuje logiczne znaczenie widoków danych za pomocą ich identyfikatorów autouzupełniania. Problem pojawia się, gdy system wykorzystuje widoki danych w tym układzie, a identyfikatory logiczne widoków pozostają takie same, co powoduje powiązanie z identyfikatorem autouzupełniania nieprawidłowych danych użytkownika.

Aby rozwiązać ten problem na urządzeniach z Androidem 9 (poziom interfejsu API 28) lub nowszym, zarządzaj identyfikatorem autouzupełniania widoków używanych przez RecyclerView. Aby to zrobić, wykonaj te czynności:

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

Rozwiązywanie znanych problemów

W tej sekcji przedstawiamy sposoby obejścia znanych problemów z systemem autouzupełniania.

Autouzupełnianie powoduje awarie aplikacji na Androidzie 8.0, 8.1

Na 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 obejść potencjalne problemy, otaguj widoki danych, które nie są automatycznie wypełniane parametrem importantForAutofill=no. Możesz też oznaczyć całą aktywność tagiem importantForAutofill=noExcludeDescendants.

Zmienione rozmiary okien nie są brane pod uwagę podczas autouzupełniania

W Androidzie 8.1 (poziom interfejsu API 27) i starszych, jeśli rozmiar widoku w oknie jest już zmieniony po jego wyświetleniu, nie jest on uwzględniany w autouzupełnianiu. Widoki te 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łnić wyświetleń.

Aby obejść ten problem, zastąp właściwość token parametrów okna dialogowego właściwością token aktywności, która tworzy okno. 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().

Poniższy fragment kodu zawiera klasę wykorzystującą 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, z tego fragmentu kodu dowiesz się, jak sprawdzić, czy autouzupełnianie jest obsługiwane na urządzeniu i włączone dla bieżącego użytkownika, oraz czy jest wymagane obejście tego problemu:

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 do pracy z usługami autouzupełniania sprawdź, czy działa ona zgodnie z oczekiwaniami.

Do testowania aplikacji użyj emulatora lub urządzenia fizycznego 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

Aby przetestować aplikację za pomocą autouzupełniania, musisz zainstalować inną aplikację, która udostępnia usługi autouzupełniania. Możesz w tym celu użyć aplikacji innej firmy, ale łatwiej jest użyć przykładowej usługi autouzupełniania, dzięki czemu nie musisz rejestrować się w takich usługach.

Aby przetestować swoją aplikację za pomocą usług autouzupełniania, możesz użyć przykładowej platformy autouzupełniania Androida w 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 jej 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 > Pomoc przy wprowadzaniu tekstu > Usługa autouzupełniania.

Analizowanie wymagań dotyczących danych

Aby przetestować aplikację z usługą autouzupełniania, usługa musi mieć dane, których może używać do wypełnienia aplikacji. Usługa musi też wiedzieć, jakiego typu danych należy oczekiwać w widokach aplikacji. Jeśli na przykład aplikacja ma widok danych, który wymaga nazwy użytkownika, usługa musi mieć zbiór danych zawierający nazwę użytkownika i mechanizm informujący o tym, że widok oczekuje takich danych.

Ustawiając atrybut android:autofillHints, poinformuj usługę, jakiego typu danych należy się spodziewać w Twoich widokach. Niektóre usługi używają zaawansowanych metod heurystycznych do określania typu danych, a inne – tak jak w przypadku przykładowej aplikacji – wymagają od dewelopera. Twoja aplikacja będzie działać lepiej z usługami autouzupełniania, jeśli ustawisz atrybut android:autofillHints w widokach odpowiednich do autouzupełniania.

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 aktywnej usłudze autouzupełniania, wykonaj te czynności:

  1. Otwórz aplikację zawierającą widok, który oczekuje typu danych, którego chcesz użyć podczas testu. Przykładowa aplikacja android-AutofillFramework ma interfejs użytkownika z widokami, które wymagają różnych typów danych, takich jak numery kart kredytowych i nazwy użytkowników.
  2. Kliknij widok, w którym znajdziesz potrzebne dane.
  3. Wpisz wartość w widoku.
  4. Kliknij przycisk potwierdzenia, np. Zaloguj się lub Prześlij. Zwykle musisz przesłać formularz, zanim usługa zapisze dane.
  5. Sprawdź prośbę o przyznanie uprawnień w oknie systemowym. W oknie dialogowym pojawi się nazwa aktualnie aktywnej usługi oraz pytanie, czy właśnie tej usługi chcesz użyć w teście. Jeśli chcesz skorzystać z usługi, kliknij Zapisz.

Jeśli Android nie wyświetla okna z prośbą o przyznanie uprawnień lub jeśli usługa nie jest odpowiednią usługą, której chcesz użyć w teście, sprawdź w ustawieniach systemu, czy jest ona obecnie aktywna.

Aktywowanie autouzupełniania w aplikacji

Aby włączyć autouzupełnianie w aplikacji:

  1. Otwórz aplikację i przejdź do aktywności zawierającej widoki, które chcesz przetestować.
  2. Kliknij widok, który chcesz wypełnić.
  3. System wyświetli interfejs autouzupełniania zawierający zbiory danych, które mogą wypełnić cały widok, jak widać na Rysunku 1.
  4. Kliknij zbiór danych zawierający dane, których chcesz użyć. W widoku wyświetlane są dane przechowywane wcześniej w usłudze.
Interfejs autouzupełniania wyświetlający zbiór danych „dataset-2” jako dostępny zbiór danych
Rysunek 1. Interfejs autouzupełniania wyświetlający dostępne zbiory danych.

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

  • Sprawdź, czy widoki danych w Twojej aplikacji mają prawidłową wartość atrybutu android:autofillHints. Listę możliwych wartości atrybutu znajdziesz w stałych z prefiksem AUTOFILL_HINT w klasie View.
  • Sprawdź, czy atrybut android:importantForAutofill ma w widoku danych inną wartość niż no lub w widoku danych albo w jednym z jego elementów nadrzędnych ma wartość inną niż noExcludeDescendants.