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 */ 16package com.example.android.supportv4.media; 17 18import android.app.Fragment; 19import android.content.ComponentName; 20import android.content.Context; 21import android.os.Bundle; 22import android.os.RemoteException; 23import android.support.v4.media.MediaBrowserCompat; 24import android.support.v4.media.session.MediaControllerCompat; 25import android.util.Log; 26import android.view.LayoutInflater; 27import android.view.View; 28import android.view.ViewGroup; 29import android.widget.AbsListView; 30import android.widget.AdapterView; 31import android.widget.ArrayAdapter; 32import android.widget.ImageView; 33import android.widget.ListView; 34import android.widget.TextView; 35import android.widget.Toast; 36 37import com.example.android.supportv4.R; 38 39import java.util.ArrayList; 40import java.util.List; 41 42/** 43 * A Fragment that lists all the various browsable queues available 44 * from a {@link android.service.media.MediaBrowserService}. 45 * <p/> 46 * It uses a {@link MediaBrowserCompat} to connect to the {@link MediaBrowserServiceSupport}. 47 * Once connected, the fragment subscribes to get all the children. All 48 * {@link MediaBrowserCompat.MediaItem} objects that can be browsed are shown in a ListView. 49 */ 50public class BrowseFragment extends Fragment { 51 52 private static final String TAG = "BrowseFragment"; 53 54 public static final String ARG_MEDIA_ID = "media_id"; 55 56 // The number of media items per page. 57 private static final int PAGE_SIZE = 6; 58 59 public static interface FragmentDataHelper { 60 void onMediaItemSelected(MediaBrowserCompat.MediaItem item); 61 } 62 63 // The mediaId to be used for subscribing for children using the MediaBrowser. 64 private String mMediaId; 65 private final List<MediaBrowserCompat.MediaItem> mMediaItems = new ArrayList<>(); 66 67 private boolean mCanLoadNewPage; 68 private MediaBrowserCompat mMediaBrowser; 69 private BrowseAdapter mBrowserAdapter; 70 71 private MediaBrowserCompat.SubscriptionCallback mSubscriptionCallback = 72 new MediaBrowserCompat.SubscriptionCallback() { 73 @Override 74 public void onChildrenLoaded(String parentId, List<MediaBrowserCompat.MediaItem> children, 75 Bundle options) { 76 int page = options.getInt(MediaBrowserCompat.EXTRA_PAGE, -1); 77 int pageSize = options.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1); 78 if (page < 0 || pageSize != PAGE_SIZE || children == null 79 || children.size() > PAGE_SIZE) { 80 return; 81 } 82 83 int itemIndex = page * PAGE_SIZE; 84 if (itemIndex >= mMediaItems.size()) { 85 if (children.size() == 0) { 86 return; 87 } 88 // An additional page is loaded. 89 mMediaItems.addAll(children); 90 } else { 91 // An existing page is replaced by the newly loaded page. 92 for (MediaBrowserCompat.MediaItem item : children) { 93 if (itemIndex < mMediaItems.size()) { 94 mMediaItems.set(itemIndex, item); 95 } else { 96 mMediaItems.add(item); 97 } 98 itemIndex++; 99 } 100 101 // If the newly loaded page contains less than {PAGE_SIZE} items, 102 // then this page should be the last page. 103 if (children.size() < PAGE_SIZE) { 104 while (mMediaItems.size() > itemIndex) { 105 mMediaItems.remove(mMediaItems.size() - 1); 106 } 107 } 108 } 109 mBrowserAdapter.notifyDataSetChanged(); 110 mCanLoadNewPage = true; 111 } 112 113 @Override 114 public void onChildrenLoaded(String parentId, List<MediaBrowserCompat.MediaItem> children) { 115 Log.d(TAG, "onChildrenLoaded: parentId=" + parentId); 116 mMediaItems.clear(); 117 mMediaItems.addAll(children); 118 mBrowserAdapter.notifyDataSetChanged(); 119 } 120 121 @Override 122 public void onError(String id) { 123 Toast.makeText(getActivity(), R.string.error_loading_media, 124 Toast.LENGTH_LONG).show(); 125 } 126 }; 127 128 private MediaBrowserCompat.ConnectionCallback mConnectionCallback = 129 new MediaBrowserCompat.ConnectionCallback() { 130 @Override 131 public void onConnected() { 132 Log.d(TAG, "onConnected: session token " + mMediaBrowser.getSessionToken()); 133 134 if (mMediaBrowser.getSessionToken() == null) { 135 throw new IllegalArgumentException("No Session token"); 136 } 137 MediaControllerCompat mediaController = null; 138 try { 139 mediaController = new MediaControllerCompat(getActivity(), 140 mMediaBrowser.getSessionToken()); 141 } catch (RemoteException e) { 142 Log.e(TAG, "Failed to create MediaController.", e); 143 } 144 ((MediaBrowserSupport) getActivity()).setMediaController(mediaController); 145 146 if (mMediaId == null) { 147 mMediaId = mMediaBrowser.getRoot(); 148 } 149 150 if (mMediaItems.size() == 0) { 151 loadPage(0); 152 } 153 } 154 155 @Override 156 public void onConnectionFailed() { 157 Log.d(TAG, "onConnectionFailed"); 158 } 159 160 @Override 161 public void onConnectionSuspended() { 162 Log.d(TAG, "onConnectionSuspended"); 163 getActivity().setMediaController(null); 164 } 165 }; 166 167 public static BrowseFragment newInstance(String mediaId) { 168 Bundle args = new Bundle(); 169 args.putString(ARG_MEDIA_ID, mediaId); 170 BrowseFragment fragment = new BrowseFragment(); 171 fragment.setArguments(args); 172 return fragment; 173 } 174 175 @Override 176 public View onCreateView(LayoutInflater inflater, ViewGroup container, 177 Bundle savedInstanceState) { 178 View rootView = inflater.inflate(R.layout.fragment_list, container, false); 179 180 mBrowserAdapter = new BrowseAdapter(getActivity(), mMediaItems); 181 182 View controls = rootView.findViewById(R.id.controls); 183 controls.setVisibility(View.GONE); 184 185 ListView listView = (ListView) rootView.findViewById(R.id.list_view); 186 listView.setAdapter(mBrowserAdapter); 187 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 188 @Override 189 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 190 MediaBrowserCompat.MediaItem item = mBrowserAdapter.getItem(position); 191 try { 192 FragmentDataHelper listener = (FragmentDataHelper) getActivity(); 193 listener.onMediaItemSelected(item); 194 } catch (ClassCastException ex) { 195 Log.e(TAG, "Exception trying to cast to FragmentDataHelper", ex); 196 } 197 } 198 }); 199 200 Bundle args = getArguments(); 201 mMediaId = args.getString(ARG_MEDIA_ID, null); 202 203 mMediaBrowser = new MediaBrowserCompat(getActivity(), 204 new ComponentName(getActivity(), MediaBrowserServiceSupport.class), 205 mConnectionCallback, null); 206 207 listView.setOnScrollListener(new AbsListView.OnScrollListener() { 208 @Override 209 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, 210 int totalItemCount) { 211 if (mCanLoadNewPage && firstVisibleItem + visibleItemCount == totalItemCount) { 212 mCanLoadNewPage = false; 213 loadPage((mMediaItems.size() + PAGE_SIZE - 1) / PAGE_SIZE); 214 } 215 } 216 217 @Override 218 public void onScrollStateChanged(AbsListView view, int scrollState) { 219 // Do nothing 220 } 221 }); 222 223 return rootView; 224 } 225 226 @Override 227 public void onStart() { 228 super.onStart(); 229 mMediaBrowser.connect(); 230 } 231 232 @Override 233 public void onStop() { 234 super.onStop(); 235 mMediaBrowser.disconnect(); 236 } 237 238 private void loadPage(int page) { 239 Bundle options = new Bundle(); 240 options.putInt(MediaBrowserCompat.EXTRA_PAGE, page); 241 options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, PAGE_SIZE); 242 mMediaBrowser.subscribe(mMediaId, options, mSubscriptionCallback); 243 } 244 245 // An adapter for showing the list of browsed MediaItem objects 246 private static class BrowseAdapter extends ArrayAdapter<MediaBrowserCompat.MediaItem> { 247 248 public BrowseAdapter(Context context, List<MediaBrowserCompat.MediaItem> mediaItems) { 249 super(context, R.layout.media_list_item, mediaItems); 250 } 251 252 static class ViewHolder { 253 ImageView mImageView; 254 TextView mTitleView; 255 TextView mDescriptionView; 256 } 257 258 @Override 259 public View getView(int position, View convertView, ViewGroup parent) { 260 261 ViewHolder holder; 262 263 if (convertView == null) { 264 convertView = LayoutInflater.from(getContext()) 265 .inflate(R.layout.media_list_item, parent, false); 266 holder = new ViewHolder(); 267 holder.mImageView = (ImageView) convertView.findViewById(R.id.play_eq); 268 holder.mImageView.setVisibility(View.GONE); 269 holder.mTitleView = (TextView) convertView.findViewById(R.id.title); 270 holder.mDescriptionView = (TextView) convertView.findViewById(R.id.description); 271 convertView.setTag(holder); 272 } else { 273 holder = (ViewHolder) convertView.getTag(); 274 } 275 276 MediaBrowserCompat.MediaItem item = getItem(position); 277 holder.mTitleView.setText(item.getDescription().getTitle()); 278 holder.mDescriptionView.setText(item.getDescription().getDescription()); 279 if (item.isPlayable()) { 280 holder.mImageView.setImageDrawable(getContext().getResources() 281 .getDrawable(R.drawable.ic_play_arrow_white_24dp)); 282 holder.mImageView.setVisibility(View.VISIBLE); 283 } else { 284 holder.mImageView.setVisibility(View.GONE); 285 } 286 return convertView; 287 } 288 } 289}