[Android] NCMBプッシュ通知を実装する際の注意点

NCMBのプッシュ通知を実装しました。
公式チュートリアルの情報量が少なく、ハマりポイントが多いです。
備忘録としてハマった点をざっくり挙げます。
導入手順はリンクを参照してください。

#ライブラリの導入
今回はNCMBの導入は済んでいるものとして、プッシュ機能に必要な情報だけ記述しようと思います。NCMB自体の導入方法は[公式チュートリアル](http://mb.cloud.nifty.com/doc/current/introduction/quickstart_android.html)を参照してください。
##Firebaseとの連携
新規にプッシュ通知機能を実装する場合はGCM(GoogleCloudMessaging)の新しいバージョンであるFCM(FirebaseCloudMessaging)を利用することになります。Firebase導入手順は、
1. [Firebase Console](https://console.firebase.google.com/)にて自分のプロジェクトを登録
2. Firebaseの[設定ファイルをダウンロード](https://firebase.google.com/docs/android/setup)
3. プロジェクトにimport
という感じになります。
###ハマりポイント
+ 設定ファイルは`google_service.json`というファイルになるのですが、この**ファイルの格納位置はapp/直下**でなければなりません。格納した後は`build.gradle(Project)`のclasspathで指定してあげましょう。
-階層
![スクリーンショット 2016-12-18 15.45.26.png](https://qiita-image-store.s3.amazonaws.com/0/110584/7bad4b5d-92a4-c4d5-385b-934ecdd0d5f3.png)
-`build.gradle(Project)`
![スクリーンショット 2016-12-18 15.51.57.png](https://qiita-image-store.s3.amazonaws.com/0/110584/d4d120ed-89f4-6386-3366-b1dba9c9623c.png)

+ `build.gradle`でFirebaseをコンパイルさせるのですが、**`play-services`と`firebase`のバージョンを合わせないとエラーを吐きます**。
![スクリーンショット 2016-12-18 16.02.57.png](https://qiita-image-store.s3.amazonaws.com/0/110584/d78dcb92-fb38-2970-91ad-ca61ff2293b5.png)
これでFirebaseの導入は完了です。


##Manifestの定義
次に、プッシュ通知を受け取るためにManifestを編集しましょう。プッシュ通知に対するパーミッションとmeta情報の追加、レシーバーとサービスの指定をしています。
ざっくりしか挙げないので詳細は[公式チュートリアル(Push通知)](http://mb.cloud.nifty.com/doc/current/push/basic_usage_android.html)を参照してください。

```xml:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="プロジェクトパッケージ">

    <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
        android:name="com.nifty.cloud.mb.core.NCMBApplicationController"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.AppCompat.Light.DarkActionBar">

        <!-- プッシュ通知タップ時に起動するActivityの設定(必須) -->
        <meta-data android:name="openPushStartActivity" android:value="任意のActivity"/>
        <!-- 通知エリアに表示されるアイコンの設定(option) -->
        <meta-data android:name="smallIcon" android:resource="任意の画像データ"/>
        <!-- 通知エリアにプッシュ通知を複数表示する設定　0:最新のみ表示 , 1:複数表示(option) -->
        <meta-data android:name="notificationOverlap" android:value="1"/>

        <!--PushNotificationReceiver-->
        <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>
        <!--PushNotificationService-->
        <service
            android:name="com.nifty.cloud.mb.core.NCMBGcmListenerService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
            </intent-filter>
        </service>

        <activity
            android:name=".PushTestActivity"
            android:label="@string/app_name">
        </activity>
    </application>
</manifest>
```

###ハマりポイント
+ Firebase連携のハマりポイントに挙げているので大丈夫だとは思いますが、ここで指定しているレシーバーとサービスは`play-services`内のクラスを継承しているので、`build.gradle`できちんと`play-services`もしくは`play-services-gcm`をコンパイルしていないと動作してくれません。僕はここでかなりハマりました。


#コーディング
コードではFCMに端末のIDを登録すればいいだけです。[サンプルコード](https://github.com/ncmbadmin/android_push_demo)のほぼ丸写しです。SENDER IDは[FCM連携に必要な設定](http://mb.cloud.nifty.com/doc/current/tutorial/push_setup_android.html)を参照してください。

```java:PushTestActivity.java
public class PushTestActivity extends Activity {

    //NCMB DBのappKeyとclientKey
    private final static String APP_KEY = "API KEY";
    private final static String CLIENT_KEY = "CLIENT KEY";
    //Firebase senderID
    private final static String SENDER_ID = "SENDER ID";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_push_test);

        //NCMBの初期化
        NCMB.initialize(this.getApplicationContext(), APP_KEY, CLIENT_KEY);

        //FCMに端末IDを登録
        final NCMBInstallation installation = NCMBInstallation.getCurrentInstallation();
        installation.getRegistrationIdInBackground(SENDER_ID, new DoneCallback() {
            @Override
            public void done(NCMBException e) {
                if (e == null) {
                    installation.saveInBackground(new DoneCallback() {
                        @Override
                        public void done(NCMBException e) {
                            if (e == null) {
                                //保存成功
                                Log.d(CLASS_TAG, "登録成功");
                            } else if (NCMBException.DUPLICATE_VALUE.equals(e.getCode())) {
                                updateInstallation(installation);
                            } else {
                                Log.d(CLASS_TAG, "保存失敗");
                            }
                        }
                    });
                } else {
                    Log.d(CLASS_TAG, "取得失敗");
                }
            }
        });
    }

    public static void updateInstallation(final NCMBInstallation installation) {
        NCMBQuery<NCMBInstallation> query = NCMBInstallation.getQuery();
        query.whereEqualTo("deviceToken", installation.getDeviceToken());
        query.findInBackground(new FindCallback<NCMBInstallation>() {
            @Override
            public void done(List<NCMBInstallation> list, NCMBException e) {
                installation.setObjectId(list.get(0).getObjectId());
                installation.saveInBackground();
            }
        });
    }
}
```

DB上のInstallationクラスにデバイストークンが登録されていることを確認したら、ダッシュボード上でプッシュ通知の送信をしてみましょう。多分届くはずです。送信方法は[公式チュートリアル(Push通知)](http://mb.cloud.nifty.com/doc/current/push/basic_usage_android.html)の下の方に書いてあります。


#まとめ
Firebaseと連携してNCMB PushNotificationを実装する文献は見当たらなかったのまとめてみました。全部説明すると長くなるのでリンクに頼っているところが大きいですが、注意すべき点は挙げられたと思います。
おかしな所や追加すべき点がありましたらご教授願います。