Changes

Jump to: navigation, search

Android Picture-in-Picture Mode using Kotlin

9,702 bytes added, 15:38, 29 November 2017
Created page with "When multi-tasking in Android was covered in earlier chapters, Picture-in-picture (PiP) mode was mentioned briefly but not covered in any detail. Intended primarily for video..."
When multi-tasking in Android was covered in earlier chapters, Picture-in-picture (PiP) mode was mentioned briefly but not covered in any detail. 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.

This chapter will provide an overview of Picture-in-Picture mode before Picture-in-Picture support is added to the VideoPlayer project in the next chapter.

== Picture-in-Picture Features ==

As will be explained later in the chapter, and demonstrated in the next chapter, an activity is placed into PiP mode via an API call from within the running app. When 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. Figure 70-1, for example, shows a video playback activity in PiP mode:


[[File:as3.0_videoplayer_pip_example.png]]


Figure 70-2 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. In the case of Figure 70-2, the PiP window includes custom play and pause action buttons:


[[File:as3.0_videoplayer_pip_action_example.png]]


The remainder of this chapter will outline how PiP mode is enabled and managed from within an Android app.

== Enabling Picture-in-Picture Mode ==

PiP mode is currently only supported on devices running Android 8.0 (API 26) or newer. The first 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:

<pre>
<activity android:name=".MyActivity"
    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>
</pre>

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.

== Configuring Picture-in-Picture Parameters ==

PiP behavior is defined through the use of the PictureInPictureParams class, instances of which can be created using the Builder class as follows:

<pre>
val params = PictureInPictureParams.Builder().build()
</pre>

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. Actions will be covered in more detail later in this chapter.

* '''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:

<pre>
val rational = Rational(videoView.width,
        videoView.height)
 

val params = PictureInPictureParams.Builder()
        .setAspectRatio(rational)
        .setActions(actions)
        .build()
</pre>

Once defined, PiP parameters may be set at any time using the setPictureInPictureParams() method as follows:

<pre>
setPictureInPictureParams(params) 
</pre>

Parameters may also be specified when entering PiP mode.

== Entering Picture-in-Picture Mode ==

An activity is placed into Picture-in-Picture mode via a call to the enterPictureInPictureMode() method, passing through a PictureInPictureParams object:

<pre>
enterPictureInPictureMode(params)
</pre>

If no parameters are required, simply create a default PictureInPictureParams object as outlined in the previous section. If parameters have previously been set using the setPictureInPictureParams() method, these parameters are combined with those specified during the enterPictureInPictureMode() method call.

== Detecting Picture-in-Picture Mode Changes ==

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:

<pre>
override fun onPictureInPictureModeChanged(
isInPictureInPictureMode: Boolean, newConfig: Configuration?) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
    if (isInPictureInPictureMode) {
        // Acitivity entered Picture-in-Picture mode
    } else {
        // Activity entered full screen mode
    }
}
</pre>

== 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:

<pre>
val actions = ArrayList<RemoteAction>()
 
val actionIntent = Intent("MY_PIP_ACTION")
 
val pendingIntent = PendingIntent.getBroadcast(this@MyActivity,
                                            REQUEST_CODE, actionIntent, 0)
 
val icon = Icon.createWithResource(this, R.drawable.action_icon)
 
val remoteAction = RemoteAction(icon, 
                                    "My Action Title", 
                                    "My Action Description", 
                                    pendingIntent)
 
actions.add(remoteAction)
 
val params = PictureInPictureParams.Builder()
                .setActions(actions)
                .build()
 
setPictureInPictureParams(params)
</pre>

== Summary ==

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.

PiP mode behavior is configured using instances of the PictureInPictureParams class and initiated via a call to the enterPictureInPictureMode() method from within the activity. When in PiP mode, only the video playback should be visible, requiring that any other user interface elements be hidden until full screen mode is selected. These and other mode transition related tasks can be performed by overriding the onPictureInPictureModeChanged() method.

PiP actions appear as icons overlaid onto the PiP window when it is tapped by the user. When selected, these actions trigger behavior within the activity. The activity is notified of an action by the PiP window using broadcast receivers and pending intents.

Navigation menu