Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright 2018 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.media;
     18 
     19 import android.annotation.CallbackExecutor;
     20 import android.annotation.NonNull;
     21 import android.annotation.Nullable;
     22 import android.app.PendingIntent;
     23 import android.media.MediaLibraryService2.MediaLibrarySession.Builder;
     24 import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
     25 import android.media.MediaSession2.ControllerInfo;
     26 import android.media.update.ApiLoader;
     27 import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
     28 import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
     29 import android.media.update.MediaSessionService2Provider;
     30 import android.os.Bundle;
     31 
     32 import java.util.List;
     33 import java.util.concurrent.Executor;
     34 
     35 /**
     36  * @hide
     37  * Base class for media library services.
     38  * <p>
     39  * Media library services enable applications to browse media content provided by an application
     40  * and ask the application to start playing it. They may also be used to control content that
     41  * is already playing by way of a {@link MediaSession2}.
     42  * <p>
     43  * When extending this class, also add the following to your {@code AndroidManifest.xml}.
     44  * <pre>
     45  * &lt;service android:name="component_name_of_your_implementation" &gt;
     46  *   &lt;intent-filter&gt;
     47  *     &lt;action android:name="android.media.MediaLibraryService2" /&gt;
     48  *   &lt;/intent-filter&gt;
     49  * &lt;/service&gt;</pre>
     50  * <p>
     51  * The {@link MediaLibraryService2} class derives from {@link MediaSessionService2}. IDs shouldn't
     52  * be shared between the {@link MediaSessionService2} and {@link MediaSession2}. By
     53  * default, an empty string will be used for ID of the service. If you want to specify an ID,
     54  * declare metadata in the manifest as follows.
     55  *
     56  * @see MediaSessionService2
     57  */
     58 public abstract class MediaLibraryService2 extends MediaSessionService2 {
     59     /**
     60      * This is the interface name that a service implementing a session service should say that it
     61      * support -- that is, this is the action it uses for its intent filter.
     62      */
     63     public static final String SERVICE_INTERFACE = "android.media.MediaLibraryService2";
     64 
     65     /**
     66      * Session for the {@link MediaLibraryService2}. Build this object with
     67      * {@link Builder} and return in {@link #onCreateSession(String)}.
     68      */
     69     public static final class MediaLibrarySession extends MediaSession2 {
     70         private final MediaLibrarySessionProvider mProvider;
     71 
     72         /**
     73          * Callback for the {@link MediaLibrarySession}.
     74          */
     75         public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
     76             public MediaLibrarySessionCallback() {
     77                 super();
     78             }
     79 
     80             /**
     81              * Called to get the root information for browsing by a particular client.
     82              * <p>
     83              * The implementation should verify that the client package has permission
     84              * to access browse media information before returning the root id; it
     85              * should return null if the client is not allowed to access this
     86              * information.
     87              *
     88              * @param session the session for this event
     89              * @param controllerInfo information of the controller requesting access to browse media.
     90              * @param rootHints An optional bundle of service-specific arguments to send
     91              * to the media library service when connecting and retrieving the
     92              * root id for browsing, or null if none. The contents of this
     93              * bundle may affect the information returned when browsing.
     94              * @return The {@link LibraryRoot} for accessing this app's content or null.
     95              * @see LibraryRoot#EXTRA_RECENT
     96              * @see LibraryRoot#EXTRA_OFFLINE
     97              * @see LibraryRoot#EXTRA_SUGGESTED
     98              */
     99             public @Nullable LibraryRoot onGetLibraryRoot(@NonNull MediaLibrarySession session,
    100                     @NonNull ControllerInfo controllerInfo, @Nullable Bundle rootHints) {
    101                 return null;
    102             }
    103 
    104             /**
    105              * Called to get an item. Return result here for the browser.
    106              * <p>
    107              * Return {@code null} for no result or error.
    108              *
    109              * @param session the session for this event
    110              * @param mediaId item id to get media item.
    111              * @return a media item. {@code null} for no result or error.
    112              */
    113             public @Nullable MediaItem2 onGetItem(@NonNull MediaLibrarySession session,
    114                     @NonNull ControllerInfo controllerInfo, @NonNull String mediaId) {
    115                 return null;
    116             }
    117 
    118             /**
    119              * Called to get children of given parent id. Return the children here for the browser.
    120              * <p>
    121              * Return an empty list for no children, and return {@code null} for the error.
    122              *
    123              * @param session the session for this event
    124              * @param parentId parent id to get children
    125              * @param page number of page
    126              * @param pageSize size of the page
    127              * @param extras extra bundle
    128              * @return list of children. Can be {@code null}.
    129              */
    130             public @Nullable List<MediaItem2> onGetChildren(@NonNull MediaLibrarySession session,
    131                     @NonNull ControllerInfo controller, @NonNull String parentId, int page,
    132                     int pageSize, @Nullable Bundle extras) {
    133                 return null;
    134             }
    135 
    136             /**
    137              * Called when a controller subscribes to the parent.
    138              * <p>
    139              * It's your responsibility to keep subscriptions by your own and call
    140              * {@link MediaLibrarySession#notifyChildrenChanged(ControllerInfo, String, int, Bundle)}
    141              * when the parent is changed.
    142              *
    143              * @param session the session for this event
    144              * @param controller controller
    145              * @param parentId parent id
    146              * @param extras extra bundle
    147              */
    148             public void onSubscribe(@NonNull MediaLibrarySession session,
    149                     @NonNull ControllerInfo controller, @NonNull String parentId,
    150                     @Nullable Bundle extras) {
    151             }
    152 
    153             /**
    154              * Called when a controller unsubscribes to the parent.
    155              *
    156              * @param session the session for this event
    157              * @param controller controller
    158              * @param parentId parent id
    159              */
    160             public void onUnsubscribe(@NonNull MediaLibrarySession session,
    161                     @NonNull ControllerInfo controller, @NonNull String parentId) {
    162             }
    163 
    164             /**
    165              * Called when a controller requests search.
    166              *
    167              * @param session the session for this event
    168              * @param query The search query sent from the media browser. It contains keywords
    169              *              separated by space.
    170              * @param extras The bundle of service-specific arguments sent from the media browser.
    171              */
    172             public void onSearch(@NonNull MediaLibrarySession session,
    173                     @NonNull ControllerInfo controllerInfo, @NonNull String query,
    174                     @Nullable Bundle extras) {
    175             }
    176 
    177             /**
    178              * Called to get the search result. Return search result here for the browser which has
    179              * requested search previously.
    180              * <p>
    181              * Return an empty list for no search result, and return {@code null} for the error.
    182              *
    183              * @param session the session for this event
    184              * @param controllerInfo Information of the controller requesting the search result.
    185              * @param query The search query which was previously sent through
    186              *              {@link #onSearch(MediaLibrarySession, ControllerInfo, String, Bundle)}.
    187              * @param page page number. Starts from {@code 1}.
    188              * @param pageSize page size. Should be greater or equal to {@code 1}.
    189              * @param extras The bundle of service-specific arguments sent from the media browser.
    190              * @return search result. {@code null} for error.
    191              */
    192             public @Nullable List<MediaItem2> onGetSearchResult(
    193                     @NonNull MediaLibrarySession session, @NonNull ControllerInfo controllerInfo,
    194                     @NonNull String query, int page, int pageSize, @Nullable Bundle extras) {
    195                 return null;
    196             }
    197         }
    198 
    199         /**
    200          * Builder for {@link MediaLibrarySession}.
    201          */
    202         // Override all methods just to show them with the type instead of generics in Javadoc.
    203         // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
    204         public static final class Builder extends BuilderBase<MediaLibrarySession, Builder,
    205                 MediaLibrarySessionCallback> {
    206             // Builder requires MediaLibraryService2 instead of Context just to ensure that the
    207             // builder can be only instantiated within the MediaLibraryService2.
    208             // Ideally it's better to make it inner class of service to enforce, it violates API
    209             // guideline that Builders should be the inner class of the building target.
    210             public Builder(@NonNull MediaLibraryService2 service,
    211                     @NonNull @CallbackExecutor Executor callbackExecutor,
    212                     @NonNull MediaLibrarySessionCallback callback) {
    213                 super((instance) -> ApiLoader.getProvider().createMediaLibraryService2Builder(
    214                         service, (Builder) instance, callbackExecutor, callback));
    215             }
    216 
    217             @Override
    218             public Builder setPlayer(@NonNull MediaPlayerBase player) {
    219                 return super.setPlayer(player);
    220             }
    221 
    222             @Override
    223             public Builder setPlaylistAgent(@NonNull MediaPlaylistAgent playlistAgent) {
    224                 return super.setPlaylistAgent(playlistAgent);
    225             }
    226 
    227             @Override
    228             public Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
    229                 return super.setVolumeProvider(volumeProvider);
    230             }
    231 
    232             @Override
    233             public Builder setSessionActivity(@Nullable PendingIntent pi) {
    234                 return super.setSessionActivity(pi);
    235             }
    236 
    237             @Override
    238             public Builder setId(@NonNull String id) {
    239                 return super.setId(id);
    240             }
    241 
    242             @Override
    243             public Builder setSessionCallback(@NonNull @CallbackExecutor Executor executor,
    244                     @NonNull MediaLibrarySessionCallback callback) {
    245                 return super.setSessionCallback(executor, callback);
    246             }
    247 
    248             @Override
    249             public MediaLibrarySession build() {
    250                 return super.build();
    251             }
    252         }
    253 
    254         /**
    255          * @hide
    256          */
    257         public MediaLibrarySession(MediaLibrarySessionProvider provider) {
    258             super(provider);
    259             mProvider = provider;
    260         }
    261 
    262         /**
    263          * Notify the controller of the change in a parent's children.
    264          * <p>
    265          * If the controller hasn't subscribed to the parent, the API will do nothing.
    266          * <p>
    267          * Controllers will use {@link MediaBrowser2#getChildren(String, int, int, Bundle)} to get
    268          * the list of children.
    269          *
    270          * @param controller controller to notify
    271          * @param parentId parent id with changes in its children
    272          * @param itemCount number of children.
    273          * @param extras extra information from session to controller
    274          */
    275         public void notifyChildrenChanged(@NonNull ControllerInfo controller,
    276                 @NonNull String parentId, int itemCount, @Nullable Bundle extras) {
    277             mProvider.notifyChildrenChanged_impl(controller, parentId, itemCount, extras);
    278         }
    279 
    280         /**
    281          * Notify all controllers that subscribed to the parent about change in the parent's
    282          * children, regardless of the extra bundle supplied by
    283          * {@link MediaBrowser2#subscribe(String, Bundle)}.
    284          *
    285          * @param parentId parent id
    286          * @param itemCount number of children
    287          * @param extras extra information from session to controller
    288          */
    289         // This is for the backward compatibility.
    290         public void notifyChildrenChanged(@NonNull String parentId, int itemCount,
    291                 @Nullable Bundle extras) {
    292             mProvider.notifyChildrenChanged_impl(parentId, itemCount, extras);
    293         }
    294 
    295         /**
    296          * Notify controller about change in the search result.
    297          *
    298          * @param controller controller to notify
    299          * @param query previously sent search query from the controller.
    300          * @param itemCount the number of items that have been found in the search.
    301          * @param extras extra bundle
    302          */
    303         public void notifySearchResultChanged(@NonNull ControllerInfo controller,
    304                 @NonNull String query, int itemCount, @NonNull Bundle extras) {
    305             mProvider.notifySearchResultChanged_impl(controller, query, itemCount, extras);
    306         }
    307     }
    308 
    309     @Override
    310     MediaSessionService2Provider createProvider() {
    311         return ApiLoader.getProvider().createMediaLibraryService2(this);
    312     }
    313 
    314     /**
    315      * Called when another app requested to start this service.
    316      * <p>
    317      * Library service will accept or reject the connection with the
    318      * {@link MediaLibrarySessionCallback} in the created session.
    319      * <p>
    320      * Service wouldn't run if {@code null} is returned or session's ID doesn't match with the
    321      * expected ID that you've specified through the AndroidManifest.xml.
    322      * <p>
    323      * This method will be called on the main thread.
    324      *
    325      * @param sessionId session id written in the AndroidManifest.xml.
    326      * @return a new library session
    327      * @see Builder
    328      * @see #getSession()
    329      * @throws RuntimeException if returned session is invalid
    330      */
    331     @Override
    332     public @NonNull abstract MediaLibrarySession onCreateSession(String sessionId);
    333 
    334     /**
    335      * Contains information that the library service needs to send to the client when
    336      * {@link MediaBrowser2#getLibraryRoot(Bundle)} is called.
    337      */
    338     public static final class LibraryRoot {
    339         /**
    340          * The lookup key for a boolean that indicates whether the library service should return a
    341          * librar root for recently played media items.
    342          *
    343          * <p>When creating a media browser for a given media library service, this key can be
    344          * supplied as a root hint for retrieving media items that are recently played.
    345          * If the media library service can provide such media items, the implementation must return
    346          * the key in the root hint when
    347          * {@link MediaLibrarySessionCallback#onGetLibraryRoot(MediaLibrarySession, ControllerInfo, Bundle)}
    348          * is called back.
    349          *
    350          * <p>The root hint may contain multiple keys.
    351          *
    352          * @see #EXTRA_OFFLINE
    353          * @see #EXTRA_SUGGESTED
    354          */
    355         public static final String EXTRA_RECENT = "android.media.extra.RECENT";
    356 
    357         /**
    358          * The lookup key for a boolean that indicates whether the library service should return a
    359          * library root for offline media items.
    360          *
    361          * <p>When creating a media browser for a given media library service, this key can be
    362          * supplied as a root hint for retrieving media items that are can be played without an
    363          * internet connection.
    364          * If the media library service can provide such media items, the implementation must return
    365          * the key in the root hint when
    366          * {@link MediaLibrarySessionCallback#onGetLibraryRoot(MediaLibrarySession, ControllerInfo, Bundle)}
    367          * is called back.
    368          *
    369          * <p>The root hint may contain multiple keys.
    370          *
    371          * @see #EXTRA_RECENT
    372          * @see #EXTRA_SUGGESTED
    373          */
    374         public static final String EXTRA_OFFLINE = "android.media.extra.OFFLINE";
    375 
    376         /**
    377          * The lookup key for a boolean that indicates whether the library service should return a
    378          * library root for suggested media items.
    379          *
    380          * <p>When creating a media browser for a given media library service, this key can be
    381          * supplied as a root hint for retrieving the media items suggested by the media library
    382          * service. The list of media items is considered ordered by relevance, first being the top
    383          * suggestion.
    384          * If the media library service can provide such media items, the implementation must return
    385          * the key in the root hint when
    386          * {@link MediaLibrarySessionCallback#onGetLibraryRoot(MediaLibrarySession, ControllerInfo, Bundle)}
    387          * is called back.
    388          *
    389          * <p>The root hint may contain multiple keys.
    390          *
    391          * @see #EXTRA_RECENT
    392          * @see #EXTRA_OFFLINE
    393          */
    394         public static final String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED";
    395 
    396         private final LibraryRootProvider mProvider;
    397 
    398         /**
    399          * Constructs a library root.
    400          * @param rootId The root id for browsing.
    401          * @param extras Any extras about the library service.
    402          */
    403         public LibraryRoot(@NonNull String rootId, @Nullable Bundle extras) {
    404             mProvider = ApiLoader.getProvider().createMediaLibraryService2LibraryRoot(
    405                     this, rootId, extras);
    406         }
    407 
    408         /**
    409          * Gets the root id for browsing.
    410          */
    411         public String getRootId() {
    412             return mProvider.getRootId_impl();
    413         }
    414 
    415         /**
    416          * Gets any extras about the library service.
    417          */
    418         public Bundle getExtras() {
    419             return mProvider.getExtras_impl();
    420         }
    421     }
    422 }
    423