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