向复杂功能提供数据

数据提供程序应用会通过提供包含文本、字符串、图片和数字的字段,向表盘复杂功能提供信息。

数据提供程序服务扩展了 ComplicationProviderService,可直接将有用的信息提供给表盘。

创建数据提供程序项目

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

  1. 依次点击 File > New > New project
  2. Project Template 窗口中,点击 Wear OS 标签页,选择 No Activity,然后点击 Next
  3. Configure Your Project 窗口中,为项目命名,填写标准项目信息,然后点击 Finish
  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 系统才会将其视为数据提供程序。本部分介绍了数据提供程序应用必需的设置。

在应用清单中,声明相应服务并添加更新请求操作 intent 过滤器。清单还必须通过添加 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 分钟),这是系统为了节省设备电池电量而执行的最短更新周期。另请注意,当设备处于氛围模式或未佩戴时,更新请求的频率会降低。

如需详细了解如何发送更新,请参阅 Wear OS API 参考文档中所列的 ComplicationProviderService 类的键。

添加配置 activity

如果需要,提供程序可以包含配置 activity,当用户选择数据提供程序时,系统会向用户显示该 activity。若要添加配置 activity,请在清单中的提供程序服务声明内添加具有以下键的元数据项:

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

该值可为任意操作。

然后,创建包含相应操作的 intent 过滤器的配置 activity。配置 activity 必须与提供程序位于同一个软件包中。配置 activity 必须返回 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 实例调用 requestUpdate() 的平均频率不得超过 5 分钟一次。

提供动态价值

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

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

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

提供具有时效性的值

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

请不要每秒或每分钟更新一次复杂功能来确保这些值保持最新状态,而是使用具有时效性的文本指定相对于当前日期或时间的值。 您可以使用 ComplicationText 类中的构建器来创建此类具有时效性的值。

复杂功能更新频率

您可能需要快速更新复杂功能。不过,这可能会影响设备的电池续航时间。您可以选择使用具有特权的 Complication request API,使特定复杂功能可以更频繁地更新。 不过,必须获得手表制造商的许可,才能使用此 API。每个手表制造商都会决定哪些复杂功能的更新频率可以超过通常允许的频率。