Android Picture in picture mode and backstack management for multiple activities

Alok Verma
MindOrks
Published in
3 min readNov 26, 2018

Hello Guys!!

This blog we will talk about the picture in picture mode, how to implement it and how to manage back stack if there are multiple activities.

Now if the app is containing only one activity and you have to implement pip, it's very simple, all you need to make your activity single task Here is code snippet-

<activity
android:name="HomeActivity"
android:launchMode="singleTask"
android:supportsPictureInPicture="true"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

Override onUserLeaveHint method in Home activity.

override fun onUserLeaveHint() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
if (!isInPictureInPictureMode) {
var pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
Logger.err(TAG, "onUserLeaveHintCalled")
var aspectRatio = Rational(ScreenUtils.getScreenWidth(this), ScreenUtils.getSceenHeight(this))
pictureInPictureParamsBuilder.setAspectRatio(aspectRatio).build()
enterPictureInPictureMode(pictureInPictureParamsBuilder.build())
}
}

As you can see we have to make our single Activity as a single task and provide support for the picture in picture true. The reason behind why we have to make our activity as the singleTask cause if the user is in pip mode and if the user clicks on the app icon, no new task will be created, the same task will be reused and intent will be passed to onNewIntent(Intent intent) method. By this way, we can implement pip in single activity app as we don't have to maintain backstack in a single activity.

Now if we have more than one Activity in-app, and you want to maintain backstack after getting into pip.

Suppose, We have a scenario like Login -> VideoList -> VideoDetail -> VideoPlayer Activity . And after reaching VideoPlayer activity that actually playback activity for Video and when the user goes in pip mode, now user maximize this and leave pip and then click back button, the user directly goes to home screen/last open activity of another app, but we want our user to go to Vod Detail page from which we played VideoPlay. For this scenario, we have to follow these steps-

1 - Add pip support in the manifest for VideoPlayer activity.

2 - Make this activity launch mode to singleTask

3 - Add Taskaffinity for this activity with unique

4 - Set exclude from recent true

<activity
android:name=".ui.videodetail.VodDetailActivity"
android:taskAffinity=".ui.videodetail.VodDetailActivity"
android:supportsPictureInPicture="true"
android:parentActivityName=".ui.dashboard.DashboardActivity"
android:launchMode="singleTask"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:resizeableActivity="true"
android:autoRemoveFromRecents="true"
android:excludeFromRecents="true"
android:allowTaskReparenting="true"
android:theme="@style/AppTheme.NoActionBar"
android:screenOrientation="portrait" />

As you can see, I have set taskAffinity and set exclude from recent true as we don't want to display two task in android app stacks. Single task launch mode and different affinity will start a new task for pip and we have to remove from android app stack as we don't want to display two stacks for same application

Now if the user clicks the back icon on this video player page, first we have to check that if the user is clicking directly back button without going in pip mode, then just call super.finish(), but if the activity has been in pip mode and then we have to check if android app stacks containing task for your app in app stacks then we have to move that task to the front on click of the back button. Code for this-

/**
* Bring up launcher task to front
*/
public static void navToLauncherTask(Context appContext) {
ActivityManager activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
// iterate app tasks available and navigate to launcher task (browse task)
assert activityManager != null;
final List<ActivityManager.AppTask> appTasks = activityManager.getAppTasks();
for (ActivityManager.AppTask task : appTasks) {
final Intent baseIntent = task.getTaskInfo().baseIntent;
final Set<String> categories = baseIntent.getCategories();
if (categories != null && categories.contains(Intent.CATEGORY_LAUNCHER)) {
task.moveToFront();
return;
}
}
}

By this way, we can maintain back stack with pipe mode too, but this is not a good practice, if you want to implement pip for your app then try to make single activity app containing multiple fragments.

Thanks,

Alok V.

--

--

Alok Verma
MindOrks

Sr. Software Engineer @OYO, Cinema fanatic, Technology enthusiast.