Kotlinでプッシュ通知を送ろう！

# 【Kotlin×mBaaS】シリーズ概要
* Kotlinで[ニフクラ mobile backend](http://mbaas.nifcloud.com/)(通称：mBaaS)を使ってKotlinで開発を始めたい人向けのドキュメントです
* [ニフクラ mobile backendのAndroid(Java)用ドキュメント](https://mbaas.nifcloud.com/doc/current/index.html#/Android)を__Kotlin__用に書き換えて動かしてみたものをまとめました
* 初心者でもわかりやすいよう心掛けて作っていますが、わかりにくい部分がありましたらコメントをいただければ訂正しますのでお気軽にご意見をお願いします
* 今回は__＜プッシュ通知編＞__です！

# __Kotlinでプッシュ通知を送ろう！__
　プッシュ通知を送るにはGoogleが提供している__FCM__ と連携させることでプッシュ通知は送信されます
![Notification.png](https://qiita-image-store.s3.amazonaws.com/0/72656/2f658f0c-3d19-07dc-2ab0-1e10fe66df60.png)

※FCMとは、[Firebase Cloud Message](https://firebase.google.com/docs/cloud-messaging/)の略で、サーバーからの通知をAndroidなどの端末に転送するGoogleのプッシュ通知を送るためのサービスのことです。

## 準備するもの・使うもの
* Android Studio 3以上 
* Android phone
 * 接続用にケーブルが必要です
* Googleアカウントの取得（無料）
 * [Firebase](https://console.firebase.google.com) を利用します（無料）

## 手順
1. Android Studio上にKotlinアプリを作成する
1. mBaaS上にアプリを作成し、サーバーキーの設定を行う
1. アプリでの設定 
1. 実機デバッグビルドを行う 
1. 動作確認！プッシュ通知を送りましょう

# 操作
## **1. Android Studio上にKotlinアプリを作成する**
* 新規でも既存のアプリでも大丈夫ですが、**SDKのインストールなど事前準備が必要**になります
* 下記ページご覧いただき操作をお願いします
 * [KotlinでmBaaSを始めよう！](https://qiita.com/nifcloud_mb/items/5b6290977f3147c3bc4f)

## **2. mBaaS上にアプリを作成し、サーバーキーの設定を行う**
* まず、ニフクラ mobile backendと連携させるために必要な「サーバーキー」と「送信者ID」をFCMから取得します。
 * [Firebase](https://console.firebase.google.com/?hl=ja)にGoogleアカウントでログインし、プロジェクトを新規作成します。
 * プロジェクト名などは自由に設定してください

![Firebase_CreateApp.png](https://qiita-image-store.s3.amazonaws.com/0/72656/826d4f83-f5ab-5b22-0076-acb0538519c2.png)

* プロジェクト作成が完了した後、左メニューの設定アイコンから「プロジェクトの設定」を開きます。設定画面で「クラウドメッセージング」をクリックすると、プロジェクトキ認証情報に「サーバーキー」と「送信者ID」が確認できます。

![Firebase_Setting_CloudMessaging.png](https://qiita-image-store.s3.amazonaws.com/0/72656/bfc5a972-59eb-2fb2-d62a-75214b23b03e.png)

* ここで確認した「サーバーキー」はプッシュ通知を利用するための「APIキー」としてニフクラ mobile backend に登録をします。また、「送信者ID」は配信端末情報の登録処理コーディング時に「SenderID」として利用します。
* ニフクラ mobile backend に必要な設定を行います
 * __1.__ で作成済みの ニフクラ mobile backend アプリのダッシュボードを開きます
 * 「アプリ設定」＞「プッシュ通知」をクリックすると設定画面が表示されます
 * 「プッシュ通知」の「プッシュ通知の許可」のところの「許可する」にチェックをつけて、「保存する」をクリックします
* 次に、「プッシュ通知」の「Androidプッシュ通知」のところの「APIキー」にFCMで取得した「サーバーキー」を設定し、「保存する」をクリックします
 * 「設定を保存しました」と表示されればOKです

![controlpanel_pushsetting.png](https://qiita-image-store.s3.amazonaws.com/0/72656/be3a24cd-d539-14f6-935e-7222cbef048a.png)


## **3. アプリでの設定**
アプリ側では、以下のような設定を行います。

* 必要なライブラリのインストール
* AndroidManifestの編集
* 端末情報をニフクラ mobile backendに登録する処理の実装

なお、ニフクラ mobile backendのAndroid SDKは
インストールと初期化が済んでいるものとして説明しますので、
未実施の場合は[KotlinでmBaaSを始めよう！](https://qiita.com/nifcloud_mb/items/5b6290977f3147c3bc4f)を確認してください。

### **ライブラリのインストール**

プッシュ通知を mobile backend で送受信するためには、以下のライブラリが必要です。

* Google Play Services SDK
 * プッシュ通知を受信するために必要です
* Support Library
 * 通知を表示させるために必要です

これらのライブラリは以下の手順でインストールします

* SDK Managerで必要なライブラリをインストールする
 * Android Support Library
 * Android Support Repository（Android Studioの場合）
 * Google Play Services
* アプリケーションモジュールのbuild.gradleファイルを編集する
 * repositoriesに追加

```
repositories {
    maven {
        google()
    }
}
```
* dependenciesに追加

```
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation project(':NCMB')
    compile 'com.google.android.gms:play-services-gcm:11.8.0'
}
```
詳細な手順については、Googleが提供しているドキュメントをご覧ください

### **Android Manifestの編集**
AndroidManifest.xmlにて、利用するパーミッションの宣言と
レシーバー、サービスの登録を行います。

まずは、manifestタグの要素として以下のパーミッションの利用を宣言してください。
android.permission.VIBRATEが不要な場合は削除しても構いません。
パッケージ名は、各自のプロジェクトのものに適宜変更してください。

```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission android:name="パッケージ名.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="パッケージ名.permission.C2D_MESSAGE" />
```
次に、applicationタグの要素としてreceiverとserviceの登録を行います。

* receiverの登録
 * パッケージ名は適宜変更してください

```xml
<receiver
    android:name="com.nifty.cloud.mb.core.NCMBGcmReceiver"
    android:exported="true"
    android:permission="com.google.android.c2dm.permission.SEND">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
        <category android:name="パッケージ名"/>
    </intent-filter>
</receiver>
```
* serviceの登録

```xml
<service
    android:name="com.nifcloud.mbaas.core.NCMBGcmListenerService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
    </intent-filter>
</service>
```
* meta-dataの設定

v2より以下の項目がAndroidManifestで設定可能となりました。
**プッシュ通知タップ時に起動するActivityの設定は必須の設定となります。**

```xml
<!-- プッシュ通知タップ時に起動するActivityの設定 -->
<meta-data android:name="openPushStartActivity" android:value=".MainActivity"/>

<!-- 通知エリアに表示されるアイコンの設定 -->
<meta-data android:name="smallIcon" android:resource="@drawable/icon"/>

<!-- 通知エリアに表示されるアイコンカラーの設定 -->
<meta-data android:name="smallIconColor" android:value="@color/カラー名"/>

<!-- 通知エリアにプッシュ通知を複数表示する設定　0:最新のみ表示 , 1:複数表示 -->
<meta-data android:name="notificationOverlap" android:value="0"/>

<!-- カスタムダイアログプッシュを利用する場合のみ背景画像の設定 -->
<meta-data android:name="dialogPushBackgroundImage" android:resource="@drawable/balloon"/>
```

### **配信端末情報の登録**

FCMでは、プッシュ通知の配信端末を一意に識別するRegistration IDを利用し、
各端末への配信を行っています。

そのため、FCMからRegistration IDを取得したあとで、
データストアに配信端末情報として登録する必要があります。
以下の端末情報の登録処理を、プッシュ通知を受信するアクティビティのonCreateメソッド内に記入してください。
「YOUR_SENDER_ID」の部分には、FCMでプロジェクト作成時に発行されたSender ID(送信者ID)を設定します。

```kotlin:Kotlin
val installation = NCMBInstallation.getCurrentInstallation()

//FCMからRegistrationIdを取得
installation.getRegistrationIdInBackground("YOUR_SENDER_ID") { e ->
    if (e == null) {
        //端末情報をデータストアに登録
        installation.saveInBackground { saveErr ->
            if (saveErr != null) {
                //端末情報登録時のエラー処理
            }
        }
    } else {
        //RegistrationId取得時のエラー処理
    }
}
```

### **アプリの再インストールを考慮する場合**

アプリが再インストールされた場合などに、
前回のインストール時に保存された端末情報がデータスト上に残ったままの場合が考えられます。
その場合、端末情報の登録でRegistration IDの重複エラーが発生するため、
エラーが発生したときの処理を実装する必要があります。

```kotlin:Kotlin
val installation = NCMBInstallation.getCurrentInstallation()

//FCMからRegistrationIdを取得しinstallationに設定する
installation.getRegistrationIdInBackground("YOUR_SENDER_ID") { e ->
    if (e == null) {
        installation.saveInBackground { e ->
            if (e == null) {
                //保存成功
            } else if (NCMBException.DUPLICATE_VALUE == e.code) {
                //保存失敗 : registrationID重複
                updateInstallation(installation)
            } else {
                //保存失敗 : その他
            }
        }
    } else {
        //ID取得失敗
    }
}
```
端末情報の更新を行うupdateInstallationメソッドでは
同じRegistration IDの端末情報を取得して、更新を行っています。

```kotlin:Kotlin
fun updateInstallation(installation: NCMBInstallation) {

    //installationクラスを検索するクエリの作成
    val query = NCMBInstallation.getQuery()

    //同じRegistration IDをdeviceTokenフィールドに持つ端末情報を検索する
    query.whereEqualTo("deviceToken", installation.deviceToken)

    //データストアの検索を実行
    query.findInBackground { results, e ->
        //検索された端末情報のobjectIdを設定
        installation.objectId = results[0].objectId

        //端末情報を更新する
        installation.saveInBackground()
    }
}
```

### **installationが削除された時を考慮する場合** 
以下の例のように、何らかの理由により有効な installation が削除されてしまった場合、通常の実装では installation の再登録は行われず、該当の端末へはプッシュ通知を配信することができなくなってしまいます。

* 管理者の操作ミスによる削除
 * コントロールパネル上の操作ミスなど
* 管理者が意図しない操作による削除
 * mobile backend では[アプリがアンインストールされた端末情報(installation)の自動削除](http://mbaas.nifcloud.com/doc/current/common/dev_guide.html#%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E9%80%9A%E7%9F%A5%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)機能において、配信依頼時のレスポンスとして返却される「無効なレジスタレーションID」を削除しています。 「有効なレジスタレーションID」であっても、FCMの判断で返却された場合は、mobile backend 側では削除対象となります。

そのため、 installation を再度登録するには、以下の処理を追加実装する必要があります。

```kotlin:Kotlin
val installation = NCMBInstallation.getCurrentInstallation()

//FCMからRegistrationIdを取得しinstallationに設定する
installation.getRegistrationIdInBackground("YOUR_SENDER_ID") { e ->
    if (e == null) {
        installation.saveInBackground { e ->
            if (e == null) {
                //保存成功
            } else if (NCMBException.DATA_NOT_FOUND == e.code) {
                //保存失敗 : 端末情報の該当データがない
                reRegistInstallation(installation)
            } else {
                //保存失敗 : その他
            }
        }
    } else {
        //ID取得失敗
    }
}
```
installation が削除された場合（端末情報の該当データがmoble backend 上に存在しない場合）は、以下のように installation を再作成します。

```kotlin:Kotlin
fun reRegistInstallation(installation: NCMBInstallation) {

    //objectIdと作成日・更新日のみ削除し、installationが再生成されるようにする
    installation.objectId = null
    installation.remove("createDate")
    installation.remove("updateDate")

    //データストアの端末情報の再登録を実行
    installation.saveInBackground { e ->
        if (e == null) {
            //登録成功
        } else {
            //登録失敗
        }
    }
}
```

以上でアプリ側での設定は完了です。
アプリをAndroid端末で実行し、端末情報がデータストアに登録されるか確認してください

## **4.実機デバッグビルドを行う**

* Android実機をMacに接続して、メニューから[Run]または[Debug]を選択し、Device選択欄から接続したAndroid実機を選択します 
<img width="1301" alt="Run.png" src="https://qiita-image-store.s3.amazonaws.com/0/72656/e326de72-d634-fbac-2d81-231cd4a6fcc9.png">

* 実行ボタンをクリック！！
* 実機でビルドに成功できたら（※Android実機でアプリを起動し、プッシュ通知の許可までを行ってください）、[ニフクラ mobile backend](http://mbaas.nifcloud.com/)ダッシュボード上を確認します
* データストアのinstallationクラスにデバイストークンが登録されたことを確認します
![Installation.png](https://qiita-image-store.s3.amazonaws.com/0/72656/f7d3d650-ec2f-6ba2-f114-5fc6a71122fe.png)

## **5. 動作確認！プッシュ通知を送りましょう**
* [ニフクラ mobile backend](http://mbaas.nifcloud.com/)ダッシュボード「プッシュ通知」＞「プッシュ通知」をクリックすると、一覧画面がでます
* 右上の「＋新しいプッシュ通知」をクリックするとここからプッシュ通知を作成できます
![controlpanel_createnewpush.png](https://qiita-image-store.s3.amazonaws.com/0/72656/6b9e62c6-ae20-43f5-bcab-27f9738f7374.png)

* 操作は以上です！ちゃんと届きましたか？
![AndroidNotifications.png](https://qiita-image-store.s3.amazonaws.com/0/72656/d29eb5c5-0813-5fbe-4d8e-c78883936a9c.png)

# 参考
* [ニフクラ mobile backendのAndroid(Java SDK)用ドキュメント（プッシュ通知）](http://mbaas.nifcloud.com/doc/current/push/basic_usage_android.html)