Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2013 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.Dialog;
     22 import android.app.MediaRouteActionProvider;
     23 import android.app.MediaRouteButton;
     24 import android.content.Context;
     25 import android.graphics.drawable.Drawable;
     26 import android.media.MediaRouter;
     27 import android.media.MediaRouter.RouteGroup;
     28 import android.media.MediaRouter.RouteInfo;
     29 import android.os.Bundle;
     30 import android.view.KeyEvent;
     31 import android.view.View;
     32 import android.view.Window;
     33 import android.widget.Button;
     34 import android.widget.FrameLayout;
     35 import android.widget.LinearLayout;
     36 import android.widget.SeekBar;
     37 
     38 /**
     39  * This class implements the route controller dialog for {@link MediaRouter}.
     40  * <p>
     41  * This dialog allows the user to control or disconnect from the currently selected route.
     42  * </p>
     43  *
     44  * @see MediaRouteButton
     45  * @see MediaRouteActionProvider
     46  *
     47  * TODO: Move this back into the API, as in the support library media router.
     48  */
     49 public class MediaRouteControllerDialog extends Dialog {
     50     // Time to wait before updating the volume when the user lets go of the seek bar
     51     // to allow the route provider time to propagate the change and publish a new
     52     // route descriptor.
     53     private static final int VOLUME_UPDATE_DELAY_MILLIS = 250;
     54 
     55     private final MediaRouter mRouter;
     56     private final MediaRouterCallback mCallback;
     57     private final MediaRouter.RouteInfo mRoute;
     58 
     59     private boolean mCreated;
     60     private Drawable mMediaRouteConnectingDrawable;
     61     private Drawable mMediaRouteOnDrawable;
     62     private Drawable mCurrentIconDrawable;
     63 
     64     private boolean mVolumeControlEnabled = true;
     65     private LinearLayout mVolumeLayout;
     66     private SeekBar mVolumeSlider;
     67     private boolean mVolumeSliderTouched;
     68 
     69     private View mControlView;
     70 
     71     private Button mDisconnectButton;
     72 
     73     public MediaRouteControllerDialog(Context context, int theme) {
     74         super(context, theme);
     75 
     76         mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
     77         mCallback = new MediaRouterCallback();
     78         mRoute = mRouter.getSelectedRoute();
     79     }
     80 
     81     /**
     82      * Gets the route that this dialog is controlling.
     83      */
     84     public MediaRouter.RouteInfo getRoute() {
     85         return mRoute;
     86     }
     87 
     88     /**
     89      * Provides the subclass an opportunity to create a view that will
     90      * be included within the body of the dialog to offer additional media controls
     91      * for the currently playing content.
     92      *
     93      * @param savedInstanceState The dialog's saved instance state.
     94      * @return The media control view, or null if none.
     95      */
     96     public View onCreateMediaControlView(Bundle savedInstanceState) {
     97         return null;
     98     }
     99 
    100     /**
    101      * Gets the media control view that was created by {@link #onCreateMediaControlView(Bundle)}.
    102      *
    103      * @return The media control view, or null if none.
    104      */
    105     public View getMediaControlView() {
    106         return mControlView;
    107     }
    108 
    109     /**
    110      * Sets whether to enable the volume slider and volume control using the volume keys
    111      * when the route supports it.
    112      * <p>
    113      * The default value is true.
    114      * </p>
    115      */
    116     public void setVolumeControlEnabled(boolean enable) {
    117         if (mVolumeControlEnabled != enable) {
    118             mVolumeControlEnabled = enable;
    119             if (mCreated) {
    120                 updateVolume();
    121             }
    122         }
    123     }
    124 
    125     /**
    126      * Returns whether to enable the volume slider and volume control using the volume keys
    127      * when the route supports it.
    128      */
    129     public boolean isVolumeControlEnabled() {
    130         return mVolumeControlEnabled;
    131     }
    132 
    133     @Override
    134     protected void onCreate(Bundle savedInstanceState) {
    135         super.onCreate(savedInstanceState);
    136 
    137         getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
    138 
    139         setContentView(R.layout.media_route_controller_dialog);
    140 
    141         mVolumeLayout = (LinearLayout)findViewById(R.id.media_route_volume_layout);
    142         mVolumeSlider = (SeekBar)findViewById(R.id.media_route_volume_slider);
    143         mVolumeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    144             private final Runnable mStopTrackingTouch = new Runnable() {
    145                 @Override
    146                 public void run() {
    147                     if (mVolumeSliderTouched) {
    148                         mVolumeSliderTouched = false;
    149                         updateVolume();
    150                     }
    151                 }
    152             };
    153 
    154             @Override
    155             public void onStartTrackingTouch(SeekBar seekBar) {
    156                 if (mVolumeSliderTouched) {
    157                     mVolumeSlider.removeCallbacks(mStopTrackingTouch);
    158                 } else {
    159                     mVolumeSliderTouched = true;
    160                 }
    161             }
    162 
    163             @Override
    164             public void onStopTrackingTouch(SeekBar seekBar) {
    165                 // Defer resetting mVolumeSliderTouched to allow the media route provider
    166                 // a little time to settle into its new state and publish the final
    167                 // volume update.
    168                 mVolumeSlider.postDelayed(mStopTrackingTouch, VOLUME_UPDATE_DELAY_MILLIS);
    169             }
    170 
    171             @Override
    172             public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    173                 if (fromUser) {
    174                     mRoute.requestSetVolume(progress);
    175                 }
    176             }
    177         });
    178 
    179         mDisconnectButton = (Button)findViewById(R.id.media_route_disconnect_button);
    180         mDisconnectButton.setOnClickListener(new View.OnClickListener() {
    181             @Override
    182             public void onClick(View v) {
    183                 if (mRoute.isSelected()) {
    184                     mRouter.getDefaultRoute().select();
    185                 }
    186                 dismiss();
    187             }
    188         });
    189 
    190         mCreated = true;
    191         if (update()) {
    192             mControlView = onCreateMediaControlView(savedInstanceState);
    193             FrameLayout controlFrame =
    194                     (FrameLayout)findViewById(R.id.media_route_control_frame);
    195             if (mControlView != null) {
    196                 controlFrame.addView(mControlView);
    197                 controlFrame.setVisibility(View.VISIBLE);
    198             } else {
    199                 controlFrame.setVisibility(View.GONE);
    200             }
    201         }
    202     }
    203 
    204 
    205     @Override
    206     public void onAttachedToWindow() {
    207         super.onAttachedToWindow();
    208 
    209         mRouter.addCallback(0, mCallback, MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
    210         update();
    211     }
    212 
    213     @Override
    214     public void onDetachedFromWindow() {
    215         mRouter.removeCallback(mCallback);
    216 
    217         super.onDetachedFromWindow();
    218     }
    219 
    220     @Override
    221     public boolean onKeyDown(int keyCode, KeyEvent event) {
    222         if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
    223                 || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
    224             mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1);
    225             return true;
    226         }
    227         return super.onKeyDown(keyCode, event);
    228     }
    229 
    230     @Override
    231     public boolean onKeyUp(int keyCode, KeyEvent event) {
    232         if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
    233                 || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
    234             return true;
    235         }
    236         return super.onKeyUp(keyCode, event);
    237     }
    238 
    239     private boolean update() {
    240         if (!mRoute.isSelected() || mRoute.isDefault()) {
    241             dismiss();
    242             return false;
    243         }
    244 
    245         setTitle(mRoute.getName());
    246         updateVolume();
    247 
    248         Drawable icon = getIconDrawable();
    249         if (icon != mCurrentIconDrawable) {
    250             mCurrentIconDrawable = icon;
    251             getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, icon);
    252         }
    253         return true;
    254     }
    255 
    256     private Drawable getIconDrawable() {
    257         if (mRoute.isConnecting()) {
    258             if (mMediaRouteConnectingDrawable == null) {
    259                 mMediaRouteConnectingDrawable = getContext().getDrawable(
    260                         R.drawable.ic_media_route_connecting_holo_dark);
    261             }
    262             return mMediaRouteConnectingDrawable;
    263         } else {
    264             if (mMediaRouteOnDrawable == null) {
    265                 mMediaRouteOnDrawable = getContext().getDrawable(
    266                         R.drawable.ic_media_route_on_holo_dark);
    267             }
    268             return mMediaRouteOnDrawable;
    269         }
    270     }
    271 
    272     private void updateVolume() {
    273         if (!mVolumeSliderTouched) {
    274             if (isVolumeControlAvailable()) {
    275                 mVolumeLayout.setVisibility(View.VISIBLE);
    276                 mVolumeSlider.setMax(mRoute.getVolumeMax());
    277                 mVolumeSlider.setProgress(mRoute.getVolume());
    278             } else {
    279                 mVolumeLayout.setVisibility(View.GONE);
    280             }
    281         }
    282     }
    283 
    284     private boolean isVolumeControlAvailable() {
    285         return mVolumeControlEnabled && mRoute.getVolumeHandling() ==
    286                 MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
    287     }
    288 
    289     private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
    290         @Override
    291         public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
    292             update();
    293         }
    294 
    295         @Override
    296         public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
    297             update();
    298         }
    299 
    300         @Override
    301         public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) {
    302             if (route == mRoute) {
    303                 updateVolume();
    304             }
    305         }
    306 
    307         @Override
    308         public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
    309                 int index) {
    310             update();
    311         }
    312 
    313         @Override
    314         public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
    315             update();
    316         }
    317     }
    318 }
    319