Hoạt động đang diễn ra

Trong Wear OS, thao tác ghép nối một hoạt động đang diễn ra với một thông báo hiển thị liên tục sẽ thêm thông báo đó vào các nền tảng khác trong giao diện người dùng Wear OS. Điều này cho phép người dùng tương tác nhiều hơn với các hoạt động chạy trong thời gian dài.

Thông báo hiển thị liên tục thường được dùng để chỉ báo một thông báo có tác vụ trong nền mà người dùng đang tích cực tương tác hoặc đang chờ xử lý và do đó sẽ chiếm dụng thiết bị.

Ví dụ: người dùng Wear OS có thể sử dụng một ứng dụng tập thể dục để ghi lại quá trình chạy qua một hoạt động, sau đó rời khỏi ứng dụng này để bắt đầu một số nhiệm vụ khác. Khi người dùng di chuyển khỏi ứng dụng tập thể dục, ứng dụng sẽ chuyển đổi sang một thông báo hiển thị liên tục gắn liền với một số tác vụ chạy ở chế độ nền để tiếp tục thông báo cho người dùng về quá trình tập luyện của họ. Thông báo cung cấp cho người dùng thông tin cập nhật và một cách dễ dàng để quay lại ứng dụng.

Tuy nhiên, để xem thông báo này, người dùng phải vuốt vào khay thông báo bên dưới mặt đồng hồ để tìm thông báo phù hợp. Chức năng này không thuận tiện như ở các nền tảng khác.

Nhờ Ongoing Activity API (API Hoạt động đang diễn ra), thông báo hiển thị liên tục của một ứng dụng có thể đưa thông tin lên nhiều khu vực mới thuận tiện trên Wear OS nhằm duy trì tương tác với người dùng.

Ví dụ: trong ứng dụng tập thể dục này, thông tin có thể xuất hiện trên mặt đồng hồ của người dùng dưới dạng một biểu tượng đang chạy có thể nhấn vào:

biểu tượng đang chạy

Hình 1. Chỉ báo hoạt động

Mục Recents (Gần đây) của trình chạy ứng dụng chung cũng liệt kê mọi hoạt động đang diễn ra:

trình chạy

Hình 2. Global launcher (Trình chạy chung)

Sau đây là các tình huống phù hợp để sử dụng một thông báo hiển thị liên tục gắn liền với hoạt động đang diễn ra:

hẹn giờ

Hình 3. Timer (Bộ tính giờ): Chủ động đếm ngược thời gian và kết thúc khi bộ tính giờ tạm dừng/dừng.

map

Hình 4. Chỉ đường từng chặng Thông báo chỉ đường đến một điểm đến. Kết thúc khi người dùng đến điểm đến hoặc ngừng điều hướng.

âm nhạc

Hình 5. Nội dung nghe nhìn: Phát nhạc trong một phiên. Kết thúc ngay khi người dùng tạm dừng phiên.

Wear tự động tạo các hoạt động đang diễn ra cho ứng dụng đa phương tiện.

Hãy xem lớp học lập trình về Hoạt động đang diễn ra để tìm hiểu ví dụ chuyên sâu về cách tạo hoạt động đang diễn ra cho các loại ứng dụng khác.

Thiết lập

Để bắt đầu sử dụng Ongoing Activity API (API Hoạt động đang diễn ra) trong ứng dụng, hãy thêm các phần phụ thuộc sau vào tệp build.gradle của ứng dụng:

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

Bắt đầu một hoạt động đang diễn ra

Hãy bắt đầu bằng cách tạo một thông báo hiển thị liên tục, sau đó tạo một hoạt động đang diễn ra.

Tạo một thông báo hiển thị liên tục

Một hoạt động đang diễn ra có liên quan chặt chẽ đến một thông báo hiển thị liên tục. Cả hai đều phối hợp hoạt động để thông báo cho người dùng về một nhiệm vụ mà họ đang tích cực thực hiện hoặc đang chờ xử lý và do đó sẽ chiếm dụng thiết bị.

Bạn phải ghép nối một hoạt động đang diễn ra với một thông báo hiển thị liên tục. Việc liên kết hoạt động đang diễn ra với một thông báo mang lại nhiều lợi ích như sau:

  • Thông báo là tính năng dự phòng trên các thiết bị không hỗ trợ hoạt động đang diễn ra. Thông báo là nền tảng duy nhất mà ứng dụng của bạn cho thấy khi chạy ở chế độ nền.
  • Trên Android 11 trở lên, Wear OS ẩn thông báo trong khay thông báo khi ứng dụng xuất hiện dưới dạng một hoạt động đang diễn ra trên các nền tảng khác.
  • Cách triển khai hiện tại sử dụng chính Notification làm cơ chế giao tiếp.

Tạo một thông báo hiển thị liên tục bằng cách sử dụng Notification.Builder.setOngoing.

Bắt đầu một hoạt động đang diễn ra

Khi bạn đã có một thông báo hiển thị liên tục, hãy tạo một hoạt động đang diễn ra theo mẫu sau. Hãy xem các nhận xét đi kèm để hiểu hành vi của từng sản phẩm.

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

