アプリ内で通知からスタックした複数画面を開くときに裏のActivityを残す

自身のアプリを起動中に、プッシュ通知などでnotificationを表示したとき、何も考えずに[`TaskStackBuilder`](https://developer.android.com/reference/android/support/v4/app/TaskStackBuilder.html)を使ってスタックした複数の`Activity`を起動すると、それまでに開いていた画面がクリアされてしまいます。例えば、

ActivityA | ActivityB | ActivityC

と画面遷移していたときに通知が来て、 ActivityDとActivityEを起動したいとき、

ActivityD | ActivityE | ActivityA | ActivityB | ActivityC

となってほしいところが　

ActivityD | ActivityE

のみになってしまい、適切にupアクションや`parentActivity`を設定していないと全ての画面が破棄されてしまいます。

アプリを開いていて、メッセージ機能などリアルタイムに別画面へ遷移をしたら下の画面が消えてしまうと困る場合の対処です。

## ポイントは[`TaskStackBuilder`](https://developer.android.com/reference/android/support/v4/app/TaskStackBuilder.html)の[`getIntents`](https://developer.android.com/reference/android/support/v4/app/TaskStackBuilder.html#getIntents())

通常のこのような実装をすると思います。

```java
TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context.getApplicationContext());
NotificationCompat.Builder notificationBuilder = new　NotificationCompat.Builder(getApplicationContext());
notificationBuilder.setContentTitle("normal").setContentText("nomal");
notificationBuilder.setAutoCancel(true);

Intent intent = new Intent(context, NextActivity.class);
taskStackBuilder.addNextIntent(intent);
PendingIntent pi = PendingIntent.getActivities(context, 0, taskStackBuilder.getIntents(), PendingIntent.FLAG_CANCEL_CURRENT);
notificationBuilder.setFullScreenIntent(pi, true);
notificationBuilder.setContentIntent(pi);

NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
```

この場合だと下のActivityは全て破棄されてしまいます。
`TaskStackBuilder`の`getIntent`の実装は以下のようになっています。

```java:TaskStackBuilder.java#getIntents
/**
 * Return an array containing the intents added to this builder. The intent at the
 * root of the task stack will appear as the first item in the array and the
 * intent at the top of the stack will appear as the last item.
 *
 * @return An array containing the intents added to this builder.
 */
public Intent[] getIntents() {
    Intent[] intents = new Intent[mIntents.size()];
    if (intents.length == 0) return intents;

    intents[0] = new Intent(mIntents.get(0)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
           IntentCompat.FLAG_ACTIVITY_CLEAR_TASK |
           IntentCompat.FLAG_ACTIVITY_TASK_ON_HOME);
    for (int i = 1; i < intents.length; i++) {
        intents[i] = new Intent(mIntents.get(i));
    }
    return intents;
}
```

つまり`getIntents`すると`TaskStackBuilder`に積まれた一番下の`Intent`の起動フラグが`FLAG_ACTIVITY_CLEAR_TASK`になってしまいます。そのせいで通知から起動すると下の画面が全て破棄されます。よってここを変えれば下の画面を残したまま通知から別な画面を起動できます。

```java
NotificationCompat.Builder notificationBuilder = new　NotificationCompat.Builder(getApplicationContext());
notificationBuilder.setContentTitle("normal").setContentText("nomal");
notificationBuilder.setAutoCancel(true);

Intent intent = new Intent(context, NextActivity.class);
Intent[] intents = new Intent[1];
intents[0] = intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//PendingIntent pi = PendingIntent.getActivities(context, 0, taskStackBuilder.getIntents(), PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent pi = PendingIntent.getActivities(context, 0, intents, PendingIntent.FLAG_CANCEL_CURRENT);
notificationBuilder.setFullScreenIntent(pi, true);
notificationBuilder.setContentIntent(pi);

NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
```

やり方はこれ以外にもあると思いますが、重要なのは
**`getIntents`から取得した`Intent`配列で０番目のフラグを変えればいい（もしくは自分で配列を作って渡す）**ということです。

以上です。

## sample

サンプルを作ったので触ってみると動きがわかります。(名前ミスったかな…

[PendingIntentCustomSample](https://github.com/kaelaela/PendingIntentCustomSample)

---

ActivityスタックやIntentのフラグについてよくわからない方はyanzmさんの記事がわかりやすいと思います。

[Android　Activity, Task, Stack, Launch mode | Y.A.M の 雑記帳](http://y-anz-m.blogspot.jp/2010/08/android_10.html)

Notificationについて詳しい記事はYahoo!の以下の記事がわかりやすいと思います。

[LollipopでのNotification | Yahoo! Tech blog](https://techblog.yahoo.co.jp/android/2014-lollipop-notification/)