[go: nahoru, domu]

blob: ae15547656fb5e996db541bf711d391256dd9976 [file] [log] [blame]
Insun Kangfbbf8072018-04-09 14:51:51 +09001/*
Insun Kang930b1992019-04-19 19:07:12 +09002 * Copyright 2019 The Android Open Source Project
Insun Kangfbbf8072018-04-09 14:51:51 +09003 *
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
Insun Kang930b1992019-04-19 19:07:12 +090017package androidx.media2.session;
Insun Kangfbbf8072018-04-09 14:51:51 +090018
Insun Kang930b1992019-04-19 19:07:12 +090019import static androidx.media2.session.LibraryResult.RESULT_ERROR_NOT_SUPPORTED;
Jaewan Kim1cf1ac52018-10-24 14:44:03 +090020
Insun Kangfbbf8072018-04-09 14:51:51 +090021import android.app.PendingIntent;
Jaewan Kim48329542018-05-14 10:41:10 +090022import android.content.Context;
Insun Kangfbbf8072018-04-09 14:51:51 +090023import android.content.Intent;
Jaewan Kima25a8352018-06-16 14:03:12 +090024import android.media.browse.MediaBrowser;
Insun Kangfbbf8072018-04-09 14:51:51 +090025import android.os.Bundle;
26import android.os.IBinder;
Hyundo Moon10daf4c2018-10-26 20:18:26 +090027import android.text.TextUtils;
Insun Kangfbbf8072018-04-09 14:51:51 +090028
Hyundo Moon10daf4c2018-10-26 20:18:26 +090029import androidx.annotation.IntRange;
Insun Kangfbbf8072018-04-09 14:51:51 +090030import androidx.annotation.NonNull;
31import androidx.annotation.Nullable;
Insun Kangde8fad12018-08-31 15:28:09 +090032import androidx.core.content.ContextCompat;
Jaewan Kimfa9f9742020-04-13 22:05:18 +090033import androidx.media.MediaSessionManager.RemoteUserInfo;
Insun Kang930b1992019-04-19 19:07:12 +090034import androidx.media2.common.MediaMetadata;
35import androidx.media2.common.SessionPlayer;
36import androidx.media2.session.LibraryResult.ResultCode;
37import androidx.media2.session.MediaSession.ControllerInfo;
Jaewan Kim1cf1ac52018-10-24 14:44:03 +090038import androidx.versionedparcelable.ParcelField;
39import androidx.versionedparcelable.VersionedParcelable;
40import androidx.versionedparcelable.VersionedParcelize;
Insun Kangfbbf8072018-04-09 14:51:51 +090041
Insun Kangfbbf8072018-04-09 14:51:51 +090042import java.util.concurrent.Executor;
Insun Kangfbbf8072018-04-09 14:51:51 +090043
44/**
Jaewan Kim9ca6bb12018-08-07 16:33:57 +090045 * Base class for media library services, which is the service containing
46 * {@link MediaLibrarySession}.
Insun Kangfbbf8072018-04-09 14:51:51 +090047 * <p>
48 * Media library services enable applications to browse media content provided by an application
49 * and ask the application to start playing it. They may also be used to control content that
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +090050 * is already playing by way of a {@link MediaSession}.
Insun Kangfbbf8072018-04-09 14:51:51 +090051 * <p>
52 * When extending this class, also add the following to your {@code AndroidManifest.xml}.
53 * <pre>
54 * &lt;service android:name="component_name_of_your_implementation" &gt;
55 * &lt;intent-filter&gt;
Insun Kang930b1992019-04-19 19:07:12 +090056 * &lt;action android:name="androidx.media2.session.MediaLibraryService" /&gt;
Insun Kangfbbf8072018-04-09 14:51:51 +090057 * &lt;/intent-filter&gt;
58 * &lt;/service&gt;</pre>
Jaewan Kimd917b8d2018-08-08 14:51:48 +090059 * <p>
60 * You may also declare <pre>android.media.browse.MediaBrowserService</pre> for compatibility with
61 * {@link android.support.v4.media.MediaBrowserCompat}. This service can handle it automatically.
Insun Kangfbbf8072018-04-09 14:51:51 +090062 *
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +090063 * @see MediaSessionService
Insun Kangfbbf8072018-04-09 14:51:51 +090064 */
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +090065public abstract class MediaLibraryService extends MediaSessionService {
Insun Kangfbbf8072018-04-09 14:51:51 +090066 /**
Sungsoo Lim6bb93102018-07-17 13:02:23 +090067 * The {@link Intent} that must be declared as handled by the service.
Insun Kangfbbf8072018-04-09 14:51:51 +090068 */
Insun Kang930b1992019-04-19 19:07:12 +090069 public static final String SERVICE_INTERFACE = "androidx.media2.session.MediaLibraryService";
Insun Kangfbbf8072018-04-09 14:51:51 +090070
71 /**
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +090072 * Session for the {@link MediaLibraryService}. Build this object with
Hyundo Moon17552c72019-04-19 00:51:02 +090073 * {@link Builder} and return in {@link MediaSessionService#onGetSession(ControllerInfo)}.
Jaewan Kimfa9f9742020-04-13 22:05:18 +090074 *
75 * <h3 id="BackwardCompatibility">Backward compatibility with legacy media browser APIs</h3>
76 * Media library session supports connection from both {@link MediaBrowser} and
77 * {@link android.support.v4.media.MediaBrowserCompat}, but {@link ControllerInfo} may not be
78 * precise. Here are current limitations with details.
79 *
80 * <table>
81 * <tr><th>SDK version</th>
82 * <th>{@link ControllerInfo#getPackageName()}<br>for legacy browser</th>
83 * <th>{@link ControllerInfo#getUid()}<br>for legacy browser</th></tr>
84 * <tr><td>{@code SDK_VERSION} &lt; 21</td>
85 * <td>Actual package name via {@link Context#getPackageName()}</td>
86 * <td>Actual UID</td></tr>
87 * <tr><td>21 &ge; {@code SDK_VERSION} &lt; 28,<br>
88 * {@code MediaLibrarySessionCallback#onConnect} and<br>
89 * {@code MediaLibrarySessionCallback#onGetLibraryRoot}</td>
90 * <td>Actual package name via {@link Context#getPackageName()}</td>
91 * <td>Actual UID</td></tr>
92 * <tr><td>21 &ge; {@code SDK_VERSION} &lt; 28, for other callbacks</td>
93 * <td>{@link RemoteUserInfo#LEGACY_CONTROLLER}</td>
94 * <td>Negative value</td></tr>
95 * <tr><td>28 &ge; {@code SDK_VERSION}</td>
96 * <td>Actual package name via {@link Context#getPackageName()}</td>
97 * <td>Actual UID</td></tr>
98 * </table>
99 **/
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900100 public static final class MediaLibrarySession extends MediaSession {
Insun Kangfbbf8072018-04-09 14:51:51 +0900101 /**
102 * Callback for the {@link MediaLibrarySession}.
Kyunglyul Hyuna27fe4e2018-11-02 14:55:54 +0900103 * <p>
104 * When you return {@link LibraryResult} with media items,
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900105 * items must have valid {@link MediaMetadata#METADATA_KEY_MEDIA_ID} and
106 * specify {@link MediaMetadata#METADATA_KEY_BROWSABLE} and
107 * {@link MediaMetadata#METADATA_KEY_PLAYABLE}.
Insun Kangfbbf8072018-04-09 14:51:51 +0900108 */
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900109 public static class MediaLibrarySessionCallback extends MediaSession.SessionCallback {
Insun Kangfbbf8072018-04-09 14:51:51 +0900110 /**
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900111 * Called to get the root information for browsing by a {@link MediaBrowser}.
Insun Kangfbbf8072018-04-09 14:51:51 +0900112 * <p>
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900113 * To allow browsing media information, return the {@link LibraryResult} with the
Jaewan Kim0c5c4d62018-12-10 19:53:05 +0900114 * {@link LibraryResult#RESULT_SUCCESS} and the root media item with the valid
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900115 * {@link MediaMetadata#METADATA_KEY_MEDIA_ID media id}. The media id must be included
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900116 * for the browser to get the children under it.
Jaewan Kime57a84c2018-04-10 07:19:52 +0000117 * <p>
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900118 * Interoperability: this callback may be called on the main thread, regardless of the
119 * callback executor.
Insun Kangfbbf8072018-04-09 14:51:51 +0900120 *
121 * @param session the session for this event
Jaewan Kim69670762018-05-11 15:57:22 +0900122 * @param controller information of the controller requesting access to browse media.
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900123 * @param params An optional library params of service-specific arguments to send
124 * to the media library service when connecting and retrieving the
125 * root id for browsing, or {@code null} if none.
126 * @return a library result with the root media item with the id. A runtime exception
127 * will be thrown if an invalid result is returned.
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900128 * @see SessionCommand#COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT
129 * @see MediaMetadata#METADATA_KEY_MEDIA_ID
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900130 * @see LibraryParams
Insun Kangfbbf8072018-04-09 14:51:51 +0900131 */
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900132 @NonNull
133 public LibraryResult onGetLibraryRoot(@NonNull MediaLibrarySession session,
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900134 @NonNull ControllerInfo controller, @Nullable LibraryParams params) {
Jaewan Kim0c5c4d62018-12-10 19:53:05 +0900135 return new LibraryResult(RESULT_ERROR_NOT_SUPPORTED);
Insun Kangfbbf8072018-04-09 14:51:51 +0900136 }
137
138 /**
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900139 * Called to get an item.
Insun Kangfbbf8072018-04-09 14:51:51 +0900140 * <p>
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900141 * To allow getting the item, return the {@link LibraryResult} with the
Jaewan Kim0c5c4d62018-12-10 19:53:05 +0900142 * {@link LibraryResult#RESULT_SUCCESS} and the media item.
Insun Kangfbbf8072018-04-09 14:51:51 +0900143 *
144 * @param session the session for this event
Jaewan Kim69670762018-05-11 15:57:22 +0900145 * @param controller controller
Hyundo Moon184ac0a2018-11-06 16:52:50 +0900146 * @param mediaId non-empty media id of the requested item
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900147 * @return a library result with a media item with the id. A runtime exception
148 * will be thrown if an invalid result is returned.
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900149 * @see SessionCommand#COMMAND_CODE_LIBRARY_GET_ITEM
Insun Kangfbbf8072018-04-09 14:51:51 +0900150 */
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900151 @NonNull
152 public LibraryResult onGetItem(@NonNull MediaLibrarySession session,
Jaewan Kim69670762018-05-11 15:57:22 +0900153 @NonNull ControllerInfo controller, @NonNull String mediaId) {
Jaewan Kim0c5c4d62018-12-10 19:53:05 +0900154 return new LibraryResult(RESULT_ERROR_NOT_SUPPORTED);
Insun Kangfbbf8072018-04-09 14:51:51 +0900155 }
156
157 /**
158 * Called to get children of given parent id. Return the children here for the browser.
159 * <p>
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900160 * To allow getting the children, return the {@link LibraryResult} with the
Jaewan Kim0c5c4d62018-12-10 19:53:05 +0900161 * {@link LibraryResult#RESULT_SUCCESS} and the list of media item. Return an empty
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900162 * list for no children rather than using result code for error.
Insun Kangfbbf8072018-04-09 14:51:51 +0900163 *
164 * @param session the session for this event
Jaewan Kim69670762018-05-11 15:57:22 +0900165 * @param controller controller
Hyundo Moon184ac0a2018-11-06 16:52:50 +0900166 * @param parentId non-empty parent id to get children
Jaewan Kima8786412018-07-09 21:12:36 +0900167 * @param page page number. Starts from {@code 0}.
168 * @param pageSize page size. Should be greater or equal to {@code 1}.
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900169 * @param params library params
170 * @return a library result with a list of media item with the id. A runtime exception
171 * will be thrown if an invalid result is returned.
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900172 * @see SessionCommand#COMMAND_CODE_LIBRARY_GET_CHILDREN
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900173 * @see LibraryParams
Insun Kangfbbf8072018-04-09 14:51:51 +0900174 */
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900175 @NonNull
176 public LibraryResult onGetChildren(@NonNull MediaLibrarySession session,
Hyundo Moon3e2a8b32018-11-05 15:35:52 +0900177 @NonNull ControllerInfo controller, @NonNull String parentId,
178 @IntRange(from = 0) int page, @IntRange(from = 1) int pageSize,
179 @Nullable LibraryParams params) {
Jaewan Kim0c5c4d62018-12-10 19:53:05 +0900180 return new LibraryResult(RESULT_ERROR_NOT_SUPPORTED);
Insun Kangfbbf8072018-04-09 14:51:51 +0900181 }
182
183 /**
184 * Called when a controller subscribes to the parent.
185 * <p>
186 * It's your responsibility to keep subscriptions by your own and call
Hyundo Moon999d7142018-06-19 14:30:19 +0900187 * {@link MediaLibrarySession#notifyChildrenChanged(
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900188 * ControllerInfo, String, int, LibraryParams)} when the parent is changed until it's
189 * unsubscribed.
Jaewan Kima25a8352018-06-16 14:03:12 +0900190 * <p>
191 * Interoperability: This will be called when
192 * {@link android.support.v4.media.MediaBrowserCompat#subscribe} is called.
193 * However, this won't be called when {@link MediaBrowser#subscribe} is called.
Insun Kangfbbf8072018-04-09 14:51:51 +0900194 *
195 * @param session the session for this event
196 * @param controller controller
Hyundo Moon184ac0a2018-11-06 16:52:50 +0900197 * @param parentId non-empty parent id
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900198 * @param params library params
199 * @return result code
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900200 * @see SessionCommand#COMMAND_CODE_LIBRARY_SUBSCRIBE
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900201 * @see LibraryParams
Insun Kangfbbf8072018-04-09 14:51:51 +0900202 */
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900203 @ResultCode
204 public int onSubscribe(@NonNull MediaLibrarySession session,
Insun Kangfbbf8072018-04-09 14:51:51 +0900205 @NonNull ControllerInfo controller, @NonNull String parentId,
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900206 @Nullable LibraryParams params) {
Jaewan Kim0c5c4d62018-12-10 19:53:05 +0900207 return RESULT_ERROR_NOT_SUPPORTED;
Insun Kangfbbf8072018-04-09 14:51:51 +0900208 }
209
210 /**
211 * Called when a controller unsubscribes to the parent.
Jaewan Kima25a8352018-06-16 14:03:12 +0900212 * <p>
213 * Interoperability: This wouldn't be called if {@link MediaBrowser#unsubscribe} is
214 * called while works well with
215 * {@link android.support.v4.media.MediaBrowserCompat#unsubscribe}.
Insun Kangfbbf8072018-04-09 14:51:51 +0900216 *
217 * @param session the session for this event
218 * @param controller controller
Hyundo Moon184ac0a2018-11-06 16:52:50 +0900219 * @param parentId non-empty parent id
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900220 * @return result code
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900221 * @see SessionCommand#COMMAND_CODE_LIBRARY_UNSUBSCRIBE
Insun Kangfbbf8072018-04-09 14:51:51 +0900222 */
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900223 @ResultCode
224 public int onUnsubscribe(@NonNull MediaLibrarySession session,
Insun Kangfbbf8072018-04-09 14:51:51 +0900225 @NonNull ControllerInfo controller, @NonNull String parentId) {
Jaewan Kim0c5c4d62018-12-10 19:53:05 +0900226 return RESULT_ERROR_NOT_SUPPORTED;
Insun Kangfbbf8072018-04-09 14:51:51 +0900227 }
228
229 /**
230 * Called when a controller requests search.
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900231 * <p>
232 * Return immediately with the result of the attempt to search with the query. Notify
233 * the number of search result through
234 * {@link #notifySearchResultChanged(ControllerInfo, String, int, LibraryParams)}.
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900235 * {@link MediaBrowser} will ask the search result with the pagination later.
Insun Kangfbbf8072018-04-09 14:51:51 +0900236 *
237 * @param session the session for this event
Jaewan Kim69670762018-05-11 15:57:22 +0900238 * @param controller controller
Hyundo Moon184ac0a2018-11-06 16:52:50 +0900239 * @param query The non-empty search query sent from the media browser.
240 * It contains keywords separated by space.
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900241 * @param params library params
242 * @return result code
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900243 * @see SessionCommand#COMMAND_CODE_LIBRARY_SEARCH
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900244 * @see #notifySearchResultChanged(ControllerInfo, String, int, LibraryParams)
245 * @see LibraryParams
Insun Kangfbbf8072018-04-09 14:51:51 +0900246 */
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900247 @ResultCode
248 public int onSearch(@NonNull MediaLibrarySession session,
Jaewan Kim69670762018-05-11 15:57:22 +0900249 @NonNull ControllerInfo controller, @NonNull String query,
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900250 @Nullable LibraryParams params) {
Jaewan Kim0c5c4d62018-12-10 19:53:05 +0900251 return RESULT_ERROR_NOT_SUPPORTED;
Insun Kangfbbf8072018-04-09 14:51:51 +0900252 }
253
254 /**
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900255 * Called to get the search result.
Insun Kangfbbf8072018-04-09 14:51:51 +0900256 * <p>
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900257 * To allow getting the search result, return the {@link LibraryResult} with the
Jaewan Kim0c5c4d62018-12-10 19:53:05 +0900258 * {@link LibraryResult#RESULT_SUCCESS} and the list of media item. Return an empty
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900259 * list for no search result rather than using result code for error.
Jaewan Kima25a8352018-06-16 14:03:12 +0900260 * <p>
261 * This may be called with a query that hasn't called with {@link #onSearch}, especially
262 * when {@link android.support.v4.media.MediaBrowserCompat#search} is used.
Insun Kangfbbf8072018-04-09 14:51:51 +0900263 *
264 * @param session the session for this event
Jaewan Kim69670762018-05-11 15:57:22 +0900265 * @param controller controller
Hyundo Moon184ac0a2018-11-06 16:52:50 +0900266 * @param query The non-empty search query which was previously sent through
267 * {@link #onSearch}.
Jaewan Kima8786412018-07-09 21:12:36 +0900268 * @param page page number. Starts from {@code 0}.
Insun Kangfbbf8072018-04-09 14:51:51 +0900269 * @param pageSize page size. Should be greater or equal to {@code 1}.
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900270 * @param params library params
271 * @return a library result with a list of media item with the id. A runtime exception
272 * will be thrown if an invalid result is returned.
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900273 * @see SessionCommand#COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900274 * @see LibraryParams
Insun Kangfbbf8072018-04-09 14:51:51 +0900275 */
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900276 @NonNull
277 public LibraryResult onGetSearchResult(
Jaewan Kim69670762018-05-11 15:57:22 +0900278 @NonNull MediaLibrarySession session, @NonNull ControllerInfo controller,
Hyundo Moon3e2a8b32018-11-05 15:35:52 +0900279 @NonNull String query, @IntRange(from = 0) int page,
280 @IntRange(from = 1) int pageSize, @Nullable LibraryParams params) {
Jaewan Kim0c5c4d62018-12-10 19:53:05 +0900281 return new LibraryResult(RESULT_ERROR_NOT_SUPPORTED);
Insun Kangfbbf8072018-04-09 14:51:51 +0900282 }
283 }
284
285 /**
286 * Builder for {@link MediaLibrarySession}.
Jaewan Kim77bbd4b2018-12-26 11:15:48 +0900287 * <p>
288 * Any incoming event from the {@link MediaController} will be handled on the callback
289 * executor. If it's not set, {@link ContextCompat#getMainExecutor(Context)} will be used by
290 * default.
Insun Kangfbbf8072018-04-09 14:51:51 +0900291 */
292 // Override all methods just to show them with the type instead of generics in Javadoc.
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900293 // This workarounds javadoc issue described in the MediaSession.BuilderBase.
Jaewan Kim917d3a32018-04-16 23:05:18 +0900294 // Note: Don't override #setSessionCallback() because the callback can be set by the
295 // constructor.
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900296 public static final class Builder extends MediaSession.BuilderBase<MediaLibrarySession,
Insun Kangfbbf8072018-04-09 14:51:51 +0900297 Builder, MediaLibrarySessionCallback> {
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900298 // Builder requires MediaLibraryService instead of Context just to ensure that the
299 // builder can be only instantiated within the MediaLibraryService.
Jaewan Kim48329542018-05-14 10:41:10 +0900300 // Ideally it's better to make it inner class of service to enforce, but it violates API
Insun Kangfbbf8072018-04-09 14:51:51 +0900301 // guideline that Builders should be the inner class of the building target.
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900302 public Builder(@NonNull MediaLibraryService service,
303 @NonNull SessionPlayer player,
Insun Kangfbbf8072018-04-09 14:51:51 +0900304 @NonNull Executor callbackExecutor,
305 @NonNull MediaLibrarySessionCallback callback) {
Jaewan Kim85388bf2018-09-17 19:47:24 +0900306 super(service, player);
Insun Kangfbbf8072018-04-09 14:51:51 +0900307 setSessionCallback(callbackExecutor, callback);
308 }
309
310 @Override
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900311 @NonNull
312 public Builder setSessionActivity(@Nullable PendingIntent pi) {
Insun Kangfbbf8072018-04-09 14:51:51 +0900313 return super.setSessionActivity(pi);
314 }
315
316 @Override
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900317 @NonNull
318 public Builder setId(@NonNull String id) {
Insun Kangfbbf8072018-04-09 14:51:51 +0900319 return super.setId(id);
320 }
321
Hyundo Moone3b30342019-04-10 23:04:28 +0900322 @Override
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900323 @NonNull
Hyundo Moone3b30342019-04-10 23:04:28 +0900324 public Builder setExtras(@NonNull Bundle extras) {
325 return super.setExtras(extras);
326 }
327
Insun Kangfbbf8072018-04-09 14:51:51 +0900328 @Override
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900329 @NonNull
330 public MediaLibrarySession build() {
Jaewan Kim48329542018-05-14 10:41:10 +0900331 if (mCallbackExecutor == null) {
Insun Kangde8fad12018-08-31 15:28:09 +0900332 mCallbackExecutor = ContextCompat.getMainExecutor(mContext);
Jaewan Kim48329542018-05-14 10:41:10 +0900333 }
334 if (mCallback == null) {
335 mCallback = new MediaLibrarySession.MediaLibrarySessionCallback() {};
336 }
Jaewan Kim85388bf2018-09-17 19:47:24 +0900337 return new MediaLibrarySession(mContext, mId, mPlayer, mSessionActivity,
Hyundo Moone3b30342019-04-10 23:04:28 +0900338 mCallbackExecutor, mCallback, mExtras);
Insun Kangfbbf8072018-04-09 14:51:51 +0900339 }
340 }
341
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900342 MediaLibrarySession(Context context, String id, SessionPlayer player,
Jaewan Kim1f272812018-09-11 20:12:12 +0900343 PendingIntent sessionActivity, Executor callbackExecutor,
Hyundo Moone3b30342019-04-10 23:04:28 +0900344 MediaSession.SessionCallback callback, Bundle tokenExtras) {
345 super(context, id, player, sessionActivity, callbackExecutor, callback, tokenExtras);
Jaewan Kim48329542018-05-14 10:41:10 +0900346 }
347
348 @Override
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900349 MediaLibrarySessionImpl createImpl(Context context, String id, SessionPlayer player,
Jaewan Kim1f272812018-09-11 20:12:12 +0900350 PendingIntent sessionActivity, Executor callbackExecutor,
Hyundo Moone3b30342019-04-10 23:04:28 +0900351 MediaSession.SessionCallback callback, Bundle tokenExtras) {
Jaewan Kim1f272812018-09-11 20:12:12 +0900352 return new MediaLibrarySessionImplBase(this, context, id, player, sessionActivity,
Hyundo Moone3b30342019-04-10 23:04:28 +0900353 callbackExecutor, callback, tokenExtras);
Insun Kangfbbf8072018-04-09 14:51:51 +0900354 }
355
Jaewan Kim69670762018-05-11 15:57:22 +0900356 @Override
Sungsoo Limf2a4ed82018-07-16 13:31:25 +0900357 MediaLibrarySessionImpl getImpl() {
358 return (MediaLibrarySessionImpl) super.getImpl();
Jaewan Kim69670762018-05-11 15:57:22 +0900359 }
360
Insun Kangfbbf8072018-04-09 14:51:51 +0900361 /**
Kyunglyul Hyunbc6e6c72019-07-31 02:50:00 -0700362 * Notifies the controller of the change in a parent's children.
Insun Kangfbbf8072018-04-09 14:51:51 +0900363 * <p>
364 * If the controller hasn't subscribed to the parent, the API will do nothing.
365 * <p>
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900366 * Controllers will use {@link MediaBrowser#getChildren(String, int, int, LibraryParams)}
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900367 * to get the list of children.
Insun Kangfbbf8072018-04-09 14:51:51 +0900368 *
369 * @param controller controller to notify
Hyundo Moon184ac0a2018-11-06 16:52:50 +0900370 * @param parentId non-empty parent id with changes in its children
Insun Kangfbbf8072018-04-09 14:51:51 +0900371 * @param itemCount number of children.
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900372 * @param params library params
Insun Kangfbbf8072018-04-09 14:51:51 +0900373 */
374 public void notifyChildrenChanged(@NonNull ControllerInfo controller,
Hyundo Moon10daf4c2018-10-26 20:18:26 +0900375 @NonNull String parentId, @IntRange(from = 0) int itemCount,
376 @Nullable LibraryParams params) {
377 if (controller == null) {
Sungsoo Lim590422b2019-04-25 10:55:23 +0900378 throw new NullPointerException("controller shouldn't be null");
Hyundo Moon10daf4c2018-10-26 20:18:26 +0900379 }
Sungsoo Lim590422b2019-04-25 10:55:23 +0900380 if (parentId == null) {
381 throw new NullPointerException("parentId shouldn't be null");
382 } else if (TextUtils.isEmpty(parentId)) {
Hyundo Moon10daf4c2018-10-26 20:18:26 +0900383 throw new IllegalArgumentException("parentId shouldn't be empty");
384 }
385 if (itemCount < 0) {
386 throw new IllegalArgumentException("itemCount shouldn't be negative");
387 }
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900388 getImpl().notifyChildrenChanged(controller, parentId, itemCount, params);
Insun Kangfbbf8072018-04-09 14:51:51 +0900389 }
390
391 /**
Kyunglyul Hyunbc6e6c72019-07-31 02:50:00 -0700392 * Notifies all controllers that subscribed to the parent about change in the parent's
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900393 * children, regardless of the library params supplied by
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900394 * {@link MediaBrowser#subscribe(String, LibraryParams)}.
Hyundo Moon184ac0a2018-11-06 16:52:50 +0900395 * @param parentId non-empty parent id
Insun Kangfbbf8072018-04-09 14:51:51 +0900396 * @param itemCount number of children
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900397 * @param params library params
Insun Kangfbbf8072018-04-09 14:51:51 +0900398 */
399 // This is for the backward compatibility.
400 public void notifyChildrenChanged(@NonNull String parentId, int itemCount,
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900401 @Nullable LibraryParams params) {
Hyundo Moon10daf4c2018-10-26 20:18:26 +0900402 if (TextUtils.isEmpty(parentId)) {
403 throw new IllegalArgumentException("parentId shouldn't be empty");
404 }
405 if (itemCount < 0) {
406 throw new IllegalArgumentException("itemCount shouldn't be negative");
407 }
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900408 getImpl().notifyChildrenChanged(parentId, itemCount, params);
Insun Kangfbbf8072018-04-09 14:51:51 +0900409 }
410
411 /**
Kyunglyul Hyunbc6e6c72019-07-31 02:50:00 -0700412 * Notifies controller about change in the search result.
Insun Kangfbbf8072018-04-09 14:51:51 +0900413 *
414 * @param controller controller to notify
Hyundo Moon184ac0a2018-11-06 16:52:50 +0900415 * @param query previously sent non-empty search query from the controller.
Insun Kangfbbf8072018-04-09 14:51:51 +0900416 * @param itemCount the number of items that have been found in the search.
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900417 * @param params library params
Insun Kangfbbf8072018-04-09 14:51:51 +0900418 */
419 public void notifySearchResultChanged(@NonNull ControllerInfo controller,
Hyundo Moon10daf4c2018-10-26 20:18:26 +0900420 @NonNull String query, @IntRange(from = 0) int itemCount,
421 @Nullable LibraryParams params) {
422 if (controller == null) {
Sungsoo Lim590422b2019-04-25 10:55:23 +0900423 throw new NullPointerException("controller shouldn't be null");
Hyundo Moon10daf4c2018-10-26 20:18:26 +0900424 }
Sungsoo Lim590422b2019-04-25 10:55:23 +0900425 if (query == null) {
426 throw new NullPointerException("query shouldn't be null");
427 } else if (TextUtils.isEmpty(query)) {
Hyundo Moon10daf4c2018-10-26 20:18:26 +0900428 throw new IllegalArgumentException("query shouldn't be empty");
429 }
430 if (itemCount < 0) {
431 throw new IllegalArgumentException("itemCount shouldn't be negative");
432 }
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900433 getImpl().notifySearchResultChanged(controller, query, itemCount, params);
Insun Kangfbbf8072018-04-09 14:51:51 +0900434 }
435
Insun Kangfbbf8072018-04-09 14:51:51 +0900436 @Override
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900437 @NonNull
Insun Kangfbbf8072018-04-09 14:51:51 +0900438 MediaLibrarySessionCallback getCallback() {
439 return (MediaLibrarySessionCallback) super.getCallback();
440 }
Jaewan Kim69670762018-05-11 15:57:22 +0900441
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900442 interface MediaLibrarySessionImpl extends MediaSessionImpl {
Jaewan Kim69670762018-05-11 15:57:22 +0900443 // LibrarySession methods
444 void notifyChildrenChanged(
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900445 @NonNull String parentId, int itemCount, @Nullable LibraryParams params);
Jaewan Kim69670762018-05-11 15:57:22 +0900446 void notifyChildrenChanged(@NonNull ControllerInfo controller,
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900447 @NonNull String parentId, int itemCount, @Nullable LibraryParams params);
Jaewan Kim69670762018-05-11 15:57:22 +0900448 void notifySearchResultChanged(@NonNull ControllerInfo controller,
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900449 @NonNull String query, int itemCount, @Nullable LibraryParams params);
Jaewan Kim69670762018-05-11 15:57:22 +0900450
451 // LibrarySession callback implementations called on the executors
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900452 LibraryResult onGetLibraryRootOnExecutor(@NonNull ControllerInfo controller,
453 @Nullable LibraryParams params);
454 LibraryResult onGetItemOnExecutor(@NonNull ControllerInfo controller,
Jaewan Kim69670762018-05-11 15:57:22 +0900455 @NonNull String mediaId);
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900456 LibraryResult onGetChildrenOnExecutor(@NonNull ControllerInfo controller,
457 @NonNull String parentId, int page, int pageSize,
458 @Nullable LibraryParams params);
459 int onSubscribeOnExecutor(@NonNull ControllerInfo controller,
460 @NonNull String parentId, @Nullable LibraryParams params);
461 int onUnsubscribeOnExecutor(@NonNull ControllerInfo controller,
Jaewan Kim69670762018-05-11 15:57:22 +0900462 @NonNull String parentId);
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900463 int onSearchOnExecutor(@NonNull ControllerInfo controller, @NonNull String query,
464 @Nullable LibraryParams params);
465 LibraryResult onGetSearchResultOnExecutor(@NonNull ControllerInfo controller,
466 @NonNull String query, int page, int pageSize, @Nullable LibraryParams params);
Jaewan Kim69670762018-05-11 15:57:22 +0900467
468 // Internally used methods - only changing return type
469 @Override
470 MediaLibrarySession getInstance();
471
472 @Override
473 MediaLibrarySessionCallback getCallback();
Jaewan Kim69670762018-05-11 15:57:22 +0900474 }
Insun Kangfbbf8072018-04-09 14:51:51 +0900475 }
476
Jaewan Kim69670762018-05-11 15:57:22 +0900477 @Override
Sungsoo Lim9e1e3d52018-11-08 14:46:16 +0900478 MediaSessionServiceImpl createImpl() {
479 return new MediaLibraryServiceImplBase();
Jaewan Kime57a84c2018-04-10 07:19:52 +0000480 }
481
482 @Override
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900483 public IBinder onBind(@NonNull Intent intent) {
Insun Kangfbbf8072018-04-09 14:51:51 +0900484 return super.onBind(intent);
485 }
486
Insun Kangfbbf8072018-04-09 14:51:51 +0900487 @Override
Hyundo Moon17552c72019-04-19 00:51:02 +0900488 @Nullable
489 public abstract MediaLibrarySession onGetSession(@NonNull ControllerInfo controllerInfo);
Insun Kangfbbf8072018-04-09 14:51:51 +0900490
491 /**
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900492 * Contains information that the library service needs to send to the client.
493 * <p>
494 * When the browser supplies {@link LibraryParams}, it's optional field when getting the media
495 * item(s). The library session is recommended to do the best effort to provide such result.
496 * It's not an error even when the library session didn't return such items.
497 * <p>
498 * The library params returned in the library session callback must include the information
499 * about the returned media item(s).
Insun Kangfbbf8072018-04-09 14:51:51 +0900500 */
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900501 @VersionedParcelize
502 public static final class LibraryParams implements VersionedParcelable {
503 @ParcelField(1)
504 Bundle mBundle;
505
506 // Types are intentionally Integer for future extension of the value with less effort.
507 @ParcelField(2)
508 int mRecent;
509 @ParcelField(3)
510 int mOffline;
511 @ParcelField(4)
512 int mSuggested;
513
Gyumin Sim7bdf2932020-04-03 13:54:44 +0900514 // WARNING: Adding a new ParcelField may break old library users (b/152830728)
515
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900516 // For versioned parcelable.
517 LibraryParams() {
518 // no-op
519 }
520
521 @SuppressWarnings("WeakerAccess") /* synthetic access */
522 LibraryParams(Bundle bundle, boolean recent, boolean offline, boolean suggested) {
523 // Keeps the booleans in Integer type.
524 // Types are intentionally Integer for future extension of the value with less effort.
525 this(bundle,
526 convertToInteger(recent),
527 convertToInteger(offline),
528 convertToInteger(suggested));
529 }
530
531 private LibraryParams(Bundle bundle, int recent, int offline, int suggested) {
532 mBundle = bundle;
533 mRecent = recent;
534 mOffline = offline;
535 mSuggested = suggested;
536 }
537
538 private static int convertToInteger(boolean a) {
539 return a ? 1 : 0;
540 }
541
542 private static boolean convertToBoolean(int a) {
543 return a == 0 ? false : true;
544 }
Insun Kangfbbf8072018-04-09 14:51:51 +0900545
546 /**
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900547 * Returns {@code true} for recent media items.
548 * <p>
549 * When the browser supplies {@link LibraryParams} with the {@code true}, library
550 * session is recommended to provide such media items. If so, the library session
551 * implementation must return the params with the {@code true} as well. The list of
552 * media items is considered ordered by relevance, first being the top suggestion.
Insun Kangfbbf8072018-04-09 14:51:51 +0900553 *
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900554 * @return {@code true} for recent items. {@code false} otherwise.
Insun Kangfbbf8072018-04-09 14:51:51 +0900555 */
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900556 public boolean isRecent() {
557 return convertToBoolean(mRecent);
558 }
Insun Kangfbbf8072018-04-09 14:51:51 +0900559
560 /**
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900561 * Returns {@code true} for offline media items, which can be played without an internet
562 * connection.
563 * <p>
564 * When the browser supplies {@link LibraryParams} with the {@code true}, library
565 * session is recommended to provide such media items. If so, the library session
566 * implementation must return the params with the {@code true} as well.
Insun Kangfbbf8072018-04-09 14:51:51 +0900567 *
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900568 * @return {@code true} for offline items. {@code false} otherwise.
569 **/
570 public boolean isOffline() {
571 return convertToBoolean(mOffline);
572 }
Insun Kangfbbf8072018-04-09 14:51:51 +0900573
574 /**
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900575 * Returns {@code true} for suggested media items.
576 * <p>
577 * When the browser supplies {@link LibraryParams} with the {@code true}, library
578 * session is recommended to provide such media items. If so, the library session
579 * implementation must return the params with the {@code true} as well. The list of
580 * media items is considered ordered by relevance, first being the top suggestion.
581 *
582 * @return {@code true} for suggested items. {@code false} otherwise
583 **/
584 public boolean isSuggested() {
585 return convertToBoolean(mSuggested);
586 }
587
588 /**
589 * Gets the extras.
590 * <p>
591 * Extras are the private contract between browser and library session.
Insun Kangfbbf8072018-04-09 14:51:51 +0900592 */
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900593 @Nullable
594 public Bundle getExtras() {
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900595 return mBundle;
596 }
597
598 /**
Kyunglyul Hyunbc6e6c72019-07-31 02:50:00 -0700599 * Builds a {@link LibraryParams}.
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900600 */
601 public static final class Builder {
602 private boolean mRecent;
603 private boolean mOffline;
604 private boolean mSuggested;
605
606 private Bundle mBundle;
607
608 /**
609 * Sets whether recently played media item.
610 * <p>
611 * When the browser supplies the {@link LibraryParams} with the {@code true}, library
612 * session is recommended to provide such media items. If so, the library session
613 * implementation must return the params with the {@code true} as well.
614 *
615 * @param recent {@code true} for recent items. {@code false} otherwise.
616 * @return this builder
617 */
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900618 @NonNull
619 public Builder setRecent(boolean recent) {
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900620 mRecent = recent;
621 return this;
Insun Kangfbbf8072018-04-09 14:51:51 +0900622 }
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900623
624 /**
625 * Sets whether offline media items, which can be played without an internet connection.
626 * <p>
627 * When the browser supplies {@link LibraryParams} with the {@code true}, library
628 * session is recommended to provide such media items. If so, the library session
629 * implementation must return the params with the {@code true} as well.
630 *
631 * @param offline {@code true} for offline items. {@code false} otherwise.
632 * @return this builder
633 */
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900634 @NonNull
635 public Builder setOffline(boolean offline) {
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900636 mOffline = offline;
637 return this;
638 }
639
640 /**
641 * Sets whether suggested media items.
642 * <p>
643 * When the browser supplies {@link LibraryParams} with the {@code true}, library
644 * session is recommended to provide such media items. If so, the library session
645 * implementation must return the params with the {@code true} as well. The list of
646 * media items is considered ordered by relevance, first being the top suggestion.
647 *
648 * @param suggested {@code true} for suggested items. {@code false} otherwise
649 * @return this builder
650 */
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900651 @NonNull
652 public Builder setSuggested(boolean suggested) {
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900653 mSuggested = suggested;
654 return this;
655 }
656
657 /**
658 * Set a bundle of extras, that browser and library session can understand each other.
659 *
660 * @param extras The extras or null.
661 * @return this builder
662 */
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900663 @NonNull
664 public Builder setExtras(@Nullable Bundle extras) {
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900665 mBundle = extras;
666 return this;
667 }
668
669 /**
Kyunglyul Hyunbc6e6c72019-07-31 02:50:00 -0700670 * Builds a {@link LibraryParams}.
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900671 *
672 * @return new LibraryParams
673 */
Sungsoo Limfdb7e082019-06-20 07:54:42 +0900674 @NonNull
675 public LibraryParams build() {
Jaewan Kim1cf1ac52018-10-24 14:44:03 +0900676 return new LibraryParams(mBundle, mRecent, mOffline, mSuggested);
677 }
678 }
679 }
Jaewan Kim96464f12018-05-11 15:57:22 +0900680}