In Wear OS, pairing an ongoing activity with an ongoing notification
adds that notification to additional surfaces within the Wear OS user interface.
This lets users stay more engaged with long-running activities.

Ongoing notifications are typically used to indicate that a notification has a
background task that the user is actively engaged with or is pending in some way
and therefore occupying the device.

For example, a Wear OS user might use a workout app to record a run from an
activity, then navigate away from that app to start some other task.
When the user navigates away from the workout app, the app
transitions to an ongoing notification tied to some background work to keep the user informed on their run.
The notification provides the user updates and an easy way to tap back into the
app.

However, to view the notification, the user has to swipe into the
notification tray below the watch face and find the right notification. This
isn't as convenient as on other surfaces.

With the Ongoing Activity API, an app's ongoing notification can expose
information to multiple new, convenient surfaces on Wear OS to keep the user
engaged.
| **Note:** Starting in Wear OS 4, ongoing notifications are affected by the [notification runtime
| permission](https://developer.android.com/develop/ui/views/notifications/notification-permission).

For example, in this workout app, the information can appear on the user's watch
face as a tappable running icon:

![running-icon](https://developer.android.com/static/wear/images/ongoing_activity_5.png)

**Figure 1.** Activity indicator.

The *Recents* section of the global app launcher also lists any ongoing
activities:

![launcher](https://developer.android.com/static/wear/images/ongoing_activity_4.png)

**Figure 2.** Global launcher.
| **Note:** When using an ongoing activity, app information appears in new surfaces, but the original notification no longer appears in the notification tray.

The following are good situations to use an ongoing notification tied to an
ongoing activity:

![timer](https://developer.android.com/static/wear/images/ongoing_activity_2.png)

**Figure 3.** **Timer:**
Actively counts down time and ends when the timer is
paused or stopped.

![map](https://developer.android.com/static/wear/images/ongoing_activity_3.png)

**Figure 4.** **Turn by turn navigation:**
Announces directions to a destination.
Ends when the user reaches the destination or stops navigation.

![music](https://developer.android.com/static/wear/images/ongoing_activity_1.png)

**Figure 5.** **Media:**
Plays music throughout a session. Ends immediately after
the user pauses the session.

Wear creates ongoing activities automatically for media apps.

See the [Ongoing Activity codelab](https://developer.android.com/codelabs/ongoing-activity) for an
in-depth example of creating ongoing activities for other kinds of apps.

## Setup

To start using the Ongoing Activity API in your app, add the following
dependencies to your app's `build.gradle` file:  

    dependencies {
      implementation "androidx.wear:wear-ongoing:1.0.0"
      // Includes LocusIdCompat and new Notification categories for Ongoing Activity.
      implementation "androidx.core:core:1.6.0"
    }

| **Note:** Several new categories in `NotificationCompat` (workout, stopwatch, and location sharing) help ensure that all apps using the Ongoing Activity API have a matching category.

## Start an ongoing activity

Get started by creating an ongoing notification and then an ongoing activity.

### Create an ongoing notification

An ongoing activity is closely related to an ongoing notification.
They work together to inform users of a task the user is actively engaged
with or a task that is pending in some way and therefore occupying the device.

You must pair an ongoing activity with an ongoing notification.
There are many benefits to linking your ongoing activity to a notification,
including the following:

- Notifications are the fallback on devices that don't support ongoing activities. The notification is the only surface your app shows while running in the background.
- On Android 11 and higher, Wear OS hides the notification in the notification tray when the app is visible as an ongoing activity on additional surfaces.
- The current implementation uses the `Notification` itself as the communication mechanism.

Create an ongoing notification using
[Notification.Builder.setOngoing](https://developer.android.com/reference/android/app/Notification.Builder#setOngoing(boolean)).

## Start an ongoing activity

Once you have an ongoing notification, create an ongoing activity as shown
in the following sample.
Check the included comments to understand each property's behavior.  

### Kotlin

```kotlin
var notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
      ...
      .setSmallIcon(..)
      .setOngoing(true)

val ongoingActivityStatus = Status.Builder()
    // Sets the text used across various surfaces.
    .addTemplate(mainText)
    .build()

val ongoingActivity =
    OngoingActivity.Builder(
        applicationContext, NOTIFICATION_ID, notificationBuilder
    )
        // Sets the animated icon that will appear on the watch face in
        // active mode.
        // If it isn't set, the watch face will use the static icon in
        // active mode.
        .setAnimatedIcon(R.drawable.ic_walk)
        // Sets the icon that will appear on the watch face in ambient mode.
        // Falls back to Notification's smallIcon if not set.
        // If neither is set, an Exception is thrown.
        .setStaticIcon(R.drawable.ic_walk)
        // Sets the tap/touch event so users can re-enter your app from the
        // other surfaces.
        // Falls back to Notification's contentIntent if not set.
        // If neither is set, an Exception is thrown.
        .setTouchIntent(activityPendingIntent)
        // Here, sets the text used for the Ongoing Activity (more
        // options are available for timers and stopwatches).
        .setStatus(ongoingActivityStatus)
        .build()

ongoingActivity.apply(applicationContext)

notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
```

### Java

```java
NotificationCompat.Builder notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
      ...
      .setSmallIcon(..)
      .setOngoing(true);

OngoingActivityStatus ongoingActivityStatus = OngoingActivityStatus.Builder()
    // Sets the text used across various surfaces.
    .addTemplate(mainText)
    .build();

OngoingActivity ongoingActivity =
    OngoingActivity.Builder(
        applicationContext, NOTIFICATION_ID, notificationBuilder
    )
        // Sets the animated icon that will appear on the watch face in
        // active mode.
        // If it isn't set, the watch face will use the static icon in
        // active mode.
        .setAnimatedIcon(R.drawable.ic_walk)
        // Sets the icon that will appear on the watch face in ambient mode.
        // Falls back to Notification's smallIcon if not set.
        // If neither is set, an Exception is thrown.
        .setStaticIcon(R.drawable.ic_walk)
        // Sets the tap/touch event so users can re-enter your app from the
        // other surfaces.
        // Falls back to Notification's contentIntent if not set.
        // If neither is set, an Exception is thrown.
        .setTouchIntent(activityPendingIntent)
        // Here, sets the text used for the Ongoing Activity (more
        // options are available for timers and stopwatches).
        .setStatus(ongoingActivityStatus)
        .build();

ongoingActivity.apply(applicationContext);

notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
```

The following steps call out the most important part of the previous example:

1. Call `.setOngoing(true)` on the
   `NotificationCompat.Builder` and set any optional
   fields.

2. Create an `OngoingActivityStatus`---or another status option, as
   described in the following section---to represent the text.

3. Create an `OngoingActivity` and set a notification ID.

4. Call `apply()` on `OngoingActivity` with the context.

5. Call `notificationManager.notify()` and pass in the same notification
   ID that is set in the ongoing activity to tie them together.

| **Note:** The notification that the ongoing activity is tied to must have a channel ID.

## Status

You use the [`Status`](https://developer.android.com/reference/androidx/wear/ongoing/Status)
to expose the current, live status of the
`OngoingActivity` to the user on new surfaces, like the *Recents*
section of the launcher. To use the feature, use the
[`Status.Builder`](https://developer.android.com/reference/androidx/wear/ongoing/Status.Builder) subclass.

In most cases, you only need to
[add a template](https://developer.android.com/reference/androidx/wear/ongoing/Status.Builder#addTemplate(java.lang.CharSequence))
that represents the text you want to appear in the *Recents* section of the app launcher.

You can then customize how text appears with
[spans](https://developer.android.com/guide/topics/text/spans) using the [`addTemplate()`](https://developer.android.com/reference/androidx/wear/ongoing/Status.Builder#addTemplate(java.lang.CharSequence)) method and
specifying any dynamic parts of the text as a
[`Status.Part`](https://developer.android.com/reference/kotlin/androidx/wear/ongoing/Status.Part).

The following example shows how to make the word "time" appear in red. The
example uses a
[`Status.StopwatchPart`](https://developer.android.com/reference/androidx/wear/ongoing/Status.StopwatchPart)
to represent a stopwatch in the *Recents* section of the app launcher.  

### Kotlin

```kotlin
val htmlStatus =
        "<p>The <font color=\"red\">time</font> on your current #type# is #time#.</p>"

val statusTemplate =
        Html.fromHtml(
                htmlStatus,
                Html.FROM_HTML_MODE_COMPACT
        )

// Creates a 5 minute timer.
// Note the use of SystemClock.elapsedRealtime(), not System.currentTimeMillis().
val runStartTime = SystemClock.elapsedRealtime() + TimeUnit.MINUTES.toMillis(5)

val status = new Status.Builder()
   .addTemplate(statusTemplate)
   .addPart("type", Status.TextPart("run"))
   .addPart("time", Status.StopwatchPart(runStartTime)
   .build()
```

### Java

```java
String htmlStatus =
        "<p>The <font color=\"red\">time</font> on your current #type# is #time#.</p>";

Spanned statusTemplate =
        Html.fromHtml(
                htmlStatus,
                Html.FROM_HTML_MODE_COMPACT
        );

// Creates a 5 minute timer.
// Note the use of SystemClock.elapsedRealtime(), not System.currentTimeMillis().
Long runStartTime = SystemClock.elapsedRealtime() + TimeUnit.MINUTES.toMillis(5);

Status status = new Status.Builder()
   .addTemplate(statusTemplate)
   .addPart("type", new Status.TextPart("run"))
   .addPart("time", new Status.StopwatchPart(runStartTime)
   .build();
```

To reference a part from the template, use the name surrounded by `#`.
To produce `#` in the output, use `##` in the template.

The previous example uses
[`HTMLCompat`](https://developer.android.com/reference/androidx/core/text/HtmlCompat#fromHtml(java.lang.String,%20int))
to generate a `CharSequence` to pass to the template, which is easier than
manually defining a `Spannable` object.

## Additional customizations

Beyond `Status`, you can customize your ongoing activity or
notifications in the following ways. However, these customizations might not be
used, based on the OEM's implementation.

**Ongoing Notification**

- The **category** set determines the priority of the ongoing activity.
  - **`CATEGORY_CALL`:** an incoming voice or video call or a similar synchronous communication request
  - **`CATEGORY_NAVIGATION`:** a map or turn-by-turn navigation
  - **`CATEGORY_TRANSPORT`:** media transport control for playback
  - **`CATEGORY_ALARM`:** an alarm or timer
  - **`CATEGORY_WORKOUT`:** a workout (new category)
  - **`CATEGORY_LOCATION_SHARING`:** temporary location sharing (new category)
  - **`CATEGORY_STOPWATCH`:** stopwatch (new category)

**Ongoing Activity**

- **Animated icon:** a black and white vector, preferably with a transparent
  background. Displays on the watch face in active mode. If the animated icon is not
  provided, the default notification icon is used. (The default notification icon is
  different for every application.)

- **Static icon:** a vector icon with transparent background. Displays on the
  watch face in ambient mode. If the animated icon isn't set, the static icon
  is used on the watch face in active mode. If this is not provided, the
  notification icon is used. If neither is set, an exception is
  thrown. (The app launcher still uses the app icon.)

- **OngoingActivityStatus:** plain text or a `Chronometer`. Displays in the
  *Recents* section of the app launcher. If not provided, the notification
  *"context text"* is used.

- **Touch Intent:** a `PendingIntent` used to switch back to the app if the
  user taps on the ongoing activity icon. Displays on the watch face or on the
  launcher item. It can be different from the original intent used to
  launch the app. If not provided, the notification's content intent is
  used. If neither is set an exception is thrown.

- **[`LocusId`](https://developer.android.com/reference/android/content/LocusId):** ID that assigns the
  launcher shortcut that the ongoing activity corresponds to. Displays on the
  launcher in the *Recents* section while the activity is ongoing. If not
  provided, the launcher hides all app items in the *Recents* section from
  the same package and only shows the ongoing activity.

- **Ongoing Activity ID:** ID used to disambiguate calls to
  [`fromExistingOngoingActivity()`](https://developer.android.com/reference/androidx/wear/ongoing/OngoingActivity#fromExistingOngoingActivity(android.content.Context)) when an application has more than one ongoing
  activity.

## Update an ongoing activity

In most cases, developers create a new ongoing notification and a new
ongoing activity when they need to update the data on the screen. However, the
Ongoing Activity API also offers helper methods to update an
`OngoingActivity` if you want to retain an instance rather than recreate it.

If the app is running in the background, it can send updates to the Ongoing
Activity API. However, don't do this too frequently, because the update method
ignores calls that are too close to each other. A few updates per minute is
reasonable.

To update the ongoing activity and the posted notification, use the object you
created before and call `update()`, as shown in the following example:  

### Kotlin

```kotlin
ongoingActivity.update(context, newStatus)
```

### Java

```java
ongoingActivity.update(context, newStatus);
```

As a convenience, there is a static method to create an ongoing activity.  

### Kotlin

```kotlin
OngoingActivity.recoverOngoingActivity(context)
               .update(context, newStatus)
```

### Java

```java
OngoingActivity.recoverOngoingActivity(context)
               .update(context, newStatus);
```

## Stop an ongoing activity

When the app is finished running as an ongoing activity, it only needs to cancel
the ongoing notification.

You can also choose to cancel the notification or ongoing activity
when it comes to the foreground, then recreate them when going back into the
background, but this is not required.

### Pause an ongoing activity

If your app has an explicit stop action, continue the ongoing activity after
it is unpaused. For an app without an explicit stop action,
end the activity when it is paused.

## Best practices

Remember the following things when working with the Ongoing Activity API:

- Call `ongoingActivity.apply(context)` before calling `notificationManager.notify(...)`.
- Set a static icon for your Ongoing Activity, either
  [explicitly](https://developer.android.com/reference/androidx/wear/ongoing/OngoingActivity.Builder#setStaticIcon(android.graphics.drawable.Icon))
  or as a fallback via the
  [notification](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder#setSmallIcon(int)).
  If you don't, you get an `IllegalArgumentException`.

- Use black and white vector icons with transparent backgrounds.

- Set a touch intent for your ongoing activity, either
  [explicitly](https://developer.android.com/reference/androidx/wear/ongoing/OngoingActivity.Builder#setTouchIntent(android.app.PendingIntent))
  or as a fallback using the
  [notification](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder#setContentIntent(android.app.PendingIntent)).
  If you don't, you get an `IllegalArgumentException`.

- For `NotificationCompat`, use the Core AndroidX library
  `core:1.5.0-alpha05+`, which includes the
  [`LocusIdCompat`](https://developer.android.com/reference/androidx/core/content/LocusIdCompat) and the [new
  categories](https://developer.android.com/jetpack/androidx/releases/core#1.5.0-alpha05) for workout,
  stopwatch, and location sharing.

- If your app has more than one `MAIN LAUNCHER` activity declared in the
  manifest, publish a [dynamic
  shortcut](https://developer.android.com/guide/topics/ui/shortcuts/creating-shortcuts#dynamic) and
  associate it with your ongoing activity using `LocusId`.

## Publish media notifications when playing media on Wear OS devices

If media content is playing on a Wear OS device,
[publish a media notification](https://developer.android.com/reference/android/app/Notification.MediaStyle).
This lets the system create the corresponding ongoing activity.
| **Note:** If media content is playing on a handheld device and not on a Wear OS device, don't post a media notification on the Wear OS device.

If you are using Media3, the notification is published automatically. If you
create you notification manually, it should use the
[`MediaStyleNotificationHelper.MediaStyle`](https://developer.android.com/reference/androidx/media3/session/MediaStyleNotificationHelper.MediaStyle),
and the corresponding `MediaSession` should have its
[session activity](https://developer.android.com/reference/androidx/media3/session/MediaSession.Builder#setSessionActivity(android.app.PendingIntent))
populated.

## Recommended for you

- Note: link text is displayed when JavaScript is off
- [Create a notification {:#notification}](https://developer.android.com/develop/ui/views/notifications/build-notification)
- [Engage Wear OS users in new ways with the Ongoing Activity API](https://developer.android.com/codelabs/ongoing-activity)
- [Create an expandable notification {:#expandable-notification}](https://developer.android.com/develop/ui/views/notifications/expanded)