5 package com.andrew.apollo;
\r
7 import android.content.BroadcastReceiver;
\r
8 import android.content.Context;
\r
9 import android.content.Intent;
\r
10 import android.content.IntentFilter;
\r
11 import android.os.AsyncTask;
\r
12 import android.os.Bundle;
\r
13 import android.os.Handler;
\r
14 import android.os.Message;
\r
15 import android.os.RemoteException;
\r
16 import android.os.SystemClock;
\r
17 import android.provider.BaseColumns;
\r
18 import android.provider.MediaStore.Audio;
\r
19 import android.support.v4.app.Fragment;
\r
20 import android.view.LayoutInflater;
\r
21 import android.view.View;
\r
22 import android.view.View.OnClickListener;
\r
23 import android.view.ViewGroup;
\r
24 import android.widget.ImageButton;
\r
25 import android.widget.ImageView;
\r
26 import android.widget.SeekBar;
\r
27 import android.widget.SeekBar.OnSeekBarChangeListener;
\r
28 import android.widget.TextView;
\r
29 import android.widget.Toast;
\r
31 import com.andrew.apollo.activities.TracksBrowser;
\r
32 import com.andrew.apollo.service.ApolloService;
\r
33 import com.andrew.apollo.tasks.GetCachedImages;
\r
34 import com.andrew.apollo.tasks.LastfmGetAlbumImages;
\r
35 import com.andrew.apollo.ui.widgets.RepeatingImageButton;
\r
36 import com.andrew.apollo.utils.ApolloUtils;
\r
37 import com.andrew.apollo.utils.MusicUtils;
\r
38 import com.andrew.apollo.utils.ThemeUtils;
\r
40 import static com.andrew.apollo.Constants.ALBUM_KEY;
\r
41 import static com.andrew.apollo.Constants.ALBUM_IMAGE;
\r
42 import static com.andrew.apollo.Constants.ARTIST_ID;
\r
43 import static com.andrew.apollo.Constants.ARTIST_KEY;
\r
44 import static com.andrew.apollo.Constants.MIME_TYPE;
\r
47 * @author Andrew Neal
\r
49 public class AudioPlayerFragment extends Fragment {
\r
51 // Track, album, and artist name
\r
52 private TextView mTrackName, mAlbumArtistName;
\r
54 // Total and current time
\r
55 private TextView mTotalTime, mCurrentTime;
\r
58 private ImageView mAlbumArt;
\r
61 private ImageButton mRepeat, mPlay, mShuffle;
\r
63 private RepeatingImageButton mPrev, mNext;
\r
66 private SeekBar mProgress;
\r
68 // Where we are in the track
\r
69 private long mDuration, mLastSeekEventTime, mPosOverride = -1, mStartSeekPos = 0;
\r
71 private boolean mFromTouch, paused = false;
\r
74 private static final int REFRESH = 1, UPDATEINFO = 2;
\r
76 // Notify if repeat or shuffle changes
\r
77 private Toast mToast;
\r
80 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
\r
81 View root = inflater.inflate(R.layout.audio_player, container, false);
\r
83 mTrackName = (TextView)root.findViewById(R.id.audio_player_track);
\r
84 mTrackName.setOnClickListener(new OnClickListener() {
\r
87 public void onClick(View v) {
\r
91 mAlbumArtistName = (TextView)root.findViewById(R.id.audio_player_album_artist);
\r
92 mAlbumArtistName.setOnClickListener(new OnClickListener() {
\r
95 public void onClick(View v) {
\r
96 tracksBrowserArtist();
\r
100 mTotalTime = (TextView)root.findViewById(R.id.audio_player_total_time);
\r
101 mCurrentTime = (TextView)root.findViewById(R.id.audio_player_current_time);
\r
103 mAlbumArt = (ImageView)root.findViewById(R.id.audio_player_album_art);
\r
105 mRepeat = (ImageButton)root.findViewById(R.id.audio_player_repeat);
\r
106 mPrev = (RepeatingImageButton)root.findViewById(R.id.audio_player_prev);
\r
107 mPlay = (ImageButton)root.findViewById(R.id.audio_player_play);
\r
108 mNext = (RepeatingImageButton)root.findViewById(R.id.audio_player_next);
\r
109 mShuffle = (ImageButton)root.findViewById(R.id.audio_player_shuffle);
\r
111 mRepeat.setOnClickListener(new OnClickListener() {
\r
114 public void onClick(View v) {
\r
119 mPrev.setRepeatListener(mRewListener, 260);
\r
120 mPrev.setOnClickListener(new OnClickListener() {
\r
123 public void onClick(View v) {
\r
124 if (MusicUtils.mService == null)
\r
127 if (MusicUtils.mService.position() < 2000) {
\r
128 MusicUtils.mService.prev();
\r
130 MusicUtils.mService.seek(0);
\r
131 MusicUtils.mService.play();
\r
133 } catch (RemoteException ex) {
\r
134 ex.printStackTrace();
\r
139 mPlay.setOnClickListener(new OnClickListener() {
\r
142 public void onClick(View v) {
\r
147 mNext.setRepeatListener(mFfwdListener, 260);
\r
148 mNext.setOnClickListener(new OnClickListener() {
\r
151 public void onClick(View v) {
\r
152 if (MusicUtils.mService == null)
\r
155 MusicUtils.mService.next();
\r
156 } catch (RemoteException ex) {
\r
157 ex.printStackTrace();
\r
162 mShuffle.setOnClickListener(new OnClickListener() {
\r
165 public void onClick(View v) {
\r
170 mProgress = (SeekBar)root.findViewById(android.R.id.progress);
\r
171 if (mProgress instanceof SeekBar) {
\r
172 SeekBar seeker = mProgress;
\r
173 seeker.setOnSeekBarChangeListener(mSeekListener);
\r
175 mProgress.setMax(1000);
\r
178 ThemeUtils.setImageButton(getActivity(), mPrev, "apollo_previous");
\r
179 ThemeUtils.setImageButton(getActivity(), mNext, "apollo_next");
\r
180 ThemeUtils.setProgessDrawable(getActivity(), mProgress, "apollo_seekbar_background");
\r
185 * Update everything as the meta or playstate changes
\r
187 private final BroadcastReceiver mStatusListener = new BroadcastReceiver() {
\r
189 public void onReceive(Context context, Intent intent) {
\r
190 if (intent.getAction().equals(ApolloService.META_CHANGED))
\r
191 mHandler.sendMessage(mHandler.obtainMessage(UPDATEINFO));
\r
192 setPauseButtonImage();
\r
193 setShuffleButtonImage();
\r
194 setRepeatButtonImage();
\r
199 public void onStart() {
\r
201 IntentFilter f = new IntentFilter();
\r
202 f.addAction(ApolloService.PLAYSTATE_CHANGED);
\r
203 f.addAction(ApolloService.META_CHANGED);
\r
204 getActivity().registerReceiver(mStatusListener, new IntentFilter(f));
\r
206 long next = refreshNow();
\r
207 queueNextRefresh(next);
\r
211 public void onDestroy() {
\r
214 mHandler.removeMessages(REFRESH);
\r
215 getActivity().unregisterReceiver(mStatusListener);
\r
219 * Cycle repeat states
\r
221 private void cycleRepeat() {
\r
222 if (MusicUtils.mService == null) {
\r
226 int mode = MusicUtils.mService.getRepeatMode();
\r
227 if (mode == ApolloService.REPEAT_NONE) {
\r
228 MusicUtils.mService.setRepeatMode(ApolloService.REPEAT_ALL);
\r
229 ApolloUtils.showToast(R.string.repeat_all, mToast, getActivity());
\r
230 } else if (mode == ApolloService.REPEAT_ALL) {
\r
231 MusicUtils.mService.setRepeatMode(ApolloService.REPEAT_CURRENT);
\r
232 if (MusicUtils.mService.getShuffleMode() != ApolloService.SHUFFLE_NONE) {
\r
233 MusicUtils.mService.setShuffleMode(ApolloService.SHUFFLE_NONE);
\r
234 setShuffleButtonImage();
\r
236 ApolloUtils.showToast(R.string.repeat_one, mToast, getActivity());
\r
238 MusicUtils.mService.setRepeatMode(ApolloService.REPEAT_NONE);
\r
239 ApolloUtils.showToast(R.string.repeat_off, mToast, getActivity());
\r
241 setRepeatButtonImage();
\r
242 } catch (RemoteException ex) {
\r
243 ex.printStackTrace();
\r
251 private final RepeatingImageButton.RepeatListener mRewListener = new RepeatingImageButton.RepeatListener() {
\r
253 public void onRepeat(View v, long howlong, int repcnt) {
\r
254 scanBackward(repcnt, howlong);
\r
259 * Play and pause music
\r
261 private void doPauseResume() {
\r
263 if (MusicUtils.mService != null) {
\r
264 if (MusicUtils.mService.isPlaying()) {
\r
265 MusicUtils.mService.pause();
\r
267 MusicUtils.mService.play();
\r
271 setPauseButtonImage();
\r
272 } catch (RemoteException ex) {
\r
273 ex.printStackTrace();
\r
280 private final RepeatingImageButton.RepeatListener mFfwdListener = new RepeatingImageButton.RepeatListener() {
\r
282 public void onRepeat(View v, long howlong, int repcnt) {
\r
283 scanForward(repcnt, howlong);
\r
288 * Set the shuffle mode
\r
290 private void toggleShuffle() {
\r
291 if (MusicUtils.mService == null) {
\r
295 int shuffle = MusicUtils.mService.getShuffleMode();
\r
296 if (shuffle == ApolloService.SHUFFLE_NONE) {
\r
297 MusicUtils.mService.setShuffleMode(ApolloService.SHUFFLE_NORMAL);
\r
298 if (MusicUtils.mService.getRepeatMode() == ApolloService.REPEAT_CURRENT) {
\r
299 MusicUtils.mService.setRepeatMode(ApolloService.REPEAT_ALL);
\r
300 setRepeatButtonImage();
\r
302 ApolloUtils.showToast(R.string.shuffle_on, mToast, getActivity());
\r
303 } else if (shuffle == ApolloService.SHUFFLE_NORMAL
\r
304 || shuffle == ApolloService.SHUFFLE_AUTO) {
\r
305 MusicUtils.mService.setShuffleMode(ApolloService.SHUFFLE_NONE);
\r
306 ApolloUtils.showToast(R.string.shuffle_off, mToast, getActivity());
\r
308 setShuffleButtonImage();
\r
309 } catch (RemoteException ex) {
\r
310 ex.printStackTrace();
\r
314 private void scanBackward(int repcnt, long delta) {
\r
315 if (MusicUtils.mService == null)
\r
319 mStartSeekPos = MusicUtils.mService.position();
\r
320 mLastSeekEventTime = 0;
\r
322 if (delta < 5000) {
\r
323 // seek at 10x speed for the first 5 seconds
\r
324 delta = delta * 10;
\r
326 // seek at 40x after that
\r
327 delta = 50000 + (delta - 5000) * 40;
\r
329 long newpos = mStartSeekPos - delta;
\r
331 // move to previous track
\r
332 MusicUtils.mService.prev();
\r
333 long duration = MusicUtils.mService.duration();
\r
334 mStartSeekPos += duration;
\r
335 newpos += duration;
\r
337 if (((delta - mLastSeekEventTime) > 250) || repcnt < 0) {
\r
338 MusicUtils.mService.seek(newpos);
\r
339 mLastSeekEventTime = delta;
\r
342 mPosOverride = newpos;
\r
348 } catch (RemoteException ex) {
\r
349 ex.printStackTrace();
\r
353 private void scanForward(int repcnt, long delta) {
\r
354 if (MusicUtils.mService == null)
\r
358 mStartSeekPos = MusicUtils.mService.position();
\r
359 mLastSeekEventTime = 0;
\r
361 if (delta < 5000) {
\r
362 // seek at 10x speed for the first 5 seconds
\r
363 delta = delta * 10;
\r
365 // seek at 40x after that
\r
366 delta = 50000 + (delta - 5000) * 40;
\r
368 long newpos = mStartSeekPos + delta;
\r
369 long duration = MusicUtils.mService.duration();
\r
370 if (newpos >= duration) {
\r
371 // move to next track
\r
372 MusicUtils.mService.next();
\r
373 mStartSeekPos -= duration; // is OK to go negative
\r
374 newpos -= duration;
\r
376 if (((delta - mLastSeekEventTime) > 250) || repcnt < 0) {
\r
377 MusicUtils.mService.seek(newpos);
\r
378 mLastSeekEventTime = delta;
\r
381 mPosOverride = newpos;
\r
387 } catch (RemoteException ex) {
\r
388 ex.printStackTrace();
\r
393 * Set the repeat images
\r
395 private void setRepeatButtonImage() {
\r
396 if (MusicUtils.mService == null)
\r
399 switch (MusicUtils.mService.getRepeatMode()) {
\r
400 case ApolloService.REPEAT_ALL:
\r
401 mRepeat.setImageResource(R.drawable.apollo_holo_light_repeat_all);
\r
403 case ApolloService.REPEAT_CURRENT:
\r
404 mRepeat.setImageResource(R.drawable.apollo_holo_light_repeat_one);
\r
407 mRepeat.setImageResource(R.drawable.apollo_holo_light_repeat_normal);
\r
409 ThemeUtils.setImageButton(getActivity(), mRepeat, "apollo_repeat_normal");
\r
412 } catch (RemoteException ex) {
\r
413 ex.printStackTrace();
\r
418 * Set the shuffle images
\r
420 private void setShuffleButtonImage() {
\r
421 if (MusicUtils.mService == null)
\r
424 switch (MusicUtils.mService.getShuffleMode()) {
\r
425 case ApolloService.SHUFFLE_NONE:
\r
426 mShuffle.setImageResource(R.drawable.apollo_holo_light_shuffle_normal);
\r
428 ThemeUtils.setImageButton(getActivity(), mShuffle, "apollo_shuffle_normal");
\r
430 case ApolloService.SHUFFLE_AUTO:
\r
431 mShuffle.setImageResource(R.drawable.apollo_holo_light_shuffle_on);
\r
434 mShuffle.setImageResource(R.drawable.apollo_holo_light_shuffle_on);
\r
437 } catch (RemoteException ex) {
\r
438 ex.printStackTrace();
\r
443 * Set the play and pause image
\r
445 private void setPauseButtonImage() {
\r
447 if (MusicUtils.mService != null && MusicUtils.mService.isPlaying()) {
\r
448 mPlay.setImageResource(R.drawable.apollo_holo_light_pause);
\r
450 ThemeUtils.setImageButton(getActivity(), mPlay, "apollo_pause");
\r
452 mPlay.setImageResource(R.drawable.apollo_holo_light_play);
\r
454 ThemeUtils.setImageButton(getActivity(), mPlay, "apollo_play");
\r
456 } catch (RemoteException ex) {
\r
457 ex.printStackTrace();
\r
464 private void queueNextRefresh(long delay) {
\r
466 Message msg = mHandler.obtainMessage(REFRESH);
\r
467 mHandler.removeMessages(REFRESH);
\r
468 mHandler.sendMessageDelayed(msg, delay);
\r
473 * We need to refresh the time via a Handler
\r
475 private final Handler mHandler = new Handler() {
\r
477 public void handleMessage(Message msg) {
\r
478 switch (msg.what) {
\r
480 long next = refreshNow();
\r
481 queueNextRefresh(next);
\r
493 * Drag to a specfic duration
\r
495 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
\r
497 public void onStartTrackingTouch(SeekBar bar) {
\r
498 mLastSeekEventTime = 0;
\r
503 public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
\r
504 if (!fromuser || (MusicUtils.mService == null))
\r
506 long now = SystemClock.elapsedRealtime();
\r
507 if ((now - mLastSeekEventTime) > 250) {
\r
508 mLastSeekEventTime = now;
\r
509 mPosOverride = mDuration * progress / 1000;
\r
511 MusicUtils.mService.seek(mPosOverride);
\r
512 } catch (RemoteException ex) {
\r
513 ex.printStackTrace();
\r
524 public void onStopTrackingTouch(SeekBar bar) {
\r
526 mFromTouch = false;
\r
531 * @return current time
\r
533 private long refreshNow() {
\r
534 if (MusicUtils.mService == null)
\r
537 long pos = mPosOverride < 0 ? MusicUtils.mService.position() : mPosOverride;
\r
538 long remaining = 1000 - (pos % 1000);
\r
539 if ((pos >= 0) && (mDuration > 0)) {
\r
540 mCurrentTime.setText(MusicUtils.makeTimeString(getActivity(), pos / 1000));
\r
542 if (MusicUtils.mService.isPlaying()) {
\r
543 mCurrentTime.setVisibility(View.VISIBLE);
\r
544 mCurrentTime.setTextColor(getResources().getColor(R.color.transparent_black));
\r
546 ThemeUtils.setTextColor(getActivity(), mCurrentTime, "audio_player_text_color");
\r
548 // blink the counter
\r
549 int col = mCurrentTime.getCurrentTextColor();
\r
550 mCurrentTime.setTextColor(col == getResources().getColor(
\r
551 R.color.transparent_black) ? getResources().getColor(
\r
552 R.color.holo_blue_dark) : getResources().getColor(
\r
553 R.color.transparent_black));
\r
556 ThemeUtils.setTextColor(getActivity(), mCurrentTime, "audio_player_text_color");
\r
559 mProgress.setProgress((int)(1000 * pos / mDuration));
\r
561 mCurrentTime.setText("--:--");
\r
562 mProgress.setProgress(1000);
\r
565 } catch (RemoteException ex) {
\r
566 ex.printStackTrace();
\r
572 * Update what's playing
\r
574 private void updateMusicInfo() {
\r
575 if (MusicUtils.mService == null) {
\r
579 String artistName = MusicUtils.getArtistName();
\r
580 String albumName = MusicUtils.getAlbumName();
\r
581 String trackName = MusicUtils.getTrackName();
\r
582 mTrackName.setText(trackName);
\r
583 mAlbumArtistName.setText(albumName + " - " + artistName);
\r
584 mDuration = MusicUtils.getDuration();
\r
585 mTotalTime.setText(MusicUtils.makeTimeString(getActivity(), mDuration / 1000));
\r
587 if (ApolloUtils.getImageURL(albumName, ALBUM_IMAGE, getActivity()) == null)
\r
588 new LastfmGetAlbumImages(getActivity(), mAlbumArt, 1).executeOnExecutor(
\r
589 AsyncTask.THREAD_POOL_EXECUTOR, artistName, albumName);
\r
591 new GetCachedImages(getActivity(), 1, mAlbumArt).executeOnExecutor(
\r
592 AsyncTask.THREAD_POOL_EXECUTOR, albumName);
\r
595 ThemeUtils.setTextColor(getActivity(), mTrackName, "audio_player_text_color");
\r
596 ThemeUtils.setTextColor(getActivity(), mAlbumArtistName, "audio_player_text_color");
\r
597 ThemeUtils.setTextColor(getActivity(), mTotalTime, "audio_player_text_color");
\r
602 * Takes you into the @TracksBrowser to view all of the tracks on the
\r
605 private void tracksBrowser() {
\r
607 String artistName = MusicUtils.getArtistName();
\r
608 String albumName = MusicUtils.getAlbumName();
\r
609 long id = MusicUtils.getCurrentAlbumId();
\r
611 Bundle bundle = new Bundle();
\r
612 bundle.putString(MIME_TYPE, Audio.Albums.CONTENT_TYPE);
\r
613 bundle.putString(ARTIST_KEY, artistName);
\r
614 bundle.putString(ALBUM_KEY, albumName);
\r
615 bundle.putLong(BaseColumns._ID, id);
\r
617 Intent intent = new Intent(Intent.ACTION_VIEW);
\r
618 intent.setClass(getActivity(), TracksBrowser.class);
\r
619 intent.putExtras(bundle);
\r
620 getActivity().startActivity(intent);
\r
624 * Takes you into the @TracksBrowser to view all of the tracks and albums by
\r
625 * the current artist
\r
627 private void tracksBrowserArtist() {
\r
629 String artistName = MusicUtils.getArtistName();
\r
630 long id = MusicUtils.getCurrentArtistId();
\r
632 Bundle bundle = new Bundle();
\r
633 bundle.putString(MIME_TYPE, Audio.Artists.CONTENT_TYPE);
\r
634 bundle.putString(ARTIST_KEY, artistName);
\r
635 bundle.putLong(BaseColumns._ID, id);
\r
637 ApolloUtils.setArtistId(artistName, id, ARTIST_ID, getActivity());
\r
639 Intent intent = new Intent(Intent.ACTION_VIEW);
\r
640 intent.setClass(getActivity(), TracksBrowser.class);
\r
641 intent.putExtras(bundle);
\r
642 getActivity().startActivity(intent);
\r