通知を作成する

通知は、アプリが使用されていないときに、アプリ内のイベントに関する簡潔な情報をタイムリーに提供します。このドキュメントでは、さまざまな機能を使用して通知を作成する方法について説明します。Android で通知を表示する方法の概要については、 通知の概要をご覧ください。 通知を使用するサンプルコードについては、SociaLite サンプルを GitHub でご覧ください。

このページのコードでは、AndroidX ライブラリの NotificationCompat API を使用しています。これらの API を使用すると、Android 9(API レベル 28)との後方互換性を維持しつつ、新しいバージョンの Android でのみ使用可能な機能を追加できます。 ただし、インライン返信アクションなどの一部の機能は、古いバージョンでは何も実施しません。

基本的な通知を作成する

最も基本的でコンパクトなフォーム(折りたたみ フォームとも呼びます)の通知は、アイコンとタイトルと少量のテキスト コンテンツを表示します。このセクションでは、ユーザーがタップしてアプリのアクティビティを起動できる通知を作成する方法を説明します。

図 1.アイコン、タイトル、テキストを含む通知。

通知を構成する各要素の詳細については、通知 の構造をご覧ください。

実行時の権限を宣言する

Android 13(API レベル 33)以降では、アプリから除外対象外(フォアグラウンド サービス(FGS)を含む)通知を送信するための実行時の権限がサポートされています。

アプリのマニフェスト ファイルで宣言する必要がある権限を、次のコード スニペットに示します。

<manifest ...>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    <application ...>
        ...
    </application>
</manifest>

実行時の権限の詳細については、 通知の実行時の権限をご覧ください。

通知のコンテンツを設定する

最初に、通知のコンテンツとチャネルを NotificationCompat.Builderオブジェクトを使用して設定します。下記の例は、次の要素を含む通知を作成する方法を示しています。

  • setSmallIcon() で設定される小さなアイコン。これは、ユーザーに表示されるコンテンツのうち、唯一必須な要素です。

  • setContentTitle() で設定されるタイトル。

  • `setContentText()` で設定される本文テキスト。

  • setPriority() で設定される通知の優先度。Android 7.1 以前では、優先度は、通知がユーザーの操作に割り込む度合いを表します。Android 8.0 以降では、代わりに次のセクションに示すようにチャネルの重要度を設定します。

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle(textTitle)
        .setContentText(textContent)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)

NotificationCompat.Builder コンストラクタは、チャネル ID の指定を必要とすることにご注意ください。これが必要なのは Android 8.0(API レベル 26)以降との互換性のためですが、古いバージョンでは無視されます。

デフォルトでは、通知のテキスト コンテンツは 1 行に収まるように切り捨てられます。展開可能な通知を作成すると、追加情報を表示できます。

図 2.折りたたみフォームと展開フォームの展開可能な通知。

通知で長いテキストを使用したい場合は、展開可能な 通知を有効にするには、setStyle()でスタイル テンプレートを追加します。たとえば次のコードは、より大きなテキスト領域を作成します。

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Much longer text that cannot fit one line...")
        .setStyle(NotificationCompat.BigTextStyle()
                .bigText("Much longer text that cannot fit one line..."))
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)

その他のサイズの大きな通知のスタイル(メディアの再生コントロールや画像を追加する方法を含む)について詳しくは、 展開可能な通知を作成するをご覧ください。

チャネルを作成して重要度を設定する

Android 8.0 以降で通知を配信するには、アプリの 通知チャネルをシステムに登録しておく必要があります。これを行うには、 NotificationChannelのインスタンスをcreateNotificationChannel()に渡します。次のコードは SDK_INT バージョンの条件によってブロックされます。

private fun createNotificationChannel(context: Context) {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is not in the Support Library.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val name = context.getString(R.string.channel_name)
        val descriptionText = context.getString(R.string.channel_description)
        val importance = NotificationManager.IMPORTANCE_DEFAULT
        val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
            description = descriptionText
        }
        // Register the channel with the system.
        val notificationManager: NotificationManager =
            context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(channel)
    }
}

