Aktualizacje wprowadzone w tych obszarach związanych z wyświetlaniem znajdziesz na tej stronie.
Dekoracje systemowe
Android 10 dodaje obsługę konfigurowania dodatkowych wyświetlaczy do wyświetlania niektórych dekoracji systemowych, takich jak tapeta, pasek nawigacyjny i program uruchamiający. Domyślnie na wyświetlaczu głównym wyświetlane są wszystkie dekoracje systemowe, a na wyświetlaczach dodatkowych – te, które zostały opcjonalnie włączone. Obsługę edytora metod wprowadzania (IME) możesz ustawić oddzielnie od innych dekoracji systemowych.
Aby dodać obsługę dekoracji systemowych na określonym wyświetlaczu lub podać wartość domyślną w pliku /data/system/display_settings.xml, użyj DisplayWindowSettings#setShouldShowSystemDecorsLocked. Przykłady znajdziesz w artykule Ustawienia okna wyświetlania.
Implementacja
W celu testowania DisplayWindowSettings#setShouldShowSystemDecorsLocked jest też udostępniana w WindowManager#setShouldShowSystemDecors. Wywołanie tej metody z zamiarem włączenia dekoracji systemowych nie spowoduje dodania okien dekoracji, których wcześniej brakowało, ani usunięcia ich, jeśli były wcześniej obecne. W większości przypadków zmiana obsługi dekoracji systemowych zacznie 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 systemowego, aby sprawdzić, czy pasek nawigacyjny powinien być widoczny) – za pomocą WindowManager#shouldShowSystemDecors.
Aby dowiedzieć się, co jest kontrolowane przez to ustawienie, sprawdź punkty wywołania tych metod.
Okna dekoracji interfejsu systemowego
Android 10 dodaje obsługę okien dekoracji systemowych 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. Pasek nawigacyjny jest uwzględniany tylko wtedy, gdy wyświetlacz docelowy obsługuje dekoracje systemowe (patrz DisplayWindowSettings).
Pasek stanu jest bardziej złożonym oknem systemowym, ponieważ zawiera też obszar powiadomień, szybkie ustawienia i ekran blokady. W Androidzie 10 pasek stanu nie jest obsługiwany na wyświetlaczach dodatkowych. Dlatego powiadomienia, ustawienia i pełna blokada ekranu są dostępne tylko na wyświetlaczu głównym.
Okno systemowe Przegląd lub Ostatnie nie jest obsługiwane na ekranach dodatkowych. W Androidzie 10 AOSP wyświetla ostatnie aktywności tylko na wyświetlaczu domyślnym i zawiera aktywności ze wszystkich wyświetlaczy. Domyślnie, gdy aktywność, która była na wyświetlaczu dodatkowym, zostanie uruchomiona z poziomu Ostatnich, zostanie przeniesiona na pierwszy plan na tym wyświetlaczu. To podejście ma pewne znane problemy, np. nieaktualizowanie się od razu, gdy aplikacje pojawiają się na innych ekranach.
Implementacja
Aby zaimplementować dodatkowe funkcje interfejsu systemowego, producenci urządzeń powinni używać jednego komponentu interfejsu systemowego, który nasłuchuje dodawania lub usuwania wyświetlaczy i wyświetla odpowiednie treści.
Komponent interfejsu systemowego, który obsługuje wiele wyświetlaczy, powinien obsługiwać te przypadki:
- inicjowanie wielu wyświetlaczy podczas uruchamiania,
- dodawanie wyświetlacza w czasie działania,
- usuwanie wyświetlacza w czasie działania.
Gdy interfejs systemu wykryje dodanie wyświetlacza przed WindowManager, tworzy się sytuacja wyścigu. Możesz tego uniknąć, implementując niestandardowe wywołanie zwrotne z WindowManager do interfejsu systemowego, gdy dodawany jest wyświetlacz, zamiast subskrybować zdarzenia DisplayManager.DisplayListener. Implementację referencyjną znajdziesz w CommandQueue.Callbacks#onDisplayAddSystemDecorations w przypadku obsługi paska nawigacyjnego oraz w WallpaperManagerInternal#onDisplayAddSystemDecorations w przypadku tapet.
Android 10 zawiera też te aktualizacje:
- Klasa
NavigationBarControllerkontroluje wszystkie funkcje związane z paskami nawigacyjnymi. - Aby wyświetlić dostosowany pasek nawigacyjny, zobacz
CarStatusBar. TYPE_NAVIGATION_BARnie jest już ograniczony do jednej instancji i może być używany na każdym wyświetlaczu.IWindowManager#hasNavigationBarjest aktualizowany tak, aby zawierał parametrdisplayIdtylko w przypadku interfejsu systemowego.
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 z typem WindowConfiguration#ACTIVITY_TYPE_HOME. Każdy wyświetlacz używa osobnej instancji aktywności programu uruchamiającego:


