Simplify some of the logic.

- Renamed/refactored MediaBrowserAdapter as MediaBrowserHelper.
- Move logic about the queue from MediaBrowserHelper into MainActivity.
- Simplify some of the UI code.

Bug: 67896004
Test: Manually verified.

Change-Id: I43e99ee3abe32723cfeec7800d922416cfa92476
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/client/MediaBrowserAdapter.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/client/MediaBrowserAdapter.java
deleted file mode 100644
index d2f0b7f..0000000
--- a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/client/MediaBrowserAdapter.java
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright 2017 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.example.android.mediasession.client;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.RemoteException;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.media.MediaBrowserCompat;
-import android.support.v4.media.MediaMetadataCompat;
-import android.support.v4.media.session.MediaControllerCompat;
-import android.support.v4.media.session.MediaSessionCompat;
-import android.support.v4.media.session.PlaybackStateCompat;
-import android.util.Log;
-
-import com.example.android.mediasession.service.MusicService;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Adapter for a MediaBrowser that handles connecting, disconnecting,
- * and basic browsing.
- */
-public class MediaBrowserAdapter {
-
-    private static final String TAG = MediaBrowserAdapter.class.getSimpleName();
-
-    /**
-     * Helper class for easily subscribing to changes in a MediaBrowserService connection.
-     */
-    public static abstract class MediaBrowserChangeListener {
-
-        public void onConnected(@Nullable MediaControllerCompat mediaController) {
-        }
-
-        public void onMetadataChanged(@Nullable MediaMetadataCompat mediaMetadata) {
-        }
-
-        public void onPlaybackStateChanged(@Nullable PlaybackStateCompat playbackState) {
-        }
-    }
-
-    private final InternalState mState;
-
-    private final Context mContext;
-    private final List<MediaBrowserChangeListener> mListeners = new ArrayList<>();
-
-    private final MediaBrowserConnectionCallback mMediaBrowserConnectionCallback =
-            new MediaBrowserConnectionCallback();
-    private final MediaControllerCallback mMediaControllerCallback =
-            new MediaControllerCallback();
-    private final MediaBrowserSubscriptionCallback mMediaBrowserSubscriptionCallback =
-            new MediaBrowserSubscriptionCallback();
-
-    private MediaBrowserCompat mMediaBrowser;
-
-    @Nullable
-    private MediaControllerCompat mMediaController;
-
-    public MediaBrowserAdapter(Context context) {
-        mContext = context;
-        mState = new InternalState();
-    }
-
-    public void onStart() {
-        if (mMediaBrowser == null) {
-            mMediaBrowser =
-                    new MediaBrowserCompat(
-                            mContext,
-                            new ComponentName(mContext, MusicService.class),
-                            mMediaBrowserConnectionCallback,
-                            null);
-            mMediaBrowser.connect();
-        }
-        Log.d(TAG, "onStart: Creating MediaBrowser, and connecting");
-    }
-
-    public void onStop() {
-        if (mMediaController != null) {
-            mMediaController.unregisterCallback(mMediaControllerCallback);
-            mMediaController = null;
-        }
-        if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
-            mMediaBrowser.disconnect();
-            mMediaBrowser = null;
-        }
-        resetState();
-        Log.d(TAG, "onStop: Releasing MediaController, Disconnecting from MediaBrowser");
-    }
-
-    /**
-     * The internal state of the app needs to revert to what it looks like when it started before
-     * any connections to the {@link MusicService} happens via the {@link MediaSessionCompat}.
-     */
-    private void resetState() {
-        mState.reset();
-        performOnAllListeners(new ListenerCommand() {
-            @Override
-            public void perform(@NonNull MediaBrowserChangeListener listener) {
-                listener.onPlaybackStateChanged(null);
-            }
-        });
-        Log.d(TAG, "resetState: ");
-    }
-
-    public MediaControllerCompat.TransportControls getTransportControls() {
-        if (mMediaController == null) {
-            Log.d(TAG, "getTransportControls: MediaController is null!");
-            throw new IllegalStateException();
-        }
-        return mMediaController.getTransportControls();
-    }
-
-    public void addListener(MediaBrowserChangeListener listener) {
-        if (listener != null) {
-            mListeners.add(listener);
-        }
-    }
-
-    public void removeListener(MediaBrowserChangeListener listener) {
-        if (listener != null) {
-            if (mListeners.contains(listener)) {
-                mListeners.remove(listener);
-            }
-        }
-    }
-
-    public void performOnAllListeners(@NonNull ListenerCommand command) {
-        for (MediaBrowserChangeListener listener : mListeners) {
-            if (listener != null) {
-                try {
-                    command.perform(listener);
-                } catch (Exception e) {
-                    removeListener(listener);
-                }
-            }
-        }
-    }
-
-    public interface ListenerCommand {
-
-        void perform(@NonNull MediaBrowserChangeListener listener);
-    }
-
-    // Receives callbacks from the MediaBrowser when it has successfully connected to the
-    // MediaBrowserService (MusicService).
-    public class MediaBrowserConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
-
-        // Happens as a result of onStart().
-        @Override
-        public void onConnected() {
-            try {
-                // Get a MediaController for the MediaSession.
-                mMediaController = new MediaControllerCompat(mContext,
-                                                             mMediaBrowser.getSessionToken());
-                mMediaController.registerCallback(mMediaControllerCallback);
-
-                // Sync existing MediaSession state to the UI.
-                mMediaControllerCallback.onMetadataChanged(
-                        mMediaController.getMetadata());
-                mMediaControllerCallback
-                        .onPlaybackStateChanged(mMediaController.getPlaybackState());
-
-                performOnAllListeners(new ListenerCommand() {
-                    @Override
-                    public void perform(@NonNull MediaBrowserChangeListener listener) {
-                        listener.onConnected(mMediaController);
-                    }
-                });
-            } catch (RemoteException e) {
-                Log.d(TAG, String.format("onConnected: Problem: %s", e.toString()));
-                throw new RuntimeException(e);
-            }
-
-            mMediaBrowser.subscribe(mMediaBrowser.getRoot(), mMediaBrowserSubscriptionCallback);
-        }
-    }
-
-    // Receives callbacks from the MediaBrowser when the MediaBrowserService has loaded new media
-    // that is ready for playback.
-    public class MediaBrowserSubscriptionCallback extends MediaBrowserCompat.SubscriptionCallback {
-
-        @Override
-        public void onChildrenLoaded(@NonNull String parentId,
-                                     @NonNull List<MediaBrowserCompat.MediaItem> children) {
-            assert mMediaController != null;
-
-            // Queue up all media items for this simple sample.
-            for (final MediaBrowserCompat.MediaItem mediaItem : children) {
-                mMediaController.addQueueItem(mediaItem.getDescription());
-            }
-
-            // Call "playFromMedia" so the UI is updated.
-            mMediaController.getTransportControls().prepare();
-        }
-    }
-
-    // Receives callbacks from the MediaController and updates the UI state,
-    // i.e.: Which is the current item, whether it's playing or paused, etc.
-    public class MediaControllerCallback extends MediaControllerCompat.Callback {
-
-        @Override
-        public void onMetadataChanged(final MediaMetadataCompat metadata) {
-            // Filtering out needless updates, given that the metadata has not changed.
-            if (isMediaIdSame(metadata, mState.getMediaMetadata())) {
-                Log.d(TAG, "onMetadataChanged: Filtering out needless onMetadataChanged() update");
-                return;
-            } else {
-                mState.setMediaMetadata(metadata);
-            }
-            performOnAllListeners(new ListenerCommand() {
-                @Override
-                public void perform(@NonNull MediaBrowserChangeListener listener) {
-                    listener.onMetadataChanged(metadata);
-                }
-            });
-        }
-
-        @Override
-        public void onPlaybackStateChanged(@Nullable final PlaybackStateCompat state) {
-            mState.setPlaybackState(state);
-            performOnAllListeners(new ListenerCommand() {
-                @Override
-                public void perform(@NonNull MediaBrowserChangeListener listener) {
-                    listener.onPlaybackStateChanged(state);
-                }
-            });
-        }
-
-        // This might happen if the MusicService is killed while the Activity is in the
-        // foreground and onStart() has been called (but not onStop()).
-        @Override
-        public void onSessionDestroyed() {
-            resetState();
-            onPlaybackStateChanged(null);
-            Log.d(TAG, "onSessionDestroyed: MusicService is dead!!!");
-        }
-
-        private boolean isMediaIdSame(MediaMetadataCompat currentMedia,
-                                     MediaMetadataCompat newMedia) {
-            if (currentMedia == null || newMedia == null) {
-                return false;
-            }
-            String newMediaId =
-                    newMedia.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID);
-            String currentMediaId =
-                    currentMedia.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID);
-            return newMediaId.equals(currentMediaId);
-        }
-
-    }
-
-    // A holder class that contains the internal state.
-    public class InternalState {
-
-        private PlaybackStateCompat playbackState;
-        private MediaMetadataCompat mediaMetadata;
-
-        public void reset() {
-            playbackState = null;
-            mediaMetadata = null;
-        }
-
-        public PlaybackStateCompat getPlaybackState() {
-            return playbackState;
-        }
-
-        public void setPlaybackState(PlaybackStateCompat playbackState) {
-            this.playbackState = playbackState;
-        }
-
-        public MediaMetadataCompat getMediaMetadata() {
-            return mediaMetadata;
-        }
-
-        public void setMediaMetadata(MediaMetadataCompat mediaMetadata) {
-            this.mediaMetadata = mediaMetadata;
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/client/MediaBrowserHelper.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/client/MediaBrowserHelper.java
new file mode 100644
index 0000000..4586712
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/client/MediaBrowserHelper.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2017 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.example.android.mediasession.client;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.RemoteException;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.media.MediaBrowserCompat;
+import android.support.v4.media.MediaBrowserServiceCompat;
+import android.support.v4.media.MediaMetadataCompat;
+import android.support.v4.media.session.MediaControllerCompat;
+import android.support.v4.media.session.MediaControllerCompat.Callback;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
+import android.util.Log;
+
+import com.example.android.mediasession.service.MusicService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class for a MediaBrowser that handles connecting, disconnecting,
+ * and basic browsing with simplified callbacks.
+ */
+public class MediaBrowserHelper {
+
+    private static final String TAG = MediaBrowserHelper.class.getSimpleName();
+
+    private final Context mContext;
+    private final Class<? extends MediaBrowserServiceCompat> mMediaBrowserServiceClass;
+
+    private final List<Callback> mCallbackList = new ArrayList<>();
+
+    private final MediaBrowserConnectionCallback mMediaBrowserConnectionCallback;
+    private final MediaControllerCallback mMediaControllerCallback;
+    private final MediaBrowserSubscriptionCallback mMediaBrowserSubscriptionCallback;
+
+    private MediaBrowserCompat mMediaBrowser;
+
+    @Nullable
+    private MediaControllerCompat mMediaController;
+
+    public MediaBrowserHelper(Context context,
+                              Class<? extends MediaBrowserServiceCompat> serviceClass) {
+        mContext = context;
+        mMediaBrowserServiceClass = serviceClass;
+
+        mMediaBrowserConnectionCallback = new MediaBrowserConnectionCallback();
+        mMediaControllerCallback = new MediaControllerCallback();
+        mMediaBrowserSubscriptionCallback = new MediaBrowserSubscriptionCallback();
+    }
+
+    public void onStart() {
+        if (mMediaBrowser == null) {
+            mMediaBrowser =
+                    new MediaBrowserCompat(
+                            mContext,
+                            new ComponentName(mContext, mMediaBrowserServiceClass),
+                            mMediaBrowserConnectionCallback,
+                            null);
+            mMediaBrowser.connect();
+        }
+        Log.d(TAG, "onStart: Creating MediaBrowser, and connecting");
+    }
+
+    public void onStop() {
+        if (mMediaController != null) {
+            mMediaController.unregisterCallback(mMediaControllerCallback);
+            mMediaController = null;
+        }
+        if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
+            mMediaBrowser.disconnect();
+            mMediaBrowser = null;
+        }
+        resetState();
+        Log.d(TAG, "onStop: Releasing MediaController, Disconnecting from MediaBrowser");
+    }
+
+    /**
+     * Called after connecting with a {@link MediaBrowserServiceCompat}.
+     * <p>
+     * Override to perform processing after a connection is established.
+     *
+     * @param mediaController {@link MediaControllerCompat} associated with the connected
+     *                        MediaSession.
+     */
+    protected void onConnected(@NonNull MediaControllerCompat mediaController) {
+    }
+
+    /**
+     * Called after loading a browsable {@link MediaBrowserCompat.MediaItem}
+     *
+     * @param parentId The media ID of the parent item.
+     * @param children List (possibly empty) of child items.
+     */
+    protected void onChildrenLoaded(@NonNull String parentId,
+                                    @NonNull List<MediaBrowserCompat.MediaItem> children) {
+    }
+
+    /**
+     * Called when the {@link MediaBrowserServiceCompat} connection is lost.
+     */
+    protected void onDisconnected() {
+    }
+
+    @NonNull
+    protected final MediaControllerCompat getMediaController() {
+        if (mMediaController == null) {
+            throw new IllegalStateException("MediaController is null!");
+        }
+        return mMediaController;
+    }
+
+    /**
+     * The internal state of the app needs to revert to what it looks like when it started before
+     * any connections to the {@link MusicService} happens via the {@link MediaSessionCompat}.
+     */
+    private void resetState() {
+        performOnAllCallbacks(new CallbackCommand() {
+            @Override
+            public void perform(@NonNull Callback callback) {
+                callback.onPlaybackStateChanged(null);
+            }
+        });
+        Log.d(TAG, "resetState: ");
+    }
+
+    public MediaControllerCompat.TransportControls getTransportControls() {
+        if (mMediaController == null) {
+            Log.d(TAG, "getTransportControls: MediaController is null!");
+            throw new IllegalStateException("MediaController is null!");
+        }
+        return mMediaController.getTransportControls();
+    }
+
+    public void registerCallback(Callback callback) {
+        if (callback != null) {
+            mCallbackList.add(callback);
+
+            // Update with the latest metadata/playback state.
+            if (mMediaController != null) {
+                final MediaMetadataCompat metadata = mMediaController.getMetadata();
+                if (metadata != null) {
+                    callback.onMetadataChanged(metadata);
+                }
+
+                final PlaybackStateCompat playbackState = mMediaController.getPlaybackState();
+                if (playbackState != null) {
+                    callback.onPlaybackStateChanged(playbackState);
+                }
+            }
+        }
+    }
+
+    private void performOnAllCallbacks(@NonNull CallbackCommand command) {
+        for (Callback callback : mCallbackList) {
+            if (callback != null) {
+                command.perform(callback);
+            }
+        }
+    }
+
+    /**
+     * Helper for more easily performing operations on all listening clients.
+     */
+    private interface CallbackCommand {
+        void perform(@NonNull Callback callback);
+    }
+
+    // Receives callbacks from the MediaBrowser when it has successfully connected to the
+    // MediaBrowserService (MusicService).
+    private class MediaBrowserConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
+
+        // Happens as a result of onStart().
+        @Override
+        public void onConnected() {
+            try {
+                // Get a MediaController for the MediaSession.
+                mMediaController =
+                        new MediaControllerCompat(mContext, mMediaBrowser.getSessionToken());
+                mMediaController.registerCallback(mMediaControllerCallback);
+
+                // Sync existing MediaSession state to the UI.
+                mMediaControllerCallback.onMetadataChanged(mMediaController.getMetadata());
+                mMediaControllerCallback.onPlaybackStateChanged(
+                        mMediaController.getPlaybackState());
+
+                MediaBrowserHelper.this.onConnected(mMediaController);
+            } catch (RemoteException e) {
+                Log.d(TAG, String.format("onConnected: Problem: %s", e.toString()));
+                throw new RuntimeException(e);
+            }
+
+            mMediaBrowser.subscribe(mMediaBrowser.getRoot(), mMediaBrowserSubscriptionCallback);
+        }
+    }
+
+    // Receives callbacks from the MediaBrowser when the MediaBrowserService has loaded new media
+    // that is ready for playback.
+    public class MediaBrowserSubscriptionCallback extends MediaBrowserCompat.SubscriptionCallback {
+
+        @Override
+        public void onChildrenLoaded(@NonNull String parentId,
+                                     @NonNull List<MediaBrowserCompat.MediaItem> children) {
+            MediaBrowserHelper.this.onChildrenLoaded(parentId, children);
+        }
+    }
+
+    // Receives callbacks from the MediaController and updates the UI state,
+    // i.e.: Which is the current item, whether it's playing or paused, etc.
+    private class MediaControllerCallback extends MediaControllerCompat.Callback {
+
+        @Override
+        public void onMetadataChanged(final MediaMetadataCompat metadata) {
+            performOnAllCallbacks(new CallbackCommand() {
+                @Override
+                public void perform(@NonNull Callback callback) {
+                    callback.onMetadataChanged(metadata);
+                }
+            });
+        }
+
+        @Override
+        public void onPlaybackStateChanged(@Nullable final PlaybackStateCompat state) {
+            performOnAllCallbacks(new CallbackCommand() {
+                @Override
+                public void perform(@NonNull Callback callback) {
+                    callback.onPlaybackStateChanged(state);
+                }
+            });
+        }
+
+        // This might happen if the MusicService is killed while the Activity is in the
+        // foreground and onStart() has been called (but not onStop()).
+        @Override
+        public void onSessionDestroyed() {
+            resetState();
+            onPlaybackStateChanged(null);
+
+            MediaBrowserHelper.this.onDisconnected();
+        }
+    }
+}
\ No newline at end of file
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/service/MusicService.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/service/MusicService.java
index 5d98bc2..df4b736 100644
--- a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/service/MusicService.java
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/service/MusicService.java
@@ -104,12 +104,14 @@
         public void onAddQueueItem(MediaDescriptionCompat description) {
             mPlaylist.add(new MediaSessionCompat.QueueItem(description, description.hashCode()));
             mQueueIndex = (mQueueIndex == -1) ? 0 : mQueueIndex;
+            mSession.setQueue(mPlaylist);
         }
 
         @Override
         public void onRemoveQueueItem(MediaDescriptionCompat description) {
             mPlaylist.remove(new MediaSessionCompat.QueueItem(description, description.hashCode()));
             mQueueIndex = (mPlaylist.isEmpty()) ? -1 : mQueueIndex;
+            mSession.setQueue(mPlaylist);
         }
 
         @Override
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/ui/MainActivity.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/ui/MainActivity.java
index 4991789..530cf73 100644
--- a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/ui/MainActivity.java
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/ui/MainActivity.java
@@ -16,10 +16,13 @@
 
 package com.example.android.mediasession.ui;
 
