Use picture-in-picture (PiP) (original) (raw)

Try the Compose way

Jetpack Compose is the recommended UI toolkit for Android. Learn how to support picture-in-picture in Compose.

Starting in Android 8.0 (API level 26), Android allows activities to launch in picture-in-picture (PiP) mode. PiP is a special type of multi-window mode used for video playback, video calls, and navigation. It lets the user pin the existing activity window to a corner of the screen while navigating between apps or browsing content on the main screen.

Alas, your browser doesn't support HTML5 video. That's OK! You can stilldownload the video and watch it with a video player.

PiP leverages the multi-window APIs made available in Android 7.0 to provide the pinned video overlay window. To add PiP to your app, you need to register your activities that support PiP, switch your activity to PiP mode as needed, and make sure UI elements are hidden and video playback continues when the activity is in PiP mode.

The PiP window appears in the topmost layer of the screen, in a corner chosen by the system.

PiP is also supported on compatible Android TV OS devices running Android 14 (API level 34) or later. While there are many similarities, there are additional considerations when usingPiP on TV.

How users can interact with the PiP window

Users can drag the PiP window to another location. Starting in Android 12, users can also:

Your app controls when the current activity enters PiP mode. Here are some examples:

Declare PiP support

By default, the system does not automatically support PiP for apps. If you want support PiP in your app, register your video activity in your manifest by setting android:supportsPictureInPicture to true. Also, specify that your activity handles layout configuration changes so that your activity doesn't relaunch when layout changes occur during PiP mode transitions.

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

Implement PiP with Jetpack

Use the Jetpack Picture-in-Picture library to implement picture-in-picture experience as it streamlines integration and reduces common in-app issues. Refer to our platform sample app to see an example of its usage. However, if you prefer to implement PiP using the platform APIs, refer to the following documentation.

Switch your activity to PiP

Starting with Android 12, you can switch your activity to PiP mode by setting the setAutoEnterEnabled flag to true. With this setting, an activity automatically switches to PiP mode as needed without having to explicitly callenterPictureInPictureMode() in onUserLeaveHint. And this has the added benefit of providing much smoother transitions. For details, see Make transitions to PiP mode smoother from gesture navigation.

If you're targeting Android 11 or lower, an activity must callenterPictureInPictureMode()to switch to PiP mode. For example, the following code switches an activity to PiP mode when the user clicks a dedicated button in the app's UI:

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; } ... }

You might want to include logic that switches an activity into PiP mode instead of going into the background. For example, Google Maps switches to PiP mode if the user presses the home or recents button while the app is navigating. You can catch this case by overridingonUserLeaveHint():

Kotlin

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

Java

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

Android 12 added significant cosmetic improvements to the animated transitions between fullscreen and PiP windows. We strongly recommend implementing all applicable changes; once you've done so, these changes automatically scale to large screens such as foldables and tablets without any further required work.

If your app doesn't include applicable updates, PiP transitions are still functional, but the animations are less polished. For example, transitioning from fullscreen to PiP mode can cause the PiP window to disappear during the transition before it reappears when the transition is complete.

These changes involve the following.

Refer to the Android Kotlin PictureInPicture sampleas a reference for enabling a polished transition experience.

Make transitions to PiP mode smoother from gesture navigation

Starting in Android 12, the setAutoEnterEnabled flag provides much smoother animation for transitioning to video content in PiP mode using gesture navigation—for example, when swiping up to home from fullscreen.

Complete the following steps to make this change:

  1. Use setAutoEnterEnabled to constructPictureInPictureParams.Builder:

Kotlin

setPictureInPictureParams(PictureInPictureParams.Builder()
.setAspectRatio(aspectRatio)
.setSourceRectHint(sourceRectHint)
.setAutoEnterEnabled(true)
.build())

Java

setPictureInPictureParams(new PictureInPictureParams.Builder()
.setAspectRatio(aspectRatio)
.setSourceRectHint(sourceRectHint)
.setAutoEnterEnabled(true)
.build()); 2. Call setPictureInPictureParams with the up-to-datePictureInPictureParams early. The app doesn't wait for theonUserLeaveHint callback (as it would have done in Android 11).
For example, you may want to call setPictureInPictureParams on the very first playback and any following playback if the aspect ratio is changed. 3. Call setAutoEnterEnabled(false), but only as it's necessary. For example, you probably don't want to enter PiP if the current playback is in a paused state.

Set a proper sourceRectHint for entering and exiting PiP mode

