A continuación, se proporcionan las actualizaciones realizadas en estas áreas específicas de la pantalla:
- Cómo cambiar el tamaño de las actividades y las pantallas
- Tamaños de pantalla y relaciones de aspecto
- Políticas de Display
- Configuración de la ventana de visualización
- Identificadores de anuncios gráficos estáticos
- Cómo usar más de dos pantallas
- Enfoque por pantalla
Cambia 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
. Estos son algunos de los problemas comunes que encuentran las apps cuando se cambia el tamaño de las actividades:
- Una actividad puede tener una configuración diferente de la app o de otro componente no visual. Un error común es leer las métricas de visualización desde el contexto de la app. Los valores que se muestran no se ajustarán a las métricas de á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 aquellas que se relacionan con la posición de la ventana), lo que puede interrumpir la entrada en el modo multiventana.
En Android 7 (y versiones posteriores), se puede configurar una app para que resizeableActivity=false
se ejecute siempre en modo de pantalla completa. 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 este atributo explícitamente en false
en el manifiesto no se deben iniciar 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 apps.
En Android 10, la plataforma aún evita que las actividades que no se pueden cambiar de tamaño entren en el modo de pantalla dividida, pero se pueden escalar temporalmente si la actividad declaró una orientación o una relación de aspecto fijas. De lo contrario, el tamaño de la actividad cambiará para ocupar toda la pantalla, como en Android 9 y versiones anteriores.
La implementación predeterminada aplica la siguiente política:
Cuando una actividad se declara incompatible con el modo multiventana a través del uso del atributo android:resizeableActivity
y cuando esa actividad cumple con una de las condiciones que se describen a continuación, cuando la configuración de la pantalla aplicada debe cambiar, la actividad y el proceso se guardan con la configuración original y se le proporciona al usuario una indicación visual para reiniciar el proceso de la app y 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 se 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, la configuración, el tamaño y la relación de aspecto de la actividad no cambian, pero se muestra la opción para reiniciar la actividad.
Cuando no se establece resizeableActivity
(o se establece como true
), la app admite por completo 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 visualización actual.
Si la actividad de SCM no puede ocupar toda la pantalla, se alinea en la parte superior 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 delgadas 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 más pequeños que los que requiere Android 9 y más bajos (un mínimo de 6.35 cm de ancho o alto, y un mínimo de 320 dp para smallestScreenWidth
), pero solo se pueden colocar allí las actividades que habilitan la compatibilidad con estas pantallas pequeñas.
Para habilitar esta función, las apps deben declarar un tamaño mínimo compatible que sea menor o igual al tamaño de pantalla 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 visualización 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 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 del marco 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 trasladó a DisplayRotation
.
Configuración de la ventana de la pantalla
En Android 10, se expandió la configuración de ventanas configurable por pantalla para incluir lo siguiente:
- Modo de ventana de visualización predeterminado
- Valores de sobreescalado
- 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 detalles, 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, como el archivo se almacena en /data
, es posible que se necesite una 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. uniqueId
se debe propagar 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 la 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 tiene varias pantallas disponibles desde el inicio, se les pueden asignar identificadores diferentes, según el momento. 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 que es estable en 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 forma estática. Por ejemplo, se usa en DisplayWindowSettings
). DisplayAddress.Network
contiene la dirección MAC y se puede crear con DisplayAddress#fromMacAddress()
.
Estas incorporaciones permiten que los fabricantes de dispositivos identifiquen pantallas en configuraciones estáticas de varias pantallas y configuren diferentes parámetros de configuración y funciones del sistema con identificadores de pantallas estáticas, 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 información del fabricante o el modelo del EDID para generar los IDs de pantalla estables de 64 bits expuestos al framework. Si este método no es compatible o muestra errores, SurfaceFlinger recurre al modo MD heredado, en el que DisplayInfo#address
es nulo y DisplayInfo#uniqueId
está codificado de forma fija, como se describió anteriormente.
Para verificar que esta función sea compatible, ejecuta lo siguiente:
$ 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"
Cómo usar 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
para 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 quita 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
(antes conocidas comoDisplay.TYPE_BUILT_IN
yDisplay.TYPE_HDMI
, respectivamente) según su tipo de conexión.
Implementación
En Android 9 y versiones anteriores, las pantallas se identifican con IDs de 32 bits, en los que 0 es la pantalla interna, 1 es la pantalla externa, [2, INT32_MAX]
son pantallas virtuales de HWC y -1 representa una pantalla no válida o una pantalla virtual que no es de 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 HWC. Sin compatibilidad con HWC, SurfaceFlinger recurre al comportamiento heredado con un máximo de dos pantallas físicas.
Enfoque por pantalla
Para admitir varias fuentes de entrada que se orienten a pantallas individuales al mismo tiempo, Android 10 se puede configurar para admitir varias ventanas enfocadas, como máximo una por pantalla. Esto está diseñado solo para tipos de dispositivos especiales cuando 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 no habilitar esta función para dispositivos normales, incluidos los dispositivos multipantalla o los que se usan para experiencias similares a las de computadoras 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 pantalla 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 en el que se enfoquen dos ventanas del mismo proceso, el sistema solo enfocará la ventana que esté más alta en el orden en Z. Esta restricción se quita para las apps que se orientan a Android 10, en cuyo caso se espera que admitan que se enfoquen varias ventanas de forma simultánea.
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 en función del orden en Z en lugar de almacenar en caché el valor. Esto se hace para que solo una fuente, WindowManager, deba hacer un seguimiento del orden en Z de las actividades.
ActivityStackSupervisor#getTopDisplayFocusedStack()
adopta un enfoque similar para los casos en los que se debe identificar la pila enfocada más alta del sistema. Las pilas se recorren de arriba abajo en busca de la primera pila apta.
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 seguimiento y actualización de enfoque relacionados se movieron de WindowManagerService
a DisplayContent
.