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