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 
AlbumBrowserActivity()83     public AlbumBrowserActivity()
84     {
85     }
86 
87     /** Called when the activity is first created. */
88     @Override
onCreate(Bundle icicle)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
onRetainNonConfigurationInstance()142     public Object onRetainNonConfigurationInstance() {
143         mAdapterSent = true;
144         return mAdapter;
145     }
146 
147     @Override
onSaveInstanceState(Bundle outcicle)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
onDestroy()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
onResume()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
onPause()224     public void onPause() {
225         unregisterReceiver(mTrackListListener);
226         mReScanHandler.removeCallbacksAndMessages(null);
227         super.onPause();
228     }
229 
init(Cursor c)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 
setTitle()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
onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn)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
onContextItemSelected(MenuItem item)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 
doSearch()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
onActivityResult(int requestCode, int resultCode, Intent intent)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
onListItemClick(ListView l, View v, int position, long id)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
onCreateOptionsMenu(Menu menu)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
onPrepareOptionsMenu(Menu menu)423     public boolean onPrepareOptionsMenu(Menu menu) {
424         MusicUtils.setPartyShuffleMenuIcon(menu);
425         return super.onPrepareOptionsMenu(menu);
426     }
427 
428     @Override
onOptionsItemSelected(MenuItem item)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 
getAlbumCursor(AsyncQueryHandler async, String filter)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 {
QueryHandler(ContentResolver res)518             QueryHandler(ContentResolver res) {
519                 super(res);
520             }
521 
522             @Override
onQueryComplete(int token, Object cookie, Cursor cursor)523             protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
524                 //Log.i("@@@", "query complete");
525                 mActivity.init(cursor);
526             }
527         }
528 
AlbumListAdapter(Context context, AlbumBrowserActivity currentactivity, int layout, Cursor cursor, String[] from, int[] to)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 
getColumnIndices(Cursor cursor)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 
setActivity(AlbumBrowserActivity newactivity)567         public void setActivity(AlbumBrowserActivity newactivity) {
568             mActivity = newactivity;
569         }
570 
getQueryHandler()571         public AsyncQueryHandler getQueryHandler() {
572             return mQueryHandler;
573         }
574 
575         @Override
newView(Context context, Cursor cursor, ViewGroup parent)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
bindView(View view, Context context, Cursor cursor)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
changeCursor(Cursor cursor)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
runQueryOnBackgroundThread(CharSequence constraint)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 
getSections()657         public Object[] getSections() {
658             return mIndexer.getSections();
659         }
660 
getPositionForSection(int section)661         public int getPositionForSection(int section) {
662             return mIndexer.getPositionForSection(section);
663         }
664 
getSectionForPosition(int position)665         public int getSectionForPosition(int position) {
666             return 0;
667         }
668     }
669 
670     private Cursor mAlbumCursor;
671     private String mArtistId;
672 
onServiceConnected(ComponentName name, IBinder service)673     public void onServiceConnected(ComponentName name, IBinder service) {
674         MusicUtils.updateNowPlaying(this);
675     }
676 
onServiceDisconnected(ComponentName name)677     public void onServiceDisconnected(ComponentName name) {
678         finish();
679     }
680 }
681 
682