lunes, 27 de febrero de 2012

Aplicación en modo kiosko para Android

Hace un tiempo tuve que migrar una aplicación de Windows Mobile a Android. En sentido general el desarrollo en Android fue más simple y más rápido; pero cuando llegué al punto de duplicar el comportamiento del "modo kiosko", Android se tornó casi tan arisco como Windows Mobile. Aquí publico hoy la solución que apliqué para que sirva de base a otros desarrollos.

No es que hacer una aplicación que se ejecute en modo kiosko en Windows Mobile sea simple. De hecho lograr que la aplicación se mantuviera siempre en primer plano bloqueando los botones de hardware del teléfono fue toda una odisea que necesitó alguna chapucilla código particularizado para cubrir limitaciones (como que en los teléfonos Samsung se nos estaba vetado deshabilitar el botón de la cámara -según nos confirmaron desde Samsung- y, por tanto, cada vez que se pulsaba el botón de la cámara la aplicación perdía el foco).

Los teléfonos con Android, por suerte, tienen unas APIs más uniformes, pero no por esto el modo kiosko deja de ser un reto. Para comenzar, no encontré ningún lugar donde pusieran en claro qué hacer para que una interfaz se mantuviera en primer plano y a pantalla completa, independientemente de la interacción que tuviera el usuario con el dispositivo. Debido a que la información que encontré era incompleta y estaba dispersa, hoy pongo aquí el resumen de lo necesario para crear una aplicación que se ejecute en modo kiosko, acompañado del código fuente de un proyecto sobre el que se puede partir para hacer una aplicación que ya satisface estos requisitos.

Para comenzar, una aplicación en modo kiosko en Android debe:
  1. Mostrarse a pantalla completa, de modo que el usuario no se salga de la aplicación accediendo a otras opciones que aparezcan en la zona de notificaciones, en la barra de título.
  2. Bloquear los soft buttons y botones de hardware, de modo que el usuario no pueda salirse de la aplicacion presionando el botón back ni se interrumpa la aplicación por la presión de otros botones como el control del volumen.
  3. Remplazar el home del dispositivo por la pantalla inicial de la aplicación, de modo que, al presionar el botón "home" (que lamentablemente no puede ser interceptado, como el resto de botones) en lugar de salir de la aplicación para mostrar la pantalla de inicio del teléfono, se muestre la aplicación y se pueda restablecer la interfaz al punto donde se estaba.
Debo puntualizar que esta solución no es La Solución para todas las aplicaciones que requieran ejecutarse en modo kiosko pues cada una puede requerir un mayor o menor grado de control. Sin embargo, la esencia de dicho control puede obtenerse haciendo que las actividades de nuestra aplicación extiendan la clase KioskModeActivity cuyo código es el siguiente:

package org.carlosbello.android.kioskmode;

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;

/**
 * Actividad de base para crear interfaces en modo kiosko.
 * @author Carlos Bello
 */
public class KioskModeActivity extends Activity {
    /** Indica si deberá desactivarse el uso del botón de retroceso. */
    protected boolean blockBackButton = false;
    
    /**
     * Establece la visualización a pantalla completa para evitar una salida 
     * accidental a través de la barra de título o cualquier otro elemento 
     * interactivo que no sea nuestra propia interfaz.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
    }
    
    /**
     * Intercepta los eventos del teclado para evitar la interacción con 
     * cualquier elemento que no forme parte de nuestra interfaz, salvo la 
     * funcionalidad del botón de retroceso, si estuviera habilitado.
     */    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event)  {
        return !blockBackButton && keyCode == KeyEvent.KEYCODE_BACK
            ? super.onKeyDown(keyCode, event)
            : true;
    }
}

Ahora, para evitar salirse de la aplicación, la actividad inicial deberá establecer blockBackButton a true y registrarse en el manifiesto del proyecto con la categoría HOME; de modo que si reiniciamos el terminal se abrirá nuestra aplicación primero que cualquier otra y al pulsar el botón home se mostrará siempre nuestra aplicación.

Claro que convertir nuestra aplicación en el home del sistema trae como consecuencia que si queremos salir de la aplicación, terminaremos entrando en ella nuevamente pues el home del sistema es nuestra propia aplicación. De hecho, ese es parte del objetivo: que si nos salimos por accidente de la aplicación, ésta se abra automáticamente. Este "inconveniente" puede sortearse buscando otra aplicación que pueda funcionar como home y abriéndola explícitamente.

Como el código para hacer esta búsqueda es algo más extenso, dejo en el siguiente enlace un proyecto de ejemplo de una aplicación que puede ejecutarse en en modo kiosko:
http://ejectodemo.googlecode.com/files/20120227_AndroidKioskMode.zip

Update: desde agosto de 2014 el código está disponible en GitHub:
https://github.com/carlosbello/efectodemo/tree/master/AndroidKioskMode

Atención: Debe tenerse en cuenta que si durante la ejecución de la aplicación se presiona el botón home y se marca la casilla de "Usar por defecto" se reescribirá la pantalla home del sistema y se necesitará eliminar los valores por defecto, almacenados para la aplicación AndroidKioskMode o desinstalarla para restablecer el home original.

Bueno, esto es todo. Agradeceré cualquier colaboración que mejore esta solución o plantee una solución diferente.