Starting with the introduction of PiP in Android 8.0, setSourceRectHintindicated the area of the activity that is visible following the transition into picture-in-picture—for example, the video view bounds in a video player.

With Android 12, the system uses sourceRectHint to implement a much smoother animation both when entering and exiting PiP mode.

To properly set sourceRectHint for entering and exiting PiP mode:

  1. Construct PictureInPictureParamsusing the proper bounds as sourceRectHint. We recommend also attaching a layout change listener to the video player:

Kotlin

val mOnLayoutChangeListener =
OnLayoutChangeListener { v: View?, oldLeft: Int,
oldTop: Int, oldRight: Int, oldBottom: Int, newLeft: Int, newTop:
Int, newRight: Int, newBottom: Int ->
val sourceRectHint = Rect()
mYourVideoView.getGlobalVisibleRect(sourceRectHint)
val builder = PictureInPictureParams.Builder()
.setSourceRectHint(sourceRectHint)
setPictureInPictureParams(builder.build())
}
mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener)

Java

private final View.OnLayoutChangeListener mOnLayoutChangeListener =
(v, oldLeft, oldTop, oldRight, oldBottom, newLeft, newTop, newRight,
newBottom) -> {
final Rect sourceRectHint = new Rect();
mYourVideoView.getGlobalVisibleRect(sourceRectHint);
final PictureInPictureParams.Builder builder =
new PictureInPictureParams.Builder()
.setSourceRectHint(sourceRectHint);
setPictureInPictureParams(builder.build());
};
mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener); 2. If necessary, update the sourceRectHint before the system starts the exit transition. When the system is about to exit PiP mode, the activity's view hierarchy is laid out to its destination configuration (for example, full screen). The app can attach a layout change listener to its root view or target view (such as the video player view) to detect the event and update the sourceRectHint before the animation begins.

Kotlin

// Listener is called immediately after the user exits PiP but before animating.
playerView.addOnLayoutChangeListener { _, left, top, right, bottom,
oldLeft, oldTop, oldRight, oldBottom ->
if (left != oldLeft
|| right != oldRight
|| top != oldTop
|| bottom != oldBottom) {
// The playerView's bounds changed, update the source hint rect to
// reflect its new bounds.
val sourceRectHint = Rect()
playerView.getGlobalVisibleRect(sourceRectHint)
setPictureInPictureParams(
PictureInPictureParams.Builder()
.setSourceRectHint(sourceRectHint)
.build()
)
}
}

Java

// Listener is called right after the user exits PiP but before animating.
playerView.addOnLayoutChangeListener((v, left, top, right, bottom,
oldLeft, oldTop, oldRight, oldBottom) -> {
if (left != oldLeft
|| right != oldRight
|| top != oldTop
|| bottom != oldBottom) {
// The playerView's bounds changed, update the source hint rect to
// reflect its new bounds.
final Rect sourceRectHint = new Rect();
playerView.getGlobalVisibleRect(sourceRectHint);
setPictureInPictureParams(
new PictureInPictureParams.Builder()
.setSourceRectHint(sourceRectHint)
.build());
}
});

Disable seamless resizing for non-video content

Android 12 adds the setSeamlessResizeEnabled flag, which provides a much smoother cross-fading animation when resizing non-video content in the PiP window. Previously, resizing non-video content in a PiP window could create jarring visual artifacts.

To enable seamless resizing for video content:

Kotlin

setPictureInPictureParams(PictureInPictureParams.Builder() .setSeamlessResizeEnabled(true) .build())

Java

setPictureInPictureParams(new PictureInPictureParams.Builder() .setSeamlessResizeEnabled(true) .build());

Handle UI during PiP

When the activity enters or exits Picture-in-Picture (PiP) mode, the system calls Activity.onPictureInPictureModeChanged()or Fragment.onPictureInPictureModeChanged().

Android 15 introduces changes that ensure an even smoother transition when entering PiP mode. This is beneficial for apps that have UI elements overlaid on top of their main UI, which goes into PiP.

Developers use the onPictureInPictureModeChanged() callback to define logic that toggles the visibility of the overlaid UI elements. This callback is triggered when the PiP enter or exit animation is completed. Beginning in Android 15, the PictureInPictureUiState class includes a new state.

