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