Android FCM(Firebase Cloud Messaging)の実装

# 実装の流れ

#### 資格情報
実装する FCM 機能によっては、Firebase プロジェクトから次の[資格情報](https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ja&authuser=0#credentials)が必要になる場合がある。

#### AndroidManifestの編集

バックグラウンドでアプリの通知を受信する以外のメッセージ処理を行う場合に必要です。
フォアグラウンド アプリで通知を受信したり、データ ペイロードを受信したり、アップストリーム メッセージを送信したりするには、このサービスを拡張する必要があります。([ドキュメント](https://firebase.google.com/docs/cloud-messaging/android/client?hl=ja))
```
<service
    android:name=".java.MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>
```

#### 通知
 通知には二種類ある。
- 通知メッセージ：FCM SDKによって自動で処理される=FCMによって表示処理がされる
- データメッセージ：クライアントアプリで処理する必要がある。

```
//通知メッセージ
{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification":{
      "title":"Portugal vs. Denmark",
      "body":"great match!"
    }
  }
}

//データメッセージ
{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "data":{
      "Nick" : "Mario",
      "body" : "great match!",
      "Room" : "PortugalVSDenmark"
    }
  }
}
```
また、この二つを含めることも可能。

#### 通知許可

- Android13以降では通知許可を[リクエスト](https://firebase.google.com/docs/cloud-messaging/android/client?hl=ja&authuser=0#request-permission13)する必要がある。
この権限が許可されるまで通知を表示することはできない。

- Android12以下はアプリが初めて通知チャンネルを作成するときに自動で許可を求めるが、アプリがバックグラウンドで実行されているときに最初の通知チャネルを作成する場合 (FCM 通知を受信するときに FCM SDK が行う)、Android は通知の表示を許可せず、次の通知までユーザーに通知許可を求めない。アプリが開かれた時間。これは、アプリが開かれ、ユーザーが許可を受け入れる前に受信した通知が失われることを意味する。
そのため、 Android13を対象とするようにアプリを更新して、権限を要求することを進めている。(対象に入れる必要はあるか？)

#### デバイス登録トークン

- アプリの最初の起動時に、FCM SDK はクライアント アプリ インスタンスの登録トークンを生成する。単一のデバイスをターゲットにするか、デバイス グループを作成する場合は、 [FirebaseMessagingService](FirebaseMessagingService)を拡張し、 onNewTokenをオーバーライドして、このトークンにアクセスする必要がある。トークンは最初の起動後にローテーションされる可能性があるため、[最新の更新された登録トークンを取得](https://firebase.google.com/docs/cloud-messaging/android/client?hl=ja&authuser=0#retrieve-the-current-registration-token)することを勧めている。
今回利用した際には、RealtimeDatabaseでユーザーごとにこのトークンを保持しておき、REST APIで通知表示させたいユーザーの登録トークンを利用した。

 登録トークンは、次の場合に変更されることがある。

- アプリは新しいデバイスに復元される
- ユーザーがアプリをアンインストール/再インストールする
- ユーザーがアプリデータを消去する。

FirebaseMessagingServiceは、onMessageReceivedおよび[onDeletedMessages](https://firebase.google.com/docs/cloud-messaging/android/receive?hl=ja&authuser=0#override-ondeletedmessages)コールバックをオーバーライドする必要がある。
受信から 20 秒以内 (Android Marshmallow では 10 秒) 以内にメッセージを処理する必要がある。 
onMessageReceivedを呼び出す前に発生した OS の遅延によっては、時間枠が短くなる場合がある。
通知を比較的連続して表示させたい場合には、優先度を高くする必要がある?(まだ試せていない)
```
class PushNotificationService: FirebaseMessagingService() {

    //onNewTokenコールバックは、新しいトークンが生成されるたびに発生。
    override fun onNewToken(token: String) {
        super.onNewToken(token)
    }
    
    //メッセージ受信、foreGroundでも通知処理をしたい場合等に必要
    override fun onMessageReceived(message: RemoteMessage) {
        super.onMessageReceived(message)
    }
}
```
onMessageReceivedは、次の例外を除いて、ほとんどのメッセージ タイプに提供されます。

アプリがバックグラウンドにあるときに配信される通知メッセージ。この場合、通知はデバイスのシステム トレイに配信されます。ユーザーが通知をタップすると、デフォルトでアプリ ランチャーが開きます。

バックグラウンドで受信した場合、通知とデータ ペイロードの両方を含むメッセージ。この場合、通知はデバイスのシステム トレイに配信され、データ ペイロードはランチャー アクティビティのインテントのエクストラで配信されます。
##### 要約
| アプリの状態 | 通知 | データ | 両方 |
|:-:|:-:|:-:|:-:|
|  foreground |onMessageReceived   | onMessageReceived  |  onMessageReceived |
|background   | システムトレイ  |onMessageReceived      |通知: システム トレイ データ:インテントのエクストラ。|

主にFCMからのメッセージを取得したらonMessageReceived(フォアグラウンドで必要)で通知投稿の処理をする。

#### REST API

デバイスからある時に他の端末に通知を投稿したい場合にはFirebaseの[REST API](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send?hl=ja)を利用すか、[ここ](https://firebase.google.com/docs/cloud-messaging/send-message?hl=ja#send-messages-to-specific-devices)の記述にある様に実装する(ただし、[FirebaseAdminSDK](https://firebase.google.com/docs/admin/setup?hl=ja)を利用する必要がある。)

#### 考え中
今回作成したアプリでは、通知メッセージを利用しており、通知をバックグラウンドのみで表示したかったためAndroidManifestの変更やFirebaseMessagingServiceの実装をしなければバックグラウンドのみになるかと思っていたが、表示されなかった。onMessageReceived()のコールバックでフォアグラウンドかバックグラウンドかを判定することでバックグラウンドでのみ表示されるように変更できたが、正規じゃないはず。、