Flag This Hub

How to Make a Simple Media Player for Android

By


Let's get to work Android!
Let's get to work Android!

Android Media Player

People ask me all the time how to develop an Android program that can load up and play music. Sadly, there are not a lot of good answers on the internet. The good news is, it's really not all that bad. Today we are going to:

  1. Learn how to implement features of the android.media.MediaPlayer class.
  2. Load Drawables on the fly from the SD card using the createFromPath() function
  3. Learn what assets are and how to incorporate these into our program
  4. Learn how to load media from an SD card
  5. Write a fully functional Media Player program
  6. Become a better Java Monkey!

By the end of this tutorial, you will not only have a fully functional simple media player, but you will also have a clear understanding of how to incorporate media into your apps. Before going any further, make sure your development environment is set up correctly for Android development. If you are unsure, check my guide here.

*** I recommend you download the source code for this project here, as it contains sample tracks and all of the drawables you will need, as well as a cheesy icon I made myself ***

File   New   Android Project
See all 9 photos
File New Android Project
New Android Project (Name, Path, Target)
New Android Project (Name, Path, Target)
New Android Project (Properties)
New Android Project (Properties)
File   Import
File Import
Import Existing Projects into Workspace
Import Existing Projects into Workspace
Browse for the project you want to import and click OK, check the checkbox, and then click Finish
Browse for the project you want to import and click OK, check the checkbox, and then click Finish

Here we go!

When making a media player, a few things come to mind:

  1. There will be multiple tracks to load
  2. Moving from track to track
  3. Play, Pause, and Stop functionality
  4. Shuffle and Loop functionality
  5. The SD Card must be mounted by the phone in order to load music/pictures from it

Doesn't sound too hard right? So let's begin:

  1. Create a new project File > New > Android Project
  2. Name it MyMediaPlayer
  3. Set the MinSdk to 3 and the TargetSdk to 8
  4. Name your package
  5. Eclipse will create the onCreate Activity for you (e.g. 'MyMediaPlayerActivity.java' ) If you want to name it something else, go ahead; it won't hurt anything.

If you have downloaded the source for this project and want to simply import it:

  1. Make sure that the MyMediaPlayer project is in the "Eclipse Projects" folder
  2. File > Import > Existing Projects into Workspace
  3. Browse > MyMediaPlayer > OK
  4. Make sure the checkbox in the "Projects:" box is checked > Finish


Now let's take a look at the manifest file.

Manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.technegames.mymediaplayer"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="8" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MyMediaPlayerActivity" android:configChanges="orientation|keyboardHidden"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
    <uses-permission android:name="android.permission.WAKE_LOCK" />
</manifest>

Manifest

As you can see, the manifest acts like a parental guardian for your App. It is responsible for supplying the versionCode, which is important when uploading applications to the Android Market. It also determines how and when activities can be launched via intent filters. The manifest is also where you specify certain permissions such as internet access. For example, in MyMediaPlayer, we use the WAKE_LOCK permission so that we can keep the phone on while the app is running. Otherwise, the song would pause itself after 20 seconds or so. Without the permission specified in the manifest, the application would crash upon loading because it attempted to utilize a wake lock without permission.

The Android Manifest is an important XML document, so familiarize yourself with it. Eclipse will generate a manifest file for you when creating a new project, so if you created this project from scratch, the only line you would have to add would be the "uses-permission" for the WAKE_LOCK

Alright, while on the topic of XML documents, let's take a look at our main layout. After this we will get to the Java code I promise!