With this new UI state, apps targeting Android 15 observe the Activity#onPictureInPictureUiStateChanged()callback being invoked with isTransitioningToPip() as soon as the PiP animation starts. There are many UI elements that are not relevant for the app when it is in PiP mode, for example, views or layout that include information such as suggestions, upcoming video, ratings, and titles. When the app goes into PiP mode, use the onPictureInPictureUiStateChanged() callback to hide these UI elements. When the app goes to full screen mode from the PiP window, use the onPictureInPictureModeChanged() callback to unhide these elements, as shown in the following examples:

Kotlin

override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) { if (pipState.isTransitioningToPip()) { // Hide UI elements. } }

Java

@Override public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) { if (pipState.isTransitioningToPip()) { // Hide UI elements. } }

Kotlin

override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) { if (isInPictureInPictureMode) { // Unhide UI elements. } }

Java

@Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { if (isInPictureInPictureMode) { // Unhide UI elements. } }

This quick visibility toggle of irrelevant UI elements (for a PiP window) helps ensure a smoother and flicker-free PiP enter animation.

Override these callbacks to redraw the activity's UI elements. Keep in mind that, in PiP mode, your activity is shown in a small window. Users cannot interact with your app's UI elements when the app is in PiP mode and the details of small UI elements may be difficult to see. Video playback activities with minimal UI provide the best user experience.

If your app needs to provide custom actions for PiP, see Add controls on this page. Remove other UI elements before your activity enters PiP and restore them when your activity becomes full screen again.

Add controls

The PiP window can display controls when the user opens the window's menu (by tapping the window on a mobile device, or selecting the menu from the TV remote.)

If an app has an active media session, then play, pause, next, and previous controls will appear.

You can also specify custom actions explicitly by buildingPictureInPictureParamswithPictureInPictureParams.Builder.setActions()before entering PiP mode, and pass the params when you enter PiP mode usingenterPictureInPictureMode(android.app.PictureInPictureParams)orsetPictureInPictureParams(android.app.PictureInPictureParams). Be careful. If you try to add more thangetMaxNumPictureInPictureActions(), you'll only get the maximum number.

Continuing video playback while in PiP

When your activity switches to PiP, the system places the activity in the paused state and calls the activity'sonPause() method. Video playback shouldn't be paused and instead continue playing if the activity is paused while transitioning to PiP mode.

In Android 7.0 and later, you should pause and resume video playback when the system calls your activity'sonStop() andonStart(). By doing this, you can avoid having to check if your app is in PiP mode in onPause() and explicitly continuing playback.

If you haven't set the setAutoEnterEnabled flag to true and you need to pause playback in your onPause() implementation, check for PiP mode by callingisInPictureInPictureMode() and handle playback appropriately. For example:

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. ... } }

When your activity switches out of PiP mode back to full-screen mode, the system resumes your activity and calls youronResume() method.

Use a single playback activity for PiP

In your app, a user might select a new video when browsing for content on the main screen, while a video playback activity is in PiP mode. Play the new video in the existing playback activity in full screen mode, instead of launching a new activity that might confuse the user.

To ensure a single activity is used for video playback requests and switched into or out of PiP mode as needed, set the activity's android:launchMode tosingleTask in your manifest:

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

In your activity, overrideonNewIntent()and handle the new video, stopping any existing video playback if needed.

Best practices

PiP might be disabled on devices that have low RAM. Before your app uses PiP, check to be sure it is available by callinghasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE).

PiP is intended for activities that play full-screen video. When switching your activity into PiP mode, avoid showing anything except video content. Track when your activity enters PiP mode and hide UI elements, as described in Handling UI during PiP.

When an activity is in PiP mode, by default it doesn't get input focus. To receive input events while in PiP mode, useMediaSession.setCallback(). For more information on using setCallback() see Display a Now Playing card.

When your app is in PiP mode, video playback in the PiP window can cause audio interference with another app, such as a music player app or voice search app. To avoid this, request audio focus when you start playing the video, and handle audio focus change notifications, as described in Managing Audio Focus. If you receive notification of audio focus loss when in PiP mode, pause or stop video playback.

When your app is about to enter PiP, note only the top activity enters picture-in-picture. In some situations such as on multi-window devices, it is possible the activity below will now be shown and become visible again alongside the PiP activity. You should handle this case accordingly, including the activity below getting an onResume() or an onPause() callback. It is also possible that the user may interact with the activity. For example, if you have a video list activity displayed and the playing video activity in PiP mode, the user might select a new video from the list and the PiP activity should update accordingly.

Additional sample code

To download a sample app written in Kotlin, see Android PictureInPicture Sample (Kotlin).