Fullscreen the Exoplayer
Asked Answered
R

12

27

I try to show the show video (.mp4) with exoplayer in RecyclerView and ViewPager. I show the video controller with custom layout. so far so good.

Now try to fullscreen the video like other video player how use before but can't find a good way in the exoplayer doc.

can anyone help me?

Rosco answered 24/1, 2018 at 11:7 Comment(1)
I've explained how you can achieve this in https://mcmap.net/q/534412/-add-button-for-full-screen-video-with-exo-player using a new full screen Activity on top of the current one. The solution is really simple and works well, but has the downside that it requires downloading bits of the video again, which stops the playback. I also show an alternative solution that avoids this using PlayerView.switchTargetView.Preceding
V
19

The ExoPlayer library currently does not provide a built-in way to enable/disable fullscreen mode. You need to implement this yourself or find some third party code for this I'm afraid.

Essentially two steps are required:

a) Set the window and activity properties to fullscreen and/or immersive mode and (if desired) change to landscape mode. That's not difficult. See this page on Android Developers.

b) Transition rendering to a SimpleExoPlayerView (actually it's about the Surface) which covers the entire viewport in immersive mode. This is more of a challenge to achieve optimal user experience on all API levels.

For an optimal user experience, we want to keep the player playing while doing the transition to full screen and back, to continue playback seamlessly. In a RecyclerView this is kind of tricky to solve for all API levels.

Approach 1

The easiest way is probably having a separate instance of SimpleExoPlayerView which you put on top of your layout as soon as you entered immersive mode (some people open a dialog with the second view for this, some have the second view simply on top in the layout somehow and show/hide it on demand).

Then you detach the player instance from the SimpleExoPlayerView embedded in the RV and attach it to the fullscreen view with a call to a static helper method:

SimpleExoPlayerView.switchTargetView(simpleExoPlayer, oldPlayerView, newPlayerView);

This approach works very well on API >=23. With API 23 the method MediaCodec.setOutputSurface allowing to swap surfaces dynamically has been added. The static method above makes sure this technique is applied. As a result audio and video keep playing and the user experience from entering and exiting fullscreen is super smooth. For API <=22 a new codec instance needs to be create to swap to another surface. This interrupts playback and the user experience for this approach is degraded.

Approach 2

To avoid swapping to another surface on lower API level, you need to use a single surface and transition it to fullscreen somehow. You can either just hide everything else than the SimpleExoPlayerView and set the layout width and height to match it's parent or, you can replace the video view with a placeholder and put it on top and back.

This can work quite well for simple layouts but with complex layouts probably including fragments, viewpagers, recyclerviews this can be a quite intrusive operation causing something to flicker or interrupting playback shortly (on some API levels for instance when removing the player view from the view hierarchy). I've seen this working nicely for various layouts though.

Further approaches/challenges

There might be other and probably better approaches when you dig deeper and/or if you are not using SimpleExoPlayerView at all.

Vassaux answered 29/1, 2018 at 23:27 Comment(2)
SimpleExoPlayerView.switchTargetView is deprecated. You should now use PlayerView.switchTargetView.Preceding
I used the first approach and it worked. I think it should be the correct answer.Carner
G
13

You can easily do this from the XML of the ExoPlayer. Set the following attribute:

app:resize_mode="fill"
Gyre answered 16/1, 2020 at 17:26 Comment(2)
I'm not codding android anymore but I hope to be correct and help the community. In that case, you will receive a correct tick.Rosco
This does not answer the original question, which is how to make a video full screen / immersive. All this does is apply the fill resize so that any streamed video fits the view. If your view is in a recycler it will be limited to whatever size the list item is.Karikaria
S
3

Exoplayer doesn't provide the fullscreen so here is the workaround that worked for me. Here I have restricted the Screen to rotate and manually changing the orientation programmatically change the width and height of the player_view and has set the visibility of the toolbar gone. Used Data Binding and Kotlin.

Follows a code block for XML

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/video_player_container"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        app:layout_constraintTop_toTopOf="parent">


        <com.google.android.exoplayer2.ui.SimpleExoPlayerView
            android:id="@+id/player_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:focusable="true"
            android:keepScreenOn="true"
            android:padding="0dp"
            app:controller_layout_id="@layout/exo_playback_control_view"
            app:layout_constraintTop_toTopOf="parent"
            app:resize_mode="fill"
            app:use_controller="true" />

        <LinearLayout
            android:id="@+id/nextVideoContainer"
            android:layout_width="wrap_content"
            android:layout_height="@dimen/spacing_32"
            android:background="#90000000"
            android:onClick="@{() -> vm.onNextVideo()}"
            android:orientation="horizontal"
            android:paddingLeft="@dimen/spacing_16"
            android:paddingRight="@dimen/spacing_16"
            android:visibility="@{vm.shouldShowNextBtn}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <com.sharedcode.widgets.CustomTextView
                android:id="@+id/next_video_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:text="@string/label_next"
                android:textColor="@android:color/white" />

            <com.sharedcode.widgets.CustomImageView
                android:id="@+id/next_video_image"
                android:layout_width="10dp"
                android:layout_height="10dp"
                android:layout_gravity="center_vertical"
                android:layout_marginStart="8dp"
                android:layout_marginLeft="8dp"
                android:paddingTop="2dp"
                android:src="@drawable/ic_play_next" />
        </LinearLayout>

        <RelativeLayout
            android:id="@+id/retry_container"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="#90000000"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:onClick="@{() -> vm.onRetry()}">

            <com.sharedcode.widgets.CustomTextView
                android:id="@+id/txt_no_internet"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="0dp"
                android:layout_centerInParent="true"
                android:text="@string/txt_no_internet_connection"
                android:textColor="@android:color/white"
                android:textSize="@dimen/font_16" />

            <com.sharedcode.widgets.CustomTextView
                android:layout_width="`wrap_content`"
                android:layout_height="wrap_content"
                android:layout_below="@+id/txt_no_internet"
                android:layout_centerInParent="true"
                android:layout_marginTop="@dimen/spacing_16"
                android:maxHeight="@dimen/spacing_32"
                android:text="@string/txt_tap_to_retry"
                android:textColor="@android:color/white"
                android:textSize="@dimen/font_16" />


        </RelativeLayout>
     </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>

Changes in Android Manifest

<activity android:name=".yourPackage.ClassName"
        android:screenOrientation="portrait"
        android:configChanges="orientation|screenSize|layoutDirection"/>

Check the orientation and rotate it via the following code

mBinding.playerView.exo_fullscreen_btn.setOnClickListener {
        if ((activity as TrainingVideoActivity).checkLandscapeOrientation()) {
            (activity as TrainingVideoActivity).changeOrientationToLandscape(false)

        } else {
            (activity as TrainingVideoActivity).changeOrientationToLandscape(true)

        }

    }

Methods Signature are following

/**
 * Changes the Orientation
 * @param shouldLandscape
 */
fun changeOrientationToLandscape(shouldLandscape: Boolean) {
    requestedOrientation = if (shouldLandscape) {
        ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
    } else {
        ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
    }
}

/**
 * Checks the Orientation
 * And returns true if Landscape else false
 */
fun checkLandscapeOrientation() : Boolean {
    val orientation = resources.configuration.orientation
    return orientation == Configuration.ORIENTATION_LANDSCAPE
}

Now Just Override onConfigurationChanged method in your fragment/activity, since here I have used Fragment. So here I have changed the width/height of the parent container in which ExoplayerView is placed.

/**
 * Used for Showing Video on Full Screen
 * @param newConfig
 * Used EXO_PLAYER_VIEW_HEIGHT as 250
 */
override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        hideToolbarAndShowFullScreen()
        mBinding.playerView.exo_fullscreen_btn.setImageDrawable(ContextCompat.getDrawable(activity!!, R.drawable.ic_shrink))
        val params = mBinding.videoPlayerContainer.layoutParams as ConstraintLayout.LayoutParams
        params.width = ViewGroup.LayoutParams.MATCH_PARENT
        params.height = ViewGroup.LayoutParams.MATCH_PARENT
        mBinding.videoPlayerContainer.layoutParams = params
    } else {
        showToolbarAndClearFullScreen()
        mBinding.playerView.exo_fullscreen_btn.setImageDrawable(ContextCompat.getDrawable(activity!!, R.drawable.ic_fullscreen))
        val params = mBinding.videoPlayerContainer.layoutParams as ConstraintLayout.LayoutParams
        val factor = mBinding.playerView.context.resources.displayMetrics.density
        params.width = ViewGroup.LayoutParams.MATCH_PARENT
        params.height = (EXO_PLAYER_VIEW_HEIGHT * factor).toInt()
        mBinding.videoPlayerContainer.layoutParams = params
    }
}

