summaryrefslogtreecommitdiff
path: root/android/media/MediaLibraryService2.java
blob: d7e43ec9962a93be0e34edd7b49eb3d1e7046751 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
import android.media.MediaSession2.BuilderBase;
import android.media.MediaSession2.ControllerInfo;
import android.media.update.ApiLoader;
import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
import android.media.update.MediaSession2Provider;
import android.media.update.MediaSessionService2Provider;
import android.os.Bundle;
import android.service.media.MediaBrowserService.BrowserRoot;

import java.util.List;
import java.util.concurrent.Executor;

/**
 * Base class for media library services.
 * <p>
 * Media library services enable applications to browse media content provided by an application
 * and ask the application to start playing it. They may also be used to control content that
 * is already playing by way of a {@link MediaSession2}.
 * <p>
 * To extend this class, adding followings directly to your {@code AndroidManifest.xml}.
 * <pre>
 * &lt;service android:name="component_name_of_your_implementation" &gt;
 *   &lt;intent-filter&gt;
 *     &lt;action android:name="android.media.MediaLibraryService2" /&gt;
 *   &lt;/intent-filter&gt;
 * &lt;/service&gt;</pre>
 * <p>
 * A {@link MediaLibraryService2} is extension of {@link MediaSessionService2}. IDs shouldn't
 * be shared between the {@link MediaSessionService2} and {@link MediaSession2}. By
 * default, an empty string will be used for ID of the service. If you want to specify an ID,
 * declare metadata in the manifest as follows.
 * @hide
 */
// TODO(jaewan): Unhide
public abstract class MediaLibraryService2 extends MediaSessionService2 {
    /**
     * This is the interface name that a service implementing a session service should say that it
     * support -- that is, this is the action it uses for its intent filter.
     */
    public static final String SERVICE_INTERFACE = "android.media.MediaLibraryService2";

    /**
     * Session for the media library service.
     */
    public class MediaLibrarySession extends MediaSession2 {
        private final MediaLibrarySessionProvider mProvider;

        MediaLibrarySession(Context context, MediaPlayerBase player, String id,
                Executor callbackExecutor, SessionCallback callback, VolumeProvider volumeProvider,
                int ratingType, PendingIntent sessionActivity) {
            super(context, player, id, callbackExecutor, callback, volumeProvider, ratingType,
                    sessionActivity);
            mProvider = (MediaLibrarySessionProvider) getProvider();
        }

        @Override
        MediaSession2Provider createProvider(Context context, MediaPlayerBase player, String id,
                Executor callbackExecutor, SessionCallback callback, VolumeProvider volumeProvider,
                int ratingType, PendingIntent sessionActivity) {
            return ApiLoader.getProvider(context)
                    .createMediaLibraryService2MediaLibrarySession(this, context, player, id,
                            callbackExecutor, (MediaLibrarySessionCallback) callback,
                            volumeProvider, ratingType, sessionActivity);
        }

        /**
         * Notify subscribed controller about change in a parent's children.
         *
         * @param controller controller to notify
         * @param parentId
         * @param options
         */
        public void notifyChildrenChanged(@NonNull ControllerInfo controller,
                @NonNull String parentId, @NonNull Bundle options) {
            mProvider.notifyChildrenChanged_impl(controller, parentId, options);
        }

        /**
         * Notify subscribed controller about change in a parent's children.
         *
         * @param parentId parent id
         * @param options optional bundle
         */
        // This is for the backward compatibility.
        public void notifyChildrenChanged(@NonNull String parentId, @Nullable Bundle options) {
            mProvider.notifyChildrenChanged_impl(parentId, options);
        }
    }

    public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
        /**
         * Called to get the root information for browsing by a particular client.
         * <p>
         * The implementation should verify that the client package has permission
         * to access browse media information before returning the root id; it
         * should return null if the client is not allowed to access this
         * information.
         *
         * @param controllerInfo information of the controller requesting access to browse media.
         * @param rootHints An optional bundle of service-specific arguments to send
         * to the media browser service when connecting and retrieving the
         * root id for browsing, or null if none. The contents of this
         * bundle may affect the information returned when browsing.
         * @return The {@link BrowserRoot} for accessing this app's content or null.
         * @see BrowserRoot#EXTRA_RECENT
         * @see BrowserRoot#EXTRA_OFFLINE
         * @see BrowserRoot#EXTRA_SUGGESTED
         */
        public @Nullable BrowserRoot onGetRoot(@NonNull ControllerInfo controllerInfo,
                @Nullable Bundle rootHints) {
            return null;
        }

        /**
         * Called to get the search result. Return search result here for the browser.
         * <p>
         * Return an empty list for no search result, and return {@code null} for the error.
         *
         * @param query The search query sent from the media browser. It contains keywords separated
         *            by space.
         * @param extras The bundle of service-specific arguments sent from the media browser.
         * @return search result. {@code null} for error.
         */
        public @Nullable List<MediaItem2> onSearch(@NonNull ControllerInfo controllerInfo,
                @NonNull String query, @Nullable Bundle extras) {
            return null;
        }

        /**
         * Called to get the search result . Return result here for the browser.
         * <p>
         * Return an empty list for no search result, and return {@code null} for the error.
         *
         * @param itemId item id to get media item.
         * @return media item2. {@code null} for error.
         */
        public @Nullable MediaItem2 onLoadItem(@NonNull ControllerInfo controllerInfo,
                @NonNull String itemId) {
            return null;
        }

