Obsługa dekoracji systemu

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

Dekoracje systemu

Android 10 umożliwia konfigurowanie dodatkowych wyświetlaczy, aby wyświetlały określone elementy systemu, takie jak tapeta, pasek nawigacji i wyskakujące okienko. Domyślnie ekran główny wyświetla wszystkie ozdobienia systemu, a ekrany dodatkowe wyświetlają te opcjonalnie włączone. Obsługę edytora metody wprowadzania (IME) można ustawić oddzielnie od innych dekoracji systemowych.

Użyj elementu DisplayWindowSettings#setShouldShowSystemDecorsLocked(), aby dodać obsługę dekoracji systemu na określonym wyświetlaczu, lub podaj wartość domyślną w elemencie /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 pliku WindowManager#setShouldShowSystemDecors(). Uruchomienie tej metody w celu włączenia dekoracji systemu nie powoduje dodania okien dekoracji, które były wcześniej nieobecne, ani ich usunięcia, jeśli były wcześniej obecne. 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 bazie kodu WindowManager zwykle odbywa się za pomocą funkcji DisplayContent#supportsSystemDecorations(), a sprawdzanie usług zewnętrznych (np. interfejsu System UI w celu sprawdzenia, czy ma być wyświetlany pasek nawigacyjny) – za pomocą funkcji WindowManager#shouldShowSystemDecors(). Aby dowiedzieć się, co jest kontrolowane przez to ustawienie, zapoznaj się z punktami wywołania tych metod.

Okna dekoracji 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 wyświetlają się opcje 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 dodatkowych ekranach. Dlatego powiadomienia, ustawienia i pełna klawiatura 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 otworzysz 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 System UI, producenci urządzeń powinni użyć pojedynczego komponentu System UI, który będzie nasłuchiwał dodawania i usuwania wyświetlaczy oraz wyświetlał odpowiednie treści.

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, spowoduje to warunek wyścigu. Można tego uniknąć, implementując niestandardową funkcję wywołania z WindowManager do SystemUI, gdy dodano wyświetlacz, zamiast subskrybować zdarzenia DisplayManager.DisplayListener. Przykładowe wdrożenie znajdziesz w CommandQueue.Callbacks#onDisplayReady (obsługa paska nawigacyjnego) i WallpaperManagerInternal#onDisplayReady (tapety).

Ponadto Android 10 zawiera te zmiany:

  • Klasa NavigationBarController kontroluje wszystkie funkcje związane z paskami nawigacyjnymi.
  • Aby wyświetlić dostosowany pasek nawigacyjny, zapoznaj się z artykułem 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.

Program uruchamiający

W Androidzie 10 każde urządzenie wyświetlające, które obsługuje dekoracje systemowe, ma domyślnie dedykowany stos aktywności z typem WindowConfiguration#ACTIVITY_TYPE_HOME. Każdy wyświetlacz używa osobnego wystąpienia aktywności Launchera.

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 też oczekuje się 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. Identyfikatory tej aktywności są używane na wszystkich wyświetlaczach, które obsługują systemowe dekoracje, po jednym 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 uruchamiania wielu instancji, i musi być dostosowywana do różnych rozmiarów ekranu. Tryb uruchamiania nie może być singleInstance ani singleTask.

Implementacja

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

Ograniczenia zabezpieczeń

Oprócz ograniczeń dotyczących działań na wyświetlaczach dodatkowych, aby uniknąć możliwości utworzenia przez złośliwą aplikację wyświetlacza wirtualnego z włączonymi ozdobami systemowymi i odczytania poufnych informacji użytkownika z powierzchni, Launcher wyświetla się tylko na wyświetlaczach wirtualnych należących do systemu. Nawigator nie wyświetla treści na wyświetlaczach wirtualnych, które nie są wyświetlaczami systemowymi.

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().

Framework tworzy jedną instancję WallpaperService.Engine na wyświetlacz, więc każdy silnik ma własny kontekst wyświetlania i powierzchni. 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.

W Androidzie 10 dodano jednak DisplayInfo.mAddress, który zawiera stabilne identyfikatory wyświetlaczy fizycznych i może być w przyszłości wykorzystany do pełnej implementacji. Niestety, jest już 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. Po stronie frameworku użyj wartości displayId uzyskanej z obiektu Context i przypisz ją do identyfikatora statycznego (np. portu 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ła otwarta na określonym ekranie i używa odpowiedniego kontekstu, to gdy wywoła ustawienie tapety, system może automatycznie zidentyfikować ekran.

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

Ograniczenia zabezpieczeń

System nie będzie wyświetlać tapet na wirtualnych wyświetlaczach, które nie należą do niego. Jest to związane z ochroną prywatności – aplikacja zawierająca złośliwe oprogramowanie może 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 zawiera mechanizm tapety i połączenie dla każdego wyświetlacza. W ramach usługi WindowManager kontrolery tapet są tworzone dla każdego obiektu DisplayContent podczas tworzenia, 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 na wiele ekranów.

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ępowania tapety na dodatkowych ekranach