Programación Android, Creación de Widgets II

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0

Creación de widgets
Después de un tiempo excesivo de inactividad (lo lamentamos, pero entre cursos, viajes y trabajo ha sido imposible sentarse a escribir una nueva entrada), volvemos a la carga para terminar el tutorial que dejamos a medias sobre cómo crear widgets.

En la anterior entrada comenzamos con la creación de widgets, para que el sistema lo detectara como tal y pudieramos agregarlo a la pantalla principal del dispositivo. En esta ocasión vamos a retomar este ejemplo, y vamos a añadirle la posibilidad de interactuar con él. Lo que vamos a crear es un sencillo widget que nos va a permitir cambiar el estado del sonido del dispositivo, intercambiando entre el volumen que haya dejado el usuario en el dispositivo y ponerlo en silencio con vibración.

Creación de widget

En primer lugar, para poder hacer esto, vamos a modificar el xml del widget, cambiando el TextView que contiene por un ImageView que nos permitirá configurar un recurso que indique al usuario si el dispositivo está en silencio o no, y cambiarlo cada vez que el usuario pulse sobre nuestro widget, además le eliminaremos el shape que inicialmente configuramos:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/widget"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/ivVolumeState"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:clickable="true"
        android:src="@drawable/ic_launcher" />
</RelativeLayout>

Como iconos para indicar en el widget si el dispositivo está o no en silencio, en el ejemplo vamos a utilizar recursos del propio sistema, pero si queréis podéis buscar otros recursos, o crearlos vosotros mismos. En el siguiente código de ejemplo se puede ver dónde y cómo poder usarlos.

Crear un IntentService

A continuación vamos a crear una clase interna en nuestra clase AppWdidgetProvider, que consistirá en un servicio que lanzaremos cuando el usuario pulse sobre nuestro widget, y que se encargará de cambiar el estado del dispositivo y actualizar la imagen del widget. Las explicaciones las añadimos al propio código, para que sea más fácil entenderlo:

/**
 * Clase interna encargada de modificar y actualizar el estado del dispositivo y del widget en
 * pantalla.
 */
public static class ToggleVolumeService extends IntentService {

    /**
     * Necesario el constructor por motivos de depuración, si no se añadiera el sistema daría
     * una excepción. El String que se le pasa al super es el nombre de nuestra clase
     * AppWidgetProvider, seguido del símbolo dólar y por último el nombre de esta clase
     * interna.
     */
    public ToggleVolumeService() {
        super("Widget$ToggleVolumeService");
    }

    /**
     * Encargado de manejar el intent que se le pase al servicio. En este caso recibirá el
     * intent que se ha lanzado desde los métodos del widget. Como el Intent lo hemos lanzado
     * directamente para ejecutar esta tarea, no nos es necesario analizar ningún dato para
     * seleccionar una tarea u otra.
     *
     * @param intent
     */
    @Override
    public void onHandleIntent(Intent intent) {
        // El objeto ComponentName será el que nos permita comunicarnos con el propio widget
        // desde el servicio.
        ComponentName cn = new ComponentName(this, Widget.class);
        // Creamos un manager para abrir la conexión con la vista remota (el propio widget en
        // pantalla.
        AppWidgetManager mngr = AppWidgetManager.getInstance(this);

        // Actualizamos el estado del widget.
        mngr.updateAppWidget(cn, buildUpdate(this));
    }

    /**
     * Este método crea una instancia del widget (RemoteViews) y actualiza el estado tanto del
     * sonido como del propio widget.
     *
     * @param cntx
     * @return
     */
    private RemoteViews buildUpdate(Context cntx) {
        // Crear una instancia del widget.
        RemoteViews updateViews = new RemoteViews(cntx.getPackageName(), R.layout.widget);
        // Crear un manager para poder modificar el estado del sonido.
        AudioManager audioManager =
                (AudioManager) cntx.getSystemService(Activity.AUDIO_SERVICE);

        // Comprobamos si el estado actual es el modo vibración o no.
        if (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
            // Si estaba en vibración establecemos el estado normal.
            updateViews.setImageViewResource(R.id.ivVolumeState,
                    android.R.drawable.ic_lock_silent_mode_off);
            audioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
        } else {
            // Si estaba el estado normal, establecemos el modo vibración.
            updateViews.setImageViewResource(R.id.ivVolumeState,
                    android.R.drawable.ic_lock_silent_mode);
            audioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
        }

        // Creamos un intent que se comunique con el widget.
        Intent intent = new Intent(this, Widget.class);
        // Creamos un PendingIntent que se lanzará cuandos se pulse sobre el widget.
        PendingIntent pi = PendingIntent.getBroadcast(cntx, 0, intent, 0);
        // Establecemos el PendingIntent que se debe lanzar al pulsar sobre el widget.
        updateViews.setOnClickPendingIntent(R.id.ivVolumeState, pi);

        return updateViews;
    }
}

