• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.internal.app;
18 
19 import com.android.internal.R;
20 
21 import android.app.Activity;
22 import android.app.Dialog;
23 import android.app.DialogFragment;
24 import android.app.MediaRouteActionProvider;
25 import android.app.MediaRouteButton;
26 import android.content.Context;
27 import android.graphics.drawable.Drawable;
28 import android.media.AudioManager;
29 import android.media.MediaRouter;
30 import android.media.MediaRouter.RouteCategory;
31 import android.media.MediaRouter.RouteGroup;
32 import android.media.MediaRouter.RouteInfo;
33 import android.os.Bundle;
34 import android.text.TextUtils;
35 import android.view.KeyEvent;
36 import android.view.LayoutInflater;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import android.widget.AdapterView;
40 import android.widget.BaseAdapter;
41 import android.widget.CheckBox;
42 import android.widget.Checkable;
43 import android.widget.ImageButton;
44 import android.widget.ImageView;
45 import android.widget.ListView;
46 import android.widget.SeekBar;
47 import android.widget.TextView;
48 
49 import java.util.ArrayList;
50 import java.util.Collections;
51 import java.util.Comparator;
52 import java.util.List;
53 
54 /**
55  * This class implements the route chooser dialog for {@link MediaRouter}.
56  *
57  * @see MediaRouteButton
58  * @see MediaRouteActionProvider
59  */
60 public class MediaRouteChooserDialogFragment extends DialogFragment {
61     private static final String TAG = "MediaRouteChooserDialogFragment";
62     public static final String FRAGMENT_TAG = "android:MediaRouteChooserDialogFragment";
63 
64     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,
67         R.layout.media_route_list_item,
68         R.layout.media_route_list_item_checkable,
69         R.layout.media_route_list_item_collapse_group
70     };
71 
72     MediaRouter mRouter;
73     private int mRouteTypes;
74 
75     private LayoutInflater mInflater;
76     private LauncherListener mLauncherListener;
77     private View.OnClickListener mExtendedSettingsListener;
78     private RouteAdapter mAdapter;
79     private ListView mListView;
80     private SeekBar mVolumeSlider;
81     private ImageView mVolumeIcon;
82 
83     final RouteComparator mComparator = new RouteComparator();
84     final MediaRouterCallback mCallback = new MediaRouterCallback();
85     private boolean mIgnoreSliderVolumeChanges;
86     private boolean mIgnoreCallbackVolumeChanges;
87 
MediaRouteChooserDialogFragment()88     public MediaRouteChooserDialogFragment() {
89         setStyle(STYLE_NO_TITLE, R.style.Theme_DeviceDefault_Dialog);
90     }
91 
setLauncherListener(LauncherListener listener)92     public void setLauncherListener(LauncherListener listener) {
93         mLauncherListener = listener;
94     }
95 
96     @Override
onAttach(Activity activity)97     public void onAttach(Activity activity) {
98         super.onAttach(activity);
99         mRouter = (MediaRouter) activity.getSystemService(Context.MEDIA_ROUTER_SERVICE);
100     }
101 
102     @Override
onDetach()103     public void onDetach() {
104         super.onDetach();
105         if (mLauncherListener != null) {
106             mLauncherListener.onDetached(this);
107         }
108         if (mAdapter != null) {
109             mAdapter = null;
110         }
111         mInflater = null;
112         mRouter.removeCallback(mCallback);
113         mRouter = null;
114     }
115 
setExtendedSettingsClickListener(View.OnClickListener listener)116     public void setExtendedSettingsClickListener(View.OnClickListener listener) {
117         mExtendedSettingsListener = listener;
118     }
119 
setRouteTypes(int types)120     public void setRouteTypes(int types) {
121         mRouteTypes = types;
122     }
123 
updateVolume()124     void updateVolume() {
125         if (mRouter == null) return;
126 
127         final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
128         mVolumeIcon.setImageResource(
129                 selectedRoute.getPlaybackType() == RouteInfo.PLAYBACK_TYPE_LOCAL ?
130                 R.drawable.ic_audio_vol : R.drawable.ic_media_route_on_holo_dark);
131 
132         mIgnoreSliderVolumeChanges = true;
133 
134         if (selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_FIXED) {
135             // Disable the slider and show it at max volume.
136             mVolumeSlider.setMax(1);
137             mVolumeSlider.setProgress(1);
138             mVolumeSlider.setEnabled(false);
139         } else {
140             mVolumeSlider.setEnabled(true);
141             mVolumeSlider.setMax(selectedRoute.getVolumeMax());
142             mVolumeSlider.setProgress(selectedRoute.getVolume());
143         }
144 
145         mIgnoreSliderVolumeChanges = false;
146     }
147 
changeVolume(int newValue)148     void changeVolume(int newValue) {
149         if (mIgnoreSliderVolumeChanges) return;
150 
151         final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
152         if (selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_VARIABLE) {
153             final int maxVolume = selectedRoute.getVolumeMax();
154             newValue = Math.max(0, Math.min(newValue, maxVolume));
155             selectedRoute.requestSetVolume(newValue);
156         }
157     }
158 
159     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)160     public View onCreateView(LayoutInflater inflater, ViewGroup container,
161             Bundle savedInstanceState) {
162         mInflater = inflater;
163         final View layout = inflater.inflate(R.layout.media_route_chooser_layout, container, false);
164 
165         mVolumeIcon = (ImageView) layout.findViewById(R.id.volume_icon);
166         mVolumeSlider = (SeekBar) layout.findViewById(R.id.volume_slider);
167         updateVolume();
168         mVolumeSlider.setOnSeekBarChangeListener(new VolumeSliderChangeListener());
169 
170         if (mExtendedSettingsListener != null) {
171             final View extendedSettingsButton = layout.findViewById(R.id.extended_settings);
172             extendedSettingsButton.setVisibility(View.VISIBLE);
173             extendedSettingsButton.setOnClickListener(mExtendedSettingsListener);
174         }
175 
176         final ListView list = (ListView) layout.findViewById(R.id.list);
177         list.setItemsCanFocus(true);
178         list.setAdapter(mAdapter = new RouteAdapter());
179         list.setOnItemClickListener(mAdapter);
180 
181         mListView = list;
182         mRouter.addCallback(mRouteTypes, mCallback);
183 
184         mAdapter.scrollToSelectedItem();
185 
186         return layout;
187     }
188 
189     @Override
onCreateDialog(Bundle savedInstanceState)190     public Dialog onCreateDialog(Bundle savedInstanceState) {
191         return new RouteChooserDialog(getActivity(), getTheme());
192     }
193 
194     @Override
onResume()195     public void onResume() {
196         super.onResume();
197     }
198 
199     private static class ViewHolder {
200         public TextView text1;
201         public TextView text2;
202         public ImageView icon;
203         public ImageButton expandGroupButton;
204         public RouteAdapter.ExpandGroupListener expandGroupListener;
205         public int position;
206         public CheckBox check;
207     }
208 
209     private class RouteAdapter extends BaseAdapter implements ListView.OnItemClickListener {
210         private static final int VIEW_TOP_HEADER = 0;
211         private static final int VIEW_SECTION_HEADER = 1;
212         private static final int VIEW_ROUTE = 2;
213         private static final int VIEW_GROUPING_ROUTE = 3;
214         private static final int VIEW_GROUPING_DONE = 4;
215 
216         private int mSelectedItemPosition = -1;
217         private final ArrayList<Object> mItems = new ArrayList<Object>();
218 
219         private RouteCategory mCategoryEditingGroups;
220         private RouteGroup mEditingGroup;
221 
222         // Temporary lists for manipulation
223         private final ArrayList<RouteInfo> mCatRouteList = new ArrayList<RouteInfo>();
224         private final ArrayList<RouteInfo> mSortRouteList = new ArrayList<RouteInfo>();
225 
226         private boolean mIgnoreUpdates;
227 
RouteAdapter()228         RouteAdapter() {
229             update();
230         }
231 
update()232         void update() {
233             /*
234              * This is kind of wacky, but our data sets are going to be
235              * fairly small on average. Ideally we should be able to do some of this stuff
236              * in-place instead.
237              *
238              * Basic idea: each entry in mItems represents an item in the list for quick access.
239              * Entries can be a RouteCategory (section header), a RouteInfo with a category of
240              * mCategoryEditingGroups (a flattened RouteInfo pulled out of its group, allowing
241              * the user to change the group),
242              */
243             if (mIgnoreUpdates) return;
244 
245             mItems.clear();
246 
247             final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
248             mSelectedItemPosition = -1;
249 
250             List<RouteInfo> routes;
251             final int catCount = mRouter.getCategoryCount();
252             for (int i = 0; i < catCount; i++) {
253                 final RouteCategory cat = mRouter.getCategoryAt(i);
254                 routes = cat.getRoutes(mCatRouteList);
255 
256                 mItems.add(cat);
257 
258                 if (cat == mCategoryEditingGroups) {
259                     addGroupEditingCategoryRoutes(routes);
260                 } else {
261                     addSelectableRoutes(selectedRoute, routes);
262                 }
263 
264                 routes.clear();
265             }
266 
267             notifyDataSetChanged();
268             if (mListView != null && mSelectedItemPosition >= 0) {
269                 mListView.setItemChecked(mSelectedItemPosition, true);
270             }
271         }
272 
scrollToEditingGroup()273         void scrollToEditingGroup() {
274             if (mCategoryEditingGroups == null || mListView == null) return;
275 
276             int pos = 0;
277             int bound = 0;
278             final int itemCount = mItems.size();
279             for (int i = 0; i < itemCount; i++) {
280                 final Object item = mItems.get(i);
281                 if (item != null && item == mCategoryEditingGroups) {
282                     bound = i;
283                 }
284                 if (item == null) {
285                     pos = i;
286                     break; // this is always below the category header; we can stop here.
287                 }
288             }
289 
290             mListView.smoothScrollToPosition(pos, bound);
291         }
292 
scrollToSelectedItem()293         void scrollToSelectedItem() {
294             if (mListView == null || mSelectedItemPosition < 0) return;
295 
296             mListView.smoothScrollToPosition(mSelectedItemPosition);
297         }
298 
addSelectableRoutes(RouteInfo selectedRoute, List<RouteInfo> from)299         void addSelectableRoutes(RouteInfo selectedRoute, List<RouteInfo> from) {
300             final int routeCount = from.size();
301             for (int j = 0; j < routeCount; j++) {
302                 final RouteInfo info = from.get(j);
303                 if (info == selectedRoute) {
304                     mSelectedItemPosition = mItems.size();
305                 }
306                 mItems.add(info);
307             }
308         }
309 
addGroupEditingCategoryRoutes(List<RouteInfo> from)310         void addGroupEditingCategoryRoutes(List<RouteInfo> from) {
311             // Unpack groups and flatten for presentation
312             // mSortRouteList will always be empty here.
313             final int topCount = from.size();
314             for (int i = 0; i < topCount; i++) {
315                 final RouteInfo route = from.get(i);
316                 final RouteGroup group = route.getGroup();
317                 if (group == route) {
318                     // This is a group, unpack it.
319                     final int groupCount = group.getRouteCount();
320                     for (int j = 0; j < groupCount; j++) {
321                         final RouteInfo innerRoute = group.getRouteAt(j);
322                         mSortRouteList.add(innerRoute);
323                     }
324                 } else {
325                     mSortRouteList.add(route);
326                 }
327             }
328             // Sort by name. This will keep the route positions relatively stable even though they
329             // will be repeatedly added and removed.
330             Collections.sort(mSortRouteList, mComparator);
331 
332             mItems.addAll(mSortRouteList);
333             mSortRouteList.clear();
334 
335             mItems.add(null); // Sentinel reserving space for the "done" button.
336         }
337 
338         @Override
getCount()339         public int getCount() {
340             return mItems.size();
341         }
342 
343         @Override
getViewTypeCount()344         public int getViewTypeCount() {
345             return 5;
346         }
347 
348         @Override
getItemViewType(int position)349         public int getItemViewType(int position) {
350             final Object item = getItem(position);
351             if (item instanceof RouteCategory) {
352                 return position == 0 ? VIEW_TOP_HEADER : VIEW_SECTION_HEADER;
353             } else if (item == null) {
354                 return VIEW_GROUPING_DONE;
355             } else {
356                 final RouteInfo info = (RouteInfo) item;
357                 if (info.getCategory() == mCategoryEditingGroups) {
358                     return VIEW_GROUPING_ROUTE;
359                 }
360                 return VIEW_ROUTE;
361             }
362         }
363 
364         @Override
areAllItemsEnabled()365         public boolean areAllItemsEnabled() {
366             return false;
367         }
368 
369         @Override
isEnabled(int position)370         public boolean isEnabled(int position) {
371             switch (getItemViewType(position)) {
372                 case VIEW_ROUTE:
373                 case VIEW_GROUPING_ROUTE:
374                 case VIEW_GROUPING_DONE:
375                     return true;
376                 default:
377                     return false;
378             }
379         }
380 
381         @Override
getItem(int position)382         public Object getItem(int position) {
383             return mItems.get(position);
384         }
385 
386         @Override
getItemId(int position)387         public long getItemId(int position) {
388             return position;
389         }
390 
391         @Override
getView(int position, View convertView, ViewGroup parent)392         public View getView(int position, View convertView, ViewGroup parent) {
393             final int viewType = getItemViewType(position);
394 
395             ViewHolder holder;
396             if (convertView == null) {
397                 convertView = mInflater.inflate(ITEM_LAYOUTS[viewType], parent, false);
398                 holder = new ViewHolder();
399                 holder.position = position;
400                 holder.text1 = (TextView) convertView.findViewById(R.id.text1);
401                 holder.text2 = (TextView) convertView.findViewById(R.id.text2);
402                 holder.icon = (ImageView) convertView.findViewById(R.id.icon);
403                 holder.check = (CheckBox) convertView.findViewById(R.id.check);
404                 holder.expandGroupButton = (ImageButton) convertView.findViewById(
405                         R.id.expand_button);
406                 if (holder.expandGroupButton != null) {
407                     holder.expandGroupListener = new ExpandGroupListener();
408                     holder.expandGroupButton.setOnClickListener(holder.expandGroupListener);
409                 }
410 
411                 final View fview = convertView;
412                 final ListView list = (ListView) parent;
413                 final ViewHolder fholder = holder;
414                 convertView.setOnClickListener(new View.OnClickListener() {
415                     @Override public void onClick(View v) {
416                         list.performItemClick(fview, fholder.position, 0);
417                     }
418                 });
419                 convertView.setTag(holder);
420             } else {
421                 holder = (ViewHolder) convertView.getTag();
422                 holder.position = position;
423             }
424 
425             switch (viewType) {
426                 case VIEW_ROUTE:
427                 case VIEW_GROUPING_ROUTE:
428                     bindItemView(position, holder);
429                     break;
430                 case VIEW_SECTION_HEADER:
431                 case VIEW_TOP_HEADER:
432                     bindHeaderView(position, holder);
433                     break;
434             }
435 
436             convertView.setActivated(position == mSelectedItemPosition);
437 
438             return convertView;
439         }
440 
bindItemView(int position, ViewHolder holder)441         void bindItemView(int position, ViewHolder holder) {
442             RouteInfo info = (RouteInfo) mItems.get(position);
443             holder.text1.setText(info.getName(getActivity()));
444             final CharSequence status = info.getStatus();
445             if (TextUtils.isEmpty(status)) {
446                 holder.text2.setVisibility(View.GONE);
447             } else {
448                 holder.text2.setVisibility(View.VISIBLE);
449                 holder.text2.setText(status);
450             }
451             Drawable icon = info.getIconDrawable();
452             if (icon != null) {
453                 // Make sure we have a fresh drawable where it doesn't matter if we mutate it
454                 icon = icon.getConstantState().newDrawable(getResources());
455             }
456             holder.icon.setImageDrawable(icon);
457             holder.icon.setVisibility(icon != null ? View.VISIBLE : View.GONE);
458 
459             RouteCategory cat = info.getCategory();
460             boolean canGroup = false;
461             if (cat == mCategoryEditingGroups) {
462                 RouteGroup group = info.getGroup();
463                 holder.check.setEnabled(group.getRouteCount() > 1);
464                 holder.check.setChecked(group == mEditingGroup);
465             } else {
466                 if (cat.isGroupable()) {
467                     final RouteGroup group = (RouteGroup) info;
468                     canGroup = group.getRouteCount() > 1 ||
469                             getItemViewType(position - 1) == VIEW_ROUTE ||
470                             (position < getCount() - 1 &&
471                                     getItemViewType(position + 1) == VIEW_ROUTE);
472                 }
473             }
474 
475             if (holder.expandGroupButton != null) {
476                 holder.expandGroupButton.setVisibility(canGroup ? View.VISIBLE : View.GONE);
477                 holder.expandGroupListener.position = position;
478             }
479         }
480 
bindHeaderView(int position, ViewHolder holder)481         void bindHeaderView(int position, ViewHolder holder) {
482             RouteCategory cat = (RouteCategory) mItems.get(position);
483             holder.text1.setText(cat.getName(getActivity()));
484         }
485 
486         @Override
onItemClick(AdapterView<?> parent, View view, int position, long id)487         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
488             final int type = getItemViewType(position);
489             if (type == VIEW_SECTION_HEADER || type == VIEW_TOP_HEADER) {
490                 return;
491             } else if (type == VIEW_GROUPING_DONE) {
492                 finishGrouping();
493                 return;
494             } else {
495                 final Object item = getItem(position);
496                 if (!(item instanceof RouteInfo)) {
497                     // Oops. Stale event running around? Skip it.
498                     return;
499                 }
500 
501                 final RouteInfo route = (RouteInfo) item;
502                 if (type == VIEW_ROUTE) {
503                     mRouter.selectRouteInt(mRouteTypes, route);
504                     dismiss();
505                 } else if (type == VIEW_GROUPING_ROUTE) {
506                     final Checkable c = (Checkable) view;
507                     final boolean wasChecked = c.isChecked();
508 
509                     mIgnoreUpdates = true;
510                     RouteGroup oldGroup = route.getGroup();
511                     if (!wasChecked && oldGroup != mEditingGroup) {
512                         // Assumption: in a groupable category oldGroup will never be null.
513                         if (mRouter.getSelectedRoute(mRouteTypes) == oldGroup) {
514                             // Old group was selected but is now empty. Select the group
515                             // we're manipulating since that's where the last route went.
516                             mRouter.selectRouteInt(mRouteTypes, mEditingGroup);
517                         }
518                         oldGroup.removeRoute(route);
519                         mEditingGroup.addRoute(route);
520                         c.setChecked(true);
521                     } else if (wasChecked && mEditingGroup.getRouteCount() > 1) {
522                         mEditingGroup.removeRoute(route);
523 
524                         // In a groupable category this will add
525                         // the route into its own new group.
526                         mRouter.addRouteInt(route);
527                     }
528                     mIgnoreUpdates = false;
529                     update();
530                 }
531             }
532         }
533 
isGrouping()534         boolean isGrouping() {
535             return mCategoryEditingGroups != null;
536         }
537 
finishGrouping()538         void finishGrouping() {
539             mCategoryEditingGroups = null;
540             mEditingGroup = null;
541             getDialog().setCanceledOnTouchOutside(true);
542             update();
543             scrollToSelectedItem();
544         }
545 
546         class ExpandGroupListener implements View.OnClickListener {
547             int position;
548 
549             @Override
onClick(View v)550             public void onClick(View v) {
551                 // Assumption: this is only available for the user to click if we're presenting
552                 // a groupable category, where every top-level route in the category is a group.
553                 final RouteGroup group = (RouteGroup) getItem(position);
554                 mEditingGroup = group;
555                 mCategoryEditingGroups = group.getCategory();
556                 getDialog().setCanceledOnTouchOutside(false);
557                 mRouter.selectRouteInt(mRouteTypes, mEditingGroup);
558                 update();
559                 scrollToEditingGroup();
560             }
561         }
562     }
563 
564     class MediaRouterCallback extends MediaRouter.Callback {
565         @Override
onRouteSelected(MediaRouter router, int type, RouteInfo info)566         public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
567             mAdapter.update();
568             updateVolume();
569         }
570 
571         @Override
onRouteUnselected(MediaRouter router, int type, RouteInfo info)572         public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
573             mAdapter.update();
574         }
575 
576         @Override
onRouteAdded(MediaRouter router, RouteInfo info)577         public void onRouteAdded(MediaRouter router, RouteInfo info) {
578             mAdapter.update();
579         }
580 
581         @Override
onRouteRemoved(MediaRouter router, RouteInfo info)582         public void onRouteRemoved(MediaRouter router, RouteInfo info) {
583             if (info == mAdapter.mEditingGroup) {
584                 mAdapter.finishGrouping();
585             }
586             mAdapter.update();
587         }
588 
589         @Override
onRouteChanged(MediaRouter router, RouteInfo info)590         public void onRouteChanged(MediaRouter router, RouteInfo info) {
591             mAdapter.notifyDataSetChanged();
592         }
593 
594         @Override
onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group, int index)595         public void onRouteGrouped(MediaRouter router, RouteInfo info,
596                 RouteGroup group, int index) {
597             mAdapter.update();
598         }
599 
600         @Override
onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group)601         public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
602             mAdapter.update();
603         }
604 
605         @Override
onRouteVolumeChanged(MediaRouter router, RouteInfo info)606         public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) {
607             if (!mIgnoreCallbackVolumeChanges) {
608                 updateVolume();
609             }
610         }
611     }
612 
613     class RouteComparator implements Comparator<RouteInfo> {
614         @Override
compare(RouteInfo lhs, RouteInfo rhs)615         public int compare(RouteInfo lhs, RouteInfo rhs) {
616             return lhs.getName(getActivity()).toString()
617                     .compareTo(rhs.getName(getActivity()).toString());
618         }
619     }
620 
621     class RouteChooserDialog extends Dialog {
RouteChooserDialog(Context context, int theme)622         public RouteChooserDialog(Context context, int theme) {
623             super(context, theme);
624         }
625 
626         @Override
onBackPressed()627         public void onBackPressed() {
628             if (mAdapter != null && mAdapter.isGrouping()) {
629                 mAdapter.finishGrouping();
630             } else {
631                 super.onBackPressed();
632             }
633         }
634 
onKeyDown(int keyCode, KeyEvent event)635         public boolean onKeyDown(int keyCode, KeyEvent event) {
636             if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) {
637                 mRouter.getSelectedRoute(mRouteTypes).requestUpdateVolume(-1);
638                 return true;
639             } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) {
640                 mRouter.getSelectedRoute(mRouteTypes).requestUpdateVolume(1);
641                 return true;
642             } else {
643                 return super.onKeyDown(keyCode, event);
644             }
645         }
646 
onKeyUp(int keyCode, KeyEvent event)647         public boolean onKeyUp(int keyCode, KeyEvent event) {
648             if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) {
649                 return true;
650             } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) {
651                 return true;
652             } else {
653                 return super.onKeyUp(keyCode, event);
654             }
655         }
656     }
657 
658     /**
659      * Implemented by the MediaRouteButton that launched this dialog
660      */
661     public interface LauncherListener {
onDetached(MediaRouteChooserDialogFragment detachedFragment)662         public void onDetached(MediaRouteChooserDialogFragment detachedFragment);
663     }
664 
665     class VolumeSliderChangeListener implements SeekBar.OnSeekBarChangeListener {
666 
667         @Override
onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)668         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
669             changeVolume(progress);
670         }
671 
672         @Override
onStartTrackingTouch(SeekBar seekBar)673         public void onStartTrackingTouch(SeekBar seekBar) {
674             mIgnoreCallbackVolumeChanges = true;
675         }
676 
677         @Override
onStopTrackingTouch(SeekBar seekBar)678         public void onStopTrackingTouch(SeekBar seekBar) {
679             mIgnoreCallbackVolumeChanges = false;
680             updateVolume();
681         }
682 
683     }
684 }
685