Poniżej przedstawiamy zmiany wprowadzone w tych obszarach dotyczących reklam displayowych:
- Zmiana rozmiaru aktywności i wyświetlaczy
- Rozmiary i formaty obrazu
- Zasady dotyczące wyświetlania
- Ustawienia okna wyświetlania
- Statyczne identyfikatory wyświetlania
- Korzystanie z więcej niż 2 wyświetlaczy
- Ustawienie ostrości na wyświetlaczu
Zmienianie rozmiaru aktywności i wyświetlaczy
Aby wskazać, że aplikacja może nie obsługiwać trybu wielookiennego lub zmiany rozmiaru, w atrybutach używaj atrybutu resizeableActivity=false
. Typowe problemy występujące w aplikacjach podczas zmiany rozmiaru aktywności:
- Aktywność może mieć inną konfigurację niż aplikacja lub inny komponent niewizualny. Typowym błędem jest odczytywanie danych wyświetlania z kontekstu aplikacji. Zwracane wartości nie będą dostosowywane do danych dotyczących widocznego obszaru, w którym wyświetlana jest aktywność.
- Aktywność może nie obsługiwać zmiany rozmiaru i spowodować awarię, wyświetlać zniekształcony interfejs użytkownika lub utracić stan z powodu ponownego uruchomienia bez zapisywania stanu instancji.
- Aplikacja może próbować używać bezwzględnych współrzędnych wejścia (zamiast tych względnych do pozycji okna), co może spowodować przerwanie wprowadzania danych w trybie wielookiennym.
W Androidzie 7 (i nowszych) aplikację można skonfigurować tak, aby zawsze działała w trybie pełnoekranowym.resizeableActivity=false
W takim przypadku platforma uniemożliwia otwieranie działań, których rozmiarów nie można zmieniać, na podzielonym ekranie. Jeśli użytkownik spróbuje wywołać działanie, którego nie można skalować, z wyszukiwarki, gdy jest już w trybie podzielonego ekranu, platforma wyłączy tryb podzielonego ekranu i uruchomi działanie, którego nie można skalować, w trybie pełnoekranowym.
Aplikacje, które w pliku manifestu mają ten atrybut ustawiony na wartość false
, nie mogą być uruchamiane w trybie wielu okien, chyba że zastosowano tryb zgodności:
- Do procesu stosowana jest ta sama konfiguracja, która zawiera wszystkie komponenty działań i komponenty niezwiązane z działaniami.
- Zastosowane ustawienia spełniają wymagania CDD dotyczące wyświetlaczy zgodnych z aplikacją.
W Androidzie 10 platforma nadal uniemożliwia przełączanie się na tryb podzielonego ekranu w przypadku aktywności, których rozmiarów nie można zmieniać, ale można je tymczasowo skalować, jeśli deklarowana jest w nich stała orientacja lub proporcje. W przeciwnym razie rozmiar aktywności zostanie dostosowany, aby wypełnić cały ekran, tak jak w Androidzie 9 i starszych.
Domyślna implementacja stosuje te zasady:
Jeśli aktywność zadeklarowana jako niezgodna z wielozadaniowością za pomocą atrybutu android:resizeableActivity
spełnia jedno z podanych niżej warunków, a w przypadku zmiany konfiguracji ekranu, aktywność i proces są zapisywane z użyciem pierwotnej konfiguracji, a użytkownik otrzymuje możliwość ponownego uruchomienia procesu aplikacji w celu użycia zaktualizowanej konfiguracji ekranu.
- Czy jest to orientacja stała w aplikacji
android:screenOrientation
- Aplikacja ma domyślny maksymalny lub minimalny format obrazu, ponieważ jest kierowana na poziom API lub deklaruje format obrazu w sposób jawny
Ilustracja przedstawiająca aktywność o niezmiennym rozmiarze z deklarowanym współczynnikiem proporcji. Gdy urządzenie jest złożone, okno jest zmniejszane, aby pasowało do obszaru, przy zachowaniu współczynnika proporcji za pomocą odpowiedniego letterboxa. Dodatkowo użytkownikowi wyświetla się opcja ponownego uruchomienia aktywności za każdym razem, gdy zmienia się obszar wyświetlania aktywności.
Po rozłożeniu urządzenia konfiguracja, rozmiar i format aktywności nie zmieniają się, ale wyświetla się opcja ponownego uruchomienia aktywności.
Jeśli zasada resizeableActivity
nie jest skonfigurowana (lub ma wartość true
), aplikacja w pełni obsługuje zmianę rozmiaru.
Implementacja
W kodzie aktywność, której nie można skalować, a która ma stałą orientację lub proporcje, jest nazywana trybem zgodności z rozmiarem (SCM). Warunek jest zdefiniowany w ActivityRecord#shouldUseSizeCompatMode()
. Gdy uruchamiasz aktywność SCM, konfiguracja związana z ekranem (np. rozmiar lub gęstość) jest ustalana w ramach żądanej konfiguracji zastąpienia, dzięki czemu aktywność nie zależy już od bieżącej konfiguracji wyświetlania.
Jeśli aktywność SCM nie wypełnia całego ekranu, jest wyrównana u góry i wyśrodkowana poziomo. Granice aktywności są obliczane przez funkcję AppWindowToken#calculateCompatBoundsTransformation()
.
Gdy aktywność SCM używa innej konfiguracji ekranu niż jej kontener (np. rozmiar wyświetlacza jest zmieniony lub aktywność została przeniesiona na inny wyświetlacz), ActivityRecord#inSizeCompatMode()
ma wartość prawda, a SizeCompatModeActivityController
(w interfejsie System UI) otrzymuje wywołanie zwrotne, aby wyświetlić przycisk ponownego uruchamiania procesu.
Rozmiary i formaty obrazu
Android 10 obsługuje nowe formaty obrazu, od formatów o wysokich proporcjach na długich i wąskich ekranach do formatu 1:1. Aplikacje mogą definiować ApplicationInfo#maxAspectRatio
i ApplicationInfo#minAspectRatio
ekranu, które są w stanie obsłużyć.
Rysunek 1. Przykładowe współczynniki proporcji aplikacji obsługiwane w Androidzie 10
Implementacje urządzeń mogą mieć wyświetlacze dodatkowe o rozmiarach i rozdzielczościach mniejszych niż wymagane przez Androida 9 lub mniejsze (szerokość lub wysokość co najmniej 2,5 cala, w przypadku smallestScreenWidth
– co najmniej 320 DP), ale można na nich umieszczać tylko aktywności, które obsługują te małe wyświetlacze.
Aplikacje mogą się do tego programu zakwalifikować, deklarując obsługiwany rozmiar mniejszy niż docelowy rozmiar wyświetlania lub równy temu rozmiarowi. Aby to zrobić, użyj w pliku AndroidManifest atrybutów układu aktywności android:minHeight
i android:minWidth
.
Zasady dotyczące reklam displayowych
Android 10 oddziela niektóre zasady dotyczące wyświetlania i przenosi je z domyślnej implementacji WindowManagerPolicy
w PhoneWindowManager
do klas wyświetlania, np.:
- Stan i obrót wyświetlacza
- Niektóre klawisze i śledzenie zdarzeń związanych z ruchu
- interfejs systemu i okna dekoracyjne;
W Androidzie 9 (i starszych) klasa PhoneWindowManager
obsługiwała zasady wyświetlania, stan i ustawienia, obracanie, śledzenie ramki okna dekoracji i inne funkcje. W Androidzie 10 większość tych funkcji została przeniesiona do klasy DisplayPolicy
, z wyjątkiem śledzenia orientacji, które zostało przeniesione do klasy DisplayRotation
.
Ustawienia okna
W Androidzie 10 rozszerzono konfigurowalne ustawienie okna na wyświetlaczu, aby obejmowało:
- Domyślny tryb wyświetlania okna
- Wartości wyostrzania
- Rotacja użytkowników i tryb rotacji
- Wymuszony rozmiar, gęstość i tryb skalowania
- Tryb usuwania treści (gdy wyświetlanie jest wyłączone)
- obsługa dekoracji systemu i systemu IME;
Klasa DisplayWindowSettings
zawiera ustawienia tych opcji. Są one zapisywane na dysku w partycji /data
w display_settings.xml
za każdym razem, gdy ustawienie zostanie zmienione. Więcej informacji znajdziesz w artykułach DisplayWindowSettings.AtomicFileStorage
i DisplayWindowSettings#writeSettings()
. Producenci urządzeń mogą podać wartości domyślne w display_settings.xml
dla konfiguracji urządzenia. Plik jest jednak przechowywany w /data
, więc do jego przywrócenia może być potrzebna dodatkowa logika, jeśli został on usunięty przez wyczyszczenie.
Domyślnie Android 10 używa identyfikatora DisplayInfo#uniqueId
jako identyfikatora wyświetlacza podczas zapisywania ustawień. Wartość uniqueId
powinna być wypełniona we wszystkich wyświetlaczach. Ponadto jest stabilny w przypadku wyświetlaczy fizycznych i sieciowych. Jako identyfikator można też użyć portu fizycznego wyświetlacza, który można skonfigurować w DisplayWindowSettings#mIdentifier
. Podczas każdej operacji zapisu wszystkie ustawienia są zapisywane, więc można bezpiecznie aktualizować klucz używany do wyświetlania wpisu w pamięci. Szczegółowe informacje znajdziesz w artykule Identyfikatory statycznych wyświetlaczy.
Ustawienia są przechowywane w katalogu /data
ze względów historycznych. Pierwotnie służyły do zapisywania ustawień użytkownika, takich jak orientacja wyświetlacza.
Statyczne identyfikatory wyświetlania
Android 9 (i starsze) nie zapewniał stabilnych identyfikatorów wyświetlaczy w ramach frameworku. Gdy wyświetlacz został dodany do systemu, dla tego wyświetlacza został wygenerowany identyfikator Display#mDisplayId
lub DisplayInfo#displayId
poprzez zwiększenie stałego licznika. Jeśli system dodał i usunął ten sam wyświetlacz, w wyniku uzyskano inny identyfikator.
Jeśli urządzenie ma kilka wyświetlaczy dostępnych po uruchomieniu, w zależności od czasu mogą one mieć przypisane różne identyfikatory. Chociaż Android 9 (i starsze wersje) zawierał element DisplayInfo#uniqueId
, nie zawierał wystarczających informacji, aby rozróżnić wyświetlacze, ponieważ fizyczne wyświetlacze były identyfikowane jako local:0
lub local:1
, co oznaczało odpowiednio wbudowany i zewnętrzny wyświetlacz.
Android 10 wprowadza zmiany DisplayInfo#uniqueId
w celu dodania stabilnego identyfikatora i odróżnienia wyświetlaczy lokalnych, sieciowych i wirtualnych.
Typ wyświetlania | Format |
---|---|
Lokalny | local:<stable-id> |
Sieć | network:<mac-address> |
Wirtualne | virtual:<package-name-and-name> |
Oprócz aktualizacji uniqueId
,
DisplayInfo.address
zawiera DisplayAddress
, czyli identyfikator wyświetlania, który jest stabilny po ponownym uruchomieniu. W Androidzie 10 DisplayAddress
obsługuje wyświetlacze fizyczne i sieciowe. DisplayAddress.Physical
zawiera stabilny identyfikator wyświetlania (taki sam jak w uniqueId
) i może być tworzony za pomocą DisplayAddress#fromPhysicalDisplayId()
.
Android 10 udostępnia też wygodną metodę uzyskiwania informacji o portach (Physical#getPort()
). Można jej używać w ramach, aby statycznie identyfikować wyświetlacze. Jest on używany na przykład w DisplayWindowSettings
. DisplayAddress.Network
zawiera adres MAC i może być tworzony za pomocą DisplayAddress#fromMacAddress()
.
Te dodatki umożliwiają producentom urządzeń identyfikowanie wyświetlaczy w statycznych konfiguracjach z wieloma wyświetlaczami oraz konfigurowanie różnych ustawień i funkcji systemu za pomocą statycznych identyfikatorów wyświetlaczy, takich jak porty do fizycznych wyświetlaczy. Te metody są ukryte i przeznaczone tylko do użytku w ramach system_server
.
IComposerClient
Na podstawie identyfikatora wyświetlacza HWC (który może być nieprzezroczysty i nie zawsze stabilny) ta metoda zwraca 8-bitowy numer portu (specyficzny dla platformy), który identyfikuje złącze fizyczne wyświetlacza, a także dane EDID wyświetlacza.
SurfaceFlinger wyodrębnia z EDIDu informacje o producencie lub modelu, aby wygenerować stabilne 64-bitowe identyfikatory wyświetlacza udostępniane w ramach frameworka. Jeśli ta metoda nie jest obsługiwana lub generuje błędy, SurfaceFlinger przechodzi do starszego trybu MD, w którym DisplayInfo#address
jest równe null, a DisplayInfo#uniqueId
jest zakodowane 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żywanie więcej niż 2 wyświetlaczy
W Androidzie 9 (i starszych) SurfaceFlinger i DisplayManagerService
zakładały istnienie maksymalnie 2 fizycznych wyświetlaczy z zakodowanymi identyfikatorami 0 i 1.
Począwszy od Androida 10, SurfaceFlinger może korzystać z interfejsu Hardware Composer (HWC) API do generowania stabilnych identyfikatorów wyświetlaczy, co umożliwia zarządzanie dowolną liczbą fizycznych wyświetlaczy. Więcej informacji znajdziesz w artykule Identyfikatory statycznych wyświetleń.
Framework może wyszukiwać token IBinder
dla fizycznego wyświetlacza za pomocą SurfaceControl#getPhysicalDisplayToken
po uzyskaniu 64-bitowego identyfikatora wyświetlacza z SurfaceControl#getPhysicalDisplayIds
lub ze zdarzenia DisplayEventReceiver
hotplug.
W Androidzie 10 (i starszych) główny wyświetlacz wewnętrzny jest oznaczony jako TYPE_INTERNAL
, a wszystkie wyświetlacze dodatkowe są oznaczone jako TYPE_EXTERNAL
niezależnie od typu połączenia. Dlatego dodatkowe wyświetlacze wewnętrzne są traktowane jako zewnętrzne.
Jako obejście problemu kod specyficzny dla urządzenia może zakładać, że DisplayAddress.Physical#getPort
jest znany, a logika przypisywania portów jest przewidywalna.
To ograniczenie zostało usunięte w Androidzie 11 (i nowszych).
- W Androidzie 11 pierwszy wyświetlacz zgłoszony podczas uruchamiania jest wyświetlaczem głównym. Typ połączenia (wewnętrzne lub zewnętrzne) nie ma znaczenia. Nie można jednak odłączyć wyświetlacza głównego, dlatego w praktyce musi to być wyświetlacz wewnętrzny. Pamiętaj, że niektóre składane telefony mają kilka ekranów wewnętrznych.
- Wyświetlacze dodatkowe są prawidłowo przypisane do kategorii
Display.TYPE_INTERNAL
lubDisplay.TYPE_EXTERNAL
(dawniej odpowiednioDisplay.TYPE_BUILT_IN
iDisplay.TYPE_HDMI
) w zależności od typu połączenia.
Implementacja
W Androidzie 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 to nieprawidłowy wyświetlacz lub wirtualny wyświetlacz inny niż HWC.
Począwszy od Androida 10 wyświetlacze mają stabilne i trwałe identyfikatory, co pozwala aplikacji SurfaceFlinger i DisplayManagerService
śledzić więcej niż 2 wyświetlacze oraz rozpoznawać wyświetlacze, które były już widoczne. Jeśli HWC obsługuje IComposerClient.getDisplayIdentificationData
i udostępnia dane identyfikacyjne wyświetlacza, SurfaceFlinger analizuje strukturę EDID i przypisuje stabilne 64-bitowe identyfikatory wyświetlaczy do fizycznych i wirtualnych wyświetlaczy HWC. Identyfikatory są wyrażane za pomocą typu opcji, gdzie wartość null reprezentuje nieprawidłowy wyświetlacz lub wirtualny wyświetlacz niebędący wyświetlaczem wysokiej jakości. Bez obsługi HWC SurfaceFlinger korzysta z zachowania zgodnego ze starszymi wersjami z maksymalnie 2 wyświetlaczami fizycznymi.
Skupienie na wyświetlaczu
Aby obsługiwać kilka źródeł danych, które jednocześnie kierują treści na poszczególne wyświetlacze, Androida 10 można skonfigurować tak, aby obsługiwał wiele okien fokusowanych (maksymalnie jedno na wyświetlacz). Ta funkcja jest przeznaczona tylko do specjalnych typów urządzeń, gdy wielu użytkowników korzysta z tego samego urządzenia w tym samym czasie i korzysta z różnych metod wprowadzania danych lub urządzeń, takich jak Android Automotive.
Zdecydowanie zalecamy, aby ta funkcja nie była włączona na zwykłych urządzeniach, w tym na urządzeniach z wieloma ekranami lub na urządzeniach używanych do korzystania z funkcji podobnych do tych dostępnych na komputerach. Wynika to głównie z powodów bezpieczeństwa, ponieważ użytkownicy mogą się zastanawiać, które okno ma fokus.
Wyobraź sobie, że użytkownik wpisze informacje zabezpieczone w polu tekstowym, na przykład logując się w aplikacji bankowej lub wpisując tekst zawierający poufne informacje. Złośliwa aplikacja może utworzyć wirtualny ekran, aby wykonać działanie, a także pole do wprowadzania tekstu. Legalne i złośliwe działania są w centrum uwagi i oba wyświetlają wskaźnik aktywnego wprowadzania danych (migający kursor).
Jednak ponieważ dane wejściowe z klawiatury (sprzętowej lub programowej) są wprowadzane tylko do głównej aktywności (aplikacji, która została uruchomiona jako ostatnia), tworząc ukryty wirtualny wyświetlacz, złośliwa aplikacja może przechwycić dane wejściowe użytkownika, nawet gdy używa on klawiatury programowej na głównym wyświetlaczu urządzenia.
Użyj com.android.internal.R.bool.config_perDisplayFocusEnabled
, aby ustawić fokus na poszczególnych wyświetlaczach.
Zgodność
Problem: w Androidzie 9 i starszych w danym momencie może być aktywne maksymalnie 1 okno w systemie.
Rozwiązanie: w rzadkich przypadkach, gdy dwa okna z tego samego procesu byłyby w centrum uwagi, system skupia się tylko na oknie, które jest wyżej w kolejności Z. Ta ograniczenie nie dotyczy aplikacji kierowanych na Androida 10, ponieważ w tym przypadku można oczekiwać, że będą one obsługiwać większą liczbę okien jednocześnie.
Implementacja
WindowManagerService#mPerDisplayFocusEnabled
określa dostępność tej funkcji. W funkcji ActivityManager
zamiast globalnego śledzenia w zmiennej jest teraz używana zmienna ActivityDisplay#getFocusedStack()
. ActivityDisplay#getFocusedStack()
określa fokus na podstawie porządku Z zamiast przechowywania wartości w pamięci podręcznej. Dzięki temu tylko jedno źródło (WindowManager) musi śledzić kolejność Z czynności.
ActivityStackSupervisor#getTopDisplayFocusedStack()
stosuje podobne podejście w przypadkach, gdy trzeba zidentyfikować najbardziej skupiony stos w systemie. Stosy są przeszukiwane od góry do dołu w celu znalezienia pierwszego odpowiedniego stosu.
InputDispatcher
może teraz mieć wiele okien fokusowych (po jednym na ekran). Jeśli zdarzenie wejściowe jest związane z konkretnym wyświetlaczem, jest wysyłane do okna, które jest aktualnie aktywne na tym wyświetlaczu. W przeciwnym razie jest wysyłane do aktywnego okna na aktywnym wyświetlaczu, czyli wyświetlacza, z którym użytkownik ostatnio wszedł w interakcję.
Zapoznaj się z tymi dokumentami: InputDispatcher::mFocusedWindowHandlesByDisplay
i InputDispatcher::setFocusedDisplay()
. Aplikacje w centrum uwagi są również aktualizowane osobno w InputManagerService za pomocą funkcji NativeInputManager::setFocusedApplication()
.
W WindowManager
okna z aktywnym fokusem są również śledzone osobno.
Zapoznaj się z tymi dokumentami: DisplayContent#mCurrentFocus
i DisplayContent#mFocusedApp
oraz ich odpowiednimi zastosowaniami. Powiązane metody śledzenia i aktualizowania danych o punktach skupienia zostały przeniesione z poziomu WindowManagerService
na poziom DisplayContent
.