Sử dụng thông báo trên Android

Lớp học lập trình này nằm trong khóa học Nâng cao về Android trong Kotlin. Bạn sẽ nhận được nhiều giá trị nhất từ khóa học này nếu bạn làm việc qua các lớp học lập trình theo trình tự, nhưng bạn không bắt buộc phải làm vậy. Tất cả các lớp học lập trình đều có trên trang đích của các lớp học lập trình Android nâng cao trong Kotlin.

Giới thiệu

Thông báo là những thông báo hiển thị cho người dùng bên ngoài giao diện người dùng của ứng dụng. Thông báo sẽ hiển thị ở đầu màn hình nếu thiết bị được mở khóa hoặc màn hình khóa sẽ tùy thuộc vào chế độ cài đặt bảo mật.

Thông báo thông thường bao gồm tiêu đề, nội dung mô tả và biểu tượng. Thông báo cũng có thể chứa các thao tác có thể nhấp vào, trả lời nhanh, nội dung và hình ảnh có thể mở rộng.

Thông báo có thể gửi thông tin kịp thời và có thể có các nút cho phép người dùng thực hiện thao tác nhanh, chẳng hạn như gửi tin nhắn trả lời hoặc tạm hoãn chuông báo. Khi nhấp vào một thông báo, người dùng sẽ được chuyển đến một chế độ xem trong ứng dụng liên quan đến nội dung thông báo đó.

Thông báo là một cách hữu ích để nhắc người dùng về một việc quan trọng, cho họ biết điều gì đó đã xảy ra hoặc truyền đạt thông tin quan trọng mà họ cần ngay lập tức trong khi ứng dụng của bạn chạy ở chế độ nền. Sử dụng thông báo một cách thận trọng. Hành động này không chỉ tôn trọng người dùng mà còn tăng khả năng nhận được thông báo của ứng dụng.

Trong lớp học lập trình này, bạn sẽ tìm hiểu cách tạo và sử dụng thông báo trong ứng dụng Android.

Kiến thức bạn cần có

Bạn cần thông thạo:

  • Cách đánh giá các ứng dụng Android trong Kotlin. Cụ thể, bạn nên sử dụng SDK Android.
  • Cách kiến trúc ứng dụng bằng Thành phần cấu trúc và liên kết dữ liệu.
  • Tìm hiểu cơ bản về BroadcastRecipientrs
  • Tìm hiểu cơ bản về AlarmManager

Kiến thức bạn sẽ học được

  • Cách tạo, tạo kiểu và gửi thông báo.
  • Cách hủy thông báo.
  • Cách tạo kênh thông báo.
  • Cách thêm hành động nhanh vào thông báo.
  • Cách hiển thị huy hiệu thông báo trên biểu tượng ứng dụng.

Bạn sẽ thực hiện

  • Thêm thông báo vào ứng dụng dành cho người mới bắt đầu.
  • Hủy thông báo mà bạn đã gửi trước đây.
  • Tạo kênh cho các loại thông báo.
  • Tùy chỉnh thông báo trong ứng dụng dành cho người mới bắt đầu.
  • Thêm Thao tác nhanh để tương tác với thông báo của bạn.
  • Tắt huy hiệu thông báo.

Nấu trứng rất đơn giản, nhưng cũng có thể là một nhiệm vụ khó khăn nếu bạn không theo dõi thời gian. Trong lớp học lập trình này, bạn sẽ làm việc với một ứng dụng hẹn giờ trứng và tạo ứng dụng đó hoàn hảo, giống như trứng của bạn trong tương lai. Bạn sẽ bắt đầu với ứng dụng hẹn giờ trứng đang hoạt động cho phép người dùng đặt các cài đặt thời gian nấu khác nhau cho các kiểu trứng khác nhau. Đồng hồ hẹn giờ sẽ đếm ngược từ khoảng thời gian đã chọn và hiển thị thông báo ngắn khi trứng đã sẵn sàng.

Điều này có vẻ hữu ích, nhưng nó chưa hoàn hảo và không thực sự thân thiện với người dùng. Để bắt đầu, thông báo ngắn sẽ chỉ hiển thị trong một khoảng thời gian ngắn nên rất dễ bỏ lỡ. Ngoài ra, nếu ứng dụng không chạy trên nền trước hoặc thiết bị bị khóa, thì sẽ không có chỉ báo bằng hình ảnh nào cho trạng thái của đồng hồ hẹn giờ sau khi thông báo ngắn này biến mất.

Tốt nhất là đồng hồ hẹn giờ trứng nên sử dụng thông báo để cho người dùng biết khi hết thời gian. Người dùng thực sự cần phải biết trứng đã sẵn sàng ngay lập tức, nếu không thì trứng sẽ bị nướng quá mức! Thông báo bằng hình ảnh, có thể bao gồm âm thanh và có thể khiến thiết bị rung — tất cả các cách để thu hút sự chú ý của người dùng! Bằng cách này, bạn có thể đạt được những quả trứng hoàn hảo và những người dùng hài lòng, ăn ngon.

Để tải ứng dụng mẫu, bạn có thể:

Sao chép kho lưu trữ từ GitHub và chuyển sang nhánh start.

$  git clone https://github.com/googlecodelabs/android-kotlin-notifications


Hoặc bạn có thể tải tệp lưu trữ xuống dưới dạng tệp Zip, giải nén và mở tệp đó trong Android Studio.

Tải tệp zip xuống

  1. Mở và chạy ứng dụng trong Android Studio.

