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