{"id":9,"date":"2017-09-11T19:58:38","date_gmt":"2017-09-11T19:58:38","guid":{"rendered":"http:\/\/geoffledak.com\/wordpress\/?p=9"},"modified":"2017-09-12T21:56:59","modified_gmt":"2017-09-12T21:56:59","slug":"how-to-add-a-fullscreen-toggle-button-to-exoplayer-in-android","status":"publish","type":"post","link":"https:\/\/geoffledak.com\/blog\/2017\/09\/11\/how-to-add-a-fullscreen-toggle-button-to-exoplayer-in-android\/","title":{"rendered":"How to Add a Fullscreen Toggle Button to ExoPlayer in Android"},"content":{"rendered":"<p><code><strong>Download the complete source code for this project:<\/strong><br \/>\n- <a href=\"https:\/\/github.com\/GeoffLedak\/ExoplayerFullscreen\/archive\/master.zip\">as a ZIP file<\/a><br \/>\n- <a href=\"https:\/\/github.com\/GeoffLedak\/ExoplayerFullscreen\">view on Github<\/a><\/code><\/p>\n<p><a href=\"https:\/\/github.com\/google\/ExoPlayer\">ExoPlayer<\/a> is a great alternative to Android&#8217;s MediaPlayer API and adds support for HLS, DASH, and SmoothStreaming adaptive playback. While it is far superior to Android&#8217;s default media player, it lacks the ability to easily toggle a video in and out of a full screen mode. Today I&#8217;ll show you a solution to this problem.<\/p>\n<div style=\"width: 480px;\" class=\"wp-video\"><!--[if lt IE 9]><script>document.createElement('video');<\/script><![endif]-->\n<video class=\"wp-video-shortcode\" id=\"video-9-1\" width=\"480\" height=\"270\" loop autoplay preload=\"metadata\" controls=\"controls\"><source type=\"video\/webm\" src=\"https:\/\/geoffledak.com\/blog\/wp-content\/uploads\/2017\/09\/fullscreen-toggle-example.webm?_=1\" \/><a href=\"https:\/\/geoffledak.com\/blog\/wp-content\/uploads\/2017\/09\/fullscreen-toggle-example.webm\">https:\/\/geoffledak.com\/blog\/wp-content\/uploads\/2017\/09\/fullscreen-toggle-example.webm<\/a><\/video><\/div>\n<p>Expand! Shrink!<\/p>\n<p>Rather than creating a new activity, this solution uses the magic of a fullscreen dialog. When the video is toggled into fullscreen mode, the <code>SimpleExoPlayerView<\/code> will be removed from the activity layout, and added to a new dialog. The transition between views is very fast, and there is no audio stuttering or buffering when streaming video.<\/p>\n<p>&nbsp;<\/p>\n<h2>The XML<\/h2>\n<p>First we&#8217;ll add the fullscreen toggle button to the Exoplayer controls. This can be done by overriding the default\u00a0<code>PlaybackControlView<\/code> layout. To do this, copy the <a href=\"https:\/\/github.com\/google\/ExoPlayer\/blob\/release-v2\/library\/ui\/src\/main\/res\/layout\/exo_playback_control_view.xml\">exo_playback_control_view.xml<\/a> file from the exoplayer-ui package into your project&#8217;s layout directory. You&#8217;ll also want some image assets to use for the expand and shrink icons. You can download the icons used in this example here:<\/p>\n<table>\n<tbody>\n<tr>\n<th><img loading=\"lazy\" decoding=\"async\" class=\"alignnone\" src=\"https:\/\/raw.githubusercontent.com\/GeoffLedak\/ExoplayerFullscreen\/master\/app\/src\/main\/res\/drawable\/ic_fullscreen_expand.png\" alt=\"\" width=\"88\" height=\"88\" \/><\/th>\n<td>&#8211; <a href=\"https:\/\/raw.githubusercontent.com\/GeoffLedak\/ExoplayerFullscreen\/master\/app\/src\/main\/res\/drawable\/ic_fullscreen_expand.png\">ic_fullscreen_expand.png<\/a><\/td>\n<\/tr>\n<tr>\n<th><img loading=\"lazy\" decoding=\"async\" class=\"alignnone\" src=\"https:\/\/raw.githubusercontent.com\/GeoffLedak\/ExoplayerFullscreen\/master\/app\/src\/main\/res\/drawable\/ic_fullscreen_skrink.png\" alt=\"\" width=\"88\" height=\"88\" \/><\/th>\n<td>&#8211; <a href=\"https:\/\/raw.githubusercontent.com\/GeoffLedak\/ExoplayerFullscreen\/master\/app\/src\/main\/res\/drawable\/ic_fullscreen_skrink.png\">ic_fullscreen_shrink.png<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Ideally, you&#8217;d want different sized images for different screen densities, but for this example you can just copy these assets into your project&#8217;s drawable directory.<\/p>\n<p>Next, we&#8217;ll edit the <code>exo_playback_control_view.xml<\/code> file. Add the following to the second horizontal <code>LinearLayout<\/code>. This code should go towards the bottom of the file, after the <code>exo_duration<\/code> <code>TextView<\/code> and before the second-to-last closing <code>&lt;\/LinearLayout&gt;<\/code> tag:<\/p>\n<pre>      &lt;FrameLayout\r\n          android:id=\"@+id\/exo_fullscreen_button\"\r\n          android:layout_width=\"32dp\"\r\n          android:layout_height=\"32dp\"\r\n          android:layout_gravity=\"right\"&gt;\r\n\r\n         &lt;ImageView\r\n             android:id=\"@+id\/exo_fullscreen_icon\"\r\n             android:layout_width=\"18dp\"\r\n             android:layout_height=\"18dp\"\r\n             android:layout_gravity=\"center\"\r\n             android:adjustViewBounds=\"true\"\r\n             android:scaleType=\"fitCenter\"\r\n             android:src=\"@drawable\/ic_fullscreen_expand\"\/&gt;\r\n\r\n      &lt;\/FrameLayout&gt;<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-76 size-full\" src=\"https:\/\/geoffledak.com\/blog\/wp-content\/uploads\/2017\/09\/before_small.png\" alt=\"\" width=\"320\" height=\"83\" srcset=\"https:\/\/geoffledak.com\/blog\/wp-content\/uploads\/2017\/09\/before_small.png 320w, https:\/\/geoffledak.com\/blog\/wp-content\/uploads\/2017\/09\/before_small-300x78.png 300w\" sizes=\"auto, (max-width: 320px) 100vw, 320px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-75 size-full\" src=\"https:\/\/geoffledak.com\/blog\/wp-content\/uploads\/2017\/09\/after_small.png\" alt=\"\" width=\"320\" height=\"83\" srcset=\"https:\/\/geoffledak.com\/blog\/wp-content\/uploads\/2017\/09\/after_small.png 320w, https:\/\/geoffledak.com\/blog\/wp-content\/uploads\/2017\/09\/after_small-300x78.png 300w\" sizes=\"auto, (max-width: 320px) 100vw, 320px\" \/><\/p>\n<p>&nbsp;<\/p>\n<h3>Add The Controller Id<\/h3>\n<p>Now copy the <a href=\"https:\/\/github.com\/google\/ExoPlayer\/blob\/release-v2\/library\/ui\/src\/main\/res\/layout\/exo_simple_player_view.xml\">exo_simple_player_view.xml<\/a> file into your project&#8217;s layout directory. Add the following towards the bottom of the file, between the <code>exo_controller_placeholder<\/code> View and the <code>exo_overlay<\/code> FrameLayout:<\/p>\n<pre>&lt;com.google.android.exoplayer2.ui.PlaybackControlView\r\n    android:id=\"@id\/exo_controller\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\" \/&gt;<\/pre>\n<p>This step is necessary in order to get a reference to the <code>PlayBackControlView<\/code> from our activity by calling findViewById on the <code>SimpleExoPlayerView<\/code> object (more on this later.)<\/p>\n<p>&nbsp;<\/p>\n<h3>Add The Player View To MainActivity&#8217;s Layout<\/h3>\n<p>To finish up the XML, we&#8217;ll add SimpleExoPlayerView to our activity. Inside of activity_main.xml, add the following:<\/p>\n<pre>      &lt;FrameLayout\r\n          android:id=\"@+id\/main_media_frame\"\r\n          android:layout_width=\"match_parent\"\r\n          android:layout_height=\"match_parent\"\r\n          android:background=\"#000000\"&gt;\r\n\r\n         &lt;com.google.android.exoplayer2.ui.SimpleExoPlayerView\r\n             android:id=\"@+id\/exoplayer\"\r\n             android:layout_width=\"match_parent\"\r\n             android:layout_height=\"match_parent\"\r\n             android:gravity=\"center\" \/&gt;\r\n\r\n      &lt;\/FrameLayout&gt;<\/pre>\n<p>Adjust the FrameLayout&#8217;s width and height to your liking. The sample project assigns the layout a height of 0dp with a weight of 0.5 so it will fill half of the screen.<\/p>\n<p>We wrap the SimpleExoPlayerView in a FrameLayout so that the view can be removed and re-added when the video is toggled in and out of full screen.<\/p>\n<p>&nbsp;<\/p>\n<h2>The Java<\/h2>\n<p>Ok, onto the Java code. Create a Dialog member variable called mFullScreenDialog. When initializing it, override the dialog&#8217;s onBackPressed() method:<\/p>\n<pre>private void initFullscreenDialog() {\r\n\r\n    mFullScreenDialog = new Dialog(this, android.R.style.Theme_Black_NoTitleBar_Fullscreen) {\r\n        public void onBackPressed() {\r\n            if (mExoPlayerFullscreen)\r\n                closeFullscreenDialog();\r\n            super.onBackPressed();\r\n        }\r\n    };\r\n}<\/pre>\n<p>This allows the user to exit fullscreen mode by either pressing the shrink button in the lower right of their screen, or by using their device&#8217;s back button. Also notice that we passed the <code>android.R.style.Theme_Black_NoTitleBar_Fullscreen<\/code> style into the dialog&#8217;s constructor. This will make the dialog fill the entire screen when it&#8217;s shown.<\/p>\n<p>&nbsp;<\/p>\n<h3>Entering Fullscreen<\/h3>\n<p>Next, create a method called <code>openFullscreenDialog()<\/code>. This method programmatically removes the SimpleExoPlayerView from the activity, adds a new instance of the view to the fullscreen dialog, and shows the dialog:<\/p>\n<pre>private void openFullscreenDialog() {\r\n\r\n    ((ViewGroup) mExoPlayerView.getParent()).removeView(mExoPlayerView);\r\n    mFullScreenDialog.addContentView(mExoPlayerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\r\n    mFullScreenIcon.setImageDrawable(ContextCompat.getDrawable(MainActivity.this, R.drawable.ic_fullscreen_skrink));\r\n    mExoPlayerFullscreen = true;\r\n    mFullScreenDialog.show();\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h3>Exiting Fullscreen<\/h3>\n<p>Now create a method called <code>closeFullscreenDialog()<\/code>. This method adds a new SimpleExoPlayerView to the activity, removes the view from the fullscreen dialog, and dismisses the dialog:<\/p>\n<pre>private void closeFullscreenDialog() {\r\n\r\n    ((ViewGroup) mExoPlayerView.getParent()).removeView(mExoPlayerView);\r\n    ((FrameLayout) findViewById(R.id.main_media_frame)).addView(mExoPlayerView);\r\n    mExoPlayerFullscreen = false;\r\n    mFullScreenDialog.dismiss();\r\n    mFullScreenIcon.setImageDrawable(ContextCompat.getDrawable(MainActivity.this, R.drawable.ic_fullscreen_expand));\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h3>Initializing The Fullscreen Button<\/h3>\n<p>Finally, create a method to initialize the fullscreen button. In order to get a reference to the button, we first need a reference to the player&#8217;s PlaybackControlView. To do this, we call <code>mExoPlayerView.findViewById(R.id.exo_controller)<\/code>. We added a view with this id earlier to <code>exo_simple_player_view.xml<\/code>.<\/p>\n<pre>private void initFullscreenButton() {\r\n\r\n    PlaybackControlView controlView = mExoPlayerView.findViewById(R.id.exo_controller);\r\n    mFullScreenIcon = controlView.findViewById(R.id.exo_fullscreen_icon);\r\n    mFullScreenButton = controlView.findViewById(R.id.exo_fullscreen_button);\r\n    mFullScreenButton.setOnClickListener(new View.OnClickListener() {\r\n        @Override\r\n        public void onClick(View v) {\r\n            if (!mExoPlayerFullscreen)\r\n                openFullscreenDialog();\r\n            else\r\n                closeFullscreenDialog();\r\n        }\r\n    });\r\n}<\/pre>\n<p>To understand why a view with this specific id is necessary, we can take a look at the source code of <code>SimpleExoPlayerView<\/code>. Inside the view&#8217;s constructor we see the following:<\/p>\n<pre>PlaybackControlView customController = (PlaybackControlView) findViewById(R.id.exo_controller);\r\n...\r\nif (customController != null) {\r\n  this.controller = customController;\r\n}\r\n...\r\nelse {\r\n  this.controller = null;\r\n}<\/pre>\n<p>If this id isn&#8217;t found, the PlaybackControlView is created elsewhere programmatically.<\/p>\n<p>&nbsp;<\/p>\n<h3>That&#8217;s It!<\/h3>\n<p>That should be everything you need to add a fullscreen mode to an existing Exoplayer implementation. If you need further examples of how to initialize Exoplayer and tie everything together, you can view the complete source code for the activity <a href=\"https:\/\/github.com\/GeoffLedak\/ExoplayerFullscreen\/blob\/master\/app\/src\/main\/java\/com\/geoffledak\/exoplayerfullscreen\/MainActivity.java\">here<\/a>, or download the complete project on <a href=\"https:\/\/github.com\/GeoffLedak\/ExoplayerFullscreen\">GitHub<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Download the complete source code for this project: &#8211; as a ZIP file &#8211; view on Github ExoPlayer is a great alternative to Android&#8217;s MediaPlayer API and adds support for HLS, DASH, and SmoothStreaming adaptive playback. While it is far superior to Android&#8217;s default media player, it lacks the ability to easily toggle a video &#8230; <span class=\"more\"><a class=\"more-link\" href=\"https:\/\/geoffledak.com\/blog\/2017\/09\/11\/how-to-add-a-fullscreen-toggle-button-to-exoplayer-in-android\/\">[Continue reading&#8230;]<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[4,7,3,6,5,8],"class_list":{"0":"entry","1":"post","2":"publish","3":"author-geoffledak","4":"post-9","6":"format-standard","7":"category-android","8":"post_tag-android","9":"post_tag-button","10":"post_tag-exoplayer","11":"post_tag-full-screen","12":"post_tag-fullscreen","13":"post_tag-media"},"_links":{"self":[{"href":"https:\/\/geoffledak.com\/blog\/wp-json\/wp\/v2\/posts\/9","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/geoffledak.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/geoffledak.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/geoffledak.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/geoffledak.com\/blog\/wp-json\/wp\/v2\/comments?post=9"}],"version-history":[{"count":86,"href":"https:\/\/geoffledak.com\/blog\/wp-json\/wp\/v2\/posts\/9\/revisions"}],"predecessor-version":[{"id":123,"href":"https:\/\/geoffledak.com\/blog\/wp-json\/wp\/v2\/posts\/9\/revisions\/123"}],"wp:attachment":[{"href":"https:\/\/geoffledak.com\/blog\/wp-json\/wp\/v2\/media?parent=9"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/geoffledak.com\/blog\/wp-json\/wp\/v2\/categories?post=9"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/geoffledak.com\/blog\/wp-json\/wp\/v2\/tags?post=9"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}