Bạn sẽ thấy hình ảnh quả trứng và trình đơn thả xuống có danh sách các khoảng thời gian định sẵn để nấu trứng. Nhấp vào hình tam giác cho trình đơn thả xuống Luộc mềm. Tùy chọn đầu tiên trong danh sách được đưa ra cho mục đích thử nghiệm và đặt chuông báo chỉ ở mức 10 giây. Bên cạnh danh sách, sẽ có một công tắc khởi động bộ hẹn giờ trứng. Bạn có thể dùng công tắc này để bắt đầu và dừng đồng hồ hẹn giờ trứng bất cứ lúc nào mình muốn. Mã điều kiện khởi động có đầy đủ chức năng, nghĩa là bạn có thể thiết lập đồng hồ hẹn giờ trứng và đếm ngược đến 0. Khi đồng hồ hẹn giờ kết thúc, thông báo ngắn sẽ hiển thị như bên dưới.

  1. Kiểm tra mã nguồn. Ứng dụng khởi động bao gồm một hoạt động có tên là MainActivity. Có ba gói phụ là receiver, uiutil.

  • /Recipientr—Gói receiver chứa hai bộ thu phát có tên là AlarmReceiverSnoozeReceiver. AlarmReceiver được AlarmManager kích hoạt để gửi thông báo khi hết giờ hẹn do người dùng xác định. SnoozeReceiver xử lý thao tác nhấp của người dùng để tạm hoãn thông báo.
  • /ui – Thông tin này chứa EggTimerFragment, thuộc phần giao diện người dùng của ứng dụng. EggTimerViewModel chịu trách nhiệm bắt đầu và hủy đồng hồ hẹn giờ cũng như các tác vụ khác trong ứng dụng liên quan đến vòng đời.
  • /util – Trong gói này, có hai tệp. BindingUtils.kt có bộ chuyển đổi liên kết để cho phép liên kết dữ liệu giữa giao diện người dùng của ứng dụng và ViewModel. NotificationUtils.kt có các phương thức mở rộng trên NotificationManager.

Sử dụng thông báo là một cách tuyệt vời để thu hút sự chú ý của người dùng vào ứng dụng của bạn. Cho dù ứng dụng của bạn không chạy hay chạy ở nền trước, thông báo sẽ hiển thị cửa sổ bật lên ở đầu màn hình và có thể bao gồm âm thanh hoặc rung. Để tạo thông báo, bạn cần sử dụng trình tạo thông báo và cung cấp văn bản tiêu đề, văn bản nội dung và biểu tượng. Sau khi trình tạo có tất cả các trường cần thiết, NotificationManager, là dịch vụ hệ thống, sẽ giúp bạn hiển thị nội dung này dưới dạng thông báo. NotificationManager chịu trách nhiệm gửi thông báo, cập nhật nội dung và hủy thông báo. Trong các bước sau, bạn sẽ thêm các phương thức mở rộng vào NotificationManager. Theo cách này, mỗi khi bạn cần sử dụng NotificationManager, bạn sẽ có thể sử dụng các chức năng tiện ích này để đạt được chức năng cần thiết.

Bước 1: Tạo thông báo cơ bản

Trong nhiệm vụ này, bạn đang tạo một thông báo mới, đặt thông báo cho người dùng và gửi thông báo.

  1. Mở lớp NotificationUtils.kt rồi tìm TODO: Step 1.1. Bạn sẽ tìm thấy việc cần làm phù hợp trong lớp học lập trình này và mã ứng dụng.
  2. Kiểm tra hàm sendNotification() đã cho. Bạn sẽ mở rộng chức năng tiện ích này thành NotificationManager để gửi thông báo.
//NotificationUtils.kt
// TODO: Step 1.1 extension function to send messages (GIVEN)
/**
 * Builds and delivers a notification.
 *
 * @param messageBody, notification text.
 * @param context, activity context.
 */
fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
  1. Nhận bản sao của trình tạo thông báo, chuyển trong ngữ cảnh ứng dụng và mã nhận dạng kênh. Mã nhận dạng kênh là một giá trị chuỗi cho kênh.

Kênh thông báo là một cách để nhóm thông báo. Bằng cách nhóm các loại thông báo tương tự với nhau, nhà phát triển và người dùng có thể kiểm soát tất cả thông báo trong kênh. Sau khi tạo kênh, bạn có thể sử dụng kênh đó để gửi số lượng thông báo bất kỳ.

//NotificationUtils.kt
// TODO: Step 1.2 get an instance of NotificationCompat.Builder
val builder = NotificationCompat.Builder(
        applicationContext,
        applicationContext.getString(R.string.egg_notification_channel_id)
)
  1. Đặt biểu tượng thông báo để đại diện cho ứng dụng của bạn, tiêu đề và nội dung nội dung của thông báo bạn muốn đưa ra cho người dùng. Bạn sẽ thấy nhiều tùy chọn hơn để tùy chỉnh thêm thông báo của mình trong lớp học lập trình, nhưng đây là lượng dữ liệu tối thiểu bạn cần đặt để gửi thông báo.
//NotificationUtils.kt
   // TODO: Step 1.3 set title, text and icon to builder
   .setSmallIcon(R.drawable.cooked_egg)
   .setContentTitle(applicationContext.getString(R.string.notification_title))
   .setContentText(messageBody)
  1. Tiếp theo, bạn cần gọi notify() bằng một mã nhận dạng duy nhất cho thông báo và với đối tượng Notification từ trình tạo của bạn.

