A continuación, se proporcionan las actualizaciones que se realizan en estas áreas específicas de la pantalla:
- Cómo cambiar el tamaño de las actividades y las pantallas
- Tamaños y relaciones de aspecto de la pantalla
- Políticas de Display
- Configuración de la ventana de visualización
- Identificadores de anuncios gráficos estáticos
- Uso de más de dos pantallas
- Enfoque por pantalla
Cómo cambiar el tamaño de las actividades y las pantallas
Para indicar que una app puede no admitir el modo multiventana o el cambio de tamaño, las actividades usan el atributo resizeableActivity=false
. Entre los problemas comunes que encuentran las apps cuando se cambia el tamaño de las actividades, se incluyen los siguientes:
- Una actividad puede tener una configuración diferente a la de la app o algún otro componente no visual. Un error común es leer las métricas de visualización desde el contexto de la app. Los valores mostrados no se ajustarán a las métricas del área visible en las que se muestra una actividad.
- Es posible que una actividad no controle el cambio de tamaño y falle, muestre una IU distorsionada o pierda el estado debido a que se reinicia sin guardar el estado de la instancia.
- Una app puede intentar usar coordenadas de entrada absolutas (en lugar de las relativas a la posición de la ventana), lo que puede romper la entrada en el modo multiventana.
En Android 7 (y versiones posteriores), se puede configurar una app para que se ejecute siempre en modo de pantalla completa. Para ello, se debe establecer resizeableActivity=false
. En este caso, la plataforma evita que las actividades que no se pueden cambiar de tamaño entren en pantalla dividida. Si el usuario intenta invocar una actividad que no se puede cambiar de tamaño desde el selector mientras ya está en el modo de pantalla dividida, la plataforma sale del modo de pantalla dividida y, luego, inicia la actividad que no se puede cambiar de tamaño en el modo de pantalla completa.
Las apps que establecen explícitamente este atributo como false
en el manifiesto no deben iniciarse en el modo multiventana, a menos que se aplique el modo de compatibilidad:
- Se aplica la misma configuración al proceso, que contiene todas las actividades y los componentes que no son de actividad.
- La configuración aplicada cumple con los requisitos del CDD para pantallas compatibles con la app.
En Android 10, la plataforma aún evita que las actividades que no pueden cambiar de tamaño pasen al modo de pantalla dividida, pero se pueden escalar de manera temporal si la actividad declaró una orientación o relación de aspecto fijas. De lo contrario, la actividad cambia de tamaño para llenar toda la pantalla, como en Android 9 y versiones anteriores.
La implementación predeterminada aplica la siguiente política:
Cuando una actividad se declaró incompatible con el modo multiventana usando el atributo android:resizeableActivity
y cuando esta cumple con una de las condiciones que se describen a continuación, cuando debe cambiar la configuración de pantalla aplicada, la actividad y el proceso se guardan con la configuración original, y se le proporciona al usuario una indicación para reiniciar el proceso de la app a fin de usar la configuración de pantalla actualizada.
- Tiene una orientación fija a través de la aplicación de
android:screenOrientation
. - La app tiene una relación de aspecto máxima o mínima predeterminada porque se orienta al nivel de API o declara la relación de aspecto de forma explícita.
En esta figura, se muestra una actividad que no puede cambiar de tamaño con una relación de aspecto declarada. Cuando se pliega el dispositivo, la ventana se reduce para adaptarse al área y, al mismo tiempo, se mantiene la relación de aspecto con el formato letterbox adecuado. Además, se le proporciona al usuario una opción para reiniciar la actividad cada vez que se cambia el área de visualización de la actividad.
Cuando se despliega el dispositivo, no cambian la configuración, el tamaño ni la relación de aspecto de la actividad, pero se muestra la opción para reiniciarla.
Cuando no se establece resizeableActivity
(o se establece en true
), la app es totalmente compatible con el cambio de tamaño.
Implementación
Una actividad que no puede cambiar de tamaño con orientación o relación de aspecto fijas se denomina modo de compatibilidad de tamaño (SCM) en el código. La condición se define en ActivityRecord#shouldUseSizeCompatMode()
. Cuando se inicia una actividad de SCM, la configuración relacionada con la pantalla (como el tamaño o la densidad) se fija en la configuración de anulación solicitada, por lo que la actividad ya no depende de la configuración de pantalla actual.
Si la actividad de SCM no puede ocupar toda la pantalla, se alinea hacia arriba y se centra horizontalmente. AppWindowToken#calculateCompatBoundsTransformation()
calcula los límites de la actividad.
Cuando una actividad de SCM usa una configuración de pantalla diferente a la de su contenedor (por ejemplo, se cambia el tamaño de la pantalla o se mueve la actividad a otra pantalla), ActivityRecord#inSizeCompatMode()
es verdadero y SizeCompatModeActivityController
(en la IU del sistema) recibe la devolución de llamada para mostrar el botón de reinicio del proceso.
Tamaños de pantalla y relaciones de aspecto
Android 10 admite nuevas relaciones de aspecto, desde relaciones altas de pantallas largas y finas hasta relaciones de 1:1. Las apps pueden definir ApplicationInfo#maxAspectRatio
y el ApplicationInfo#minAspectRatio
de la pantalla que pueden controlar.
Figura 1: Ejemplos de relaciones de aspecto de apps compatibles con Android 10
Las implementaciones de dispositivos pueden tener pantallas secundarias con tamaños y resoluciones inferiores a los que requiere Android 9, e inferiores (un mínimo de 2.5 pulgadas de ancho o alto, y un mínimo de 320 DP para smallestScreenWidth
), pero solo se pueden colocar en ellas las actividades que admitan estas pantallas pequeñas.
Para ello, las apps pueden declarar un tamaño mínimo admitido que sea menor o igual que el tamaño de visualización objetivo. Para ello, usa los atributos de diseño de actividad android:minHeight
y android:minWidth
en AndroidManifest.
Políticas de Display
Android 10 separa y mueve ciertas políticas de pantalla de la implementación predeterminada de WindowManagerPolicy
en PhoneWindowManager
a clases por pantalla, como las siguientes:
- Estado y rotación de la pantalla
- Algunas teclas y el seguimiento de eventos de movimiento
- IU del sistema y ventanas de decoración
En Android 9 (y versiones anteriores), la clase PhoneWindowManager
controlaba las políticas de visualización, el estado y la configuración, la rotación, el seguimiento de marcos de la ventana de decoración y mucho más. Android 10 mueve la mayor parte de esto a la clase DisplayPolicy
, excepto el seguimiento de rotación, que se movió a DisplayRotation
.
Configuración de la ventana de visualización
En Android 10, se amplió la configuración del sistema de ventanas configurable por pantalla para incluir lo siguiente:
- Modo de ventana de visualización predeterminado
- Sobrebarrido de valores
- Rotación del usuario y modo de rotación
- Tamaño, densidad y modo de escalamiento forzados
- Modo de eliminación de contenido (cuando se quita la pantalla)
- Compatibilidad con decoraciones del sistema y IME
La clase DisplayWindowSettings
contiene la configuración de estas opciones. Se conservan en el disco en la partición /data
en display_settings.xml
cada vez que se cambia un parámetro de configuración. Para obtener más información, consulta DisplayWindowSettings.AtomicFileStorage
y DisplayWindowSettings#writeSettings()
. Los fabricantes de dispositivos pueden proporcionar valores predeterminados en display_settings.xml
para la configuración de sus dispositivos. Sin embargo, debido a que el archivo se almacena en /data
, es posible que se necesite lógica adicional para restablecerlo si se borra con una limpieza.
De forma predeterminada, Android 10 usa DisplayInfo#uniqueId
como identificador de una pantalla cuando conserva la configuración. Se debe propagar uniqueId
para todas las pantallas. Además, es estable para pantallas físicas y de red. También es posible usar el puerto de una pantalla física como identificador, que se puede configurar en DisplayWindowSettings#mIdentifier
. En cada operación de escritura, se escriben todos los parámetros de configuración para que sea seguro actualizar la clave que se usa para una entrada de visualización en el almacenamiento. Para obtener más información, consulta Identificadores de pantallas estáticas.
La configuración se conserva en el directorio /data
por motivos históricos. Originalmente, se usaban para conservar la configuración establecida por el usuario, como la rotación de pantalla.
Identificadores de visualización estáticos
Android 9 (y versiones anteriores) no proporcionaba identificadores estables para las pantallas en el framework. Cuando se agregó una pantalla al sistema, se generó Display#mDisplayId
o DisplayInfo#displayId
para esa pantalla incrementando un contador estático. Si el sistema agregó y quitó la misma pantalla, se generó un ID diferente.
Si un dispositivo tuviera varias pantallas disponibles desde el inicio, se les podrían asignar diferentes identificadores, según el tiempo. Si bien Android 9 (y versiones anteriores) incluían DisplayInfo#uniqueId
, no contenían suficiente información para diferenciar entre pantallas, ya que las pantallas físicas se identificaban como local:0
o local:1
para representar la pantalla integrada y la externa.
Android 10 cambia DisplayInfo#uniqueId
para agregar un identificador estable y diferenciar entre pantallas locales, de red y virtuales.
Tipo de pantalla | Formato |
---|---|
Local | local:<stable-id> |
Red | network:<mac-address> |
Virtual | virtual:<package-name-and-name> |
Además de las actualizaciones de uniqueId
, DisplayInfo.address
contiene DisplayAddress
, un identificador de pantalla estable después de todos los reinicios. En Android 10, DisplayAddress
admite pantallas físicas y de red. DisplayAddress.Physical
contiene un ID de visualización estable (igual que en uniqueId
) y se puede crear con DisplayAddress#fromPhysicalDisplayId()
.
Android 10 también proporciona un método conveniente para obtener información del puerto (Physical#getPort()
). Este método se puede usar en el framework para identificar pantallas de manera estática. Por ejemplo, se usa en DisplayWindowSettings
). DisplayAddress.Network
contiene la dirección MAC y se puede crear con DisplayAddress#fromMacAddress()
.
Estas adiciones permiten a los fabricantes de dispositivos identificar pantallas en configuraciones estáticas de varias pantallas y establecer diferentes funciones y parámetros de configuración del sistema mediante identificadores de pantalla estáticos, como puertos para pantallas físicas. Estos métodos están ocultos y solo se deben usar dentro de system_server
.
Dado un ID de pantalla HWC (que puede ser opaco y no siempre estable), este método muestra el número de puerto de 8 bits (específico de la plataforma) que identifica un conector físico para la salida de la pantalla, así como el blob EDID de la pantalla.
SurfaceFlinger extrae la información del fabricante o del modelo del EDID para generar los IDs de pantalla estables de 64 bits expuestos al framework. Si no se admite este método o se produce un error, SurfaceFlinger recurre al modo MD heredado, en el que DisplayInfo#address
es nulo y DisplayInfo#uniqueId
está hard-coded, como se describió anteriormente.
Para verificar que esta función sea compatible, ejecuta el siguiente comando:
$ 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"
Usa más de dos pantallas
En Android 9 (y versiones anteriores), SurfaceFlinger y DisplayManagerService
asumían la existencia de, como máximo, dos pantallas físicas con los IDs 0 y 1 codificados.
A partir de Android 10, SurfaceFlinger puede aprovechar una API de Hardware Composer (HWC) para generar IDs de pantalla estables, lo que le permite administrar una cantidad arbitraria de pantallas físicas. Para obtener más información, consulta Identificadores de visualización estáticos.
El framework puede buscar el token IBinder
de una pantalla física a través de SurfaceControl#getPhysicalDisplayToken
después de obtener el ID de pantalla de 64 bits de SurfaceControl#getPhysicalDisplayIds
o de un evento de hotplug DisplayEventReceiver
.
En Android 10 (y versiones anteriores), la pantalla interna principal es TYPE_INTERNAL
y todas las pantallas secundarias se marcan como TYPE_EXTERNAL
, independientemente del tipo de conexión. Por lo tanto, las pantallas internas adicionales se consideran externas.
Como solución alternativa, el código específico del dispositivo puede hacer suposiciones sobre DisplayAddress.Physical#getPort
si se conoce el HWC y la lógica de asignación de puertos es predecible.
Esta limitación se quitará en Android 11 (y versiones posteriores).
- En Android 11, la primera pantalla que se informa durante el inicio es la pantalla principal. El tipo de conexión (interna o externa) es irrelevante. Sin embargo, sigue siendo cierto que la pantalla principal no se puede desconectar y, en la práctica, debe ser una pantalla interna. Ten en cuenta que algunos teléfonos plegables tienen varias pantallas internas.
- Las pantallas secundarias se clasifican correctamente como
Display.TYPE_INTERNAL
oDisplay.TYPE_EXTERNAL
(anteriormente conocidas comoDisplay.TYPE_BUILT_IN
yDisplay.TYPE_HDMI
, respectivamente) según el tipo de conexión.
Implementación
En Android 9 y versiones anteriores, las pantallas se identifican mediante IDs de 32 bits, en los que 0 es la pantalla interna, 1 es la pantalla externa, [2, INT32_MAX]
son pantallas virtuales HWC y -1 representa una pantalla no válida o sin HWC.
A partir de Android 10, las pantallas tienen IDs estables y persistentes, lo que permite que SurfaceFlinger y DisplayManagerService
hagan un seguimiento de más de dos pantallas y reconozcan pantallas vistas anteriormente. Si el HWC admite IComposerClient.getDisplayIdentificationData
y proporciona datos de identificación de la pantalla, SurfaceFlinger analiza la estructura EDID y asigna IDs de pantalla estables de 64 bits para pantallas físicas y virtuales de HWC. Los IDs se expresan con un tipo de opción, en el que el valor nulo representa una pantalla no válida o una pantalla virtual que no es de HWC. Sin compatibilidad con HWC, SurfaceFlinger recurre al comportamiento heredado con un máximo de dos pantallas físicas.
Enfoque por pantalla
A fin de admitir varias fuentes de entrada que se orientan a pantallas individuales al mismo tiempo, se puede configurar Android 10 para que admita varias ventanas enfocadas, una por pantalla como máximo. Esto está diseñado únicamente para tipos especiales de dispositivos en los que varios usuarios interactúan con el mismo dispositivo al mismo tiempo y usan diferentes métodos o dispositivos de entrada, como Android Automotive.
Se recomienda que esta función no esté habilitada en dispositivos normales, incluidos los multipantalla o de escritorio. Esto se debe principalmente a una preocupación de seguridad que puede hacer que los usuarios se pregunten qué ventana tiene el enfoque de entrada.
Imagina que el usuario ingresa información segura en un campo de entrada de texto, quizás accede a una app bancaria o ingresa texto que contiene información sensible. Una app maliciosa podría crear una visualización virtual fuera de la pantalla con la que ejecutar una actividad, también con un campo de entrada de texto. Las actividades legítimas y maliciosas tienen enfoque y ambas muestran un indicador de entrada activo (cursor parpadeante).
Sin embargo, como la entrada de un teclado (hardware o software) se ingresa solo en la actividad más alta (la app que se inició más recientemente), si se crea una pantalla virtual oculta, una app maliciosa podría capturar la entrada del usuario, incluso cuando se usa un teclado en pantalla en la pantalla principal del dispositivo.
Usa com.android.internal.R.bool.config_perDisplayFocusEnabled
para establecer el enfoque por pantalla.
Compatibilidad
Problema: En Android 9 y versiones anteriores, como máximo, una ventana del sistema tiene el enfoque a la vez.
Solución: En el caso poco frecuente de que dos ventanas del mismo proceso estén enfocadas, el sistema solo proporciona el enfoque a la ventana que está más arriba en el orden en Z. Esta restricción se quita para las apps orientadas a Android 10, en cuyo caso se espera que puedan admitir varias ventanas en las que se enfoquen a la vez.
Implementación
WindowManagerService#mPerDisplayFocusEnabled
controla la disponibilidad de esta función. En ActivityManager
, ahora se usa ActivityDisplay#getFocusedStack()
en lugar del seguimiento global en una variable. ActivityDisplay#getFocusedStack()
determina el enfoque según el orden en Z, en lugar de almacenar el valor en caché. Esto es para que solo una fuente, WindowManager, necesite realizar un seguimiento del orden en Z de las actividades.
ActivityStackSupervisor#getTopDisplayFocusedStack()
adopta un enfoque similar para los casos en los que debe identificarse la pila enfocada en la parte superior del sistema. Las pilas se recorren de arriba abajo, en busca de la primera pila elegible.
InputDispatcher
ahora puede tener varias ventanas enfocadas (una por pantalla). Si un evento de entrada es específico de la pantalla, se envía a la ventana enfocada en la pantalla correspondiente. De lo contrario, se envía a la ventana enfocada en la pantalla enfocada, que es la pantalla con la que el usuario interactuó más recientemente.
Consulta InputDispatcher::mFocusedWindowHandlesByDisplay
y InputDispatcher::setFocusedDisplay()
. Las apps enfocadas también se actualizan por separado en InputManagerService a través de NativeInputManager::setFocusedApplication()
.
En WindowManager
, también se realiza un seguimiento de las ventanas enfocadas por separado.
Consulta DisplayContent#mCurrentFocus
y DisplayContent#mFocusedApp
, y sus respectivos usos. Los métodos de actualización y seguimiento de enfoque relacionados se movieron de WindowManagerService
a DisplayContent
.