Commit 0667c74d by ibaker Committed by microkatz

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 287c7579)
parent 06d3c07a
...@@ -38,6 +38,9 @@ import com.google.common.collect.ImmutableList; ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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; ...@@ -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,
() -> { () -> {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment