Desarrollo de Apps para dispositivos foldables
Mi nombre es David Giménez y soy desarrollador de aplicaciones móviles desde 2013. Descubrí la comunidad Samsung Dev Spain en 2017 y desde entonces he participado en varios retos de “Dispositivos por apps”. Cada uno de estos retos me ha permitido aprender nuevos SDKs (como “Galaxy Edge” o “S Pen Remote”) a la vez que mejoraba como desarrollador al implementar nuevas técnicas de desarrollo como por ejemplo la optimización para dispositivos foldables.
Este artículo trata sobre ese último punto, el desarrollo para dispositivos foldables. Comentaré lo que considero, bajo mi punto de vista, los elementos clave para el desarrollo de aplicaciones optimizadas para este tipo de dispositivos.
- App Continuity: capacidad de mantener el estado de la aplicación al desplegar el dispositivo. Ejemplo: mantener la información mostrada en pantalla, mantener la posición del scroll, mostrar un diálogo si se estaba mostrando antes de desplegar, etc.
- Optimización de la interfaz: aprovechar el espacio disponible en la pantalla interna de dispositivos Fold creando interfaces específicas para tablet.
- Flex Mode: capacidad de ajustar la interfaz al doblar parcialmente el dispositivo.
- Drag&Drop: capacidad de recibir información desde una aplicación externa mediante la acción de “arrastrar y soltar”.
He desarrollado una aplicación de ejemplo que contiene todos los puntos que se van a tratar en el artículo. El código está disponible en:
https://github.com/WhiteDog-Apps/EjemploFoldable_Android
App Continuity
Al desplegar un dispositivo foldable se produce un cambio de configuración al igual que sucede al rotar la pantalla, cambiar al modo oscuro, etc… Esto produce que el Activity se destruya y se vuelva a crear desde cero perdiendo así la información aportada por el usuario.
Podemos evitar esta pérdida de información de las siguientes maneras:
- SavedInstanceState: antes de que se destruya el Activity el sistema llama al método “onSaveInstanceState(outState: Bundle)”. Este método permite guardar pares de información (clave-valor) de forma que al volver a crear el Activity se puede recuperar esa información en el método “onCreate(savedInstanceState: Bundle?)”.
- ViewModel: el elemento ViewModel tiene un ciclo de vida que no se ve afectado por los cambios de configuración (no se destruye al rotar la pantalla ni al desplegar un dispositivo Fold) como si le ocurre al Activity. Al almacenar la información dentro de un ViewModel nos evitamos tener que implementar el guardado de los datos. Aún así, tendríamos asignar las propiedades de los elementos de la interfaz:
Una forma de evitar tener que realizar este paso sería combinando el uso del ViewModel con DataBinding, de forma que se sincroniza el estado de la interfaz con los datos del ViewModel:
NOTA: un elemento importante que muchas aplicaciones suelen perder en los cambios de configuración son los diálogos. Una forma de evitarlo es almacenar en una variable el estado del diálogo y comprobar dicha variable en el método “onCreate()” para ver si es necesario volver a mostrarlo tras un cambio de configuración. En los ejemplos anteriores, esta función la realiza la variable “mostrandoResultado”.
Optimización de la interfaz
Al diseñar la interfaz de aplicaciones orientadas a dispositivos Fold, hay que tener en cuenta sus dos pantallas. La pantalla exterior se asemeja a la de un dispositivo tradicional mientras que la interna tiene proporciones de tablet.
Para mejorar la experiencia de usuario se recomienda implementar una interfaz específica para la pantalla interna. Para ello se debe crear una modificación del layout bajo la variación “layout-sw600dp”.
En mi opinión, si la interfaz lo permite, basta con dividir la vista original en dos columnas. Por ejemplo:
Otras opciones pueden ser el utilizar el elemento “SlidingPaneLayout”. Este componente muestra dos paneles en pantallas grandes mientras que en pantallas más reducidas únicamente muestra uno de ellos. De esta forma se puede implementar fácilmente el patrón Master-Detail. (https://developer.android.com/develop/ui/views/layout/twopane).
Flex Mode
Al doblar parcialmente un dispositivo foldable entramos en lo que se denomina “FlexMode”. Entre otras cosas, este modo permite al usuario colocar el dispositivo sobre una superficie mientras mantiene una parte de la pantalla levantada (similar a un portátil abierto). Al detectar que la aplicación entra en FlexMode debemos ajustar la interfaz de forma que los elementos pulsables queden en la parte inferior al doblez. De esta forma evitaríamos una mala experiencia al tener que pulsar sobre la parte de pantalla inclinada pudiendo ocasionar que el móvil “vuelque” hacia atrás.
Android dispone de mecanismos para detectar cuando una aplicación entra en FlexMode. Podéis encontrar más información en:
https://developer.android.com/guide/topics/large-screens/make-apps-fold-aware
Para simplificar el proceso, he creado una librería que nos permite detectar estos cambios de una manera más sencilla.
El código de la librería y un ejemplo de uso está disponible en GitHub:
https://github.com/WhiteDog-Apps/FoldableActivity_Android
Esta librería nos ofrece una clase “FoldableActivity” que implementa la lógica necesaria para detectar los cambios de FlexMode. Para no extender mucho más el artículo no detallaré cómo se implementa. En el enlace a la librería podéis ver cómo incluirla en vuestros proyectos.
Como ejemplo del uso de FlexMode:
Drag&Drop
Una de las principales ventajas de los dispositivos Fold es poder abrir hasta 3 aplicaciones a la vez. Esto permite, por ejemplo, escribir este artículo a la vez que busco en internet información y mientras reviso mi galería para encontrar las imágenes y videos usados como ejemplo. Con la funcionalidad de Drag&Drop podemos recibir texto, imágenes, ficheros, etc de otras aplicaciones para incluir esa información. Por ejemplo:
Para implementar esta funcionalidad de forma sencilla podemos utilizar el API “DropHelper”:
https://developer.android.com/jetpack/androidx/releases/draganddrop
Primero tenemos que declarar el listener que será el encargado de recibir los datos de la aplicación externa.
Después, mediante “DropHelper.Options” podemos establecer las opciones de configuración la capa de resalte que indicará al usuario dónde podrá soltar el elemento.
Por último, asociaremos el listener, las opciones de configuración, los tipos de datos que queremos aceptar desde otras aplicaciones y el elemento de nuestra interfaz en el que el usuario deberá soltar la información.
Conclusión
Como hemos visto, el desarrollo de aplicaciones adaptadas a dispositivos foldables presenta varios retos que son vitales para garantizar una buena experiencia de usuario (App Continuity y la Optimización de la interfaz). A parte podemos dar funcionalidades extras implementando FlexMode o Drag&Drop.