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.Context;
     20 import android.content.Intent;
     21 import android.os.Handler;
     22 import android.os.Message;
     23 import android.support.v7.media.MediaRouter.ControlRequestCallback;
     24 import android.text.TextUtils;
     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(context.getPackageName());
     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 String mPackageName;
    260 
    261         /**
    262          * Creates a provider metadata object.
    263          *
    264          * @param packageName The provider application's package name.
    265          */
    266         public ProviderMetadata(String packageName) {
    267             if (TextUtils.isEmpty(packageName)) {
    268                 throw new IllegalArgumentException("packageName must not be null or empty");
    269             }
    270             mPackageName = packageName;
    271         }
    272 
    273         /**
    274          * Gets the provider application's package name.
    275          */
    276         public String getPackageName() {
    277             return mPackageName;
    278         }
    279 
    280         @Override
    281         public String toString() {
    282             return "ProviderMetadata{ packageName=" + mPackageName + " }";
    283         }
    284     }
    285 
    286     /**
    287      * Provides control over a particular route.
    288      * <p>
    289      * The media router obtains a route controller for a route whenever it needs
    290      * to control a route.  When a route is selected, the media router invokes
    291      * the {@link #onSelect} method of its route controller.  While selected,
    292      * the media router may call other methods of the route controller to
    293      * request that it perform certain actions to the route.  When a route is
    294      * unselected, the media router invokes the {@link #onUnselect} method of its
    295      * route controller.  When the media route no longer needs the route controller
    296      * it will invoke the {@link #onRelease} method to allow the route controller
    297      * to free its resources.
    298      * </p><p>
    299      * There may be multiple route controllers simultaneously active for the
    300      * same route.  Each route controller will be released separately.
    301      * </p><p>
    302      * All operations on the route controller are asynchronous and
    303      * results are communicated via callbacks.
    304      * </p>
    305      */
    306     public static abstract class RouteController {
    307         /**
    308          * Releases the route controller, allowing it to free its resources.
    309          */
    310         public void onRelease() {
    311         }
    312 
    313         /**
    314          * Selects the route.
    315          */
    316         public void onSelect() {
    317         }
    318 
    319         /**
    320          * Unselects the route.
    321          */
    322         public void onUnselect() {
    323         }
    324 
    325         /**
    326          * Requests to set the volume of the route.
    327          *
    328          * @param volume The new volume value between 0 and {@link MediaRouteDescriptor#getVolumeMax}.
    329          */
    330         public void onSetVolume(int volume) {
    331         }
    332 
    333         /**
    334          * Requests an incremental volume update for the route.
    335          *
    336          * @param delta The delta to add to the current volume.
    337          */
    338         public void onUpdateVolume(int delta) {
    339         }
    340 
    341         /**
    342          * Performs a {@link MediaControlIntent media control} request
    343          * asynchronously on behalf of the route.
    344          *
    345          * @param intent A {@link MediaControlIntent media control intent}.
    346          * @param callback A {@link ControlRequestCallback} to invoke with the result
    347          * of the request, or null if no result is required.
    348          * @return True if the controller intends to handle the request and will
    349          * invoke the callback when finished.  False if the controller will not
    350          * handle the request and will not invoke the callback.
    351          *
    352          * @see MediaControlIntent
    353          */
    354         public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
    355             return false;
    356         }
    357     }
    358 
    359     /**
    360      * Callback which is invoked when route information becomes available or changes.
    361      */
    362     public static abstract class Callback {
    363         /**
    364          * Called when information about a route provider and its routes changes.
    365          *
    366          * @param provider The media route provider that changed, never null.
    367          * @param descriptor The new media route provider descriptor, or null if none.
    368          */
    369         public void onDescriptorChanged(MediaRouteProvider provider,
    370                 MediaRouteProviderDescriptor descriptor) {
    371         }
    372     }
    373 
    374     private final class ProviderHandler extends Handler {
    375         @Override
    376         public void handleMessage(Message msg) {
    377             switch (msg.what) {
    378                 case MSG_DELIVER_DESCRIPTOR_CHANGED:
    379                     deliverDescriptorChanged();
    380                     break;
    381                 case MSG_DELIVER_DISCOVERY_REQUEST_CHANGED:
    382                     deliverDiscoveryRequestChanged();
    383                     break;
    384             }
    385         }
    386     }
    387 }
    388