Prise en charge de l'affichage

Les mises à jour apportées à ces zones spécifiques à l'affichage sont fournies ci-dessous :

Redimensionner les activités et les affichages

Pour indiquer qu'une application peut ne pas prendre en charge le mode multi-fenêtres ou le redimensionnement, les activités utilisent l'attribut resizeableActivity=false . Les problèmes courants rencontrés par les applications lors du redimensionnement des activités incluent :

  • Une activité peut avoir une configuration différente de celle de l'application ou d'un autre composant non visuel. Une erreur courante consiste à lire les métriques d’affichage à partir du contexte de l’application. Les valeurs renvoyées ne seront pas ajustées aux métriques de la zone visible dans laquelle une activité est affichée.
  • Une activité peut ne pas gérer le redimensionnement et planter, afficher une interface utilisateur déformée ou perdre son état en raison d'une relance sans enregistrer l'état de l'instance.
  • Une application peut tenter d'utiliser des coordonnées d'entrée absolues (au lieu de celles relatives à la position de la fenêtre), ce qui peut interrompre l'entrée en mode multi-fenêtre.

Sous Android 7 (et versions ultérieures), une application peut être définie comme resizeableActivity=false pour toujours s'exécuter en mode plein écran. Dans ce cas, la plateforme empêche les activités non redimensionnables de passer en écran partagé. Si l'utilisateur tente d'invoquer une activité non redimensionnable depuis le lanceur alors qu'il est déjà en mode écran partagé, la plateforme quitte le mode écran partagé et lance l'activité non redimensionnable en mode plein écran.

Les applications qui définissent explicitement cet attribut sur false dans le manifeste ne doivent pas être lancées en mode multi-fenêtre, sauf si le mode de compatibilité est appliqué :

  • La même configuration est appliquée au processus, qui contient tous les composants d'activités et de non-activités.
  • La configuration appliquée répond aux exigences CDD pour les écrans compatibles avec les applications.

Dans Android 10, la plate-forme empêche toujours les activités non redimensionnables de passer en mode écran partagé, mais elles peuvent être temporairement mises à l'échelle si l'activité a déclaré une orientation ou un rapport hauteur/largeur fixe. Sinon, l'activité est redimensionnée pour remplir tout l'écran comme dans Android 9 et versions antérieures.

L'implémentation par défaut applique la stratégie suivante :

Lorsqu'une activité est déclarée incompatible avec le multi-fenêtre grâce à l'utilisation de l'attribut android:resizeableActivity et lorsque cette activité remplit l'une des conditions décrites ci-dessous, alors lorsque la configuration d'écran appliquée doit changer, l'activité et le processus sont enregistrés avec la configuration d'origine. et l'utilisateur a la possibilité de relancer le processus d'application afin d'utiliser la configuration d'écran mise à jour.

  • L'orientation est fixe via l'application d' android:screenOrientation
  • L'application a un rapport hauteur/largeur maximum ou minimum par défaut en ciblant le niveau de l'API ou déclare explicitement le rapport hauteur/largeur

Cette figure affiche une activité non redimensionnable avec un rapport hauteur/largeur déclaré. Lors du pliage de l'appareil, la fenêtre est réduite pour s'adapter à la zone tout en conservant le rapport hauteur/largeur en utilisant la boîte aux lettres appropriée. De plus, une option de redémarrage de l'activité est proposée à l'utilisateur à chaque fois que la zone d'affichage de l'activité est modifiée.

Lors du dépliage de l'appareil, la configuration, la taille et le rapport hauteur/largeur de l'activité ne changent pas, mais l'option de redémarrage de l'activité s'affiche.

Lorsque resizeableActivity n'est pas défini (ou qu'il est défini sur true ), l'application prend entièrement en charge le redimensionnement.

Mise en œuvre

Une activité non redimensionnable avec une orientation ou un rapport hauteur/largeur fixe est appelée mode de compatibilité de taille (SCM) dans le code. La condition est définie dans ActivityRecord#shouldUseSizeCompatMode() . Lorsqu'une activité SCM est lancée, la configuration liée à l'écran (telle que la taille ou la densité) est fixée dans la configuration de remplacement demandée, de sorte que l'activité ne dépend plus de la configuration d'affichage actuelle.

Si l’activité SCM ne peut pas remplir la totalité de l’écran, elle est alignée en haut et centrée horizontalement. Les limites d'activité sont calculées par AppWindowToken#calculateCompatBoundsTransformation() .

