向复杂功能公开数据

数据提供程序应用向手表表盘复杂功能公开信息,提供包含文本、字符串、图像和数字的字段。

数据提供程序服务扩展 ComplicationProviderService 以直接向手表表盘提供有用的信息。

创建数据提供程序项目

要在 Android Studio 中为您的数据提供程序应用创建项目,请完成以下步骤

  1. 点击文件 > 新建 > 新建项目
  2. 项目模板窗口中,点击 Wear OS 选项卡,选择无 Activity,然后点击下一步
  3. 配置您的项目窗口中,命名您的项目,填写标准项目信息,然后点击完成
  4. Android Studio 将创建一个包含用于数据提供程序的应用模块的项目。有关 Android Studio 中项目的更多信息,请参阅创建项目
  5. 通过创建一个扩展 BroadcastReceiver的新类来开始您的数据提供程序应用。该类的目的是侦听来自 Wear OS 系统的复杂功能更新请求。此外,创建一个扩展 ComplicationProviderService的新类,以根据相应复杂功能的请求提供数据。有关更多信息,请参阅以下内容

    注意:为您的数据提供程序添加 Activity 是可选的。例如,您可能希望在用户点击复杂功能时才启动 Activity。

实现更新请求的方法

当需要复杂功能数据时,Wear OS 系统会将更新请求发送到您的数据提供程序。请求由您的 BroadcastReceiver接收。为了响应更新请求,您的数据提供程序必须实现ComplicationProviderService类的 onComplicationUpdate()方法。

当 Wear OS 系统需要来自您提供程序的数据时(例如,使用您提供程序的复杂功能变为活动状态或经过固定时间段时),它会调用 onComplicationUpdate()。它将 ComplicationManager 对象作为参数传递给 onComplicationUpdate,该对象用于将数据发送回系统。

注意:当您的数据提供程序应用提供数据时,表盘会接收您发送的原始值,以便其可以绘制信息。

以下代码片段显示了 onComplicationUpdate 方法的示例实现

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);
   }
}

清单声明和权限

数据提供程序应用必须在其应用清单中包含特定声明,才能被 Android 系统视为数据提供程序。本部分介绍数据提供程序应用所需的设置。

在您的应用清单中,声明服务并添加更新请求操作意图过滤器。清单还必须通过添加 BIND_COMPLICATION_PROVIDER 权限来保护服务,以确保只有 Wear OS 系统才能绑定到提供程序服务。

此外,在 service 元素中包含一个 android:icon 属性,该属性提供单色白色图标。我们建议使用矢量可绘制对象作为图标。该图标表示提供程序,并在提供程序选择器中显示。

这是一个示例

<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>

指定元数据元素

在您的清单文件中,包含元数据以指定支持的类型、更新周期和配置操作,如下例所示

<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" />

当您的复杂功能数据提供程序处于活动状态时,UPDATE_PERIOD_SECONDS 指定您希望系统多久检查一次数据的更新。如果复杂功能中显示的信息不需要定期更新,例如当您 使用推送更新 时,请将此值设置为 0

如果您未将 UPDATE_PERIOD_SECONDS 设置为 0,则必须使用至少 300(5 分钟)的值,这是系统强制执行的最小更新周期,以节省设备电量。此外,请记住,当设备处于环境模式或未佩戴时,更新请求的频率较低。

有关发送更新的更多详细信息,请参阅 ComplicationProviderService 类在 Wear OS API 参考 中列出的键。

添加配置活动

如果需要,提供程序可以包含一个配置活动,当用户选择数据提供程序时向用户显示。要包含配置活动,请在清单中提供程序服务的声明中包含一个元数据项,并使用以下键

<meta-data
    android:name="android.support.wearable.complications.PROVIDER_CONFIG_ACTION"
    android:value="PROVIDER_CONFIG_ACTION"/>

该值可以是任何操作。

然后,使用该操作的意图过滤器创建配置活动。配置活动必须位于与提供程序相同的包中。配置活动必须返回 RESULT_OKRESULT_CANCELED 以告知系统是否应设置提供程序。

提供程序指定的安全表盘

提供程序可以将某些表盘指定为“安全”以接收其数据。这仅在表盘尝试将提供程序用作默认值且提供程序信任表盘应用时使用。

要将表盘声明为安全,提供程序会添加元数据,其键为 android.support.wearable.complications.SAFE_WATCH_FACES。元数据值是 WatchFaceService 组件名称(如同调用 ComponentName.flattenToString() 一样)或应用包名称(在这种情况下,指定应用中的每个表盘都被视为安全)的逗号分隔列表。值列表中的空格将被忽略。例如

<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"/>

提供防烧屏图像

在易受烧屏影响的屏幕上,应避免在环境模式下使用纯色块。如果您的图标或图像包含纯色块,请提供防烧屏版本。

当您使用 ComplicationData.Builder#setIcon 提供图标时,请使用 ComplicationData.Builder#setBurnInProtectionIcon 包含防烧屏版本。

当您使用 ComplicationData.Builder#setSmallImage 提供图像时,请使用 ComplicationData.Builder#setBurnInProtectionSmallImage 包含防烧屏版本。

使用推送更新

作为在应用清单中为复杂功能指定常量非零更新间隔的替代方法,您可以使用 ComplicationDataSourceUpdateRequester 的实例来动态请求更新。要请求更新复杂功能的用户可见内容,请调用 requestUpdate()

注意:为了节省设备电量,请勿从 ComplicationDataSourceUpdateRequester 的实例中每 5 分钟平均调用一次以上 requestUpdate()

提供动态值

从 Wear OS 4 开始,某些复杂功能可以显示基于平台可以直接访问的值更频繁地刷新的值。要在您的复杂功能中提供此功能,请使用 ComplicationData 字段,这些字段接受 动态值。平台会频繁评估和更新这些值,而无需运行复杂功能提供程序。

示例字段包括 GoalProgressComplicationData 的动态值字段 DynamicComplicationText,它可以在任何 ComplicationText 字段中使用。这些动态值基于 androidx.wear.protolayout.expression 库。

在某些情况下,平台无法评估动态值

提供与时间相关的值

某些复杂功能需要显示与当前时间相关的数值。示例包括当前日期、下次会议之前的时间或另一个时区的时间。

不要每秒或每分钟更新一次复杂功能以保持这些值最新。相反,使用与时间相关的文本将值指定为相对于当前日期或时间。您可以使用 ComplicationText 类中的构建器来创建这些与时间相关的值。

复杂功能更新速率

您可能希望以较快的速率更新复杂功能。但是,这可能会影响设备的电池寿命。您可以选择使用特权 复杂功能请求 API,该 API 允许特定复杂功能更频繁地更新。但是,必须 获得手表制造商的许可才能使用此 API。每个手表制造商都会决定哪些复杂功能可以比通常允许的更新速度更快。