使用 Android 通知

本程式碼研究室是 Kotlin 進階課程的一部分。只要您按部就班完成程式碼研究室,就能發揮本課程的最大效益。不過,您不一定要這麼做。所有課程程式碼研究室清單均列於進階 Android 版的 Kotlin 程式碼研究室到達網頁中。

引言

通知是指使用者在您應用程式 UI 之外看到的訊息。裝置處於解鎖狀態時,畫面頂端會顯示通知;視裝置的安全性設定而定,當裝置處於鎖定狀態時,系統仍會在螢幕鎖定畫面上顯示通知。

一般通知包括標題、說明和圖示。通知也可以含有可點擊的動作、快速回覆、可延伸內容和圖片。

通知具有時效性,還能提供按鈕讓使用者執行快速操作,例如傳送回覆或延後鬧鐘。只要按一下通知,使用者就會在應用程式中瀏覽與通知內容相關的檢視畫面。

通知可以有效提醒使用者執行重要工作、通知使用者發生了什麼事,或在應用程式於背景執行時立即傳達重要資訊。謹慎使用通知。這種做法不僅可尊重使用者,也更有可能讓您的應用程式通知獲得應有的關注。

在這個程式碼研究室中,您將瞭解如何在 Android 應用程式中建立及使用通知。

須知事項

您應該很熟悉:

  • 如何在 Android Kotlin 中集結 Android 應用程式。(尤其是 Android SDK) 的運作方式。
  • 如何使用架構元件和資料繫結建構應用程式。
  • 瞭解 BroadcastReceivers 的基本知識
  • 瞭解 AlarmManager 的基本知識

課程內容

  • 如何建立、設定樣式及傳送通知。
  • 如何取消通知。
  • 如何建立通知管道。
  • 如何在通知中新增快速動作。
  • 如何在應用程式圖示上顯示通知徽章。

執行步驟

  • 在入門應用程式中新增通知。
  • 取消您先前傳送的通知。
  • 為不同類型的通知建立頻道。
  • 自訂新手應用程式中的通知。
  • 新增快速操作,讓通知更具互動性。
  • 關閉通知標記。

料理雞蛋很簡單,但如果無法追蹤時間,就很難做它。在本程式碼研究室中,您將會著手製作蛋計時器計時器應用程式,並和之後的卵子一樣,讓它更完美。您會先使用可運作的雞蛋計時器應用程式,讓使用者為不同的雞蛋樣式設定不同的烹調時間設定。計時器會在指定的時間間隔內倒數,並在雞蛋就緒時顯示您的訊息。

這看起來似乎很可行,但也太完美了,但並非易用。首先,顯示短訊息的訊息只會顯示一小段時間,因此很容易遺漏。此外,如果應用程式未於前景運作,或者裝置處於鎖定狀態,則在訊息傳遞狀態消失後,計時器狀態就不會顯示視覺指標。

在理想情況下,時間計時器應透過通知告知使用者時間到了。使用者需要知道,雞蛋已經準備好了通知內容可清楚呈現,而且可能含聲音,而且會讓裝置震動,而且可透過各種方式抓住使用者的注意力!如此一來,您便能實現完美的雞蛋,以及充滿熱忱的好朋友。

如要取得範例應用程式,您可以採取下列其中一種做法:

從 GitHub 複製存放區並切換至 starter 分支版本。

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


或者,您也可以將存放區下載為 ZIP 檔案,然後解壓縮,然後在 Android Studio 中開啟該檔案。

下載 Zip

  1. 在 Android Studio 中開啟並執行應用程式。

