Wyświetl wsparcie

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

Zmień rozmiar działań i wyświetlaczy

Aby wskazać, że aplikacja może nie obsługiwać trybu wielu okien lub zmiany rozmiaru, w działaniach używany jest atrybut resizeableActivity=false . Typowe problemy napotykane przez aplikacje podczas zmiany rozmiaru działań obejmują:

  • Działanie może mieć inną konfigurację niż aplikacja lub inny komponent niewizualny. Częstym błędem jest odczytywanie wskaźników wyświetlania z kontekstu aplikacji. Zwrócone wartości nie zostaną dopasowane do metryk widocznego obszaru, w którym wyświetlana jest aktywność.
  • Działanie może nie obsłużyć zmiany rozmiaru i awarii, wyświetlić zniekształcony interfejs użytkownika lub utracić stan z powodu ponownego uruchomienia bez zapisania stanu instancji.
  • Aplikacja może próbować użyć bezwzględnych współrzędnych wejściowych (zamiast współrzędnych odnoszących się do położenia okna), co może przerwać wprowadzanie danych w wielu oknach.

W systemie Android 7 (i nowszych) można ustawić aplikację resizeableActivity=false , aby zawsze działała w trybie pełnoekranowym. W takim przypadku platforma zapobiega przedostawaniu się działań, których rozmiaru nie można zmienić, na podzielony ekran. Jeśli użytkownik spróbuje wywołać w programie uruchamiającym czynność, której rozmiaru nie można zmienić, będąc już w trybie podzielonego ekranu, platforma wyjdzie z trybu podzielonego ekranu i uruchomi czynność, której rozmiaru nie można zmienić w trybie pełnoekranowym.

Aplikacji, które w manifeście jawnie ustawiają ten atrybut na wartość false , nie można uruchamiać w trybie wielu okien, chyba że zastosowany zostanie tryb zgodności:

  • Ta sama konfiguracja jest stosowana do procesu, który zawiera wszystkie działania i komponenty niebędące działaniami.
  • Zastosowana konfiguracja spełnia wymagania CDD dla wyświetlaczy kompatybilnych z aplikacją.

W systemie Android 10 platforma nadal zapobiega przechodzeniu czynności, których rozmiaru nie można zmienić, w tryb podzielonego ekranu, ale można je tymczasowo skalować, jeśli aktywność ma zadeklarowaną stałą orientację lub współczynnik proporcji. Jeśli nie, aktywność zmienia się tak, aby wypełnić cały ekran, tak jak w Androidzie 9 i starszych wersjach.

Domyślna implementacja stosuje następującą politykę:

Gdy aktywność zostanie zadeklarowana jako niezgodna z wieloma oknami poprzez użycie atrybutu android:resizeableActivity i gdy aktywność ta spełni jeden z warunków opisanych poniżej, to gdy zastosowana konfiguracja ekranu musi ulec zmianie, czynność i proces zostaną zapisane z pierwotną konfiguracją a użytkownik ma możliwość ponownego uruchomienia procesu aplikacji w celu skorzystania ze zaktualizowanej konfiguracji ekranu.

  • Orientacja jest stała za pomocą aplikacji android:screenOrientation
  • Aplikacja ma domyślny maksymalny lub minimalny współczynnik proporcji, kierując na poziom API lub jawnie deklaruje współczynnik proporcji

Ten rysunek przedstawia aktywność, której nie można zmienić, z zadeklarowanym współczynnikiem proporcji. Podczas składania urządzenia okno jest zmniejszane w celu dopasowania do powierzchni przy zachowaniu proporcji przy zastosowaniu odpowiedniego letterboxingu. Dodatkowo, za każdym razem, gdy zmienia się obszar wyświetlania aktywności, dostępna jest dla użytkownika opcja ponownego uruchomienia działania.

Po rozłożeniu urządzenia konfiguracja, rozmiar i proporcje aktywności nie ulegają zmianie, ale wyświetla się opcja ponownego uruchomienia aktywności.

Jeśli resizeableActivity nie jest ustawione (lub ma wartość true ), aplikacja w pełni obsługuje zmianę rozmiaru.

Realizacja

