Obsługa dekoracji systemu

Aktualizacje tych obszarów związanych z wyświetlaniem są podawane na tej stronie.

Dekoracje systemowe

Android 10 umożliwia skonfigurowanie dodatkowych wyświetlaczy tak, aby wyświetlały określone elementy dekoracyjne systemu, takie jak tapeta, pasek nawigacyjny i launcher. Domyślnie na wyświetlaczu głównym widać wszystkie elementy systemowe, a na wyświetlaczach dodatkowych – te, które zostały opcjonalnie włączone. Obsługę edytora IME możesz ustawić oddzielnie od innych elementów systemu.

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

Implementacja

DisplayWindowSettings#setShouldShowSystemDecorsLocked() jest też udostępniana w WindowManager#setShouldShowSystemDecors() na potrzeby testowania. Wywołanie tej metody w celu włączenia dekoracji systemowych nie powoduje dodania okien dekoracji, których wcześniej brakowało, ani usunięcia tych, które były wcześniej obecne. W większości przypadków zmiana obsługi dekoracji systemowych zaczyna w pełni obowiązywać dopiero po ponownym uruchomieniu urządzenia.

Sprawdzanie obsługi dekoracji systemowych w bazie kodu WindowManager zwykle odbywa się za pomocą DisplayContent#supportsSystemDecorations(), a sprawdzanie usług zewnętrznych (np. interfejsu systemu, aby sprawdzić, czy pasek nawigacyjny powinien być widoczny) odbywa się za pomocą WindowManager#shouldShowSystemDecors(). Aby dowiedzieć się, co kontroluje to ustawienie, zapoznaj się z punktami wywołania tych metod.

Okna dekoracyjne interfejsu systemu

Android 10 dodaje obsługę okna dekoracji systemowej tylko w przypadku paska nawigacyjnego, ponieważ jest on niezbędny do poruszania się między aktywnościami i aplikacjami. Domyślnie na pasku nawigacyjnym wyświetlają się przyciski Wstecz i Strona główna. Jest to uwzględniane tylko wtedy, gdy wyświetlacz docelowy obsługuje dekoracje systemowe (patrz DisplayWindowSettings).

Pasek stanu to bardziej złożone okno systemowe, ponieważ zawiera też obszar powiadomień, szybkie ustawienia i ekran blokady. W Androidzie 10 pasek stanu nie jest obsługiwany na dodatkowych wyświetlaczach. Dlatego powiadomienia, ustawienia i pełna ochrona klawiatury są dostępne tylko na wyświetlaczu głównym.

Okno systemowe Przegląd/Ostatnie nie jest obsługiwane na dodatkowych ekranach. W Androidzie 10 AOSP wyświetla sekcję Niedawno wyszukiwane hasła tylko na domyślnym ekranie i zawiera ona aktywności ze wszystkich ekranów. Gdy aktywność, która była wyświetlana na ekranie dodatkowym, zostanie uruchomiona z sekcji Ostatnie, domyślnie pojawi się na pierwszym planie na tym ekranie. To podejście ma pewne znane problemy, np. nie aktualizuje się od razu, gdy aplikacje pojawiają się na innych ekranach.

Implementacja

Aby wdrożyć dodatkowe funkcje interfejsu systemu, producenci urządzeń powinni używać jednego komponentu interfejsu systemu, który nasłuchuje dodawania i usuwania wyświetlaczy oraz prezentuje odpowiednie treści.

Komponent interfejsu systemu, który obsługuje wiele wyświetlaczy, powinien obsługiwać te przypadki:

  • Inicjowanie wielu wyświetlaczy podczas uruchamiania
  • Wyświetlacz dodany w czasie działania
  • Wyświetlacz usunięty w czasie działania

Gdy interfejs systemu wykryje dodanie wyświetlacza przed menedżerem okien, tworzy warunek wyścigu. Możesz tego uniknąć, wdrażając niestandardowe wywołanie zwrotne z WindowManager do interfejsu systemu, gdy dodawany jest wyświetlacz, zamiast subskrybować zdarzenia DisplayManager.DisplayListener. Przykładowe wdrożenie znajdziesz w CommandQueue.Callbacks#onDisplayAddSystemDecorations sekcji dotyczącej obsługi paska nawigacyjnegoWallpaperManagerInternal#onDisplayAddSystemDecorationsWallpaperManagerInternal#onDisplayAddSystemDecorations sekcji dotyczącej tapet.

Android 10 wprowadza też te zmiany:

  • Klasa NavigationBarController kontroluje wszystkie funkcje specyficzne dla pasków nawigacyjnych.
  • Aby wyświetlić dostosowany pasek nawigacyjny, zobacz CarStatusBar.
  • TYPE_NAVIGATION_BAR nie jest już ograniczony do jednej instancji i może być używany na każdym wyświetlaczu.
  • IWindowManager#hasNavigationBar() został zaktualizowany, aby zawierać parametr displayId tylko w przypadku interfejsu systemu.

Program uruchamiający

W Androidzie 10 każdy wyświetlacz skonfigurowany do obsługi dekoracji systemowych ma domyślnie dedykowany stos główny dla aktywności programu uruchamiającego typu WindowConfiguration#ACTIVITY_TYPE_HOME. Każdy wyświetlacz korzysta z osobnej instancji aktywności programu uruchamiającego:

