Cómo compilar apps para dispositivos plegables

Android 10 (nivel de API 29) agrega más compatibilidad para dispositivos plegables y diferentes patrones de plegado.

Desplegar un dispositivo para proporcionar una pantalla más grande puede tener un impacto positivo en los usuarios:

  • Por lo general, una pantalla más grande implica una experiencia más envolvente.
  • Con las multiventanas, el usuario puede realizar varias tareas al mismo tiempo.

Plegar y desplegar un dispositivo puede cambiar el tamaño de la pantalla, la densidad o la relación de aspecto. Este no es un problema nuevo en el desarrollo de Android. Ya sucede en estos casos en los que no se pliega el dispositivo:

  • Teléfonos, al cambiar entre el modo vertical y el horizontal.
  • Sistema operativo Chrome que se ejecuta en modo escritorio, al redimensionar apps de Android.
  • Dispositivos con pantallas múltiples o adicionales.

En esta página, se describen las prácticas recomendadas que debes seguir para asegurarte de que tu app funcione correctamente con factores de forma plegables.

También te recomendamos que leas el resumen de Android 10 sobre los cambios que implica la compatibilidad con dispositivos plegables.

Continuidad de apps

Cuando una app se ejecuta en un dispositivo plegable, puede pasar de una pantalla a otra automáticamente. Para ofrecer una gran experiencia del usuario, es muy importante que la tarea actual continúe ejecutándose sin problemas después de la transición. La app debe reanudarse en el mismo estado y ubicación. Ten en cuenta que los dispositivos plegables pueden plegarse de muchas maneras, como hacia dentro o hacia fuera:

Dado que el sistema activará un cambio de configuración durante la transición, una app debe guardar el estado de la IU y admitir cambios de configuración de manera fluida.

Cómo admitir el cambio de tamaño de tu app

Debes asegurarte de que tu app funcione en el modo multiventana y con el redimensionamiento dinámico. Para ello, establece resizeableActivity=true. Esto proporciona compatibilidad máxima con cualquier factor de forma y entorno en el que se pueda ejecutar tu app (como dispositivos plegables, modo de escritorio o ventanas de forma libre). Prueba el comportamiento de tu app en pantalla dividida o con un emulador de dispositivos plegables.

Si tu app establece resizeableActivity=false, esto le indica a la plataforma que no admite el modo multiventana. El sistema puede cambiar el tamaño de la app o ponerla en modo multiventana, pero se implementará la compatibilidad aplicando la misma configuración a todos los componentes de la app (incluidos los elementos de actividades, servicios y mucho más). En algunos casos, las modificaciones importantes (como un cambio en el tamaño de la pantalla) pueden reiniciar el proceso en lugar de cambiar la configuración.

Por ejemplo, la actividad que aparece a continuación estableció resizableActivity=false junto con un maxAspectRatio. Cuando se despliega el dispositivo, se mantienen la configuración de la actividad, el tamaño y la relación de aspecto, ya que la app se ejecuta en el modo de compatibilidad.

Si no estableces resizeableActivity, o si cambias su valor por uno verdadero, el sistema asumirá que la app es totalmente compatible con el modo multiventana y que es redimensionable.

Ten en cuenta que algunos OEM pueden implementar una función que agregue un pequeño ícono de reinicio en la pantalla cada vez que cambie el área de visualización de la actividad. Esto le permite al usuario reiniciar la actividad en la nueva configuración.

Nuevas relaciones de pantalla

Android 10 (nivel de API 29) y versiones posteriores admite una gama más amplia de relaciones de aspecto. En los dispositivos plegables, los factores de forma pueden variar desde pantallas largas y delgadas de gran altura (como 21:9 para un dispositivo plegado) hasta 1:1.

Para admitir tantos dispositivos como sea posible, deberías probar tus apps en busca de tantas relaciones de pantalla como puedas:

Si no puedes admitir algunas de estas relaciones, usa maxAspectRatio (como antes) o minAspectRatio para indicar las relaciones más altas y más bajas que puede admitir tu app. En los casos en que las pantallas superen esos límites, es posible que puedas poner tu app en el modo de compatibilidad.

Cuando hay cinco íconos en la vista de navegación inferior, se garantiza a los dispositivos que ejecutan Android 10 (nivel de API 29) y versiones posteriores un tamaño mínimo del objetivo táctil de 2 pulgadas. Consulta el Documento de definición de compatibilidad.

Multiventana

Poder ejecutar varias ventanas es uno de los beneficios de las pantallas grandes. Hace algunos años, tener dos apps una al lado de la otra era común en algunos dispositivos. La tecnología ha mejorado tanto que, en la actualidad, es posible ejecutar tres o más apps en una sola pantalla al mismo tiempo, así como compartir contenido entre ellas:

Si una app no admite correctamente el modo multiventana, puede establecer resizeableActivity=false. Para obtener más información, lee la Guía sobre el modo multiventana.

Con el aumento del uso de ventanas múltiples, considera la posibilidad de admitir el gesto arrastrar y soltar en tu app.

Multirreanudación

En Android 9.0 y versiones anteriores, solo la app enfocada se encuentra en el estado reanudada. Se pausará cualquier otra actividad visible. Esto puede crear problemas si las apps cierran recursos o detienen la reproducción de contenido cuando se pausan.

En Android 10, se modificó este comportamiento para que todas las actividades permanezcan en el estado reanudada cuando el dispositivo esté en el modo multiventana. Eso se llama multirreanudación. Ten en cuenta que se puede pausar una actividad si hay una actividad transparente encima o si no se puede enfocar la actividad (por ejemplo, el modo pantalla en pantalla). También es posible que ninguna actividad tenga foco en un momento dado, por ejemplo, si está abierto el panel lateral de Notificación. OnStop seguirá funcionando como de costumbre. Se lo llamará cada vez que desaparezca la actividad de la pantalla.

La multirreanudación también está disponible en algunos dispositivos que ejecutan Android 9.0. Para habilitar la multirreanudación en ellos, agrega los siguientes metadatos de manifiesto:

<meta-data
    android:name="android.allow_multiple_resumed_activities" android:value="true" />

Para verificar que un dispositivo específico admita ese manifiesto de metadatos, consulta las especificaciones del dispositivo.

Acceso a recursos exclusivos

Para ayudar a admitir la multirreanudación, hay una nueva función de devolución de llamada en el ciclo de vida: Activity#onTopResumedActivityChanged().

Se invoca a ese método cuando una actividad gana o pierde la posición de actividad que se ha reanudado más arriba. Eso es importante para saber cuándo una actividad utiliza un recurso compartido de un solo usuario, como el micrófono o la cámara.

protected void onTopResumedActivityChanged(boolean topResumed) {
    if (topResumed) {
        // Top resumed activity
        // Can be a signal to re-acquire exclusive resources
    } else {
        // No longer the top resumed activity
    }
}

Ten en cuenta que una app puede perder recursos por varias otras razones, como, por ejemplo, al quitar una pieza de hardware compartida.

En cualquier caso, una app debe administrar de manera fluida los eventos de pérdida de recursos y los cambios de estado que afectan a los recursos disponibles.

Para las apps que utilizan una cámara, se recomienda utilizar el método CameraManager.AvailabilityCallback#onCameraAccessPrioritiesChanged() a modo de sugerencia de que podría ser un buen momento para intentar acceder a la cámara. Ese método está disponible en Android 10 (nivel de API 29) y versiones posteriores.

Recuerda que resizeableActivity=false no es una garantía de acceso exclusivo a la cámara, ya que otras apps que la utilizan se pueden abrir en otras pantallas.

Cámara en el modo multiventana

Tu app no necesita dejar de usar la cámara cuando pierde el foco. Por ejemplo, es posible que quieras continuar con la vista previa de la cámara mientras el usuario interactúa con la app recién enfocada que se reanudó más arriba. No hay problema si tu app sigue ejecutando la cámara cuando no es la app que se reanudó más arriba, pero debe administrar el caso de desconexión correctamente. Cuando la app reanudada más arriba quiera usar la cámara, podrá abrirla, y tu app perderá el acceso. Tu app podrá volver a abrir la cámara cuando recupere el foco.

Después de que una app reciba una devolución de llamada CameraDevice.StateCallback#onDisconnected(), las llamadas subsecuentes del dispositivo de cámara arrojarán un CameraAccessException.

Multipantalla

En el futuro, es posible que haya teléfonos plegables que admitan más de una pantalla a la vez. Administrar esta configuración es similar a la forma en que los desarrolladores trabajan con pantallas proyectadas en el Sistema operativo Chrome.

Android 10 (nivel de API 29) y versiones posteriores admite actividades en pantallas secundarias. Si se ejecuta una actividad en un dispositivo con varias pantallas, los usuarios pueden mover la actividad de una pantalla a otra. La multirreanudación también se aplica a los casos de multipantalla. Varias actividades pueden recibir entradas del usuario al mismo tiempo.

Una app puede especificar en qué pantalla debe ejecutarse cuando se inicia o cuando crea otra actividad. Este comportamiento depende del modo de lanzamiento de la actividad definido en el archivo de manifiesto, así como de las marcas de intent y las opciones establecidas por la entidad que lanza la actividad. Para obtener más información, consulta ActivityOptions.

Al igual que con las transiciones plegables, cuando se mueve una actividad a una pantalla secundaria, puede pasar por una actualización de contexto, un cambio de tamaño de ventana o modificaciones de configuración y recursos. Si la actividad administra el cambio de configuración, se le notificará en onConfigurationChanged(). De lo contrario, se volverá a lanzar.

Una actividad debe verificar la visualización actual en onCreate y onConfigurationChanged si se los administra. Asegúrate de actualizar los recursos y diseños cuando cambie la visualización.

Si el modo de lanzamiento seleccionado para una actividad admite varias instancias, recuerda que el lanzamiento en una pantalla secundaria puede crear una nueva instancia de la actividad. Se reanudarán ambas actividades al mismo tiempo.

Múltiples instancias de una actividad en varias pantallas

También puedes leer sobre las API multipantalla que se introdujeron en Android 8.0.

Recortes de pantalla

La geometría de recorte de los dispositivos plegables puede ser diferente en función de si están plegados o desplegados. Para evitar problemas de recorte, lee Prácticas recomendadas para recortes de pantalla.

Contexto de aplicación frente al de actividad

El uso del contexto correcto es crucial en el modo multipantalla. Cuando se accede a recursos, el contexto de la actividad (que se visualiza) es diferente del contexto de la app (que no se visualiza).

El contexto de la actividad contiene información sobre la pantalla y siempre se ajusta al área en la que aparece. Para obtener métricas de pantalla y recursos actuales, utiliza el contexto de la actividad. Esto también afecta a algunas API del sistema que usan información del contexto (como Avisos).

La configuración de la ventana de actividad y la pantalla principal definen los recursos y el contexto. Para obtener el uso actual de la pantalla, haz lo siguiente:

val activityDisplay = activity.windowManager.defaultDisplay

Para obtener las métricas de la ventana de actividad actual, utiliza:

val windowMetrics = DisplayMetrics()
activityDisplay.getMetrics(windowMetrics)

O bien:

val windowMetrics = activity.resources.displayMetrics

Cómo utilizar pantallas secundarias

Puedes obtener las pantallas disponibles del servicio del sistema DisplayManager:

val dm = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val displays = dm.displays

Usa la clase Display para obtener información sobre una pantalla en particular:

  • Las métricas de pantalla te proporcionan información sobre el tamaño, la resolución y la densidad de la pantalla.
  • Consulta las marcas para ver si una pantalla es segura.