Como hemos creado un servicio dentro de nuestra aplicación, para que funcione debemos configurarlo también en nuestro manifest.xml, pondremos lo siguiente debajo del receiver que ya configuramos previamente:

<service
      android:name="com.iae.gps.first_widget.widget.Widget$ToggleVolumeService" />

Actualizar el widget

Y por último, nos falta configurar el propio Widget para que actualice de forma correcta, pues ya no tendrá que llamar a la Activity que creamos, sino que tendrá que llamar a nuestro servicio para poder actualizar el estado del sonido del dispositivo. Como en nuestra aplicación vamos a tener un único widget (no vamos a configurar múltiples widgets para diversas funcionalidades), vamos a modificar el código que teníamos en onUpdate, para que lance siempre el servicio que acabamos de configurar, y además vamos a añadir un nuevo método, onReceive, que se encargará de mostrar el estado correcto en el icono cuando inicialmente lo posicionemos en pantalla. De nuevo, dentro del código añadiremos los comentarios para que sea más sencillo entenderlo:

/**
 * Este método se ejecuta automáticamente cuando el usuario lo selecciona del listado de widgets
 * para posicionarlo en pantalla, en él vamos a recoger el estado actual del dispositivo, para
 * mostrar el debido icono en el widget.
 *
 * @param cntx
 * @param intent
 */
@Override
public void onReceive(Context cntx, Intent intent) {
    RemoteViews updateViews = new RemoteViews(cntx.getPackageName(), R.layout.widget);
    // Crear un manager para poder comprobar el estado del sonido.
    AudioManager audioManager =
            (AudioManager) cntx.getSystemService(Activity.AUDIO_SERVICE);

    // Comprobamos el estado actual y establecemos el icono correcto.
    if (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
        updateViews.setImageViewResource(R.id.ivVolumeState,
                android.R.drawable.ic_lock_silent_mode);
    } else {
        updateViews.setImageViewResource(R.id.ivVolumeState,
                android.R.drawable.ic_lock_silent_mode_off);
    }
}

@Override
public void onUpdate(Context cntx, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    cntx.startService(new Intent(cntx, ToggleVolumeService.class));
}

Si además de esto, queremos que nuestra aplicación no tenga ningún icono que actúe como lanzador de la aplicación, porque no tenga ningún tipo de Activity (por ejemplo si sólo queremos crear un paquete de widgets), además de eliminar los archivos xml y java referentes a toda Activity de nuestro proyecto, también lo haremos con las referencias en el archivo manifest.xml. En el código fuente de nuestro ejemplo lo hemos hecho así, de manera que podéis descargarlo para poder analizarlo si lo deseáis.

Descargas

Puedes descargar el proyecto completo aquí.

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0
The following two tabs change content below.
Reborn as IT Developer. Desarrollador Android y fundador de Proyecto Simio. "En realidad, yo no puedo enseñar nada a nadie, sólo puedo hacerles pensar." - Sócrates.

2 thoughts on “Programación Android, Creación de Widgets II

  1. Buenas, no se si aún respondan este tema. El caso es que soy nuevo en esto de programación para android y quería testear el widget. Mi inconveniente es que no se donde colocar (en que ruta de mis directorios del smarthphone) todos esos archivos para que el widget me aparezca como disponible, o si necesito instalar algo; la verdad es que estoy perdido.
    Cualquier cosa estaría agradecido :)

Deja un comentario