summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MusicDemo/src/main/AndroidManifest.xml9
-rw-r--r--MusicDemo/src/main/java/com/example/android/musicservicedemo/BrowseFragment.java208
-rw-r--r--MusicDemo/src/main/java/com/example/android/musicservicedemo/MusicPlayerActivity.java61
-rw-r--r--MusicDemo/src/main/java/com/example/android/musicservicedemo/MusicService.java6
-rw-r--r--MusicDemo/src/main/java/com/example/android/musicservicedemo/QueueAdapter.java82
-rw-r--r--MusicDemo/src/main/java/com/example/android/musicservicedemo/QueueFragment.java319
-rw-r--r--MusicDemo/src/main/java/com/example/android/musicservicedemo/utils/QueueHelper.java2
-rw-r--r--MusicDemo/src/main/res/drawable-xhdpi/ic_equalizer_white_24dp.pngbin0 -> 207 bytes
-rw-r--r--MusicDemo/src/main/res/drawable-xxhdpi/ic_equalizer_white_24dp.pngbin0 -> 265 bytes
-rw-r--r--MusicDemo/src/main/res/layout/activity_player.xml22
-rw-r--r--MusicDemo/src/main/res/layout/fragment_list.xml60
-rw-r--r--MusicDemo/src/main/res/layout/list_item.xml55
-rw-r--r--MusicDemo/src/main/res/values-v21/styles.xml2
-rw-r--r--MusicDemo/src/main/res/values/dimens.xml19
-rw-r--r--MusicDemo/src/main/res/values/strings.xml5
15 files changed, 848 insertions, 2 deletions
diff --git a/MusicDemo/src/main/AndroidManifest.xml b/MusicDemo/src/main/AndroidManifest.xml
index f84ee42..58e54c2 100644
--- a/MusicDemo/src/main/AndroidManifest.xml
+++ b/MusicDemo/src/main/AndroidManifest.xml
@@ -35,6 +35,15 @@
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
+
+ <activity android:name=".MusicPlayerActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
<!-- (OPTIONAL) use this meta data to indicate which icon should be used in media
notifications (for example, when the music changes and the user is
looking at another app) -->
diff --git a/MusicDemo/src/main/java/com/example/android/musicservicedemo/BrowseFragment.java b/MusicDemo/src/main/java/com/example/android/musicservicedemo/BrowseFragment.java
new file mode 100644
index 0000000..2b98263
--- /dev/null
+++ b/MusicDemo/src/main/java/com/example/android/musicservicedemo/BrowseFragment.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2014 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.musicservicedemo;
+
+import android.app.Fragment;
+import android.content.ComponentName;
+import android.content.Context;
+import android.media.browse.MediaBrowser;
+import android.media.session.MediaController;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.example.android.musicservicedemo.utils.LogHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Fragment that lists all the various browsable queues available
+ * from a {@link android.service.media.MediaBrowserService}.
+ * <p/>
+ * It uses a {@link MediaBrowser} to connect to the {@link MusicService}. Once connected,
+ * the fragment subscribes to get all the children. All {@link MediaBrowser.MediaItem}'s
+ * that can be browsed are shown in a ListView.
+ */
+public class BrowseFragment extends Fragment {
+
+ private static final String TAG = BrowseFragment.class.getSimpleName();
+
+ public static final String ARG_MEDIA_ID = "media_id";
+
+ public static interface FragmentDataHelper {
+ void onMediaItemSelected(MediaBrowser.MediaItem item);
+ }
+
+ // The mediaId to be used for subscribing for children using the MediaBrowser.
+ private String mMediaId;
+
+ private MediaBrowser mMediaBrowser;
+ private BrowseAdapter mBrowserAdapter;
+
+ private MediaBrowser.SubscriptionCallback mSubscriptionCallback = new MediaBrowser.SubscriptionCallback() {
+
+ @Override
+ public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) {
+ mBrowserAdapter.clear();
+ mBrowserAdapter.notifyDataSetInvalidated();
+ for (MediaBrowser.MediaItem item : children) {
+ LogHelper.d(TAG, "MediaItem: -> " + item.getDescription().getDescription());
+ mBrowserAdapter.add(item);
+ }
+ mBrowserAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onError(String id) {
+ Toast.makeText(getActivity(), 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());
+
+ if (mMediaId == null) {
+ mMediaId = mMediaBrowser.getRoot();
+ }
+ mMediaBrowser.subscribe(mMediaId, mSubscriptionCallback);
+ MediaController mediaController = new MediaController(getActivity(),
+ mMediaBrowser.getSessionToken());
+ getActivity().setMediaController(mediaController);
+ }
+
+ @Override
+ public void onConnectionFailed() {
+ LogHelper.d(TAG, "onConnectionFailed");
+ }
+
+ @Override
+ public void onConnectionSuspended() {
+ LogHelper.d(TAG, "onConnectionSuspended");
+ getActivity().setMediaController(null);
+ }
+ };
+
+ public static BrowseFragment newInstance(String mediaId) {
+ Bundle args = new Bundle();
+ args.putString(ARG_MEDIA_ID, mediaId);
+ BrowseFragment fragment = new BrowseFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.fragment_list, container, false);
+
+ mBrowserAdapter = new BrowseAdapter(getActivity());
+
+ View controls = rootView.findViewById(R.id.controls);
+ controls.setVisibility(View.GONE);
+
+ ListView listView = (ListView) rootView.findViewById(R.id.list_view);
+ listView.setAdapter(mBrowserAdapter);
+ listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ MediaBrowser.MediaItem item = mBrowserAdapter.getItem(position);
+ try {
+ FragmentDataHelper listener = (FragmentDataHelper) getActivity();
+ listener.onMediaItemSelected(item);
+ } catch (ClassCastException ex) {
+ Log.e(TAG, "Exception trying to cast to FragmentDataHelper", ex);
+ }
+ }
+ });
+
+ Bundle args = getArguments();
+ mMediaId = args.getString(ARG_MEDIA_ID, null);
+
+ mMediaBrowser = new MediaBrowser(getActivity(),
+ new ComponentName(getActivity(), MusicService.class),
+ mConnectionCallback, null);
+
+ return rootView;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mMediaBrowser.connect();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ mMediaBrowser.disconnect();
+ }
+
+ // An adapter for showing the list of browsed MediaItem's
+ private static class BrowseAdapter extends ArrayAdapter<MediaBrowser.MediaItem> {
+
+ public BrowseAdapter(Context context) {
+ super(context, R.layout.list_item, new ArrayList<MediaBrowser.MediaItem>());
+ }
+
+ static class ViewHolder {
+ ImageView mImageView;
+ TextView mTitleView;
+ TextView mDescriptionView;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ ViewHolder holder;
+
+ if (convertView == null) {
+ convertView = LayoutInflater.from(getContext())
+ .inflate(R.layout.list_item, parent, false);
+ holder = new ViewHolder();
+ holder.mImageView = (ImageView) convertView.findViewById(R.id.play_eq);
+ holder.mImageView.setVisibility(View.GONE);
+ holder.mTitleView = (TextView) convertView.findViewById(R.id.title);
+ holder.mDescriptionView = (TextView) convertView.findViewById(R.id.description);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ MediaBrowser.MediaItem item = getItem(position);
+ holder.mTitleView.setText(item.getDescription().getTitle());
+ holder.mDescriptionView.setText(item.getDescription().getDescription());
+ if (item.isPlayable()) {
+ holder.mImageView.setImageDrawable(
+ getContext().getDrawable(R.drawable.ic_play_arrow_white_24dp));
+ holder.mImageView.setVisibility(View.VISIBLE);
+ }
+ return convertView;
+ }
+ }
+}
diff --git a/MusicDemo/src/main/java/com/example/android/musicservicedemo/MusicPlayerActivity.java b/MusicDemo/src/main/java/com/example/android/musicservicedemo/MusicPlayerActivity.java
new file mode 100644
index 0000000..7a2f8fb
--- /dev/null
+++ b/MusicDemo/src/main/java/com/example/android/musicservicedemo/MusicPlayerActivity.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 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.musicservicedemo;
+
+import android.app.Activity;
+import android.media.browse.MediaBrowser;
+import android.media.session.MediaController;
+import android.os.Bundle;
+
+/**
+ * Main activity for the music player.
+ */
+public class MusicPlayerActivity extends Activity
+ implements BrowseFragment.FragmentDataHelper {
+
+ private static final String TAG = MusicPlayerActivity.class.getSimpleName();
+
+ private MediaBrowser mMediaBrowser;
+ private MediaController mMediaController;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_player);
+ if (savedInstanceState == null) {
+ getFragmentManager().beginTransaction()
+ .add(R.id.container, BrowseFragment.newInstance(null))
+ .commit();
+ }
+ }
+
+ @Override
+ public void onMediaItemSelected(MediaBrowser.MediaItem item) {
+ if (item.isPlayable()) {
+ getMediaController().getTransportControls().playFromMediaId(item.getMediaId(), null);
+ QueueFragment queueFragment = QueueFragment.newInstance(item.getMediaId());
+ getFragmentManager().beginTransaction()
+ .replace(R.id.container, queueFragment)
+ .addToBackStack(null)
+ .commit();
+ } else if (item.isBrowsable()) {
+ getFragmentManager().beginTransaction()
+ .replace(R.id.container, BrowseFragment.newInstance(item.getMediaId()))
+ .addToBackStack(null)
+ .commit();
+ }
+ }
+}
diff --git a/MusicDemo/src/main/java/com/example/android/musicservicedemo/MusicService.java b/MusicDemo/src/main/java/com/example/android/musicservicedemo/MusicService.java
index e085275..c737ba4 100644
--- a/MusicDemo/src/main/java/com/example/android/musicservicedemo/MusicService.java
+++ b/MusicDemo/src/main/java/com/example/android/musicservicedemo/MusicService.java
@@ -847,6 +847,12 @@ public class MusicService extends MediaBrowserService implements OnPreparedListe
}
stateBuilder.setState(mState, position, 1.0f, SystemClock.elapsedRealtime());
+ // Set the activeQueueItemId if the current index is valid.
+ if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+ MediaSession.QueueItem item = mPlayingQueue.get(mCurrentIndexOnQueue);
+ stateBuilder.setActiveQueueItemId(item.getQueueId());
+ }
+
mSession.setPlaybackState(stateBuilder.build());
if (mState == PlaybackState.STATE_PLAYING || mState == PlaybackState.STATE_PAUSED) {
diff --git a/MusicDemo/src/main/java/com/example/android/musicservicedemo/QueueAdapter.java b/MusicDemo/src/main/java/com/example/android/musicservicedemo/QueueAdapter.java
new file mode 100644
index 0000000..9c7870b
--- /dev/null
+++ b/MusicDemo/src/main/java/com/example/android/musicservicedemo/QueueAdapter.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 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.musicservicedemo;
+
+import android.app.Activity;
+import android.media.session.MediaSession;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * A list adapter for items in a queue
+ */
+public class QueueAdapter extends ArrayAdapter<MediaSession.QueueItem> {
+
+ // The currently selected/active queue item Id.
+ private long mActiveQueueItemId = MediaSession.QueueItem.UNKNOWN_ID;
+
+ public QueueAdapter(Activity context) {
+ super(context, R.layout.list_item, new ArrayList<MediaSession.QueueItem>());
+ }
+
+ public void setActiveQueueItemId(long id) {
+ this.mActiveQueueItemId = id;
+ }
+
+ private static class ViewHolder {
+ ImageView mImageView;
+ TextView mTitleView;
+ TextView mDescriptionView;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder holder;
+
+ if (convertView == null) {
+ convertView = LayoutInflater.from(getContext())
+ .inflate(R.layout.list_item, parent, false);
+ holder = new ViewHolder();
+ holder.mImageView = (ImageView) convertView.findViewById(R.id.play_eq);
+ holder.mTitleView = (TextView) convertView.findViewById(R.id.title);
+ holder.mDescriptionView = (TextView) convertView.findViewById(R.id.description);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ MediaSession.QueueItem item = getItem(position);
+ holder.mTitleView.setText(item.getDescription().getTitle());
+ if (item.getDescription().getDescription() != null) {
+ holder.mDescriptionView.setText(item.getDescription().getDescription());
+ }
+
+ // If the itemId matches the active Id then use a different icon
+ if (mActiveQueueItemId == item.getQueueId()) {
+ holder.mImageView.setImageDrawable(
+ getContext().getDrawable(R.drawable.ic_equalizer_white_24dp));
+ } else {
+ holder.mImageView.setImageDrawable(
+ getContext().getDrawable(R.drawable.ic_play_arrow_white_24dp));
+ }
+ return convertView;
+ }
+}
diff --git a/MusicDemo/src/main/java/com/example/android/musicservicedemo/QueueFragment.java b/MusicDemo/src/main/java/com/example/android/musicservicedemo/QueueFragment.java
new file mode 100644
index 0000000..233b282
--- /dev/null
+++ b/MusicDemo/src/main/java/com/example/android/musicservicedemo/QueueFragment.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2014 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.musicservicedemo;
+
+import android.app.Fragment;
+import android.content.ComponentName;
+import android.media.MediaMetadata;
+import android.media.browse.MediaBrowser;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ImageButton;
+import android.widget.ListView;
+
+import com.example.android.musicservicedemo.utils.LogHelper;
+
+import java.util.List;
+
+/**
+ * A class that shows the queue to the user
+ */
+public class QueueFragment extends Fragment {
+
+ private static final String TAG = QueueFragment.class.getSimpleName() + "BLAH";
+ private static final String ARGS_MEDIA_ID = "media_id";
+
+ private ImageButton mSkipNext;
+ private ImageButton mSkipPrevious;
+ private ImageButton mPlayPause;
+
+ private MediaBrowser mMediaBrowser;
+ private MediaController.TransportControls mTransportControls;
+ private MediaController mMediaController;
+ private PlaybackState mPlaybackState;
+ private boolean mConnected;
+
+ private QueueAdapter mQueueAdapter;
+ private String mMediaId;
+ private ListView mListView;
+
+ private MediaBrowser.ConnectionCallback mConnectionCallback =
+ new MediaBrowser.ConnectionCallback() {
+ @Override
+ public void onConnected() {
+ LogHelper.d(TAG, "onConnected: session token " + mMediaBrowser.getSessionToken());
+
+ mMediaController = new MediaController(getActivity(),
+ mMediaBrowser.getSessionToken());
+ mTransportControls = mMediaController.getTransportControls();
+ mMediaController.registerCallback(mSessionCallback);
+
+ getActivity().setMediaController(mMediaController);
+ mPlaybackState = mMediaController.getPlaybackState();
+
+ long activeItemId = MediaSession.QueueItem.UNKNOWN_ID;
+ if (mPlaybackState != null) {
+ activeItemId = mPlaybackState.getActiveQueueItemId();
+ }
+ mQueueAdapter.setActiveQueueItemId(activeItemId);
+
+ List<MediaSession.QueueItem> queue = mMediaController.getQueue();
+ if (queue != null) {
+ mQueueAdapter.clear();
+ mQueueAdapter.notifyDataSetInvalidated();
+ mQueueAdapter.addAll(queue);
+ mQueueAdapter.notifyDataSetChanged();
+ }
+
+ LogHelper.d(TAG, "got playback state " + mPlaybackState);
+ mConnected = true;
+ onPlaybackStateChanged(mPlaybackState);
+ }
+
+ @Override
+ public void onConnectionFailed() {
+ LogHelper.d(TAG, "onConnectionFailed");
+ mConnected = false;
+ }
+
+ @Override
+ public void onConnectionSuspended() {
+ LogHelper.d(TAG, "onConnectionSuspended");
+ mMediaController.unregisterCallback(mSessionCallback);
+ mTransportControls = null;
+ mConnected = false;
+ mMediaController = null;
+ getActivity().setMediaController(null);
+ }
+ };
+
+ // Receive callbacks from the MediaController. Here we update our state such as which queue
+ // is being shown, the current title and description and the PlaybackState.
+ private MediaController.Callback mSessionCallback = new MediaController.Callback() {
+
+ @Override
+ public void onSessionDestroyed() {
+ LogHelper.d(TAG, "Session destroyed. Need to fetch a new Media Session");
+ }
+
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ if (state == null) {
+ return;
+ }
+ LogHelper.d(TAG, "Received playback state change to state " + state.getState());
+ mPlaybackState = state;
+ QueueFragment.this.onPlaybackStateChanged(state);
+ }
+
+ @Override
+ public void onQueueChanged(List<MediaSession.QueueItem> queue) {
+ LogHelper.d(TAG, "onQueueChanged " + queue);
+ if (queue != null) {
+ mQueueAdapter.clear();
+ mQueueAdapter.notifyDataSetInvalidated();
+ mQueueAdapter.addAll(queue);
+ mQueueAdapter.notifyDataSetChanged();
+ }
+ }
+ };
+
+ public static QueueFragment newInstance(String mediaId) {
+ QueueFragment fragment = new QueueFragment();
+ Bundle args = new Bundle();
+ args.putString(ARGS_MEDIA_ID, mediaId);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.fragment_list, container, false);
+
+ mSkipPrevious = (ImageButton) rootView.findViewById(R.id.skip_previous);
+ mSkipPrevious.setEnabled(false);
+ mSkipPrevious.setOnClickListener(mButtonListener);
+
+ mSkipNext = (ImageButton) rootView.findViewById(R.id.skip_next);
+ mSkipNext.setEnabled(false);
+ mSkipNext.setOnClickListener(mButtonListener);
+
+ mPlayPause = (ImageButton) rootView.findViewById(R.id.play_pause);
+ mPlayPause.setEnabled(true);
+ mPlayPause.setOnClickListener(mButtonListener);
+
+ mQueueAdapter = new QueueAdapter(getActivity());
+
+ mListView = (ListView) rootView.findViewById(R.id.list_view);
+ mListView.setAdapter(mQueueAdapter);
+ mListView.setFocusable(true);
+ mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ MediaSession.QueueItem item = mQueueAdapter.getItem(position);
+ mTransportControls.skipToQueueItem(item.getQueueId());
+ mQueueAdapter.setActiveQueueItemId(item.getQueueId());
+ mQueueAdapter.notifyDataSetChanged();
+ }
+ });
+
+ Bundle args = getArguments();
+ mMediaId = args.getString(ARGS_MEDIA_ID);
+
+ mMediaBrowser = new MediaBrowser(getActivity(),
+ new ComponentName(getActivity(), MusicService.class),
+ mConnectionCallback, null);
+
+ return rootView;
+ }
+
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (mMediaBrowser != null) {
+ mMediaBrowser.connect();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (mMediaController != null) {
+ mMediaController.unregisterCallback(mSessionCallback);
+ }
+ if (mMediaBrowser != null) {
+ mMediaBrowser.disconnect();
+ }
+ }
+
+
+ private void onPlaybackStateChanged(PlaybackState state) {
+ LogHelper.d(TAG, "onPlaybackStateChanged " + state);
+ if (state == null) {
+ return;
+ }
+ mQueueAdapter.setActiveQueueItemId(state.getActiveQueueItemId());
+ mQueueAdapter.notifyDataSetChanged();
+ boolean enablePlay = false;
+ StringBuilder statusBuilder = new StringBuilder();
+ switch (state.getState()) {
+ case PlaybackState.STATE_PLAYING:
+ statusBuilder.append("playing");
+ enablePlay = false;
+ break;
+ case PlaybackState.STATE_PAUSED:
+ statusBuilder.append("paused");
+ enablePlay = true;
+ break;
+ case PlaybackState.STATE_STOPPED:
+ statusBuilder.append("ended");
+ enablePlay = true;
+ break;
+ case PlaybackState.STATE_ERROR:
+ statusBuilder.append("error: ").append(state.getErrorMessage());
+ break;
+ case PlaybackState.STATE_BUFFERING:
+ statusBuilder.append("buffering");
+ break;
+ case PlaybackState.STATE_NONE:
+ statusBuilder.append("none");
+ enablePlay = false;
+ break;
+ case PlaybackState.STATE_CONNECTING:
+ statusBuilder.append("connecting");
+ break;
+ default:
+ statusBuilder.append(mPlaybackState);
+ }
+ statusBuilder.append(" -- At position: ").append(state.getPosition());
+ LogHelper.d(TAG, statusBuilder.toString());
+
+ if (enablePlay) {
+ mPlayPause.setImageDrawable(
+ getActivity().getDrawable(R.drawable.ic_play_arrow_white_24dp));
+ } else {
+ mPlayPause.setImageDrawable(getActivity().getDrawable(R.drawable.ic_pause_white_24dp));
+ }
+
+ mSkipPrevious.setEnabled((state.getActions() & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0);
+ mSkipNext.setEnabled((state.getActions() & PlaybackState.ACTION_SKIP_TO_NEXT) != 0);
+
+ LogHelper.d(TAG, "Queue From MediaController *** Title " +
+ mMediaController.getQueueTitle() + "\n: Queue: " + mMediaController.getQueue() +
+ "\n Metadata " + mMediaController.getMetadata());
+ }
+
+ private View.OnClickListener mButtonListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final int state = mPlaybackState == null ?
+ PlaybackState.STATE_NONE : mPlaybackState.getState();
+ switch (v.getId()) {
+ case R.id.play_pause:
+ LogHelper.d(TAG, "Play button pressed, in state " + state);
+ if (state == PlaybackState.STATE_PAUSED ||
+ state == PlaybackState.STATE_STOPPED ||
+ state == PlaybackState.STATE_NONE) {
+ playMedia();
+ } else if (state == PlaybackState.STATE_PLAYING) {
+ pauseMedia();
+ }
+ break;
+ case R.id.skip_previous:
+ LogHelper.d(TAG, "Start button pressed, in state " + state);
+ skipToPrevious();
+ break;
+ case R.id.skip_next:
+ skipToNext();
+ break;
+ }
+
+ }
+ };
+
+ private void playMedia() {
+ if (mTransportControls != null) {
+ mTransportControls.play();
+ }
+ }
+
+ private void pauseMedia() {
+ if (mTransportControls != null) {
+ mTransportControls.pause();
+ }
+ }
+
+ private void skipToPrevious() {
+ if (mTransportControls != null) {
+ mTransportControls.skipToPrevious();
+ }
+ }
+
+ private void skipToNext() {
+ if (mTransportControls != null) {
+ mTransportControls.skipToNext();
+ }
+ }
+}
diff --git a/MusicDemo/src/main/java/com/example/android/musicservicedemo/utils/QueueHelper.java b/MusicDemo/src/main/java/com/example/android/musicservicedemo/utils/QueueHelper.java
index 4dc7a96..9a510fb 100644
--- a/MusicDemo/src/main/java/com/example/android/musicservicedemo/utils/QueueHelper.java
+++ b/MusicDemo/src/main/java/com/example/android/musicservicedemo/utils/QueueHelper.java
@@ -126,4 +126,4 @@ public class QueueHelper {
public static final boolean isIndexPlayable(int index, List<MediaSession.QueueItem> queue) {
return (queue != null && index >= 0 && index < queue.size());
}
-} \ No newline at end of file
+}
diff --git a/MusicDemo/src/main/res/drawable-xhdpi/ic_equalizer_white_24dp.png b/MusicDemo/src/main/res/drawable-xhdpi/ic_equalizer_white_24dp.png
new file mode 100644
index 0000000..dbba844
--- /dev/null
+++ b/MusicDemo/src/main/res/drawable-xhdpi/ic_equalizer_white_24dp.png
Binary files differ
diff --git a/MusicDemo/src/main/res/drawable-xxhdpi/ic_equalizer_white_24dp.png b/MusicDemo/src/main/res/drawable-xxhdpi/ic_equalizer_white_24dp.png
new file mode 100644
index 0000000..b82a8d9
--- /dev/null
+++ b/MusicDemo/src/main/res/drawable-xxhdpi/ic_equalizer_white_24dp.png
Binary files differ
diff --git a/MusicDemo/src/main/res/layout/activity_player.xml b/MusicDemo/src/main/res/layout/activity_player.xml
new file mode 100644
index 0000000..21cdbbd
--- /dev/null
+++ b/MusicDemo/src/main/res/layout/activity_player.xml
@@ -0,0 +1,22 @@
+<!--
+ Copyright (C) 2014 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.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MusicPlayerActivity"
+ tools:ignore="MergeRootFrame" />
diff --git a/MusicDemo/src/main/res/layout/fragment_list.xml b/MusicDemo/src/main/res/layout/fragment_list.xml
new file mode 100644
index 0000000..a40240f
--- /dev/null
+++ b/MusicDemo/src/main/res/layout/fragment_list.xml
@@ -0,0 +1,60 @@
+<!--
+ Copyright (C) 2014 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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="16dp">
+
+ <LinearLayout
+ android:id="@+id/controls"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageButton
+ android:id="@+id/skip_previous"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:src="@drawable/ic_skip_previous_white_24dp"
+ android:contentDescription="@string/skip_previous"/>
+
+ <ImageButton
+ android:id="@+id/play_pause"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:src="@drawable/ic_play_arrow_white_24dp"
+ android:contentDescription="@string/play_pause"/>
+
+ <ImageButton
+ android:id="@+id/skip_next"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:src="@drawable/ic_skip_next_white_24dp"
+ android:contentDescription="@string/skip_next"/>
+
+ </LinearLayout>
+
+ <ListView
+ android:id="@+id/list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ </ListView>
+
+</LinearLayout>
diff --git a/MusicDemo/src/main/res/layout/list_item.xml b/MusicDemo/src/main/res/layout/list_item.xml
new file mode 100644
index 0000000..729628f
--- /dev/null
+++ b/MusicDemo/src/main/res/layout/list_item.xml
@@ -0,0 +1,55 @@
+<!--
+ Copyright (C) 2014 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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:listPreferredItemHeight"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/play_eq"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:contentDescription="@string/play_item"
+ android:src="@drawable/ic_play_arrow_white_24dp"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:mode="twoLine"
+ android:padding="4dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/margin_text_view"
+ android:layout_marginTop="@dimen/margin_text_view"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/margin_text_view"
+ android:layout_marginTop="@dimen/margin_text_view"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/MusicDemo/src/main/res/values-v21/styles.xml b/MusicDemo/src/main/res/values-v21/styles.xml
index 602ce1c..21bb211 100644
--- a/MusicDemo/src/main/res/values-v21/styles.xml
+++ b/MusicDemo/src/main/res/values-v21/styles.xml
@@ -16,7 +16,7 @@
-->
<resources>
- <style name="AppBaseTheme" parent="android:Theme.Light">
+ <style name="AppBaseTheme" parent="android:Theme.Material">
<!-- colorPrimary is used for Notification icon and bottom facet bar icons
and overflow actions -->
<item name="android:colorPrimary">#ffff5722</item>
diff --git a/MusicDemo/src/main/res/values/dimens.xml b/MusicDemo/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..f905809
--- /dev/null
+++ b/MusicDemo/src/main/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+ -->
+<resources>
+ <dimen name="margin_text_view">6dip</dimen>
+</resources>
diff --git a/MusicDemo/src/main/res/values/strings.xml b/MusicDemo/src/main/res/values/strings.xml
index 82e07b0..7a012e8 100644
--- a/MusicDemo/src/main/res/values/strings.xml
+++ b/MusicDemo/src/main/res/values/strings.xml
@@ -24,5 +24,10 @@
<string name="browse_musics_by_genre_subtitle">%1$s songs</string>
<string name="random_queue_title">Random music</string>
<string name="error_cannot_skip">Cannot skip</string>
+ <string name="error_loading_media">Error Loading Media</string>
+ <string name="play_item">Play item</string>
+ <string name="skip_previous">Skip to previous</string>
+ <string name="play_pause">play or pause</string>
+ <string name="skip_next">Skip to next</string>
</resources>