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. The app is resumed after the user interacts with the watch if any of the following conditions are met:
- The user interaction occurs within a certain timeframe (before timeout).
- The app declares and has started an ongoing activity.
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 system 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:
- Create or update your project based on the configurations on the Create and run a wearable app page.
-
(Only needed on Wear OS 4 or lower) Add the
WAKE_LOCK
permission to the Android manifest file:
<uses-permission android:name="android.permission.WAKE_LOCK" android:maxSdkVersion="33"/>
Enable always-on mode
Starting from Wear OS 6, apps with targetSdkVersion
set to 36 or above are always-on by default.
These apps remain visible during system ambient mode for a limited time period without any configuration.
If your app's targetSdkVersion
is lower than 36, or if your app needs to run on Wear OS 5 or lower,
use the AmbientLifecycleObserver
class to make your app always-on.
React to ambient mode events using the AmbientLifecycleObserver class
Apps can also use the
AmbientLifecycleObserver
class to directly react to ambient mode events:
-
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. } }
-
Create an
AmbientLifecycleObserver
and register the observer. Typically, this would be used inonCreate()
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) // ... }
- Remove the observer, by calling
removeObserver()
, when the always-on behavior is no longer required. For example, you might call this method in theonDestroy()
method of your activity.
Update time text using the TimeText widget
Starting from Wear OS 6, the TimeText
widget is ambient-mode aware. If your
app only needs to update a time text every minute during ambient mode, you can
just use the TimeText
widget without using the AmbientLifecycleObserver
.
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 user experience 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. Consider showing only critical information in ambient mode, and provide more detail when the user enters interactive mode.
- Keep at least 85% of the screen black, remove fills, and use outlines for buttons and large icons.
- Avoid displaying extraneous information, such as non-functional branding and background images.
- Keep elements in the same position across active and always-on modes, and always show the time.
- Adjust any content for less-frequent updates. For example, show timers to the nearest minute instead of the nearest second.
- Remove or show placeholder UI for alphanumeric content that updates frequently, such as distance or time.
- Remove progress indicators that update frequently, such as for countdown rings and media sessions.
- When entering always on mode, if the user had previously been on a configuration or settings screen in your app, consider showing a more relevant screen in your app instead.
- In the
AmbientDetails
object passed toonEnterAmbient()
:- If
deviceHasLowBitAmbient
is set, disable anti-aliasing where possible. - If
burnInProtectionRequired
is set, shift the visualization around periodically, and avoid solid white areas.
- If
- Avoid running continuous animation during ambient mode. Starting in Wear OS 5.1, animators might stop running during ambient mode.
Checklist for uninterrupted display
There might be situations where you want maximum control over the display as the device moves through different states, for example when a workout app wants to avoid the watchface appearing on the display during a workout. In these cases, do the following:
- Implement the AmbientLifecycleObserver.AmbientLifecycleCallback interface.
- Create a new low-powered layout for use when the device is in system ambient mode.
- For the duration of the workout, implement an ongoing activity.
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.