1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.example.android.mediabrowserservice;
17 
18 import android.app.Fragment;
19 import android.content.ComponentName;
20 import android.media.browse.MediaBrowser;
21 import android.media.session.MediaController;
22 import android.media.session.MediaSession;
23 import android.media.session.PlaybackState;
24 import android.os.Bundle;
25 import android.view.LayoutInflater;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.widget.AdapterView;
29 import android.widget.ImageButton;
30 import android.widget.ListView;
31 
32 import com.example.android.mediabrowserservice.utils.LogHelper;
33 
34 import java.util.List;
35 
36 /**
37  * A class that shows the Media Queue to the user.
38  */
39 public class QueueFragment extends Fragment {
40 
41     private static final String TAG = LogHelper.makeLogTag(QueueFragment.class.getSimpleName());
42 
43     private ImageButton mSkipNext;
44     private ImageButton mSkipPrevious;
45     private ImageButton mPlayPause;
46 
47     private MediaBrowser mMediaBrowser;
48     private MediaController.TransportControls mTransportControls;
49     private MediaController mMediaController;
50     private PlaybackState mPlaybackState;
51 
52     private QueueAdapter mQueueAdapter;
53 
54     private MediaBrowser.ConnectionCallback mConnectionCallback =
55             new MediaBrowser.ConnectionCallback() {
56         @Override
57         public void onConnected() {
58             LogHelper.d(TAG, "onConnected: session token ", mMediaBrowser.getSessionToken());
59 
60             if (mMediaBrowser.getSessionToken() == null) {
61                 throw new IllegalArgumentException("No Session token");
62             }
63 
64             mMediaController = new MediaController(getActivity(),
65                     mMediaBrowser.getSessionToken());
66             mTransportControls = mMediaController.getTransportControls();
67             mMediaController.registerCallback(mSessionCallback);
68 
69             getActivity().setMediaController(mMediaController);
70             mPlaybackState = mMediaController.getPlaybackState();
71 
72             List<MediaSession.QueueItem> queue = mMediaController.getQueue();
73             if (queue != null) {
74                 mQueueAdapter.clear();
75                 mQueueAdapter.notifyDataSetInvalidated();
76                 mQueueAdapter.addAll(queue);
77                 mQueueAdapter.notifyDataSetChanged();
78             }
79             onPlaybackStateChanged(mPlaybackState);
80         }
81 
82         @Override
83         public void onConnectionFailed() {
84             LogHelper.d(TAG, "onConnectionFailed");
85         }
86 
87         @Override
88         public void onConnectionSuspended() {
89             LogHelper.d(TAG, "onConnectionSuspended");
90             mMediaController.unregisterCallback(mSessionCallback);
91             mTransportControls = null;
92             mMediaController = null;
93             getActivity().setMediaController(null);
94         }
95     };
96 
97     // Receive callbacks from the MediaController. Here we update our state such as which queue
98     // is being shown, the current title and description and the PlaybackState.
99     private MediaController.Callback mSessionCallback = new MediaController.Callback() {
100 
101         @Override
102         public void onSessionDestroyed() {
103             LogHelper.d(TAG, "Session destroyed. Need to fetch a new Media Session");
104         }
105 
106         @Override
107         public void onPlaybackStateChanged(PlaybackState state) {
108             if (state == null) {
109                 return;
110             }
111             LogHelper.d(TAG, "Received playback state change to state ", state.getState());
112             mPlaybackState = state;
113             QueueFragment.this.onPlaybackStateChanged(state);
114         }
115 
116         @Override
117         public void onQueueChanged(List<MediaSession.QueueItem> queue) {
118             LogHelper.d(TAG, "onQueueChanged ", queue);
119             if (queue != null) {
120                 mQueueAdapter.clear();
121                 mQueueAdapter.notifyDataSetInvalidated();
122                 mQueueAdapter.addAll(queue);
123                 mQueueAdapter.notifyDataSetChanged();
124             }
125         }
126     };
127 
newInstance()128     public static QueueFragment newInstance() {
129         return new QueueFragment();
130     }
131 
132     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)133     public View onCreateView(LayoutInflater inflater, ViewGroup container,
134                              Bundle savedInstanceState) {
135         View rootView = inflater.inflate(R.layout.fragment_list, container, false);
136 
137         mSkipPrevious = (ImageButton) rootView.findViewById(R.id.skip_previous);
138         mSkipPrevious.setEnabled(false);
139         mSkipPrevious.setOnClickListener(mButtonListener);
140 
141         mSkipNext = (ImageButton) rootView.findViewById(R.id.skip_next);
142         mSkipNext.setEnabled(false);
143         mSkipNext.setOnClickListener(mButtonListener);
144 
145         mPlayPause = (ImageButton) rootView.findViewById(R.id.play_pause);
146         mPlayPause.setEnabled(true);
147         mPlayPause.setOnClickListener(mButtonListener);
148 
149         mQueueAdapter = new QueueAdapter(getActivity());
150 
151         ListView mListView = (ListView) rootView.findViewById(R.id.list_view);
152         mListView.setAdapter(mQueueAdapter);
153         mListView.setFocusable(true);
154         mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
155             @Override
156             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
157                 MediaSession.QueueItem item = mQueueAdapter.getItem(position);
158                 mTransportControls.skipToQueueItem(item.getQueueId());
159             }
160         });
161 
162         mMediaBrowser = new MediaBrowser(getActivity(),
163                 new ComponentName(getActivity(), MusicService.class),
164                 mConnectionCallback, null);
165 
166         return rootView;
167     }
168 
169     @Override
onResume()170     public void onResume() {
171         super.onResume();
172         if (mMediaBrowser != null) {
173             mMediaBrowser.connect();
174         }
175     }
176 
177     @Override
onPause()178     public void onPause() {
179         super.onPause();
180         if (mMediaController != null) {
181             mMediaController.unregisterCallback(mSessionCallback);
182         }
183         if (mMediaBrowser != null) {
184             mMediaBrowser.disconnect();
185         }
186     }
187 
188 
onPlaybackStateChanged(PlaybackState state)189     private void onPlaybackStateChanged(PlaybackState state) {
190         LogHelper.d(TAG, "onPlaybackStateChanged ", state);
191         if (state == null) {
192             return;
193         }
194         mQueueAdapter.setActiveQueueItemId(state.getActiveQueueItemId());
195         mQueueAdapter.notifyDataSetChanged();
196         boolean enablePlay = false;
197         StringBuilder statusBuilder = new StringBuilder();
198         switch (state.getState()) {
199             case PlaybackState.STATE_PLAYING:
200                 statusBuilder.append("playing");
201                 enablePlay = false;
202                 break;
203             case PlaybackState.STATE_PAUSED:
204                 statusBuilder.append("paused");
205                 enablePlay = true;
206                 break;
207             case PlaybackState.STATE_STOPPED:
208                 statusBuilder.append("ended");
209                 enablePlay = true;
210                 break;
211             case PlaybackState.STATE_ERROR:
212                 statusBuilder.append("error: ").append(state.getErrorMessage());
213                 break;
214             case PlaybackState.STATE_BUFFERING:
215                 statusBuilder.append("buffering");
216                 break;
217             case PlaybackState.STATE_NONE:
218                 statusBuilder.append("none");
219                 enablePlay = false;
220                 break;
221             case PlaybackState.STATE_CONNECTING:
222                 statusBuilder.append("connecting");
223                 break;
224             default:
225                 statusBuilder.append(mPlaybackState);
226         }
227         statusBuilder.append(" -- At position: ").append(state.getPosition());
228         LogHelper.d(TAG, statusBuilder.toString());
229 
230         if (enablePlay) {
231             mPlayPause.setImageDrawable(
232                     getActivity().getDrawable(R.drawable.ic_play_arrow_white_24dp));
233         } else {
234             mPlayPause.setImageDrawable(getActivity().getDrawable(R.drawable.ic_pause_white_24dp));
235         }
236 
237         mSkipPrevious.setEnabled((state.getActions() & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0);
238         mSkipNext.setEnabled((state.getActions() & PlaybackState.ACTION_SKIP_TO_NEXT) != 0);
239 
240         LogHelper.d(TAG, "Queue From MediaController *** Title " +
241                 mMediaController.getQueueTitle() + "\n: Queue: " + mMediaController.getQueue() +
242                 "\n Metadata " + mMediaController.getMetadata());
243     }
244 
245     private View.OnClickListener mButtonListener = new View.OnClickListener() {
246         @Override
247         public void onClick(View v) {
248             final int state = mPlaybackState == null ?
249                     PlaybackState.STATE_NONE : mPlaybackState.getState();
250             switch (v.getId()) {
251                 case R.id.play_pause:
252                     LogHelper.d(TAG, "Play button pressed, in state " + state);
253                     if (state == PlaybackState.STATE_PAUSED ||
254                             state == PlaybackState.STATE_STOPPED ||
255                             state == PlaybackState.STATE_NONE) {
256                         playMedia();
257                     } else if (state == PlaybackState.STATE_PLAYING) {
258                         pauseMedia();
259                     }
260                     break;
261                 case R.id.skip_previous:
262                     LogHelper.d(TAG, "Start button pressed, in state " + state);
263                     skipToPrevious();
264                     break;
265                 case R.id.skip_next:
266                     skipToNext();
267                     break;
268             }
269         }
270     };
271 
playMedia()272     private void playMedia() {
273         if (mTransportControls != null) {
274             mTransportControls.play();
275         }
276     }
277 
pauseMedia()278     private void pauseMedia() {
279         if (mTransportControls != null) {
280             mTransportControls.pause();
281         }
282     }
283 
skipToPrevious()284     private void skipToPrevious() {
285         if (mTransportControls != null) {
286             mTransportControls.skipToPrevious();
287         }
288     }
289 
skipToNext()290     private void skipToNext() {
291         if (mTransportControls != null) {
292             mTransportControls.skipToNext();
293         }
294     }
295 }
296