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