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