blob: 2bc80ff717a3f1c1c4d1aab1155ce5b7eeebbe4a [file] [log] [blame]
Adam Powell70e11e52012-06-12 16:59:45 -07001/*
2 * Copyright (C) 2012 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 */
16
17package com.android.internal.app;
18
19import com.android.internal.R;
20
21import android.app.Activity;
Adam Powelld6d0bdd2012-06-13 23:15:49 -070022import android.app.Dialog;
Adam Powell70e11e52012-06-12 16:59:45 -070023import android.app.DialogFragment;
24import android.app.MediaRouteActionProvider;
25import android.app.MediaRouteButton;
26import android.content.Context;
Adam Powelld6d0bdd2012-06-13 23:15:49 -070027import android.graphics.drawable.Drawable;
Adam Powell705ab802012-09-17 13:30:51 -070028import android.hardware.display.DisplayManager;
Adam Powell70e11e52012-06-12 16:59:45 -070029import android.media.MediaRouter;
30import android.media.MediaRouter.RouteCategory;
31import android.media.MediaRouter.RouteGroup;
32import android.media.MediaRouter.RouteInfo;
33import android.os.Bundle;
34import android.text.TextUtils;
Adam Powell45996962012-06-16 14:58:39 -070035import android.view.KeyEvent;
Adam Powell70e11e52012-06-12 16:59:45 -070036import android.view.LayoutInflater;
37import android.view.View;
38import android.view.ViewGroup;
39import android.widget.AdapterView;
40import android.widget.BaseAdapter;
Adam Powellb5e2af52012-06-14 23:06:24 -070041import android.widget.CheckBox;
42import android.widget.Checkable;
Adam Powelld6d0bdd2012-06-13 23:15:49 -070043import android.widget.ImageButton;
44import android.widget.ImageView;
Adam Powell70e11e52012-06-12 16:59:45 -070045import android.widget.ListView;
Adam Powell45996962012-06-16 14:58:39 -070046import android.widget.SeekBar;
Adam Powell70e11e52012-06-12 16:59:45 -070047import android.widget.TextView;
48
49import java.util.ArrayList;
Adam Powelld6d0bdd2012-06-13 23:15:49 -070050import java.util.Collections;
51import java.util.Comparator;
Adam Powellb5e2af52012-06-14 23:06:24 -070052import java.util.List;
Adam Powell70e11e52012-06-12 16:59:45 -070053
54/**
55 * This class implements the route chooser dialog for {@link MediaRouter}.
56 *
57 * @see MediaRouteButton
58 * @see MediaRouteActionProvider
59 */
60public class MediaRouteChooserDialogFragment extends DialogFragment {
61 private static final String TAG = "MediaRouteChooserDialogFragment";
62 public static final String FRAGMENT_TAG = "android:MediaRouteChooserDialogFragment";
63
Adam Powelld6d0bdd2012-06-13 23:15:49 -070064 private static final int[] ITEM_LAYOUTS = new int[] {
65 R.layout.media_route_list_item_top_header,
66 R.layout.media_route_list_item_section_header,
Adam Powellb5e2af52012-06-14 23:06:24 -070067 R.layout.media_route_list_item,
Adam Powelld6d0bdd2012-06-13 23:15:49 -070068 R.layout.media_route_list_item_checkable,
69 R.layout.media_route_list_item_collapse_group
70 };
71
Adam Powell70e11e52012-06-12 16:59:45 -070072 MediaRouter mRouter;
Adam Powell705ab802012-09-17 13:30:51 -070073 DisplayManager mDisplayService;
Adam Powell70e11e52012-06-12 16:59:45 -070074 private int mRouteTypes;
75
Adam Powelld6d0bdd2012-06-13 23:15:49 -070076 private LayoutInflater mInflater;
Adam Powell70e11e52012-06-12 16:59:45 -070077 private LauncherListener mLauncherListener;
78 private View.OnClickListener mExtendedSettingsListener;
79 private RouteAdapter mAdapter;
80 private ListView mListView;
Adam Powell45996962012-06-16 14:58:39 -070081 private SeekBar mVolumeSlider;
82 private ImageView mVolumeIcon;
Adam Powell70e11e52012-06-12 16:59:45 -070083
Adam Powell0d03c042012-06-14 16:04:12 -070084 final RouteComparator mComparator = new RouteComparator();
Adam Powell45996962012-06-16 14:58:39 -070085 final MediaRouterCallback mCallback = new MediaRouterCallback();
Adam Powell8e37a852012-06-20 15:56:39 -070086 private boolean mIgnoreSliderVolumeChanges;
87 private boolean mIgnoreCallbackVolumeChanges;
Adam Powelld6d0bdd2012-06-13 23:15:49 -070088
Adam Powell70e11e52012-06-12 16:59:45 -070089 public MediaRouteChooserDialogFragment() {
90 setStyle(STYLE_NO_TITLE, R.style.Theme_DeviceDefault_Dialog);
91 }
92
93 public void setLauncherListener(LauncherListener listener) {
94 mLauncherListener = listener;
95 }
96
97 @Override
98 public void onAttach(Activity activity) {
99 super.onAttach(activity);
100 mRouter = (MediaRouter) activity.getSystemService(Context.MEDIA_ROUTER_SERVICE);
Adam Powell705ab802012-09-17 13:30:51 -0700101 mDisplayService = (DisplayManager) activity.getSystemService(Context.DISPLAY_SERVICE);
Adam Powell70e11e52012-06-12 16:59:45 -0700102 }
103
104 @Override
105 public void onDetach() {
106 super.onDetach();
107 if (mLauncherListener != null) {
108 mLauncherListener.onDetached(this);
109 }
110 if (mAdapter != null) {
Adam Powell70e11e52012-06-12 16:59:45 -0700111 mAdapter = null;
112 }
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700113 mInflater = null;
Adam Powell45996962012-06-16 14:58:39 -0700114 mRouter.removeCallback(mCallback);
Adam Powell70e11e52012-06-12 16:59:45 -0700115 mRouter = null;
116 }
117
Adam Powell70e11e52012-06-12 16:59:45 -0700118 public void setExtendedSettingsClickListener(View.OnClickListener listener) {
119 mExtendedSettingsListener = listener;
120 }
121
122 public void setRouteTypes(int types) {
123 mRouteTypes = types;
Adam Powell705ab802012-09-17 13:30:51 -0700124 if ((mRouteTypes & MediaRouter.ROUTE_TYPE_LIVE_VIDEO) != 0 && mDisplayService == null) {
125 final Context activity = getActivity();
126 if (activity != null) {
127 mDisplayService = (DisplayManager) activity.getSystemService(
128 Context.DISPLAY_SERVICE);
129 }
130 } else {
131 mDisplayService = null;
132 }
Adam Powell70e11e52012-06-12 16:59:45 -0700133 }
134
Adam Powell45996962012-06-16 14:58:39 -0700135 void updateVolume() {
Adam Powell1b4ea7e2012-06-22 20:41:31 -0700136 if (mRouter == null) return;
137
Adam Powell45996962012-06-16 14:58:39 -0700138 final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
Adam Powell1cf2ca82012-11-28 10:46:56 -0800139 mVolumeIcon.setImageResource(selectedRoute == null ||
Adam Powell8e37a852012-06-20 15:56:39 -0700140 selectedRoute.getPlaybackType() == RouteInfo.PLAYBACK_TYPE_LOCAL ?
Adam Powell45996962012-06-16 14:58:39 -0700141 R.drawable.ic_audio_vol : R.drawable.ic_media_route_on_holo_dark);
142
Adam Powell8e37a852012-06-20 15:56:39 -0700143 mIgnoreSliderVolumeChanges = true;
144
Adam Powell1cf2ca82012-11-28 10:46:56 -0800145 if (selectedRoute == null ||
146 selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_FIXED) {
Adam Powell8e37a852012-06-20 15:56:39 -0700147 // Disable the slider and show it at max volume.
148 mVolumeSlider.setMax(1);
149 mVolumeSlider.setProgress(1);
150 mVolumeSlider.setEnabled(false);
Adam Powell45996962012-06-16 14:58:39 -0700151 } else {
Adam Powell8e37a852012-06-20 15:56:39 -0700152 mVolumeSlider.setEnabled(true);
153 mVolumeSlider.setMax(selectedRoute.getVolumeMax());
154 mVolumeSlider.setProgress(selectedRoute.getVolume());
Adam Powell45996962012-06-16 14:58:39 -0700155 }
Adam Powell8e37a852012-06-20 15:56:39 -0700156
157 mIgnoreSliderVolumeChanges = false;
Adam Powell45996962012-06-16 14:58:39 -0700158 }
159
160 void changeVolume(int newValue) {
Adam Powell8e37a852012-06-20 15:56:39 -0700161 if (mIgnoreSliderVolumeChanges) return;
Adam Powell45996962012-06-16 14:58:39 -0700162
Adam Powell8e37a852012-06-20 15:56:39 -0700163 final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
Adam Powell1cf2ca82012-11-28 10:46:56 -0800164 if (selectedRoute != null &&
165 selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_VARIABLE) {
Adam Powell4dd21c82012-06-21 19:59:53 -0700166 final int maxVolume = selectedRoute.getVolumeMax();
Adam Powell45996962012-06-16 14:58:39 -0700167 newValue = Math.max(0, Math.min(newValue, maxVolume));
Adam Powell8e37a852012-06-20 15:56:39 -0700168 selectedRoute.requestSetVolume(newValue);
Adam Powell45996962012-06-16 14:58:39 -0700169 }
170 }
171
Adam Powell70e11e52012-06-12 16:59:45 -0700172 @Override
173 public View onCreateView(LayoutInflater inflater, ViewGroup container,
174 Bundle savedInstanceState) {
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700175 mInflater = inflater;
Adam Powell70e11e52012-06-12 16:59:45 -0700176 final View layout = inflater.inflate(R.layout.media_route_chooser_layout, container, false);
Adam Powell45996962012-06-16 14:58:39 -0700177
178 mVolumeIcon = (ImageView) layout.findViewById(R.id.volume_icon);
179 mVolumeSlider = (SeekBar) layout.findViewById(R.id.volume_slider);
180 updateVolume();
181 mVolumeSlider.setOnSeekBarChangeListener(new VolumeSliderChangeListener());
Adam Powell70e11e52012-06-12 16:59:45 -0700182
183 if (mExtendedSettingsListener != null) {
Adam Powell45996962012-06-16 14:58:39 -0700184 final View extendedSettingsButton = layout.findViewById(R.id.extended_settings);
Adam Powell70e11e52012-06-12 16:59:45 -0700185 extendedSettingsButton.setVisibility(View.VISIBLE);
186 extendedSettingsButton.setOnClickListener(mExtendedSettingsListener);
187 }
188
189 final ListView list = (ListView) layout.findViewById(R.id.list);
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700190 list.setItemsCanFocus(true);
191 list.setAdapter(mAdapter = new RouteAdapter());
Adam Powell70e11e52012-06-12 16:59:45 -0700192 list.setOnItemClickListener(mAdapter);
193
194 mListView = list;
Adam Powell45996962012-06-16 14:58:39 -0700195 mRouter.addCallback(mRouteTypes, mCallback);
Adam Powell70e11e52012-06-12 16:59:45 -0700196
Adam Powellb5e2af52012-06-14 23:06:24 -0700197 mAdapter.scrollToSelectedItem();
198
Adam Powell70e11e52012-06-12 16:59:45 -0700199 return layout;
200 }
201
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700202 @Override
203 public Dialog onCreateDialog(Bundle savedInstanceState) {
204 return new RouteChooserDialog(getActivity(), getTheme());
205 }
206
207 @Override
208 public void onResume() {
209 super.onResume();
Adam Powell705ab802012-09-17 13:30:51 -0700210 if (mDisplayService != null) {
211 mDisplayService.scanWifiDisplays();
212 }
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700213 }
214
215 private static class ViewHolder {
216 public TextView text1;
217 public TextView text2;
218 public ImageView icon;
219 public ImageButton expandGroupButton;
220 public RouteAdapter.ExpandGroupListener expandGroupListener;
221 public int position;
Adam Powellb5e2af52012-06-14 23:06:24 -0700222 public CheckBox check;
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700223 }
Adam Powell70e11e52012-06-12 16:59:45 -0700224
Adam Powell0d03c042012-06-14 16:04:12 -0700225 private class RouteAdapter extends BaseAdapter implements ListView.OnItemClickListener {
Adam Powell70e11e52012-06-12 16:59:45 -0700226 private static final int VIEW_TOP_HEADER = 0;
227 private static final int VIEW_SECTION_HEADER = 1;
228 private static final int VIEW_ROUTE = 2;
Adam Powellb5e2af52012-06-14 23:06:24 -0700229 private static final int VIEW_GROUPING_ROUTE = 3;
230 private static final int VIEW_GROUPING_DONE = 4;
Adam Powell70e11e52012-06-12 16:59:45 -0700231
Adam Powellb5e2af52012-06-14 23:06:24 -0700232 private int mSelectedItemPosition = -1;
Adam Powell70e11e52012-06-12 16:59:45 -0700233 private final ArrayList<Object> mItems = new ArrayList<Object>();
Adam Powell70e11e52012-06-12 16:59:45 -0700234
Adam Powellb5e2af52012-06-14 23:06:24 -0700235 private RouteCategory mCategoryEditingGroups;
236 private RouteGroup mEditingGroup;
237
238 // Temporary lists for manipulation
239 private final ArrayList<RouteInfo> mCatRouteList = new ArrayList<RouteInfo>();
240 private final ArrayList<RouteInfo> mSortRouteList = new ArrayList<RouteInfo>();
241
242 private boolean mIgnoreUpdates;
243
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700244 RouteAdapter() {
Adam Powell70e11e52012-06-12 16:59:45 -0700245 update();
246 }
247
248 void update() {
Adam Powellb5e2af52012-06-14 23:06:24 -0700249 /*
250 * This is kind of wacky, but our data sets are going to be
251 * fairly small on average. Ideally we should be able to do some of this stuff
252 * in-place instead.
253 *
254 * Basic idea: each entry in mItems represents an item in the list for quick access.
255 * Entries can be a RouteCategory (section header), a RouteInfo with a category of
256 * mCategoryEditingGroups (a flattened RouteInfo pulled out of its group, allowing
257 * the user to change the group),
258 */
259 if (mIgnoreUpdates) return;
260
Adam Powell70e11e52012-06-12 16:59:45 -0700261 mItems.clear();
262
263 final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
Adam Powellb5e2af52012-06-14 23:06:24 -0700264 mSelectedItemPosition = -1;
Adam Powell70e11e52012-06-12 16:59:45 -0700265
Adam Powellb5e2af52012-06-14 23:06:24 -0700266 List<RouteInfo> routes;
Adam Powell70e11e52012-06-12 16:59:45 -0700267 final int catCount = mRouter.getCategoryCount();
268 for (int i = 0; i < catCount; i++) {
269 final RouteCategory cat = mRouter.getCategoryAt(i);
Adam Powellb5e2af52012-06-14 23:06:24 -0700270 routes = cat.getRoutes(mCatRouteList);
Adam Powell70e11e52012-06-12 16:59:45 -0700271
Adam Powell705ab802012-09-17 13:30:51 -0700272 if (!cat.isSystem()) {
273 mItems.add(cat);
274 }
Adam Powell70e11e52012-06-12 16:59:45 -0700275
Adam Powellb5e2af52012-06-14 23:06:24 -0700276 if (cat == mCategoryEditingGroups) {
277 addGroupEditingCategoryRoutes(routes);
278 } else {
279 addSelectableRoutes(selectedRoute, routes);
Adam Powell70e11e52012-06-12 16:59:45 -0700280 }
Adam Powellb5e2af52012-06-14 23:06:24 -0700281
282 routes.clear();
Adam Powell70e11e52012-06-12 16:59:45 -0700283 }
284
285 notifyDataSetChanged();
Adam Powellb5e2af52012-06-14 23:06:24 -0700286 if (mListView != null && mSelectedItemPosition >= 0) {
Adam Powell70e11e52012-06-12 16:59:45 -0700287 mListView.setItemChecked(mSelectedItemPosition, true);
288 }
289 }
290
Adam Powellb5e2af52012-06-14 23:06:24 -0700291 void scrollToEditingGroup() {
292 if (mCategoryEditingGroups == null || mListView == null) return;
293
294 int pos = 0;
295 int bound = 0;
296 final int itemCount = mItems.size();
297 for (int i = 0; i < itemCount; i++) {
298 final Object item = mItems.get(i);
299 if (item != null && item == mCategoryEditingGroups) {
300 bound = i;
301 }
302 if (item == null) {
303 pos = i;
304 break; // this is always below the category header; we can stop here.
305 }
306 }
307
308 mListView.smoothScrollToPosition(pos, bound);
309 }
310
311 void scrollToSelectedItem() {
312 if (mListView == null || mSelectedItemPosition < 0) return;
313
314 mListView.smoothScrollToPosition(mSelectedItemPosition);
315 }
316
317 void addSelectableRoutes(RouteInfo selectedRoute, List<RouteInfo> from) {
318 final int routeCount = from.size();
319 for (int j = 0; j < routeCount; j++) {
320 final RouteInfo info = from.get(j);
321 if (info == selectedRoute) {
322 mSelectedItemPosition = mItems.size();
323 }
324 mItems.add(info);
325 }
326 }
327
328 void addGroupEditingCategoryRoutes(List<RouteInfo> from) {
329 // Unpack groups and flatten for presentation
330 // mSortRouteList will always be empty here.
331 final int topCount = from.size();
332 for (int i = 0; i < topCount; i++) {
333 final RouteInfo route = from.get(i);
334 final RouteGroup group = route.getGroup();
335 if (group == route) {
336 // This is a group, unpack it.
337 final int groupCount = group.getRouteCount();
338 for (int j = 0; j < groupCount; j++) {
339 final RouteInfo innerRoute = group.getRouteAt(j);
340 mSortRouteList.add(innerRoute);
341 }
342 } else {
343 mSortRouteList.add(route);
344 }
345 }
346 // Sort by name. This will keep the route positions relatively stable even though they
347 // will be repeatedly added and removed.
348 Collections.sort(mSortRouteList, mComparator);
349
350 mItems.addAll(mSortRouteList);
351 mSortRouteList.clear();
352
353 mItems.add(null); // Sentinel reserving space for the "done" button.
354 }
355
Adam Powell70e11e52012-06-12 16:59:45 -0700356 @Override
357 public int getCount() {
358 return mItems.size();
359 }
360
361 @Override
362 public int getViewTypeCount() {
Adam Powellb5e2af52012-06-14 23:06:24 -0700363 return 5;
Adam Powell70e11e52012-06-12 16:59:45 -0700364 }
365
366 @Override
367 public int getItemViewType(int position) {
368 final Object item = getItem(position);
369 if (item instanceof RouteCategory) {
370 return position == 0 ? VIEW_TOP_HEADER : VIEW_SECTION_HEADER;
Adam Powellb5e2af52012-06-14 23:06:24 -0700371 } else if (item == null) {
372 return VIEW_GROUPING_DONE;
Adam Powell70e11e52012-06-12 16:59:45 -0700373 } else {
Adam Powellb5e2af52012-06-14 23:06:24 -0700374 final RouteInfo info = (RouteInfo) item;
375 if (info.getCategory() == mCategoryEditingGroups) {
376 return VIEW_GROUPING_ROUTE;
377 }
Adam Powell70e11e52012-06-12 16:59:45 -0700378 return VIEW_ROUTE;
379 }
380 }
381
382 @Override
383 public boolean areAllItemsEnabled() {
384 return false;
385 }
386
387 @Override
388 public boolean isEnabled(int position) {
Adam Powellb5e2af52012-06-14 23:06:24 -0700389 switch (getItemViewType(position)) {
390 case VIEW_ROUTE:
Adam Powell705ab802012-09-17 13:30:51 -0700391 return ((RouteInfo) mItems.get(position)).isEnabled();
Adam Powellb5e2af52012-06-14 23:06:24 -0700392 case VIEW_GROUPING_ROUTE:
393 case VIEW_GROUPING_DONE:
394 return true;
395 default:
396 return false;
397 }
Adam Powell70e11e52012-06-12 16:59:45 -0700398 }
399
400 @Override
401 public Object getItem(int position) {
402 return mItems.get(position);
403 }
404
405 @Override
406 public long getItemId(int position) {
407 return position;
408 }
409
410 @Override
411 public View getView(int position, View convertView, ViewGroup parent) {
412 final int viewType = getItemViewType(position);
413
414 ViewHolder holder;
415 if (convertView == null) {
416 convertView = mInflater.inflate(ITEM_LAYOUTS[viewType], parent, false);
417 holder = new ViewHolder();
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700418 holder.position = position;
Adam Powell70e11e52012-06-12 16:59:45 -0700419 holder.text1 = (TextView) convertView.findViewById(R.id.text1);
420 holder.text2 = (TextView) convertView.findViewById(R.id.text2);
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700421 holder.icon = (ImageView) convertView.findViewById(R.id.icon);
Adam Powellb5e2af52012-06-14 23:06:24 -0700422 holder.check = (CheckBox) convertView.findViewById(R.id.check);
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700423 holder.expandGroupButton = (ImageButton) convertView.findViewById(
424 R.id.expand_button);
425 if (holder.expandGroupButton != null) {
426 holder.expandGroupListener = new ExpandGroupListener();
427 holder.expandGroupButton.setOnClickListener(holder.expandGroupListener);
428 }
429
430 final View fview = convertView;
431 final ListView list = (ListView) parent;
432 final ViewHolder fholder = holder;
433 convertView.setOnClickListener(new View.OnClickListener() {
434 @Override public void onClick(View v) {
435 list.performItemClick(fview, fholder.position, 0);
436 }
437 });
Adam Powell70e11e52012-06-12 16:59:45 -0700438 convertView.setTag(holder);
439 } else {
440 holder = (ViewHolder) convertView.getTag();
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700441 holder.position = position;
Adam Powell70e11e52012-06-12 16:59:45 -0700442 }
443
Adam Powellb5e2af52012-06-14 23:06:24 -0700444 switch (viewType) {
445 case VIEW_ROUTE:
446 case VIEW_GROUPING_ROUTE:
447 bindItemView(position, holder);
448 break;
449 case VIEW_SECTION_HEADER:
450 case VIEW_TOP_HEADER:
451 bindHeaderView(position, holder);
452 break;
Adam Powell70e11e52012-06-12 16:59:45 -0700453 }
454
Adam Powellb5e2af52012-06-14 23:06:24 -0700455 convertView.setActivated(position == mSelectedItemPosition);
Adam Powell705ab802012-09-17 13:30:51 -0700456 convertView.setEnabled(isEnabled(position));
Adam Powellb5e2af52012-06-14 23:06:24 -0700457
Adam Powell70e11e52012-06-12 16:59:45 -0700458 return convertView;
459 }
460
461 void bindItemView(int position, ViewHolder holder) {
462 RouteInfo info = (RouteInfo) mItems.get(position);
Adam Powell0d03c042012-06-14 16:04:12 -0700463 holder.text1.setText(info.getName(getActivity()));
Adam Powell70e11e52012-06-12 16:59:45 -0700464 final CharSequence status = info.getStatus();
465 if (TextUtils.isEmpty(status)) {
466 holder.text2.setVisibility(View.GONE);
467 } else {
468 holder.text2.setVisibility(View.VISIBLE);
469 holder.text2.setText(status);
470 }
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700471 Drawable icon = info.getIconDrawable();
472 if (icon != null) {
473 // Make sure we have a fresh drawable where it doesn't matter if we mutate it
474 icon = icon.getConstantState().newDrawable(getResources());
475 }
476 holder.icon.setImageDrawable(icon);
477 holder.icon.setVisibility(icon != null ? View.VISIBLE : View.GONE);
478
479 RouteCategory cat = info.getCategory();
480 boolean canGroup = false;
Adam Powellb5e2af52012-06-14 23:06:24 -0700481 if (cat == mCategoryEditingGroups) {
482 RouteGroup group = info.getGroup();
483 holder.check.setEnabled(group.getRouteCount() > 1);
484 holder.check.setChecked(group == mEditingGroup);
485 } else {
486 if (cat.isGroupable()) {
487 final RouteGroup group = (RouteGroup) info;
488 canGroup = group.getRouteCount() > 1 ||
489 getItemViewType(position - 1) == VIEW_ROUTE ||
490 (position < getCount() - 1 &&
491 getItemViewType(position + 1) == VIEW_ROUTE);
492 }
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700493 }
Adam Powellb5e2af52012-06-14 23:06:24 -0700494
495 if (holder.expandGroupButton != null) {
496 holder.expandGroupButton.setVisibility(canGroup ? View.VISIBLE : View.GONE);
497 holder.expandGroupListener.position = position;
498 }
Adam Powell70e11e52012-06-12 16:59:45 -0700499 }
500
501 void bindHeaderView(int position, ViewHolder holder) {
502 RouteCategory cat = (RouteCategory) mItems.get(position);
Adam Powell0d03c042012-06-14 16:04:12 -0700503 holder.text1.setText(cat.getName(getActivity()));
Adam Powell70e11e52012-06-12 16:59:45 -0700504 }
505
Adam Powell70e11e52012-06-12 16:59:45 -0700506 @Override
Adam Powell70e11e52012-06-12 16:59:45 -0700507 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powellb5e2af52012-06-14 23:06:24 -0700508 final int type = getItemViewType(position);
509 if (type == VIEW_SECTION_HEADER || type == VIEW_TOP_HEADER) {
Adam Powell70e11e52012-06-12 16:59:45 -0700510 return;
Adam Powellb5e2af52012-06-14 23:06:24 -0700511 } else if (type == VIEW_GROUPING_DONE) {
512 finishGrouping();
513 return;
514 } else {
515 final Object item = getItem(position);
516 if (!(item instanceof RouteInfo)) {
517 // Oops. Stale event running around? Skip it.
518 return;
519 }
520
521 final RouteInfo route = (RouteInfo) item;
522 if (type == VIEW_ROUTE) {
523 mRouter.selectRouteInt(mRouteTypes, route);
524 dismiss();
525 } else if (type == VIEW_GROUPING_ROUTE) {
526 final Checkable c = (Checkable) view;
527 final boolean wasChecked = c.isChecked();
528
529 mIgnoreUpdates = true;
530 RouteGroup oldGroup = route.getGroup();
531 if (!wasChecked && oldGroup != mEditingGroup) {
532 // Assumption: in a groupable category oldGroup will never be null.
533 if (mRouter.getSelectedRoute(mRouteTypes) == oldGroup) {
534 // Old group was selected but is now empty. Select the group
535 // we're manipulating since that's where the last route went.
536 mRouter.selectRouteInt(mRouteTypes, mEditingGroup);
537 }
538 oldGroup.removeRoute(route);
539 mEditingGroup.addRoute(route);
540 c.setChecked(true);
541 } else if (wasChecked && mEditingGroup.getRouteCount() > 1) {
542 mEditingGroup.removeRoute(route);
543
544 // In a groupable category this will add
545 // the route into its own new group.
546 mRouter.addRouteInt(route);
547 }
548 mIgnoreUpdates = false;
549 update();
550 }
Adam Powell70e11e52012-06-12 16:59:45 -0700551 }
Adam Powellb5e2af52012-06-14 23:06:24 -0700552 }
553
554 boolean isGrouping() {
555 return mCategoryEditingGroups != null;
556 }
557
558 void finishGrouping() {
559 mCategoryEditingGroups = null;
560 mEditingGroup = null;
561 getDialog().setCanceledOnTouchOutside(true);
562 update();
563 scrollToSelectedItem();
Adam Powell70e11e52012-06-12 16:59:45 -0700564 }
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700565
566 class ExpandGroupListener implements View.OnClickListener {
567 int position;
568
569 @Override
570 public void onClick(View v) {
571 // Assumption: this is only available for the user to click if we're presenting
572 // a groupable category, where every top-level route in the category is a group.
Adam Powellb5e2af52012-06-14 23:06:24 -0700573 final RouteGroup group = (RouteGroup) getItem(position);
574 mEditingGroup = group;
575 mCategoryEditingGroups = group.getCategory();
576 getDialog().setCanceledOnTouchOutside(false);
577 mRouter.selectRouteInt(mRouteTypes, mEditingGroup);
578 update();
579 scrollToEditingGroup();
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700580 }
581 }
Adam Powell45996962012-06-16 14:58:39 -0700582 }
Adam Powell0d03c042012-06-14 16:04:12 -0700583
Adam Powell45996962012-06-16 14:58:39 -0700584 class MediaRouterCallback extends MediaRouter.Callback {
585 @Override
586 public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
587 mAdapter.update();
588 updateVolume();
589 }
Adam Powell0d03c042012-06-14 16:04:12 -0700590
Adam Powell45996962012-06-16 14:58:39 -0700591 @Override
592 public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
593 mAdapter.update();
594 }
Adam Powell0d03c042012-06-14 16:04:12 -0700595
Adam Powell45996962012-06-16 14:58:39 -0700596 @Override
597 public void onRouteAdded(MediaRouter router, RouteInfo info) {
598 mAdapter.update();
Adam Powell45996962012-06-16 14:58:39 -0700599 }
Adam Powell0d03c042012-06-14 16:04:12 -0700600
Adam Powell45996962012-06-16 14:58:39 -0700601 @Override
602 public void onRouteRemoved(MediaRouter router, RouteInfo info) {
603 if (info == mAdapter.mEditingGroup) {
604 mAdapter.finishGrouping();
Adam Powell0d03c042012-06-14 16:04:12 -0700605 }
Adam Powell45996962012-06-16 14:58:39 -0700606 mAdapter.update();
Adam Powell45996962012-06-16 14:58:39 -0700607 }
Adam Powell0d03c042012-06-14 16:04:12 -0700608
Adam Powell45996962012-06-16 14:58:39 -0700609 @Override
610 public void onRouteChanged(MediaRouter router, RouteInfo info) {
611 mAdapter.notifyDataSetChanged();
612 }
Adam Powell0d03c042012-06-14 16:04:12 -0700613
Adam Powell45996962012-06-16 14:58:39 -0700614 @Override
615 public void onRouteGrouped(MediaRouter router, RouteInfo info,
616 RouteGroup group, int index) {
617 mAdapter.update();
618 }
Adam Powell0d03c042012-06-14 16:04:12 -0700619
Adam Powell45996962012-06-16 14:58:39 -0700620 @Override
621 public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
622 mAdapter.update();
Adam Powell0d03c042012-06-14 16:04:12 -0700623 }
Adam Powell8e37a852012-06-20 15:56:39 -0700624
625 @Override
626 public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) {
627 if (!mIgnoreCallbackVolumeChanges) {
628 updateVolume();
629 }
630 }
Adam Powell70e11e52012-06-12 16:59:45 -0700631 }
632
Adam Powell0d03c042012-06-14 16:04:12 -0700633 class RouteComparator implements Comparator<RouteInfo> {
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700634 @Override
635 public int compare(RouteInfo lhs, RouteInfo rhs) {
Adam Powell0d03c042012-06-14 16:04:12 -0700636 return lhs.getName(getActivity()).toString()
637 .compareTo(rhs.getName(getActivity()).toString());
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700638 }
639 }
640
641 class RouteChooserDialog extends Dialog {
642 public RouteChooserDialog(Context context, int theme) {
643 super(context, theme);
644 }
645
646 @Override
647 public void onBackPressed() {
Adam Powellb5e2af52012-06-14 23:06:24 -0700648 if (mAdapter != null && mAdapter.isGrouping()) {
649 mAdapter.finishGrouping();
Adam Powelld6d0bdd2012-06-13 23:15:49 -0700650 } else {
651 super.onBackPressed();
652 }
Adam Powell70e11e52012-06-12 16:59:45 -0700653 }
Adam Powell45996962012-06-16 14:58:39 -0700654
655 public boolean onKeyDown(int keyCode, KeyEvent event) {
656 if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) {
Adam Powell1cf2ca82012-11-28 10:46:56 -0800657 final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
658 if (selectedRoute != null) {
659 selectedRoute.requestUpdateVolume(-1);
660 return true;
661 }
Adam Powell45996962012-06-16 14:58:39 -0700662 } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) {
Adam Powell1cf2ca82012-11-28 10:46:56 -0800663 final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
664 if (selectedRoute != null) {
665 mRouter.getSelectedRoute(mRouteTypes).requestUpdateVolume(1);
666 return true;
667 }
Adam Powell45996962012-06-16 14:58:39 -0700668 }
Adam Powell1cf2ca82012-11-28 10:46:56 -0800669 return super.onKeyDown(keyCode, event);
Adam Powell45996962012-06-16 14:58:39 -0700670 }
Adam Powell8e37a852012-06-20 15:56:39 -0700671
672 public boolean onKeyUp(int keyCode, KeyEvent event) {
673 if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) {
674 return true;
675 } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) {
676 return true;
677 } else {
678 return super.onKeyUp(keyCode, event);
679 }
680 }
Adam Powell45996962012-06-16 14:58:39 -0700681 }
682
683 /**
684 * Implemented by the MediaRouteButton that launched this dialog
685 */
686 public interface LauncherListener {
687 public void onDetached(MediaRouteChooserDialogFragment detachedFragment);
688 }
689
690 class VolumeSliderChangeListener implements SeekBar.OnSeekBarChangeListener {
691
692 @Override
693 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
694 changeVolume(progress);
695 }
696
697 @Override
698 public void onStartTrackingTouch(SeekBar seekBar) {
Adam Powell8e37a852012-06-20 15:56:39 -0700699 mIgnoreCallbackVolumeChanges = true;
Adam Powell45996962012-06-16 14:58:39 -0700700 }
701
702 @Override
703 public void onStopTrackingTouch(SeekBar seekBar) {
Adam Powell8e37a852012-06-20 15:56:39 -0700704 mIgnoreCallbackVolumeChanges = false;
705 updateVolume();
Adam Powell45996962012-06-16 14:58:39 -0700706 }
707
Adam Powell70e11e52012-06-12 16:59:45 -0700708 }
709}