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' resizeableActivity=false . Les problèmes courants rencontrés par les applications lorsque les activités sont redimensionnées incluent :

  • Une activité peut avoir une configuration différente 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 mesures 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'un redémarrage 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 multi-fenêtres.

Dans Android 7 (et versions ultérieures), une application peut être définie sur 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 essaie d'invoquer une activité non redimensionnable à partir du 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êtres, sauf si le mode de compatibilité est appliqué :

  • La même configuration est appliquée au processus, qui contient toutes les activités et les composants hors activité.
  • 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 d'aspect fixe. Sinon, l'activité se redimensionne pour remplir tout l'écran comme dans Android 9 et inférieur.

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

Lorsqu'une activité est déclarée incompatible avec le multi-fenêtre via l'utilisation de l'attribut android:resizeableActivity et que cette activité répond à 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 reçoit une possibilité de relancer le processus d'application pour utiliser la configuration d'écran mise à jour.

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

Cette figure affiche une activité non redimensionnable avec un rapport d'aspect déclaré. Lors du pliage de l'appareil, la fenêtre est réduite pour s'adapter à la zone tout en conservant le rapport d'aspect 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 format 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 des proportions fixes 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 tout 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é déplacée vers un autre affichage), ActivityRecord#inSizeCompatMode() est vrai et SizeCompatModeActivityController (dans l'interface utilisateur système) reçoit le rappel pour afficher le processus bouton de redémarrage.

Tailles d'affichage et rapports hauteur/largeur

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

ratios d'applications dans Android 10

Figure 1. Exemples de ratios d'applications 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 acceptent 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. Utilisez les attributs de mise en page d'activité android:minHeight et android:minWidth minWidth dans AndroidManifest pour ce faire.

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
  • Certaines touches et le suivi des événements de mouvement
  • Interface utilisateur 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 la 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 affichage a été étendu pour inclure :

  • Mode de fenêtrage d'affichage par défaut
  • Valeurs de surbalayage
  • Rotation de l'utilisateur et mode de rotation
  • Taille, densité et mode de mise à l'échelle forcés
  • Mode de suppression de contenu (lorsque l'affichage est supprimé)
  • Prise en charge des décorations système et 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 effacement.

Par défaut, Android 10 utilise DisplayInfo#uniqueId comme identifiant pour un affichage lors de la persistance 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 affichage physique comme identifiant, qui peut être défini dans DisplayWindowSettings#mIdentifier . À chaque écriture, tous les paramètres sont écrits, il est donc sûr de mettre à jour la clé utilisée pour une entrée d'affichage dans le stockage. Pour plus de détails, voir Identificateurs d'affichage statiques .

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 statiques

Android 9 (et versions antérieures) ne fournissait pas d'identifiants stables pour les affichages dans le cadre. 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 ID différent en résultait.

Si un périphérique disposait de plusieurs écrans disponibles au démarrage, les écrans pouvaient se voir attribuer différents identifiants, en fonction du moment. Alors 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
Local
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 qui est 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 affichages dans des configurations multi-affichages statiques et de configurer différents paramètres et fonctionnalités du système à l'aide d'identificateurs d'affichage statiques, tels que des ports pour les affichages 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'affichage. SurfaceFlinger extrait les informations du fabricant ou du 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 génère des erreurs, 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"

Utilisation de 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 tirer parti d'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'affichages physiques. Pour en savoir plus, voir Identifiants d'affichage statiques .

Le framework 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 à partir d'un événement de branchement à DisplayEventReceiver .

Dans Android 10 (et versions antérieures), l'affichage interne principal est TYPE_INTERNAL et tous les affichages secondaires sont signalés comme TYPE_EXTERNAL , quel que soit le type de connexion. Par conséquent, les écrans internes supplémentaires sont traités comme externes. Pour contourner ce 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 supérieur).

  • Dans 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 primaire 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 ont plusieurs écrans internes.
  • Les écrans secondaires sont classés correctement en tant que Display.TYPE_INTERNAL ou Display.TYPE_EXTERNAL (anciennement appelés Display.TYPE_BUILT_IN et Display.TYPE_HDMI ) en fonction de leur type de connexion.

Mise en œuvre

Dans 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 prise en charge de HWC, SurfaceFlinger revient au comportement hérité avec au plus deux écrans physiques.

Mise au point par écran

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

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

Imaginez l'utilisateur qui saisit des informations sécurisées dans un champ de saisie de texte, se connectant peut-être à une application bancaire ou saisissant un 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 sont ciblées et affichent toutes deux un indicateur d'entrée active (curseur clignotant).

Cependant, étant donné que l'entrée d'un clavier (matériel ou logiciel) n'est entrée que dans l'activité la plus élevée (cette application qui a été lancée le plus récemment), en créant un affichage virtuel caché, une application malveillante pourrait saisir l'entrée de l'utilisateur, même lors de l'utilisation d'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 : dans Android 9 et les versions antérieures, au plus une fenêtre du système a le focus à la fois.

Solution : dans les rares cas où deux fenêtres du même processus seraient ciblées, le système fournit uniquement le focus à la fenêtre la plus élevée dans l'ordre Z. Cette restriction est supprimée pour les applications qui ciblent Android 10, auquel cas on s'attend à ce 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 en cache la valeur. C'est ainsi qu'une seule source, WindowManager, doit suivre l'ordre Z des activités.

ActivityStackSupervisor#getTopDisplayFocusedStack() adopte une approche similaire dans les cas où la pile ciblée la plus élevé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 maintenant avoir plusieurs fenêtres ciblées (une par affichage). Si un événement d'entrée est spécifique à l'affichage, il est envoyé à la fenêtre ciblée dans l'affichage correspondant. Sinon, il est envoyé à 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 .