Działanie, którego nie można zmienić, ze stałą orientacją lub współczynnikiem proporcji, nazywane jest w kodzie trybem zgodności rozmiaru (SCM). Warunek jest zdefiniowany w ActivityRecord#shouldUseSizeCompatMode() . Po uruchomieniu działania SCM konfiguracja związana z ekranem (taka jak rozmiar lub gęstość) jest ustalana w żądanej konfiguracji zastępowania, więc działanie nie jest już zależne od bieżącej konfiguracji wyświetlacza.

Jeśli działanie SCM nie może wypełnić całego ekranu, jest wyrównane do góry i wyśrodkowane w poziomie. Granice aktywności są obliczane przez AppWindowToken#calculateCompatBoundsTransformation() .

Gdy działanie SCM wykorzystuje inną konfigurację ekranu niż jego kontener (na przykład zmieniono rozmiar wyświetlacza lub działanie zostało przeniesione na inny ekran), ActivityRecord#inSizeCompatMode() ma wartość true, a SizeCompatModeActivityController (w interfejsie użytkownika systemu) odbiera wywołanie zwrotne, aby pokazać proces przycisk ponownego uruchomienia.

Rozmiary i proporcje wyświetlacza

Android 10 zapewnia obsługę nowych współczynników kształtu, od wysokich współczynników długich i cienkich ekranów po współczynniki 1:1. Aplikacje mogą definiować ApplicationInfo#maxAspectRatio i ApplicationInfo#minAspectRatio ekranu, który są w stanie obsłużyć.

współczynniki aplikacji w systemie Android 10

Rysunek 1. Przykładowe współczynniki aplikacji obsługiwane w systemie Android 10

Implementacje urządzeń mogą mieć dodatkowe wyświetlacze o rozmiarach i rozdzielczościach mniejszych niż wymagane w systemie Android 9 lub niższych (co najmniej 2,5 cala szerokości lub wysokości, co najmniej 320 DP dla smallestScreenWidth ), ale można wykonywać tylko działania, które wyrażają zgodę na obsługę tych małych wyświetlaczy tam umieszczone.

Aplikacje mogą wyrazić zgodę, deklarując minimalny obsługiwany rozmiar mniejszy niż oe równy docelowemu rozmiarowi wyświetlacza. Aby to zrobić, użyj atrybutów układu aktywności android:minHeight i android:minWidth w pliku AndroidManifest.

Wyświetl zasady

Android 10 oddziela i przenosi niektóre zasady wyświetlania z domyślnej implementacji WindowManagerPolicy w PhoneWindowManager do klas poszczególnych wyświetlaczy, takich jak:

  • Stan wyświetlania i rotacja
  • Niektóre klawisze i śledzenie zdarzeń ruchu
  • Interfejs systemu i okna dekoracyjne

W systemie Android 9 (i starszych) klasa PhoneWindowManager obsługiwała zasady wyświetlania, stan i ustawienia, rotację, śledzenie ramek okien dekoracji i nie tylko. Android 10 przenosi większość z nich do klasy DisplayPolicy , z wyjątkiem śledzenia rotacji, które zostało przeniesione do DisplayRotation .

Ustawienia okna wyświetlania

W systemie Android 10 konfigurowalne ustawienie okien dla poszczególnych wyświetlaczy zostało rozszerzone o:

  • Domyślny tryb okna wyświetlania
  • Wartości przeskanowania
  • Rotacja użytkowników i tryb rotacji
  • Wymuszony rozmiar, gęstość i tryb skalowania
  • Tryb usuwania treści (po usunięciu wyświetlacza)
  • Obsługa dekoracji systemowych i edytora IME

Klasa DisplayWindowSettings zawiera ustawienia tych opcji. Są one utrwalane na dysku w partycji /data w pliku display_settings.xml za każdym razem, gdy zmieniane jest ustawienie. Aby uzyskać szczegółowe informacje, zobacz DisplayWindowSettings.AtomicFileStorage i DisplayWindowSettings#writeSettings() . Producenci urządzeń mogą podać wartości domyślne w display_settings.xml dla konfiguracji swoich urządzeń. Ponieważ jednak plik jest przechowywany w /data , może być potrzebna dodatkowa logika, aby przywrócić plik, jeśli zostanie usunięty przez czyszczenie.