Lorsqu'une activité SCM utilise une configuration d'écran différente de celle de son conteneur (par exemple, l'affichage est redimensionné ou l'activité est déplacée vers un autre affichage), ActivityRecord#inSizeCompatMode() est true et SizeCompatModeActivityController (dans l'interface utilisateur système) reçoit le rappel pour afficher le processus. bouton de redémarrage.

Tailles d'affichage et proportions

Android 10 prend en charge de nouveaux formats d'image, allant des ratios élevés d'écrans longs et fins aux ratios 1:1. Les applications peuvent définir ApplicationInfo#maxAspectRatio et ApplicationInfo#minAspectRatio de l'écran qu'elles sont capables de gérer.

ratios d'application dans Android 10

Figure 1. Exemples de ratios d'application pris en charge dans Android 10

Les implémentations d'appareils peuvent avoir des écrans secondaires avec des tailles et des résolutions inférieures à celles requises par Android 9, et inférieures (minimum de 2,5 pouces de largeur ou de hauteur, minimum de 320 DP pour smallestScreenWidth ), mais seules les activités qui choisissent de prendre en charge ces petits écrans peuvent être placé là.

Les applications peuvent s'inscrire en déclarant une taille minimale prise en charge inférieure à oe égale à la taille d'affichage cible. Pour ce faire, utilisez les attributs de présentation d'activité android:minHeight et android:minWidth dans AndroidManifest.

Politiques d'affichage

Android 10 sépare et déplace certaines politiques d'affichage de l'implémentation par défaut de WindowManagerPolicy dans PhoneWindowManager vers des classes par affichage, telles que :

  • État d'affichage et rotation
  • Quelques touches et suivi des événements de mouvement
  • Interface utilisateur du système et fenêtres de décoration

Dans Android 9 (et versions antérieures), la classe PhoneWindowManager gérait les politiques d'affichage, l'état et les paramètres, la rotation, le suivi du cadre de la fenêtre de décoration, etc. Android 10 déplace la majeure partie de cela vers la classe DisplayPolicy , à l'exception du suivi de rotation, qui a été déplacé vers DisplayRotation .

Paramètres de la fenêtre d'affichage

