Wednesday, June 13, 2012

Customized Android Media Player

Android comes equipped with a built-in media player API. For one of the projects, I had to customize the Android Media player to:
– re-skin UI
– Retrieve the media from a remote URL source
– integrate Twitter, Facebook and Email support
– play, pause, stop modes for the media player
NOTE: Regard this post, MORE SO on coding the media player, rather than the social integration of Twitter and Facebook.
Screenshot

File: media_player_view.xml

01<?xml version="1.0" encoding="utf-8"?>
02<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
03    android:orientation="vertical" android:layout_gravity="center"
04    android:background="@drawable/background" android:layout_width="fill_parent"
05    android:layout_height="fill_parent">
06 
07    <FrameLayout android:layout_alignParentTop="true"
08        android:id="@+id/top_fragment" android:layout_width="wrap_content"
09        android:layout_height="wrap_content">
10        <ImageView android:id="@+id/mixTapeHeaderLogo" android:src="@drawable/mix_tape_header_logo"
11            android:layout_gravity="center" android:layout_width="wrap_content"
12            android:paddingTop="50dip" android:layout_height="wrap_content" />
13        <include layout="@layout/top_event_date_fragment"
14            android:layout_width="wrap_content" android:layout_height="wrap_content"
15            android:paddingBottom="30dip" />
16    </FrameLayout>
17 
18    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
19        android:id="@+id/scrollView" android:layout_width="fill_parent"
20        android:layout_below="@+id/top_fragment" android:layout_above="@+id/social_media_mix_fragment"
21        android:layout_height="wrap_content">
22        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
23            android:paddingLeft="20dip" android:paddingRight="20dip"
24            android:orientation="vertical" android:layout_width="fill_parent"
25            android:layout_height="wrap_content">
26            <TextView android:id="@+id/musicMixTitle" android:gravity="center"
27                android:paddingBottom="10dip" android:text="Song Name"
28                android:textColor="@color/brown" android:textStyle="bold"
29                android:textSize="32sp" android:layout_width="fill_parent"
30                android:layout_height="wrap_content" />
31            <TextView android:id="@+id/musicMixArtist" android:gravity="center"
32                android:paddingBottom="30dip" android:text="Song Artist"
33                android:textColor="@color/brown" android:textStyle="bold"
34                android:textSize="14sp" android:layout_width="fill_parent"
35                android:layout_height="wrap_content" />
36 
37            <include layout="@layout/media_player_fragment" android:id="@+id/media_player_fragment"
38                android:layout_gravity="center" android:layout_width="fill_parent"
39                android:layout_height="wrap_content" />
40 
41            <TextView android:id="@+id/malibu_disclaimer_fragment_textview"
42                android:text="@string/static_malibu_disclaimer_text"
43                android:textColor="@color/brown" android:gravity="center"
44                android:paddingTop="10dip" android:paddingBottom="10dip"
45                android:layout_width="wrap_content" android:layout_height="wrap_content" />
46        </LinearLayout>
47    </ScrollView>
48 
49    <include layout="@layout/social_media_mix_fragment" android:id="@+id/social_media_mix_fragment"
50        android:layout_gravity="center" android:layout_alignParentBottom="true"
51        android:layout_width="fill_parent" android:layout_height="wrap_content" />
52</RelativeLayout>
File: media_player_fragment.xml

01<?xml version="1.0" encoding="utf-8"?>
02<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03    android:id="@+id/mediaPlayerLayout" android:orientation="vertical"
04    android:paddingBottom="80dip" android:gravity="center"
05    android:layout_width="fill_parent" android:layout_height="wrap_content">
06 
07    <LinearLayout android:orientation="horizontal"
08        android:gravity="center" android:layout_width="fill_parent"
09        android:layout_height="wrap_content">
10 
11        <ImageView android:id="@+id/mediaStopButton" android:src="@drawable/media_stop_button_selector"
12            android:paddingLeft="10dip" android:paddingRight="10dip"
13            android:layout_width="wrap_content" android:layout_height="wrap_content" />
14 
15        <ImageView android:id="@+id/mediaPlayButton" android:src="@drawable/media_play_button_selector"
16            android:paddingLeft="10dip" android:paddingRight="10dip"
17            android:layout_width="wrap_content" android:layout_height="wrap_content" />
18             
19        <ImageView android:id="@+id/mediaPauseButton" android:src="@drawable/media_pause_button_selector"
20            android:paddingLeft="10dip" android:paddingRight="10dip"
21            android:layout_width="wrap_content" android:layout_height="wrap_content" />
22 
23    </LinearLayout>
24</LinearLayout>
File: MediaPlayerView.java