Android 8.0 以降では、通知を送信する前に通知チャネルを作成する必要があるため、アプリが起動したらすぐにこのコードを実行する必要があります。既存の通知チャネルを作成してもオペレーションは実行されないので、これを繰り返し呼び出しても問題ありません。

NotificationChannel コンストラクタには、importance が必要であることにご注意ください。これは、NotificationManager クラスの 定数の 1 つを使用します。このパラメータは、このチャネルに属する通知でユーザーの操作に割り込む方法を決定します。前の例に示すように、Android 7.1 以前をサポートするために setPriority() で優先度を設定します。

次の例に示すように通知の重要度または優先度を設定する必要がありますが、設定したとおりにアラートが動作する保証はありません。場合によっては、システムは他の要因に基づいて重要度レベルを変更することがあり、ユーザーは特定のチャネルの重要度レベルをいつでも再定義できます。

各種のレベルの意味については、 通知の重要度レベルをご覧ください。

通知のタップ アクションを設定する

すべての通知はタップに応答する必要があります。一般的には、通知に対応するアクティビティをアプリで開きます。そのためには、コンテンツ インテント を指定し、PendingIntent オブジェクトで定義されたものを setContentIntent()に渡します。

次のスニペットは、ユーザーが通知をタップしたときにアクティビティを開く基本的なインテントを作成する方法を示しています。

// Create an explicit intent for an Activity in your app.
val intent = Intent(context, AlertDetails::class.java).apply {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent =
    PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        // Set the intent that fires when the user taps the notification.
        .setContentIntent(pendingIntent)
        .setAutoCancel(true)

このコードは setAutoCancel() を呼び出すことに留意してください。これにより、ユーザーが通知をタップすると、自動的に通知が消去されます

前の例のインテント フラグは、ユーザーが通知を使用してアプリを開いた後、ユーザーが期待するナビゲーション エクスペリエンスを維持します。開始するアクティビティのタイプ(次のいずれか)に応じて、使用することをおすすめします。

  • 通知への応答のためだけに存在するアクティビティ。 アプリの通常の使用中にユーザーがこのアクティビティに移動する理由はないため、 アクティビティはアプリの 既存のタスクとバックスタックに追加される代わりに、新しいタスクを開始します。これは、前のサンプルで作成されるインテントのタイプです。

  • アプリの通常のアプリフロー内に存在するアクティビティ。この場合、 アクティビティを開始するとバックスタックが作成され、ユーザーが期待する 「戻る」ボタンと「上へ」ボタンが維持されます。

通知を表示する

通知を表示するには、 NotificationManagerCompat.notify() を呼び出して、 通知の一意の ID と NotificationCompat.Builder.build() の結果を渡します。これを次の例に示します。

with(NotificationManagerCompat.from(context)) {
    if (ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.POST_NOTIFICATIONS
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        // TODO: Consider calling ActivityCompat#requestPermissions here
        // to request the missing permissions, and then overriding
        // public fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>,
        //                                        grantResults: IntArray)
        // to handle the case where the user grants the permission. See the documentation
        // for ActivityCompat#requestPermissions for more details.

        return@with
    }
    // notificationId is a unique int for each notification that you must define.
    notify(NOTIFICATION_ID, builder.build())
}

NotificationManagerCompat.notify() に渡す通知 ID は、 後で通知を更新または 削除する場合に必要になるため、必ず保存しておいてください。

また、Android 13 以降を搭載したデバイスで基本的な通知をテストするには、通知を手動でオンにするか、通知をリクエストするダイアログを作成します。

アクション ボタンを追加する

通知には、ユーザーがすばやく応答できるようにするためのアクション ボタン(リマインダーのスヌーズやテキスト メッセージへの返信など)を最大で 3 つ設定できます。ただし、これらの アクション ボタンは、ユーザーが通知をタップしたときに実行されるアクションと同じであってはなりません

図 3.操作ボタンが 1 つある通知。

操作ボタンを追加するには、PendingIntentaddAction() メソッドに渡します。これは、通知のデフォルトのタップアクションを設定する場合と同様です。ただし、 アクティビティを起動する代わりに、バックグラウンドでジョブを実行する BroadcastReceiverを開始するなどのさまざまな処理を実行でき、 アクションはすでに開いているアプリを中断しません。

たとえば次のコードは、特定のレシーバにブロードキャストを送信する方法を示しています。

val ACTION_SNOOZE = "snooze"

val snoozeIntent = Intent(context, MyBroadcastReceiver::class.java).apply {
    action = ACTION_SNOOZE
    putExtra(EXTRA_NOTIFICATION_ID, 0)
}
val snoozePendingIntent: PendingIntent =
    PendingIntent.getBroadcast(context, 0, snoozeIntent, PendingIntent.FLAG_IMMUTABLE)
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setContentIntent(pendingIntent)
        .addAction(R.drawable.ic_snooze, context.getString(R.string.snooze),
                snoozePendingIntent)

バックグラウンド処理を実行する BroadcastReceiver を作成する方法については、ブロードキャストの概要をご覧ください。

また、メディア再生ボタン(トラックの一時停止やスキップなど)を含む通知を作成する場合は、メディア コントロールを備えた通知を作成する方法をご覧ください。

ダイレクト返信アクションを追加する

Android 7.0(API レベル 24)で導入されたダイレクト返信アクションでは、ユーザーは通知にテキストを直接入力できます。返信テキストは、アクティビティを開かずにアプリに配信されます。たとえば、ダイレクト返信アクションにより、ユーザーがテキスト メッセージに返信したり、通知内からタスクリストを更新したりすることが可能になります。

図 4.返信ボタンをタップするとテキスト入力が開く。

ダイレクト返信アクションは、テキスト入力を開く通知内の追加ボタンとして表示されます。ユーザーが入力を完了すると、通知アクションに指定したインテントにテキスト応答が添付され、アプリにインテントが送信されます。

返信ボタンを追加する

ダイレクト返信をサポートする通知アクションを作成する手順は次のとおりです。

通知アクションに追加可能な RemoteInput.Builder のインスタンスを作成します。このクラスのコンストラクタは、システムがテキスト入力のキーとして使用する文字列を受け取ります。アプリは後でそのキーを使用して、入力テキストを取得します。

// Key for the string that's delivered in the action's intent.
private val KEY_TEXT_REPLY = "key_text_reply"
val replyLabel: String = context.resources.getString(R.string.reply_label)
val remoteInput: RemoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).run {
    setLabel(replyLabel)
    build()
}

返信アクション用の PendingIntent を作成します。

// Build a PendingIntent for the reply action to trigger.
val replyPendingIntent: PendingIntent =
    PendingIntent.getBroadcast(context,
        conversation.getConversationId(),
        getMessageReplyIntent(conversation.getConversationId()),
        PendingIntent.FLAG_MUTABLE)

addRemoteInput() を使用して RemoteInput オブジェクトをアクションにアタッチします。

// Create the reply action and add the remote input.
val action: NotificationCompat.Action =
    NotificationCompat.Action.Builder(R.drawable.ic_reply_icon,
        context.getString(R.string.label), replyPendingIntent)
        .addRemoteInput(remoteInput)
        .build()

アクションを通知に適用し、通知を発行します。

// Build the notification and add the action.
val newMessageNotification = NotificationCompat.Builder(context, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_message)
    .setContentTitle(context.getString(R.string.title))
    .setContentText(context.getString(R.string.content))
    .addAction(action)
    .build()

// Issue the notification.
NotificationManagerCompat.from(context).notify(notificationId, newMessageNotification)

図 4 に示すように、通知アクションをトリガーすると、システムは応答の入力をユーザーに促します。

返信からユーザー入力を取得する

通知の返信 UI からユーザー入力を受け取るには、 RemoteInput.getResultsFromIntent() を呼び出して、Intent が受信した あなたの BroadcastReceiver を渡します。

private fun getMessageText(intent: Intent): CharSequence? {
    return RemoteInput.getResultsFromIntent(intent)?.getCharSequence(KEY_TEXT_REPLY)
}

テキストを処理したら、同じ ID とタグ(使用している場合)を指定して NotificationManagerCompat.notify() を呼び出して、通知を更新します。これが必要なのは、ダイレクト返信 UI を非表示にして、返信が正常に受信および処理されたことをユーザーに確認するためです。