main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:id="@+id/screen" android:layout_width="fill_parent" android:layout_height="fill_parent" >
	<LinearLayout android:orientation="vertical" android:layout_alignParentTop="true" 
		android:layout_alignParentLeft="true" android:layout_width="fill_parent" android:layout_height="fill_parent" 
		android:weightSum="7" >
		<LinearLayout android:layout_weight="6" android:orientation="vertical" android:layout_alignParentTop="true" 
			android:layout_alignParentLeft="true" android:layout_width="fill_parent" android:layout_height="0dip" >
			<ImageView android:id="@+id/bg" android:layout_width="fill_parent" android:layout_height="fill_parent" >
			</ImageView>
		</LinearLayout>
		<LinearLayout android:layout_weight="1" android:id="@+id/buttons" android:orientation="horizontal" android:weightSum="5" 
			android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:gravity="center" 
			android:layout_width="fill_parent" android:layout_height="0dip" android:background="#FFFFFF" >
			<Button android:id="@+id/btnPrevious" android:layout_width="0dip" android:layout_height="fill_parent" 
				android:layout_weight="1" android:background="@drawable/previous" android:layout_margin="5dp" 
				android:onClick="click" />
			<View android:layout_weight="1" android:layout_width="0dip" android:layout_height="0dip" />
			<Button android:id="@+id/btnPlay" android:layout_width="0dip" android:layout_height="fill_parent" 
				android:layout_weight="1" android:background="@drawable/play" android:layout_margin="5dp" 
				android:onClick="click" />
			<View android:layout_weight="1" android:layout_width="0dip" android:layout_height="0dip" />
			<Button android:id="@+id/btnNext" android:layout_width="0dip" android:layout_height="fill_parent" 
				android:layout_weight="1" android:background="@drawable/next" android:layout_margin="5dp" 
				android:onClick="click" />
		</LinearLayout>
	</LinearLayout>
</RelativeLayout>

main

If you are unfamilar with XML, at first glance this code might look very confusing. I assure you it is very simple, and is one of the greatest features of Android. If you are coming into Android Development from a Java Swing background, you will be happy to know that you can completely separate your layout/gui elements from your main code by setting it up in an XMLdocument.

In the MyMediaPlayer program, we use a Relative Layout with Linear Layouts nested inside. The Relative Layout allows us to specify where the Linear Layouts are placed in relation to each other. The topmost Linear Layout is used for the album art. We use the android:layout_alignParentTop="true" and android:layout_alignParentLeft="true" to specify that the album art layout is aligned to the topleft corner of the screen. We use layout_weight to dictate that the layout containing the image takes up 6/7 of the screen, with the remaining 1/7 for the buttons. Speaking of buttons, in each of the buttons there is android:onClick="click". This means that when you click on any of these buttons, they call a function called click in MyMediaPlayerActivity.java, which we will get to in a bit.

The best way to learn Android layouts is to simply play around with one that works. Adjust settings and values and notice the changes. Parent/Child relationships can get very complicated, so it is best to just take it one step at a time. There is a nice Tool online that generates an XML layout based on what you drag-and-drop onto the screen. If you think this tool may help you with Android Layouts, click here to download it!

Alright, Let's dive into some java code!

Music.java

package com.technegames.mymediaplayer;

import java.io.FileDescriptor;
import java.io.IOException;

import android.content.res.AssetFileDescriptor;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;

public class Music implements OnCompletionListener{
	MediaPlayer mediaPlayer;
	boolean isPrepared = false;
	
	public Music(AssetFileDescriptor assetDescriptor){
		mediaPlayer = new MediaPlayer();
		try{
			mediaPlayer.setDataSource(assetDescriptor.getFileDescriptor(), assetDescriptor.getStartOffset(), assetDescriptor.getLength());
			mediaPlayer.prepare();
			isPrepared = true;
			mediaPlayer.setOnCompletionListener(this);
		} catch(Exception ex){
			throw new RuntimeException("Couldn't load music, uh oh!");
		}
	}
	
	public Music(FileDescriptor fileDescriptor){
		mediaPlayer = new MediaPlayer();
		try{
			mediaPlayer.setDataSource(fileDescriptor);
			mediaPlayer.prepare();
			isPrepared = true;
			mediaPlayer.setOnCompletionListener(this);
		} catch(Exception ex){
			throw new RuntimeException("Couldn't load music, uh oh!");
		}
	}
	
	public void onCompletion(MediaPlayer mediaPlayer) {
		synchronized(this){
			isPrepared = false;
		}
	}

	public void play() {
		if(mediaPlayer.isPlaying()){
			return;
		}
		try{
			synchronized(this){
				if(!isPrepared){
					mediaPlayer.prepare();
				}
				mediaPlayer.start();
			}
		} catch(IllegalStateException ex){
			ex.printStackTrace();
		} catch(IOException ex){
			ex.printStackTrace();
		}
	}

	public void stop() {
		mediaPlayer.stop();
		synchronized(this){
			isPrepared = false;
		}
	}
	
