ParseのAndroid SDK 1.7.1によるPush通知

1.7.1より、使い方が多少変わり、盛大にはまったので、その辺りをメインに記述します。
要点をまとめると以下のとおりです。

- 1.7.1以前は、通知タップ後の処理について気にする必要が無かった
- 従来の実装のまま通知をタップするとActivityNotFoundExceptionが発生する

今回説明する方法では、Parseの使い方を調べている際にGoogle先生が教えてくれた

- PushService.setDefaultPushCallbackを使う〜
- BroadcastReceiverを拡張してonReceiveする〜

は使いません。というか、使わなくなりました。

## Pushの変更について
これまで使用していたPushService.setDefaultPushCallbackメソッドが非推奨になりました。
(厳密にはPushService.setDefaultPushCallbackの機能はParsePushBroadcastReceiverに移されました)
これに伴い、実装方法をいくつか変える必要があります。

また、非推奨になったメソッドが知りたい場合は、公式のAPIリファレンスを参照してください。
<a href="http://www.parse.com/docs/android/api/deprecated-list.html">Deprecated List</a>

## 新しいバージョンでPush通知を送る方法について

まず、これまで使用していたPushService.setDefaultPushCallbackが使えなくなったため、通知をタップした後のActivity遷移が出来なくなっています。
これまでParseを使用した場合、通知センターへの表示やタップ後の遷移も宜しくやってくれたそうですが、それらを実装する必要があります(今でも通知センターへの表示はParseがやってくれます)。

基本的には下記のParseの公開しているスタートアップに従えば大丈夫です。公式の内容でも、更新されていない情報も見受けられたので、見るページには注意したほうが良さそうですが…
<a href="https://parse.com/tutorials/android-push-notifications"> Android Push Notifications | Parse</a>


スタートアップの手順を進めた後、通知タップによるActivityの遷移についてです。
次の3つを行う必要があります。

- ParsePushBroadcastReceiverを拡張した独自レシーバの実装
- マニフェストファイルに該当するレシーバを登録
- 独自レシーバで通知をキャッチした後に、目的のActivityに対してIntentを発行
 
### ParsePushBroadcastReceiverを拡張した独自レシーバの実装

```java:UserCustomReceiver.java
public class UserCustomReceiver extends ParsePushBroadcastReceiver{

	//onPushOpenは通知をタップした時に呼ばれる
    @Override
    protected void onPushOpen(Context context, Intent intent) {

        Log.d("UserCustomReceiver","onPushOpen");

        Bundle bundle = intent.getExtras();
        
        //遷移先のActivityを設定
        Intent i = new Intent(context,MainActivity.class);
        i.putExtras(bundle);
        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        context.startActivity(i);
    }
}
```


###マニフェストファイルに該当するレシーバを登録
AndroidManifest.xml

	<!--ユーザ定義のレシーバ-->
        <receiver android:name=".UserCustomReceiver" android:exported="false">
            <intent-filter>
                <action android:name="com.parse.push.intent.RECEIVE" />
                <action android:name="com.parse.push.intent.OPEN" />
                <action android:name="com.parse.push.intent.DELETE" />
            </intent-filter>
        </receiver>

通知タップ後の遷移Intentについては、ParsePushBroadcastReceiverの拡張に書いてある通りです。
onPushOpenをオーバーライドすることで、通知のタップをハンドリングすることが出来ます。この他のメソッドについては以下で。

## ParsePushBroadcastReceiverについて
このクラスを拡張することで、ParseからのPush通知をハンドルするメソッドをオーバーライドすることが出来ます。ハンドルしたいタイミングによって、いくつかメソッドが用意されているので、アプリに合わせて必要なメソッドをオーバーライドします。
それぞれのタイミングはざっくり次のようになっています。

- getNotification … 通知の表示そのものをカスタマイズしたい場合
- onPushReceive … 通知を受信した
- onPushOpen … 通知をタップした
- onPushDismiss … 通知を解放した

通知をタップしてアプリを起動する場合、onPushOpenをオーバーライドすればよいでしょう。
一つ注意すべき点は、1.7.1以前に使用していたonReceiveを実装すると、それ以外のメソッドが呼ばれなくなるようです。

--

細かい所まで確認していないので、ミスがあればコメントお願いします。
また、関連する質問がstack overflowに存在するので、そちらの方が参考になるかもしれません。

<a href="http://stackoverflow.com/questions/26154855/exception-when-opening-parse-push-notification">
android - Exception when opening Parse push notification - Stack Overflow
</a>
以上です。


-- 

# 2015-09-30 追記

この他にハマったところやTipsなど

### 通知が2つ届く
* AndroidManifestに余計なreceiverが登録されていたため。
ParsePushBroadcastReceiverをExtendsしたクラス以外のreceiverは、com.parse.ParseBroadcastReceiverとcom.parse.GcmBroadcastReceiverの2つだけでよい

### 本番とSTGなどで分けたい
* GradleのproductFlavorsで開発用と本番用でapplicationIdを分けていて、Parseの方も開発・本番で分けて運用しているとします。この場合に開発用でPushのテスト行なう場合、AndroidManifestファイルのapplicationIdに依存する部分を書き換える必要があり、非常に面倒です。
具体的にはC2D_MESSAGEのpermissionだとかreceiverのcategoryのapplicationIdとかです。

```AndroidManifest.xml
<permission android:protectionLevel="signature"
    android:name="com.parse.starter.permission.C2D_MESSAGE" />	<!--ここ！-->
<uses-permission android:name="com.parse.starter.permission.C2D_MESSAGE" />	<!--ここ！-->


<receiver android:name="com.parse.GcmBroadcastReceiver"
    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.parse.starter" />	<!--ここ！-->
  </intent-filter>
</receiver>
```

公式のガイドにも載ってる”ここを変えろ(com.parse.starter)”の部分ですね。
これをManifest ManagerのPlaceholder機能を使って変数 ${applicationId} に置き換えます。

公式 → http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger

> An implicit placeholder ${applicationId} resolution will be automatically provided out of the box with the build.gradle applicationId value. 
> 
> Examples :
> 
> ```
> <activity android:name=".Main">
>       <intent-filter>
>        <action android:name="${applicationId}.foo">
>           </action>
>   </intent-filter>
> </activity>
> ```

公式のサンプル見れば使い方がわかると思いますが、先程のParseの設定部分を実際に置き換えると下のような感じになります。

```AndroidManifest.xml
<permission
	android:protectionLevel="signature"
	android:name="${applicationId}.permission.C2D_MESSAGE"/>
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>

<receiver
	android:name="com.parse.GcmBroadcastReceiver"
	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="${applicationId}"/>
	</intent-filter>
</receiver>

```

--

# 追記：2015-10-05

## Android 5以降の端末で通知アイコンが表示されない

Android 5で通知関係の強化・変更があったため、ParsePushBroadcastReceiverクラスのメソッド2つをオーバーライドしないと、Notificationのアイコン(ステータスバーと通知センターのアイコン)が正常に表示されません。

実装するメソッドは下記の2つです。

* getSmallIconId(Context,Intent)
* getLargeIcon(Context,Intent)

上記のメソッドがステータスバーのアイコンを、
下記のメソッドで通知センターに表示されるアイコンを設定します。

また、これらのアイコン表示はAndroid 5未満の端末では上記のメソッドを実装していなくても今までどおり表示されます（これの所為で気付かなかった...


Notificationの変更などの詳細については、こちらの方がまとめてくださっているので参考に

* Android 5.0 LollipopでのNotificationについて - Qiita http://qiita.com/mstssk/items/099a22976dd2dccdd1ea