Rysunek 1. Przykład programu uruchamiającego na wiele wyświetlaczy w przypadku platformy/development/samples/MultiDisplay.

Większość obecnych programów uruchamiających nie obsługuje wielu instancji i nie jest zoptymalizowana pod kątem dużych ekranów. Na wyświetlaczach dodatkowych/zewnętrznych często oczekuje się też innego rodzaju działania. Aby zapewnić dedykowaną aktywność na ekranach dodatkowych, w Androidzie 10 wprowadziliśmy kategorię SECONDARY_HOME w filtrach intencji. Instancje tej aktywności są używane na wszystkich wyświetlaczach obsługujących dekoracje systemowe – po jednej 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 uniemożliwia tworzenia wielu instancji i dostosowuje się do różnych rozmiarów ekranu. Tryb uruchamiania nie może mieć wartości singleInstance ani singleTask.

Implementacja

W Androidzie 10 RootActivityContainer#startHomeOnDisplay()automatycznie wybiera odpowiedni komponent i intencję w zależności od wyświetlacza, na którym uruchamiany jest 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żywać domyślnego programu systemowego (patrz ActivityTaskManagerService#getSecondaryHomeIntent()).

Ograniczenia zabezpieczeń

Oprócz ograniczeń dotyczących aktywności na wyświetlaczach dodatkowych, aby uniknąć możliwości utworzenia przez złośliwą aplikację wirtualnego wyświetlacza z włączonymi dekoracjami systemowymi i odczytywania z niego informacji poufnych użytkownika, program uruchamiający pojawia się tylko na wirtualnych wyświetlaczach należących do systemu. Launcher nie wyświetla treści na wirtualnych wyświetlaczach innych niż systemowe.

Tapety

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

Rysunek 2. Animowana tapeta na ekranie wewnętrznym (u góry) i zewnętrznym (u dołu).

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

Framework tworzy 1 WallpaperService.Engine instancję na wyświetlacz, więc każdy silnik ma własną powierzchnię i kontekst wyświetlania. Deweloper musi zadbać o to, aby każdy silnik mógł rysować niezależnie, z różną liczbą klatek na sekundę, z uwzględnieniem synchronizacji pionowej.

Wybieranie tapet na poszczególne ekrany

Android 10 nie obsługuje bezpośrednio wybierania tapet na poszczególne ekrany. W tym celu potrzebny jest stabilny identyfikator wyświetlacza, który będzie zachowywał ustawienia tapety dla każdego wyświetlacza. Display#getDisplayId() jest dynamiczny, więc nie ma gwarancji, że po ponownym uruchomieniu fizyczny wyświetlacz będzie miał ten sam identyfikator.

W Androidzie 10 dodano jednak DisplayInfo.mAddress, które zawiera stabilne identyfikatory wyświetlaczy fizycznych i może być w przyszłości w pełni wykorzystywane. Niestety jest już za późno na wdrożenie logiki w przypadku Androida 10. Sugerowane rozwiązanie:

  1. Aby ustawić tapety, użyj klasy WallpaperManager.

    WallpaperManager jest uzyskiwana z obiektu Context, a każdy obiekt Context zawiera informacje o odpowiednim wyświetlaczu (Context#getDisplay()/getDisplayId()). Dlatego możesz uzyskać displayId z instancji WallpaperManager bez dodawania nowych metod.

  2. Po stronie platformy użyj wartości displayId uzyskanej z obiektu Context i przypisz ją do statycznego identyfikatora (np. portu wyświetlacza fizycznego). Użyj statycznego identyfikatora, aby zachować wybraną tapetę.

To obejście wykorzystuje istniejące implementacje selektorów tapet. Jeśli została otwarta na konkretnym wyświetlaczu i używa odpowiedniego kontekstu, to gdy wywoła ustawienie tapety, system może automatycznie zidentyfikować wyświetlacz.

Jeśli chcesz ustawić tapetę na wyświetlaczu innym niż bieżący, utwórz nowy obiekt Context dla docelowego wyświetlacza (Context#createDisplayContext) i uzyskaj instancję WallpaperManager z tego wyświetlacza.

Ograniczenia zabezpieczeń

System nie będzie wyświetlać tapet na wirtualnych wyświetlaczach, których nie jest właścicielem. Jest to związane z zagrożeniem bezpieczeństwa, ponieważ złośliwa aplikacja może utworzyć wirtualny wyświetlacz z włączoną obsługą dekoracji systemowych i odczytać z powierzchni informacje poufne użytkownika (np. osobiste zdjęcie).

Implementacja

W Androidzie 10 interfejsy IWallpaperConnection#attachEngine()IWallpaperService#attach() akceptują parametr displayId, aby tworzyć połączenia dla poszczególnych wyświetlaczy. WallpaperManagerService.DisplayConnector obejmuje silnik tapety i połączenie dla każdego wyświetlacza. W WindowManager kontrolery tapet są tworzone dla każdego obiektu DisplayContent podczas tworzenia, a nie pojedynczego obiektu WallpaperController dla wszystkich wyświetlaczy.

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

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

Rysunek 3. Logika rezerwowa tapety na wyświetlaczach dodatkowych.