Wear Complications API

In horology a complication is, as defined by Wikipedia,

any feature in a timepiece beyond the simple display of hours and minutes.

Complications are certainly nothing new for traditional watches as we have the first reports of pocket watches including extra complications from back in the 16th century.

The movement of the first mechanical watch I built

In the current time, smartwatches are the perfect house for this kind of additional features, thanks to the huge potential provided by a digital screen. In Android Wear we already have several examples of watch faces including complications (displaying more information to the user like a step counter, weather forecast, details about the next meeting and much more).

How these complications worked so far has a big limitation: every single watch face app had to implement its own logic to fetch the data. If two watch faces were displaying the weather forecast for the day, for example, they would have had to implement two similar mechanisms to fetch the same relevant data. Such a waste of resources!

Android Wear 2.0 aims to solve this problem with the new Complications API.


Complications API

More specifically Android Wear puts into communication two main actors:

  • Data providers, which include the logic to fetch the data
  • Watch faces, which will display the information exposed by the data providers

It’s important to notice that a watch face has never a direct access to a data provider. It will instead receive a callback when some new data is available for the complications the user selected. On the other hand, the data provider doesn’t know how the exposed information will be displayed: that is completely up to the watch face, depending on its style.


Complications types

In order to define some sort of communication protocol between the two actors, Complications API specifies a set of Complications Types which can be used by a provider when exposing data.

Complications Types (CC BY d.android.com)

Each type has at least one required field which represents the primary piece of data. The watch face will expect to always receive this required field, and potentially some optional ones which can be used to provide additional information.
In the Short Text type, for example, the required field is a short amount of text, which may be accompanied by a short title or an icon to make the main information even more explicit.
Icons in general should be single-color with transparency, given that they might be tinted by the watch face.

For a description of all the available types, with the required and optional fields, have a look at the official documentation.


Complication data provider

It immediately appeared to me that these new API has a huge potential, so I decided to use it with an existing project, in order to expose some of the already available data from the app using an ad-hoc complication.

The chosen project is an open source app created by my colleague and friend Alexandros which I’ve been using for a while now: Memento Namedays. The main goal of the app is to remind birthdays and name days of your contacts, and this looks like a perfect use case for a complication displaying information about the next upcoming birthday.

Let’s have a look at how I managed to create such a complication data provider.

First of all I made the needed data available to the wear module of the app. To do so, I used the Wearable Data Layer API to sync this data between handheld and wearable every time that a relevant change happens.

In the wear module I created the complication data provider, which is a service that extends ComplicationProviderService. This base class gives us a set of callbacks in order to know when the provider is selected as the data source for a complication in the current watch face (onComplicationActivated), when the complication is not selected anymore (onComplicationDeactivated) and finally when it is time to provide an updated set of data to populate the complication (onComplicationUpdate).

The last method is the most important one because there we need to load new data and bundle it to be forwarded to the watch face.
Among the callback parameters, the complication type allows the data provider to add the required fields depending on the type supported by the watch face.

In case of a Short Text type I populate the ComplicationData with the date of the next event and an optional icon:

ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
.setShortText(ComplicationText.plainText(formatShortDate(date)))
.setIcon(Icon.createWithResource(context, R.drawable.ic_event))
.setTapAction(createContactEventsActivityIntent())
.build();

While in case of a Long Text I also added the list of contacts names associated to the next event:

ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
.setLongTitle(ComplicationText.plainText(formatLongDate(date)))
.setLongText(ComplicationText.plainText(formatList(names)))
.setIcon(Icon.createWithResource(context, R.drawable.ic_event))
.setTapAction(createContactEventsActivityIntent())
.build();

It’s worth noticing that complications might be tappable by the user directly from the watch face. In order to support this, I used the builder method setTapAction to specify a PendingIntent pointing to an activity displaying all the information for the given event. It will be then responsibility of the watch face to trigger that PendingIntent on user tap.

From left to right: the exposed complication with Short Text and Long Text types displayed in two different watch faces and the activity displaying the event details

Update period

It is possible to specify an update period for the ComplicationProviderService in its manifest entry using the following key:

android.support.wearable.complications.UPDATE_PERIOD_SECONDS

This value should be set as large as possible, to not have a too big impact on the device battery, due to frequent updates. It’s worth also noticing that this value is not guaranteeing a constant update frequency: the system will optimise the update calls depending on the status of the device (with less frequent updates if in ambient mode or not worn).

In our case the complication data doesn’t change often and the phone app already handles that logic updating the synched data. I decided, then, to be a good citizen and go for a push style update mechanism. It is possible to do so by setting the UPDATE_PERIOD_SECONDS value to 0 in the manifest and manually requesting an update using a ProviderUpdateRequester:

ComponentName providerComponentName = new ComponentName(
context,
MyComplicationProviderService.class
);
ProviderUpdateRequester providerUpdateRequester = new
ProviderUpdateRequester(context, providerComponentName);
providerUpdateRequester.requestUpdateAll();

Conclusions

In my opinion it might not be interesting for every Android application to integrate a dedicated watch face. Thanks to the Complications API though, it is very easy to export some of the application data and make it available at a glance directly on the user’s favourite watch face.

At the moment of writing, the Complications API is still in a preview state, so it might change before the final release of Android Wear 2.0, but its potential is already very interesting and I really hope to see a lot of complications available in the near future.

If you want to have an idea about how easy it is to update an existing app to support complications, most of the code changes described in this post are included in this pull request.

For more information about the Complications API, please have a look at the official documentation at https://developer.android.com/wear/preview/features/complications.html