        /**
         * Called to get the search result. Return search result here for the browser.
         * <p>
         * Return an empty list for no search result, and return {@code null} for the error.
         *
         * @param parentId parent id to get children
         * @param page number of page
         * @param pageSize size of the page
         * @param options
         * @return list of children. Can be {@code null}.
         */
        public @Nullable List<MediaItem2> onLoadChildren(@NonNull ControllerInfo controller,
                @NonNull String parentId, int page, int pageSize, @Nullable Bundle options) {
            return null;
        }

        /**
         * Called when a controller subscribes to the parent.
         *
         * @param controller controller
         * @param parentId parent id
         * @param options optional bundle
         */
        public void onSubscribed(@NonNull ControllerInfo controller,
                String parentId, @Nullable Bundle options) {
        }

        /**
         * Called when a controller unsubscribes to the parent.
         *
         * @param controller controller
         * @param parentId parent id
         * @param options optional bundle
         */
        public void onUnsubscribed(@NonNull ControllerInfo controller,
                String parentId, @Nullable Bundle options) {
        }
    }

    /**
     * Builder for {@link MediaLibrarySession}.
     */
    // TODO(jaewan): Move this to updatable.
    public class MediaLibrarySessionBuilder
            extends BuilderBase<MediaLibrarySessionBuilder, MediaLibrarySessionCallback> {
        public MediaLibrarySessionBuilder(
                @NonNull Context context, @NonNull MediaPlayerBase player,
                @NonNull @CallbackExecutor Executor callbackExecutor,
                @NonNull MediaLibrarySessionCallback callback) {
            super(context, player);
            setSessionCallback(callbackExecutor, callback);
        }

        @Override
        public MediaLibrarySessionBuilder setSessionCallback(
                @NonNull @CallbackExecutor Executor callbackExecutor,
                @NonNull MediaLibrarySessionCallback callback) {
            if (callback == null) {
                throw new IllegalArgumentException("MediaLibrarySessionCallback cannot be null");
            }
            return super.setSessionCallback(callbackExecutor, callback);
        }

        @Override
        public MediaLibrarySession build() throws IllegalStateException {
            return new MediaLibrarySession(mContext, mPlayer, mId, mCallbackExecutor, mCallback,
                    mVolumeProvider, mRatingType, mSessionActivity);
        }
    }

    @Override
    MediaSessionService2Provider createProvider() {
        return ApiLoader.getProvider(this).createMediaLibraryService2(this);
    }

    /**
     * Called when another app requested to start this service.
     * <p>
     * Library service will accept or reject the connection with the
     * {@link MediaLibrarySessionCallback} in the created session.
     * <p>
     * Service wouldn't run if {@code null} is returned or session's ID doesn't match with the
     * expected ID that you've specified through the AndroidManifest.xml.
     * <p>
     * This method will be called on the main thread.
     *
     * @param sessionId session id written in the AndroidManifest.xml.
     * @return a new browser session
     * @see MediaLibrarySessionBuilder
     * @see #getSession()
     * @throws RuntimeException if returned session is invalid
     */
    @Override
    public @NonNull abstract MediaLibrarySession onCreateSession(String sessionId);

    /**
     * Contains information that the browser service needs to send to the client
     * when first connected.
     */
    public static final class BrowserRoot {
        /**
         * The lookup key for a boolean that indicates whether the browser service should return a
         * browser root for recently played media items.
         *
         * <p>When creating a media browser for a given media browser service, this key can be
         * supplied as a root hint for retrieving media items that are recently played.
         * If the media browser service can provide such media items, the implementation must return
         * the key in the root hint when
         * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
         *
         * <p>The root hint may contain multiple keys.
         *
         * @see #EXTRA_OFFLINE
         * @see #EXTRA_SUGGESTED
         */
        public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";

        /**
         * The lookup key for a boolean that indicates whether the browser service should return a
         * browser root for offline media items.
         *
         * <p>When creating a media browser for a given media browser service, this key can be
         * supplied as a root hint for retrieving media items that are can be played without an
         * internet connection.
         * If the media browser service can provide such media items, the implementation must return
         * the key in the root hint when
         * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
         *
         * <p>The root hint may contain multiple keys.
         *
         * @see #EXTRA_RECENT
         * @see #EXTRA_SUGGESTED
         */
        public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";

        /**
         * The lookup key for a boolean that indicates whether the browser service should return a
         * browser root for suggested media items.
         *
         * <p>When creating a media browser for a given media browser service, this key can be
         * supplied as a root hint for retrieving the media items suggested by the media browser
         * service. The list of media items passed in {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)}
         * is considered ordered by relevance, first being the top suggestion.
         * If the media browser service can provide such media items, the implementation must return
         * the key in the root hint when
         * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
         *
         * <p>The root hint may contain multiple keys.
         *
         * @see #EXTRA_RECENT
         * @see #EXTRA_OFFLINE
         */
        public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";

        final private String mRootId;
        final private Bundle mExtras;

        /**
         * Constructs a browser root.
         * @param rootId The root id for browsing.
         * @param extras Any extras about the browser service.
         */
        public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
            if (rootId == null) {
                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
                        "Use null for BrowserRoot instead.");
            }
            mRootId = rootId;
            mExtras = extras;
        }

        /**
         * Gets the root id for browsing.
         */
        public String getRootId() {
            return mRootId;
        }

        /**
         * Gets any extras about the browser service.
         */
        public Bundle getExtras() {
            return mExtras;
        }
    }
}