Zoptymalizuj aplikację pod kątem autouzupełniania

Aplikacje, które korzystają ze standardowych widoków, działają z platformą autouzupełniania bez konieczności specjalnej konfiguracji. Możesz też zoptymalizować sposób, w jaki aplikacja współpracuje z tym frameworkiem.

Konfigurowanie środowiska autouzupełniania

W tej sekcji opisano, jak skonfigurować podstawową funkcję autouzupełniania w aplikacji.

Konfigurowanie usługi autouzupełniania

Aby aplikacja mogła korzystać z platformy autouzupełniania, na urządzeniu musi być skonfigurowana usługa autouzupełniania. Większość telefonów i tabletów z Androidem 8.0 (API na poziomie 26) lub nowszym jest dostarczana z usługą autouzupełniania. Podczas testowania aplikacji zalecamy jednak korzystanie z usługi testowej, np. usługi autouzupełniania w przykładowym frameworku autouzupełniania na Androidzie. 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 ją, wybierając 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 Testowanie aplikacji za pomocą autouzupełniania.

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 heurystyk, zachowanie autouzupełniania może się nieoczekiwanie zmienić podczas aktualizacji aplikacji. Aby usługa autouzupełniania prawidłowo identyfikowała formaty aplikacji, podaj wskazówki dotyczące autouzupełniania.

Wskazówki dotyczące autouzupełniania możesz ustawić za pomocą atrybutu android:autofillHints. W tym przykładzie ustawiamy wskazówkę "password" w elemencie EditText:

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

Wskazówki możesz też ustawić programowo 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 podpowiedzi

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

Używając kombinacji tych stałych, możesz tworzyć układy dla typowych scenariuszy autouzupełniania:

Dane logowania

W formularzu logowania możesz umieścić wskazówki dotyczące danych logowania do konta, np. AUTOFILL_HINT_USERNAMEAUTOFILL_HINT_PASSWORD.

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

Informacje o karcie kredytowej

Podczas wysyłania prośby o informacje o karcie kredytowej możesz używać wskazówek, takich jak AUTOFILL_HINT_CREDIT_CARD_NUMBER i AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE.

W przypadku dat ważności kart kredytowych wykonaj jedną z tych czynności:

Adres pocztowy

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

Imiona i nazwiska

Gdy prosisz o podanie imion, możesz użyć takich wskazówek:

Numery telefonów

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

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 jest mapowany na jedną cyfrę kodu OTP, możesz użyć metody generateSmsOtpHintForCharacterPosition(), aby wygenerować podpowiedzi dla poszczególnych znaków.

Łączenie danych witryny i aplikacji mobilnej

Usługi autouzupełniania, takie jak autouzupełnianie Google, mogą udostępniać dane logowania użytkownika między przeglądarkami i urządzeniami z Androidem po powiązaniu aplikacji i witryny. 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 podczas logowania się w odpowiedniej aplikacji na Androida.

Aby powiązać aplikację na Androida z witryną, umieść w niej Digital Asset Link z relacją delegate_permission/common.get_login_creds. Następnie zadeklaruj powiązanie w pliku AndroidManifest.xml aplikacji. Szczegółowe instrukcje dotyczące powiązania 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 opisujemy konkretne scenariusze, w których możesz podjąć działania, aby ulepszyć funkcję autouzupełniania dla użytkowników Twojej aplikacji.

Sprawdzanie, czy autouzupełnianie jest włączone

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

Na przykład TextView pokazuje wpis autouzupełniania w menu przepełnienia, jeśli autouzupełnianie jest włączone dla użytkownika. Aby sprawdzić, czy autouzupełnianie jest włączone dla użytkownika, wywołaj metodę isEnabled() obiektu AutofillManager.

Aby mieć pewność, że proces rejestracji i logowania jest zoptymalizowany pod kątem użytkowników, którzy nie korzystają z autouzupełniania, wdróż logowanie jednym kliknięciem.

Wymuszanie żądania autouzupełniania

Czasami musisz wymusić wysłanie żądania autouzupełniania w odpowiedzi na działanie użytkownika. Na przykład TextView oferuje element menu autouzupełniania, gdy użytkownik dotknie widoku i przytrzyma go. Poniższy przykład kodu 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. Może to być przydatne, jeśli masz przycisk, który czyści pola na stronie logowania.