	public void switchTracks(){
		mediaPlayer.seekTo(0);
		mediaPlayer.pause();
	}
	
	public void pause() {
		mediaPlayer.pause();
	}

	public boolean isPlaying() {
		return mediaPlayer.isPlaying();
	}
	
	public boolean isLooping() {
		return mediaPlayer.isLooping();
	}
	
	public void setLooping(boolean isLooping) {
		mediaPlayer.setLooping(isLooping);
	}

	public void setVolume(float volumeLeft, float volumeRight) {
		mediaPlayer.setVolume(volumeLeft, volumeRight);
	}

	public void dispose() {
		if(mediaPlayer.isPlaying()){
			stop();
		}
		mediaPlayer.release();
	}
}

Music

Alright, if you gave the above code a good scan, you probably noticed that Music.java is responsible for instantiating our media player and controlling it. It implements the OnCompletionListener so that we can monitor when a song is finished playing. The reason we do all of this in a separate class is because we will instantiate it every time we seek to a different track, rather than trying to load all of the tracks into memory.

Whenever you feel comfortable with the Music class, let's move on to the main attraction, MyMediaPlayerActivity.java

MyMediaPlayerActivity.java Variables

package com.technegames.mymediaplayer;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import android.app.Activity;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

public class MyMediaPlayerActivity extends Activity {
	WakeLock wakeLock;
	private static final String[] EXTENSIONS = { ".mp3", ".mid", ".wav", ".ogg", ".mp4" }; //Playable Extensions
	List<String> trackNames; //Playable Track Titles
	List<String> trackArtworks; //Track artwork names
	AssetManager assets; //Assets (Compiled with APK)
	File path; //directory where music is loaded from on SD Card
	File path2; //directory where album artwork is loaded from on SD Card
	Music track; //currently loaded track
	ImageView bg; //Track artwork
	Button btnPlay; //The play button will need to change from 'play' to 'pause', so we need an instance of it
	Random random; //used for shuffle
	boolean shuffle; //is shuffle mode on?
	boolean isTuning; //is user currently jammin out, if so automatically start playing the next track
	int currentTrack; //index of current track selected
	int type; //0 for loading from assets, 1 for loading from SD card

MyMediaPlayerActivity Variables

Before we discuss the onCreate activity, let's talk about our Class Level variables.

First, we have our wakeLock, which if you recall from earlier is used to keep the phone on. We also have our EXTENSIONS array, which is used to validate that the files we load into our program are actual sound files. The trackNames ArrayList is used to store all of the track names in memory for reference by the media player. The trackArtworks ArrayList matches the trackNames List except it does not contain any extensions. It is used when trying to find a picture with the same name as the track currently playing and if so load that image. The assets are used to retrieve a list of all the files in the 'assets' folder of your program. Assets are the resources that get compiled along with your program. I've included 4 tracks in the source of this program below. The program by default loads up the assets folder. By pressing the MENU button on your phone and then clicking the SOURCE option you can specify the program to load tracks from the 'music' folder on the root of your SD card. The rest of the variables are pretty self explanatory and if not I've included inline comments.

MyMediaPlayerActivity.java onCreate, onResume, onPause

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
		setVolumeControlStream(AudioManager.STREAM_MUSIC);
		PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
		wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "Lexiconda");
        setContentView(R.layout.main);
        
        initialize(0);
    }
    
    @Override
    public void onResume(){
    	super.onResume();
    	wakeLock.acquire();
    }
	
    @Override
	public void onPause(){
		super.onPause();
		wakeLock.release();
		if(track != null){
			if(track.isPlaying()){
				track.pause();
				isTuning = false;
				btnPlay.setBackgroundResource(R.drawable.play);
			}
			if(isFinishing()){
				track.dispose();
				finish();
			}
		} else{
			if(isFinishing()){
				finish();
			}
		}
	}

onCreate, onResume, onPause

These functions override their parent counterparts from Activity class. They are important because they handle the Activity Lifecycle. Lines 4-8 set up full-screen mode and a wakeLock to insure that the phone screen does not turn off (which would result in an onPause event). On Line 9 we set the content view to the layout we created above, meaning we will have an ImageView and 3 Buttons on the screen. The last thing we do in onCreate is call initialize(0). The 0 in the parentheses means that the media player will load files from the program's assets upon load. Change it to a 1 to load from the SD Card upon load. We will look at the initialize method in the next code block.