/**
 * Show the Toolbar and reset to original in the Portrait Mode
 */
private fun showToolbarAndClearFullScreen() {
    (activity as TrainingVideoActivity).supportActionBar!!.show()
    (activity as TrainingVideoActivity).window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)

}

Lastly, XML for the player_controller

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#99000000">


    <LinearLayout
        android:id="@+id/container_play_pause"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:orientation="horizontal">

        <ImageButton
            android:id="@id/exo_play"
            style="@style/ExoMediaButton.Play"
            android:src="@drawable/ic_play_exoplayer"
            />

        <ImageButton
            android:id="@id/exo_pause"
            style="@style/ExoMediaButton.Pause"
            android:src="@drawable/ic_pause_exoplayer"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/seekbar_bottom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_gravity="bottom"
        android:background="#CC000000"
        android:clickable="false"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            tools:ignore="UselessParent">

            <com.sharedcode.widgets.CustomTextView
                android:id="@id/exo_position"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:includeFontPadding="false"
                android:paddingLeft="4dp"
                android:paddingRight="4dp"
                android:textColor="#FFBEBEBE"
                android:textSize="14sp"
                android:textStyle="bold" />

            <com.bnb.paynearby.utils.exoplayer.ExtendedTimebar
                android:id="@id/exo_progress"
                android:layout_width="0dp"
                android:layout_height="50dp"
                android:layout_weight="1"
                app:buffered_color="@color/white"
                app:played_color="@color/color_red"
                app:scrubber_color="@color/color_red"
                app:scrubber_disabled_size="10dp"
                app:unplayed_color="#484848" />

            <com.sharedcode.widgets.CustomTextView
                android:id="@id/exo_duration"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:clickable="false"
                android:includeFontPadding="false"
                android:paddingLeft="4dp"
                android:paddingRight="4dp"
                android:textColor="#FFBEBEBE"
                android:textSize="14sp"
                android:textStyle="bold" />

            <com.sharedcode.widgets.CustomImageView
                android:id="@+id/exo_fullscreen_btn"
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:layout_margin="8dp"
                android:src="@drawable/ic_fullscreen"
                 />

        </LinearLayout>
    </LinearLayout>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Let me know if this works.