+import android.content.Context;
 import android.os.Bundle;
-import android.support.annotation.Nullable;
+import android.support.annotation.NonNull;
+import android.support.v4.media.MediaBrowserCompat;
 import android.support.v4.media.MediaMetadataCompat;
 import android.support.v4.media.session.MediaControllerCompat;
+import android.support.v4.media.session.MediaSessionCompat;
 import android.support.v4.media.session.PlaybackStateCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.view.View;
@@ -28,9 +31,12 @@
 import android.widget.TextView;
 
 import com.example.android.mediasession.R;
-import com.example.android.mediasession.client.MediaBrowserAdapter;
+import com.example.android.mediasession.client.MediaBrowserHelper;
+import com.example.android.mediasession.service.MusicService;
 import com.example.android.mediasession.service.contentcatalogs.MusicLibrary;
 
+import java.util.List;
+
 public class MainActivity extends AppCompatActivity {
 
     private ImageView mAlbumArt;
@@ -39,7 +45,7 @@
     private ImageView mMediaControlsImage;
     private MediaSeekBar mSeekBarAudio;
 
-    private MediaBrowserAdapter mMediaBrowserAdapter;
+    private MediaBrowserHelper mMediaBrowserHelper;
 
     private boolean mIsPlaying;
 
@@ -47,69 +53,103 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
-        initializeUI();
-        mMediaBrowserAdapter = new MediaBrowserAdapter(this);
-        mMediaBrowserAdapter.addListener(new MediaBrowserListener());
-    }
 
-    private void initializeUI() {
-        mTitleTextView = (TextView) findViewById(R.id.song_title);
-        mArtistTextView = (TextView) findViewById(R.id.song_artist);
-        mAlbumArt = (ImageView) findViewById(R.id.album_art);
-        mMediaControlsImage = (ImageView) findViewById(R.id.media_controls);
-        mSeekBarAudio = (MediaSeekBar) findViewById(R.id.seekbar_audio);
+        mTitleTextView = findViewById(R.id.song_title);
+        mArtistTextView = findViewById(R.id.song_artist);
+        mAlbumArt = findViewById(R.id.album_art);
+        mMediaControlsImage = findViewById(R.id.media_controls);
+        mSeekBarAudio = findViewById(R.id.seekbar_audio);
 
-        final Button buttonPrevious = (Button) findViewById(R.id.button_previous);
-        buttonPrevious.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mMediaBrowserAdapter.getTransportControls().skipToPrevious();
-            }
-        });
+        final ClickListener clickListener = new ClickListener();
+        findViewById(R.id.button_previous).setOnClickListener(clickListener);
+        findViewById(R.id.button_play).setOnClickListener(clickListener);
+        findViewById(R.id.button_next).setOnClickListener(clickListener);
 
-        final Button buttonPlay = (Button) findViewById(R.id.button_play);
-        buttonPlay.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mIsPlaying) {
-                    mMediaBrowserAdapter.getTransportControls().pause();
-                } else {
-                    mMediaBrowserAdapter.getTransportControls().play();
-                }
-            }
-        });
-
-        final Button buttonNext = (Button) findViewById(R.id.button_next);
-        buttonNext.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mMediaBrowserAdapter.getTransportControls().skipToNext();
-            }
-        });
+        mMediaBrowserHelper = new MediaBrowserConnection(this);
+        mMediaBrowserHelper.registerCallback(new MediaBrowserListener());
     }
 
     @Override
     public void onStart() {
         super.onStart();
-        mMediaBrowserAdapter.onStart();
+        mMediaBrowserHelper.onStart();
     }
 
     @Override
     public void onStop() {
         super.onStop();
         mSeekBarAudio.disconnectController();
-        mMediaBrowserAdapter.onStop();
+        mMediaBrowserHelper.onStop();
     }
 
-    private class MediaBrowserListener extends MediaBrowserAdapter.MediaBrowserChangeListener {
+    /**
+     * Convenience class to collect the click listeners together.
+     * <p>
+     * In a larger app it's better to split the listeners out or to use your favorite
+     * library.
+     */
+    private class ClickListener implements View.OnClickListener {
+        @Override
+        public void onClick(View v) {
+            switch (v.getId()) {
+                case R.id.button_previous:
+                    mMediaBrowserHelper.getTransportControls().skipToPrevious();
+                    break;
+                case R.id.button_play:
+                    if (mIsPlaying) {
+                        mMediaBrowserHelper.getTransportControls().pause();
+                    } else {
+                        mMediaBrowserHelper.getTransportControls().play();
+                    }
+                    break;
+                case R.id.button_next:
+                    mMediaBrowserHelper.getTransportControls().skipToNext();
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Customize the connection to our {@link android.support.v4.media.MediaBrowserServiceCompat}
+     * and implement our app specific desires.
+     */
+    private class MediaBrowserConnection extends MediaBrowserHelper {
+        private MediaBrowserConnection(Context context) {
+            super(context, MusicService.class);
+        }
 
         @Override
-        public void onConnected(@Nullable MediaControllerCompat mediaController) {
-            super.onConnected(mediaController);
+        protected void onConnected(@NonNull MediaControllerCompat mediaController) {
             mSeekBarAudio.setMediaController(mediaController);
         }
 
         @Override
+        protected void onChildrenLoaded(@NonNull String parentId,
+                                        @NonNull List<MediaBrowserCompat.MediaItem> children) {
+            super.onChildrenLoaded(parentId, children);
+
+            final MediaControllerCompat mediaController = getMediaController();
+
+            // Queue up all media items for this simple sample.
+            for (final MediaBrowserCompat.MediaItem mediaItem : children) {
+                mediaController.addQueueItem(mediaItem.getDescription());
+            }
+
+            // Call prepare now so pressing play just works.
+            mediaController.getTransportControls().prepare();
+        }
+    }
+
+    /**
+     * Implementation of the {@link MediaControllerCompat.Callback} methods we're interested in.
+     * <p>
+     * Here would also be where one could override
+     * {@code onQueueChanged(List<MediaSessionCompat.QueueItem> queue)} to get informed when items
+     * are added or removed from the queue. We don't do this here in order to keep the UI
+     * simple.
+     */
+    private class MediaBrowserListener extends MediaControllerCompat.Callback {
+        @Override
         public void onPlaybackStateChanged(PlaybackStateCompat playbackState) {
             mIsPlaying = playbackState != null &&
                     playbackState.getState() == PlaybackStateCompat.STATE_PLAYING;
@@ -129,5 +169,15 @@
                     MainActivity.this,
                     mediaMetadata.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID)));
         }
+
+        @Override
+        public void onSessionDestroyed() {
+            super.onSessionDestroyed();
+        }
+
+        @Override
+        public void onQueueChanged(List<MediaSessionCompat.QueueItem> queue) {
+            super.onQueueChanged(queue);
+        }
     }
 }
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/ui/MediaSeekBar.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/ui/MediaSeekBar.java
index 71de9ca..e70af6d 100644
--- a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/ui/MediaSeekBar.java
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediasession/ui/MediaSeekBar.java
@@ -71,8 +71,9 @@
     }
 
     @Override
-    public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
+    public final void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
         // Prohibit adding seek listeners to this subclass.
+        throw new UnsupportedOperationException("Cannot add listeners to a MediaSeekBar");
     }
 
     public void setMediaController(final MediaControllerCompat mediaController) {