Mã này đại diện cho phiên bản thông báo hiện tại và cần thiết để cập nhật hoặc hủy thông báo này. Vì ứng dụng của bạn sẽ chỉ có một thông báo đang hoạt động tại một thời điểm nhất định, nên bạn có thể sử dụng cùng một mã nhận dạng cho tất cả thông báo của mình. Bạn đã nhận được một hằng số cho mục đích này có tên là NOTIFICATION_ID trong NotificationUtils.kt. Xin lưu ý rằng bạn có thể gọi trực tiếp cho notify() vì bạn đang thực hiện lệnh gọi từ một hàm mở rộng của cùng một lớp.

//NotificationUtils.kt
   // TODO: Step 1.4 call notify to send the notification
    // Deliver the notification
    notify(NOTIFICATION_ID, builder.build())
  1. Mở ui/EggTimerViewModel.kt rồi tìm hàm startTimer(). Chức năng này tạo ra một chuông báo với khoảng thời gian đã chọn khi người dùng bật bộ tính giờ trứng.
  2. Bạn sẽ kích hoạt thông báo trong chức năng này khi người dùng bắt đầu bộ tính giờ. Để gọi hàm sendNotification() mà bạn đã triển khai trước đó, bạn cần có một thực thể của NotificationManager. NotificationManager là một dịch vụ hệ thống cung cấp tất cả các chức năng của API thông báo, bao gồm cả chức năng mở rộng mà bạn đã thêm. Bất cứ khi nào bạn muốn gửi, hủy hoặc cập nhật thông báo mà bạn cần yêu cầu bản sao của NotificationManager từ hệ thống. Gọi hàm sendNotification()| kèm theo tin nhắn thông báo và bối cảnh.
// EggTimerViewModel.kt
// TODO: Step 1.5 get an instance of NotificationManager 
// and call sendNotification

val notificationManager = ContextCompat.getSystemService(
    app, 
    NotificationManager::class.java
) as NotificationManager
                notificationManager.sendNotification(app.getString(R.string.timer_running), app)

Bạn sắp hoàn tất rồi. Tuy nhiên, nếu bạn chạy ứng dụng ngay bây giờ và đặt hẹn giờ, bạn sẽ không nhận được thông báo.

  1. Mở logcat rồi tìm "No Channel found". Bạn sẽ thấy thông báo lỗi cho biết egg_channel không tồn tại. Trong các bước sau, bạn sẽ tìm hiểu thêm về Kênh thông báo và khắc phục vấn đề này.

Bước 2: Kênh thông báo

Bắt đầu từ API cấp 26, bạn phải chỉ định tất cả thông báo cho một kênh. Nếu nhấn và giữ biểu tượng trình chạy ứng dụng, chọn thông tin ứng dụng rồi nhấn vào thông báo, bạn sẽ thấy một danh sách các kênh thông báo liên kết với ứng dụng. Hiện tại, danh sách đang trống vì ứng dụng của bạn chưa tạo kênh nào.

Kênh đại diện cho thông tin "loại&quat; ví dụ: đồng hồ hẹn giờ trứng có thể gửi thông báo khi trứng được nấu và cũng sử dụng một kênh khác để gửi thông báo hàng ngày để nhắc bạn ăn trứng với bữa sáng. Tất cả thông báo trong một kênh được nhóm lại với nhau và người dùng có thể định cấu hình chế độ cài đặt thông báo cho toàn bộ kênh. Điều này cho phép người dùng cá nhân hoá các chế độ cài đặt thông báo dựa trên loại thông báo mà họ quan tâm. Ví dụ: người dùng của bạn có thể tắt thông báo về bữa sáng, nhưng vẫn chọn xem thông báo từ đồng hồ hẹn giờ.

Nhà phát triển đặt các chế độ cài đặt ban đầu, mức độ quan trọng và hành vi để áp dụng cho tất cả các thông báo trên kênh. Sau khi bạn đặt các chế độ cài đặt ban đầu, người dùng có thể ghi đè các chế độ cài đặt này.

Ở Bước 1.1, bạn sử dụng egg_notification_channel_id làm kênh thông báo của mình. Do đó, bây giờ bạn cần thực sự tạo và tùy chỉnh các chế độ cài đặt thông báo cũng như hành vi của kênh này.

  1. Mở EggTimerFragment.kt rồi tìm hàm createChannel().
  2. Chuyển mã nhận dạng kênh duy nhất vào hàm dựng của NotificationChannel.
  3. Chuyển tên kênh thông báo. Người dùng cũng sẽ thấy trong màn hình Cài đặt.
  4. Ở thông số cuối cùng, hãy chuyển mức độ quan trọng cho kênh thông báo. Các cấp độ quan trọng sẽ được đề cập ở phần sau trong lớp học lập trình này. Vì vậy, bây giờ bạn có thể dùng NotificationManager.IMPORTANCE_LOW.
  5. Trên đối tượng notificationChannel, hãy đặt enableLights thành true. Chế độ cài đặt này sẽ bật đèn khi có thông báo.
  6. Trên đối tượng notificationChannel, hãy đặt lightColor thành màu đỏ để hiển thị đèn màu đỏ khi một thông báo hiển thị.
  7. Trên đối tượng notificationChannel, hãy đặt enableVibration thành true để bật chế độ rung.
  8. Trên đối tượng notificationChannel, hãy mô tả kênh thành ‘Time for breakfast#39;
  9. Nhận bản sao của NotificationManager bằng cách gọi getSystemService().
  10. Gọi createNotificationChannel() trên NotificationManager và chuyển đối tượng notificationChannel mà bạn đã tạo ở bước trước.
