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