001package com.malibu.views.mediaplayer;
002 
003import java.io.IOException;
004 
005import org.codehaus.jackson.JsonParseException;
006import org.codehaus.jackson.JsonParser;
007import org.codehaus.jackson.map.JsonMappingException;
008import org.codehaus.jackson.map.ObjectMapper;
009 
010import android.app.Activity;
011import android.app.ProgressDialog;
012import android.content.Context;
013import android.content.Intent;
014import android.content.SharedPreferences;
015import android.media.MediaPlayer;
016import android.os.Bundle;
017import android.os.Handler;
018import android.util.Log;
019import android.view.View;
020import android.view.View.OnClickListener;
021import android.widget.ImageView;
022import android.widget.SeekBar;
023import android.widget.SeekBar.OnSeekBarChangeListener;
024import android.widget.TextView;
025import android.widget.Toast;
026 
027import com.facebook.android.AsyncFacebookRunner;
028import com.facebook.android.DialogError;
029import com.facebook.android.Facebook;
030import com.facebook.android.Facebook.DialogListener;
031import com.facebook.android.FacebookError;
032import com.pernod_ricard.malibu_rum.android.R;
033import com.malibu.app.config.Config;
034import com.malibu.app.legal.TermsAndConditionsScreen;
035import com.malibu.app.utils.AuthHttpRequest;
036import com.malibu.controllers.social.Twitter;
037import com.malibu.models.music.MusicMix;
038 
039/**
040 * @author wdavid01
041 *
042 */
043public class MediaPlayerScreen extends Activity implements OnClickListener, OnSeekBarChangeListener
044  {
045    private static final String tag = MediaPlayerScreen.class.getName();
046    private Context context;
047 
048    private MediaPlayer mMediaPlayer = null;
049    private int playbackPosition = 0;
050    private boolean hasPressedPlayed = false;
051    private boolean hasPressedPaused = false;
052    private boolean hasPressedStopped = false;
053    private final boolean hasPressedRewind = false;
054    private final boolean hasPressedForward = false;
055 
056    // Control buttons
057    private ImageView stopButton = null;
058    private ImageView playButton = null;
059    private ImageView pauseButton = null;
060 
061    // Social media buttons
062    private TextView disclaimer;
063    private ImageView socialMediaFacebookButton = null;
064    private ImageView socialMediaTwitterButton = null;
065    private ImageView socialMediaEmailButton = null;
066 
067    // Remote Music File
068    private String mediaUrl = "http://akamai.mindblossom.net/malibu/music/malibu.mp3";
069    private TextView topEventDateTextview;
070    private SharedPreferences prefs;
071    private Facebook facebook;
072    private AsyncFacebookRunner asyncRunner;
073 
074    private final Handler handler = new Handler();
075    private final Runnable runner = new Runnable()
076      {
077        @Override
078        public void run()
079          {
080            setData();
081          }
082      };
083    private MusicMix mix;
084    private ProgressDialog dialog;
085    private TextView socialMediaShare;
086    private TextView musicMixTitle;
087    private TextView musicMixArtist;
088 
089    @Override
090    public void onCreate(Bundle savedInstanceState)
091      {
092        super.onCreate(savedInstanceState);
093        Log.d(tag, getResources().getString(R.string.CREATING_VIEW) + MediaPlayerScreen.class.getName());
094        setContentView(R.layout.media_player_view);
095        context = this;
096 
097        prefs = getSharedPreferences(Config.PREFS, Context.MODE_PRIVATE);
098 
099        facebook = new Facebook(Config.FACEBOOK_APP_ID);
100        facebook.setAccessToken(prefs.getString(Config.FACEBOOK_ACCESS_TOKEN, null));
101        facebook.setAccessExpires(prefs.getLong(Config.FACEBOOK_EXPIRES, 0));
102 
103        asyncRunner = new AsyncFacebookRunner(facebook);
104 
105        topEventDateTextview = (TextView) findViewById(R.id.top_event_date_textview);
106        topEventDateTextview.setText(prefs.getString(Config.PREFS_CURRENT_EVENT_TITLE, ""));
107 
108        socialMediaShare = (TextView) findViewById(R.id.social_media_share);
109        socialMediaShare.setOnClickListener(this);
110 
111        musicMixTitle = (TextView) findViewById(R.id.musicMixTitle);
112        musicMixArtist = (TextView) findViewById(R.id.musicMixArtist);
113 
114        // Initial Play button
115        pauseButton = (ImageView) this.findViewById(R.id.mediaPauseButton);
116        pauseButton.setOnClickListener(this);
117        playButton = (ImageView) this.findViewById(R.id.mediaPlayButton);
118        playButton.setOnClickListener(this);
119        stopButton = (ImageView) this.findViewById(R.id.mediaStopButton);
120        stopButton.setOnClickListener(this);
121        // Init bottom navigation fragment
122        initBottomNavigationFragment();
123 
124        getData();
125      }
126 
127    @Override
128    public void onDestroy()
129      {
130        super.onDestroy();
131        Log.d(tag, getResources().getString(R.string.DESTROYING_VIEW));
132        killMediaPlayer();
133      }
134 
135    public void getData()
136      {
137        dialog = ProgressDialog.show(this, "", getResources().getString(R.string.Loading));
138        Thread thread = new Thread()
139          {
140 
141            @Override
142            public void run()
143              {
144                try
145                  {
146                    JsonParser jp = AuthHttpRequest.getJsonParser("musicMix", "mix");
147                    ObjectMapper mapper = new ObjectMapper();
148 
149                    mix = mapper.readValue(jp, MusicMix.class);
150                    handler.post(runner);
151                  }
152                catch (JsonParseException e)
153                  {
154                    e.printStackTrace();
155                  }
156                catch (JsonMappingException e)
157                  {
158                    e.printStackTrace();
159                  }
160                catch (IOException e)
161                  {
162                    e.printStackTrace();
163                  }
164              }
165          };
166        thread.start();
167      }
168 
169    private void setData()
170      {
171        if (mix != null)
172          {
173            musicMixArtist.setText(mix.attendeeName);
174            musicMixTitle.setText(mix.name);
175            mediaUrl = mix.url;
176          }
177        dialog.dismiss();
178      }
179 
180    private void initBottomNavigationFragment()
181      {
182        disclaimer = (TextView) this.findViewById(R.id.malibu_disclaimer_fragment_textview);
183        disclaimer.setOnClickListener(this);
184        socialMediaFacebookButton = (ImageView) this.findViewById(R.id.social_media_facebook);
185        socialMediaFacebookButton.setOnClickListener(this);
186        socialMediaTwitterButton = (ImageView) this.findViewById(R.id.social_media_twitter);
187        socialMediaTwitterButton.setOnClickListener(this);
188        socialMediaEmailButton = (ImageView) this.findViewById(R.id.social_media_email);
189        socialMediaEmailButton.setOnClickListener(this);
190      }
191 
192    private void killMediaPlayer()
193      {
194        Log.d(tag, "Closing MediaPlayer ....!");
195        if (mMediaPlayer != null)
196          {
197            try
198              {
199                Log.d(tag, "Releasing MediaPlayer ....!");
200                mMediaPlayer.release();
201              }
202            catch (Exception e)
203              {
204                Log.d(tag, e.getMessage());
205              }
206          }
207      }
208 
209    private void startPlay(String url) throws IllegalArgumentException, IllegalStateException, IOException
210      {
211 
212        Log.d(tag, mediaUrl);
213 
214        if (hasPressedPaused == true && hasPressedPlayed == true)
215          {
216 
217            playbackPosition = mMediaPlayer.getCurrentPosition();
218            Log.d(tag, "Resume @: " + playbackPosition);
219            mMediaPlayer.seekTo(playbackPosition);
220            mMediaPlayer.start();
221          }
222        else if (hasPressedStopped == true)
223          {
224            Log.d(tag, "Start...");
225            killMediaPlayer();
226 
227            Log.d(tag, "Starting MediaPlayer ....!");
228 
229            mMediaPlayer = new MediaPlayer();
230            mMediaPlayer.setDataSource(mediaUrl);
231            mMediaPlayer.prepare();
232            mMediaPlayer.start();
233            hasPressedPlayed = true;
234          }
235        else
236          {
237            Log.d(tag, "Start...");
238            killMediaPlayer();
239 
240            Log.d(tag, "Starting MediaPlayer ....!");
241            mMediaPlayer = new MediaPlayer();
242            mMediaPlayer.setDataSource(mediaUrl);
243            mMediaPlayer.prepare();
244            mMediaPlayer.start();
245            hasPressedPlayed = true;
246          }
247      }
248 
249    private void pause()
250      {
251        Log.d(tag, "Pausing media player...");
252        mMediaPlayer.pause();
253        playbackPosition = mMediaPlayer.getCurrentPosition();
254        hasPressedPaused = true;
255      }
256 
257    @Override
258    public void onClick(View v)
259      {
260        if (v == disclaimer)
261          {
262            Intent intent = new Intent(this, TermsAndConditionsScreen.class);
263            startActivity(intent);
264          }
265 
266        if (v == playButton)
267          {
268            try
269              {
270                startPlay(getMediaUrl());
271              }
272            catch (IllegalArgumentException e)
273              {
274                e.printStackTrace();
275              }
276            catch (IllegalStateException e)
277              {
278                e.printStackTrace();
279              }
280            catch (IOException e)
281              {
282                e.printStackTrace();
283              }
284          }
285        if (v == pauseButton)
286          {
287            if (mMediaPlayer != null)
288              {
289                Log.d(tag, "MediaPlayer paused...");
290                pause();
291              }
292          }
293        if (v == stopButton)
294          {
295            if (mMediaPlayer != null)
296              {
297                Log.d(tag, "MediaPlayer stopped...");
298                mMediaPlayer.stop();
299                hasPressedPlayed = false;
300                hasPressedStopped = true;
301                playbackPosition = 0;
302              }
303          }
304 
305        // social media
306        if (v == socialMediaFacebookButton)
307          {
308            Log.d(tag, getResources().getString(R.string.VIEW_CLICKED) + "Facebook Connect ...");
309            Toast.makeText(this, "Facebook Connect", Toast.LENGTH_SHORT).show();
310 
311            String description = "I'm listening to " + mix.name + " by " + mix.attendeeName + ".";
312 
313            Bundle params = new Bundle();
314            params.putString("description", description);
315            params.putString("link", mix.musicMixUrl);
316 
317            facebook.dialog(this, "stream.publish", params, new DialogListener()
318              {
319                @Override
320                public void onComplete(Bundle values)
321                  {
322                  }
323 
324                @Override
325                public void onFacebookError(FacebookError e)
326                  {
327                  }
328 
329                @Override
330                public void onError(DialogError e)
331                  {
332                  }
333 
334                @Override
335                public void onCancel()
336                  {
337                  }
338              });
339          }
340 
341        if (v == socialMediaTwitterButton)
342          {
343            String tweet = "IÕm listening to " + mix.name + " by " + mix.attendeeName + ". " + mix.musicMixUrl + " via #stationinvasion";
344            new Twitter(this, tweet);
345          }
346 
347        if (v == socialMediaEmailButton)
348          {
349            Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
350            String description = "I'm listening to " + mix.name + " by " + mix.attendeeName + ".\n\n" + mix.musicMixUrl;
351 
352            emailIntent.setType("plain/text");
353            emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Listening to a music mix | Malibu Station Invasion");
354            emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, description);
355 
356            startActivity(Intent.createChooser(emailIntent, "Send mail..."));
357          }
358      }
359 
360    private void setMediaUrl(String mediaUrl)
361      {
362        this.mediaUrl = mediaUrl;
363      }
364 
365    private String getMediaUrl()
366      {
367        return mediaUrl;
368      }
369 
370    @Override
371    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
372      {
373        seekBar.setProgress(mMediaPlayer.getCurrentPosition());
374      }
375 
376    @Override
377    public void onStartTrackingTouch(SeekBar seekBar)
378      {
379        // TODO Auto-generated method stub
380 
381      }
382 
383    @Override
384    public void onStopTrackingTouch(SeekBar seekBar)
385      {
386        // TODO Auto-generated method stub
387 
388      }
389  }

No comments:

Post a Comment