您會看到雞蛋和下拉式選單,當中列出烹調雞蛋的預先定義時間間隔清單。按一下 [Soft Boiled] 下拉式選單。清單中的第一個選項是測試用途,而且只會將鬧鐘設為 10 秒。清單旁邊的開關會啟動雞蛋計時器您可以隨時透過這個開關啟動及停止雞蛋計時器。啟動代碼功能可完全正常運作,這表示你可以設定雞蛋計時器,然後把計時器倒數計時到 0。計時結束後,螢幕上會顯示烤訊息,如下所示。

  1. 檢查原始碼。入門應用程式是由名為 MainActivity 的單一活動組成。有三個子套件,分別命名為 receiveruiutil

  • /receiver - receiver 套件包含兩個名為 AlarmReceiverSnoozeReceiver 的廣播接收器。「AlarmManager」會觸發 AlarmReceiver,在使用者定義計時器啟動時傳送通知。「SnoozeReceiver」會處理使用者點擊以延後通知。
  • /ui:這個類別包含 EggTimerFragment 的使用者介面 UI 部分。EggTimerViewModel 負責啟動及取消計時器,以及其他與生命週期相關的應用程式工作。
  • /util:這個套件中包含兩個檔案。BindingUtils.kt 具有繫結轉接程式,可在應用程式 UI 與 ViewModel 之間建立資料繫結。NotificationUtils.kt 具有 NotificationManager 的擴充功能方法。

使用通知是吸引使用者註意您應用程式的絕佳方式。不論您的應用程式是否在前景執行或正在執行,通知都會顯示在畫面頂端,同時可能出現音效或震動。如要建立通知,您必須使用通知建立工具,並提供標題文字、內容文字和圖示。建構工具取得所有必要欄位後,「NotificationManager」是系統服務,可協助您將這項內容顯示為通知。NotificationManager必須負責傳送通知、更新其內容及取消通知。您將在下列步驟中新增擴充功能方法至 NotificationManager。這樣一來,每次使用 NotificationManager 時,您都可以使用下列擴充功能功能來使用您所需的功能。

步驟 1:建立基本通知

在這項工作中,您會建立新通知、為使用者設定訊息,以及傳送通知

  1. 開啟 NotificationUtils.kt 類別並找到 TODO: Step 1.1。您可以在這個程式碼研究室和應用程式的程式碼中找到相符的待辦事項。
  2. 檢查指定的 sendNotification() 函式。我們會將這項擴充功能功能延伸至 NotificationManager,以傳送通知。
//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. 取得通知建構工具的執行個體,然後傳入應用程式的內容和頻道 ID。頻道 ID 是管道的字串值。

通知管道可用來將通知分組。只要將類似的通知分門別類,開發人員和使用者就能控管頻道中的所有通知。建立頻道後,您就能透過這個管道傳送任意數量的通知。

//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. 設定通知圖示來代表應用程式、標題,以及要向使用者顯示的訊息內容文字。您會在程式碼研究室中看到更多自訂通知選項,但這是設定通知時所需的資料量下限。
//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. 接著,您需要呼叫專屬 ID 來呼叫 notify(),並使用通知中的 Notification 物件。

這個 ID 代表目前的通知例項,用於更新或取消這項通知。您的應用程式在特定時間只會有一則有效通知,因此您可以為每個通知使用相同的 ID。您已在 NotificationUtils.kt中為這個用途設定了一個固定值 (名稱:NOTIFICATION_ID)。請注意,由於您是對同一類別的擴充功能函式執行呼叫,因此可以直接呼叫 notify()

//NotificationUtils.kt
   // TODO: Step 1.4 call notify to send the notification
    // Deliver the notification
    notify(NOTIFICATION_ID, builder.build())
  1. 開啟 ui/EggTimerViewModel.kt 並找到 startTimer() 函式。這個函式會在使用者啟動雞蛋計時器的情況下,建立具有所選時間間隔的鬧鐘。
  2. 當使用者啟動計時器時,在這個函式中觸發通知。為了呼叫您先前實作的 sendNotification() 函式,您需要 NotificationManager 的執行個體。NotificationManager 是一項系統服務,提供針對通知 API 公開的所有函式,包括新增的擴充功能函式。每當您要傳送、取消或更新通知時,都必須向系統要求 NotificationManager 的執行個體。呼叫 sendNotification()| 函式搭配通知訊息,並提供背景資訊。
// 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)

