Android の通知を使用する

この Codelab は、Kotlin での高度な Android 開発コースの一部です。Codelab を順番に進めていくと、このコースを最大限に活用できますが、これは必須ではありません。すべてのコース Codelab は Kotlin での Codelab の高度な Codelab のランディング ページに掲載されています。

はじめに

通知は、アプリの UI の外でユーザーに表示されるメッセージです。通知は、デバイスのロックが解除されている場合は画面の上部に表示されます。セキュリティ設定によっては、デバイスがロックされているときにロック画面に表示されます。

一般的な通知は、タイトル、説明、アイコンで構成されます。通知には、クリック可能なアクション、クイック返信、拡張可能なコンテンツ、画像などを含めることもできます。

通知にはタイムリーな素材や、返信の送信やアラームのスヌーズなどのクイック操作を行うためのボタンがあります。ユーザーが通知をクリックすると、通知の内容に関連するアプリ内のビューが表示されます。

通知は、重要なタスクについてユーザーに通知したり、何かが起きたことをユーザーに知らせたり、アプリがバックグラウンドで動作しているときに必要な情報をすばやく伝えたりするのに役立つ手段です。通知は慎重に使用してください。ユーザーへの配慮はもちろんのこと、アプリの通知に値する可能性も高くなります。

この Codelab では、Android アプリで通知を作成して使用する方法について学びます。

前提となる知識

以下について把握しておく必要があります。

  • Kotlin で Android アプリを作成する特に、Android SDK を使用してください。
  • アーキテクチャ コンポーネントとデータ バインディングを使用してアプリを設計する方法。
  • BroadcastReceiver に関する基礎知識
  • AlarmManager に関する基本的な知識

学習内容

  • 通知を作成、スタイル設定、送信する方法
  • 通知をキャンセルする方法
  • 通知チャンネルを作成する方法。
  • 通知にクイック操作を追加する方法
  • アプリアイコンに通知バッジを表示する方法。

演習内容

  • スターター アプリに通知を追加します。
  • 以前に送信した通知をキャンセルします。
  • さまざまな種類の通知用のチャンネルを作成します。
  • スターター アプリで通知をカスタマイズします。
  • クイック操作を追加して通知をインタラクティブにします。
  • 通知バッジをオフにします。

卵を作るのは簡単ではありませんが、時間を忘れなければ難しくなります。この Codelab では、卵卵タイマーアプリを作成し、将来の卵のように完璧に仕上げます。まず、さまざまな卵料理のスタイルで調理時間を設定できる、稼働中の卵タイマーアプリを作成します。タイマーは選択した間隔からカウントダウンし、卵の準備が整うとトースト メッセージを表示します。

機能しているように見えますが、完璧ではなく、ユーザー フレンドリーでもありません。まず、トースト メッセージは短期間のみ表示されるため、見落とされがちです。また、アプリがフォアグラウンドにない場合、またはデバイスがロックされている場合、トースト メッセージが消えた後、タイマーの状態は視覚的に示されません。

理想的なのは、タイマーが時間に達したことを通知するために、タイマーで通知を使用することです。ユーザーは、卵の用意ができていないとすぐに気づく必要があります。さもなければ、卵は調理しすぎてしまいます。通知は視覚的なもので、音を鳴らして、デバイスを振動させるなど、ユーザーの注意を引き付けることができます。こうすることで、卵子が十分に食べられ、満足感のあるユーザーを得ることができます。

次のいずれかの方法でサンプルアプリを入手できます。

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 という 3 つのサブパッケージがあります。

  • /receiver - receiver パッケージには、AlarmReceiverSnoozeReceiver という名前の 2 つのブロードキャスト レシーバが含まれています。AlarmReceiverAlarmManager によってトリガーされ、ユーザー定義のタイマーが始動したときに通知を送信します。SnoozeReceiver は、ユーザーがクリックして通知をスヌーズできるようにするものです。
  • /ui - アプリの UI 部分である EggTimerFragment が含まれます。EggTimerViewModel は、タイマーの開始とキャンセル、および他のライフサイクル関連アプリのタスクを担当します。
  • /util - このパッケージには 2 つのファイルがあります。BindingUtils.kt には、アプリ UI と ViewModel 間のデータ バインディングを有効にするバインディング アダプターがあります。NotificationUtils.kt には、NotificationManager の拡張メソッドがあります。

通知を使用すると、ユーザーの注意を引くことができます。アプリが実行中かフォアグラウンドかにかかわらず、通知は画面の上部にポップアップ ウィンドウで表示され、通知音やバイブレーションを含めることができます。通知を作成するには、通知ビルダーを使用して、タイトル テキスト、コンテンツ テキスト、アイコンを指定する必要があります。ビルダーに必要なフィールドをすべて設定したら、システム サービスである NotificationManager を使用して、このコンテンツを通知として表示できます。NotificationManager は通知の送信、そのコンテンツの更新、通知のキャンセルを担当します。次の手順では、拡張メソッドを NotificationManager に追加します。このように、NotificationManager を使用する必要があるたびに、これらの拡張関数を使用して必要な機能を実現できます。

