1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.music;
18
19import android.app.Activity;
20import android.app.SearchManager;
21import android.content.ComponentName;
22import android.content.Intent;
23import android.graphics.Bitmap;
24import android.graphics.BitmapFactory;
25import android.graphics.drawable.BitmapDrawable;
26import android.media.AudioManager;
27import android.media.MediaDescription;
28import android.media.MediaMetadata;
29import android.media.browse.MediaBrowser;
30import android.media.session.MediaController;
31import android.media.session.PlaybackState;
32import android.net.Uri;
33import android.os.Bundle;
34import android.os.Handler;
35import android.os.Message;
36import android.os.SystemClock;
37import android.provider.MediaStore;
38import android.text.Layout;
39import android.text.TextUtils.TruncateAt;
40import android.util.Log;
41import android.view.MotionEvent;
42import android.view.View;
43import android.view.ViewConfiguration;
44import android.view.Window;
45import android.widget.ImageButton;
46import android.widget.ImageView;
47import android.widget.LinearLayout;
48import android.widget.ProgressBar;
49import android.widget.SeekBar;
50import android.widget.SeekBar.OnSeekBarChangeListener;
51import android.widget.TextView;
52import android.widget.Toast;
53
54import com.android.music.utils.LogHelper;
55import com.android.music.utils.MediaIDHelper;
56import com.android.music.utils.MusicProvider;
57
58/*
59This is the Now Playing Activity
60 */
61public class MediaPlaybackActivity
62        extends Activity implements View.OnTouchListener, View.OnLongClickListener {
63    private static final String TAG = LogHelper.makeLogTag(MediaPlaybackActivity.class);
64
65    private long mStartSeekPos = 0;
66    private long mLastSeekEventTime;
67    private RepeatingImageButton mPrevButton;
68    private ImageButton mPlayPauseButton;
69    private RepeatingImageButton mNextButton;
70    private ImageButton mRepeatButton;
71    private ImageButton mShuffleButton;
72    private ImageButton mQueueButton;
73    private int mTouchSlop;
74
75    private ImageView mAlbumArt;
76    private TextView mCurrentTime;
77    private TextView mTotalTime;
78    private TextView mArtistName;
79    private TextView mAlbumName;
80    private TextView mTrackName;
81    private LinearLayout mTrackInfo;
82    private ProgressBar mProgress;
83    private BitmapDrawable mDefaultAlbumArt;
84    private Toast mToast;
85
86    private MediaBrowser mMediaBrowser;
87    private final Handler mHandler = new Handler();
88
89    /** Called when the activity is first created. */
90    @Override
91    public void onCreate(Bundle icicle) {
92        LogHelper.d(TAG, "onCreate()");
93        super.onCreate(icicle);
94        setVolumeControlStream(AudioManager.STREAM_MUSIC);
95
96        requestWindowFeature(Window.FEATURE_NO_TITLE);
97        setContentView(R.layout.audio_player);
98
99        mCurrentTime = (TextView) findViewById(R.id.currenttime);
100        mTotalTime = (TextView) findViewById(R.id.totaltime);
101        mProgress = (ProgressBar) findViewById(android.R.id.progress);
102        mAlbumArt = (ImageView) findViewById(R.id.album);
103        mArtistName = (TextView) findViewById(R.id.artistname);
104        mAlbumName = (TextView) findViewById(R.id.albumname);
105        mTrackName = (TextView) findViewById(R.id.trackname);
106        mTrackInfo = (LinearLayout) findViewById(R.id.trackinfo);
107
108        Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.albumart_mp_unknown);
109        mDefaultAlbumArt = new BitmapDrawable(getResources(), b);
110        // no filter or dither, it's a lot faster and we can't tell the difference
111        mDefaultAlbumArt.setFilterBitmap(false);
112        mDefaultAlbumArt.setDither(false);
113
114        /* Set metadata listeners */
115        View v = (View) mArtistName.getParent();
116        v.setOnTouchListener(this);
117        v.setOnLongClickListener(this);
118        v = (View) mAlbumName.getParent();
119        v.setOnTouchListener(this);
120        v.setOnLongClickListener(this);
121        v = (View) mTrackName.getParent();
122        v.setOnTouchListener(this);
123        v.setOnLongClickListener(this);
124
125        /* Set button listeners */
126        mPrevButton = (RepeatingImageButton) findViewById(R.id.prev);
127        mPrevButton.setOnClickListener(new View.OnClickListener() {
128            public void onClick(View v) {
129                if (getMediaController() == null) return;
130                if (getMediaController().getPlaybackState().getPosition() < 2000) {
131                    getMediaController().getTransportControls().skipToPrevious();
132                } else {
133                    getMediaController().getTransportControls().seekTo(0);
134                    getMediaController().getTransportControls().play();
135                }
136            }
137        });
138        mPrevButton.setRepeatListener(new RepeatingImageButton.RepeatListener() {
139            public void onRepeat(View v, long howlong, int repcnt) {
140                scanBackward(repcnt, howlong);
141            }
142        }, 260);
143        mPlayPauseButton = (ImageButton) findViewById(R.id.pause);
144        mPlayPauseButton.requestFocus();
145        mPlayPauseButton.setOnClickListener(new View.OnClickListener() {
146            public void onClick(View v) {
147                if (getMediaController() != null) {
148                    if (getMediaController().getPlaybackState().getState()
149                            != PlaybackState.STATE_PLAYING) {
150                        getMediaController().getTransportControls().play();
151                    } else {
152                        getMediaController().getTransportControls().pause();
153                    }
154                }
155            }
156        });
157        mNextButton = (RepeatingImageButton) findViewById(R.id.next);
158        mNextButton.setOnClickListener(new View.OnClickListener() {
159            public void onClick(View v) {
160                if (getMediaController() == null) return;
161                getMediaController().getTransportControls().skipToNext();
162            }
163        });
164        mNextButton.setRepeatListener(new RepeatingImageButton.RepeatListener() {
165            public void onRepeat(View v, long howlong, int repcnt) {
166                scanForward(repcnt, howlong);
167            }
168        }, 260);
169        mQueueButton = (ImageButton) findViewById(R.id.curplaylist);
170        mQueueButton.setOnClickListener(new View.OnClickListener() {
171            public void onClick(View v) {
172                LogHelper.d(TAG, "mQueueButton onClick");
173                MediaBrowser.MediaItem parentItem = new MediaBrowser.MediaItem(
174                        new MediaDescription.Builder()
175                                .setMediaId(MediaIDHelper.createBrowseCategoryMediaID(
176                                        MediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST,
177                                        MediaIDHelper.MEDIA_ID_NOW_PLAYING))
178                                .setTitle("Now Playing")
179                                .build(),
180                        MediaBrowser.MediaItem.FLAG_BROWSABLE);
181                Intent intent = new Intent(Intent.ACTION_PICK)
182                                        .setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track")
183                                        .putExtra(MusicUtils.TAG_WITH_TABS, false)
184                                        .putExtra(MusicUtils.TAG_PARENT_ITEM, parentItem);
185                startActivity(intent);
186            }
187        });
188        mShuffleButton = ((ImageButton) findViewById(R.id.shuffle));
189        mShuffleButton.setOnClickListener(new View.OnClickListener() {
190            public void onClick(View v) {
191                toggleShuffle();
192            }
193        });
194        mRepeatButton = ((ImageButton) findViewById(R.id.repeat));
195        mRepeatButton.setOnClickListener(new View.OnClickListener() {
196            public void onClick(View v) {
197                LogHelper.d(TAG, "Repeat button clicked");
198                if (getMediaController() == null) return;
199                Bundle extras = getMediaController().getExtras();
200                if (extras == null) return;
201                MediaPlaybackService.RepeatMode repeatMode =
202                        MediaPlaybackService.RepeatMode
203                                .values()[extras.getInt(MediaPlaybackService.REPEAT_MODE)];
204                MediaPlaybackService.RepeatMode nextRepeatMode;
205                switch (repeatMode) {
206                    case REPEAT_NONE:
207                        nextRepeatMode = MediaPlaybackService.RepeatMode.REPEAT_ALL;
208                        showToast(R.string.repeat_all_notif);
209                        break;
210                    case REPEAT_ALL:
211                        nextRepeatMode = MediaPlaybackService.RepeatMode.REPEAT_CURRENT;
212                        showToast(R.string.repeat_current_notif);
213                        break;
214                    case REPEAT_CURRENT:
215                    default:
216                        nextRepeatMode = MediaPlaybackService.RepeatMode.REPEAT_NONE;
217                        showToast(R.string.repeat_off_notif);
218                        break;
219                }
220                setRepeatMode(nextRepeatMode);
221                // TODO(siyuanh): Should use a callback to register changes on service side
222                setRepeatButtonImage(nextRepeatMode);
223            }
224        });
225
226        if (mProgress instanceof SeekBar) {
227            SeekBar seeker = (SeekBar) mProgress;
228            seeker.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
229                boolean mmFromTouch = false;
230                public void onStartTrackingTouch(SeekBar bar) {
231                    mLastSeekEventTime = 0;
232                    mmFromTouch = true;
233                }
234                public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
235                    if (!fromuser || (getMediaController() == null)) return;
236                    long now = SystemClock.elapsedRealtime();
237                    if ((now - mLastSeekEventTime) > 250) {
238                        mLastSeekEventTime = now;
239                        long duration = getMediaController().getMetadata().getLong(
240                                MediaMetadata.METADATA_KEY_DURATION);
241                        long position = duration * progress / 1000;
242                        getMediaController().getTransportControls().seekTo(position);
243                        // trackball event, allow progress updates
244                        if (!mmFromTouch) {
245                            updateProgressBar();
246                        }
247                    }
248                }
249                public void onStopTrackingTouch(SeekBar bar) {
250                    mmFromTouch = false;
251                }
252            });
253        } else {
254            LogHelper.d(TAG, "Seeking not supported");
255        }
256        mProgress.setMax(1000);
257
258        mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
259
260        Log.d(TAG, "Creating MediaBrowser");
261        mMediaBrowser = new MediaBrowser(this, new ComponentName(this, MediaPlaybackService.class),
262                mConnectionCallback, null);
263    }
264
265    // Receive callbacks from the MediaController. Here we update our state such as which queue
266    // is being shown, the current title and description and the PlaybackState.
267    private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
268
269        @Override
270        public void onSessionDestroyed() {
271            LogHelper.d(TAG, "Session destroyed. Need to fetch a new Media Session");
272        }
273
274        @Override
275        public void onPlaybackStateChanged(PlaybackState state) {
276            if (state == null) {
277                return;
278            }
279            LogHelper.d(TAG, "Received playback state change to state ", state.toString());
280            updateProgressBar();
281            setPauseButtonImage();
282        }
283
284        @Override
285        public void onMetadataChanged(MediaMetadata metadata) {
286            if (metadata == null) {
287                return;
288            }
289            LogHelper.d(TAG, "Received updated metadata: ", metadata);
290            updateTrackInfo();
291        }
292    };
293
294    private MediaBrowser.ConnectionCallback mConnectionCallback =
295            new MediaBrowser.ConnectionCallback() {
296                @Override
297                public void onConnected() {
298                    Log.d(TAG, "onConnected: session token " + mMediaBrowser.getSessionToken());
299                    if (mMediaBrowser.getSessionToken() == null) {
300                        throw new IllegalArgumentException("No Session token");
301                    }
302                    MediaController mediaController = new MediaController(
303                            MediaPlaybackActivity.this, mMediaBrowser.getSessionToken());
304                    mediaController.registerCallback(mMediaControllerCallback);
305                    MediaPlaybackActivity.this.setMediaController(mediaController);
306                    mRepeatButton.setVisibility(View.VISIBLE);
307                    mShuffleButton.setVisibility(View.VISIBLE);
308                    mQueueButton.setVisibility(View.VISIBLE);
309                    setRepeatButtonImage(null);
310                    setShuffleButtonImage();
311                    setPauseButtonImage();
312                    updateTrackInfo();
313                    mHandler.post(new Runnable() {
314                        @Override
315                        public void run() {
316                            long delay = updateProgressBar();
317                            mHandler.postDelayed(this, delay);
318                        }
319                    });
320                }
321
322                @Override
323                public void onConnectionFailed() {
324                    Log.d(TAG, "onConnectionFailed");
325                }
326
327                @Override
328                public void onConnectionSuspended() {
329                    Log.d(TAG, "onConnectionSuspended");
330                    mHandler.removeCallbacksAndMessages(null);
331                    MediaPlaybackActivity.this.setMediaController(null);
332                }
333            };
334
335    int mInitialX = -1;
336    int mLastX = -1;
337    int mTextWidth = 0;
338    int mViewWidth = 0;
339    boolean mDraggingLabel = false;
340
341    TextView textViewForContainer(View v) {
342        View vv = v.findViewById(R.id.artistname);
343        if (vv != null) return (TextView) vv;
344        vv = v.findViewById(R.id.albumname);
345        if (vv != null) return (TextView) vv;
346        vv = v.findViewById(R.id.trackname);
347        if (vv != null) return (TextView) vv;
348        return null;
349    }
350
351    public boolean onTouch(View v, MotionEvent event) {
352        int action = event.getAction();
353        TextView tv = textViewForContainer(v);
354        if (tv == null) {
355            return false;
356        }
357        if (action == MotionEvent.ACTION_DOWN) {
358            v.setBackgroundColor(0xff606060);
359            mInitialX = mLastX = (int) event.getX();
360            mDraggingLabel = false;
361        } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
362            v.setBackgroundColor(0);
363            if (mDraggingLabel) {
364                Message msg = mLabelScroller.obtainMessage(0, tv);
365                mLabelScroller.sendMessageDelayed(msg, 1000);
366            }
367        } else if (action == MotionEvent.ACTION_MOVE) {
368            if (mDraggingLabel) {
369                int scrollx = tv.getScrollX();
370                int x = (int) event.getX();
371                int delta = mLastX - x;
372                if (delta != 0) {
373                    mLastX = x;
374                    scrollx += delta;
375                    if (scrollx > mTextWidth) {
376                        // scrolled the text completely off the view to the left
377                        scrollx -= mTextWidth;
378                        scrollx -= mViewWidth;
379                    }
380                    if (scrollx < -mViewWidth) {
381                        // scrolled the text completely off the view to the right
382                        scrollx += mViewWidth;
383                        scrollx += mTextWidth;
384                    }
385                    tv.scrollTo(scrollx, 0);
386                }
387                return true;
388            }
389            int delta = mInitialX - (int) event.getX();
390            if (Math.abs(delta) > mTouchSlop) {
391                // start moving
392                mLabelScroller.removeMessages(0, tv);
393
394                // Only turn ellipsizing off when it's not already off, because it
395                // causes the scroll position to be reset to 0.
396                if (tv.getEllipsize() != null) {
397                    tv.setEllipsize(null);
398                }
399                Layout ll = tv.getLayout();
400                // layout might be null if the text just changed, or ellipsizing
401                // was just turned off
402                if (ll == null) {
403                    return false;
404                }
405                // get the non-ellipsized line width, to determine whether scrolling
406                // should even be allowed
407                mTextWidth = (int) tv.getLayout().getLineWidth(0);
408                mViewWidth = tv.getWidth();
409                if (mViewWidth > mTextWidth) {
410                    tv.setEllipsize(TruncateAt.END);
411                    v.cancelLongPress();
412                    return false;
413                }
414                mDraggingLabel = true;
415                tv.setHorizontalFadingEdgeEnabled(true);
416                v.cancelLongPress();
417                return true;
418            }
419        }
420        return false;
421    }
422
423    Handler mLabelScroller = new Handler() {
424        @Override
425        public void handleMessage(Message msg) {
426            TextView tv = (TextView) msg.obj;
427            int x = tv.getScrollX();
428            x = x * 3 / 4;
429            tv.scrollTo(x, 0);
430            if (x == 0) {
431                tv.setEllipsize(TruncateAt.END);
432            } else {
433                Message newmsg = obtainMessage(0, tv);
434                mLabelScroller.sendMessageDelayed(newmsg, 15);
435            }
436        }
437    };
438
439    public boolean onLongClick(View view) {
440        CharSequence title = null;
441        String mime = null;
442        String query = null;
443        if (getMediaController() == null) {
444            LogHelper.d(TAG, "No media controller avalable yet");
445            return true;
446        }
447        MediaMetadata metadata = getMediaController().getMetadata();
448        if (metadata == null) {
449            LogHelper.d(TAG, "No metadata avalable yet");
450            return true;
451        }
452        String artist = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
453        String album = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
454        String song = metadata.getString(MediaMetadata.METADATA_KEY_TITLE);
455        long audioid = metadata.getLong(MediaMetadata.METADATA_KEY_MEDIA_ID);
456
457        if (album == null && artist == null && song != null && song.startsWith("recording")) {
458            LogHelper.d(TAG, "Item is not music");
459            return false;
460        }
461
462        if (audioid < 0) {
463            return false;
464        }
465
466        boolean knownartist = (artist != null) && !MediaStore.UNKNOWN_STRING.equals(artist);
467        boolean knownalbum = (album != null) && !MediaStore.UNKNOWN_STRING.equals(album);
468
469        if (knownartist && view.equals(mArtistName.getParent())) {
470            title = artist;
471            query = artist;
472            mime = MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE;
473        } else if (knownalbum && view.equals(mAlbumName.getParent())) {
474            title = album;
475            if (knownartist) {
476                query = artist + " " + album;
477            } else {
478                query = album;
479            }
480            mime = MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE;
481        } else if (view.equals(mTrackName.getParent()) || !knownartist || !knownalbum) {
482            if ((song == null) || MediaStore.UNKNOWN_STRING.equals(song)) {
483                // A popup of the form "Search for null/'' using ..." is pretty
484                // unhelpful, plus, we won't find any way to buy it anyway.
485                return true;
486            }
487
488            title = song;
489            if (knownartist) {
490                query = artist + " " + song;
491            } else {
492                query = song;
493            }
494            mime = "audio/*"; // the specific type doesn't matter, so don't bother retrieving it
495        } else {
496            throw new RuntimeException("shouldn't be here");
497        }
498        title = getString(R.string.mediasearch, title);
499
500        Intent i = new Intent();
501        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
502        i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH);
503        i.putExtra(SearchManager.QUERY, query);
504        if (knownartist) {
505            i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist);
506        }
507        if (knownalbum) {
508            i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, album);
509        }
510        i.putExtra(MediaStore.EXTRA_MEDIA_TITLE, song);
511        i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, mime);
512
513        startActivity(Intent.createChooser(i, title));
514        return true;
515    }
516
517    @Override
518    public void onStart() {
519        LogHelper.d(TAG, "onStart()");
520        super.onStart();
521        mMediaBrowser.connect();
522    }
523
524    @Override
525    public void onStop() {
526        LogHelper.d(TAG, "onStop()");
527        mMediaBrowser.disconnect();
528        super.onStop();
529    }
530
531    @Override
532    public void onResume() {
533        LogHelper.d(TAG, "onResume()");
534        super.onResume();
535        updateTrackInfo();
536        setPauseButtonImage();
537    }
538
539    @Override
540    public void onDestroy() {
541        LogHelper.d(TAG, "onDestroy()");
542        super.onDestroy();
543    }
544
545    private void scanBackward(int repcnt, long delta) {
546        if (getMediaController() == null) return;
547        if (repcnt == 0) {
548            mStartSeekPos = getMediaController().getPlaybackState().getPosition();
549            mLastSeekEventTime = 0;
550        } else {
551            if (delta < 5000) {
552                // seek at 10x speed for the first 5 seconds
553                delta = delta * 10;
554            } else {
555                // seek at 40x after that
556                delta = 50000 + (delta - 5000) * 40;
557            }
558            long newpos = mStartSeekPos - delta;
559            if (newpos < 0) {
560                // move to previous track
561                getMediaController().getTransportControls().skipToPrevious();
562                long duration = getMediaController().getMetadata().getLong(
563                        MediaMetadata.METADATA_KEY_DURATION);
564                mStartSeekPos += duration;
565                newpos += duration;
566            }
567            if (((delta - mLastSeekEventTime) > 250) || repcnt < 0) {
568                getMediaController().getTransportControls().seekTo(newpos);
569                mLastSeekEventTime = delta;
570            }
571            updateProgressBar();
572        }
573    }
574
575    private void scanForward(int repcnt, long delta) {
576        if (getMediaController() == null) return;
577        if (repcnt == 0) {
578            mStartSeekPos = getMediaController().getPlaybackState().getPosition();
579            mLastSeekEventTime = 0;
580        } else {
581            if (delta < 5000) {
582                // seek at 10x speed for the first 5 seconds
583                delta = delta * 10;
584            } else {
585                // seek at 40x after that
586                delta = 50000 + (delta - 5000) * 40;
587            }
588            long newpos = mStartSeekPos + delta;
589            long duration =
590                    getMediaController().getMetadata().getLong(MediaMetadata.METADATA_KEY_DURATION);
591            if (newpos >= duration) {
592                // move to next track
593                getMediaController().getTransportControls().skipToNext();
594                mStartSeekPos -= duration; // is OK to go negative
595                newpos -= duration;
596            }
597            if (((delta - mLastSeekEventTime) > 250) || repcnt < 0) {
598                getMediaController().getTransportControls().seekTo(newpos);
599                mLastSeekEventTime = delta;
600            }
601            updateProgressBar();
602        }
603    }
604
605    private void toggleShuffle() {
606        // TODO(b/36371715): Implement shuffle for SHUFFLE_NORMAL, SHUFFLE_AUTO, SHUFFLE_NONE
607        LogHelper.d(TAG, "Shuffle not implemented yet");
608        Toast.makeText(this, "Shuffle not implemented yet", Toast.LENGTH_SHORT).show();
609    }
610
611    private void setRepeatMode(MediaPlaybackService.RepeatMode repeatMode) {
612        Bundle extras = new Bundle();
613        extras.putInt(MediaPlaybackService.REPEAT_MODE, repeatMode.ordinal());
614        getMediaController().getTransportControls().sendCustomAction(
615                MediaPlaybackService.CMD_REPEAT, extras);
616    }
617
618    private void setRepeatButtonImage(MediaPlaybackService.RepeatMode repeatMode) {
619        if (getMediaController() == null) return;
620        Bundle extras = getMediaController().getExtras();
621        if (extras == null) return;
622        if (repeatMode == null) {
623            repeatMode = MediaPlaybackService.RepeatMode
624                                 .values()[extras.getInt(MediaPlaybackService.REPEAT_MODE)];
625        }
626        switch (repeatMode) {
627            case REPEAT_CURRENT:
628                mRepeatButton.setImageResource(R.drawable.ic_mp_repeat_once_btn);
629                break;
630            case REPEAT_ALL:
631                mRepeatButton.setImageResource(R.drawable.ic_mp_repeat_all_btn);
632                break;
633            case REPEAT_NONE:
634            default:
635                mRepeatButton.setImageResource(R.drawable.ic_mp_repeat_off_btn);
636                break;
637        }
638    }
639
640    private void showToast(int resid) {
641        if (mToast == null) {
642            mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
643        }
644        mToast.setText(resid);
645        mToast.show();
646    }
647
648    private void setShuffleButtonImage() {
649        if (getMediaController() == null) return;
650        mShuffleButton.setImageResource(R.drawable.ic_mp_shuffle_off_btn);
651    }
652
653    private void setPauseButtonImage() {
654        if (getMediaController() == null) {
655            return;
656        }
657        if (getMediaController().getPlaybackState().getState() != PlaybackState.STATE_PLAYING) {
658            mPlayPauseButton.setImageResource(android.R.drawable.ic_media_play);
659        } else {
660            mPlayPauseButton.setImageResource(android.R.drawable.ic_media_pause);
661        }
662    }
663
664    private long updateProgressBar() {
665        MediaController mediaController = getMediaController();
666        if (mediaController == null || mediaController.getMetadata() == null
667                || mediaController.getPlaybackState() == null) {
668            return 500;
669        }
670        long duration = mediaController.getMetadata().getLong(MediaMetadata.METADATA_KEY_DURATION);
671        long pos = mediaController.getPlaybackState().getPosition();
672        if ((pos >= 0) && (duration > 0)) {
673            mCurrentTime.setText(MusicUtils.makeTimeString(this, pos / 1000));
674            int progress = (int) (1000 * pos / duration);
675            mProgress.setProgress(progress);
676
677            if (mediaController.getPlaybackState().getState() == PlaybackState.STATE_PLAYING) {
678                mCurrentTime.setVisibility(View.VISIBLE);
679            } else {
680                // blink the counter
681                int vis = mCurrentTime.getVisibility();
682                mCurrentTime.setVisibility(vis == View.INVISIBLE ? View.VISIBLE : View.INVISIBLE);
683                return 500;
684            }
685        } else {
686            mCurrentTime.setText("--:--");
687            mProgress.setProgress(1000);
688        }
689        // calculate the number of milliseconds until the next full second, so
690        // the counter can be updated at just the right time
691        long remaining = 1000 - (pos % 1000);
692
693        // approximate how often we would need to refresh the slider to
694        // move it smoothly
695        int width = mProgress.getWidth();
696        if (width == 0) width = 320;
697        long smoothrefreshtime = duration / width;
698
699        if (smoothrefreshtime > remaining) return remaining;
700        if (smoothrefreshtime < 20) return 20;
701        return smoothrefreshtime;
702    }
703
704    private void updateTrackInfo() {
705        LogHelper.d(TAG, "updateTrackInfo()");
706        if (getMediaController() == null) {
707            return;
708        }
709        MediaMetadata metadata = getMediaController().getMetadata();
710        if (metadata == null) {
711            return;
712        }
713        mTrackInfo.setVisibility(View.VISIBLE);
714        mTrackName.setText(metadata.getString(MediaMetadata.METADATA_KEY_TITLE));
715        LogHelper.d(TAG, "Track Name: ", mTrackName.getText());
716        String artistName = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
717        if (artistName.equals(MusicProvider.UNKOWN)) {
718            artistName = getString(R.string.unknown_artist_name);
719        }
720        mArtistName.setText(artistName);
721        String albumName = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
722        if (albumName.equals(MusicProvider.UNKOWN)) {
723            albumName = getString(R.string.unknown_album_name);
724        }
725        mAlbumName.setText(albumName);
726        Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
727        if (albumArt != null) {
728            mAlbumArt.setImageBitmap(albumArt);
729        } else {
730            mAlbumArt.setImageDrawable(mDefaultAlbumArt);
731        }
732        mAlbumArt.setVisibility(View.VISIBLE);
733
734        long duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
735        mTotalTime.setText(MusicUtils.makeTimeString(this, duration / 1000));
736    }
737}
738