Xamarin.Forms側からAndroidの通知を表示し通知がタップされたらForms側で何かする方法

タイトルが長いｗ

## シナリオ

1. Xamarin.Forms 製のアプリのとある画面(Page)から、Androidの通知領域（ステータスバー）に通知を表示させる
2. その通知がタップされたら、新しい画面(Page)を開く(など、任意の Xamarin.Forms 依存の処理を行う)

## 方法

だらだら説明しようと思ったけど、面倒になったので Github にサンプルを上げました。

* [amay077/NotificationSampleXamarinForms: Xamarin.Forms から Android の通知を表示して、通知からの呼び出しも行うサンプル](https://github.com/amay077/NotificationSampleXamarinForms)

## ポイント

### 通知はネイティブ固有の機能です

Android の通知は Intent などネイティブの概念と深く結びついており、Forms 側で共通化することは不可能（うまく隠蔽したライブラリはあるかも、ご存じの方教えてください）、つまりネイティブ側に処理をさせる必要がある。

その為に、このサンプルでは [``MessagingCenter`` を使っている](https://github.com/amay077/NotificationSampleXamarinForms/blob/cff00d514b7e1096966e8828927dbf75567b335d/NotificationSample/MainPage.xaml.cs#L18)。ここは DI でやってもよい。

### 通知を表示するには？

[この辺](https://github.com/amay077/NotificationSampleXamarinForms/blob/cff00d514b7e1096966e8828927dbf75567b335d/Droid/MainActivity.cs#L55)。

* [AndroidでNotificationをまた実装するときのメモ - Qiita](http://qiita.com/roga7zl/items/4c9e1b62db1b427a9226)

を参考にさせてもらったけど、通知関連は Android OS のバージョンアップで仕様/APIが変わりやすいので要注意。

### 通知がタップされた時の処理は？

[ココらへん](https://github.com/amay077/NotificationSampleXamarinForms/blob/cff00d514b7e1096966e8828927dbf75567b335d/Droid/MainActivity.cs#L61-L72)。

通知がタップされた時に送信される ``Intent`` を設定しておく。
ここでは、 このアプリのエントリポイントである ``MainActivity`` を再度呼び出させるようにしている。

この時、 ``SetFlags(ActivityFlags.SingleTop)`` を忘れないこと。
これを付けないと、通知をタップした時に、画面(``MainAcitivity``) がもう一つ起動してしまう。

このあたりで戦うには、次のようなネイティブの知識が必要になる。

* [タスクとバックスタック / Android Developers](https://developer.android.com/guide/components/tasks-and-back-stack.html?hl=ja)
* [Y.A.M の 雑記帳: Android　launchMode の違い](http://y-anz-m.blogspot.jp/2011/02/androidlauchmode.html)
* [launchModeをsingleTopにしておくと何が起こるか - woshidan's blog](http://woshidan.hatenablog.com/entry/2016/01/18/083000)
* [IntentのCategoryとExtraとFlagの一覧表を作ってみたよ - hyoromoのブログ](http://hyoromo.hatenablog.com/entry/20091003/1254590170)

### 通知から呼び出された事をどうやって判別する？

まず、通知がタップされた時、アプリ(正確にはAcitivity)が **必ずしも起動しているとは限らない事** を理解しなければならない。

表題の答えは、``SingleTop`` を使っている場合、

1. アプリが起動中の場合、タップが通知された事は [``MainActivity.OnNewIntent`` が呼び出されることで判る](https://github.com/amay077/NotificationSampleXamarinForms/blob/cff00d514b7e1096966e8828927dbf75567b335d/Droid/MainActivity.cs#L81)
2. アプリが起動してない場合、タップされるとアプリが起動し、通知からの起動かどうかは [``MainActivity.OnCreate`` で ``Intent.Data`` にデータが入っていることで判る](https://github.com/amay077/NotificationSampleXamarinForms/blob/cff00d514b7e1096966e8828927dbf75567b335d/Droid/MainActivity.cs#L40) ※ ``if (Intent?.Data != null) {`` とか。

の２つとなる。こちらも例えば次のようなネイティブの知識が必要。

* [SINGLE_TOPで起動したActivityでgetIntentしてもセットした情報が取り出せない - Qiita](http://qiita.com/hishida/items/a4e025c4f0a4f5369494)

### 通知をネイティブ側で検知できたら、次はどうすれば？

アプリ起動中に ``OnNewIntent`` で検知した場合は、``OnCreate`` で生成された [Xamarin.Forms 側の ``App`` クラスにアクセスできるはず](https://github.com/amay077/NotificationSampleXamarinForms/blob/cff00d514b7e1096966e8828927dbf75567b335d/Droid/MainActivity.cs#L49) なので、さらに ``App`` クラスに「[現在のPageが取得できるような細工](https://github.com/amay077/NotificationSampleXamarinForms/blob/cff00d514b7e1096966e8828927dbf75567b335d/NotificationSample/App.xaml.cs#L27)」をしておくことで、現在のページから ``PushAsync`` などが使える。

通知タップでアプリが起動された場合は、前述の [``App`` クラスに何らかのフラグを持たせ、それを用いて、Xamarin.Forms 側の起動時の挙動を変える](https://github.com/amay077/NotificationSampleXamarinForms/blob/cff00d514b7e1096966e8828927dbf75567b335d/NotificationSample/App.xaml.cs#L16)。

起動時にナビゲーションの奥の方の画面を開くような要件は実装が面倒だが、 Prism.Forms の DeepLinking を使うと楽になれるかもしれない。

* [Prism for Xamarin.Forms入門　NavigationService　Quickstart - nuits.jp blog](http://www.nuits.jp/entry/2016/08/21/161324)

## 実際の動き

github のサンプルを動かすと、下図のようになります。

### アプリ起動中に通知をタップした場合

![](http://blog.amay077.net/assets/images/posts/show_and_reaction_notification_with_Xamarin_forms_01.png)

### アプリが起動してない状態で、通知をタップした場合

![](http://blog.amay077.net/assets/images/posts/show_and_reaction_notification_with_Xamarin_forms_02.png)

## まとめ

結果的にだらだら説明してしまった気がするけどまとめ。

* 通知はネイティブ固有機能だから Xamarin.Forms "だけ" ではムリー
* 通知のタップで自アプリを反応させるには ``ActivityFlags.SingleTop`` を使うとよいぞ
* アプリが起動してない時に通知をタップされた時の仕様を考える必要があるぞ

次は iOS・・・。