TL;DR
他のアプリが Picture in Picture モードに入り、そのオーバーレイ表示が自分のアプリに重なる場合、自分のアプリのActivity
やView
のonWindowFocusChanged(boolean)
は次の順で計3回呼び出されます。
onWindowFocusChanged(true)
: タスクが他のアプリから自分のアプリに切り替わったときonWindowFocusChanged(false)
: Picture in Picture の表示(アニメーション)が完了してフォーカスがその表示に奪われたときonWindowFocusChanged(true)
: 2からおよそ3〜4秒後に自分のアプリにフォーカスが戻ったとき
なお Android P Beta3 on Essential Phone と Android P on Essential Phone で確認しています。
Picture in Picture とは
Picture in Picture (PiP) は Android O で登場した機能で、アプリの画面 (Activity
) を他のアプリに重ねてスクリーンの隅に小さく表示する機能です。たとえば Google Maps でナビゲーションをしているときに他のアプリに切り替えると、ナビゲーションの地図が自動で縮小されてスクリーンの隅に表示されるような機能を指して Picture in Picture と呼んでいます。
この機能はActivity
を縮小すると同時にその縮小表示を他のアプリに重ねるため、Runtime Permissions とは別の権限を必要とします。インストールと同時に許可がおりるので普段はあまり気にすることはありませんが、Picture in Picture の表示やシステムの設定画面から、Picture in Picture の権限を取り消すことができます。
Window Focus とは
ざっくりいうと、KeyEvent や TouchEvent を受け付けるWindow
を管理する仕組みです。
Window
そのものについてはAndroid を支える技術〈Ⅰ〉に解説がありますが、基本的には各Activity
はひとつのWindow
に属し、WindowManager
の管理下に置かれるようになっています。WindowManager
に直接自分でView
を登録する方法もあり、この場合登録したView
は新たなWindow
に属し、WindowManager
の管理下に入ります。つまり一つのアプリケーションで複数のWindow
が存在し得ます。
Picture in Picture に入る処理
AndroidManifestでPicture in Pictureをサポートしている宣言をし、アプリケーションがActivity#enterPictureInPictureMode(PictureInPictureParams)
を呼び出すとPicture in Pictureモードに入ります。このときの処理の実体は(ActivityManagerService#enterPictureInPictureMode(IBinder, PictureInPictureParams)
)https://android.googlesource.com/platform/frameworks/base.git/+/android-cts-8.1_r7/services/core/java/com/android/server/am/ActivityManagerService.java#8089にあります。おおよそ次のような順で Picture in Picture モードに入る処理を呼び出します。
- すでに Picture in Picture モードの場合は何もしない
- Activity が Picture in Picture モードをサポートしない場合も何もしない
- KeyGuard が表示されている場合は解除を試み、成功したら Picture in Picture モードに入る
- KeyGuard が表示されていないければ即座に Picture in Picture モードに入る
この処理の中の"Picture in Picture モードに入る"部分にあたるのはActivityStackSupervisor#moveActivityToPinnedStackLocked(ActivityRecord, Rect, float, boolean, String)
です。
ここではフォアグラウンドにいた Activity を Picture in Picture モードにするため、画面のリサイズに関する計算やアニメーションの再生をしています。
このメソッドの最後で Picture in Picture の状態を監視しているリスナーに、Picture in Picture モードに入ったことを知らせています。
Picture in Picture モードに入ったかどうかを監視するリスナーのひとつに、PipManager
があります。
このPipManager
はハンドセット端末用のものと、TV 端末用のものとがそれぞれ別に存在します。
ハンドセット端末用のPipManager
は、Picture in Picture の表示を管理する PipMenuActivityController
や、タッチのインタラクションを制御するPipMotionHelper
、PipTouchHandler
などへ処理を委譲します。
Picture in Picture に入ってからの処理
Picture in Picture の表示は SystemUI の PipMenuActivity が担当しています。このActivity
は WindowFocus を取得してから 3500 ミリ秒後に、Picture in Picture のメニュー(閉じるボタンや設定ボタンなど)を非表示にします。このタイミングでPipMenuActivity
から Window Focus が失われ、その時フォアグラウンドにいるアプリの Activity に Window Focus が移動します。冒頭のTL;DRに書いた"およそ3~4秒後"というのはまさに、この 3500 ミリ秒後にメニューを非表示にする処理によるものです。
Picture in Picture に入ったとき以外にも、Picture in Picture モードの表示をタップしてすこし拡大したときにもPipMenuActivity
が立ち上がって WindowFocus を奪います。よって、ソフトキーボードが出ている状態で Picture in Picture の表示をタップするとソフトキーボードが閉じるというわけです。