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.setRouteTypes(mRouteTypes);
    123         mButton.setExtendedSettingsClickListener(mExtendedSettingsListener);
    124         mButton.setLayoutParams(new ViewGroup.LayoutParams(
    125                 ViewGroup.LayoutParams.WRAP_CONTENT,
    126                 ViewGroup.LayoutParams.MATCH_PARENT));
    127         return mButton;
    128     }
    129 
    130     @Override
    131     public boolean onPerformDefaultAction() {
    132         if (mButton != null) {
    133             return mButton.showDialogInternal();
    134         }
    135         return false;
    136     }
    137 
    138     @Override
    139     public boolean overridesItemVisibility() {
    140         return true;
    141     }
    142 
    143     @Override
    144     public boolean isVisible() {
    145         return mRouter.isRouteAvailable(mRouteTypes,
    146                 MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE);
    147     }
    148 
    149     private void refreshRoute() {
    150         refreshVisibility();
    151     }
    152 
    153     private static class MediaRouterCallback extends MediaRouter.SimpleCallback {
    154         private final WeakReference<MediaRouteActionProvider> mProviderWeak;
    155 
    156         public MediaRouterCallback(MediaRouteActionProvider provider) {
    157             mProviderWeak = new WeakReference<MediaRouteActionProvider>(provider);
    158         }
    159 
    160         @Override
    161         public void onRouteAdded(MediaRouter router, RouteInfo info) {
    162             refreshRoute(router);
    163         }
    164 
    165         @Override
    166         public void onRouteRemoved(MediaRouter router, RouteInfo info) {
    167             refreshRoute(router);
    168         }
    169 
    170         @Override
    171         public void onRouteChanged(MediaRouter router, RouteInfo info) {
    172             refreshRoute(router);
    173         }
    174 
    175         private void refreshRoute(MediaRouter router) {
    176             MediaRouteActionProvider provider = mProviderWeak.get();
    177             if (provider != null) {
    178                 provider.refreshRoute();
    179             } else {
    180                 router.removeCallback(this);
    181             }
    182         }
    183     }
    184 }
    185