即將大功告成!不過,如果你現在執行應用程式並設定計時器,將不會收到通知。

  1. 開啟「logcat」並搜尋「"No Channel found"」。系統應該會顯示 egg_channel 不存在的錯誤訊息。接下來,我們將進一步說明通知管道並修正問題。

步驟 2:通知管道

從 API 級別 26 開始,所有通知都必須指派至某個頻道。按住應用程式啟動器圖示並選取應用程式資訊,接著輕觸通知,即可看到與應用程式相關的通知管道清單。由於您的應用程式尚未建立任何頻道,因此清單已空白。

「頻道」代表「通知類型」;舉例來說,雞蛋計時器會在雞蛋烹調時傳送通知,同時還會透過其他頻道發送通知,提醒您吃早餐。單一頻道中的所有通知都會歸為同一群組,使用者也可以調整整個頻道的通知設定。這樣一來,使用者就能根據他們感興趣的通知類型,自訂合適的通知設定。舉例來說,使用者可以停用早餐通知,但仍選擇透過計時器查看通知。

開發人員要調整初始設定、重要性和行為,以套用到頻道中的所有通知。完成初始設定後,使用者可以覆寫這些設定。

在步驟 1.1 中,您會使用 egg_notification_channel_id 做為通知管道,因此您需要實際建立及自訂這個頻道的通知設定和行為。

  1. 開啟 EggTimerFragment.kt 並找到 createChannel() 函式。
  2. 將專屬管道 ID 傳送至 NotificationChannel 的建構函式。
  3. 傳送通知管道名稱;使用者也會在「設定」畫面中看到通知名稱。
  4. 最後再傳送通知管道的重要性等級。本程式碼研究室將說明其他等級,因此您可以使用 NotificationManager.IMPORTANCE_LOW
  5. notificationChannel 物件上,將 enableLights 設為 true。這項設定會在顯示通知時亮起。
  6. notificationChannel 物件上,將 lightColor 設為紅色,以便在顯示通知時顯示紅色指示燈。
  7. notificationChannel 物件上,將 enableVibration 設為 true,即可啟用震動功能。
  8. notificationChannel 物件上,將頻道說明設為 ‘Time for breakfast'。
  9. 呼叫 getSystemService() 以取得 NotificationManager 的執行個體。
  10. 呼叫 NotificationManager 上的 createNotificationChannel(),並傳送您在上一步建立的 notificationChannel 物件。
//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. 接下來,如要建立頻道,您必須呼叫剛才撰寫的 createChannel() 函式 (步驟 1.7)。這個函式會使用兩個參數:頻道編號和頻道名稱。您必須從專案中提供的字串資源查詢頻道 ID 和頻道名稱。
// EggTimerFragment.kt
    // TODO: Step 1.7 call createChannel
    createChannel(
          getString(R.string.egg_notification_channel_id),
          getString(R.string.egg_notification_channel_name)
    )
  1. 您必須將頻道 ID 傳送至通知製作工具。您已執行步驟 1.2。設定錯誤值,因為頻道 ID 會導致通知失敗。開啟 NotificationUtils.kt 以驗證您先前設定的頻道 ID 是否正確。
// 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. 執行應用程式後,應用程式就會在每次啟動計時器時傳送通知給您。
  2. 提取狀態列,然後查看通知標題、內容和圖示是否與您在上一個步驟的設定相同。
  3. 如要驗證新建立的頻道,請關閉應用程式並找出應用程式圖示。長按應用程式圖示並選取 [應用程式資訊]

  1. 從設定清單中選取 [通知]。您應該會在 [顯示通知] 設定下方看到名為 Egg 的新頻道。

現在當您執行應用程式時,系統就會顯示通知。您和應用程式開發人員都可以調整這個頻道上所有通知的設定和行為。恭喜,你已建立通知!

步驟 3:新增應用程式通知

到目前為止,顯示通知 API 的基本用途,但在啟動計時器後立即傳送通知並不合理。雞蛋準備就緒後,使用者可能希望收到通知。在程式碼研究室的下列部分,您將要修正這個問題,並將浮動訊息改為通知。