Các bước sau đây nêu rõ phần quan trọng nhất trong ví dụ trước:

  1. Gọi .setOngoing(true) trên NotificationCompat.Builder và đặt bất kỳ trường tuỳ chọn nào.

  2. Tạo một OngoingActivityStatus hoặc một tuỳ chọn trạng thái khác như được mô tả trong phần sau để biểu thị văn bản.

  3. Tạo OngoingActivity và đặt mã thông báo (bắt buộc).

  4. Gọi apply() trên OngoingActivity theo ngữ cảnh.

  5. Gọi notificationManager.notify() và chuyển vào cùng một mã thông báo đã được thiết lập trong hoạt động đang diễn ra để liên kết chúng với nhau.

Trạng thái

Bạn sử dụng Status để hiện trạng thái trực tiếp hiện tại của OngoingActivity cho người dùng trên các nền tảng mới như mục Recents (Gần đây) trong trình chạy. Để sử dụng tính năng này, hãy dùng lớp con Status.Builder.

Trong hầu hết trường hợp, bạn chỉ cần thêm một mẫu đại diện cho văn bản mà bạn muốn được xuất hiện trong phần Recents (Gần đây) của trình chạy ứng dụng.

Sau đó, bạn có thể tuỳ chỉnh cách văn bản được trình bày với spans bằng cách sử dụng phương thức addTemplate() và chỉ định mọi phần động của văn bản dưới dạng Status.Part.

Ví dụ sau cho thấy cách làm cho từ "thời gian" xuất hiện với màu đỏ. Ví dụ này sử dụng Status.StopwatchPart để biểu thị đồng hồ bấm giờ trong mục Recents (Gần đây) của trình chạy ứng dụng.

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

Để tham chiếu một phần của mẫu, hãy sử dụng tên được bao quanh bởi #. Để tạo # trong kết quả, hãy sử dụng ## trong mẫu.

Ví dụ trước sử dụng HTMLCompat để tạo CharSequence giúp chuyển mẫu một cách dễ dàng hơn so với việc xác định đối tượng Spannable theo cách thủ công.

Chế độ tuỳ chỉnh khác

Ngoài Status, bạn có thể tuỳ chỉnh hoạt động đang diễn ra hoặc thông báo hiển thị liên tục theo những cách sau. Tuy nhiên, các cách tuỳ chỉnh này có thể không được sử dụng dựa trên cách triển khai của OEM.

Thông báo hiển thị liên tục

  • Tập hợp danh mục xác định mức độ ưu tiên của hoạt động đang diễn ra.
    • CATEGORY_CALL: cuộc gọi thoại, cuộc gọi video đến hoặc yêu cầu giao tiếp đồng bộ tương tự
    • CATEGORY_NAVIGATION: bản đồ hoặc tính năng chỉ đường từng chặng.
    • CATEGORY_TRANSPORT: trình điều khiển truyền tải nội dung nghe nhìn để phát lại
    • CATEGORY_ALARM: chuông báo hoặc đồng hồ hẹn giờ
    • CATEGORY_WORKOUT: một bài tập thể dục (danh mục mới)
    • CATEGORY_LOCATION_SHARING: chia sẻ vị trí tạm thời (danh mục mới)
    • CATEGORY_STOPWATCH: đồng hồ bấm giờ (danh mục mới)

Các hoạt động đang diễn ra

  • Animated icon (Biểu tượng động): vectơ màu đen trắng, tốt nhất là có nền trong suốt. Hiện trên mặt đồng hồ ở chế độ đang hoạt động. Nếu bạn không cung cấp biểu tượng động thì biểu tượng thông báo mặc định sẽ được sử dụng. (Biểu tượng thông báo mặc định của mỗi ứng dụng sẽ khác nhau.)

  • Static icon (Biểu tượng tĩnh): biểu tượng vectơ có nền trong suốt. Hiện trên mặt đồng hồ ở chế độ môi trường xung quanh. Nếu bạn không đặt biểu tượng động thì biểu tượng tĩnh sẽ được dùng trên mặt đồng hồ cho chế độ đang hoạt động. Nếu bạn không đặt biểu tượng này thì hệ thống sẽ sử dụng biểu tượng thông báo. Nếu bạn không đặt biểu tượng nào thì hệ thống sẽ gửi một trường hợp ngoại lệ. (Trình chạy ứng dụng vẫn sử dụng biểu tượng ứng dụng.)

  • OngoingActivityStatus: văn bản thuần tuý hoặc Chronometer. Hiện trong mục Recents(Gần đây) của trình chạy ứng dụng. Hệ thống sẽ sử dụng thông báo bằng “văn bản theo ngữ cảnh” nếu trạng thái không được cung cấp.

  • Touch Intent (Ý định nhấn): một PendingIntent dùng để chuyển trở lại ứng dụng nếu người dùng nhấn vào biểu tượng hoạt động đang diễn ra. Hiện trên mặt đồng hồ hoặc trên mục trình chạy. Ý định này có thể khác với ý định ban đầu dùng để chạy ứng dụng. Nếu không được cung cấp, hệ thống sẽ sử dụng ý định nội dung của thông báo. Nếu bạn không đặt biểu tượng nào thì hệ thống sẽ gửi một trường hợp ngoại lệ.

  • LocusId: Mã nhận dạng chỉ định lối tắt của trình chạy tương ứng với hoạt động đang diễn ra. Hiện trên trình chạy ở mục Recents (Gần đây) trong khi hoạt động đang diễn ra. Nếu mã không được cung cấp, trình chạy sẽ ẩn toàn bộ mục của ứng dụng trong mục Recents (Gần đây) khỏi gói chung và chỉ hiện hoạt động đang diễn ra.

  • Ongoing Activity ID (Mã nhận dạng hoạt động đang diễn ra): Mã được dùng để phân biệt các lệnh gọi tới fromExistingOngoingActivity() khi một ứng dụng có nhiều hoạt động đang diễn ra.

