KiiCloudを使ったPush通知について

#プッシュ通知
##概要
本投稿は「Kiicloudのユーザー間のプッシュ通知（Android）」について書きます．
[前回記事](http://qiita.com/taka4kato/items/f3913a6ffb1847c2ee76)

##github
volleyとkiicloudのライブラリを設定の上、以下のパラメータを設定するとログインユーザーがTARGET_IDに指定したユーザーに対し、プッシュ通知を送信できます。
[サンプルプロジェクト](https://github.com/kato-takashi/kiicloudPushNotification)

```java:ApplicationValue.java
//以下のIDとKEYを設定するとTARGET_IDのuserへプッシュ通知が送れます。
// TODO:APP_IDとAPP_KEYはKiiCloudでアプリケーションを作成した際に発行された値に書き換えてください。
	public static final String APP_ID = "your App ID";
	public static final String APP_KEY = "your App Key";
	// TODO:Google Developers Console Project ID SENDER_IDはGCMの設定を有効にした際に発行された値に書き換えてください。
	public static final String SENDER_ID = "your Project ID";
	// TODO:送信したいtest用のuserのID。
	public static final String TARGET_ID = "TEST USER ID";

```

##プッシュ通知の種類
KiiCloudのプッシュ通知は、GCM、APNS、JPush のアプリケーションサーバを想定した実装モデルを採用しているそうです。
Kii Cloud では、以下の3つのプッシュ通知を利用できます。
[参考](http://documentation.kii.com/ja/guides/android/managing-push-notification/)
<dl>
  <dt>プッシュ通知</dt>
  <dd>**サーバ上変更のプッシュ通知 (Push To App):** 
購読済みの Bucket 内の更新をプッシュ通知として受け取ります。</dd>
  <dd>**ユーザープッシュ通知 (Push to User):** 
購読済みのトピックへのメッセージをプッシュ通知として受け取ります。</dd>
  <dd>**運用者メッセージのプッシュ通知 (Direct Push):** 
開発者ポータルからアプリ開発者がプッシュ通知を送信します。</dd>
</dl>

今回はグループに招待するためにプッシュ通知を利用したいので，ユーザープッシュ通知を使用します．
[参考：ユーザープッシュ通知について](http://documentation.kii.com/ja/starts/cloudsdk/managing-push-notification/push_kiicloud/push-to-user/)

#実装
##プロジェクト，GCMの設定
以下参考は開発者ポータルからのプッシュ通知ですが，設定周りは大体同じなので，1-7を設定します．
[参考：Android (GCM) プッシュ通知設定チュートリアル](http://documentation.kii.com/ja/samples/push-notifications/push-notifications-android/)
Android (GCM) プッシュ通知設定チュートリアル
1. Android アプリの作成
2. Google プロジェクトの作成
3. Google Cloud Messaging の設定
4. Kii Cloud の設定
5. ビルド環境の設定
6. マニフェストの設定
7. プログラムの実装(受信側の設定)

##送信の実装
[参考：Kii Cloudを用いたチャットアプリケーションの開発 [その4] ―メッセージ送受信機能の実装](http://gihyo.jp/dev/serial/01/mbaas/0008)

###トピックの作成
トピックは、プッシュメッセージを送信するためのチャンネルの役割を果たします。あるトピックに興味のあるユーザーは、このトピックを購読することにより、以後このトピックに送信されたプッシュメッセージを受信できます。
[参考：トピックとは](http://documentation.kii.com/ja/starts/cloudsdk/managing-push-notification/push_kiicloud/push-to-user/)

```java:UserActivity.java
//ログイン時のアクティビティーに記述
//....省略
//push通知用のtopicを作成
    private void createPushTopic(final KiiUser user) {
        Log.d("pushTopic", user.getUsername());
        // GCMの設定
        try {
            installGCMRegistrationID();
            Log.d("GCM", "セッティング");
        } catch (Exception e) {
            e.printStackTrace();
        }
        final KiiTopic topic = KiiUser.topic(ApplicationValue.TOPIC_INVITE_NOTIFICATION);
        // Save the topic to Kii Cloud
        topic.save(new KiiTopicCallBack() {
            @Override
            public void onSaveCompleted(int taskId, KiiTopic target, Exception exception) {
　　　　　　　　//////////////////////////                
　　　　　　　　//トピックの ACL を変更する
　　　　　　　　//////////////////////////                
            }
        });

    }

    //GCMの設定
    private void installGCMRegistrationID() throws Exception {
        String registrationId = GCMUtils.register(senderID);
        KiiUser.pushInstallation().install(registrationId);
    }

```

[参考：トピックの作成](http://documentation.kii.com/ja/guides/android/managing-push-notification/push-to-user/creating-topic/)

###トピックのACLを変更する
トピックにプッシュメッセージを送信するための権限を付与します。

```java:UserActivity.java
//上記コードの  //トピックの ACL を変更する部分
//....省略
// Save the topic to Kii Cloud
        topic.save(new KiiTopicCallBack() {
            @Override
            public void onSaveCompleted(int taskId, KiiTopic target, Exception exception) {
                if (exception != null) {
                    // Error handling
                    Log.d("topic err", exception.toString());
                    return;
                }
                KiiACL acl = topic.acl();
                //権限付与
                acl.putACLEntry(new KiiACLEntry(KiiAnyAuthenticatedUser.create(), KiiACL.TopicAction.SEND_MESSAGE_TO_TOPIC, true));
                acl.save(new KiiACLCallBack() {
                    @Override
                    public void onSaveCompleted(int token, KiiACL acl, Exception exception) {
                        if (exception != null) {
                            // Error handling
                            Log.d("topic err acl", exception.toString());
                            return;
                        }
                        KiiPushSubscription subscription = user.pushSubscription();
                        subscription.subscribe(topic, new KiiPushCallBack() {
                            @Override
                            public void onSubscribeCompleted(int taskId, KiiSubscribable target, Exception exception) {
                                if (exception != null) {
                                    // Error handling
                                    Log.d("topic err subscription", exception.toString());
                                    return;
                                }
                            }
                        });
                    }
                });

            }
        });

```
[参考：トピックの ACL を変更する](http://documentation.kii.com/ja/guides/android/managing-push-notification/push-to-user/settings-topic-acl/)

###GCMの準備(Google Cloud Messaging)
[参考：GCMの設定](http://documentation.kii.com/ja/samples/push-notifications/push-notifications-android/configure-gcm/)

```java:MainActivity.java

//senderIDはGoogle Developers Consoleのproject ID
//ログイン後のKiiUserを取得後に記述
//SharedPreferences に GCM から取得した登録 ID を文字列として保存
    private void registerGCM(final KiiUser user) {
        new AsyncTask<Void, Void, String>() {
            @Override
            protected String doInBackground(Void... params) {
                try {
                    // call register
                    String regId = gcm.register(senderID);

                    // install user device
                    boolean development = false;
                    user.pushInstallation(development).install(regId);

                    // if all succeeded, save registration ID to preference.
                    GCMPreference.setRegistrationId(MainActivity.this.getApplicationContext(), regId);
                    Log.d("regId", regId);
                    return null;
                } catch (Exception e) {
                    // Error Handling
                    Log.d("registerGCM", e.toString());
                    return null;
                }
            }
        }.execute();
    }

```


```java:GCMPreference.java

public class GCMPreference {
    private static final String PREFERENCE_NAME = "KiiTest";
    private static final String PROPERTY_REG_ID = "GCMregId";

    public static String getRegistrationId(Context context) {
        SharedPreferences prefs = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
        String registrationId = prefs.getString(PROPERTY_REG_ID, "");
        return registrationId;
    }

    public static void setRegistrationId(Context context, String regId) {
        SharedPreferences prefs = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();
        editor.putString(PROPERTY_REG_ID, regId);
        editor.commit();
    }
}

```


###メッセージの送信

```java:MainActivity.java

//受け取るときにApplicationValue.SEND_MESSAGE_INTENTを使います。
//受信すると"Hello push 受信したよ!!"のToastがでます。
//push通知 sendMessage
    private void sendMessage() {
        final KiiUser target = KiiUser.userWithID(targetID);
        //ユーザーの取得
        target.refresh(new KiiUserCallBack() {
            @Override
            public void onRefreshCompleted(int token, Exception exception) {
                if (exception != null) {
                    // Error handling
                    Log.d("Refresh err", exception.toString());
                    return;
                }

                KiiTopic topic = target.topicOfThisUser(TOPIC_INVITE_NOTIFICATION);
                Log.d("target topic", topic.getName());

                KiiPushMessage.Data data = new KiiPushMessage.Data();
                data.put(ApplicationValue.SEND_MESSAGE_INTENT, "Hello push 受信したよ!!");
                KiiPushMessage message = KiiPushMessage.buildWith(data).build();
                // Send the push message.
                topic.sendMessage(message, new KiiTopicCallBack() {
                    @Override
                    public void onSendMessageCompleted(int taskId, KiiTopic target, KiiPushMessage message, Exception exception) {
                        if (exception != null) {
                            // Error handling
                            Log.d("SendMessage", exception.toString());
                            return;
                        }
                        Log.d("送信完了", "success");
                    }
                });

                // check whether user is disabled.
                boolean disabled = target.disabled();
                if (target.isLinkedWithSocialProvider(KiiSocialNetworkConnector.Provider.FACEBOOK)) {
                    // User is linked to the Facebook account.
                }
                if (target.isLinkedWithSocialProvider(KiiSocialNetworkConnector.Provider.TWITTER)) {
                    // User is linked to the Twitter account.
                }

            }
        });
    }

```

##受信の実装
###onReceiveの実装
BroadcastReceiverのonReceive() を実装します。
以下ではswich文でプッシュ通知の種類を分岐してありますが、本投稿ではユーザー間のプッシュ通知なので「PUSH_TO_USER」の部分しか動作しません。
[参考：メッセージを受信する](http://documentation.kii.com/ja/guides/android/managing-push-notification/push-to-user/receiving-messages/)

```java:KiiPushBroadcastReceiver.java
public class KiiPushBroadcastReceiver extends BroadcastReceiver {
        public KiiPushBroadcastReceiver(){
    }

    @Override
    public void onReceive(final Context context, final Intent intent) {
        Log.d("onReceive", "received push message");
        if (KiiUser.getCurrentUser() == null) {
            // ログイン中でない場合は何もしない
            Log.d("onReceive", "user null return");
            return;
        }
        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
        String messageType = gcm.getMessageType(intent);
        if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
            final Bundle extras = intent.getExtras();
            final ReceivedMessage message = PushMessageBundleHelper.parse(extras);
            PushMessageBundleHelper.MessageType type = message.pushMessageType();
            switch (type) {
                case DIRECT_PUSH:
                    Toast.makeText(context, "DIRECT_PUSH", Toast.LENGTH_LONG).show();
                    Log.d("onReceive", "DIRECT_PUSH");
                    return;
                case PUSH_TO_APP:
                    Log.d("onReceive", "PUSH_TO_APP");
                    Toast.makeText(context, "PUSH_TO_APP", Toast.LENGTH_LONG).show();
                    try {
                        Log.d("onReceive", "PUSH_TO_USER");
                    } catch (Exception e) {
                    }
                    break;
                case PUSH_TO_USER:
                    // ユーザー間のプッシュ通知
                    Log.d("onReceive", "received PUSH_TO_USER");
                    String getMessage = extras.getString(ApplicationValue.SEND_MESSAGE_INTENT);
                    sendBroadcast(context, ApplicationValue.ACTION_RECEIVED_INTENT, getMessage);
                    Toast.makeText(context, "PUSH_TO_USER: " + getMessage, Toast.LENGTH_LONG).show();
                    break;
            }
        }
    }

    /**
     * プッシュ通知を受信したことをBroadcast Intentを使ってActivityに通知します。
     *
     */
    private void sendBroadcast(Context context, String action, String message) {
        Intent intent = new Intent(action);
        intent.putExtra(ApplicationValue.GET_MESSAGE_INTENT, message);
        context.sendBroadcast(intent);
    }

}


```

###Activityで取得
受け取れた場合、「push通知：hogehoge」みたいなToastが出ます。

```java:MainActivity.java
//KiiPushBroadcastReceiverから受信したメッセージをActivityで取得
    private final BroadcastReceiver handleNewReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String message = intent.getStringExtra(ApplicationValue.GET_MESSAGE_INTENT);
            Toast.makeText(context, "push通知："+ message, Toast.LENGTH_LONG).show();
            Log.d("push通知",  message);
        }
    };

    @Override
    public void onResume() {
        super.onResume();
        registerReceiver(this.handleNewReceiver, new IntentFilter(ApplicationValue.ACTION_RECEIVED_INTENT));
    }

    @Override
    public void onPause() {
        super.onPause();
        unregisterReceiver(this.handleNewReceiver);
    }


```

###Manifestの設定
receiverが必要です。

```xml:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.luky_ponies.pushtest" >

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <permission android:name="com.luky_ponies.pushtest.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    <uses-permission android:name="com.luky_ponies.pushtest.permission.C2D_MESSAGE" />

    <application
        android:name=".VolleyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".UserActivity"
            android:label="UserActivity"></activity>

        <!-- *** add following lines *** -->
        <receiver android:name="com.luky_ponies.pushtest.KiiPushBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="com.luky_ponies" />
            </intent-filter>
        </receiver>

    </application>

</manifest>


```


#送信の検証
##注意
メッセージの送信を検証するためには，実機を2台使うのが理想的です．genymotionを用いた実験では実機→エミュレータでは送信と受信は可能ですが，**エミュレータ→実機では送信ができないこと**が注意です．
エミュレータではregisterGCMのメソッド部分でエラーが出て登録ができていないようです．
##メッセージの送信機能でグループに追加
このプッシュ通知にuserIDなどをのせて通知を行えば、グループのメンバーの追加ができるかと思います。
[前回記事](http://qiita.com/taka4kato/items/f3913a6ffb1847c2ee76)