您已發送通知,並留意通知向使用者呈現的方式,但這只是製作優質通知的第一步。在這個步驟中,您將更改通知在更合適的時間傳送。

您的應用程式會使用「AlarmManager」設定鬧鐘。與 AlarmManager 相關的程式碼已可在範例程式碼中顯示,而且用於顯示浮動訊息。AlarmManager 會追蹤您想要的時間,並在時間結束時觸發 AlarmReceiver.ktonReceive() 函式。開啟 AlarmReceiver.kt 並前往 onReceive() 後,你應該會看到設定訊息計時器時看到的吐司訊息。

  1. 開啟 AlarmReceiver.kt,也就是 NotificationManager 的執行個體,然後以訊息文字和內容參數呼叫 sendNotification() 函式。
// 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. 視需要移除不要,因為應用程式會在計時器響起時傳送通知。
// AlarmReceiver.kt
     // TODO: Step 1.10 [Optional] remove toast
//   Toast.makeText(
//       context, 
//       context.getText(R.string.eggs_ready),
//       Toast.LENGTH_SHORT
//   ).show()
  1. 執行您的應用程式 。每次啟動計時器和計時器響起時,你應該都會收到通知。

這並不是理想的結果。您不會傳送太多通知給使用者。您可以移除在使用者啟動計時器時傳送的第一則通知。

  1. 開啟 EggTimerFragment.kt 並移除步驟 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. 再次執行應用程式。
  2. 設定計時器並將計時器設為背景,然後等待時間結束。系統隨即會顯示通知。這則通知提供更實用的通知。

步驟 4:新增內容意圖

  1. 如果應用程式尚未執行,請再次執行此應用程式。
  2. 按一下通知。沒有回應!

顯示通知並告知使用者非常棒,但是只要使用者點選通知,就會想要回到對應的應用程式。在這個程式碼研究室的這個部分中,您會在通知中加入意圖,讓使用者回到計時器畫面。

Intent 是一種訊息物件,可用來要求其他應用程式元件執行的動作。意圖可用來啟動活動、服務或傳送廣播。在這種情況下,您可以利用這個意圖告知系統,在使用者輕觸通知時開啟 MainActivity。您的應用程式只有一項資料檢視,所以這裡沒有太多選項。然而,在大型應用程式中,通知應引導使用者前往與通知互動時的畫面,以打造流暢的使用體驗。

  1. 開啟 NotificationUtils.kt 並找出 sendNotification() 擴充功能函式。
  2. 使用你的applicationContext和即將開始的活動,建立 IntentMainActivity::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)

您建立了意圖,但通知顯示在應用程式外。如要在應用程式外使用意圖,您必須建立新的 PendingIntent

PendingIntent 會授權讓其他應用程式或系統代表您執行相關作業。PendingIntent 本身只是系統參照的符記,用來描述用來擷取該權杖的原始資料。也就是說,即使此應用程式的擁有者程序已經終止,PendingIntent 本身仍可透過其提供的其他程序使用。在這種情況下,無論計時器應用程式是否正在執行,系統都會使用待處理意圖代您開啟應用程式。

  1. 使用 applicationContextNOTIFICATION_ID、您在上一步建立的 contentIntent 以及 PendingIntent 標記建立 PendingIntentPendingIntent 旗標會指定建立新 PendingIntent 或使用現有選項的選項。您必須將 PendingIntent.FLAG_UPDATE_CURRENT 設為旗標,這樣就不會在已有新通知的情況下建立新的通知。這樣一來,您就可以修改與您提供的意圖相關聯的目前 PendingIntent
// NotificationUtils.kt
   // TODO: Step 1.12 create PendingIntent
    val contentPendingIntent = PendingIntent.getActivity(
        applicationContext, 
        NOTIFICATION_ID,
        contentIntent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )
  1. PendingIntent 傳送到您的通知。方法是呼叫 NotificationBuilder 上的 setContentIntent()。現在,當您按一下通知,就會觸發 PendingIntent,開啟您的 MainActivity
  2. 此外,請將 setAutoCancel() 設為 true,這樣一來,當使用者輕觸通知時,通知便會自行關閉,屆時應用程式便會將通知關閉。
// NotificationUtils.kt
    // TODO: Step 1.13 set content intent
    .setContentIntent(contentPendingIntent)
    .setAutoCancel(true)
  1. 再次執行應用程式。
  2. 設定計時器,將應用程式設為在背景執行,然後等待系統顯示通知。
  3. 看到通知後,只要向下拉狀態列並點選通知列,即可瞭解通知是如何讓應用程式在前景執行。

步驟 5:取消通知

你的計時器計時器功能正常,但有通知,但發生小問題。如果您設定了計時器、接收通知並再次設定計時器,那麼新通知執行期間,先前的通知會一直在狀態列上。如果應用程式會在背景執行,可能會讓使用者感到困惑,進而造成你的蛋煮蛋。

如要修正這個問題,你必須在啟動新的計時器時清除先前的通知。首先,請在您的 NotificationUtils.kt 中建立其他擴充功能函式。NotificationManager 的 API 可用來取消所有名為 cancelAll() 的有效通知。

  1. 開啟 NotificationsUtil.kt
  2. NotificationManager 上新增會呼叫 cancelAll() 的擴充功能函式。
// NotificationUtils.kt

// TODO: Step 1.14 Cancel all notifications
/**
 * Cancels all notifications.
 *
 */
fun NotificationManager.cancelNotifications() {
    cancelAll()
}
  1. 開啟 EggTimerViewModel.kt 並前往 startTimer() 函式。
  2. startTimer() 內,從系統內取得 NotificationManager 的執行個體,並呼叫 cancelNotifications()
//  EggTimerViewModel.kt
   //TODO Step 1.15 call cancel notification
    val notificationManager =
       ContextCompat.getSystemService(
            app,
            NotificationManager::class.java
        ) as NotificationManager
    notificationManager.cancelNotifications()       
  1. 執行應用程式,然後啟動計時器。
  2. 看到通知後,請重新啟動計時器,並觀察我們的應用程式如何從狀態列自動刪除先前的通知。

通知架構提供了多種自訂選項,可讓開發人員設定自訂動作,並視需要設定通知樣式。在這項工作中,您將瞭解如何自訂雞蛋計時器通知。

步驟 1:設定通知樣式

您可以根據自身需求設定通知和通知內容,讓通知脫穎而出,看起來就像應用程式的擴充功能。通知架構提供數種內建樣式,可協助您隨時建立各種樣式。

NotificationCompat 針對以下項目提供內建樣式:

  • BigTextStyle:可顯示大量文字,例如在展開後顯示電子郵件內容。
  • BigPictureStyle:顯示含有大型圖片附件的大型格式通知。
  • InboxStyle:顯示對話樣式的文字內容。
  • MediaStyle:顯示媒體播放的控制項。
  • MessagingStyle:這個格式會顯示大型通知,其中包含數量不限的多則訊息。

如要進一步瞭解其他樣式,請參閱建立可展開式通知說明文件。在這個步驟中,您會使用 NotificationCompat.BigPictureStyle 建立可展開式通知,在展開時顯示大型的蛋圖片。

  1. 開啟 NotificationUtils.kt 並找到 sendNotification() 函式。
  2. 首先,請使用 BitmapFactoryresources 載入圖片。
// NotificationUtils.kt

// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
     applicationContext.resources, 
     R.drawable.cooked_egg
)
  1. 建立新的 BigPictureStyle 並設定圖片。
  2. bigLargeIcon() 設為 null,這樣展開通知時,大型圖示就會消失。
// 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. setStyle() 的樣式設為 bigPicStyle
  2. setLargeIcon() 大型圖示設為 eggImage,這樣當收合通知時,圖片就會縮小為較小的圖示。
// NotificationUtils.kt
// TODO: Step 2.1 add style to builder
.setStyle(bigPicStyle)
.setLargeIcon(eggImage)
  1. 執行應用程式並設定計時器。第一次顯示通知時,通知導覽匣中的已收合狀態會顯示通知,展開通知後,延伸通知區域就會顯示大型圖片。

