From 4dc3db4da3da486b9c9ec1780aa595da8de5330c Mon Sep 17 00:00:00 2001 From: bachinger Date: Wed, 11 Oct 2023 04:07:05 -0700 Subject: [PATCH] Add MediaSession.Builder().setPeriodicPositionUpdateEnabled() This allows to disable periodic position updates when building the session. #minor-release PiperOrigin-RevId: 572531837 --- .../media3/session/MediaLibraryService.java | 29 +++++++-- .../session/MediaLibrarySessionImpl.java | 6 +- .../androidx/media3/session/MediaSession.java | 38 ++++++++++-- .../media3/session/MediaSessionImpl.java | 8 ++- .../media3/session/MediaSessionTest.java | 61 +++++++++++++++++++ 5 files changed, 127 insertions(+), 15 deletions(-) diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java index 7be92db023..d2d50ccf67 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java @@ -518,6 +518,20 @@ public abstract class MediaLibraryService extends MediaSessionService { 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. + * + *

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}. * @@ -539,7 +553,8 @@ public abstract class MediaLibraryService extends MediaSessionService { callback, extras, checkNotNull(bitmapLoader), - playIfSuppressed); + playIfSuppressed, + isPeriodicPositionUpdateEnabled); } } @@ -552,7 +567,8 @@ public abstract class MediaLibraryService extends MediaSessionService { MediaSession.Callback callback, Bundle tokenExtras, BitmapLoader bitmapLoader, - boolean playIfSuppressed) { + boolean playIfSuppressed, + boolean isPeriodicPositionUpdateEnabled) { super( context, id, @@ -562,7 +578,8 @@ public abstract class MediaLibraryService extends MediaSessionService { callback, tokenExtras, bitmapLoader, - playIfSuppressed); + playIfSuppressed, + isPeriodicPositionUpdateEnabled); } @Override @@ -575,7 +592,8 @@ public abstract class MediaLibraryService extends MediaSessionService { MediaSession.Callback callback, Bundle tokenExtras, BitmapLoader bitmapLoader, - boolean playIfSuppressed) { + boolean playIfSuppressed, + boolean isPeriodicPositionUpdateEnabled) { return new MediaLibrarySessionImpl( this, context, @@ -586,7 +604,8 @@ public abstract class MediaLibraryService extends MediaSessionService { (Callback) callback, tokenExtras, bitmapLoader, - playIfSuppressed); + playIfSuppressed, + isPeriodicPositionUpdateEnabled); } @Override diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java index b25fda59be..53f2098091 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java @@ -75,7 +75,8 @@ import java.util.concurrent.Future; MediaLibrarySession.Callback callback, Bundle tokenExtras, BitmapLoader bitmapLoader, - boolean playIfSuppressed) { + boolean playIfSuppressed, + boolean isPeriodicPositionUpdateEnabled) { super( instance, context, @@ -86,7 +87,8 @@ import java.util.concurrent.Future; callback, tokenExtras, bitmapLoader, - playIfSuppressed); + playIfSuppressed, + isPeriodicPositionUpdateEnabled); this.instance = instance; this.callback = callback; parentIdToSubscribedControllers = HashMultimap.create(); diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java index 995e6543cc..42aa9b2b3d 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java @@ -376,6 +376,20 @@ public class MediaSession { 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. + * + *

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 * Player#getPlaybackSuppressionReason() suppressed}. @@ -413,7 +427,8 @@ public class MediaSession { callback, extras, checkNotNull(bitmapLoader), - playIfSuppressed); + playIfSuppressed, + isPeriodicPositionUpdateEnabled); } } @@ -606,7 +621,8 @@ public class MediaSession { Callback callback, Bundle tokenExtras, BitmapLoader bitmapLoader, - boolean playIfSuppressed) { + boolean playIfSuppressed, + boolean isPeriodicPositionUpdateEnabled) { synchronized (STATIC_LOCK) { if (SESSION_ID_TO_SESSION_MAP.containsKey(id)) { throw new IllegalStateException("Session ID must be unique. ID=" + id); @@ -623,7 +639,8 @@ public class MediaSession { callback, tokenExtras, bitmapLoader, - playIfSuppressed); + playIfSuppressed, + isPeriodicPositionUpdateEnabled); } /* package */ MediaSessionImpl createImpl( @@ -635,7 +652,8 @@ public class MediaSession { Callback callback, Bundle tokenExtras, BitmapLoader bitmapLoader, - boolean playIfSuppressed) { + boolean playIfSuppressed, + boolean isPeriodicPositionUpdateEnabled) { return new MediaSessionImpl( this, context, @@ -646,7 +664,8 @@ public class MediaSession { callback, tokenExtras, bitmapLoader, - playIfSuppressed); + playIfSuppressed, + isPeriodicPositionUpdateEnabled); } /* package */ MediaSessionImpl getImpl() { @@ -1798,8 +1817,8 @@ public class MediaSession { /* package */ Bundle extras; /* package */ @MonotonicNonNull BitmapLoader bitmapLoader; /* package */ boolean playIfSuppressed; - /* package */ ImmutableList customLayout; + /* package */ boolean isPeriodicPositionUpdateEnabled; public BuilderBase(Context context, Player player, CallbackT callback) { this.context = checkNotNull(context); @@ -1810,6 +1829,7 @@ public class MediaSession { extras = Bundle.EMPTY; customLayout = ImmutableList.of(); playIfSuppressed = true; + isPeriodicPositionUpdateEnabled = true; } @SuppressWarnings("unchecked") @@ -1855,6 +1875,12 @@ public class MediaSession { return (BuilderT) this; } + @SuppressWarnings("unchecked") + public BuilderT setPeriodicPositionUpdateEnabled(boolean isPeriodicPositionUpdateEnabled) { + this.isPeriodicPositionUpdateEnabled = isPeriodicPositionUpdateEnabled; + return (BuilderT) this; + } + public abstract SessionT build(); } } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java index aae28c7d11..4b43243596 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -117,6 +117,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private final Runnable periodicSessionPositionInfoUpdateRunnable; private final Handler mainHandler; private final boolean playIfSuppressed; + private final boolean isPeriodicPositionUpdateEnabled; private PlayerInfo playerInfo; private PlayerWrapper playerWrapper; @@ -148,7 +149,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; MediaSession.Callback callback, Bundle tokenExtras, BitmapLoader bitmapLoader, - boolean playIfSuppressed) { + boolean playIfSuppressed, + boolean isPeriodicPositionUpdateEnabled) { this.context = context; this.instance = instance; @@ -166,6 +168,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; this.callback = callback; this.bitmapLoader = bitmapLoader; this.playIfSuppressed = playIfSuppressed; + this.isPeriodicPositionUpdateEnabled = isPeriodicPositionUpdateEnabled; playerInfo = PlayerInfo.DEFAULT; onPlayerInfoChangedHandler = new PlayerInfoChangedHandler(player.getApplicationLooper()); @@ -1053,7 +1056,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private void schedulePeriodicSessionPositionInfoChanges() { applicationHandler.removeCallbacks(periodicSessionPositionInfoUpdateRunnable); - if (sessionPositionUpdateDelayMs > 0 + if (isPeriodicPositionUpdateEnabled + && sessionPositionUpdateDelayMs > 0 && (playerWrapper.isPlaying() || playerWrapper.isLoading())) { applicationHandler.postDelayed( periodicSessionPositionInfoUpdateRunnable, sessionPositionUpdateDelayMs); diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionTest.java index 6dda5e9bab..b8f4ff04c4 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionTest.java @@ -33,6 +33,7 @@ import android.support.v4.media.session.MediaSessionCompat; import android.text.TextUtils; import androidx.media.MediaSessionManager; import androidx.media3.common.MediaLibraryInfo; +import androidx.media3.common.Player; import androidx.media3.common.util.Log; import androidx.media3.common.util.Util; 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.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -433,4 +436,62 @@ public class MediaSessionTest { // version of remote controller. 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 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 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(); + } }