The onResume method is where we will acquire the wakeLock. We do this in onResume because we release it in the onPause method. If we were to acquire the wakeLock in the onCreate method, it would only be done so once.

The onPause method is fired when the program is interrupted. For example, the phone is receiving a phone call or the user presses the HOME button. To prepare for something like this, we override the onPause method from Activity. If track is not null and it is playing it gets paused and the pause button gets turned into a play button. We could have also chosen to override onStop, but the isFinishing method serves the same purpose. If the program is finishing, we dispose the mediaplayer object and call the Activity's finish() method.

MyMediaPlayerActivity.java initialize, addTracks, loadTrack

    private void initialize(int type){
    	bg = (ImageView) findViewById(R.id.bg);
        btnPlay = (Button) findViewById(R.id.btnPlay);
        btnPlay.setBackgroundResource(R.drawable.play);
    	trackNames = new ArrayList<String>();
    	trackArtworks = new ArrayList<String>();
    	assets = getAssets();
    	currentTrack = 0;
    	shuffle = false;
    	isTuning = false;
    	random = new Random();
    	this.type = type;
    	
    	addTracks(getTracks());
    	loadTrack();
    }
    
    //Generate a String Array that represents all of the files found
    private String[] getTracks(){
    	if(type == 0){
    		try {
    			String[] temp = getAssets().list("");
    			return temp;
    		} catch (IOException e) {
    			e.printStackTrace();
    			Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_LONG).show();
    		}
    	} else if(type == 1){
    		if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) 
        			|| Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY)){
        		path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
        		path2 = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
    			String[] temp = path.list();
    			return temp;
    		} else{
    			Toast.makeText(getBaseContext(), "SD Card is either mounted elsewhere or is unusable", Toast.LENGTH_LONG).show();
    		}
    	}
    	return null;
    }
    
    //Adds the playable files to the trackNames List
    private void addTracks(String[] temp){
    	if(temp != null){
			for(int i = 0; i < temp.length; i++){
				//Only accept files that have one of the extensions in the EXTENSIONS array
				if(trackChecker(temp[i])){
					trackNames.add(temp[i]);
					trackArtworks.add(temp[i].substring(0, temp[i].length()-4));
				}
			}
			Toast.makeText(getBaseContext(), "Loaded " + Integer.toString(trackNames.size()) + " Tracks", Toast.LENGTH_SHORT).show();
		}
    }
    
    //Checks to make sure that the track to be loaded has a correct extenson
    private boolean trackChecker(String trackToTest){
    	for(int j = 0; j < EXTENSIONS.length; j++){
			if(trackToTest.contains(EXTENSIONS[j])){
				return true;
			}
		}
    	return false;
    }
    
    //Loads the track by calling loadMusic
    private void loadTrack(){
    	if(track != null){
    		track.dispose();
    	}
    	if(trackNames.size() > 0){
    		track = loadMusic(type);
    		setImage("drawable/" + trackArtworks.get(currentTrack));
    	}
    }

initialize, addTracks, loadTrack

The initialize method initializes all of our variables so that we do not end up getting null pointer exceptions. The bottom of the method adds all of the tracks from either the compiled assets or from "scard/music", making sure to only add tracks with a valid extension ( e.g. ".mp3", ".mid" ) After that, the loadTrack() method is called.

The loadTrack() method will be called everytime a new track has been selected. In the loadTrack() method, we call loadMusic() and setImage(), which we will take a look at below.

