Home | History | Annotate | Download | only in music
      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 
     17 package com.android.music;
     18 
     19 import android.app.Activity;
     20 import android.app.SearchManager;
     21 import android.content.ComponentName;
     22 import android.content.Intent;
     23 import android.graphics.Bitmap;
     24 import android.graphics.BitmapFactory;
     25 import android.graphics.drawable.BitmapDrawable;
     26 import android.media.AudioManager;
     27 import android.media.MediaDescription;
     28 import android.media.MediaMetadata;
     29 import android.media.browse.MediaBrowser;
     30 import android.media.session.MediaController;
     31 import android.media.session.PlaybackState;
     32 import android.net.Uri;
     33 import android.os.Bundle;
     34 import android.os.Handler;
     35 import android.os.Message;
     36 import android.os.SystemClock;
     37 import android.provider.MediaStore;
     38 import android.text.Layout;
     39 import android.text.TextUtils.TruncateAt;
     40 import android.util.Log;
     41 import android.view.MotionEvent;
     42 import android.view.View;
     43 import android.view.ViewConfiguration;
     44 import android.view.Window;
     45 import android.widget.ImageButton;
     46 import android.widget.ImageView;
     47 import android.widget.LinearLayout;
     48 import android.widget.ProgressBar;
     49 import android.widget.SeekBar;
     50 import android.widget.SeekBar.OnSeekBarChangeListener;
     51 import android.widget.TextView;
     52 import android.widget.Toast;
     53 
     54 import com.android.music.utils.LogHelper;
     55 import com.android.music.utils.MediaIDHelper;
     56 import com.android.music.utils.MusicProvider;
     57 
     58 /*
     59 This is the Now Playing Activity
     60  */
     61 public 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