//EggTimerFragment.kt
private fun createChannel(channelId: String, channelName: String) {
    // TODO: Step 1.6 START create a channel
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val notificationChannel = NotificationChannel(
            channelId,
            channelName,
            // TODO: Step 2.4 change importance
            NotificationManager.IMPORTANCE_LOW
        )
        // TODO: Step 2.6 disable badges for this channel

        notificationChannel.enableLights(true)
        notificationChannel.lightColor = Color.RED
        notificationChannel.enableVibration(true)
        notificationChannel.description = "Time for breakfast"

        val notificationManager = requireActivity().getSystemService(
            NotificationManager::class.java
        )
        notificationManager.createNotificationChannel(notificationChannel)
    }
    // TODO: Step 1.6 END create channel
}
  1. Tiếp theo, để tạo một kênh, bạn cần gọi hàm createChannel() mà bạn vừa viết (Bước 1.7). Hàm này nhận 2 tham số, mã nhận dạng kênh và tên kênh. Bạn cần tra cứu mã nhận dạng kênh và tên kênh của mình trong những tài nguyên chuỗi đã được cung cấp trong dự án.
// EggTimerFragment.kt
    // TODO: Step 1.7 call createChannel
    createChannel(
          getString(R.string.egg_notification_channel_id),
          getString(R.string.egg_notification_channel_name)
    )
  1. Bạn cần chuyển mã nhận dạng kênh cho trình tạo thông báo. Bạn đã thực hiện bước này trong Bước 1.2. Việc đặt giá trị không chính xác làm mã nhận dạng kênh sẽ khiến thông báo không thành công. Mở NotificationUtils.kt để xác minh mã nhận dạng kênh mà bạn đã đặt trước đó là chính xác.
// NotificationUtils.kt
val builder = NotificationCompat.Builder(
        applicationContext,
       // TODO: Step 1.8 verify the notification channel name
        applicationContext.getString(R.string.egg_notification_channel_id)
)
  1. Chạy ứng dụng và bạn sẽ thấy ứng dụng gửi thông báo mỗi khi bạn khởi động đồng hồ hẹn giờ.
  2. Kéo thanh trạng thái và quan sát rằng tiêu đề, nội dung và biểu tượng thông báo giống như bạn đã đặt trong các bước trước đó.
  3. Để xác minh kênh mới tạo, hãy đóng ứng dụng rồi tìm biểu tượng ứng dụng. Nhấn và giữ biểu tượng ứng dụng rồi chọn Thông tin ứng dụng.

  1. Chọn Thông báo từ danh sách các chế độ cài đặt. Bạn sẽ thấy một kênh mới có tên Egg, ngay bên dưới tùy chọn cài đặt Hiển thị thông báo.

Giờ đây, khi bạn chạy ứng dụng, thông báo sẽ hiển thị. Cả bạn, với tư cách là nhà phát triển ứng dụng, đều có thể tùy chỉnh cài đặt và hành vi cho tất cả các thông báo gửi trên kênh này. Xin chúc mừng, bạn đã tạo một thông báo!

Bước 3: Thêm thông báo vào ứng dụng

Cho đến nay, quá trình này cho thấy cách sử dụng cơ bản của API thông báo, nhưng việc gửi thông báo ngay sau khi bắt đầu hẹn giờ không có ý nghĩa. Người dùng có thể muốn được thông báo khi trứng đã sẵn sàng. Trong phần sau đây của lớp học lập trình, bạn sẽ sửa lỗi này và thay đổi thông báo ngắn thành thông báo.

Bạn đã gửi thông báo và quan sát cách thông báo được hiển thị cho người dùng, nhưng đây chỉ là bước đầu tiên để tạo thông báo tuyệt vời. Ở bước này, bạn sẽ thay đổi để thông báo được gửi vào thời điểm thích hợp hơn.

Ứng dụng của bạn sử dụng AlarmManager để thiết lập chuông báo. Mã liên quan đến AlarmManager đã được cung cấp trong mã dành cho người mới bắt đầu và được dùng để hiển thị thông báo nhanh. AlarmManager theo dõi lựa chọn thời gian mong muốn và sẽ kích hoạt hàm onReceive() của AlarmReceiver.kt khi hết thời gian. Nếu mở AlarmReceiver.kt và chuyển đến onReceive(), bạn sẽ thấy thông báo ngắn tùy chọn được hiển thị mỗi khi bạn thiết lập đồng hồ hẹn giờ trứng.

  1. Mở AlarmReceiver.kt, một phiên bản của NotificationManager và gọi hàm sendNotification() cùng với các thông số bối cảnh và văn bản thông báo.
// AlarmReceiver.kt
   // TODO: Step 1.9 add call to sendNotification
   val notificationManager = ContextCompat.getSystemService(
       context, 
       NotificationManager::class.java
   ) as NotificationManager
             
   notificationManager.sendNotification(
       context.getText(R.string.eggs_ready).toString(), 
       context
   )
  1. Bạn có thể xóa thông báo nhanh này vì ứng dụng của bạn sẽ gửi thông báo khi hết thời gian.
// AlarmReceiver.kt
     // TODO: Step 1.10 [Optional] remove toast
//   Toast.makeText(
//       context, 
//       context.getText(R.string.eggs_ready),
//       Toast.LENGTH_SHORT
//   ).show()
  1. Chạy ứng dụng của bạn . Bạn sẽ thấy một thông báo mỗi khi bạn khởi động đồng hồ hẹn giờ và mỗi khi đồng hồ hẹn giờ kết thúc.

Thông tin này không lý tưởng. Bạn không muốn gửi quá nhiều thông báo cho người dùng. Bạn có thể xóa thông báo đầu tiên được gửi khi người dùng bắt đầu bộ tính giờ.

  1. Mở EggTimerFragment.kt và xóa mã thông báo cho Bước 1.5.
// EggTimeViewModel.kt

