HTTP POSTで特定のAndroid移動機にFirebase Notificationを送る

# 前提
FirebaseのProject Setupが完了していて，Notificationを受けるAPK(Client App)にFirebaseの何かの機能(Analyticsとか)がすでに組み込まれていること
(google-services.jsonをapp/にCopyしたりするところまでは完了していること)

↓ このPostでAndroid AppにFirebase Analytics組み込むまでをやってます．
http://qiita.com/fezrestia/items/c303143a20b049909ca4

# やること
ざっくり，
　1. Client AppにFCM(Firebase Cloud Messaging)機能を追加
　2. Client Appを特定するためのInstance ID Tokenを取得
　3. サーバ等からHTTP POST Requestを発行
の3 StepでOK.

## Step1 Client AppにFCM機能を追加
Ref : https://firebase.google.com/docs/cloud-messaging/android/client

↓ App Levelのbuild.gradleにfirebase-messagingのLibを追加．

```text:app/build.gradle
dependencies {
    compile 'com.google.firebase:firebase-messaging:9.6.1'
}
```

↓ FirebaseのEventを受けるためのServiceをAndroid Manifestに追加．

```xml:app/src/main/AndroidManifest.xml
<!-- Firebase -->
<service
    android:name=".firebase.FcmMessagingService"
>
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

<service
    android:name=".firebase.FcmInstanceIdService"
>
    <intent-filter>
        <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
    </intent-filter>
</service>
```

↓ Serviceの実装.javaも追加．

```java:app/src/main/java/.../firebase/FcmMessagingService.java
public class FcmMessagingService extends FirebaseMessagingService {
    // AppがBackgroundの状態でNotification出すだけならカラ実装でOKのはず．
    // ForegroundでNotificationを受けるときはonMessageReceived()とかOverrideする．
}
```
```java:app/src/main/java/.../firebase/FcmInstanceIdService.java
public class FcmInstanceIdService extends FirebaseInstanceIdService {
    // TODO: Step2で実装追加
}
```

これでClient AppへのFCM機能組み込みは完了．

## Step2 Client Appを特定するためのInstance ID Tokenを取得
Ref : https://firebase.google.com/docs/cloud-messaging/android/client

Stpe1で追加した，FirebaseInstanceIdService(FirebaseInstanceId)を使ってInstance ID Tokenを取得する．
初めてAppを起動した時に，自動的にTokenが払い出されるのでそれをLog出力とかで取得する．

```java:app/src/main/java/.../firebase/FcmInstanceIdService.java
public class FcmInstanceIdService extends FirebaseInstanceIdService {
    @Override
    public void onTokenRefresh() {
        // Get updated Instance ID token.
        String refreshedToken = FirebaseInstanceId.getInstance().getToken();
        Log.d("Instance ID Token", "onTokenRefresh() : Token=" + refreshedToken);

        //TODO: Send Instance ID Token to server.
    }
}
```

変更時にしかonTokenRefresh()はCallbackされないので，Install後の初回起動時にしかCallbackされない．
適当なActivityのonResume()とかで
FirebaseInstanceId.getInstance().getToken();
とかして取り出したほうが楽かも．

TokenのReferenceによると，AppのInstallやClear Data時にTokenがRefreshされるとのこと．
本番運用のためには，Token更新後にServerとかに送って保持する仕組みが必要．

Ref : https://firebase.google.com/docs/reference/android/com/google/firebase/iid/FirebaseInstanceIdService.html#onTokenRefresh()

## Step3 サーバ等からHTTP POST Requestを発行
Ref : https://firebase.google.com/docs/cloud-messaging/server

送信先の種類によって実装が色々なパターンになる模様．
ここでは特定の1つの端末に向けてNotificationを発行する．

### Firebase Cloud Messaging Token取得
Firebase ConsoleからFCM Tokenを取得．
Server Keyでも動作するけど，Obsoletedになっているようなので使わないほうが良さそう．
![fcm_token_on_firebase_console.png](https://qiita-image-store.s3.amazonaws.com/0/129035/59e9f04a-c0fe-9d6a-1e8e-d4658c5805f8.png)

### HTTP POST Request発行
サーバ等から，
https://fcm.googleapis.com/fcm/send
に向けてHTTP POSTを発行する．

今回はRailsサーバから発行してみた．

```rb:server.rb
require 'net/https'

# HTTP Request
uri = URI.parse("https://fcm.googleapis.com/fcm/send")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(uri.path)

# Header
request["Content-Type"] = "application/json"
request["Authorization"] = "key=<Firebase Consoleから取得したFCM Token>}"

# Body
payload = "{
    \"notification\": {
        \"title\": \"HELLO WORLD !\",
        \"text\": \"1st notification test\"
    },
    \"to\": \"<Step2で取得したClient AppのInstance ID Token>\"
}"
request.body = payload

# Submit
response = http.request(request)
```

これでAndroid移動機上にNotificationがでるはず．

# まとめ
Firebase ConsoleからNotificationを送るよりも，Client App側への仕込みが必要だったり，Instance ID Tokenをサーバ側(HTTP POST送信元)で保持する必要があったり，いくらか面倒．
今回はサーバ側で定期実行しているTaskから移動機に通知を出したかったので調べてみました．

Firebase Cloud Messaging自体，送信先をTopicでカテゴライズできたりとか，いろいろ機能が豊富なので，まだまだいろんなことできそう．

///---