mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Slightly disentangle MediaBrowser/Controller(Impl)Base/Legacy
These constructors are currently very intertwined, passing `this`
references from the constructor of one to the constructor of another
before the first constructor is complete (and so the `this` reference
isn't really valid yet).
This change uses checker framework `@UnderInitialization` and
`@NotOnlyInitialized` annotations to make it more clear that the
references are not available yet. For the one 'direct' access needed
in the second constructor (calling `getApplicationLooper()`) we now
pass the `applicationLooper` directly alongside (to avoid needing to
dereference the reference 'too early').
This change also ensures that where a class hierarchy has a
'dependent' class hierarchy, the 'subclass' instance is always used
(by both subclass and superclass) without casting or manually hiding
the superclass field, by defining an overridable `getFoo()` method
instead and always using it.
#minor-release
PiperOrigin-RevId: 462335043
(cherry picked from commit 287c757944)
This commit is contained in:
parent
06d3c07a1c
commit
0667c74dc5
7 changed files with 241 additions and 162 deletions
|
|
@ -38,6 +38,9 @@ import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import org.checkerframework.checker.initialization.qual.NotOnlyInitialized;
|
||||||
|
import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||||
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Browses media content offered by a {@link MediaLibraryService} in addition to the {@link
|
* Browses media content offered by a {@link MediaLibraryService} in addition to the {@link
|
||||||
|
|
@ -45,12 +48,6 @@ import java.util.concurrent.Executor;
|
||||||
*/
|
*/
|
||||||
public final class MediaBrowser extends MediaController {
|
public final class MediaBrowser extends MediaController {
|
||||||
|
|
||||||
private static final String WRONG_THREAD_ERROR_MESSAGE =
|
|
||||||
"MediaBrowser method is called from a wrong thread."
|
|
||||||
+ " See javadoc of MediaController for details.";
|
|
||||||
|
|
||||||
private final MediaBrowserImpl impl;
|
|
||||||
|
|
||||||
/** A builder for {@link MediaBrowser}. */
|
/** A builder for {@link MediaBrowser}. */
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
|
|
||||||
|
|
@ -201,6 +198,12 @@ public final class MediaBrowser extends MediaController {
|
||||||
@Nullable LibraryParams params) {}
|
@Nullable LibraryParams params) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String WRONG_THREAD_ERROR_MESSAGE =
|
||||||
|
"MediaBrowser method is called from a wrong thread."
|
||||||
|
+ " See javadoc of MediaController for details.";
|
||||||
|
|
||||||
|
@NotOnlyInitialized private @MonotonicNonNull MediaBrowserImpl impl;
|
||||||
|
|
||||||
/** Creates an instance from the {@link SessionToken}. */
|
/** Creates an instance from the {@link SessionToken}. */
|
||||||
/* package */ MediaBrowser(
|
/* package */ MediaBrowser(
|
||||||
Context context,
|
Context context,
|
||||||
|
|
@ -210,17 +213,24 @@ public final class MediaBrowser extends MediaController {
|
||||||
Looper applicationLooper,
|
Looper applicationLooper,
|
||||||
ConnectionCallback connectionCallback) {
|
ConnectionCallback connectionCallback) {
|
||||||
super(context, token, connectionHints, listener, applicationLooper, connectionCallback);
|
super(context, token, connectionHints, listener, applicationLooper, connectionCallback);
|
||||||
this.impl = (MediaBrowserImpl) super.impl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
/* package */ MediaBrowserImpl createImpl(
|
/* package */ @UnderInitialization
|
||||||
Context context, MediaController thisRef, SessionToken token, Bundle connectionHints) {
|
MediaBrowserImpl createImpl(
|
||||||
|
@UnderInitialization MediaBrowser this,
|
||||||
|
Context context,
|
||||||
|
SessionToken token,
|
||||||
|
Bundle connectionHints,
|
||||||
|
Looper applicationLooper) {
|
||||||
|
MediaBrowserImpl impl;
|
||||||
if (token.isLegacySession()) {
|
if (token.isLegacySession()) {
|
||||||
return new MediaBrowserImplLegacy(context, (MediaBrowser) thisRef, token);
|
impl = new MediaBrowserImplLegacy(context, this, token, applicationLooper);
|
||||||
} else {
|
} else {
|
||||||
return new MediaBrowserImplBase(context, thisRef, token, connectionHints);
|
impl = new MediaBrowserImplBase(context, this, token, connectionHints, applicationLooper);
|
||||||
}
|
}
|
||||||
|
this.impl = impl;
|
||||||
|
return impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -234,7 +244,7 @@ public final class MediaBrowser extends MediaController {
|
||||||
public ListenableFuture<LibraryResult<MediaItem>> getLibraryRoot(@Nullable LibraryParams params) {
|
public ListenableFuture<LibraryResult<MediaItem>> getLibraryRoot(@Nullable LibraryParams params) {
|
||||||
verifyApplicationThread();
|
verifyApplicationThread();
|
||||||
if (isConnected()) {
|
if (isConnected()) {
|
||||||
return impl.getLibraryRoot(params);
|
return checkNotNull(impl).getLibraryRoot(params);
|
||||||
}
|
}
|
||||||
return createDisconnectedFuture();
|
return createDisconnectedFuture();
|
||||||
}
|
}
|
||||||
|
|
@ -254,7 +264,7 @@ public final class MediaBrowser extends MediaController {
|
||||||
verifyApplicationThread();
|
verifyApplicationThread();
|
||||||
checkNotEmpty(parentId, "parentId must not be empty");
|
checkNotEmpty(parentId, "parentId must not be empty");
|
||||||
if (isConnected()) {
|
if (isConnected()) {
|
||||||
return impl.subscribe(parentId, params);
|
return checkNotNull(impl).subscribe(parentId, params);
|
||||||
}
|
}
|
||||||
return createDisconnectedFuture();
|
return createDisconnectedFuture();
|
||||||
}
|
}
|
||||||
|
|
@ -273,7 +283,7 @@ public final class MediaBrowser extends MediaController {
|
||||||
verifyApplicationThread();
|
verifyApplicationThread();
|
||||||
checkNotEmpty(parentId, "parentId must not be empty");
|
checkNotEmpty(parentId, "parentId must not be empty");
|
||||||
if (isConnected()) {
|
if (isConnected()) {
|
||||||
return impl.unsubscribe(parentId);
|
return checkNotNull(impl).unsubscribe(parentId);
|
||||||
}
|
}
|
||||||
return createDisconnectedFuture();
|
return createDisconnectedFuture();
|
||||||
}
|
}
|
||||||
|
|
@ -299,7 +309,7 @@ public final class MediaBrowser extends MediaController {
|
||||||
checkArgument(page >= 0, "page must not be negative");
|
checkArgument(page >= 0, "page must not be negative");
|
||||||
checkArgument(pageSize >= 1, "pageSize must not be less than 1");
|
checkArgument(pageSize >= 1, "pageSize must not be less than 1");
|
||||||
if (isConnected()) {
|
if (isConnected()) {
|
||||||
return impl.getChildren(parentId, page, pageSize, params);
|
return checkNotNull(impl).getChildren(parentId, page, pageSize, params);
|
||||||
}
|
}
|
||||||
return createDisconnectedFuture();
|
return createDisconnectedFuture();
|
||||||
}
|
}
|
||||||
|
|
@ -316,7 +326,7 @@ public final class MediaBrowser extends MediaController {
|
||||||
verifyApplicationThread();
|
verifyApplicationThread();
|
||||||
checkNotEmpty(mediaId, "mediaId must not be empty");
|
checkNotEmpty(mediaId, "mediaId must not be empty");
|
||||||
if (isConnected()) {
|
if (isConnected()) {
|
||||||
return impl.getItem(mediaId);
|
return checkNotNull(impl).getItem(mediaId);
|
||||||
}
|
}
|
||||||
return createDisconnectedFuture();
|
return createDisconnectedFuture();
|
||||||
}
|
}
|
||||||
|
|
@ -338,7 +348,7 @@ public final class MediaBrowser extends MediaController {
|
||||||
verifyApplicationThread();
|
verifyApplicationThread();
|
||||||
checkNotEmpty(query, "query must not be empty");
|
checkNotEmpty(query, "query must not be empty");
|
||||||
if (isConnected()) {
|
if (isConnected()) {
|
||||||
return impl.search(query, params);
|
return checkNotNull(impl).search(query, params);
|
||||||
}
|
}
|
||||||
return createDisconnectedFuture();
|
return createDisconnectedFuture();
|
||||||
}
|
}
|
||||||
|
|
@ -365,7 +375,7 @@ public final class MediaBrowser extends MediaController {
|
||||||
checkArgument(page >= 0, "page must not be negative");
|
checkArgument(page >= 0, "page must not be negative");
|
||||||
checkArgument(pageSize >= 1, "pageSize must not be less than 1");
|
checkArgument(pageSize >= 1, "pageSize must not be less than 1");
|
||||||
if (isConnected()) {
|
if (isConnected()) {
|
||||||
return impl.getSearchResult(query, page, pageSize, params);
|
return checkNotNull(impl).getSearchResult(query, page, pageSize, params);
|
||||||
}
|
}
|
||||||
return createDisconnectedFuture();
|
return createDisconnectedFuture();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import static androidx.media3.session.SessionCommand.COMMAND_CODE_LIBRARY_UNSUBS
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Looper;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
|
|
@ -37,18 +38,27 @@ import androidx.media3.session.SequencedFutureManager.SequencedFuture;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||||
|
|
||||||
/** Base implementation of MediaBrowser. */
|
/** Base implementation of MediaBrowser. */
|
||||||
/* package */ class MediaBrowserImplBase extends MediaControllerImplBase
|
/* package */ class MediaBrowserImplBase extends MediaControllerImplBase
|
||||||
implements MediaBrowser.MediaBrowserImpl {
|
implements MediaBrowser.MediaBrowserImpl {
|
||||||
|
|
||||||
|
private final MediaBrowser instance;
|
||||||
|
|
||||||
MediaBrowserImplBase(
|
MediaBrowserImplBase(
|
||||||
Context context, MediaController instance, SessionToken token, Bundle connectionHints) {
|
Context context,
|
||||||
super(context, instance, token, connectionHints);
|
@UnderInitialization MediaBrowser instance,
|
||||||
|
SessionToken token,
|
||||||
|
Bundle connectionHints,
|
||||||
|
Looper applicationLooper) {
|
||||||
|
super(context, instance, token, connectionHints, applicationLooper);
|
||||||
|
this.instance = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaBrowser getMediaBrowser() {
|
@Override
|
||||||
return (MediaBrowser) instance;
|
/* package */ MediaBrowser getInstance() {
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -157,10 +167,10 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||||
if (!isConnected()) {
|
if (!isConnected()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getMediaBrowser()
|
getInstance()
|
||||||
.notifyBrowserListener(
|
.notifyBrowserListener(
|
||||||
listener ->
|
listener ->
|
||||||
listener.onSearchResultChanged(getMediaBrowser(), query, itemCount, libraryParams));
|
listener.onSearchResultChanged(getInstance(), query, itemCount, libraryParams));
|
||||||
}
|
}
|
||||||
|
|
||||||
void notifyChildrenChanged(
|
void notifyChildrenChanged(
|
||||||
|
|
@ -168,10 +178,10 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||||
if (!isConnected()) {
|
if (!isConnected()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getMediaBrowser()
|
getInstance()
|
||||||
.notifyBrowserListener(
|
.notifyBrowserListener(
|
||||||
listener ->
|
listener ->
|
||||||
listener.onChildrenChanged(getMediaBrowser(), parentId, itemCount, libraryParams));
|
listener.onChildrenChanged(getInstance(), parentId, itemCount, libraryParams));
|
||||||
}
|
}
|
||||||
|
|
||||||
private <V> ListenableFuture<LibraryResult<V>> dispatchRemoteLibrarySessionTask(
|
private <V> ListenableFuture<LibraryResult<V>> dispatchRemoteLibrarySessionTask(
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import static androidx.media3.session.LibraryResult.RESULT_ERROR_UNKNOWN;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Looper;
|
||||||
import android.support.v4.media.MediaBrowserCompat;
|
import android.support.v4.media.MediaBrowserCompat;
|
||||||
import android.support.v4.media.MediaBrowserCompat.ItemCallback;
|
import android.support.v4.media.MediaBrowserCompat.ItemCallback;
|
||||||
import android.support.v4.media.MediaBrowserCompat.SubscriptionCallback;
|
import android.support.v4.media.MediaBrowserCompat.SubscriptionCallback;
|
||||||
|
|
@ -38,6 +39,7 @@ import com.google.common.util.concurrent.SettableFuture;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||||
|
|
||||||
/** Implementation of MediaBrowser with the {@link MediaBrowserCompat} for legacy support. */
|
/** Implementation of MediaBrowser with the {@link MediaBrowserCompat} for legacy support. */
|
||||||
/* package */ class MediaBrowserImplLegacy extends MediaControllerImplLegacy
|
/* package */ class MediaBrowserImplLegacy extends MediaControllerImplLegacy
|
||||||
|
|
@ -49,12 +51,20 @@ import java.util.List;
|
||||||
|
|
||||||
private final HashMap<String, List<SubscribeCallback>> subscribeCallbacks = new HashMap<>();
|
private final HashMap<String, List<SubscribeCallback>> subscribeCallbacks = new HashMap<>();
|
||||||
|
|
||||||
MediaBrowserImplLegacy(Context context, MediaBrowser instance, SessionToken token) {
|
private final MediaBrowser instance;
|
||||||
super(context, instance, token);
|
|
||||||
|
MediaBrowserImplLegacy(
|
||||||
|
Context context,
|
||||||
|
@UnderInitialization MediaBrowser instance,
|
||||||
|
SessionToken token,
|
||||||
|
Looper applicationLooper) {
|
||||||
|
super(context, instance, token, applicationLooper);
|
||||||
|
this.instance = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaBrowser getMediaBrowser() {
|
@Override
|
||||||
return (MediaBrowser) instance;
|
/* package*/ MediaBrowser getInstance() {
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -78,7 +88,8 @@ import java.util.List;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<LibraryResult<MediaItem>> getLibraryRoot(@Nullable LibraryParams params) {
|
public ListenableFuture<LibraryResult<MediaItem>> getLibraryRoot(@Nullable LibraryParams params) {
|
||||||
if (!instance.isSessionCommandAvailable(SessionCommand.COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT)) {
|
if (!getInstance()
|
||||||
|
.isSessionCommandAvailable(SessionCommand.COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT)) {
|
||||||
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
||||||
}
|
}
|
||||||
SettableFuture<LibraryResult<MediaItem>> result = SettableFuture.create();
|
SettableFuture<LibraryResult<MediaItem>> result = SettableFuture.create();
|
||||||
|
|
@ -103,7 +114,7 @@ import java.util.List;
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<LibraryResult<Void>> subscribe(
|
public ListenableFuture<LibraryResult<Void>> subscribe(
|
||||||
String parentId, @Nullable LibraryParams params) {
|
String parentId, @Nullable LibraryParams params) {
|
||||||
if (!instance.isSessionCommandAvailable(SessionCommand.COMMAND_CODE_LIBRARY_SUBSCRIBE)) {
|
if (!getInstance().isSessionCommandAvailable(SessionCommand.COMMAND_CODE_LIBRARY_SUBSCRIBE)) {
|
||||||
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
||||||
}
|
}
|
||||||
MediaBrowserCompat browserCompat = getBrowserCompat();
|
MediaBrowserCompat browserCompat = getBrowserCompat();
|
||||||
|
|
@ -124,7 +135,7 @@ import java.util.List;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<LibraryResult<Void>> unsubscribe(String parentId) {
|
public ListenableFuture<LibraryResult<Void>> unsubscribe(String parentId) {
|
||||||
if (!instance.isSessionCommandAvailable(SessionCommand.COMMAND_CODE_LIBRARY_UNSUBSCRIBE)) {
|
if (!getInstance().isSessionCommandAvailable(SessionCommand.COMMAND_CODE_LIBRARY_UNSUBSCRIBE)) {
|
||||||
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
||||||
}
|
}
|
||||||
MediaBrowserCompat browserCompat = getBrowserCompat();
|
MediaBrowserCompat browserCompat = getBrowserCompat();
|
||||||
|
|
@ -148,7 +159,8 @@ import java.util.List;
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getChildren(
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getChildren(
|
||||||
String parentId, int page, int pageSize, @Nullable LibraryParams params) {
|
String parentId, int page, int pageSize, @Nullable LibraryParams params) {
|
||||||
if (!instance.isSessionCommandAvailable(SessionCommand.COMMAND_CODE_LIBRARY_GET_CHILDREN)) {
|
if (!getInstance()
|
||||||
|
.isSessionCommandAvailable(SessionCommand.COMMAND_CODE_LIBRARY_GET_CHILDREN)) {
|
||||||
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
||||||
}
|
}
|
||||||
MediaBrowserCompat browserCompat = getBrowserCompat();
|
MediaBrowserCompat browserCompat = getBrowserCompat();
|
||||||
|
|
@ -164,7 +176,7 @@ import java.util.List;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<LibraryResult<MediaItem>> getItem(String mediaId) {
|
public ListenableFuture<LibraryResult<MediaItem>> getItem(String mediaId) {
|
||||||
if (!instance.isSessionCommandAvailable(SessionCommand.COMMAND_CODE_LIBRARY_GET_ITEM)) {
|
if (!getInstance().isSessionCommandAvailable(SessionCommand.COMMAND_CODE_LIBRARY_GET_ITEM)) {
|
||||||
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
||||||
}
|
}
|
||||||
MediaBrowserCompat browserCompat = getBrowserCompat();
|
MediaBrowserCompat browserCompat = getBrowserCompat();
|
||||||
|
|
@ -196,7 +208,7 @@ import java.util.List;
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<LibraryResult<Void>> search(
|
public ListenableFuture<LibraryResult<Void>> search(
|
||||||
String query, @Nullable LibraryParams params) {
|
String query, @Nullable LibraryParams params) {
|
||||||
if (!instance.isSessionCommandAvailable(SessionCommand.COMMAND_CODE_LIBRARY_SEARCH)) {
|
if (!getInstance().isSessionCommandAvailable(SessionCommand.COMMAND_CODE_LIBRARY_SEARCH)) {
|
||||||
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
||||||
}
|
}
|
||||||
MediaBrowserCompat browserCompat = getBrowserCompat();
|
MediaBrowserCompat browserCompat = getBrowserCompat();
|
||||||
|
|
@ -210,7 +222,7 @@ import java.util.List;
|
||||||
@Override
|
@Override
|
||||||
public void onSearchResult(
|
public void onSearchResult(
|
||||||
String query, Bundle extras, List<MediaBrowserCompat.MediaItem> items) {
|
String query, Bundle extras, List<MediaBrowserCompat.MediaItem> items) {
|
||||||
getMediaBrowser()
|
getInstance()
|
||||||
.notifyBrowserListener(
|
.notifyBrowserListener(
|
||||||
listener -> {
|
listener -> {
|
||||||
// Set extra null here, because 'extra' have different meanings between old
|
// Set extra null here, because 'extra' have different meanings between old
|
||||||
|
|
@ -218,20 +230,20 @@ import java.util.List;
|
||||||
// - Old API: Extra/Option specified with search().
|
// - Old API: Extra/Option specified with search().
|
||||||
// - New API: Extra from MediaLibraryService to MediaBrowser
|
// - New API: Extra from MediaLibraryService to MediaBrowser
|
||||||
// TODO(b/193193565): Cache search result for later getSearchResult() calls.
|
// TODO(b/193193565): Cache search result for later getSearchResult() calls.
|
||||||
listener.onSearchResultChanged(getMediaBrowser(), query, items.size(), null);
|
listener.onSearchResultChanged(getInstance(), query, items.size(), null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(String query, Bundle extras) {
|
public void onError(String query, Bundle extras) {
|
||||||
getMediaBrowser()
|
getInstance()
|
||||||
.notifyBrowserListener(
|
.notifyBrowserListener(
|
||||||
listener -> {
|
listener -> {
|
||||||
// Set extra null here, because 'extra' have different meanings between old
|
// Set extra null here, because 'extra' have different meanings between old
|
||||||
// API and new API as follows.
|
// API and new API as follows.
|
||||||
// - Old API: Extra/Option specified with search().
|
// - Old API: Extra/Option specified with search().
|
||||||
// - New API: Extra from MediaLibraryService to MediaBrowser
|
// - New API: Extra from MediaLibraryService to MediaBrowser
|
||||||
listener.onSearchResultChanged(getMediaBrowser(), query, 0, null);
|
listener.onSearchResultChanged(getInstance(), query, 0, null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -242,8 +254,8 @@ import java.util.List;
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getSearchResult(
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getSearchResult(
|
||||||
String query, int page, int pageSize, @Nullable LibraryParams params) {
|
String query, int page, int pageSize, @Nullable LibraryParams params) {
|
||||||
if (!instance.isSessionCommandAvailable(
|
if (!getInstance()
|
||||||
SessionCommand.COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT)) {
|
.isSessionCommandAvailable(SessionCommand.COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT)) {
|
||||||
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_PERMISSION_DENIED));
|
||||||
}
|
}
|
||||||
MediaBrowserCompat browserCompat = getBrowserCompat();
|
MediaBrowserCompat browserCompat = getBrowserCompat();
|
||||||
|
|
@ -401,11 +413,11 @@ import java.util.List;
|
||||||
LibraryParams params =
|
LibraryParams params =
|
||||||
MediaUtils.convertToLibraryParams(
|
MediaUtils.convertToLibraryParams(
|
||||||
context, browserCompat.getNotifyChildrenChangedOptions());
|
context, browserCompat.getNotifyChildrenChangedOptions());
|
||||||
getMediaBrowser()
|
getInstance()
|
||||||
.notifyBrowserListener(
|
.notifyBrowserListener(
|
||||||
listener -> {
|
listener -> {
|
||||||
// TODO(b/193193565): Cache children result for later getChildren() calls.
|
// TODO(b/193193565): Cache children result for later getChildren() calls.
|
||||||
listener.onChildrenChanged(getMediaBrowser(), parentId, itemCount, params);
|
listener.onChildrenChanged(getInstance(), parentId, itemCount, params);
|
||||||
});
|
});
|
||||||
future.set(LibraryResult.ofVoid());
|
future.set(LibraryResult.ofVoid());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,8 @@ import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import org.checkerframework.checker.initialization.qual.Initialized;
|
import org.checkerframework.checker.initialization.qual.NotOnlyInitialized;
|
||||||
|
import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A controller that interacts with a {@link MediaSession}, a {@link MediaSessionService} hosting a
|
* A controller that interacts with a {@link MediaSession}, a {@link MediaSessionService} hosting a
|
||||||
|
|
@ -375,7 +376,7 @@ public class MediaController implements Player {
|
||||||
|
|
||||||
private boolean released;
|
private boolean released;
|
||||||
|
|
||||||
/* package */ final MediaControllerImpl impl;
|
@NotOnlyInitialized private final MediaControllerImpl impl;
|
||||||
|
|
||||||
/* package */ final Listener listener;
|
/* package */ final Listener listener;
|
||||||
|
|
||||||
|
|
@ -407,19 +408,21 @@ public class MediaController implements Player {
|
||||||
applicationHandler = new Handler(applicationLooper);
|
applicationHandler = new Handler(applicationLooper);
|
||||||
this.connectionCallback = connectionCallback;
|
this.connectionCallback = connectionCallback;
|
||||||
|
|
||||||
@SuppressWarnings("nullness:assignment")
|
impl = createImpl(context, token, connectionHints, applicationLooper);
|
||||||
@Initialized
|
|
||||||
MediaController thisRef = this;
|
|
||||||
impl = thisRef.createImpl(context, thisRef, token, connectionHints);
|
|
||||||
impl.connect();
|
impl.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ MediaControllerImpl createImpl(
|
/* package */ @UnderInitialization
|
||||||
Context context, MediaController thisRef, SessionToken token, Bundle connectionHints) {
|
MediaControllerImpl createImpl(
|
||||||
|
@UnderInitialization MediaController this,
|
||||||
|
Context context,
|
||||||
|
SessionToken token,
|
||||||
|
Bundle connectionHints,
|
||||||
|
Looper applicationLooper) {
|
||||||
if (token.isLegacySession()) {
|
if (token.isLegacySession()) {
|
||||||
return new MediaControllerImplLegacy(context, thisRef, token);
|
return new MediaControllerImplLegacy(context, this, token, applicationLooper);
|
||||||
} else {
|
} else {
|
||||||
return new MediaControllerImplBase(context, thisRef, token, connectionHints);
|
return new MediaControllerImplBase(context, this, token, connectionHints, applicationLooper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1805,7 +1808,7 @@ public class MediaController implements Player {
|
||||||
|
|
||||||
interface MediaControllerImpl {
|
interface MediaControllerImpl {
|
||||||
|
|
||||||
void connect();
|
void connect(@UnderInitialization MediaControllerImpl this);
|
||||||
|
|
||||||
void addListener(Player.Listener listener);
|
void addListener(Player.Listener listener);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,7 @@ import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
@SuppressWarnings("FutureReturnValueIgnored") // TODO(b/138091975): Not to ignore if feasible
|
@SuppressWarnings("FutureReturnValueIgnored") // TODO(b/138091975): Not to ignore if feasible
|
||||||
|
|
@ -161,7 +162,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
public static final String TAG = "MCImplBase";
|
public static final String TAG = "MCImplBase";
|
||||||
|
|
||||||
protected final MediaController instance;
|
private final MediaController instance;
|
||||||
protected final SequencedFutureManager sequencedFutureManager;
|
protected final SequencedFutureManager sequencedFutureManager;
|
||||||
protected final MediaControllerStub controllerStub;
|
protected final MediaControllerStub controllerStub;
|
||||||
|
|
||||||
|
|
@ -192,7 +193,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
private long lastSetPlayWhenReadyCalledTimeMs;
|
private long lastSetPlayWhenReadyCalledTimeMs;
|
||||||
|
|
||||||
public MediaControllerImplBase(
|
public MediaControllerImplBase(
|
||||||
Context context, MediaController instance, SessionToken token, Bundle connectionHints) {
|
Context context,
|
||||||
|
@UnderInitialization MediaController instance,
|
||||||
|
SessionToken token,
|
||||||
|
Bundle connectionHints,
|
||||||
|
Looper applicationLooper) {
|
||||||
// Initialize default values.
|
// Initialize default values.
|
||||||
playerInfo = PlayerInfo.DEFAULT;
|
playerInfo = PlayerInfo.DEFAULT;
|
||||||
sessionCommands = SessionCommands.EMPTY;
|
sessionCommands = SessionCommands.EMPTY;
|
||||||
|
|
@ -201,9 +206,9 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
intersectedPlayerCommands = Commands.EMPTY;
|
intersectedPlayerCommands = Commands.EMPTY;
|
||||||
listeners =
|
listeners =
|
||||||
new ListenerSet<>(
|
new ListenerSet<>(
|
||||||
instance.getApplicationLooper(),
|
applicationLooper,
|
||||||
Clock.DEFAULT,
|
Clock.DEFAULT,
|
||||||
(listener, flags) -> listener.onEvents(instance, new Events(flags)));
|
(listener, flags) -> listener.onEvents(getInstance(), new Events(flags)));
|
||||||
|
|
||||||
// Initialize members
|
// Initialize members
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
|
|
@ -216,21 +221,26 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
this.connectionHints = connectionHints;
|
this.connectionHints = connectionHints;
|
||||||
deathRecipient =
|
deathRecipient =
|
||||||
() ->
|
() ->
|
||||||
MediaControllerImplBase.this.instance.runOnApplicationLooper(
|
MediaControllerImplBase.this
|
||||||
MediaControllerImplBase.this.instance::release);
|
.getInstance()
|
||||||
|
.runOnApplicationLooper(MediaControllerImplBase.this.getInstance()::release);
|
||||||
surfaceCallback = new SurfaceCallback();
|
surfaceCallback = new SurfaceCallback();
|
||||||
|
|
||||||
serviceConnection =
|
serviceConnection =
|
||||||
(this.token.getType() == TYPE_SESSION)
|
(this.token.getType() == TYPE_SESSION)
|
||||||
? null
|
? null
|
||||||
: new SessionServiceConnection(connectionHints);
|
: new SessionServiceConnection(connectionHints);
|
||||||
flushCommandQueueHandler = new FlushCommandQueueHandler(instance.getApplicationLooper());
|
flushCommandQueueHandler = new FlushCommandQueueHandler(applicationLooper);
|
||||||
lastReturnedContentPositionMs = C.TIME_UNSET;
|
lastReturnedContentPositionMs = C.TIME_UNSET;
|
||||||
lastSetPlayWhenReadyCalledTimeMs = C.TIME_UNSET;
|
lastSetPlayWhenReadyCalledTimeMs = C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* package*/ MediaController getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect() {
|
public void connect(@UnderInitialization MediaControllerImplBase this) {
|
||||||
boolean connectionRequested;
|
boolean connectionRequested;
|
||||||
if (this.token.getType() == TYPE_SESSION) {
|
if (this.token.getType() == TYPE_SESSION) {
|
||||||
// Session
|
// Session
|
||||||
|
|
@ -241,7 +251,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
connectionRequested = requestConnectToService();
|
connectionRequested = requestConnectToService();
|
||||||
}
|
}
|
||||||
if (!connectionRequested) {
|
if (!connectionRequested) {
|
||||||
this.instance.runOnApplicationLooper(MediaControllerImplBase.this.instance::release);
|
getInstance().runOnApplicationLooper(getInstance()::release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -640,8 +650,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
return playerInfo.sessionPositionInfo.positionInfo.positionMs;
|
return playerInfo.sessionPositionInfo.positionInfo.positionMs;
|
||||||
}
|
}
|
||||||
long elapsedTimeMs =
|
long elapsedTimeMs =
|
||||||
(instance.getTimeDiffMs() != C.TIME_UNSET)
|
(getInstance().getTimeDiffMs() != C.TIME_UNSET)
|
||||||
? instance.getTimeDiffMs()
|
? getInstance().getTimeDiffMs()
|
||||||
: SystemClock.elapsedRealtime() - playerInfo.sessionPositionInfo.eventTimeMs;
|
: SystemClock.elapsedRealtime() - playerInfo.sessionPositionInfo.eventTimeMs;
|
||||||
long estimatedPositionMs =
|
long estimatedPositionMs =
|
||||||
playerInfo.sessionPositionInfo.positionInfo.positionMs
|
playerInfo.sessionPositionInfo.positionInfo.positionMs
|
||||||
|
|
@ -694,8 +704,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
long elapsedTimeMs =
|
long elapsedTimeMs =
|
||||||
(instance.getTimeDiffMs() != C.TIME_UNSET)
|
(getInstance().getTimeDiffMs() != C.TIME_UNSET)
|
||||||
? instance.getTimeDiffMs()
|
? getInstance().getTimeDiffMs()
|
||||||
: SystemClock.elapsedRealtime() - playerInfo.sessionPositionInfo.eventTimeMs;
|
: SystemClock.elapsedRealtime() - playerInfo.sessionPositionInfo.eventTimeMs;
|
||||||
long estimatedPositionMs =
|
long estimatedPositionMs =
|
||||||
playerInfo.sessionPositionInfo.positionInfo.contentPositionMs
|
playerInfo.sessionPositionInfo.positionInfo.contentPositionMs
|
||||||
|
|
@ -2289,7 +2299,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
TAG,
|
TAG,
|
||||||
"Cannot be notified about the connection result many times."
|
"Cannot be notified about the connection result many times."
|
||||||
+ " Probably a bug or malicious app.");
|
+ " Probably a bug or malicious app.");
|
||||||
instance.release();
|
getInstance().release();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
iSession = result.sessionBinder;
|
iSession = result.sessionBinder;
|
||||||
|
|
@ -2304,7 +2314,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
// so can be used without worrying about deadlock.
|
// so can be used without worrying about deadlock.
|
||||||
result.sessionBinder.asBinder().linkToDeath(deathRecipient, 0);
|
result.sessionBinder.asBinder().linkToDeath(deathRecipient, 0);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
instance.release();
|
getInstance().release();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
connectedToken =
|
connectedToken =
|
||||||
|
|
@ -2315,7 +2325,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
token.getPackageName(),
|
token.getPackageName(),
|
||||||
result.sessionBinder,
|
result.sessionBinder,
|
||||||
result.tokenExtras);
|
result.tokenExtras);
|
||||||
instance.notifyAccepted();
|
getInstance().notifyAccepted();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendControllerResult(int seq, SessionResult result) {
|
private void sendControllerResult(int seq, SessionResult result) {
|
||||||
|
|
@ -2350,14 +2360,15 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
if (!isConnected()) {
|
if (!isConnected()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
instance.notifyControllerListener(
|
getInstance()
|
||||||
listener -> {
|
.notifyControllerListener(
|
||||||
ListenableFuture<SessionResult> future =
|
listener -> {
|
||||||
checkNotNull(
|
ListenableFuture<SessionResult> future =
|
||||||
listener.onCustomCommand(instance, command, args),
|
checkNotNull(
|
||||||
"ControllerCallback#onCustomCommand() must not return null");
|
listener.onCustomCommand(getInstance(), command, args),
|
||||||
sendControllerResultWhenReady(seq, future);
|
"ControllerCallback#onCustomCommand() must not return null");
|
||||||
});
|
sendControllerResultWhenReady(seq, future);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Implementing and calling deprecated listener method.
|
@SuppressWarnings("deprecation") // Implementing and calling deprecated listener method.
|
||||||
|
|
@ -2558,8 +2569,10 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
listener -> listener.onAvailableCommandsChanged(intersectedPlayerCommands));
|
listener -> listener.onAvailableCommandsChanged(intersectedPlayerCommands));
|
||||||
}
|
}
|
||||||
if (sessionCommandsChanged) {
|
if (sessionCommandsChanged) {
|
||||||
instance.notifyControllerListener(
|
getInstance()
|
||||||
listener -> listener.onAvailableSessionCommandsChanged(instance, sessionCommands));
|
.notifyControllerListener(
|
||||||
|
listener ->
|
||||||
|
listener.onAvailableSessionCommandsChanged(getInstance(), sessionCommands));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2596,21 +2609,23 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
validatedCustomLayout.add(button);
|
validatedCustomLayout.add(button);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
instance.notifyControllerListener(
|
getInstance()
|
||||||
listener -> {
|
.notifyControllerListener(
|
||||||
ListenableFuture<SessionResult> future =
|
listener -> {
|
||||||
checkNotNull(
|
ListenableFuture<SessionResult> future =
|
||||||
listener.onSetCustomLayout(instance, validatedCustomLayout),
|
checkNotNull(
|
||||||
"MediaController.Listener#onSetCustomLayout() must not return null");
|
listener.onSetCustomLayout(getInstance(), validatedCustomLayout),
|
||||||
sendControllerResultWhenReady(seq, future);
|
"MediaController.Listener#onSetCustomLayout() must not return null");
|
||||||
});
|
sendControllerResultWhenReady(seq, future);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onExtrasChanged(Bundle extras) {
|
public void onExtrasChanged(Bundle extras) {
|
||||||
if (!isConnected()) {
|
if (!isConnected()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
instance.notifyControllerListener(listener -> listener.onExtrasChanged(instance, extras));
|
getInstance()
|
||||||
|
.notifyControllerListener(listener -> listener.onExtrasChanged(getInstance(), extras));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRenderedFirstFrame() {
|
public void onRenderedFirstFrame() {
|
||||||
|
|
@ -2961,7 +2976,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
Log.w(TAG, "Service " + name + " has died prematurely");
|
Log.w(TAG, "Service " + name + " has died prematurely");
|
||||||
} finally {
|
} finally {
|
||||||
if (!connectionRequested) {
|
if (!connectionRequested) {
|
||||||
instance.runOnApplicationLooper(instance::release);
|
getInstance().runOnApplicationLooper(getInstance()::release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2972,7 +2987,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
// rebind, but we'd better to release() here. Otherwise ControllerCallback#onConnected()
|
// rebind, but we'd better to release() here. Otherwise ControllerCallback#onConnected()
|
||||||
// would be called multiple times, and the controller would be connected to the
|
// would be called multiple times, and the controller would be connected to the
|
||||||
// different session everytime.
|
// different session everytime.
|
||||||
instance.runOnApplicationLooper(instance::release);
|
getInstance().runOnApplicationLooper(getInstance()::release);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -2980,7 +2995,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
// Permanent lose of the binding because of the service package update or removed.
|
// Permanent lose of the binding because of the service package update or removed.
|
||||||
// This SessionServiceRecord will be removed accordingly, but forget session binder here
|
// This SessionServiceRecord will be removed accordingly, but forget session binder here
|
||||||
// for sure.
|
// for sure.
|
||||||
instance.runOnApplicationLooper(instance::release);
|
getInstance().runOnApplicationLooper(getInstance()::release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ import android.content.Context;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.support.v4.media.MediaBrowserCompat;
|
import android.support.v4.media.MediaBrowserCompat;
|
||||||
|
|
@ -98,6 +99,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||||
import org.checkerframework.checker.nullness.compatqual.NullableType;
|
import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
|
|
||||||
/* package */ class MediaControllerImplLegacy implements MediaController.MediaControllerImpl {
|
/* package */ class MediaControllerImplLegacy implements MediaController.MediaControllerImpl {
|
||||||
|
|
@ -110,7 +112,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
new CueGroup(ImmutableList.of(), /* presentationTimeUs= */ 0);
|
new CueGroup(ImmutableList.of(), /* presentationTimeUs= */ 0);
|
||||||
|
|
||||||
/* package */ final Context context;
|
/* package */ final Context context;
|
||||||
/* package */ final MediaController instance;
|
private final MediaController instance;
|
||||||
|
|
||||||
private final SessionToken token;
|
private final SessionToken token;
|
||||||
private final ListenerSet<Listener> listeners;
|
private final ListenerSet<Listener> listeners;
|
||||||
|
|
@ -124,26 +126,34 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
private LegacyPlayerInfo pendingLegacyPlayerInfo;
|
private LegacyPlayerInfo pendingLegacyPlayerInfo;
|
||||||
private ControllerInfo controllerInfo;
|
private ControllerInfo controllerInfo;
|
||||||
|
|
||||||
public MediaControllerImplLegacy(Context context, MediaController instance, SessionToken token) {
|
public MediaControllerImplLegacy(
|
||||||
|
Context context,
|
||||||
|
@UnderInitialization MediaController instance,
|
||||||
|
SessionToken token,
|
||||||
|
Looper applicationLooper) {
|
||||||
// Initialize default values.
|
// Initialize default values.
|
||||||
legacyPlayerInfo = new LegacyPlayerInfo();
|
legacyPlayerInfo = new LegacyPlayerInfo();
|
||||||
pendingLegacyPlayerInfo = new LegacyPlayerInfo();
|
pendingLegacyPlayerInfo = new LegacyPlayerInfo();
|
||||||
controllerInfo = new ControllerInfo();
|
controllerInfo = new ControllerInfo();
|
||||||
listeners =
|
listeners =
|
||||||
new ListenerSet<>(
|
new ListenerSet<>(
|
||||||
instance.getApplicationLooper(),
|
applicationLooper,
|
||||||
Clock.DEFAULT,
|
Clock.DEFAULT,
|
||||||
(listener, flags) -> listener.onEvents(instance, new Events(flags)));
|
(listener, flags) -> listener.onEvents(getInstance(), new Events(flags)));
|
||||||
|
|
||||||
// Initialize members.
|
// Initialize members.
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
controllerCompatCallback = new ControllerCompatCallback();
|
controllerCompatCallback = new ControllerCompatCallback(applicationLooper);
|
||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* package */ MediaController getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect() {
|
public void connect(@UnderInitialization MediaControllerImplLegacy this) {
|
||||||
if (this.token.getType() == SessionToken.TYPE_SESSION) {
|
if (this.token.getType() == SessionToken.TYPE_SESSION) {
|
||||||
connectToSession((MediaSessionCompat.Token) checkStateNotNull(this.token.getBinder()));
|
connectToSession((MediaSessionCompat.Token) checkStateNotNull(this.token.getBinder()));
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -590,7 +600,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
}
|
}
|
||||||
SettableFuture<SessionResult> result = SettableFuture.create();
|
SettableFuture<SessionResult> result = SettableFuture.create();
|
||||||
ResultReceiver cb =
|
ResultReceiver cb =
|
||||||
new ResultReceiver(instance.applicationHandler) {
|
new ResultReceiver(getInstance().applicationHandler) {
|
||||||
@Override
|
@Override
|
||||||
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||||||
result.set(
|
result.set(
|
||||||
|
|
@ -1233,36 +1243,43 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connectToSession(MediaSessionCompat.Token sessionCompatToken) {
|
private void connectToSession(MediaSessionCompat.Token sessionCompatToken) {
|
||||||
instance.runOnApplicationLooper(
|
getInstance()
|
||||||
() -> {
|
.runOnApplicationLooper(
|
||||||
controllerCompat = new MediaControllerCompat(context, sessionCompatToken);
|
() -> {
|
||||||
// Note: registerCallback() will invoke MediaControllerCompat.Callback#onSessionReady()
|
controllerCompat = new MediaControllerCompat(context, sessionCompatToken);
|
||||||
// if the controller is already ready.
|
// Note: registerCallback() will invoke
|
||||||
controllerCompat.registerCallback(controllerCompatCallback, instance.applicationHandler);
|
// MediaControllerCompat.Callback#onSessionReady()
|
||||||
});
|
// if the controller is already ready.
|
||||||
|
controllerCompat.registerCallback(
|
||||||
|
controllerCompatCallback, getInstance().applicationHandler);
|
||||||
|
});
|
||||||
// Post a runnable to prevent callbacks from being called by onConnectedNotLocked()
|
// Post a runnable to prevent callbacks from being called by onConnectedNotLocked()
|
||||||
// before the constructor returns (b/196941334).
|
// before the constructor returns (b/196941334).
|
||||||
instance.applicationHandler.post(
|
getInstance()
|
||||||
() -> {
|
.applicationHandler
|
||||||
if (!controllerCompat.isSessionReady()) {
|
.post(
|
||||||
// If the session not ready here, then call onConnectedNotLocked() immediately. The
|
() -> {
|
||||||
// session may be a framework MediaSession and we cannot know whether it can be ready
|
if (!controllerCompat.isSessionReady()) {
|
||||||
// later.
|
// If the session not ready here, then call onConnectedNotLocked() immediately. The
|
||||||
onConnectedNotLocked();
|
// session may be a framework MediaSession and we cannot know whether it can be
|
||||||
}
|
// ready
|
||||||
});
|
// later.
|
||||||
|
onConnectedNotLocked();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connectToService() {
|
private void connectToService() {
|
||||||
instance.runOnApplicationLooper(
|
getInstance()
|
||||||
() -> {
|
.runOnApplicationLooper(
|
||||||
// BrowserCompat can only be used on the thread that it's created.
|
() -> {
|
||||||
// Create it on the application looper to respect that.
|
// BrowserCompat can only be used on the thread that it's created.
|
||||||
browserCompat =
|
// Create it on the application looper to respect that.
|
||||||
new MediaBrowserCompat(
|
browserCompat =
|
||||||
context, token.getComponentName(), new ConnectionCallback(), null);
|
new MediaBrowserCompat(
|
||||||
browserCompat.connect();
|
context, token.getComponentName(), new ConnectionCallback(), null);
|
||||||
});
|
browserCompat.connect();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPrepared() {
|
private boolean isPrepared() {
|
||||||
|
|
@ -1365,14 +1382,14 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
controllerCompat.getFlags(),
|
controllerCompat.getFlags(),
|
||||||
controllerCompat.isSessionReady(),
|
controllerCompat.isSessionReady(),
|
||||||
controllerCompat.getRatingType(),
|
controllerCompat.getRatingType(),
|
||||||
instance.getTimeDiffMs());
|
getInstance().getTimeDiffMs());
|
||||||
Pair<@NullableType Integer, @NullableType Integer> reasons =
|
Pair<@NullableType Integer, @NullableType Integer> reasons =
|
||||||
calculateDiscontinuityAndTransitionReason(
|
calculateDiscontinuityAndTransitionReason(
|
||||||
legacyPlayerInfo,
|
legacyPlayerInfo,
|
||||||
controllerInfo,
|
controllerInfo,
|
||||||
newLegacyPlayerInfo,
|
newLegacyPlayerInfo,
|
||||||
newControllerInfo,
|
newControllerInfo,
|
||||||
instance.getTimeDiffMs());
|
getInstance().getTimeDiffMs());
|
||||||
updateControllerInfo(
|
updateControllerInfo(
|
||||||
notifyConnected,
|
notifyConnected,
|
||||||
newLegacyPlayerInfo,
|
newLegacyPlayerInfo,
|
||||||
|
|
@ -1412,11 +1429,13 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
controllerInfo = newControllerInfo;
|
controllerInfo = newControllerInfo;
|
||||||
|
|
||||||
if (notifyConnected) {
|
if (notifyConnected) {
|
||||||
instance.notifyAccepted();
|
getInstance().notifyAccepted();
|
||||||
if (!oldControllerInfo.customLayout.equals(newControllerInfo.customLayout)) {
|
if (!oldControllerInfo.customLayout.equals(newControllerInfo.customLayout)) {
|
||||||
instance.notifyControllerListener(
|
getInstance()
|
||||||
listener ->
|
.notifyControllerListener(
|
||||||
ignoreFuture(listener.onSetCustomLayout(instance, newControllerInfo.customLayout)));
|
listener ->
|
||||||
|
ignoreFuture(
|
||||||
|
listener.onSetCustomLayout(getInstance(), newControllerInfo.customLayout)));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1537,15 +1556,18 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
}
|
}
|
||||||
if (!oldControllerInfo.availableSessionCommands.equals(
|
if (!oldControllerInfo.availableSessionCommands.equals(
|
||||||
newControllerInfo.availableSessionCommands)) {
|
newControllerInfo.availableSessionCommands)) {
|
||||||
instance.notifyControllerListener(
|
getInstance()
|
||||||
listener ->
|
.notifyControllerListener(
|
||||||
listener.onAvailableSessionCommandsChanged(
|
listener ->
|
||||||
instance, newControllerInfo.availableSessionCommands));
|
listener.onAvailableSessionCommandsChanged(
|
||||||
|
getInstance(), newControllerInfo.availableSessionCommands));
|
||||||
}
|
}
|
||||||
if (!oldControllerInfo.customLayout.equals(newControllerInfo.customLayout)) {
|
if (!oldControllerInfo.customLayout.equals(newControllerInfo.customLayout)) {
|
||||||
instance.notifyControllerListener(
|
getInstance()
|
||||||
listener ->
|
.notifyControllerListener(
|
||||||
ignoreFuture(listener.onSetCustomLayout(instance, newControllerInfo.customLayout)));
|
listener ->
|
||||||
|
ignoreFuture(
|
||||||
|
listener.onSetCustomLayout(getInstance(), newControllerInfo.customLayout)));
|
||||||
}
|
}
|
||||||
listeners.flushEvents();
|
listeners.flushEvents();
|
||||||
}
|
}
|
||||||
|
|
@ -1566,12 +1588,12 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionSuspended() {
|
public void onConnectionSuspended() {
|
||||||
instance.release();
|
getInstance().release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionFailed() {
|
public void onConnectionFailed() {
|
||||||
instance.release();
|
getInstance().release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1581,10 +1603,10 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
|
|
||||||
private final Handler pendingChangesHandler;
|
private final Handler pendingChangesHandler;
|
||||||
|
|
||||||
public ControllerCompatCallback() {
|
public ControllerCompatCallback(Looper applicationLooper) {
|
||||||
pendingChangesHandler =
|
pendingChangesHandler =
|
||||||
new Handler(
|
new Handler(
|
||||||
instance.applicationHandler.getLooper(),
|
applicationLooper,
|
||||||
(msg) -> {
|
(msg) -> {
|
||||||
if (msg.what == MSG_HANDLE_PENDING_UPDATES) {
|
if (msg.what == MSG_HANDLE_PENDING_UPDATES) {
|
||||||
handleNewLegacyParameters(/* notifyConnected= */ false, pendingLegacyPlayerInfo);
|
handleNewLegacyParameters(/* notifyConnected= */ false, pendingLegacyPlayerInfo);
|
||||||
|
|
@ -1620,16 +1642,19 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSessionDestroyed() {
|
public void onSessionDestroyed() {
|
||||||
instance.release();
|
getInstance().release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSessionEvent(String event, Bundle extras) {
|
public void onSessionEvent(String event, Bundle extras) {
|
||||||
instance.notifyControllerListener(
|
getInstance()
|
||||||
listener ->
|
.notifyControllerListener(
|
||||||
ignoreFuture(
|
listener ->
|
||||||
listener.onCustomCommand(
|
ignoreFuture(
|
||||||
instance, new SessionCommand(event, /* extras= */ Bundle.EMPTY), extras)));
|
listener.onCustomCommand(
|
||||||
|
getInstance(),
|
||||||
|
new SessionCommand(event, /* extras= */ Bundle.EMPTY),
|
||||||
|
extras)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -1661,7 +1686,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onExtrasChanged(Bundle extras) {
|
public void onExtrasChanged(Bundle extras) {
|
||||||
instance.notifyControllerListener(listener -> listener.onExtrasChanged(instance, extras));
|
getInstance()
|
||||||
|
.notifyControllerListener(listener -> listener.onExtrasChanged(getInstance(), extras));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -1672,17 +1698,19 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCaptioningEnabledChanged(boolean enabled) {
|
public void onCaptioningEnabledChanged(boolean enabled) {
|
||||||
instance.notifyControllerListener(
|
getInstance()
|
||||||
listener -> {
|
.notifyControllerListener(
|
||||||
Bundle args = new Bundle();
|
listener -> {
|
||||||
args.putBoolean(ARGUMENT_CAPTIONING_ENABLED, enabled);
|
Bundle args = new Bundle();
|
||||||
ignoreFuture(
|
args.putBoolean(ARGUMENT_CAPTIONING_ENABLED, enabled);
|
||||||
listener.onCustomCommand(
|
ignoreFuture(
|
||||||
instance,
|
listener.onCustomCommand(
|
||||||
new SessionCommand(
|
getInstance(),
|
||||||
SESSION_COMMAND_ON_CAPTIONING_ENABLED_CHANGED, /* extras= */ Bundle.EMPTY),
|
new SessionCommand(
|
||||||
args));
|
SESSION_COMMAND_ON_CAPTIONING_ENABLED_CHANGED,
|
||||||
});
|
/* extras= */ Bundle.EMPTY),
|
||||||
|
args));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,8 @@ import java.util.List;
|
||||||
@Override
|
@Override
|
||||||
public void onDisconnected(int seq) {
|
public void onDisconnected(int seq) {
|
||||||
dispatchControllerTaskOnHandler(
|
dispatchControllerTaskOnHandler(
|
||||||
controller -> controller.instance.runOnApplicationLooper(controller.instance::release));
|
controller ->
|
||||||
|
controller.getInstance().runOnApplicationLooper(controller.getInstance()::release));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -268,7 +269,7 @@ import java.util.List;
|
||||||
if (controller == null) {
|
if (controller == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Handler handler = controller.instance.applicationHandler;
|
Handler handler = controller.getInstance().applicationHandler;
|
||||||
postOrRun(
|
postOrRun(
|
||||||
handler,
|
handler,
|
||||||
() -> {
|
() -> {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue