Android NDK开发详解Wear之持续性活动


在 Wear OS 中,通过将持续性活动与持续性通知配对,可将相应通知添加到 Wear OS 界面中的其他 surface。这样,用户就可以与长时间进行的活动保持更高的互动度。

持续性通知通常用于表明,相应通知存在后台任务且用户正与之积极互动,或者存在正等待进行某种处理并因此占用设备资源的后台任务。

例如,Wear OS 用户可能使用某款锻炼应用记录进行活动时的跑步数据,然后离开该应用并启动一些其他任务。当用户离开锻炼应用时,该应用将转换为与某项后台工作相关联的持续性通知,以便让用户随时了解其跑步数据。通过该通知,用户可以了解最新锻炼数据,并能轻松地通过点按返回到锻炼应用。

不过,若要查看通知,用户必须滑动到表盘下方的通知栏中,然后找到相应通知。这样的操作方式不如其他 surface 方便。

利用 Ongoing Activity API,应用的持续性通知可以将信息显示在 Wear OS 中多个便捷的新 surface 中,方便用户保持互动。

注意:从 Wear OS 4 开始,持续性通知会受到通知运行时权限的影响。
例如,在下面这款锻炼应用中,信息可在用户的表盘上显示为可点按的跑步图标:

跑步图标
在这里插入图片描述

图 1. 活动指示器。

全局应用启动器的“最近用过”部分也会列出所有持续性活动:

启动器
在这里插入图片描述

图 2. 全局启动器。

注意:使用持续性活动时,应用信息会显示在新 surface 中,但原始通知将不再显示在通知栏中。
下列情况很适合使用与持续性活动相关联的持续性通知:

计时器
在这里插入图片描述

图 3. 计时器:主动倒计时,并在计时器暂停或停止时结束。

地图
在这里插入图片描述

图 4. 精细导航:播报前往目的地的路线。当用户到达目的地或停止导航时结束。

音乐
在这里插入图片描述

图 5. 媒体:在整个会话期间播放音乐。在用户暂停会话后立即结束。

Wear 会自动为媒体应用创建持续性活动。请参阅“持续性活动”Codelab,通过详尽的示例了解如何为其他类型的应用创建持续性活动。

设置

如需开始在您的应用中使用 Ongoing Activity API,请将以下依赖项添加到应用的 build.gradle 文件中:

dependencies {
    
    
  implementation "androidx.wear:wear-ongoing:1.0.0"
  // Includes LocusIdCompat and new Notification categories for Ongoing Activity.
  implementation "androidx.core:core:1.6.0"
}

注意:NotificationCompat 中的一些新类别(锻炼、秒表和位置信息分享)有助于确保所有使用 Ongoing Activity API 的应用都具有匹配的类别。

开始持续性活动

首先,请创建一个持续性通知,然后创建一个持续性活动。

创建持续性通知

持续性活动与持续性通知密切相关。二者共同发挥作用,让用户了解某项任务,并且用户正与该任务积极互动,或该任务正等待进行某种处理并因此占用设备资源。

您必须将持续性活动与持续性通知配对。将持续性活动与通知相关联有很多好处,其中包括:

在不支持持续性活动的设备上,通知是后备方式。当应用在后台运行时,通知是您的应用显示的唯一 surface。
在 Android 11 及更高版本中,当应用在其他 surface 上显示为持续性活动时,Wear OS 会隐藏通知栏中的通知。
当前的实现使用 Notification 本身作为通信机制。
使用 Notification.Builder.setOngoing 创建持续性通知。

开始持续性活动
在创建持续性通知后,创建一个持续性活动,如以下示例所示。查看包含的注释,了解每个属性的行为。

Kotlin

var notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID).setSmallIcon(..)
      .setOngoing(true)

val ongoingActivityStatus = Status.Builder()
    // Sets the text used across various surfaces.
    .addTemplate(mainText)
    .build()

val ongoingActivity =
    OngoingActivity.Builder(
        applicationContext, NOTIFICATION_ID, notificationBuilder
    )
        // Sets the animated icon that will appear on the watch face in
        // active mode.
        // If it isn't set, the watch face will use the static icon in
        // active mode.
        .setAnimatedIcon(R.drawable.ic_walk)
        // Sets the icon that will appear on the watch face in ambient mode.
        // Falls back to Notification's smallIcon if not set.
        // If neither is set, an Exception is thrown.
        .setStaticIcon(R.drawable.ic_walk)
        // Sets the tap/touch event so users can re-enter your app from the
        // other surfaces.
        // Falls back to Notification's contentIntent if not set.
        // If neither is set, an Exception is thrown.
        .setTouchIntent(activityPendingIntent)
        // Here, sets the text used for the Ongoing Activity (more
        // options are available for timers and stopwatches).
        .setStatus(ongoingActivityStatus)
        .build()