ステップ 1: 基本的な通知を作成する

このタスクでは、新しい通知の作成、ユーザーへのメッセージの設定、通知の送信を行います。

  1. NotificationUtils.kt クラスを開き、TODO: Step 1.1 を見つけます。この Codelab とアプリコードには、一致する ToDo リストがあります。
  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. 通知アイコンは、ユーザーに与えるメッセージ、アプリのタイトル、コンテンツ テキストを表すものにします。Codelab では、通知をカスタマイズするオプションがさらに表示されますが、これは通知を送信するために必要な最小データ量です。
//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 と、ビルダーからの Notification オブジェクトを使用して、notify() を呼び出す必要があります。

この ID は現在の通知インスタンスを表し、この通知を更新またはキャンセルするために必要です。アプリには一度に 1 つの有効な通知しかないため、すべての通知に同じ ID を使用できます。この目的のために、NotificationUtils.ktNOTIFICATION_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 は、追加した拡張関数を含め、Notifications 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. 最後のパラメータとして、通知チャンネルの重要度を渡します。重要度のレベルについては、この Codelab の後半で説明します。ここでは、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 とチャンネル名の 2 つのパラメータを取ります。すでにプロジェクト内で指定されている文字列リソースから、チャンネル 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. 設定のリストから [通知] を選択します。[通知の表示] のすぐ下に [] という新しいチャンネルが表示されます。

アプリを実行すると、通知が表示されます。アプリのデベロッパーとユーザーは、このチャンネルで送信されるすべての通知の設定と動作をカスタマイズできます。おめでとうございます!通知を作成しました。

ステップ 3: アプリに通知を追加する

これまでは Notifications API の基本的な使用例を示しましたが、タイマーを開始したらすぐに通知を送信するのはあまり意味がありません。ユーザーは、卵の準備が整ったときに通知を受け取りたいと思うでしょう。Codelab の次の部分では、これを修正して、トースト メッセージを通知に変更します。

通知は送信済みで、ユーザーに対する表示も確認しましたが、これは優れた通知を作成するための第一歩にすぎません。このステップでは、より適切なタイミングに送信されるように通知を変更します。

アプリは AlarmManager を使用してアラームを設定します。AlarmManager に関連するコードはスターター コードですでに指定されていて、トースト メッセージの表示に使用されています。AlarmManager は希望の時間選択を追跡し、時間終了時に AlarmReceiver.ktonReceive() 関数をトリガーします。AlarmReceiver.kt を開いて onReceive() に移動すると、エッグタイマーを設定するたびにトースト メッセージが表示されます。

  1. NotificationManager のインスタンスである AlarmReceiver.kt を開き、メッセージ テキストとコンテキスト パラメータを指定して 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. この通知をクリックすると、何も起こりません。

通知を表示してユーザーに情報を提供することは重要ですが、ユーザーが通知をクリックすると、対応するアプリに戻ることを期待します。Codelab のこの部分では、通知にインテントを追加して、ユーザーをタイマー画面に戻します。

Intent は、別のアプリ コンポーネントからアクションをリクエストするために使用できるメッセージング オブジェクトです。インテントを使用して、アクティビティ、サービスを開始したり、ブロードキャストを配信したりできます。この場合は、このインテントを使用して、ユーザーが通知をタップしたときに MainActivity を開くようシステムに指示します。アプリは 1 つのビューのみで構成されているため、多くの選択肢はありません。一方、大規模なアプリでは、ユーザーが通知を操作したときに意味のある画面に誘導することで、シームレスなエクスペリエンスが実現されます。

  1. NotificationUtils.kt を開き、sendNotification() 拡張関数を見つけます。
  2. applicationContext と起動するアクティビティ MainActivity::class.java を指定して Intent を作成します。
// 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、前のステップで作成した contentIntentPendingIntent フラグで PendingIntent を作成します。PendingIntent フラグは、新しい 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 を通知に渡します。これを行うには、NotificationBuildersetContentIntent() を呼び出します。通知をクリックすると 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 には cancelAll() というアクティブな通知をすべてキャンセルする API があります。

  1. NotificationsUtil.kt を開きます。
  2. cancelAll() を呼び出す拡張関数を NotificationManager に追加します。
// 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. まず、BitmapFactory を使用して resources から画像を読み込みます。
// 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: 通知の操作

通知の操作は、通知に追加できるカスタマイズです。現在、ユーザーが通知をクリックすると、アプリにリダイレクトされます。このデフォルトの通知アクションに加えて、通知からアプリ関連のタスクを実行するアクション ボタンを追加できます。

通知には最大 3 つの操作ボタンがあり、リマインダーのスヌーズやテキスト メッセージへの返信など、ユーザーがすばやく応答できます。これらの操作ボタンは、ユーザーが通知をタップしたときに実行される操作を複製してはなりません。

アクション ボタンを追加するには、ビルダーで PendingIntentaddAction() 関数に渡します。これは、setContentIntent() を呼び出して通知のデフォルトのタップ アクションを設定する場合と似ていますが、アクティビティを起動する代わりに、バックグラウンドで実行されるジョブを実行する BroadcastReceiver を起動するなど、さまざまな操作を行うことができます。