步驟 2:通知動作

通知動作是另一項可以為通知新增的自訂項目。您的通知目前會在使用者點擊時,重新導向至您的應用程式。除了上述預設通知動作外,您還可以新增動作按鈕,透過通知完成應用程式相關工作。

通知最多可提供三個動作按鈕,讓使用者可以快速回應,例如延後提醒或回覆簡訊。這些動作按鈕不應重複輕觸使用者輕觸通知時所執行的動作。

如要新增動作按鈕,請將 PendingIntent 傳遞至建構工具上的 addAction() 函式。這類似於呼叫 setContentIntent() 來設定通知的預設輕觸動作,但是您可以執行各種其他動作,例如啟動可在背景執行工作的 BroadcastReceiver ,因此動作並不會中斷已經開啟的應用程式。

在本程式碼研究室中,您已經獲得名為 BoadcastReceiverSnoozeReceiver。您會使用 SnoozeReceiver 來接收使用者的「通知」動作。以下步驟將加入程式碼,讓使用者點擊計時器動作按鈕後延後 60 秒。點選暫停動作後,SnoozeReceiver 會收到意圖,並會在 60 秒後建立新的鬧鐘傳送新的通知。

  1. 開啟 SnoozeReceiver.kt。這個類別與您先前使用過的 AlarmReceiver 類似。在下列步驟中,您將加入會觸發 SnoozeReceiveronReceive() 函式的程式碼。簡單來說,SnoozeReceiver中的驗證碼會建立一個新鬧鐘,並於 1 分鐘後傳送通知。向下捲動到 onReceive 函式的底部,從系統取得通知 Manager 的執行個體,並呼叫 unAll。
// SnoozeReceiver.kt
        val notificationManager = ContextCompat.getSystemService(
            context,
            NotificationManager::class.java
        ) as NotificationManager
        notificationManager.cancelAll()
  1. 如要使用 SnoozeReceiver,請開啟 NotificationUtils.kt
  2. sendNotification() 函式中的樣式後方直接為 SnoozeReceiver 建立新的 Intent snoozeIntent
  3. PendingIntent 上呼叫 getBroadcast() 方法,建立預期意圖,藉此在各個步驟中取得參數。當使用者輕觸貪睡按鈕後,系統會透過這個 PendingIntent 設定新鬧鐘,並在 60 秒後發布新的通知。
  4. 第一個參數是此 PendingIntent 應啟動活動的應用程式環境。
  5. 第二個參數是要求代碼,也就是這個待處理意圖的要求代碼。如果您需要更新或取消這項待處理的意圖,就必須使用這組代碼來存取待處理意圖。
  6. 接著,新增 snoozeIntent 物件,也就是要啟動活動的意圖。
  7. 最後,請新增 #FLAG_ONE_SHOT 旗標值,因為只會使用一次意圖。第一次輕觸後,快速操作和通知就會消失,因此只能使用一次意圖。
// 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. 接著,呼叫 notificationBuilder 上的 addAction() 函式。此函式預期必須以圖示和文字來描述您執行的動作。您也必須新增 snoozeIntent。系統會在使用者執行動作時,透過這個意圖觸發適當的boadcastReceiver
// NotificationUtils.kt
// TODO: Step 2.3 add snooze action
.addAction(
    R.drawable.egg_icon, 
    applicationContext.getString(R.string.snooze),
    snoozePendingIntent
)
  1. 執行蛋計時器計時器應用程式來測試延後動作。
  2. 執行計時器並將應用程式置於背景,計時器倒數完畢後,請展開通知,你會看到通知中的暫停動作按鈕,讓雞蛋計時器延後一分鐘。

步驟 3:通知的重要性

重要性決定了通知在視覺和聽覺上會中斷的程度。對使用者而言,重要性較高的通知會更加分散。