// TODO: Step 1.5 get an instance of NotificationManager 
// and call sendNotification
// val notificationManager = ContextCompat.getSystemService(
//      app,
//      NotificationManager::class.java
// ) as NotificationManager
// notificationManager.sendNotification(app.getString(R.string.eggs_ready), app)
  1. Chạy lại ứng dụng.
  2. Đặt đồng hồ hẹn giờ, đặt ở chế độ nền rồi chờ thời gian hoàn tất. Bạn sẽ thấy một thông báo. Đây là thông báo hữu ích hơn nhiều.

Bước 4: Thêm ý định nội dung

  1. Chạy lại ứng dụng nếu ứng dụng chưa chạy.
  2. Nhấp vào thông báo. Không có gì xảy ra cả!

Hiện thông báo và thông báo cho người dùng thật tuyệt vời, nhưng khi người dùng nhấp vào thông báo, họ mong muốn quay lại ứng dụng tương ứng. Trong phần này của lớp học lập trình, bạn sẽ thêm một ý định vào thông báo để đưa người dùng trở lại màn hình hẹn giờ.

Intent là một đối tượng nhắn tin mà bạn có thể dùng để yêu cầu một thao tác từ một thành phần ứng dụng khác. Ý định có thể dùng để bắt đầu một hoạt động, dịch vụ hoặc phân phối một chương trình phát sóng. Trong trường hợp này, bạn sử dụng ý định này để yêu cầu hệ thống mở MainActivity khi người dùng nhấn vào thông báo. Vì ứng dụng của bạn chỉ bao gồm một chế độ xem, nên bạn không có nhiều tùy chọn tại đây. Tuy nhiên, trong một ứng dụng lớn hơn, thông báo phải tạo ra trải nghiệm liền mạch bằng cách đưa người dùng đến màn hình phù hợp khi họ tương tác với thông báo.

  1. Mở NotificationUtils.kt rồi tìm hàm mở rộng sendNotification().
  2. Tạo Intent cùng với applicationContext của bạn và hoạt động cần chạy, MainActivity::class.java.
// NotificationUtils.kt

fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
    // Create the content intent for the notification, which launches
    // this activity
   // TODO: Step 1.11 create intent
    val contentIntent = Intent(applicationContext, MainActivity::class.java)

Bạn đã tạo ý định nhưng thông báo sẽ hiển thị bên ngoài ứng dụng của bạn. Để thực hiện ý định này bên ngoài ứng dụng, bạn cần tạo một PendingIntent mới.

PendingIntent cấp quyền cho một ứng dụng khác hoặc hệ thống để thực hiện thao tác thay mặt cho ứng dụng của bạn. Bản thân PendingIntent chỉ là tham chiếu đến một mã thông báo do hệ thống duy trì, dùng để mô tả dữ liệu gốc dùng để truy xuất mã đó. Điều này có nghĩa là ngay cả khi quy trình sở hữu của ứng dụng bị hủy, thì PendingIntent vẫn có thể dùng được từ các quy trình khác mà quy trình đó được đưa vào. Trong trường hợp này, hệ thống sẽ sử dụng ý định đang chờ xử lý để mở ứng dụng thay mặt bạn, bất kể ứng dụng bộ tính giờ có đang chạy hay không.

  1. Tạo PendingIntent với applicationContext, NOTIFICATION_ID, contentIntent mà bạn đã tạo ở bước trước đó và cờ PendingIntent. Cờ PendingIntent chỉ định tùy chọn tạo PendingIntent mới hoặc sử dụng thẻ hiện có. Bạn cần đặt PendingIntent.FLAG_UPDATE_CURRENT làm cờ vì bạn không muốn tạo thông báo mới nếu có thông báo hiện có. Bằng cách này, bạn sẽ sửa đổi PendingIntent hiện tại, liên kết với ý định mà bạn đang cung cấp.
// NotificationUtils.kt
   // TODO: Step 1.12 create PendingIntent
    val contentPendingIntent = PendingIntent.getActivity(
        applicationContext, 
        NOTIFICATION_ID,
        contentIntent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )
  1. Chuyển PendingIntent đến thông báo của bạn. Bạn làm việc này bằng cách gọi setContentIntent() trên NotificationBuilder. Bây giờ, khi bạn nhấp vào thông báo, PendingIntent sẽ được kích hoạt để mở MainActivity của bạn.
  2. Đồng thời đặt setAutoCancel() thành true để khi người dùng nhấn vào thông báo, thông báo sẽ tự loại bỏ khi đưa họ đến ứng dụng.
// NotificationUtils.kt
    // TODO: Step 1.13 set content intent
    .setContentIntent(contentPendingIntent)
    .setAutoCancel(true)
  1. Chạy lại ứng dụng.
  2. Đặt hẹn giờ, đặt ứng dụng trong nền và chờ thông báo xuất hiện.
  3. Sau khi bạn nhìn thấy thông báo, hãy nhấp vào thông báo bằng cách kéo thanh trạng thái xuống và quan sát cách ứng dụng đưa lên nền trước.

Bước 5: Hủy thông báo

Bạn có một đồng hồ hẹn giờ hình trứng đang hoạt động và có thông báo, nhưng có một vấn đề nhỏ. Nếu bạn đặt hẹn giờ, nhận thông báo và đặt lại đồng hồ hẹn giờ, thì thông báo trước đó sẽ vẫn còn trên thanh trạng thái trong khi đồng hồ hẹn giờ mới đang chạy. Điều này có thể khiến người dùng nhầm lẫn nếu ứng dụng chạy ở chế độ nền và có thể dẫn đến việc trứng bị chín.