MyMediaPlayerActivity.java loadMusic, setImage

	//loads a Music instance using either a built in asset or an external resource
    private Music loadMusic(int type){
    	switch(type){
    	case 0:
    		try{
    			AssetFileDescriptor assetDescriptor = assets.openFd(trackNames.get(currentTrack));
    			return new Music(assetDescriptor);
    		} catch(IOException e){
    			e.printStackTrace();
    			Toast.makeText(getBaseContext(), "Error Loading " + trackNames.get(currentTrack), Toast.LENGTH_LONG).show();
    		}
    		return null;
    	case 1:
    		try{
    			FileInputStream fis = new FileInputStream(new File(path, trackNames.get(currentTrack)));
    			FileDescriptor fileDescriptor = fis.getFD();
    			return new Music(fileDescriptor);
    		} catch(IOException e){
    			e.printStackTrace();
    			Toast.makeText(getBaseContext(), "Error Loading " + trackNames.get(currentTrack), Toast.LENGTH_LONG).show();
    		}
    		return null;
    	default:
    		return null;
    	}
    }
    
    //Sets the background image to match the track currently playing or a default image
	private void setImage(String name) {
		if(type == 0){
			int imageResource = getResources().getIdentifier(name, null, getPackageName());
		    if(imageResource != 0){
		    	Drawable image = getResources().getDrawable(imageResource);
		    	bg.setImageDrawable(image);
		    } else{
		    	int defaultImageResource = getResources().getIdentifier("drawable/defaultbg", null, getPackageName());
			    if(defaultImageResource != 0){
			    	Drawable image = getResources().getDrawable(defaultImageResource);
			    	bg.setImageDrawable(image);
			    }
		    }
		} else if(type == 1){
			if(new File(path2.getAbsolutePath(), trackArtworks.get(currentTrack) + ".jpg").exists()){
				bg.setImageDrawable(Drawable.createFromPath(path2.getAbsolutePath() + "/" + trackArtworks.get(currentTrack) + ".jpg"));
			} else{
		    	int defaultImageResource = getResources().getIdentifier("drawable/defaultbg", null, getPackageName());
			    if(defaultImageResource != 0){
			    	Drawable image = getResources().getDrawable(defaultImageResource);
			    	bg.setImageDrawable(image);
			    }
		    }
		}
	}

loadMusic, setImage

The loadMusic is probably the most important method in the entire program. You will want to learn the Music.java class and this method because they can translate to any program that needs music, not just a media player.

In our loadMusic() method, we either load up a track from our compiled assets (the ones I include with the download above) or one of your songs located in the "music" folder on your SD Card.

The setImage() method is called immediately the loadMusic() method to change the album art accordingly. In our simple example, it simply scans the "pictures" folder on your SD Card for a picture with same name as the track currently playing excluding the extension and sets it accordingly. If no image can be found, a default image is loaded up from the drawable folder of your project.

Alright, I know we have covered a lot of ground here, but we are almost done. The next two sections cover the user input.

MyMediaPlayerActivity.java Menu, Options, setShuffle

    @Override
    public boolean onCreateOptionsMenu(Menu menu){
		super.onCreateOptionsMenu(menu);
		createMenu(menu);
		return true;
	}
    
    private void createMenu(Menu menu){
		MenuItem miLooping = menu.add(0, 0, 0, "Looping");{
			miLooping.setIcon(R.drawable.looping);
		}
		MenuItem miShuffle = menu.add(0, 1, 1, "Shuffle");{
			miShuffle.setIcon(R.drawable.shuffle);
		}
		MenuItem miStop = menu.add(0, 2, 2, "Stop");{
			miStop.setIcon(R.drawable.stop);
		}
		MenuItem miSource = menu.add(0, 3, 3, "Source");{
			miSource.setIcon(R.drawable.source);
		}
	}
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item){
		switch(item.getItemId()){
		case 0:
			//Set Looping
			synchronized(this){
				if(track.isLooping()){
					track.setLooping(false);
					Toast.makeText(getBaseContext(), "Playing Tracks Sequentially", Toast.LENGTH_SHORT).show();
				} else{
					track.setLooping(true);
					Toast.makeText(getBaseContext(), "Looping " + trackNames.get(currentTrack), Toast.LENGTH_SHORT).show();
				}
			}
			return true;
		case 1:
			//Set Shuffle
			synchronized(this){
				if(shuffle){
					setShuffle(false);
				} else{
					setShuffle(true);
				}
			}
			return true;
		case 2:
			//Stop Music
			synchronized(this){
				track.switchTracks();
				btnPlay.setBackgroundResource(R.drawable.play);
			}
			return true;
		case 3:
			//Change Source from Assets to SD Card and vice versa
			synchronized(this){
				type++;
				if(type > 1){
					type = 0;
				}
			}
			if(type == 0){
				Toast.makeText(getBaseContext(), "Loading Tracks from Assets ", Toast.LENGTH_SHORT).show();
			} else if(type == 1){
				Toast.makeText(getBaseContext(), "Loading Tracks from SD Card", Toast.LENGTH_SHORT).show();
			}
			initialize(type);
			return true;
		default:
			return false;
		}
	}
    
    //Simply sets shuffle to isShuffle and then displays a message for confirmation
    private void setShuffle(boolean isShuffle) {
    	shuffle = isShuffle;
    	if(shuffle){
    		Toast.makeText(getBaseContext(), "Shuffle On", Toast.LENGTH_SHORT).show();
    	} else{
    		Toast.makeText(getBaseContext(), "Shuffle Off", Toast.LENGTH_SHORT).show();
    	}
	}