您必須在 NotificationChannel 建構函式中指定重要性等級。您一開始為雞蛋計時器應用程式設定了低度重要性。您可以使用以下五種重要性等級:IMPORTANCE_NONE(0)IMPORTANCE_HIGH(4)。您為頻道指定的重要性等級會套用至您發布的所有通知訊息。

頻道重要性層級

使用者可見重要性等級

重要性 (Android 8.0 以上版本)

優先順序 (Android 7.1 以下版本)

發出音效並顯示為抬頭通知 (顯示在畫面頂端)

IMPORTANCE_HIGH

PRIORITY_HIGH / PRIORITY_MAX

發出音效

IMPORTANCE_DEFAULT

PRIORITY_DEFAULT

不發出音效

IMPORTANCE_LOW

PRIORITY_LOW

沒有聲音,也不會出現在狀態列中

IMPORTANCE_MIN

PRIORITY_MIN

想瞭解如何選擇適當的優先順序等級,請參閱通知設計指南中的「優先等級」一節。為應用程式選擇通知的重要性等級時,請務必謹慎小心。您必須根據使用者的時間和注意力來選擇頻道重要性。如果有不嚴重的通知,就認定為緊急事件,可能會導致不必要的鬧鐘響起,讓分心。使用者可以全面掌控通知的重要性等級,因此,如果您建立了令人困擾的通知,使用者就能完全關閉您的通知管道。

首次在步驟 1.6 建立通知時,雞蛋計時器設定為傳送低優先順序的通知,因為其設計不會幹擾使用者傳送通知。然而,在雞蛋烹調之前,最好能吸引使用者註意。如要變更通知的重要性等級,請先調整頻道設定。頻道重要性會影響頻道發布的所有通知,且必須在 NotificationChannel 建構函式中指定。

  1. 如要變更應用程式通知管道的重要性等級,請開啟 EggTimerFragment.kt 並前往 createChannel()。將重要性等級從「IMPORTANCE_LOW」變更為「IMPORTANCE_HIGH」。
// EggTimerFragment.kt
    val notificationChannel = NotificationChannel(
        channelId,
        channelName,
        // TODO: Step 2.4 change importance
        NotificationManager.IMPORTANCE_HIGH
    )

如要支援搭載 Android 7.1 (API 級別 25) 以下版本的裝置,您必須使用 NotificationCompat 類別的優先順序值,為每項通知呼叫 setPriority()

  1. 開啟 NotificationUtils.kt 並將以下內容新增至通知建構工具物件。
// 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. 執行應用程式前,請先長按裝置或模擬器上的應用程式圖示,然後選取 [解除安裝],即可清除先前的頻道設定。如果你無法解除安裝應用程式,頻道優先順序設定將維持不變,因此通知發布後並不會改變行為。
  2. 現在再次執行應用程式並啟動計時器。無論應用程式是在前景或背景執行,這次通知都會在應用程式頂端顯示。

步驟 4:通知標記

通知標記是一個小點,它會在應用程式有有效通知時,顯示在相關應用程式的啟動器圖示上。使用者只要長按應用程式圖示,即可查看通知。

這些點名稱為「徽章」,預設顯示的應用程式不會出現任何資訊。不過,在某些情況下,徽章可能不適合用於通知,因此您可以在每個管道中呼叫 setShowBadge(false),以停用這些管道。雞蛋計時器在特定時間只會有一則有效通知,因此應用程式圖示上的徽章對您的使用者並沒有太大的助益。以下步驟將停用徽章,且只會顯示雞蛋計時器的通知。

  1. setShowBadge(false) 新增至雞蛋計時器的頻道代碼,以停用徽章功能。
// EggTimerFragment.kt

    ).apply {
        // TODO: Step 2.6 disable badges for this channel
        setShowBadge(false)
    }
  1. 再次執行應用程式並啟動計時器,然後查看應用程式圖示。應用程式圖示上可能不會顯示任何徽章。

此解決方案程式碼位於您下載的程式碼的主要分支版本中。

Udacity 課程:

Android 開發人員說明文件:

如要瞭解本課程中其他程式碼研究室的連結,請參閱 Kotlin 的進階 Android 程式碼研究室到達網頁。