Để khắc phục sự cố này, bạn cần xóa thông báo trước đó khi bắt đầu một đồng hồ hẹn giờ mới. Hãy bắt đầu bằng cách tạo một hàm mở rộng khác trong NotificationUtils.kt. NotificationManager có một API để hủy tất cả thông báo đang hoạt động được gọi là cancelAll().

  1. Mở NotificationsUtil.kt.
  2. Thêm một hàm mở rộng trên NotificationManager để gọi cancelAll().
// NotificationUtils.kt

// TODO: Step 1.14 Cancel all notifications
/**
 * Cancels all notifications.
 *
 */
fun NotificationManager.cancelNotifications() {
    cancelAll()
}
  1. Mở EggTimerViewModel.kt và chuyển đến hàm startTimer().
  2. Bên trong startTimer(), hãy lấy một bản sao của NotificationManager từ hệ thống và gọi cancelNotifications().
//  EggTimerViewModel.kt
   //TODO Step 1.15 call cancel notification
    val notificationManager =
       ContextCompat.getSystemService(
            app,
            NotificationManager::class.java
        ) as NotificationManager
    notificationManager.cancelNotifications()       
  1. Chạy ứng dụng và bắt đầu hẹn giờ.
  2. Sau khi bạn thấy thông báo, hãy bắt đầu lại đồng hồ hẹn giờ và quan sát cách ứng dụng của chúng tôi tự động xóa thông báo trước đó khỏi thanh trạng thái.

Khung thông báo cung cấp nhiều tùy chọn tùy chỉnh để nhà phát triển đặt hành động tùy chỉnh và tạo kiểu thông báo khi cần. Trong nhiệm vụ này, bạn sẽ tìm hiểu cách tùy chỉnh thông báo từ bộ tính giờ trứng.

Bước 1: Tạo kiểu cho thông báo

Tạo kiểu thông báo theo nhu cầu của bạn và nội dung thông báo sẽ làm cho thông báo trở nên nổi bật và trông giống như một phần mở rộng của ứng dụng. Khung thông báo đi kèm với một số kiểu được tích hợp sẵn để trợ giúp và bạn luôn có thể tạo kiểu của riêng mình.

NotificationCompat cung cấp các kiểu tích hợp sẵn cho:

  • BigTextStyle có thể hiển thị một khối văn bản lớn, chẳng hạn như hiển thị nội dung của email khi mở rộng.
  • BigPictureStyle, trình bày các thông báo có định dạng lớn bao gồm một tệp đính kèm hình ảnh lớn.
  • InboxStyle, hiển thị nội dung văn bản kiểu cuộc trò chuyện.
  • MediaStyle hiển thị các nút điều khiển chế độ phát nội dung nghe nhìn.
  • MessagingStyle, hiển thị các thông báo có định dạng lớn bao gồm nhiều tin nhắn giữa mọi người.

Bạn có thể xem thêm thông tin về các kiểu khác trong tài liệu để Tạo thông báo có thể mở rộng. Ở bước này, bạn sẽ dùng NotificationCompat.BigPictureStyle để tạo thông báo có thể mở rộng nhằm hiển thị hình ảnh quả trứng lớn khi được mở rộng.

  1. Mở NotificationUtils.kt rồi tìm hàm sendNotification().
  2. Bắt đầu bằng cách tải hình ảnh từ resources bằng BitmapFactory.
// NotificationUtils.kt

// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
     applicationContext.resources, 
     R.drawable.cooked_egg
)
  1. Tạo BigPictureStyle mới và đặt hình ảnh của bạn.
  2. Đặt bigLargeIcon() thành null để biểu tượng lớn biến mất khi thông báo được mở rộng.
// NotificationUtils.kt

// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
     applicationContext.resources, 
     R.drawable.cooked_egg
)
val bigPicStyle = NotificationCompat.BigPictureStyle()
        .bigPicture(eggImage)
        .bigLargeIcon(null)
  1. Đặt kiểu với setStyle()bigPicStyle.
  2. Đặt biểu tượng lớn với setLargeIcon() thành eggImage để hình ảnh được hiển thị dưới dạng một biểu tượng nhỏ hơn khi thông báo được thu gọn.
// NotificationUtils.kt
// TODO: Step 2.1 add style to builder
.setStyle(bigPicStyle)
.setLargeIcon(eggImage)
  1. Chạy ứng dụng và hẹn giờ. Khi thông báo được hiển thị lần đầu tiên, thông báo sẽ ở trạng thái thu gọn trong ngăn thông báo. Nếu bạn mở rộng thông báo, hình ảnh lớn sẽ hiển thị trong vùng thông báo mở rộng.

Bước 2: Hành động đối với thông báo

Hành động thông báo là một tùy chỉnh khác mà bạn có thể thêm vào thông báo của mình. Thông báo hiện chuyển hướng đến ứng dụng của bạn khi người dùng nhấp vào. Ngoài hành động thông báo mặc định này, bạn có thể thêm các nút hành động hoàn thành việc cần làm liên quan đến ứng dụng trong thông báo.

Một thông báo có thể cung cấp tối đa ba nút hành động cho phép người dùng phản hồi nhanh, chẳng hạn như tạm hoãn lời nhắc hoặc trả lời tin nhắn văn bản. Các nút hành động này không được sao chép hành động được thực hiện khi người dùng nhấn vào thông báo.

Để thêm một nút hành động, hãy chuyển một hàm PendingIntent vào hàm addAction() trên trình tạo. Điều này tương tự như việc thiết lập hành động nhấn mặc định của thông báo bằng cách gọi setContentIntent(), ngoại trừ thay vì chạy một hoạt động, bạn có thể làm nhiều việc khác nhau, ví dụ: bắt đầu một BroadcastReceiver thực hiện một công việc trong nền để thao tác này không làm gián đoạn ứng dụng đã mở.

