Obsługa dekoracji systemu

Poniżej przedstawiamy zmiany wprowadzone w tych obszarach dotyczących reklam displayowych:

Dekoracje systemu

Android 10 obsługuje konfigurowanie ekranów dodatkowych, aby wyświetlały określone ozdoby systemowe, takie jak tapeta, pasek nawigacyjny czy program uruchamiający. Domyślnie wyświetlacz główny pokazuje wszystkie elementy systemu, a wyświetlacze dodatkowe wyświetlają elementy opcjonalnie włączone. Obsługę edytora metody wprowadzania (IME) można ustawić oddzielnie od innych dekoracji systemowych.

Użyj DisplayWindowSettings#setShouldShowSystemDecorsLocked(), aby dodać obsługę dekoracji systemu na określonym wyświetlaczu, lub podaj wartość domyślną w /data/system/display_settings.xml. Przykłady znajdziesz w sekcji Ustawienia okna wyświetlania.

Implementacja

Aby umożliwić testowanie, funkcja DisplayWindowSettings#setShouldShowSystemDecorsLocked() jest też dostępna w ramach WindowManager#setShouldShowSystemDecors(). Uruchomienie tej metody w celu włączenia dekoracji systemu nie powoduje dodania okien dekoracji, które były wcześniej niedostępne, ani ich usunięcia, jeśli były wcześniej dostępne. W większości przypadków zmiana obsługi dekoracji systemu zacznie obowiązywać dopiero po ponownym uruchomieniu urządzenia.

Sprawdzanie obsługi dekoracji systemu w podstawie kodu WindowManager zwykle odbywa się za pomocą funkcji DisplayContent#supportsSystemDecorations(), podczas gdy sprawdzanie usług zewnętrznych (takich jak interfejs System UI, aby sprawdzić, czy ma być wyświetlany pasek nawigacyjny) odbywa się za pomocą funkcji WindowManager#shouldShowSystemDecors(). Aby dowiedzieć się, co jest kontrolowane przez to ustawienie, zapoznaj się z punktami wywołania tych metod.

Okna dekoracyjne interfejsu systemu

Android 10 obsługuje okno dekoracji systemu tylko na pasku nawigacyjnym, ponieważ jest on niezbędny do poruszania się między aktywnościami i aplikacjami. Domyślnie na pasku nawigacyjnym widać afordancje Wstecz i Strona główna. Jest ona uwzględniana tylko wtedy, gdy wyświetlacz docelowy obsługuje ozdoby systemowe (patrz DisplayWindowSettings).

Pasek stanu to bardziej skomplikowane okno systemu, ponieważ zawiera też panel powiadomień, szybkie ustawienia i ekran blokady. W Androidzie 10 pasek stanu nie jest obsługiwany na ekranach dodatkowych. Dlatego powiadomienia, ustawienia i pełna blokada klawiszy są dostępne tylko na ekranie głównym.

Okno systemowe Przegląd/Ostatnie nie jest obsługiwane na ekranach dodatkowych. W Androidzie 10 AOSP wyświetla tylko ostatnio używane aplikacje na ekranie domyślnym i zawiera aktywności ze wszystkich ekranów. Gdy uruchomisz ostatnio używaną aplikację, aktywność, która była na dodatkowym ekranie, zostanie domyślnie wyświetlona na tym ekranie. Takie podejście ma pewne znane problemy, takie jak brak aktualizacji, gdy aplikacje pojawiają się na innych ekranach.

Implementacja

Aby wdrożyć dodatkowe funkcje interfejsu systemu, producenci urządzeń powinni użyć jednego komponentu interfejsu systemu, który nasłuchuje po dodaniu lub usunięciu wyświetlaczy i prezentuje odpowiednią treść.

Komponent interfejsu systemowego, który obsługuje wyświetlanie na wielu ekranach (MD), powinien obsługiwać te przypadki:

  • Inicjowanie wielu wyświetlaczy podczas uruchamiania
  • Wyświetlanie dodane w czasie wykonywania
  • Wyświetlanie usunięte w czasie wykonywania

