ピクチャー イン ピクチャーのサポート

Android 8.0(API レベル 26)では、アクティビティをピクチャー イン ピクチャー(PIP)モードで起動できます。PIP は特別なタイプのマルチウィンドウ モードで、主に動画の再生に使用されます。 ユーザーは、メイン画面でアプリ間を移動したりコンテンツをブラウジングしたりしながら、画面の隅に固定された小さなウィンドウで動画を視聴し続けることができます。

PIP は、Android 7.0 で使用可能になったマルチウィンドウ API を利用して、固定された動画オーバーレイ ウィンドウを提供します。アプリに PIP を追加するには、PIP をサポートするアクティビティを登録し、必要に応じてアクティビティを PIP モードに切り替える必要があります。アクティビティが PIP モードのときは、UI 要素を非表示にして動画の再生を続行します。

PIP ウィンドウは、画面の最上位レイヤーの、システムによって選択された隅の領域に表示されます。PIP ウィンドウを別の位置にドラッグすることもできます。ウィンドウをタップすると、2 つの特別なコントロールが表示されます。それは、全画面切り替えボタン(ウィンドウの中央にあります)と閉じるボタン(右上隅の「X」)です。

現在のアクティビティが PIP モードに入るタイミングは、アプリで制御します。次に例を示します。

  • ユーザーがホームボタンまたは「最近使ったアプリ」ボタンをタップして別のアプリを選択したら、アクティビティを PIP モードに切り替えます(Google マップは、ユーザーが別のアクティビティを同時に実行している間、この方法でルートを表示し続けます)。
  • ユーザーが動画から他のコンテンツに移動してブラウジングを開始したら、動画を PIP モードに切り替えます。
  • ユーザーが見ている動画コンテンツがエピソードの終盤に差し掛かったら、動画を PIP モードに切り替えます。メイン画面には、シリーズの次のエピソードに関する宣伝情報やあらすじを表示します。
  • ユーザーが動画を見ながら他のコンテンツをキューに追加できるようにします。PIP モードで動画を再生し続けながら、メイン画面にコンテンツ選択アクティビティを表示します。

ピクチャー イン ピクチャーのサポートを宣言する

デフォルトでは、システムはアプリによる PIP の使用を自動的にサポートすることはありません。 アプリで PIP をサポートするには、android:supportsPictureInPicturetrue に設定することにより、動画アクティビティをマニフェストに登録します。また、PIP モードへの移行中にレイアウト変更が発生した場合にアクティビティが再起動されないようにするため、アクティビティがレイアウト構成の変更を処理するように指定します。

<activity android:name="VideoActivity"
    android:supportsPictureInPicture="true"
    android:configChanges=
        "screenSize|smallestScreenSize|screenLayout|orientation"
    ...

アクティビティをピクチャー イン ピクチャーに切り替える

ピクチャー イン ピクチャー モードに切り替えるには、アクティビティから enterPictureInPictureMode() を呼び出す必要があります。次のコード例では、ユーザーがアプリの UI の専用ボタンをクリックしたときに、アクティビティを PIP モードに切り替えています。

Kotlin

override fun onActionClicked(action: Action) {
    if (action.id.toInt() == R.id.lb_control_picture_in_picture) {
        activity?.enterPictureInPictureMode()
        return
    }
}

Java

@Override
public void onActionClicked(Action action) {
    if (action.getId() == R.id.lb_control_picture_in_picture) {
        getActivity().enterPictureInPictureMode();
        return;
    }
    ...
}

アクティビティをバックグラウンドに移行する代わりに PIP モードに切り替えるロジックを含めることもできます。たとえば、Google マップは、ユーザーがホームボタンまたは「最近使ったアプリ」ボタンを押して別のアプリに移動すると、PIP モードに切り替わります。このようなケースをキャッチするには、次のように onUserLeaveHint() をオーバーライドします。

Kotlin

override fun onUserLeaveHint() {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode()
    }
}

Java

@Override
public void onUserLeaveHint () {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode();
    }
}

ピクチャー イン ピクチャー時の UI の処理

アクティビティがピクチャー イン ピクチャー モードに入るかピクチャー イン ピクチャー モードから抜けると、システムは Activity.onPictureInPictureModeChanged() または Fragment.onPictureInPictureModeChanged() を呼び出します。

アプリでは、これらのコールバックをオーバーライドして、アクティビティの UI 要素を再描画する必要があります。PIP モードでは、アクティビティを表示するウィンドウが小さいことに留意してください。PIP モードでは、ユーザーが UI 要素を操作しづらい場合や、UI 要素が小さすぎて細部が見えづらい場合があります。動画再生アクティビティでは、UI の数を最小限にすることがユーザー エクスペリエンスの向上につながります。アクティビティは動画再生コントロールのみを表示するべきです。他の UI 要素は、アクティビティが PIP に切り替わる前に削除し、全画面モードに戻るときに復元するようにします。

Kotlin

override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean,
                                           newConfig: Configuration) {
    if (isInPictureInPictureMode) {
        // Hide the full-screen UI (controls, etc.) while in picture-in-picture mode.
    } else {
        // Restore the full-screen UI.
    }
}

Java

@Override
public void onPictureInPictureModeChanged (boolean isInPictureInPictureMode, Configuration newConfig) {
    if (isInPictureInPictureMode) {
        // Hide the full-screen UI (controls, etc.) while in picture-in-picture mode.
    } else {
        // Restore the full-screen UI.
        ...
    }
}

コントロールを追加する

