How to Build a Custom Android Media Controller
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 theonInitializeAccessibilityEvent
andonInitializeAccessibilityNodeInfo
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));
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!