ongoingActivity.apply(applicationContext)

notificationManager.notify(NOTIFICATION_ID, builder.build())

Java

NotificationCompat.Builder notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID).setSmallIcon(..)
      .setOngoing(true);

OngoingActivityStatus ongoingActivityStatus = OngoingActivityStatus.Builder()
    // Sets the text used across various surfaces.
    .addTemplate(mainText)
    .build();

OngoingActivity ongoingActivity =
    OngoingActivity.Builder(
        applicationContext, NOTIFICATION_ID, notificationBuilder
    )
        // Sets the animated icon that will appear on the watch face in
        // active mode.
        // If it isn't set, the watch face will use the static icon in
        // active mode.
        .setAnimatedIcon(R.drawable.ic_walk)
        // Sets the icon that will appear on the watch face in ambient mode.
        // Falls back to Notification's smallIcon if not set.
        // If neither is set, an Exception is thrown.
        .setStaticIcon(R.drawable.ic_walk)
        // Sets the tap/touch event so users can re-enter your app from the
        // other surfaces.
        // Falls back to Notification's contentIntent if not set.
        // If neither is set, an Exception is thrown.
        .setTouchIntent(activityPendingIntent)
        // Here, sets the text used for the Ongoing Activity (more
        // options are available for timers and stopwatches).
        .setStatus(ongoingActivityStatus)
        .build();

ongoingActivity.apply(applicationContext);

notificationManager.notify(NOTIFICATION_ID, builder.build());

下列步骤指出了上一个示例中最重要的部分:

对 NotificationCompat.Builder 调用 .setOngoing(true),并设置任何可选字段。

创建一个 OngoingActivityStatus 或其他状态选项(如下一部分所述),以表示文本。

创建一个 OngoingActivity,并设置通知 ID。

使用上下文对 OngoingActivity 调用 apply()。

调用 notificationManager.notify() 并传入在持续性活动中设置的通知 ID,将二者关联起来。

注意:与持续性活动相关联的通知必须具有渠道 ID。

状态

您可以使用 Status 在新 surface(例如启动器的“最近用过”部分)上向用户显示 OngoingActivity 的当前实时状态。如需使用此功能,请使用 Status.Builder 子类。

在大多数情况下,您只需要添加模板来表示想要在应用启动器的“最近用过”部分显示的文本。

然后,您可以通过 span 自定义文本外观:使用 addTemplate() 方法并将文本的任意动态部分指定为 Status.Part。

以下示例展示了如何使单词“time”显示为红色。该示例使用 Status.StopwatchPart 在应用启动器的“最近用过”部分表示秒表。

Kotlin

val htmlStatus =
        "<p>The <font color=\"red\">time</font> on your current #type# is #time#.</p>"

val statusTemplate =
        Html.fromHtml(
                htmlStatus,
                Html.FROM_HTML_MODE_COMPACT
        )

// Creates a 5 minute timer.
// Note the use of SystemClock.elapsedRealtime(), not System.currentTimeMillis().
val runStartTime = SystemClock.elapsedRealtime() + TimeUnit.MINUTES.toMillis(5)