Rysunek 1. Przykład programu uruchamiającego na wielu wyświetlaczach 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. Ponadto na wyświetlaczach dodatkowych/zewnętrznych często oczekuje się innego rodzaju wrażeń. 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, które obsługują dekoracje systemowe – po jednej na wyświetlacz.
<activity>
...
<intent-filter>
<category android:name="android.intent.category.SECONDARY_HOME" />
...
</intent-filter>
</activity>Aktywność musi mieć tryb uruchamiania, który nie uniemożliwia tworzenia wielu instancji, i powinna dostosowywać się do różnych rozmiarów ekranu. Tryb uruchamiania nie może być 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 bezpieczeństwa
Oprócz ograniczeń, które dotyczą aktywności na wyświetlaczach dodatkowych, aby uniknąć sytuacji, w której złośliwa aplikacja może utworzyć wyświetlacz wirtualny z włączonymi dekoracjami systemowymi i odczytać z powierzchni informacje wrażliwe dla użytkownika, program uruchamiający pojawia się tylko na wyświetlaczach wirtualnych należących do systemu. Program uruchamiający nie wyświetla treści na wyświetlaczach wirtualnych nienależących do systemu.
Tapety
W Androidzie 10 i nowszych tapety są obsługiwane na wyświetlaczach dodatkowych:


