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 * <service android:name="component_name_of_your_implementation" > 46 * <intent-filter> 47 * <action android:name="android.media.MediaLibraryService2" /> 48 * </intent-filter> 49 * </service></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