Obsługa dekoracji systemowych

Poniżej przedstawiono aktualizacje wprowadzone w tych obszarach specyficznych dla wyświetlacza:

Dekoracje systemowe

W systemie Android 10 dodano obsługę konfigurowania wyświetlaczy dodatkowych tak, aby wyświetlały określone dekoracje systemu, takie jak tapeta, pasek nawigacyjny i program uruchamiający. Domyślnie na ekranie głównym wyświetlane są wszystkie dekoracje systemu, a na wyświetlaczach pomocniczych te, które są opcjonalnie włączone. Obsługa edytora metody wprowadzania (IME) może być ustawiona niezależnie od innych dekoracji systemu.

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

Realizacja

DisplayWindowSettings#setShouldShowSystemDecorsLocked() jest także udostępniana w WindowManager#setShouldShowSystemDecors() na potrzeby testowania. Wyzwolenie tej metody w celu włączenia dekorów systemowych nie powoduje dodania okien dekoracyjnych, 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 zostaje w pełni zastosowana dopiero po ponownym uruchomieniu urządzenia.

Sprawdzanie obsługi dekoracji systemowych w bazie kodu WindowManager zwykle odbywa się za pomocą DisplayContent#supportsSystemDecorations() natomiast sprawdzanie usług zewnętrznych (takich jak interfejs użytkownika systemu w celu sprawdzenia, czy powinien być wyświetlany pasek nawigacji) za pomocą WindowManager#shouldShowSystemDecors() . Aby zrozumieć, co jest kontrolowane przez to ustawienie, zapoznaj się z punktami wywołań tych metod.

Okna dekoracyjne interfejsu użytkownika systemu

W systemie Android 10 dodano obsługę okien wystroju systemu tylko dla paska nawigacyjnego, ponieważ pasek nawigacyjny jest niezbędny do nawigacji między czynnościami i aplikacjami. Domyślnie pasek nawigacyjny pokazuje afordancje Wstecz i Strona główna. Jest to uwzględnione tylko wtedy, gdy docelowy wyświetlacz obsługuje dekoracje systemowe (patrz DisplayWindowSettings ).

Pasek stanu jest bardziej skomplikowanym oknem systemowym, ponieważ zawiera również Cień powiadomień, Szybkie ustawienia i Ekran blokady. W systemie Android 10 pasek stanu nie jest obsługiwany na dodatkowych wyświetlaczach. Dlatego powiadomienia, ustawienia i pełna blokada klawiatury są dostępne tylko na głównym wyświetlaczu.

Okno systemowe Przegląd/Ostatnie nie jest obsługiwane na ekranach dodatkowych. W systemie Android 10 funkcja AOSP wyświetla tylko ostatnie na wyświetlaczu domyślnym i zawiera aktywności ze wszystkich wyświetlaczy. Po uruchomieniu z poziomu Niedawnych aktywność, która była na ekranie dodatkowym, domyślnie jest wyświetlana na pierwszym planie tego ekranu. Z tym podejściem wiążą się pewne znane problemy, takie jak brak natychmiastowej aktualizacji, gdy aplikacje pojawiają się na innych ekranach.

Realizacja

Aby zaimplementować dodatkowe funkcje interfejsu użytkownika systemu, producenci urządzeń powinni używać pojedynczego komponentu interfejsu użytkownika systemu, który nasłuchuje dodawania/usuwania wyświetlaczy i prezentuje odpowiednią treść.

Komponent interfejsu użytkownika systemu obsługujący wiele wyświetlaczy (MD) powinien obsługiwać następujące przypadki:

  • Inicjalizacja wielu wyświetlaczy przy uruchomieniu
  • Wyświetlacz dodany w czasie wykonywania
  • Wyświetlacz usunięty w czasie wykonywania

Gdy interfejs użytkownika systemu wykryje dodanie wyświetlacza przed menedżerem WindowManager, tworzy się sytuacja wyścigu. Można tego uniknąć, implementując niestandardowe wywołanie zwrotne z WindowManager do interfejsu użytkownika systemu, gdy dodawany jest wyświetlacz zamiast subskrybować zdarzenia DisplayManager .DisplayListener . Aby zapoznać się z implementacją referencyjną, zobacz CommandQueue.Callbacks#onDisplayReady dla obsługi paska nawigacji i WallpaperManagerInternal#onDisplayReady dla tapet.

Ponadto Android 10 udostępnia następujące aktualizacje:

  • 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 pojedynczej instancji i może być używany na każdym wyświetlaczu.
  • Zaktualizowano IWindowManager#hasNavigationBar() w celu uwzględnienia parametru displayId tylko dla interfejsu użytkownika systemu.

Wyrzutnia

W systemie Android 10 każdy wyświetlacz skonfigurowany do obsługi dekoracji systemowych ma domyślnie dedykowany stos główny dla działań programu uruchamiającego z typem WindowConfiguration#ACTIVITY_TYPE_HOME . Każdy wyświetlacz wykorzystuje oddzielną instancję działania programu uruchamiającego.

Rysunek 1. Przykładowy program uruchamiający na wielu wyświetlaczach dla platform/development/samples/MultiDisplay

