Support MediaBrowserService in AOSP Music

* Use MediaSession and MediaController to interact between playback
  service and playback applications
* Introduced MusicProvdier to scan music resources on local disk
* Removed unnecessary intents and unused activities
* Disabled playlist add, edit, delete function temporarily

Bug: 34748293
Test: make, playing music, browse through Bluetooth on Carkit
Change-Id: Ic88847aa0b3dd4ef5e13afcb839c78544ac05a2a
diff --git a/src/com/android/music/ArtistAlbumBrowserActivity.java b/src/com/android/music/ArtistAlbumBrowserActivity.java
index 474ff37..ffccd32 100644
--- a/src/com/android/music/ArtistAlbumBrowserActivity.java
+++ b/src/com/android/music/ArtistAlbumBrowserActivity.java
@@ -16,617 +16,271 @@
 
 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.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.browse.MediaBrowser;
+import android.media.session.MediaController;
 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.SimpleExpandableListAdapter;
 import android.widget.TextView;
-import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.Toast;
 
-import java.text.Collator;
+import com.android.music.utils.LogHelper;
+import com.android.music.utils.MediaIDHelper;
 
-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;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class ArtistAlbumBrowserActivity extends ExpandableListActivity {
+    private static final String TAG = LogHelper.makeLogTag(ArtistAlbumBrowserActivity.class);
+    private static final String KEY_NUM_ALBUMS = "__NUM_ALBUMS__";
+    private static final MediaBrowser.MediaItem DEFAULT_PARENT_ITEM =
+            new MediaBrowser.MediaItem(new MediaDescription.Builder()
+                                               .setMediaId(MediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST)
+                                               .build(),
+                    MediaBrowser.MediaItem.FLAG_BROWSABLE);
+
     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;
+    private MediaBrowser mMediaBrowser;
+    private MediaBrowser.MediaItem mParentItem;
 
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+        LogHelper.d(TAG, "onCreate()");
+        // Handle past states
+        if (icicle != null) {
+            mParentItem = icicle.getParcelable(MusicUtils.TAG_PARENT_ITEM);
+        } else if (getIntent() != null) {
+            mParentItem = getIntent().getExtras().getParcelable(MusicUtils.TAG_PARENT_ITEM);
+        }
+        if (mParentItem == null) {
+            mParentItem = DEFAULT_PARENT_ITEM;
+        }
         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);
-
+        // Init layout
         setContentView(R.layout.media_picker_activity_expanding);
         MusicUtils.updateButtonBar(this, R.id.artisttab);
-        ExpandableListView lv = getExpandableListView();
-        lv.setOnCreateContextMenuListener(this);
-        lv.setTextFilterEnabled(true);
 
+        // Init expandable list
+        ExpandableListView lv = getExpandableListView();
+        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[] {});
+            mAdapter = new ArtistAlbumListAdapter(this, new ArrayList<>(), new ArrayList<>());
             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);
-            }
         }
+        setListAdapter(mAdapter);
+        LogHelper.d(TAG, "Creating MediaBrowser");
+        mMediaBrowser = new MediaBrowser(this, new ComponentName(this, MediaPlaybackService.class),
+                mConnectionCallback, 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);
+        outcicle.putParcelable(MusicUtils.TAG_PARENT_ITEM, mParentItem);
         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);
+    public void onStart() {
+        LogHelper.d(TAG, "onStart()");
+        super.onStart();
+        mMediaBrowser.connect();
     }
 