この Codelab では、すでに SnoozeReceiver という名前の BoadcastReceiver が付与されています。SnoozeReceiver を使用して、ユーザーが通知アクションをクリックするようにします。次の手順では、ユーザーがスヌーズ操作ボタンをクリックした際に卵タイマーの通知を 60 秒間スヌーズするコードを追加します。スヌーズ アクションがクリックされると、SnoozeReceiver はインテントを受け取り、新しいアラームを作成して 60 秒後に新しい通知を送信します。

  1. SnoozeReceiver.kt を開きます。このクラスは、前に使用した AlarmReceiver に似ています。次の手順では、SnoozeReceiveronReceive() 関数をトリガーするコードを追加します。簡単に言うと、SnoozeReceiver のコードにより、1 分後に新しい通知を送信する新しいアラームが作成されます。onReceive 関数の下まで下にスクロールして、システムから notificationManager のインスタンスを取得し、cancelAll を呼び出します。
// SnoozeReceiver.kt
        val notificationManager = ContextCompat.getSystemService(
            context,
            NotificationManager::class.java
        ) as NotificationManager
        notificationManager.cancelAll()
  1. SnoozeReceiverを使用するには、NotificationUtils.ktを開いてください。
  2. sendNotification() 関数のスタイルの直後に、SnoozeReceiver の新しい Intent snoozeIntent を作成します。
  3. 次のステップのパラメータが要求される PendingIntentgetBroadcast() メソッドを呼び出して、ペンディング インテントを作成します。この PendingIntent は、ユーザーがスヌーズボタンがタップされてから 60 秒後に新しい通知を投稿するための新しいアラームを設定するために、システムによって使用されます。
  4. 最初のパラメータは、この PendingIntent がアクティビティを開始するアプリのコンテキストです。
  5. 2 つ目のパラメータはリクエスト コードです。これは、このペンディング インテントのリクエスト コードです。このペンディング インテントを更新またはキャンセルする場合は、このコードを使用してペンディング インテントにアクセスする必要があります。
  6. 次に、起動するアクティビティのインテントである snoozeIntent オブジェクトを追加します。
  7. 最後に、インテントは 1 回だけ使用するため、#FLAG_ONE_SHOT のフラグ値を追加します。クイック アクションと通知は、最初のタップ後に消えます。そのため、インテントは 1 回しか使用できません。
// 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. タイマーを実行してアプリをバックグラウンドに移行します。タイマーが終了しました。通知を展開すると、卵のタイマーが 1 分スヌーズするスヌーズ操作ボタンが表示されます。

ステップ 3: 通知の重要度

重要性は、通知がどのくらい視覚的に、またはユーザーの邪魔になるかを決定します。重要度の高い通知は、ユーザーに対する割り込みが多くなります。

重要度は NotificationChannel コンストラクタで指定する必要があります。最初に、卵タイマーアプリの重要度を低く設定しました。IMPORTANCE_NONE(0)IMPORTANCE_HIGH(4) の 5 つの重要度レベルのいずれかを使用できます。チャンネルに割り当てた重要度は、そのチャンネルに投稿したすべての通知メッセージに適用されます。

チャンネルの重要度レベル

ユーザーに表示される重要度

重要度(Android 8.0 以降)

優先度(Android 7.1 以前)

音が鳴り、ヘッドアップ通知として表示されます(画面上部にポップアップ表示されます)。

IMPORTANCE_HIGH

PRIORITY_HIGH / PRIORITY_MAX

音を鳴らす

IMPORTANCE_DEFAULT

PRIORITY_DEFAULT

無音

IMPORTANCE_LOW(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: 通知バッジ

通知バッジは、アプリに有効な通知がある場合に、関連するアプリのランチャー アイコンに表示される小さな点です。ユーザーはアプリアイコンを長押しして、通知を表示できます。

バッジと呼ばれるこうしたドットはデフォルトで表示され、アプリで対応する必要はありません。ただし、通知が意味をなさない場合もあるため、NotificationChannel オブジェクトに対して setShowBadge(false) を呼び出すことにより、チャンネルごとにバッジを無効にすることができます。エッグタイマーには一度に 1 つのアクティブな通知しかないため、アプリアイコンのバッジはユーザーにとってあまりメリットがありません。次のステップでは、バッジを無効にして、卵タイマーの通知のみを表示します。

  1. 卵タイマーのチャンネル作成コードに setShowBadge(false) を追加して、バッジを無効にします。
// EggTimerFragment.kt

    ).apply {
        // TODO: Step 2.6 disable badges for this channel
        setShowBadge(false)
    }
  1. アプリを再度実行し、タイマーを開始して、アプリのアイコンを確認します。アプリアイコンにはバッジが表示されません。

解答コードは、ダウンロードしたコードのマスター ブランチにあります。

Udacity コース:

Android デベロッパー ドキュメント:

このコースの他の Codelab へのリンクについては、Kotlin Codelab の高度な Codelab のランディング ページをご覧ください。