Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 1 | page.title=Adding Picture-in-picture |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 2 | page.keywords=preview,sdk,PIP,Picture-in-picture |
| 3 | page.tags=androidn |
Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 4 | helpoutsWidget=true |
| 5 | |
| 6 | trainingnavtop=true |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 7 | |
| 8 | @jd:body |
| 9 | |
Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 10 | <div id="tb-wrapper"> |
| 11 | <div id="tb"> |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 12 | |
| 13 | <h2>In this document</h2> |
| 14 | <ol> |
| 15 | <li><a href="#declaring">Declaring Your Activity Supports |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 16 | Picture-in-picture</a></li> |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 17 | <li><a href="#pip_button">Switching Your Activity to Picture-in-picture</a> |
| 18 | </li> |
| 19 | <li><a href="#handling_ui">Handling UI During Picture-in-picture</a> |
| 20 | </li> |
| 21 | <li><a href="#continuing_playback">Continuing Video Playback While in |
| 22 | Picture-in-picture</a></li> |
Daniel Yu | c5d9c8f | 2016-03-21 16:41:26 -0700 | [diff] [blame] | 23 | <li><a href="#single_playback">Using a Single Playback Activity for |
| 24 | Picture-in-picture</a></li> |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 25 | <li><a href="#best">Best Practices</a></li> |
| 26 | </ol> |
| 27 | |
| 28 | <h2>See Also</h2> |
| 29 | <ol> |
| 30 | <li><a href="{@docRoot}preview/features/multi-window.html">Multi-Window |
| 31 | Support</a></li> |
| 32 | </ol> |
| 33 | |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 34 | </div> |
| 35 | </div> |
| 36 | |
Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 37 | <p>In Android 7.0, Android TV users can now watch a video |
| 38 | in a pinned window in a corner of the screen when navigating within or |
| 39 | between apps. Picture-in-picture (PIP) mode lets apps run a video |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 40 | activity in the pinned window while another activity continues in the |
Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 41 | background. The PIP window lets users multitask while using Android TV, |
| 42 | which helps users be more productive.</p> |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 43 | |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 44 | <p>Your app can decide when to trigger PIP mode. Here are some examples of |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 45 | when to enter PIP mode:</p> |
| 46 | |
| 47 | <ul> |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 48 | <li>Your app can move a video into PIP mode when the user navigates |
| 49 | back from the video to browse other content.</li> |
| 50 | <li>Your app can switch a video into PIP mode while a user watches the end |
| 51 | of an episode of content. The main screen displays promotional or summary |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 52 | information about the next episode in the series.</li> |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 53 | <li>Your app can provide a way for users to queue up additional content while |
| 54 | they watch a video. The video continues playing in PIP mode while the main |
| 55 | screen displays a content selection activity.</li> |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 56 | </ul> |
| 57 | |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 58 | <p>The PIP window is 240x135 dp and is shown at the top-most layer in one of |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 59 | the four corners of the screen, chosen by the system. The user can bring up a |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 60 | PIP menu that lets them toggle the PIP window to full-screen, or close the PIP |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 61 | window, by holding down the <b>Home</b> button on the remote. If another |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 62 | video starts playing on the main screen, the PIP window is automatically |
Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 63 | closed.</p> |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 64 | |
Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 65 | <img src="{@docRoot}images/android-7.0/pip-active.png" /> |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 66 | <p class="img-caption"><strong>Figure 1.</strong> A Picture-in-picture |
| 67 | video visible in a corner of the screen while the user browses content |
| 68 | on the main screen.</p> |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 69 | |
Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 70 | <p>PIP leverages the multi-window APIs available in Android 7.0 to |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 71 | provide the pinned video overlay window. To add PIP to your app, you need to |
| 72 | register your activities that support PIP, switch your activity to PIP mode as |
| 73 | needed, and make sure UI elements are hidden and video playback continues when |
| 74 | the activity is in PIP mode.</p> |
| 75 | |
| 76 | <h2 id="declaring">Declaring Your Activity Supports Picture-in-picture</h2> |
| 77 | |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 78 | <p>By default, the system does not automatically support PIP for apps. |
| 79 | If you want support PIP in your app, register your video |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 80 | activity in your manifest by setting |
| 81 | <code>android:supportsPictureInPicture</code> and |
| 82 | <code>android:resizeableActivity</code> to <code>true</code>. Also, specify |
| 83 | that your activity handles layout configuration changes so that your activity |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 84 | doesn't relaunch when layout changes occur during PIP mode transitions.</p> |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 85 | |
| 86 | <pre> |
| 87 | <activity android:name="VideoActivity" |
| 88 | android:resizeableActivity="true" |
| 89 | android:supportsPictureInPicture="true" |
| 90 | android:configChanges= |
| 91 | "screenSize|smallestScreenSize|screenLayout|orientation" |
| 92 | ... |
| 93 | </pre> |
| 94 | |
| 95 | <p>When registering your activity, keep in mind that in PIP mode, your |
| 96 | activity is shown in a small overlay window on a TV screen. Video playback |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 97 | activities with minimal UI provide the best user experience. Activities that |
| 98 | contain small UI elements might not provide a good user experience |
| 99 | when switched to PIP mode, because users can't see details of the UI elements |
| 100 | in the PIP window.</p> |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 101 | |
| 102 | <h2 id="pip_button">Switching Your Activity to Picture-in-picture</h2> |
| 103 | |
| 104 | When you need to switch your activity into PIP mode, call |
Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 105 | {@link android.app.Activity#enterPictureInPictureMode |
| 106 | enterPictureInPictureMode()}. The following example |
Daniel Yu | 82f8756 | 2016-04-05 13:46:49 -0700 | [diff] [blame] | 107 | switches to PIP mode when the user selects a dedicated PIP button on a media |
| 108 | control bar:</p> |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 109 | |
| 110 | <pre> |
| 111 | @Override |
| 112 | public void onActionClicked(Action action) { |
| 113 | if (action.getId() == R.id.lb_control_picture_in_picture) { |
Daniel Yu | 82f8756 | 2016-04-05 13:46:49 -0700 | [diff] [blame] | 114 | getActivity().enterPictureInPictureMode(); |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 115 | return; |
| 116 | } |
| 117 | ... |
| 118 | </pre> |
| 119 | |
| 120 | <p>Adding a PIP button to your media control bar lets your user easily switch |
| 121 | to PIP mode while controlling video playback.</p> |
| 122 | |
Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 123 | <img src="{@docRoot}images/android-7.0/pip-button.png" /> |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 124 | <p class="img-caption"><strong>Figure 1.</strong> A Picture-in-picture |
| 125 | button on a media control bar.</p> |
| 126 | |
Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 127 | <p>Android 7.0 includes a |
| 128 | {@link android.support.v17.leanback.widget.PlaybackControlsRow.PictureInPictureAction |
| 129 | PlaybackControlsRow.PictureInPictureAction} class which defines |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 130 | control bar PIP actions and uses the PIP icon.</p> |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 131 | |
| 132 | <h2 id="handling_ui">Handling UI During Picture-in-picture</h2> |
| 133 | |
| 134 | <p>When your activity enters PIP mode, your activity should only show video |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 135 | playback. Remove UI elements before your activity enters PIP, |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 136 | and restore these elements when your activity becomes full-screen again. |
Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 137 | Override {@link android.app.Activity#onPictureInPictureModeChanged |
| 138 | Activity.onPictureInPictureModeChanged()} or |
| 139 | {@link android.app.Fragment#onPictureInPictureModeChanged |
| 140 | Fragment.onPictureInPictureModeChanged()} and enable or |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 141 | disable your UI elements as needed, for example:</p> |
| 142 | |
| 143 | <pre> |
| 144 | @Override |
Daniel Yu | 82f8756 | 2016-04-05 13:46:49 -0700 | [diff] [blame] | 145 | public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { |
| 146 | if (isInPictureInPictureMode) { |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 147 | // Hide the controls in picture-in-picture mode. |
| 148 | ... |
| 149 | } else { |
| 150 | // Restore the playback UI based on the playback status. |
| 151 | ... |
| 152 | } |
| 153 | } |
| 154 | </pre> |
| 155 | |
| 156 | <h2 id="continuing_playback">Continuing Video Playback While in |
| 157 | Picture-in-picture</h2> |
| 158 | |
| 159 | <p>When your activity switches to PIP, the system considers the activity in a |
Daniel Yu | 83b25f7 | 2016-06-22 09:49:34 -0700 | [diff] [blame] | 160 | paused state, and calls your activity's {@link android.app.Activity#onPause |
| 161 | onPause()} method. Video playback should not be paused and should continue |
| 162 | playing if the activity is paused due to PIP mode.</p> |
| 163 | |
Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 164 | <p>In Android 7.0, you should pause and resume video playback when the system |
Daniel Yu | 83b25f7 | 2016-06-22 09:49:34 -0700 | [diff] [blame] | 165 | calls your activity's {@link android.app.Activity#onStop onStop()} and |
| 166 | {@link android.app.Activity#onStart onStart()}. By doing this, you can avoid |
| 167 | having to check if your app is in PIP mode in |
| 168 | {@link android.app.Activity#onPause onPause()} and explicitly |
| 169 | continuing playback.</p> |
| 170 | |
| 171 | <p>If you have to pause playback in your {@link android.app.Activity#onPause |
| 172 | onPause()} implementation, check for PIP mode by calling {@code |
| 173 | isInPictureInPictureMode()} and handle playback appropriately, for example:</p> |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 174 | |
| 175 | <pre> |
| 176 | @Override |
| 177 | public void onPause() { |
Daniel Yu | 82f8756 | 2016-04-05 13:46:49 -0700 | [diff] [blame] | 178 | // If called while in PIP mode, do not pause playback |
| 179 | if (isInPictureInPictureMode()) { |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 180 | // Continue playback |
| 181 | ... |
| 182 | } |
| 183 | // If paused but not in PIP, pause playback if necessary |
| 184 | ... |
| 185 | } |
| 186 | </pre> |
| 187 | |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 188 | <p>When your activity switches out of PIP mode back to full-screen mode, the |
Daniel Yu | 83b25f7 | 2016-06-22 09:49:34 -0700 | [diff] [blame] | 189 | system resumes your activity and calls your |
| 190 | {@link android.app.Activity#onResume onResume()} method.</p> |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 191 | |
Daniel Yu | c5d9c8f | 2016-03-21 16:41:26 -0700 | [diff] [blame] | 192 | <h2 id="single_playback">Using a Single Playback Activity for |
| 193 | Picture-in-picture</h2> |
| 194 | |
| 195 | <p>In your app, a user might select a new video when browsing for content on |
| 196 | the main screen, while a video playback activity is in PIP mode. Play the new |
| 197 | video in the existing playback activity in full screen mode, instead of |
| 198 | launching a new activity that might confuse the user.</p> |
| 199 | |
| 200 | <p>To ensure a single activity is used for video playback requests and |
| 201 | switched into or out of PIP mode as needed, set the activity's |
| 202 | <code>android:launchMode</code> to <code>singleTask</code> in your manifest: |
| 203 | </p> |
| 204 | |
| 205 | <pre> |
| 206 | <activity android:name="VideoActivity" |
| 207 | ... |
| 208 | android:supportsPictureInPicture="true" |
| 209 | android:launchMode="singleTask" |
| 210 | ... |
| 211 | </pre> |
| 212 | |
| 213 | <p>In your activity, override {@link android.app.Activity#onNewIntent |
Daniel Yu | e0fa75c | 2016-07-19 13:45:38 -0700 | [diff] [blame^] | 214 | onNewIntent()} and handle the new video, stopping any existing video |
Daniel Yu | c5d9c8f | 2016-03-21 16:41:26 -0700 | [diff] [blame] | 215 | playback if needed.</p> |
| 216 | |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 217 | <h2 id="best">Best Practices</h2> |
| 218 | |
| 219 | <p>PIP is intended for activities that play full-screen video. When switching |
| 220 | your activity into PIP mode, avoid showing anything except video content. |
| 221 | Track when your activity enters PIP mode and hide UI elements, as described |
Daniel Yu | d78890f | 2016-03-04 11:16:46 -0800 | [diff] [blame] | 222 | in <a href="#handling_ui">Handling UI During Picture-in-picture</a>.</p> |
Daniel Yu | 192d56d | 2016-02-23 16:43:04 -0800 | [diff] [blame] | 223 | |
| 224 | <p>Since the PIP window is shown as a floating window in the corner of the |
| 225 | screen, you should avoid showing critical information in the main screen |
| 226 | in any area that can be obscured by the PIP window.</p> |
| 227 | |
| 228 | <p>When an activity is in PIP mode, by default it doesn't get input focus. To |
| 229 | receive input events while in PIP mode, use |
Daniel Yu | 83b25f7 | 2016-06-22 09:49:34 -0700 | [diff] [blame] | 230 | {@link android.media.session.MediaSession#setCallback |
| 231 | MediaSession.setCallback()}. For more information on using |
| 232 | {@link android.media.session.MediaSession#setCallback setCallback()} see |
| 233 | <a href="{@docRoot}training/tv/playback/now-playing.html">Displaying |
| 234 | a Now Playing Card</a>.</p> |
| 235 | |
| 236 | <p>When your app is in PIP mode, video playback in the PIP window can cause |
| 237 | audio interference with another app, such as a music player app or voice search |
| 238 | app. To avoid this, request audio focus when you start playing the video, |
| 239 | and handle audio focus change notifications, as described in |
| 240 | <a href="{@docRoot}training/managing-audio/audio-focus.html">Managing Audio |
| 241 | Focus</a>. If you receive notification of audio focus loss when in PIP mode, |
| 242 | pause or stop video playback.</p> |