Menu, Options, setShuffle

First off, we must override a functon called onCreateOptionsMenu(). It is called whenever the Menu Button is pressed on your phone. It is particularly useful if there are extra controls you want to provide to your users without having to clutter the screen with additional buttons. In our onCreateOptionsMenu() method, we call a createMenu() method.

The createMenu() method is responsible for setting populating the menu options. We have 4 menu options in total: "Looping", "Shuffle", "Stop", and "Source"

The onOptionsItemSelected() method provides functionality for these menu options. We use a switch on the item.getItemId() function, which returns an int that distinguishes the options from each other. The "Looping" and "Stop" Options seem pretty self explanatory, so I won't go over those.

When the "Shuffle" Option is clicked, the setShuffle() method is called, which sets shuffle to to either true of false and displays a message for confirmation.

When the "Source" Option is clicked, type is switched from 0 to 1 and vice versa and the initialize() method is called again, except this time with the new type. If the type is set to 1, the SD Card will be loaded up instead of the "assets" folder of your project.

Alright, let's move on to the on-screen controls!

MyMediaPlayerActivity.java click, setTrack, playTrack

    public void click(View view){
		int id = view.getId();
		switch(id){
		case R.id.btnPlay:
			synchronized(this){
				if(isTuning){
					isTuning = false;
					btnPlay.setBackgroundResource(R.drawable.play);
					track.pause();
				} else{
					isTuning = true;
					btnPlay.setBackgroundResource(R.drawable.pause);
					playTrack();
				}
			}
			return;
		case R.id.btnPrevious:
			setTrack(0);
			loadTrack();
			playTrack();
			return;
		case R.id.btnNext:
			setTrack(1);
			loadTrack();
			playTrack();
			return;
		default:
			return;
		}
	}
    
    private void setTrack(int direction){
    	if(direction == 0){
    		currentTrack--;
			if(currentTrack < 0){
				currentTrack = trackNames.size()-1;
			}
    	} else if(direction == 1){
    		currentTrack++;
			if(currentTrack > trackNames.size()-1){
				currentTrack = 0;
			}
    	}
    	if(shuffle){
			int temp = random.nextInt(trackNames.size());
			while(true){
				if(temp != currentTrack){
					currentTrack = temp;
					break;
				}
				temp++;
				if(temp > trackNames.size()-1){
					temp = 0;
				}
			}
		}
    }
    
    //Plays the Track
    private void playTrack(){
    	if(isTuning && track != null){
			track.play();
			Toast.makeText(getBaseContext(), "Playing " + trackNames.get(currentTrack).substring(0, trackNames.get(currentTrack).length()-4), Toast.LENGTH_SHORT).show();
		}
    }

click, setTrack, playTrack, setShuffle

If you recall from earlier when we created our main.xml layout, we talked about the android:onClick="click" in each of the buttons. The click function must be public, have a return type of void, and accept a View instance as a parameter; otherwise you will receive errors. Since all of the buttons call the same function, how do we know which button called it? We set up a switch on the view.getId() function, which returns the id of the button. The Next and Previous Buttons simply call the setTrack() method, the loadTrack() method we discussed earlier, and then the playTrack() method, which plays the newly loaded song if the user was playing the last song.

The setTrack() method moves the currentTrack index either forward or backward. If shuffle is on, then currentTrack index becomes a random number not equal to what it was before the method call.

The playTrack() method simply calls the play() method from Music.java, provided that track is not null.

Run   Run As   Android Application
Run Run As Android Application

Compiling and Running!

Did I forget anything? I think that's about it, if you have been following along and creating your project from scratch, make sure you do not have any pesky red underlines in your code. At this point, we can go ahead and run our program. You can either click Run > Run As > Android Application or right-click your project and Run As > Android Application

