Home | History | Annotate | Download | only in app
      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 
     88     public MediaRouteChooserDialogFragment() {
     89         setStyle(STYLE_NO_TITLE, R.style.Theme_DeviceDefault_Dialog);
     90     }
     91 
     92     public void setLauncherListener(LauncherListener listener) {
     93         mLauncherListener = listener;
     94     }
     95 
     96     @Override
     97     public void onAttach(Activity activity) {
     98         super.onAttach(activity);
     99         mRouter = (MediaRouter) activity.getSystemService(Context.MEDIA_ROUTER_SERVICE);
    100     }
    101 
    102     @Override
    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 
    116     public void setExtendedSettingsClickListener(View.OnClickListener listener) {
    117         mExtendedSettingsListener = listener;
    118     }
    119 
    120     public void setRouteTypes(int types) {
    121         mRouteTypes = types;
    122     }
    123 
    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 
    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
    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
    190     public Dialog onCreateDialog(Bundle savedInstanceState) {
    191         return new RouteChooserDialog(getActivity(), getTheme());
    192     }
    193 
    194     @Override
    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 
    228         RouteAdapter() {
    229             update();
    230         }
    231 
    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 
    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 
    293         void scrollToSelectedItem() {
    294             if (mListView == null || mSelectedItemPosition < 0) return;
    295 
    296             mListView.smoothScrollToPosition(mSelectedItemPosition);
    297         }
    298 
    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 
    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
    339         public int getCount() {
    340             return mItems.size();
    341         }
    342 
    343         @Override
    344         public int getViewTypeCount() {
    345             return 5;
    346         }
    347 
    348         @Override
    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
    365         public boolean areAllItemsEnabled() {
    366             return false;
    367         }
    368 
    369         @Override
    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
    382         public Object getItem(int position) {
    383             return mItems.get(position);
    384         }
    385 
    386         @Override
    387         public long getItemId(int position) {
    388             return position;
    389         }
    390 
    391         @Override
    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 
    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 
    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
    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 
    534         boolean isGrouping() {
    535             return mCategoryEditingGroups != null;
    536         }
    537 
    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
    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
    566         public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
    567             mAdapter.update();
    568             updateVolume();
    569         }
    570 
    571         @Override
    572         public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
    573             mAdapter.update();
    574         }
    575 
    576         @Override
    577         public void onRouteAdded(MediaRouter router, RouteInfo info) {
    578             mAdapter.update();
    579         }
    580 
    581         @Override
    582         public void onRouteRemoved(MediaRouter router, RouteInfo info) {
    583             if (info == mAdapter.mEditingGroup) {
    584                 mAdapter.finishGrouping();
    585             }
    586             mAdapter.update();
    587         }
    588 
    589         @Override
    590         public void onRouteChanged(MediaRouter router, RouteInfo info) {
    591             mAdapter.notifyDataSetChanged();
    592         }
    593 
    594         @Override
    595         public void onRouteGrouped(MediaRouter router, RouteInfo info,
    596                 RouteGroup group, int index) {
    597             mAdapter.update();
    598         }
    599 
    600         @Override
    601         public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
    602             mAdapter.update();
    603         }
    604 
    605         @Override
    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
    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 {
    622         public RouteChooserDialog(Context context, int theme) {
    623             super(context, theme);
    624         }
    625 
    626         @Override
    627         public void onBackPressed() {
    628             if (mAdapter != null && mAdapter.isGrouping()) {
    629                 mAdapter.finishGrouping();
    630             } else {
    631                 super.onBackPressed();
    632             }
    633         }
    634 
    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 
    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 {
    662         public void onDetached(MediaRouteChooserDialogFragment detachedFragment);
    663     }
    664 
    665     class VolumeSliderChangeListener implements SeekBar.OnSeekBarChangeListener {
    666 
    667         @Override
    668         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    669             changeVolume(progress);
    670         }
    671 
    672         @Override
    673         public void onStartTrackingTouch(SeekBar seekBar) {
    674             mIgnoreCallbackVolumeChanges = true;
    675         }
    676 
    677         @Override
    678         public void onStopTrackingTouch(SeekBar seekBar) {
    679             mIgnoreCallbackVolumeChanges = false;
    680             updateVolume();
    681         }
    682 
    683     }
    684 }
    685