28
28
import android .os .Bundle ;
29
29
import android .os .Handler ;
30
30
import android .os .HandlerThread ;
31
+ import android .os .Looper ;
31
32
import android .os .ResultReceiver ;
32
33
import android .support .v4 .media .session .MediaControllerCompat ;
33
34
import android .support .v4 .media .session .MediaSessionCompat ;
34
35
import android .text .TextUtils ;
35
36
import androidx .annotation .IntDef ;
36
37
import androidx .annotation .Nullable ;
38
+ import androidx .annotation .RequiresApi ;
37
39
import androidx .media .MediaBrowserServiceCompat ;
38
40
import androidx .media3 .common .Bundleable ;
39
41
import androidx .media3 .common .C ;
@@ -258,37 +260,86 @@ public Bundle getExtras() {
258
260
}
259
261
260
262
/**
261
- * Creates a token from {@link MediaSessionCompat .Token}.
263
+ * Creates a token from a {@link android.media.session.MediaSession .Token}.
262
264
*
263
- * @return a {@link ListenableFuture} of {@link SessionToken}
265
+ * @param context A {@link Context}.
266
+ * @param token The {@link android.media.session.MediaSession.Token}.
267
+ * @return A {@link ListenableFuture} for the {@link SessionToken}.
264
268
*/
269
+ @ SuppressWarnings ("UnnecessarilyFullyQualified" ) // Avoiding clash with Media3 MediaSession.
265
270
@ UnstableApi
271
+ @ RequiresApi (21 )
266
272
public static ListenableFuture <SessionToken > createSessionToken (
267
- Context context , Object compatToken ) {
268
- checkNotNull (context , "context must not be null" );
269
- checkNotNull (compatToken , "compatToken must not be null" );
270
- checkArgument (compatToken instanceof MediaSessionCompat .Token );
273
+ Context context , android .media .session .MediaSession .Token token ) {
274
+ return createSessionToken (context , MediaSessionCompat .Token .fromToken (token ));
275
+ }
271
276
277
+ /**
278
+ * Creates a token from a {@link android.media.session.MediaSession.Token}.
279
+ *
280
+ * @param context A {@link Context}.
281
+ * @param token The {@link android.media.session.MediaSession.Token}.
282
+ * @param completionLooper The {@link Looper} on which the returned {@link ListenableFuture}
283
+ * completes. This {@link Looper} can't be used to call {@code future.get()} on the returned
284
+ * {@link ListenableFuture}.
285
+ * @return A {@link ListenableFuture} for the {@link SessionToken}.
286
+ */
287
+ @ SuppressWarnings ("UnnecessarilyFullyQualified" ) // Avoiding clash with Media3 MediaSession.
288
+ @ UnstableApi
289
+ @ RequiresApi (21 )
290
+ public static ListenableFuture <SessionToken > createSessionToken (
291
+ Context context , android .media .session .MediaSession .Token token , Looper completionLooper ) {
292
+ return createSessionToken (context , MediaSessionCompat .Token .fromToken (token ), completionLooper );
293
+ }
294
+
295
+ /**
296
+ * Creates a token from a {@link MediaSessionCompat.Token}.
297
+ *
298
+ * @param context A {@link Context}.
299
+ * @param compatToken The {@link MediaSessionCompat.Token}.
300
+ * @return A {@link ListenableFuture} for the {@link SessionToken}.
301
+ */
302
+ @ UnstableApi
303
+ public static ListenableFuture <SessionToken > createSessionToken (
304
+ Context context , MediaSessionCompat .Token compatToken ) {
272
305
HandlerThread thread = new HandlerThread ("SessionTokenThread" );
273
306
thread .start ();
307
+ ListenableFuture <SessionToken > tokenFuture =
308
+ createSessionToken (context , compatToken , thread .getLooper ());
309
+ tokenFuture .addListener (thread ::quit , MoreExecutors .directExecutor ());
310
+ return tokenFuture ;
311
+ }
312
+
313
+ /**
314
+ * Creates a token from a {@link MediaSessionCompat.Token}.
315
+ *
316
+ * @param context A {@link Context}.
317
+ * @param compatToken The {@link MediaSessionCompat.Token}.
318
+ * @param completionLooper The {@link Looper} on which the returned {@link ListenableFuture}
319
+ * completes. This {@link Looper} can't be used to call {@code future.get()} on the returned
320
+ * {@link ListenableFuture}.
321
+ * @return A {@link ListenableFuture} for the {@link SessionToken}.
322
+ */
323
+ @ UnstableApi
324
+ public static ListenableFuture <SessionToken > createSessionToken (
325
+ Context context , MediaSessionCompat .Token compatToken , Looper completionLooper ) {
326
+ checkNotNull (context , "context must not be null" );
327
+ checkNotNull (compatToken , "compatToken must not be null" );
274
328
275
329
SettableFuture <SessionToken > future = SettableFuture .create ();
276
330
// Try retrieving media3 token by connecting to the session.
277
- MediaControllerCompat controller =
278
- createMediaControllerCompat (context , (MediaSessionCompat .Token ) compatToken );
331
+ MediaControllerCompat controller = new MediaControllerCompat (context , compatToken );
279
332
String packageName = controller .getPackageName ();
280
- Handler handler = new Handler (thread . getLooper () );
333
+ Handler handler = new Handler (completionLooper );
281
334
Runnable createFallbackLegacyToken =
282
335
() -> {
283
336
int uid = getUid (context .getPackageManager (), packageName );
284
337
SessionToken resultToken =
285
- new SessionToken (
286
- (MediaSessionCompat .Token ) compatToken ,
287
- packageName ,
288
- uid ,
289
- controller .getSessionInfo ());
338
+ new SessionToken (compatToken , packageName , uid , controller .getSessionInfo ());
290
339
future .set (resultToken );
291
340
};
341
+ // Post creating a fallback token if the command receives no result after a timeout.
342
+ handler .postDelayed (createFallbackLegacyToken , WAIT_TIME_MS_FOR_SESSION3_TOKEN );
292
343
controller .sendCommand (
293
344
MediaConstants .SESSION_COMMAND_REQUEST_SESSION3_TOKEN ,
294
345
/* params= */ null ,
@@ -306,17 +357,13 @@ protected void onReceiveResult(int resultCode, Bundle resultData) {
306
357
}
307
358
}
308
359
});
309
- // Post creating a fallback token if the command receives no result after a timeout.
310
- handler .postDelayed (createFallbackLegacyToken , WAIT_TIME_MS_FOR_SESSION3_TOKEN );
311
- future .addListener (() -> thread .quit (), MoreExecutors .directExecutor ());
312
360
return future ;
313
361
}
314
362
315
363
/**
316
- * Returns a {@link ImmutableSet} of {@link SessionToken} for media session services; {@link
317
- * MediaSessionService}, {@link MediaLibraryService}, and {@link MediaBrowserServiceCompat}
318
- * regardless of their activeness. This set represents media apps that publish {@link
319
- * MediaSession}.
364
+ * Returns an {@link ImmutableSet} of {@linkplain SessionToken session tokens} for media session
365
+ * services; {@link MediaSessionService}, {@link MediaLibraryService}, and {@link
366
+ * MediaBrowserServiceCompat} regardless of their activeness.
320
367
*
321
368
* <p>The app targeting API level 30 or higher must include a {@code <queries>} element in their
322
369
* manifest to get service tokens of other apps. See the following example and <a
@@ -334,6 +381,8 @@ protected void onReceiveResult(int resultCode, Bundle resultData) {
334
381
* </intent>
335
382
* }</pre>
336
383
*/
384
+ // We ask the app to declare the <queries> tags, so it's expected that they are missing.
385
+ @ SuppressWarnings ("QueryPermissionsNeeded" )
337
386
public static ImmutableSet <SessionToken > getAllServiceTokens (Context context ) {
338
387
PackageManager pm = context .getPackageManager ();
339
388
List <ResolveInfo > services = new ArrayList <>();
@@ -370,6 +419,8 @@ public static ImmutableSet<SessionToken> getAllServiceTokens(Context context) {
370
419
return sessionServiceTokens .build ();
371
420
}
372
421
422
+ // We ask the app to declare the <queries> tags, so it's expected that they are missing.
423
+ @ SuppressWarnings ("QueryPermissionsNeeded" )
373
424
private static boolean isInterfaceDeclared (
374
425
PackageManager manager , String serviceInterface , ComponentName serviceComponent ) {
375
426
Intent serviceIntent = new Intent (serviceInterface );
@@ -402,11 +453,6 @@ private static int getUid(PackageManager manager, String packageName) {
402
453
}
403
454
}
404
455
405
- private static MediaControllerCompat createMediaControllerCompat (
406
- Context context , MediaSessionCompat .Token sessionToken ) {
407
- return new MediaControllerCompat (context , sessionToken );
408
- }
409
-
410
456
/* package */ interface SessionTokenImpl extends Bundleable {
411
457
412
458
boolean isLegacySession ();
0 commit comments