Les modifications apportées à ces zones spécifiques à l'affichage sont indiquées ci-dessous :
- Redimensionnement des activités et de l'écran
- Tailles et formats d'écran
- Règles d'affichage
- Paramètres de la fenêtre d'affichage
- Identifiants d'affichage statique
- Utiliser plus de deux écrans
- Mise au point par écran
Redimensionner les activités et les écrans
Pour indiquer qu'une application peut ne pas prendre en charge 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 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 sont pas ajustées en fonction des métriques de zone visible dans lesquelles une activité est affichée.
- Une activité peut ne pas gérer le redimensionnement et le plantage, afficher une interface utilisateur déformée ou perdre un état lors d'un redémarrage sans enregistrer l'état de l'instance.
- Une application peut tenter d'utiliser des coordonnées d'entrée absolues (plutôt que celles relatives à la position de la fenêtre), ce qui peut endommager la saisie en mode multifenêtre.
Sous Android 7 (et versions ultérieures), vous pouvez définir une application resizeableActivity=false
pour qu'elle s'exécute toujours en mode plein écran. Dans ce cas, la plate-forme empêche les activités non redimensionnables de passer à l'écran partagé. Si l'utilisateur tente d'appeler une activité non redimensionnable à partir du 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 les composants hors activité.
- La configuration appliquée répond aux exigences de la 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. Si ce n'est pas le cas, l'activité est redimensionnée pour remplir tout l'écran, comme sous Android 9 ou version antérieure.
L'implémentation par défaut applique la stratégie suivante :
Lorsqu'une activité déclarée comme incompatible avec le mode multifenêtre via l'utilisation de l'attribut android:resizeableActivity
et qu'elle répond à l'une des conditions décrites ci-dessous, 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 dispose d'une affordance pour relancer le processus d'application afin d'utiliser la configuration d'écran mise à jour.
- Est à orientation fixe via l'application de
android:screenOrientation
- L'application utilise 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 vous pliez l'appareil, la fenêtre est réduite pour s'adapter à la zone tout en conservant le format à l'aide du format letterbox 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 vous dépliez l'appareil, 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 s'il 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 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 (comme 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é déplacée vers un autre écran), ActivityRecord#inSizeCompatMode()
est défini sur "true" et SizeCompatModeActivityController
(dans l'UI du système) reçoit le rappel pour afficher le bouton de redémarrage du processus.
Tailles et formats d'affichage
Android 10 est compatible avec de nouveaux formats, allant des écrans longs et fins aux formats 1:1. Les applications peuvent définir ApplicationInfo#maxAspectRatio
et le ApplicationInfo#minAspectRatio
de l'écran qu'elles sont capables de gérer.
Figure 1 : Exemples de formats d'application compatibles avec Android 10
Les implémentations d'appareils peuvent comporter des écrans secondaires dont les tailles et les résolutions sont inférieures à celles requises par Android 9 (largeur ou hauteur minimale de 6,35 cm, 320 dp minimum pour smallestScreenWidth
), mais seules les activités qui acceptent ces petits écrans peuvent y être placées.
Les applications peuvent l'activer en déclarant une taille minimale prise en charge 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, par exemple :
- État et rotation de l'écran
- Certaines touches et le suivi des événements de mouvement
- UI du système et fenêtres de décoration
Sous 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 fenêtre de décoration, etc. Android 10 transfère 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 les éléments suivants :
- 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 scaling imposés
- Mode de suppression de contenu (lorsque l'écran est retiré)
- Compatibilité avec les décorations système et l'IME
La classe DisplayWindowSettings
contient des paramètres pour ces options. Ils sont conservés sur le disque de la partition /data
dans 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 leur appareil. Toutefois, comme le fichier est stocké dans /data
, une logique supplémentaire peut être nécessaire pour le restaurer s'il est effacé par un effacement de données.
Par défaut, Android 10 utilise DisplayInfo#uniqueId
comme identifiant d'un écran lors de la persistance des paramètres. uniqueId
doit être renseigné pour tous les écrans. En outre, 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. Il est donc possible de mettre à jour la clé utilisée pour une entrée d'affichage dans l'espace de stockage. Pour en savoir plus, consultez la section Identifiants d'affichage statique.
Les paramètres sont conservés dans le répertoire /data
pour des raisons historiques. À l'origine, ils servaient à conserver les paramètres définis par l'utilisateur, tels que la rotation de l'écran.
Identifiants d'affichage statiques
Android 9 (et versions antérieures) ne fournissait pas d'identifiants stables pour les écrans 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 a ajouté et supprimé le même écran, un ID différent est généré.
Si plusieurs écrans sont disponibles au démarrage sur un appareil, différents identifiants peuvent lui être attribués en fonction du temps nécessaire. Bien qu'Android 9 (et versions antérieures) incluait DisplayInfo#uniqueId
, il ne contenait pas suffisamment d'informations pour différencier les écrans, car les écrans physiques étaient identifiés en tant que 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 différencier 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. Sous Android 10, DisplayAddress
est compatible avec 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 de port (Physical#getPort()
). Cette méthode peut être utilisée dans le framework pour identifier des écrans de manière statique. (par exemple, 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-écran statiques, et de configurer différents paramètres et fonctionnalités système à l'aide d'identifiants d'écran statiques, tels que les ports d'écrans physiques. Ces méthodes sont masquées et ne sont destinées à être utilisées que dans system_server
.
Avec 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 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 compatible 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 compatible, 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
Sous Android 9 (et versions antérieures), SurfaceFlinger et DisplayManagerService
supposaient l'existence d'au plus deux écrans physiques avec les ID 0 et 1 codés en dur.
À partir d'Android 10, SurfaceFlinger peut 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 la section Identifiants d'écran statiques.
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 hotplug DisplayEventReceiver
.
Sous 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 émettre des hypothèses sur DisplayAddress.Physical#getPort
si le HWC est connu et que la logique d'allocation de ports est prévisible.
Cette limitation a été supprimée dans Android 11 (et versions ultérieures).
- Sous Android 11, le premier écran signalé lors du 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é, ce qui implique qu'il doit s'agir d'un écran interne. Notez que certains téléphones pliables disposent de plusieurs écrans internes.
- Les écrans secondaires sont classés correctement comme
Display.TYPE_INTERNAL
ouDisplay.TYPE_EXTERNAL
(anciennementDisplay.TYPE_BUILT_IN
etDisplay.TYPE_HDMI
, respectivement) en fonction de leur type de connexion.
Implémentation
Sous Android 9 et versions antérieures, les écrans sont identifiés par des ID 32 bits, où 0 correspond à l'écran interne, 1 à l'écran externe, [2, INT32_MAX]
à des écrans virtuels HWC et -1 à un écran non valide ou à un écran virtuel non HWC.
À partir d'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 d'écran, SurfaceFlinger analyse la structure EDID et alloue des ID d'écran 64 bits stables pour les écrans physiques et virtuels du 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 à l'ancien comportement avec au maximum deux écrans physiques.
Sélection 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, une par écran au maximum. Cela n'est destiné qu'aux types d'appareils spéciaux lorsque plusieurs utilisateurs interagissent avec le même appareil en même temps et utilisent différents modes d'entrée ou appareils, tels qu'Android Automotive.
Nous vous recommandons vivement de ne pas activer cette fonctionnalité pour les appareils standards, y compris les appareils multi-écrans ou ceux utilisés pour des expériences semblables à celles d'un ordinateur. Cela est principalement dû à un problème de sécurité qui peut amener les utilisateurs à se demander quelle fenêtre est sélectionnée.
Imaginez que l'utilisateur saisisse 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 peut créer un écran virtuel hors écran pour exécuter une activité, avec un champ de saisie de texte. Les activités légitimes et malveillantes sont ciblées et affichent toutes deux un indicateur de saisie actif (curseur clignotant).
Toutefois, étant donné que les entrées effectuées à partir d'un clavier (matériel ou logiciel) ne sont saisies que dans l'activité de niveau supérieur (l'application la plus récemment lancée), en créant un écran virtuel masqué, une application malveillante peut récupérer les entrées utilisateur, même lorsqu'elles utilisent un clavier virtuel sur l'écran principal de l'appareil.
Utilisez com.android.internal.R.bool.config_perDisplayFocusEnabled
pour définir la sélection par écran.
Compatibilité
Problème:Sous Android 9 et versions antérieures, une seule 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 ne sélectionne que la fenêtre la plus haute dans l'ordre Z. Cette restriction est supprimée pour les applications qui ciblent Android 10. À ce stade, il est attendu qu'elles puissent 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 sélection en fonction de l'ordre Z au lieu de mettre en cache la valeur. Ainsi, une seule source, WindowManager, doit suivre l'ordre de plan des activités.
ActivityStackSupervisor#getTopDisplayFocusedStack()
adopte une approche similaire dans les cas où la pile de premier plan du système doit être identifiée. Les piles sont balayées de haut en bas, à la recherche de 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 affichage, il est envoyé à la fenêtre sélectionnée de l'écran correspondant. Sinon, elle est envoyée à la fenêtre sélectionnée de l'écran sélectionné, qui correspond à l'écran avec lequel l'utilisateur a interagi le plus récemment.
Consultez InputDispatcher::mFocusedWindowHandlesByDisplay
et InputDispatcher::setFocusedDisplay()
. Les applications en mode focus sont également mises à jour séparément dans InputManagerService via NativeInputManager::setFocusedApplication()
.
Dans WindowManager
, les fenêtres sélectionnées font également l'objet d'un suivi séparé.
Consultez DisplayContent#mCurrentFocus
et DisplayContent#mFocusedApp
, ainsi que les utilisations respectives. Les méthodes de suivi et de mise à jour de la sélection associées ont été déplacées de WindowManagerService
vers DisplayContent
.