Well? Do the Buttons work? How about the Menu Options? Try alternating between Loop and Shuffle Mode. Click the Source button to load up your SD Card, but make sure you disconnect your phone from the PC first!

Playing the built in tracks from the "assets" folder of your project
Playing the built in tracks from the "assets" folder of your project
Playing Songs from the SD Card's "music" folder
Playing Songs from the SD Card's "music" folder

Congratulations

You now have a working custom media player for your Android Device! The program works decently, but there is more to be desired isn't there? If you wanted to publish this it most likely would not perform well on the Market, as there are much better media players available. To make this media player better, you could add a thread that constantly checks if the onCompletionListener event is satisfied and if so go to the next song. Or you could stream album art from a locally stored database instead of relying on the "pictures" folder of the SD card. Animating visualizations would be nice too wouldn't they? The possibilities are endless! As an Android Developer, these choices are entirely yours, so experiment and try new things!

*** If you had any problems with this tutorial, get the source code here and take a look at it. Play around with the code and understand how it all works together. Good Luck! ***

Scan this box with your Android Phone to download Lexiconda!
Scan this box with your Android Phone to download Lexiconda!

Questions?

You can comment this page below and I will respond as quickly as possible.

If you enjoyed this tutorial or it helped you in a project you were working on, consider checking out my game on the Android Market by scanning the QR code on the right -->

Comments

thewebprograms 4 months ago

Excellent article, I like it very much, thank you, I am going to try this now, to create a Media player under my name..thanks again

Alucard_1990 4 months ago

@thewebprograms,

The fun part will be making the media player more dynamic! Also, it would be fun to incorporate a web service of some kind to fetch album art/information!

Have fun!

Leandro Llevado 3 months ago

Wow awesome work!!!!

Helps me alot by the way im creating music visualization could you teach me how?

Nuhendra 7 weeks ago

Grt tutorial..but i want some more functionality like rewind..fast forward and progress slidebar in mediaplayer.

Hope u can help me to achieve this thnks

Alucard_1990 7 weeks ago

@Nuhendra,

I have given you everything you need to figure it out on your own, which is funnest part anyway!

Look at Music.java

Play with the mediaPlayer object, see what you can do with it; whenever you find something useful, turn it into an accessible function that you can call from your activity.

Give it a try!

freezbay 7 weeks ago

Really complete guide. You took the time to write it down. Really appreciate it!

phi 6 weeks ago

I use the API 7. So where path = Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_MUSIC);

path2 = Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_PICTURES) I will have to change how? Thanks

Alucard_1990 6 weeks ago

@phi,

Those Environment String constants are only applicable with API level 8 and above.

Therefore, you gotta do it the old fashioned way.

String path2 = Environment.getExternalStoragePublicDirectory("Pictures/");

phi 6 weeks ago

Thank you so much! But still doesn't work.

Error:The method getExternalStoragePublicDirectory()is undefined for the type Enviroment.

Can you please guide you how to add next and previous button?

Thanks!

Alucard_1990 6 weeks ago

@phi,

Oh wow, I didn't bother to check the method you were using. Yeah you have to be using API Level 8 and above for that one.

If you're using API Level 7 or lower, use getExternalStorageDirectory(), to open a File representing the root of the external storage. You should then write your data in the following directory:

/Android/data/files/

phi 6 weeks ago

I did it, I admire you too!

Thanks you so much!

Please guide me how after the play finished a song can automatically play the next song.

Alucard_1990 6 weeks ago

@phi,

In this method below:

public void onCompletion(MediaPlayer mediaPlayer) {

synchronized(this){

isPrepared = false;

}

}

You will need to send a broadcast to MyMediaPlayerActivity telling it that you have completed a file. When MyMediaPlayerActivity receives this broadcast, it can decide whether or not to play the next track, or any track you want!

phi 6 weeks ago

I really do not understand it. can you do that on the code?

Thanks.

sam 6 weeks ago

Thanks alot for the very detailed tutorial...

but the player force closes everytime I scan the sdcard and press the play button....this is the logcat report:

04-13 04:21:03.588: E/AndroidRuntime(275): FATAL EXCEPTION: main

