Affiliate links on Android Authority may earn us a commission. Learn more.
Implementing Notification Channels for Android app developers
Starting with Android 8.0 you can separate your app’s notifications into notification channels. All notifications that are posted to the same channel have the same behaviour, for example you could create a channel for your app’s most urgent notifications, where each notification is announced with an alert sound, vibration and a notification light, and then create “quieter” channels for the rest of your app’s notifications.
Android Oreo also gives users more control over notifications than ever before, as in Android 8.0 users can modify the settings for any notification channel that’s present on their device.
Notification channels have a lot to offer, but there’s an even more pressing reason to implement notification channels in your Android Oreo apps: you don’t have a choice! If your app targets Android 8.0 and you attempt to post a notification without assigning it to a notification channel, then that notification won’t be displayed to the user.
In this article, we’re going to get to grips with this new feature, by creating an app that consists of two notification channels, each with its own unique settings. I’ll also show you how to provide a better user experience, by making it easy for your users to customize each of your notification channel’s settings.
Prepare your development environment
Before you can develop for Android Oreo, you need to make sure you have the following or higher installed:
- Android Studio 3.0 Beta.
- SDK Build Tools 26.0.1.
- SDK Platform Tools 26.0.0.
- Android Emulator 26.1.4.
- Android Support Library 26.0.2.
- The Android O SDK.
If your Android smartphone or tablet hasn’t received the Android 8.0 update yet, then you’ll also need to create an Android Virtual Device (AVD). Select ‘Tools > Android > AVD Manager’ from the Android Studio toolbar, click the ‘Create Virtual Device…’ button, and then select ‘Oreo – API Level 26’ as your system image.
The final bit of prep, is creating a new project that targets Android 8.0 Oreo (API level 26), and uses the ‘Empty Activity’ template.
Create your layout
In this application, we’re focusing on the process of implementing a notification channel, so I’m just going to create two notification channels (Channel One and Channel Two) and assign them some random settings. However, when you’re implementing notification channels in your own apps, you’ll need to consider factors such as all the different kinds of notifications your app can generate; potential ways of grouping these notifications together; and the behavior each “type” of notification should have, as all of these will have an impact on what notification channel(s) you need to create. You should also be critical about the number of notification channels you create, as more channels mean more opportunities for user customization – but create too many channels and you run the risk of overwhelming the user.
In our layout, I’m going to create the following UI elements for each of our notification channels:
- An EditText, where the user can type some text that’ll be used as the title for the subsequent notification.
- A ‘Post Notification’ button, that the user can press to post a new notification.
- A ‘Settings’ button, that’ll provide the user with easy access to each channel’s notification settings menu.
Let’s add all of these elements to our activity_main.xml file:
<?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="match_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:orientation="vertical"
android:id="@+id/activity_main">
<EditText
android:id="@+id/channel_one_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/channel_one_text"/>
<Button
android:id="@+id/post_to_channel_one"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/button_label" />
<Button
android:id="@+id/channel_one_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings" />
<EditText
android:id="@+id/channel_two_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/channel_two_text"/>
<Button
android:id="@+id/post_to_channel_two"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/button_label" />
<Button
android:id="@+id/channel_two_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings" />
</LinearLayout>
We also need to create a few strings:
<resources>
<string name="app_name">Notification Channels</string>
<string name="channel_one_text">Channel One</string>
<string name="channel_two_text">Channel Two</string>
<string name="button_label">Post Notification</string>
<string name="settings">Settings</string>
<string name="channel_one_body">Notification Body Text</string>
<string name="channel_two_body">Notification Body Text</string>
</resources>
Create a Notification Channel Helper Class
In this section, we’re going to look at the process of creating a notification channel, and how you assign a notification to a specific channel.
I’m going to include this functionality in a separate class, so:
- Control-click your project’s root package.
- Select ‘New > Java Class.’
- Name this class NotificationHelper, and then click ‘OK.’
1. Create your first notification channel
To create a notification channel, you need to construct a NotificationChannel object and pass it the following:
- An ID. This must be unique to your package.
- The channel’s name. This label will be displayed in the channel’s settings screen.
- An importance level. In Android Oreo you can no longer set the priority level for individual notifications. Instead, you need to specify the channel’s importance level, which is then applied to every notification that’s posted to this channel.
This is also your chance to set the channel’s default behavior, including whether this channel will display a notification badge (sometimes known as a notification dot) on supported launchers. When an app has notifications that the user hasn’t yet acted on, Android Oreo can display a badge next to this application’s icon. The user can then view more information about these notifications, by long-pressing the application icon.
In the following code, I’m creating a notification channel and defining the NotificationManager:
class NotificationHelper extends ContextWrapper {
private NotificationManager notifManager;
//Set the channel’s ID//
public static final String CHANNEL_ONE_ID = "com.jessicathornsby.myapplication.ONE";
//Set the channel’s user-visible name//
public static final String CHANNEL_ONE_NAME = "Channel One";
...
...
...
// Create the channel object, using the channel ID//
NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ONE_ID,
CHANNEL_ONE_NAME, notifManager.IMPORTANCE_HIGH);
//Configure the channel’s initial settings//
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setShowBadge(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
You then need to submit this NotificationChannel object to the NotificationManager, using createNotificationChannel(). Note that once you’ve submitted the channel object, you can no longer edit that channel’s settings (with the exception of updating the channel name and channel description) so make sure you’re happy with your channel’s configuration before calling the createNotificationChannel() method.
getManager().createNotificationChannel(notificationChannel);
To create Channel Two, simply repeat the above code with a different channel ID and channel name, and the channel settings of your choice – or take a look at the complete NotificationHelper class, which I’ve included later in this tutorial.
2. Assigning a notification to a channel
If your app targets Android 8.0 then you must specify a notification channel every time you create a notification, by passing the channel’s ID to the Notification.Builder constructor:
public Notification.Builder getNotification1(String title, String body)
//Pass the notification channel’s ID as the second argument//
return new Notification.Builder(getApplicationContext(), CHANNEL_ONE_ID)
//Set the notification’s title, which in this instance will be the contents of our EditText//
.setContentTitle(title)
//Set the notification’s body text//
.setContentText(body)
//Set the notification’s icon, which we’ll be creating later//
.setSmallIcon(R.drawable.warning)
.setAutoCancel(true);
}
Alternatively, you can assign your notification to a channel using the setChannelId() method.
If your app ever attempts to post a notification without specifying a notification channel, then that notification won’t appear onscreen. To help you catch this error during testing, you may want to tell Android 8.0 to display a toast everytime your app tries to post a notification that doesn’t have an assigned channel:
- Open your device’s ‘Settings’ app.
- Navigate to ‘System > Developer options.’
- Push the ‘Show notification channel warnings’ slider to the ‘On’ position.
If ‘Developer Options’ isn’t appearing in your settings, then you can enable it by navigating to ‘Settings > System > About emulated device’ and tapping the build number seven times.
The complete NotificationHelper class
Once you know how to create one channel and assign a notification to this channel, you have the formula for creating subsequent channels and notifications, as you can see in our complete NotificationHelper code:
package com.jessicathornsby.myapplication;
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;
public static final String CHANNEL_ONE_ID = "com.jessicathornsby.myapplication.ONE";
public static final String CHANNEL_ONE_NAME = "Channel One";
public static final String CHANNEL_TWO_ID = "com.jessicathornsby.myapplication.TWO";
public static final String CHANNEL_TWO_NAME = "Channel Two";
//Create your notification channels//
public NotificationHelper(Context base) {
super(base);
createChannels();
}
public void createChannels() {
NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ONE_ID,
CHANNEL_ONE_NAME, notifManager.IMPORTANCE_HIGH);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setShowBadge(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
getManager().createNotificationChannel(notificationChannel);
NotificationChannel notificationChannel2 = new NotificationChannel(CHANNEL_TWO_ID,
CHANNEL_TWO_NAME, notifManager.IMPORTANCE_DEFAULT);
notificationChannel2.enableLights(false);
notificationChannel2.enableVibration(true);
notificationChannel2.setLightColor(Color.RED);
notificationChannel2.setShowBadge(false);
getManager().createNotificationChannel(notificationChannel2);
}
//Create the notification that’ll be posted to Channel One//
public Notification.Builder getNotification1(String title, String body) {
return new Notification.Builder(getApplicationContext(), CHANNEL_ONE_ID)
.setContentTitle(title)
.setContentText(body)
.setSmallIcon(R.drawable.warning)
.setAutoCancel(true);
}
//Create the notification that’ll be posted to Channel Two//
public Notification.Builder getNotification2(String title, String body) {
return new Notification.Builder(getApplicationContext(), CHANNEL_TWO_ID)
.setContentTitle(title)
.setContentText(body)
.setSmallIcon(R.drawable.alert)
.setAutoCancel(true);
}
public void notify(int id, Notification.Builder notification) {
getManager().notify(id, notification.build());
}
//Send your notifications to the NotificationManager system service//
private NotificationManager getManager() {
if (notifManager == null) {
notifManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
return notifManager;
}
}
Loading the notification channel’s settings
Now let’s switch to our MainActivity class. When it comes to implementing notification channels, the most important task we’re going to perform in MainActivity is linking each ‘Settings’ button to the screen that contains the settings for each of our notification channels.
By default, users can change the settings for any notification channel by opening their device’s ‘Settings’ app and navigating to ‘Apps & Notifications > Notifications > Notifications.’ They can then select any app from this list, to get access to a number of settings that control how this app manages notifications in general.
From here, the user can gain access to the settings screen for a specific notification channel, by selecting that channel from the ‘Categories’ list (note that channels are referred to as ‘Categories’ in Oreo’s user interface).
Alternatively, users can jump directly to a channel’s settings, by long-pressing a notification that belongs to that channel, and then tapping ‘All Categories.’
While you may not be too keen on the idea of users messing around with your carefully-crafted channel settings, the ability to customize an app’s notification channels has huge benefits for the user. And, even in the worst case scenario where someone uses these settings to disable one (or even all) of your notification channels, this is still preferable to them getting so frustrated with your app’s notifications that they wind up uninstalling your app.
While users can modify any notification channel via their device’s ‘Settings’ app, giving your users a way of accessing this menu directly from your application is going to make for a much better user experience.
To link our ‘Settings’ buttons to each of our notification channel’s settings, we need to create an intent and pass it the action Settings.ACTION_APP_NOTIFICATION_SETTINGS. We also need to make sure the system is launching the correct settings screen, by passing it our application’s package name, and EXTRA_CHANNEL_ID as intent extras.
public void goToNotificationSettings(String channel) {
Intent i = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
i.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
i.putExtra(Settings.EXTRA_CHANNEL_ID, channel);
//Start the Activity with the intent//
startActivity(i);
}
...
...
...
case R.id.channel_one_settings:
goToNotificationSettings(NotificationHelper.CHANNEL_ONE_ID);
break;
To provide the user with a shortcut to your app’s general notification settings, simply repeat the above code with the channel ID removed:
public void goToNotificationSettings() {
Intent i = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
i.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
startActivity(i);
}
The complete MainActivity class
In addition to loading the settings screens, we also need to ensure that whenever the user taps one of our ‘Post Notification’ buttons, our app retrieves the contents of the corresponding EditText, incorporates it into a notification, and then posts this notification to the appropriate channel.
Here’s the finished MainActivity class, complete with onClickListeners and our settings code:
package com.jessicathornsby.myapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.content.Intent;
import android.app.Notification;
import android.provider.Settings;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final int notification_one = 101;
private static final int notification_two = 102;
private MainUi mainUI;
private NotificationHelper notificationHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
notificationHelper = new NotificationHelper(this);
mainUI = new MainUi(findViewById(R.id.activity_main));
}
//Post the notifications//
public void postNotification(int id, String title) {
Notification.Builder notificationBuilder = null;
switch (id) {
case notification_one:
notificationBuilder = notificationHelper.getNotification1(title,
getString(R.string.channel_one_body));
break;
case notification_two:
notificationBuilder = notificationHelper.getNotification2(title,
getString(R.string.channel_two_body));
break;
}
if (notificationBuilder != null) {
notificationHelper.notify(id, notificationBuilder);
}
}
//Load the settings screen for the selected notification channel//
public void goToNotificationSettings(String channel) {
Intent i = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
i.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
i.putExtra(Settings.EXTRA_CHANNEL_ID, channel);
startActivity(i);
}
//Implement our onClickListeners//
class MainUi implements View.OnClickListener {
final EditText editTextOne;
final EditText editTextTwo;
private MainUi(View root) {
editTextOne = (EditText) root.findViewById(R.id.channel_one_text);
((Button) root.findViewById(R.id.post_to_channel_one)).setOnClickListener(this);
((Button) root.findViewById(R.id.channel_one_settings)).setOnClickListener(this);
editTextTwo = (EditText) root.findViewById(R.id.channel_two_text);
((Button) root.findViewById(R.id.post_to_channel_two)).setOnClickListener(this);
((Button) root.findViewById(R.id.channel_two_settings)).setOnClickListener(this);
}
//Retrieve the contents of each EditText//
private String getChannelOneText() {
if (editTextOne != null) {
return editTextOne.getText().toString();
}
return "";
}
private String getChannelTwoText() {
if (editTextOne != null) {
return editTextTwo.getText().toString();
}
return "";
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.post_to_channel_one:
postNotification(notification_one, getChannelOneText());
break;
case R.id.channel_one_settings:
goToNotificationSettings(NotificationHelper.CHANNEL_ONE_ID);
break;
case R.id.post_to_channel_two:
postNotification(notification_two, getChannelTwoText());
break;
case R.id.channel_two_settings:
goToNotificationSettings(NotificationHelper.CHANNEL_TWO_ID);
break;
}
}
}
}
Adding your notification icons
To create a clearer distinction between notifications that belong to Channel One, and notifications that belong to Channel Two, I’m going to assign a different notification icon to each channel.
The easiest way to create icons (including Android 8.0’s adaptive icons), is to use Android Studio’s Image Asset Studio. This Studio provides access to over 900 Material Design system icons, plus Image Studio will automatically generate alternate versions of your chosen drawable for each of the different generalized screen densities, and place them within the appropriate res/drawable directories.
To create your first notification icon:
- Control-click your project’s ‘res’ folder and select ‘New > Image Asset.’
- Open the ‘Icon type’ dropdown, and select ‘Notification Icons.’
- Give your icon the name ‘alert.’
- Select the ‘Clip art’ radio button.
- Click the button with the little Android on it (where the cursor is positioned in the following screenshot).
- In the subsequent popup, choose the icon you want to use for your first notification icon. I’m going to select ‘add alert.’ Click ‘OK.
- Click the ‘Next’ button.
- Check the information about where this icon is going to be stored, and if you’re happy to proceed click ‘Finish.’
Rinse and repeat to create the second notification icon (I’m using the ‘warning’ icon). Note that if you name these drawables anything other than ‘warning’ and ‘alert,’ you’ll need to modify the following lines in your NotificationHelper class:
.setSmallIcon(R.drawable.warning)
And:
.setSmallIcon(R.drawable.alert)
Run your app
It’s finally time to put your app to test, so launch a compatible AVD or attach a physical Android device to your development machine, and then select ‘Run > Run app’ from the Android Studio toolbar.
Once your app has finished loading, experiment with entering different text into the EditTexts, and posting these notifications to the different channels.
You can also change the settings for either notification channel, by clicking its corresponding ‘Settings’ button. If you do modify a channel’s settings, then just be aware that these customizations will remain in effect, even if you re-run your project from Android Studio. If you want to restore your channels to their default settings, then you’ll either have to do this manually by tapping the ‘Settings’ button, or by uninstalling and then re-installing your project.
Wrapping Up
If you want to try writing an app specifically for Android 8.0 Oreo, then testing out the new notification channels is a perfect way to start. However this isn’t just for those on the bleeding edge. Ultimately this new mechanism will be the only way to post notifications, so it is worth understanding it now!
How do you plan on using notification channels in your own Android projects? Let us know in the comments below!