Data provider apps expose information to watch face complications, supplying fields that contain text, strings, images, and numbers.
A data provider service extends
ComplicationProviderService
to deliver useful
information directly to a watch face.
Create a data provider project
To create a project in Android Studio for your data provider app, complete the following steps:
- Click File > New > New project.
- In the Project Template window, click the Wear OS tab, select No Activity, and click Next.
- In the Configure Your Project window, name your project, fill out the standard project information, and click Finish.
- Android Studio creates a project with an app module for your data provider. For more information about projects in Android Studio, see Create a project.
- Begin your data provider app by creating a new class that extends
BroadcastReceiver
. The purpose of that class is to listen for complication update requests from the Wear OS system. Additionally, create a new class that extendsComplicationProviderService
to provide data as requested by appropriate complications. For more information, see the following:- Implement a method for update requests
- The
ComplicationTapBroadcastReceiver
andCustomComplicationProviderService
classes in the following codelab: Exposing data to watch face complications on Wear OS ComplicationToggleReceiver
,LongTextProviderService
, and other classes in the test suite sample
Note: Adding an activity for your data provider is optional. For example, you might want an activity that launches only when the user taps a complication.
Implement a method for update requests
When complication data is needed, the Wear OS system sends update requests to your data
provider. The requests are received by your
BroadcastReceiver
. To respond to the update requests,
your data provider must implement the
onComplicationUpdate()
method of the ComplicationProviderService
class.
The Wear OS system calls onComplicationUpdate()
when it needs data from your
provider—for example, when a complication using your provider becomes
active or when a fixed amount of time passes.
It passes a
ComplicationManager
object as a parameter to
onComplicationUpdate
, which is used to send data back to the system.
Note: When your data provider app provides data, the watch face receives the raw values that you send so it can draw the information.
The following code snippet shows a sample implementation of the
onComplicationUpdate
method:
Kotlin
override fun onComplicationUpdate( complicationId: Int, dataType: Int, complicationManager: ComplicationManager) { Log.d(TAG, "onComplicationUpdate() id: $complicationId") // Used to create a unique key to use with SharedPreferences for this complication. val thisProvider = ComponentName(this, javaClass) // Retrieves your data; in this case, grabs an incrementing number from SharedPrefs. val preferences = getSharedPreferences(ComplicationTapBroadcastReceiver.COMPLICATION_PROVIDER_PREFERENCES_FILE_KEY, 0) val number = preferences.getInt( ComplicationTapBroadcastReceiver.getPreferenceKey( thisProvider, complicationId), 0) val numberText = String.format(Locale.getDefault(), "%d!", number) var complicationData: ComplicationData? = null when (dataType) { ComplicationData.TYPE_SHORT_TEXT -> complicationData = ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) .setShortText(ComplicationText.plainText(numberText)) .build() else -> if (Log.isLoggable(TAG, Log.WARN)) { Log.w(TAG, "Unexpected complication type $dataType") } } if (complicationData != null) { complicationManager.updateComplicationData(complicationId, complicationData) } else { // If no data is sent, we still need to inform the ComplicationManager, so // the update job can finish and the wake lock isn't held any longer. complicationManager.noUpdateRequired(complicationId) } }
Java
@Override public void onComplicationUpdate( int complicationId, int dataType, ComplicationManager complicationManager) { Log.d(TAG, "onComplicationUpdate() id: " + complicationId); // Used to create a unique key to use with SharedPreferences for this complication. ComponentName thisProvider = new ComponentName(this, getClass()); // Retrieves your data; in this case, grabs an incrementing number from SharedPrefs. SharedPreferences preferences = getSharedPreferences( ComplicationTapBroadcastReceiver.COMPLICATION_PROVIDER_PREFERENCES_FILE_KEY, 0); int number = preferences.getInt( ComplicationTapBroadcastReceiver.getPreferenceKey( thisProvider, complicationId), 0); String numberText = String.format(Locale.getDefault(), "%d!", number); ComplicationData complicationData = null; switch (dataType) { case ComplicationData.TYPE_SHORT_TEXT: complicationData = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) .setShortText(ComplicationText.plainText(numberText)) .build(); break; default: if (Log.isLoggable(TAG, Log.WARN)) { Log.w(TAG, "Unexpected complication type " + dataType); } } if (complicationData != null) { complicationManager.updateComplicationData(complicationId, complicationData); } else { // If no data is sent, we still need to inform the ComplicationManager, so // the update job can finish and the wake lock isn't held any longer. complicationManager.noUpdateRequired(complicationId); } }
Manifest declarations and permissions
Data provider apps must include specific declarations in their app manifest to be treated as a data provider by the Android system. This section explains the required settings for data provider apps.
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 provider and is shown in the provider
chooser.
Here's an example:
<service android:name=".provider.IncrementingNumberComplicationProviderService" android:icon="@drawable/icn_complications" android:label="@string/complications_provider_incrementing_number" 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> </service>
Specify the meta data elements
In your manifest file, include metadata to specify the supported types, update period, and configuration action, as shown in the following example:
<meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES" android:value="RANGED_VALUE,SHORT_TEXT,LONG_TEXT" /> <meta-data android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS" android:value="300" />
When your complication data provider 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.
For more details on sending updates, see the keys listed for the
ComplicationProviderService
class in the
Wear OS API
Reference.
Add a configuration activity
If required, a provider can include a configuration activity that is shown to the user when the user chooses a data provider. To include the configuration activity, include a metadata item in the provider service declaration in the manifest with the following key:
<meta-data android:name="android.support.wearable.complications.PROVIDER_CONFIG_ACTION" android:value="PROVIDER_CONFIG_ACTION"/>
The value can be any action.
Then, create the configuration activity with an intent filter for that
action. 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 provider
should be set.
Provider-specified safe watch faces
Providers can specify certain watch faces as "safe" to receive their data. This is only used when a watch face attempts to use the provider as a default and the provider trusts the watch face app.
To declare watch faces as safe, the provider adds metadata with a key of
android.support.wearable.complications.SAFE_WATCH_FACES
. The
metadata value is a comma-separated list of either
WatchFaceService
component names, given as if
ComponentName.flattenToString()
is called, or
app package names, in which case every watch face within a
specified app is considered safe. Whitespace in the value list is ignored. For example:
<meta-data android:name="android.support.wearable.complications.SAFE_WATCH_FACES" android:value=" com.app.watchface/com.app.watchface.MyWatchFaceService, com.anotherapp.anotherwatchface/com.something.WatchFaceService, com.something.text"/>
Provide burn-in safe images
On screens susceptible to burn-in, solid blocks of color should be avoided in ambient mode. If your icons or images include solid blocks of color, also provide a burn-in safe version.
When you provide an icon using
ComplicationData.Builder#setIcon
, include a burn-in safe version
using
ComplicationData.Builder#setBurnInProtectionIcon
.
When you provide an image using
ComplicationData.Builder#setSmallImage
, include a burn-in safe version
using
ComplicationData.Builder#setBurnInProtectionSmallImage
.
Use push updates
As an alternative to specifying a constant, nonzero update interval for a
complication in your app's manifest, you can use an instance of
ComplicationDataSourceUpdateRequester
to request updates dynamically.
To request an update to the complication's user-visible content, 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 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:
- Dynamic value is sometimes not available: This happens, for example, when the
device is off-wrist. In these situations, the platform uses the value
of the
dynamic value invalidation fallback field instead, in a
NoDataComplicationData
's placeholder field. - Dynamic value is never available: This happens on a device that is running on an
older release of Wear OS 4. In this situation, the platform uses a companion fallback field,
such as
getFallbackValue()
.
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.
You can use builders in the
ComplicationText
class to create these time-dependent values.
Complication update rate
You might want to update complications at a rapid rate. However, this might impact the battery life of the device. You can choose to use a privileged Complication request API that lets specific complications update more frequently. However, use of this API must be permitted by the watch manufacturer. Each watch manufacturer decides which complications can update at a faster rate than usually permitted.