A continuación, se indican 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 pantalla 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 podría 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 de la app o de otro componente no visual. Un error común es leer las métricas de visualización del contexto de la app. Los valores devueltos 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 un reinicio sin guardar el estado de la instancia.
- Es posible que una app intente usar coordenadas de entrada absolutas (en lugar de las relativas a la posición de la ventana), lo que podría interrumpir la entrada en el modo multiventana.
En Android 7 (y versiones posteriores), se puede configurar una app resizeableActivity=false
para que siempre se ejecute en modo de pantalla completa. En este caso, la plataforma impide que las actividades no redimensionables 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 lanza la actividad que no se puede cambiar de tamaño en el modo de pantalla completa.
Las apps que establecen explícitamente este atributo 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 todos los componentes de actividad y no actividad.
- La configuración aplicada cumple con los requisitos del CDD para pantallas compatibles con apps.
En Android 10, la plataforma sigue impidiendo 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, 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 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, entonces, 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 opción para reiniciar el proceso de la app y usar la configuración de pantalla actualizada.
- La orientación fija se logra 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 a un 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 ajustarse al área y, al mismo tiempo, se mantiene la relación de aspecto con el letterboxing 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 reiniciar la actividad.
Cuando resizeableActivity
no está establecido (o se establece en true
), la app admite el cambio de tamaño por completo.
Implementación
En el código, una actividad que no se puede cambiar de tamaño y que tiene una orientación o una relación de aspecto fijas se denomina modo de compatibilidad de tamaño (SCM). 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 llenar 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 1:1. Las apps pueden definir ApplicationInfo#maxAspectRatio
y el ApplicationInfo#minAspectRatio
de la pantalla que pueden controlar.
Figura 1: Ejemplo 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 versiones anteriores (mínimo de 2.5 pulgadas de ancho o alto, mínimo de 320 DP para smallestScreenWidth
), pero solo se pueden colocar allí las actividades que habiliten la compatibilidad con estas pantallas pequeñas.
Las apps pueden habilitar esta opción declarando un tamaño mínimo admitido que sea menor o igual que el tamaño de la pantalla de destino. Para ello, usa los atributos de diseño de actividad android:minHeight
y android:minWidth
en AndroidManifest.
Políticas de visualización
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
- Seguimiento de algunos eventos de teclas y movimiento
- IU del sistema y ventanas de decoración
En Android 9 (y versiones anteriores), la clase PhoneWindowManager
controlaba las políticas, el estado y la configuración de la pantalla, 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 la rotación, que se movió a DisplayRotation
.
Configuración de la ventana de visualización
En Android 10, se expandió el parámetro de configuración de ventanas por pantalla configurable para incluir lo siguiente:
- Modo de ventanas de visualización predeterminado
- Valores de sobreescaneo
- Rotación de usuarios 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 IME y decoraciones del sistema
La clase DisplayWindowSettings
contiene la configuración de estas opciones. Se conservan en el disco en la partición /data
de 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 lógica adicional para restablecerlo si se borra con un restablecimiento.
De forma predeterminada, Android 10 usa DisplayInfo#uniqueId
como identificador de una pantalla cuando se conservan los parámetros de configuración. uniqueId
debe completarse para todas las pantallas. Además, es estable para las 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 escritura, se escriben todos los parámetros de configuración, por lo que es seguro actualizar la clave que se usa para una entrada de pantalla en el almacenamiento. Para obtener más información, consulta Identificadores de pantalla estáticos.
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 pantalla 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 tenía varias pantallas disponibles desde el inicio, se les podían asignar diferentes identificadores, según el momento. Si bien Android 9 (y versiones anteriores) incluía DisplayInfo#uniqueId
, no contenía 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 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 pantalla estáticos, como puertos para pantallas físicas. Estos métodos están ocultos y solo se pueden usar dentro de system_server
.
Dado un ID de pantalla de HWC (que puede ser opaco y no siempre estable), este método devuelve 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 de EDID de la pantalla.
SurfaceFlinger extrae información del fabricante o del modelo del EDID para generar los IDs de pantalla estables de 64 bits que se exponen al framework. Si este método no es compatible o genera un error, SurfaceFlinger recurre al modo MD heredado, en el que DisplayInfo#address
es nulo y DisplayInfo#uniqueId
está codificado de forma rígida, como se describió anteriormente.
Para verificar que se admita esta función, 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"
Usar más de dos pantallas
En Android 9 (y versiones anteriores), SurfaceFlinger y DisplayManagerService
suponían la existencia de, como máximo, dos pantallas físicas con IDs codificados de forma rígida 0 y 1.
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 pantalla 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 conexión en caliente 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 quitó 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) no es relevante. Sin embargo, sigue siendo cierto que la pantalla principal no se puede desconectar y, por lo tanto, debe ser una pantalla interna en la práctica. Ten en cuenta que algunos teléfonos plegables tienen varias pantallas internas.
- Las pantallas secundarias se categorizan 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 reciben IDs estables y persistentes, lo que permite que SurfaceFlinger y DisplayManagerService
realicen un seguimiento de más de dos pantallas y reconozcan las pantallas que se vieron 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 las pantallas físicas y virtuales del 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 la 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 dirigen a pantallas individuales al mismo tiempo, Android 10 se puede configurar para admitir varias ventanas enfocadas, como máximo una por pantalla. Esto solo se aplica a tipos especiales de dispositivos 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 de varias pantallas o los que se usan para experiencias similares a las de escritorio. Esto se debe principalmente a un problema de seguridad que puede hacer que los usuarios se pregunten qué ventana tiene el enfoque de entrada.
Imagina al usuario que ingresa información segura en un campo de entrada de texto, tal vez para acceder a una app bancaria o ingresar 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 el enfoque y ambas muestran un indicador de entrada activo (cursor parpadeante).
Sin embargo, dado que la entrada de un teclado (hardware o software) se ingresa solo en la actividad superior (la app que se inició más recientemente), al crear 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 enfocarían dos ventanas del mismo proceso, el sistema solo enfoca la ventana que se encuentra más arriba en el orden Z. Esta restricción se quita para las apps que segmentan Android 10, momento en el que se espera que puedan admitir 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 según el orden Z en lugar de almacenar en caché el valor. Esto se debe a que solo una fuente, WindowManager, necesita hacer un seguimiento del orden Z de las actividades.
ActivityStackSupervisor#getTopDisplayFocusedStack()
adopta un enfoque similar para aquellos casos en los que se debe identificar la pila enfocada más superior del sistema. Las pilas se recorren de arriba hacia abajo en busca de la primera pila apta.
Ahora InputDispatcher
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
, las ventanas enfocadas también se rastrean por separado.
Consulta DisplayContent#mCurrentFocus
y DisplayContent#mFocusedApp
, y sus respectivos usos. Los métodos relacionados de seguimiento y actualización del enfoque se transfirieron de WindowManagerService
a DisplayContent
.