04-13 04:21:03.588: E/AndroidRuntime(275): java.lang.IllegalStateException: Could not execute method of the activity

04-13 04:21:03.588: E/AndroidRuntime(275): at android.view.View$1.onClick(View.java:2072)

04-13 04:21:03.588: E/AndroidRuntime(275): at android.view.View.performClick(View.java:2408)

04-13 04:21:03.588: E/AndroidRuntime(275): at android.view.View$PerformClick.run(View.java:8816)

04-13 04:21:03.588: E/AndroidRuntime(275): at android.os.Handler.handleCallback(Handler.java:587)

04-13 04:21:03.588: E/AndroidRuntime(275): at android.os.Handler.dispatchMessage(Handler.java:92)

04-13 04:21:03.588: E/AndroidRuntime(275): at android.os.Looper.loop(Looper.java:123)

04-13 04:21:03.588: E/AndroidRuntime(275): at android.app.ActivityThread.main(ActivityThread.java:4627)

04-13 04:21:03.588: E/AndroidRuntime(275): at java.lang.reflect.Method.invokeNative(Native Method)

04-13 04:21:03.588: E/AndroidRuntime(275): at java.lang.reflect.Method.invoke(Method.java:521)

04-13 04:21:03.588: E/AndroidRuntime(275): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)

04-13 04:21:03.588: E/AndroidRuntime(275): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)

04-13 04:21:03.588: E/AndroidRuntime(275): at dalvik.system.NativeStart.main(Native Method)

04-13 04:21:03.588: E/AndroidRuntime(275): Caused by: java.lang.reflect.InvocationTargetException

04-13 04:21:03.588: E/AndroidRuntime(275): at com.technegames.mymediaplayer.MyMediaPlayerActivity.click(MyMediaPlayerActivity.java:309)

04-13 04:21:03.588: E/AndroidRuntime(275): at java.lang.reflect.Method.invokeNative(Native Method)

04-13 04:21:03.588: E/AndroidRuntime(275): at java.lang.reflect.Method.invoke(Method.java:521)

04-13 04:21:03.588: E/AndroidRuntime(275): at android.view.View$1.onClick(View.java:2067)

04-13 04:21:03.588: E/AndroidRuntime(275): ... 11 more

04-13 04:21:03.588: E/AndroidRuntime(275): Caused by: java.lang.IllegalStateException

04-13 04:21:03.588: E/AndroidRuntime(275): at android.media.MediaPlayer.isPlaying(Native Method)

04-13 04:21:03.588: E/AndroidRuntime(275): at com.technegames.mymediaplayer.Music.dispose(Music.java:95)

04-13 04:21:03.588: E/AndroidRuntime(275): at com.technegames.mymediaplayer.MyMediaPlayerActivity.loadTrack(MyMediaPlayerActivity.java:155)

04-13 04:21:03.588: E/AndroidRuntime(275): ... 15 more

What could be the problem?

balbirMathsApps 3 weeks ago

Great program exactley what need formy next project. Could you please explain how to put my own photo and music into the app so that I can send to my family. Thanks.

balbirMathsapps 3 weeks ago

I found your music in the assets. I have been playing around with music in raw folder. Thanks.

vivek.takneek@gmail.com 12 days ago

Your article is much good .

Please send me the code for fetch video from server and run this video on my custom media player. And noone can run these videoes excepting 50 members[they have this application].

Again Thank You Very Much *-*

Alucard_1990 12 days ago

@vivek.takneek@gmail.com,

You are asking a very vague question. I need more context before I can even begin to think about helping you via code.

nomi 5 days ago

Theak hy . par thora complex laga......

shesna 3 days ago

i want to control my pc's media player by using my android phone ?.how it is possible?.

Alucard_1990 3 days ago

@ shesna,

I'm sorry, but that question exceeds the scope of this article. I am prepared to answer your programming questions and help you along starting out, but that is all.

Robin 37 hours ago

This was awesome...can you please help me in creating playlist...

i'll be veru thankful to you.

robinroyal15@gmail.com

Robin 37 hours ago

This was awesome

can you please help me in creating playlist of audio tracks

i'll be very thankful to you

waiting for ur reply

robinroyal15@gmail.com

Submit a Comment
Members and Guests

Sign in or sign up and post using a hubpages account.



    Like this Hub?
    Please wait working