Skip to content

Instantly share code, notes, and snippets.

@artemisia-absynthium
Last active November 19, 2022 10:34
Show Gist options
  • Save artemisia-absynthium/e2333a58695dfa7e7eeed62193de8c26 to your computer and use it in GitHub Desktop.
Save artemisia-absynthium/e2333a58695dfa7e7eeed62193de8c26 to your computer and use it in GitHub Desktop.
Android Exoplayer fullscreen feature
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/enclosing_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
tools:context=".FullscreenVideoActivity">
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
...
<activity
android:name=".FullscreenVideoActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="@string/app_name"
android:theme="@style/FullscreenTheme" />
...
...
<color name="black_overlay">#66000000</color>
...
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#CC000000"
android:layoutDirection="ltr"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:paddingTop="4dp">
<ImageButton
android:id="@id/exo_prev"
style="@style/ExoMediaButton.Previous" />
<ImageButton
android:id="@id/exo_rew"
style="@style/ExoMediaButton.Rewind" />
<ImageButton
android:id="@id/exo_shuffle"
style="@style/ExoMediaButton.Shuffle" />
<ImageButton
android:id="@id/exo_repeat_toggle"
style="@style/ExoMediaButton" />
<ImageButton
android:id="@id/exo_play"
style="@style/ExoMediaButton.Play" />
<ImageButton
android:id="@id/exo_pause"
style="@style/ExoMediaButton.Pause" />
<ImageButton
android:id="@id/exo_ffwd"
style="@style/ExoMediaButton.FastForward" />
<ImageButton
android:id="@id/exo_next"
style="@style/ExoMediaButton.Next" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
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.google.android.exoplayer2.ui.DefaultTimeBar
android:id="@id/exo_progress"
android:layout_width="0dp"
android:layout_height="26dp"
android:layout_weight="1" />
<TextView
android:id="@id/exo_duration"
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" />
<FrameLayout
android:id="@+id/exo_fullscreen_button"
android:layout_width="32dp"
android:layout_height="wrap_content"
android:layout_gravity="right">
<ImageView
android:id="@+id/exo_fullscreen_icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_fullscreen" />
</FrameLayout>
</LinearLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<com.google.android.exoplayer2.ui.AspectRatioFrameLayout
android:id="@id/exo_content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
<!-- Video surface will be inserted as the first child of the content frame. -->
<View
android:id="@id/exo_shutter"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black" />
<ImageView
android:id="@id/exo_artwork"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY" />
<com.google.android.exoplayer2.ui.SubtitleView
android:id="@id/exo_subtitles"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.google.android.exoplayer2.ui.AspectRatioFrameLayout>
<FrameLayout
android:id="@id/exo_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.exoplayer2.ui.PlayerControlView
android:id="@id/exo_controller"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</merge>
public class ExoPlayerViewManager {
private static final String TAG = "ExoPlayerViewManager";
public static final String EXTRA_VIDEO_URI = "video_uri";
private static Map<String, ExoPlayerViewManager> instances = new HashMap<>();
private Uri videoUri;
public static ExoPlayerViewManager getInstance(String videoUri) {
ExoPlayerViewManager instance = instances.get(videoUri);
if (instance == null) {
instance = new ExoPlayerViewManager(videoUri);
instances.put(videoUri, instance);
}
return instance;
}
private SimpleExoPlayer player;
private boolean isPlayerPlaying;
private ExoPlayerViewManager(String videoUri) {
this.videoUri = Uri.parse(videoUri);
}
public void prepareExoPlayer(Context context, PlayerView exoPlayerView) {
if (context == null || exoPlayerView == null) {
return;
}
if (player == null) {
// Create a new player if the player is null or
// we want to play a new video
// Do all the standard ExoPlayer code here...
// Prepare the player with the source.
player.prepare(videoSource);
}
player.clearVideoSurface();
player.setVideoSurfaceView((SurfaceView) exoPlayerView.getVideoSurfaceView());
player.seekTo(player.getCurrentPosition() + 1);
exoPlayerView.setPlayer(player);
}
public void releaseVideoPlayer() {
if (player != null) {
player.release();
}
player = null;
}
public void goToBackground() {
if (player != null) {
isPlayerPlaying = player.getPlayWhenReady();
player.setPlayWhenReady(false);
}
}
public void goToForeground() {
if (player != null) {
player.setPlayWhenReady(isPlayerPlaying);
}
}
}
// Fullscreen related code taken from Android Studio blueprint
public class FullscreenVideoActivity extends AppCompatActivity {
/**
* Some older devices needs a small delay between UI widget updates
* and a change of the status and navigation bar.
*/
private static final int UI_ANIMATION_DELAY = 300;
private final Handler mHideHandler = new Handler();
private View mContentView;
private final Runnable mHidePart2Runnable = new Runnable() {
@SuppressLint("InlinedApi")
@Override
public void run() {
// Delayed removal of status and navigation bar
// Note that some of these constants are new as of
// API 19 (KitKat). It is safe to use them, as they are inlined
// at compile-time and do nothing on earlier devices.
mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
}
};
private final Runnable mHideRunnable = new Runnable() {
@Override
public void run() {
hide();
}
};
private String mVideoUri;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fullscreen_video);
mContentView = findViewById(R.id.enclosing_layout);
PlayerView playerView = findViewById(R.id.player_view);
mVideoUri = getIntent().getStringExtra(ExoPlayerViewManager.EXTRA_VIDEO_URI);
ExoPlayerViewManager.getInstance(mVideoUri)
.prepareExoPlayer(this, playerView);
// Set the fullscreen button to "close fullscreen" icon
View controlView = playerView.findViewById(R.id.exo_controller);
ImageView fullscreenIcon = controlView.findViewById(R.id.exo_fullscreen_icon);
fullscreenIcon.setImageResource(R.drawable.exo_controls_fullscreen_exit);
controlView.findViewById(R.id.exo_fullscreen_button)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
@Override
public void onResume() {
super.onResume();
ExoPlayerViewManager.getInstance(mVideoUri).goToForeground();
}
@Override
public void onPause() {
super.onPause();
ExoPlayerViewManager.getInstance(mVideoUri).goToBackground();
}
@Override
public void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Trigger the initial hide() shortly after the activity has been
// created, to briefly hint to the user that UI controls
// are available.
delayedHide();
}
private void hide() {
// Schedule a runnable to remove the status and navigation bar after a delay
mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY);
}
/**
* Schedules a call to hide() in delay milliseconds, canceling any
* previously scheduled calls.
*/
private void delayedHide() {
mHideHandler.removeCallbacks(mHideRunnable);
mHideHandler.postDelayed(mHideRunnable, 100);
}
}
public class MyActivity extends AppCompatActivity {
private List<String> mVideoUrls;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Your activity setup code...
for (String videoUrl : mVideoUrls) {
setupPlayerView(videoView, videoUrl);
}
}
@Override
public void onResume() {
super.onResume();
for (String videoUrl : mVideoUrls) {
ExoPlayerViewManager.getInstance(videoUrl).goToForeground();
}
}
@Override
public void onPause() {
super.onPause();
for (String videoUrl : mVideoUrls) {
ExoPlayerViewManager.getInstance(videoUrl).goToBackground();
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
for (String videoUrl : mVideoUrls) {
ExoPlayerViewManager.getInstance(videoUrl).releaseVideoPlayer();
}
}
private void setupPlayerView(final PlayerView videoView, final String videoUrl) {
ExoPlayerViewManager.getInstance(videoUrl).prepareExoPlayer(getContext(), videoView);
ExoPlayerViewManager.getInstance(videoUrl).goToForeground();
View controlView = videoView.findViewById(R.id.exo_controller);
controlView.findViewById(R.id.exo_fullscreen_button)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getContext(), FullscreenVideoActivity.class);
intent.putExtra(ExoPlayerViewManager.EXTRA_VIDEO_URI, videoUrl);
getActivity().startActivity(intent);
}
});
}
}
...
<style name="FullscreenTheme" parent="AppTheme">
<item name="android:actionBarStyle">@style/FullscreenActionBarStyle</item>
<item name="android:windowActionBarOverlay">true</item>
<item name="android:windowBackground">@null</item>
</style>
<style name="FullscreenActionBarStyle" parent="Widget.AppCompat.ActionBar">
<item name="android:background">@color/black_overlay</item>
</style>
...
@varun7952
Copy link

