Zoptymalizuj aplikację pod kątem autouzupełniania

Aplikacje, które używają widoków standardowych, współpracują z platformą autouzupełniania bez konieczności specjalnej konfiguracji. Możesz też zoptymalizować sposób działania aplikacji w ramach tej platformy.

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. Chociaż większość telefonów i tabletów z Androidem 8.0 (poziom interfejsu API 26) lub nowszym jest dostarczana z usługą autouzupełniania, zalecamy używanie usługi testowej podczas testowania aplikacji, np. usługi autouzupełniania w przykładzie platformy autouzupełniania Androida. Jeśli używasz emulatora, wyraźnie ustaw usługę autouzupełniania, ponieważ emulator może nie mieć usługi domyślnej.

Po zainstalowaniu testowej usługi autouzupełniania z przykładowej aplikacji włącz ją, klikając Ustawienia > System > Języki i metody wprowadzania > Zaawansowane > Pomoc przy wprowadzaniu > Usługa autouzupełniania.

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

Używanie kontekstu aktywności w widokach

Do prawidłowego działania usługi autouzupełniania wymagany jest kontekst aktywności. Podczas tworzenia instancji widoku użyj Activity jako kontekstu, aby można było prawidłowo go autouzupełniać.

Podawanie wskazówek dotyczących autouzupełniania

Usługa autouzupełniania określa typ każdego widoku za pomocą heurystyki. Jeśli jednak aplikacja korzysta z tych heurystyk, zachowanie autouzupełniania może się nieoczekiwanie zmienić po zaktualizowaniu aplikacji. Aby mieć pewność, że usługa autouzupełniania prawidłowo identyfikuje formaty aplikacji, podaj wskazówki dotyczące autouzupełniania.

Wskazówki autouzupełniania możesz ustawić za pomocą atrybutu android:autofillHints. W tym przykładzie ustawiono wskazówkę "password" w 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ówek

Platforma autouzupełniania nie weryfikuje podpowiedzi. 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.

Za pomocą 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, takie jak AUTOFILL_HINT_USERNAME i AUTOFILL_HINT_PASSWORD.

Aby utworzyć nowe konto lub gdy użytkownicy zmieniają 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 użyć 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 dotyczących adresu fizycznego możesz użyć takich wskazówek:

imiona i nazwiska osób,

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

Numery telefonów

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

Hasło jednorazowe

Aby uzyskać 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ę hasła jednorazowego, możesz użyć metody generateSmsOtpHintForCharacterPosition(), aby wygenerować podpowiedzi dla poszczególnych znaków.

Powiązanie danych z 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 i witryny. Gdy użytkownik wybierze tę samą usługę autouzupełniania na obu platformach, zalogowanie się w aplikacji internetowej spowoduje, że dane logowania będą dostępne do autouzupełniania podczas logowania się w odpowiedniej aplikacji na Androida.

Aby powiązać aplikację na Androida ze swoją 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 logowania automatycznego w aplikacjach i witrynach.

Ukończenie procesu autouzupełniania

W tej sekcji opisano konkretne scenariusze, w których możesz podjąć działania, aby ulepszyć funkcję 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, klikając Ustawienia > System > Języki i metody wprowadzania > Zaawansowane > Pomoc przy wprowadzaniu > 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 jej konkretnych widokach, jeśli autouzupełnianie jest dostępne dla użytkownika.

Na przykład TextView pokazuje wpis autouzupełniania w rozszerzonym menu, 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 dotknięciem.

Wymuszanie prośby o autouzupełnianie

Czasami trzeba wymusić żądanie autouzupełniania w odpowiedzi na działanie użytkownika. Na przykład TextView oferuje element menu autouzupełniania, gdy użytkownik naciśnie 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. 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 elementach sterujących 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 o dacie lub godzinie. Na przykład w formularzu karty kredytowej selektor daty umożliwia użytkownikom wpisanie lub zmianę daty ważności karty kredytowej. Musisz jednak użyć innego widoku, np. EditText, aby wyświetlić dane, gdy selektor nie jest widoczny.

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 Używanie selektorów z autouzupełnianiem.

Dokończ kontekst autouzupełniania

Platforma autouzupełniania zapisuje dane wprowadzane przez użytkownika do wykorzystania w przyszłości, wyświetlając okno „Zapisać do autouzupełniania?” po zakończeniu kontekstu autouzupełniania. Zwykle kontekst autouzupełniania kończy się po zakończeniu aktywności. Są jednak sytuacje, w których musisz wyraźnie powiadomić platformę – na przykład jeśli używasz tej samej aktywności, ale różnych fragmentów dla ekranów 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, które są udostępniane platformie autouzupełniania za pomocą interfejsu API autouzupełniania. Niektóre widoki działają jako kontenery elementów podrzędnych, np. widoki zawierające interfejs renderowany przez OpenGL. Aby te widoki mogły działać z platformą autouzupełniania, muszą używać interfejsu API do określania struktury informacji używanych w aplikacji.

Jeśli aplikacja używa widoków niestandardowych, weź pod uwagę te scenariusze:

  • Widok niestandardowy zapewnia strukturę widoku standardowego lub domyślnego.
  • Widok niestandardowy ma strukturę wirtualną lub strukturę widoku, która nie jest dostępna dla platformy autouzupełniania.

Widoki niestandardowe ze strukturą widoku standardowego

Widoki niestandardowe mogą definiować metadane, których wymaga autouzupełnianie. Upewnij się, że widok niestandardowy odpowiednio zarządza metadanymi, aby działać z platformą autouzupełniania. Widok niestandardowy musi wykonywać te działania:

  • Obsługuj wartość autouzupełniania wysyłaną do aplikacji przez platformę.
  • Przekaż platformie typ i wartość autouzupełniania.

Gdy zostanie uruchomione autouzupełnianie, platforma autouzupełniania wywołuje metodę autofill() w widoku i wysyła wartość, której widok musi użyć. Zaimplementuj autofill(), aby określić, jak widok niestandardowy ma obsługiwać 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 – na przykład gdy widok jest wyłączony. W takich przypadkach getAutofillType() musi zwracać AUTOFILL_TYPE_NONE, getAutofillValue() musi zwracać null, a autofill() nie musi nic robić.

W tych przypadkach wymagane są dodatkowe czynności, aby zapewnić prawidłowe działanie w ramach platformy:

  • 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 metodę 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 poufny.

Ogólnie widoki, których zawartość pochodzi z zasobów statycznych, nie zawierają danych wrażliwych, natomiast widoki, których zawartość jest ustawiana dynamicznie, mogą zawierać dane wrażliwe. Na przykład etykieta zawierająca tekst Wpisz nazwę użytkownika nie zawiera danych wrażliwych, ale etykieta zawierająca tekst Cześć, Jan już tak.

Platforma autouzupełniania domyślnie zakłada, że wszystkie dane są poufne. 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ładowy kod 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 mogą być używane 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.

Podobnie jest w przypadku widoków, które używają adaptera, np. Spinner. Na przykład spinner, który udostępnia dynamicznie tworzone lata na podstawie bieżącego roku do użycia w polach daty ważności karty kredytowej, może implementować metodę getAutofillOptions() interfejsu Adapter, aby udostępniać listę lat.

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

Widoki niestandardowe ze strukturą wirtualną

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

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

W takich przypadkach możesz określić strukturę widoku, implementując interfejs 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 pod numer newChild().
  3. Ustaw identyfikator autouzupełniania dla elementu 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 elemencie podrzędnym są poufne, przekaż true do setDataIsSensitive(). W przeciwnym razie przekaż false.

Poniższy 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 o tym platformę, wykonując te czynności:

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

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

Jeśli aplikacja udostępnia własne widoki autouzupełniania, potrzebujesz mechanizmu, który informuje aplikację, aby włączała lub wyłączała widoki w odpowiedzi 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 aplikacja może używać z widokami wirtualnymi. Dostępne stany są zdefiniowane jako stałe w wywołaniu zwrotnym.

Wywołanie zwrotne możesz zarejestrować 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 elementu rysowalnego autouzupełniania

Gdy widok jest wypełniany automatycznie, platforma renderuje nad nim element Drawable, aby wskazać, że zawartość widoku jest wypełniana automatycznie. Domyślnie jest to jednolity prostokąt o półprzezroczystym kolorze, który jest nieco ciemniejszy niż kolor motywu używany do rysowania tła. Nie trzeba zmieniać elementu rysowalnego, ale można 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 w funkcji 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.

Nie musisz aktualizować aplikacji, aby obsługiwała uwierzytelnianie, ponieważ uwierzytelnianie odbywa się w usłudze. Musisz jednak upewnić się, że struktura widoku aktywności zostanie zachowana po ponownym uruchomieniu aktywności, np. przez utworzenie struktury widoku w metodzie onCreate(), a nie w metodzie onStart() lub onResume().

