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