val status = new Status.Builder()
   .addTemplate(statusTemplate)
   .addPart("type", Status.TextPart("run"))
   .addPart("time", Status.StopwatchPart(runStartTime)
   .build()

Java

String htmlStatus =
        "<p>The <font color=\"red\">time</font> on your current #type# is #time#.</p>";

Spanned statusTemplate =
        Html.fromHtml(
                htmlStatus,
                Html.FROM_HTML_MODE_COMPACT
        );

// Creates a 5 minute timer.
// Note the use of SystemClock.elapsedRealtime(), not System.currentTimeMillis().
Long runStartTime = SystemClock.elapsedRealtime() + TimeUnit.MINUTES.toMillis(5);

Status status = new Status.Builder()
   .addTemplate(statusTemplate)
   .addPart("type", new Status.TextPart("run"))
   .addPart("time", new Status.StopwatchPart(runStartTime)
   .build();

如需引用模板中的某个部分,请使用前后加 # 的名称。如需在输出中生成 #,请在模板中使用 ##。

前面的示例使用 HTMLCompat 生成 CharSequence 来传递给模板,这比手动定义 Spannable 对象更简单。

其他自定义

除了 Status 之外,您还可以通过以下方式自定义持续性活动或通知。不过,这些自定义可能无法使用,具体取决于 OEM 的实现。

持续性通知

设置的类别决定了持续性活动的优先级。
CATEGORY_CALL:语音通话或视频通话请求,或者类似的同步通信请求
CATEGORY_NAVIGATION:地图或精细导航
CATEGORY_TRANSPORT:用于播放的媒体传输控件
CATEGORY_ALARM:闹钟或计时器
CATEGORY_WORKOUT:健身(新类别)
CATEGORY_LOCATION_SHARING:临时位置信息分享(新类别)
CATEGORY_STOPWATCH:秒表(新类别)
持续性活动

动画图标:黑白矢量图标,最好为透明背景。在氛围模式下会显示在表盘上。如果未提供动画图标,系统会使用默认通知图标。每个应用的默认通知图标各不相同。

静态图标:具有透明背景的矢量图标。在氛围模式下会显示在表盘上。如果未设置动画图标,系统会在活动模式期间在表盘上使用静态图标。如果未提供该图标,系统会使用通知图标。如果二者都未设置,系统会抛出异常。应用启动器仍会使用应用图标。

OngoingActivityStatus:纯文本或 Chronometer。会显示在应用启动器的“最近用过”部分。如果未提供该内容,系统会使用通知的“上下文文本”。

触摸 intent:一个 PendingIntent,用于在用户点按持续性活动图标时切换回应用。会显示在表盘上或启动器项上。它可以与用于启动应用的原始 intent 不同。如果未提供该 intent,系统会使用通知的内容 intent。如果二者都未设置,系统会抛出异常。

LocusId:该 ID 用于指定持续性活动对应的启动器快捷方式。在活动持续期间,会显示在启动器的“最近用过”部分。如果未提供该 ID,启动器会隐藏“最近用过”部分中来自相应软件包的所有应用项,只显示持续性活动。

持续性活动 ID:当应用有多个持续性活动时,该 ID 用于消除 fromExistingOngoingActivity() 调用方面的歧义。

更新持续性活动

在大多数情况下,当开发者需要更新屏幕上的数据时,他们会创建一个新的持续性通知和一个新的持续性活动。不过,如果您想要保留实例而不是重新创建实例,Ongoing Activity API 还提供了用于更新 OngoingActivity 的辅助方法。

应用在后台运行时,可以向 Ongoing Activity API 发送更新。不过,切勿过于频繁地执行此操作,因为更新方法会忽略彼此太过接近的调用。每分钟进行几次更新是合理的频率。

如需更新持续性活动和已发布的通知,请使用您之前创建的对象并调用 update(),如以下示例所示:

Kotlin

ongoingActivity.update(context, newStatus)

Java

ongoingActivity.update(context, newStatus);

为方便起见,您可以采用一种静态方法来创建持续性活动。

Kotlin

OngoingActivity.recoverOngoingActivity(context)
               .update(context, newStatus)

Java

OngoingActivity.recoverOngoingActivity(context)
               .update(context, newStatus);

停止持续性活动

当应用作为持续性活动完成运行时,只需取消持续性通知即可。

您还可以选择在应用进入前台时取消通知或持续性活动,然后在应用返回后台时重新创建它们,但这并不是必需的。

暂停持续性活动

如果您的应用有明确的停止操作,请在取消暂停后让持续性活动继续。对于没有明确停止操作的应用,请在活动暂停时结束活动。

最佳实践

使用 Ongoing Activity API 时,请注意以下几点:

先调用 ongoingActivity.apply(context),然后再调用 notificationManager.notify(…)。
请务必为持续性活动设置静态图标,显式设置或设置为通过通知使用的后备图标均可。如果不进行此设置,您会收到 IllegalArgumentException。

使用具有透明背景的黑白矢量图标。

请务必为持续性活动设置轻触 intent,显式设置或设置为使用通知的后备 intent 均可。如果不进行此设置,您会收到 IllegalArgumentException。

对于 NotificationCompat,请使用 Core AndroidX 库 core:1.5.0-alpha05+,其中包含 LocusIdCompat,以及对应于锻炼、秒表和位置信息分享的新类别。

如果应用在清单中声明了多个 MAIN LAUNCHER 活动,请发布一个动态快捷方式,并使用 LocusId 将其与持续性活动相关联。

目前没有任何推荐文档页面。

请尝试登录您的 Google 账号。

本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。

最后更新时间 (UTC):2023-11-27。

猜你喜欢

转载自blog.csdn.net/hnjzfwy/article/details/134915177