Always-on apps and system ambient mode

keywords: wear,ambient,alwayson

Wear OS automatically handles moving into low-power mode for an active app when a user is no longer using their watch. This is called system ambient mode. If the user interacts with the watch again within a certain time frame, Wear OS brings the user back into the app where they left off.

For specific use cases—for example, a user wanting to see heart rate and pace during a run—you can also control what displays in the low-power ambient mode. Wear OS apps that run in both ambient and interactive modes are called always-on apps.

Making an app constantly visible impacts battery life, so consider that impact when adding this feature to your app.

Configure your project

To support ambient mode, follow these steps:

  1. Create or update your project based on the configurations on the Create and run a wearable app page.
  2. Add the WAKE_LOCK permission to the Android Manifest file:
<uses-permission android:name="android.permission.WAKE_LOCK" />

Enable always-on mode

To use the AmbientLifecycleObserver class, do the following:

  1. Implement the AmbientLifecycleObserver.AmbientLifecycleCallback interface, as in the following example. At this stage, the methods are empty, but later in the guide provides details of what changes you should ensure you are making to the visualization for entering and exiting ambient mode.

    Kotlin

    val ambientCallback = object : AmbientLifecycleObserver.AmbientLifecycleCallback {
        override fun onEnterAmbient(ambientDetails: AmbientLifecycleObserver.AmbientDetails) {
        // ... Called when moving from interactive mode into ambient mode.
        }
    
        override fun onExitAmbient() {
        // ... Called when leaving ambient mode, back into interactive mode.
        }
    
        override fun onUpdateAmbient() {
        // ... Called by the system in order to allow the app to periodically
        // update the display while in ambient mode. Typically the system will
        // call this every 60 seconds.
        }
    }
  2. Create an AmbientLifecycleObserver and register the observer. Typically, this would be used in onCreate() or the top-level composable if using Compose for Wear OS, to allow the always-on behavior to be enabled throughout the lifecycle of the activity.

    Kotlin

    private val ambientObserver = AmbientLifecycleObserver(activity, callback)
    
    override fun onCreate(savedInstanceState: Bundle) {
      super.onCreate(savedInstanceState)
      lifecycle.addObserver(observer)
    
      // ...
    }
  3. Remove the observer, by calling removeObserver(), when the always-on behavior is no longer required. For example, you might call this method in the onDestroy() method of your activity.

Always-on apps can move to the background

Starting in Wear OS 5, the system moves always-on apps to the background after they're visible in ambient mode for a certain period of time. Users can configure the timeout in system settings.

If your always-on app displays information about an ongoing user task—such as music playback or a workout session—you might want to keep the ongoing activity visible until the task ends. To do so, use the Ongoing Activity API to post an ongoing notification that is linked to your always-on activity.

In order for the system to recognize the ongoing activity, the ongoing notification's touch intent must point to your always-on activity, as shown in the following code snippet:

// Create a pending intent that point to your always-on activity
val touchIntent =
    PendingIntent.getActivity(
        context,
        0,
        Intent(context, MyAlwaysOnActivity::class.java),
        PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
    )

val notificationBuilder =
    NotificationCompat.Builder(this, CHANNEL_ID)
    // ...
    .setOngoing(true)

val ongoingActivity =
    OngoingActivity.Builder(
        applicationContext, NOTIFICATION_ID, notificationBuilder
    )
    // ...
    .setTouchIntent(touchIntent)
    .build()

ongoingActivity.apply(applicationContext)

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

Modify the visual appearance in ambient mode

By default, when implementing always-on, the screen doesn't change its appearance when the watch enters ambient mode. You can modify this behavior by overriding the methods in the AmbientLifecycleCallback.

To help conserve power, do the following:

  • Illuminate fewer pixels, and leave most of the screen black. Consider showing only critical information in ambient mode, and provide more detail when the user enters interactive mode.
  • Adjust any content for less-frequent updates. For example, show timers to the nearest minute instead of the nearest second.
  • In the AmbientDetails object passed to onEnterAmbient():
    • If deviceHasLowBitAmbient is set, disable anti-aliasing where possible.
    • If burnInProtectionRequired is set, shift the visualization around periodically, and avoid solid white areas.

When using Compose for Wear OS, we recommend using these callback methods to update state, which allows the system to recompose your UI appropriately.

For an example of how this can be achieved, check out the compose-based Exercise sample on GitHub, which makes use of the AmbientAware composable from the Horologist library.