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