Android Notification Channel, Notification Dots

Filed Under: Android

In this tutorial, we’ll be looking into how the introduction of Android Oreo has brought a drastic change in the Notifications. You aren’t far away from getting an Android Oreo update. Before you do get, let’s look into the working of notifications and what you need to change and adapt as Android Developer.

Android Notification Channel

We have discussed and implemented Notification, here and here.

With the introduction of Android Oreo, Google has strived the Notifications system more user-friendly. Android Oreo has completely redesigned notifications. The power to receive the kinds of notifications has been given in the hands of the end users. The reason this all became possible is: Notification Channels.
android oreo notification channel
Notification Channels allow us to separate notifications into different groups/categories. Every channel would have a common functionality. It allows the user to customize their notification settings.

Thanks to this feature the user can do the following things from the Apps Settings:

  • Block notifications from a particular channel.
  • Set priority/silent on different notification channels.

Without configuring Notification Channels, you cannot build notification for applications with Android API >=26. Notification Channels would be ignored for older applications with the Android API < 26. Let's get down to the creation part.

Creating Notification Channel

Following code creates a notification channel:


NotificationChannel notificationChannel = new NotificationChannel(channel_id , channel_name, NotificationManager.IMPORTANCE_HIGH);
                notificationChannel.enableLights(true);
                notificationChannel.enableVibration(true);
                notificationChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(notificationChannel);

The NotificationChannel constructor requires us to specify the channel_id and channel_name strings. The Importance argument is an int which specifies the level of interruption by the notification. It can be one of the following values:

  • IMPORTANCE_DEFAULT – Shows up in the system tray. Makes sound. Doesn’t visually pop up.
  • IMPORTANCE_HIGH – Visually pops up too.
  • IMPORTANCE_LOW – Shows in the tray. No pop up. No sound.
  • IMPORTANCE_NONE – Doesn’t show up. Kind of blocked notifications.

Besides the public methods specified above, following are some handy methods that come with NotificationChannels.

  • setGroup()/getGroup() – Setters and getters for the channel. We’ll look at this later.
  • ?

  • setBypassDnd()?- Set the INTERRUPTION_PRIORITY_VALUE to bypass do not disturb.
  • canBypassDnd() – Check whether the notification channel can display notification in DND mode.
  • setLockScreenVisibility() – Set whether notifications from this channel should be displayed on the lock screen or not.
  • canShowBadge() – Can show the badge/notification dot on the application icon.
  • getName()/getId() – Retrieve the channel name and id respectively.

Once the Notification Channel is created using createNotificationChannel(), every notification created from it will have common properties unless modified.

Note: The above code snippet is valid for Android version Oreo and above only. Hence it must be enclosed in the following condition.


if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
}

Creating a Notification

The following code snippet creates a Notification from NotificationChannel.


NotificationCompat.Builder notification = new NotificationCompat.Builder(this, "channel_id")
                            .setContentTitle("Test Title")
                            .setContentText("Test Message")
                            .setSmallIcon(R.mipmap.ic_launcher);

notificationManager.notify(1, notification.build());

From Android Oreo, it is mandatory to specify the NotificationChannel id in the Builder constructor itself.

Reading And Deleting Notification Channels

To retrieve a notification channel we can call the method getNotificationChannel() on the NotificationManager.
We need to pass the channel_id of the relevant channel.
Also, to retrieve a list of all NotificationChannels we can invoke the method getNotificationChannels().


List<NotificationChannel> notificationChannels = notificationManager.getNotificationChannels();

Deleting a NotificationChannel
To delete a NotificationChannel, the following snippet is used.


notificationManager.deleteNotificationChannel("channel_id");

Notification Channel Groups

A NotificationChannelGroup is used to created different categories for NotificationChannels.
The same NotificationChannels can be used in different circumstances depending on the group from which they are invoked.
You can have two groups named Hourly, Daily. All the Notification Channels would be present in both the groups. It’s your choice from which group you want to use the channel to create Notifications.

Creating NotificationChannelGroup

Following is one way to create NotificationChannelGroups.


List<NotificationChannelGroup> list = new ArrayList<>();
            list.add(new NotificationChannelGroup(group_id_1, group_name_2));
            list.add(new NotificationChannelGroup(group_id_2, group_name_2));

            notificationManager.createNotificationChannelGroups(list);

You need to set the group_id and group_name.
Furthermore, you need to set the group on the NotificationChannel too using setGroup(). Pass in the group_id in the setGroup() method.

Modifying Notification from Settings