PIP ウィンドウでは、ユーザーが(モバイル デバイスでウィンドウをタップするか、テレビリモコンでメニューを選択して)ウィンドウのメニューを開いたときに、コントロールを表示できます。

アプリにアクティブなメディア セッションがある場合は、「再生」、「一時停止」、「次へ」、「前へ」の各コントロールが表示されます。

また、あらかじめ PictureInPictureParams.Builder.setActions()PictureInPictureParams を設定してカスタム アクションを明示的に指定しておき、PIP モードに入るときに enterPictureInPictureMode(android.app.PictureInPictureParams) または setPictureInPictureParams(android.app.PictureInPictureParams) を使用してそのパラメータを渡すこともできます。 ここで注意が必要なのは、getMaxNumPictureInPictureActions() を超える数のアクションを追加しようとしても、最大数のアクションしか追加できないという点です。

ピクチャー イン ピクチャー時に動画再生を続行する

アクティビティが PIP に切り替わるとき、システムはアクティビティを一時停止状態にして、アクティビティの onPause() メソッドを呼び出します。PIP モード中にアクティビティが一時停止された場合は、動画再生を一時停止せず、再生を続行する必要があります。

Android 7.0 以降では、システムがアクティビティの onStop() または onStart() を呼び出したとき、アプリで動画再生の一時停止または再開を行う必要があります。これにより、onPause() でアプリが PIP モードかどうかを確認したり、明示的に再生を続行したりする必要がなくなります。

onPause() の実装で再生を一時停止する必要がある場合は、isInPictureInPictureMode() を呼び出して PIP モードかどうかを確認し、再生を適切に処理します。次に例を示します。

Kotlin

override fun onPause() {
    super.onPause()
    // If called while in PIP mode, do not pause playback
    if (isInPictureInPictureMode) {
        // Continue playback
    } else {
        // Use existing playback logic for paused Activity behavior.
    }
}

Java

@Override
public void onPause() {
    // If called while in PIP mode, do not pause playback
    if (isInPictureInPictureMode()) {
        // Continue playback
        ...
    } else {
        // Use existing playback logic for paused Activity behavior.
        ...
    }
}

アクティビティが PIP モードから全画面モードに戻ると、システムはアクティビティを再開して onResume() を呼び出します。

ピクチャー イン ピクチャー用に単一の再生アクティビティを使用する

動画再生アクティビティが PIP モードのときに、ユーザーがメイン画面でコンテンツをブラウジングして新しい動画を選択することがあります。その場合は、新しいアクティビティを起動するとユーザーを混乱させる可能性があるため、既存の再生アクティビティを全画面モードにして新しい動画を再生します。

動画再生リクエストに対して必ず単一のアクティビティが使用され、必要に応じて PIP モードの開始と終了が切り替えられるようにするには、次のように、マニフェストでアクティビティの android:launchModesingleTask に設定します。

<activity android:name="VideoActivity"
    ...
    android:supportsPictureInPicture="true"
    android:launchMode="singleTask"
    ...

アクティビティでは、onNewIntent() をオーバーライドして新しい動画を処理し、必要なときは既存の動画再生を停止します。

おすすめの方法

RAM が少ないデバイスでは、PIP が無効になっている場合があります。アプリで PIP を使用する前に、hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) を呼び出して PIP が使用可能であることを確認してください。

PIP は、フルスクリーン動画を再生するアクティビティを対象としています。アクティビティを PIP モードに切り替えるときは、動画以外のコンテンツを表示しないようにしてください。 ピクチャー イン ピクチャー時の UI の処理で説明しているように、アクティビティが PIP モードに入るタイミングをトラッキングして UI 要素を非表示にします。

PIP ウィンドウは画面の隅にフローティング ウィンドウとして表示されるため、PIP ウィンドウで隠される可能性があるメイン画面の領域には、重要な情報を表示しないでください。

アクティビティが PIP モードのとき、デフォルトでは入力フォーカスを取得しません。PIP モードのときに入力イベントを受信するには、MediaSession.setCallback() を使用します。setCallback() の使用方法の詳細については、[再生しています] カードを表示するをご覧ください。

アプリが PIP モードのとき、PIP ウィンドウで動画を再生することにより、別のアプリ(音楽プレーヤー アプリや音声検索アプリなど)が音声干渉を受ける可能性があります。これを回避するには、動画再生を開始するときに音声フォーカスをリクエストし、音声フォーカスの管理の説明に従って、音声フォーカス変更通知を処理します。PIP モード中に音声フォーカスを失ったという通知を受け取った場合は、動画再生を一時停止または停止します。

アプリが PIP に入る際には、トップ アクティビティのみがピクチャー イン ピクチャー モードに入ることに注意してください。ある種の状況では(マルチウィンドウ デバイスなど)、下位のアクティビティが表示されて PIP アクティビティとともにユーザーに再表示される可能性があります。このようなケース(onResume() または onPause() コールバックを取得する下位のアクティビティなど)は、状況に応じて処理する必要があります。 また、ユーザーがアクティビティを操作する可能性もあります。たとえば、動画リスト アクティビティが表示され、動画再生アクティビティが PIP モードで実行されている場合、ユーザーがリストから新しい動画を選択すると、それに応じて PIP アクティビティが更新されます。

他のサンプルコード

Android で作成されたサンプルアプリをダウンロードするには、Android PictureInPicture サンプルをご覧ください。 Kotlin で記述されたサンプルアプリをダウンロードするには、Android PictureInPicture サンプル(Kotlin)をご覧ください。