Multi-Window上で共有機能を使うとどうなるのか実験


いまのところデベロッパー側に好意的に受け入れられている気配を感じないMulti-Window機能ですが、自分がプライベート開発しているアプリでは可能性を感じたので、そのあたりで調べたことを書きます。あと5/25（水）に行われた[potatotips #29](http://potatotips.connpass.com/event/30510/)の発表内容＋α版です。

対象アプリ：[AAびゅーわ - AA表示アプリ - Google Play の Android アプリ](https://play.google.com/store/apps/details?id=jp.yamaglo.aaviewer&hl=ja)

ついでにここで主張したいのですが、AAの表示がズレる環境のまま資料や記事に公開する行為は、思った以上にAA職人の精神にダメージがあります（個人差があります）。注意してください。

## 前置き：やれなかったＤ＆Ｄ機能

本来はMulti-Windowに合わせて実装された「アプリ間のドラッグ・アンド・ドロップ（D&D）機能」を使い、テキストをドロップすることでビューワを更新させたかったのですが、ドラッグする側（ブラウザだったり、ソフトキーボードアプリだったり）がドラッグ機能を実装してくれない限りは意味がないことに気がついたので、今回の実装は諦めることになりました。

![Screenshot_20160524-114811_2.png](https://qiita-image-store.s3.amazonaws.com/0/4560/909c9cdc-346c-606e-d95b-436660df8496.png "Screenshot_20160524-114811_2.png")

ただこの画面の構成上、ブラウザ側の共有機能でビューワの画面を更新できるんじゃないかと思って、その辺りの動きを見ることにしました。

なお、確認にはAndroid N Preview 3公開時点での最新のAndroid Emulatorで行っております。

![Screenshot_20160524-114811_3.png](https://qiita-image-store.s3.amazonaws.com/0/4560/aaa0d89a-2302-d926-a5b5-b27c2ac335b9.png "Screenshot_20160524-114811_3.png")

## 本題

### Multi-Window上で「共有」をすると何が起こるのか

というわけで、とりあえず現在のアプリの構成のまま（`android:resizeableActivity=true`は実装済み）、ブラウザの共有機能からビューワを呼んでみました。

![Screenshot_20160523-181356.png](https://qiita-image-store.s3.amazonaws.com/0/4560/cc304d0c-311b-cc05-b3ba-962a8662dd5b.png "Screenshot_20160523-181356.png")

↓結果

![Screenshot_20160523-181409.png](https://qiita-image-store.s3.amazonaws.com/0/4560/8349f42e-93da-4f07-3392-7bf0f679c724.png "Screenshot_20160523-181409.png")

まあ、予想はしてましたがブラウザ側のWindowでビューワが起動してしまいました。何も考えずに呼び出すと、もう一方のWindowの状態は関係なく新規タスクとしてアプリが起動してしまうようです。

では、呼ばれる側（ビューワ）が`launchMode=singleTask`の場合、どうなるでしょうか。

```xml:app/AndroidManifest.xml
<activity
    android:name="jp.yamaglo.aaviewer.MainActivity"
    android:label="@string/app_name"
    android:launchMode="singleTask"
    >
```

![Screenshot_20160524-120737.png](https://qiita-image-store.s3.amazonaws.com/0/4560/b463c0a3-5218-2422-efe3-438e376237c2.png "Screenshot_20160524-120737.png")

↓結果
![Screenshot_20160524-120747.png](https://qiita-image-store.s3.amazonaws.com/0/4560/098817f8-3a42-a056-9a1b-1ffbbbe5c129.png "Screenshot_20160524-120747.png")

更新されました！
`launchMode=singleTask`の場合は新規でタスクを作成せず、現在表示中のビューワの`onNewIntent()`が呼ばれていました。

### `Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT` を付けて呼び出した時の動作

とりあえず`launchMode=singleTask`であれば、「もう一方のWindowでアプリを開いている場合」は更新されることがわかりました。しかし「もう一方のWindowでアプリを開いていない場合」では、呼び出した側のWindowでアプリが立ち上がってしまいます。

となると、「共有先は必ず片方のWindowで開きたい」場合はどうすればいいのでしょうか。

ここで公式のドキュメントを見ると、以下のことが書かれていました。

[マルチ ウィンドウのサポート | Android Developers](https://developer.android.com/preview/features/multi-window.html)

> 新しいアクティビティを起動するときに、可能であれば、新しいアクティビティを現在のアクティビティの隣に表示する必用があるかどうかをシステムに示すことができます。 そうするには、フラグ Intent.FLAG_ACTIVITY_LAUNCH_TO_ADJACENT を使用します。 このフラグを渡すと、次の動作がリクエストされます。

ということで、 `Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT` （公式には`_TO_`が入っているが、誤字）を使って呼び出してみました。

```java:(testApplication)/MainActivity.java
        Button button = (Button)findViewById(R.id.button_launch);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_SEND);
                intent.setType("text/plain");
                intent.putExtra(Intent.EXTRA_TEXT, "text");
                intent.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
                startActivity(intent);
            }
        });
```

で、結果は以下のようになりました。

|  | `lanchMode=Default`| `lanchMode=singleTask`|
|:--|:--|:--|
| 片方のWindowで表示中 | 呼び出し側で立ち上がる | 開いているアプリの`onNewIntent()`が呼ばれる |
| 片方のWindowで非表示 | 呼び出し側で立ち上がる | 片方のWindowでアプリが立ち上がる |

と、わざわざ表にするまでもなかったですが、`launchMode=singleTask`のアプリであれば、もう片方のWindowでアプリが立ち上がってくれるようです。
なぜsingleTaskである必要があるのかは、よくわかりません……。

いちおう先ほどのドキュメントを読む限り、`ActivityOptions.setLaunchBounds()`を使って何かしら対応できそうなことが書かれていますが、何も変化はありませんでした。
なんとなくですが、ドキュメントのサポートを見てもMulti-Windowモードでの共有動作についてはあまり考慮されていないのかもしれません。

### 補足：FloatingActionModeでの呼び出しについて

ついでにAndroid 6.0から実装されたFloatingActionModeからでも呼び出せるようにしましたが、こちらの場合では **launchModeの設定が如何にせよ、必ず呼び出し側のWindowでアプリが「新規に」立ち上がります**。

![Screenshot_20160523-184549(2).png](https://qiita-image-store.s3.amazonaws.com/0/4560/d79e1808-2e97-cf99-d88c-7a200fc02921.png "Screenshot_20160523-184549(2).png")

↓結果（テキストが読み込めてないんですが、こちらの実装ミスです）

![Screenshot_20160523-184602.png](https://qiita-image-store.s3.amazonaws.com/0/4560/b5b1faf8-6876-4662-e20e-3f843110e6ed.png "Screenshot_20160523-184602.png")

これはAndrodi6.0の頃からlaunchModeがどんな設定だろうと新タスクとして起動されてしまうので、バグではなく仕様です（この動きがいいかどうかは置いといて……）。

## まとめ

- 呼ばれるアプリ（Activity）のlaunchModeがsingleTaskの場合、表示中のアプリを更新（`onNewIntent()`の呼び出し）が行われる
- `Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT`付きのIntent呼び出しの場合、launchModeがsingleTaskなら片方のWindowでアプリが立ち上がる
- launchModeがデフォルトだと、必ず呼び出し元のWindowでアプリが立ち上がる
- Window間の連携は、一方のアプリだけ対応してもダメっぽいので辛い

## 参考

- [マルチ ウィンドウのサポート | Android Developers](https://developer.android.com/preview/features/multi-window.html)