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:
- Create or update your project based on the configurations on the Create and run a wearable app page.
- 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:
-
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.
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 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
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.