Beginning with Android API level 26, persistent notifications are required for
foreground services. This requirement is meant to prevent you from hiding
services that might put excessive demands on system resources, including the
battery in particular. This requirement creates a potential problem: If an app
with multiple foreground services doesn't carefully manage the notification so
that it is shared across all services, then there can be multiple persistent
undismissable notifications, leading to unwelcomed clutter in the active list of
notifications.

This problem becomes more challenging when you use SDKs such as the Navigation
SDK, that run foreground services independent of the app that have their
own independent persistent notifications, making them difficult to consolidate.
To address these issues, the Navigation SDK v1.11 introduced a simple API to
help manage persistent notifications across the app, including within the SDK.

<br />

![Consolidate persistent notifications](https://developers.google.com/static/maps/documentation/navigation/android-sdk/images/shared_notifications2.png)

## Components

The foreground service manager provides a wrapper around the Android foreground
service class and the persistent notification class. This wrapper's main
function is to enforce reuse of the Notification ID so that the notification is
shared across all foreground services using the manager.

![](https://developers.google.com/static/maps/documentation/navigation/android-sdk/images/notification_components2.png)  

The Navigation SDK contains static methods for initializing and getting the
`ForegroundServiceManager` singleton. This singleton can only be initialized
once in the lifetime of the Navigation SDK. Consequently, if you use one of the
initialization calls (`initForegroundServiceManagerMessageAndIntent()` or
`initForegroundServiceManagerProvider()`), then you should surround
it with a try-catch block in case that path is reentered. The Navigation SDK
throws a runtime exception if you call either method more than once unless you
first clear all references to the `ForegroundServiceManager` and call
`clearForegroundServiceManager()` before each subsequent call.

The four parameters of `initForegroundServiceManagerMessageAndIntent()` are
`application`, `notificationId`, `defaultMessage`, and `resumeIntent`. If the
final three parameters are null, then the notification is the standard
Navigation SDK notification. It's still possible to hide other foreground
services in the app behind this notification. The `notificationId` parameter
specifies the notification ID that should be used for the notification. If it is
null, then an arbitrary value is used. You can set it explicitly to work around
conflicts with other notifications, such as those from another SDK. The
`defaultMessage` is a string that is displayed when the system is not
navigating. The `resumeIntent` is an intent that is fired when the notification
is clicked on. If the `resumeIntent` is null, then clicks on the notification
are ignored.

The three parameters of `initForegroundServiceManagerProvider()` are
`application`, `notificationId`, and `notificationProvider`. If the final
two parameters are null, then the notification is the standard Navigation SDK
notification. The `notificationId` parameter specifies the notification ID that
should be used for the notification. If it is null, then an arbitrary value is
used. You can set it explicitly to work around conflicts with other
notifications, such as those from another SDK. If the `notificationProvider` is
set, then the provider is always responsible for
generating the notification to be rendered.

The Navigation SDK `getForegroundServiceManager()` method returns the
foreground service manager singleton. If you haven't generated one yet, then
it's the equivalent of calling `initForegroundServiceManagerMessageAndIntent()`
with null parameters for the `notificationId`, `defaultMessage`, and
`resumeIntent`.

The `ForegroundServiceManager` has three simple methods. The first two are for
moving a service into and out of the foreground, and are typically called from
within the service that has been created. Using these methods ensures that the
services are associated with the shared persistent notification. The final
method, `updateNotification()`, flags the manager that the notification has
changed, and should be re-rendered.

If you need complete control of the shared persistent notification, then
the API provides a `NotificationContentProvider` interface for defining a
notification provider, which contains a single method for getting a notification
with the current content. It also provides a base class, which you can
optionally use to help define the provider. One of the base class's main
purposes is that it provides a way to call `updateNotification()` without the
need to access the `ForegroundServiceManager`. If you use an instance of the
notification provider to receive new notification messages, you can call this
internal method directly to render the message in the notification.

## Usage scenarios

This section details the usage scenarios for using shared persistent
notifications.

Hide persistent notifications of other app foreground services
:   The easiest scenario is to preserve current behavior, and only use the
    persistent notification for rendering Navigation SDK information. Other services
    can hide behind this notification by using the foreground service manager
    `startForeground()` and `stopForeground()` methods.

Hide persistent notifications of other app foreground services, but set
default text shown when not navigating
:   The second easiest scenario is to preserve current behavior, and only use
    the persistent notification for rendering Navigation SDK information, except
    when the system is not navigating. When the system is not navigating, the
    string provided to `initForegroundServiceManagerMessageAndIntent()`
    is displayed rather than the default Navigation SDK string that mentions
    "Google Maps". You can also use this call to set the resume intent that
    fires when the notification is clicked.

Take full control of the rendering of the persistent notification
:   The final scenario requires defining and creating a notification provider
    and passing it to the `ForegroundServiceManager` using
    `initForegroundServiceManagerProvider()`. This option gives you
    full control of what is rendered in the notification, but it also
    disconnects the Navigation SDK notification information from the
    notification, thereby removing the helpful turn-by-turn prompts shown in the
    notification. Google doesn't provide a simple means for retrieving this
    information and inserting it into the notification.

## Example notification provider

The following code example demonstrates how to create and return notifications
using a simple notification content provider.

    public class NotificationContentProviderImpl
       extends NotificationContentProviderBase
       implements NotificationContentProvider {
     private String channelId;
     private Context context;
     private String message;

     /** Constructor */
     public NotificationContentProviderImpl(Application application) {
       super(application);
       message = "-- uninitialized --";
       channelId = null;
       this.context = application;
     }

     /**
      * Sets message to display in the notification. Calls updateNotification
      * to display the message immediately.
      *
      * @param msg The message to display in the notification.
      */
     public void setMessage(String msg) {
       message = msg;
       updateNotification();
     }

     /**
      * Returns the notification as it should be rendered.
      */
     @Override
     public Notification getNotification() {
       Notification notification;

       if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
         Spanned styledText = Html.fromHtml(message, FROM_HTML_MODE_LEGACY);
         String channelId = getChannelId(context);
         notification =
             new Notification.Builder(context, channelId)
                 .setContentTitle("Notifications Demo")
                 .setStyle(new Notification.BigTextStyle()
                     .bigText(styledText))
                 .setSmallIcon(R.drawable.ic_navigation_white_24dp)
                 .setTicker("ticker text")
                 .build();
       } else {
         notification = new Notification.Builder(context)
             .setContentTitle("Notification Demo")
             .setContentText("testing non-O text")
             .build();
       }

       return notification;
     }

     // Helper to set up a channel ID.
     private String getChannelId(Context context) {
       if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
         if (channelId == null) {
           NotificationManager notificationManager =
               (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
           NotificationChannel channel = new NotificationChannel(
               "default", "navigation", NotificationManager.IMPORTANCE_DEFAULT);
           channel.setDescription("For navigation persistent notification.");
           notificationManager.createNotificationChannel(channel);
           channelId = channel.getId();
         }
         return channelId;
       } else {
         return "";
       }
     }
    }

After you've created `NotificationContentProviderImpl`, you connect the
Navigation SDK to it using the following code:

    ForegroundServiceManager f = NavigationApi.getForegroundServiceManager(getApplication());
    mNotification = new NotificationContentProviderImpl(getApplication());
    NavigationApi.clearForegroundServiceManager();
    NavigationApi.initForegroundServiceManagerProvider(getApplication(), null, mNotification);

## Caveats and future plans

- Be sure to call `initForegroundServiceManagerMessageAndIntent()` or `initForegroundServiceManagerProvider()` early so that the expected usage scenario is well-defined. You must call this method before you create a new Navigator.
- Be sure to catch exceptions from calls to `initForegroundServiceManagerMessageAndIntent()` or `initForegroundServiceManagerProvider()` in case the code pathway is entered more than once. In the Navigation SDK v2.0, calling this method multiple times throws a checked exception rather than a runtime exception.
- Google still might have work to do to get consistent styling over the lifetime of the notification that matches the header styling.
- When you define a notification provider, you can control heads-up behavior with the priority.
- Google does not provide a simple means for retrieving turn-by-turn information that a notification provider might insert into the notification.