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