Androidアプリで通知を実装する

## はじめに

Androidアプリの通知の実装をしたことがなかったので挑戦してみました。
最低限の通知の実装のことしか書いていないので、ボタンをつけたりなど高度な(?)通知を実装したい方は他の記事を参照されることをお勧めします。

### 環境
- macOS Catalina(Version 10.15.7)
- Android Studio 4.0.1

## 実装ステップ

大まかに分けると4ステップで実現できます。

1. [通知内容の作成](#1-通知内容の作成)
2. [チャネルの作成](#2-チャネルの作成)
3. [通知タップ時のアクションの設定](#3-通知タップ時のアクションの設定)
4. [通知を表示する](#4-通知を表示する)

## 1. 通知内容の作成

### 1-1. チャネルIDの作成

Android 8.0（API レベル 26）以上の端末をサポートする場合に設定が必要です。
[NotificationCompat.Builder](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder?hl=ja) オブジェクトを使って表示内容を設定する際、チャネルIDを渡します。
※チャネルについては後述

### 1-2. 表示内容の設定

smallアイコン、タイトル、文字などを設定します
表示内容のうち必須の設定項目はsmallアイコンのみで、これが設定されていないとクラッシュします。
その他は任意項目です。(とはいえタイトルもテキストも設定されていない通知とかあるの・・？)

- smallアイコン：アプリ名の左に表示されるアイコンのこと。設定必須。
- 通知内容のTitle：1行目に表示されるタイトル。
- 通知内容のText：タイトルの下に表示されるテキスト。デフォルトでは1行表示。

![noti.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587653/002108de-ef13-a2e4-9261-31f6b1936ede.png)

### 1-3. 通知の優先度の設定

Android 7.1 以下をサポートする場合、`setPriority()` で通知の優先度を設定します。
Android 8.0 以上の場合は後述する通知チャネルで設定する値が優先されるので設定は不要です。
重要度の一覧は[こちら](https://developer.android.com/training/notify-user/channels?hl=ja)。

### サンプルコード

```Kotlin
        // 1-1. チャネルIDを作成
        val channelId = "NOTIFICATION_CHANNEL_ID_TRIAL"

        val builder = NotificationCompat.Builder(this, channelId)
            // 1-2. 表示内容の設定
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("通知のTitle🍩")
            .setContentText("通知のText🍮通知ですよ〜")
            // 1-3. 優先度の設定
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)

```

## 2. チャネルの作成

Android 8.0 以上をサポートする場合に必要な処理です。
システムにアプリの通知チャネルを登録することで、設定画面からチャネルごとに表示の大きさやサウンドなどが調節できるようになります。
※通知チャネルの詳細については[こちら](https://developer.android.com/training/notify-user/channels?hl=ja)をご確認ください

### 2-1. NotificationChannel オブジェクトを作成

チャネルIDや名前を指定して[NotificationChannel](https://developer.android.com/reference/android/app/NotificationChannel?hl=ja)オブジェクトを作成します。
- チャネルID：一意のID。ここでは `1-1. チャネルIDの作成` で作成したものを設定。
- チャネル名：ユーザー向けのチャネル名。設定画面で表示される。
- 説明文：チャネルの説明文。設定画面で表示されるため、必要に応じて設定。
- 重要度：`1-3. 通知の優先度の設定` の8.0以上の端末向けの設定。重要度の一覧は[こちら](https://developer.android.com/training/notify-user/channels?hl=ja)。

### 2-2. チャネルをシステムに登録

作成したチャネルをシステムに登録します。
登録には NotificationManager の[createNotificationChannel](https://developer.android.com/reference/android/app/NotificationManager?hl=ja#createNotificationChannel(android.app.NotificationChannel))を使用します。

### サンプルコード

```Kotlin
        // Android 8.0以上の場合のみ実行する
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // 2-1. NotificationChannel オブジェクトを作成
            val name = "お試し通知"
            val descriptionText = "お試しで通知を送るための通知チャンネルです😊！"
            val importance = NotificationManager.IMPORTANCE_DEFAULT
            val channel = NotificationChannel(channelId, name, importance).apply {
                description = descriptionText
            }
            // 2-2. チャネルをシステムに登録
            val notificationManager: NotificationManager =
                getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(channel)
        }
```

## 3. 通知タップ時のアクションの設定

ユーザーが通知をタップした時のアクションを設定します。
ここでは、通知をタップしたらアプリが開くようにします。

### 3-1. アプリ内アクティビティへのインテントを設定

通知をタップされた時に開きたいアクティビティを指定してインテントを作成します。
[PendingIntentオブジェクト](https://developer.android.com/reference/android/app/PendingIntent?hl=ja)を使って作成することで、指定したタイミング(この場合は通知タップ時)にintentが発行されます。

### 3-2. インテントをbuilderに設定

`setContentIntent()` を使って`1. 通知内容の作成`で作成したbuilderにインテントを設定し、通知タップ時に開くようにします。
※通知タップ時に通知が消されるように`setAutoCancel()`をtrueにする設定もしておきます。

```kotlin
        // 3-1. アプリ内アクティビティへのインテントを設定
        val intent = Intent(this, MainActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        }
        val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)

        val builder = NotificationCompat.Builder(this, channelId)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("通知のTitle🍩")
            .setContentText("通知のText🍮通知ですよ〜")
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            // 3-2. インテントをbuilderに設定
            .setContentIntent(pendingIntent)
            .setAutoCancel(true)

```

## 4. 通知を表示する

通知を表示する時は[NotificationManagerCompat.notify()](https://developer.android.com/reference/androidx/core/app/NotificationManagerCompat?hl=ja#notify(int,%20android.app.Notification))を呼び出します。
この時指定する`notificationId`は通知ごとにユニークなIDを設定します。このIDは通知の更新や削除にも使われます。

```kotlin
        // 4. 通知を表示する
        with(NotificationManagerCompat.from(this)) {
            // notificationIDとbuilder.build()を渡します
            notify(12345, builder.build())
        }
```

ビルドして通知を送ると、こんな感じで表示されます↓↓
わーい成功:sparkles:
※通知を発火させる用のボタンを用意して動作確認しました。(`onClickListener`内に↑↑のコードたちを書きました)

![basic_notification.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587653/faff5871-2d50-3790-b096-e6955b151061.png)

## 参考資料

この記事は以下の情報を参考にして執筆しました。

- 通知の実装方法について
  - [通知の概要(Androidデベロッパー)](https://developer.android.com/guide/topics/ui/notifiers/notifications?hl=ja)
  - [通知を作成する(Androidデベロッパー)](https://developer.android.com/training/notify-user/build-notification?hl=ja)
- 通知のデザインと操作パターンについて
  - [Android notifications(Material Design)](https://material.io/design/platform-guidance/android-notifications.html)

## おわりに

今回はかなり最低限の通知を実装しましたが、ボタンをつけてアクションを設定したり展開可能にするなど色々なオプションがあるのでそちらも試してみるつもりです。