-    private BroadcastReceiver mTrackListListener = new BroadcastReceiver() {
+    @Override
+    public void onStop() {
+        LogHelper.d(TAG, "onStop()");
+        super.onStop();
+        mMediaBrowser.disconnect();
+    }
+
+    private MediaBrowser
+            .SubscriptionCallback mSubscriptionCallback = new MediaBrowser.SubscriptionCallback() {
         @Override
-        public void onReceive(Context context, Intent intent) {
-            getExpandableListView().invalidateViews();
+        public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) {
+            if (parentId.equals(MediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST)) {
+                mAdapter.getArtistMap().clear();
+                mAdapter.getGroupData().clear();
+                mAdapter.notifyDataSetInvalidated();
+                for (MediaBrowser.MediaItem item : children) {
+                    ConcurrentHashMap<String, MediaBrowser.MediaItem> entry =
+                            new ConcurrentHashMap<>();
+                    entry.put(MediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST, item);
+                    synchronized (this) {
+                        mAdapter.getArtistMap().put(item.getDescription().getTitle().toString(),
+                                mAdapter.getGroupData().size());
+                        mAdapter.getGroupData().add(entry);
+                        mAdapter.getChildData().add(new ArrayList<>());
+                    }
+                    mMediaBrowser.subscribe(item.getMediaId(), this);
+                }
+                mAdapter.notifyDataSetChanged();
+            } else if (parentId.startsWith(MediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST)) {
+                String artist = MediaIDHelper.getHierarchy(parentId)[1];
+                if (!mAdapter.getArtistMap().containsKey(artist)) {
+                    return;
+                }
+                int artistIndex = mAdapter.getArtistMap().get(artist);
+                mAdapter.getChildData().get(artistIndex).clear();
+                mAdapter.notifyDataSetInvalidated();
+                Bundle extras = new Bundle();
+                extras.putLong(KEY_NUM_ALBUMS, children.size());
+                MediaBrowser.MediaItem newArtistItem =
+                        new MediaBrowser.MediaItem(new MediaDescription.Builder()
+                                                           .setMediaId("Count")
+                                                           .setExtras(extras)
+                                                           .build(),
+                                0);
+                mAdapter.getGroupData().get(artistIndex).put(KEY_NUM_ALBUMS, newArtistItem);
+                for (MediaBrowser.MediaItem item : children) {
+                    ConcurrentHashMap<String, MediaBrowser.MediaItem> entry =
+                            new ConcurrentHashMap<>();
+                    entry.put(MediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM, item);
+                    mAdapter.getChildData().get(artistIndex).add(entry);
+                }
+                mAdapter.notifyDataSetChanged();
+            }
+        }
+
+        @Override
+        public void onError(String id) {
+            Toast.makeText(getApplicationContext(), R.string.error_loading_media, Toast.LENGTH_LONG)
+                    .show();
+        }
+    };
+
+    private MediaBrowser.ConnectionCallback mConnectionCallback =
+            new MediaBrowser.ConnectionCallback() {
+                @Override
+                public void onConnected() {
+                    LogHelper.d(
+                            TAG, "onConnected: session token ", mMediaBrowser.getSessionToken());
+                    mMediaBrowser.subscribe(mParentItem.getMediaId(), mSubscriptionCallback);
+                    if (mMediaBrowser.getSessionToken() == null) {
+                        throw new IllegalArgumentException("No Session token");
+                    }
+                    MediaController mediaController = new MediaController(
+                            ArtistAlbumBrowserActivity.this, mMediaBrowser.getSessionToken());
+                    mediaController.registerCallback(mMediaControllerCallback);
+                    ArtistAlbumBrowserActivity.this.setMediaController(mediaController);
+                    if (mediaController.getMetadata() != null) {
+                        MusicUtils.updateNowPlaying(ArtistAlbumBrowserActivity.this);
+                    }
+                }
+
+                @Override
+                public void onConnectionFailed() {
+                    LogHelper.d(TAG, "onConnectionFailed");
+                }
+
+                @Override
+                public void onConnectionSuspended() {
+                    LogHelper.d(TAG, "onConnectionSuspended");
+                    ArtistAlbumBrowserActivity.this.setMediaController(null);
+                }
+            };
+
+    private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            super.onMetadataChanged(metadata);
             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 class ArtistAlbumListAdapter extends SimpleExpandableListAdapter {
         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;
+        private ArrayList<ConcurrentHashMap<String, MediaBrowser.MediaItem>> mGroupData;
+        private ArrayList < ArrayList
+                < ConcurrentHashMap<String, MediaBrowser.MediaItem>>> mChildData;
+        private ConcurrentHashMap<String, Integer> mArtistMap;
 
-        static class ViewHolder {
+        private 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);
+        ArtistAlbumListAdapter(ArtistAlbumBrowserActivity currentActivity,
+                ArrayList<ConcurrentHashMap<String, MediaBrowser.MediaItem>> groupData,
+                ArrayList < ArrayList
+                        < ConcurrentHashMap<String, MediaBrowser.MediaItem>>> childData) {
+            super(currentActivity, groupData, R.layout.track_list_item_group, new String[] {},
+                    new int[] {}, childData, R.layout.track_list_item_child, new String[] {},
+                    new int[] {});
+            mGroupData = groupData;
+            mChildData = childData;
+            mActivity = currentActivity;
+            mNowPlayingOverlay = currentActivity.getResources().getDrawable(
+                    R.drawable.indicator_ic_mp_playing_list, currentActivity.getTheme());
+            mDefaultAlbumIcon = (BitmapDrawable) currentActivity.getResources().getDrawable(
+                    R.drawable.albumart_mp_unknown_list, currentActivity.getTheme());
             // 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);
+            mArtistMap = new ConcurrentHashMap<>();
         }
 
-        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 ArrayList<ConcurrentHashMap<String, MediaBrowser.MediaItem>> getGroupData() {
+            return mGroupData;
+        }
+
+        public ArrayList < ArrayList
+                < ConcurrentHashMap<String, MediaBrowser.MediaItem>>> getChildData() {
+            return mChildData;
+        }
+
+        public Map<String, Integer> getArtistMap() {
+            return mArtistMap;
         }
 
         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);
+        public View newGroupView(boolean isExpanded, ViewGroup parent) {
+            View v = super.newGroupView(isExpanded, parent);
             ImageView iv = (ImageView) v.findViewById(R.id.icon);
             ViewGroup.LayoutParams p = iv.getLayoutParams();
             p.width = ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -642,218 +296,111 @@
         }
 
         @Override
-        public View newChildView(
-                Context context, Cursor cursor, boolean isLastChild, ViewGroup parent) {
-            View v = super.newChildView(context, cursor, isLastChild, parent);
+        public View newChildView(boolean isLastChild, ViewGroup parent) {
+            View v = super.newChildView(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.setBackground(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;
+        public View getGroupView(
+                int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = newGroupView(isExpanded, parent);
             }
-            vh.line1.setText(displayartist);
-
-            int numalbums = cursor.getInt(mGroupAlbumIdx);
-            int numsongs = cursor.getInt(mGroupSongIdx);
-
-            String songs_albums = MusicUtils.makeAlbumsLabel(context, numalbums, numsongs, unknown);
-
+            Map<String, MediaBrowser.MediaItem> artistEntry =
+                    (Map<String, MediaBrowser.MediaItem>) getGroup(groupPosition);
+            MediaBrowser.MediaItem artistItem =
+                    artistEntry.get(MediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST);
+            MediaBrowser.MediaItem countItem = artistEntry.get(KEY_NUM_ALBUMS);
+            ViewHolder vh = (ViewHolder) convertView.getTag();
+            vh.line1.setText(artistItem.getDescription().getTitle());
+            int numalbums = -1;
+            if (countItem != null) {
+                Bundle extras = countItem.getDescription().getExtras();
+                if (extras != null) {
+                    numalbums = (int) extras.getLong(KEY_NUM_ALBUMS);
+                }
+            }
+            String songs_albums = MusicUtils.makeAlbumsLabel(mActivity, numalbums, -1, false);
             vh.line2.setText(songs_albums);
-
-            long currentartistid = MusicUtils.getCurrentArtistId();
-            long artistid = cursor.getLong(mGroupArtistIdIdx);
-            if (currentartistid == artistid && !isexpanded) {
+            MediaController mediaController = mActivity.getMediaController();
+            if (mediaController == null) {
+                vh.play_indicator.setImageDrawable(null);
+                return convertView;
+            }
+            MediaMetadata metadata = mediaController.getMetadata();
+            if (metadata == null) {
+                vh.play_indicator.setImageDrawable(null);
+                return convertView;
+            }
+            if (metadata.getString(MediaMetadata.METADATA_KEY_ARTIST)
+                            .equals(artistItem.getDescription().getTitle())
+                    && !isExpanded) {
                 vh.play_indicator.setImageDrawable(mNowPlayingOverlay);
             } else {
                 vh.play_indicator.setImageDrawable(null);
             }
+            return convertView;
         }
 
         @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;
+        public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
+                View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = newChildView(isLastChild, parent);
             }
-            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));
+            Map<String, MediaBrowser.MediaItem> albumEntry =
+                    (Map<String, MediaBrowser.MediaItem>) getChild(groupPosition, childPosition);
+            MediaBrowser.MediaItem albumItem =
+                    albumEntry.get(MediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM);
+            ViewHolder vh = (ViewHolder) convertView.getTag();
+            vh.line1.setText(albumItem.getDescription().getTitle());
+            vh.line2.setText(albumItem.getDescription().getDescription());
+            Bitmap albumArt = albumItem.getDescription().getIconBitmap();
+            if (albumArt == null) {
+                vh.icon.setBackground(mDefaultAlbumIcon);
             } 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.icon.setImageDrawable(MusicUtils.getDrawableBitmap(albumArt, mDefaultAlbumIcon));
             }
-            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);
+            MediaController mediaController = mActivity.getMediaController();
+            if (mediaController == null) {
+                vh.play_indicator.setImageDrawable(null);
+                return convertView;
+            }
+            MediaMetadata metadata = mediaController.getMetadata();
+            if (metadata == null) {
+                vh.play_indicator.setImageDrawable(null);
+                return convertView;
+            }
+            if (albumItem.getDescription().getTitle().equals(
+                        metadata.getString(MediaMetadata.METADATA_KEY_ALBUM))) {
+                vh.play_indicator.setImageDrawable(mNowPlayingOverlay);
             } else {
-                long artIndex = cursor.getLong(0);
-                Drawable d = MusicUtils.getCachedArtwork(context, artIndex, mDefaultAlbumIcon);
-                iv.setImageDrawable(d);
+                vh.play_indicator.setImageDrawable(null);
             }
-
-            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;
+            return convertView;
         }
     }
 
-    private Cursor mArtistCursor;
-
-    public void onServiceConnected(ComponentName name, IBinder service) {
-        MusicUtils.updateNowPlaying(this);
-    }
-
-    public void onServiceDisconnected(ComponentName name) {
-        finish();
+    @Override
+    public boolean onChildClick(
+            ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
+        Map<String, MediaBrowser.MediaItem> albumEntry =
+                (Map<String, MediaBrowser.MediaItem>) mAdapter.getChild(
+                        groupPosition, childPosition);
+        Intent intent = new Intent(Intent.ACTION_PICK);
+        intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
+        intent.putExtra(
+                MusicUtils.TAG_PARENT_ITEM, albumEntry.get(MediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM));
+        startActivity(intent);
+        return true;
     }
 }