Domyślnie system Android 10 używa DisplayInfo#uniqueId jako identyfikatora wyświetlacza podczas utrwalania ustawień. uniqueId należy wypełnić dla wszystkich wyświetlaczy. Ponadto jest stabilny w przypadku wyświetlaczy fizycznych i sieciowych. Możliwe jest również użycie portu fizycznego wyświetlacza jako identyfikatora, który można ustawić w DisplayWindowSettings#mIdentifier . Przy każdym zapisie wszystkie ustawienia są zapisywane, więc można bezpiecznie zaktualizować klucz używany do wyświetlania wpisu w pamięci. Aby uzyskać szczegółowe informacje, zobacz sekcję Statyczne identyfikatory wyświetlane .

Ustawienia są zachowywane w katalogu /data ze względów historycznych. Pierwotnie służyły do ​​utrwalania ustawień ustawionych przez użytkownika, takich jak obrót wyświetlacza.

Statyczne identyfikatory wyświetlania

Android 9 (i starsze) nie zapewniał stabilnych identyfikatorów wyświetlaczy w frameworku. Po dodaniu wyświetlacza do systemu wygenerowano dla niego Display#mDisplayId lub DisplayInfo#displayId poprzez zwiększenie licznika statycznego. Jeśli system dodał i usunął ten sam wyświetlacz, otrzymano inny identyfikator.

Jeśli urządzenie miało wiele wyświetlaczy dostępnych od chwili rozruchu, wyświetlaczom można przypisać różne identyfikatory, w zależności od czasu. Chociaż system Android 9 (i wcześniejsze wersje) zawierał DisplayInfo#uniqueId , nie zawierał wystarczających informacji, aby rozróżnić wyświetlacze, ponieważ wyświetlacze fizyczne były identyfikowane jako local:0 lub local:1 w celu reprezentowania wyświetlacza wbudowanego i zewnętrznego.

Android 10 zmienia DisplayInfo#uniqueId , aby dodać stabilny identyfikator i rozróżnić wyświetlacze lokalne, sieciowe i wirtualne.

Typ wyświetlacza Format
Lokalny
local:<stable-id>
Sieć
network:<mac-address>
Wirtualny
virtual:<package-name-and-name>

Oprócz aktualizacji uniqueId , DisplayInfo.address zawiera DisplayAddress , identyfikator wyświetlania, który jest stabilny po ponownym uruchomieniu. W systemie Android 10 DisplayAddress obsługuje wyświetlacze fizyczne i sieciowe. DisplayAddress.Physical zawiera stabilny identyfikator wyświetlania (taki sam jak w uniqueId ) i można go utworzyć za pomocą DisplayAddress#fromPhysicalDisplayId() .

