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.
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> |
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.
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" ); } }); } } |
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.
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 ); } } |
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"); } }); } } |
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 ); } |
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"); } }); } } |
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 ); } } |
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 :