Obsługa reklam displayowych

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

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#maxAspectRatioApplicationInfo#minAspectRatioekranu, które są w stanie obsłużyć.

Formaty aplikacji w Androidzie 10

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:minHeightandroid:minWidth.

Zasady dotyczące reklam displayowych

Android 10 oddziela niektóre zasady dotyczące wyświetlania i przenosi je z domyślnej implementacji WindowManagerPolicyPhoneWindowManager 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 /datadisplay_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#uniqueIdw 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 lub Display.TYPE_EXTERNAL (dawniej odpowiednio Display.TYPE_BUILT_IN i Display.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::mFocusedWindowHandlesByDisplayInputDispatcher::setFocusedDisplay(). Aplikacje w centrum uwagi są również aktualizowane osobno w InputManagerService za pomocą funkcji NativeInputManager::setFocusedApplication().

WindowManager okna z aktywnym fokusem są również śledzone osobno. Zapoznaj się z tymi dokumentami: DisplayContent#mCurrentFocusDisplayContent#mFocusedApp oraz ich odpowiednimi zastosowaniami. Powiązane metody śledzenia i aktualizowania danych o punktach skupienia zostały przeniesione z poziomu WindowManagerService na poziom DisplayContent.