134134 private final MediaSessionManager sessionManager ;
135135 private final ControllerLegacyCbForBroadcast controllerLegacyCbForBroadcast ;
136136 private final ConnectionTimeoutHandler connectionTimeoutHandler ;
137+ private final boolean mayNeedButtonReservationWorkaroundForSeekbar ;
138+ @ Nullable private final AndroidAutoConnectionStateObserver androidAutoObserver ;
137139 private final MediaSessionCompat sessionCompat ;
138140 @ Nullable private final MediaButtonReceiver runtimeBroadcastReceiver ;
139141 @ Nullable private final ComponentName broadcastReceiverComponentName ;
@@ -182,6 +184,8 @@ public MediaSessionLegacyStub(
182184 connectionTimeoutHandler =
183185 new ConnectionTimeoutHandler (
184186 session .getApplicationHandler ().getLooper (), connectedControllersManager );
187+ mayNeedButtonReservationWorkaroundForSeekbar =
188+ mayNeedButtonReservationWorkaroundForSeekbar (context );
185189
186190 if (!mediaButtonPreferences .isEmpty ()) {
187191 updateCustomLayoutAndLegacyExtrasForMediaButtonPreferences ();
@@ -260,6 +264,12 @@ public MediaSessionLegacyStub(
260264 @ Initialized
261265 MediaSessionLegacyStub thisRef = this ;
262266 sessionCompat .setCallback (thisRef , handler );
267+
268+ androidAutoObserver =
269+ mayNeedButtonReservationWorkaroundForSeekbar
270+ ? new AndroidAutoConnectionStateObserver (
271+ context , thisRef ::onAndroidAutoConnectionStateChanged )
272+ : null ;
263273 }
264274
265275 /**
@@ -281,25 +291,7 @@ public void setAvailableCommands(
281291 this .availablePlayerCommands = playerCommands ;
282292
283293 if (!mediaButtonPreferences .isEmpty ()) {
284- boolean hadPrevReservation =
285- legacyExtras .getBoolean (
286- MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV , /* defaultValue= */ false );
287- boolean hadNextReservation =
288- legacyExtras .getBoolean (
289- MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT , /* defaultValue= */ false );
290- updateCustomLayoutAndLegacyExtrasForMediaButtonPreferences ();
291- boolean extrasChanged =
292- (legacyExtras .getBoolean (
293- MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV ,
294- /* defaultValue= */ false )
295- != hadPrevReservation )
296- || (legacyExtras .getBoolean (
297- MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT ,
298- /* defaultValue= */ false )
299- != hadNextReservation );
300- if (extrasChanged ) {
301- getSessionCompat ().setExtras (legacyExtras );
302- }
294+ updateCustomLayoutAndLegacyExtrasForMediaButtonPreferencesAndInformExtrasChanged ();
303295 }
304296
305297 if (commandGetTimelineChanged ) {
@@ -344,25 +336,7 @@ public void setPlatformCustomLayout(ImmutableList<CommandButton> customLayout) {
344336 public void setPlatformMediaButtonPreferences (
345337 ImmutableList <CommandButton > mediaButtonPreferences ) {
346338 this .mediaButtonPreferences = mediaButtonPreferences ;
347- boolean hadPrevReservation =
348- legacyExtras .getBoolean (
349- MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV , /* defaultValue= */ false );
350- boolean hadNextReservation =
351- legacyExtras .getBoolean (
352- MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT , /* defaultValue= */ false );
353- updateCustomLayoutAndLegacyExtrasForMediaButtonPreferences ();
354- boolean extrasChanged =
355- (legacyExtras .getBoolean (
356- MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV ,
357- /* defaultValue= */ false )
358- != hadPrevReservation )
359- || (legacyExtras .getBoolean (
360- MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT ,
361- /* defaultValue= */ false )
362- != hadNextReservation );
363- if (extrasChanged ) {
364- getSessionCompat ().setExtras (legacyExtras );
365- }
339+ updateCustomLayoutAndLegacyExtrasForMediaButtonPreferencesAndInformExtrasChanged ();
366340 }
367341
368342 /**
@@ -485,6 +459,9 @@ public void release() {
485459 if (runtimeBroadcastReceiver != null ) {
486460 sessionImpl .getContext ().unregisterReceiver (runtimeBroadcastReceiver );
487461 }
462+ if (androidAutoObserver != null ) {
463+ androidAutoObserver .release ();
464+ }
488465 // No check for COMMAND_RELEASE needed as MediaControllers can always be released.
489466 sessionCompat .release ();
490467 }
@@ -1253,6 +1230,34 @@ private boolean isQueueEnabled() {
12531230 && playerWrapper .getAvailableCommands ().contains (Player .COMMAND_GET_TIMELINE );
12541231 }
12551232
1233+ private void onAndroidAutoConnectionStateChanged () {
1234+ postOrRun (
1235+ sessionImpl .getApplicationHandler (),
1236+ this ::updateCustomLayoutAndLegacyExtrasForMediaButtonPreferencesAndInformExtrasChanged );
1237+ }
1238+
1239+ private void updateCustomLayoutAndLegacyExtrasForMediaButtonPreferencesAndInformExtrasChanged () {
1240+ boolean hadPrevReservation =
1241+ legacyExtras .getBoolean (
1242+ MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV , /* defaultValue= */ false );
1243+ boolean hadNextReservation =
1244+ legacyExtras .getBoolean (
1245+ MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT , /* defaultValue= */ false );
1246+ updateCustomLayoutAndLegacyExtrasForMediaButtonPreferences ();
1247+ boolean extrasChanged =
1248+ (legacyExtras .getBoolean (
1249+ MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV ,
1250+ /* defaultValue= */ false )
1251+ != hadPrevReservation )
1252+ || (legacyExtras .getBoolean (
1253+ MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT ,
1254+ /* defaultValue= */ false )
1255+ != hadNextReservation );
1256+ if (extrasChanged ) {
1257+ getSessionCompat ().setExtras (legacyExtras );
1258+ }
1259+ }
1260+
12561261 private void updateCustomLayoutAndLegacyExtrasForMediaButtonPreferences () {
12571262 ImmutableList <CommandButton > mediaButtonPreferencesWithUnavailableButtonsDisabled =
12581263 CommandButton .copyWithUnavailableButtonsDisabled (
@@ -1266,16 +1271,27 @@ private void updateCustomLayoutAndLegacyExtrasForMediaButtonPreferences() {
12661271 mediaButtonPreferencesWithUnavailableButtonsDisabled ,
12671272 /* backSlotAllowed= */ true ,
12681273 /* forwardSlotAllowed= */ true );
1269- // If no custom back slot button is defined and other custom forward or overflow buttons exist,
1270- // we need to reserve the back slot to prevent the other buttons from moving into this slot. The
1271- // forward slot should never be reserved to avoid gaps in the output. We explicitly clear the
1272- // value to avoid any manually defined extras to interfere with our logic.
1273- boolean reserveBackSpaceSlot =
1274- !customLayout .isEmpty ()
1275- && !CommandButton .containsButtonForSlot (customLayout , CommandButton .SLOT_BACK );
1276- legacyExtras .putBoolean (
1277- MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV , reserveBackSpaceSlot );
1278- legacyExtras .putBoolean (MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT , false );
1274+ if (needsButtonReservationWorkaroundForSeekbar (androidAutoObserver )) {
1275+ // When applying the workaround, if no custom back slot button is defined and other custom
1276+ // forward or overflow buttons exist, we need to reserve the back slot to prevent the other
1277+ // buttons from moving into this slot. The forward slot should never be reserved to avoid gaps
1278+ // in the output. We explicitly clear the value to avoid any manually defined extras to
1279+ // interfere with our logic.
1280+ boolean reserveBackSpaceSlot =
1281+ !customLayout .isEmpty ()
1282+ && !CommandButton .containsButtonForSlot (customLayout , CommandButton .SLOT_BACK );
1283+ legacyExtras .putBoolean (
1284+ MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV , reserveBackSpaceSlot );
1285+ legacyExtras .putBoolean (MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT , false );
1286+ } else {
1287+ // Without the workaround, set the reservations to match our actual slot definition.
1288+ legacyExtras .putBoolean (
1289+ MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV ,
1290+ !CommandButton .containsButtonForSlot (customLayout , CommandButton .SLOT_BACK ));
1291+ legacyExtras .putBoolean (
1292+ MediaConstants .EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT ,
1293+ !CommandButton .containsButtonForSlot (customLayout , CommandButton .SLOT_FORWARD ));
1294+ }
12791295 }
12801296
12811297 private static MediaItem createMediaItemForMediaRequest (
@@ -2112,6 +2128,35 @@ public void onAdjustVolume(int direction) {
21122128 };
21132129 }
21142130
2131+ private boolean needsButtonReservationWorkaroundForSeekbar (
2132+ @ Nullable AndroidAutoConnectionStateObserver androidAutoObserver ) {
2133+ // Check if the device is generally known to require the workaround. Also disable the workaround
2134+ // when connected to Android Auto under the assumption that it is the main user interface while
2135+ // connected. See https://github.com/androidx/media/issues/3041.
2136+ if (!mayNeedButtonReservationWorkaroundForSeekbar ) {
2137+ return false ;
2138+ }
2139+ return androidAutoObserver == null || !androidAutoObserver .isConnected ();
2140+ }
2141+
2142+ private static boolean mayNeedButtonReservationWorkaroundForSeekbar (Context context ) {
2143+ // The stock system UMO has an issue that when a navigation button is reserved, it doesn't
2144+ // automatically fill its empty space with an extended seek bar, leaving an unexpected gap.
2145+ // This affects all manufacturers known to rely on the stock UMO from API 33. See
2146+ // https://github.com/androidx/media/issues/2976.
2147+ if (SDK_INT < 33 ) {
2148+ return false ;
2149+ }
2150+ if (Util .isAutomotive (context )) {
2151+ return false ;
2152+ }
2153+ return Build .MANUFACTURER .equals ("Google" )
2154+ || Build .MANUFACTURER .equals ("motorola" )
2155+ || Build .MANUFACTURER .equals ("vivo" )
2156+ || Build .MANUFACTURER .equals ("Sony" )
2157+ || Build .MANUFACTURER .equals ("Nothing" );
2158+ }
2159+
21152160 /** Describes a legacy error. */
21162161 private static final class LegacyError {
21172162 public final boolean isFatal ;
0 commit comments