Les modifications apportées à ces zones spécifiques à l'affichage sont décrites ci-dessous :
- Redimensionner les activités et les écrans
- Tailles d'écran et formats
- Règles concernant le display
- Paramètres de la fenêtre d'affichage
- Identifiants d'affichage statique
- Utiliser plus de deux écrans
- Focus par écran
Redimensionner les activités et les affichages
Pour indiquer qu'une application n'est pas compatible avec le mode multifenêtre ou le redimensionnement, les activités utilisent l'attribut resizeableActivity=false
. Voici quelques problèmes courants rencontrés par les applications lorsque les activités sont redimensionnées :
- 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.
- Il est possible qu'une activité ne gère pas le redimensionnement et plante, affiche une UI déformée ou perde son état en raison d'un relancement sans enregistrement de 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 perturber l'entrée en mode multifenêtre.
Dans Android 7 (et versions ultérieures), une application peut être définie sur resizeableActivity=false
pour s'exécuter toujours en mode plein écran. Dans ce cas, la plate-forme empêche les activités non redimensionnables de passer en mode écran partagé. Si l'utilisateur tente d'appeler une activité non redimensionnable depuis le lanceur d'applications alors qu'il est déjà en mode Écran partagé, la plate-forme 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 fichier manifeste ne doivent pas être lancées en mode multifenêtre, sauf si le mode de compatibilité est appliqué :
- La même configuration est appliquée au processus, qui contient toutes les activités et tous les composants non liés aux activités.
- La configuration appliquée répond aux exigences du 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 mises à l'échelle temporairement si l'activité a déclaré une orientation ou un format fixe. Dans le cas contraire, l'activité est redimensionnée pour remplir tout l'écran, comme dans Android 9 et les versions antérieures.
L'implémentation par défaut applique la règle suivante :
Lorsqu'une activité déclarée incompatible avec le multifenêtre à l'aide de l'attribut android:resizeableActivity
et que cette activité remplit l'une des conditions décrites ci-dessous, l'activité et le processus sont enregistrés avec la configuration d'origine lorsque la configuration de l'écran appliqué doit changer. L'utilisateur est alors invité à relancer le processus de l'application pour utiliser la configuration d'écran mise à jour.
- L'orientation fixe est-elle possible en appliquant
android:screenOrientation
? - L'application a un format maximal ou minimal par défaut en ciblant le niveau d'API ou déclare explicitement le format.
Cette figure montre une activité non redimensionnable avec un format déclaré. Lorsque l'appareil est plié, la fenêtre est réduite pour s'adapter à la zone tout en conservant les proportions à l'aide du letterboxing approprié. 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.
Lorsque l'appareil est déplié, la configuration, la taille et le format de l'activité ne changent pas, mais l'option permettant de redémarrer l'activité s'affiche.
Lorsque resizeableActivity
n'est pas défini (ou est défini sur true
), l'application est entièrement compatible avec le redimensionnement.
Implémentation
Une activité non redimensionnable avec une orientation ou un format 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 fixe dans la configuration de remplacement demandée. L'activité ne dépend donc plus de la configuration d'affichage actuelle.
Si l'activité SCM ne peut pas remplir l'intégralité 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'écran est redimensionné ou l'activité est déplacée vers un autre écran), ActivityRecord#inSizeCompatMode()
est défini sur "true" et SizeCompatModeActivityController
(dans l'UI système) reçoit le rappel pour afficher le bouton de redémarrage du processus.
Tailles d'écran et formats
Android 10 est compatible avec de nouveaux formats, allant des formats élevés des écrans longs et fins aux formats 1:1. Les applications peuvent définir ApplicationInfo#maxAspectRatio
et ApplicationInfo#minAspectRatio
de l'écran qu'elles peuvent gérer.
Figure 1 : Exemples de ratios d'application compatibles avec Android 10
Les implémentations d'appareils peuvent comporter des écrans secondaires dont la taille et la résolution sont inférieures à celles requises par Android 9 et versions ultérieures (minimum de 6, 35 cm de largeur ou de hauteur, minimum de 320 dp pour smallestScreenWidth
). Toutefois, seules les activités qui acceptent la prise en charge de ces petits écrans peuvent y être placées.
Les applications peuvent s'inscrire en déclarant une taille minimale compatible inférieure ou égale à la taille d'affichage cible. Pour ce faire, utilisez les attributs de mise en page d'activité android:minHeight
et android:minWidth
dans le fichier AndroidManifest.
Règles relatives aux annonces display
Android 10 sépare et déplace certaines règles d'affichage de l'implémentation WindowManagerPolicy
par défaut dans PhoneWindowManager
vers des classes par écran, telles que :
- État et rotation de l'écran
- Suivi de certaines touches et de certains événements de mouvement
- UI du système et fenêtres de décoration
Dans Android 9 (et versions antérieures), la classe PhoneWindowManager
gérait les règles 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 plupart de ces éléments 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 écran a été étendu pour inclure :
- Mode de fenêtrage de l'écran par défaut
- Valeurs de surbalayage
- Rotation des utilisateurs et mode de rotation
- Taille, densité et mode de mise à l'échelle forcés
- Mode de suppression du contenu (lorsque l'écran 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
de display_settings.xml
chaque fois qu'un paramètre est modifié. Pour en savoir plus, 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 leurs appareils. Toutefois, comme le fichier est stocké dans /data
, une logique supplémentaire peut être nécessaire pour le restaurer s'il est effacé par une réinitialisation.
Par défaut, Android 10 utilise DisplayInfo#uniqueId
comme identifiant pour un écran lors de la persistance des paramètres. uniqueId
doit être renseigné pour tous les écrans. De plus, il est stable pour les écrans 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. Vous pouvez donc mettre à jour la clé utilisée pour une entrée d'affichage dans le stockage. Pour en savoir plus, consultez Identifiants 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'écran.
Identifiants d'affichage statique
Android 9 (et les versions antérieures) ne fournissaient pas d'identifiants stables pour les écrans dans le framework. Lorsqu'un écran a été ajouté au système, Display#mDisplayId
ou DisplayInfo#displayId
a été généré pour cet écran en incrémentant un compteur statique. Si le système ajoutait et supprimait le même écran, un ID différent était généré.
Si un appareil disposait de plusieurs écrans disponibles au démarrage, des identifiants différents pouvaient leur être attribués, en fonction du timing. Bien qu'Android 9 (et les versions antérieures) incluent DisplayInfo#uniqueId
, il ne contient pas suffisamment d'informations pour faire la différence entre les écrans, car les écrans physiques sont identifiés comme local:0
ou local:1
pour représenter l'écran intégré et l'écran externe.
Android 10 modifie DisplayInfo#uniqueId
pour ajouter un identifiant stable et faire la distinction entre les écrans 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 stable lors des redémarrages. Dans Android 10, DisplayAddress
est compatible avec les écrans physiques et réseau. DisplayAddress.Physical
contient un ID d'affichage stable (identique à celui de uniqueId
) et peut être créé avec DisplayAddress#fromPhysicalDisplayId()
.
Android 10 fournit également une méthode pratique pour obtenir des informations sur les ports (Physical#getPort()
). Cette méthode peut être utilisée dans le framework pour identifier statiquement les écrans. 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 les configurations multi-écrans statiques et de configurer différents paramètres et fonctionnalités système à l'aide d'identifiants d'écran statiques, tels que les ports pour les écrans physiques. Ces méthodes sont masquées et ne sont destinées à être utilisées que 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 stables de 64 bits exposés au framework. Si cette méthode n'est pas prise en charge ou génère des erreurs, SurfaceFlinger revient à l'ancien mode MD, 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 la commande suivante :
$ 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 maximum deux écrans physiques avec les ID codés en dur 0 et 1.
À partir d'Android 10, SurfaceFlinger peut utiliser une API Hardware Composer (HWC) pour générer des ID d'écran stables, ce qui lui permet de gérer un nombre arbitraire d'écrans physiques. Pour en savoir plus, consultez Identifiants d'affichage statique.
Le framework peut rechercher le jeton IBinder
pour un écran physique via SurfaceControl#getPhysicalDisplayToken
après avoir obtenu l'ID d'écran 64 bits à partir de SurfaceControl#getPhysicalDisplayIds
ou d'un événement de branchement à chaud DisplayEventReceiver
.
Dans Android 10 (et versions antérieures), l'écran interne principal est TYPE_INTERNAL
et tous les écrans 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 des écrans 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'attribution des ports est prévisible.
Cette limitation est supprimée dans Android 11 (et versions ultérieures).
- Dans Android 11, le premier écran signalé au démarrage est l'écran principal. Le type de connexion (interne ou externe) n'a pas d'importance. Toutefois, il reste vrai que l'écran principal ne peut pas être déconnecté et qu'il doit donc être un écran interne en pratique. Notez que certains téléphones pliables sont dotés de plusieurs écrans intérieurs.
- Les écrans secondaires sont correctement classés comme
Display.TYPE_INTERNAL
ouDisplay.TYPE_EXTERNAL
(anciennementDisplay.TYPE_BUILT_IN
etDisplay.TYPE_HDMI
, respectivement) en fonction de leur type de connexion.
Implémentation
Dans Android 9 et les versions antérieures, les écrans sont identifiés par des ID de 32 bits, où 0 correspond à l'écran interne, 1 à l'écran externe, [2, INT32_MAX]
aux écrans virtuels HWC et -1 à un écran non valide ou à un écran virtuel non HWC.
Depuis Android 10, les écrans reçoivent des ID stables et persistants, ce qui permet à SurfaceFlinger et à DisplayManagerService
de suivre plus de deux écrans et de reconnaître les écrans déjà vus. Si le HWC est compatible avec IComposerClient.getDisplayIdentificationData
et fournit des données d'identification de l'écran, SurfaceFlinger analyse la structure EDID et attribue des ID d'écran stables de 64 bits pour les écrans 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 à l'ancien comportement avec au maximum deux écrans physiques.
Focus par affichage
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 sélectionnées, au maximum une par écran. Cette fonctionnalité est réservée aux types d'appareils spéciaux où plusieurs utilisateurs interagissent avec le même appareil en même temps et utilisent différentes méthodes ou appareils de saisie, comme Android Automotive.
Nous vous recommandons vivement 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 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 d'entrée.
Imaginez l'utilisateur qui saisit des informations sécurisées dans un champ de saisie de texte, peut-être pour se connecter à une application bancaire ou pour saisir du texte contenant des informations sensibles. Une application malveillante peut créer un écran virtuel hors écran pour exécuter une activité, également avec un champ de saisie de texte. Les activités légitimes et malveillantes sont mises en évidence et affichent toutes deux un indicateur d'entrée actif (curseur clignotant).
Toutefois, comme les saisies au clavier (matériel ou logiciel) ne sont entrées que dans l'activité la plus haute (l'application lancée le plus récemment), une application malveillante pourrait, en créant un écran virtuel masqué, récupérer les saisies de l'utilisateur, même lorsqu'il utilise un clavier logiciel sur l'écran principal de l'appareil.
Utilisez com.android.internal.R.bool.config_perDisplayFocusEnabled
pour définir la mise au point par écran.
Compatibilité
Problème : Sous Android 9 ou version antérieure, au maximum une fenêtre du système peut être active à la fois.
Solution : Dans le rare cas où deux fenêtres du même processus seraient mises au point, le système ne met au point que la fenêtre qui se trouve en haut de l'ordre Z. Cette restriction est supprimée pour les applications ciblant Android 10, qui sont censées pouvoir prendre en charge plusieurs fenêtres simultanément.
Implémentation
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 la mise au point en fonction de l'ordre Z au lieu de mettre en cache la valeur. Cela permet à une seule source, WindowManager, de suivre l'ordre Z des activités.
ActivityStackSupervisor#getTopDisplayFocusedStack()
adopte une approche similaire pour les cas où la pile axée sur le haut du système doit être identifiée. Les piles sont parcourues de haut en bas pour trouver la première pile éligible.
InputDispatcher
peut désormais avoir plusieurs fenêtres sélectionnées (une par écran). Si un événement d'entrée est spécifique à un écran, il est distribué à la fenêtre sélectionnée sur l'écran correspondant. Sinon, il est envoyé à la fenêtre sélectionnée sur l'écran sélectionné, qui est l'écran avec lequel l'utilisateur a interagi le plus récemment.
Consultez 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 sélectionnées sont également suivies séparément.
Consultez les DisplayContent#mCurrentFocus
et DisplayContent#mFocusedApp
, ainsi que leurs utilisations respectives. Les méthodes associées de suivi et de mise à jour de la mise au point ont été déplacées de WindowManagerService
vers DisplayContent
.