Rysunek 2. Animowana tapeta na wyświetlaczu 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świetlacza w WallpaperService.Engine#getDisplayContext.
Platforma tworzy 1 instancję WallpaperService.Engine na wyświetlacz, więc każdy silnik ma własną powierzchnię i kontekst wyświetlacza. Deweloper musi się upewnić, że każdy silnik może rysować niezależnie, z różnymi liczbami klatek na sekundę, z uwzględnieniem VSync.
Wybieranie tapet na poszczególne ekrany
Android 10 nie zapewnia bezpośredniej obsługi platformy w zakresie wybierania tapet na poszczególne ekrany. Aby to zrobić, potrzebny jest stabilny identyfikator wyświetlacza, który pozwoli zachować ustawienia tapety na każdym wyświetlaczu.
Display#getDisplayId jest dynamiczny, więc nie ma gwarancji, że wyświetlacz fizyczny będzie miał ten sam identyfikator po ponownym uruchomieniu.
W Androidzie 10 dodaliśmy jednak DisplayInfo.mAddress, który zawiera stabilne identyfikatory wyświetlaczy fizycznych i może być używany do pełnej implementacji w przyszłości. Niestety, na implementację logiki w Androidzie 10 jest już za późno. Sugerowane rozwiązanie:
- Do ustawiania tapet używaj klasy
WallpaperManager.WallpaperManagerjest uzyskiwany z obiektuContext, a każdy obiektContextzawiera informacje o odpowiednim wyświetlaczu (Context#getDisplay/getDisplayId). Dlatego możesz uzyskaćdisplayIdz instancjiWallpaperManagerbez dodawania nowych metod. - Po stronie platformy użyj
displayIduzyskanego z obiektuContexti przypisz go 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 selektor został otwarty na określonym wyświetlaczu i używa odpowiedniego kontekstu, to gdy wywoła ustawienie tapety, system może automatycznie zidentyfikować wyświetlacz.
Jeśli trzeba ustawić tapetę na wyświetlaczu innym niż bieżący
wyświetlacz, utwórz nowy obiekt Context dla wyświetlacza docelowego
(Context#createDisplayContext) i uzyskaj
instancję WallpaperManager z tego wyświetlacza.
Ograniczenia bezpieczeństwa
System nie będzie wyświetlać tapet na wyświetlaczach wirtualnych, które nie należą do niego. Wynika to z obawy o bezpieczeństwo, że złośliwa aplikacja może utworzyć wyświetlacz wirtualny z włączoną obsługą dekoracji systemowych i odczytać z powierzchni informacje wrażliwe dla użytkownika (np. zdjęcie osobiste).
Implementacja
W Androidzie 10 interfejsy IWallpaperConnection#attachEngine i IWallpaperService#attach akceptują parametr displayId, aby tworzyć połączenia na wyświetlacz.
WallpaperManagerService.DisplayConnector obejmuje silnik tapety i połączenie na wyświetlacz. W WindowManager kontrolery tapet są tworzone dla każdego obiektu DisplayContent podczas konstrukcji, a nie jako pojedynczy WallpaperController dla wszystkich wyświetlaczy.
Niektóre implementacje publicznej metody WallpaperManager (np.
WallpaperManager#getDesiredMinimumWidth) zostały zaktualizowane, aby obliczać
i udostępniać informacje o odpowiednich wyświetlaczach.
Dodaliśmy WallpaperInfo#supportsMultipleDisplays i odpowiedni atrybut zasobu, aby deweloperzy aplikacji mogli zgłaszać, które tapety są gotowe do użycia na wielu ekranach.
Jeśli usługa tapety wyświetlana na wyświetlaczu domyślnym nie obsługuje wielu wyświetlaczy, system wyświetla domyślną tapetę na wyświetlaczach dodatkowych:

Rysunek 3. Logika rezerwowa tapety na wyświetlaczach dodatkowych.
Włączanie obsługi animowanych tapet
W Androidzie 10 i nowszych (API 29) deweloperzy mogą używać atrybutu android:supportsMultipleDisplays
aby wskazać, czy ich tapeta może obejmować różne wyświetlacze. W trybie okien na pulpicie, gdzie wielozadaniowość jest intensywna, renderowanie animowanych tapet na wyświetlaczach zewnętrznych może znacząco wpłynąć na obciążenie GPU i pamięci.
Aby zachować zasoby systemowe, system domyślnie nie renderuje animowanych tapet na podłączonych wyświetlaczach. Gdy animowana tapeta jest ograniczona przez konfigurację systemu lub manifest aplikacji, system renderuje statyczną tapetę rezerwową.
Producenci OEM mogą dostosować to działanie, włączając obsługę animowanych tapet w przypadku sprzętu z wyższej półki lub dostosowując statyczną tapetę rezerwową, aby uzyskać wygląd zgodny z marką.
Jeśli Twój sprzęt może renderować wiele instancji animowanych tapet, zastąp tę konfigurację:
| Ścieżka zasobu | frameworks/base/core/res/res/values/config.xml |
|---|---|
| Nazwa konfiguracji | config_isLiveWallpaperSupportedInDesktopExperience |
Dostosowywanie tapety rezerwowej
Jeśli animowane tapety są wyłączone lub nie są obsługiwane przez dostawcę, system używa komponentu domyślnego. Możesz wskazać własnego dostawcę statycznych tapet:
| Ścieżka zasobu | frameworks/base/core/res/res/values/config.xml |
|---|---|
| Nazwa konfiguracji | fallback_wallpaper_component |
Implementowanie obsługi tapet
Aby zastosować te zmiany, użyj nakładki zasobów w czasie kompilacji w folderze specyficznym dla urządzenia, który
zwykle znajduje się w device/<vendor>/<product>/overlay/frameworks/base/core/res/res/values/.