• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AlbumBrowserActivity()82     public AlbumBrowserActivity() {}
83 
84     /** Called when the activity is first created. */
85     @Override
onCreate(Bundle icicle)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
onRetainNonConfigurationInstance()133     public Object onRetainNonConfigurationInstance() {
134         mAdapterSent = true;
135         return mAdapter;
136     }
137 
138     @Override
onSaveInstanceState(Bundle outcicle)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
onDestroy()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
onResume()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
onPause()215     public void onPause() {
216         unregisterReceiver(mTrackListListener);
217         mReScanHandler.removeCallbacksAndMessages(null);
218         super.onPause();
219     }
220 
init(Cursor c)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 
setTitle()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
onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn)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
onContextItemSelected(MenuItem item)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 
doSearch()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
onActivityResult(int requestCode, int resultCode, Intent intent)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
onListItemClick(ListView l, View v, int position, long id)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
onCreateOptionsMenu(Menu menu)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
onPrepareOptionsMenu(Menu menu)416     public boolean onPrepareOptionsMenu(Menu menu) {
417         MusicUtils.setPartyShuffleMenuIcon(menu);
418         return super.onPrepareOptionsMenu(menu);
419     }
420 
421     @Override
onOptionsItemSelected(MenuItem item)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 
getAlbumCursor(AsyncQueryHandler async, String filter)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 {
QueryHandler(ContentResolver res)504             QueryHandler(ContentResolver res) {
505                 super(res);
506             }
507 
508             @Override
onQueryComplete(int token, Object cookie, Cursor cursor)509             protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
510                 // Log.i("@@@", "query complete");
511                 mActivity.init(cursor);
512             }
513         }
514 
AlbumListAdapter(Context context, AlbumBrowserActivity currentactivity, int layout, Cursor cursor, String[] from, int[] to)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 
getColumnIndices(Cursor cursor)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 
setActivity(AlbumBrowserActivity newactivity)553         public void setActivity(AlbumBrowserActivity newactivity) {
554             mActivity = newactivity;
555         }
556 
getQueryHandler()557         public AsyncQueryHandler getQueryHandler() {
558             return mQueryHandler;
559         }
560 
561         @Override
newView(Context context, Cursor cursor, ViewGroup parent)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
bindView(View view, Context context, Cursor cursor)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
changeCursor(Cursor cursor)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
runQueryOnBackgroundThread(CharSequence constraint)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 
getSections()641         public Object[] getSections() {
642             return mIndexer.getSections();
643         }
644 
getPositionForSection(int section)645         public int getPositionForSection(int section) {
646             return mIndexer.getPositionForSection(section);
647         }
648 
getSectionForPosition(int position)649         public int getSectionForPosition(int position) {
650             return 0;
651         }
652     }
653 
654     private Cursor mAlbumCursor;
655     private String mArtistId;
656 
onServiceConnected(ComponentName name, IBinder service)657     public void onServiceConnected(ComponentName name, IBinder service) {
658         MusicUtils.updateNowPlaying(this);
659     }
660 
onServiceDisconnected(ComponentName name)661     public void onServiceDisconnected(ComponentName name) {
662         finish();
663     }
664 }
665