Expose data to complications

Complication data sources expose information to watch face complications, supplying text, images, and numbers that the watch face can render.

A data source service extends SuspendingComplicationDataSourceService to deliver useful information directly to a watch face.

Getting started

Add the following dependency to your app module:

dependencies {
  implementiation("androidx.wear.watchface:watchface-complications-data-source-ktx:1.2.1")
}

Create the data source service

When complication data is needed, the Wear OS system sends update requests to your data source. To respond to the update requests, your data source must implement the onComplicationRequest() method of the SuspendingComplicationDataSourceService class.

The Wear OS system calls onComplicationRequest() when it needs data from your source—for example, when a complication using your data source becomes active or when a fixed amount of time passes.

Note: When your data source provides data, the watch face receives the raw values. The watch face is responsible for formatting the data for display.

The following code snippet shows a sample implementation:

class MyComplicationDataSourceService : SuspendingComplicationDataSourceService() {
    override suspend fun onComplicationRequest(request: ComplicationRequest): ComplicationData? {
        // Retrieve the latest info for inclusion in the data.
        val text = getLatestData()
        return shortTextComplicationData(text)
    }

    override fun getPreviewData(type: ComplicationType): ComplicationData? {
        return shortTextComplicationData("Event 1")
    }

    private fun shortTextComplicationData(text: String) =
        ShortTextComplicationData.Builder(
            text = PlainComplicationText.Builder(text).build(),
            contentDescription = PlainComplicationText.Builder(text).build()
        )
            // Add further optional details here such as icon, tap action, and title.
            .build()

    // ...
}

Manifest declarations and permissions

Data sources must include specific declarations in their app manifest to be treated as a data source by the Android system. This section explains the required settings for data sources.

In your app's manifest, declare the service and add an update request action intent filter. The manifest must also protect the service by adding the BIND_COMPLICATION_PROVIDER permission to ensure that only the Wear OS system can bind to provider services.

Also, include an android:icon attribute in the service element that provides a single-color white icon. We recommend vector drawables for the icons. The icon represents the data source and is shown in the complication picker.

Here's an example:

<service
    android:name=".snippets.complication.MyComplicationDataSourceService"
    android:exported="true"
    android:label="@string/my_complication_service_label"
    android:icon="@drawable/complication_icon"
    android:permission="com.google.android.wearable.permission.BIND_COMPLICATION_PROVIDER">
    <intent-filter>
        <action android:name="android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST" />
    </intent-filter>

    <!-- Supported types should be comma-separated e.g. SHORT_TEXT,SMALL_IMAGE -->
    <meta-data
        android:name="android.support.wearable.complications.SUPPORTED_TYPES"
        android:value="SHORT_TEXT" />
    <meta-data
        android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
        android:value="300" />

    <!--
        Optionally, the complication can be configured by the user by specifying a
        configuration activity.
    -->
    <meta-data
        android:name="android.support.wearable.complications.PROVIDER_CONFIG_ACTION"
        android:value="MY_CONFIG_ACTION" />

</service>

Metadata elements

In your manifest file, notice the following metadata elements:

  • android:name="android.support.wearable.complications.SUPPORTED_TYPES": Specifies the types of complication data that the data source supports.
  • android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS": Specifies how often the system should check for updates to the data.

When your complication data data source is active, UPDATE_PERIOD_SECONDS specifies how often you want the system to check for updates to the data. If the information shown in the complication doesn't need to update on a regular schedule, such as when you're using push updates, set this value to 0.

If you don't set UPDATE_PERIOD_SECONDS to 0, you must use a value of at least 300 (5 minutes), which is the minimum update period that the system enforces, to preserve device battery life. In addition, keep in mind that update requests come less often when the device is in ambient mode or isn't being worn.

Add a configuration activity

If required, a data source can include a configuration activity that is shown to the user when the user chooses that particularly data source from the complication picker. For example, a world clock data source might have a configuration activity that allows the user to choose the city or time zone to display.

The example manifest includes a meta-data element with the PROVIDER_CONFIG_ACTION key. The value of this element is the action that is used to launch the configuration activity.

Create the configuration activity, and add an intent filter that matches the action for it in your manifest file.

<intent-filter>
    <action android:name="MY_CONFIG_ACTION" />
    <category android:name="android.support.wearable.complications.category.PROVIDER_CONFIG" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

The activity can obtain details of the complication slot it is configuring from the intent within the onCreate() method of the activity:

// Keys defined on ComplicationDataSourceService
val id = intent.getIntExtra(EXTRA_CONFIG_COMPLICATION_ID, -1)
val type = intent.getIntExtra(EXTRA_CONFIG_COMPLICATION_TYPE, -1)
val source = intent.getStringExtra(EXTRA_CONFIG_DATA_SOURCE_COMPONENT)

The configuration activity must reside in the same package as the provider. The configuration activity must return RESULT_OK or RESULT_CANCELED to tell the system whether the data source should be set:

setResult(RESULT_OK) // Or RESULT_CANCELED to cancel configuration
finish()

Use push updates

As an alternative to specifying an update interval in your app's manifest, you can use an instance of ComplicationDataSourceUpdateRequester to initiate updates dynamically. To request an update, call requestUpdate().

Caution: To preserve device battery life, don't call requestUpdate() from your instance of ComplicationDataSourceUpdateRequester more often than every 5 minutes on average.

Provide time-dependent values

Some complications need to display a value that relates to the current time. Examples include the current date, the time until the next meeting, or the time in another time zone.

Don't update a complication every second or minute to keep those values up to date. Instead, specify the values as relative to the current date or time using time-dependent text. The following classes allow you to create these time-dependent values:

Timeline data

For complication data sources that provide a sequence of values at pre-defined times, use SuspendingTimelineComplicationDataSourceService.

An example of this would be a "next event" data source from a calendar app: Instead of the system having to poll the data source regularly for the next event, the data source can provide a timeline of events once, and then the data source can initiate updates if the calendar changes. This minimizes the load on the system and lets the complication show the correct event in a timely manner:

class MyTimelineComplicationDataSourceService : SuspendingTimelineComplicationDataSourceService() {
    override suspend fun onComplicationRequest(request: ComplicationRequest): ComplicationDataTimeline? {
        if (request.complicationType != ComplicationType.SHORT_TEXT) {
            return ComplicationDataTimeline(
                defaultComplicationData = NoDataComplicationData(),
                timelineEntries = emptyList()
            )
        }
        // Retrieve list of events from your own datasource / database.
        val events = getCalendarEvents()
        return ComplicationDataTimeline(
            defaultComplicationData = shortTextComplicationData("No event"),
            timelineEntries = events.map {
                TimelineEntry(
                    validity = TimeInterval(it.start, it.end),
                    complicationData = shortTextComplicationData(it.name)
                )
            }
        )
    }

    override fun getPreviewData(type: ComplicationType): ComplicationData? {
        return shortTextComplicationData("Event 1")
    }

    private fun shortTextComplicationData(text: String) =
        ShortTextComplicationData.Builder(
            text = PlainComplicationText.Builder(text).build(),
            contentDescription = PlainComplicationText.Builder(text).build()
        )
            // Add further optional details here such as icon, tap action, title etc
            .build()

    // ...
}

The behavior of the SuspendingTimelineComplicationDataSourceService is as follows:

  • When the current time falls within the start and end time of an entry in the timeline, the watch face uses that value.
  • When the current time doesn't fall within any entry in the timeline, the default value is used. For example, in the calendar app, this could be "No event."
  • If the current time falls within multiple events, the shortest event is used.

Provide dynamic values

Starting in Wear OS 4, some complications can display values that refresh more frequently based on values that are available directly to the platform. To provide this capability in your complications, use ComplicationData fields that accept dynamic values. The platform evaluates and updates these values frequently, without requiring the complication provider to be running.

Example fields include GoalProgressComplicationData's dynamic value field, and DynamicComplicationText, which can be used in any ComplicationText field. These dynamic values are based on the androidx.wear.protolayout.expression library.

In certain situations, the platform cannot evaluate dynamic values: