〈kotlin〉通知(NotificationCompat)

Android studioでkotlinを使ってAndroidアプリ作成の勉強中。

NotificationCompat APIを使用して通知を作成する方法をまとめる。

通知チャネルの作成

Android 8.0(APIレベル 26)以降、通知はすべてチャネルに割り当てる必要がある。
端末側ではチャネルごとに通知の設定をするため、種類ごとに分けてチャネルを割り当てる。

//古いバージョンではチャネルの設定がなく落ちてしまうので、バージョンによる条件分岐を入れる
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val name = getString(R.string.channel_name) //表示するチャネル名
        val descriptionText = getString(R.string.channel_description) //チャネルの説明文
        val importance = NotificationManager.IMPORTANCE_DEFAULT //通知の重要度

        //一意のチャネルID、名前、重要度を指定したNotificationChannelオブジェクト
        val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
            description = descriptionText
        }
        
        val notificationManager: NotificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        //チャネルの新規作成
        notificationManager.createNotificationChannel(channel)
    }

通知を行う前にチャネルを作成する必要があるため、アプリ起動時にこのコードを実行する。
(既存のチャネルは再作成されることはないため、何度呼び出されても大丈夫。)

通知の作成、送信

NotificationCompat.Builderに割り当てたい通知チャネルのIDを渡し、通知の内容を設定する。
NotificationManagerCompat.notify()を呼び出して、
通知ごとに一意のIDと作成したNotificationCompat.Builder.build()の結果を渡す。

var builder = NotificationCompat.Builder(this, CHANNEL_ID) //thisはcontext
        .setSmallIcon(R.drawable.notification_icon) 
        .setContentTitle(textTitle)
        .setContentText(textContent) //通常は1行に収まるように切り捨てて表示される
        .setPriority(NotificationCompat.PRIORITY_DEFAULT) // Android 7.1以下での通知の優先度を指定
        .setStyle(NotificationCompat.BigTextStyle() //収まり切らない通知は展開して表示できるようにする
                .bigText("通知の全文")) 

with(NotificationManagerCompat.from(this)) {
        notify(notificationId, builder.build())
}

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

通常、通知はタップすると対応するアプリのアクティビティを開くように設定する。
アクティビティの開き方は2種類。
どちらの場合もPendingIntent オブジェクトでインテントを指定し、それをsetContentIntent()に渡し、
さらに setAutoCancel()をtrueにして、タップ後に自動で通知を消すよう設定する。

1.バックスタックを作成する
通知からアクティビティを開くと、バックスタックも作成されて、
戻るボタンを押したときに上位の階層に移動する(ホーム画面など)。
アクティビティの階層を定義する
マニフェストファイルにandroid:parentActivityName 属性を追加する。

<activity
        android:name=".MainActivity"
        android:label="@string/app_name" >
        ...
    </activity>
    
    <activity
        android:name=".ResultActivity"
        android:parentActivityName=".MainActivity" />
        ...
    </activity>

バックスタックを含むアクティビティを起動するPendinigIntent作成
TaskStackBuilderを使用してPendingIntentをセットアップし、新しいバックスタックが作成されるようにする。
TaskStackBuilderのインスタンスからaddNextIntentWithParentStack()を呼び出して、
起動するアクティビティの Intentを渡す。

val resultIntent = Intent(this, ResultActivity::class.java)
val resultPendingIntent: PendingIntent? = TaskStackBuilder.create(this).run {
    addNextIntentWithParentStack(resultIntent)
    getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
    //引数はリスクエストコードとIntentを制御するフラグ
}

val builder = NotificationCompat.Builder(this, CHANNEL_ID).apply {
    setContentIntent(resultPendingIntent)
    ...
}


2.バックスタックはなく、単独で開始する
通知では表示しにくい情報をアクティビティで提供するために、
通知からしか表示されないアクティビティを開く。
アクティビティの属性を定義する
マニフェストファイルに属性を追加し、他のアクティビティ群から開かれないよう設定する。

<activity
        android:name=".ResultActivity"
        android:launchMode="singleTask"
        android:taskAffinity="" // FLAG_ACTIVITY_NEW_TASK フラグと組み合わせて、アプリの他のタスクに含まれないようにする
        android:excludeFromRecents="true" //履歴からこのアクティビティのタスクを除外する>
</activity>

PendingIntent作成

val notifyIntent = Intent(this, ResultActivity::class.java).apply {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val notifyPendingIntent = PendingIntent.getActivity(
        this, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT
)
val builder = NotificationCompat.Builder(this, CHANNEL_ID).apply {
        setContentIntent(notifyPendingIntent)
        ...
}


基本的な通知の作成の流れはここまで。
他にも画像や会話を表示する展開可能な通知や、
アクションボタンやダイレクト返信を追加、進行状況バーなどカスタマイズできる。
また、Android13から新しく通知実行時の権限が追加されているようなので、
これも見ていく必要がありそう。
通知に関する実行時の権限  |  Android デベロッパー  |  Android Developers

参考
Create a Notification  |  Android Developers
Start an Activity from a Notification  |  Android Developers