Android 10 zapewnia również wygodną metodę uzyskiwania informacji o porcie ( Physical#getPort() ). Metodę tę można zastosować w środowisku do statycznej identyfikacji wyświetlaczy. Na przykład jest używany w DisplayWindowSettings ). DisplayAddress.Network zawiera adres MAC i można go utworzyć za pomocą DisplayAddress#fromMacAddress() .

Te dodatki umożliwiają producentom urządzeń identyfikację wyświetlaczy w statycznych konfiguracjach z wieloma wyświetlaczami oraz konfigurowanie różnych ustawień i funkcji systemu przy użyciu statycznych identyfikatorów wyświetlaczy, takich jak porty dla wyświetlaczy fizycznych. Metody te są ukryte i można ich używać wyłącznie w obrębie system_server .

Biorąc pod uwagę identyfikator wyświetlacza HWC (który może być nieprzezroczysty i nie zawsze stabilny), metoda ta zwraca (specyficzny dla platformy) 8-bitowy numer portu, który identyfikuje fizyczne złącze wyjścia wyświetlacza, a także obiekt blob EDID wyświetlacza. SurfaceFlinger wyodrębnia informacje o producencie lub modelu z identyfikatora EDID w celu wygenerowania stabilnych 64-bitowych identyfikatorów wyświetlaczy udostępnionych platformie. Jeśli ta metoda nie jest obsługiwana lub występują błędy, SurfaceFlinger powraca do starszego trybu MD, w którym DisplayInfo#address ma wartość null, a DisplayInfo#uniqueId jest zakodowany na stałe, jak opisano powyżej.

Aby sprawdzić, czy ta funkcja jest obsługiwana, uruchom:

$ dumpsys SurfaceFlinger --display-id
# Example output.
Display 21691504607621632 (HWC display 0): port=0 pnpId=SHP displayName="LQ123P1JX32"
Display 9834494747159041 (HWC display 2): port=1 pnpId=HWP displayName="HP Z24i"
Display 1886279400700944 (HWC display 1): port=2 pnpId=AUS displayName="ASUS MB16AP"

Użyj więcej niż dwóch wyświetlaczy

W systemie Android 9 (i starszych) SurfaceFlinger i DisplayManagerService zakładały istnienie co najwyżej dwóch fizycznych wyświetlaczy z zakodowanymi na stałe identyfikatorami 0 i 1.

Począwszy od Androida 10, SurfaceFlinger może wykorzystywać interfejs API Hardware Composer (HWC) do generowania stabilnych identyfikatorów wyświetlaczy, co umożliwia zarządzanie dowolną liczbą fizycznych wyświetlaczy. Aby dowiedzieć się więcej, zobacz Statyczne identyfikatory wyświetlane .

Struktura może wyszukiwać token IBinder pod kątem fizycznego wyświetlacza za pomocą SurfaceControl#getPhysicalDisplayToken po uzyskaniu 64-bitowego identyfikatora wyświetlacza z SurfaceControl#getPhysicalDisplayIds lub ze zdarzenia wtyczki typu hotplug DisplayEventReceiver .

W systemie Android 10 (i starszych) główny wyświetlacz wewnętrzny to TYPE_INTERNAL , a wszystkie dodatkowe wyświetlacze są oznaczone jako TYPE_EXTERNAL niezależnie od typu połączenia. Dlatego dodatkowe wyświetlacze wewnętrzne są traktowane jako zewnętrzne. Aby obejść ten problem, w kodzie specyficznym dla urządzenia można przyjąć założenia dotyczące DisplayAddress.Physical#getPort jeśli znany jest HWC i przewidywalna logika alokacji portów.

To ograniczenie zostało usunięte w systemie Android 11 (i nowszych wersjach).

  • W systemie Android 11 pierwszym wyświetlaczem zgłaszanym podczas uruchamiania jest wyświetlacz główny. Rodzaj połączenia (wewnętrzne czy zewnętrzne) nie ma znaczenia. Prawdą jest jednak, że wyświetlacza głównego nie można odłączyć i wynika z tego, że w praktyce musi to być wyświetlacz wewnętrzny. Należy pamiętać, że niektóre telefony składane mają wiele wyświetlaczy wewnętrznych.
  • Dodatkowe wyświetlacze są poprawnie klasyfikowane jako Display.TYPE_INTERNAL lub Display.TYPE_EXTERNAL (wcześniej znane odpowiednio jako Display.TYPE_BUILT_IN i Display.TYPE_HDMI ) w zależności od typu połączenia.

Realizacja

W systemie Android 9 i starszych wyświetlacze są identyfikowane za pomocą 32-bitowych identyfikatorów, gdzie 0 to wyświetlacz wewnętrzny, 1 to wyświetlacz zewnętrzny, [2, INT32_MAX] to wirtualne wyświetlacze HWC, a -1 oznacza nieprawidłowy wyświetlacz lub ekran inny niż HWC wirtualny wyświetlacz.

Począwszy od Androida 10, wyświetlacze otrzymują stabilne i trwałe identyfikatory, co umożliwia SurfaceFlinger i DisplayManagerService śledzenie więcej niż dwóch wyświetlaczy i rozpoznawanie wcześniej oglądanych wyświetlaczy. Jeśli HWC obsługuje IComposerClient.getDisplayIdentificationData i dostarcza dane identyfikacyjne wyświetlacza, SurfaceFlinger analizuje strukturę EDID i przydziela stabilne 64-bitowe identyfikatory wyświetlaczy dla wyświetlaczy fizycznych i wirtualnych HWC. Identyfikatory są wyrażane przy użyciu typu opcji, gdzie wartość null oznacza nieprawidłowy wyświetlacz lub wirtualny wyświetlacz inny niż HWC. Bez wsparcia HWC SurfaceFlinger powraca do starszego zachowania z maksymalnie dwoma fizycznymi wyświetlaczami.

Fokus na każdy wyświetlacz

Aby obsługiwać kilka źródeł wejściowych jednocześnie przeznaczonych dla poszczególnych wyświetlaczy, system Android 10 można skonfigurować tak, aby obsługiwał wiele skupionych okien, maksymalnie jedno na wyświetlacz. Jest to przeznaczone tylko dla specjalnych typów urządzeń, gdy wielu użytkowników korzysta z tego samego urządzenia w tym samym czasie i używa różnych metod wprowadzania danych lub urządzeń, np. Android Automotive.

Zdecydowanie zaleca się, aby nie włączać tej funkcji w przypadku zwykłych urządzeń, w tym urządzeń z wieloma ekranami lub urządzeń używanych do obsługi komputera stacjonarnego. Wynika to przede wszystkim ze względów bezpieczeństwa, które mogą powodować, że użytkownicy będą się zastanawiać, które okno ma fokus na wprowadzanie danych.

Wyobraź sobie użytkownika, który wprowadza bezpieczne informacje w polu tekstowym, na przykład logując się do aplikacji bankowej lub wprowadzając tekst zawierający poufne informacje. Złośliwa aplikacja może utworzyć wirtualny ekran poza ekranem, za pomocą którego można wykonać czynność, również z polem do wprowadzania tekstu. Aktywność legalna i złośliwa jest skupiona i w obu przypadkach wyświetlany jest aktywny wskaźnik wprowadzania danych (migający kursor).

Ponieważ jednak dane wejściowe z klawiatury (sprzętu lub oprogramowania) są wprowadzane tylko do najwyższego działania (ostatnio uruchomionej aplikacji), tworząc ukryty wirtualny ekran, złośliwa aplikacja może przechwycić dane wprowadzone przez użytkownika, nawet jeśli korzysta z klawiatury programowej na głównym wyświetlaczu urządzenia.

Użyj com.android.internal.R.bool.config_perDisplayFocusEnabled , aby ustawić fokus na każdy wyświetlacz.

Zgodność

Problem: w systemie Android 9 i starszych wersjach w danym momencie aktywne jest maksymalnie jedno okno w systemie.

Rozwiązanie: W rzadkim przypadku, gdy dwa okna z tego samego procesu byłyby aktywne, system ustawia fokus tylko na oknie znajdującym się wyżej w kolejności Z. To ograniczenie zostało usunięte w przypadku aplikacji przeznaczonych dla systemu Android 10 i od tego momentu oczekuje się, że będą mogły obsługiwać wiele okien jednocześnie.

Realizacja

WindowManagerService#mPerDisplayFocusEnabled kontroluje dostępność tej funkcji. W ActivityManager zamiast globalnego śledzenia w zmiennej używana jest teraz ActivityDisplay#getFocusedStack() . ActivityDisplay#getFocusedStack() określa fokus na podstawie kolejności Z zamiast buforowania wartości. Dzieje się tak, że tylko jedno źródło, WindowManager, musi śledzić kolejność Z działań.

ActivityStackSupervisor#getTopDisplayFocusedStack() przyjmuje podobne podejście w przypadkach, gdy należy zidentyfikować najbardziej skupiony stos w systemie. Stosy przechodzą od góry do dołu w poszukiwaniu pierwszego odpowiedniego stosu.

InputDispatcher może teraz mieć wiele aktywnych okien (po jednym na wyświetlacz). Jeśli zdarzenie wejściowe jest specyficzne dla wyświetlacza, jest wysyłane do aktywnego okna na odpowiednim ekranie. W przeciwnym razie jest wysyłany do aktywnego okna na aktywnym ekranie, czyli do ekranu, z którym użytkownik ostatnio wchodził w interakcję.

Zobacz InputDispatcher::mFocusedWindowHandlesByDisplay i InputDispatcher::setFocusedDisplay() . Ukierunkowane aplikacje są również aktualizowane oddzielnie w usłudze InputManagerService za pomocą NativeInputManager::setFocusedApplication() .

W WindowManager aktywne okna są również śledzone oddzielnie. Zobacz DisplayContent#mCurrentFocus i DisplayContent#mFocusedApp oraz odpowiednie zastosowania. Powiązane metody śledzenia i aktualizacji fokusu zostały przeniesione z WindowManagerService do DisplayContent .