For a recent Android project we needed to provide a custom media player based on the design provided for the app. We also needed to add a mute button to the media controls. Here’s how we did it:
Instead of writing your own media controller from scratch, you could start with the MediaController
class that is included in Android, however you’ll need to make a lot of tweaks to the code (such as removing references to com.android.internal.R
).
A better starting point is provided in this StackOverflow post, which I’ll use for this post.
Download VideoControllerView.java
into your project then make the following changes:
minSdkVersion
is less than 14, delete the onInitializeAccessibilityEvent
and onInitializeAccessibilityNodeInfo
methods on lines 561-571.Download media_controller.xml
into your project’s layout folder. This file references a string called description
so either create a string with this name (see 'Create a Description String') or delete the android:contentDescription
references in the layout.
To create a description
string, open strings.xml
and add a line like <string name="description">Media Controls</string>
Download the 4 images into your project’s drawable-xhdpi
folder and name them as ic_media_play.png
, ic_media_pause.png
, ic_media_fullscreen_shrink.png
and ic_media_fullscreen_stretch.png
With these files in place, we can now create a layout for our media player. In the layout folder, create a new XML file called activity_video_player.xml
and copy the following code into it:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/video_container" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black" android:gravity="center_horizontal|center_vertical" android:orientation="vertical" > <FrameLayout android:id="@+id/videoSurfaceContainer" android:layout_width="match_parent" android:layout_height="wrap_content" > <SurfaceView android:id="@+id/videoSurface" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </FrameLayout> </LinearLayout>
Next, create a new Activity
class called VideoPlayerActivity
. We’ll start with some basic code and build up the functionality:
public class VideoPlayerActivity extends Activity implements SurfaceHolder.Callback, MediaPlayer.OnPreparedListener, VideoControllerView.MediaPlayerControl {
SurfaceView videoSurface;
MediaPlayer player;
VideoControllerView controller;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_player);
}
}
Remember to add VideoPlayerActivity
to your manifest file. The first thing to do inside onCreate
is initialise some variables so add the following code after setContentView()
-
videoSurface = (SurfaceView) findViewById(R.id.videoSurface); SurfaceHolder videoHolder = videoSurface.getHolder(); videoHolder.addCallback(this); player = new MediaPlayer(); controller = new VideoMediaController(this);
Here we’re finding the SurfaceView from our layout and linking its lifecycle to the activity (the addCallback(this)
call is related to the implements SurfaceHolder.Callback
in our class definition. We’ll implement the relevant methods later.
We can now tell our player object the location of the video to play and also link the player’s lifecycle to the activity (setOnPreparedListener(this)
relates to the MediaPlayer.OnPreparedListener
reference in the class definition. Again, we’ll implement the relevant methods later.
try { player.setAudioStreamType(AudioManager.STREAM_MUSIC); player.setDataSource(this, Uri.parse("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")); player.setOnPreparedListener(this); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
You’ll notice that I’ve included an example video URL. Playing this will require internet access so add the following line to your manifest file -
<uses-permission android:name="android.permission.INTERNET" />
We want the controller to appear when we touch the screen so we’ll implement the activity’s onTouchEvent()
method -
@Override public boolean onTouchEvent(MotionEvent event) { controller.show(); return false; }
We’ll now implement the SurfaceHolder.Callback
interface. Add the following code to your activity -
// Implement SurfaceHolder.Callback @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { player.setDisplay(holder); player.prepareAsync(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { } // End SurfaceHolder.Callback
All we’re doing is linking the player to the surface once the surface is ready and then telling the player to prepare itself asynchronously. It’s this asynchronous behaviour that causes us to need the MediaPlayer.OnPreparedListener
interface which we’ll implement next:
// Implement MediaPlayer.OnPreparedListener @Override public void onPrepared(MediaPlayer mp) { controller.setMediaPlayer(this); controller.setAnchorView((FrameLayout) findViewById(R.id.videoSurfaceContainer)); player.start(); } // End MediaPlayer.OnPreparedListener
This code links the media controller with the media player and places the controller in the same container as the player. Finally, we’ll implement the VideoControllerView.MediaPlayerControl
interface:
// Implement VideoMediaController.MediaPlayerControl @Override public boolean canPause() { return true; } @Override public boolean canSeekBackward() { return true; } @Override public boolean canSeekForward() { return true; } @Override public int getBufferPercentage() { return 0; } @Override public int getCurrentPosition() { return player.getCurrentPosition(); } @Override public int getDuration() { return player.getDuration(); } @Override public boolean isPlaying() { return player.isPlaying(); } @Override public void pause() { player.pause(); } @Override public void seekTo(int i) { player.seekTo(i); } @Override public void start() { player.start(); } @Override public boolean isFullScreen() { return false; } @Override public void toggleFullScreen() { } // End VideoMediaController.MediaPlayerControl
These just link the controller buttons with the player so that, for example, clicking play calls the start()
method on the player. The video player activity is now ready to be used. You just need to add a way of launching the activity to your app e.g. add an onClickListenter
on a button which calls:
startActivity(new Intent(this, VideoPlayerActivity.class));
To see our app development work in action, view our portfolio.We’re proud to work with incredible clients, developing forward-thinking, high-performance apps that deliver great results. We hope our work inspires you!
Search over 350 blog posts from our team
Subscribe to our monthly digest of blogs to stay in the loop and come with us on our journey to make things better!