/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.android.supportv4.media; import android.app.Fragment; import android.content.ComponentName; import android.content.Context; import android.os.Bundle; import android.os.RemoteException; import android.support.v4.content.ContextCompat; import android.support.v4.media.MediaBrowserCompat; import android.support.v4.media.session.MediaControllerCompat; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.example.android.supportv4.R; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * A Fragment that lists all the various browsable queues available * from a {@link android.service.media.MediaBrowserService}. * <p/> * It uses a {@link MediaBrowserCompat} to connect to the {@link MediaBrowserServiceSupport}. * Once connected, the fragment subscribes to get all the children. All * {@link MediaBrowserCompat.MediaItem} objects that can be browsed are shown in a ListView. */ public class BrowseFragment extends Fragment { private static final String TAG = "BrowseFragment"; public static final String ARG_MEDIA_ID = "media_id"; // The number of media items per page. private static final int PAGE_SIZE = 6; public static interface FragmentDataHelper { void onMediaItemSelected(MediaBrowserCompat.MediaItem item); } // The mediaId to be used for subscribing for children using the MediaBrowser. private String mMediaId; private final List<MediaBrowserCompat.MediaItem> mMediaItems = new ArrayList<>(); private boolean mCanLoadNewPage; private final Set<Integer> mSubscribedPages = new HashSet<Integer>(); private MediaBrowserCompat mMediaBrowser; private BrowseAdapter mBrowserAdapter; private MediaBrowserCompat.SubscriptionCallback mSubscriptionCallback = new MediaBrowserCompat.SubscriptionCallback() { @Override public void onChildrenLoaded(String parentId, List<MediaBrowserCompat.MediaItem> children, Bundle options) { int page = options.getInt(MediaBrowserCompat.EXTRA_PAGE, -1); int pageSize = options.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1); if (page < 0 || pageSize != PAGE_SIZE || children == null || children.size() > PAGE_SIZE) { return; } int itemIndex = page * PAGE_SIZE; if (itemIndex >= mMediaItems.size()) { if (children.size() == 0) { return; } // An additional page is loaded. mMediaItems.addAll(children); } else { // An existing page is replaced by the newly loaded page. for (MediaBrowserCompat.MediaItem item : children) { if (itemIndex < mMediaItems.size()) { mMediaItems.set(itemIndex, item); } else { mMediaItems.add(item); } itemIndex++; } // If the newly loaded page contains less than {PAGE_SIZE} items, // then this page should be the last page. if (children.size() < PAGE_SIZE) { while (mMediaItems.size() > itemIndex) { mMediaItems.remove(mMediaItems.size() - 1); } } } mBrowserAdapter.notifyDataSetChanged(); mCanLoadNewPage = true; } @Override public void onChildrenLoaded(String parentId, List<MediaBrowserCompat.MediaItem> children) { Log.d(TAG, "onChildrenLoaded: parentId=" + parentId); mMediaItems.clear(); mMediaItems.addAll(children); mBrowserAdapter.notifyDataSetChanged(); } @Override public void onError(String id) { Toast.makeText(getActivity(), R.string.error_loading_media, Toast.LENGTH_LONG).show(); } }; private MediaBrowserCompat.ConnectionCallback mConnectionCallback = new MediaBrowserCompat.ConnectionCallback() { @Override public void onConnected() { Log.d(TAG, "onConnected: session token " + mMediaBrowser.getSessionToken()); if (mMediaBrowser.getSessionToken() == null) { throw new IllegalArgumentException("No Session token"); } MediaControllerCompat mediaController = null; try { mediaController = new MediaControllerCompat(getActivity(), mMediaBrowser.getSessionToken()); } catch (RemoteException e) { Log.e(TAG, "Failed to create MediaController.", e); } ((MediaBrowserSupport) getActivity()).setMediaController(mediaController); if (mMediaId == null) { mMediaId = mMediaBrowser.getRoot(); } if (mMediaItems.size() == 0) { loadPage(0); } } @Override public void onConnectionFailed() { Log.d(TAG, "onConnectionFailed"); } @Override public void onConnectionSuspended() { Log.d(TAG, "onConnectionSuspended"); getActivity().setMediaController(null); } }; public static BrowseFragment newInstance(String mediaId) { Bundle args = new Bundle(); args.putString(ARG_MEDIA_ID, mediaId); BrowseFragment fragment = new BrowseFragment(); fragment.setArguments(args); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_list, container, false); mBrowserAdapter = new BrowseAdapter(getActivity(), mMediaItems); View controls = rootView.findViewById(R.id.controls); controls.setVisibility(View.GONE); ListView listView = (ListView) rootView.findViewById(R.id.list_view); listView.setAdapter(mBrowserAdapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { MediaBrowserCompat.MediaItem item = mBrowserAdapter.getItem(position); try { FragmentDataHelper listener = (FragmentDataHelper) getActivity(); listener.onMediaItemSelected(item); } catch (ClassCastException ex) { Log.e(TAG, "Exception trying to cast to FragmentDataHelper", ex); } } }); Bundle args = getArguments(); mMediaId = args.getString(ARG_MEDIA_ID, null); mMediaBrowser = new MediaBrowserCompat(getActivity(), new ComponentName(getActivity(), MediaBrowserServiceSupport.class), mConnectionCallback, null); listView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (mCanLoadNewPage && firstVisibleItem + visibleItemCount == totalItemCount) { mCanLoadNewPage = false; loadPage((mMediaItems.size() + PAGE_SIZE - 1) / PAGE_SIZE); } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // Do nothing } }); return rootView; } @Override public void onStart() { super.onStart(); mMediaBrowser.connect(); } @Override public void onStop() { super.onStop(); mMediaBrowser.disconnect(); mSubscribedPages.clear(); } private void loadPage(int page) { Integer pageInteger = Integer.valueOf(page); if (mSubscribedPages.contains(pageInteger)) { return; } mSubscribedPages.add(pageInteger); Bundle options = new Bundle(); options.putInt(MediaBrowserCompat.EXTRA_PAGE, page); options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, PAGE_SIZE); mMediaBrowser.subscribe(mMediaId, options, mSubscriptionCallback); } // An adapter for showing the list of browsed MediaItem objects private static class BrowseAdapter extends ArrayAdapter<MediaBrowserCompat.MediaItem> { public BrowseAdapter(Context context, List<MediaBrowserCompat.MediaItem> mediaItems) { super(context, R.layout.media_list_item, mediaItems); } static class ViewHolder { ImageView mImageView; TextView mTitleView; TextView mDescriptionView; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.media_list_item, parent, false); holder = new ViewHolder(); holder.mImageView = (ImageView) convertView.findViewById(R.id.play_eq); holder.mImageView.setVisibility(View.GONE); holder.mTitleView = (TextView) convertView.findViewById(R.id.title); holder.mDescriptionView = (TextView) convertView.findViewById(R.id.description); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } MediaBrowserCompat.MediaItem item = getItem(position); holder.mTitleView.setText(item.getDescription().getTitle()); holder.mDescriptionView.setText(item.getDescription().getDescription()); if (item.isPlayable()) { holder.mImageView.setImageDrawable(ContextCompat.getDrawable( getContext(), R.drawable.ic_play_arrow_white_24dp)); holder.mImageView.setVisibility(View.VISIBLE); } else { holder.mImageView.setVisibility(View.GONE); } return convertView; } } }