Home | History | Annotate | Download | only in media
      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 android.support.v7.media;
     18 
     19 import android.content.ComponentName;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.os.Handler;
     23 import android.os.Message;
     24 import android.support.v7.media.MediaRouter.ControlRequestCallback;
     25 
     26 /**
     27  * Media route providers are used to publish additional media routes for
     28  * use within an application.  Media route providers may also be declared
     29  * as a service to publish additional media routes to all applications
     30  * in the system.
     31  * <p>
     32  * The purpose of a media route provider is to discover media routes that satisfy
     33  * the criteria specified by the current {@link MediaRouteDiscoveryRequest} and publish a
     34  * {@link MediaRouteProviderDescriptor} with information about each route by calling
     35  * {@link #setDescriptor} to notify the currently registered {@link Callback}.
     36  * </p><p>
     37  * The provider should watch for changes to the discovery request by implementing
     38  * {@link #onDiscoveryRequestChanged} and updating the set of routes that it is
     39  * attempting to discover.  It should also handle route control requests such
     40  * as volume changes or {@link MediaControlIntent media control intents}
     41  * by implementing {@link #onCreateRouteController} to return a {@link RouteController}
     42  * for a particular route.
     43  * </p><p>
     44  * A media route provider may be used privately within the scope of a single
     45  * application process by calling {@link MediaRouter#addProvider MediaRouter.addProvider}
     46  * to add it to the local {@link MediaRouter}.  A media route provider may also be made
     47  * available globally to all applications by registering a {@link MediaRouteProviderService}
     48  * in the provider's manifest.  When the media route provider is registered
     49  * as a service, all applications that use the media router API will be able to
     50  * discover and used the provider's routes without having to install anything else.
     51  * </p><p>
     52  * This object must only be accessed on the main thread.
     53  * </p>
     54  */
     55 public abstract class MediaRouteProvider {
     56     private static final int MSG_DELIVER_DESCRIPTOR_CHANGED = 1;
     57     private static final int MSG_DELIVER_DISCOVERY_REQUEST_CHANGED = 2;
     58 
     59     private final Context mContext;
     60     private final ProviderMetadata mMetadata;
     61     private final ProviderHandler mHandler = new ProviderHandler();
     62 
     63     private Callback mCallback;
     64 
     65     private MediaRouteDiscoveryRequest mDiscoveryRequest;
     66     private boolean mPendingDiscoveryRequestChange;
     67 
     68     private MediaRouteProviderDescriptor mDescriptor;
     69     private boolean mPendingDescriptorChange;
     70 
     71     /**
     72      * Creates a media route provider.
     73      *
     74      * @param context The context.
     75      */
     76     public MediaRouteProvider(Context context) {
     77         this(context, null);
     78     }
     79 
     80     MediaRouteProvider(Context context, ProviderMetadata metadata) {
     81         if (context == null) {
     82             throw new IllegalArgumentException("context must not be null");
     83         }
     84 
     85         mContext = context;
     86         if (metadata == null) {
     87             mMetadata = new ProviderMetadata(new ComponentName(context, getClass()));
     88         } else {
     89             mMetadata = metadata;
     90         }
     91     }
     92 
     93     /**
     94      * Gets the context of the media route provider.
     95      */
     96     public final Context getContext() {
     97         return mContext;
     98     }
     99 
    100     /**
    101      * Gets the provider's handler which is associated with the main thread.
    102      */
    103     public final Handler getHandler() {
    104         return mHandler;
    105     }
    106 
    107     /**
    108      * Gets some metadata about the provider's implementation.
    109      */
    110     public final ProviderMetadata getMetadata() {
    111         return mMetadata;
    112     }
    113 
    114     /**
    115      * Sets a callback to invoke when the provider's descriptor changes.
    116      *
    117      * @param callback The callback to use, or null if none.
    118      */
    119     public final void setCallback(Callback callback) {
    120         MediaRouter.checkCallingThread();
    121         mCallback = callback;
    122     }
    123 
    124     /**
    125      * Gets the current discovery request which informs the provider about the
    126      * kinds of routes to discover and whether to perform active scanning.
    127      *
    128      * @return The current discovery request, or null if no discovery is needed at this time.
    129      *
    130      * @see #onDiscoveryRequestChanged
    131      */
    132     public final MediaRouteDiscoveryRequest getDiscoveryRequest() {
    133         return mDiscoveryRequest;
    134     }
    135 
    136     /**
    137      * Sets a discovery request to inform the provider about the kinds of
    138      * routes that its clients would like to discover and whether to perform active scanning.
    139      *
    140      * @param request The discovery request, or null if no discovery is needed at this time.
    141      *
    142      * @see #onDiscoveryRequestChanged
    143      */
    144     public final void setDiscoveryRequest(MediaRouteDiscoveryRequest request) {
    145         MediaRouter.checkCallingThread();
    146 
    147         if (mDiscoveryRequest == request
    148                 || (mDiscoveryRequest != null && mDiscoveryRequest.equals(request))) {
    149             return;
    150         }
    151 
    152         mDiscoveryRequest = request;
    153         if (!mPendingDiscoveryRequestChange) {
    154             mPendingDiscoveryRequestChange = true;
    155             mHandler.sendEmptyMessage(MSG_DELIVER_DISCOVERY_REQUEST_CHANGED);
    156         }
    157     }
    158 
    159     private void deliverDiscoveryRequestChanged() {
    160         mPendingDiscoveryRequestChange = false;
    161         onDiscoveryRequestChanged(mDiscoveryRequest);
    162     }
    163 
    164     /**
    165      * Called by the media router when the {@link MediaRouteDiscoveryRequest discovery request}
    166      * has changed.
    167      * <p>
    168      * Whenever an applications calls {@link MediaRouter#addCallback} to register
    169      * a callback, it also provides a selector to specify the kinds of routes that
    170      * it is interested in.  The media router combines all of these selectors together
    171      * to generate a {@link MediaRouteDiscoveryRequest} and notifies each provider when a change
    172      * occurs by calling {@link #setDiscoveryRequest} which posts a message to invoke
    173      * this method asynchronously.
    174      * </p><p>
    175      * The provider should examine the {@link MediaControlIntent media control categories}
    176      * in the discovery request's {@link MediaRouteSelector selector} to determine what
    177      * kinds of routes it should try to discover and whether it should perform active
    178      * or passive scans.  In many cases, the provider may be able to save power by
    179      * determining that the selector does not contain any categories that it supports
    180      * and it can therefore avoid performing any scans at all.
    181      * </p>
    182      *
    183      * @param request The new discovery request, or null if no discovery is needed at this time.
    184      *
    185      * @see MediaRouter#addCallback
    186      */
    187     public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) {
    188     }
    189 
    190     /**
    191      * Gets the provider's descriptor.
    192      * <p>
    193      * The descriptor describes the state of the media route provider and
    194      * the routes that it publishes.  Watch for changes to the descriptor
    195      * by registering a {@link Callback callback} with {@link #setCallback}.
    196      * </p>
    197      *
    198      * @return The media route provider descriptor, or null if none.
    199      *
    200      * @see Callback#onDescriptorChanged
    201      */
    202     public final MediaRouteProviderDescriptor getDescriptor() {
    203         return mDescriptor;
    204     }
    205 
    206     /**
    207      * Sets the provider's descriptor.
    208      * <p>
    209      * The provider must call this method to notify the currently registered
    210      * {@link Callback callback} about the change to the provider's descriptor.
    211      * </p>
    212      *
    213      * @param descriptor The updated route provider descriptor, or null if none.
    214      *
    215      * @see Callback#onDescriptorChanged
    216      */
    217     public final void setDescriptor(MediaRouteProviderDescriptor descriptor) {
    218         MediaRouter.checkCallingThread();
    219 
    220         if (mDescriptor != descriptor) {
    221             mDescriptor = descriptor;
    222             if (!mPendingDescriptorChange) {
    223                 mPendingDescriptorChange = true;
    224                 mHandler.sendEmptyMessage(MSG_DELIVER_DESCRIPTOR_CHANGED);
    225             }
    226         }
    227     }
    228 
    229     private void deliverDescriptorChanged() {
    230         mPendingDescriptorChange = false;
    231 
    232         if (mCallback != null) {
    233             mCallback.onDescriptorChanged(this, mDescriptor);
    234         }
    235     }
    236 
    237     /**
    238      * Called by the media router to obtain a route controller for a particular route.
    239      * <p>
    240      * The media router will invoke the {@link RouteController#onRelease} method of the route
    241      * controller when it is no longer needed to allow it to free its resources.
    242      * </p>
    243      *
    244      * @param routeId The unique id of the route.
    245      * @return The route controller.  Returns null if there is no such route or if the route
    246      * cannot be controlled using the route controller interface.
    247      */
    248     public RouteController onCreateRouteController(String routeId) {
    249         return null;
    250     }
    251 
    252     /**
    253      * Describes properties of the route provider's implementation.
    254      * <p>
    255      * This object is immutable once created.
    256      * </p>
    257      */
    258     public static final class ProviderMetadata {
    259         private final ComponentName mComponentName;
    260 
    261         ProviderMetadata(ComponentName componentName) {
    262             if (componentName == null) {
    263                 throw new IllegalArgumentException("componentName must not be null");
    264             }
    265             mComponentName = componentName;
    266         }
    267 
    268         /**
    269          * Gets the provider's package name.
    270          */
    271         public String getPackageName() {
    272             return mComponentName.getPackageName();
    273         }
    274 
    275         /**
    276          * Gets the provider's component name.
    277          */
    278         public ComponentName getComponentName() {
    279             return mComponentName;
    280         }
    281 
    282         @Override
    283         public String toString() {
    284             return "ProviderMetadata{ componentName="
    285                     + mComponentName.flattenToShortString() + " }";
    286         }
    287     }
    288 
    289     /**
    290      * Provides control over a particular route.
    291      * <p>
    292      * The media router obtains a route controller for a route whenever it needs
    293      * to control a route.  When a route is selected, the media router invokes
    294      * the {@link #onSelect} method of its route controller.  While selected,
    295      * the media router may call other methods of the route controller to
    296      * request that it perform certain actions to the route.  When a route is
    297      * unselected, the media router invokes the {@link #onUnselect} method of its
    298      * route controller.  When the media route no longer needs the route controller
    299      * it will invoke the {@link #onRelease} method to allow the route controller
    300      * to free its resources.
    301      * </p><p>
    302      * There may be multiple route controllers simultaneously active for the
    303      * same route.  Each route controller will be released separately.
    304      * </p><p>
    305      * All operations on the route controller are asynchronous and
    306      * results are communicated via callbacks.
    307      * </p>
    308      */
    309     public static abstract class RouteController {
    310         /**
    311          * Releases the route controller, allowing it to free its resources.
    312          */
    313         public void onRelease() {
    314         }
    315 
    316         /**
    317          * Selects the route.
    318          */
    319         public void onSelect() {
    320         }
    321 
    322         /**
    323          * Unselects the route.
    324          */
    325         public void onUnselect() {
    326         }
    327 
    328         /**
    329          * Requests to set the volume of the route.
    330          *
    331          * @param volume The new volume value between 0 and {@link MediaRouteDescriptor#getVolumeMax}.
    332          */
    333         public void onSetVolume(int volume) {
    334         }
    335 
    336         /**
    337          * Requests an incremental volume update for the route.
    338          *
    339          * @param delta The delta to add to the current volume.
    340          */
    341         public void onUpdateVolume(int delta) {
    342         }
    343 
    344         /**
    345          * Performs a {@link MediaControlIntent media control} request
    346          * asynchronously on behalf of the route.
    347          *
    348          * @param intent A {@link MediaControlIntent media control intent}.
    349          * @param callback A {@link ControlRequestCallback} to invoke with the result
    350          * of the request, or null if no result is required.
    351          * @return True if the controller intends to handle the request and will
    352          * invoke the callback when finished.  False if the controller will not
    353          * handle the request and will not invoke the callback.
    354          *
    355          * @see MediaControlIntent
    356          */
    357         public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
    358             return false;
    359         }
    360     }
    361 
    362     /**
    363      * Callback which is invoked when route information becomes available or changes.
    364      */
    365     public static abstract class Callback {
    366         /**
    367          * Called when information about a route provider and its routes changes.
    368          *
    369          * @param provider The media route provider that changed, never null.
    370          * @param descriptor The new media route provider descriptor, or null if none.
    371          */
    372         public void onDescriptorChanged(MediaRouteProvider provider,
    373                 MediaRouteProviderDescriptor descriptor) {
    374         }
    375     }
    376 
    377     private final class ProviderHandler extends Handler {
    378         @Override
    379         public void handleMessage(Message msg) {
    380             switch (msg.what) {
    381                 case MSG_DELIVER_DESCRIPTOR_CHANGED:
    382                     deliverDescriptorChanged();
    383                     break;
    384                 case MSG_DELIVER_DISCOVERY_REQUEST_CHANGED:
    385                     deliverDiscoveryRequestChanged();
    386                     break;
    387             }
    388         }
    389     }
    390 }
    391