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 com.android.music.MusicUtils.ServiceToken;
     20 
     21 import android.app.Activity;
     22 import android.app.AlertDialog;
     23 import android.app.KeyguardManager;
     24 import android.app.SearchManager;
     25 import android.content.BroadcastReceiver;
     26 import android.content.ComponentName;
     27 import android.content.ContentResolver;
     28 import android.content.ContentUris;
     29 import android.content.Context;
     30 import android.content.DialogInterface;
     31 import android.content.Intent;
     32 import android.content.IntentFilter;
     33 import android.content.ServiceConnection;
     34 import android.content.pm.ResolveInfo;
     35 import android.content.res.Configuration;
     36 import android.database.Cursor;
     37 import android.graphics.Bitmap;
     38 import android.media.audiofx.AudioEffect;
     39 import android.media.AudioManager;
     40 import android.net.Uri;
     41 import android.os.Bundle;
     42 import android.os.Handler;
     43 import android.os.IBinder;
     44 import android.os.Looper;
     45 import android.os.Message;
     46 import android.os.RemoteException;
     47 import android.os.SystemClock;
     48 import android.provider.MediaStore;
     49 import android.text.Layout;
     50 import android.text.TextUtils.TruncateAt;
     51 import android.util.Log;
     52 import android.view.KeyEvent;
     53 import android.view.Menu;
     54 import android.view.MenuItem;
     55 import android.view.MotionEvent;
     56 import android.view.SubMenu;
     57 import android.view.View;
     58 import android.view.ViewConfiguration;
     59 import android.view.Window;
     60 import android.widget.ImageButton;
     61 import android.widget.ImageView;
     62 import android.widget.ProgressBar;
     63 import android.widget.SeekBar;
     64 import android.widget.TextView;
     65 import android.widget.Toast;
     66 import android.widget.SeekBar.OnSeekBarChangeListener;
     67 
     68 public class MediaPlaybackActivity extends Activity
     69         implements MusicUtils.Defs, View.OnTouchListener, View.OnLongClickListener {
     70     private static final int USE_AS_RINGTONE = CHILD_MENU_BASE;
     71 
     72     private boolean mSeeking = false;
     73     private boolean mDeviceHasDpad;
     74     private long mStartSeekPos = 0;
     75     private long mLastSeekEventTime;
     76     private IMediaPlaybackService mService = null;
     77     private RepeatingImageButton mPrevButton;
     78     private ImageButton mPauseButton;
     79     private RepeatingImageButton mNextButton;
     80     private ImageButton mRepeatButton;
     81     private ImageButton mShuffleButton;
     82     private ImageButton mQueueButton;
     83     private Worker mAlbumArtWorker;
     84     private AlbumArtHandler mAlbumArtHandler;
     85     private Toast mToast;
     86     private int mTouchSlop;
     87     private ServiceToken mToken;
     88 
     89     public MediaPlaybackActivity() {}
     90 
     91     /** Called when the activity is first created. */
     92     @Override
     93     public void onCreate(Bundle icicle) {
     94         super.onCreate(icicle);
     95         setVolumeControlStream(AudioManager.STREAM_MUSIC);
     96 
     97         mAlbumArtWorker = new Worker("album art worker");
     98         mAlbumArtHandler = new AlbumArtHandler(mAlbumArtWorker.getLooper());
     99 
    100         requestWindowFeature(Window.FEATURE_NO_TITLE);
    101         setContentView(R.layout.audio_player);
    102 
    103         mCurrentTime = (TextView) findViewById(R.id.currenttime);
    104         mTotalTime = (TextView) findViewById(R.id.totaltime);
    105         mProgress = (ProgressBar) findViewById(android.R.id.progress);
    106         mAlbum = (ImageView) findViewById(R.id.album);
    107         mArtistName = (TextView) findViewById(R.id.artistname);
    108         mAlbumName = (TextView) findViewById(R.id.albumname);
    109         mTrackName = (TextView) findViewById(R.id.trackname);
    110 
    111         View v = (View) mArtistName.getParent();
    112         v.setOnTouchListener(this);
    113         v.setOnLongClickListener(this);
    114 
    115         v = (View) mAlbumName.getParent();
    116         v.setOnTouchListener(this);
    117         v.setOnLongClickListener(this);
    118 
    119         v = (View) mTrackName.getParent();
    120         v.setOnTouchListener(this);
    121         v.setOnLongClickListener(this);
    122 
    123         mPrevButton = (RepeatingImageButton) findViewById(R.id.prev);
    124         mPrevButton.setOnClickListener(mPrevListener);
    125         mPrevButton.setRepeatListener(mRewListener, 260);
    126         mPauseButton = (ImageButton) findViewById(R.id.pause);
    127         mPauseButton.requestFocus();
    128         mPauseButton.setOnClickListener(mPauseListener);
    129         mNextButton = (RepeatingImageButton) findViewById(R.id.next);
    130         mNextButton.setOnClickListener(mNextListener);
    131         mNextButton.setRepeatListener(mFfwdListener, 260);
    132         seekmethod = 1;
    133 
    134         mDeviceHasDpad =
    135                 (getResources().getConfiguration().navigation == Configuration.NAVIGATION_DPAD);
    136 
    137         mQueueButton = (ImageButton) findViewById(R.id.curplaylist);
    138         mQueueButton.setOnClickListener(mQueueListener);
    139         mShuffleButton = ((ImageButton) findViewById(R.id.shuffle));
    140         mShuffleButton.setOnClickListener(mShuffleListener);
    141         mRepeatButton = ((ImageButton) findViewById(R.id.repeat));
    142         mRepeatButton.setOnClickListener(mRepeatListener);
    143 
    144         if (mProgress instanceof SeekBar) {
    145             SeekBar seeker = (SeekBar) mProgress;
    146             seeker.setOnSeekBarChangeListener(mSeekListener);
    147         }
    148         mProgress.setMax(1000);
    149 
    150         mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
    151     }
    152 
    153     int mInitialX = -1;
    154     int mLastX = -1;
    155     int mTextWidth = 0;
    156     int mViewWidth = 0;
    157     boolean mDraggingLabel = false;
    158 
    159     TextView textViewForContainer(View v) {
    160         View vv = v.findViewById(R.id.artistname);
    161         if (vv != null) return (TextView) vv;
    162         vv = v.findViewById(R.id.albumname);
    163         if (vv != null) return (TextView) vv;
    164         vv = v.findViewById(R.id.trackname);
    165         if (vv != null) return (TextView) vv;
    166         return null;
    167     }
    168 
    169     public boolean onTouch(View v, MotionEvent event) {
    170         int action = event.getAction();
    171         TextView tv = textViewForContainer(v);
    172         if (tv == null) {
    173             return false;
    174         }
    175         if (action == MotionEvent.ACTION_DOWN) {
    176             v.setBackgroundColor(0xff606060);
    177             mInitialX = mLastX = (int) event.getX();
    178             mDraggingLabel = false;
    179         } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
    180             v.setBackgroundColor(0);
    181             if (mDraggingLabel) {
    182                 Message msg = mLabelScroller.obtainMessage(0, tv);
    183                 mLabelScroller.sendMessageDelayed(msg, 1000);
    184             }
    185         } else if (action == MotionEvent.ACTION_MOVE) {
    186             if (mDraggingLabel) {
    187                 int scrollx = tv.getScrollX();
    188                 int x = (int) event.getX();
    189                 int delta = mLastX - x;
    190                 if (delta != 0) {
    191                     mLastX = x;
    192                     scrollx += delta;
    193                     if (scrollx > mTextWidth) {
    194                         // scrolled the text completely off the view to the left
    195                         scrollx -= mTextWidth;
    196                         scrollx -= mViewWidth;
    197                     }
    198                     if (scrollx < -mViewWidth) {
    199                         // scrolled the text completely off the view to the right
    200                         scrollx += mViewWidth;
    201                         scrollx += mTextWidth;
    202                     }
    203                     tv.scrollTo(scrollx, 0);
    204                 }
    205                 return true;
    206             }
    207             int delta = mInitialX - (int) event.getX();
    208             if (Math.abs(delta) > mTouchSlop) {
    209                 // start moving
    210                 mLabelScroller.removeMessages(0, tv);
    211 
    212                 // Only turn ellipsizing off when it's not already off, because it
    213                 // causes the scroll position to be reset to 0.
    214                 if (tv.getEllipsize() != null) {
    215                     tv.setEllipsize(null);
    216                 }
    217                 Layout ll = tv.getLayout();
    218                 // layout might be null if the text just changed, or ellipsizing
    219                 // was just turned off
    220                 if (ll == null) {
    221                     return false;
    222                 }
    223                 // get the non-ellipsized line width, to determine whether scrolling
    224                 // should even be allowed
    225                 mTextWidth = (int) tv.getLayout().getLineWidth(0);
    226                 mViewWidth = tv.getWidth();
    227                 if (mViewWidth > mTextWidth) {
    228                     tv.setEllipsize(TruncateAt.END);
    229                     v.cancelLongPress();
    230                     return false;
    231                 }
    232                 mDraggingLabel = true;
    233                 tv.setHorizontalFadingEdgeEnabled(true);
    234                 v.cancelLongPress();
    235                 return true;
    236             }
    237         }
    238         return false;
    239     }
    240 
    241     Handler mLabelScroller = new Handler() {
    242         @Override
    243         public void handleMessage(Message msg) {
    244             TextView tv = (TextView) msg.obj;
    245             int x = tv.getScrollX();
    246             x = x * 3 / 4;
    247             tv.scrollTo(x, 0);
    248             if (x == 0) {
    249                 tv.setEllipsize(TruncateAt.END);
    250             } else {
    251                 Message newmsg = obtainMessage(0, tv);
    252                 mLabelScroller.sendMessageDelayed(newmsg, 15);
    253             }
    254         }
    255     };
    256 
    257     public boolean onLongClick(View view) {
    258         CharSequence title = null;
    259         String mime = null;
    260         String query = null;
    261         String artist;
    262         String album;
    263         String song;
    264         long audioid;
    265 
    266         try {
    267             artist = mService.getArtistName();
    268             album = mService.getAlbumName();
    269             song = mService.getTrackName();
    270             audioid = mService.getAudioId();
    271         } catch (RemoteException ex) {
    272             return true;
    273         } catch (NullPointerException ex) {
    274             // we might not actually have the service yet
    275             return true;
    276         }
    277 
    278         if (MediaStore.UNKNOWN_STRING.equals(album) && MediaStore.UNKNOWN_STRING.equals(artist)
    279                 && song != null && song.startsWith("recording")) {
    280             // not music
    281             return false;
    282         }
    283 
    284         if (audioid < 0) {
    285             return false;
    286         }
    287 
    288         Cursor c = MusicUtils.query(this,
    289                 ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, audioid),
    290                 new String[] {MediaStore.Audio.Media.IS_MUSIC}, null, null, null);
    291         boolean ismusic = true;
    292         if (c != null) {
    293             if (c.moveToFirst()) {
    294                 ismusic = c.getInt(0) != 0;
    295             }
    296             c.close();
    297         }
    298         if (!ismusic) {
    299             return false;
    300         }
    301 
    302         boolean knownartist = (artist != null) && !MediaStore.UNKNOWN_STRING.equals(artist);
    303 
    304         boolean knownalbum = (album != null) && !MediaStore.UNKNOWN_STRING.equals(album);
    305 
    306         if (knownartist && view.equals(mArtistName.getParent())) {
    307             title = artist;
    308             query = artist;
    309             mime = MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE;
    310         } else if (knownalbum && view.equals(mAlbumName.getParent())) {
    311             title = album;
    312             if (knownartist) {
    313                 query = artist + " " + album;
    314             } else {
    315                 query = album;
    316             }
    317             mime = MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE;
    318         } else if (view.equals(mTrackName.getParent()) || !knownartist || !knownalbum) {
    319             if ((song == null) || MediaStore.UNKNOWN_STRING.equals(song)) {
    320                 // A popup of the form "Search for null/'' using ..." is pretty
    321                 // unhelpful, plus, we won't find any way to buy it anyway.
    322                 return true;
    323             }
    324 
    325             title = song;
    326             if (knownartist) {
    327                 query = artist + " " + song;
    328             } else {
    329                 query = song;
    330             }
    331             mime = "audio/*"; // the specific type doesn't matter, so don't bother retrieving it
    332         } else {
    333             throw new RuntimeException("shouldn't be here");
    334         }
    335         title = getString(R.string.mediasearch, title);
    336 
    337         Intent i = new Intent();
    338         i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    339         i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH);
    340         i.putExtra(SearchManager.QUERY, query);
    341         if (knownartist) {
    342             i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist);
    343         }
    344         if (knownalbum) {
    345             i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, album);
    346         }
    347         i.putExtra(MediaStore.EXTRA_MEDIA_TITLE, song);
    348         i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, mime);
    349 
    350         startActivity(Intent.createChooser(i, title));
    351         return true;
    352     }
    353 
    354     private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
    355         public void onStartTrackingTouch(SeekBar bar) {
    356             mLastSeekEventTime = 0;
    357             mFromTouch = true;
    358         }
    359         public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
    360             if (!fromuser || (mService == null)) return;
    361             long now = SystemClock.elapsedRealtime();
    362             if ((now - mLastSeekEventTime) > 250) {
    363                 mLastSeekEventTime = now;
    364                 mPosOverride = mDuration * progress / 1000;
    365                 try {
    366                     mService.seek(mPosOverride);
    367                 } catch (RemoteException ex) {
    368                 }
    369 
    370                 // trackball event, allow progress updates
    371                 if (!mFromTouch) {
    372                     refreshNow();
    373                     mPosOverride = -1;
    374                 }
    375             }
    376         }
    377         public void onStopTrackingTouch(SeekBar bar) {
    378             mPosOverride = -1;
    379             mFromTouch = false;
    380         }
    381     };
    382 
    383     private View.OnClickListener mQueueListener = new View.OnClickListener() {
    384         public void onClick(View v) {
    385             startActivity(new Intent(Intent.ACTION_EDIT)
    386                                   .setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track")
    387                                   .putExtra("playlist", "nowplaying"));
    388         }
    389     };
    390 
    391     private View.OnClickListener mShuffleListener = new View.OnClickListener() {
    392         public void onClick(View v) {
    393             toggleShuffle();
    394         }
    395     };
    396 
    397     private View.OnClickListener mRepeatListener = new View.OnClickListener() {
    398         public void onClick(View v) {
    399             cycleRepeat();
    400         }
    401     };
    402 
    403     private View.OnClickListener mPauseListener = new View.OnClickListener() {
    404         public void onClick(View v) {
    405             doPauseResume();
    406         }
    407     };
    408 
    409     private View.OnClickListener mPrevListener = new View.OnClickListener() {
    410         public void onClick(View v) {
    411             if (mService == null) return;
    412             try {
    413                 if (mService.position() < 2000) {
    414                     mService.prev();
    415                 } else {
    416                     mService.seek(0);
    417                     mService.play();
    418                 }
    419             } catch (RemoteException ex) {
    420             }
    421         }
    422     };
    423 
    424     private View.OnClickListener mNextListener = new View.OnClickListener() {
    425         public void onClick(View v) {
    426             if (mService == null) return;
    427             try {
    428                 mService.next();
    429             } catch (RemoteException ex) {
    430             }
    431         }
    432     };
    433 
    434     private RepeatingImageButton.RepeatListener mRewListener =
    435             new RepeatingImageButton.RepeatListener() {
    436                 public void onRepeat(View v, long howlong, int repcnt) {
    437                     scanBackward(repcnt, howlong);
    438                 }
    439             };
    440 
    441     private RepeatingImageButton.RepeatListener mFfwdListener =
    442             new RepeatingImageButton.RepeatListener() {
    443                 public void onRepeat(View v, long howlong, int repcnt) {
    444                     scanForward(repcnt, howlong);
    445                 }
    446             };
    447 
    448     @Override
    449     public void onStop() {
    450         paused = true;
    451         mHandler.removeMessages(REFRESH);
    452         unregisterReceiver(mStatusListener);
    453         MusicUtils.unbindFromService(mToken);
    454         mService = null;
    455         super.onStop();
    456     }
    457 
    458     @Override
    459     public void onStart() {
    460         super.onStart();
    461         paused = false;
    462 
    463         mToken = MusicUtils.bindToService(this, osc);
    464         if (mToken == null) {
    465             // something went wrong
    466             mHandler.sendEmptyMessage(QUIT);
    467         }
    468 
    469         IntentFilter f = new IntentFilter();
    470         f.addAction(MediaPlaybackService.PLAYSTATE_CHANGED);
    471         f.addAction(MediaPlaybackService.META_CHANGED);
    472         registerReceiver(mStatusListener, new IntentFilter(f));
    473         updateTrackInfo();
    474         long next = refreshNow();
    475         queueNextRefresh(next);
    476     }
    477 
    478     @Override
    479     public void onNewIntent(Intent intent) {
    480         setIntent(intent);
    481     }
    482 
    483     @Override
    484     public void onResume() {
    485         super.onResume();
    486         updateTrackInfo();
    487         setPauseButtonImage();
    488     }
    489 
    490     @Override
    491     public void onDestroy() {
    492         mAlbumArtWorker.quit();
    493         super.onDestroy();
    494         // System.out.println("***************** playback activity onDestroy\n");
    495     }
    496 
    497     @Override
    498     public boolean onCreateOptionsMenu(Menu menu) {
    499         super.onCreateOptionsMenu(menu);
    500         // Don't show the menu items if we got launched by path/filedescriptor, or
    501         // if we're in one shot mode. In most cases, these menu items are not
    502         // useful in those modes, so for consistency we never show them in these
    503         // modes, instead of tailoring them to the specific file being played.
    504         if (MusicUtils.getCurrentAudioId() >= 0) {
    505             menu.add(0, GOTO_START, 0, R.string.goto_start)
    506                     .setIcon(R.drawable.ic_menu_music_library);
    507             menu.add(0, PARTY_SHUFFLE, 0,
    508                     R.string.party_shuffle); // icon will be set in onPrepareOptionsMenu()
    509             SubMenu sub = menu.addSubMenu(0, ADD_TO_PLAYLIST, 0, R.string.add_to_playlist)
    510                                   .setIcon(android.R.drawable.ic_menu_add);
    511             // these next two are in a separate group, so they can be shown/hidden as needed
    512             // based on the keyguard state
    513             menu.add(1, USE_AS_RINGTONE, 0, R.string.ringtone_menu_short)
    514                     .setIcon(R.drawable.ic_menu_set_as_ringtone);
    515             menu.add(1, DELETE_ITEM, 0, R.string.delete_item).setIcon(R.drawable.ic_menu_delete);
    516 
    517             Intent i = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL);
    518             if (getPackageManager().resolveActivity(i, 0) != null) {
    519                 menu.add(0, EFFECTS_PANEL, 0, R.string.effectspanel).setIcon(R.drawable.ic_menu_eq);
    520             }
    521 
    522             return true;
    523         }
    524         return false;
    525     }
    526 
    527     @Override
    528     public boolean onPrepareOptionsMenu(Menu menu) {
    529         if (mService == null) return false;
    530         MenuItem item = menu.findItem(PARTY_SHUFFLE);
    531         if (item != null) {
    532             int shuffle = MusicUtils.getCurrentShuffleMode();
    533             if (shuffle == MediaPlaybackService.SHUFFLE_AUTO) {
    534                 item.setIcon(R.drawable.ic_menu_party_shuffle);
    535                 item.setTitle(R.string.party_shuffle_off);
    536             } else {
    537                 item.setIcon(R.drawable.ic_menu_party_shuffle);
    538                 item.setTitle(R.string.party_shuffle);
    539             }
    540         }
    541 
    542         item = menu.findItem(ADD_TO_PLAYLIST);
    543         if (item != null) {
    544             SubMenu sub = item.getSubMenu();
    545             MusicUtils.makePlaylistMenu(this, sub);
    546         }
    547 
    548         KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
    549         menu.setGroupVisible(1, !km.inKeyguardRestrictedInputMode());
    550 
    551         return true;
    552     }
    553 
    554     @Override
    555     public boolean onOptionsItemSelected(MenuItem item) {
    556         Intent intent;
    557         try {
    558             switch (item.getItemId()) {
    559                 case GOTO_START:
    560                     intent = new Intent();
    561                     intent.setClass(this, MusicBrowserActivity.class);
    562                     intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
    563                     startActivity(intent);
    564                     finish();
    565                     break;
    566                 case USE_AS_RINGTONE: {
    567                     // Set the system setting to make this the current ringtone
    568                     if (mService != null) {
    569                         MusicUtils.setRingtone(this, mService.getAudioId());
    570                     }
    571                     return true;
    572                 }
    573                 case PARTY_SHUFFLE:
    574                     MusicUtils.togglePartyShuffle();
    575                     setShuffleButtonImage();
    576                     break;
    577 
    578                 case NEW_PLAYLIST: {
    579                     intent = new Intent();
    580                     intent.setClass(this, CreatePlaylist.class);
    581                     startActivityForResult(intent, NEW_PLAYLIST);
    582                     return true;
    583                 }
    584 
    585                 case PLAYLIST_SELECTED: {
    586                     long[] list = new long[1];
    587                     list[0] = MusicUtils.getCurrentAudioId();
    588                     long playlist = item.getIntent().getLongExtra("playlist", 0);
    589                     MusicUtils.addToPlaylist(this, list, playlist);
    590                     return true;
    591                 }
    592 
    593                 case DELETE_ITEM: {
    594                     if (mService != null) {
    595                         long[] list = new long[1];
    596                         list[0] = MusicUtils.getCurrentAudioId();
    597                         Bundle b = new Bundle();
    598                         String f;
    599                         if (android.os.Environment.isExternalStorageRemovable()) {
    600                             f = getString(R.string.delete_song_desc, mService.getTrackName());
    601                         } else {
    602                             f = getString(
    603                                     R.string.delete_song_desc_nosdcard, mService.getTrackName());
    604                         }
    605                         b.putString("description", f);
    606                         b.putLongArray("items", list);
    607                         intent = new Intent();
    608                         intent.setClass(this, DeleteItems.class);
    609                         intent.putExtras(b);
    610                         startActivityForResult(intent, -1);
    611                     }
    612                     return true;
    613                 }
    614 
    615                 case EFFECTS_PANEL: {
    616                     Intent i = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL);
    617                     i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mService.getAudioSessionId());
    618                     startActivityForResult(i, EFFECTS_PANEL);
    619                     return true;
    620                 }
    621             }
    622         } catch (RemoteException ex) {
    623         }
    624         return super.onOptionsItemSelected(item);
    625     }
    626 
    627     @Override
    628     protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    629         if (resultCode != RESULT_OK) {
    630             return;
    631         }
    632         switch (requestCode) {
    633             case NEW_PLAYLIST:
    634                 Uri uri = intent.getData();
    635                 if (uri != null) {
    636                     long[] list = new long[1];
    637                     list[0] = MusicUtils.getCurrentAudioId();
    638                     int playlist = Integer.parseInt(uri.getLastPathSegment());
    639                     MusicUtils.addToPlaylist(this, list, playlist);
    640                 }
    641                 break;
    642         }
    643     }
    644     private final int keyboard[][] = {
    645             {
    646                     KeyEvent.KEYCODE_Q, KeyEvent.KEYCODE_W, KeyEvent.KEYCODE_E, KeyEvent.KEYCODE_R,
    647                     KeyEvent.KEYCODE_T, KeyEvent.KEYCODE_Y, KeyEvent.KEYCODE_U, KeyEvent.KEYCODE_I,
    648                     KeyEvent.KEYCODE_O, KeyEvent.KEYCODE_P,
    649             },
    650             {
    651                     KeyEvent.KEYCODE_A, KeyEvent.KEYCODE_S, KeyEvent.KEYCODE_D, KeyEvent.KEYCODE_F,
    652                     KeyEvent.KEYCODE_G, KeyEvent.KEYCODE_H, KeyEvent.KEYCODE_J, KeyEvent.KEYCODE_K,
    653                     KeyEvent.KEYCODE_L, KeyEvent.KEYCODE_DEL,
    654             },
    655             {KeyEvent.KEYCODE_Z, KeyEvent.KEYCODE_X, KeyEvent.KEYCODE_C, KeyEvent.KEYCODE_V,
    656                     KeyEvent.KEYCODE_B, KeyEvent.KEYCODE_N, KeyEvent.KEYCODE_M,
    657                     KeyEvent.KEYCODE_COMMA, KeyEvent.KEYCODE_PERIOD, KeyEvent.KEYCODE_ENTER}
    658 
    659     };
    660 
    661     private int lastX;
    662     private int lastY;
    663 
    664     private boolean seekMethod1(int keyCode) {
    665         if (mService == null) return false;
    666         for (int x = 0; x < 10; x++) {
    667             for (int y = 0; y < 3; y++) {
    668                 if (keyboard[y][x] == keyCode) {
    669                     int dir = 0;
    670                     // top row
    671                     if (x == lastX && y == lastY)
    672                         dir = 0;
    673                     else if (y == 0 && lastY == 0 && x > lastX)
    674                         dir = 1;
    675                     else if (y == 0 && lastY == 0 && x < lastX)
    676                         dir = -1;
    677                     // bottom row
    678                     else if (y == 2 && lastY == 2 && x > lastX)
    679                         dir = -1;
    680                     else if (y == 2 && lastY == 2 && x < lastX)
    681                         dir = 1;
    682                     // moving up
    683                     else if (y < lastY && x <= 4)
    684                         dir = 1;
    685                     else if (y < lastY && x >= 5)
    686                         dir = -1;
    687                     // moving down
    688                     else if (y > lastY && x <= 4)
    689                         dir = -1;
    690                     else if (y > lastY && x >= 5)
    691                         dir = 1;
    692                     lastX = x;
    693                     lastY = y;
    694                     try {
    695                         mService.seek(mService.position() + dir * 5);
    696                     } catch (RemoteException ex) {
    697                     }
    698                     refreshNow();
    699                     return true;
    700                 }
    701             }
    702         }
    703         lastX = -1;
    704         lastY = -1;
    705         return false;
    706     }
    707 
    708     private boolean seekMethod2(int keyCode) {
    709         if (mService == null) return false;
    710         for (int i = 0; i < 10; i++) {
    711             if (keyboard[0][i] == keyCode) {
    712                 int seekpercentage = 100 * i / 10;
    713                 try {
    714                     mService.seek(mService.duration() * seekpercentage / 100);
    715                 } catch (RemoteException ex) {
    716                 }
    717                 refreshNow();
    718                 return true;
    719             }
    720         }
    721         return false;
    722     }
    723 
    724     @Override
    725     public boolean onKeyUp(int keyCode, KeyEvent event) {
    726         try {
    727             switch (keyCode) {
    728                 case KeyEvent.KEYCODE_DPAD_LEFT:
    729                     if (!useDpadMusicControl()) {
    730                         break;
    731                     }
    732                     if (mService != null) {
    733                         if (!mSeeking && mStartSeekPos >= 0) {
    734                             mPauseButton.requestFocus();
    735                             if (mStartSeekPos < 1000) {
    736                                 mService.prev();
    737                             } else {
    738                                 mService.seek(0);
    739                             }
    740                         } else {
    741                             scanBackward(-1, event.getEventTime() - event.getDownTime());
    742                             mPauseButton.requestFocus();
    743                             mStartSeekPos = -1;
    744                         }
    745                     }
    746                     mSeeking = false;
    747                     mPosOverride = -1;
    748                     return true;
    749                 case KeyEvent.KEYCODE_DPAD_RIGHT:
    750                     if (!useDpadMusicControl()) {
    751                         break;
    752                     }
    753                     if (mService != null) {
    754                         if (!mSeeking && mStartSeekPos >= 0) {
    755                             mPauseButton.requestFocus();
    756                             mService.next();
    757                         } else {
    758                             scanForward(-1, event.getEventTime() - event.getDownTime());
    759                             mPauseButton.requestFocus();
    760                             mStartSeekPos = -1;
    761                         }
    762                     }
    763                     mSeeking = false;
    764                     mPosOverride = -1;
    765                     return true;
    766             }
    767         } catch (RemoteException ex) {
    768         }
    769         return super.onKeyUp(keyCode, event);
    770     }
    771 
    772     private boolean useDpadMusicControl() {
    773         if (mDeviceHasDpad && (mPrevButton.isFocused() || mNextButton.isFocused()
    774                                       || mPauseButton.isFocused())) {
    775             return true;
    776         }
    777         return false;
    778     }
    779 
    780     @Override
    781     public boolean onKeyDown(int keyCode, KeyEvent event) {
    782         int direction = -1;
    783         int repcnt = event.getRepeatCount();
    784 
    785         if ((seekmethod == 0) ? seekMethod1(keyCode) : seekMethod2(keyCode)) return true;
    786 
    787         switch (keyCode) {
    788             /*
    789                         // image scale
    790                         case KeyEvent.KEYCODE_Q: av.adjustParams(-0.05, 0.0, 0.0, 0.0, 0.0,-1.0);
    791                break;
    792                         case KeyEvent.KEYCODE_E: av.adjustParams( 0.05, 0.0, 0.0, 0.0, 0.0, 1.0);
    793                break;
    794                         // image translate
    795                         case KeyEvent.KEYCODE_W: av.adjustParams(    0.0, 0.0,-1.0, 0.0, 0.0, 0.0);
    796                break;
    797                         case KeyEvent.KEYCODE_X: av.adjustParams(    0.0, 0.0, 1.0, 0.0, 0.0, 0.0);
    798                break;
    799                         case KeyEvent.KEYCODE_A: av.adjustParams(    0.0,-1.0, 0.0, 0.0, 0.0, 0.0);
    800                break;
    801                         case KeyEvent.KEYCODE_D: av.adjustParams(    0.0, 1.0, 0.0, 0.0, 0.0, 0.0);
    802                break;
    803                         // camera rotation
    804                         case KeyEvent.KEYCODE_R: av.adjustParams(    0.0, 0.0, 0.0, 0.0, 0.0,-1.0);
    805                break;
    806                         case KeyEvent.KEYCODE_U: av.adjustParams(    0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
    807                break;
    808                         // camera translate
    809                         case KeyEvent.KEYCODE_Y: av.adjustParams(    0.0, 0.0, 0.0, 0.0,-1.0, 0.0);
    810                break;
    811                         case KeyEvent.KEYCODE_N: av.adjustParams(    0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
    812                break;
    813                         case KeyEvent.KEYCODE_G: av.adjustParams(    0.0, 0.0, 0.0,-1.0, 0.0, 0.0);
    814                break;
    815                         case KeyEvent.KEYCODE_J: av.adjustParams(    0.0, 0.0, 0.0, 1.0, 0.0, 0.0);
    816                break;
    817 
    818             */
    819 
    820             case KeyEvent.KEYCODE_SLASH:
    821                 seekmethod = 1 - seekmethod;
    822                 return true;
    823 
    824             case KeyEvent.KEYCODE_DPAD_LEFT:
    825                 if (!useDpadMusicControl()) {
    826                     break;
    827                 }
    828                 if (!mPrevButton.hasFocus()) {
    829                     mPrevButton.requestFocus();
    830                 }
    831                 scanBackward(repcnt, event.getEventTime() - event.getDownTime());
    832                 return true;
    833             case KeyEvent.KEYCODE_DPAD_RIGHT:
    834                 if (!useDpadMusicControl()) {
    835                     break;
    836                 }
    837                 if (!mNextButton.hasFocus()) {
    838                     mNextButton.requestFocus();
    839                 }
    840                 scanForward(repcnt, event.getEventTime() - event.getDownTime());
    841                 return true;
    842 
    843             case KeyEvent.KEYCODE_S:
    844                 toggleShuffle();
    845                 return true;
    846 
    847             case KeyEvent.KEYCODE_DPAD_CENTER:
    848             case KeyEvent.KEYCODE_SPACE:
    849                 doPauseResume();
    850                 return true;
    851         }
    852         return super.onKeyDown(keyCode, event);
    853     }
    854 
    855     private void scanBackward(int repcnt, long delta) {
    856         if (mService == null) return;
    857         try {
    858             if (repcnt == 0) {
    859                 mStartSeekPos = mService.position();
    860                 mLastSeekEventTime = 0;
    861                 mSeeking = false;
    862             } else {
    863                 mSeeking = true;
    864                 if (delta < 5000) {
    865                     // seek at 10x speed for the first 5 seconds
    866                     delta = delta * 10;
    867                 } else {
    868                     // seek at 40x after that
    869                     delta = 50000 + (delta - 5000) * 40;
    870                 }
    871                 long newpos = mStartSeekPos - delta;
    872                 if (newpos < 0) {
    873                     // move to previous track
    874                     mService.prev();
    875                     long duration = mService.duration();
    876                     mStartSeekPos += duration;
    877                     newpos += duration;
    878                 }
    879                 if (((delta - mLastSeekEventTime) > 250) || repcnt < 0) {
    880                     mService.seek(newpos);
    881                     mLastSeekEventTime = delta;
    882                 }
    883                 if (repcnt >= 0) {
    884                     mPosOverride = newpos;
    885                 } else {
    886                     mPosOverride = -1;
    887                 }
    888                 refreshNow();
    889             }
    890         } catch (RemoteException ex) {
    891         }
    892     }
    893 
    894     private void scanForward(int repcnt, long delta) {
    895         if (mService == null) return;
    896         try {
    897             if (repcnt == 0) {
    898                 mStartSeekPos = mService.position();
    899                 mLastSeekEventTime = 0;
    900                 mSeeking = false;
    901             } else {
    902                 mSeeking = true;
    903                 if (delta < 5000) {
    904                     // seek at 10x speed for the first 5 seconds
    905                     delta = delta * 10;
    906                 } else {
    907                     // seek at 40x after that
    908                     delta = 50000 + (delta - 5000) * 40;
    909                 }
    910                 long newpos = mStartSeekPos + delta;
    911                 long duration = mService.duration();
    912                 if (newpos >= duration) {
    913                     // move to next track
    914                     mService.next();
    915                     mStartSeekPos -= duration; // is OK to go negative
    916                     newpos -= duration;
    917                 }
    918                 if (((delta - mLastSeekEventTime) > 250) || repcnt < 0) {
    919                     mService.seek(newpos);
    920                     mLastSeekEventTime = delta;
    921                 }
    922                 if (repcnt >= 0) {
    923                     mPosOverride = newpos;
    924                 } else {
    925                     mPosOverride = -1;
    926                 }
    927                 refreshNow();
    928             }
    929         } catch (RemoteException ex) {
    930         }
    931     }
    932 
    933     private void doPauseResume() {
    934         try {
    935             if (mService != null) {
    936                 if (mService.isPlaying()) {
    937                     mService.pause();
    938                 } else {
    939                     mService.play();
    940                 }
    941                 refreshNow();
    942                 setPauseButtonImage();
    943             }
    944         } catch (RemoteException ex) {
    945         }
    946     }
    947 
    948     private void toggleShuffle() {
    949         if (mService == null) {
    950             return;
    951         }
    952         try {
    953             int shuffle = mService.getShuffleMode();
    954             if (shuffle == MediaPlaybackService.SHUFFLE_NONE) {
    955                 mService.setShuffleMode(MediaPlaybackService.SHUFFLE_NORMAL);
    956                 if (mService.getRepeatMode() == MediaPlaybackService.REPEAT_CURRENT) {
    957                     mService.setRepeatMode(MediaPlaybackService.REPEAT_ALL);
    958                     setRepeatButtonImage();
    959                 }
    960                 showToast(R.string.shuffle_on_notif);
    961             } else if (shuffle == MediaPlaybackService.SHUFFLE_NORMAL
    962                     || shuffle == MediaPlaybackService.SHUFFLE_AUTO) {
    963                 mService.setShuffleMode(MediaPlaybackService.SHUFFLE_NONE);
    964                 showToast(R.string.shuffle_off_notif);
    965             } else {
    966                 Log.e("MediaPlaybackActivity", "Invalid shuffle mode: " + shuffle);
    967             }
    968             setShuffleButtonImage();
    969         } catch (RemoteException ex) {
    970         }
    971     }
    972 
    973     private void cycleRepeat() {
    974         if (mService == null) {
    975             return;
    976         }
    977         try {
    978             int mode = mService.getRepeatMode();
    979             if (mode == MediaPlaybackService.REPEAT_NONE) {
    980                 mService.setRepeatMode(MediaPlaybackService.REPEAT_ALL);
    981                 showToast(R.string.repeat_all_notif);
    982             } else if (mode == MediaPlaybackService.REPEAT_ALL) {
    983                 mService.setRepeatMode(MediaPlaybackService.REPEAT_CURRENT);
    984                 if (mService.getShuffleMode() != MediaPlaybackService.SHUFFLE_NONE) {
    985                     mService.setShuffleMode(MediaPlaybackService.SHUFFLE_NONE);
    986                     setShuffleButtonImage();
    987                 }
    988                 showToast(R.string.repeat_current_notif);
    989             } else {
    990                 mService.setRepeatMode(MediaPlaybackService.REPEAT_NONE);
    991                 showToast(R.string.repeat_off_notif);
    992             }
    993             setRepeatButtonImage();
    994         } catch (RemoteException ex) {
    995         }
    996     }
    997 
    998     private void showToast(int resid) {
    999         if (mToast == null) {
   1000             mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
   1001         }
   1002         mToast.setText(resid);
   1003         mToast.show();
   1004     }
   1005 
   1006     private void startPlayback() {
   1007         if (mService == null) return;
   1008         Intent intent = getIntent();
   1009         String filename = "";
   1010         Uri uri = intent.getData();
   1011         if (uri != null && uri.toString().length() > 0) {
   1012             // If this is a file:// URI, just use the path directly instead
   1013             // of going through the open-from-filedescriptor codepath.
   1014             String scheme = uri.getScheme();
   1015             if ("file".equals(scheme)) {
   1016                 filename = uri.getPath();
   1017             } else {
   1018                 filename = uri.toString();
   1019             }
   1020             try {
   1021                 mService.stop();
   1022                 mService.openFile(filename);
   1023                 mService.play();
   1024                 setIntent(new Intent());
   1025             } catch (Exception ex) {
   1026                 Log.d("MediaPlaybackActivity", "couldn't start playback: " + ex);
   1027             }
   1028         }
   1029 
   1030         updateTrackInfo();
   1031         long next = refreshNow();
   1032         queueNextRefresh(next);
   1033     }
   1034 
   1035     private ServiceConnection osc = new ServiceConnection() {
   1036         public void onServiceConnected(ComponentName classname, IBinder obj) {
   1037             mService = IMediaPlaybackService.Stub.asInterface(obj);
   1038             startPlayback();
   1039             try {
   1040                 // Assume something is playing when the service says it is,
   1041                 // but also if the audio ID is valid but the service is paused.
   1042                 if (mService.getAudioId() >= 0 || mService.isPlaying()
   1043                         || mService.getPath() != null) {
   1044                     // something is playing now, we're done
   1045                     mRepeatButton.setVisibility(View.VISIBLE);
   1046                     mShuffleButton.setVisibility(View.VISIBLE);
   1047                     mQueueButton.setVisibility(View.VISIBLE);
   1048                     setRepeatButtonImage();
   1049                     setShuffleButtonImage();
   1050                     setPauseButtonImage();
   1051                     return;
   1052                 }
   1053             } catch (RemoteException ex) {
   1054             }
   1055             // Service is dead or not playing anything. If we got here as part
   1056             // of a "play this file" Intent, exit. Otherwise go to the Music
   1057             // app start screen.
   1058             if (getIntent().getData() == null) {
   1059                 Intent intent = new Intent(Intent.ACTION_MAIN);
   1060                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1061                 intent.setClass(MediaPlaybackActivity.this, MusicBrowserActivity.class);
   1062                 startActivity(intent);
   1063             }
   1064             finish();
   1065         }
   1066         public void onServiceDisconnected(ComponentName classname) {
   1067             mService = null;
   1068         }
   1069     };
   1070 
   1071     private void setRepeatButtonImage() {
   1072         if (mService == null) return;
   1073         try {
   1074             switch (mService.getRepeatMode()) {
   1075                 case MediaPlaybackService.REPEAT_ALL:
   1076                     mRepeatButton.setImageResource(R.drawable.ic_mp_repeat_all_btn);
   1077                     break;
   1078                 case MediaPlaybackService.REPEAT_CURRENT:
   1079                     mRepeatButton.setImageResource(R.drawable.ic_mp_repeat_once_btn);
   1080                     break;
   1081                 default:
   1082                     mRepeatButton.setImageResource(R.drawable.ic_mp_repeat_off_btn);
   1083                     break;
   1084             }
   1085         } catch (RemoteException ex) {
   1086         }
   1087     }
   1088 
   1089     private void setShuffleButtonImage() {
   1090         if (mService == null) return;
   1091         try {
   1092             switch (mService.getShuffleMode()) {
   1093                 case MediaPlaybackService.SHUFFLE_NONE:
   1094                     mShuffleButton.setImageResource(R.drawable.ic_mp_shuffle_off_btn);
   1095                     break;
   1096                 case MediaPlaybackService.SHUFFLE_AUTO:
   1097                     mShuffleButton.setImageResource(R.drawable.ic_mp_partyshuffle_on_btn);
   1098                     break;
   1099                 default:
   1100                     mShuffleButton.setImageResource(R.drawable.ic_mp_shuffle_on_btn);
   1101                     break;
   1102             }
   1103         } catch (RemoteException ex) {
   1104         }
   1105     }
   1106 
   1107     private void setPauseButtonImage() {
   1108         try {
   1109             if (mService != null && mService.isPlaying()) {
   1110                 mPauseButton.setImageResource(android.R.drawable.ic_media_pause);
   1111             } else {
   1112                 mPauseButton.setImageResource(android.R.drawable.ic_media_play);
   1113             }
   1114         } catch (RemoteException ex) {
   1115         }
   1116     }
   1117 
   1118     private ImageView mAlbum;
   1119     private TextView mCurrentTime;
   1120     private TextView mTotalTime;
   1121     private TextView mArtistName;
   1122     private TextView mAlbumName;
   1123     private TextView mTrackName;
   1124     private ProgressBar mProgress;
   1125     private long mPosOverride = -1;
   1126     private boolean mFromTouch = false;
   1127     private long mDuration;
   1128     private int seekmethod;
   1129     private boolean paused;
   1130 
   1131     private static final int REFRESH = 1;
   1132     private static final int QUIT = 2;
   1133     private static final int GET_ALBUM_ART = 3;
   1134     private static final int ALBUM_ART_DECODED = 4;
   1135 
   1136     private void queueNextRefresh(long delay) {
   1137         if (!paused) {
   1138             Message msg = mHandler.obtainMessage(REFRESH);
   1139             mHandler.removeMessages(REFRESH);
   1140             mHandler.sendMessageDelayed(msg, delay);
   1141         }
   1142     }
   1143 
   1144     private long refreshNow() {
   1145         if (mService == null) return 500;
   1146         try {
   1147             long pos = mPosOverride < 0 ? mService.position() : mPosOverride;
   1148             if ((pos >= 0) && (mDuration > 0)) {
   1149                 mCurrentTime.setText(MusicUtils.makeTimeString(this, pos / 1000));
   1150                 int progress = (int) (1000 * pos / mDuration);
   1151                 mProgress.setProgress(progress);
   1152 
   1153                 if (mService.isPlaying()) {
   1154                     mCurrentTime.setVisibility(View.VISIBLE);
   1155                 } else {
   1156                     // blink the counter
   1157                     int vis = mCurrentTime.getVisibility();
   1158                     mCurrentTime.setVisibility(
   1159                             vis == View.INVISIBLE ? View.VISIBLE : View.INVISIBLE);
   1160                     return 500;
   1161                 }
   1162             } else {
   1163                 mCurrentTime.setText("--:--");
   1164                 mProgress.setProgress(1000);
   1165             }
   1166             // calculate the number of milliseconds until the next full second, so
   1167             // the counter can be updated at just the right time
   1168             long remaining = 1000 - (pos % 1000);
   1169 
   1170             // approximate how often we would need to refresh the slider to
   1171             // move it smoothly
   1172             int width = mProgress.getWidth();
   1173             if (width == 0) width = 320;
   1174             long smoothrefreshtime = mDuration / width;
   1175 
   1176             if (smoothrefreshtime > remaining) return remaining;
   1177             if (smoothrefreshtime < 20) return 20;
   1178             return smoothrefreshtime;
   1179         } catch (RemoteException ex) {
   1180         }
   1181         return 500;
   1182     }
   1183 
   1184     private final Handler mHandler = new Handler() {
   1185         @Override
   1186         public void handleMessage(Message msg) {
   1187             switch (msg.what) {
   1188                 case ALBUM_ART_DECODED:
   1189                     mAlbum.setImageBitmap((Bitmap) msg.obj);
   1190                     mAlbum.getDrawable().setDither(true);
   1191                     break;
   1192 
   1193                 case REFRESH:
   1194                     long next = refreshNow();
   1195                     queueNextRefresh(next);
   1196                     break;
   1197 
   1198                 case QUIT:
   1199                     // This can be moved back to onCreate once the bug that prevents
   1200                     // Dialogs from being started from onCreate/onResume is fixed.
   1201                     new AlertDialog.Builder(MediaPlaybackActivity.this)
   1202                             .setTitle(R.string.service_start_error_title)
   1203                             .setMessage(R.string.service_start_error_msg)
   1204                             .setPositiveButton(R.string.service_start_error_button,
   1205                                     new DialogInterface.OnClickListener() {
   1206                                         public void onClick(
   1207                                                 DialogInterface dialog, int whichButton) {
   1208                                             finish();
   1209                                         }
   1210                                     })
   1211                             .setCancelable(false)
   1212                             .show();
   1213                     break;
   1214 
   1215                 default:
   1216                     break;
   1217             }
   1218         }
   1219     };
   1220 
   1221     private BroadcastReceiver mStatusListener = new BroadcastReceiver() {
   1222         @Override
   1223         public void onReceive(Context context, Intent intent) {
   1224             String action = intent.getAction();
   1225             if (action.equals(MediaPlaybackService.META_CHANGED)) {
   1226                 // redraw the artist/title info and
   1227                 // set new max for progress bar
   1228                 updateTrackInfo();
   1229                 setPauseButtonImage();
   1230                 queueNextRefresh(1);
   1231             } else if (action.equals(MediaPlaybackService.PLAYSTATE_CHANGED)) {
   1232                 setPauseButtonImage();
   1233             }
   1234         }
   1235     };
   1236 
   1237     private static class AlbumSongIdWrapper {
   1238         public long albumid;
   1239         public long songid;
   1240         AlbumSongIdWrapper(long aid, long sid) {
   1241             albumid = aid;
   1242             songid = sid;
   1243         }
   1244     }
   1245 
   1246     private void updateTrackInfo() {
   1247         if (mService == null) {
   1248             return;
   1249         }
   1250         try {
   1251             String path = mService.getPath();
   1252             if (path == null) {
   1253                 finish();
   1254                 return;
   1255             }
   1256 
   1257             long songid = mService.getAudioId();
   1258             if (songid < 0 && path.toLowerCase().startsWith("http://")) {
   1259                 // Once we can get album art and meta data from MediaPlayer, we
   1260                 // can show that info again when streaming.
   1261                 ((View) mArtistName.getParent()).setVisibility(View.INVISIBLE);
   1262                 ((View) mAlbumName.getParent()).setVisibility(View.INVISIBLE);
   1263                 mAlbum.setVisibility(View.GONE);
   1264                 mTrackName.setText(path);
   1265                 mAlbumArtHandler.removeMessages(GET_ALBUM_ART);
   1266                 mAlbumArtHandler.obtainMessage(GET_ALBUM_ART, new AlbumSongIdWrapper(-1, -1))
   1267                         .sendToTarget();
   1268             } else {
   1269                 ((View) mArtistName.getParent()).setVisibility(View.VISIBLE);
   1270                 ((View) mAlbumName.getParent()).setVisibility(View.VISIBLE);
   1271                 String artistName = mService.getArtistName();
   1272                 if (MediaStore.UNKNOWN_STRING.equals(artistName)) {
   1273                     artistName = getString(R.string.unknown_artist_name);
   1274                 }
   1275                 mArtistName.setText(artistName);
   1276                 String albumName = mService.getAlbumName();
   1277                 long albumid = mService.getAlbumId();
   1278                 if (MediaStore.UNKNOWN_STRING.equals(albumName)) {
   1279                     albumName = getString(R.string.unknown_album_name);
   1280                     albumid = -1;
   1281                 }
   1282                 mAlbumName.setText(albumName);
   1283                 mTrackName.setText(mService.getTrackName());
   1284                 mAlbumArtHandler.removeMessages(GET_ALBUM_ART);
   1285                 mAlbumArtHandler
   1286                         .obtainMessage(GET_ALBUM_ART, new AlbumSongIdWrapper(albumid, songid))
   1287                         .sendToTarget();
   1288                 mAlbum.setVisibility(View.VISIBLE);
   1289             }
   1290             mDuration = mService.duration();
   1291             mTotalTime.setText(MusicUtils.makeTimeString(this, mDuration / 1000));
   1292         } catch (RemoteException ex) {
   1293             finish();
   1294         }
   1295     }
   1296 
   1297     public class AlbumArtHandler extends Handler {
   1298         private long mAlbumId = -1;
   1299 
   1300         public AlbumArtHandler(Looper looper) {
   1301             super(looper);
   1302         }
   1303         @Override
   1304         public void handleMessage(Message msg) {
   1305             long albumid = ((AlbumSongIdWrapper) msg.obj).albumid;
   1306             long songid = ((AlbumSongIdWrapper) msg.obj).songid;
   1307             if (msg.what == GET_ALBUM_ART && (mAlbumId != albumid || albumid < 0)) {
   1308                 // while decoding the new image, show the default album art
   1309                 Message numsg = mHandler.obtainMessage(ALBUM_ART_DECODED, null);
   1310                 mHandler.removeMessages(ALBUM_ART_DECODED);
   1311                 mHandler.sendMessageDelayed(numsg, 300);
   1312                 // Don't allow default artwork here, because we want to fall back to song-specific
   1313                 // album art if we can't find anything for the album.
   1314                 Bitmap bm =
   1315                         MusicUtils.getArtwork(MediaPlaybackActivity.this, songid, albumid, false);
   1316                 if (bm == null) {
   1317                     bm = MusicUtils.getArtwork(MediaPlaybackActivity.this, songid, -1);
   1318                     albumid = -1;
   1319                 }
   1320                 if (bm != null) {
   1321                     numsg = mHandler.obtainMessage(ALBUM_ART_DECODED, bm);
   1322                     mHandler.removeMessages(ALBUM_ART_DECODED);
   1323                     mHandler.sendMessage(numsg);
   1324                 }
   1325                 mAlbumId = albumid;
   1326             }
   1327         }
   1328     }
   1329 
   1330     private static class Worker implements Runnable {
   1331         private final Object mLock = new Object();
   1332         private Looper mLooper;
   1333 
   1334         /**
   1335          * Creates a worker thread with the given name. The thread
   1336          * then runs a {@link android.os.Looper}.
   1337          * @param name A name for the new thread
   1338          */
   1339         Worker(String name) {
   1340             Thread t = new Thread(null, this, name);
   1341             t.setPriority(Thread.MIN_PRIORITY);
   1342             t.start();
   1343             synchronized (mLock) {
   1344                 while (mLooper == null) {
   1345                     try {
   1346                         mLock.wait();
   1347                     } catch (InterruptedException ex) {
   1348                     }
   1349                 }
   1350             }
   1351         }
   1352 
   1353         public Looper getLooper() {
   1354             return mLooper;
   1355         }
   1356 
   1357         public void run() {
   1358             synchronized (mLock) {
   1359                 Looper.prepare();
   1360                 mLooper = Looper.myLooper();
   1361                 mLock.notifyAll();
   1362             }
   1363             Looper.loop();
   1364         }
   1365 
   1366         public void quit() {
   1367             mLooper.quit();
   1368         }
   1369     }
   1370 }
   1371