Add MediaSession.Builder().setPeriodicPositionUpdateEnabled()

This allows to disable periodic position updates when building
the session.

#minor-release

PiperOrigin-RevId: 572531837
This commit is contained in:
bachinger 2023-10-11 04:07:05 -07:00 committed by Copybara-Service
parent 164e658839
commit 4dc3db4da3
5 changed files with 127 additions and 15 deletions

View File

@ -518,6 +518,20 @@ public abstract class MediaLibraryService extends MediaSessionService {
return super.setShowPlayButtonIfPlaybackIsSuppressed(showPlayButtonIfPlaybackIsSuppressed); return super.setShowPlayButtonIfPlaybackIsSuppressed(showPlayButtonIfPlaybackIsSuppressed);
} }
/**
* Sets whether periodic position updates should be sent to controllers while playing. If
* false, no periodic position updates are sent to controllers.
*
* <p>The default is {@code true}.
*
* @param isEnabled Whether periodic position update is enabled.
*/
@UnstableApi
@Override
public Builder setPeriodicPositionUpdateEnabled(boolean isEnabled) {
return super.setPeriodicPositionUpdateEnabled(isEnabled);
}
/** /**
* Builds a {@link MediaLibrarySession}. * Builds a {@link MediaLibrarySession}.
* *
@ -539,7 +553,8 @@ public abstract class MediaLibraryService extends MediaSessionService {
callback, callback,
extras, extras,
checkNotNull(bitmapLoader), checkNotNull(bitmapLoader),
playIfSuppressed); playIfSuppressed,
isPeriodicPositionUpdateEnabled);
} }
} }
@ -552,7 +567,8 @@ public abstract class MediaLibraryService extends MediaSessionService {
MediaSession.Callback callback, MediaSession.Callback callback,
Bundle tokenExtras, Bundle tokenExtras,
BitmapLoader bitmapLoader, BitmapLoader bitmapLoader,
boolean playIfSuppressed) { boolean playIfSuppressed,
boolean isPeriodicPositionUpdateEnabled) {
super( super(
context, context,
id, id,
@ -562,7 +578,8 @@ public abstract class MediaLibraryService extends MediaSessionService {
callback, callback,
tokenExtras, tokenExtras,
bitmapLoader, bitmapLoader,
playIfSuppressed); playIfSuppressed,
isPeriodicPositionUpdateEnabled);
} }
@Override @Override
@ -575,7 +592,8 @@ public abstract class MediaLibraryService extends MediaSessionService {
MediaSession.Callback callback, MediaSession.Callback callback,
Bundle tokenExtras, Bundle tokenExtras,
BitmapLoader bitmapLoader, BitmapLoader bitmapLoader,
boolean playIfSuppressed) { boolean playIfSuppressed,
boolean isPeriodicPositionUpdateEnabled) {
return new MediaLibrarySessionImpl( return new MediaLibrarySessionImpl(
this, this,
context, context,
@ -586,7 +604,8 @@ public abstract class MediaLibraryService extends MediaSessionService {
(Callback) callback, (Callback) callback,
tokenExtras, tokenExtras,
bitmapLoader, bitmapLoader,
playIfSuppressed); playIfSuppressed,
isPeriodicPositionUpdateEnabled);
} }
@Override @Override

View File

@ -75,7 +75,8 @@ import java.util.concurrent.Future;
MediaLibrarySession.Callback callback, MediaLibrarySession.Callback callback,
Bundle tokenExtras, Bundle tokenExtras,
BitmapLoader bitmapLoader, BitmapLoader bitmapLoader,
boolean playIfSuppressed) { boolean playIfSuppressed,
boolean isPeriodicPositionUpdateEnabled) {
super( super(
instance, instance,
context, context,
@ -86,7 +87,8 @@ import java.util.concurrent.Future;
callback, callback,
tokenExtras, tokenExtras,
bitmapLoader, bitmapLoader,
playIfSuppressed); playIfSuppressed,
isPeriodicPositionUpdateEnabled);
this.instance = instance; this.instance = instance;
this.callback = callback; this.callback = callback;
parentIdToSubscribedControllers = HashMultimap.create(); parentIdToSubscribedControllers = HashMultimap.create();

View File

@ -376,6 +376,20 @@ public class MediaSession {
return super.setCustomLayout(customLayout); return super.setCustomLayout(customLayout);
} }
/**
* Sets whether periodic position updates should be sent to controllers while playing. If false,
* no periodic position updates are sent to controllers.
*
* <p>The default is {@code true}.
*
* @param isEnabled Whether periodic position update is enabled.
*/
@UnstableApi
@Override
public Builder setPeriodicPositionUpdateEnabled(boolean isEnabled) {
return super.setPeriodicPositionUpdateEnabled(isEnabled);
}
/** /**
* Sets whether a play button is shown if playback is {@linkplain * Sets whether a play button is shown if playback is {@linkplain
* Player#getPlaybackSuppressionReason() suppressed}. * Player#getPlaybackSuppressionReason() suppressed}.
@ -413,7 +427,8 @@ public class MediaSession {
callback, callback,
extras, extras,
checkNotNull(bitmapLoader), checkNotNull(bitmapLoader),
playIfSuppressed); playIfSuppressed,
isPeriodicPositionUpdateEnabled);
} }
} }
@ -606,7 +621,8 @@ public class MediaSession {
Callback callback, Callback callback,
Bundle tokenExtras, Bundle tokenExtras,
BitmapLoader bitmapLoader, BitmapLoader bitmapLoader,
boolean playIfSuppressed) { boolean playIfSuppressed,
boolean isPeriodicPositionUpdateEnabled) {
synchronized (STATIC_LOCK) { synchronized (STATIC_LOCK) {
if (SESSION_ID_TO_SESSION_MAP.containsKey(id)) { if (SESSION_ID_TO_SESSION_MAP.containsKey(id)) {
throw new IllegalStateException("Session ID must be unique. ID=" + id); throw new IllegalStateException("Session ID must be unique. ID=" + id);
@ -623,7 +639,8 @@ public class MediaSession {
callback, callback,
tokenExtras, tokenExtras,
bitmapLoader, bitmapLoader,
playIfSuppressed); playIfSuppressed,
isPeriodicPositionUpdateEnabled);
} }
/* package */ MediaSessionImpl createImpl( /* package */ MediaSessionImpl createImpl(
@ -635,7 +652,8 @@ public class MediaSession {
Callback callback, Callback callback,
Bundle tokenExtras, Bundle tokenExtras,
BitmapLoader bitmapLoader, BitmapLoader bitmapLoader,
boolean playIfSuppressed) { boolean playIfSuppressed,
boolean isPeriodicPositionUpdateEnabled) {
return new MediaSessionImpl( return new MediaSessionImpl(
this, this,
context, context,
@ -646,7 +664,8 @@ public class MediaSession {
callback, callback,
tokenExtras, tokenExtras,
bitmapLoader, bitmapLoader,
playIfSuppressed); playIfSuppressed,
isPeriodicPositionUpdateEnabled);
} }
/* package */ MediaSessionImpl getImpl() { /* package */ MediaSessionImpl getImpl() {
@ -1798,8 +1817,8 @@ public class MediaSession {
/* package */ Bundle extras; /* package */ Bundle extras;
/* package */ @MonotonicNonNull BitmapLoader bitmapLoader; /* package */ @MonotonicNonNull BitmapLoader bitmapLoader;
/* package */ boolean playIfSuppressed; /* package */ boolean playIfSuppressed;
/* package */ ImmutableList<CommandButton> customLayout; /* package */ ImmutableList<CommandButton> customLayout;
/* package */ boolean isPeriodicPositionUpdateEnabled;
public BuilderBase(Context context, Player player, CallbackT callback) { public BuilderBase(Context context, Player player, CallbackT callback) {
this.context = checkNotNull(context); this.context = checkNotNull(context);
@ -1810,6 +1829,7 @@ public class MediaSession {
extras = Bundle.EMPTY; extras = Bundle.EMPTY;
customLayout = ImmutableList.of(); customLayout = ImmutableList.of();
playIfSuppressed = true; playIfSuppressed = true;
isPeriodicPositionUpdateEnabled = true;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -1855,6 +1875,12 @@ public class MediaSession {
return (BuilderT) this; return (BuilderT) this;
} }
@SuppressWarnings("unchecked")
public BuilderT setPeriodicPositionUpdateEnabled(boolean isPeriodicPositionUpdateEnabled) {
this.isPeriodicPositionUpdateEnabled = isPeriodicPositionUpdateEnabled;
return (BuilderT) this;
}
public abstract SessionT build(); public abstract SessionT build();
} }
} }

View File

@ -117,6 +117,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final Runnable periodicSessionPositionInfoUpdateRunnable; private final Runnable periodicSessionPositionInfoUpdateRunnable;
private final Handler mainHandler; private final Handler mainHandler;
private final boolean playIfSuppressed; private final boolean playIfSuppressed;
private final boolean isPeriodicPositionUpdateEnabled;
private PlayerInfo playerInfo; private PlayerInfo playerInfo;
private PlayerWrapper playerWrapper; private PlayerWrapper playerWrapper;
@ -148,7 +149,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
MediaSession.Callback callback, MediaSession.Callback callback,
Bundle tokenExtras, Bundle tokenExtras,
BitmapLoader bitmapLoader, BitmapLoader bitmapLoader,
boolean playIfSuppressed) { boolean playIfSuppressed,
boolean isPeriodicPositionUpdateEnabled) {
this.context = context; this.context = context;
this.instance = instance; this.instance = instance;
@ -166,6 +168,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
this.callback = callback; this.callback = callback;
this.bitmapLoader = bitmapLoader; this.bitmapLoader = bitmapLoader;
this.playIfSuppressed = playIfSuppressed; this.playIfSuppressed = playIfSuppressed;
this.isPeriodicPositionUpdateEnabled = isPeriodicPositionUpdateEnabled;
playerInfo = PlayerInfo.DEFAULT; playerInfo = PlayerInfo.DEFAULT;
onPlayerInfoChangedHandler = new PlayerInfoChangedHandler(player.getApplicationLooper()); onPlayerInfoChangedHandler = new PlayerInfoChangedHandler(player.getApplicationLooper());
@ -1053,7 +1056,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private void schedulePeriodicSessionPositionInfoChanges() { private void schedulePeriodicSessionPositionInfoChanges() {
applicationHandler.removeCallbacks(periodicSessionPositionInfoUpdateRunnable); applicationHandler.removeCallbacks(periodicSessionPositionInfoUpdateRunnable);
if (sessionPositionUpdateDelayMs > 0 if (isPeriodicPositionUpdateEnabled
&& sessionPositionUpdateDelayMs > 0
&& (playerWrapper.isPlaying() || playerWrapper.isLoading())) { && (playerWrapper.isPlaying() || playerWrapper.isLoading())) {
applicationHandler.postDelayed( applicationHandler.postDelayed(
periodicSessionPositionInfoUpdateRunnable, sessionPositionUpdateDelayMs); periodicSessionPositionInfoUpdateRunnable, sessionPositionUpdateDelayMs);

View File

@ -33,6 +33,7 @@ import android.support.v4.media.session.MediaSessionCompat;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.media.MediaSessionManager; import androidx.media.MediaSessionManager;
import androidx.media3.common.MediaLibraryInfo; import androidx.media3.common.MediaLibraryInfo;
import androidx.media3.common.Player;
import androidx.media3.common.util.Log; import androidx.media3.common.util.Log;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.test.session.common.HandlerThreadTestRule; import androidx.media3.test.session.common.HandlerThreadTestRule;
@ -41,6 +42,8 @@ import androidx.media3.test.session.common.TestHandler;
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 androidx.test.filters.LargeTest; import androidx.test.filters.LargeTest;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
@ -433,4 +436,62 @@ public class MediaSessionTest {
// version of remote controller. // version of remote controller.
assertThat(controllerVersionRef.get()).isEqualTo(MediaLibraryInfo.VERSION_INT); assertThat(controllerVersionRef.get()).isEqualTo(MediaLibraryInfo.VERSION_INT);
} }
@Test
public void setPeriodicPositionUpdateEnabled_periodicUpdatesEnabled_bufferedPositionMsUpdated()
throws Exception {
player.playWhenReady = true;
player.playbackState = Player.STATE_READY;
MediaSession session =
sessionTestRule.ensureReleaseAfterTest(
new MediaSession.Builder(context, player)
.setPeriodicPositionUpdateEnabled(true)
.setId(
"setPeriodicPositionUpdateEnabled_periodicUpdatesEnabled_bufferedPositionMsUpdated")
.build());
threadTestRule.getHandler().postAndSync(() -> session.setSessionPositionUpdateDelayMs(10L));
MediaController controller =
new MediaController.Builder(ApplicationProvider.getApplicationContext(), session.getToken())
.buildAsync()
.get();
List<Long> bufferedPositionsMs = new ArrayList<>();
TestHandler testHandler = new TestHandler(controller.getApplicationLooper());
for (long bufferedPositionMs = 0; bufferedPositionMs < 5000; bufferedPositionMs += 1000) {
player.bufferedPosition = bufferedPositionMs;
Thread.sleep(50L);
bufferedPositionsMs.add(testHandler.postAndSync(controller::getBufferedPosition));
}
assertThat(bufferedPositionsMs).containsExactly(0L, 1000L, 2000L, 3000L, 4000L).inOrder();
}
@Test
public void setPeriodicPositionUpdateEnabled_periodicUpdatesDisabled_bufferedPositionMsUnchanged()
throws Exception {
player.playWhenReady = true;
player.playbackState = Player.STATE_READY;
MediaSession session =
sessionTestRule.ensureReleaseAfterTest(
new MediaSession.Builder(context, player)
.setPeriodicPositionUpdateEnabled(false)
.setId(
"setPeriodicPositionUpdateEnabled_periodicUpdatesDisabled_bufferedPositionMsUnchanged")
.build());
threadTestRule.getHandler().postAndSync(() -> session.setSessionPositionUpdateDelayMs(10L));
MediaController controller =
new MediaController.Builder(ApplicationProvider.getApplicationContext(), session.getToken())
.buildAsync()
.get();
List<Long> bufferedPositionsMs = new ArrayList<>();
TestHandler testHandler = new TestHandler(controller.getApplicationLooper());
for (long bufferedPositionMs = 0; bufferedPositionMs < 5000; bufferedPositionMs += 1000) {
player.bufferedPosition = bufferedPositionMs;
Thread.sleep(50L);
bufferedPositionsMs.add(testHandler.postAndSync(controller::getBufferedPosition));
}
assertThat(bufferedPositionsMs).containsExactly(0L, 0L, 0L, 0L, 0L).inOrder();
}
} }