mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Move unsuitable output path logic <API31 into SuitableOutputChecker
This avoids distributing the logic between multiple classes and keeps ExoPlayerImpl simpler. PiperOrigin-RevId: 726874038 (cherry picked from commit 1015ef8b565ed04e88a9c596798d294327d05536)
This commit is contained in:
parent
841e27ae5c
commit
41af00f100
@ -15,9 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer;
|
package androidx.media3.exoplayer;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.media.AudioDeviceCallback;
|
||||||
|
import android.media.AudioDeviceInfo;
|
||||||
|
import android.media.AudioManager;
|
||||||
import android.media.MediaRoute2Info;
|
import android.media.MediaRoute2Info;
|
||||||
import android.media.MediaRouter2;
|
import android.media.MediaRouter2;
|
||||||
import android.media.MediaRouter2.ControllerCallback;
|
import android.media.MediaRouter2.ControllerCallback;
|
||||||
@ -31,72 +36,120 @@ import androidx.annotation.RequiresApi;
|
|||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/** Default implementation for {@link SuitableOutputChecker}. */
|
/** Default implementation for {@link SuitableOutputChecker}. */
|
||||||
@RequiresApi(35)
|
|
||||||
/* package */ final class DefaultSuitableOutputChecker implements SuitableOutputChecker {
|
/* package */ final class DefaultSuitableOutputChecker implements SuitableOutputChecker {
|
||||||
|
|
||||||
|
@Nullable private final SuitableOutputChecker impl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the default {@link SuitableOutputChecker}.
|
||||||
|
*
|
||||||
|
* @param context A {@link Context}.
|
||||||
|
* @param eventHandler A {@link Handler} to trigger {@link Callback} methods on.
|
||||||
|
*/
|
||||||
|
public DefaultSuitableOutputChecker(Context context, Handler eventHandler) {
|
||||||
|
if (Util.SDK_INT >= 35) {
|
||||||
|
impl = new ImplApi35(context, eventHandler);
|
||||||
|
} else if (Util.SDK_INT >= 23) {
|
||||||
|
impl = new ImplApi23(context, eventHandler);
|
||||||
|
} else {
|
||||||
|
impl = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enable(Callback callback) {
|
||||||
|
if (impl != null) {
|
||||||
|
impl.enable(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
if (impl != null) {
|
||||||
|
impl.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSelectedOutputSuitableForPlayback() {
|
||||||
|
return impl == null || impl.isSelectedOutputSuitableForPlayback();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(35)
|
||||||
|
private static final class ImplApi35 implements SuitableOutputChecker {
|
||||||
private static final RouteDiscoveryPreference EMPTY_DISCOVERY_PREFERENCE =
|
private static final RouteDiscoveryPreference EMPTY_DISCOVERY_PREFERENCE =
|
||||||
new RouteDiscoveryPreference.Builder(
|
new RouteDiscoveryPreference.Builder(
|
||||||
/* preferredFeatures= */ ImmutableList.of(), /* activeScan= */ false)
|
/* preferredFeatures= */ ImmutableList.of(), /* activeScan= */ false)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private final MediaRouter2 router;
|
private final Context applicationContext;
|
||||||
private final RouteCallback routeCallback;
|
private final Handler eventHandler;
|
||||||
private final Executor executor;
|
|
||||||
|
|
||||||
|
private @MonotonicNonNull MediaRouter2 router;
|
||||||
|
private @MonotonicNonNull RouteCallback routeCallback;
|
||||||
@Nullable private ControllerCallback controllerCallback;
|
@Nullable private ControllerCallback controllerCallback;
|
||||||
private boolean isPreviousSelectedOutputSuitableForPlayback;
|
private boolean isSelectedOutputSuitableForPlayback;
|
||||||
|
|
||||||
public DefaultSuitableOutputChecker(Context context, Handler eventHandler) {
|
public ImplApi35(Context context, Handler eventHandler) {
|
||||||
router = MediaRouter2.getInstance(context);
|
this.applicationContext = context.getApplicationContext();
|
||||||
|
this.eventHandler = eventHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ThreadSafe") // Handler is thread-safe, but not annotated.
|
||||||
|
@Override
|
||||||
|
public void enable(Callback callback) {
|
||||||
|
router = MediaRouter2.getInstance(applicationContext);
|
||||||
routeCallback = new RouteCallback() {};
|
routeCallback = new RouteCallback() {};
|
||||||
executor =
|
Executor executor =
|
||||||
new Executor() {
|
new Executor() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(Runnable command) {
|
public void execute(Runnable command) {
|
||||||
Util.postOrRun(eventHandler, command);
|
Util.postOrRun(eventHandler, command);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void enable(Callback callback) {
|
|
||||||
router.registerRouteCallback(executor, routeCallback, EMPTY_DISCOVERY_PREFERENCE);
|
router.registerRouteCallback(executor, routeCallback, EMPTY_DISCOVERY_PREFERENCE);
|
||||||
controllerCallback =
|
controllerCallback =
|
||||||
new ControllerCallback() {
|
new ControllerCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onControllerUpdated(RoutingController controller) {
|
public void onControllerUpdated(RoutingController controller) {
|
||||||
boolean isCurrentSelectedOutputSuitableForPlayback =
|
boolean isCurrentSelectedOutputSuitableForPlayback =
|
||||||
isSelectedOutputSuitableForPlayback();
|
isSelectedOutputSuitableForPlayback(router);
|
||||||
if (isPreviousSelectedOutputSuitableForPlayback
|
if (isSelectedOutputSuitableForPlayback
|
||||||
!= isCurrentSelectedOutputSuitableForPlayback) {
|
!= isCurrentSelectedOutputSuitableForPlayback) {
|
||||||
isPreviousSelectedOutputSuitableForPlayback =
|
isSelectedOutputSuitableForPlayback = isCurrentSelectedOutputSuitableForPlayback;
|
||||||
isCurrentSelectedOutputSuitableForPlayback;
|
|
||||||
callback.onSelectedOutputSuitabilityChanged(
|
callback.onSelectedOutputSuitabilityChanged(
|
||||||
isCurrentSelectedOutputSuitableForPlayback);
|
isCurrentSelectedOutputSuitableForPlayback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
router.registerControllerCallback(executor, controllerCallback);
|
router.registerControllerCallback(executor, controllerCallback);
|
||||||
isPreviousSelectedOutputSuitableForPlayback = isSelectedOutputSuitableForPlayback();
|
isSelectedOutputSuitableForPlayback = isSelectedOutputSuitableForPlayback(router);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disable() {
|
public void disable() {
|
||||||
checkStateNotNull(controllerCallback, "SuitableOutputChecker is not enabled");
|
checkStateNotNull(controllerCallback, "SuitableOutputChecker is not enabled");
|
||||||
router.unregisterControllerCallback(controllerCallback);
|
checkNotNull(router).unregisterControllerCallback(controllerCallback);
|
||||||
controllerCallback = null;
|
controllerCallback = null;
|
||||||
router.unregisterRouteCallback(routeCallback);
|
router.unregisterRouteCallback(checkNotNull(routeCallback));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSelectedOutputSuitableForPlayback() {
|
public boolean isSelectedOutputSuitableForPlayback() {
|
||||||
checkStateNotNull(controllerCallback, "SuitableOutputChecker is not enabled");
|
return isSelectedOutputSuitableForPlayback;
|
||||||
int transferReason = router.getSystemController().getRoutingSessionInfo().getTransferReason();
|
}
|
||||||
boolean wasTransferInitiatedBySelf = router.getSystemController().wasTransferInitiatedBySelf();
|
|
||||||
|
private static boolean isSelectedOutputSuitableForPlayback(MediaRouter2 router) {
|
||||||
|
int transferReason =
|
||||||
|
checkNotNull(router).getSystemController().getRoutingSessionInfo().getTransferReason();
|
||||||
|
boolean wasTransferInitiatedBySelf =
|
||||||
|
router.getSystemController().wasTransferInitiatedBySelf();
|
||||||
for (MediaRoute2Info routeInfo : router.getSystemController().getSelectedRoutes()) {
|
for (MediaRoute2Info routeInfo : router.getSystemController().getSelectedRoutes()) {
|
||||||
if (isRouteSuitableForMediaPlayback(routeInfo, transferReason, wasTransferInitiatedBySelf)) {
|
if (isRouteSuitableForMediaPlayback(
|
||||||
|
routeInfo, transferReason, wasTransferInitiatedBySelf)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,3 +169,98 @@ import java.util.concurrent.Executor;
|
|||||||
return suitabilityStatus == MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
|
return suitabilityStatus == MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(23)
|
||||||
|
private static final class ImplApi23 implements SuitableOutputChecker {
|
||||||
|
|
||||||
|
private final Context applicationContext;
|
||||||
|
private final Handler eventHandler;
|
||||||
|
|
||||||
|
@Nullable private AudioManager audioManager;
|
||||||
|
private @MonotonicNonNull AudioDeviceCallback audioDeviceCallback;
|
||||||
|
private boolean isSelectedOutputSuitableForPlayback;
|
||||||
|
|
||||||
|
public ImplApi23(Context context, Handler eventHandler) {
|
||||||
|
this.applicationContext = context.getApplicationContext();
|
||||||
|
this.eventHandler = eventHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enable(Callback callback) {
|
||||||
|
AudioManager audioManager =
|
||||||
|
(AudioManager) applicationContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
if (audioManager == null) {
|
||||||
|
isSelectedOutputSuitableForPlayback = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.audioManager = audioManager;
|
||||||
|
audioDeviceCallback =
|
||||||
|
new AudioDeviceCallback() {
|
||||||
|
@Override
|
||||||
|
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
|
||||||
|
updateIsSelectedOutputSuitableForPlayback(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
|
||||||
|
updateIsSelectedOutputSuitableForPlayback(callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
audioManager.registerAudioDeviceCallback(audioDeviceCallback, eventHandler);
|
||||||
|
isSelectedOutputSuitableForPlayback = hasSupportedAudioOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
if (audioManager != null) {
|
||||||
|
audioManager.unregisterAudioDeviceCallback(checkNotNull(audioDeviceCallback));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSelectedOutputSuitableForPlayback() {
|
||||||
|
return isSelectedOutputSuitableForPlayback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateIsSelectedOutputSuitableForPlayback(Callback callback) {
|
||||||
|
boolean isSelectedOutputSuitableForPlayback = hasSupportedAudioOutput();
|
||||||
|
if (this.isSelectedOutputSuitableForPlayback != isSelectedOutputSuitableForPlayback) {
|
||||||
|
this.isSelectedOutputSuitableForPlayback = isSelectedOutputSuitableForPlayback;
|
||||||
|
callback.onSelectedOutputSuitabilityChanged(isSelectedOutputSuitableForPlayback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasSupportedAudioOutput() {
|
||||||
|
if (!Util.isWear(applicationContext)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
AudioDeviceInfo[] audioDeviceInfos =
|
||||||
|
checkStateNotNull(audioManager).getDevices(AudioManager.GET_DEVICES_OUTPUTS);
|
||||||
|
for (AudioDeviceInfo device : audioDeviceInfos) {
|
||||||
|
if (device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
|
||||||
|
|| device.getType() == AudioDeviceInfo.TYPE_LINE_ANALOG
|
||||||
|
|| device.getType() == AudioDeviceInfo.TYPE_LINE_DIGITAL
|
||||||
|
|| device.getType() == AudioDeviceInfo.TYPE_USB_DEVICE
|
||||||
|
|| device.getType() == AudioDeviceInfo.TYPE_WIRED_HEADPHONES
|
||||||
|
|| device.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Util.SDK_INT >= 26 && device.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Util.SDK_INT >= 28 && device.getType() == AudioDeviceInfo.TYPE_HEARING_AID) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Util.SDK_INT >= 31
|
||||||
|
&& (device.getType() == AudioDeviceInfo.TYPE_BLE_HEADSET
|
||||||
|
|| device.getType() == AudioDeviceInfo.TYPE_BLE_SPEAKER)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Util.SDK_INT >= 33 && device.getType() == AudioDeviceInfo.TYPE_BLE_BROADCAST) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -252,7 +252,7 @@ public interface ExoPlayer extends Player {
|
|||||||
/* package */ boolean suppressPlaybackOnUnsuitableOutput;
|
/* package */ boolean suppressPlaybackOnUnsuitableOutput;
|
||||||
/* package */ String playerName;
|
/* package */ String playerName;
|
||||||
/* package */ boolean dynamicSchedulingEnabled;
|
/* package */ boolean dynamicSchedulingEnabled;
|
||||||
@Nullable /* package */ SuitableOutputChecker suitableOutputChecker;
|
/* package */ SuitableOutputChecker suitableOutputChecker;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a builder.
|
* Creates a builder.
|
||||||
@ -282,6 +282,7 @@ public interface ExoPlayer extends Player {
|
|||||||
* <li>{@link AudioAttributes}: {@link AudioAttributes#DEFAULT}, not handling audio focus
|
* <li>{@link AudioAttributes}: {@link AudioAttributes#DEFAULT}, not handling audio focus
|
||||||
* <li>{@link C.WakeMode}: {@link C#WAKE_MODE_NONE}
|
* <li>{@link C.WakeMode}: {@link C#WAKE_MODE_NONE}
|
||||||
* <li>{@code handleAudioBecomingNoisy}: {@code false}
|
* <li>{@code handleAudioBecomingNoisy}: {@code false}
|
||||||
|
* <li>{@code suppressPlaybackOnUnsuitableOutput}: {@code false}
|
||||||
* <li>{@code skipSilenceEnabled}: {@code false}
|
* <li>{@code skipSilenceEnabled}: {@code false}
|
||||||
* <li>{@link C.VideoScalingMode}: {@link C#VIDEO_SCALING_MODE_DEFAULT}
|
* <li>{@link C.VideoScalingMode}: {@link C#VIDEO_SCALING_MODE_DEFAULT}
|
||||||
* <li>{@link C.VideoChangeFrameRateStrategy}: {@link
|
* <li>{@link C.VideoChangeFrameRateStrategy}: {@link
|
||||||
@ -459,6 +460,7 @@ public interface ExoPlayer extends Player {
|
|||||||
usePlatformDiagnostics = true;
|
usePlatformDiagnostics = true;
|
||||||
playerName = "";
|
playerName = "";
|
||||||
priority = C.PRIORITY_PLAYBACK;
|
priority = C.PRIORITY_PLAYBACK;
|
||||||
|
suitableOutputChecker = new DefaultSuitableOutputChecker(context, new Handler(looper));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1021,7 +1023,6 @@ public interface ExoPlayer extends Player {
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
@RestrictTo(LIBRARY_GROUP)
|
@RestrictTo(LIBRARY_GROUP)
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@RequiresApi(35)
|
|
||||||
public Builder setSuitableOutputChecker(SuitableOutputChecker suitableOutputChecker) {
|
public Builder setSuitableOutputChecker(SuitableOutputChecker suitableOutputChecker) {
|
||||||
checkState(!buildCalled);
|
checkState(!buildCalled);
|
||||||
this.suitableOutputChecker = suitableOutputChecker;
|
this.suitableOutputChecker = suitableOutputChecker;
|
||||||
@ -1088,11 +1089,6 @@ public interface ExoPlayer extends Player {
|
|||||||
public ExoPlayer build() {
|
public ExoPlayer build() {
|
||||||
checkState(!buildCalled);
|
checkState(!buildCalled);
|
||||||
buildCalled = true;
|
buildCalled = true;
|
||||||
if (suitableOutputChecker == null
|
|
||||||
&& Util.SDK_INT >= 35
|
|
||||||
&& suppressPlaybackOnUnsuitableOutput) {
|
|
||||||
suitableOutputChecker = new DefaultSuitableOutputChecker(context, new Handler(looper));
|
|
||||||
}
|
|
||||||
return new ExoPlayerImpl(/* builder= */ this, /* wrappingPlayer= */ null);
|
return new ExoPlayerImpl(/* builder= */ this, /* wrappingPlayer= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,9 +45,7 @@ import android.annotation.SuppressLint;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.SurfaceTexture;
|
import android.graphics.SurfaceTexture;
|
||||||
import android.media.AudioDeviceCallback;
|
|
||||||
import android.media.AudioDeviceInfo;
|
import android.media.AudioDeviceInfo;
|
||||||
import android.media.AudioManager;
|
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@ -176,8 +174,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
private final WakeLockManager wakeLockManager;
|
private final WakeLockManager wakeLockManager;
|
||||||
private final WifiLockManager wifiLockManager;
|
private final WifiLockManager wifiLockManager;
|
||||||
private final long detachSurfaceTimeoutMs;
|
private final long detachSurfaceTimeoutMs;
|
||||||
@Nullable private AudioManager audioManager;
|
|
||||||
private final boolean suppressPlaybackOnUnsuitableOutput;
|
|
||||||
@Nullable private final SuitableOutputChecker suitableOutputChecker;
|
@Nullable private final SuitableOutputChecker suitableOutputChecker;
|
||||||
private final BackgroundThreadStateHandler<Integer> audioSessionIdState;
|
private final BackgroundThreadStateHandler<Integer> audioSessionIdState;
|
||||||
|
|
||||||
@ -293,7 +289,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
this.applicationLooper = builder.looper;
|
this.applicationLooper = builder.looper;
|
||||||
this.clock = builder.clock;
|
this.clock = builder.clock;
|
||||||
this.wrappingPlayer = wrappingPlayer == null ? this : wrappingPlayer;
|
this.wrappingPlayer = wrappingPlayer == null ? this : wrappingPlayer;
|
||||||
this.suppressPlaybackOnUnsuitableOutput = builder.suppressPlaybackOnUnsuitableOutput;
|
|
||||||
listeners =
|
listeners =
|
||||||
new ListenerSet<>(
|
new ListenerSet<>(
|
||||||
applicationLooper,
|
applicationLooper,
|
||||||
@ -416,15 +411,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
audioFocusManager = new AudioFocusManager(builder.context, eventHandler, componentListener);
|
audioFocusManager = new AudioFocusManager(builder.context, eventHandler, componentListener);
|
||||||
audioFocusManager.setAudioAttributes(builder.handleAudioFocus ? audioAttributes : null);
|
audioFocusManager.setAudioAttributes(builder.handleAudioFocus ? audioAttributes : null);
|
||||||
|
|
||||||
|
if (builder.suppressPlaybackOnUnsuitableOutput) {
|
||||||
suitableOutputChecker = builder.suitableOutputChecker;
|
suitableOutputChecker = builder.suitableOutputChecker;
|
||||||
if (suitableOutputChecker != null && Util.SDK_INT >= 35) {
|
|
||||||
suitableOutputChecker.enable(this::onSelectedOutputSuitabilityChanged);
|
suitableOutputChecker.enable(this::onSelectedOutputSuitabilityChanged);
|
||||||
} else if (suppressPlaybackOnUnsuitableOutput && Util.SDK_INT >= 23) {
|
} else {
|
||||||
audioManager = (AudioManager) applicationContext.getSystemService(Context.AUDIO_SERVICE);
|
suitableOutputChecker = null;
|
||||||
Api23.registerAudioDeviceCallback(
|
|
||||||
audioManager,
|
|
||||||
new NoSuitableOutputPlaybackSuppressionAudioDeviceCallback(),
|
|
||||||
new Handler(applicationLooper));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (builder.deviceVolumeControlEnabled) {
|
if (builder.deviceVolumeControlEnabled) {
|
||||||
@ -1037,6 +1028,9 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
wakeLockManager.setStayAwake(false);
|
wakeLockManager.setStayAwake(false);
|
||||||
wifiLockManager.setStayAwake(false);
|
wifiLockManager.setStayAwake(false);
|
||||||
audioFocusManager.release();
|
audioFocusManager.release();
|
||||||
|
if (suitableOutputChecker != null) {
|
||||||
|
suitableOutputChecker.disable();
|
||||||
|
}
|
||||||
if (!internalPlayer.release()) {
|
if (!internalPlayer.release()) {
|
||||||
// One of the renderers timed out releasing its resources.
|
// One of the renderers timed out releasing its resources.
|
||||||
listeners.sendEvent(
|
listeners.sendEvent(
|
||||||
@ -1053,9 +1047,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
if (playbackInfo.sleepingForOffload) {
|
if (playbackInfo.sleepingForOffload) {
|
||||||
playbackInfo = playbackInfo.copyWithEstimatedPosition();
|
playbackInfo = playbackInfo.copyWithEstimatedPosition();
|
||||||
}
|
}
|
||||||
if (suitableOutputChecker != null && Util.SDK_INT >= 35) {
|
|
||||||
suitableOutputChecker.disable();
|
|
||||||
}
|
|
||||||
playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE);
|
playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE);
|
||||||
playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(playbackInfo.periodId);
|
playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(playbackInfo.periodId);
|
||||||
playbackInfo.bufferedPositionUs = playbackInfo.positionUs;
|
playbackInfo.bufferedPositionUs = playbackInfo.positionUs;
|
||||||
@ -2793,8 +2784,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
if (playerCommand == AudioFocusManager.PLAYER_COMMAND_WAIT_FOR_CALLBACK) {
|
if (playerCommand == AudioFocusManager.PLAYER_COMMAND_WAIT_FOR_CALLBACK) {
|
||||||
return Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS;
|
return Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS;
|
||||||
}
|
}
|
||||||
if (suppressPlaybackOnUnsuitableOutput) {
|
if (suitableOutputChecker != null) {
|
||||||
if (playWhenReady && !hasSupportedAudioOutput()) {
|
if (playWhenReady && !suitableOutputChecker.isSelectedOutputSuitableForPlayback()) {
|
||||||
return Player.PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT;
|
return Player.PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT;
|
||||||
}
|
}
|
||||||
if (!playWhenReady
|
if (!playWhenReady
|
||||||
@ -2806,19 +2797,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
return Player.PLAYBACK_SUPPRESSION_REASON_NONE;
|
return Player.PLAYBACK_SUPPRESSION_REASON_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasSupportedAudioOutput() {
|
|
||||||
if (Util.SDK_INT >= 35 && suitableOutputChecker != null) {
|
|
||||||
return suitableOutputChecker.isSelectedOutputSuitableForPlayback();
|
|
||||||
} else if (Util.SDK_INT >= 23 && audioManager != null) {
|
|
||||||
return Api23.isSuitableExternalAudioOutputPresentInAudioDeviceInfoList(
|
|
||||||
applicationContext, audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS));
|
|
||||||
} else {
|
|
||||||
// The Audio Manager API to determine the list of connected audio devices is available only in
|
|
||||||
// API >= 23.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateWakeAndWifiLock() {
|
private void updateWakeAndWifiLock() {
|
||||||
@State int playbackState = getPlaybackState();
|
@State int playbackState = getPlaybackState();
|
||||||
switch (playbackState) {
|
switch (playbackState) {
|
||||||
@ -2927,6 +2905,10 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onSelectedOutputSuitabilityChanged(boolean isSelectedOutputSuitableForPlayback) {
|
private void onSelectedOutputSuitabilityChanged(boolean isSelectedOutputSuitableForPlayback) {
|
||||||
|
if (playerReleased) {
|
||||||
|
// Stale event.
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (isSelectedOutputSuitableForPlayback) {
|
if (isSelectedOutputSuitableForPlayback) {
|
||||||
if (playbackInfo.playbackSuppressionReason
|
if (playbackInfo.playbackSuppressionReason
|
||||||
== Player.PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT) {
|
== Player.PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT) {
|
||||||
@ -3397,78 +3379,4 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(23)
|
|
||||||
private static final class Api23 {
|
|
||||||
private Api23() {}
|
|
||||||
|
|
||||||
public static boolean isSuitableExternalAudioOutputPresentInAudioDeviceInfoList(
|
|
||||||
Context context, AudioDeviceInfo[] audioDeviceInfos) {
|
|
||||||
if (!Util.isWear(context)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (AudioDeviceInfo device : audioDeviceInfos) {
|
|
||||||
if (device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
|
|
||||||
|| device.getType() == AudioDeviceInfo.TYPE_LINE_ANALOG
|
|
||||||
|| device.getType() == AudioDeviceInfo.TYPE_LINE_DIGITAL
|
|
||||||
|| device.getType() == AudioDeviceInfo.TYPE_USB_DEVICE
|
|
||||||
|| device.getType() == AudioDeviceInfo.TYPE_WIRED_HEADPHONES
|
|
||||||
|| device.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (Util.SDK_INT >= 26 && device.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (Util.SDK_INT >= 28 && device.getType() == AudioDeviceInfo.TYPE_HEARING_AID) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (Util.SDK_INT >= 31
|
|
||||||
&& (device.getType() == AudioDeviceInfo.TYPE_BLE_HEADSET
|
|
||||||
|| device.getType() == AudioDeviceInfo.TYPE_BLE_SPEAKER)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (Util.SDK_INT >= 33 && device.getType() == AudioDeviceInfo.TYPE_BLE_BROADCAST) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void registerAudioDeviceCallback(
|
|
||||||
AudioManager audioManager, AudioDeviceCallback audioDeviceCallback, Handler handler) {
|
|
||||||
audioManager.registerAudioDeviceCallback(audioDeviceCallback, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link AudioDeviceCallback} to change playback suppression reason when suitable audio outputs
|
|
||||||
* are either added in unsuitable output based playback suppression state or removed during an
|
|
||||||
* ongoing playback.
|
|
||||||
*/
|
|
||||||
@RequiresApi(23)
|
|
||||||
private final class NoSuitableOutputPlaybackSuppressionAudioDeviceCallback
|
|
||||||
extends AudioDeviceCallback {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
|
|
||||||
if (hasSupportedAudioOutput()
|
|
||||||
&& playbackInfo.playbackSuppressionReason
|
|
||||||
== Player.PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT) {
|
|
||||||
updatePlaybackInfoForPlayWhenReadyAndSuppressionReasonStates(
|
|
||||||
playbackInfo.playWhenReady,
|
|
||||||
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
|
||||||
Player.PLAYBACK_SUPPRESSION_REASON_NONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
|
|
||||||
if (!hasSupportedAudioOutput()) {
|
|
||||||
updatePlaybackInfoForPlayWhenReadyAndSuppressionReasonStates(
|
|
||||||
playbackInfo.playWhenReady,
|
|
||||||
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
|
||||||
Player.PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,10 @@ package androidx.media3.exoplayer;
|
|||||||
|
|
||||||
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
|
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
|
||||||
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.annotation.RestrictTo;
|
import androidx.annotation.RestrictTo;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
/** Provides methods to check the suitability of selected media outputs. */
|
/** Provides methods to check the suitability of selected media outputs. */
|
||||||
@RequiresApi(35)
|
|
||||||
@RestrictTo(LIBRARY_GROUP)
|
@RestrictTo(LIBRARY_GROUP)
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public interface SuitableOutputChecker {
|
public interface SuitableOutputChecker {
|
||||||
|
@ -94,6 +94,7 @@ import static org.mockito.Mockito.verify;
|
|||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.robolectric.Shadows.shadowOf;
|
import static org.robolectric.Shadows.shadowOf;
|
||||||
|
import static org.robolectric.annotation.Config.ALL_SDKS;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -14022,7 +14023,7 @@ public class ExoPlayerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Config(sdk = Config.ALL_SDKS)
|
@Config(sdk = ALL_SDKS)
|
||||||
public void builder_inBackgroundThreadWithAllowedAnyThreadMethods_doesNotThrow()
|
public void builder_inBackgroundThreadWithAllowedAnyThreadMethods_doesNotThrow()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
AtomicReference<Player> playerReference = new AtomicReference<>();
|
AtomicReference<Player> playerReference = new AtomicReference<>();
|
||||||
@ -15023,6 +15024,8 @@ public class ExoPlayerTest {
|
|||||||
* Tests playback suppression for playback with only unsuitable outputs (e.g. builtin speaker) on
|
* Tests playback suppression for playback with only unsuitable outputs (e.g. builtin speaker) on
|
||||||
* the Wear OS.
|
* the Wear OS.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void play_withOnlyUnsuitableOutputsOnWear_shouldSuppressPlayback() throws Exception {
|
public void play_withOnlyUnsuitableOutputsOnWear_shouldSuppressPlayback() throws Exception {
|
||||||
addWatchAsSystemFeature();
|
addWatchAsSystemFeature();
|
||||||
@ -15059,6 +15062,8 @@ public class ExoPlayerTest {
|
|||||||
* Tests no playback suppression for playback with suitable output (e.g. BluetoothA2DP) on the
|
* Tests no playback suppression for playback with suitable output (e.g. BluetoothA2DP) on the
|
||||||
* Wear OS.
|
* Wear OS.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void play_withAtleastOneSuitableOutputOnWear_shouldNotSuppressPlayback() throws Exception {
|
public void play_withAtleastOneSuitableOutputOnWear_shouldNotSuppressPlayback() throws Exception {
|
||||||
addWatchAsSystemFeature();
|
addWatchAsSystemFeature();
|
||||||
@ -15095,6 +15100,8 @@ public class ExoPlayerTest {
|
|||||||
* Tests same playback suppression reason for multiple play calls with only unsuitable output
|
* Tests same playback suppression reason for multiple play calls with only unsuitable output
|
||||||
* (e.g. builtin speaker) on the Wear OS.
|
* (e.g. builtin speaker) on the Wear OS.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void
|
public void
|
||||||
play_callMultipleTimesOnUnsuitableOutputFollowedByPause_shouldRetainSameSuppressionReason()
|
play_callMultipleTimesOnUnsuitableOutputFollowedByPause_shouldRetainSameSuppressionReason()
|
||||||
@ -15134,6 +15141,8 @@ public class ExoPlayerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Tests playback suppression for playback on the built-speaker on non-Wear OS surfaces. */
|
/** Tests playback suppression for playback on the built-speaker on non-Wear OS surfaces. */
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void play_onBuiltinSpeakerWithoutWearPresentAsSystemFeature_shouldNotSuppressPlayback()
|
public void play_onBuiltinSpeakerWithoutWearPresentAsSystemFeature_shouldNotSuppressPlayback()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@ -15171,6 +15180,8 @@ public class ExoPlayerTest {
|
|||||||
* speaker) on Wear OS but {@link
|
* speaker) on Wear OS but {@link
|
||||||
* ExoPlayer.Builder#setSuppressPlaybackOnUnsuitableOutput(boolean)} is not called with true.
|
* ExoPlayer.Builder#setSuppressPlaybackOnUnsuitableOutput(boolean)} is not called with true.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void
|
public void
|
||||||
play_withOnlyUnsuitableOutputsWithoutEnablingPlaybackSuppression_shouldNotSuppressPlayback()
|
play_withOnlyUnsuitableOutputsWithoutEnablingPlaybackSuppression_shouldNotSuppressPlayback()
|
||||||
@ -15206,6 +15217,8 @@ public class ExoPlayerTest {
|
|||||||
* Player#PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT} when a suitable audio output is
|
* Player#PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT} when a suitable audio output is
|
||||||
* added.
|
* added.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void addSuitableOutputWhenPlaybackSuppressed_shouldRemovePlaybackSuppression()
|
public void addSuitableOutputWhenPlaybackSuppressed_shouldRemovePlaybackSuppression()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@ -15246,6 +15259,8 @@ public class ExoPlayerTest {
|
|||||||
* Tests no change in the playback suppression reason when an unsuitable audio output is connected
|
* Tests no change in the playback suppression reason when an unsuitable audio output is connected
|
||||||
* while playback was suppressed earlier.
|
* while playback was suppressed earlier.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void addUnsuitableOutputWhenPlaybackIsSuppressed_shouldNotRemovePlaybackSuppression()
|
public void addUnsuitableOutputWhenPlaybackIsSuppressed_shouldNotRemovePlaybackSuppression()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@ -15277,6 +15292,8 @@ public class ExoPlayerTest {
|
|||||||
* Tests no change in the playback suppression reason when a suitable audio output is added but
|
* Tests no change in the playback suppression reason when a suitable audio output is added but
|
||||||
* playback was not suppressed earlier.
|
* playback was not suppressed earlier.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void addSuitableOutputWhenPlaybackNotSuppressed_shouldNotRemovePlaybackSuppression()
|
public void addSuitableOutputWhenPlaybackNotSuppressed_shouldNotRemovePlaybackSuppression()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@ -15308,6 +15325,8 @@ public class ExoPlayerTest {
|
|||||||
* Player#PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT} when all the suitable audio outputs
|
* Player#PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT} when all the suitable audio outputs
|
||||||
* have been removed during an ongoing playback.
|
* have been removed during an ongoing playback.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void removeAllSuitableOutputsWhenPlaybackOngoing_shouldSetPlaybackSuppression()
|
public void removeAllSuitableOutputsWhenPlaybackOngoing_shouldSetPlaybackSuppression()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@ -15340,6 +15359,8 @@ public class ExoPlayerTest {
|
|||||||
* Tests no change in the playback suppression reason when any unsuitable audio outputs has been
|
* Tests no change in the playback suppression reason when any unsuitable audio outputs has been
|
||||||
* removed during an ongoing playback but some suitable audio outputs are still available.
|
* removed during an ongoing playback but some suitable audio outputs are still available.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void removeAnyUnsuitableOutputWhenPlaybackOngoing_shouldNotSetPlaybackSuppression()
|
public void removeAnyUnsuitableOutputWhenPlaybackOngoing_shouldNotSetPlaybackSuppression()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@ -15376,6 +15397,8 @@ public class ExoPlayerTest {
|
|||||||
* removed during an ongoing playback but at least one another suitable audio output is still
|
* removed during an ongoing playback but at least one another suitable audio output is still
|
||||||
* connected to the device.
|
* connected to the device.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void
|
public void
|
||||||
removeAnySuitableOutputButOneSuitableDeviceStillConnected_shouldNotSetPlaybackSuppression()
|
removeAnySuitableOutputButOneSuitableDeviceStillConnected_shouldNotSetPlaybackSuppression()
|
||||||
@ -15406,9 +15429,8 @@ public class ExoPlayerTest {
|
|||||||
player.release();
|
player.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Tests suppression of playback when no situable output is found. */
|
/** Tests suppression of playback when no suitable output is found. */
|
||||||
@Test
|
@Test
|
||||||
@Config(minSdk = 35)
|
|
||||||
public void verifySuitableOutput_shouldSuppressPlaybackWhenNoSuitableOutputAvailable()
|
public void verifySuitableOutput_shouldSuppressPlaybackWhenNoSuitableOutputAvailable()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
FakeSuitableOutputChecker suitableMediaOutputChecker =
|
FakeSuitableOutputChecker suitableMediaOutputChecker =
|
||||||
@ -15439,7 +15461,6 @@ public class ExoPlayerTest {
|
|||||||
|
|
||||||
/** Tests no occurrences of suppression of playback when situable output is found. */
|
/** Tests no occurrences of suppression of playback when situable output is found. */
|
||||||
@Test
|
@Test
|
||||||
@Config(minSdk = 35)
|
|
||||||
public void verifySuitableOutput_shouldNotSuppressPlaybackWhenSuitableOutputIsAvailable()
|
public void verifySuitableOutput_shouldNotSuppressPlaybackWhenSuitableOutputIsAvailable()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
FakeSuitableOutputChecker suitableMediaOutputChecker =
|
FakeSuitableOutputChecker suitableMediaOutputChecker =
|
||||||
@ -15472,7 +15493,6 @@ public class ExoPlayerTest {
|
|||||||
* disabled.
|
* disabled.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Config(minSdk = 35)
|
|
||||||
public void
|
public void
|
||||||
verifySuitableOutput_playbackSuppressionOnUnsuitableOutputDisabled_shouldNotSuppressPlayback()
|
verifySuitableOutput_playbackSuppressionOnUnsuitableOutputDisabled_shouldNotSuppressPlayback()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@ -15502,7 +15522,6 @@ public class ExoPlayerTest {
|
|||||||
|
|
||||||
/** Tests removal of suppression of playback when a suitable output is added. */
|
/** Tests removal of suppression of playback when a suitable output is added. */
|
||||||
@Test
|
@Test
|
||||||
@Config(minSdk = 35)
|
|
||||||
public void verifySuitableOutput_shouldRemovePlaybackSuppressionOnAdditionOfSuitableOutput()
|
public void verifySuitableOutput_shouldRemovePlaybackSuppressionOnAdditionOfSuitableOutput()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
FakeSuitableOutputChecker suitableMediaOutputChecker =
|
FakeSuitableOutputChecker suitableMediaOutputChecker =
|
||||||
@ -15538,7 +15557,6 @@ public class ExoPlayerTest {
|
|||||||
|
|
||||||
/** Tests suppression of playback when a suitable output is removed. */
|
/** Tests suppression of playback when a suitable output is removed. */
|
||||||
@Test
|
@Test
|
||||||
@Config(minSdk = 35)
|
|
||||||
public void verifySuitableOutput_shouldSuppressPlaybackOnRemovalOfSuitableOutput()
|
public void verifySuitableOutput_shouldSuppressPlaybackOnRemovalOfSuitableOutput()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
FakeSuitableOutputChecker suitableMediaOutputChecker =
|
FakeSuitableOutputChecker suitableMediaOutputChecker =
|
||||||
@ -15571,7 +15589,6 @@ public class ExoPlayerTest {
|
|||||||
|
|
||||||
/** Tests suppression of playback back again when a suitable output added before is removed. */
|
/** Tests suppression of playback back again when a suitable output added before is removed. */
|
||||||
@Test
|
@Test
|
||||||
@Config(minSdk = 35)
|
|
||||||
public void verifySuitableOutput_shouldSuppressPlaybackAgainAfterRemovalOfAddedSuitableOutput()
|
public void verifySuitableOutput_shouldSuppressPlaybackAgainAfterRemovalOfAddedSuitableOutput()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
FakeSuitableOutputChecker suitableMediaOutputChecker =
|
FakeSuitableOutputChecker suitableMediaOutputChecker =
|
||||||
|
@ -19,7 +19,6 @@ import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
|
|||||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.annotation.RestrictTo;
|
import androidx.annotation.RestrictTo;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.exoplayer.SuitableOutputChecker;
|
import androidx.media3.exoplayer.SuitableOutputChecker;
|
||||||
@ -28,7 +27,6 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
|||||||
/** Fake implementation for {@link SuitableOutputChecker}. */
|
/** Fake implementation for {@link SuitableOutputChecker}. */
|
||||||
@RestrictTo(LIBRARY_GROUP)
|
@RestrictTo(LIBRARY_GROUP)
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@RequiresApi(35)
|
|
||||||
public final class FakeSuitableOutputChecker implements SuitableOutputChecker {
|
public final class FakeSuitableOutputChecker implements SuitableOutputChecker {
|
||||||
|
|
||||||
/** Builder for {@link FakeSuitableOutputChecker} instance. */
|
/** Builder for {@link FakeSuitableOutputChecker} instance. */
|
||||||
|
@ -18,7 +18,6 @@ package androidx.media3.test.utils;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build.VERSION;
|
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
@ -423,7 +422,7 @@ public class TestExoPlayerBuilder {
|
|||||||
.setDeviceVolumeControlEnabled(deviceVolumeControlEnabled)
|
.setDeviceVolumeControlEnabled(deviceVolumeControlEnabled)
|
||||||
.setSuppressPlaybackOnUnsuitableOutput(suppressPlaybackWhenUnsuitableOutput)
|
.setSuppressPlaybackOnUnsuitableOutput(suppressPlaybackWhenUnsuitableOutput)
|
||||||
.experimentalSetDynamicSchedulingEnabled(dynamicSchedulingEnabled);
|
.experimentalSetDynamicSchedulingEnabled(dynamicSchedulingEnabled);
|
||||||
if (VERSION.SDK_INT >= 35 && suitableOutputChecker != null) {
|
if (suitableOutputChecker != null) {
|
||||||
builder.setSuitableOutputChecker(suitableOutputChecker);
|
builder.setSuitableOutputChecker(suitableOutputChecker);
|
||||||
}
|
}
|
||||||
if (mediaSourceFactory != null) {
|
if (mediaSourceFactory != null) {
|
||||||
|
@ -43,7 +43,6 @@ import androidx.media3.test.utils.FakeSuitableOutputChecker;
|
|||||||
import androidx.media3.test.utils.TestExoPlayerBuilder;
|
import androidx.media3.test.utils.TestExoPlayerBuilder;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -81,10 +80,13 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
shadowPackageManager =
|
||||||
|
shadowOf(ApplicationProvider.getApplicationContext().getPackageManager());
|
||||||
|
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
||||||
|
|
||||||
TestExoPlayerBuilder builder =
|
TestExoPlayerBuilder builder =
|
||||||
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext())
|
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext())
|
||||||
.setSuppressPlaybackOnUnsuitableOutput(true);
|
.setSuppressPlaybackOnUnsuitableOutput(true);
|
||||||
|
|
||||||
if (Util.SDK_INT >= 35) {
|
if (Util.SDK_INT >= 35) {
|
||||||
suitableMediaOutputChecker =
|
suitableMediaOutputChecker =
|
||||||
new FakeSuitableOutputChecker.Builder()
|
new FakeSuitableOutputChecker.Builder()
|
||||||
@ -92,11 +94,9 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
.build();
|
.build();
|
||||||
builder.setSuitableOutputChecker(suitableMediaOutputChecker);
|
builder.setSuitableOutputChecker(suitableMediaOutputChecker);
|
||||||
}
|
}
|
||||||
|
|
||||||
testPlayer = builder.build();
|
testPlayer = builder.build();
|
||||||
|
|
||||||
shadowApplication = shadowOf((Application) ApplicationProvider.getApplicationContext());
|
shadowApplication = shadowOf((Application) ApplicationProvider.getApplicationContext());
|
||||||
shadowPackageManager =
|
|
||||||
shadowOf(ApplicationProvider.getApplicationContext().getPackageManager());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -108,10 +108,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test end-to-end flow from launch of output switcher to playback getting resumed when the
|
* Test end-to-end flow from launch of output switcher to playback getting resumed when the
|
||||||
* playback is suppressed and then unsuppressed.
|
* playback is suppressed and then unsuppressed.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void playbackSuppressionFollowedByResolution_shouldLaunchOutputSwitcherAndStartPlayback()
|
public void playbackSuppressionFollowedByResolution_shouldLaunchOutputSwitcherAndStartPlayback()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
||||||
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
||||||
@ -153,10 +154,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test for the launch of system updated Output Switcher app when playback is suppressed due to
|
* Test for the launch of system updated Output Switcher app when playback is suppressed due to
|
||||||
* unsuitable output and the system updated Output Switcher is present on the device.
|
* unsuitable output and the system updated Output Switcher is present on the device.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void playEventWithPlaybackSuppression_shouldLaunchOutputSwitcher()
|
public void playEventWithPlaybackSuppression_shouldLaunchOutputSwitcher()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
||||||
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
||||||
@ -185,11 +187,12 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test for the launch of system Output Switcher app when playback is suppressed due to unsuitable
|
* Test for the launch of system Output Switcher app when playback is suppressed due to unsuitable
|
||||||
* output and both the system as well as user installed Output Switcher are present on the device.
|
* output and both the system as well as user installed Output Switcher are present on the device.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void
|
public void
|
||||||
playbackSuppressionWithSystemAndUserInstalledComponentsPresent_shouldLaunchSystemComponent()
|
playbackSuppressionWithSystemAndUserInstalledComponentsPresent_shouldLaunchSystemComponent()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
||||||
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
||||||
@ -223,11 +226,12 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test for no launch of system Output Switcher app when running on non-Wear OS device with
|
* Test for no launch of system Output Switcher app when running on non-Wear OS device with
|
||||||
* playback suppression conditions and the system Output Switcher present on the device.
|
* playback suppression conditions and the system Output Switcher present on the device.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void
|
public void
|
||||||
playEventWithPlaybackSuppressionConditionsOnNonWearOSDevice_shouldNotLaunchOutputSwitcher()
|
playEventWithPlaybackSuppressionConditionsOnNonWearOSDevice_shouldNotLaunchOutputSwitcher()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
||||||
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
||||||
@ -266,11 +270,12 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* output with the system Bluetooth Settings app present while the system Output Switcher app is
|
* output with the system Bluetooth Settings app present while the system Output Switcher app is
|
||||||
* not present on the device.
|
* not present on the device.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void
|
public void
|
||||||
playEventWithPlaybackSuppressionWhenOnlySystemBTSettingsPresent_shouldLaunchBTSettings()
|
playEventWithPlaybackSuppressionWhenOnlySystemBTSettingsPresent_shouldLaunchBTSettings()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
Settings.ACTION_BLUETOOTH_SETTINGS,
|
Settings.ACTION_BLUETOOTH_SETTINGS,
|
||||||
FAKE_SYSTEM_BT_SETTINGS_PACKAGE_NAME,
|
FAKE_SYSTEM_BT_SETTINGS_PACKAGE_NAME,
|
||||||
@ -299,10 +304,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* output with the updated system Bluetooth Settings app present while the Output Switcher app is
|
* output with the updated system Bluetooth Settings app present while the Output Switcher app is
|
||||||
* not present on the device.
|
* not present on the device.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void playbackSuppressionWhenOnlyUpdatedSystemBTSettingsPresent_shouldLaunchBTSettings()
|
public void playbackSuppressionWhenOnlyUpdatedSystemBTSettingsPresent_shouldLaunchBTSettings()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
Settings.ACTION_BLUETOOTH_SETTINGS,
|
Settings.ACTION_BLUETOOTH_SETTINGS,
|
||||||
FAKE_SYSTEM_BT_SETTINGS_PACKAGE_NAME,
|
FAKE_SYSTEM_BT_SETTINGS_PACKAGE_NAME,
|
||||||
@ -330,10 +336,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test for the launch of Output Switcher app when playback is suppressed due to unsuitable output
|
* Test for the launch of Output Switcher app when playback is suppressed due to unsuitable output
|
||||||
* and both Output Switcher as well as the Bluetooth settings are present on the device.
|
* and both Output Switcher as well as the Bluetooth settings are present on the device.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void playbackSuppressionWhenMultipleSystemComponentsPresent_shouldLaunchOutputSwitcher()
|
public void playbackSuppressionWhenMultipleSystemComponentsPresent_shouldLaunchOutputSwitcher()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
||||||
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
||||||
@ -367,10 +374,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test for no launch of the non-system and non-system updated Output Switcher app when playback
|
* Test for no launch of the non-system and non-system updated Output Switcher app when playback
|
||||||
* is suppressed due to unsuitable output.
|
* is suppressed due to unsuitable output.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void playbackSuppressionWhenOnlyUserInstalledComponentsPresent_shouldNotLaunchAnyApp()
|
public void playbackSuppressionWhenOnlyUserInstalledComponentsPresent_shouldNotLaunchAnyApp()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
||||||
"com.fake.userinstalled.outputswitcher",
|
"com.fake.userinstalled.outputswitcher",
|
||||||
@ -400,10 +408,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test for no launch of any system media output switching dialog app when playback is not
|
* Test for no launch of any system media output switching dialog app when playback is not
|
||||||
* suppressed due to unsuitable output.
|
* suppressed due to unsuitable output.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void playEventWithoutPlaybackSuppression_shouldNotLaunchOutputSwitcherOrBTSettings()
|
public void playEventWithoutPlaybackSuppression_shouldNotLaunchOutputSwitcherOrBTSettings()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
||||||
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
||||||
@ -433,10 +442,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test for no launch of any system media output switching dialog app when playback is suppressed
|
* Test for no launch of any system media output switching dialog app when playback is suppressed
|
||||||
* due to removal of all suitable audio outputs in mid of an ongoing playback.
|
* due to removal of all suitable audio outputs in mid of an ongoing playback.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void playbackSuppressionDuringOngoingPlayback_shouldOnlyPauseButNotLaunchSystemComponent()
|
public void playbackSuppressionDuringOngoingPlayback_shouldOnlyPauseButNotLaunchSystemComponent()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
||||||
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
||||||
@ -475,10 +485,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Test for pause on the Player when the playback is suppressed due to unsuitable output. */
|
/** Test for pause on the Player when the playback is suppressed due to unsuitable output. */
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void playEventWithSuppressedPlaybackCondition_shouldCallPauseOnPlayer()
|
public void playEventWithSuppressedPlaybackCondition_shouldCallPauseOnPlayer()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
||||||
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
||||||
@ -511,11 +522,12 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test for automatic resumption of the ongoing playback when it is transferred from one suitable
|
* Test for automatic resumption of the ongoing playback when it is transferred from one suitable
|
||||||
* device to another within set time out.
|
* device to another within set time out.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void
|
public void
|
||||||
transferOnGoingPlaybackFromOneSuitableDeviceToAnotherWithinSetTimeOut_shouldContinuePlayback()
|
transferOnGoingPlaybackFromOneSuitableDeviceToAnotherWithinSetTimeOut_shouldContinuePlayback()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
setupConnectedAudioOutput(
|
setupConnectedAudioOutput(
|
||||||
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP);
|
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP);
|
||||||
testPlayer.addListener(
|
testPlayer.addListener(
|
||||||
@ -541,11 +553,12 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test for automatic pause of the ongoing playback when it is transferred from one suitable
|
* Test for automatic pause of the ongoing playback when it is transferred from one suitable
|
||||||
* device to another and the time difference between switching is more than default time out
|
* device to another and the time difference between switching is more than default time out
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void
|
public void
|
||||||
transferOnGoingPlaybackFromOneSuitableDeviceToAnotherAfterTimeOut_shouldNotContinuePlayback()
|
transferOnGoingPlaybackFromOneSuitableDeviceToAnotherAfterTimeOut_shouldNotContinuePlayback()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
setupConnectedAudioOutput(
|
setupConnectedAudioOutput(
|
||||||
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP);
|
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP);
|
||||||
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||||
@ -587,10 +600,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
/**
|
/**
|
||||||
* Test for no pause on the Player when the playback is not suppressed due to unsuitable output.
|
* Test for no pause on the Player when the playback is not suppressed due to unsuitable output.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void playEventWithoutSuppressedPlaybackCondition_shouldNotCallPauseOnPlayer()
|
public void playEventWithoutSuppressedPlaybackCondition_shouldNotCallPauseOnPlayer()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
OUTPUT_SWITCHER_INTENT_ACTION_NAME,
|
||||||
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
FAKE_SYSTEM_OUTPUT_SWITCHER_PACKAGE_NAME,
|
||||||
@ -624,11 +638,12 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test to ensure player is not playing when the playback suppression due to unsuitable output is
|
* Test to ensure player is not playing when the playback suppression due to unsuitable output is
|
||||||
* removed after the default timeout.
|
* removed after the default timeout.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void
|
public void
|
||||||
playbackSuppressionChangeToNoneAfterDefaultTimeout_shouldNotChangePlaybackStateToPlaying()
|
playbackSuppressionChangeToNoneAfterDefaultTimeout_shouldNotChangePlaybackStateToPlaying()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
||||||
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||||
testPlayer.addListener(
|
testPlayer.addListener(
|
||||||
@ -667,10 +682,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test to ensure player is playing when the playback suppression due to unsuitable output is
|
* Test to ensure player is playing when the playback suppression due to unsuitable output is
|
||||||
* removed within the set timeout.
|
* removed within the set timeout.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void playbackSuppressionChangeToNoneWithinSetTimeout_shouldChangePlaybackStateToPlaying()
|
public void playbackSuppressionChangeToNoneWithinSetTimeout_shouldChangePlaybackStateToPlaying()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
||||||
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||||
testPlayer.addListener(
|
testPlayer.addListener(
|
||||||
@ -694,11 +710,12 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test to ensure player is not playing when the playback suppression due to unsuitable output is
|
* Test to ensure player is not playing when the playback suppression due to unsuitable output is
|
||||||
* removed after the set timeout.
|
* removed after the set timeout.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void
|
public void
|
||||||
playbackSuppressionChangeToNoneAfterSetTimeout_shouldNotChangeFinalPlaybackStateToPlaying()
|
playbackSuppressionChangeToNoneAfterSetTimeout_shouldNotChangeFinalPlaybackStateToPlaying()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
||||||
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||||
testPlayer.addListener(
|
testPlayer.addListener(
|
||||||
@ -728,10 +745,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Test to ensure wake lock is acquired when playback is suppressed due to unsuitable output. */
|
/** Test to ensure wake lock is acquired when playback is suppressed due to unsuitable output. */
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void playEventWithSuppressedPlaybackCondition_shouldAcquireWakeLock()
|
public void playEventWithSuppressedPlaybackCondition_shouldAcquireWakeLock()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
||||||
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||||
testPlayer.addListener(
|
testPlayer.addListener(
|
||||||
@ -752,10 +770,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test to ensure that the wake lock acquired with playback suppression due to unsuitable output
|
* Test to ensure that the wake lock acquired with playback suppression due to unsuitable output
|
||||||
* is released after the set timeout.
|
* is released after the set timeout.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void playEventWithSuppressedPlaybackCondition_shouldReleaseAcquiredWakeLockAfterTimeout()
|
public void playEventWithSuppressedPlaybackCondition_shouldReleaseAcquiredWakeLockAfterTimeout()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
||||||
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||||
testPlayer.addListener(
|
testPlayer.addListener(
|
||||||
@ -778,10 +797,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* Test to ensure that the wake lock acquired with playback suppression due to unsuitable output
|
* Test to ensure that the wake lock acquired with playback suppression due to unsuitable output
|
||||||
* is released after suitable output gets added.
|
* is released after suitable output gets added.
|
||||||
*/
|
*/
|
||||||
|
// TODO: remove maxSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
|
@Config(minSdk = 23, maxSdk = 34)
|
||||||
@Test
|
@Test
|
||||||
public void playEventWithSuppressedPlaybackConditionRemoved_shouldReleaseAcquiredWakeLock()
|
public void playEventWithSuppressedPlaybackConditionRemoved_shouldReleaseAcquiredWakeLock()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
||||||
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||||
testPlayer.addListener(
|
testPlayer.addListener(
|
||||||
@ -805,10 +825,9 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
|
|
||||||
/** Test to verify that attempted playback is paused when the suitable output is not present. */
|
/** Test to verify that attempted playback is paused when the suitable output is not present. */
|
||||||
@Test
|
@Test
|
||||||
@Config(minSdk = 35)
|
@Config(minSdk = 35) // Remove minSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
public void playEvent_withSuitableOutputNotPresent_shouldPausePlaybackAndLaunchOutputSwitcher()
|
public void playEvent_withSuitableOutputNotPresent_shouldPausePlaybackAndLaunchOutputSwitcher()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
suitableMediaOutputChecker.updateIsSelectedSuitableOutputAvailableAndNotify(
|
suitableMediaOutputChecker.updateIsSelectedSuitableOutputAvailableAndNotify(
|
||||||
/* isSelectedOutputSuitableForPlayback= */ false);
|
/* isSelectedOutputSuitableForPlayback= */ false);
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
@ -850,10 +869,9 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* present.
|
* present.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Config(minSdk = 35)
|
@Config(minSdk = 35) // Remove minSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
public void playEvent_withSuitableOutputPresent_shouldNotPausePlaybackOrLaunchOutputSwitcher()
|
public void playEvent_withSuitableOutputPresent_shouldNotPausePlaybackOrLaunchOutputSwitcher()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
suitableMediaOutputChecker.updateIsSelectedSuitableOutputAvailableAndNotify(
|
suitableMediaOutputChecker.updateIsSelectedSuitableOutputAvailableAndNotify(
|
||||||
/* isSelectedOutputSuitableForPlayback= */ false);
|
/* isSelectedOutputSuitableForPlayback= */ false);
|
||||||
registerFakeActivity(
|
registerFakeActivity(
|
||||||
@ -892,10 +910,9 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
* time out.
|
* time out.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Config(minSdk = 35)
|
@Config(minSdk = 35) // Remove minSdk once Robolectric supports MediaRouter2 (b/382017156)
|
||||||
public void playEvent_suitableOutputAddedAfterTimeOut_shouldNotResumePlayback()
|
public void playEvent_suitableOutputAddedAfterTimeOut_shouldNotResumePlayback()
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
|
||||||
suitableMediaOutputChecker.updateIsSelectedSuitableOutputAvailableAndNotify(
|
suitableMediaOutputChecker.updateIsSelectedSuitableOutputAvailableAndNotify(
|
||||||
/* isSelectedOutputSuitableForPlayback= */ false);
|
/* isSelectedOutputSuitableForPlayback= */ false);
|
||||||
testPlayer.setMediaItem(
|
testPlayer.setMediaItem(
|
||||||
@ -945,11 +962,11 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
|||||||
private void setupConnectedAudioOutput(int... deviceTypes) {
|
private void setupConnectedAudioOutput(int... deviceTypes) {
|
||||||
ShadowAudioManager shadowAudioManager =
|
ShadowAudioManager shadowAudioManager =
|
||||||
shadowOf(ApplicationProvider.getApplicationContext().getSystemService(AudioManager.class));
|
shadowOf(ApplicationProvider.getApplicationContext().getSystemService(AudioManager.class));
|
||||||
ImmutableList.Builder<AudioDeviceInfo> deviceListBuilder = ImmutableList.builder();
|
|
||||||
for (int deviceType : deviceTypes) {
|
for (int deviceType : deviceTypes) {
|
||||||
deviceListBuilder.add(AudioDeviceInfoBuilder.newBuilder().setType(deviceType).build());
|
shadowAudioManager.addOutputDevice(
|
||||||
|
AudioDeviceInfoBuilder.newBuilder().setType(deviceType).build(),
|
||||||
|
/* notifyAudioDeviceCallbacks= */ true);
|
||||||
}
|
}
|
||||||
shadowAudioManager.setOutputDevices(deviceListBuilder.build());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addConnectedAudioOutput(int deviceTypes, boolean notifyAudioDeviceCallbacks) {
|
private void addConnectedAudioOutput(int deviceTypes, boolean notifyAudioDeviceCallbacks) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user