Jouons avec les alarmes d'Android

21-06-10 Cause I'd Rather Pretend I'll Still Be There At The End ~ Explored #1

Comme promis plus tôt, nous allons jouer avec les alarmes proposées par le système Android. Par alarme, il ne faut pas entendre « le truc qui sonne et qui vous empêche de dormir quand vous voulez faire la grasse mat' », mais plutôt les alarmes systèmes qui vous permettront de déclencher des événements spécifiques à un moment précis.

Vous souvenez-vous du précédent article ? Il contenait le code permettant la création d'un widget basique. En cliquant sur ce widget, je déclenchais l'affichage d'une notification. Je vais réutiliser ce widget. Au lieu de déclencher l'action tout de suite, un clic sur le widget devra programmer l'affichage de la notification quelques secondes plus tard.

Vas-y Einstein, fais nous ton speech !

Comment fonctionnent les alarmes ? Vous savez déjà que l'interaction entre les composants Android se fait grâce à l'émission d'Intents. Programmer une alarme sous Android, c'est simplement programmer l'émission d'un intent.

Une chose importante à savoir : si vous rebootez votre téléphone, les alarmes ne seront pas conservées. Si vous voulez des alarmes persistentes, il faudra les stocker quelque part, et avoir un service se lançant au boot et reconfigurant toutes les alarmes.

Rien de bien sorcier là dedans, passons donc à la pratique.

Autant pour la théorie

Quand nous cliquons notre widget, nous ne voulons plus afficher la notification, mais démarrer l'alarme. Nous allons donc créer une nouvelle action dans le fichier NapplyWidget.java :

// En haut du fichier
// Ne pas oublier de déclarer l'action dans l'intent-filter du Manifest
private static final String ACTION_START_ALARM = "fr.miximum.widget.START_ALARM";

Dans la fonction updateAppWidget, remplacez l'action SHOW_NOTIFICATION par START_ALARM :

static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
    // Prepare widget views
    RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.napply_widget_layout);
    views.setTextViewText(R.id.nap_time, "Erase me");

    // Prepare intent to launch on widget click
    Intent intent = new Intent(context, NapplyWidget.class);
    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    intent.setAction(ACTION_START_ALARM); //ici
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
    views.setOnClickPendingIntent(R.id.napply_widget, pendingIntent);

    appWidgetManager.updateAppWidget(appWidgetId, views);
}

Dans la méthode onReceive, nous allons traiter les deux actions :

@Override
public void onReceive(Context context, Intent intent) {
    super.onReceive(context, intent);

    if (ACTION_SHOW_NOTIFICATION.equals(intent.getAction())) {
        showNotification(context);
    }
    else if (ACTION_START_ALARM.equals(intent.getAction())) {
        startAlarm(context);
    }
}

Enfin, il ne nous reste plus qu'à créer la méthode qui configure l'alarme :

protected void startAlarm(Context context) {
    // Prepare intent to launch notification
    Intent intent = new Intent(context, NapplyWidget.class);
    intent.setAction(ACTION_SHOW_NOTIFICATION);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

    // The alarm manager is an android system service
    AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 5000, pendingIntent);
}

La fonction set de la classe AlarmManager me permet de configurer l'alarme. Deux possibilités : je peux la configurer pour se déclencher à une heure précise, ou à un delta par rapport au démarrage du système. La constante ELAPSED_REALTIME_WAKEUP signifie que j'ai choisi cette seconde possibilité, et que l'alarme réveillera le téléphone si celui-ci est en veille.

Comme je veux que l'alarme se déclenche dans 5 secondes par rapport à maintenant, j'ajoute 5000 au nombre de millisecondes passées depuis le démarrage du système.

Enfin, je passe le pendingIntent qui sera envoyé.

On compile tout ça, on recharge le widget, on clique dessus, on attends 5 secondes, et Ô miracle ! La notification survient.

C'est tout ?!

Autre fonctionnalité : les alarmes avec fréquence de répétition. À utiliser si vous souhaitez déclencher une action régulièrement, genre toutes les heures ou toutes les dix minutes. C'est enfantin, remplaçons la fonction set par setRepeating.

// Repeat every five seconds
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 5000, 5000, pendingIntent);

L'alarme se répétera tant que vous ne l'aurez pas annulé. Tiens, mais comment annule-t-on une alarme, d'ailleurs ? Facile, il suffit d'appeler la fonction cancel(), et de lui passer le même pendingIntent que celui passé à la fonction set().

C'est tout pour aujourd'hui. La prochaine fois, nous apprendrons à jouer avec le blocage / déblocage de l'écran et du clavier.