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