1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.music; 18 19import com.android.music.MusicUtils.ServiceToken; 20 21import android.app.ListActivity; 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.graphics.Bitmap; 34import android.graphics.BitmapFactory; 35import android.graphics.drawable.BitmapDrawable; 36import android.graphics.drawable.Drawable; 37import android.media.AudioManager; 38import android.net.Uri; 39import android.os.Bundle; 40import android.os.Handler; 41import android.os.IBinder; 42import android.os.Message; 43import android.provider.MediaStore; 44import android.text.TextUtils; 45import android.util.Log; 46import android.view.ContextMenu; 47import android.view.Menu; 48import android.view.MenuItem; 49import android.view.SubMenu; 50import android.view.View; 51import android.view.ViewGroup; 52import android.view.Window; 53import android.view.ContextMenu.ContextMenuInfo; 54import android.widget.Adapter; 55import android.widget.AlphabetIndexer; 56import android.widget.CursorAdapter; 57import android.widget.ExpandableListView; 58import android.widget.ImageView; 59import android.widget.ListAdapter; 60import android.widget.ListView; 61import android.widget.SectionIndexer; 62import android.widget.SimpleCursorAdapter; 63import android.widget.TextView; 64import android.widget.AdapterView.AdapterContextMenuInfo; 65 66import java.text.Collator; 67 68public class AlbumBrowserActivity extends ListActivity 69 implements View.OnCreateContextMenuListener, MusicUtils.Defs, ServiceConnection 70{ 71 private String mCurrentAlbumId; 72 private String mCurrentAlbumName; 73 private String mCurrentArtistNameForAlbum; 74 boolean mIsUnknownArtist; 75 boolean mIsUnknownAlbum; 76 private AlbumListAdapter 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 private ServiceToken mToken; 82 83 public AlbumBrowserActivity() 84 { 85 } 86 87 /** Called when the activity is first created. */ 88 @Override 89 public void onCreate(Bundle icicle) 90 { 91 if (icicle != null) { 92 mCurrentAlbumId = icicle.getString("selectedalbum"); 93 mArtistId = icicle.getString("artist"); 94 } else { 95 mArtistId = getIntent().getStringExtra("artist"); 96 } 97 super.onCreate(icicle); 98 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 99 requestWindowFeature(Window.FEATURE_NO_TITLE); 100 setVolumeControlStream(AudioManager.STREAM_MUSIC); 101 mToken = MusicUtils.bindToService(this, this); 102 103 IntentFilter f = new IntentFilter(); 104 f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED); 105 f.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); 106 f.addAction(Intent.ACTION_MEDIA_UNMOUNTED); 107 f.addDataScheme("file"); 108 registerReceiver(mScanListener, f); 109 110 setContentView(R.layout.media_picker_activity); 111 MusicUtils.updateButtonBar(this, R.id.albumtab); 112 ListView lv = getListView(); 113 lv.setOnCreateContextMenuListener(this); 114 lv.setTextFilterEnabled(true); 115 116 mAdapter = (AlbumListAdapter) getLastNonConfigurationInstance(); 117 if (mAdapter == null) { 118 //Log.i("@@@", "starting query"); 119 mAdapter = new AlbumListAdapter( 120 getApplication(), 121 this, 122 R.layout.track_list_item, 123 mAlbumCursor, 124 new String[] {}, 125 new int[] {}); 126 setListAdapter(mAdapter); 127 setTitle(R.string.working_albums); 128 getAlbumCursor(mAdapter.getQueryHandler(), null); 129 } else { 130 mAdapter.setActivity(this); 131 setListAdapter(mAdapter); 132 mAlbumCursor = mAdapter.getCursor(); 133 if (mAlbumCursor != null) { 134 init(mAlbumCursor); 135 } else { 136 getAlbumCursor(mAdapter.getQueryHandler(), null); 137 } 138 } 139 } 140 141 @Override 142 public Object onRetainNonConfigurationInstance() { 143 mAdapterSent = true; 144 return mAdapter; 145 } 146 147 @Override 148 public void onSaveInstanceState(Bundle outcicle) { 149 // need to store the selected item so we don't lose it in case 150 // of an orientation switch. Otherwise we could lose it while 151 // in the middle of specifying a playlist to add the item to. 152 outcicle.putString("selectedalbum", mCurrentAlbumId); 153 outcicle.putString("artist", mArtistId); 154 super.onSaveInstanceState(outcicle); 155 } 156 157 @Override 158 public void onDestroy() { 159 ListView lv = getListView(); 160 if (lv != null) { 161 mLastListPosCourse = lv.getFirstVisiblePosition(); 162 View cv = lv.getChildAt(0); 163 if (cv != null) { 164 mLastListPosFine = cv.getTop(); 165 } 166 } 167 MusicUtils.unbindFromService(mToken); 168 // If we have an adapter and didn't send it off to another activity yet, we should 169 // close its cursor, which we do by assigning a null cursor to it. Doing this 170 // instead of closing the cursor directly keeps the framework from accessing 171 // the closed cursor later. 172 if (!mAdapterSent && mAdapter != null) { 173 mAdapter.changeCursor(null); 174 } 175 // Because we pass the adapter to the next activity, we need to make 176 // sure it doesn't keep a reference to this activity. We can do this 177 // by clearing its DatasetObservers, which setListAdapter(null) does. 178 setListAdapter(null); 179 mAdapter = null; 180 unregisterReceiver(mScanListener); 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 getListView().invalidateViews(); 200 MusicUtils.updateNowPlaying(AlbumBrowserActivity.this); 201 } 202 }; 203 private BroadcastReceiver mScanListener = new BroadcastReceiver() { 204 @Override 205 public void onReceive(Context context, Intent intent) { 206 MusicUtils.setSpinnerState(AlbumBrowserActivity.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 getAlbumCursor(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 mAlbumCursor 236 237 if (mAlbumCursor == 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 getListView().setSelectionFromTop(mLastListPosCourse, mLastListPosFine); 247 mLastListPosCourse = -1; 248 } 249 250 MusicUtils.hideDatabaseError(this); 251 MusicUtils.updateButtonBar(this, R.id.albumtab); 252 setTitle(); 253 } 254 255 private void setTitle() { 256 CharSequence fancyName = ""; 257 if (mAlbumCursor != null && mAlbumCursor.getCount() > 0) { 258 mAlbumCursor.moveToFirst(); 259 fancyName = mAlbumCursor.getString( 260 mAlbumCursor.getColumnIndex(MediaStore.Audio.Albums.ARTIST)); 261 if (fancyName == null || fancyName.equals(MediaStore.UNKNOWN_STRING)) 262 fancyName = getText(R.string.unknown_artist_name); 263 } 264 265 if (mArtistId != null && fancyName != null) 266 setTitle(fancyName); 267 else 268 setTitle(R.string.albums_title); 269 } 270 271 @Override 272 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) { 273 menu.add(0, PLAY_SELECTION, 0, R.string.play_selection); 274 SubMenu sub = menu.addSubMenu(0, ADD_TO_PLAYLIST, 0, R.string.add_to_playlist); 275 MusicUtils.makePlaylistMenu(this, sub); 276 menu.add(0, DELETE_ITEM, 0, R.string.delete_item); 277 278 AdapterContextMenuInfo mi = (AdapterContextMenuInfo) menuInfoIn; 279 mAlbumCursor.moveToPosition(mi.position); 280 mCurrentAlbumId = mAlbumCursor.getString(mAlbumCursor.getColumnIndexOrThrow(MediaStore.Audio.Albums._ID)); 281 mCurrentAlbumName = mAlbumCursor.getString(mAlbumCursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM)); 282 mCurrentArtistNameForAlbum = mAlbumCursor.getString( 283 mAlbumCursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ARTIST)); 284 mIsUnknownArtist = mCurrentArtistNameForAlbum == null || 285 mCurrentArtistNameForAlbum.equals(MediaStore.UNKNOWN_STRING); 286 mIsUnknownAlbum = mCurrentAlbumName == null || 287 mCurrentAlbumName.equals(MediaStore.UNKNOWN_STRING); 288 if (mIsUnknownAlbum) { 289 menu.setHeaderTitle(getString(R.string.unknown_album_name)); 290 } else { 291 menu.setHeaderTitle(mCurrentAlbumName); 292 } 293 if (!mIsUnknownAlbum || !mIsUnknownArtist) { 294 menu.add(0, SEARCH, 0, R.string.search_title); 295 } 296 } 297 298 @Override 299 public boolean onContextItemSelected(MenuItem item) { 300 switch (item.getItemId()) { 301 case PLAY_SELECTION: { 302 // play the selected album 303 long [] list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId)); 304 MusicUtils.playAll(this, list, 0); 305 return true; 306 } 307 308 case QUEUE: { 309 long [] list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId)); 310 MusicUtils.addToCurrentPlaylist(this, list); 311 return true; 312 } 313 314 case NEW_PLAYLIST: { 315 Intent intent = new Intent(); 316 intent.setClass(this, CreatePlaylist.class); 317 startActivityForResult(intent, NEW_PLAYLIST); 318 return true; 319 } 320 321 case PLAYLIST_SELECTED: { 322 long [] list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId)); 323 long playlist = item.getIntent().getLongExtra("playlist", 0); 324 MusicUtils.addToPlaylist(this, list, playlist); 325 return true; 326 } 327 case DELETE_ITEM: { 328 long [] list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId)); 329 String f; 330 if (android.os.Environment.isExternalStorageRemovable()) { 331 f = getString(R.string.delete_album_desc); 332 } else { 333 f = getString(R.string.delete_album_desc_nosdcard); 334 } 335 String desc = String.format(f, mCurrentAlbumName); 336 Bundle b = new Bundle(); 337 b.putString("description", desc); 338 b.putLongArray("items", list); 339 Intent intent = new Intent(); 340 intent.setClass(this, DeleteItems.class); 341 intent.putExtras(b); 342 startActivityForResult(intent, -1); 343 return true; 344 } 345 case SEARCH: 346 doSearch(); 347 return true; 348 349 } 350 return super.onContextItemSelected(item); 351 } 352 353 void doSearch() { 354 CharSequence title = null; 355 String query = ""; 356 357 Intent i = new Intent(); 358 i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH); 359 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 360 361 title = ""; 362 if (!mIsUnknownAlbum) { 363 query = mCurrentAlbumName; 364 i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, mCurrentAlbumName); 365 title = mCurrentAlbumName; 366 } 367 if(!mIsUnknownArtist) { 368 query = query + " " + mCurrentArtistNameForAlbum; 369 i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, mCurrentArtistNameForAlbum); 370 title = title + " " + mCurrentArtistNameForAlbum; 371 } 372 // Since we hide the 'search' menu item when both album and artist are 373 // unknown, the query and title strings will have at least one of those. 374 i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE); 375 title = getString(R.string.mediasearch, title); 376 i.putExtra(SearchManager.QUERY, query); 377 378 startActivity(Intent.createChooser(i, title)); 379 } 380 381 @Override 382 protected void onActivityResult(int requestCode, int resultCode, Intent intent) { 383 switch (requestCode) { 384 case SCAN_DONE: 385 if (resultCode == RESULT_CANCELED) { 386 finish(); 387 } else { 388 getAlbumCursor(mAdapter.getQueryHandler(), null); 389 } 390 break; 391 392 case NEW_PLAYLIST: 393 if (resultCode == RESULT_OK) { 394 Uri uri = intent.getData(); 395 if (uri != null) { 396 long [] list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId)); 397 MusicUtils.addToPlaylist(this, list, Long.parseLong(uri.getLastPathSegment())); 398 } 399 } 400 break; 401 } 402 } 403 404 @Override 405 protected void onListItemClick(ListView l, View v, int position, long id) 406 { 407 Intent intent = new Intent(Intent.ACTION_PICK); 408 intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track"); 409 intent.putExtra("album", Long.valueOf(id).toString()); 410 intent.putExtra("artist", mArtistId); 411 startActivity(intent); 412 } 413 414 @Override 415 public boolean onCreateOptionsMenu(Menu menu) { 416 super.onCreateOptionsMenu(menu); 417 menu.add(0, PARTY_SHUFFLE, 0, R.string.party_shuffle); // icon will be set in onPrepareOptionsMenu() 418 menu.add(0, SHUFFLE_ALL, 0, R.string.shuffle_all).setIcon(R.drawable.ic_menu_shuffle); 419 return true; 420 } 421 422 @Override 423 public boolean onPrepareOptionsMenu(Menu menu) { 424 MusicUtils.setPartyShuffleMenuIcon(menu); 425 return super.onPrepareOptionsMenu(menu); 426 } 427 428 @Override 429 public boolean onOptionsItemSelected(MenuItem item) { 430 Intent intent; 431 Cursor cursor; 432 switch (item.getItemId()) { 433 case PARTY_SHUFFLE: 434 MusicUtils.togglePartyShuffle(); 435 break; 436 437 case SHUFFLE_ALL: 438 cursor = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, 439 new String [] { MediaStore.Audio.Media._ID}, 440 MediaStore.Audio.Media.IS_MUSIC + "=1", null, 441 MediaStore.Audio.Media.DEFAULT_SORT_ORDER); 442 if (cursor != null) { 443 MusicUtils.shuffleAll(this, cursor); 444 cursor.close(); 445 } 446 return true; 447 } 448 return super.onOptionsItemSelected(item); 449 } 450 451 private Cursor getAlbumCursor(AsyncQueryHandler async, String filter) { 452 String[] cols = new String[] { 453 MediaStore.Audio.Albums._ID, 454 MediaStore.Audio.Albums.ARTIST, 455 MediaStore.Audio.Albums.ALBUM, 456 MediaStore.Audio.Albums.ALBUM_ART 457 }; 458 459 460 Cursor ret = null; 461 if (mArtistId != null) { 462 Uri uri = MediaStore.Audio.Artists.Albums.getContentUri("external", 463 Long.valueOf(mArtistId)); 464 if (!TextUtils.isEmpty(filter)) { 465 uri = uri.buildUpon().appendQueryParameter("filter", Uri.encode(filter)).build(); 466 } 467 if (async != null) { 468 async.startQuery(0, null, uri, 469 cols, null, null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER); 470 } else { 471 ret = MusicUtils.query(this, uri, 472 cols, null, null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER); 473 } 474 } else { 475 Uri uri = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI; 476 if (!TextUtils.isEmpty(filter)) { 477 uri = uri.buildUpon().appendQueryParameter("filter", Uri.encode(filter)).build(); 478 } 479 if (async != null) { 480 async.startQuery(0, null, 481 uri, 482 cols, null, null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER); 483 } else { 484 ret = MusicUtils.query(this, uri, 485 cols, null, null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER); 486 } 487 } 488 return ret; 489 } 490 491 static class AlbumListAdapter extends SimpleCursorAdapter implements SectionIndexer { 492 493 private final Drawable mNowPlayingOverlay; 494 private final BitmapDrawable mDefaultAlbumIcon; 495 private int mAlbumIdx; 496 private int mArtistIdx; 497 private int mAlbumArtIndex; 498 private final Resources mResources; 499 private final StringBuilder mStringBuilder = new StringBuilder(); 500 private final String mUnknownAlbum; 501 private final String mUnknownArtist; 502 private final String mAlbumSongSeparator; 503 private final Object[] mFormatArgs = new Object[1]; 504 private AlphabetIndexer mIndexer; 505 private AlbumBrowserActivity mActivity; 506 private AsyncQueryHandler mQueryHandler; 507 private String mConstraint = null; 508 private boolean mConstraintIsValid = false; 509 510 static class ViewHolder { 511 TextView line1; 512 TextView line2; 513 ImageView play_indicator; 514 ImageView icon; 515 } 516 517 class QueryHandler extends AsyncQueryHandler { 518 QueryHandler(ContentResolver res) { 519 super(res); 520 } 521 522 @Override 523 protected void onQueryComplete(int token, Object cookie, Cursor cursor) { 524 //Log.i("@@@", "query complete"); 525 mActivity.init(cursor); 526 } 527 } 528 529 AlbumListAdapter(Context context, AlbumBrowserActivity currentactivity, 530 int layout, Cursor cursor, String[] from, int[] to) { 531 super(context, layout, cursor, from, to); 532 533 mActivity = currentactivity; 534 mQueryHandler = new QueryHandler(context.getContentResolver()); 535 536 mUnknownAlbum = context.getString(R.string.unknown_album_name); 537 mUnknownArtist = context.getString(R.string.unknown_artist_name); 538 mAlbumSongSeparator = context.getString(R.string.albumsongseparator); 539 540 Resources r = context.getResources(); 541 mNowPlayingOverlay = r.getDrawable(R.drawable.indicator_ic_mp_playing_list); 542 543 Bitmap b = BitmapFactory.decodeResource(r, R.drawable.albumart_mp_unknown_list); 544 mDefaultAlbumIcon = new BitmapDrawable(context.getResources(), b); 545 // no filter or dither, it's a lot faster and we can't tell the difference 546 mDefaultAlbumIcon.setFilterBitmap(false); 547 mDefaultAlbumIcon.setDither(false); 548 getColumnIndices(cursor); 549 mResources = context.getResources(); 550 } 551 552 private void getColumnIndices(Cursor cursor) { 553 if (cursor != null) { 554 mAlbumIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM); 555 mArtistIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ARTIST); 556 mAlbumArtIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM_ART); 557 558 if (mIndexer != null) { 559 mIndexer.setCursor(cursor); 560 } else { 561 mIndexer = new MusicAlphabetIndexer(cursor, mAlbumIdx, mResources.getString( 562 R.string.fast_scroll_alphabet)); 563 } 564 } 565 } 566 567 public void setActivity(AlbumBrowserActivity newactivity) { 568 mActivity = newactivity; 569 } 570 571 public AsyncQueryHandler getQueryHandler() { 572 return mQueryHandler; 573 } 574 575 @Override 576 public View newView(Context context, Cursor cursor, ViewGroup parent) { 577 View v = super.newView(context, cursor, parent); 578 ViewHolder vh = new ViewHolder(); 579 vh.line1 = (TextView) v.findViewById(R.id.line1); 580 vh.line2 = (TextView) v.findViewById(R.id.line2); 581 vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator); 582 vh.icon = (ImageView) v.findViewById(R.id.icon); 583 vh.icon.setBackgroundDrawable(mDefaultAlbumIcon); 584 vh.icon.setPadding(0, 0, 1, 0); 585 v.setTag(vh); 586 return v; 587 } 588 589 @Override 590 public void bindView(View view, Context context, Cursor cursor) { 591 592 ViewHolder vh = (ViewHolder) view.getTag(); 593 594 String name = cursor.getString(mAlbumIdx); 595 String displayname = name; 596 boolean unknown = name == null || name.equals(MediaStore.UNKNOWN_STRING); 597 if (unknown) { 598 displayname = mUnknownAlbum; 599 } 600 vh.line1.setText(displayname); 601 602 name = cursor.getString(mArtistIdx); 603 displayname = name; 604 if (name == null || name.equals(MediaStore.UNKNOWN_STRING)) { 605 displayname = mUnknownArtist; 606 } 607 vh.line2.setText(displayname); 608 609 ImageView iv = vh.icon; 610 // We don't actually need the path to the thumbnail file, 611 // we just use it to see if there is album art or not 612 String art = cursor.getString(mAlbumArtIndex); 613 long aid = cursor.getLong(0); 614 if (unknown || art == null || art.length() == 0) { 615 iv.setImageDrawable(null); 616 } else { 617 Drawable d = MusicUtils.getCachedArtwork(context, aid, mDefaultAlbumIcon); 618 iv.setImageDrawable(d); 619 } 620 621 long currentalbumid = MusicUtils.getCurrentAlbumId(); 622 iv = vh.play_indicator; 623 if (currentalbumid == aid) { 624 iv.setImageDrawable(mNowPlayingOverlay); 625 } else { 626 iv.setImageDrawable(null); 627 } 628 } 629 630 @Override 631 public void changeCursor(Cursor cursor) { 632 if (mActivity.isFinishing() && cursor != null) { 633 cursor.close(); 634 cursor = null; 635 } 636 if (cursor != mActivity.mAlbumCursor) { 637 mActivity.mAlbumCursor = cursor; 638 getColumnIndices(cursor); 639 super.changeCursor(cursor); 640 } 641 } 642 643 @Override 644 public Cursor runQueryOnBackgroundThread(CharSequence constraint) { 645 String s = constraint.toString(); 646 if (mConstraintIsValid && ( 647 (s == null && mConstraint == null) || 648 (s != null && s.equals(mConstraint)))) { 649 return getCursor(); 650 } 651 Cursor c = mActivity.getAlbumCursor(null, s); 652 mConstraint = s; 653 mConstraintIsValid = true; 654 return c; 655 } 656 657 public Object[] getSections() { 658 return mIndexer.getSections(); 659 } 660 661 public int getPositionForSection(int section) { 662 return mIndexer.getPositionForSection(section); 663 } 664 665 public int getSectionForPosition(int position) { 666 return 0; 667 } 668 } 669 670 private Cursor mAlbumCursor; 671 private String mArtistId; 672 673 public void onServiceConnected(ComponentName name, IBinder service) { 674 MusicUtils.updateNowPlaying(this); 675 } 676 677 public void onServiceDisconnected(ComponentName name) { 678 finish(); 679 } 680} 681 682