Justin Klaassen | b8042fc | 2018-04-15 00:41:15 -0400 | [diff] [blame] | 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 androidx.media; |
| 18 | |
| 19 | import static android.support.v4.media.MediaBrowserCompat.EXTRA_PAGE; |
| 20 | import static android.support.v4.media.MediaBrowserCompat.EXTRA_PAGE_SIZE; |
| 21 | |
| 22 | import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; |
| 23 | |
| 24 | import android.app.PendingIntent; |
| 25 | import android.content.Intent; |
| 26 | import android.os.Bundle; |
| 27 | import android.os.IBinder; |
| 28 | import android.support.v4.media.MediaBrowserCompat.MediaItem; |
| 29 | |
| 30 | import androidx.annotation.NonNull; |
| 31 | import androidx.annotation.Nullable; |
| 32 | import androidx.annotation.RestrictTo; |
| 33 | import androidx.media.MediaLibraryService2.MediaLibrarySession.Builder; |
| 34 | import androidx.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback; |
| 35 | import androidx.media.MediaSession2.ControllerInfo; |
| 36 | |
| 37 | import java.util.ArrayList; |
| 38 | import java.util.List; |
| 39 | import java.util.concurrent.CountDownLatch; |
| 40 | import java.util.concurrent.Executor; |
| 41 | |
| 42 | /** |
| 43 | * @hide |
| 44 | * Base class for media library services. |
| 45 | * <p> |
| 46 | * Media library services enable applications to browse media content provided by an application |
| 47 | * and ask the application to start playing it. They may also be used to control content that |
| 48 | * is already playing by way of a {@link MediaSession2}. |
| 49 | * <p> |
| 50 | * When extending this class, also add the following to your {@code AndroidManifest.xml}. |
| 51 | * <pre> |
| 52 | * <service android:name="component_name_of_your_implementation" > |
| 53 | * <intent-filter> |
| 54 | * <action android:name="android.media.MediaLibraryService2" /> |
| 55 | * </intent-filter> |
| 56 | * </service></pre> |
| 57 | * <p> |
| 58 | * The {@link MediaLibraryService2} class derives from {@link MediaSessionService2}. IDs shouldn't |
| 59 | * be shared between the {@link MediaSessionService2} and {@link MediaSession2}. By |
| 60 | * default, an empty string will be used for ID of the service. If you want to specify an ID, |
| 61 | * declare metadata in the manifest as follows. |
| 62 | * |
| 63 | * @see MediaSessionService2 |
| 64 | */ |
| 65 | @RestrictTo(LIBRARY_GROUP) |
| 66 | public abstract class MediaLibraryService2 extends MediaSessionService2 { |
| 67 | /** |
| 68 | * This is the interface name that a service implementing a session service should say that it |
| 69 | * support -- that is, this is the action it uses for its intent filter. |
| 70 | */ |
| 71 | public static final String SERVICE_INTERFACE = "android.media.MediaLibraryService2"; |
| 72 | |
| 73 | // TODO: Revisit this value. |
| 74 | |
| 75 | /** |
| 76 | * Session for the {@link MediaLibraryService2}. Build this object with |
| 77 | * {@link Builder} and return in {@link #onCreateSession(String)}. |
| 78 | */ |
| 79 | public static final class MediaLibrarySession extends MediaSession2 { |
| 80 | /** |
| 81 | * Callback for the {@link MediaLibrarySession}. |
| 82 | */ |
| 83 | public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback { |
| 84 | /** |
| 85 | * Called to get the root information for browsing by a particular client. |
| 86 | * <p> |
| 87 | * The implementation should verify that the client package has permission |
| 88 | * to access browse media information before returning the root id; it |
| 89 | * should return null if the client is not allowed to access this |
| 90 | * information. |
| 91 | * <p> |
| 92 | * Note: this callback may be called on the main thread, regardless of the callback |
| 93 | * executor. |
| 94 | * |
| 95 | * @param session the session for this event |
| 96 | * @param controllerInfo information of the controller requesting access to browse |
| 97 | * media. |
| 98 | * @param extras An optional bundle of service-specific arguments to send |
| 99 | * to the media library service when connecting and retrieving the |
| 100 | * root id for browsing, or null if none. The contents of this |
| 101 | * bundle may affect the information returned when browsing. |
| 102 | * @return The {@link LibraryRoot} for accessing this app's content or null. |
| 103 | * @see LibraryRoot#EXTRA_RECENT |
| 104 | * @see LibraryRoot#EXTRA_OFFLINE |
| 105 | * @see LibraryRoot#EXTRA_SUGGESTED |
| 106 | * @see SessionCommand2#COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT |
| 107 | */ |
| 108 | public @Nullable LibraryRoot onGetLibraryRoot(@NonNull MediaLibrarySession session, |
| 109 | @NonNull ControllerInfo controllerInfo, @Nullable Bundle extras) { |
| 110 | return null; |
| 111 | } |
| 112 | |
| 113 | /** |
| 114 | * Called to get an item. Return result here for the browser. |
| 115 | * <p> |
| 116 | * Return {@code null} for no result or error. |
| 117 | * |
| 118 | * @param session the session for this event |
| 119 | * @param mediaId item id to get media item. |
| 120 | * @return a media item. {@code null} for no result or error. |
| 121 | * @see SessionCommand2#COMMAND_CODE_LIBRARY_GET_ITEM |
| 122 | */ |
| 123 | public @Nullable MediaItem2 onGetItem(@NonNull MediaLibrarySession session, |
| 124 | @NonNull ControllerInfo controllerInfo, @NonNull String mediaId) { |
| 125 | return null; |
| 126 | } |
| 127 | |
| 128 | /** |
| 129 | * Called to get children of given parent id. Return the children here for the browser. |
| 130 | * <p> |
| 131 | * Return an empty list for no children, and return {@code null} for the error. |
| 132 | * |
| 133 | * @param session the session for this event |
| 134 | * @param parentId parent id to get children |
| 135 | * @param page number of page |
| 136 | * @param pageSize size of the page |
| 137 | * @param extras extra bundle |
| 138 | * @return list of children. Can be {@code null}. |
| 139 | * @see SessionCommand2#COMMAND_CODE_LIBRARY_GET_CHILDREN |
| 140 | */ |
| 141 | public @Nullable List<MediaItem2> onGetChildren(@NonNull MediaLibrarySession session, |
| 142 | @NonNull ControllerInfo controller, @NonNull String parentId, int page, |
| 143 | int pageSize, @Nullable Bundle extras) { |
| 144 | return null; |
| 145 | } |
| 146 | |
| 147 | /** |
| 148 | * Called when a controller subscribes to the parent. |
| 149 | * <p> |
| 150 | * It's your responsibility to keep subscriptions by your own and call |
| 151 | * {@link MediaLibrarySession#notifyChildrenChanged(ControllerInfo, String, int, Bundle)} |
| 152 | * when the parent is changed. |
| 153 | * |
| 154 | * @param session the session for this event |
| 155 | * @param controller controller |
| 156 | * @param parentId parent id |
| 157 | * @param extras extra bundle |
| 158 | * @see SessionCommand2#COMMAND_CODE_LIBRARY_SUBSCRIBE |
| 159 | */ |
| 160 | public void onSubscribe(@NonNull MediaLibrarySession session, |
| 161 | @NonNull ControllerInfo controller, @NonNull String parentId, |
| 162 | @Nullable Bundle extras) { |
| 163 | } |
| 164 | |
| 165 | /** |
| 166 | * Called when a controller unsubscribes to the parent. |
| 167 | * |
| 168 | * @param session the session for this event |
| 169 | * @param controller controller |
| 170 | * @param parentId parent id |
| 171 | * @see SessionCommand2#COMMAND_CODE_LIBRARY_UNSUBSCRIBE |
| 172 | */ |
| 173 | // TODO: Make this to be called. |
| 174 | public void onUnsubscribe(@NonNull MediaLibrarySession session, |
| 175 | @NonNull ControllerInfo controller, @NonNull String parentId) { |
| 176 | } |
| 177 | |
| 178 | /** |
| 179 | * Called when a controller requests search. |
| 180 | * |
| 181 | * @param session the session for this event |
| 182 | * @param query The search query sent from the media browser. It contains keywords |
| 183 | * separated by space. |
| 184 | * @param extras The bundle of service-specific arguments sent from the media browser. |
| 185 | * @see SessionCommand2#COMMAND_CODE_LIBRARY_SEARCH |
| 186 | */ |
| 187 | public void onSearch(@NonNull MediaLibrarySession session, |
| 188 | @NonNull ControllerInfo controllerInfo, @NonNull String query, |
| 189 | @Nullable Bundle extras) { |
| 190 | } |
| 191 | |
| 192 | /** |
| 193 | * Called to get the search result. Return search result here for the browser which has |
| 194 | * requested search previously. |
| 195 | * <p> |
| 196 | * Return an empty list for no search result, and return {@code null} for the error. |
| 197 | * |
| 198 | * @param session the session for this event |
| 199 | * @param controllerInfo Information of the controller requesting the search result. |
| 200 | * @param query The search query which was previously sent through |
| 201 | * {@link #onSearch(MediaLibrarySession, ControllerInfo, String, Bundle)}. |
| 202 | * @param page page number. Starts from {@code 1}. |
| 203 | * @param pageSize page size. Should be greater or equal to {@code 1}. |
| 204 | * @param extras The bundle of service-specific arguments sent from the media browser. |
| 205 | * @return search result. {@code null} for error. |
| 206 | * @see SessionCommand2#COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT |
| 207 | */ |
| 208 | public @Nullable List<MediaItem2> onGetSearchResult( |
| 209 | @NonNull MediaLibrarySession session, @NonNull ControllerInfo controllerInfo, |
| 210 | @NonNull String query, int page, int pageSize, @Nullable Bundle extras) { |
| 211 | return null; |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | /** |
| 216 | * Builder for {@link MediaLibrarySession}. |
| 217 | */ |
| 218 | // Override all methods just to show them with the type instead of generics in Javadoc. |
| 219 | // This workarounds javadoc issue described in the MediaSession2.BuilderBase. |
| 220 | public static final class Builder extends MediaSession2.BuilderBase<MediaLibrarySession, |
| 221 | Builder, MediaLibrarySessionCallback> { |
| 222 | private MediaLibrarySessionImplBase.Builder mImpl; |
| 223 | |
| 224 | // Builder requires MediaLibraryService2 instead of Context just to ensure that the |
| 225 | // builder can be only instantiated within the MediaLibraryService2. |
| 226 | // Ideally it's better to make it inner class of service to enforce, it violates API |
| 227 | // guideline that Builders should be the inner class of the building target. |
| 228 | public Builder(@NonNull MediaLibraryService2 service, |
| 229 | @NonNull Executor callbackExecutor, |
| 230 | @NonNull MediaLibrarySessionCallback callback) { |
| 231 | super(service); |
| 232 | mImpl = new MediaLibrarySessionImplBase.Builder(service); |
| 233 | setImpl(mImpl); |
| 234 | setSessionCallback(callbackExecutor, callback); |
| 235 | } |
| 236 | |
| 237 | @Override |
| 238 | public @NonNull Builder setPlayer(@NonNull MediaPlayerBase player) { |
| 239 | return super.setPlayer(player); |
| 240 | } |
| 241 | |
| 242 | @Override |
| 243 | public @NonNull Builder setPlaylistAgent(@NonNull MediaPlaylistAgent playlistAgent) { |
| 244 | return super.setPlaylistAgent(playlistAgent); |
| 245 | } |
| 246 | |
| 247 | @Override |
| 248 | public @NonNull Builder setVolumeProvider( |
| 249 | @Nullable VolumeProviderCompat volumeProvider) { |
| 250 | return super.setVolumeProvider(volumeProvider); |
| 251 | } |
| 252 | |
| 253 | @Override |
| 254 | public @NonNull Builder setSessionActivity(@Nullable PendingIntent pi) { |
| 255 | return super.setSessionActivity(pi); |
| 256 | } |
| 257 | |
| 258 | @Override |
| 259 | public @NonNull Builder setId(@NonNull String id) { |
| 260 | return super.setId(id); |
| 261 | } |
| 262 | |
| 263 | @Override |
| 264 | public @NonNull Builder setSessionCallback(@NonNull Executor executor, |
| 265 | @NonNull MediaLibrarySessionCallback callback) { |
| 266 | return super.setSessionCallback(executor, callback); |
| 267 | } |
| 268 | |
| 269 | @Override |
| 270 | public @NonNull MediaLibrarySession build() { |
| 271 | return super.build(); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | MediaLibrarySession(SupportLibraryImpl impl) { |
| 276 | super(impl); |
| 277 | } |
| 278 | |
| 279 | /** |
| 280 | * Notify the controller of the change in a parent's children. |
| 281 | * <p> |
| 282 | * If the controller hasn't subscribed to the parent, the API will do nothing. |
| 283 | * <p> |
| 284 | * Controllers will use {@link MediaBrowser2#getChildren(String, int, int, Bundle)} to get |
| 285 | * the list of children. |
| 286 | * |
| 287 | * @param controller controller to notify |
| 288 | * @param parentId parent id with changes in its children |
| 289 | * @param itemCount number of children. |
| 290 | * @param extras extra information from session to controller |
| 291 | */ |
| 292 | public void notifyChildrenChanged(@NonNull ControllerInfo controller, |
| 293 | @NonNull String parentId, int itemCount, @Nullable Bundle extras) { |
| 294 | Bundle options = new Bundle(extras); |
| 295 | options.putInt(MediaBrowser2.EXTRA_ITEM_COUNT, itemCount); |
| 296 | options.putBundle(MediaBrowser2.EXTRA_TARGET, controller.toBundle()); |
| 297 | } |
| 298 | |
| 299 | /** |
| 300 | * Notify all controllers that subscribed to the parent about change in the parent's |
| 301 | * children, regardless of the extra bundle supplied by |
| 302 | * {@link MediaBrowser2#subscribe(String, Bundle)}. |
| 303 | * |
| 304 | * @param parentId parent id |
| 305 | * @param itemCount number of children |
| 306 | * @param extras extra information from session to controller |
| 307 | */ |
| 308 | // This is for the backward compatibility. |
| 309 | public void notifyChildrenChanged(@NonNull String parentId, int itemCount, |
| 310 | @Nullable Bundle extras) { |
| 311 | Bundle options = new Bundle(extras); |
| 312 | options.putInt(MediaBrowser2.EXTRA_ITEM_COUNT, itemCount); |
| 313 | getServiceCompat().notifyChildrenChanged(parentId, options); |
| 314 | } |
| 315 | |
| 316 | /** |
| 317 | * Notify controller about change in the search result. |
| 318 | * |
| 319 | * @param controller controller to notify |
| 320 | * @param query previously sent search query from the controller. |
| 321 | * @param itemCount the number of items that have been found in the search. |
| 322 | * @param extras extra bundle |
| 323 | */ |
| 324 | public void notifySearchResultChanged(@NonNull ControllerInfo controller, |
| 325 | @NonNull String query, int itemCount, @NonNull Bundle extras) { |
| 326 | // TODO: Implement |
| 327 | } |
| 328 | |
| 329 | private MediaLibraryService2 getService() { |
| 330 | return (MediaLibraryService2) getContext(); |
| 331 | } |
| 332 | |
| 333 | private MediaBrowserServiceCompat getServiceCompat() { |
| 334 | return getService().getServiceCompat(); |
| 335 | } |
| 336 | |
| 337 | @Override |
| 338 | MediaLibrarySessionCallback getCallback() { |
| 339 | return (MediaLibrarySessionCallback) super.getCallback(); |
| 340 | } |
| 341 | } |
| 342 | |
| 343 | @Override |
| 344 | MediaBrowserServiceCompat createBrowserServiceCompat() { |
| 345 | return new MyBrowserService(); |
| 346 | } |
| 347 | |
| 348 | @Override |
| 349 | int getSessionType() { |
| 350 | return SessionToken2.TYPE_LIBRARY_SERVICE; |
| 351 | } |
| 352 | |
| 353 | @Override |
| 354 | public void onCreate() { |
| 355 | super.onCreate(); |
| 356 | |
| 357 | MediaSession2 session = getSession(); |
| 358 | if (!(session instanceof MediaLibrarySession)) { |
| 359 | throw new RuntimeException("Expected MediaLibrarySession, but returned MediaSession2"); |
| 360 | } |
| 361 | } |
| 362 | |
| 363 | private MediaLibrarySession getLibrarySession() { |
| 364 | return (MediaLibrarySession) getSession(); |
| 365 | } |
| 366 | |
| 367 | @Override |
| 368 | public IBinder onBind(Intent intent) { |
| 369 | return super.onBind(intent); |
| 370 | } |
| 371 | |
| 372 | /** |
| 373 | * Called when another app requested to start this service. |
| 374 | * <p> |
| 375 | * Library service will accept or reject the connection with the |
| 376 | * {@link MediaLibrarySessionCallback} in the created session. |
| 377 | * <p> |
| 378 | * Service wouldn't run if {@code null} is returned or session's ID doesn't match with the |
| 379 | * expected ID that you've specified through the AndroidManifest.xml. |
| 380 | * <p> |
| 381 | * This method will be called on the main thread. |
| 382 | * |
| 383 | * @param sessionId session id written in the AndroidManifest.xml. |
| 384 | * @return a new library session |
| 385 | * @see Builder |
| 386 | * @see #getSession() |
| 387 | * @throws RuntimeException if returned session is invalid |
| 388 | */ |
| 389 | @Override |
| 390 | public @NonNull abstract MediaLibrarySession onCreateSession(String sessionId); |
| 391 | |
| 392 | /** |
| 393 | * Contains information that the library service needs to send to the client when |
| 394 | * {@link MediaBrowser2#getLibraryRoot(Bundle)} is called. |
| 395 | */ |
| 396 | public static final class LibraryRoot { |
| 397 | /** |
| 398 | * The lookup key for a boolean that indicates whether the library service should return a |
| 399 | * librar root for recently played media items. |
| 400 | * |
| 401 | * <p>When creating a media browser for a given media library service, this key can be |
| 402 | * supplied as a root hint for retrieving media items that are recently played. |
| 403 | * If the media library service can provide such media items, the implementation must return |
| 404 | * the key in the root hint when |
| 405 | * {@link MediaLibrarySessionCallback#onGetLibraryRoot} |
| 406 | * is called back. |
| 407 | * |
| 408 | * <p>The root hint may contain multiple keys. |
| 409 | * |
| 410 | * @see #EXTRA_OFFLINE |
| 411 | * @see #EXTRA_SUGGESTED |
| 412 | */ |
| 413 | public static final String EXTRA_RECENT = "android.media.extra.RECENT"; |
| 414 | |
| 415 | /** |
| 416 | * The lookup key for a boolean that indicates whether the library service should return a |
| 417 | * library root for offline media items. |
| 418 | * |
| 419 | * <p>When creating a media browser for a given media library service, this key can be |
| 420 | * supplied as a root hint for retrieving media items that are can be played without an |
| 421 | * internet connection. |
| 422 | * If the media library service can provide such media items, the implementation must return |
| 423 | * the key in the root hint when |
| 424 | * {@link MediaLibrarySessionCallback#onGetLibraryRoot} |
| 425 | * is called back. |
| 426 | * |
| 427 | * <p>The root hint may contain multiple keys. |
| 428 | * |
| 429 | * @see #EXTRA_RECENT |
| 430 | * @see #EXTRA_SUGGESTED |
| 431 | */ |
| 432 | public static final String EXTRA_OFFLINE = "android.media.extra.OFFLINE"; |
| 433 | |
| 434 | /** |
| 435 | * The lookup key for a boolean that indicates whether the library service should return a |
| 436 | * library root for suggested media items. |
| 437 | * |
| 438 | * <p>When creating a media browser for a given media library service, this key can be |
| 439 | * supplied as a root hint for retrieving the media items suggested by the media library |
| 440 | * service. The list of media items is considered ordered by relevance, first being the top |
| 441 | * suggestion. |
| 442 | * If the media library service can provide such media items, the implementation must return |
| 443 | * the key in the root hint when |
| 444 | * {@link MediaLibrarySessionCallback#onGetLibraryRoot} |
| 445 | * is called back. |
| 446 | * |
| 447 | * <p>The root hint may contain multiple keys. |
| 448 | * |
| 449 | * @see #EXTRA_RECENT |
| 450 | * @see #EXTRA_OFFLINE |
| 451 | */ |
| 452 | public static final String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED"; |
| 453 | |
| 454 | private final String mRootId; |
| 455 | private final Bundle mExtras; |
| 456 | |
| 457 | //private final LibraryRootProvider mProvider; |
| 458 | |
| 459 | /** |
| 460 | * Constructs a library root. |
| 461 | * @param rootId The root id for browsing. |
| 462 | * @param extras Any extras about the library service. |
| 463 | */ |
| 464 | public LibraryRoot(@NonNull String rootId, @Nullable Bundle extras) { |
| 465 | if (rootId == null) { |
| 466 | throw new IllegalArgumentException("rootId shouldn't be null"); |
| 467 | } |
| 468 | mRootId = rootId; |
| 469 | mExtras = extras; |
| 470 | } |
| 471 | |
| 472 | /** |
| 473 | * Gets the root id for browsing. |
| 474 | */ |
| 475 | public String getRootId() { |
| 476 | return mRootId; |
| 477 | } |
| 478 | |
| 479 | /** |
| 480 | * Gets any extras about the library service. |
| 481 | */ |
| 482 | public Bundle getExtras() { |
| 483 | return mExtras; |
| 484 | } |
| 485 | } |
| 486 | |
| 487 | private class MyBrowserService extends MediaBrowserServiceCompat { |
| 488 | @Override |
| 489 | public BrowserRoot onGetRoot(String clientPackageName, int clientUid, |
| 490 | final Bundle extras) { |
| 491 | if (MediaUtils2.isDefaultLibraryRootHint(extras)) { |
| 492 | // For connection request from the MediaController2. accept the connection from |
| 493 | // here, and let MediaLibrarySession decide whether to accept or reject the |
| 494 | // controller. |
| 495 | return sDefaultBrowserRoot; |
| 496 | } |
| 497 | final CountDownLatch latch = new CountDownLatch(1); |
| 498 | // TODO: Revisit this when we support caller information. |
| 499 | final ControllerInfo info = new ControllerInfo(MediaLibraryService2.this, clientUid, -1, |
| 500 | clientPackageName, null); |
| 501 | MediaLibrarySession session = getLibrarySession(); |
| 502 | // Call onGetLibraryRoot() directly instead of execute on the executor. Here's the |
| 503 | // reason. |
| 504 | // We need to return browser root here. So if we run the callback on the executor, we |
| 505 | // should wait for the completion. |
| 506 | // However, we cannot wait if the callback executor is the main executor, which posts |
| 507 | // the runnable to the main thread's. In that case, since this onGetRoot() always runs |
| 508 | // on the main thread, the posted runnable for calling onGetLibraryRoot() wouldn't run |
| 509 | // in here. Even worse, we cannot know whether it would be run on the main thread or |
| 510 | // not. |
| 511 | // Because of the reason, just call onGetLibraryRoot directly here. onGetLibraryRoot() |
| 512 | // has documentation that it may be called on the main thread. |
| 513 | LibraryRoot libraryRoot = session.getCallback().onGetLibraryRoot( |
| 514 | session, info, extras); |
| 515 | if (libraryRoot == null) { |
| 516 | return null; |
| 517 | } |
| 518 | return new BrowserRoot(libraryRoot.getRootId(), libraryRoot.getExtras()); |
| 519 | } |
| 520 | |
| 521 | @Override |
| 522 | public void onLoadChildren(String parentId, Result<List<MediaItem>> result) { |
| 523 | onLoadChildren(parentId, result, null); |
| 524 | } |
| 525 | |
| 526 | @Override |
| 527 | public void onLoadChildren(final String parentId, final Result<List<MediaItem>> result, |
| 528 | final Bundle options) { |
| 529 | final ControllerInfo controller = getController(); |
| 530 | getLibrarySession().getCallbackExecutor().execute(new Runnable() { |
| 531 | @Override |
| 532 | public void run() { |
| 533 | int page = options.getInt(EXTRA_PAGE, -1); |
| 534 | int pageSize = options.getInt(EXTRA_PAGE_SIZE, -1); |
| 535 | if (page >= 0 && pageSize >= 0) { |
| 536 | // Requesting the list of children through the pagenation. |
| 537 | List<MediaItem2> children = getLibrarySession().getCallback().onGetChildren( |
| 538 | getLibrarySession(), controller, parentId, page, pageSize, options); |
| 539 | if (children == null) { |
| 540 | result.sendError(null); |
| 541 | } else { |
| 542 | List<MediaItem> list = new ArrayList<>(); |
| 543 | for (int i = 0; i < children.size(); i++) { |
| 544 | list.add(MediaUtils2.createMediaItem(children.get(i))); |
| 545 | } |
| 546 | result.sendResult(list); |
| 547 | } |
| 548 | } else { |
| 549 | // Only wants to register callbacks |
| 550 | getLibrarySession().getCallback().onSubscribe(getLibrarySession(), |
| 551 | controller, parentId, options); |
| 552 | } |
| 553 | } |
| 554 | }); |
| 555 | } |
| 556 | |
| 557 | @Override |
| 558 | public void onLoadItem(final String itemId, final Result<MediaItem> result) { |
| 559 | final ControllerInfo controller = getController(); |
| 560 | getLibrarySession().getCallbackExecutor().execute(new Runnable() { |
| 561 | @Override |
| 562 | public void run() { |
| 563 | MediaItem2 item = getLibrarySession().getCallback().onGetItem( |
| 564 | getLibrarySession(), controller, itemId); |
| 565 | if (item == null) { |
| 566 | result.sendError(null); |
| 567 | } else { |
| 568 | result.sendResult(MediaUtils2.createMediaItem(item)); |
| 569 | } |
| 570 | } |
| 571 | }); |
| 572 | } |
| 573 | |
| 574 | @Override |
| 575 | public void onSearch(String query, Bundle extras, Result<List<MediaItem>> result) { |
| 576 | // TODO: Implement |
| 577 | } |
| 578 | |
| 579 | @Override |
| 580 | public void onCustomAction(String action, Bundle extras, Result<Bundle> result) { |
| 581 | // TODO: Implement |
| 582 | } |
| 583 | |
| 584 | private ControllerInfo getController() { |
| 585 | // TODO: Implement, by using getBrowserRootHints() / getCurrentBrowserInfo() / ... |
| 586 | return null; |
| 587 | } |
| 588 | } |
| 589 | } |