Trong lớp học lập trình này, bạn đã được cung cấp BoadcastReceiver có tên là SnoozeReceiver. Bạn sẽ sử dụng SnoozeReceiver để nhận hành động nhấp vào Thông báo cho người dùng. Trong các bước sau đây, bạn sẽ thêm mã để tạm ẩn thông báo về bộ hẹn giờ trứng trong 60 giây khi người dùng nhấp vào nút tạm ẩn hành động. Khi người dùng nhấp vào hành động tạm hoãn, SnoozeReceiver sẽ nhận được một ý định và sẽ tạo một chuông báo mới để gửi thông báo mới sau 60 giây.

  1. Mở SnoozeReceiver.kt. Lớp này tương tự như AlarmReceiver mà bạn đã dùng trước đó. Trong các bước sau đây, bạn sẽ thêm mã sẽ kích hoạt hàm onReceive() của SnoozeReceiver. Nói ngắn gọn, mã trong SnoozeReceiver sẽ tạo một chuông báo mới để gửi thông báo mới sau đó một phút. Di chuyển xuống cuối chức năng onRecipient, nhận một bản sao của thông báo dựa trên hệ thống và gọi hủy tất cả.
// SnoozeReceiver.kt
        val notificationManager = ContextCompat.getSystemService(
            context,
            NotificationManager::class.java
        ) as NotificationManager
        notificationManager.cancelAll()
  1. Để dùng SnoozeReceiver, hãy mở NotificationUtils.kt.
  2. Tạo Intent snoozeIntent mới cho SnoozeReceiver ngay sau kiểu trong hàm sendNotification().
  3. Tạo một ý định đang chờ xử lý bằng cách gọi phương thức getBroadcast() trên PendingIntent. Phương thức này dự kiến có các thông số trong các bước sau. PendingIntent này sẽ được hệ thống dùng để thiết lập một chuông báo mới để đăng thông báo mới sau 60 giây khi người dùng nhấn vào nút tạm hoãn.
  4. Thông số đầu tiên là ngữ cảnh ứng dụng mà trong đó PendingIntent này sẽ bắt đầu hoạt động.
  5. Thông số thứ hai là mã yêu cầu, là mã yêu cầu cho ý định đang chờ xử lý này. Nếu cần cập nhật hoặc hủy ý định đang chờ xử lý này, bạn cần sử dụng mã này để truy cập ý định đang chờ xử lý.
  6. Tiếp theo, hãy thêm đối tượng snoozeIntent (đây là ý định của hoạt động sẽ chạy).
  7. Cuối cùng, hãy thêm giá trị gắn cờ của #FLAG_ONE_SHOT vì ý định này sẽ chỉ được dùng một lần. Thao tác nhanh và thông báo sẽ biến mất sau lần nhấn đầu tiên. Đó là lý do bạn chỉ có thể sử dụng ý định một lần.
// NotificationUtils.kt

// TODO: Step 2.2 add snooze action
val snoozeIntent = Intent(applicationContext, SnoozeReceiver::class.java)
val snoozePendingIntent: PendingIntent = PendingIntent.getBroadcast(
    applicationContext, 
    REQUEST_CODE, 
    snoozeIntent, 
    FLAGS
)
  1. Tiếp theo, hãy gọi hàm addAction() trên notificationBuilder. Chức năng này yêu cầu một biểu tượng và văn bản mô tả hành động của bạn cho người dùng. Bạn cũng cần thêm snoozeIntent. Ý định này sẽ được dùng để kích hoạt boadcastReceiver phù hợp khi người dùng nhấp vào hành động của bạn.
// NotificationUtils.kt
// TODO: Step 2.3 add snooze action
.addAction(
    R.drawable.egg_icon, 
    applicationContext.getString(R.string.snooze),
    snoozePendingIntent
)
  1. Chạy ứng dụng hẹn giờ trứng để kiểm tra hành động tạm ẩn.
  2. Chạy bộ hẹn giờ và đặt ứng dụng trong nền. Khi đồng hồ hẹn giờ kết thúc, hãy mở rộng thông báo và bạn sẽ thấy thông báo hiện có nút hành động tạm hoãn để tạm hoãn đồng hồ hẹn giờ trứng thêm một phút nữa.

Bước 3: Tầm quan trọng của thông báo

Tầm quan trọng của việc xác định mức độ thông báo làm gián đoạn người dùng một cách trực quan và rõ ràng. Thông báo có mức độ quan trọng cao hơn sẽ gây gián đoạn cho người dùng hơn.

Bạn phải chỉ định mức độ quan trọng trong hàm dựng NotificationChannel. Ban đầu, bạn đặt mức độ quan trọng thấp cho ứng dụng hẹn giờ trứng. Bạn có thể sử dụng một trong năm mức độ quan trọng, từ IMPORTANCE_NONE(0) đến IMPORTANCE_HIGH(4). Mức độ quan trọng mà bạn chỉ định cho một kênh sẽ áp dụng cho tất cả thông báo thông báo mà bạn đăng lên kênh đó.

Mức độ quan trọng của kênh

Mức độ quan trọng mà người dùng có thể nhận thấy

Tầm quan trọng (Android 8.0 trở lên)

Mức độ ưu tiên (Android 7.1 trở xuống)

Phát ra âm thanh và xuất hiện dưới dạng thông báo quan trọng (hiển thị ở đầu màn hình)

IMPORTIMPORT_HIGH

PRIORITY_HIGH/PRIORITY_MAX

