持續進行的活動

Wear OS 裝置通常用於長時間的體驗,例如追蹤運動。這會造成使用者體驗上的問題:如果使用者開始執行工作,然後返回錶面,該如何返回工作?使用啟動器返回應用程式可能很困難,尤其是在移動時,會造成不必要的阻礙。

解決方法是將持續性通知與 OngoingActivity 配對。這樣一來,裝置就能在使用者介面中顯示長時間執行的活動資訊,並啟用錶面底部的可輕觸圖示等功能。這樣一來,使用者就能瞭解背景工作,並透過輕觸返回應用程式。

舉例來說,在這個健身應用程式中,資訊可在使用者的錶面上顯示為可輕觸的「跑步中」圖示:

「跑步中」圖示

圖 1. 活動指標。

持續性通知也會在全域應用程式啟動器的「近期」部分顯示資訊。使用者可以透過這個便利的管道查看工作狀態,並再次與應用程式互動:

啟動器

圖 2. 全域啟動器。

與持續性活動繫結的持續性通知非常適合用於以下情況:

計時器

圖 3. 計時器:主動倒數計時,並在計時器暫停或停止時結束。

地圖

圖 4. 即時路線導航:播報前往目的地的路線,在使用者抵達目的地或停止導航時結束。

音樂

圖 5. 媒體:在工作階段全程播放音樂,當使用者暫停工作階段後立即結束。

Wear 會自動為媒體應用程式建立持續性活動。

請參閱「持續性活動程式碼研究室」,瞭解為其他類型的應用程式建立持續性活動的詳細範例。

設定

如果您想開始在應用程式中使用 Ongoing Activity API,請在應用程式的 build.gradle 檔案中加入以下依附元件:

dependencies {
  implementation "androidx.wear:wear-ongoing:1.1.0"
  implementation "androidx.core:core:1.17.0"
}

建立持續性活動

這個程序包含三個步驟:

  1. 建立標準 NotificationCompat.Builder,並將其設定為持續進行。
  2. 建立及設定 OngoingActivity 物件,並將通知建構工具傳遞至該物件。
  3. 將持續性活動套用至通知建構工具,然後發布產生的通知。

建立及設定通知

請先建立 NotificationCompat.Builder。重要步驟是呼叫 setOngoing(true),將通知標示為持續性通知。您也可以在這個階段設定其他通知屬性,例如小圖示和類別。

// Create a PendingIntent to pass to the notification builder
val pendingIntent =
    PendingIntent.getActivity(
        this,
        0,
        Intent(this, AlwaysOnActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
        },
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
    )

val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
    .setContentTitle("Always On Service")
    .setContentText("Service is running in background")
    .setSmallIcon(R.drawable.animated_walk)
    // Category helps the system prioritize the ongoing activity
    .setCategory(NotificationCompat.CATEGORY_WORKOUT)
    .setContentIntent(pendingIntent)
    .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
    .setOngoing(true) // Important!

建立 OngoingActivity

接著,使用建構工具建立 OngoingActivity 的例項。OngoingActivity.Builder 需要 Context、通知 ID,以及您在上一個步驟中建立的 NotificationCompat.Builder

設定將顯示在新 UI 介面上的重要屬性:

  • 動畫和靜態圖示:提供在正常模式和微光模式下,錶面上顯示的圖示。
  • 觸控意圖:使用者輕觸持續進行的活動圖示時,系統會使用 PendingIntent 將使用者帶回應用程式。您可以重複使用上一個步驟中建立的 pendingIndent

val ongoingActivity =
    OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
        // Sets the icon that appears on the watch face in active mode.
        .setAnimatedIcon(R.drawable.animated_walk)
        // Sets the icon that appears on the watch face in ambient mode.
        .setStaticIcon(R.drawable.ic_walk)
        // Sets the tap target to bring the user back to the app.
        .setTouchIntent(pendingIntent)
        .build()

套用至通知和貼文

最後一步是將 OngoingActivity 連結至通知,然後發布。ongoingActivity.apply() 方法會修改原始通知建構工具,加入必要資料,讓系統能在額外介面上顯示通知。套用後,您就可以照常建立及發布通知。

// This call modifies notificationBuilder to include the ongoing activity data.
ongoingActivity.apply(applicationContext)

// Post the notification.
startForeground(NOTIFICATION_ID, notificationBuilder.build())

在啟動器中新增動態狀態文字

上述程式碼會在錶面中新增可輕觸的圖示。如要在啟動器的「最近」部分提供更豐富的即時更新資訊,請建立 Status 物件並附加至 OngoingActivity。如果未提供自訂 Status,系統預設會使用通知的內容文字 (使用 setContentText() 設定)。

如要顯示動態文字,請使用 Status.Builder。您可以定義含有預留位置的範本字串,並提供 Status.Part 物件來填入這些預留位置。Status.Part 可以是動態的,例如碼錶或計時器。

以下範例說明如何建立顯示「Run for [a stopwatch timer]」的狀態:

// Define a template with placeholders for the activity type and the timer.
val statusTemplate = "#type# for #time#"

// Set the start time for a stopwatch.
// Use SystemClock.elapsedRealtime() for time-based parts.
val runStartTime = SystemClock.elapsedRealtime()