Używanie prawidłowego typu autouzupełniania w przypadku danych w kontrolkach selektora

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

Obiekt EditText natywnie oczekuje danych autouzupełniania typu AUTOFILL_TYPE_TEXT. Jeśli używasz innego typu danych, utwórz widok niestandardowy, który dziedziczy po 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 Korzystanie z selektorów z autouzupełnianiem.

Dokończ kontekst autouzupełniania

Platforma autouzupełniania zapisuje dane wprowadzane przez użytkownika, aby można było z nich korzystać w przyszłości. Po zakończeniu kontekstu autouzupełniania wyświetla okno „Zapisać do autouzupełniania?”. Zazwyczaj kontekst autouzupełniania kończy się wraz z zakończeniem działania. W niektórych sytuacjach musisz jednak wyraźnie powiadomić platformę, np. jeśli używasz tego samego działania, ale różnych fragmentów na ekranach logowania i 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 platformie autouzupełniania za pomocą interfejsu Autofill API. Niektóre widoki działają jako kontener wirtualnych elementów podrzędnych, np. widoki zawierające interfejs renderowany za pomocą OpenGL. Zanim te widoki będą mogły współpracować z platformą autouzupełniania, muszą używać 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 strukturę wirtualną, czyli strukturę widoku, która nie jest dostępna dla platformy autouzupełniania.

Widoki niestandardowe o strukturze widoku standardowego

Widoki niestandardowe mogą definiować metadane, które są wymagane do działania autouzupełniania. Upewnij się, że widok niestandardowy odpowiednio zarządza metadanymi, aby współpracować z platformą autouzupełniania. Niestandardowy widok musi wykonać te czynności:

  • Obsługuj wartość autouzupełniania, którą platforma wysyła do aplikacji.
  • Przekaż do platformy typ i wartość autouzupełniania.

Gdy zostanie uruchomione automatyczne wypełnianie, framework automatycznego wypełniania wywołuje autofill() w widoku i wysyła wartość, której musi użyć widok. Zaimplementuj autofill(), aby określić, jak niestandardowy widok obsługuje wartość autouzupełniania.

Widok musi określać typ i wartość autouzupełniania przez zastąpienie odpowiednio metod getAutofillType() i getAutofillValue().

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

W tych przypadkach wymagane są dodatkowe czynności, aby prawidłowo korzystać z ram:

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

Widok niestandardowy można edytować

Jeśli widok można edytować, powiadom platformę autouzupełniania o zmianach, wywołując notifyValueChanged() na obiekcie 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 zawierający dane wrażliwe.

Ogólnie rzecz biorąc, widoki, których treść pochodzi ze statycznych zasobów, nie zawierają danych wrażliwych, natomiast widoki, których treść jest ustawiana dynamicznie, mogą zawierać dane wrażliwe. Na przykład etykieta zawierająca tekst wpisz nazwę użytkownika nie zawiera danych wrażliwych, a etykieta zawierająca tekst Cześć, Janie już tak.

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

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

Poniższy przykład kodu pokazuje, jak oznaczyć dane w strukturze widoku jako nieobjęte ochroną:

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() do ustawienia opcji, które można wykorzystać do autouzupeł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 działać lepiej, jeśli zna opcje dostępne do wypełnienia widoku.

Podobna sytuacja występuje w przypadku widoków, które korzystają z adaptera, np. Spinner. Na przykład selektor, który dynamicznie tworzy lata na podstawie bieżącego roku, aby używać ich w polach daty ważności karty kredytowej, może implementować metodę getAutofillOptions() interfejsu Adapter, aby udostępniać listę lat.

Widoki, które korzystają z 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 ze strukturą wirtualną

Aby edytować i zapisywać informacje w interfejsie aplikacji, platforma autouzupełniania wymaga struktury widoku. Struktura widoku jest niedostępna dla platformy w tych sytuacjach:

  • Aplikacja używa silnika renderowania niskiego poziomu, np. OpenGL, do renderowania interfejsu.
  • Aplikacja używa instancji Canvas do rysowania interfejsu.

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 w strukturze widoku, wywołując metodę addChildCount().
  2. Dodaj dziecko, dzwoniąc newChild().
  3. Ustaw identyfikator autouzupełniania dla elementu podrzędnego, wywołując funkcję setAutofillId().
  4. Ustaw odpowiednie właściwości, takie jak wartość i typ automatycznego wypełniania.
  5. Jeśli dane w wirtualnym profilu dziecka są wrażliwe, przekaż wartość true do setDataIsSensitive(); w przeciwnym razie przekaż wartość false.

Poniższy fragment kodu pokazuje, jak utworzyć nowe dziecko 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 o tym platformę, wykonując te czynności:

  • Jeśli fokus w elementach podrzędnych się zmieni, wywołaj notifyViewEntered() i notifyViewExited() na obiekcie AutofillManager.
  • Jeśli wartość elementu podrzędnego ulegnie zmianie, wywołaj funkcję notifyValueChanged() na obiekcie AutofillManager.
  • Jeśli hierarchia widoków jest już niedostępna, ponieważ użytkownik wykonał krok w procesie, np. zalogował się za pomocą formularza logowania, wywołaj metodę commit() na obiekcie AutofillManager.
  • Jeśli hierarchia widoków jest nieprawidłowa, ponieważ użytkownik anulował krok w procesie, np. kliknął przycisk, który czyści formularz logowania, wywołaj metodę cancel() na obiekcie AutofillManager.

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

Jeśli Twoja aplikacja udostępnia własne widoki autouzupełniania, potrzebujesz mechanizmu, który informuje aplikację o włączaniu lub wyłączaniu widoków w odpowiedzi na zmiany w funkcji autouzupełniania interfejsu. 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 aplikacja może używać w przypadku wirtualnych wyświetleń. Dostępne stany są zdefiniowane jako stałe w wywołaniu zwrotnym.

Możesz zarejestrować wywołanie zwrotne za pomocą metody registerCallback() klasy AutofillManager. Poniższy przykład kodu 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 podświetlonego elementu rysowalnego autouzupełniania

Gdy widok zostanie wypełniony automatycznie, platforma renderuje element Drawable nad widokiem, aby wskazać, że jego zawartość została wypełniona automatycznie. Domyślnie jest to prostokąt wypełniony jednolitym, półprzezroczystym kolorem, który jest nieco ciemniejszy niż kolor motywu używany do rysowania tła. Nie musisz zmieniać elementu rysowalnego, ale możesz go dostosować, zastępując element android:autofilledHighlight motywu używanego przez aplikację lub aktywność, 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">

Uwierzytelnianie na potrzeby autouzupełniania

Usługa autouzupełniania może wymagać od użytkownika uwierzytelnienia, 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 aplikacji.

Nie musisz aktualizować aplikacji, aby obsługiwała uwierzytelnianie, ponieważ odbywa się ono w ramach usługi. Musisz jednak zadbać o to, aby struktura widoku aktywności została zachowana po jej ponownym uruchomieniu, np. tworząc strukturę widoku w onCreate(), a nie w onStart() ani onResume().

Możesz sprawdzić, jak aplikacja zachowuje się, gdy usługa autouzupełniania wymaga uwierzytelniania, korzystając z HeuristicsService z przykładowej aplikacji AutofillFramework i konfigurując ją tak, aby wymagała uwierzytelniania odpowiedzi na wypełnienie. Aby zasymulować ten problem, możesz też użyć przykładowego kodu BadViewStructureCreationSignInActivity.

Przypisywanie identyfikatorów autouzupełniania do widoków z odzysku

Kontenery, które ponownie wykorzystują widoki, np. klasa RecyclerView, są przydatne w przypadku aplikacji, 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 zawierają one nowe treści.

Jeśli początkowa zawartość widoku z odzysku zostanie wypełniona, usługa autouzupełniania zachowa logiczne znaczenie widoków za pomocą ich identyfikatorów autouzupełniania. Problem pojawia się, gdy system ponownie wykorzystuje widoki w układzie. Ich identyfikatory logiczne pozostają takie same, co powoduje, że z identyfikatorem autouzupełniania są powiązane nieprawidłowe dane użytkownika.

Aby rozwiązać ten problem na urządzeniach z Androidem 9 (poziom interfejsu API 28) lub nowszym,RecyclerViewwyraźnie zarządzaj identyfikatorem autouzupełniania widoków używanych przezRecyclerViewza pomocą tych metod:

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

Rozwiązywanie znanych problemów