Squirearchy answered 4/12, 2019 at 6:37 Comment(0)
W
2

Be sure to set the length and width of the player view to match with its parent.

  1. Use this to hide the status bar: getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

  2. Use this to hide the navigation bar: getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);

Waldron answered 13/2, 2019 at 4:34 Comment(0)
E
2

You can use this library if you want a FullScreen Exoplayer :

https://github.com/Norulab/android-exoplayer-fullscreen

This library contain some extensions functions for ExoPlayer :

val player = SimpleExoPlayer.Builder(context).build()
player.preparePlayer(playerView)
player.setSource(applicationContext, "http://html5videoformatconverter.com/data/images/happyfit2.mp4")
Emalia answered 8/4, 2020 at 8:21 Comment(1)
Keep improving your github repo. It's very goodSafranine
E
2

To show the complete full screen video on expo player use this line in you xml file app:resize_mode="fill"

Elizbeth answered 5/8, 2020 at 14:13 Comment(1)
i am not able to achieve the full screen mode in landscape using ur suggested solution when i have set height to 700px for portrait. I am able to achieve full screen in landscape when i set height to match_parent, but i want to show height of 700px when in portrait. Any help?Acupuncture
C
2

You need to just add the listener to your exoplayer if you do it on Create then add this lane = binding.exoplayerView.setControllerOnFullScreenModeChangedListener {}

this will make the exoplayer button appear, remember to have the version's exoplayer Version or higher = '2.16.1'

Corelli answered 14/4, 2023 at 8:40 Comment(0)
S
1

You can set the player to full screen by setting the params of the player.

        Params params = (LinearLayout.LayoutParams) 
exoPlayerView.getLayoutParams();
        params.width=params.MATCH_PARENT;
        params.height=params.MATCH_PARENT;
        exoPlayerView.setLayoutParams(params);
    }

And hide the the Action bar:

 getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
 getActionBar().hide();
Sontag answered 10/5, 2018 at 9:16 Comment(0)
H
1

You can apply magic-using ExoPlayerView

