Les modifications apportées à ces zones spécifiques à l'affichage sont indiquées ci-dessous:
- Redimensionner les activités et les écrans
- Tailles et formats d'écran
- Règles concernant les annonces display
- Paramètres de la fenêtre d'affichage
- Identifiants d'écran statiques
- Utiliser plus de deux écrans
- Mise au point par écran
Redimensionner des activités et des écrans
Pour indiquer qu'une application n'est peut-être 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 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 UI 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 (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), 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 d'être affichées en mode fractionné. Si l'utilisateur tente d'appeler une activité non redimensionnable à partir du lanceur 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 non liés aux activités.
- 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 temporairement mises à l'échelle si l'activité a déclaré une orientation ou un format fixes. Dans le cas contraire, l'activité se redimensionne pour occuper l'intégralité de l'écran, comme dans Android 9 et versions antérieures.
L'implémentation par défaut applique la stratégie suivante:
Lorsqu'activité déclarée incompatible avec le multifenêtre à l'aide 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 de l'application afin d'utiliser la configuration d'écran mise à jour.
- Orientation fixe via l'application de
android:screenOrientation
- L'application a un format maximal ou minimal par défaut en ciblant le niveau d'API ou en déclarant le format explicitement
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 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 fixée dans la configuration de forçage demandée. L'activité n'est donc plus dépendante 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 et formats d'affichage
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 les ApplicationInfo#minAspectRatio
de l'écran qu'elles peuvent 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 activer cette fonctionnalité 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, 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 mise à l'échelle forcé
- Mode de suppression de contenu (lorsque l'affichage est supprimé)
- Compatibilité avec les décorations système et 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 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.
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. 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. 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 statiques.
Les paramètres sont conservés dans le répertoire /data
pour des raisons historiques. À l'origine, elles étaient utilisées pour 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 é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 a ajouté et supprimé le même écran, un ID différent a été généré.
Si un appareil dispose de plusieurs écrans disponibles au démarrage, des identifiants différents peuvent être attribués aux écrans, en fonction du calendrier. Bien qu'Android 9 (et versions antérieures) inclue DisplayInfo#uniqueId
, il ne contenait pas suffisamment d'informations pour différencier les écrans, car les écrans physiques étaient 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 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. 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 de manière statique les écrans. (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 des 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 qu'à être utilisées dans system_server
.
Étant donné un ID d'écran 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'écran, 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 une erreur se produit, SurfaceFlinger revient au mode MD précédent, 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
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'écran stables, ce qui lui permet de gérer un nombre arbitraire d'écrans physiques. Pour en savoir plus, consultez la section Identifiants d'affichage 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 faire des suppositions 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 é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 correctement catégorisés en tant que
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.
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, une par écran au maximum. Cette fonctionnalité n'est destinée qu'à 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 ou appareils d'entrée, comme 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.
Imaginons un utilisateur qui saisit des informations sécurisées dans un champ de saisie de texte, par exemple pour se connecter à une application bancaire ou saisir 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 sélectionnées et affichent toutes deux un indicateur d'entrée actif (curseur clignotant).
Toutefois, comme les entrées d'un clavier (matériel ou logiciel) ne sont saisies que dans l'activité la plus élevée (l'application lancée le plus récemment), en créant un écran virtuel masqué, une application malveillante peut récupérer les entrées utilisateur, même lorsque vous utilisez un clavier logiciel 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 ou version antérieure, une seule fenêtre du système est active à la fois.
Solution:Dans les rares cas où deux fenêtres du même processus sont sélectionnées, le système ne sélectionne que la fenêtre qui se trouve plus haut dans l'ordre Z. Cette restriction est supprimée pour les applications qui ciblent Android 10, à partir de quoi elles devraient pouvoir prendre en charge plusieurs fenêtres en mode plein écran 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 Z des activités.
ActivityStackSupervisor#getTopDisplayFocusedStack()
adopte une approche similaire dans les cas où la pile de premier plan 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 désormais avoir plusieurs fenêtres sélectionnées (une par écran). Si un événement de saisie est spécifique à l'écran, il est distribué à la fenêtre active sur l'écran correspondant. Sinon, il est distribué à la fenêtre sélectionnée sur l'écran sélectionné, qui correspond à l'écran avec lequel l'utilisateur a interagi le plus récemment.
Consultez les InputDispatcher::mFocusedWindowHandlesByDisplay
et l'InputDispatcher::setFocusedDisplay()
. Les applications en mode plein écran 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 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
.