Skip to main content

Implementing Picture in Picture Mode

Overview

Android supports Picture-in-picture (PiP) mode starting from android 8.0 (API level 26). Picture-in-picture (PiP) is a special case of the multi-window UI which lets the user watch a video in a small window pinned to a corner of the screen while navigating between apps or browsing content on the main screen. To add PiP to your app, you need to register your activities that support PiP, switch your activity to PiP mode as required, 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.

How users can interact with the picture-in-picture (PiP) window

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

  • Single-tap the window to display a full-screen toggle, a close button, a settings button, and custom actions provided by your app (for example, play controls).
  • Double-tap the window to toggle between the current PiP size and maximum PiP size.
  • Resize the PiP window using pinch-to-zoom.

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

  • An activity can enter PiP mode when the user taps the home button.
  • Your app can move a video into PiP mode when the user navigates back from the video to browse other content.

Declare picture-in-picture support

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=".PlayerActivity"
android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout"
android:supportsPictureInPicture="true">

Picture in picture implementation

The react native SDK provides two ways to implement a player. One with native controls and one with user-implemented custom controls. So, there are two methods to implement picture-in-picture mode.

Picture-in-picture (PiP) for player with native controls

After declaring the picture in picture support in the manifest file, pass setPictureInPictureSupport: true in embed info along with the otp and playbackinfo to VdoPlayerView component.

import {VdoPlayerView} from 'vdocipher-rn-bridge';

const embedInfo = {
otp: 'some-otp',
playbackInfo: 'some-playbackInfo',
setPictureInPictureSupport: true,
};

// in JSX

<VdoPlayerView style={{height: 200, width: '100%'}} embedInfo={embedInfo} />;

To use PIP mode support for native controls, the pip should also be enabled from player configuration from dashboard as well.

Picture-in-picture (PiP) for player with custom controls

To enter picture-in-picture mode for custom controls, an activity must call enterPictureInPictureMode(). For example, the following code switches an activity to PiP mode when a the user clicks the home or recent button:

import android.app.PictureInPictureParams;
import android.os.Bundle;
import android.os.Build;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;

@RequiresApi(api = Build.VERSION_CODES.O)
@Override
protected void onUserLeaveHint() {
super.onUserLeaveHint();
//switch to PiP mode if the user presses the home or recent button,
Fragment vdoPlayerFragment = getSupportFragmentManager().findFragmentByTag("VdoPlayerUIFragmentRN");
if (vdoPlayerFragment != null && vdoPlayerFragment.isVisible()) {
enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
}
}

The check for vdoPlayerFragment needs to be added if you do not wish to enter pip mode for any other screen.

After performing above steps, pass setPictureInPictureSupport: true in embed info along with the otp and playbackinfo to VdoPlayerView component.

import {VdoPlayerView} from 'vdocipher-rn-bridge';

const embedInfo = {
otp: 'some-otp',
playbackInfo: 'some-playbackInfo',
setPictureInPictureSupport: true,
};

// in JSX

<VdoPlayerView
style={{height: 200, width: '100%'}}
embedInfo={embedInfo}
showNativeControls={false}
/>;

Handle UI during picture-in-picture for custom controls

Use onPictureInPictureModeChanged event based prop to handle UI during pip mode. Refer example below to show/hide custom controls based on isInPictureInPictureMode flag

import {VdoPlayerView} from 'vdocipher-rn-bridge';

const embedInfo = {
otp: 'some-otp',
playbackInfo: 'some-playbackInfo',
setPictureInPictureSupport: true,
};

// in JSX

var isInPictureInPictureMode = this.state.isInPictureInPictureMode;

<VdoPlayerView
style={{height: 200, width: '100%'}}
embedInfo={embedInfo}
showNativeControls={false}
onPictureInPictureModeChanged={this._onPictureInPictureModeChanged}
/>;
{
!isInPictureInPictureMode && (
<View style={styles.controls.container}>
<TouchableWithoutFeedback onPress={this._onPlayButtonTouch}>
<Icon name={showPlayIcon ? 'play' : 'pause'} size={30} color="#FFF" />
</TouchableWithoutFeedback>
<Text style={styles.controls.position}>
{digitalTime(Math.floor(this.state.position))}
</Text>
{this._renderSeekbar()}
<Text style={styles.controls.duration}>
{digitalTime(Math.floor(this.state.duration))}
</Text>
<TouchableWithoutFeedback onPress={this._toggleFullscreen}>
<MatIcon
name={isFullscreen ? 'fullscreen-exit' : 'fullscreen'}
style={styles.controls.fullscreen}
size={30}
color="#FFF"
/>
</TouchableWithoutFeedback>
</View>
);
}
_onPictureInPictureModeChanged = (pictureInPictureModeInfo) => {
this.setState({
isInPictureInPictureMode:
pictureInPictureModeInfo.nativeEvent.isInPictureInPictureMode,
});
};