val ongoingActivityStatus = Status.Builder()
    // Sets the template string.
    .addTemplate(statusTemplate)
    // Fills the #type# placeholder with a static text part.
    .addPart("type", Status.TextPart("Run"))
    // Fills the #time# placeholder with a stopwatch part.
    .addPart("time", Status.StopwatchPart(runStartTime))
    .build()

最後,在 OngoingActivity.Builder 上呼叫 setStatus(),將這個 Status 連結至 OngoingActivity

val ongoingActivity =
    OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
        // ...
        // Add the status to the OngoingActivity.
        .setStatus(ongoingActivityStatus)
        .build()

其他自訂項目

除了 Status 以外,您也可以透過下列方式自訂持續性活動或通知。不過,這些自訂項目不一定會用到,具體視原始設備製造商 (OEM) 的實作方式而定。

持續性通知

  • 類別集可以判斷持續性活動的優先順序。
    • CATEGORY_CALL語音/視訊通話來電,或類似的同步通訊要求
    • CATEGORY_NAVIGATION地圖或即時路線導航
    • CATEGORY_TRANSPORT用於播放的媒體傳輸控制項
    • CATEGORY_ALARM鬧鐘或計時器
    • CATEGORY_WORKOUT健身
    • CATEGORY_LOCATION_SHARING臨時位置資訊分享 (新類別)
    • CATEGORY_STOPWATCH碼錶

持續性活動

  • 動畫圖示︰黑白向量,建議使用透明背景。此圖示會顯示在正常模式的錶面上。如未提供動畫圖示,系統會使用預設通知圖示 每個應用程式的預設通知圖示不盡相同。

  • 靜態圖示︰有透明背景的向量圖示,會顯示在微光模式的錶面上。如未設定動畫圖示,則正常模式的錶面會使用靜態圖示。如未提供此內容,則會使用通知圖示。如果兩者皆未設定,便會擲回例外狀況。(應用程式啟動器仍會使用應用程式圖示)。

  • OngoingActivityStatus:純文字或 Chronometer,會顯示在應用程式啟動器的「Recents」部分。如果未提供此內容,則會使用通知「內容文字」。

  • 觸控意圖︰這個 PendingIntent 用於在使用者輕觸持續性活動圖示時切換回應用程式,會顯示在錶面或啟動器項目上。這種意圖可能會與用來啟動應用程式的原始意圖不同。如未提供此意圖,系統會使用通知的內容意圖。如果兩者皆未設定,便會擲回例外狀況。

  • LocusId此 ID 用於指派持續性活動對應的啟動器捷徑。當活動持續進行時,啟動器的「Recents」部分會顯示此 ID。如未提供此 ID,啟動器會在「Recents」部分隱藏相同套件內的所有應用程式項目,只顯示持續性活動。

  • 持續性活動 ID:此 ID 可在應用程式有多個持續性活動時,用來區分對 fromExistingOngoingActivity() 的呼叫。

更新持續性活動

在大多數情況下,開發人員需要更新畫面上的資料時,會建立新的持續性通知和持續性活動。不過,如果您想保留例項,而不重新建立例項,Ongoing Activity API 也能提供用於更新 OngoingActivity 的輔助方法。

如果應用程式在背景執行,可以傳送更新內容至 Ongoing Activity API。但是,採取這個做法的頻率不能過高,因為更新方法會忽略彼此太過接近的呼叫。合理的頻率是每分鐘進行幾次更新。

如要更新持續性活動和已發布的通知,請使用您之前建立的物件並呼叫 update(),如以下範例所示:

ongoingActivity.update(context, newStatus)

為了方便起見,這裡使用靜態方法建立持續性活動。

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

停止執行持續性活動

當應用程式的持續性活動執行完畢時,只需取消持續性通知即可。

您也可以選擇在應用程式進入前景時,取消通知或持續性活動,然後在應用程式返回背景時,重新建立上述通知和活動,但這不是硬性規定。

暫停執行持續性活動

如果應用程式有明確的停止動作,請在取消暫停後繼續執行持續性活動。至於沒有明確停止動作的應用程式,請在活動暫停時結束活動。

最佳做法

使用 Ongoing Activity API 時,請特別注意下列事項:

  • 請為持續性活動設定靜態圖示,不論是要明確設定,還是設為使用通知的備用方案皆可。如未設定,就會發生 IllegalArgumentException

  • 請使用背景是透明的黑白向量圖示。

  • 請為持續性活動設定觸控意圖,不論是要明確設定,還是設為使用通知的備用方案皆可。如未設定,就會發生 IllegalArgumentException

  • 如果應用程式在資訊清單中宣告的 MAIN LAUNCHER 活動不只一種,請發布動態捷徑,然後使用 LocusId 將捷徑設為持續性活動的關聯資源。

在 Wear OS 裝置上播放媒體時發布媒體通知

如果 Wear OS 裝置正在播放媒體內容,請發布媒體通知。系統會據此建立相應的持續性活動。

如果您使用 Media3,系統會自動發布通知。如果手動建立通知,則應使用 MediaStyleNotificationHelper.MediaStyle,且對應的 MediaSession 應已填入工作階段活動