return new Music(fileDescriptor); } catch(IOException e){ e.printStackTrace(); Toast.makeText(getBaseContext(), "Error Loading " + trackNames.get(curren } return null; default: return null; }
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
} //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/default if(defaultImageResource != 0){ Drawable image = getResources().getDrawable(defaultImageResource); bg.setImageDrawable(image); } } } else if(type == 1){ if(new File(path2.getAbsolutePath(), trackArtworks.get(currentTrack) + ".jpg") bg.setImageDrawable(Drawable.createFromPath(path2.getAbsolutePath() + "/" } else{ int defaultImageResource = getResources().getIdentifier("drawable/default if(defaultImageResource != 0){ Drawable image = getResources().getDrawable(defaultImageResource); bg.setImageDrawable(image); } } } }
45 46 47 48 49 50 51 52 53
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 ? 1 2 3 4 5
@Override public boolean onCreateOptionsMenu(Menu menu){ super.onCreateOptionsMenu(menu); createMenu(menu); return true; }
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
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 } else{ track.setLooping(true); Toast.makeText(getBaseContext(), "Looping " + trackNames.get(currentT } } 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.LEN } else if(type == 1){ Toast.makeText(getBaseContext(), "Loading Tracks from SD Card", Toast.LEN } initialize(type); return true; default: return false; }
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
} //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(); } }
78 79 80 81 82 83
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 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
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; }
16 17 18 19 20 21 22 23 24 25
}
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
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).su } }
62 63 64 65
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
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 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 hereand take a look at it. Play around with the code and understand how it all works together. Good Luck! ***