Większość istniejących programów uruchamiających nie obsługuje wielu instancji i nie jest zoptymalizowana pod kątem dużych rozmiarów ekranów. Ponadto na wyświetlaczach dodatkowych/zewnętrznych często oczekuje się innego rodzaju wrażeń. Aby zapewnić dedykowaną aktywność dla ekranów dodatkowych, w systemie Android 10 wprowadzono kategorię SECONDARY_HOME w filtrach intencji. Wystąpienia tego działania są używane na wszystkich wyświetlaczach obsługujących dekoracje systemowe, po jednym 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 zapobiega wielokrotnym instancjom i oczekuje się, że dostosuje się do różnych rozmiarów ekranów. Trybem uruchamiania nie może być singleInstance ani singleTask .

Realizacja

W systemie Android 10 RootActivityContainer#startHomeOnDisplay() automatycznie wybiera żądany komponent i cel 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 może w razie potrzeby użyć ustawień domyślnych systemu (zobacz ActivityTaskManagerService#getSecondaryHomeIntent() ).

Ograniczenia bezpieczeństwa

Oprócz ograniczeń dotyczących działań na dodatkowych wyświetlaczach, aby uniknąć możliwości, że złośliwa aplikacja utworzy wirtualny wyświetlacz z włączonymi dekoracjami systemu i odczyta z powierzchni informacje wrażliwe dla użytkownika, program uruchamiający pojawia się tylko na wirtualnych wyświetlaczach należących do systemu. Program uruchamiający nie wyświetla treści na wirtualnych wyświetlaczach innych niż systemowe.

Tapety

W systemie Android 10 (i nowszych) tapety są obsługiwane na dodatkowych wyświetlaczach:

Rysunek 2. Animowana tapeta na wyświetlaczach wewnętrznych (powyżej) i zewnętrznych (poniżej)

Programiści mogą zadeklarować obsługę funkcji tapety, podając android:supportsMultipleDisplays="true" w definicji XML WallpaperInfo . Od twórców tapet oczekuje się również ładowania zasobów przy użyciu kontekstu wyświetlania w WallpaperService.Engine#getDisplayContext() .

Struktura tworzy jedną instancję WallpaperService.Engine na ekran, więc każdy silnik ma własną powierzchnię i kontekst wyświetlania. Programista musi upewnić się, że każdy silnik może rysować niezależnie, przy różnych szybkościach klatek, z poszanowaniem VSYNC.

Wybierz tapety dla poszczególnych ekranów

Android 10 nie zapewnia bezpośredniej obsługi platformy w zakresie wybierania tapet dla poszczególnych ekranów. Aby to osiągnąć, potrzebny jest stabilny identyfikator wyświetlacza, aby zachować ustawienia tapety na każdym wyświetlaczu. Display#getDisplayId() jest dynamiczna, więc nie ma gwarancji, że fizyczny wyświetlacz będzie miał ten sam identyfikator po ponownym uruchomieniu.

Jednak w systemie Android 10 dodano DisplayInfo.mAddress , który zawiera stabilne identyfikatory wyświetlaczy fizycznych i może zostać wykorzystany do pełnej implementacji w przyszłości. Niestety jest już za późno na wdrożenie logiki dla Androida 10. Sugerowane rozwiązanie:

  1. Użyj API WallpaperManager , aby ustawić tapety.
  2. WallpaperManager jest uzyskiwany z obiektu Context , a każdy obiekt Context zawiera informacje o odpowiednim wyświetlaniu ( Context#getDisplay()/getDisplayId() ). Dlatego można uzyskać displayId z instancji WallpaperManager bez dodawania nowych metod.
  3. Po stronie frameworka użyj displayId uzyskanego z obiektu Context i zamapuj go na statyczny identyfikator (taki jak port fizycznego wyświetlacza). Użyj statycznego 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żywa odpowiedniego kontekstu, to przy wywołaniu ustawienia tapety system może automatycznie zidentyfikować wyświetlacz.

Jeśli zachodzi potrzeba ustawienia tapety dla wyświetlacza innego niż bieżący, utwórz nowy obiekt Context dla wyświetlacza docelowego ( Context#createDisplayContext ) i uzyskaj instancję WallpaperManager z tego ekranu.

Ograniczenia bezpieczeństwa

System nie będzie wyświetlał tapet na wirtualnych wyświetlaczach, których nie posiada. Dzieje się tak ze względów 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 wrażliwe dla użytkownika (takie jak osobiste zdjęcie).

Realizacja

W systemie Android 10 interfejsy IWallpaperConnection#attachEngine() i IWallpaperService#attach() akceptują parametr displayId w celu tworzenia połączeń dla poszczególnych wyświetlaczy. WallpaperManagerService.DisplayConnector zawiera silnik tapety i połączenie dla każdego wyświetlacza. W programie WindowManager kontrolery tapet są tworzone dla każdego obiektu DisplayContent na etapie budowy, zamiast pojedynczego WallpaperController dla wszystkich wyświetlaczy.

Niektóre z publicznych implementacji metod WallpaperManager (takie jak WallpaperManager#getDesiredMinimumWidth() ) zostały zaktualizowane w celu obliczania i dostarczania informacji dla odpowiednich ekranów. Dodano WallpaperInfo#supportsMultipleDisplays() i odpowiedni atrybut zasobu, dzięki czemu twórcy aplikacji mogą raportować, które tapety są gotowe na wiele ekranów.

Jeśli usługa tapet wyświetlana na wyświetlaczu domyślnym nie obsługuje wielu wyświetlaczy, system wyświetli domyślną tapetę na wyświetlaczach dodatkowych.

Rysunek 3. Logika zastępowania tapety dla wyświetlaczy dodatkowych