Search code examples
androidandroid-layoutandroid-actionbarandroid-videoviewmediacontroller

Android: Adjusting MediaController to fit system UI doesn't work with Action Bar


I have an Activity that shows a video using VideoView with a custom MediaController. The system UI is hidden until the user taps the screen to show the MediaController (just like in YouTube or Photos app).

The following method takes care of hiding the system UI:

private void hideSystemUi() {
    int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
                View.SYSTEM_UI_FLAG_FULLSCREEN |
                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
        getWindow().getDecorView().setSystemUiVisibility(flags);

}

And the layout of the Activity:

<FrameLayout android:id="@+id/root"
             xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:focusable="true"
             android:keepScreenOn="true">

    <com.google.android.exoplayer.AspectRatioFrameLayout
        android:id="@+id/video_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center">

        <SurfaceView
            android:id="@+id/surface_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"/>

    </com.google.android.exoplayer.AspectRatioFrameLayout>
</FrameLayout>

I then add the MediaController to video_frame via code like so:

mMediaController.setAnchorView(findViewById(R.id.video_frame));

Testing with Nexus 5, I noticed that when showing the MediaController, the soft navigation bar is overlapping the MediaController UI. I managed to fix it by adding the following attribute to the root of MediaController layout:

android:fitsSystemWindows="true"

So far so good. This worked perfectly fine as long as my Activity theme was Theme.AppCompat.NoActionBar. Now that I'm refactoring the app, getting it as per Material design guidelines for featuring, I have to show the ActionBar. So I changed the theme to Theme.AppCompat and guess what? The system UI once again overlaps the MediaController. It seems that android:fitsSystemWindows="true" has absolutely no affect anymore.

I've tried to set android:fitsSystemWindows="true" directly to the Activity theme itself and it seemed to solve the overlapping issue, but then I got another issue where the VideoView was padded in a totally weird way from random direction, instead of scaling to fit the screen as it used to be.

I've spent hours on that and still can't figure out a clean solution. Please note that I need a solution that works for Tablets and Phones together. And as you probably know, on tablets, the soft navigation bar appears at the bottom of the screen while in landscape mode. I prefer to solve the issue via XML if possible.


Solution

  • After examining Android Studio's Fullscreen Activity template, I noticed that in the FullscreenActivity layout they are using android:fitsSystemWindows="true" in a little bit different way than me.

    Here's the layout that comes with the template (activity_fullscreen.xml):

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 xmlns:tools="http://schemas.android.com/tools"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:background="#0099cc"
                 tools:context=".FullscreenActivity">
    
        <!-- The primary full-screen view. This can be replaced with whatever view
             is needed to present your content, e.g. VideoView, SurfaceView,
             TextureView, etc. -->
        <TextView android:id="@+id/fullscreen_content"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:keepScreenOn="true"
                  android:textColor="#33b5e5"
                  android:textStyle="bold"
                  android:textSize="50sp"
                  android:gravity="center"
                  android:text="@string/dummy_content"/>
    
        <!-- This FrameLayout insets its children based on system windows using
             android:fitsSystemWindows. -->
        <FrameLayout android:layout_width="match_parent"
                     android:layout_height="match_parent"
                     android:fitsSystemWindows="true">
    
            <LinearLayout android:id="@+id/fullscreen_content_controls"
                          style="?metaButtonBarStyle"
                          android:layout_width="match_parent"
                          android:layout_height="wrap_content"
                          android:layout_gravity="bottom|center_horizontal"
                          android:background="@color/black_overlay"
                          android:orientation="horizontal"
                          tools:ignore="UselessParent">
    
                <Button android:id="@+id/dummy_button"
                        style="?metaButtonBarButtonStyle"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:text="@string/dummy_button"/>
    
            </LinearLayout>
        </FrameLayout>
    
    </FrameLayout>
    

    I noticed that they have an additional FrameLayout container set with android:fitsSystemWindows="true" that wraps the actual layout container (in my case this was the MediaController) at the bottom of the screen.

    Once I added a FrameLayout to wrap my MediaController layout as shown in the above code snippet my problem is gone! Really weird that I need to have additional layout container to solve this issue. Especially when Android Studio is saying now that I have "Useless Parent".

    So to summarize, here is the fully working solution of how my Activity layout looks like:

    <FrameLayout android:id="@+id/root"
                 xmlns:android="http://schemas.android.com/apk/res/android"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:focusable="true"
                 android:keepScreenOn="true">
    
        <com.google.android.exoplayer.AspectRatioFrameLayout
            android:id="@+id/video_frame"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center">
    
            <SurfaceView
                android:id="@+id/surface_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center"/>
    
        </com.google.android.exoplayer.AspectRatioFrameLayout>
    
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true">
    
            <LinearLayout         android:id="@+id/controller_frame"
                                  android:layout_width="match_parent"
                          android:layout_height="wrap_content"
                          android:layout_gravity="bottom|center_horizontal"
                          android:orientation="horizontal">
    
            </LinearLayout>
    
        </FrameLayout>
    </FrameLayout>
    

    and I hook up the MediaController to controller_frame instead of video_frame as I did previously:

    mMediaController.setAnchorView(findViewById(R.id.controller_frame));