// Build a new notification, which informs the user that the system
// handled their interaction with the previous notification.
val repliedNotification = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_message)
        .setContentText(context.getString(R.string.replied))
        .build()

// Issue the new notification.
NotificationManagerCompat.from(context).notify(notificationId, repliedNotification)

その他のデータを取得する

他のデータ型の処理は、RemoteInput と同様に行います。次の例では、入力として画像を使用します。

val KEY_REPLY = "key_reply"
val replyLabel: String = context.resources.getString(R.string.reply_label)
val remoteInput: RemoteInput = RemoteInput.Builder(KEY_REPLY).run {
    setLabel(replyLabel)
    // Allow for image data types in the input.
    // This method can be used again to allow for other data types.
    setAllowDataType("image/*", true)
    build()
}

RemoteInput#getDataResultsFromIntent を呼び出して、対応するデータを抽出します。

class ReplyReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val dataResults = RemoteInput.getDataResultsFromIntent(intent, KEY_REPLY)
        val imageUri: Uri? = dataResults?.get("image/*") as? Uri

        if (imageUri != null) {
            // Extract the image
            try {
                val inputStream = context.contentResolver.openInputStream(imageUri)
                val bitmap = BitmapFactory.decodeStream(inputStream)
                // Display the image
                // ...
            } catch (e: Exception) {
                Log.e("ReplyReceiver", "Failed to process image URI", e)
            }
        }
    }
}

この新しい通知を使用する場合、 レシーバの onReceive() メソッドに渡されるコンテキストを使用します。

setRemoteInputHistory() を呼び出して、通知の下部に返信を付加します。ただし、メッセージ アプリを作成する場合は、 メッセージ スタイルの通知を作成して、新しいメッセージを 会話の最後に付加してください。

メッセージング アプリからの通知に関するアドバイスについては、 メッセージング アプリのおすすめの方法をご覧ください。

緊急メッセージを表示する

アプリでは、電話の着信呼び出しやアラームなどの緊急メッセージを表示しなければならない場合があります。このような場合、全画面インテントを通知に関連付けることができます。

通知が呼び出されると、デバイスのロック状態に応じて、次のいずれかがユーザーに表示されます。

  • ユーザーのデバイスがロックされている場合、ロック画面を覆う全画面アクティビティが表示されます。
  • ユーザーのデバイスがロックされていない場合、通知に対する応答または却下の選択肢を含む展開フォームで通知が表示されます。

次のコード スニペットは、通知を全画面インテントに関連付ける方法を示しています。

val fullScreenIntent = Intent(context, ImportantActivity::class.java)
val fullScreenPendingIntent = PendingIntent.getActivity(context, 0,
    fullScreenIntent, PendingIntent.FLAG_IMMUTABLE)

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setFullScreenIntent(fullScreenPendingIntent, true)

ロック画面の可視性を設定する

ロック画面での通知に表示される情報の詳細度を制御するには、 setVisibility() を呼び出し、次の値のいずれかを指定します。

  • VISIBILITY_PUBLIC: 通知の完全なコンテンツがロック画面に表示されます。

  • VISIBILITY_SECRET: ロック画面に通知の一部が表示されません。

  • VISIBILITY_PRIVATE: 通知のアイコンやコンテンツ タイトルなどの基本情報のみがロック画面に表示されます。通知の完全なコンテンツは表示されません。

VISIBILITY_PRIVATE を設定すると、特定の詳細を非表示にした通知コンテンツの代替バージョンを提供できます。たとえば、SMS アプリで「新しいテキスト メッセージが 3 件あります」という通知を表示する場合、メッセージ コンテンツと送信者を非表示にできます。この代替バージョンの通知を提供するには、最初に NotificationCompat.Builder で通常どおりに代替通知を作成します。次に、その代替通知を通常の通知に 添付しますsetPublicVersion()

ユーザーは常に、ロック画面に通知を表示するかどうかを最終的に制御できます。さらに、アプリの通知チャネルに基づいて制御することもできます。

通知を更新する

通知を発行した後で更新するには、NotificationManagerCompat.notify() を再度呼び出して、以前に使用したのと同じ ID で通知を渡します。以前の通知が却下されている場合は、代わりに新しい通知が作成されます。