varun7952 commented Apr 19, 2019

Any idea why i am getting NullPointerException at

View controlView = videoView.findViewById(R.id.exo_controller);  
        controlView.findViewById(R.id.exo_fullscreen_button)// THIS LINE RETURNS NULL
                .setOnClickListener(new View.OnClickListener() { 
   }

@armanbisembaev
Copy link

armanbisembaev commented May 20, 2019

Any idea why i am getting NullPointerException at

View controlView = videoView.findViewById(R.id.exo_controller);  
        controlView.findViewById(R.id.exo_fullscreen_button)// THIS LINE RETURNS NULL
                .setOnClickListener(new View.OnClickListener() { 
   }

use:

videoView.findViewById(R.id.exo_fullscreen_button)

instead

View controlView = videoView.findViewById(R.id.exo_controller);  
controlView.findViewById(R.id.exo_fullscreen_button)

@carlosesteven
Copy link

carlosesteven commented Jun 15, 2019

I try implement this, but I got a problem, PlayerView first time start ok, but after tap fullscreen button and then tap to go back, PlayerView set the background to black and at background just playing audio witout playing the video.

MY CODE
https://github.com/carlosesteven/exo_player_full_screen

SCREENSHOT
image

@brian56
Copy link

brian56 commented Sep 7, 2019

Hi. Thanks for your contribution. I have successful implemented the full screen feature in my app base on your tutorial.
But I'm stuck on adding a custom Player.EventListener. I created an instance of Player.EventListener in my MainActivity, and add it to the ExoPlayerViewManager. But when I play video, it never goes to the eventListener that I've added.
I've debugged, the eventListener was not null, it was added to the ExoPlayerViewManager successfully, but still didn't know why it never reaches to the code that I implement in my eventListener.
If you guys did face this issue, please help me. Thank you.

@brian56
Copy link

brian56 commented Sep 7, 2019

I try implement this, but I got a problem, PlayerView first time start ok, but after tap fullscreen button and then tap to go back, PlayerView set the background to black and at background just playing audio witout playing the video.

MY CODE
https://github.com/carlosesteven/exo_player_full_screen

SCREENSHOT
image

Hi, I faced this issue. You can try to add these lines of code in onResume in MainActivity before calling gotoForeground():
ExoPlayerVideoHandler.getInstance() .prepareExoPlayerForUri(rootView.getContext(), Uri.parse(videoUrl), exoPlayerView);
You can read more in this article: https://medium.com/tall-programmer/fullscreen-functionality-with-android-exoplayer-5fddad45509f

@carlosesteven
Copy link

Hi, I faced this issue. You can try to add these lines of code in onResume in MainActivity before calling gotoForeground():
ExoPlayerVideoHandler.getInstance() .prepareExoPlayerForUri(rootView.getContext(), Uri.parse(videoUrl), exoPlayerView);
You can read more in this article: https://medium.com/tall-programmer/fullscreen-functionality-with-android-exoplayer-5fddad45509f

ExoPlayerViewManager.getInstance( videoUrl ) .prepareExoPlayer(this, videoView);

That fixed my problem, thanks.

@ZakAnun
Copy link

ZakAnun commented May 15, 2020

Thanks for your contribution. It works for me~

@akshat9902
Copy link

akshat9902 commented Nov 19, 2022

can we open video in fragment full screen using this library???

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment