ArtistAlbumBrowserActivity.java revision 9882f540844f6a613eeb5edf5f076d1dcc72bf9f
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 com.android.music.QueryBrowserActivity.QueryListAdapter.QueryHandler;
20
21import android.app.ExpandableListActivity;
22import android.app.SearchManager;
23import android.content.AsyncQueryHandler;
24import android.content.BroadcastReceiver;
25import android.content.ContentResolver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.res.Resources;
30import android.database.Cursor;
31import android.database.CursorWrapper;
32import android.graphics.drawable.BitmapDrawable;
33import android.graphics.drawable.Drawable;
34import android.media.AudioManager;
35import android.media.MediaFile;
36import android.net.Uri;
37import android.os.Bundle;
38import android.os.Handler;
39import android.os.Message;
40import android.provider.MediaStore;
41import android.util.Log;
42import android.view.ContextMenu;
43import android.view.Menu;
44import android.view.MenuItem;
45import android.view.SubMenu;
46import android.view.View;
47import android.view.ViewGroup;
48import android.view.Window;
49import android.view.ContextMenu.ContextMenuInfo;
50import android.widget.CursorAdapter;
51import android.widget.CursorTreeAdapter;
52import android.widget.ExpandableListAdapter;
53import android.widget.ExpandableListView;
54import android.widget.ImageView;
55import android.widget.SectionIndexer;
56import android.widget.SimpleCursorTreeAdapter;
57import android.widget.TextView;
58import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
59
60import java.text.Collator;
61
62
63public class ArtistAlbumBrowserActivity extends ExpandableListActivity
64        implements View.OnCreateContextMenuListener, MusicUtils.Defs
65{
66    private String mCurrentArtistId;
67    private String mCurrentArtistName;
68    private String mCurrentAlbumId;
69    private String mCurrentAlbumName;
70    private String mCurrentArtistNameForAlbum;
71    boolean mIsUnknownArtist;
72    boolean mIsUnknownAlbum;
73    private ArtistAlbumListAdapter mAdapter;
74    private boolean mAdapterSent;
75    private final static int SEARCH = CHILD_MENU_BASE;
76
77    public ArtistAlbumBrowserActivity()
78    {
79    }
80
81    /** Called when the activity is first created. */
82    @Override
83    public void onCreate(Bundle icicle) {
84        super.onCreate(icicle);
85        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
86        setVolumeControlStream(AudioManager.STREAM_MUSIC);
87        if (icicle != null) {
88            mCurrentAlbumId = icicle.getString("selectedalbum");
89            mCurrentAlbumName = icicle.getString("selectedalbumname");
90            mCurrentArtistId = icicle.getString("selectedartist");
91            mCurrentArtistName = icicle.getString("selectedartistname");
92        }
93        MusicUtils.bindToService(this);
94
95        IntentFilter f = new IntentFilter();
96        f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
97        f.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
98        f.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
99        f.addDataScheme("file");
100        registerReceiver(mScanListener, f);
101
102        setContentView(R.layout.media_picker_activity_expanding);
103        ExpandableListView lv = getExpandableListView();
104        lv.setFastScrollEnabled(true);
105        lv.setOnCreateContextMenuListener(this);
106        lv.setTextFilterEnabled(true);
107
108        mAdapter = (ArtistAlbumListAdapter) getLastNonConfigurationInstance();
109        if (mAdapter == null) {
110            //Log.i("@@@", "starting query");
111            mAdapter = new ArtistAlbumListAdapter(
112                    getApplication(),
113                    this,
114                    null, // cursor
115                    R.layout.track_list_item_group,
116                    new String[] {},
117                    new int[] {},
118                    R.layout.track_list_item_child,
119                    new String[] {},
120                    new int[] {});
121            setListAdapter(mAdapter);
122            setTitle(R.string.working_artists);
123            getArtistCursor(mAdapter.getQueryHandler(), null);
124        } else {
125            mAdapter.setActivity(this);
126            setListAdapter(mAdapter);
127            mArtistCursor = mAdapter.getCursor();
128            if (mArtistCursor != null) {
129                init(mArtistCursor);
130            } else {
131                getArtistCursor(mAdapter.getQueryHandler(), null);
132            }
133        }
134    }
135
136    @Override
137    public Object onRetainNonConfigurationInstance() {
138        mAdapterSent = true;
139        return mAdapter;
140    }
141
142    @Override
143    public void onSaveInstanceState(Bundle outcicle) {
144        // need to store the selected item so we don't lose it in case
145        // of an orientation switch. Otherwise we could lose it while
146        // in the middle of specifying a playlist to add the item to.
147        outcicle.putString("selectedalbum", mCurrentAlbumId);
148        outcicle.putString("selectedalbumname", mCurrentAlbumName);
149        outcicle.putString("selectedartist", mCurrentArtistId);
150        outcicle.putString("selectedartistname", mCurrentArtistName);
151        super.onSaveInstanceState(outcicle);
152    }
153
154    @Override
155    public void onDestroy() {
156        MusicUtils.unbindFromService(this);
157        if (!mAdapterSent) {
158            Cursor c = mAdapter.getCursor();
159            if (c != null) {
160                c.close();
161            }
162        }
163        // Because we pass the adapter to the next activity, we need to make
164        // sure it doesn't keep a reference to this activity. We can do this
165        // by clearing its DatasetObservers, which setListAdapter(null) does.
166        setListAdapter(null);
167        mAdapter = null;
168        unregisterReceiver(mScanListener);
169        setListAdapter(null);
170        super.onDestroy();
171    }
172
173    @Override
174    public void onResume() {
175        super.onResume();
176        IntentFilter f = new IntentFilter();
177        f.addAction(MediaPlaybackService.META_CHANGED);
178        f.addAction(MediaPlaybackService.QUEUE_CHANGED);
179        registerReceiver(mTrackListListener, f);
180        mTrackListListener.onReceive(null, null);
181
182        MusicUtils.setSpinnerState(this);
183    }
184
185    private BroadcastReceiver mTrackListListener = new BroadcastReceiver() {
186        @Override
187        public void onReceive(Context context, Intent intent) {
188            getExpandableListView().invalidateViews();
189        }
190    };
191    private BroadcastReceiver mScanListener = new BroadcastReceiver() {
192        @Override
193        public void onReceive(Context context, Intent intent) {
194            MusicUtils.setSpinnerState(ArtistAlbumBrowserActivity.this);
195            mReScanHandler.sendEmptyMessage(0);
196            if (intent.getAction().equals(Intent.ACTION_MEDIA_UNMOUNTED)) {
197                MusicUtils.clearAlbumArtCache();
198            }
199        }
200    };
201
202    private Handler mReScanHandler = new Handler() {
203        @Override
204        public void handleMessage(Message msg) {
205            getArtistCursor(mAdapter.getQueryHandler(), null);
206        }
207    };
208
209    @Override
210    public void onPause() {
211        unregisterReceiver(mTrackListListener);
212        mReScanHandler.removeCallbacksAndMessages(null);
213        super.onPause();
214    }
215
216    public void init(Cursor c) {
217
218        mAdapter.changeCursor(c); // also sets mArtistCursor
219
220        if (mArtistCursor == null) {
221            MusicUtils.displayDatabaseError(this);
222            closeContextMenu();
223            mReScanHandler.sendEmptyMessageDelayed(0, 1000);
224            return;
225        }
226
227        MusicUtils.hideDatabaseError(this);
228        setTitle();
229    }
230
231    private void setTitle() {
232        setTitle(R.string.artists_title);
233    }
234
235    @Override
236    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
237
238        mCurrentAlbumId = Long.valueOf(id).toString();
239
240        Intent intent = new Intent(Intent.ACTION_PICK);
241        intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
242        intent.putExtra("album", mCurrentAlbumId);
243        Cursor c = (Cursor) getExpandableListAdapter().getChild(groupPosition, childPosition);
244        String album = c.getString(c.getColumnIndex(MediaStore.Audio.Albums.ALBUM));
245        if (album == null || album.equals(MediaFile.UNKNOWN_STRING)) {
246            // unknown album, so we should include the artist ID to limit the songs to songs only by that artist
247            mArtistCursor.moveToPosition(groupPosition);
248            mCurrentArtistId = mArtistCursor.getString(mArtistCursor.getColumnIndex(MediaStore.Audio.Artists._ID));
249            intent.putExtra("artist", mCurrentArtistId);
250        }
251        startActivity(intent);
252        return true;
253    }
254
255    @Override
256    public boolean onCreateOptionsMenu(Menu menu) {
257        super.onCreateOptionsMenu(menu);
258        menu.add(0, GOTO_START, 0, R.string.goto_start).setIcon(R.drawable.ic_menu_music_library);
259        menu.add(0, GOTO_PLAYBACK, 0, R.string.goto_playback).setIcon(R.drawable.ic_menu_playback);
260        menu.add(0, SHUFFLE_ALL, 0, R.string.shuffle_all).setIcon(R.drawable.ic_menu_shuffle);
261        return true;
262    }
263
264    @Override
265    public boolean onPrepareOptionsMenu(Menu menu) {
266        menu.findItem(GOTO_PLAYBACK).setVisible(MusicUtils.isMusicLoaded());
267        return super.onPrepareOptionsMenu(menu);
268    }
269
270    @Override
271    public boolean onOptionsItemSelected(MenuItem item) {
272        Intent intent;
273        Cursor cursor;
274        switch (item.getItemId()) {
275            case GOTO_START:
276                intent = new Intent();
277                intent.setClass(this, MusicBrowserActivity.class);
278                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
279                startActivity(intent);
280                return true;
281
282            case GOTO_PLAYBACK:
283                intent = new Intent("com.android.music.PLAYBACK_VIEWER");
284                startActivity(intent);
285                return true;
286
287            case SHUFFLE_ALL:
288                cursor = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
289                        new String [] { MediaStore.Audio.Media._ID},
290                        MediaStore.Audio.Media.IS_MUSIC + "=1", null,
291                        MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
292                if (cursor != null) {
293                    MusicUtils.shuffleAll(this, cursor);
294                    cursor.close();
295                }
296                return true;
297        }
298        return super.onOptionsItemSelected(item);
299    }
300
301    @Override
302    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) {
303        menu.add(0, PLAY_SELECTION, 0, R.string.play_selection);
304        SubMenu sub = menu.addSubMenu(0, ADD_TO_PLAYLIST, 0, R.string.add_to_playlist);
305        MusicUtils.makePlaylistMenu(this, sub);
306        menu.add(0, DELETE_ITEM, 0, R.string.delete_item);
307
308        ExpandableListContextMenuInfo mi = (ExpandableListContextMenuInfo) menuInfoIn;
309
310        int itemtype = ExpandableListView.getPackedPositionType(mi.packedPosition);
311        int gpos = ExpandableListView.getPackedPositionGroup(mi.packedPosition);
312        int cpos = ExpandableListView.getPackedPositionChild(mi.packedPosition);
313        if (itemtype == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
314            if (gpos == -1) {
315                // this shouldn't happen
316                Log.d("Artist/Album", "no group");
317                return;
318            }
319            gpos = gpos - getExpandableListView().getHeaderViewsCount();
320            mArtistCursor.moveToPosition(gpos);
321            mCurrentArtistId = mArtistCursor.getString(mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID));
322            mCurrentArtistName = mArtistCursor.getString(mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
323            mCurrentAlbumId = null;
324            mIsUnknownArtist = mCurrentArtistName == null ||
325                    mCurrentArtistName.equals(MediaFile.UNKNOWN_STRING);
326            mIsUnknownAlbum = true;
327            if (mIsUnknownArtist) {
328                menu.setHeaderTitle(getString(R.string.unknown_artist_name));
329            } else {
330                menu.setHeaderTitle(mCurrentArtistName);
331                menu.add(0, SEARCH, 0, R.string.search_title);
332            }
333            return;
334        } else if (itemtype == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
335            if (cpos == -1) {
336                // this shouldn't happen
337                Log.d("Artist/Album", "no child");
338                return;
339            }
340            Cursor c = (Cursor) getExpandableListAdapter().getChild(gpos, cpos);
341            c.moveToPosition(cpos);
342            mCurrentArtistId = null;
343            mCurrentAlbumId = Long.valueOf(mi.id).toString();
344            mCurrentAlbumName = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM));
345            gpos = gpos - getExpandableListView().getHeaderViewsCount();
346            mArtistCursor.moveToPosition(gpos);
347            mCurrentArtistNameForAlbum = mArtistCursor.getString(
348                    mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
349            mIsUnknownArtist = mCurrentArtistNameForAlbum == null ||
350                    mCurrentArtistNameForAlbum.equals(MediaFile.UNKNOWN_STRING);
351            mIsUnknownAlbum = mCurrentAlbumName == null ||
352                    mCurrentAlbumName.equals(MediaFile.UNKNOWN_STRING);
353            if (mIsUnknownAlbum) {
354                menu.setHeaderTitle(getString(R.string.unknown_album_name));
355            } else {
356                menu.setHeaderTitle(mCurrentAlbumName);
357            }
358            if (!mIsUnknownAlbum || !mIsUnknownArtist) {
359                menu.add(0, SEARCH, 0, R.string.search_title);
360            }
361        }
362    }
363
364    @Override
365    public boolean onContextItemSelected(MenuItem item) {
366        switch (item.getItemId()) {
367            case PLAY_SELECTION: {
368                // play everything by the selected artist
369                long [] list =
370                    mCurrentArtistId != null ?
371                    MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId))
372                    : MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
373
374                MusicUtils.playAll(this, list, 0);
375                return true;
376            }
377
378            case QUEUE: {
379                long [] list =
380                    mCurrentArtistId != null ?
381                    MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId))
382                    : MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
383                MusicUtils.addToCurrentPlaylist(this, list);
384                return true;
385            }
386
387            case NEW_PLAYLIST: {
388                Intent intent = new Intent();
389                intent.setClass(this, CreatePlaylist.class);
390                startActivityForResult(intent, NEW_PLAYLIST);
391                return true;
392            }
393
394            case PLAYLIST_SELECTED: {
395                long [] list =
396                    mCurrentArtistId != null ?
397                    MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId))
398                    : MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
399                long playlist = item.getIntent().getLongExtra("playlist", 0);
400                MusicUtils.addToPlaylist(this, list, playlist);
401                return true;
402            }
403
404            case DELETE_ITEM: {
405                long [] list;
406                String desc;
407                if (mCurrentArtistId != null) {
408                    list = MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId));
409                    String f = getString(R.string.delete_artist_desc);
410                    desc = String.format(f, mCurrentArtistName);
411                } else {
412                    list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
413                    String f = getString(R.string.delete_album_desc);
414                    desc = String.format(f, mCurrentAlbumName);
415                }
416                Bundle b = new Bundle();
417                b.putString("description", desc);
418                b.putLongArray("items", list);
419                Intent intent = new Intent();
420                intent.setClass(this, DeleteItems.class);
421                intent.putExtras(b);
422                startActivityForResult(intent, -1);
423                return true;
424            }
425
426            case SEARCH:
427                doSearch();
428                return true;
429        }
430        return super.onContextItemSelected(item);
431    }
432
433    void doSearch() {
434        CharSequence title = null;
435        String query = null;
436
437        Intent i = new Intent();
438        i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH);
439        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
440
441        if (mCurrentArtistId != null) {
442            title = mCurrentArtistName;
443            query = mCurrentArtistName;
444            i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, mCurrentArtistName);
445            i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE);
446        } else {
447            if (mIsUnknownAlbum) {
448                title = query = mCurrentArtistNameForAlbum;
449            } else {
450                title = query = mCurrentAlbumName;
451                if (!mIsUnknownArtist) {
452                    query = query + " " + mCurrentArtistNameForAlbum;
453                }
454            }
455            i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, mCurrentArtistNameForAlbum);
456            i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, mCurrentAlbumName);
457            i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE);
458        }
459        title = getString(R.string.mediasearch, title);
460        i.putExtra(SearchManager.QUERY, query);
461
462        startActivity(Intent.createChooser(i, title));
463    }
464
465    @Override
466    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
467        switch (requestCode) {
468            case SCAN_DONE:
469                if (resultCode == RESULT_CANCELED) {
470                    finish();
471                } else {
472                    getArtistCursor(mAdapter.getQueryHandler(), null);
473                }
474                break;
475
476            case NEW_PLAYLIST:
477                if (resultCode == RESULT_OK) {
478                    Uri uri = intent.getData();
479                    if (uri != null) {
480                        long [] list = null;
481                        if (mCurrentArtistId != null) {
482                            list = MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId));
483                        } else if (mCurrentAlbumId != null) {
484                            list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
485                        }
486                        MusicUtils.addToPlaylist(this, list, Long.parseLong(uri.getLastPathSegment()));
487                    }
488                }
489                break;
490        }
491    }
492
493    private Cursor getArtistCursor(AsyncQueryHandler async, String filter) {
494
495        StringBuilder where = new StringBuilder();
496        where.append(MediaStore.Audio.Artists.ARTIST + " != ''");
497
498        // Add in the filtering constraints
499        String [] keywords = null;
500        if (filter != null) {
501            String [] searchWords = filter.split(" ");
502            keywords = new String[searchWords.length];
503            Collator col = Collator.getInstance();
504            col.setStrength(Collator.PRIMARY);
505            for (int i = 0; i < searchWords.length; i++) {
506                keywords[i] = '%' + MediaStore.Audio.keyFor(searchWords[i]) + '%';
507            }
508            for (int i = 0; i < searchWords.length; i++) {
509                where.append(" AND ");
510                where.append(MediaStore.Audio.Media.ARTIST_KEY + " LIKE ?");
511            }
512        }
513
514        String whereclause = where.toString();
515        String[] cols = new String[] {
516                MediaStore.Audio.Artists._ID,
517                MediaStore.Audio.Artists.ARTIST,
518                MediaStore.Audio.Artists.NUMBER_OF_ALBUMS,
519                MediaStore.Audio.Artists.NUMBER_OF_TRACKS
520        };
521        Cursor ret = null;
522        if (async != null) {
523            async.startQuery(0, null, MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
524                    cols, whereclause , keywords, MediaStore.Audio.Artists.ARTIST_KEY);
525        } else {
526            ret = MusicUtils.query(this, MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
527                    cols, whereclause , keywords, MediaStore.Audio.Artists.ARTIST_KEY);
528        }
529        return ret;
530    }
531
532    static class ArtistAlbumListAdapter extends SimpleCursorTreeAdapter implements SectionIndexer {
533
534        private final Drawable mNowPlayingOverlay;
535        private final BitmapDrawable mDefaultAlbumIcon;
536        private int mGroupArtistIdIdx;
537        private int mGroupArtistIdx;
538        private int mGroupAlbumIdx;
539        private int mGroupSongIdx;
540        private final Context mContext;
541        private final Resources mResources;
542        private final String mAlbumSongSeparator;
543        private final String mUnknownAlbum;
544        private final String mUnknownArtist;
545        private final StringBuilder mBuffer = new StringBuilder();
546        private final Object[] mFormatArgs = new Object[1];
547        private final Object[] mFormatArgs3 = new Object[3];
548        private MusicAlphabetIndexer mIndexer;
549        private ArtistAlbumBrowserActivity mActivity;
550        private AsyncQueryHandler mQueryHandler;
551        private String mConstraint = null;
552        private boolean mConstraintIsValid = false;
553
554        static class ViewHolder {
555            TextView line1;
556            TextView line2;
557            ImageView play_indicator;
558            ImageView icon;
559        }
560
561        class QueryHandler extends AsyncQueryHandler {
562            QueryHandler(ContentResolver res) {
563                super(res);
564            }
565
566            @Override
567            protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
568                //Log.i("@@@", "query complete");
569                mActivity.init(cursor);
570            }
571        }
572
573        ArtistAlbumListAdapter(Context context, ArtistAlbumBrowserActivity currentactivity,
574                Cursor cursor, int glayout, String[] gfrom, int[] gto,
575                int clayout, String[] cfrom, int[] cto) {
576            super(context, cursor, glayout, gfrom, gto, clayout, cfrom, cto);
577            mActivity = currentactivity;
578            mQueryHandler = new QueryHandler(context.getContentResolver());
579
580            Resources r = context.getResources();
581            mNowPlayingOverlay = r.getDrawable(R.drawable.indicator_ic_mp_playing_list);
582            mDefaultAlbumIcon = (BitmapDrawable) r.getDrawable(R.drawable.albumart_mp_unknown_list);
583            // no filter or dither, it's a lot faster and we can't tell the difference
584            mDefaultAlbumIcon.setFilterBitmap(false);
585            mDefaultAlbumIcon.setDither(false);
586
587            mContext = context;
588            getColumnIndices(cursor);
589            mResources = context.getResources();
590            mAlbumSongSeparator = context.getString(R.string.albumsongseparator);
591            mUnknownAlbum = context.getString(R.string.unknown_album_name);
592            mUnknownArtist = context.getString(R.string.unknown_artist_name);
593        }
594
595        private void getColumnIndices(Cursor cursor) {
596            if (cursor != null) {
597                mGroupArtistIdIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID);
598                mGroupArtistIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST);
599                mGroupAlbumIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_ALBUMS);
600                mGroupSongIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_TRACKS);
601                if (mIndexer != null) {
602                    mIndexer.setCursor(cursor);
603                } else {
604                    mIndexer = new MusicAlphabetIndexer(cursor, mGroupArtistIdx,
605                            mResources.getString(com.android.internal.R.string.fast_scroll_alphabet));
606                }
607            }
608        }
609
610        public void setActivity(ArtistAlbumBrowserActivity newactivity) {
611            mActivity = newactivity;
612        }
613
614        public AsyncQueryHandler getQueryHandler() {
615            return mQueryHandler;
616        }
617
618        @Override
619        public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) {
620            View v = super.newGroupView(context, cursor, isExpanded, parent);
621            ImageView iv = (ImageView) v.findViewById(R.id.icon);
622            ViewGroup.LayoutParams p = iv.getLayoutParams();
623            p.width = ViewGroup.LayoutParams.WRAP_CONTENT;
624            p.height = ViewGroup.LayoutParams.WRAP_CONTENT;
625            ViewHolder vh = new ViewHolder();
626            vh.line1 = (TextView) v.findViewById(R.id.line1);
627            vh.line2 = (TextView) v.findViewById(R.id.line2);
628            vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator);
629            vh.icon = (ImageView) v.findViewById(R.id.icon);
630            vh.icon.setPadding(0, 0, 1, 0);
631            v.setTag(vh);
632            return v;
633        }
634
635        @Override
636        public View newChildView(Context context, Cursor cursor, boolean isLastChild,
637                ViewGroup parent) {
638            View v = super.newChildView(context, cursor, isLastChild, parent);
639            ViewHolder vh = new ViewHolder();
640            vh.line1 = (TextView) v.findViewById(R.id.line1);
641            vh.line2 = (TextView) v.findViewById(R.id.line2);
642            vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator);
643            vh.icon = (ImageView) v.findViewById(R.id.icon);
644            vh.icon.setBackgroundDrawable(mDefaultAlbumIcon);
645            vh.icon.setPadding(0, 0, 1, 0);
646            v.setTag(vh);
647            return v;
648        }
649
650        @Override
651        public void bindGroupView(View view, Context context, Cursor cursor, boolean isexpanded) {
652
653            ViewHolder vh = (ViewHolder) view.getTag();
654
655            String artist = cursor.getString(mGroupArtistIdx);
656            String displayartist = artist;
657            boolean unknown = artist == null || artist.equals(MediaFile.UNKNOWN_STRING);
658            if (unknown) {
659                displayartist = mUnknownArtist;
660            }
661            vh.line1.setText(displayartist);
662
663            int numalbums = cursor.getInt(mGroupAlbumIdx);
664            int numsongs = cursor.getInt(mGroupSongIdx);
665
666            String songs_albums = MusicUtils.makeAlbumsLabel(context,
667                    numalbums, numsongs, unknown);
668
669            vh.line2.setText(songs_albums);
670
671            long currentartistid = MusicUtils.getCurrentArtistId();
672            long artistid = cursor.getLong(mGroupArtistIdIdx);
673            if (currentartistid == artistid && !isexpanded) {
674                vh.play_indicator.setImageDrawable(mNowPlayingOverlay);
675            } else {
676                vh.play_indicator.setImageDrawable(null);
677            }
678        }
679
680        @Override
681        public void bindChildView(View view, Context context, Cursor cursor, boolean islast) {
682
683            ViewHolder vh = (ViewHolder) view.getTag();
684
685            String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM));
686            String displayname = name;
687            boolean unknown = name == null || name.equals(MediaFile.UNKNOWN_STRING);
688            if (unknown) {
689                displayname = mUnknownAlbum;
690            }
691            vh.line1.setText(displayname);
692
693            int numsongs = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS));
694            int numartistsongs = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST));
695
696            final StringBuilder builder = mBuffer;
697            builder.delete(0, builder.length());
698            if (unknown) {
699                numsongs = numartistsongs;
700            }
701
702            if (numsongs == 1) {
703                builder.append(context.getString(R.string.onesong));
704            } else {
705                if (numsongs == numartistsongs) {
706                    final Object[] args = mFormatArgs;
707                    args[0] = numsongs;
708                    builder.append(mResources.getQuantityString(R.plurals.Nsongs, numsongs, args));
709                } else {
710                    final Object[] args = mFormatArgs3;
711                    args[0] = numsongs;
712                    args[1] = numartistsongs;
713                    args[2] = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
714                    builder.append(mResources.getQuantityString(R.plurals.Nsongscomp, numsongs, args));
715                }
716            }
717            vh.line2.setText(builder.toString());
718
719            ImageView iv = vh.icon;
720            // We don't actually need the path to the thumbnail file,
721            // we just use it to see if there is album art or not
722            String art = cursor.getString(cursor.getColumnIndexOrThrow(
723                    MediaStore.Audio.Albums.ALBUM_ART));
724            if (unknown || art == null || art.length() == 0) {
725                iv.setBackgroundDrawable(mDefaultAlbumIcon);
726                iv.setImageDrawable(null);
727            } else {
728                long artIndex = cursor.getLong(0);
729                Drawable d = MusicUtils.getCachedArtwork(context, artIndex, mDefaultAlbumIcon);
730                iv.setImageDrawable(d);
731            }
732
733            long currentalbumid = MusicUtils.getCurrentAlbumId();
734            long aid = cursor.getLong(0);
735            iv = vh.play_indicator;
736            if (currentalbumid == aid) {
737                iv.setImageDrawable(mNowPlayingOverlay);
738            } else {
739                iv.setImageDrawable(null);
740            }
741        }
742
743
744        @Override
745        protected Cursor getChildrenCursor(Cursor groupCursor) {
746
747            long id = groupCursor.getLong(groupCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID));
748
749            String[] cols = new String[] {
750                    MediaStore.Audio.Albums._ID,
751                    MediaStore.Audio.Albums.ALBUM,
752                    MediaStore.Audio.Albums.NUMBER_OF_SONGS,
753                    MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST,
754                    MediaStore.Audio.Albums.ALBUM_ART
755            };
756            Cursor c = MusicUtils.query(mActivity,
757                    MediaStore.Audio.Artists.Albums.getContentUri("external", id),
758                    cols, null, null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER);
759
760            class MyCursorWrapper extends CursorWrapper {
761                String mArtistName;
762                int mMagicColumnIdx;
763                MyCursorWrapper(Cursor c, String artist) {
764                    super(c);
765                    mArtistName = artist;
766                    if (mArtistName == null || mArtistName.equals(MediaFile.UNKNOWN_STRING)) {
767                        mArtistName = mUnknownArtist;
768                    }
769                    mMagicColumnIdx = c.getColumnCount();
770                }
771
772                @Override
773                public String getString(int columnIndex) {
774                    if (columnIndex != mMagicColumnIdx) {
775                        return super.getString(columnIndex);
776                    }
777                    return mArtistName;
778                }
779
780                @Override
781                public int getColumnIndexOrThrow(String name) {
782                    if (MediaStore.Audio.Albums.ARTIST.equals(name)) {
783                        return mMagicColumnIdx;
784                    }
785                    return super.getColumnIndexOrThrow(name);
786                }
787
788                @Override
789                public String getColumnName(int idx) {
790                    if (idx != mMagicColumnIdx) {
791                        return super.getColumnName(idx);
792                    }
793                    return MediaStore.Audio.Albums.ARTIST;
794                }
795
796                @Override
797                public int getColumnCount() {
798                    return super.getColumnCount() + 1;
799                }
800            }
801            return new MyCursorWrapper(c, groupCursor.getString(mGroupArtistIdx));
802        }
803
804        @Override
805        public void changeCursor(Cursor cursor) {
806            if (cursor != mActivity.mArtistCursor) {
807                mActivity.mArtistCursor = cursor;
808                getColumnIndices(cursor);
809                super.changeCursor(cursor);
810            }
811        }
812
813        @Override
814        public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
815            String s = constraint.toString();
816            if (mConstraintIsValid && (
817                    (s == null && mConstraint == null) ||
818                    (s != null && s.equals(mConstraint)))) {
819                return getCursor();
820            }
821            Cursor c = mActivity.getArtistCursor(null, s);
822            mConstraint = s;
823            mConstraintIsValid = true;
824            return c;
825        }
826
827        public Object[] getSections() {
828            return mIndexer.getSections();
829        }
830
831        public int getPositionForSection(int sectionIndex) {
832            return mIndexer.getPositionForSection(sectionIndex);
833        }
834
835        public int getSectionForPosition(int position) {
836            return 0;
837        }
838    }
839
840    private Cursor mArtistCursor;
841}
842
843