Możesz sprawdzić, jak zachowuje się aplikacja, gdy usługa autouzupełniania wymaga uwierzytelnienia, używając HeuristicsService z przykładowego AutofillFramework i konfigurując go tak, aby wymagał uwierzytelnienia odpowiedzi na wypełnienie. Możesz też użyć przykładowej aktywności BadViewStructureCreationSignInActivity, aby zasymulować ten problem.

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

Kontenery, które wykorzystują 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 zawierają one nowe treści.

Jeśli początkowa zawartość widoku z recyklingu jest wypełniona, usługa autouzupełniania zachowuje logiczne znaczenie widoków, używając 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 powiązanie nieprawidłowych danych użytkownika z identyfikatorem autouzupełniania.

Aby rozwiązać ten problem na urządzeniach z Androidem 9 (poziom interfejsu API 28) lub nowszym, jawnie zarządzaj identyfikatorem autouzupełniania widoków używanych przez RecyclerView za 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 z platformą autouzupełniania.

Autouzupełnianie powoduje awarie 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 powodować awarię aplikacji. Aby uniknąć potencjalnych problemów, oznacz wszystkie widoki, które nie są wypełniane automatycznie, za pomocą importantForAutofill=no. Możesz też oznaczyć całe działanie tagiem importantForAutofill=noExcludeDescendants.

Okna 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 oknie zostanie zmieniony po jego wyświetleniu, widok nie jest brany pod uwagę przy autouzupełnianiu. Te widoki nie są uwzględniane w obiekcie AssistStructure, który system Android wysyła do usługi autouzupełniania. W efekcie 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, ten fragment kodu pokazuje, jak sprawdzić, czy autouzupeł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 z autouzupełnianiem

Po zoptymalizowaniu aplikacji pod kątem usług autouzupełniania sprawdź, czy działa ona zgodnie z oczekiwaniami.

Do przetestowania 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

Zanim przetestujesz aplikację z autouzupełnianiem, 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 skorzystać z przykładowej usługi autouzupełniania, aby nie trzeba było rejestrować się w usługach innych firm.

Możesz użyć przykładowej platformy autouzupełniania na Androida w języku Java, aby przetestować aplikację z usługami autouzupełniania. 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 aplikacji. Na tej stronie znajdziesz odniesienia do przykładowej aplikacji android-AutofillFramework.

Po zainstalowaniu aplikacji włącz usługę autouzupełniania w ustawieniach systemowych emulatora, klikając Ustawienia > System > Języki i metody wprowadzania > Zaawansowane > Ułatwienia wprowadzania > 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żyć do wypełnienia aplikacji. Usługa musi też rozumieć, jakiego typu danych oczekuje się w widokach aplikacji. Jeśli na przykład aplikacja ma widok, który oczekuje nazwy użytkownika, usługa musi mieć zbiór danych zawierający nazwę użytkownika i mechanizm, który pozwoli jej dowiedzieć się, że widok oczekuje takich danych.

Poinformuj usługę, jakiego typu danych oczekujesz 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 deweloperze, który dostarcza te informacje. Aplikacja działa lepiej z usługami autouzupełniania, jeśli ustawisz atrybut android:autofillHints w widokach, które są istotne dla autouzupełniania.

Przeprowadzanie testu

Po przeanalizowaniu wymagań dotyczących danych możesz przeprowadzić test, który obejmuje zapisanie danych testowych w usłudze autouzupełniania i uruchomienie 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 oczekuje typu danych, którego 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, który zawiera potrzebny typ danych.
  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 systemowym pojawi 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 usługi, kliknij Zapisz.

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

Aktywowanie autouzupełniania w aplikacji

Aby aktywować autouzupełnianie w aplikacji:

  1. Otwórz aplikację i przejdź do aktywności, która zawiera widoki, które chcesz przetestować.
  2. Kliknij widok, który chcesz wypełnić.
  3. System wyświetli interfejs autouzupełniania, który zawiera zbiory danych, którymi można wypełnić widok, jak pokazano na rysunku 1.
  4. Kliknij zbiór danych, który zawiera dane, których chcesz użyć. Widok wyświetla dane, które były wcześniej przechowywane w usłudze.
Okno autouzupełniania z dostępnymi zbiorami 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 opcje rozwiązywania problemów:

  • Sprawdź, czy widoki w aplikacji używają prawidłowej wartości w atrybucie 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 wartość inną niż no w widoku, który ma zostać wypełniony, lub inną niż noExcludeDescendants w widoku lub jednym z jego elementów nadrzędnych.