46

We have enterPictureInPictureMode() to move an activity from its current form into a picture-in-picture representation.

What is the means by which we revert that, returning the activity to its normal state, besides destroying the activity? There is no exitPictureInPictureMode(), leavePictureInPictureMode(), or janeGetMeOffThisCrazyPictureInPictureModeThing() method on Activity, and the documentation does not seem to cover an alternative.

I am interested in a solution for Android O, for picture-in-picture mode on mobile devices, though if that works for Android TV too, wonderful!

UPDATE 2017-04-08: If what you want is to return to normal mode when the user clicks the X button to exit picture-in-picture mode, you can do something like this:

  @Override
  public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode);

    if (!isInPictureInPictureMode) {
      getApplication().startActivity(new Intent(this, getClass())
        .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT));
    }
  }

The key bits are to call startActivity() to start the current activity again with FLAG_ACTIVITY_REORDER_TO_FRONT. With a singleTask activity, you need to call that on some non-Activity context, such as the Application singleton. This does not appear to trigger onStop() or onStart(), but it does trigger onNewIntent() (with whatever Intent you pass to startActivity()).

akhilesh0707
  • 6,709
  • 5
  • 44
  • 51
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Are you following the [Using a Single Playback Activity for PIP](https://developer.android.com/training/tv/playback/picture-in-picture.html#single_playback) recommendation? – ianhanniballake Apr 02 '17 at 23:11
  • 1
    @ianhanniballake: Yes. That does not address my concern, though. I am not looking to switch the content. I merely want to expand the content back to its original size, without interrupting what is going on. For example, the user decides to enter PiP mode, realizes that the video is just too small, and wants to expand it again. – CommonsWare Apr 02 '17 at 23:13
  • 1
    Yes, there is no such methods like as you mentioned. I didn't work in PiP much, but you might use [onPictureInPictureModeChanged](https://developer.android.com/reference/android/app/Activity.html#onPictureInPictureModeChanged(boolean)) to track down normal and PIP mode switch events. – Pravin Divraniya Apr 05 '17 at 09:18
  • great ques, made me visit -- [Multiwindow support](https://developer.android.com/guide/topics/ui/multi-window.html) mentions picture in picture on TV and split-window for handheld devices, synonymously .. also, i was just wondering, wouldn't the jane go crazy if tom, dick and harry develop apps which overrule each other forcing/abusing janeEscapeLoopPiP, so.. probably leaving that part to user input/action is better. it sounds like providing a 'disconnectCall' method to the telephony api, when user is in conference – Pararth Apr 07 '17 at 07:06
  • @CommonsWare any luck on getting your issue resolved. – Anchit Mittal Aug 27 '18 at 10:44
  • @AnchitMittal: Google does not think that this is a problem. They closed [my issue](https://issuetracker.google.com/issues/37254459) as "Working as Intended". And I have not experimented with this issue since then to see if they changed anything. – CommonsWare Aug 27 '18 at 10:46
  • i saw that thread. I stuck in this issue while implementation :( – Anchit Mittal Aug 27 '18 at 10:58

6 Answers6

15
  1. Move the activity to the back

    activity.moveTaskToBack(false /* nonRoot */);
    
  2. restore the activity to the front

    Intent startIntent = new Intent(PipActivity.this, PipActivity.class);
    startIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
    activity.startActivity(startIntent);
    
rds
  • 26,253
  • 19
  • 107
  • 134
  • This pattern seems to be used for multiwindow. Hope this helps, I couldn't try myself because all my emulator images are failing with "IllegalStateException: enterPictureInPictureMode: Device doesn't support picture-in-picture mode"… – rds Apr 07 '17 at 23:37
  • That's devious! Unfortunately, I am having difficulty getting `moveTaskToBack()` to work. The PiP activity is `singleTask` (per the docs). `moveTaskToBack()` seems to affect the app's original task, not the PiP activity's task. I'll also probably need to [do this hack](http://stackoverflow.com/a/17431317/115145) as well. I'll keep poking at this, as your approach seems promising. Thanks! – CommonsWare Apr 08 '17 at 00:17
  • I just tried this solution, and `onDestroy()` is called, hence it doesn't answer your question. In fact it's called twice: once in enterPictureInPicture, once when the intent is launched, despite singleTask and FLAG_ACTIVITY_REORDER_TO_FRONT. – rds Apr 10 '17 at 08:36
  • I am granting this answer the bounty, as it comes the closest to having a solution. I am not accepting it, as it is not really a solution. :-) Thanks again, though! – CommonsWare Apr 11 '17 at 22:43
  • I have one issue, I am in picture in picture mode now i click on pip then white square displays to maximize it but on bottom play and pause button showing out side of video/exoplayer , how to display play pause button in center just like whte box to miximize. – Gyan Swaroop Awasthi Sep 15 '22 at 11:33
10

I had the same problem:

  • MainActivity opens VideoPlayerActivity which has PIP mode enabled.
  • Press the back button to go to the PIP mode.
  • Press back button again until I exit from MainActivity.
  • Press close (X) button on the PIP window.
  • Open application, It will open VideoPlayerActivity.

While none of the above solutions work for me, I found out that the only way to listen to X button is to override onStop.

When the activity is restored from PIP, onResume and onPictureInPictureModeChanged are called, when the X button is clicked, the onStop and onPictureInPictureModeChanged are called.

So I tried to call finish() inside onPictureInPictureModeChanged when the onStop is alreay called.

override fun onStop() {
    super.onStop()
    onStopCalled = true
}

override fun onResume() {
    super.onResume()
    onStopCalled = false
}

override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
        if (isInPictureInPictureMode) {
            // ...
        } else {
            if (onStopCalled) {
                finish()
            }
        }
    }
TBG
  • 154
  • 4
  • 18
Saeed Masoumi
  • 8,746
  • 7
  • 57
  • 76
  • 1
    Is it guaranteed `onStop` method will be called before`onPictureInPictureModeChanged `? Somehow, in some devices (with android 8 and 9), the `onPictureInPictureModeChanged` is called before `onStop`, preventing us to finish the activity. I have been struggling with this for days. – TiagoPereira17 Apr 22 '20 at 08:58
  • @TiagoPereira17 I know it's been a while, but any idea if you were able to find any fix for those devices? – mehul bisht Apr 20 '22 at 06:04
3

I don't think the activity can decide to leave Picture-in-picture mode.

From the Android O API preview doc:

The activity entering PIP mode goes into the paused state, but remains started. If the user taps the PIP activity, the system shows a menu for the user to interact with; no touch events reach the activity while it is in the PIP state.

The activity will be notified by onPictureInPictureModeChanged().

rds
  • 26,253
  • 19
  • 107
  • 134
  • 4
    Note: I work at Google (not in Android though) and also couldn't find any call on the `janeGetMeOffThisCrazyPictureInPictureModeThing()` or similar method. – rds Apr 07 '17 at 17:42
  • 1
    The activity should be able to leave PiP mode: simply `finish()` the activity and create a new instance. Even with transitions, this is likely to result in a UI hiccup, which is what I would hope we'd have an API to avoid. Thanks, though! – CommonsWare Apr 07 '17 at 17:51
  • And I really don't consider destroying/recreating instances the same as changing modes. – rds Apr 12 '17 at 16:19
  • 1
    FWIW, I [filed a request to have something added for this case](https://issuetracker.google.com/issues/37254459). – CommonsWare Apr 12 '17 at 16:21
  • @CommonsWare hey any idea about this problem? i am struggling with that – Gustavo Forero Carvajal Jul 23 '20 at 16:27
  • 1
    @GustavoForeroCarvajal: Google declined the feature request, and I have not played with PiP in a couple of years, so I don't know if anything showed up. Sorry! – CommonsWare Jul 23 '20 at 17:01
3

I've found a 100% reliable way to do this.

  1. Set the taskAffinity attribute for your PIP activity in the manifest to something like "com.package.pip". Doesn't matter what, it just has to be different from your package name which is the default task affinity. This will cause it to launch in a completely separate stack as if it were another app altogether.
  2. Whenever you want to exit PIP, launch it with startActivity(new Intent(this, PipActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) from within another activity.

I just looked at what happens when the app's main activity is currently in PIP mode and you launch it from the launcher and thought that I could replicate this, and I indeed could.

Grishka
  • 2,465
  • 1
  • 20
  • 38
0

Official documentation says PIP window comes up with menu which let you toggle to full screen.

The PIP window is 240x135 dp and is shown at the top-most layer in one of the four corners of the screen, chosen by the system. The user can bring up a PIP menu that lets them toggle the PIP window to full-screen, or close the PIP window, by holding down the Home button on the remote. If another video starts playing on the main screen, the PIP window is automatically closed.

And you can override PIP changed event to handle UI elements whenever the user toggles PIP mode.

link to onPictureInPictureModeChanged

@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
    if (isInPictureInPictureMode) {
        // Hide the controls in picture-in-picture mode.
        ...
    } else {
        // Restore the playback UI based on the playback status.
        ...
    }
}
ugur
  • 3,604
  • 3
  • 26
  • 57
  • That seems to be the instructions for TV. On an Android O phone (Nexus 5X), there is a close button, which destroys the activity. There is no remote. Long-pressing the window lets you move it. There does not appear to be a user-accessible means of restoring the activity to normal mode. And none of that indicates how I, programmatically, leave PiP mode, which is the core of this question. Thanks, though! – CommonsWare Apr 03 '17 at 00:21
-1

According to latest answers from another stackoverflow blog https://stackoverflow.com/a/71797433/3842263 , it should be this way. It works good for me.

@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {

    if (getLifecycle().getCurrentState() == Lifecycle.State.CREATED) {
        //when user click on Close button of PIP this will trigger.
        finishAndRemoveTask();

    }
    else if (getLifecycle().getCurrentState() == Lifecycle.State.STARTED){
        //when PIP maximize this will trigger
    }
    super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
}
Yosidroid
  • 2,053
  • 16
  • 15
  • Question is not about maximize/close trigger – Drunken Daddy Jun 21 '22 at 20:08
  • @ Drunken Daddy Yes the question is not about maximizing. It is about how to leave PIP, and the answer is right on the If statement not in the else if statement. You can ignore the else if statement if you want. For unknown reason, the code in the if statement makes you leave PIP. I did it in my app. Works good. – Yosidroid Jun 30 '22 at 14:25