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 android.app;
     18 
     19 import android.content.Context;
     20 import android.media.MediaRouter;
     21 import android.media.MediaRouter.RouteInfo;
     22 import android.util.Log;
     23 import android.view.ActionProvider;
     24 import android.view.MenuItem;
     25 import android.view.View;
     26 import android.view.ViewGroup;
     27 
     28 import java.lang.ref.WeakReference;
     29 
     30 /**
     31  * The media route action provider displays a {@link MediaRouteButton media route button}
     32  * in the application's {@link ActionBar} to allow the user to select routes and
     33  * to control the currently selected route.
     34  * <p>
     35  * The application must specify the kinds of routes that the user should be allowed
     36  * to select by specifying the route types with the {@link #setRouteTypes} method.
     37  * </p><p>
     38  * Refer to {@link MediaRouteButton} for a description of the button that will
     39  * appear in the action bar menu.  Note that instead of disabling the button
     40  * when no routes are available, the action provider will instead make the
     41  * menu item invisible.  In this way, the button will only be visible when it
     42  * is possible for the user to discover and select a matching route.
     43  * </p>
     44  */
     45 public class MediaRouteActionProvider extends ActionProvider {
     46     private static final String TAG = "MediaRouteActionProvider";
     47 
     48     private final Context mContext;
     49     private final MediaRouter mRouter;
     50     private final MediaRouterCallback mCallback;
     51 
     52     private int mRouteTypes;
     53     private MediaRouteButton mButton;
     54     private View.OnClickListener mExtendedSettingsListener;
     55 
     56     public MediaRouteActionProvider(Context context) {
     57         super(context);
     58 
     59         mContext = context;
     60         mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
     61         mCallback = new MediaRouterCallback(this);
     62 
     63         // Start with live audio by default.
     64         // TODO Update this when new route types are added; segment by API level
     65         // when different route types were added.
     66         setRouteTypes(MediaRouter.ROUTE_TYPE_LIVE_AUDIO);
     67     }
     68 
     69     /**
     70      * Sets the types of routes that will be shown in the media route chooser dialog
     71      * launched by this button.
     72      *
     73      * @param types The route types to match.
     74      */
     75     public void setRouteTypes(int types) {
     76         if (mRouteTypes != types) {
     77             // FIXME: We currently have no way of knowing whether the action provider
     78             // is still needed by the UI.  Unfortunately this means the action provider
     79             // may leak callbacks until garbage collection occurs.  This may result in
     80             // media route providers doing more work than necessary in the short term
     81             // while trying to discover routes that are no longer of interest to the
     82             // application.  To solve this problem, the action provider will need some
     83             // indication from the framework that it is being destroyed.
     84             if (mRouteTypes != 0) {
     85                 mRouter.removeCallback(mCallback);
     86             }
     87             mRouteTypes = types;
     88             if (types != 0) {
     89                 mRouter.addCallback(types, mCallback,
     90                         MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
     91             }
     92             refreshRoute();
     93 
     94             if (mButton != null) {
     95                 mButton.setRouteTypes(mRouteTypes);
     96             }
     97         }
     98     }
     99 
    100     public void setExtendedSettingsClickListener(View.OnClickListener listener) {
    101         mExtendedSettingsListener = listener;
    102         if (mButton != null) {
    103             mButton.setExtendedSettingsClickListener(listener);
    104         }
    105     }
    106 
    107     @Override
    108     @SuppressWarnings("deprecation")
    109     public View onCreateActionView() {
    110         throw new UnsupportedOperationException("Use onCreateActionView(MenuItem) instead.");
    111     }
    112 
    113     @Override
    114     public View onCreateActionView(MenuItem item) {
    115         if (mButton != null) {
    116             Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " +
    117                     "with a menu item. Don't reuse MediaRouteActionProvider instances! " +
    118                     "Abandoning the old one...");
    119         }
    120 
    121         mButton = new MediaRouteButton(mContext);
    122         mButton.setCheatSheetEnabled(true);
    123         mButton.setRouteTypes(mRouteTypes);
    124         mButton.setExtendedSettingsClickListener(mExtendedSettingsListener);
    125         mButton.setLayoutParams(new ViewGroup.LayoutParams(
    126                 ViewGroup.LayoutParams.WRAP_CONTENT,
    127                 ViewGroup.LayoutParams.MATCH_PARENT));
    128         return mButton;
    129     }
    130 
    131     @Override
    132     public boolean onPerformDefaultAction() {
    133         if (mButton != null) {
    134             return mButton.showDialogInternal();
    135         }
    136         return false;
    137     }
    138 
    139     @Override
    140     public boolean overridesItemVisibility() {
    141         return true;
    142     }
    143 
    144     @Override
    145     public boolean isVisible() {
    146         return mRouter.isRouteAvailable(mRouteTypes,
    147                 MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE);
    148     }
    149 
    150     private void refreshRoute() {
    151         refreshVisibility();
    152     }
    153 
    154     private static class MediaRouterCallback extends MediaRouter.SimpleCallback {
    155         private final WeakReference<MediaRouteActionProvider> mProviderWeak;
    156 
    157         public MediaRouterCallback(MediaRouteActionProvider provider) {
    158             mProviderWeak = new WeakReference<MediaRouteActionProvider>(provider);
    159         }
    160 
    161         @Override
    162         public void onRouteAdded(MediaRouter router, RouteInfo info) {
    163             refreshRoute(router);
    164         }
    165 
    166         @Override
    167         public void onRouteRemoved(MediaRouter router, RouteInfo info) {
    168             refreshRoute(router);
    169         }
    170 
    171         @Override
    172         public void onRouteChanged(MediaRouter router, RouteInfo info) {
    173             refreshRoute(router);
    174         }
    175 
    176         private void refreshRoute(MediaRouter router) {
    177             MediaRouteActionProvider provider = mProviderWeak.get();
    178             if (provider != null) {
    179                 provider.refreshRoute();
    180             } else {
    181                 router.removeCallback(this);
    182             }
    183         }
    184     }
    185 }
    186