Navigation patterns with PIP

Handling the back-stack

This article examines how to handle navigation patterns when restoring to full screen from a Picture in Picture (PIP) window. For an overview of PIP and how to implement it in your app, check out my previous article.


Entering PIP mode is easy from an Activity; just call enterPictureInPicture. Getting the user back into your app after entering PIP can be a challenge depending on your app’s architecture.

The problem

Imagine a user wants to watch a video and while watching the video they want to check their calendar. It is easy to enter PIP mode on the video so that the user can multi-task. A problem can occur when they finish checking their calendar and restore the video to fullscreen.

When the user taps to go back into the app from the restored full-screen video, the app might unexpectedly exit.

Why does this happen?

Upon entering PIP mode, Android moves only the PIP activity into a new task. This can be a problem if the user should stay within your app after restoring the PIP window back to full screen and tapping back. The app must re-create the back-stack in order to accomplish this. We look at how to know when to re-create the back-stack in a later section.

For example, you want to watch a video in an app. You open the app and view the HomeActivity. From the HomeActivity you view the details of a video and go to the DetailsActivity. After deciding to watch the video, you start the video which launches the PlayerActivity. Now the back-stack is three deep. You press the home button to get back to Android’s launcher to start another app so that you can multi-task while the video continues. You finish your multi-tasking and close the other app. When you restore the PIP window to full-screen, the back-stack will be lost. If you tap to go back you will end up seeing the other app. The diagram below depicts the timeline of this example.

If the app supplies a button to enter PIP mode, then the DetailsActivity stays in the foreground.

Because the PIP activity is moved into it’s own task, you will be able to stay within the app to continue browsing while the video is playing.

This explains why there are two instances of your app in the recent apps.

If there is only one activity, then it stays in the current task.

Let’s we look at the previous example but instead of an activity for each function we have fragments for each function. Since the fragment manager handles the back-stack internally to the app, we will have different behavior. As you go deeper into the app more fragments are added but it is only one activity. The activity will be moved to a new task but since the fragments are contained within the activity the navigation is preserved.

What can you do?

Depending on the application’s architecture, re-creating the back-stack might happen automatically. For example, if your app is built with fragments, the fragment manager manages the back-stack, not the framework. Since the framework splits out at the activity level, the fragment manager with your fragments are not lost.

If your app has an activity for each function, then you will need to keep track of when the back-stack is lost and re-create the back-stack when the user leaves your activity.

You can override onPictureInPictureModeChanged() to find out when the back-stack has been lost.

boolean mBackstackLost = false;
@Override
public void onPictureInPictureModeChanged (
boolean isInPictureInPictureMode, Configuration newConfig) {
if (! isInPictureInPictureMode) {
mBackstackLost = true;
// Restore the full-screen UI.
...
}
}

Once you capture that the back-stack is lost, you can override finish() to recreate the back-stack upon leaving the activity.

@Override
public void finish () {
if( mBackstackLost ){
finishAndRemoveTask();
startActivity(
Intent.makeRestartActivityTask(
new ComponentName(this, MockHomeActivity.class)));
} else {
super.finish();
}
}

If you have multiple entry points to the PIP activity, then re-creating the back-stack can be tricky. For example, a user may be able to start watching a video from your home activity or they can search and view details before watching. So where should they end up when they tap back? The home activity? The details activity?

Potential flows for an application to watch a video.

If your app does not currently keep track of the path the user takes, then you can force the user to land on a particular activity regardless of where they came from. For example, Google Play Movies will return the user to the home activity regardless of how the user found their way to the watcher activity.

Thank you

I want to thank Ryan Johnstone for surfacing this issue. Hopefully this post helps you and many others build better apps.

Test out the behavior

I wrote a sample that demonstrates the different behavior of an app that has a deep back-stack at the time of entering PIP mode. You can download the source on github to play with it yourself.

We would love to hear your feedback! If you would like to continue the discussion, leave a response or talk to me on Twitter.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.