Introduction
Intended primarily for video playback, PiP mode allows an activity screen to be reduced in size and positioned at any location on the screen. While in this state, the activity continues to run and the window remains visible regardless of any other activities running on the device. This allows the user to, for example, continue watching video playback while performing tasks such as checking email or working on a spreadsheet.
When activity placed into PiP mode, configuration options may be specified that control the aspect ratio of the PiP window and also to define the area of the activity screen that is to be included in the window. Following screenshot, for example, shows a image activity in PiP mode:
Following screenshot shows a PiP mode window after it has been tapped by the user. When in this mode, the window appears larger and includes a full screen action in the center which, when tapped, restores the window to full screen mode and an exit button in the top right-hand corner to close the window and place the app in the background. Any custom actions added to the PiP window will also appear on the screen when it is displayed in this mode.
PiP mode is currently only supported on devices running Android 8.0 (API 26) or newer.
To modify the SDK setting, locate the Gradle Scripts –> build.gradle (Module: app) file and increase the minSdkVersion
setting from 14 to 26:
android { compileSdkVersion 26 buildToolsVersion "26.0.0" defaultConfig { ... minSdkVersion 26 } }
The second step in implementing PiP mode is to enable it within the project’s manifest file.
PiP mode is configured on a per activity basis by adding the following lines to each activity element for which PiP support is required:
<activity android:name=".MainActivity" android:supportsPictureInPicture="true" android:configChanges= "screenSize|smallestScreenSize|screenLayout|orientation"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
The android:supportsPictureInPicture
entry enables PiP for the activity while the android:configChanges
property notifies Android that the activity is able to handle layout configuration changes. Without this setting, each time the activity moves in and out of PiP mode the activity will be restarted resulting in playback restarting from the beginning of the video during the transition.
PiP behavior is defined through the use of the PictureInPictureParams
class, instances of which can be created using the Builder
class as follows:
PictureInPictureParams params = new PictureInPictureParams.Builder().build();
The above code creates a default PictureInPictureParams
instance with special parameters defined. The following optional method calls may also be used to customize the parameters:
setActions()
– Used to define actions that can be performed from within the PiP window while the activity is in PiP mode. setAspectRatio()
– Declares the preferred aspect ratio for appearance of the PiP window. This method takes as an argument a Rational
object containing the height width / height ratio.setSourceRectHint()
– Takes as an argument a Rect
object defining the area of the activity screen to be displayed within the PiP window.The following code, for example, configures aspect ratio and action parameters within a PictureInPictureParams
object. In the case of the aspect ratio, this is defined using the width and height dimensions of a VideoView
instance:
Rational rational = new Rational(videoView.getWidth(), videoView.getHeight()); PictureInPictureParams params = new PictureInPictureParams.Builder() .setAspectRatio(rational) .setActions(actions) .build();
Once defined, PiP parameters may be set at any time using the setPictureInPictureParams
setPictureInPictureParams(params);
Parameters may also be specified when entering PiP mode.
An activity is placed into Picture-in-Picture mode via a call to the enterPictureInPictureMode()
method, passing through a PictureInPictureParams
object:
enterPictureInPictureMode(params);
If no parameters are required, simply create a default PictureInPictureParams
object as outlined above. If parameters have previously been set using the setPictureInPictureParams()
method, these parameters are combined with those specified during the enterPictureInPictureMode()
method call.
When an activity enters PiP mode, it is important to hide any unnecessary views so that only the video playback is visible within the PiP window. When the activity re-enters full screen mode, any hidden user interface components need to be re-instated. These and any other app specific tasks can be performed by overriding the onPictureInPictureModeChanged()
method. When added to the activity, this method is called each time the activity transitions between PiP and full screen modes and is passed a Boolean
value indicating whether the activity is currently in PiP mode:
@Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { super.onPictureInPictureModeChanged(isInPictureInPictureMode); if (isInPictureInPictureMode) { // acitivity entered Picture-in-Picture mode } else { // activity entered full screen mode } }
Adding Picture-in-Picture Actions
Picture-in-Picture actions appear as icons within the PiP window when it is tapped by the user. Implementation of PiP actions is a multi-step process that begins with implementing a way for the PiP window to notify the activity that an action has been selected. This is achieved by setting up a broadcast receiver within the activity, and then creating a pending intent within the PiP action which, in turn, is configured to broadcast an intent for which the broadcast receiver is listening. When the broadcast receiver is triggered by the intent, the data stored in the intent can be used to identify the action performed and to take the necessary action within the activity.
PiP actions are declared using the RemoteAction
instances which are initialized with an icon, a title, a description and the PendingIntent
object. Once one or more actions have been created, they are added to an ArrayList
and passed through to the setActions()
method while building a PictureInPictureParams
object.
The following code fragment demonstrates the creation of the Intent
, PendingIntent
and RemoteAction
objects together with a PictureInPictureParams
instance which is then applied to the activity’s PiP settings:
ArrayList<RemoteAction> actions = new ArrayList<>(); Intent actionIntent = new Intent("MY_PIP_ACTION"); PendingIntent pendingIntent = PendingIntent.getBroadcast(activity, REQUEST_CODE, actionIntent, 0); Icon icon = Icon.createWithResource(activity, android.R.drawable.ic_dialog_info); RemoteAction remoteAction = new RemoteAction(icon, "My Action Title", "My Action Description", pendingIntent); actions.add(remoteAction); PictureInPictureParams params = new PictureInPictureParams.Builder() .setActions(actions) .build(); setPictureInPictureParams(params);
Example
Following is the layout
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:id="@+id/iv" android:layout_width="match_parent" android:layout_height="200dp" android:layout_alignParentTop="true" android:src="@drawable/pic" android:scaleType="centerCrop"/> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" android:text="PiP mode" android:textSize="30sp" android:textColor="@android:color/white" android:layout_centerHorizontal="true" android:layout_alignParentTop="true" android:layout_marginTop="10dp" /> <Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="PiP" android:layout_centerHorizontal="true" android:layout_below="@id/iv" android:onClick="gotoPip"/> </RelativeLayout>
Following is the MainActivity
public class MainActivity extends AppCompatActivity { public static final String BROADCAST_ACTION = "me.proft.PIP_ACTION"; private Button btn; private ImageView iv; private TextView tv; private BroadcastReceiver receiver; private static final int REQUEST_CODE = 101; private ArrayList<RemoteAction> actions = new ArrayList<>(); private Activity activity = MainActivity.this; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = findViewById(R.id.btn); iv = findViewById(R.id.iv); tv = findViewById(R.id.tv); } public void gotoPip(View v) { btn.setVisibility(View.INVISIBLE); tv.setVisibility(View.VISIBLE); Rational rational = new Rational(iv.getWidth(), iv.getHeight()); PictureInPictureParams params = new PictureInPictureParams.Builder() .setAspectRatio(rational) .build(); enterPictureInPictureMode(params); } private void createPipAction() { Intent actionIntent = new Intent(BROADCAST_ACTION); final PendingIntent pendingIntent = PendingIntent.getBroadcast(activity, REQUEST_CODE, actionIntent, 0); final Icon icon = Icon.createWithResource(activity, android.R.drawable.ic_dialog_info); RemoteAction remoteAction = new RemoteAction(icon, "Info", "Some info", pendingIntent); actions.add(remoteAction); PictureInPictureParams params = new PictureInPictureParams.Builder() .setActions(actions) .build(); setPictureInPictureParams(params); } @Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { super.onPictureInPictureModeChanged(isInPictureInPictureMode); if (isInPictureInPictureMode) { // action IntentFilter filter = new IntentFilter(); filter.addAction(BROADCAST_ACTION); receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "Some action", Toast.LENGTH_LONG).show(); } }; registerReceiver(receiver, filter); createPipAction(); } else { btn.setVisibility(View.VISIBLE); tv.setVisibility(View.GONE); if (receiver != null) { unregisterReceiver(receiver); } } } }
Result
So, Picture-in-Picture mode is a multitasking feature introduced with Android 8.0 designed specifically to allow video playback to continue in a small window while the user performs tasks in other apps and activities. Before PiP mode can be used, it must first be enabled within the manifest file for those activities that require PiP support.