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.ListActivity;
     22 import android.app.SearchManager;
     23 import android.content.AsyncQueryHandler;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ComponentName;
     26 import android.content.ContentResolver;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.content.IntentFilter;
     30 import android.content.ServiceConnection;
     31 import android.content.res.Resources;
     32 import android.database.Cursor;
     33 import android.graphics.Bitmap;
     34 import android.graphics.BitmapFactory;
     35 import android.graphics.drawable.BitmapDrawable;
     36 import android.graphics.drawable.Drawable;
     37 import android.media.AudioManager;
     38 import android.net.Uri;
     39 import android.os.Bundle;
     40 import android.os.Handler;
     41 import android.os.IBinder;
     42 import android.os.Message;
     43 import android.provider.MediaStore;
     44 import android.text.TextUtils;
     45 import android.util.Log;
     46 import android.view.ContextMenu;
     47 import android.view.Menu;
     48 import android.view.MenuItem;
     49 import android.view.SubMenu;
     50 import android.view.View;
     51 import android.view.ViewGroup;
     52 import android.view.Window;
     53 import android.view.ContextMenu.ContextMenuInfo;
     54 import android.widget.Adapter;
     55 import android.widget.AlphabetIndexer;
     56 import android.widget.CursorAdapter;
     57 import android.widget.ExpandableListView;
     58 import android.widget.ImageView;
     59 import android.widget.ListAdapter;
     60 import android.widget.ListView;
     61 import android.widget.SectionIndexer;
     62 import android.widget.SimpleCursorAdapter;
     63 import android.widget.TextView;
     64 import android.widget.AdapterView.AdapterContextMenuInfo;
     65 
     66 import java.text.Collator;
     67 
     68 public class AlbumBrowserActivity extends ListActivity
     69         implements View.OnCreateContextMenuListener, MusicUtils.Defs, ServiceConnection {
     70     private String mCurrentAlbumId;
     71     private String mCurrentAlbumName;
     72     private String mCurrentArtistNameForAlbum;
     73     boolean mIsUnknownArtist;
     74     boolean mIsUnknownAlbum;
     75     private AlbumListAdapter mAdapter;
     76     private boolean mAdapterSent;
     77     private final static int SEARCH = CHILD_MENU_BASE;
     78     private static int mLastListPosCourse = -1;
     79     private static int mLastListPosFine = -1;
     80     private ServiceToken mToken;
     81 
     82     public AlbumBrowserActivity() {}
     83 
     84     /** Called when the activity is first created. */
     85     @Override
     86     public void onCreate(Bundle icicle) {
     87         if (icicle != null) {
     88             mCurrentAlbumId = icicle.getString("selectedalbum");
     89             mArtistId = icicle.getString("artist");
     90         } else {
     91             mArtistId = getIntent().getStringExtra("artist");
     92         }
     93         super.onCreate(icicle);
     94         requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
     95         requestWindowFeature(Window.FEATURE_NO_TITLE);
     96         setVolumeControlStream(AudioManager.STREAM_MUSIC);
     97         mToken = MusicUtils.bindToService(this, this);
     98 
     99         IntentFilter f = new IntentFilter();
    100         f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
    101         f.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
    102         f.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
    103         f.addDataScheme("file");
    104         registerReceiver(mScanListener, f);
    105 
    106         setContentView(R.layout.media_picker_activity);
    107         MusicUtils.updateButtonBar(this, R.id.albumtab);
    108         ListView lv = getListView();
    109         lv.setOnCreateContextMenuListener(this);
    110         lv.setTextFilterEnabled(true);
    111 
    112         mAdapter = (AlbumListAdapter) getLastNonConfigurationInstance();
    113         if (mAdapter == null) {
    114             // Log.i("@@@", "starting query");
    115             mAdapter = new AlbumListAdapter(getApplication(), this, R.layout.track_list_item,
    116                     mAlbumCursor, new String[] {}, new int[] {});
    117             setListAdapter(mAdapter);
    118             setTitle(R.string.working_albums);
    119             getAlbumCursor(mAdapter.getQueryHandler(), null);
    120         } else {
    121             mAdapter.setActivity(this);
    122             setListAdapter(mAdapter);
    123             mAlbumCursor = mAdapter.getCursor();
    124             if (mAlbumCursor != null) {
    125                 init(mAlbumCursor);
    126             } else {
    127                 getAlbumCursor(mAdapter.getQueryHandler(), null);
    128             }
    129         }
    130     }
    131 
    132     @Override
    133     public Object onRetainNonConfigurationInstance() {
    134         mAdapterSent = true;
    135         return mAdapter;
    136     }
    137 
    138     @Override
    139     public void onSaveInstanceState(Bundle outcicle) {
    140         // need to store the selected item so we don't lose it in case
    141         // of an orientation switch. Otherwise we could lose it while
    142         // in the middle of specifying a playlist to add the item to.
    143         outcicle.putString("selectedalbum", mCurrentAlbumId);
    144         outcicle.putString("artist", mArtistId);
    145         super.onSaveInstanceState(outcicle);
    146     }
    147 
    148     @Override
    149     public void onDestroy() {
    150         ListView lv = getListView();
    151         if (lv != null) {
    152             mLastListPosCourse = lv.getFirstVisiblePosition();
    153             View cv = lv.getChildAt(0);
    154             if (cv != null) {
    155                 mLastListPosFine = cv.getTop();
    156             }
    157         }
    158         MusicUtils.unbindFromService(mToken);
    159         // If we have an adapter and didn't send it off to another activity yet, we should
    160         // close its cursor, which we do by assigning a null cursor to it. Doing this
    161         // instead of closing the cursor directly keeps the framework from accessing
    162         // the closed cursor later.
    163         if (!mAdapterSent && mAdapter != null) {
    164             mAdapter.changeCursor(null);
    165         }
    166         // Because we pass the adapter to the next activity, we need to make
    167         // sure it doesn't keep a reference to this activity. We can do this
    168         // by clearing its DatasetObservers, which setListAdapter(null) does.
    169         setListAdapter(null);
    170         mAdapter = null;
    171         unregisterReceiver(mScanListener);
    172         super.onDestroy();
    173     }
    174 
    175     @Override
    176     public void onResume() {
    177         super.onResume();
    178         IntentFilter f = new IntentFilter();
    179         f.addAction(MediaPlaybackService.META_CHANGED);
    180         f.addAction(MediaPlaybackService.QUEUE_CHANGED);
    181         registerReceiver(mTrackListListener, f);
    182         mTrackListListener.onReceive(null, null);
    183 
    184         MusicUtils.setSpinnerState(this);
    185     }
    186 
    187     private BroadcastReceiver mTrackListListener = new BroadcastReceiver() {
    188         @Override
    189         public void onReceive(Context context, Intent intent) {
    190             getListView().invalidateViews();
    191             MusicUtils.updateNowPlaying(AlbumBrowserActivity.this);
    192         }
    193     };
    194     private BroadcastReceiver mScanListener = new BroadcastReceiver() {
    195         @Override
    196         public void onReceive(Context context, Intent intent) {
    197             MusicUtils.setSpinnerState(AlbumBrowserActivity.this);
    198             mReScanHandler.sendEmptyMessage(0);
    199             if (intent.getAction().equals(Intent.ACTION_MEDIA_UNMOUNTED)) {
    200                 MusicUtils.clearAlbumArtCache();
    201             }
    202         }
    203     };
    204 
    205     private Handler mReScanHandler = new Handler() {
    206         @Override
    207         public void handleMessage(Message msg) {
    208             if (mAdapter != null) {
    209                 getAlbumCursor(mAdapter.getQueryHandler(), null);
    210             }
    211         }
    212     };
    213 
    214     @Override
    215     public void onPause() {
    216         unregisterReceiver(mTrackListListener);
    217         mReScanHandler.removeCallbacksAndMessages(null);
    218         super.onPause();
    219     }
    220 
    221     public void init(Cursor c) {
    222         if (mAdapter == null) {
    223             return;
    224         }
    225         mAdapter.changeCursor(c); // also sets mAlbumCursor
    226 
    227         if (mAlbumCursor == null) {
    228             MusicUtils.displayDatabaseError(this);
    229             closeContextMenu();
    230             mReScanHandler.sendEmptyMessageDelayed(0, 1000);
    231             return;
    232         }
    233 
    234         // restore previous position
    235         if (mLastListPosCourse >= 0) {
    236             getListView().setSelectionFromTop(mLastListPosCourse, mLastListPosFine);
    237             mLastListPosCourse = -1;
    238         }
    239 
    240         MusicUtils.hideDatabaseError(this);
    241         MusicUtils.updateButtonBar(this, R.id.albumtab);
    242         setTitle();
    243     }
    244 
    245     private void setTitle() {
    246         CharSequence fancyName = "";
    247         if (mAlbumCursor != null && mAlbumCursor.getCount() > 0) {
    248             mAlbumCursor.moveToFirst();
    249             fancyName = mAlbumCursor.getString(
    250                     mAlbumCursor.getColumnIndex(MediaStore.Audio.Albums.ARTIST));
    251             if (fancyName == null || fancyName.equals(MediaStore.UNKNOWN_STRING))
    252                 fancyName = getText(R.string.unknown_artist_name);
    253         }
    254 
    255         if (mArtistId != null && fancyName != null)
    256             setTitle(fancyName);
    257         else
    258             setTitle(R.string.albums_title);
    259     }
    260 
    261     @Override
    262     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) {
    263         menu.add(0, PLAY_SELECTION, 0, R.string.play_selection);
    264         SubMenu sub = menu.addSubMenu(0, ADD_TO_PLAYLIST, 0, R.string.add_to_playlist);
    265         MusicUtils.makePlaylistMenu(this, sub);
    266         menu.add(0, DELETE_ITEM, 0, R.string.delete_item);
    267 
    268         AdapterContextMenuInfo mi = (AdapterContextMenuInfo) menuInfoIn;
    269         mAlbumCursor.moveToPosition(mi.position);
    270         mCurrentAlbumId = mAlbumCursor.getString(
    271                 mAlbumCursor.getColumnIndexOrThrow(MediaStore.Audio.Albums._ID));
    272         mCurrentAlbumName = mAlbumCursor.getString(
    273                 mAlbumCursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM));
    274         mCurrentArtistNameForAlbum = mAlbumCursor.getString(
    275                 mAlbumCursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ARTIST));
    276         mIsUnknownArtist = mCurrentArtistNameForAlbum == null
    277                 || mCurrentArtistNameForAlbum.equals(MediaStore.UNKNOWN_STRING);
    278         mIsUnknownAlbum =
    279                 mCurrentAlbumName == null || mCurrentAlbumName.equals(MediaStore.UNKNOWN_STRING);
    280         if (mIsUnknownAlbum) {
    281             menu.setHeaderTitle(getString(R.string.unknown_album_name));
    282         } else {
    283             menu.setHeaderTitle(mCurrentAlbumName);
    284         }
    285         if (!mIsUnknownAlbum || !mIsUnknownArtist) {
    286             menu.add(0, SEARCH, 0, R.string.search_title);
    287         }
    288     }
    289 
    290     @Override
    291     public boolean onContextItemSelected(MenuItem item) {
    292         switch (item.getItemId()) {
    293             case PLAY_SELECTION: {
    294                 // play the selected album
    295                 long[] list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
    296                 MusicUtils.playAll(this, list, 0);
    297                 return true;
    298             }
    299 
    300             case QUEUE: {
    301                 long[] list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
    302                 MusicUtils.addToCurrentPlaylist(this, list);
    303                 return true;
    304             }
    305 
    306             case NEW_PLAYLIST: {
    307                 Intent intent = new Intent();
    308                 intent.setClass(this, CreatePlaylist.class);
    309                 startActivityForResult(intent, NEW_PLAYLIST);
    310                 return true;
    311             }
    312 
    313             case PLAYLIST_SELECTED: {
    314                 long[] list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
    315                 long playlist = item.getIntent().getLongExtra("playlist", 0);
    316                 MusicUtils.addToPlaylist(this, list, playlist);
    317                 return true;
    318             }
    319             case DELETE_ITEM: {
    320                 long[] list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
    321                 String f;
    322                 if (android.os.Environment.isExternalStorageRemovable()) {
    323                     f = getString(R.string.delete_album_desc);
    324                 } else {
    325                     f = getString(R.string.delete_album_desc_nosdcard);
    326                 }
    327                 String desc = String.format(f, mCurrentAlbumName);
    328                 Bundle b = new Bundle();
    329                 b.putString("description", desc);
    330                 b.putLongArray("items", list);
    331                 Intent intent = new Intent();
    332                 intent.setClass(this, DeleteItems.class);
    333                 intent.putExtras(b);
    334                 startActivityForResult(intent, -1);
    335                 return true;
    336             }
    337             case SEARCH:
    338                 doSearch();
    339                 return true;
    340         }
    341         return super.onContextItemSelected(item);
    342     }
    343 
    344     void doSearch() {
    345         CharSequence title = null;
    346         String query = "";
    347 
    348         Intent i = new Intent();
    349         i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH);
    350         i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    351 
    352         title = "";
    353         if (!mIsUnknownAlbum) {
    354             query = mCurrentAlbumName;
    355             i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, mCurrentAlbumName);
    356             title = mCurrentAlbumName;
    357         }
    358         if (!mIsUnknownArtist) {
    359             query = query + " " + mCurrentArtistNameForAlbum;
    360             i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, mCurrentArtistNameForAlbum);
    361             title = title + " " + mCurrentArtistNameForAlbum;
    362         }
    363         // Since we hide the 'search' menu item when both album and artist are
    364         // unknown, the query and title strings will have at least one of those.
    365         i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE);
    366         title = getString(R.string.mediasearch, title);
    367         i.putExtra(SearchManager.QUERY, query);
    368 
    369         startActivity(Intent.createChooser(i, title));
    370     }
    371 
    372     @Override
    373     protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    374         switch (requestCode) {
    375             case SCAN_DONE:
    376                 if (resultCode == RESULT_CANCELED) {
    377                     finish();
    378                 } else {
    379                     getAlbumCursor(mAdapter.getQueryHandler(), null);
    380                 }
    381                 break;
    382 
    383             case NEW_PLAYLIST:
    384                 if (resultCode == RESULT_OK) {
    385                     Uri uri = intent.getData();
    386                     if (uri != null) {
    387                         long[] list = MusicUtils.getSongListForAlbum(
    388                                 this, Long.parseLong(mCurrentAlbumId));
    389                         MusicUtils.addToPlaylist(
    390                                 this, list, Long.parseLong(uri.getLastPathSegment()));
    391                     }
    392                 }
    393                 break;
    394         }
    395     }
    396 
    397     @Override
    398     protected void onListItemClick(ListView l, View v, int position, long id) {
    399         Intent intent = new Intent(Intent.ACTION_PICK);
    400         intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
    401         intent.putExtra("album", Long.valueOf(id).toString());
    402         intent.putExtra("artist", mArtistId);
    403         startActivity(intent);
    404     }
    405 
    406     @Override
    407     public boolean onCreateOptionsMenu(Menu menu) {
    408         super.onCreateOptionsMenu(menu);
    409         menu.add(0, PARTY_SHUFFLE, 0,
    410                 R.string.party_shuffle); // icon will be set in onPrepareOptionsMenu()
    411         menu.add(0, SHUFFLE_ALL, 0, R.string.shuffle_all).setIcon(R.drawable.ic_menu_shuffle);
    412         return true;
    413     }
    414 
    415     @Override
    416     public boolean onPrepareOptionsMenu(Menu menu) {
    417         MusicUtils.setPartyShuffleMenuIcon(menu);
    418         return super.onPrepareOptionsMenu(menu);
    419     }
    420 
    421     @Override
    422     public boolean onOptionsItemSelected(MenuItem item) {
    423         Intent intent;
    424         Cursor cursor;
    425         switch (item.getItemId()) {
    426             case PARTY_SHUFFLE:
    427                 MusicUtils.togglePartyShuffle();
    428                 break;
    429 
    430             case SHUFFLE_ALL:
    431                 cursor = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
    432                         new String[] {MediaStore.Audio.Media._ID},
    433                         MediaStore.Audio.Media.IS_MUSIC + "=1", null,
    434                         MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
    435                 if (cursor != null) {
    436                     MusicUtils.shuffleAll(this, cursor);
    437                     cursor.close();
    438                 }
    439                 return true;
    440         }
    441         return super.onOptionsItemSelected(item);
    442     }
    443 
    444     private Cursor getAlbumCursor(AsyncQueryHandler async, String filter) {
    445         String[] cols = new String[] {MediaStore.Audio.Albums._ID, MediaStore.Audio.Albums.ARTIST,
    446                 MediaStore.Audio.Albums.ALBUM, MediaStore.Audio.Albums.ALBUM_ART};
    447 
    448         Cursor ret = null;
    449         if (mArtistId != null) {
    450             Uri uri = MediaStore.Audio.Artists.Albums.getContentUri(
    451                     "external", Long.valueOf(mArtistId));
    452             if (!TextUtils.isEmpty(filter)) {
    453                 uri = uri.buildUpon().appendQueryParameter("filter", Uri.encode(filter)).build();
    454             }
    455             if (async != null) {
    456                 async.startQuery(
    457                         0, null, uri, cols, null, null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER);
    458             } else {
    459                 ret = MusicUtils.query(
    460                         this, uri, cols, null, null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER);
    461             }
    462         } else {
    463             Uri uri = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
    464             if (!TextUtils.isEmpty(filter)) {
    465                 uri = uri.buildUpon().appendQueryParameter("filter", Uri.encode(filter)).build();
    466             }
    467             if (async != null) {
    468                 async.startQuery(
    469                         0, null, uri, cols, null, null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER);
    470             } else {
    471                 ret = MusicUtils.query(
    472                         this, uri, cols, null, null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER);
    473             }
    474         }
    475         return ret;
    476     }
    477 
    478     static class AlbumListAdapter extends SimpleCursorAdapter implements SectionIndexer {
    479         private final Drawable mNowPlayingOverlay;
    480         private final BitmapDrawable mDefaultAlbumIcon;
    481         private int mAlbumIdx;
    482         private int mArtistIdx;
    483         private int mAlbumArtIndex;
    484         private final Resources mResources;
    485         private final StringBuilder mStringBuilder = new StringBuilder();
    486         private final String mUnknownAlbum;
    487         private final String mUnknownArtist;
    488         private final String mAlbumSongSeparator;
    489         private final Object[] mFormatArgs = new Object[1];
    490         private AlphabetIndexer mIndexer;
    491         private AlbumBrowserActivity mActivity;
    492         private AsyncQueryHandler mQueryHandler;
    493         private String mConstraint = null;
    494         private boolean mConstraintIsValid = false;
    495 
    496         static class ViewHolder {
    497             TextView line1;
    498             TextView line2;
    499             ImageView play_indicator;
    500             ImageView icon;
    501         }
    502 
    503         class QueryHandler extends AsyncQueryHandler {
    504             QueryHandler(ContentResolver res) {
    505                 super(res);
    506             }
    507 
    508             @Override
    509             protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
    510                 // Log.i("@@@", "query complete");
    511                 mActivity.init(cursor);
    512             }
    513         }
    514 
    515         AlbumListAdapter(Context context, AlbumBrowserActivity currentactivity, int layout,
    516                 Cursor cursor, String[] from, int[] to) {
    517             super(context, layout, cursor, from, to);
    518 
    519             mActivity = currentactivity;
    520             mQueryHandler = new QueryHandler(context.getContentResolver());
    521 
    522             mUnknownAlbum = context.getString(R.string.unknown_album_name);
    523             mUnknownArtist = context.getString(R.string.unknown_artist_name);
    524             mAlbumSongSeparator = context.getString(R.string.albumsongseparator);
    525 
    526             Resources r = context.getResources();
    527             mNowPlayingOverlay = r.getDrawable(R.drawable.indicator_ic_mp_playing_list);
    528 
    529             Bitmap b = BitmapFactory.decodeResource(r, R.drawable.albumart_mp_unknown_list);
    530             mDefaultAlbumIcon = new BitmapDrawable(context.getResources(), b);
    531             // no filter or dither, it's a lot faster and we can't tell the difference
    532             mDefaultAlbumIcon.setFilterBitmap(false);
    533             mDefaultAlbumIcon.setDither(false);
    534             getColumnIndices(cursor);
    535             mResources = context.getResources();
    536         }
    537 
    538         private void getColumnIndices(Cursor cursor) {
    539             if (cursor != null) {
    540                 mAlbumIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM);
    541                 mArtistIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ARTIST);
    542                 mAlbumArtIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM_ART);
    543 
    544                 if (mIndexer != null) {
    545                     mIndexer.setCursor(cursor);
    546                 } else {
    547                     mIndexer = new MusicAlphabetIndexer(
    548                             cursor, mAlbumIdx, mResources.getString(R.string.fast_scroll_alphabet));
    549                 }
    550             }
    551         }
    552 
    553         public void setActivity(AlbumBrowserActivity newactivity) {
    554             mActivity = newactivity;
    555         }
    556 
    557         public AsyncQueryHandler getQueryHandler() {
    558             return mQueryHandler;
    559         }
    560 
    561         @Override
    562         public View newView(Context context, Cursor cursor, ViewGroup parent) {
    563             View v = super.newView(context, cursor, parent);
    564             ViewHolder vh = new ViewHolder();
    565             vh.line1 = (TextView) v.findViewById(R.id.line1);
    566             vh.line2 = (TextView) v.findViewById(R.id.line2);
    567             vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator);
    568             vh.icon = (ImageView) v.findViewById(R.id.icon);
    569             vh.icon.setBackgroundDrawable(mDefaultAlbumIcon);
    570             vh.icon.setPadding(0, 0, 1, 0);
    571             v.setTag(vh);
    572             return v;
    573         }
    574 
    575         @Override
    576         public void bindView(View view, Context context, Cursor cursor) {
    577             ViewHolder vh = (ViewHolder) view.getTag();
    578 
    579             String name = cursor.getString(mAlbumIdx);
    580             String displayname = name;
    581             boolean unknown = name == null || name.equals(MediaStore.UNKNOWN_STRING);
    582             if (unknown) {
    583                 displayname = mUnknownAlbum;
    584             }
    585             vh.line1.setText(displayname);
    586 
    587             name = cursor.getString(mArtistIdx);
    588             displayname = name;
    589             if (name == null || name.equals(MediaStore.UNKNOWN_STRING)) {
    590                 displayname = mUnknownArtist;
    591             }
    592             vh.line2.setText(displayname);
    593 
    594             ImageView iv = vh.icon;
    595             // We don't actually need the path to the thumbnail file,
    596             // we just use it to see if there is album art or not
    597             String art = cursor.getString(mAlbumArtIndex);
    598             long aid = cursor.getLong(0);
    599             if (unknown || art == null || art.length() == 0) {
    600                 iv.setImageDrawable(null);
    601             } else {
    602                 Drawable d = MusicUtils.getCachedArtwork(context, aid, mDefaultAlbumIcon);
    603                 iv.setImageDrawable(d);
    604             }
    605 
    606             long currentalbumid = MusicUtils.getCurrentAlbumId();
    607             iv = vh.play_indicator;
    608             if (currentalbumid == aid) {
    609                 iv.setImageDrawable(mNowPlayingOverlay);
    610             } else {
    611                 iv.setImageDrawable(null);
    612             }
    613         }
    614 
    615         @Override
    616         public void changeCursor(Cursor cursor) {
    617             if (mActivity.isFinishing() && cursor != null) {
    618                 cursor.close();
    619                 cursor = null;
    620             }
    621             if (cursor != mActivity.mAlbumCursor) {
    622                 mActivity.mAlbumCursor = cursor;
    623                 getColumnIndices(cursor);
    624                 super.changeCursor(cursor);
    625             }
    626         }
    627 
    628         @Override
    629         public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
    630             String s = constraint.toString();
    631             if (mConstraintIsValid && ((s == null && mConstraint == null)
    632                                               || (s != null && s.equals(mConstraint)))) {
    633                 return getCursor();
    634             }
    635             Cursor c = mActivity.getAlbumCursor(null, s);
    636             mConstraint = s;
    637             mConstraintIsValid = true;
    638             return c;
    639         }
    640 
    641         public Object[] getSections() {
    642             return mIndexer.getSections();
    643         }
    644 
    645         public int getPositionForSection(int section) {
    646             return mIndexer.getPositionForSection(section);
    647         }
    648 
    649         public int getSectionForPosition(int position) {
    650             return 0;
    651         }
    652     }
    653 
    654     private Cursor mAlbumCursor;
    655     private String mArtistId;
    656 
    657     public void onServiceConnected(ComponentName name, IBinder service) {
    658         MusicUtils.updateNowPlaying(this);
    659     }
    660 
    661     public void onServiceDisconnected(ComponentName name) {
    662         finish();
    663     }
    664 }
    665