Phát ra âm thanh

IMPORTIMPORT_DEFAULT

PRIORITY_DEFAULT

Không có âm thanh

IMPORTIMPORT_LOW

PRIORITY_LOW

Không có âm thanh và không xuất hiện trong thanh trạng thái

IMPORTIMPORT_MIN

PRIORITY_MIN

Để biết thông tin về cách chọn mức độ ưu tiên thích hợp, hãy xem "Mức độ ưu tiên" trong Hướng dẫn thiết kế thông báo. Bạn nên cẩn thận khi chọn mức độ quan trọng cho các thông báo trong ứng dụng của mình. Bạn nên cân nhắc mức độ quan trọng và thời gian và sự chú ý của người dùng đối với kênh. Khi một thông báo không quan trọng được ngụy trang dưới dạng khẩn cấp, thông báo này có thể tạo ra chuông báo không cần thiết và gây mất tập trung. Người dùng có toàn quyền kiểm soát mức độ quan trọng của thông báo. Vì vậy, nếu bạn tạo một thông báo gây khó chịu, họ có thể tắt hoàn toàn kênh thông báo của bạn.

Khi bạn tạo thông báo lần đầu ở Bước 1.6, bộ tính giờ trứng được đặt để gửi thông báo với mức độ ưu tiên thấp vì nó được thiết kế để không làm phiền người dùng bằng thông báo. Tuy nhiên, bạn nên thu hút sự chú ý của người dùng trước khi quá nhiều trứng. Để thay đổi mức độ quan trọng của thông báo, hãy bắt đầu với chế độ cài đặt kênh. Mức độ quan trọng của kênh ảnh hưởng đến mức độ gián đoạn của tất cả các thông báo đăng trên kênh, đồng thời phải được chỉ định trong hàm dựng NotificationChannel.

  1. Để thay đổi mức độ quan trọng của kênh thông báo trong ứng dụng của bạn, hãy mở EggTimerFragment.kt và chuyển đến createChannel(). Thay đổi mức độ quan trọng từ IMPORTANCE_LOW thành IMPORTANCE_HIGH.
// EggTimerFragment.kt
    val notificationChannel = NotificationChannel(
        channelId,
        channelName,
        // TODO: Step 2.4 change importance
        NotificationManager.IMPORTANCE_HIGH
    )

Để hỗ trợ các thiết bị chạy Android 7.1 (API cấp 25) trở xuống, bạn cũng phải gọi setPriority() cho mỗi thông báo, sử dụng một hằng số ưu tiên từ lớp NotificationCompat.

  1. Mở NotificationUtils.kt và thêm phần sau vào đối tượng trình tạo thông báo.
// NotificationUtils.kt
   .addAction(
       R.drawable.common_google_signin_btn_icon_dark,
       applicationContext.getString(R.string.snooze),
       snoozePendingIntent
    )
   // TODO: Step 2.5 set priority
    .setPriority(NotificationCompat.PRIORITY_HIGH)
  1. Trước khi chạy ứng dụng, hãy nhấp và giữ biểu tượng ứng dụng trên thiết bị hoặc trình mô phỏng rồi chọn gỡ cài đặt để xóa các tùy chọn cài đặt trước đây của kênh. Nếu bạn không gỡ cài đặt ứng dụng, chế độ cài đặt mức độ ưu tiên của kênh sẽ không thay đổi và hành vi này sẽ không thay đổi khi hành vi đăng thông báo.
  2. Bây giờ, hãy chạy lại ứng dụng và bắt đầu hẹn giờ. Lần này, khi thông báo được gửi, bạn sẽ thấy một cửa sổ bật lên xuất hiện ở đầu màn hình, bất kể ứng dụng của bạn đang chạy ở nền trước hay nền.

Bước 4: Huy hiệu thông báo

Huy hiệu thông báo là các dấu chấm nhỏ xuất hiện trên biểu tượng trình chạy của ứng dụng được liên kết khi ứng dụng có thông báo đang hoạt động. Người dùng có thể nhấn và giữ biểu tượng ứng dụng để hiển thị thông báo.

Những dấu chấm này (được gọi là huy hiệu) sẽ xuất hiện theo mặc định và ứng dụng của bạn không cần phải làm gì. Tuy nhiên, có thể có những trường hợp mà các thông báo không phù hợp với bạn. Do đó, bạn có thể tắt tính năng này trên từng kênh bằng cách gọi setShowBadge(false) trên đối tượng NotificationChannel. Vì bộ hẹn giờ trứng chỉ có một thông báo đang hoạt động tại một thời điểm nhất định nên huy hiệu trên biểu tượng ứng dụng của bạn không mang lại nhiều lợi ích cho người dùng. Trong các bước sau, bạn sẽ tắt huy hiệu và chỉ hiển thị thông báo cho đồng hồ hẹn giờ trứng.

  1. Thêm setShowBadge(false) vào mã tạo kênh của bộ tính giờ trứng để tắt huy hiệu.
// EggTimerFragment.kt

    ).apply {
        // TODO: Step 2.6 disable badges for this channel
        setShowBadge(false)
    }
  1. Chạy lại ứng dụng, khởi động đồng hồ hẹn giờ và xem biểu tượng ứng dụng. Bạn sẽ không thấy huy hiệu nào trên biểu tượng ứng dụng.

Mã giải pháp nằm trong nhánh chính của mã được tải xuống.

Khóa học từ Udacity:

Tài liệu dành cho nhà phát triển Android:

Để xem đường liên kết đến các lớp học lập trình khác trong khóa học này, hãy xem trang đích Nâng cao cho Android trong Kotlin.