Para determinar si se puede iniciar una actividad en una pantalla, haz lo siguiente:

activityManager.isActivityStartAllowedOnDisplay(context, displayId, intent)

Y para lanzar una actividad en una pantalla:

val options = ActivityOptions.makeBasic()
options.launchDisplayId = targetDisplay.displayId
startActivity(intent, options.toBundle())

Compatibilidad multipantalla

Android ya es compatible con teclados de software, fondos de pantalla y selectores.

Teclados de software

Se puede mostrar un teclado en una pantalla secundaria si está configurada para admitir decoraciones del sistema. El editor de métodos de entrada aparecerá automáticamente si un campo de texto solicita interactuar con esa pantalla.

Teclado en una pantalla secundaria

Fondo de pantalla

En Android 10 (nivel de API 29) y versiones posteriores, las pantallas secundarias pueden tener un fondo de pantalla. El marco de trabajo crea una instancia separada de WallpaperService.Engine para cada pantalla. Asegúrate de que la superficie de cada motor se dibuje de manera independiente. Los desarrolladores pueden cargar recursos utilizando el contexto de visualización de WallpaperService.Engine#getDisplayContext(). Además, asegúrate de que tu archivo WallpaperInfo.xml establezca android:supportsMultipleDisplays="true".

Fondos de pantalla en un teléfono y en una pantalla secundaria

Selectores

Hay una nueva categoría de filtro de intent SECONDARY_HOME que proporciona una actividad exclusiva para las pantallas secundarias. Las instancias de esta actividad se utilizan en todas las pantallas que admiten decoraciones del sistema, una por cada pantalla.

<activity>
    ...
    <intent-filter>
        <category android:name="android.intent.category.SECONDARY_HOME" />
        ...
    </intent-filter>
</activity>

La actividad debe tener un modo de lanzamiento que no impida el uso de múltiples instancias y que pueda adaptarse a diferentes tamaños de pantalla. El modo de lanzamiento no puede ser singleInstance ni singleTask.

Como ejemplo, la implementación de AOSP de Launcher3 admite una actividad SECONDARY_HOME.

Selector con estilo material design en un teléfono

Selector con estilo material design en una pantalla secundaria

Pruebas

A fin de preparar tu app para dispositivos plegables, deberías probar cómo reacciona a lo siguiente:

  • Cambios de configuración
  • Multiventana y multirreanudación
  • Redimensionamientos y nuevas relaciones de pantalla

Emuladores de dispositivos plegables

El emulador de AOSP admite dispositivos plegables. Esto permite a los desarrolladores probar sus apps para esos casos de uso

Emulador de dispositivos plegables de 7.3"

7.3" Tamaño Resolución Pantalla lógica
de pantalla X Y densityDpi Tamaño
Sin plegar 7.3 1536 2152 420 Grande
Plegado 4.6 840 1960 420 Normal

Emulador de dispositivos plegables de 8"

8" Tamaño Resolución Pantalla lógica
de pantalla X Y densityDpi Tamaño
Sin plegar 8.03 2200 2480 420 Grande
Plegado 6.62 1148 2480 420 Normal

Emulador de dispositivos plegables de AOSP

En Android Studio 3.5, hay disponibles varios emuladores de dispositivos plegables:

Emuladores de dispositivos plegables en Android Studio 3.5

Pruebas multipantalla

La nueva opción para desarrolladores, llamada Forzar modo de escritorio, permite activar la compatibilidad con decoraciones del sistema en todas las pantallas secundarias y muestra un puntero de mouse allí en vez de en la pantalla predeterminada. Cuando se utiliza con Habilitar ventanas de forma libre, Forzar modo de escritorio simula una experiencia de escritorio multiventana y la posibilidad de cambiar el tamaño de las ventanas.

En Pixel, puedes probarlo usando Pantalla simulada. Asimismo, si tienes un dispositivo compatible con HDMI o DisplayPort a través de USB tipo C, puedes probarlo mediante una conexión por cable.

Pantalla simulada