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}