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