The end user can modify the Notification Channel from the Settings.
Settings | Apps | App Name | App Notifications

android notification channel settings

Alternatively we can redirect them from the app itself using Intents.


Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_CHANNEL_ID, notificationChannel.getId());
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
startActivity(intent);

We pass the channel id and app package name. This specifically opens the particular channel ID.

android notificationchannel channel settings

Let’s create a basic application with the use cases of NotificationChannel and NotificationChannelGroup

Project Structure

In this application, we’ll be using RadioButton to change between the groups and Spinners to choose the current NotificationChannel. The EditText would be used to set the body of the notification.

Android NotificationChannel Project Structure

Android NotificationChannel Code

The code for the activity_main.xml layout file is given below.


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Choose the group for the channel"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:padding="16dp"
        app:layout_constraintBottom_toTopOf="@+id/radioGroup"/>

    <RadioGroup
        android:id="@+id/radioGroup"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:orientation="horizontal"
        android:weightSum="2"
        app:layout_constraintBottom_toTopOf="@+id/spinner"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent">

        <RadioButton
            android:id="@+id/radioButton1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="First Type"
            android:layout_weight="1"
            android:textSize="18sp" />

        <RadioButton
            android:id="@+id/radioButton2"
            android:layout_width="wrap_content"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="Second Type"
            android:textSize="18sp" />
    </RadioGroup>


    <Spinner
        android:id="@+id/spinner"
        style="@style/Widget.AppCompat.Spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        app:layout_constraintBottom_toTopOf="@+id/inContent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />


    <EditText
        android:id="@+id/inContent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:hint="Enter message"
        app:layout_constraintBottom_toTopOf="@+id/btnNotification"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />


    <Button
        android:id="@+id/btnNotification"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Create New Notification"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


</android.support.constraint.ConstraintLayout>

The code for the MainActivity.java class is given below.


package com.journaldev.notificationchannels;

import android.app.AlertDialog;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.provider.Settings;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {


    List<String> data = new ArrayList<>();

    int notifier_counter = 0;
    NotificationManager notificationManager;
    RadioButton radioButtonFirst;
    RadioButton radioButtonSecond;
    RadioGroup radioGroup;
    EditText editText;
    Button btnNotification;
    Spinner spinner;

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

        initViews();
        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);


        data.add("Bitcoin");
        data.add("Ethereum");
        data.add("Litecoin");
        data.add("Ripple");

        createNotificationGroups();
        createNotificationChannels();


        ArrayAdapter<String> dataAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, data);
        dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(dataAdapter);
        spinner.setOnItemSelectedListener(this);


        radioGroup.check(R.id.radioButton1);


        btnNotification.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (editText.getText().toString().length() > 0) {
                    String channel_id = "";
                    String group_id = "";
                    PendingIntent contentIntent = PendingIntent.getActivity(MainActivity.this, 0, new Intent(MainActivity.this, MainActivity.class), 0);

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {


                        RadioButton radioButton = findViewById(radioGroup.getCheckedRadioButtonId());
                        group_id = radioButton.getText().toString();
                        channel_id = notificationManager.getNotificationChannel(spinner.getSelectedItem().toString() + "_" + group_id).getId();
                        contentIntent = PendingIntent.getActivity(MainActivity.this, 0, new Intent(MainActivity.this, MainActivity.class).putExtra("importance", notificationManager.getNotificationChannel(channel_id).getImportance()).putExtra("channel_id", channel_id), PendingIntent.FLAG_UPDATE_CURRENT);
                    }


                    NotificationCompat.Builder notification = new NotificationCompat.Builder(MainActivity.this, channel_id)
                            .setContentTitle(spinner.getSelectedItem().toString())
                            .setContentText(editText.getText().toString())
                            .setGroup(group_id)
                            .setContentIntent(contentIntent)
                            .setSmallIcon(R.mipmap.ic_launcher);

                    notifier_counter++;
                    notificationManager.notify(notifier_counter, notification.build());
                } else {
                    Toast.makeText(getApplicationContext(), "Please enter something in EditText", Toast.LENGTH_LONG).show();
                }
            }
        });
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {

    }

    private void initViews() {
        spinner = findViewById(R.id.spinner);
        btnNotification = findViewById(R.id.btnNotification);
        editText = findViewById(R.id.inContent);
        radioGroup = findViewById(R.id.radioGroup);
        radioButtonFirst = findViewById(R.id.radioButton1);
        radioButtonSecond = findViewById(R.id.radioButton2);
    }

    private void createNotificationGroups() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            List<NotificationChannelGroup> list = new ArrayList<>();
            list.add(new NotificationChannelGroup(radioButtonFirst.getText().toString(), radioButtonFirst.getText()));
            list.add(new NotificationChannelGroup(radioButtonSecond.getText().toString(), radioButtonSecond.getText()));

            notificationManager.createNotificationChannelGroups(list);

        }
    }


    private void createNotificationChannels() {
        for (String s : data) {
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {

                NotificationChannel notificationChannel = new NotificationChannel(s + "_" + radioButtonFirst.getText().toString(), s, NotificationManager.IMPORTANCE_HIGH);
                notificationChannel.enableLights(true);
                notificationChannel.enableVibration(true);
                notificationChannel.setGroup(radioButtonFirst.getText().toString());
                notificationChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});

                NotificationChannel notificationChannel2 = new NotificationChannel(s + "_" + radioButtonSecond.getText().toString(), s, NotificationManager.IMPORTANCE_NONE);
                notificationChannel2.enableLights(true);
                notificationChannel2.enableVibration(true);
                notificationChannel2.setGroup(radioButtonSecond.getText().toString());
                notificationChannel2.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});


                if (notificationManager != null) {
                    notificationManager.createNotificationChannel(notificationChannel);
                    notificationManager.createNotificationChannel(notificationChannel2);
                }
            }
        }
    }


    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            final Bundle bundle = intent.getExtras();
            int importance = -1;
            if (bundle != null) {
                importance = bundle.getInt("importance");
            }
            if (importance != -1) {

                AlertDialog.Builder alert = new AlertDialog.Builder(this);
                alert.setMessage("Goto settings to change the Notification channel")
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                updateNotificationSettings(bundle.getString("channel_id"));
                            }
                        }).setNegativeButton("CANCEL", null)
                        .show();


            }

        }


    }

    private void updateNotificationSettings(String channel_id) {
        Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
        intent.putExtra(Settings.EXTRA_CHANNEL_ID, channel_id);
        intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
        startActivity(intent);
    }


}

In the above code, we begin by creating the NotificationChannelGroup and NotificationChannel for the RadioButtons and Spinners respectively.

The channel_id is appended with the group_id in order to know which group does the channel belong to.

On Button click we create the notification based on which radio button and spinners were selected and retrieve the channel_id and group_id accordingly.

For this example, by default, we’ve blocked notifications from all the channels belonging to second group.
(Enable that channel from the settings to view those notifications).

On a notification click, we show up a dialog that allows the user to goto the settings to change the current NotificationChannel settings.

On Notification click launches the same activity. Hence in the Manifest file we need to set android:launchMode="singleTop" in the activity tag.

Output

The output of the above application in action is given below.

android notification channel example

Long Press on a Notification, it allows us to change the current channel settings with the slider.

The All categories option that’s visible lets us view all types of channels and groups.

Do take note that we’d toggled a few channels from the first and second group. It lead to blocked notifications in the first group and default notifications in the second.

Android Notification Dots

Notification Dots/Badges are displayed on the app icon if there are unread notifications.

We can use the method setShowBage(boolean) on the notification to display/hide the dot for the particular channel.

Thanks to Notification Dots, long pressing the app icon can now display/cancel the pending notifications too as shown below.

android notification dots example

Do note that the count for the pending notifications is displayed in the Notification Dot menu too.

This brings an end to this tutorial. You can download the final Android NotificationChannels Project from the link below.

Comments

  1. Aamil Silawat says:

    Great Keep Sharing 😀

  2. vaishali idnani says:

    my build configuration is
    compileSdkVersion 23
    buildToolsVersion ‘26.0.2’
    dexOptions {
    incremental = true;
    preDexLibraries = false
    javaMaxHeapSize “4g” // 2g should be also OK
    }
    defaultConfig {

    minSdkVersion 17
    targetSdkVersion 28
    }

    i am unable to add notification Channel . Can please help me what i need to change

    1. saeed says:

      ADD THIS

      implementation ‘com.android.support:appcompat-v7:26.1.0’

  3. aleesha says:

    could u please help to broadcast the battery status notification with a sound ??

  4. mernda says:

    it’s really helpful i love this site really <3

    1. Anupam says:

      Glad this helped you out Mernda.
      And I love this site too. <3

  5. Shruti says:

    Really helpful with notification channel.

    Great demo.

    But did not understand that do we need to call channel method each time?

    Thanks,

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages