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