playerView.setControllerVisibilityListener(new 
PlaybackControlView.VisibilityListener() {
   @Override
   public void onVisibilityChange(int i) {
      // Using Activity
      if (getActionBar() != null)
          if (i == 0) { 
            // code for show
          } else{
            // code for hide
        }
   }
});
Hampson answered 25/9, 2019 at 21:20 Comment(0)
K
1

add the below line in your StyledPlayerView app:resize_mode="fill"

after adding the line your xml should be look like the below code

<com.google.android.exoplayer2.ui.StyledPlayerView
  android:id="@+id/player"
  android:layout_width="match_parent"
  android:layout_height="160"
  app:resize_mode="fill"
  app:controller_layout_id="@layout/custom_controller_exoplayer"
  app:show_buffering="when_playing"
  app:show_shuffle_button="true" />
Kropp answered 12/12, 2022 at 12:22 Comment(1)
This has already been mentioned in other answers.Symphonize
C
1

Try this:

    fun isFullscreen(): Boolean {
        return requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE

    }

    fun setFullscreen(fullscreen: Boolean) {
        val playerView = binding.playerView
        val fullScreenbutton: ImageView = findViewById(R.id.fullscreen)

        if (fullscreen) {
            makeFullScreen(fullScreenbutton, playerView)
            // hideStatusBar()
        } else {
            // hideStatusBar()
            // Set the activity orientation back to portrait
            fullScreenbutton.setImageResource(R.drawable.baseline_fullscreen_24)
            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
            WindowInsetsControllerCompat(
                this.window,
                this.window.decorView
            ).show(WindowInsetsCompat.Type.systemBars())
            playerView.layoutParams.height =
                resources.getDimensionPixelSize(com.intuit.sdp.R.dimen._180sdp)
            playerView.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
        }
    }

    private fun makeFullScreen(
        fullScreenbutton: ImageView,
        playerView: PlayerView
    ) {
        //   hideStatusBar()
        // Set the activity orientation to landscape
        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
        fullScreenbutton.setImageResource(R.drawable.baseline_fullscreen_exit_24)

        WindowInsetsControllerCompat(this.window, this.window.decorView).let { controller ->
            controller.hide(WindowInsetsCompat.Type.systemBars())
            controller.systemBarsBehavior =
                WindowInsetsControllerCompat.BEHAVIOR_DEFAULT
        }

        playerView.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
        playerView.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
    } ```
Counts answered 7/11, 2023 at 12:1 Comment(0)
P
-3

This might be too late but this might help other developers.

Exoplayer doesn't provide the fullscreen functionality by-default. So we need a workaround for this. We can implement this fullscreen functionality using a dialog. We just need to remove and add the Playerview from our activity to the dialog.

mFullScreenDialog = new Dialog(mContext, android.R.style.Theme_Black_NoTitleBar_Fullscreen) {
        public void onBackPressed() {
            if (mExoPlayerFullscreen) {   //mExoPlayerFullscreen is a boolean that we need to maintain to know whether screen is fullscreen or not.
                ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
                closeFullscreenDialog();
            }
            super.onBackPressed();
        }
    };

Here we initialized the dialog.

mFullScreenButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (!mExoPlayerFullscreen) {
                ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
                openFullscreenDialog();
            } else {
                ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
                closeFullscreenDialog();
            }
        }
    });

Here we initialized the fullscreen button of out exoplayer.

private void openFullscreenDialog() {
    ((ViewGroup) playerView.getParent()).removeView(playerView);
    mFullScreenDialog.addContentView(playerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
    mFullScreenIcon.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.exoplayer_shrink));
    mExoPlayerFullscreen = true;
    mFullScreenDialog.show();
}

private void closeFullscreenDialog() {
    ((ViewGroup) playerView.getParent()).removeView(playerView);
    ((FrameLayout) findViewById(R.id.main_media_frame)).addView(playerView);
    mExoPlayerFullscreen = false;
    mFullScreenDialog.dismiss();
    mFullScreenIcon.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.exoplayer_expand));
}

playerview is the Playerview that you will initialize for

<FrameLayout
    android:id="@+id/main_media_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000">

    <com.google.android.exoplayer2.ui.PlayerView
        android:id="@+id/player_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:focusable="true"></com.google.android.exoplayer2.ui.PlayerView>
</FrameLayout>

and in java,

playerView = (PlayerView) findViewById(R.id.player_view);
Passkey answered 24/5, 2019 at 9:13 Comment(1)
please give full example, what is mFullScreenDialog?Airscrew

© 2022 - 2025 — McMap. All rights reserved.