Dans Android 10, le paramètre de fenêtrage configurable par écran a été étendu pour inclure :

  • Mode de fenêtrage d'affichage par défaut
  • Valeurs de surbalayage
  • Rotation des utilisateurs et mode de rotation
  • Taille forcée, densité et mode de mise à l'échelle
  • Mode de suppression de contenu (lorsque l'affichage est supprimé)
  • Prise en charge des décorations système et de l'IME

La classe DisplayWindowSettings contient les paramètres de ces options. Ils sont conservés sur le disque dans la partition /data dans display_settings.xml chaque fois qu'un paramètre est modifié. Pour plus de détails, consultez DisplayWindowSettings.AtomicFileStorage et DisplayWindowSettings#writeSettings() . Les fabricants d'appareils peuvent fournir des valeurs par défaut dans display_settings.xml pour la configuration de leur appareil. Cependant, étant donné que le fichier est stocké dans /data , une logique supplémentaire peut être nécessaire pour restaurer le fichier s'il est effacé par un nettoyage.

Par défaut, Android 10 utilise DisplayInfo#uniqueId comme identifiant d'un affichage lors de la conservation des paramètres. uniqueId doit être renseigné pour tous les affichages. De plus, il est stable pour les affichages physiques et réseau. Il est également possible d'utiliser le port d'un écran physique comme identifiant, qui peut être défini dans DisplayWindowSettings#mIdentifier . À chaque écriture, tous les paramètres sont écrits afin qu'il soit possible de mettre à jour en toute sécurité la clé utilisée pour une entrée d'affichage dans le stockage. Pour plus de détails, consultez Identifiants d'affichage statique .

Les paramètres sont conservés dans le répertoire /data pour des raisons historiques. À l’origine, ils étaient utilisés pour conserver les paramètres définis par l’utilisateur, tels que la rotation de l’affichage.

Identifiants d'affichage statique

Android 9 (et versions antérieures) ne fournissait pas d'identifiants stables pour les affichages dans le framework. Lorsqu'un affichage a été ajouté au système, Display#mDisplayId ou DisplayInfo#displayId a été généré pour cet affichage en incrémentant un compteur statique. Si le système ajoutait et supprimait le même affichage, un identifiant différent en résultait.

Si un appareil disposait de plusieurs écrans disponibles dès le démarrage, les écrans pouvaient se voir attribuer différents identifiants, en fonction du timing. Bien qu'Android 9 (et versions antérieures) incluait DisplayInfo#uniqueId , il ne contenait pas suffisamment d'informations pour différencier les affichages, car les affichages physiques étaient identifiés comme local:0 ou local:1 , pour représenter l'affichage intégré et externe.

Android 10 modifie DisplayInfo#uniqueId pour ajouter un identifiant stable et différencier les affichages locaux, réseau et virtuels.

Type d'affichage Format
Locale
local:<stable-id>
Réseau
network:<mac-address>
Virtuel
virtual:<package-name-and-name>

En plus des mises à jour de uniqueId , DisplayInfo.address contient DisplayAddress , un identifiant d'affichage stable lors des redémarrages. Dans Android 10, DisplayAddress prend en charge les affichages physiques et réseau. DisplayAddress.Physical contient un ID d'affichage stable (identique à uniqueId ) et peut être créé avec DisplayAddress#fromPhysicalDisplayId() .

Android 10 fournit également une méthode pratique pour obtenir des informations sur le port ( Physical#getPort() ). Cette méthode peut être utilisée dans le cadre pour identifier statiquement les affichages. Par exemple, il est utilisé dans DisplayWindowSettings ). DisplayAddress.Network contient l'adresse MAC et peut être créé avec DisplayAddress#fromMacAddress() .

Ces ajouts permettent aux fabricants d'appareils d'identifier les écrans dans des configurations multi-écrans statiques et de configurer différents paramètres et fonctionnalités du système à l'aide d'identifiants d'affichage statiques, tels que des ports pour les écrans physiques. Ces méthodes sont masquées et sont destinées à être utilisées uniquement dans system_server .

Étant donné un ID d'affichage HWC (qui peut être opaque et pas toujours stable), cette méthode renvoie le numéro de port 8 bits (spécifique à la plate-forme) qui identifie un connecteur physique pour la sortie d'affichage, ainsi que le blob EDID de l'écran. SurfaceFlinger extrait les informations sur le fabricant ou le modèle de l'EDID pour générer les ID d'affichage 64 bits stables exposés au framework. Si cette méthode n'est pas prise en charge ou si des erreurs surviennent, SurfaceFlinger revient au mode MD hérité, où DisplayInfo#address est nul et DisplayInfo#uniqueId est codé en dur, comme décrit ci-dessus.

Pour vérifier que cette fonctionnalité est prise en charge, exécutez :

$ 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"

Utiliser plus de deux écrans

Dans Android 9 (et versions antérieures), SurfaceFlinger et DisplayManagerService supposaient l'existence d'au plus deux écrans physiques avec les ID codés en dur 0 et 1.

À partir d'Android 10, SurfaceFlinger pourrait exploiter une API Hardware Composer (HWC) pour générer des ID d'affichage stables, ce qui lui permet de gérer un nombre arbitraire d'écrans physiques. Pour en savoir plus, consultez Identifiants d'affichage statique .

L'infrastructure peut rechercher le jeton IBinder pour un affichage physique via SurfaceControl#getPhysicalDisplayToken après avoir obtenu l'ID d'affichage 64 bits à partir de SurfaceControl#getPhysicalDisplayIds ou d'un événement de connexion à chaud DisplayEventReceiver .

Sous Android 10 (et versions antérieures), l'affichage interne principal est TYPE_INTERNAL et tous les affichages secondaires sont marqués comme TYPE_EXTERNAL quel que soit le type de connexion. Par conséquent, les affichages internes supplémentaires sont traités comme externes. Pour contourner le problème, le code spécifique à l'appareil peut faire des hypothèses sur DisplayAddress.Physical#getPort si le HWC est connu et que la logique d'allocation de port est prévisible.

Cette limitation est supprimée dans Android 11 (et versions ultérieures).

  • Sous Android 11, le premier affichage signalé lors du démarrage est l’affichage principal. Le type de connexion (interne ou externe) n'a pas d'importance. Cependant, il reste vrai que l'affichage principal ne peut pas être déconnecté et il s'ensuit qu'il doit s'agir en pratique d'un affichage interne. Notez que certains téléphones pliables disposent de plusieurs écrans internes.
  • Les écrans secondaires sont correctement classés comme Display.TYPE_INTERNAL ou Display.TYPE_EXTERNAL (anciennement appelés Display.TYPE_BUILT_IN et Display.TYPE_HDMI , respectivement) en fonction de leur type de connexion.

Mise en œuvre

Sous Android 9 et versions antérieures, les écrans sont identifiés par des identifiants 32 bits, où 0 est l'écran interne, 1 est l'écran externe, [2, INT32_MAX] sont des écrans virtuels HWC et -1 représente un affichage non valide ou non-HWC. affichage virtuel.

À partir d'Android 10, les écrans reçoivent des identifiants stables et persistants, ce qui permet à SurfaceFlinger et DisplayManagerService de suivre plus de deux écrans et de reconnaître les écrans précédemment vus. Si le HWC prend en charge IComposerClient.getDisplayIdentificationData et fournit des données d'identification d'affichage, SurfaceFlinger analyse la structure EDID et alloue des ID d'affichage 64 bits stables pour les affichages physiques et virtuels HWC. Les ID sont exprimés à l'aide d'un type d'option, où la valeur nulle représente un affichage non valide ou un affichage virtuel non HWC. Sans la prise en charge de HWC, SurfaceFlinger revient au comportement existant avec au plus deux écrans physiques.

Mise au point par écran

Pour prendre en charge plusieurs sources d'entrée ciblant des écrans individuels en même temps, Android 10 peut être configuré pour prendre en charge plusieurs fenêtres ciblées, au maximum une par écran. Ceci est destiné uniquement à des types spéciaux d'appareils lorsque plusieurs utilisateurs interagissent avec le même appareil en même temps et utilisent différentes méthodes ou appareils de saisie, tels qu'Android Automotive.

Il est fortement recommandé de ne pas activer cette fonctionnalité sur les appareils classiques, y compris les appareils multi-écrans ou ceux utilisés pour des expériences de type ordinateur de bureau. Cela est principalement dû à un problème de sécurité qui peut amener les utilisateurs à se demander quelle fenêtre a le focus de saisie.

Imaginez l'utilisateur qui saisit des informations sécurisées dans un champ de saisie de texte, par exemple en se connectant à une application bancaire ou en saisissant du texte contenant des informations sensibles. Une application malveillante pourrait créer un affichage virtuel hors écran avec lequel exécuter une activité, également avec un champ de saisie de texte. Les activités légitimes et malveillantes ont le focus et affichent toutes deux un indicateur d'entrée active (curseur clignotant).

Cependant, étant donné que la saisie à partir d'un clavier (matériel ou logiciel) est saisie uniquement dans l'activité la plus élevée (l'application la plus récemment lancée), en créant un affichage virtuel caché, une application malveillante pourrait récupérer la saisie de l'utilisateur, même en utilisant un clavier logiciel. sur l’écran principal de l’appareil.

Utilisez com.android.internal.R.bool.config_perDisplayFocusEnabled pour définir le focus par affichage.

Compatibilité

Problème : Sous Android 9 et versions antérieures, au plus une fenêtre du système est active à la fois.

Solution : dans les rares cas où deux fenêtres du même processus seraient ciblées, le système fournit le focus uniquement sur la fenêtre la plus haute dans l'ordre Z. Cette restriction est supprimée pour les applications qui ciblent Android 10, auquel cas il est prévu qu'elles puissent prendre en charge plusieurs fenêtres simultanément.

Mise en œuvre

WindowManagerService#mPerDisplayFocusEnabled contrôle la disponibilité de cette fonctionnalité. Dans ActivityManager , ActivityDisplay#getFocusedStack() est désormais utilisé à la place du suivi global dans une variable. ActivityDisplay#getFocusedStack() détermine le focus en fonction de l'ordre Z au lieu de mettre la valeur en cache. Ainsi, une seule source, WindowManager, doit suivre l'ordre Z des activités.

ActivityStackSupervisor#getTopDisplayFocusedStack() adopte une approche similaire pour les cas où la pile la plus ciblée du système doit être identifiée. Les piles sont parcourues de haut en bas, à la recherche de la première pile éligible.

InputDispatcher peut désormais avoir plusieurs fenêtres ciblées (une par affichage). Si un événement d'entrée est spécifique à l'affichage, il est alors distribué à la fenêtre ciblée dans l'affichage correspondant. Sinon, il est distribué vers la fenêtre ciblée dans l’affichage ciblé, qui est l’affichage avec lequel l’utilisateur a interagi le plus récemment.

Voir InputDispatcher::mFocusedWindowHandlesByDisplay et InputDispatcher::setFocusedDisplay() . Les applications ciblées sont également mises à jour séparément dans InputManagerService via NativeInputManager::setFocusedApplication() .

Dans WindowManager , les fenêtres ciblées sont également suivies séparément. Voir DisplayContent#mCurrentFocus et DisplayContent#mFocusedApp et les utilisations respectives. Les méthodes de suivi et de mise à jour du focus associées ont été déplacées de WindowManagerService vers DisplayContent .