Android N で注意すること その3 (Multi Window その1)

# Overview

3/9(US時間) に、Android N Developer Previewが出ました。

と、同時に developer.android.com において、behavior change が出ましたので、少しずつ読み解いていきたいと思います。

前回は、

* [Android Nで注意すること その1 : 暗黙的なintentの挙動変更](http://qiita.com/hidenorly/items/baf2735701e472569c07)
* [Android Nで注意すること その2 : NDKを使っているアプリ](http://qiita.com/hidenorly/items/f652e4fde373d31ddd9f)

# マルチウインドウ (multi window) の 説明

http://developer.android.com/preview/api-overview.html#multi-window_support

結論から書きますと、

* Appは、activityが受け付けられるsize, resizableかどうかを宣言できます
 * full screen mode onlyの宣言もできます。
* drag-and-dropをサポートすることができます (tablet/larger device only)
* Phone/Tabletは、multi-window (split screen, cascade)
* Android TVは、Picture-in-Picture

>Android N, we're introducing a new and much-requested multitasking feature into the platform — multi-window support.

抄訳: Android Nでは、新規に、多くリクエストのあったマルチタスクfeatureとしてのmulti windowサポートを導入します。

> Users can now pop open two apps on the screen at once.

抄訳: ユーザは、その画面上に同時に、２つのアプリをpop openできるようになります。

> On phones and tablets running Android N, users can run two apps side-by-side or one-above-the-other in splitscreen mode. Users can resize the apps by dragging the divider between them.

抄訳: Nが動作するPhone/Tabletでは、split-screenモードで、ユーザーは、２つのアプリを、side-by-side、または、１つのアプリの上(cascade)で動かすことができます。

> On Android TV devices, apps can put themselves in picture-in-picture mode, allowing them to continue showing content while the user browses or interacts with other apps. 

抄訳: Android TVにおいて、アプリは、自分のアプリをPicture-in-picture modeに遷移させることができます。これは、contentを表示しながら、ユーザーがbrowseしたり、他のアプリを操作することを許すためのものです。

> Especially on tablets and other larger-screen devices, multi-window support gives you new ways to engage users. You can even enable drag-and-drop in your app to let users conveniently drag content to or from your app — a great way to enhance your user experience.

抄訳: 特にtabletや他の大きい画面のデバイスでは、multi-windowサポートにより、ユーザーに新しい方法を提供できるようになります。つまり、ユーザーはdrag-and-dropで、contentをdragして、他のアプリに渡せるようになったりします。これにより、ユーザー体験は大きくenhaceされます。

> It's straightforward to add multi-window support to your app and configure how it handles multi-window display. For example, you can specify your activity's minimum allowable dimensions, preventing users from resizing the activity below that size. You can also disable multi-window display for your app, which ensures that the system will only show your app in full-screen mode.

抄訳: multi-windowをアプリでサポートするためにはmulti-window displayを扱えるようにconfigする必要があります。例えば、activityに、許容可能な最小のdimension(width, height)や、resizeをユーザーに許すかどうか、どこまでのサイズを許すかといったことを設定する必要があります。もちろん、multi-windowをあなたのアプリで無効化することもできます。つまり、あなたのアプリがfull screen modeでだけ動作するようにsystemが扱うということです。

# multi-windowでの挙動の変更概要

http://developer.android.com/preview/features/multi-window.html

* Activity Lifecycleは変わらない.

## multi window modeでは、activityがpausedでもvisibleであり得る

>In multi-window mode, only the activity the user has most recently interacted with is active at a given time. This activity is considered topmost. All other activities are in the paused state, even if they are visible. However, the system gives these paused-but-visible activities higher priority than activities that are not visible. If the user interacts with one of the paused activities, that activity is resumed, and the previously topmost activity is paused. 

抄訳: multi-window modeでは、ユーザーがinteractiveにそのとき扱っているそのactivityだけが、activeです。このactivityは、topmostとして考えられます。他のactivityは、見えていたとしても、paused stateにいます。しかし、systemは、これらのpausedでvisibleなactivityには、見えないactivityよりは高い優先度を与えています。ユーザーが他のpaused activityを触る場合、そのactivityはresumeし、前のtopmostのactivityはpausedになります。

> Note: In multi-window mode, an app can be in the paused state and still be visible to the user. An app might need to continue its activities even while paused. For example, a video-playing app that is in paused mode but is visible should continue showing its video. For this reason, we recommend that activities that play video not pause the video in their onPause() handlers. Instead, they should pause video in onStop(), and resume playback in onStart().

抄訳: 注意すべきことは、multi-windowモードにおいては、appは、paused状態で、ユーザーに見えうるということです。つまり、appは、pausedであっても、そのactivityの継続が必要になるかもしれません。例えば、video再生のアプリはpausedでも、そのvideoを継続して見えるようにすべきです。この理由から、activityは、onPause()のhandlerで、videoを停止しないことが推奨です。その代わり、appは、onStop()のハンドラーでは、videoを停止すべきです。そして、onStart()のhandlerで再生を再開すべきです。

## onConfigurationChangeがmulti-window modeに遷移するときに通知

> When the user puts an app into multi-window mode, the system notifies the activity of a configuration change, as specified in Handling Runtime Changes. Essentially, this change has the same activity-lifecycle implications as when the system notifies the app that the device has switched from portrait to landscape mode, except that the device dimensions are changed instead of just being swapped. As discussed in Handling Runtime Changes, your activity can handle the configuration change itself, or it can allow the system to destroy the activity and recreate it with the new dimensions.

抄訳: ユーザーがアプリをmulti-window modeにするとき、systemは、そのactivityにconfiguration changeを通知します。runtimeでの変化をhandlingするものとして知られてますが。本質的には、この変更は、portlaitからlandscaleモードに切り替えるときに通知されるactivity-lifecycleの実装と同じものです。例外は、deviceのdimension (width/height)が変わることです(単にwidth/heightが入れ替わるというのではなく)。runtimeでの変更の取り扱いで議論したように、あなたのactivityは、自身のconfiguration changeを扱うことができます。もしくは、systemに、activityをdestoroy(kill)して、新しいdimensionで再生成させることもできます。

## resize中は、windowBackground attributeの色でfillされる

> If the user is resizing a window and makes it larger in either dimension, the system resizes the activity to match the user action and issues runtime changes as needed. If the app lags behind in drawing in newly-exposed areas, the system temporarily fills those areas with the color specified by the windowBackground attribute or by the default windowBackgroundFallback style attribute.

抄訳: ユーザーがwindowのresizeを行い、dimensionを大きくするとき、ユーザーがactionしたのと一致するように、systemは、runtime changeを発行して、activityにresizeをさせます。もし、そのアプリが、新しく出現するエリアに描画するのが遅れると、systemは、一時的に、そのエリアをwindowBackground attributeで指定された、または、windowBackgroundFallback style attributeのdefaultの色でfillします。


## multi-windowにアプリを対応させるには

### targetSdkVetsion < Nでは、defaultでmulti-window有効だが、警告は出る

> If your app targets Android N, you can configure how and whether your app's activities support multi-window display. You can set attributes in your manifest to control both size and layout. A root activity's attribute settings apply to all activities within its task stack.

抄訳: アプリがAndroid Nをtargetにしている場合、アプリがmulti-window displayをサポートするかしないかをかけます。manifestに、sizeとlayoutを指定可能です。root activityのattributeは、そのtask stack内のすべてのactivityに適用されます。

> Note: If you build a multi-orientation app with a version of the SDK lower than Android N, and the user uses the app in multi-window mode, the system forcibly resizes the app. The system presents a dialog box warning the user that the app may behave unexpectedly. The system does not resize fixed-orientation apps; if the user attempts to open a fixed-orientation app under multi-window mode, the app takes over the whole screen.

注意すべきことは、Android Nよりも低いtargetSdkVersionのアプリでは、ユーザーはそのアプリをmulti-window modeにするときに、systemが、強制的に、アプリをresizeします。システムは、dialogを出して、予定外のアプリ挙動を警告します。システムは、fixed-orientationアプリをresizeしません。ユーザーがfixed-orientation appをmulti-windowモードで起動しようとすると、アプリは全画面 (multi-windowモード終了)になります。

## AndroidManifest.xmlに書くことは何か?

&lt;activity&gt; or &lt;application&gt;に、以下を書きましょう。

For multi-window:
```
android:resizeableActivity=["true" | "false"]
```

For Picture-in-Picture:
```
android:supportsPictureInPicture=["true" | "false"]
```

＃なんで、両方同じにしないのだろう・・・。

## Layout in AndroidManifest.xml

> With Android N, the &lt;layout&gt; manifest element supports several attributes that affect how an activity behaves in multi-window mode:

抄訳: Nでは、&lt;layout&gt;のmanifestのelementが、いくつかmulti-window modeの挙動に影響するattributeをサポートしています。

```
android:defaultWidth
```

> Default width of the activity when launched in freeform mode.

抄訳: freeform modeで起動した時のdefaultをwidth

```
android:defaultHeight
```

> Default height of the activity when launched in freeform mode.

抄訳: freeform modeで起動した時のdefaultをheight

```
android:gravity
```

> Initial placement of the activity when launched in freeform mode. See the Gravity reference for suitable values.

抄訳: freeform modeで起動した時の、activityの初期の場所. gravityの適した値について参照のこと。

```
android:minimalSize
```

> Minimum height and minimum width for the activity in both split-screen and freeform modes. If the user moves the divider in split-screen mode to make an activity smaller than the specified minimum, the system crops the activity to the size the user requests. For example, the following code shows how to specify an activity's default size and location, and its minimum size, when the activity is displayed in freeform mode:

抄訳: split-screenとfreeform modeの両方のactivityへの最小のheightと最小のwidth。もしユーザーがsplit-screen modeの区切りを、指定した最小の値よりも小さい値に動かすと、systemは、ユーザーがrequestしたサイズへ、activityをcropします。例えば、以下のコードでは、freeorm modeでそのactivityが表示されている時の、activityのdefaultのサイズと位置、その最小サイズを指定しています。

```
<activity android:name=".MyActivity">
    <layout android:defaultHeight="500dp"
          android:defaultWidth="600dp"
          android:gravity="top|end"
          android:minimalSize="450dp" />
</activity>
```

## multi-window modeで、無効化されるfeature

>Certain features are disabled or ignored when a device is in multi-window mode, because they don’t make sense for an activity which may be sharing the device screen with other activities or apps. Such features include:

> * Some System UI customization options are disabled; for example, apps cannot hide the status bar if they are not running in full-screen mode.
* The system ignores changes to the android:screenOrientation attribute.

抄訳: 
multi-window modeでは、特定の起動が無効化され、無視されます。というのは、他のactivityやアプリとデバイスの画面を共有されるとしたときに、理にかなっていないためです。例えば、

* いくつかのSystem UIのカスタマイズoptionが無効化されたりします。それによって、全画面モードで走っていない時は、アプリはstatus barをhideにできなかったりします。
* systemはandroid:screenOrientationへの変化を無視します。

## Multi-windowの変化通知と状態取得

> The following new methods have been added to the Activity class to support multi-window display. For details on each method, see the N Preview SDK Reference.

抄訳: 以下の新methodが、multi-window displayのサポートのために、Activity classに追加されています。それぞれのmethodの詳細はN Preview SDK referenceを参照のこと

```
Activity.inMultiWindow()
```

> Call to find out if the activity is in multi-window mode.

抄訳: multi-window modeに、そのactivityがいるかどうかを確認する

```
Activity.inPictureInPicture()
```

> Call to find out if the activity is in picture-in-picture mode.

抄訳: picture-in-picture modeに、そのactivityがいるかどうかを確認する


> Note: Picture-in-picture mode is a special case of multi-window mode. If myActivity.inPictureInPicture() returns true, then myActivity.inMultiWindow() also returns true.

抄訳: 注意としては、Picture-in-picture modeは、multi-window modeの特別なケースです。つまり、myActivity.inPictureInPicture()がtrueならば、myActivity.inMultiWindow()もtrueです。

```
Activity.onMultiWindowChanged()
```

> The system calls this method whenever the activity goes into or out of multi-window mode. The system passes the method a value of true if the activity is entering multi-window mode, and false if the activity is leaving multi-window mode.

抄訳: activityがmulti-window modeに入るとき、抜けるときに、systemがこのmethodを呼び出します。systemは、multi-window modeに入るときにはtrueを渡しますし、抜けるときにはfalseを渡します。

```
Activity.onPictureInPictureChanged()
```

> The system calls this method whenever the activity goes into or out of picture-in-picture mode. The system passes the method a value of true if the activity is entering picture-in-picture mode, and false if the activity is leaving picture-in-picture mode.
There are also Fragment versions of each of these methods, for example Fragment.inMultiWindow().

抄訳: activityがpicture-in-picture modeに入るとき、抜けるときに、systemがこのmethodを呼び出します。systemは、picture-in-picture modeに入るときにはtrueを渡しますし、抜けるときにはfalseを渡します。

## picture-in-picture modeに入るためには

> To put an activity in picture-in-picture mode, call the new method Activity.enterPictureInPicture(). This method has no effect if the device does not support picture-in-picture mode. For more information, see the Picture-in-Picture documentation.

抄訳: picture-in-pictureにactivityが入るためには、新method Activity.enterPictureInPicture()を読んでください。このmethodは、deviceがpicture-in-picture modeをサポートしていない時には何も影響を及ぼしません。[Picture-in-Picture](http://developer.android.com/preview/features/picture-in-picture.html)のドキュメントを参照のこと

## Multi-Window ModeでのActivityの起動

> When you launch a new activity, you can hint to the system that the new activity should be displayed adjacent to the current one, if possible. To do this, use the flag Intent.FLAG_ACTIVITY_LAUNCH_TO_ADJACENT. Passing this flag requests the following behavior:

抄訳: 新activityを起動した時、あなたはsystemにhintを与えることができます。それは、可能であれば、新activityが現在のactivityと隣り合って表示されるべきかなど、といったことです。それには、Intent.FLAG_ACTIVITY_LAUNCH_TO_ADJACENT flagを使ってください。このflagを渡すことで以下の挙動になります。

> * If the device is in split-screen mode, the system attempts to create the new activity next to the activity that launched it, so the two activities share the screen. The system is not guaranteed to be able to do this, but it makes the activities adjacent if possible.
* If the device is not in split-screen mode, this flag has no effect.
If a device is in freeform mode and you are launching a new activity, you can specify the new activity's dimensions and screen location by calling ActivityOptions.setLaunchBounds(). This method has no effect if the device is not in multi-window mode.

抄訳: 

* split-screen modeであるとき、systemは、新activityを作ったactivityの隣に、作ることを試みます。故に、２つのactivityはそのscreenを共有します。systemは、これを保証するわけではないです。しかし、可能な場合は、そのactivityは隣に作られます.
* split-screen modeで動いていない時は、このflagは、何も影響しません。デバイスがfreeform modeであるとき、ActivityOptions.setLaunchBounds()が呼び出すことによって、新activityに対し、dimensionとscreenでの位置を指定できます。

> Note: If you launch an activity within a task stack, the activity replaces the activity on the screen, inheriting all of its multi-window properties. If you want to launch the new activity as a separate window in multi-window mode, you must launch it in a new task stack.

抄訳: 注意すべきことは、もし、task stackにあるactivityを起動する時は、そのactivityは、そのscreen上ののactivityと置換し、そのmulti-windowのpropertyのすべてを継承します。もし、multi-window modeで、あなたが新actvityを別のwindowとして起動したい時は、あなたは新しいtask stackの中でそれを起動しなければなりません。

長いので、つづく。