diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java
index de3005b2bb..4019b85613 100644
--- a/libraries/common/src/main/java/androidx/media3/common/Player.java
+++ b/libraries/common/src/main/java/androidx/media3/common/Player.java
@@ -49,6 +49,10 @@ import java.util.List;
* A media player interface defining traditional high-level functionality, such as the ability to
* play, pause, seek and query properties of the currently playing media.
*
+ *
All methods must be called from a single {@linkplain #getApplicationLooper() application
+ * thread} unless indicated otherwise. Callbacks in registered listeners are called on the same
+ * thread.
+ *
*
This interface includes some convenience methods that can be implemented by calling other
* methods in the interface. {@link BasePlayer} implements these convenience methods so inheriting
* {@link BasePlayer} is recommended when implementing the interface so that only the minimal set of
@@ -1557,6 +1561,8 @@ public interface Player {
/**
* Returns the {@link Looper} associated with the application thread that's used to access the
* player and on which player events are received.
+ *
+ *
This method can be called from any thread.
*/
Looper getApplicationLooper();
@@ -1566,6 +1572,8 @@ public interface Player {
*
The listener's methods will be called on the thread associated with {@link
* #getApplicationLooper()}.
*
+ *
This method can be called from any thread.
+ *
* @param listener The listener to register.
*/
void addListener(Listener listener);
diff --git a/libraries/common/src/main/java/androidx/media3/common/SimpleBasePlayer.java b/libraries/common/src/main/java/androidx/media3/common/SimpleBasePlayer.java
index 731dca5630..2a2a3ffc45 100644
--- a/libraries/common/src/main/java/androidx/media3/common/SimpleBasePlayer.java
+++ b/libraries/common/src/main/java/androidx/media3/common/SimpleBasePlayer.java
@@ -1978,8 +1978,7 @@ public abstract class SimpleBasePlayer extends BasePlayer {
@Override
public final void removeListener(Listener listener) {
- // Don't verify application thread. We allow calls to this method from any thread.
- checkNotNull(listener);
+ verifyApplicationThreadAndInitState();
listeners.remove(listener);
}
diff --git a/libraries/common/src/main/java/androidx/media3/common/util/ListenerSet.java b/libraries/common/src/main/java/androidx/media3/common/util/ListenerSet.java
index 78e529ae3a..0ab3bab541 100644
--- a/libraries/common/src/main/java/androidx/media3/common/util/ListenerSet.java
+++ b/libraries/common/src/main/java/androidx/media3/common/util/ListenerSet.java
@@ -15,9 +15,12 @@
*/
package androidx.media3.common.util;
+import static androidx.media3.common.util.Assertions.checkState;
+
import android.os.Looper;
import android.os.Message;
import androidx.annotation.CheckResult;
+import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.FlagSet;
@@ -34,6 +37,9 @@ import org.checkerframework.checker.nullness.qual.NonNull;
*
Events are also guaranteed to be only sent to the listeners registered at the time the event
* was enqueued and haven't been removed since.
*
+ *
All methods must be called on the {@link Looper} passed to the constructor unless indicated
+ * otherwise.
+ *
* @param The listener type.
*/
@UnstableApi
@@ -76,14 +82,18 @@ public final class ListenerSet {
private final CopyOnWriteArraySet> listeners;
private final ArrayDeque flushingEvents;
private final ArrayDeque queuedEvents;
+ private final Object releasedLock;
+ @GuardedBy("releasedLock")
private boolean released;
+ private boolean throwsWhenUsingWrongThread;
+
/**
* Creates a new listener set.
*
* @param looper A {@link Looper} used to call listeners on. The same {@link Looper} must be used
- * to call all other methods of this class.
+ * to call all other methods of this class unless indicated otherwise.
* @param clock A {@link Clock}.
* @param iterationFinishedEvent An {@link IterationFinishedEvent} sent when all other events sent
* during one {@link Looper} message queue iteration were handled by the listeners.
@@ -100,17 +110,21 @@ public final class ListenerSet {
this.clock = clock;
this.listeners = listeners;
this.iterationFinishedEvent = iterationFinishedEvent;
+ releasedLock = new Object();
flushingEvents = new ArrayDeque<>();
queuedEvents = new ArrayDeque<>();
// It's safe to use "this" because we don't send a message before exiting the constructor.
@SuppressWarnings("nullness:methodref.receiver.bound")
HandlerWrapper handler = clock.createHandler(looper, this::handleMessage);
this.handler = handler;
+ throwsWhenUsingWrongThread = true;
}
/**
* Copies the listener set.
*
+ * This method can be called from any thread.
+ *
* @param looper The new {@link Looper} for the copied listener set.
* @param iterationFinishedEvent The new {@link IterationFinishedEvent} sent when all other events
* sent during one {@link Looper} message queue iteration were handled by the listeners.
@@ -124,6 +138,8 @@ public final class ListenerSet {
/**
* Copies the listener set.
*
+ * This method can be called from any thread.
+ *
* @param looper The new {@link Looper} for the copied listener set.
* @param clock The new {@link Clock} for the copied listener set.
* @param iterationFinishedEvent The new {@link IterationFinishedEvent} sent when all other events
@@ -141,14 +157,18 @@ public final class ListenerSet {
*
* If a listener is already present, it will not be added again.
*
+ *
This method can be called from any thread.
+ *
* @param listener The listener to be added.
*/
public void add(T listener) {
- if (released) {
- return;
- }
Assertions.checkNotNull(listener);
- listeners.add(new ListenerHolder<>(listener));
+ synchronized (releasedLock) {
+ if (released) {
+ return;
+ }
+ listeners.add(new ListenerHolder<>(listener));
+ }
}
/**
@@ -159,6 +179,7 @@ public final class ListenerSet {
* @param listener The listener to be removed.
*/
public void remove(T listener) {
+ verifyCurrentThread();
for (ListenerHolder listenerHolder : listeners) {
if (listenerHolder.listener.equals(listener)) {
listenerHolder.release(iterationFinishedEvent);
@@ -169,11 +190,13 @@ public final class ListenerSet {
/** Removes all listeners from the set. */
public void clear() {
+ verifyCurrentThread();
listeners.clear();
}
/** Returns the number of added listeners. */
public int size() {
+ verifyCurrentThread();
return listeners.size();
}
@@ -185,6 +208,7 @@ public final class ListenerSet {
* @param event The event.
*/
public void queueEvent(int eventFlag, Event event) {
+ verifyCurrentThread();
CopyOnWriteArraySet> listenerSnapshot = new CopyOnWriteArraySet<>(listeners);
queuedEvents.add(
() -> {
@@ -196,6 +220,7 @@ public final class ListenerSet {
/** Notifies listeners of events previously enqueued with {@link #queueEvent(int, Event)}. */
public void flushEvents() {
+ verifyCurrentThread();
if (queuedEvents.isEmpty()) {
return;
}
@@ -234,11 +259,27 @@ public final class ListenerSet {
* This will ensure no events are sent to any listener after this method has been called.
*/
public void release() {
+ verifyCurrentThread();
+ synchronized (releasedLock) {
+ released = true;
+ }
for (ListenerHolder listenerHolder : listeners) {
listenerHolder.release(iterationFinishedEvent);
}
listeners.clear();
- released = true;
+ }
+
+ /**
+ * Sets whether methods throw when using the wrong thread.
+ *
+ * Do not use this method unless to support legacy use cases.
+ *
+ * @param throwsWhenUsingWrongThread Whether to throw when using the wrong thread.
+ * @deprecated Do not use this method and ensure all calls are made from the correct thread.
+ */
+ @Deprecated
+ public void setThrowsWhenUsingWrongThread(boolean throwsWhenUsingWrongThread) {
+ this.throwsWhenUsingWrongThread = throwsWhenUsingWrongThread;
}
private boolean handleMessage(Message message) {
@@ -254,6 +295,13 @@ public final class ListenerSet {
return true;
}
+ private void verifyCurrentThread() {
+ if (!throwsWhenUsingWrongThread) {
+ return;
+ }
+ checkState(Thread.currentThread() == handler.getLooper().getThread());
+ }
+
private static final class ListenerHolder {
public final T listener;
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java
index eae251688e..e58db58847 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java
@@ -132,15 +132,15 @@ import java.util.List;
* threading model">
*
*
- * - ExoPlayer instances must be accessed from a single application thread. For the vast
- * majority of cases this should be the application's main thread. Using the application's
- * main thread is also a requirement when using ExoPlayer's UI components or the IMA
- * extension. The thread on which an ExoPlayer instance must be accessed can be explicitly
- * specified by passing a `Looper` when creating the player. If no `Looper` is specified, then
- * the `Looper` of the thread that the player is created on is used, or if that thread does
- * not have a `Looper`, the `Looper` of the application's main thread is used. In all cases
- * the `Looper` of the thread from which the player must be accessed can be queried using
- * {@link #getApplicationLooper()}.
+ *
- ExoPlayer instances must be accessed from a single application thread unless indicated
+ * otherwise. For the vast majority of cases this should be the application's main thread.
+ * Using the application's main thread is also a requirement when using ExoPlayer's UI
+ * components or the IMA extension. The thread on which an ExoPlayer instance must be accessed
+ * can be explicitly specified by passing a `Looper` when creating the player. If no `Looper`
+ * is specified, then the `Looper` of the thread that the player is created on is used, or if
+ * that thread does not have a `Looper`, the `Looper` of the application's main thread is
+ * used. In all cases the `Looper` of the thread from which the player must be accessed can be
+ * queried using {@link #getApplicationLooper()}.
*
- Registered listeners are called on the thread associated with {@link
* #getApplicationLooper()}. Note that this means registered listeners are called on the same
* thread which must be used to access the player.
@@ -1229,6 +1229,8 @@ public interface ExoPlayer extends Player {
/**
* Adds a listener to receive audio offload events.
*
+ *
This method can be called from any thread.
+ *
* @param listener The listener to register.
*/
@UnstableApi
@@ -1249,6 +1251,8 @@ public interface ExoPlayer extends Player {
/**
* Adds an {@link AnalyticsListener} to receive analytics events.
*
+ *
This method can be called from any thread.
+ *
* @param listener The listener to be added.
*/
void addAnalyticsListener(AnalyticsListener listener);
@@ -1314,11 +1318,19 @@ public interface ExoPlayer extends Player {
@Deprecated
TrackSelectionArray getCurrentTrackSelections();
- /** Returns the {@link Looper} associated with the playback thread. */
+ /**
+ * Returns the {@link Looper} associated with the playback thread.
+ *
+ *
This method may be called from any thread.
+ */
@UnstableApi
Looper getPlaybackLooper();
- /** Returns the {@link Clock} used for playback. */
+ /**
+ * Returns the {@link Clock} used for playback.
+ *
+ *
This method can be called from any thread.
+ */
@UnstableApi
Clock getClock();
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java
index 09d1ce15af..78c53a3a24 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java
@@ -89,6 +89,7 @@ import androidx.media3.exoplayer.PlayerMessage.Target;
import androidx.media3.exoplayer.Renderer.MessageType;
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
import androidx.media3.exoplayer.analytics.AnalyticsListener;
+import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector;
import androidx.media3.exoplayer.analytics.MediaMetricsListener;
import androidx.media3.exoplayer.analytics.PlayerId;
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
@@ -480,7 +481,7 @@ import java.util.concurrent.TimeoutException;
@Override
public void removeAudioOffloadListener(AudioOffloadListener listener) {
- // Don't verify application thread. We allow calls to this method from any thread.
+ verifyApplicationThread();
audioOffloadListeners.remove(listener);
}
@@ -1488,7 +1489,7 @@ import java.util.concurrent.TimeoutException;
@Override
public void removeAnalyticsListener(AnalyticsListener listener) {
- // Don't verify application thread. We allow calls to this method from any thread.
+ verifyApplicationThread();
analyticsCollector.removeListener(checkNotNull(listener));
}
@@ -1605,9 +1606,8 @@ import java.util.concurrent.TimeoutException;
@Override
public void removeListener(Listener listener) {
- // Don't verify application thread. We allow calls to this method from any thread.
- checkNotNull(listener);
- listeners.remove(listener);
+ verifyApplicationThread();
+ listeners.remove(checkNotNull(listener));
}
@Override
@@ -1690,8 +1690,14 @@ import java.util.concurrent.TimeoutException;
return false;
}
+ @SuppressWarnings("deprecation") // Calling deprecated methods.
/* package */ void setThrowsWhenUsingWrongThread(boolean throwsWhenUsingWrongThread) {
this.throwsWhenUsingWrongThread = throwsWhenUsingWrongThread;
+ listeners.setThrowsWhenUsingWrongThread(throwsWhenUsingWrongThread);
+ if (analyticsCollector instanceof DefaultAnalyticsCollector) {
+ ((DefaultAnalyticsCollector) analyticsCollector)
+ .setThrowsWhenUsingWrongThread(throwsWhenUsingWrongThread);
+ }
}
/**
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java
index 74062ab3e3..5044d8fa56 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java
@@ -96,6 +96,20 @@ public class DefaultAnalyticsCollector implements AnalyticsCollector {
eventTimes = new SparseArray<>();
}
+ /**
+ * Sets whether methods throw when using the wrong thread.
+ *
+ *
Do not use this method unless to support legacy use cases.
+ *
+ * @param throwsWhenUsingWrongThread Whether to throw when using the wrong thread.
+ * @deprecated Do not use this method and ensure all calls are made from the correct thread.
+ */
+ @SuppressWarnings("deprecation") // Calling deprecated method.
+ @Deprecated
+ public void setThrowsWhenUsingWrongThread(boolean throwsWhenUsingWrongThread) {
+ listeners.setThrowsWhenUsingWrongThread(throwsWhenUsingWrongThread);
+ }
+
@Override
@CallSuper
public void addListener(AnalyticsListener listener) {
diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java
index 4b0351de6d..c392ae5920 100644
--- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java
+++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java
@@ -12006,10 +12006,20 @@ public final class ExoPlayerTest {
@Test
@Config(sdk = Config.ALL_SDKS)
- public void builder_inBackgroundThread_doesNotThrow() throws Exception {
+ public void builder_inBackgroundThreadWithAllowedAnyThreadMethods_doesNotThrow()
+ throws Exception {
Thread builderThread =
new Thread(
- () -> new ExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build());
+ () -> {
+ ExoPlayer player =
+ new ExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build();
+ player.addListener(new Listener() {});
+ player.addAnalyticsListener(new AnalyticsListener() {});
+ player.addAudioOffloadListener(new ExoPlayer.AudioOffloadListener() {});
+ player.getClock();
+ player.getApplicationLooper();
+ player.getPlaybackLooper();
+ });
AtomicReference builderThrow = new AtomicReference<>();
builderThread.setUncaughtExceptionHandler((thread, throwable) -> builderThrow.set(throwable));
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaController.java b/libraries/session/src/main/java/androidx/media3/session/MediaController.java
index 5affe5c695..496e6ea946 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaController.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaController.java
@@ -1720,6 +1720,7 @@ public class MediaController implements Player {
@Override
public Looper getApplicationLooper() {
+ // Don't verify application thread. We allow calls to this method from any thread.
return applicationHandler.getLooper();
}
@@ -1744,12 +1745,14 @@ public class MediaController implements Player {
@Override
public void addListener(Player.Listener listener) {
+ // Don't verify application thread. We allow calls to this method from any thread.
checkNotNull(listener, "listener must not be null");
impl.addListener(listener);
}
@Override
public void removeListener(Player.Listener listener) {
+ verifyApplicationThread();
checkNotNull(listener, "listener must not be null");
impl.removeListener(listener);
}
diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerSurfaceTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerSurfaceTest.java
index cc2567c0aa..20aca9ee0f 100644
--- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerSurfaceTest.java
+++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerSurfaceTest.java
@@ -19,6 +19,7 @@ import static androidx.media3.test.session.common.CommonConstants.DEFAULT_TEST_N
import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS;
import static com.google.common.truth.Truth.assertThat;
+import android.os.Looper;
import android.os.RemoteException;
import android.view.Surface;
import android.view.SurfaceHolder;
@@ -26,7 +27,6 @@ import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
-import androidx.media3.test.session.common.HandlerThreadTestRule;
import androidx.media3.test.session.common.PollingCheck;
import androidx.media3.test.session.common.SurfaceActivity;
import androidx.test.core.app.ApplicationProvider;
@@ -37,8 +37,6 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.RuleChain;
-import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
/** Tests for {@link MediaController#setVideoSurface(Surface)}. */
@@ -47,13 +45,6 @@ import org.junit.runner.RunWith;
public class MediaControllerSurfaceTest {
private static final String TAG = "MC_SurfaceTest";
- private final HandlerThreadTestRule threadTestRule = new HandlerThreadTestRule(TAG);
- private final MediaControllerTestRule controllerTestRule =
- new MediaControllerTestRule(threadTestRule);
-
- @Rule
- public final TestRule chain = RuleChain.outerRule(threadTestRule).around(controllerTestRule);
-
private SurfaceActivity activity;
private RemoteMediaSession remoteSession;
@@ -76,93 +67,97 @@ public class MediaControllerSurfaceTest {
}
@Test
- public void setVideoSurface() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void setVideoSurface() throws Throwable {
+ MediaController controller = createController();
Surface testSurface = activity.getFirstSurfaceHolder().getSurface();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurface(testSurface));
+ activityRule.runOnUiThread(() -> controller.setVideoSurface(testSurface));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void setVideoSurface_withNull_clearsSurface() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void setVideoSurface_withNull_clearsSurface() throws Throwable {
+ MediaController controller = createController();
Surface testSurface = activity.getFirstSurfaceHolder().getSurface();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurface(testSurface));
+ activityRule.runOnUiThread(() -> controller.setVideoSurface(testSurface));
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurface(null));
+ activityRule.runOnUiThread(() -> controller.setVideoSurface(null));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoSurface_withTheSameSurface() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoSurface_withTheSameSurface() throws Throwable {
+ MediaController controller = createController();
Surface testSurface = activity.getFirstSurfaceHolder().getSurface();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurface(testSurface));
+ activityRule.runOnUiThread(() -> controller.setVideoSurface(testSurface));
- threadTestRule.getHandler().postAndSync(() -> controller.clearVideoSurface(testSurface));
+ activityRule.runOnUiThread(() -> controller.clearVideoSurface(testSurface));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoSurface_withDifferentSurface_doesNothing() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoSurface_withDifferentSurface_doesNothing() throws Throwable {
+ MediaController controller = createController();
Surface testSurface = activity.getFirstSurfaceHolder().getSurface();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurface(testSurface));
+ activityRule.runOnUiThread(() -> controller.setVideoSurface(testSurface));
Surface anotherSurface = activity.getSecondSurfaceHolder().getSurface();
- threadTestRule.getHandler().postAndSync(() -> controller.clearVideoSurface(anotherSurface));
+ activityRule.runOnUiThread(() -> controller.clearVideoSurface(anotherSurface));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoSurface_withNull_doesNothing() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoSurface_withNull_doesNothing() throws Throwable {
+ MediaController controller = createController();
Surface testSurface = activity.getFirstSurfaceHolder().getSurface();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurface(testSurface));
+ activityRule.runOnUiThread(() -> controller.setVideoSurface(testSurface));
- threadTestRule.getHandler().postAndSync(() -> controller.clearVideoSurface(null));
+ activityRule.runOnUiThread(() -> controller.clearVideoSurface(null));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void setVideoSurfaceHolder() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void setVideoSurfaceHolder() throws Throwable {
+ MediaController controller = createController();
SurfaceHolder testSurfaceHolder = activity.getFirstSurfaceHolder();
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
+ activityRule.runOnUiThread(
+ () -> {
+ controller.setVideoSurfaceHolder(testSurfaceHolder);
+ });
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void setVideoSurfaceHolder_withNull_clearsSurfaceHolder() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void setVideoSurfaceHolder_withNull_clearsSurfaceHolder() throws Throwable {
+ MediaController controller = createController();
SurfaceHolder testSurfaceHolder = activity.getFirstSurfaceHolder();
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurfaceHolder(null));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceHolder(null));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
public void setVideoSurfaceHolder_whenSurfaceIsDestroyed_surfaceIsClearedFromPlayer()
throws Throwable {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ MediaController controller = createController();
SurfaceHolder testSurfaceHolder = activity.getFirstSurfaceHolder();
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
activityRule.runOnUiThread(
() -> {
@@ -171,15 +166,14 @@ public class MediaControllerSurfaceTest {
});
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
public void setVideoSurfaceHolder_whenSurfaceIsCreated_surfaceIsSetToPlayer() throws Throwable {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ MediaController controller = createController();
SurfaceHolder testSurfaceHolder = activity.getFirstSurfaceHolder();
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
activityRule.runOnUiThread(
() -> {
SurfaceView firstSurfaceView = activity.getFirstSurfaceView();
@@ -193,79 +187,75 @@ public class MediaControllerSurfaceTest {
});
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoSurfaceHolder_withTheSameSurfaceHolder() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoSurfaceHolder_withTheSameSurfaceHolder() throws Throwable {
+ MediaController controller = createController();
SurfaceHolder testSurfaceHolder = activity.getFirstSurfaceHolder();
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.clearVideoSurfaceHolder(testSurfaceHolder));
+ activityRule.runOnUiThread(() -> controller.clearVideoSurfaceHolder(testSurfaceHolder));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoSurfaceHolder_withDifferentSurfaceHolder_doesNothing() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoSurfaceHolder_withDifferentSurfaceHolder_doesNothing() throws Throwable {
+ MediaController controller = createController();
SurfaceHolder testSurfaceHolder = activity.getFirstSurfaceHolder();
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
SurfaceHolder anotherTestSurfaceHolder = activity.getSecondSurfaceHolder();
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.clearVideoSurfaceHolder(anotherTestSurfaceHolder));
+ activityRule.runOnUiThread(() -> controller.clearVideoSurfaceHolder(anotherTestSurfaceHolder));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoSurfaceHolder_withNull_doesNothing() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoSurfaceHolder_withNull_doesNothing() throws Throwable {
+ MediaController controller = createController();
SurfaceHolder testSurfaceHolder = activity.getFirstSurfaceHolder();
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
- threadTestRule.getHandler().postAndSync(() -> controller.clearVideoSurfaceHolder(null));
+ activityRule.runOnUiThread(() -> controller.clearVideoSurfaceHolder(null));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void setVideoSurfaceView() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void setVideoSurfaceView() throws Throwable {
+ MediaController controller = createController();
SurfaceView testSurfaceView = activity.getFirstSurfaceView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurfaceView(testSurfaceView));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceView(testSurfaceView));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void setVideoSurfaceView_withNull_clearsSurfaceView() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void setVideoSurfaceView_withNull_clearsSurfaceView() throws Throwable {
+ MediaController controller = createController();
SurfaceView testSurfaceView = activity.getFirstSurfaceView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurfaceView(testSurfaceView));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceView(testSurfaceView));
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurfaceView(null));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceView(null));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
public void setVideoSurfaceView_whenSurfaceIsDestroyed_surfaceIsClearedFromPlayer()
throws Throwable {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ MediaController controller = createController();
SurfaceView testSurfaceView = activity.getFirstSurfaceView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurfaceView(testSurfaceView));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceView(testSurfaceView));
activityRule.runOnUiThread(
() -> {
@@ -273,13 +263,14 @@ public class MediaControllerSurfaceTest {
});
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
public void setVideoSurfaceView_whenSurfaceIsCreated_surfaceIsSetToPlayer() throws Throwable {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ MediaController controller = createController();
SurfaceView testSurfaceView = activity.getFirstSurfaceView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurfaceView(testSurfaceView));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceView(testSurfaceView));
activityRule.runOnUiThread(
() -> {
testSurfaceView.setVisibility(View.GONE);
@@ -291,74 +282,76 @@ public class MediaControllerSurfaceTest {
});
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoSurfaceView_withTheSameSurfaceView() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoSurfaceView_withTheSameSurfaceView() throws Throwable {
+ MediaController controller = createController();
SurfaceView testSurfaceView = activity.getFirstSurfaceView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurfaceView(testSurfaceView));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceView(testSurfaceView));
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.clearVideoSurfaceView(testSurfaceView));
+ activityRule.runOnUiThread(() -> controller.clearVideoSurfaceView(testSurfaceView));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoSurfaceView_withDifferentSurfaceView_doesNothing() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoSurfaceView_withDifferentSurfaceView_doesNothing() throws Throwable {
+ MediaController controller = createController();
SurfaceView testSurfaceView = activity.getFirstSurfaceView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurfaceView(testSurfaceView));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceView(testSurfaceView));
SurfaceView anotherTestSurfaceView = activity.getSecondSurfaceView();
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.clearVideoSurfaceView(anotherTestSurfaceView));
+ activityRule.runOnUiThread(() -> controller.clearVideoSurfaceView(anotherTestSurfaceView));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoSurfaceView_withNull_doesNothing() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoSurfaceView_withNull_doesNothing() throws Throwable {
+ MediaController controller = createController();
SurfaceView testSurfaceView = activity.getFirstSurfaceView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurfaceView(testSurfaceView));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceView(testSurfaceView));
SurfaceView anotherTestSurfaceView = activity.getSecondSurfaceView();
- threadTestRule.getHandler().postAndSync(() -> controller.clearVideoSurfaceView(null));
+ activityRule.runOnUiThread(() -> controller.clearVideoSurfaceView(null));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void setVideoTextureView() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void setVideoTextureView() throws Throwable {
+ MediaController controller = createController();
TextureView testTextureView = activity.getFirstTextureView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoTextureView(testTextureView));
+ activityRule.runOnUiThread(() -> controller.setVideoTextureView(testTextureView));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void setVideoTextureView_withNull_clearsTextureView() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void setVideoTextureView_withNull_clearsTextureView() throws Throwable {
+ MediaController controller = createController();
TextureView testTextureView = activity.getFirstTextureView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoTextureView(testTextureView));
+ activityRule.runOnUiThread(() -> controller.setVideoTextureView(testTextureView));
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoTextureView(null));
+ activityRule.runOnUiThread(() -> controller.setVideoTextureView(null));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
public void setVideoTextureView_whenSurfaceTextureIsDestroyed_surfaceIsClearedFromPlayer()
throws Throwable {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ MediaController controller = createController();
TextureView testTextureView = activity.getFirstTextureView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoTextureView(testTextureView));
+ activityRule.runOnUiThread(() -> controller.setVideoTextureView(testTextureView));
activityRule.runOnUiThread(
() -> {
@@ -368,14 +361,15 @@ public class MediaControllerSurfaceTest {
});
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
public void setVideoTextureView_whenSurfaceTextureIsAvailable_surfaceIsSetToPlayer()
throws Throwable {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ MediaController controller = createController();
TextureView testTextureView = activity.getFirstTextureView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoTextureView(testTextureView));
+ activityRule.runOnUiThread(() -> controller.setVideoTextureView(testTextureView));
activityRule.runOnUiThread(
() -> {
ViewGroup rootViewGroup = activity.getRootViewGroup();
@@ -391,89 +385,98 @@ public class MediaControllerSurfaceTest {
});
PollingCheck.waitFor(TIMEOUT_MS, () -> remoteSession.getMockPlayer().surfaceExists());
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoTextureView_withTheSameTextureView() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoTextureView_withTheSameTextureView() throws Throwable {
+ MediaController controller = createController();
TextureView testTextureView = activity.getFirstTextureView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoTextureView(testTextureView));
+ activityRule.runOnUiThread(() -> controller.setVideoTextureView(testTextureView));
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.clearVideoTextureView(testTextureView));
+ activityRule.runOnUiThread(() -> controller.clearVideoTextureView(testTextureView));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoTextureView_withDifferentTextureView_doesNothing() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoTextureView_withDifferentTextureView_doesNothing() throws Throwable {
+ MediaController controller = createController();
TextureView testTextureView = activity.getFirstTextureView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoTextureView(testTextureView));
+ activityRule.runOnUiThread(() -> controller.setVideoTextureView(testTextureView));
TextureView anotherTestTextureView = activity.getSecondTextureView();
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.clearVideoTextureView(anotherTestTextureView));
+ activityRule.runOnUiThread(() -> controller.clearVideoTextureView(anotherTestTextureView));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoTextureView_withNull_doesNothing() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoTextureView_withNull_doesNothing() throws Throwable {
+ MediaController controller = createController();
TextureView testTextureView = activity.getFirstTextureView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoTextureView(testTextureView));
+ activityRule.runOnUiThread(() -> controller.setVideoTextureView(testTextureView));
- threadTestRule.getHandler().postAndSync(() -> controller.clearVideoTextureView(null));
+ activityRule.runOnUiThread(() -> controller.clearVideoTextureView(null));
assertThat(remoteSession.getMockPlayer().surfaceExists()).isTrue();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoSurfaceWithNoArguments_afterSetVideoSurface() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoSurfaceWithNoArguments_afterSetVideoSurface() throws Throwable {
+ MediaController controller = createController();
Surface testSurface = activity.getFirstSurfaceHolder().getSurface();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurface(testSurface));
+ activityRule.runOnUiThread(() -> controller.setVideoSurface(testSurface));
- threadTestRule.getHandler().postAndSync(() -> controller.clearVideoSurface());
+ activityRule.runOnUiThread(() -> controller.clearVideoSurface());
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoSurfaceWithNoArguments_afterSetVideoSurfaceHolder() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoSurfaceWithNoArguments_afterSetVideoSurfaceHolder() throws Throwable {
+ MediaController controller = createController();
SurfaceHolder testSurfaceHolder = activity.getFirstSurfaceHolder();
- threadTestRule
- .getHandler()
- .postAndSync(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceHolder(testSurfaceHolder));
- threadTestRule.getHandler().postAndSync(() -> controller.clearVideoSurface());
+ activityRule.runOnUiThread(() -> controller.clearVideoSurface());
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoSurfaceWithNoArguments_afterSetVideoSurfaceView() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoSurfaceWithNoArguments_afterSetVideoSurfaceView() throws Throwable {
+ MediaController controller = createController();
SurfaceView testSurfaceView = activity.getFirstSurfaceView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoSurfaceView(testSurfaceView));
+ activityRule.runOnUiThread(() -> controller.setVideoSurfaceView(testSurfaceView));
- threadTestRule.getHandler().postAndSync(() -> controller.clearVideoSurface());
+ activityRule.runOnUiThread(() -> controller.clearVideoSurface());
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
}
@Test
- public void clearVideoSurfaceWithNoArguments_afterSetVideoTextureView() throws Exception {
- MediaController controller = controllerTestRule.createController(remoteSession.getToken());
+ public void clearVideoSurfaceWithNoArguments_afterSetVideoTextureView() throws Throwable {
+ MediaController controller = createController();
TextureView testTextureView = activity.getFirstTextureView();
- threadTestRule.getHandler().postAndSync(() -> controller.setVideoTextureView(testTextureView));
+ activityRule.runOnUiThread(() -> controller.setVideoTextureView(testTextureView));
- threadTestRule.getHandler().postAndSync(() -> controller.clearVideoSurface());
+ activityRule.runOnUiThread(() -> controller.clearVideoSurface());
assertThat(remoteSession.getMockPlayer().surfaceExists()).isFalse();
+ activityRule.runOnUiThread(controller::release);
+ }
+
+ private MediaController createController() throws Exception {
+ return new MediaController.Builder(activity, remoteSession.getToken())
+ .setApplicationLooper(Looper.getMainLooper())
+ .buildAsync()
+ .get();
}
}