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.content.ComponentName; 19 import android.content.Context; 20 import android.os.Bundle; 21 import android.os.RemoteException; 22 import android.support.annotation.NonNull; 23 import android.support.annotation.Nullable; 24 import android.support.v4.app.Fragment; 25 import android.support.v4.media.MediaBrowserCompat; 26 import android.support.v4.media.MediaMetadataCompat; 27 import android.support.v4.media.session.MediaControllerCompat; 28 import android.support.v4.media.session.PlaybackStateCompat; 29 import android.util.Log; 30 import android.view.LayoutInflater; 31 import android.view.View; 32 import android.view.ViewGroup; 33 import android.widget.AdapterView; 34 import android.widget.ArrayAdapter; 35 import android.widget.ImageView; 36 import android.widget.ListView; 37 import android.widget.TextView; 38 import android.widget.Toast; 39 40 import java.util.ArrayList; 41 import java.util.List; 42 43 /** 44 * A Fragment that lists all the various browsable queues available 45 * from a {@link android.service.media.MediaBrowserService}. 46 * <p/> 47 * It uses a {@link MediaBrowserCompat} to connect to the {@link MusicService}. Once connected, 48 * the fragment subscribes to get all the children. All {@link MediaBrowserCompat.MediaItem}'s 49 * that can be browsed are shown in a ListView. 50 */ 51 public class BrowseFragment extends Fragment { 52 53 private static final String TAG = BrowseFragment.class.getSimpleName(); 54 55 public static final String ARG_MEDIA_ID = "media_id"; 56 57 /** 58 * Interface between BrowseFragment and MusicPlayerActivity. 59 */ 60 public interface FragmentDataHelper { 61 void onMediaItemSelected(MediaBrowserCompat.MediaItem item, boolean isPlaying); 62 } 63 64 // The mediaId to be used for subscribing for children using the MediaBrowser. 65 private String mMediaId; 66 67 private MediaBrowserCompat mMediaBrowser; 68 private BrowseAdapter mBrowserAdapter; 69 70 private MediaBrowserCompat.SubscriptionCallback mSubscriptionCallback = 71 new MediaBrowserCompat.SubscriptionCallback() { 72 73 @Override 74 public void onChildrenLoaded(String parentId, 75 List<MediaBrowserCompat.MediaItem> children) { 76 mBrowserAdapter.clear(); 77 mBrowserAdapter.notifyDataSetInvalidated(); 78 for (MediaBrowserCompat.MediaItem item : children) { 79 mBrowserAdapter.add(item); 80 } 81 mBrowserAdapter.notifyDataSetChanged(); 82 } 83 84 @Override 85 public void onError(String id) { 86 Toast.makeText(getActivity(), R.string.error_loading_media, 87 Toast.LENGTH_LONG).show(); 88 } 89 }; 90 91 private MediaBrowserCompat.ConnectionCallback mConnectionCallback = 92 new MediaBrowserCompat.ConnectionCallback() { 93 @Override 94 public void onConnected() { 95 Log.d(TAG, "onConnected: session token " + mMediaBrowser.getSessionToken()); 96 97 if (mMediaId == null) { 98 mMediaId = mMediaBrowser.getRoot(); 99 } 100 mMediaBrowser.subscribe(mMediaId, mSubscriptionCallback); 101 try { 102 MediaControllerCompat mediaController = 103 new MediaControllerCompat(getActivity(), 104 mMediaBrowser.getSessionToken()); 105 MediaControllerCompat.setMediaController(getActivity(), mediaController); 106 107 // Register a Callback to stay in sync 108 mediaController.registerCallback(mControllerCallback); 109 } catch (RemoteException e) { 110 Log.e(TAG, "Failed to connect to MediaController", e); 111 } 112 } 113 114 @Override 115 public void onConnectionFailed() { 116 Log.e(TAG, "onConnectionFailed"); 117 } 118 119 @Override 120 public void onConnectionSuspended() { 121 Log.d(TAG, "onConnectionSuspended"); 122 MediaControllerCompat mediaController = MediaControllerCompat 123 .getMediaController(getActivity()); 124 if (mediaController != null) { 125 mediaController.unregisterCallback(mControllerCallback); 126 MediaControllerCompat.setMediaController(getActivity(), null); 127 } 128 } 129 }; 130 131 private MediaControllerCompat.Callback mControllerCallback = 132 new MediaControllerCompat.Callback() { 133 @Override 134 public void onMetadataChanged(MediaMetadataCompat metadata) { 135 if (metadata != null) { 136 mBrowserAdapter.setCurrentMediaMetadata(metadata); 137 } 138 } 139 140 @Override 141 public void onPlaybackStateChanged(PlaybackStateCompat state) { 142 mBrowserAdapter.setPlaybackState(state); 143 mBrowserAdapter.notifyDataSetChanged(); 144 } 145 }; 146 147 public static BrowseFragment newInstance(String mediaId) { 148 Bundle args = new Bundle(); 149 args.putString(ARG_MEDIA_ID, mediaId); 150 BrowseFragment fragment = new BrowseFragment(); 151 fragment.setArguments(args); 152 return fragment; 153 } 154 155 @Override 156 public View onCreateView(LayoutInflater inflater, ViewGroup container, 157 Bundle savedInstanceState) { 158 View rootView = inflater.inflate(R.layout.fragment_list, container, false); 159 160 mBrowserAdapter = new BrowseAdapter(getActivity()); 161 162 ListView listView = (ListView) rootView.findViewById(R.id.list_view); 163 listView.setAdapter(mBrowserAdapter); 164 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 165 @Override 166 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 167 MediaBrowserCompat.MediaItem item = mBrowserAdapter.getItem(position); 168 boolean isPlaying = item.getMediaId().equals(mBrowserAdapter.getPlayingMediaId()); 169 try { 170 FragmentDataHelper listener = (FragmentDataHelper) getActivity(); 171 listener.onMediaItemSelected(item, isPlaying); 172 } catch (ClassCastException ex) { 173 Log.e(TAG, "Exception trying to cast to FragmentDataHelper", ex); 174 } 175 } 176 }); 177 178 Bundle args = getArguments(); 179 mMediaId = args.getString(ARG_MEDIA_ID, null); 180 181 mMediaBrowser = new MediaBrowserCompat(getActivity(), 182 new ComponentName(getActivity(), MusicService.class), 183 mConnectionCallback, null); 184 185 return rootView; 186 } 187 188 @Override 189 public void onStart() { 190 super.onStart(); 191 mMediaBrowser.connect(); 192 } 193 194 @Override 195 public void onStop() { 196 super.onStop(); 197 mMediaBrowser.disconnect(); 198 } 199 200 // An adapter for showing the list of browsed MediaItem's 201 private static class BrowseAdapter extends ArrayAdapter<MediaBrowserCompat.MediaItem> { 202 private String mCurrentMediaId; 203 private PlaybackStateCompat mPlaybackState; 204 205 public BrowseAdapter(Context context) { 206 super(context, R.layout.media_list_item, new ArrayList<MediaBrowserCompat.MediaItem>()); 207 } 208 209 @Nullable 210 public String getPlayingMediaId() { 211 boolean isPlaying = mPlaybackState != null 212 && mPlaybackState.getState() == PlaybackStateCompat.STATE_PLAYING; 213 return isPlaying ? mCurrentMediaId : null; 214 } 215 216 private void setCurrentMediaMetadata(MediaMetadataCompat mediaMetadata) { 217 mCurrentMediaId = mediaMetadata != null 218 ? mediaMetadata.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID) 219 : null; 220 } 221 222 private void setPlaybackState(PlaybackStateCompat playbackState) { 223 mPlaybackState = playbackState; 224 } 225 226 static class ViewHolder { 227 ImageView mImageView; 228 TextView mTitleView; 229 TextView mDescriptionView; 230 } 231 232 @NonNull 233 @Override 234 public View getView(int position, View convertView, @NonNull ViewGroup parent) { 235 236 ViewHolder holder; 237 238 if (convertView == null) { 239 convertView = LayoutInflater.from(getContext()) 240 .inflate(R.layout.media_list_item, parent, false); 241 holder = new ViewHolder(); 242 holder.mImageView = (ImageView) convertView.findViewById(R.id.play_eq); 243 holder.mImageView.setVisibility(View.GONE); 244 holder.mTitleView = (TextView) convertView.findViewById(R.id.title); 245 holder.mDescriptionView = (TextView) convertView.findViewById(R.id.description); 246 convertView.setTag(holder); 247 } else { 248 holder = (ViewHolder) convertView.getTag(); 249 } 250 251 MediaBrowserCompat.MediaItem item = getItem(position); 252 holder.mTitleView.setText(item.getDescription().getTitle()); 253 holder.mDescriptionView.setText(item.getDescription().getDescription()); 254 if (item.isPlayable()) { 255 int playRes = item.getMediaId().equals(getPlayingMediaId()) 256 ? R.drawable.ic_equalizer_white_24dp 257 : R.drawable.ic_play_arrow_white_24dp; 258 holder.mImageView.setImageDrawable(getContext().getResources() 259 .getDrawable(playRes)); 260 holder.mImageView.setVisibility(View.VISIBLE); 261 } 262 return convertView; 263 } 264 265 } 266 } 267