| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.music; |
| |
| import com.android.music.MusicUtils.ServiceToken; |
| import com.android.music.QueryBrowserActivity.QueryListAdapter.QueryHandler; |
| |
| import android.app.ExpandableListActivity; |
| import android.app.SearchManager; |
| import android.content.AsyncQueryHandler; |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.ServiceConnection; |
| import android.content.res.Resources; |
| import android.database.Cursor; |
| import android.database.CursorWrapper; |
| import android.graphics.drawable.BitmapDrawable; |
| import android.graphics.drawable.Drawable; |
| import android.media.AudioManager; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Message; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.provider.MediaStore; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.util.SparseArray; |
| import android.view.ContextMenu; |
| import android.view.Menu; |
| import android.view.MenuItem; |
| import android.view.SubMenu; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.Window; |
| import android.view.ContextMenu.ContextMenuInfo; |
| import android.widget.ExpandableListView; |
| import android.widget.ImageView; |
| import android.widget.SectionIndexer; |
| import android.widget.SimpleCursorTreeAdapter; |
| import android.widget.TextView; |
| import android.widget.ExpandableListView.ExpandableListContextMenuInfo; |
| |
| import java.text.Collator; |
| |
| |
| public class ArtistAlbumBrowserActivity extends ExpandableListActivity |
| implements View.OnCreateContextMenuListener, MusicUtils.Defs, ServiceConnection |
| { |
| private String mCurrentArtistId; |
| private String mCurrentArtistName; |
| private String mCurrentAlbumId; |
| private String mCurrentAlbumName; |
| private String mCurrentArtistNameForAlbum; |
| boolean mIsUnknownArtist; |
| boolean mIsUnknownAlbum; |
| private ArtistAlbumListAdapter mAdapter; |
| private boolean mAdapterSent; |
| private final static int SEARCH = CHILD_MENU_BASE; |
| private static int mLastListPosCourse = -1; |
| private static int mLastListPosFine = -1; |
| private ServiceToken mToken; |
| |
| /** Called when the activity is first created. */ |
| @Override |
| public void onCreate(Bundle icicle) { |
| super.onCreate(icicle); |
| requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); |
| requestWindowFeature(Window.FEATURE_NO_TITLE); |
| setVolumeControlStream(AudioManager.STREAM_MUSIC); |
| if (icicle != null) { |
| mCurrentAlbumId = icicle.getString("selectedalbum"); |
| mCurrentAlbumName = icicle.getString("selectedalbumname"); |
| mCurrentArtistId = icicle.getString("selectedartist"); |
| mCurrentArtistName = icicle.getString("selectedartistname"); |
| } |
| mToken = MusicUtils.bindToService(this, this); |
| |
| IntentFilter f = new IntentFilter(); |
| f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED); |
| f.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); |
| f.addAction(Intent.ACTION_MEDIA_UNMOUNTED); |
| f.addDataScheme("file"); |
| registerReceiver(mScanListener, f); |
| |
| setContentView(R.layout.media_picker_activity_expanding); |
| MusicUtils.updateButtonBar(this, R.id.artisttab); |
| ExpandableListView lv = getExpandableListView(); |
| lv.setOnCreateContextMenuListener(this); |
| lv.setTextFilterEnabled(true); |
| |
| mAdapter = (ArtistAlbumListAdapter) getLastNonConfigurationInstance(); |
| if (mAdapter == null) { |
| //Log.i("@@@", "starting query"); |
| mAdapter = new ArtistAlbumListAdapter( |
| getApplication(), |
| this, |
| null, // cursor |
| R.layout.track_list_item_group, |
| new String[] {}, |
| new int[] {}, |
| R.layout.track_list_item_child, |
| new String[] {}, |
| new int[] {}); |
| setListAdapter(mAdapter); |
| setTitle(R.string.working_artists); |
| getArtistCursor(mAdapter.getQueryHandler(), null); |
| } else { |
| mAdapter.setActivity(this); |
| setListAdapter(mAdapter); |
| mArtistCursor = mAdapter.getCursor(); |
| if (mArtistCursor != null) { |
| init(mArtistCursor); |
| } else { |
| getArtistCursor(mAdapter.getQueryHandler(), null); |
| } |
| } |
| } |
| |
| @Override |
| public Object onRetainNonConfigurationInstance() { |
| mAdapterSent = true; |
| return mAdapter; |
| } |
| |
| @Override |
| public void onSaveInstanceState(Bundle outcicle) { |
| // need to store the selected item so we don't lose it in case |
| // of an orientation switch. Otherwise we could lose it while |
| // in the middle of specifying a playlist to add the item to. |
| outcicle.putString("selectedalbum", mCurrentAlbumId); |
| outcicle.putString("selectedalbumname", mCurrentAlbumName); |
| outcicle.putString("selectedartist", mCurrentArtistId); |
| outcicle.putString("selectedartistname", mCurrentArtistName); |
| super.onSaveInstanceState(outcicle); |
| } |
| |
| @Override |
| public void onDestroy() { |
| ExpandableListView lv = getExpandableListView(); |
| if (lv != null) { |
| mLastListPosCourse = lv.getFirstVisiblePosition(); |
| View cv = lv.getChildAt(0); |
| if (cv != null) { |
| mLastListPosFine = cv.getTop(); |
| } |
| } |
| |
| MusicUtils.unbindFromService(mToken); |
| // If we have an adapter and didn't send it off to another activity yet, we should |
| // close its cursor, which we do by assigning a null cursor to it. Doing this |
| // instead of closing the cursor directly keeps the framework from accessing |
| // the closed cursor later. |
| if (!mAdapterSent && mAdapter != null) { |
| mAdapter.changeCursor(null); |
| } |
| // Because we pass the adapter to the next activity, we need to make |
| // sure it doesn't keep a reference to this activity. We can do this |
| // by clearing its DatasetObservers, which setListAdapter(null) does. |
| setListAdapter(null); |
| mAdapter = null; |
| unregisterReceiver(mScanListener); |
| setListAdapter(null); |
| super.onDestroy(); |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| IntentFilter f = new IntentFilter(); |
| f.addAction(MediaPlaybackService.META_CHANGED); |
| f.addAction(MediaPlaybackService.QUEUE_CHANGED); |
| registerReceiver(mTrackListListener, f); |
| mTrackListListener.onReceive(null, null); |
| |
| MusicUtils.setSpinnerState(this); |
| } |
| |
| private BroadcastReceiver mTrackListListener = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| getExpandableListView().invalidateViews(); |
| MusicUtils.updateNowPlaying(ArtistAlbumBrowserActivity.this); |
| } |
| }; |
| private BroadcastReceiver mScanListener = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| MusicUtils.setSpinnerState(ArtistAlbumBrowserActivity.this); |
| mReScanHandler.sendEmptyMessage(0); |
| if (intent.getAction().equals(Intent.ACTION_MEDIA_UNMOUNTED)) { |
| MusicUtils.clearAlbumArtCache(); |
| } |
| } |
| }; |
| |
| private Handler mReScanHandler = new Handler() { |
| @Override |
| public void handleMessage(Message msg) { |
| if (mAdapter != null) { |
| getArtistCursor(mAdapter.getQueryHandler(), null); |
| } |
| } |
| }; |
| |
| @Override |
| public void onPause() { |
| unregisterReceiver(mTrackListListener); |
| mReScanHandler.removeCallbacksAndMessages(null); |
| super.onPause(); |
| } |
| |
| public void init(Cursor c) { |
| |
| if (mAdapter == null) { |
| return; |
| } |
| mAdapter.changeCursor(c); // also sets mArtistCursor |
| |
| if (mArtistCursor == null) { |
| MusicUtils.displayDatabaseError(this); |
| closeContextMenu(); |
| mReScanHandler.sendEmptyMessageDelayed(0, 1000); |
| return; |
| } |
| |
| // restore previous position |
| if (mLastListPosCourse >= 0) { |
| ExpandableListView elv = getExpandableListView(); |
| elv.setSelectionFromTop(mLastListPosCourse, mLastListPosFine); |
| mLastListPosCourse = -1; |
| } |
| |
| MusicUtils.hideDatabaseError(this); |
| MusicUtils.updateButtonBar(this, R.id.artisttab); |
| setTitle(); |
| } |
| |
| private void setTitle() { |
| setTitle(R.string.artists_title); |
| } |
| |
| @Override |
| public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { |
| |
| mCurrentAlbumId = Long.valueOf(id).toString(); |
| |
| Intent intent = new Intent(Intent.ACTION_PICK); |
| intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track"); |
| intent.putExtra("album", mCurrentAlbumId); |
| Cursor c = (Cursor) getExpandableListAdapter().getChild(groupPosition, childPosition); |
| String album = c.getString(c.getColumnIndex(MediaStore.Audio.Albums.ALBUM)); |
| if (album == null || album.equals(MediaStore.UNKNOWN_STRING)) { |
| // unknown album, so we should include the artist ID to limit the songs to songs only by that artist |
| mArtistCursor.moveToPosition(groupPosition); |
| mCurrentArtistId = mArtistCursor.getString(mArtistCursor.getColumnIndex(MediaStore.Audio.Artists._ID)); |
| intent.putExtra("artist", mCurrentArtistId); |
| } |
| startActivity(intent); |
| return true; |
| } |
| |
| @Override |
| public boolean onCreateOptionsMenu(Menu menu) { |
| super.onCreateOptionsMenu(menu); |
| menu.add(0, PARTY_SHUFFLE, 0, R.string.party_shuffle); // icon will be set in onPrepareOptionsMenu() |
| menu.add(0, SHUFFLE_ALL, 0, R.string.shuffle_all).setIcon(R.drawable.ic_menu_shuffle); |
| return true; |
| } |
| |
| @Override |
| public boolean onPrepareOptionsMenu(Menu menu) { |
| MusicUtils.setPartyShuffleMenuIcon(menu); |
| return super.onPrepareOptionsMenu(menu); |
| } |
| |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| Intent intent; |
| Cursor cursor; |
| switch (item.getItemId()) { |
| case PARTY_SHUFFLE: |
| MusicUtils.togglePartyShuffle(); |
| break; |
| |
| case SHUFFLE_ALL: |
| cursor = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, |
| new String [] { MediaStore.Audio.Media._ID}, |
| MediaStore.Audio.Media.IS_MUSIC + "=1", null, |
| MediaStore.Audio.Media.DEFAULT_SORT_ORDER); |
| if (cursor != null) { |
| MusicUtils.shuffleAll(this, cursor); |
| cursor.close(); |
| } |
| return true; |
| } |
| return super.onOptionsItemSelected(item); |
| } |
| |
| @Override |
| public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) { |
| menu.add(0, PLAY_SELECTION, 0, R.string.play_selection); |
| SubMenu sub = menu.addSubMenu(0, ADD_TO_PLAYLIST, 0, R.string.add_to_playlist); |
| MusicUtils.makePlaylistMenu(this, sub); |
| menu.add(0, DELETE_ITEM, 0, R.string.delete_item); |
| |
| ExpandableListContextMenuInfo mi = (ExpandableListContextMenuInfo) menuInfoIn; |
| |
| int itemtype = ExpandableListView.getPackedPositionType(mi.packedPosition); |
| int gpos = ExpandableListView.getPackedPositionGroup(mi.packedPosition); |
| int cpos = ExpandableListView.getPackedPositionChild(mi.packedPosition); |
| if (itemtype == ExpandableListView.PACKED_POSITION_TYPE_GROUP) { |
| if (gpos == -1) { |
| // this shouldn't happen |
| Log.d("Artist/Album", "no group"); |
| return; |
| } |
| gpos = gpos - getExpandableListView().getHeaderViewsCount(); |
| mArtistCursor.moveToPosition(gpos); |
| mCurrentArtistId = mArtistCursor.getString(mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID)); |
| mCurrentArtistName = mArtistCursor.getString(mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST)); |
| mCurrentAlbumId = null; |
| mIsUnknownArtist = mCurrentArtistName == null || |
| mCurrentArtistName.equals(MediaStore.UNKNOWN_STRING); |
| mIsUnknownAlbum = true; |
| if (mIsUnknownArtist) { |
| menu.setHeaderTitle(getString(R.string.unknown_artist_name)); |
| } else { |
| menu.setHeaderTitle(mCurrentArtistName); |
| menu.add(0, SEARCH, 0, R.string.search_title); |
| } |
| return; |
| } else if (itemtype == ExpandableListView.PACKED_POSITION_TYPE_CHILD) { |
| if (cpos == -1) { |
| // this shouldn't happen |
| Log.d("Artist/Album", "no child"); |
| return; |
| } |
| Cursor c = (Cursor) getExpandableListAdapter().getChild(gpos, cpos); |
| c.moveToPosition(cpos); |
| mCurrentArtistId = null; |
| mCurrentAlbumId = Long.valueOf(mi.id).toString(); |
| mCurrentAlbumName = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM)); |
| gpos = gpos - getExpandableListView().getHeaderViewsCount(); |
| mArtistCursor.moveToPosition(gpos); |
| mCurrentArtistNameForAlbum = mArtistCursor.getString( |
| mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST)); |
| mIsUnknownArtist = mCurrentArtistNameForAlbum == null || |
| mCurrentArtistNameForAlbum.equals(MediaStore.UNKNOWN_STRING); |
| mIsUnknownAlbum = mCurrentAlbumName == null || |
| mCurrentAlbumName.equals(MediaStore.UNKNOWN_STRING); |
| if (mIsUnknownAlbum) { |
| menu.setHeaderTitle(getString(R.string.unknown_album_name)); |
| } else { |
| menu.setHeaderTitle(mCurrentAlbumName); |
| } |
| if (!mIsUnknownAlbum || !mIsUnknownArtist) { |
| menu.add(0, SEARCH, 0, R.string.search_title); |
| } |
| } |
| } |
| |
| @Override |
| public boolean onContextItemSelected(MenuItem item) { |
| switch (item.getItemId()) { |
| case PLAY_SELECTION: { |
| // play everything by the selected artist |
| long [] list = |
| mCurrentArtistId != null ? |
| MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId)) |
| : MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId)); |
| |
| MusicUtils.playAll(this, list, 0); |
| return true; |
| } |
| |
| case QUEUE: { |
| long [] list = |
| mCurrentArtistId != null ? |
| MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId)) |
| : MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId)); |
| MusicUtils.addToCurrentPlaylist(this, list); |
| return true; |
| } |
| |
| case NEW_PLAYLIST: { |
| Intent intent = new Intent(); |
| intent.setClass(this, CreatePlaylist.class); |
| startActivityForResult(intent, NEW_PLAYLIST); |
| return true; |
| } |
| |
| case PLAYLIST_SELECTED: { |
| long [] list = |
| mCurrentArtistId != null ? |
| MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId)) |
| : MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId)); |
| long playlist = item.getIntent().getLongExtra("playlist", 0); |
| MusicUtils.addToPlaylist(this, list, playlist); |
| return true; |
| } |
| |
| case DELETE_ITEM: { |
| long [] list; |
| String desc; |
| if (mCurrentArtistId != null) { |
| list = MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId)); |
| String f; |
| if (android.os.Environment.isExternalStorageRemovable()) { |
| f = getString(R.string.delete_artist_desc); |
| } else { |
| f = getString(R.string.delete_artist_desc_nosdcard); |
| } |
| desc = String.format(f, mCurrentArtistName); |
| } else { |
| list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId)); |
| String f; |
| if (android.os.Environment.isExternalStorageRemovable()) { |
| f = getString(R.string.delete_album_desc); |
| } else { |
| f = getString(R.string.delete_album_desc_nosdcard); |
| } |
| |
| desc = String.format(f, mCurrentAlbumName); |
| } |
| Bundle b = new Bundle(); |
| b.putString("description", desc); |
| b.putLongArray("items", list); |
| Intent intent = new Intent(); |
| intent.setClass(this, DeleteItems.class); |
| intent.putExtras(b); |
| startActivityForResult(intent, -1); |
| return true; |
| } |
| |
| case SEARCH: |
| doSearch(); |
| return true; |
| } |
| return super.onContextItemSelected(item); |
| } |
| |
| void doSearch() { |
| CharSequence title = null; |
| String query = null; |
| |
| Intent i = new Intent(); |
| i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH); |
| i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| |
| if (mCurrentArtistId != null) { |
| title = mCurrentArtistName; |
| query = mCurrentArtistName; |
| i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, mCurrentArtistName); |
| i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE); |
| } else { |
| if (mIsUnknownAlbum) { |
| title = query = mCurrentArtistNameForAlbum; |
| } else { |
| title = query = mCurrentAlbumName; |
| if (!mIsUnknownArtist) { |
| query = query + " " + mCurrentArtistNameForAlbum; |
| } |
| } |
| i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, mCurrentArtistNameForAlbum); |
| i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, mCurrentAlbumName); |
| i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE); |
| } |
| title = getString(R.string.mediasearch, title); |
| i.putExtra(SearchManager.QUERY, query); |
| |
| startActivity(Intent.createChooser(i, title)); |
| } |
| |
| @Override |
| protected void onActivityResult(int requestCode, int resultCode, Intent intent) { |
| switch (requestCode) { |
| case SCAN_DONE: |
| if (resultCode == RESULT_CANCELED) { |
| finish(); |
| } else { |
| getArtistCursor(mAdapter.getQueryHandler(), null); |
| } |
| break; |
| |
| case NEW_PLAYLIST: |
| if (resultCode == RESULT_OK) { |
| Uri uri = intent.getData(); |
| if (uri != null) { |
| long [] list = null; |
| if (mCurrentArtistId != null) { |
| list = MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId)); |
| } else if (mCurrentAlbumId != null) { |
| list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId)); |
| } |
| MusicUtils.addToPlaylist(this, list, Long.parseLong(uri.getLastPathSegment())); |
| } |
| } |
| break; |
| } |
| } |
| |
| private Cursor getArtistCursor(AsyncQueryHandler async, String filter) { |
| |
| String[] cols = new String[] { |
| MediaStore.Audio.Artists._ID, |
| MediaStore.Audio.Artists.ARTIST, |
| MediaStore.Audio.Artists.NUMBER_OF_ALBUMS, |
| MediaStore.Audio.Artists.NUMBER_OF_TRACKS |
| }; |
| |
| Uri uri = MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI; |
| if (!TextUtils.isEmpty(filter)) { |
| uri = uri.buildUpon().appendQueryParameter("filter", Uri.encode(filter)).build(); |
| } |
| |
| Cursor ret = null; |
| if (async != null) { |
| async.startQuery(0, null, uri, |
| cols, null , null, MediaStore.Audio.Artists.ARTIST_KEY); |
| } else { |
| ret = MusicUtils.query(this, uri, |
| cols, null , null, MediaStore.Audio.Artists.ARTIST_KEY); |
| } |
| return ret; |
| } |
| |
| static class ArtistAlbumListAdapter extends SimpleCursorTreeAdapter implements SectionIndexer { |
| |
| private final Drawable mNowPlayingOverlay; |
| private final BitmapDrawable mDefaultAlbumIcon; |
| private int mGroupArtistIdIdx; |
| private int mGroupArtistIdx; |
| private int mGroupAlbumIdx; |
| private int mGroupSongIdx; |
| private final Context mContext; |
| private final Resources mResources; |
| private final String mAlbumSongSeparator; |
| private final String mUnknownAlbum; |
| private final String mUnknownArtist; |
| private final StringBuilder mBuffer = new StringBuilder(); |
| private final Object[] mFormatArgs = new Object[1]; |
| private final Object[] mFormatArgs3 = new Object[3]; |
| private MusicAlphabetIndexer mIndexer; |
| private ArtistAlbumBrowserActivity mActivity; |
| private AsyncQueryHandler mQueryHandler; |
| private String mConstraint = null; |
| private boolean mConstraintIsValid = false; |
| |
| static class ViewHolder { |
| TextView line1; |
| TextView line2; |
| ImageView play_indicator; |
| ImageView icon; |
| } |
| |
| class QueryHandler extends AsyncQueryHandler { |
| QueryHandler(ContentResolver res) { |
| super(res); |
| } |
| |
| @Override |
| protected void onQueryComplete(int token, Object cookie, Cursor cursor) { |
| //Log.i("@@@", "query complete"); |
| mActivity.init(cursor); |
| } |
| } |
| |
| ArtistAlbumListAdapter(Context context, ArtistAlbumBrowserActivity currentactivity, |
| Cursor cursor, int glayout, String[] gfrom, int[] gto, |
| int clayout, String[] cfrom, int[] cto) { |
| super(context, cursor, glayout, gfrom, gto, clayout, cfrom, cto); |
| mActivity = currentactivity; |
| mQueryHandler = new QueryHandler(context.getContentResolver()); |
| |
| Resources r = context.getResources(); |
| mNowPlayingOverlay = r.getDrawable(R.drawable.indicator_ic_mp_playing_list); |
| mDefaultAlbumIcon = (BitmapDrawable) r.getDrawable(R.drawable.albumart_mp_unknown_list); |
| // no filter or dither, it's a lot faster and we can't tell the difference |
| mDefaultAlbumIcon.setFilterBitmap(false); |
| mDefaultAlbumIcon.setDither(false); |
| |
| mContext = context; |
| getColumnIndices(cursor); |
| mResources = context.getResources(); |
| mAlbumSongSeparator = context.getString(R.string.albumsongseparator); |
| mUnknownAlbum = context.getString(R.string.unknown_album_name); |
| mUnknownArtist = context.getString(R.string.unknown_artist_name); |
| } |
| |
| private void getColumnIndices(Cursor cursor) { |
| if (cursor != null) { |
| mGroupArtistIdIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID); |
| mGroupArtistIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST); |
| mGroupAlbumIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_ALBUMS); |
| mGroupSongIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_TRACKS); |
| if (mIndexer != null) { |
| mIndexer.setCursor(cursor); |
| } else { |
| mIndexer = new MusicAlphabetIndexer(cursor, mGroupArtistIdx, |
| mResources.getString(R.string.fast_scroll_alphabet)); |
| } |
| } |
| } |
| |
| public void setActivity(ArtistAlbumBrowserActivity newactivity) { |
| mActivity = newactivity; |
| } |
| |
| public AsyncQueryHandler getQueryHandler() { |
| return mQueryHandler; |
| } |
| |
| @Override |
| public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) { |
| View v = super.newGroupView(context, cursor, isExpanded, parent); |
| ImageView iv = (ImageView) v.findViewById(R.id.icon); |
| ViewGroup.LayoutParams p = iv.getLayoutParams(); |
| p.width = ViewGroup.LayoutParams.WRAP_CONTENT; |
| p.height = ViewGroup.LayoutParams.WRAP_CONTENT; |
| ViewHolder vh = new ViewHolder(); |
| vh.line1 = (TextView) v.findViewById(R.id.line1); |
| vh.line2 = (TextView) v.findViewById(R.id.line2); |
| vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator); |
| vh.icon = (ImageView) v.findViewById(R.id.icon); |
| vh.icon.setPadding(0, 0, 1, 0); |
| v.setTag(vh); |
| return v; |
| } |
| |
| @Override |
| public View newChildView(Context context, Cursor cursor, boolean isLastChild, |
| ViewGroup parent) { |
| View v = super.newChildView(context, cursor, isLastChild, parent); |
| ViewHolder vh = new ViewHolder(); |
| vh.line1 = (TextView) v.findViewById(R.id.line1); |
| vh.line2 = (TextView) v.findViewById(R.id.line2); |
| vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator); |
| vh.icon = (ImageView) v.findViewById(R.id.icon); |
| vh.icon.setBackgroundDrawable(mDefaultAlbumIcon); |
| vh.icon.setPadding(0, 0, 1, 0); |
| v.setTag(vh); |
| return v; |
| } |
| |
| @Override |
| public void bindGroupView(View view, Context context, Cursor cursor, boolean isexpanded) { |
| |
| ViewHolder vh = (ViewHolder) view.getTag(); |
| |
| String artist = cursor.getString(mGroupArtistIdx); |
| String displayartist = artist; |
| boolean unknown = artist == null || artist.equals(MediaStore.UNKNOWN_STRING); |
| if (unknown) { |
| displayartist = mUnknownArtist; |
| } |
| vh.line1.setText(displayartist); |
| |
| int numalbums = cursor.getInt(mGroupAlbumIdx); |
| int numsongs = cursor.getInt(mGroupSongIdx); |
| |
| String songs_albums = MusicUtils.makeAlbumsLabel(context, |
| numalbums, numsongs, unknown); |
| |
| vh.line2.setText(songs_albums); |
| |
| long currentartistid = MusicUtils.getCurrentArtistId(); |
| long artistid = cursor.getLong(mGroupArtistIdIdx); |
| if (currentartistid == artistid && !isexpanded) { |
| vh.play_indicator.setImageDrawable(mNowPlayingOverlay); |
| } else { |
| vh.play_indicator.setImageDrawable(null); |
| } |
| } |
| |
| @Override |
| public void bindChildView(View view, Context context, Cursor cursor, boolean islast) { |
| |
| ViewHolder vh = (ViewHolder) view.getTag(); |
| |
| String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM)); |
| String displayname = name; |
| boolean unknown = name == null || name.equals(MediaStore.UNKNOWN_STRING); |
| if (unknown) { |
| displayname = mUnknownAlbum; |
| } |
| vh.line1.setText(displayname); |
| |
| int numsongs = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS)); |
| int numartistsongs = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST)); |
| |
| final StringBuilder builder = mBuffer; |
| builder.delete(0, builder.length()); |
| if (unknown) { |
| numsongs = numartistsongs; |
| } |
| |
| if (numsongs == 1) { |
| builder.append(context.getString(R.string.onesong)); |
| } else { |
| if (numsongs == numartistsongs) { |
| final Object[] args = mFormatArgs; |
| args[0] = numsongs; |
| builder.append(mResources.getQuantityString(R.plurals.Nsongs, numsongs, args)); |
| } else { |
| final Object[] args = mFormatArgs3; |
| args[0] = numsongs; |
| args[1] = numartistsongs; |
| args[2] = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST)); |
| builder.append(mResources.getQuantityString(R.plurals.Nsongscomp, numsongs, args)); |
| } |
| } |
| vh.line2.setText(builder.toString()); |
| |
| ImageView iv = vh.icon; |
| // We don't actually need the path to the thumbnail file, |
| // we just use it to see if there is album art or not |
| String art = cursor.getString(cursor.getColumnIndexOrThrow( |
| MediaStore.Audio.Albums.ALBUM_ART)); |
| if (unknown || art == null || art.length() == 0) { |
| iv.setBackgroundDrawable(mDefaultAlbumIcon); |
| iv.setImageDrawable(null); |
| } else { |
| long artIndex = cursor.getLong(0); |
| Drawable d = MusicUtils.getCachedArtwork(context, artIndex, mDefaultAlbumIcon); |
| iv.setImageDrawable(d); |
| } |
| |
| long currentalbumid = MusicUtils.getCurrentAlbumId(); |
| long aid = cursor.getLong(0); |
| iv = vh.play_indicator; |
| if (currentalbumid == aid) { |
| iv.setImageDrawable(mNowPlayingOverlay); |
| } else { |
| iv.setImageDrawable(null); |
| } |
| } |
| |
| |
| @Override |
| protected Cursor getChildrenCursor(Cursor groupCursor) { |
| |
| long id = groupCursor.getLong(groupCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID)); |
| |
| String[] cols = new String[] { |
| MediaStore.Audio.Albums._ID, |
| MediaStore.Audio.Albums.ALBUM, |
| MediaStore.Audio.Albums.NUMBER_OF_SONGS, |
| MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST, |
| MediaStore.Audio.Albums.ALBUM_ART |
| }; |
| Cursor c = MusicUtils.query(mActivity, |
| MediaStore.Audio.Artists.Albums.getContentUri("external", id), |
| cols, null, null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER); |
| |
| class MyCursorWrapper extends CursorWrapper { |
| String mArtistName; |
| int mMagicColumnIdx; |
| MyCursorWrapper(Cursor c, String artist) { |
| super(c); |
| mArtistName = artist; |
| if (mArtistName == null || mArtistName.equals(MediaStore.UNKNOWN_STRING)) { |
| mArtistName = mUnknownArtist; |
| } |
| mMagicColumnIdx = c.getColumnCount(); |
| } |
| |
| @Override |
| public String getString(int columnIndex) { |
| if (columnIndex != mMagicColumnIdx) { |
| return super.getString(columnIndex); |
| } |
| return mArtistName; |
| } |
| |
| @Override |
| public int getColumnIndexOrThrow(String name) { |
| if (MediaStore.Audio.Albums.ARTIST.equals(name)) { |
| return mMagicColumnIdx; |
| } |
| return super.getColumnIndexOrThrow(name); |
| } |
| |
| @Override |
| public String getColumnName(int idx) { |
| if (idx != mMagicColumnIdx) { |
| return super.getColumnName(idx); |
| } |
| return MediaStore.Audio.Albums.ARTIST; |
| } |
| |
| @Override |
| public int getColumnCount() { |
| return super.getColumnCount() + 1; |
| } |
| } |
| return new MyCursorWrapper(c, groupCursor.getString(mGroupArtistIdx)); |
| } |
| |
| @Override |
| public void changeCursor(Cursor cursor) { |
| if (mActivity.isFinishing() && cursor != null) { |
| cursor.close(); |
| cursor = null; |
| } |
| if (cursor != mActivity.mArtistCursor) { |
| mActivity.mArtistCursor = cursor; |
| getColumnIndices(cursor); |
| super.changeCursor(cursor); |
| } |
| } |
| |
| @Override |
| public Cursor runQueryOnBackgroundThread(CharSequence constraint) { |
| String s = constraint.toString(); |
| if (mConstraintIsValid && ( |
| (s == null && mConstraint == null) || |
| (s != null && s.equals(mConstraint)))) { |
| return getCursor(); |
| } |
| Cursor c = mActivity.getArtistCursor(null, s); |
| mConstraint = s; |
| mConstraintIsValid = true; |
| return c; |
| } |
| |
| public Object[] getSections() { |
| return mIndexer.getSections(); |
| } |
| |
| public int getPositionForSection(int sectionIndex) { |
| return mIndexer.getPositionForSection(sectionIndex); |
| } |
| |
| public int getSectionForPosition(int position) { |
| return 0; |
| } |
| } |
| |
| private Cursor mArtistCursor; |
| |
| public void onServiceConnected(ComponentName name, IBinder service) { |
| MusicUtils.updateNowPlaying(this); |
| } |
| |
| public void onServiceDisconnected(ComponentName name) { |
| finish(); |
| } |
| } |
| |