Participer au site avec un Tip
Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

Déclenchement de notifications

Accès rapide :
Déclenchement de notifications avec une compatibilité inférieure à l'API 26
Déclenchement de notifications à partir de l'API 26
Ajouter une action à l'appuie sur la notification
Annulation d'une notification

Nous allons voir, dans ce tutoriel comment déclencher des notifications sur votre périphérique Android. Une notification est produite par une activité (assez rarement) ou par un service (c'est le cas le plus général) pour vous informer d'un changement important. Les notifications sont affichées dans la barre de notification présente en haut de votre écran.

La seule difficulté à gérer réside dans le fait que l'API de notification n'a cessé d'évoluer au gré des versions d'Android. D'où une question importante : quelle version de l'API choisir ? De manière basique, j'aurais envie de vous dire : « celle qui marche sur un maximum de périphériques Android » ! Le problème, c'est que le Play Store de Google (élément qui peut être important pour votre application) impose maintenant d'avoir un comportement compatible avec l'API 26 (paramètre targetSdkVersion fixé à 26 dans votre fichier de configuration gradle). En conséquence, je vous conseille de viser cette version, même si ce n'est pas encore (en 2019) une version majoritairement déployée.

Déclenchement de notifications avec une compatibilité inférieure à l'API 26

Veuillez créer une nouvelle application Android compatible avec l'API 15 d'Android (l'application est ainsi compatible avec quasiment 100% des périphériques Android). Ajoutez un bouton dans votre ressource d'activité et appelez le btnNotif. Editez le fichier manifeste et ajoutez-y une permission pour autoriser les vibrations lors de déclenchement d'une notification. Voici à quoi pourrait ressembler votre fichier de manifeste.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.infinisoftware.testnotifs">

    <uses-permission android:name="android.permission.VIBRATE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>
Autorisation des vibrations pour votre application
si votre émulateur tourne avec une API 26 (Android Oreo), un bug apparaît si les icônes utilisées par vos notifications sont localisées dans le répertoire mipmap (quelle que soit la résolution). Copier alors les icônes souhaitées dans le répertoire drawable. A titre d'information, voici le message produit si vous tentez d'afficher une icône située dans un dossier mipmap avec une version 26 de l'API Android et notez aussi qu'aucune exception n'est affichée dans le LogCat.
System UI has stopped. Close App.

Veuillez ensuite ouvrir le fichier de code Java de l'activité et ajoutez-y le code suivant.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
package com.infinisoftware.testnotifs;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private static final int NOTIF_ID = 123;

    @Override
    protected void onCreate( Bundle savedInstanceState ) {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_main );

        Button btnNotif = findViewById( R.id.btnNotif );
        btnNotif.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick( View view ) {
                Context context = MainActivity.this;
                Resources res = context.getResources();
  
                Notification notification = new Notification.Builder(context)
                        .setSmallIcon(R.drawable.ic_launcher)     // drawable for API 26
                        .setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.ic_launcher))
                        .setWhen(System.currentTimeMillis())
                        .setAutoCancel(true)
                        .setContentTitle("A notification title")
                        .setContentText( "Full message" )
                        .setVibrate(new long[] { 0, 500, 110, 500, 110, 450, 110, 200, 110,
                                170, 40, 450, 110, 200, 110, 170, 40, 500 } )
                        .setLights(Color.RED, 3000, 3000)
                        .getNotification();     // avant l'API 16
                        //.build();             // à partir de l'API 16

                NotificationManager notifManager = (NotificationManager) 
                        context.getSystemService(Context.NOTIFICATION_SERVICE);
                notifManager.notify( NOTIF_ID, notification );
                Log.i( "MainActivity", "Notification launched" );
            }
        });
    }

}
Déclenchement d'une notification lors d'un clic sur le bouton
si vous avez choisi une API minimale en version 16, préférez l'utilisation de la méthode build (ligne 44) pour produire la notification.

Déployez l'application sur votre périphérique Android, puis cliquez sur le bouton pour faire apparaître la notification. Voici le résultat produit.

Déclenchement de notifications à partir de l'API 26

l'utilisation de l'exemple présenté dans la section précédente dans une application utilisant le paramètre targetSdkVersion fixé à une valeur 26 ou supérieure, ne produira pas de notification. Aucun message d'erreur ne sera produit et aucune notification n'apparaîtra.

L'API 26 d'Android change la manière de produire les notifications. Le principal changement réside dans l'obligation d'utiliser les NotificationChannel. Un NotificationChannel, est en quelle que sorte, une pré-configuration pour un ensemble de notifications. Lors de sa création, une notification est associée à un channel (un canal, en français).

Un NotificationChannel est associé à une notion d'importance. L'importance notifManager.IMPORTANCE_HIGH associe la notification aux icônes présentes sur votre bureau Android ou dans le gestionnaire d'applications (petit rond présent en haut et à droite de l'icône). Un appui long sur l'icône permet d'observer les notifications associées.

Afin de nous simplifier la vie je vous propose d'utiliser une classe utilitaire que nous appellerons NotificationHelper. Placez cette classe dans un nouveau projet configuré pour supporter l'API 26. En voici son code.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
package com.infinisoftware.testnotifs;

import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.content.Context;
import android.content.ContextWrapper;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;

public class NotificationHelper extends ContextWrapper {
    private NotificationManager notifManager;

    private static final String CHANNEL_HIGH_ID = "com.infinisoftware.testnotifs.HIGH_CHANNEL";
    private static final String CHANNEL_HIGH_NAME = "High Channel";

    private static final String CHANNEL_DEFAULT_ID = "com.infinisoftware.testnotifs.DEFAULT_CHANNEL";
    private static final String CHANNEL_DEFAUL_NAME = "Default Channel";


    public NotificationHelper( Context base ) {
        super( base );

        notifManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        long [] swPattern = new long[] { 0, 500, 110, 500, 110, 450, 110, 200, 110,
                170, 40, 450, 110, 200, 110, 170, 40, 500 };

        NotificationChannel notificationChannelHigh = new NotificationChannel(
                CHANNEL_HIGH_ID, CHANNEL_HIGH_NAME, notifManager.IMPORTANCE_HIGH );
        notificationChannelHigh.enableLights( true );
        notificationChannelHigh.setLightColor( Color.RED );
        notificationChannelHigh.setShowBadge( true );
        notificationChannelHigh.enableVibration( true );
        notificationChannelHigh.setVibrationPattern( swPattern );
        notificationChannelHigh.setLockscreenVisibility( Notification.VISIBILITY_PUBLIC );
        notifManager.createNotificationChannel( notificationChannelHigh );

        NotificationChannel notificationChannelDefault = new NotificationChannel(
                CHANNEL_DEFAULT_ID, CHANNEL_DEFAUL_NAME, notifManager.IMPORTANCE_DEFAULT );
        notificationChannelDefault.enableLights( true );
        notificationChannelDefault.setLightColor( Color.WHITE );
        notificationChannelDefault.enableVibration( true );
        notificationChannelDefault.setShowBadge( false );
        notifManager.createNotificationChannel( notificationChannelDefault );
    }


    public void notify( int id, boolean prioritary, String title, String message ) {
        String channelId = prioritary ? CHANNEL_HIGH_ID : CHANNEL_DEFAULT_ID;
        Resources res = getApplicationContext().getResources();

        Notification notification = new Notification.Builder( getApplicationContext(), channelId )
                .setContentTitle( title )
                .setContentText( message )
                .setSmallIcon( R.drawable.ic_launcher )
                .setLargeIcon( BitmapFactory.decodeResource(res, R.drawable.ic_launcher) )
                .setAutoCancel( true )
                .build();

        notifManager.notify( id, notification );
    }

}
Classe utilitaire permettant de produire des notifications sur un channel donné

Le choix du channel s'opère via le second paramètre de la méthode notify : la valeur true demande le channel de forte importance, la valeur false demande l'autre channel.

Voici maintenant le code permettant de déclencher la notification.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
package com.infinisoftware.testnotifs;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btnNotif = findViewById( R.id.btnNotif );
        btnNotif.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                NotificationHelper notificationHelper = new NotificationHelper(MainActivity.this);
                notificationHelper.notify(1, false, "My title", "My content" );

                Log.i("MainActivity", "Notification launched");
            }
        });
    }
}
Déclenchement de la notification via notre classe utilitaire

Ajouter une action à l'appuie sur la notification

Il est possible de lier l'activation de la notification à l'ouverture d'une activité associée. Pour ce faire, il faut ajouter une intention d'ouverture de l'activité souhaité à votre NotificationBuilder. Voici un exemple de code : il s'agit de la nouvelle version de la méthode notify présente dans notre classe utilitaire.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
public void notify( int id, boolean prioritary, String title, String message ) {
    String channelId = prioritary ? CHANNEL_HIGH_ID : CHANNEL_DEFAULT_ID;
    Resources res = getApplicationContext().getResources();
    Context context = getApplicationContext();

    /* Lien avec l'activité à ouvrir : ici MainActivity */
    Intent notificationIntent = new Intent(context, MainActivity.class);
    PendingIntent contentIntent = PendingIntent.getActivity(
            context, 456, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);

    Notification notification = new Notification.Builder( getApplicationContext(), channelId )
            .setContentIntent( contentIntent )      // On injecte le contentIntent
            .setContentTitle( title )
            .setContentText( message )
            .setSmallIcon( R.drawable.ic_launcher )
            .setLargeIcon( BitmapFactory.decodeResource(res, R.drawable.ic_launcher) )
            .setAutoCancel( true )
            .build();

    notifManager.notify( id, notification );
}
Ouverture d'une activité lors de l'activation de la notification