Cập nhật một hoạt động đang diễn ra

Trong hầu hết các trường hợp, nhà phát triển sẽ tạo một thông báo mới hiển thị liên tục và một hoạt động mới đang diễn ra khi họ cần cập nhật dữ liệu trên màn hình. Tuy nhiên, Ongoing Activity API (API Hoạt động đang diễn ra) cũng cung cấp các phương thức trợ giúp để cập nhật OngoingActivity nếu bạn muốn giữ lại một thực thể thay vì tạo lại thực thể đó.

Nếu đang chạy ở chế độ nền thì ứng dụng có thể gửi các bản cập nhật cho Ongoing Activity API (API Hoạt động đang diễn ra). Tuy nhiên, đừng thực hiện thao tác này quá thường xuyên vì phương thức cập nhật sẽ bỏ qua các lệnh gọi quá gần nhau. Mỗi phút một vài bản cập nhật là hợp lý.

Để cập nhật hoạt động đang diễn ra và thông báo đã được đăng, hãy sử dụng đối tượng mà bạn đã tạo trước đó rồi gọi update() như ví dụ sau:

Kotlin

ongoingActivity.update(context, newStatus)

Java

ongoingActivity.update(context, newStatus);

Để thuận tiện, bạn có thể dùng phương thức tĩnh tạo hoạt động đang diễn ra.

Kotlin

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

Java

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

Dừng một hoạt động đang diễn ra

Khi hoàn tất quá trình chạy dưới dạng một hoạt động đang diễn ra, ứng dụng chỉ cần huỷ thông báo hiển thị liên tục.

Bạn cũng có thể chọn huỷ thông báo hiển thị liên tục hoặc hoạt động đang diễn ra khi chạy trên nền trước, sau đó tạo lại các thông báo hoặc hoạt động này khi quay lại chế độ nền nhưng điều này là không bắt buộc.

Tạm dừng một hoạt động đang diễn ra

Nếu ứng dụng của bạn dừng một cách rõ ràng, hãy tiếp tục hoạt động đang diễn ra sau khi ứng dụng chạy trở lại. Đối với ứng dụng không dừng một cách rõ ràng, hãy kết thúc hoạt động đó khi ứng dụng bị tạm dừng.

Những phương pháp hay nhất

Hãy nhớ những điều sau khi làm việc với Ongoing Activity API (API Hoạt động đang diễn ra):

  • Hãy gọi ongoingActivity.apply(context) trước khi gọi notificationManager.notify(...).
  • Đặt biểu tượng tĩnh cho Ongoing Activity (Hoạt động đang diễn ra) của bạn một cách rõ ràng hoặc đặt dưới dạng trường hợp dự phòng thông qua thông báo. Nếu không, bạn sẽ nhận được một IllegalArgumentException.

  • Sử dụng biểu tượng vectơ đen trắng có nền trong suốt.

  • Đặt ý định nhấn cho hoạt động đang diễn ra của bạn một cách rõ ràng hoặc dưới dạng trường hợp dự phòng bằng cách sử dụng thông báo. Nếu không, bạn sẽ nhận được một IllegalArgumentException.

  • Đối với NotificationCompat, hãy sử dụng thư viện Core AndroidX core:1.5.0-alpha05+ bao gồm LocusIdCompatcác danh mục mới dành cho bài tập thể dục, đồng hồ bấm giờ và chia sẻ vị trí.

  • Nếu ứng dụng của bạn có nhiều hoạt động MAIN LAUNCHER được khai báo trong tệp kê khai, hãy xuất bản một lối tắt động và liên kết lối tắt này với hoạt động đang diễn ra bằng cách sử dụng LocusId.

Xuất bản thông báo về nội dung nghe nhìn khi phát nội dung nghe nhìn trên thiết bị Wear OS

Nếu nội dung nghe nhìn đang phát trên một thiết bị Wear OS, hãy xuất bản một thông báo về nội dung nghe nhìn. Điều này giúp hệ thống tạo hoạt động đang diễn ra tương ứng.

Nếu bạn đang dùng Media3, thông báo sẽ được xuất bản tự động. Nếu bạn tạo thông báo theo cách thủ công, thông báo đó sẽ sử dụng MediaStyleNotificationHelper.MediaStyleMediaSession tương ứng phải điều sẵn hoạt động trong phiên.