オプションとして、setOnlyAlertOnce()を呼び出すこともできます。これは、通知が最初に表示されたときにだけ、(サウンド、バイブレーション、視覚的ヒントで)ユーザーの操作を中断し、その後の更新では中断しないようにします。

通知を消去する

次のいずれかが発生するまで、通知は表示され続けます。

  • ユーザーが通知を却下する。
  • 通知の作成時に setAutoCancel() を呼び出した場合、ユーザーが通知をタップする。
  • 特定の通知 ID を指定して cancel() を呼び出す(このメソッドは、継続的な通知も削除します)。このメソッドは、継続的な通知も削除します。
  • cancelAll() を呼び出す(このメソッドは、それまでに発行した通知をすべて削除します)。
  • `setTimeoutAfter()` を使用して通知の作成時にタイムアウトを設定した場合、指定された期間が経過する。必要であれば、指定されたタイムアウト期間が経過する前に通知をキャンセルすることもできます。

メッセージング アプリのおすすめの方法

メッセージング アプリとチャットアプリの通知を作成する際は、以下のおすすめの方法を参考にしてください。

MessagingStyle を使用する

Android 7.0(API レベル 24)以上では、Android によりメッセージング コンテンツ専用の通知スタイル テンプレートが提供されます。 NotificationCompat.MessagingStyleクラスを使用して、会話タイトル、 追加メッセージ、通知のコンテンツ ビューなど、通知に表示する複数の ラベルを変更できます。

次のコード スニペットは、MessagingStyle クラスを使用して通知のスタイルをカスタマイズする方法を示しています。

val user = Person.Builder()
    .setIcon(userIcon)
    .setName(userName)
    .build()

val notification = NotificationCompat.Builder(context, CHANNEL_ID)
    .setContentTitle("2 new messages with $sender")
    .setContentText(subject)
    .setSmallIcon(R.drawable.new_message)
    .setStyle(NotificationCompat.MessagingStyle(user)
        .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getPerson())
        .addMessage(messages[2].getText(), messages[2].getTime(), messages[2].getPerson())
    )
    .build()

Android 9.0(API レベル 28)以降では、通知とそのアバターを最適にレンダリングするために Person クラスを使用する必要もあります。

NotificationCompat.MessagingStyle を使用する手順は次のとおりです。

  • MessagingStyle.setConversationTitle() を呼び出して、3 人以上の参加者とのグループ チャットのタイトルを設定できます。会話のタイトルは、グループ チャットの名前か、特に名前を付けていない場合は会話の参加者のリストにすることをおすすめします。このようにしないと、メッセージが、会話内の最新のメッセージの送信者との 1 対 1 の会話に属していると誤解される可能性があります。
  • MessagingStyle.setData() メソッドを使用して、画像などのメディア メッセージを含めます 。パターン image/* の MIME タイプがサポートされています。

ダイレクト返信を使用する

ダイレクト返信では、ユーザーがメッセージにインラインで返信できます。

  • ユーザーがインライン返信アクションで返信したら、 MessagingStyle.addMessage() を使用して MessagingStyle 通知を更新し、通知を撤回またはキャンセルしないでください。通知をキャンセルしなければ、ユーザーは通知から複数の返信を送信できます。
  • Wear OS と互換性のあるインライン返信アクションを作成するには、 Action.WearableExtender.setHintDisplayInlineAction(true) を呼び出します。
  • addHistoricMessage() メソッドを使用して、通知に履歴 メッセージを追加し、ダイレクト返信の会話にコンテキストを提供します。

スマート リプライを有効にする

  • スマート リプライを有効にするには、setAllowGeneratedResponses(true) を 返信アクションで呼び出します。これにより、通知が Wear OS デバイスにブリッジされたときに、ユーザーがスマート リプライ応答を使用できるようになります。スマート リプライ応答は、NotificationCompat.MessagingStyle の通知から提供されるコンテキストを使用して、完全にスマートウォッチ上で動作する機械学習モデルによって生成されます。応答の生成のためにデータがインターネットにアップロードされることはありません。

通知メタデータを追加する