How to Build a Custom Android Media Controller

Default blog image of logo on blue

This article will explain the process we went through to build a custom Android media controller, as well as a sample project to help you build your own.

How to Build a Custom Android Media Player from Scratch

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:

Start with the Media Controller

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 MediaControllerView

Download VideoControllerView.java into your project then make the following changes:

  • Change the package name to match your project
  • Either change the last import line to match your project or delete the line if it’s not necessary
  • If your project’s minSdkVersion is less than 14, delete the onInitializeAccessibilityEvent and onInitializeAccessibilityNodeInfo methods on lines 561-571.

Download MediaController

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.

Create a Description String

To create a description string, open strings.xml and add a line like <string name="description">Media Controls</string>

Download Images

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

Create a Media Player Layout

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>

Create a New Activity Class

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.

Object Location and Lifecycle Linking

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" />

Make Sure the Controller Appears

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

Implement VideoControllerView

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));

Download the sample project

View Our Projects

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!


Looking for something else?

Search over 400 blog posts from our team

Want to hear more?

Subscribe to our monthly digest of blogs to stay in the loop and come with us on our journey to make things better!