6

I am using the Android MediaRouter (API 16) and Presentation (API 17) classes to generate & manage a secondary display. I followed the example in ApiDemos to create unique non-mirrored output, and so far it works fine (testing with Nexus 10 connected to HDTV via HDMI cable).

Now, I have a situation where I want the Presentation object created in Activity X to continue running on the secondary display, even if Activity X invokes another Activity Y. In this situation, Activity X is still on the stack, but Activity Y is now on top of it.

The problem is that when Activity Y starts, the physical secondary display reverts to mirroring. When I exit Activity Y, the content of Activity X's Presentation goes back (since I never called dismiss() on it).

So the question is: How can I keep a Presentation attached to a secondary display running on that display, even if a subordinate Activity is running on the local device?

UPDATE: One way I thought of doing this is to instantiate the Presentation object from a background thread; then the subsequent creation of another Activity should not interfere with the content being written by the background thread to its Presentation object. But I'm not sure this will work, as it's generally not allowed to update a UI from a background thread.

Another approach would be to disable the use of secondary displays by the subordinate Activity, if possible, thereby preventing it from reverting the secondary display to mirroring when the new Activity becomes active. But I haven't found a way to do this either. Thanks again for any suggestions.

gcl1
  • 4,070
  • 11
  • 38
  • 55
  • Do you absolutely need Activity X and Activity Y to be separate activities? Couldn't you accomplish the same visual end with a single Activity X using Fragment X1 and Fragment X2? Just a thought... – CommonsWare Feb 26 '13 at 15:05
  • @CommonsWare: thanks for the suggestion. It might be possible to combine X and Y into a single Activity with different Fragments as you suggested, but in this case, not without a lot of surgery. Activity X is actually a SherlockFragmentActivity which is busy managing multiple tabs/fragments (via ViewPager and a FragmentStatePagerAdapter). And Activity Y is one of several Activities that gets invoked by the children Fragments to provide "modal" type operations. Do you think it's worth trying the background thread approach, or is that probably a dead end? Thanks again. – gcl1 Feb 26 '13 at 15:22
  • I'll be surprised if your background thread will work, as your problem doesn't seem to be that the `Presentation` is not being updated, but that it is being replaced with mirroring. – CommonsWare Feb 26 '13 at 15:24
  • @Commonsware: That is exactly right. I know the Presentation is still there, as none of the MediaRouter callbacks are invoked (such as onRoutePresentationDisplayChanged()) - which would indicate that the Presentation had been changed/torn down. Further, when Activity Y finishes and Activity X goes back on top, I can visually see Activity X's Presentation resume in all its glory on the secondary display. Any other ideas? – gcl1 Feb 26 '13 at 15:36
  • I haven't had a chance to play with `Presentation` yet -- it's on the docket for next week if all goes well. Since `Presentation` is actually a specialized type of `Dialog`, I suppose it is not surprising for it to be tied to a single `Activity`. If you cannot rework your UI to have the `Presentation` tied to a single `Activity`, you could always open a fresh `Presentation` from the second `Activity`, though there's could well be a visual hiccup as you make the transition, particularly depending on what you are presenting. – CommonsWare Feb 26 '13 at 16:52
  • Thanks, @CommonsWare. I will keep working with it, too, and update here if I find a better solution. – gcl1 Feb 26 '13 at 18:14
  • @CommonsWare: one more idea/question. You suggested avoiding creating a new Activity by using multiple Fragments within the first Activity (I assume using FragmentTransactions). I might be able to do that, but as I mentioned my Activity X is a SherlockFragmentActivity. So the question is: is it possible to manage one or more Fragments as you suggested, but outside the mechanism used by ViewPager & FragmentStatePagerAdapter to associate Fragments with tabs? Or would this interfere with how the FragmentStatePagerAdapter works? Or maybe I should be looking at using nested Fragments? – gcl1 Feb 26 '13 at 20:30
  • Nested fragments could allow you to have the entire `ViewPager` inside a fragment, to replace all of that with something else later on. – CommonsWare Feb 27 '13 at 00:45

2 Answers2

2

I implemented one of the approaches suggested by @CommonsWare (and independently by Mark Allison in answer to my question on his blog). Thanks for your suggestions!

In review, THE PROBLEM was I couldn't keep a second screen presentation running in the background across Activity invocations on a local device. This was because the Presentation class is implemented as a subclass of Dialog, and is therefore tied to an Activity instance. So when a new Activity started up, the second screen went back to mirroring (instead of displaying other content I was specifically generating for it).

THE SOLUTION was to refactor all "subordinate" Activities into Fragments of the original Activity (i.e., the one that launched the second screen). Then, instead of calling startActivity(), I start/stop the new Fragments using FragmentTransactions. The net effect is that the Activity that started the Presentation is still running, so the secondary display is no longer interrupted when a new Activity starts.

My case was further complicated by the fact that the top level Activity (which starts the second screen) was actually a SherlockFragmentActivity that uses a ViewPager and FragmentStatePagerAdapter -- so I had to cram all this into a Fragment. It also required explicit management of ActionBar tabs, menu items, and home icon.

Overall, I think the code is a little less transparent ... but it works!

NOTE: It's good that Google has implemented a secondary screen interface. But I'm not sure why they did it the way they did. Rather than shoe-horning the Presentation class into Dialog, it would have been nice if they provided a more general solution that could easily run in the background, i.e., regardless of foreground Activities on the device. A solution like this would have saved me from a lot of code refactoring, as described above.

gcl1
  • 4,070
  • 11
  • 38
  • 55
  • The above solution works, but there is still a problem if your app needs to open an external app by passing an Intent (for example, to open the camera or picture gallery). So I'm still looking for other ideas and/or help from Google in the form of an improved interface. All suggestions welcome, thanks! – gcl1 Mar 13 '13 at 13:47
  • Agreed - Ideally, the Presentation implementation should have worked like an independent app for secondary displays. The way it has been done is not too useful. – Price May 14 '15 at 09:13
  • @gcl1 , did u see what has recently changed in Android and adapter your app ? – nmxprime Jun 15 '18 at 15:48
1

Bringing this question back from the dead willing to help someone with the same problem somewhere in time,

I've recently came into a far deeper, but similar, problem: I had to display a presentation anywhere on the system (I work with embedded android) and in the main screen any app could be used.

I first thought of creating a Service that managed the display of the presentation and was initialized at the startup of the application. But the problem was that I couldn't show the presentation because, as you mention, it inherits from a Dialog and the same problem that happens when you call getApplicationContext() when building a dialog, occurred.

My solution was: There is a WindowManager.LayoutParam called TYPE_SYSTEM_ALERT that is used to display alerts like the Low Battery alert Dialog. Using this property you can create a Dialog from a service and display it properly and as the Presentation class is a children of the Dialog, simply setting this property made it work.

The magic happens here:

 WindowManager.LayoutParams l = mPresentation.getWindow()
                    .getAttributes();
 l.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
 mPresentation.show();

Just reminding that in order to achieve that your, application XML should have the SYSTEM_ALERT_WINDOW permission.

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

I think that this should solve your issue as well, but it is a little bit delicated and you need a proper treatment to stop the presentation as soon as you needed.

Davi S.
  • 111
  • 9
  • 6 years later I want to follow up on this solution. I was able to resolve the transition flicker by using the above method on Android 7.1 but I do expect to suffer memory leaks as highlighted by leakcanary. You can likely get around this solution by having a Single Activity architecture implementation and targeting Android 8 or later, neither of which are options for my team. Thanks for sharing @Davi S. – kelvin May 25 '21 at 10:55