W tej sekcji znajdziesz obejścia znanych problemów w ramach automatycznego wypełniania.

Autouzupełnianie powoduje awarie aplikacji na Androidzie 8.0 i 8.1

W Androidzie 8.0 (interfejs API na poziomie 26) i 8.1 (interfejs API na poziomie 27) autouzupełnianie może w pewnych sytuacjach powodować awarię aplikacji. Aby uniknąć potencjalnych problemów, oznacz wszystkie wyświetlenia, które nie są wypełniane automatycznie, symbolem importantForAutofill=no. Możesz też oznaczyć całą aktywność tagiem importantForAutofill=noExcludeDescendants.

Zmienione okna nie są uwzględniane w autouzupełnianiu

W Androidzie 8.1 (poziom interfejsu API 27) i starszych wersjach, jeśli rozmiar widoku w oknie zostanie zmieniony po jego wyświetleniu, nie będzie on brany pod uwagę podczas autouzupełniania. 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łnić widoków.

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 dialogowe. Po sprawdzeniu, czy autouzupełnianie jest włączone, zapisz parametry okna w metodzie onWindowAttributesChanged() klasy, która dziedziczy po 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 pokazuje klasę, która implementuje 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, poniższy fragment kodu pokazuje, jak sprawdzić, czy na urządzeniu jest obsługiwane autouzupełnianie i czy jest ono 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 usług autouzupełniania sprawdź, czy działa ona zgodnie z oczekiwaniami.

Do testowania aplikacji używaj emulatora lub urządzenia fizycznego z Androidem 8.0 (API na poziomie 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ę za pomocą autouzupełniania, musisz zainstalować inną aplikację, która udostępnia usługi autouzupełniania. W tym celu możesz użyć aplikacji innej firmy, ale łatwiej jest skorzystać z przykładowej usługi autouzupełniania, aby nie musieć rejestrować się w usługach innych firm.

Aby przetestować aplikację z usługami autouzupełniania, możesz użyć przykładowego kodu platformy autouzupełniania na Androida w Javie. Przykładowa aplikacja udostępnia usługę autouzupełniania i klasy klienta Activity, 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. W tym celu otwórz Ustawienia > System > Języki i metody wprowadzania > Zaawansowane > Pomoc przy wpisywaniu > Usługa autouzupełniania.

Analizowanie 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. Musi też wiedzieć, jakiego rodzaju dane są oczekiwane w widokach aplikacji. Jeśli na przykład aplikacja ma widok, w którym oczekiwana jest nazwa użytkownika, usługa musi mieć zbiór danych zawierający nazwę użytkownika i mechanizm, który informuje, że w widoku oczekiwane są takie dane.

Określ typ danych, które mają być widoczne w widokach, ustawiając atrybut android:autofillHints. Niektóre usługi używają zaawansowanych heurystyk do określania typu danych, ale inne, np. aplikacja przykładowa, polegają na tym, że deweloper podaje te informacje. Aplikacja lepiej współpracuje 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 przeprowadzić test, który obejmuje zapisywanie danych testowych w usłudze autouzupełniania i wywoływanie autouzupełniania w aplikacji.

Zapisywanie danych w usłudze

Aby zapisać dane w usłudze autouzupełniania, która jest obecnie aktywna:

  1. Otwórz aplikację zawierającą widok, który oczekuje typu danych, jakiego chcesz użyć podczas testu. Przykładowa aplikacja android-AutofillFramework udostępnia interfejs z widokami, które oczekują kilku typów danych, takich jak numery kart kredytowych i nazwy użytkowników.
  2. Kliknij widok zawierający typ danych, których potrzebujesz.
  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 uprawnienia w oknie systemowym. W oknie dialogowym systemu wyświetli się nazwa usługi, która jest obecnie aktywna, oraz pytanie, czy chcesz użyć tej usługi w teście. Jeśli chcesz korzystać z tej usługi, kliknij Zapisz.

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

Wywoływanie autouzupełniania w aplikacji

Aby wywołać automatyczne wypełnianie w aplikacji:

  1. Otwórz aplikację i przejdź do aktywności, w której chcesz przetestować widoki.
  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 wyświetla dane przechowywane wcześniej w usłudze.
Interfejs autouzupełniania wyświetla „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, możesz wypróbować te rozwiązania:

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