Gdy interfejs systemowy wykryje dodanie wyświetlacza przed WindowManagerem, tworzy warunek wyścigu. Aby tego uniknąć, zastosuj niestandardowe wywołanie zwrotne z WindowManager do interfejsu systemu po dodaniu wyświetlania zamiast subskrybowania zdarzeń DisplayManager.DisplayListener. Implementację referencyjną znajdziesz w CommandQueue.Callbacks#onDisplayReady (obsługa paska nawigacyjnego) i WallpaperManagerInternal#onDisplayReady (tapety).

Ponadto Android 10 wprowadza te zmiany:

  • Klasa NavigationBarController kontroluje wszystkie funkcje związane z paskami nawigacyjnymi.
  • Aby wyświetlić dostosowany pasek nawigacyjny, zobacz CarStatusBar.
  • TYPE_NAVIGATION_BAR nie jest już ograniczony do jednego wystąpienia i może być używany na każdym wyświetlaczu.
  • Parametr IWindowManager#hasNavigationBar() został zaktualizowany, aby uwzględniał parametr displayId tylko w przypadku interfejsu System.

Wyrzutnia

W Androidzie 10 każdy wyświetlacz skonfigurowany do obsługi dekoracji systemu ma domyślnie dedykowany stos aplikacji dla działań z launcherem o typie WindowConfiguration#ACTIVITY_TYPE_HOME. Każdy wyświetlacz używa osobnego wystąpienia aktywności programu uruchamiającego.

Rysunek 1. Przykład uruchamiania aplikacji na wielu wyświetlaczach w przypadku platform/development/samples/MultiDisplay

Większość dotychczasowych programów uruchamiania nie obsługuje wielu instancji i nie jest zoptymalizowana pod kątem dużych rozmiarów ekranu. Często oczekuje się też innego rodzaju wyświetlania na wyświetlaczach dodatkowych lub zewnętrznych. Aby zapewnić dedykowaną aktywność dla ekranów dodatkowych, Android 10 wprowadza kategorię SECONDARY_HOME w filtrach intencji. Instancje tej aktywności są używane na wszystkich wyświetlaczach obsługujących dekoracje systemowe – po 1 na wyświetlacz.

<activity>
    ...
    <intent-filter>
        <category android:name="android.intent.category.SECONDARY_HOME" />
        ...
    </intent-filter>
</activity>

Działanie musi mieć tryb uruchamiania, który nie blokuje wielu instancji i musi dostosowywać się do różnych rozmiarów ekranu. Nie może to być singleInstance ani singleTask.

Implementacja

W Androidzie 10 RootActivityContainer#startHomeOnDisplay() automatycznie wybiera odpowiedni komponent i intencję w zależności od ekranu, na którym uruchamia się ekran główny. RootActivityContainer#resolveSecondaryHomeActivity() zawiera logikę wyszukiwania komponentu aktywności programu uruchamiającego w zależności od aktualnie wybranego programu uruchamiającego i w razie potrzeby może użyć domyślnego systemu (patrz: ActivityTaskManagerService#getSecondaryHomeIntent()).

Ograniczenia zabezpieczeń

Oprócz ograniczeń dotyczących działań na ekranach dodatkowych, aby uniknąć utworzenia przez złośliwej aplikacji wyświetlacza wirtualnego z włączonymi dekoracjami systemu i odczytywania informacji poufnych dla użytkowników z poziomu, program uruchamiający wyświetla się tylko na wyświetlaczach wirtualnych należących do systemu. Menu z aplikacjami nie wyświetla zawartości na ekranach wirtualnych innych niż systemowe.

Tapety

W Androidzie 10 (i nowszych) tapety są obsługiwane na dodatkowych ekranach:

Rysunek 2. Animowana tapeta na wewnętrznym (na górze) i zewnętrznym (na dole) ekranie

Deweloperzy mogą zadeklarować obsługę funkcji tapety, podając element android:supportsMultipleDisplays="true" w definicji XML WallpaperInfo. Deweloperzy tapet powinni też wczytywać zasoby za pomocą kontekstu wyświetlania w aplikacji WallpaperService.Engine#getDisplayContext().

Platforma tworzy 1 instancję WallpaperService.Engine na wyświetlacz, więc każdy silnik ma własny kontekst powierzchni i wyświetlania. Deweloperzy muszą zadbać o to, aby każdy silnik mógł rysować niezależnie, z różnymi częstotliwościami klatek, z uwzględnieniem VSYNC.

Wybieranie tapet dla poszczególnych ekranów

Android 10 nie obsługuje bezpośrednio platformy w zakresie wyboru tapet na poszczególne ekrany. Aby to osiągnąć, potrzebny jest stabilny identyfikator wyświetlacza, który pozwoli zachować ustawienia tapety na poszczególnych wyświetlaczach. Display#getDisplayId() jest dynamiczny, więc nie ma gwarancji, że fizyczny wyświetlacz będzie miał ten sam identyfikator po ponownym uruchomieniu.

Jednak w Androidzie 10 dodano element DisplayInfo.mAddress, który zawiera stabilne identyfikatory ekranów fizycznych i można go w przyszłości użyć w ramach pełnej implementacji. Niestety, za późno na zaimplementowanie logiki dla Androida 10. Proponowane rozwiązanie:

  1. Aby ustawić tapety, użyj interfejsu API WallpaperManager.
  2. WallpaperManager jest uzyskiwany z obiektu Context, a każdy obiekt Context zawiera informacje o odpowiednim wyświetlaczu (Context#getDisplay()/getDisplayId()). Dlatego możesz uzyskać displayId z wystąpienia WallpaperManager bez dodawania nowych metod.
  3. W ramach platformy użyj parametru displayId uzyskanego z obiektu Context i zmapuj go na identyfikator statyczny (np. port fizycznego wyświetlacza). Użyj stałego identyfikatora, aby zachować wybraną tapetę.

To obejście wykorzystuje istniejące implementacje selektorów tapet. Jeśli został otwarty na konkretnym wyświetlaczu i użył odpowiedniego kontekstu, to po wywołaniu ustawienia tapety system automatycznie go rozpozna.

Jeśli musisz ustawić tapetę dla wyświetlacza innego niż bieżący, utwórz nowy obiekt Context dla wyświetlacza docelowego (Context#createDisplayContext) i pobierz z niego instancję WallpaperManager.

Ograniczenia zabezpieczeń

System nie wyświetla tapet na wyświetlaczach wirtualnych, które nie należą do niego. Jest to związane z ochroną prywatności – aplikacja zawierająca złośliwe oprogramowanie mogłaby utworzyć wirtualną powierzchnię z włączonym systemem dekoracji i odczytać poufne informacje użytkownika (np. zdjęcie osobiste).

Implementacja

W Androidzie 10 interfejsy IWallpaperConnection#attachEngine()IWallpaperService#attach() obsługują parametr displayId, który umożliwia tworzenie połączeń na poszczególne wyświetlacze. WallpaperManagerService.DisplayConnector otacza silnik tapety i połączenie dla poszczególnych wyświetlaczy. W usłudze WindowManager kontrolery tapet są tworzone dla każdego obiektu DisplayContent w trakcie budowy, a nie dla pojedynczego obiektu WallpaperController dla wszystkich wyświetlaczy.

Niektóre publiczne implementacje metody WallpaperManager (np. WallpaperManager#getDesiredMinimumWidth()) zostały zaktualizowane, aby obliczać i przekazywać informacje o odpowiednich wyświetlaczach. Dodano atrybut WallpaperInfo#supportsMultipleDisplays() i odpowiadający mu atrybut zasobu, aby deweloperzy aplikacji mogli zgłaszać, które tapety są gotowe do wyświetlania na wielu ekranach.

Jeśli usługa tapety wyświetlana na ekranie domyślnym nie obsługuje wielu ekranów, system wyświetla domyślną tapetę na ekranach dodatkowych.

Rysunek 3. Zasada zastępczej tapety dla dodatkowych ekranów