Annulation d'une notification

Il est possible d'annuler une notification, à condition d'en connaître son identifiant. Un identifiant de notification est une valeur numérique entière. C'est cette valeur qui est demandée en premier paramètre de la méthode notify de notre classe utilitaire. Afin de tester cette possibilité, veuillez ajouter un nouveau bouton à votre activité, appelez-le btnCancel et ajoutez-y le gestionnaire d'événement proposé dans l'exemple suivant. Notez bien que l'instance de NotificationHelper a été déplacée (ligne 16) afin d'y avoir accès dans les deux gestionnaires d'événements.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
package com.infinisoftware.testnotifs;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final NotificationHelper notificationHelper = new NotificationHelper(MainActivity.this);

        Button btnNotif = findViewById( R.id.btnNotif );
        btnNotif.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                notificationHelper.notify(1, false, "My title", "My content" );
                Log.i("MainActivity", "Notification launched");
            }
        });

        Button btnCancel = findViewById( R.id.btnCancel );
        btnCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                notificationHelper.cancelNotification( 1 );
                Log.i("MainActivity", "Notification removed");
            }
        });
    }
}
Annulation d'une notification

Modifiez ensuite votre classe NotificationHelper afin d'y ajouter la méthode cancelNotification. Voici la classe ainsi modifiée.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
 66 
 67 
 68 
 69 
 70 
 71 
 72 
 73 
 74 
 75 
 76 
 77 
package com.infinisoftware.testnotifs;

import android.app.PendingIntent;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.content.Context;
import android.content.ContextWrapper;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;

class NotificationHelper extends ContextWrapper {
    private NotificationManager notifManager;

    private static final String CHANNEL_HIGH_ID = "com.infinisoftware.testnotifs.HIGH_CHANNEL";
    private static final String CHANNEL_HIGH_NAME = "High Channel";

    private static final String CHANNEL_DEFAULT_ID = "com.infinisoftware.testnotifs.DEFAULT_CHANNEL";
    private static final String CHANNEL_DEFAUL_NAME = "Default Channel";


    public NotificationHelper( Context base ) {
        super( base );

        notifManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        long [] swPattern = new long[] { 0, 500, 110, 500, 110, 450, 110, 200, 110,
                170, 40, 450, 110, 200, 110, 170, 40, 500 };

        NotificationChannel notificationChannelHigh = new NotificationChannel(
                CHANNEL_HIGH_ID, CHANNEL_HIGH_NAME, notifManager.IMPORTANCE_HIGH );
        notificationChannelHigh.enableLights( true );
        notificationChannelHigh.setLightColor( Color.RED );
        notificationChannelHigh.setShowBadge( true );
        notificationChannelHigh.enableVibration( true );
        notificationChannelHigh.setVibrationPattern( swPattern );
        notificationChannelHigh.setLockscreenVisibility( Notification.VISIBILITY_PUBLIC );
        notifManager.createNotificationChannel( notificationChannelHigh );

        NotificationChannel notificationChannelDefault = new NotificationChannel(
                CHANNEL_DEFAULT_ID, CHANNEL_DEFAUL_NAME, notifManager.IMPORTANCE_DEFAULT );
        notificationChannelDefault.enableLights( true );
        notificationChannelDefault.setLightColor( Color.WHITE );
        notificationChannelDefault.enableVibration( true );
        notificationChannelDefault.setShowBadge( false );
        notifManager.createNotificationChannel( notificationChannelDefault );
    }


    public void notify( int id, boolean prioritary, String title, String message ) {
        String channelId = prioritary ? CHANNEL_HIGH_ID : CHANNEL_DEFAULT_ID;
        Resources res = getApplicationContext().getResources();
        Context context = getApplicationContext();

        /* Lien avec l'activité à ouvrir : ici MainActivity */
        Intent notificationIntent = new Intent(context, MainActivity.class);
        PendingIntent contentIntent = PendingIntent.getActivity(
                context, 456, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);

        Notification notification = new Notification.Builder( getApplicationContext(), channelId )
                .setContentIntent( contentIntent )      // On injecte le contentIntent
                .setContentTitle( title )
                .setContentText( message )
                .setSmallIcon( R.drawable.ic_launcher )
                .setLargeIcon( BitmapFactory.decodeResource(res, R.drawable.ic_launcher) )
                .setAutoCancel( true )
                .build();

        notifManager.notify( id, notification );
    }

    public void cancelNotification( int id ) {
        notifManager.cancel( id );
    }

}
Ajout d'une méthode de suppression de notifications