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