Search code examples
android-youtube-api

ActionBar overlaying YouTubePlayerSupportFragment from YouTube Player API


I've got an activity extending AppCompatActivity, using a theme extending Theme.AppCompat.Light.DarkActionBar. This activity's view is a LinearLayout with a VERTICAL orientation containing the following views:

  1. A FrameLayout containing a YouTubePlayerSupportFragment. Note that I'm calling the following methods on the obtained YouTubePlayer on initialization success:

    setPlayerStyle(PlayerStyle.CHROMELESS)
    addFullscreenControlFlag(YouTubePlayer.FULLSCREEN_FLAG_CUSTOM_LAYOUT)
    

    I also set an onClickListener on the player view, supposed to hide and show the ActionBar on demand.

  2. Another view containing video details and stuff

  3. Some bottom view

Note that the activity calls supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY) and that the LinearLayout has a top padding of android.support.v7.appcompat.R.attr.actionBarSize.

In the AndroidManifest.xml, the activity has android:configChanges="orientation|screenSize" set.

In the activity's onConfigurationChanged method, I'm checking for the screen orientation. If it's landscape, I'm "going fullscreen", if it's portrait, I'm going back to the normal layout.

By "going fullscreen", I mean:

  1. Setting view n°2 and view n°3 visibility to View.GONE
  2. Setting the activity's supportActionBar background color to some transparent color like 0x55000000
  3. Setting getWindow().getDecorView().systemUiVisibility to View.SYSTEM_UI_FLAG_FULLSCREEN
  4. Changing the view n°1 LayoutParams, setting both width and height to MATCH_PARENT
  5. Calling setFullscreen(true) on the YouTubePlayer

Now when I'm in my app, everything works in portrait mode: the video plays, the player is correctly placed just below the ActionBar. It continues to work as expected when going into landscape mode: everything disappears except the player (even the ActionBar). Then I click on the now fullscreen YouTube Player.

The expected result would be that the ActionBar shows up, without interrupting the playback.

The actual result is that both the system status bar and the ActionBar shows up, and the playback is interrupted with the following error:

YouTube video playback stopped due to unauthorized overlay on top of player. The YouTubePlayerView is obscured by android.support.v7.widget.ActionBarContainer{43744840 V.ED.... ........ 0,50-1280,162 #7f0d005b app:id/action_bar_container}. The view is inside the YouTubePlayerView, with the distance in px between each edge of the obscuring view and the YouTubePlayerView being: left: 0, top: 50, right: 0, bottom: 558..

The YouTubePlayer is supposed to let an ActionBar overlay it, as shown in the Overlay ActionBar Demo found in the Sample Applications. So what's the problem here? I thought it was just checking for the view covering it being an ActionBar or not, so I don't understand how this exact error is even possible!


Solution

  • Ok, so as there is really no support for the Android YouTube Player API, I'll answer with how you actually have to do it for it to work.

    First, drop the idea of using the ActionBar. It's an old API, it's ugly to have the controls in it, it's a pain to handle and it doesn't even work if you're not using the sample given by Google. The rest of this answer will explain a working method on how to add controls on top of the YouTube Player, but if you really want to use the ActionBar, you will not find an answer here (nor anywhere else as far as I know).

    Fine. Now, for this to work you can use the YouTube Player normally. I recommend using the Fragment, as having to extend an existing Activity class can be a problem. Remove all controls of the Player using player.setPlayerStyle(PlayerStyle.CHROMELESS) in the OnInitializationSuccess callback.

    Now, to get your controls on top of the Player without it pausing, you need to use a DialogFragment (Using a Dialog alone may work, but again here using the Fragment brings more controls on what you can do):

    • Create your own subclass of DialogFragment
    • Override onCreate(Bundle) and call setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Translucent_NoTitleBar)
    • Override onCreateView(LayoutInflater, ViewGroup, Bundle) and recreate your Activity layout here, however you want. I'm using Kotlin and Anko, so everything is dynamic for me, I have no XML. Anyway, recreate your Layout but without the final views (the result should be entirely invisible).
    • Now, in the view which replaces your player, let's say you used a FrameLayout, you can add your controls like if it was the Player's FrameLayout
    • Override onStart() and call the following methods on the now created Dialog, to make sure your Dialog is fullscreen and transparent:

      Dialog dialog = getDialog();
      dialog.setCanceledOnTouchOutside(false);
      dialog.setCancelable(false);
      dialog.setOnKeyListener(new OnKeyListener() {
      
          @Override
          public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
              if (keyCode == KeyEvent.KEYCODE_BACK) {
                  // TODO Dismiss the Dialog AND call onBackPressed() on the underlying Activity
                  return true;
              } else {
                  return false;
              }
          }
      
      });
      
      Window window = getWindow();
      window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
      window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
      WindowManager.LayoutParams layoutParams = window.getAttributes();
      layoutParams.setDimAmount(0f);
      layoutParams.setFlags(layoutParams.getFlags() | WindowManager.LayoutParams.FLAG_DIM_BEHIND);
      window.setAttributes(layoutParams);
      

    Now you'll have to handle your controls, make them disappear after a timer, etc. Just make sure that you put everything that will cover the Player at some point inside the Dialog(Fragment).

    I'm not sure that everything I listed here is mandatory for this to work, but that's how I did it and it works.

    Note: As I said, I'm working with Kotlin and Anko, I didn't write Java for some months now, so any code presented here may have small typos. Please just tell me if you see any error.

    Bonus: How I handled Fullscreen.

    To handle fullscreen mode, I simply set the visibility of everything but the Player AND the matching view (a FrameLayout for me) in the Dialog to GONE, the make sure that LayoutParams' width and height of those two views are set to MATCH_PARENT. To exit fullscreen, just set all views' visibility you changed back to VISIBLE.