📜 ⬆️ ⬇️

Notifications based on the builder with custom layout and picture

Greetings

I indulge in developing applications for Android, but so far I have not used Builder to create notifications, but I did it using the good old method, as described, for example, in this article . However, this method is not only outdated, but even more - it is deprecated. In addition, I was still faced with the task of displaying my own image in each Notification, which is not included in the project and I cannot refer to it through R.drawable, such as the user's avatar, which I add when using the application etc. If interested - welcome under cat.

Builder for creating Notifications is introduced with AOC 3.0 and if the minimum SDK level for the application is lower, as in my case, then you need to use the v4 compatibility library , since I use to develop AndroidStudio, then the inclusion of the library in the project consists in adding it to build.gradle in the dependencies section:

dependencies { compile 'com.android.support:support-v4:20.0.0' } 

Anyone who uses the good old Eclipse to develop can find the corresponding jar-ku in the folder where the Android SDK is installed in the folder / extras / android / support / v4 / and copy it to the libs folder of your project.
')
Actually, I wrote a small helper class, NotificationHelper, for creating notifications. Usually I fill such static classes with methods of this kind, and if a reference to Context is required inside, I initialize such a helper from the Application class using the Context of the application itself, i.e. Application Context. It is absolutely safe to keep this context even in a static field, unlike the Activity that cannot be stored in statics, in order to avoid leaks, in short, the Activity Context (aka the Base Context) I prefer to never store at all. Total NotificationHelper looks like this:

 public class NotificationsHelper { private static Context appContext; //   private static int lastNotificationId = 0; //   private static NotificationManager manager; //   //     public static void init(Context context){ if(manager==null){ appContext = context.getApplicationContext(); //    Base Context- manager = (NotificationManager) appContext.getSystemService(Context.NOTIFICATION_SERVICE); } } /** *     NotificationCompat.Builder * @return */ private static NotificationCompat.Builder getNotificationBuilder(){ final NotificationCompat.Builder nb = new NotificationCompat.Builder(appContext) .setAutoCancel(true) //        .setOnlyAlertOnce(true) //   .setWhen(System.currentTimeMillis()) //   ,       .setContentTitle(appContext.getString(R.string.app_name)) // .setDefaults(Notification.DEFAULT_ALL); // alarm   : ,   - -   return nb; } //   ,   public static void cancelAllNotifications(){ manager.cancelAll(); } //   ,    } 

Since In the initialization method, I registered appContext = context.getApplicationContext (), then this helper can be initialized from anywhere, the main thing is to have access to the context, you can even by activating by passing the activation itself as a parameter.

The method used to create a normal, standard notification I created is as follows:

  /** * * @param message -   * @param targetActivityClass -    * @param iconResId - R.drawable   * @return */ public static int createNotification(final String message, final Class targetActivityClass, final int iconResId) { //    null  ,   NPE? if (targetActivityClass==null){ new Exception("createNotification() targetActivity is null!").printStackTrace(); return -1; } if (manager==null){ new Exception("createNotification() NotificationUtils not initialized!").printStackTrace(); return -1; } final Intent notificationIntent = new Intent(appContext, targetActivityClass); //     Activity     final NotificationCompat.Builder nb = getNotificationBuilder() //    generic Builder,     .setContentText(message) // ,       .setTicker(message) //,     -   ,    .setSmallIcon(iconResId != 0 ? iconResId : R.drawable.ic_launcher) // ,  0,      .setContentIntent(PendingIntent.getActivity(appContext, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT)); //  PendingIntent- final Notification notification = nb.build(); // , getNotification() - deprecated! manager.notify(lastNotificationId, notification); // ""  return lastNotificationId++; } 

This method is called, for example, from activit, like this:

 NotificationsHelper.createNotification("Achtung message!", MessagesActivity.class, 0); 

This is the usual standard notice. In principle, since for me in all applications the application icon is always called ic_launcher, and not in any other way, this helper is universal for me. Those. it can be included without changes in any application where it is required.

But the version with a picture that is not in the project, as I noted above, can no longer be made as universal, because it requires layout in addition. It is this layout that makes it possible to display anything, and I'll start with it, perhaps (notification_layout.xml):

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="10dp"> <ImageView android:id="@+id/notification_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher" /> <TextView android:id="@+id/notification_message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="10dp" /> </LinearLayout> 

I think everything here is clear from the id-shek names themselves:
notification_image - a place under the avatar
notification_message - a place to display notification text.

And the actual method of creating a notification with a picture loaded by the application during operation:

  /** * * @param message -  * @param targetActivityClass -    * @param icon -  () * @return */ public static int createNotification(final String message, final Class targetActivityClass, final Bitmap icon){ //     null if (targetActivityClass==null){ new Exception("createNotification() targetActivity is null!").printStackTrace(); return -1; } if (manager==null){ new Exception("createNotification() NotificationUtils not initialized!").printStackTrace(); return -1; } //   RemoteViews        final RemoteViews contentView = new RemoteViews(appContext.getPackageName(), R.layout.notification_layout); contentView.setTextViewText(R.id.notification_message, message); //   contentView.setImageViewBitmap(R.id.notification_image, icon); //   ,   ,   final Intent notificationIntent = new Intent(appContext, targetActivityClass); //     Activity     final NotificationCompat.Builder nb = getNotificationBuilder() //  - .setTicker(message) // ,     -    .setContentIntent(PendingIntent.getActivity(appContext, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT)) .setSmallIcon(R.drawable.ic_launcher); //   ,        //.setContent(contentView); //    2.3.*,   -    // . http://stackoverflow.com/questions/12574386/custom-notification-layout-dont-work-on-android-2-3-or-lower final Notification notification = nb.build(); //  notification.contentView = contentView; //  setContent()     ,   manager.notify(lastNotificationId, notification); // ""  return lastNotificationId++; } 

This method is called, for example, from activit, like this:

 Bitmap avatar = getAvatarBitmap(); //           Bitmap NotificationsHelper.createNotification("Achtung message", MessagesActivity.class, avatar); 

Actually, that's all. Almost…
Still, the nuances remain, for example, if an application consists of several activations, then it is stupid to launch a specific activation through a notification - it is not entirely correct. After all, firstly, this activation can just be opened in the application, and thus, through notification, such activation will be launched again. This can be circumvented by specifying the following launch type flag in the manifest for such an activation:

 android:launchMode="singleTop" 

But this is to exclude the re-launch of the active (topmost) activation, i.e. if the activation is started (on the stack), but some other is “covered”, then the method will not help. You can try to choose another of the possible parameters of the type of launch.
And what if the application is not running? After all, it is impossible to take and launch a separate activation, which is usually launched in the application through the chain through other activations, and it cannot be launched separately from the others, and the exit from it will result in the exit from the application, instead of the user’s expected return to the previous "screen". And if in the application the start-up activation is online authorization, which is a prerequisite for the application to continue, then what? It turns out that we will launch some kind of activation, bypassing authorization, and this can lead to unpredictable consequences. You can limit yourself to calling the main, start-up activation from the notification, but again, this is good when the application is not running, but when the user is already in the application, then why would he need to re-authorize? Only the payload of the notification will help here, i.e. passing a Bundle in it with certain parameters.

I personally decided this as follows: I created the NotificationActivity stub activation, which is called from the notification, and I throw in the alert bundle of the activations that I really need to run, I check the conditions in NotificationActivity and perform the necessary actions:

 public class NotificationActivity extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Bundle extras = getIntent().getExtras(); if (extras!=null && extras.containsKey(KEY_EXTRAS_TARGET_ACTIVITY)){ if (isAppRunning()) { if (LoginActivity.isRunning() /*isActivityRunning(LoginActivity.class)*/) { // } else if (MainActivity.isRunning() /*isActivityRunning(MainActivity.class)*/) { startActivity(new Intent(this, (Class) extras.getSerializable(KEY_EXTRAS_TARGET_ACTIVITY))); } else { final Intent intent = new Intent(getBaseContext(), LoginActivity.class); intent.putExtras(extras); startActivity(intent); } } else { final Intent intent = new Intent(this, LoginActivity.class); intent.putExtras(extras); startActivity(intent); } } finish(); return; } private boolean isAppRunning() { final String process = getPackageName(); final ActivityManager activityManager = (ActivityManager) getSystemService( ACTIVITY_SERVICE ); List<ActivityManager.RunningAppProcessInfo> procInfos = activityManager.getRunningAppProcesses(); for(int i = 0; i < procInfos.size(); i++){ if(procInfos.get(i).processName.equals(process)) { return true; } } return false; } //         : // <uses-permission android:name="android.permission.GET_TASKS"/> private boolean isActivityRunning(Class activityClass) { ActivityManager activityManager = (ActivityManager) getBaseContext().getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(Integer.MAX_VALUE); for (ActivityManager.RunningTaskInfo task : tasks) { if (activityClass.getCanonicalName().equalsIgnoreCase(task.baseActivity.getClassName())) return true; } return false; } } 

Here I commented on the calls to the isActivityRunning () method, since this requires another permission in the manifest. To whom this is uncritical, he can calmly remove the call to the static method before the comment, and uncomment the call to this method. I decided that it’s better to avoid Mans with a manifest and therefore I wrote static methods for both activites:

  private static boolean isRunning; public static boolean isRunning(){return isRunning;} @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); isRunning = true; ... } @Override protected void onDestroy() { super.onDestroy(); isRunning = false; } 

Changes required for this in the notification helper (in both createNotification () methods):

 ... final Intent notificationIntent = new Intent(appContext, NotificationActivity.class); notificationIntent.putExtra(KEY_EXTRAS_TARGET_ACTIVITY, targetActivityClass); ... 

Well, it seems that all ...

Source: https://habr.com/ru/post/244423/


All Articles