Future.isDone and getDone doesn't imply the Future was successful
and it may have been cancelled or failed.
In case where we handle failure, we should also handle cancellation
to avoid CancellationException to bubble up unchecked.
In demo app code where we use isDone for field initialization, we
want to crash in the failure case (usually security exception where
the connection is disallowed), but we want to gracefully handle
cancellation. Cancellation of these variables usually happens in
Activity.onDestroy/onStop, but methods may be called after this point.
#minor-release
PiperOrigin-RevId: 572178018
The library already maintains the subscribed controllers internally. This
change adds `MediaLibrarySession.getSubscribedControllers(mediaId)` to
access subscribed controllers for a given media ID.
To accept a subscription, `MediaLibraryService.Callback.onSubscribe` is
required to return `RESULT_SUCCESS`. So far, this isn't the case for the
default implementation of the library.
This change implements `Callback.onSubscribe` to conditionally
provide `RESULT_SUCCESS`. The default calls `Callback.onGetItem(mediaId)` to
assess the availability of the media item. If the app retruns `RESULT_SUCCESS`
with a browsable item, the subscription is accepted. If receiving a valid item
fails, the subscription is rejected.
Issue: androidx/media#561
PiperOrigin-RevId: 568925079
Before this change, media button events are routed from `onStartCommand`
of the `MediaSessionService` to the `MediaSessionCompat`, resolved by
the legacy library to a session command called on
`MediaSessionCompat.Callback` from where the command is delegated back
to the Media3 session.
With this change the keycode is resolved directly to a Media3 command
that is sent to the session through the media notification controller
of the session.
After this change, a playback or custom command sent to the session
from a notification, either as a pending intent (before API 33) or as
a legacy session command, look the same and the caller is the
media notification controller on all API levels.
PiperOrigin-RevId: 568224123
With this change, the `ControllerInfo` passed to
`MediaSessionService.onGetSession(ControllerInfo)`
is the same instance that is passed later to all
callback methods of `MediaSession.Callback`.
PiperOrigin-RevId: 568216855
With this change, the notification controller that is connected by
`MediaNotificationManager`, is used as a proxy controller of the
System UI controller. An app can use the proxy at connection time
and during the lifetime of the session for configuration of the
platform session and the media notification on all API levels.
This includes using custom layout and available player and session
commands of the proxy to maintain the platform session (actions,
custom actions, session extras) and the `MediaNotification.Provider`.
The legacy System UI controller is hidden from the public API,
instead the app interacts with the Media3 proxy:
- System UI is hidden from `MediaSession.getConnectedControllers()`.
- Calls from System UI to methods of `MediaSession.Callback`/
`MediaLibrarySession.Callback` are mapped to the `ControllerInfo`
of the proxy controller.
- When `getControllerForCurrentRequest()` is called during an operation of
System UI the proxy `ControllerInfo` is returned.
PiperOrigin-RevId: 567606117
I spotted that this looked wrong while investigating test various
session test failures. However, changing this logic doesn't seem to
affect the tests so I don't know if the change is an improvement or not.
PiperOrigin-RevId: 566655318
These tests were under androidTest because we needed a functional
BitmapFactory. Robolectric now supports decoding bitmaps so moving them
under tests.
PiperOrigin-RevId: 565181239
This change avoids using a framework API that is deprecated since
API 31 and works around difficulties with this deprecated API on
devices from some manufacturers on API 33/34.
Manually tested with BT headphone connected to:
- Samsung SM-G920F (API 24)
- Pixel (API 28)
- Samsung SM-A515W (API 33)
- Pixel 6 Pro (API 33)
(verified manually with devices and adb output of dumpsys)
Issue: androidx/media#167
PiperOrigin-RevId: 562890206
By doing so, the media notification sent from DefaultMediaNotificationProvider won't be auto-grouped (https://developer.android.com/develop/ui/views/notifications/group#automatic-grouping) with the regular notifications sent from the other places in the app. As the result, when the customer attempts to dismiss the regular notifications (by swiping away), the media notification won't be dismissed as being in the same group.
Issue: androidx/media#549
PiperOrigin-RevId: 561047571
When the foreground service type is set to `mediaPlayiback` this shouldn't
be required. However, some reports from users say that setting the
foreground service behavor fixes some problems on some devices. Setting
it explicitely with this change makes sure users don't have to do that
workaround themselves.
Issue: androidx/media#167
PiperOrigin-RevId: 557608577
This changes the default logic of shouldShowPlayButton to show a play
button while the playback is temporarily suppressed. This helps to
provide better UI feedback to the fact that playback stopped and
provides a quick way for users to override the suppression and attempt
to restart playback.
Some apps may want to keep the legacy behavior depending on their app's
needs. Hence, we also add a config parameter to set this behavior both
in MediaSession and our default UI components.
Issue: google/ExoPlayer#11213
PiperOrigin-RevId: 557129171
When hardware buttons are used to control the volume of the remote device, the call propagates to `MediaSessionCompat.setPlaybackToRemote(volumeProviderCompat)`. However, `volumeProviderCompat` was created incorrectly when the new device volume commands were present (COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS and COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS), i.e. with volumeControlType = `VOLUME_CONTROL_FIXED`. This resulted in `VolumeProviderCompat` which doesn't call `onSetVolumeTo` or `onAdjustVolume` and hence doesn't propagate the calls to the `Player`. Instead, it only worked with the deprecated commands which ensured the volumeControlType was `VOLUME_CONTROL_ABSOLUTE`.
This bug was introduced in c71e4bf1ff (1.0 media 3 release) when `PlayerWrapper`'s call to `createVolumeProviderCompat` was mostly rewritten to handle the new commands, but the two if-statements were not amended. Note: this change fixes the bug only for Android 11 and below. For 12 and above, there is a tracking bug for the regression that was introduced: https://issuetracker.google.com/issues/201546605http://Issue: androidx/media#554
#minor-release
PiperOrigin-RevId: 554966361
The `MediaNotificationManager` registers an internal controller
to each session. This change marks this controller through its
connection hints and provides an API for apps to hide
implementation details of the marking.
Issue: androidx/media#389
PiperOrigin-RevId: 549712768
This ensures the DownloadService stays functional on Android 14
where defining this type is required. On Android 14 and above,
the app also needs to define the DATA_SYNC permission, which is
added to the demo app as well. In the future, this service type
will no longer be supported and DownloadService needs to be
rewritten with another background scheduling framework.
Issue: google/ExoPlayer#11239
PiperOrigin-RevId: 548994842
Unlike ExoPlayer, when play/pause is called on MediaController, the playback suppression reason is set to default causing a change in it and subsequent callbacks on the Player.Listener.
PiperOrigin-RevId: 548088931
The setPlayerCommand and setSessionCommand builder methods contain assertions to ensure that at most one of these fields is set, but this left it possible to create a command button with no command by calling build on an empty builder.
PiperOrigin-RevId: 547488248
This change also marks the buttons of the custom layout as
enabled/disabled according to available commands in the controller.
Accordingly, `CommandButton.Builder.setEnabled(boolean)` is deprecated
because the value is overridden by the library.
Issue: androidx/media#38
#minor-release
PiperOrigin-RevId: 547272588
We use `@Nullable` for IntDefs in other places and don't use this suffix
elsewhere, so I don't think we need it here either.
PiperOrigin-RevId: 543687285
MP4 edit lists sometimes ask to start playback between two samples.
If this happens, we currently change the timestamp of the first
sample to zero to trim it (e.g. to display the first frame for a
slightly shorter period of time). However, we can't do this to audio
samples are they have an inherent duration and trimming them this
way is not possible.
#minor-release
PiperOrigin-RevId: 543420218
`MediaControllerImplBase` has 2 methods for updating listeners about `PlayerInfo` changes - `updatePlayerInfo` (for masking the state) and `onPlayerInfoChanged` (when communicating with the session). There is a set number of listener callbacks related to `PlayerInfo` updates and both methods should go through the same control flow (whether we know that masking will ignore most of them or not).
A unified method `notifyPlayerInfoListenersWithReasons` encapsulates only the shared logic of 2 methods - listeners' callbacks. This ensures that both methods call them in the same order and none are missed out.
PiperOrigin-RevId: 542587879
The callbacks for `PlayerInfo` changes are currently in both `MediaControllerImplBase.updatePlayerInfo` (masking) and `MediaControllerImplBase.onPlayerInfoChanged`. But the order was different between them both and `ExoPlayerImpl.updatePlaybackInfo` which they are trying to mimic.
#minor-release
PiperOrigin-RevId: 542519070
The effects pipeline must receive images in the sRGB colorspace due to the color transfers applied in the shaders. Currently the burden to making sure images are in the right colorspaces falls onto apps. This CL ensures that this is not the case anymore.
PiperOrigin-RevId: 542323613
Current behaviour causes an app to crash if it receives an unrecognized repeat mode send over the wire. In order to avoid the crash, a sensible default had to be chosen.
For `Player.RepeatMode`, it is `Player.REPEAT_MODE_OFF`, which is the same value we use as default when unbundling `PlayerInfo`.
For `PlaybackStateCompat.RepeatMode`, it is `PlaybackStateCompat.REPEAT_MODE_NONE`, which is what we use in the no-arg `LegacyPlayerInfo` constructor.
Issue: androidx/media#448
#minor-release
PiperOrigin-RevId: 540563792
The existing NullableType has been deprecated 5 years ago and causes
crashes in Kotlin apps because Kotlin doesn't recognize this annotation
as a nullable type annotation.
While we can't align on a single @Nullable annotation yet, we can at
least replace this one by JSR305's @Nonnull(MAYBE) as it fulfils all
requirements, including full Kotlin compatiblity. To avoid the
cumbersome name, we can redefine it as our own @NullableType
annotation. (We can't use @Nullable to avoid name clashes with the main
@Nullable annotation from AndroidX)
Issue: google/ExoPlayer#6792
PiperOrigin-RevId: 540497469
Currently, the implementation of `MediaControllerImplBase` differs from `ExoPlayerImpl`. The listeners of the former are notified of player error changes only in `onPlayerInfoChanged` and not `updatePlayerInfo` (masking method). Whereas `ExoPlayerImpl` has one unified method - `updatePlaybackInfo` - which sends the events to all the available listeners.
This change fixes the lack of 2 particular callbacks - `onPlayerErrorChanged` and `onPlayerError`, however, there might be more differences. Ideally, there should be a unified method for oldPlayerInfo/newPlayerInfo comparison-update-notify-listeners flow.
Issue: androidx/media#449
#minor-release
PiperOrigin-RevId: 539961618
This change is for Android 12 and below, where the buttons are derived from the actions added with the notification. From Android 13 (https://developer.android.com/about/versions/13/behavior-changes-13#playback-controls), the system derives media controls from `PlaybackState` actions.
When adding the actions onto the notification, the logic will iterate all the command buttons. The `COMMAND_KEY_CONPACT_VIEW_INDEX` extra will be checked for each button. If that extra is set for the three buttons on the compact view, then the customized buttons and their order will be used. Otherwise, the compact view will be "seekPrev" (if any), "play/pause" (if any), "seekNext" (if any) buttons (in such order).
Issue: androidx/media#410
PiperOrigin-RevId: 538797874
When initiated by MediaController, it should be possible for `MediaSession` to pass `MediaItems` to the `Player` if they have `LocalConfiguration`. In such case, it is not required to override `MediaSession.Callback.onAddMediaItems`, because the new current default implementation will handle it.
However, in other cases, MediaItem.toBundle() will continue to strip the LocalConfiguration information.
Issue: androidx/media#282
#minor-release
PiperOrigin-RevId: 537993460
This change moves the default logic into the actual Player
implementations, but does not introduce any behavior changes compared
to addMediaItems+removeMediaItems except to make the updates "atomic"
in ExoPlayerImpl, SimpleBasePlayer and MediaController. It also
provides backwards compatbility for cases where Players don't support
the operation.
Issue: google/ExoPlayer#8046
#minor-release
PiperOrigin-RevId: 534945089
This is a follow-up to 99dac0be0f where we made the same change in
ExoPlayerImpl and SimpleBasePlayer, but for consistency it makes
sense to also update the masking code in MediaControllerImplBase to
assume the same logic.
Note: MediaControllerImplLegacy already handles this case via
setMediaItems and doesn't need to be updated further.
#minor-release
PiperOrigin-RevId: 534038759
The methods in ExoPlayerImpl and MediaControllerImplBase that determine
the new PlayerInfo/PlaybackInfo currently have a hard-to-reason-about
setup where the method generating the new info accesses other methods
that rely on the existing class field instead of working with the
passed in PlayerInfo/PlaybackInfo. This prevents reuse of the util
methods (e.g. for replaceMediaItems) because they access potentially
stale state.
This change untangles these methods a bit by making the util methods
either static or at least ensure that they don't rely on existing
class fields of PlayerInfo/PlaybackInfo. Overall, the change is a
complete no-op.
#minor-release
PiperOrigin-RevId: 534036633
MediaControllerImplBase currently drops the pending initial seek
position when a user sets an empty playlist.
When seeking in empty playlists and setting new empty playlists,
the class also drops the the period index (and wrongly assigns zero
instead of the windowIndex).
#minor-release
PiperOrigin-RevId: 534035046
Docs:
* https://developer.android.com/build/publish-library/configure-pub-variants#single-pub-var
* https://developer.android.com/reference/tools/gradle-api/8.0/com/android/build/api/dsl/PublishingOptions
This resolves the following warning from Android Gradle Plugin 7.2.2 and
helps unblock the upgrade to AGP 8.0.1:
> Software Components will not be created automatically for Maven
> publishing from Android Gradle Plugin 8.0. To opt-in to the future
> behavior, set the Gradle property
> `android.disableAutomaticComponentCreation=true` in the
> `gradle.properties` file or use the new publishing DSL.
>
> Affected Modules: `lib-cast`, `lib-common`, `lib-container`,
> `lib-database`, `lib-datasource`, `lib-datasource-cronet`,
> `lib-datasource-okhttp`, `lib-datasource-rtmp`, `lib-decoder`,
> `lib-effect`, `lib-exoplayer`, `lib-exoplayer-all (legacy)`,
> `lib-exoplayer-dash`, `lib-exoplayer-hls`, `lib-exoplayer-ima`,
> `lib-exoplayer-rtsp`, `lib-exoplayer-smoothstreaming`,
> `lib-exoplayer-workmanager`, `lib-extractor`, `lib-media2 (legacy)`,
> `lib-mediasession (legacy)`, `lib-muxer`, `lib-session`,
> `lib-transformer`, `lib-ui`, `lib-ui-leanback`, `test-utils`,
> `test-utils-robolectric`
Issue: androidx/media#409
PiperOrigin-RevId: 533464133
This is a pre-requisite for the Android Studio upgrade assistant to
upgrade from AGP 7.2.2 to 8.0.1, otherwise it fails and complains
this is missing.
Issue: androidx/media#409
PiperOrigin-RevId: 533463246
The periodic updates are only meant to happen while we are in the
same period or ad. This was already guaranteed except for two cases:
1. The Player in a session has updated its state without yet calling
its listeners
2. The session scheduled a PlayerInfo update that hasn't been sent yet
... and in both cases, the following happened:
- The change updated the mediaItemIndex to an index that didn't exist
in a previous Timeline known to the Controller
- One of the period position updates happened to be sent at exactly
this time
This problem can be avoided by only scheduling the update if we are
still in the same period/ad and haven't scheduled a normal PlayerInfo
update already.
Since new MediaControllers may still connect to old sessons with this
bug, we need an equivalent change on the controller side to ignore such
buggy updates.
PiperOrigin-RevId: 532089328
When a `MediaButtonReceiver` is found in the manifest, the library
can implement the contract of SystemUI to signal that the app wants
a playback resumption notification to be displayed.
And, vice versa, if no `MediaButtonReceiver` is in the manifest, the
library will signal to not show the notification after the app has been
terminated.
#minor-release
PiperOrigin-RevId: 531516023
An app must only have a single service in the manifest that is either
exposing `MediaLibraryService.SERVICE_INTERFACE` or
`MediaSessionService.SERVICE_INTERFACE`. Hence the component name found by
querying the package manager for a service never returns a different result
when queried again.
The static `MediaSessionImpl.serviceComponentName` can hence be removed
and the package manager queried again when a second session instance
is created.
#minor-release
PiperOrigin-RevId: 531210456
The session activity is already sent to the controller with the
`ConnectionState` when it connects. This change adds the ability to update the
activity.
This allows an app to change the intent that is used to open an activity
for the notification. An app is likely to want to change the session activity
just before the session is released. This allows to use a different activity or
more importantly the back stack of the activity for while the app is running
and when used for the playback resumption notification.
PiperOrigin-RevId: 530627102
Calling LibraryResult.toBundle() could have caused a CastClassException.
This was because when unbundled with UNKNOWN_TYPE_CREATOR.fromBundle(Bundle),
the valueType was set to VALUE_TYPE_ITEM_LIST for all types and the MediaItem
was attempted to be casted to a list.
PiperOrigin-RevId: 529717688
Also change some type parameter names in `MediaSession.BuilderBase`
because `C` now clashes with the import of `androidx.media3.common.C`.
#minor-release
PiperOrigin-RevId: 529665698
The media button has API support with
`Callback.getPlaybackResumption()` that apps need to override to provide
a playlist to resume playback with.
Issue: androidx/media#167
PiperOrigin-RevId: 529495845
References to the service are kept from MediaSessionStub
and from a long-delayed Handler messages in ConnectionTimeoutHandler.
Remove strong references from these places by making the timeout
handler static and ensuring ConnectedControllersManager only keeps
a weak reference to the service (as it's part of MediaSessionStub).
Issue: androidx/media#346
PiperOrigin-RevId: 527543396
This method doesn't really serve a purpose that isn't handled
elsewhere. The return value is also not forwarded to anyone.
PiperOrigin-RevId: 527283166
To reliably reject the System UI playback resumption notification on
all API levels (specifically API 30), the backward compatibility layer
needs to return `null` for the library root.
This is not possible in the Media3 implementation. This change allows
an app to return a `LibraryResult.ofError(RESULT_ERROR_NOT_SUPPORTED)`
that then is translated to return null by the backwards compatibility
layer.
Issue: androidx/media#355
Issue: androidx/media#167
Issue: androidx/media#27
See https://developer.android.com/guide/topics/media/media-controls#mediabrowserservice_implementation
PiperOrigin-RevId: 527276529
This is a helper method that can used to obtain information about
the controller that is currently calling a Player method.
PiperOrigin-RevId: 527268994
The only dependencies that are not updated are the ones that need
to be kept in sync with other system (like Android source tree) or
would require a Kotlin dependency in common or exoplayer modules.
As a side effect, some demo apps now need a Kotlin config and some
additional modules require desugaring/multidex logic. To simplify
the setup, the desugaring and multidex steps are added to the common
config.
PiperOrigin-RevId: 527243950
This simplifies the addition of new fields in the future.
Also do some misc clean up for the volume limit values:
- Add some documentation to mention assumed defaults
- Add the IntRange annotations to match the ones we have in Player
already
- Mention the limits in the relevant Player methods
- Avoid bundling default values
- Improve range checks for masking in MediaController
PiperOrigin-RevId: 526029619
This is a bug currently, where commands are created once but never
updated again if the actions in MediaSessionCompat are changed.
PiperOrigin-RevId: 525999084
The sessions may have different application threads for their players,
and the service with its notification provider runs on the main thread.
To ensure everything runs on the correct thread, this change labels
methods where needed and fixes thread access in some places.
Issue: androidx/media#318
PiperOrigin-RevId: 524849598
This check was a leftover from when the metadata was generated from
the MediaItem only. Since we moved to the actual MediaMetadata fields,
the check is completely unnecessary and prevents accessing metadata
when the GET_CURRENT_MEDIA_ITEM command is not available.
#minor-release
PiperOrigin-RevId: 524837587
The overrides specified by a MediaController may not use the exact
same TrackGroup instances as known to the Player because the groups
have been bundled to and from the controller. This bundling may
alter the instance slightly depending on the version used on each
side of the communication and the fields set (e.g. Format.metadata
is not supported for bundling).
This issue can be solved by creating unique track group ids for
each group on the session side before bundling. On the way back,
the groups in the track selection parameters can be mapped backed
to their original instances based on this id.
#minor-release
Issue: androidx/media#296
PiperOrigin-RevId: 523986626
Previously, ExoPlayerImpl had volume flags hardcoded to SHOW_UI, but now the developer can choose what happens on volume change. The old methods have been deprecated.
PiperOrigin-RevId: 523974358
The setter command is only used for setPlaylistMetadata and can
be named COMMAND_SET_PLAYLIST_METADATA. The getter commnad is
used to access getMediaMetadata and getPlaylistMetadata and can
be better named COMMAND_GET_METADATA to reflect this usage.
PiperOrigin-RevId: 523673286
It's currently not possible to even subclass MediaController because
the constructor is package-private. To avoid any accidental usage or
future indirect subclassing, all methods can be marked as final.
PiperOrigin-RevId: 523648114
This change selects the best suited media button receiver
component and pending intent when creating the legacy
session. This is important to ensure that a service can
be started with a media button event from BT headsets
after the app has been terminated.
The `MediaSessionLegacyStub` selects the best suited
receiver to be passed to the `MediaSessionCompat`
constructor.
1. When the app has declared a broadcast receiver for
`ACTION_MEDIA_BUTTON` in the manifest, this broadcast
receiver is used.
2. When the session is housed in a service, the service
component is used as a fallback.
3. As a last resort a receiver is created at runtime.
When the `MediaSessionLegacyStub` is released, the media
button receiver is removed unless the app has provided a
media button receiver in the manifest. In this case we
assume the app supports resuming when the BT play intent
arrives at `MediaSessionService.onStartCommand`.
#minor-release
Issue: androidx/media#167
Issue: androidx/media#27
Issue: androidx/media#314
PiperOrigin-RevId: 523638051
If the duration is reported in MediaMetadataCompat, it should
also be set in the QueueTimeline to match controller.getDuration()
PiperOrigin-RevId: 522022953
MediaItems are not meant to be unique in a playlist. If a legacy
session publishes multiple items that get converted to equal MediaItems,
the current code fails because we look up queue ids in a Map (that
doesn't allow duplicate entries).
Fix this by storing a simple list of items with additional data.
#minor-release
Issue: androidx/media#290
PiperOrigin-RevId: 521993802
It's currently not possible to even subclass MediaSession because
the constructor is package-private. To avoid any accidental usage or
future indirect subclassing, all methods can be marked as final.
PiperOrigin-RevId: 521775373
The check currently relies on the default value of 0 returned if the
Bundle doesn't define a pid. But in some cases, like Robolectric unit tests,
0 is a possible pid. The check can be improved by directly asserting that
the value is defined.
PiperOrigin-RevId: 521414649
This wasn't added so far because releasing is always allowed from a
MediaController (as it just releases the connection, not the session
player). But Player instances can be created for other purposes and
the receiver of a Player instance should not always be allowed to
call release if it doesn't own the player resource.
PiperOrigin-RevId: 519121122
Custom commands from controller to session are blocked if they are
not listed in the available session command list. This isn't well
documented in the Javadoc currently.
#minor-release
PiperOrigin-RevId: 514733568
This change includes 3 things:
- when the legacy media session is created, FLAG_HANDLES_QUEUE_COMMANDS
is advertised if the player has the COMMAND_CHANGE_MEDIA_ITEMS
available.
- when the player changes its available commands, a new
PlaybackStateCompat is sent to the remote media controller to
advertise the updated PlyabackStateCompat actions.
- when the player changes its available commands, the legacy media
session flags are sent accoridingly: FLAG_HANDLES_QUEUE_COMMANDS is
set only if the COMMAND_CHANGE_MEDIA_ITEMS is available.
#minor-release
PiperOrigin-RevId: 506605905
This change fixes an issue that can be reproduced when
a controller `onConnect` creates a `QueueTimeline` out
of the state of a legacy session and then `prepare` is called.
`activeQueueItemId`, `metadata` and the `queue` of the legacy
session are used when a `QueueTimeline` is created. The change
adds unit tests to cover the different combinatoric cases these
properties being set or unset.
PiperOrigin-RevId: 505731288
Created unified MediaUtils method to handle various logic for calling Player.setMediaItems from MediaSessionStub and MediaSessionLegacyStub
PiperOrigin-RevId: 504271877
Added onSetMediaItems callback listener to allow the session to modify/set MediaItem list, starting index and position before call to Player.setMediaItem(s).
Added conditional check in MediaSessionStub.setMediaItem methods to only call player.setMediaItem rather than setMediaItems if player does not support COMMAND_CHANGE_MEDIA_ITEMS
PiperOrigin-RevId: 503427927
We stop estimating new position when pausing until we
receive a new position from the player. However, this
means that we will continue to return a possible stale
previous position. Updating the current position before
pausing solves this issue.
PiperOrigin-RevId: 503153982
We currently only document it for the getCurrentMediaItem(), but
the command was always meant to cover all information about the
current media item and the position therein.
To correctly hide information for controllers, we need to filter
the Timeline when bundling the PlayerInfo class if only this
command is available.
PiperOrigin-RevId: 503098124
When bundling PlayerInfo, we need to remove information if the
controller is not allowed to access it. This was only partially
done at the moment.
PiperOrigin-RevId: 502852798
When bundling PlayerInfo, we remove data when the controller is not
allowed to access this data via getters. We also remove data for
performance reasons. In the toBundle() method, it's currently hard to
make the connection between allowed commands and filtering, because
the values are checked at a different place. This can be made more
readable by forwarding the applicable Commands directly.
The only functional fix is to filter the Timeline when sending the
first PlayerInfo after a connecting a controller if the command to
get the Timeline is not available. This also allows us to remove a
path to filter MediaItems from Timelines as it isn't used.
PiperOrigin-RevId: 502607391
The method to dispatch actions in MediaControllerImplBase takes
a Player.Command, but the value is only used to check if we
are setting a surface and need to handle the special blocking
call. This can be cleaned up by removing the parameter and calling
a dedicated blocking method where needed. This also ensures we
have to mention the relevant Player.Command only once in each
method.
PiperOrigin-RevId: 502341862
This fix applies to Android 12 and above.
In this fix, the `MediaSessionService` will try to start in the foreground before the session playback resumes, if ForegroundServiceStartNotAllowedException is thrown, then the app can handle the exception with their customized implementation of MediaSessionService.Listener.onForegroundServiceStartNotAllowedException. If no exception thrown, the a media notification corresponding to paused state will be sent as the consequence of successfully starting in the foreground. And when the player actually resumes, another media notification corresponding to playing state will be sent.
PiperOrigin-RevId: 501803930
The migration strategy is to deprecate `androidx.media3.session.BitmapLoader` and copy the file into common since BitmapLoader is a public interface that apps could be relying on.
PiperOrigin-RevId: 501266521
Initialising the fields as Integer and then getting a String on compute time is slow. Instead we directly initialise these fields as String. Improves the time taken in bundling PlayerInfo further to less than 200ms from ~300ms.
Also modified a test to improve productive coverage.
PiperOrigin-RevId: 500003935
The period updates were introduced to ensure the buffered position is
updated regularly and that any playback position drift is corrected.
None of these updates need to happen while the player is paused or
not loading and we can avoid the constant binder interactions.
PiperOrigin-RevId: 496329800
Some Player methods operate relative to existing indices in the
playlist (add,remove,move,seek). As these operations may be issued
from a place with a stale playlist (e.g. a controller that sends
a command while the playlist is changing), we have to handle out-
of-bounds indices gracefully. In most cases this is already
documented and implemented correctly. However, some cases are not
documented and the existing player implementations don't handle
these cases consistently (or in some cases not even correctly).
PiperOrigin-RevId: 495856295
Some Player methods like getting the Looper and adding listeners
were always allowed to be called from any thread, but this is
undocumented. This change makes the threading rules of these
methods more explicit.
Removing listeners was never meant to be called from another thread
and we also don't support it safely because final callbacks may
be triggered from the wrong thread. To find potential issues, we
can assert the correct thread when releasing listeners.
Finally, there is a potential race condition when calling addListener
from a different thread at the same time as release, which may lead to
a registered listener that could receive callbacks after the player is
released.
PiperOrigin-RevId: 493843981
The folder type has a mix of information about the item. It shows
whether the item is browsable (type != FOLDER_TYPE_NONE) and
which Bluetooth folder type to set for legacy session information.
It's a lot clearer to split this into a boolean isBrowsable and
use the existing mediaType to map back to the bluetooth folder type
where required.
folderType is not marked as deprecated yet as this would be an API
change, which will be done later.
PiperOrigin-RevId: 493544589
This allows legacy media controllers and browsers to access this
information and legacy sessions and browser services to set this
information.
PiperOrigin-RevId: 492414716
Do not log the exception stack traces raised by the BitmapLoader when a
bitmap fails to load, e.g. when the artwork's URI scheme is not
supported by the SimpleBitmapLoader. The logs are kept in place but only
a single line is printed.
#minor-release
PiperOrigin-RevId: 492191461
This CL makes it possible to create a media3 ControllerInfo in test code, which is needed to test several aspects of a media3-based media app. It does this by exposing a test-only static factory method. This is a hacky low-effort approach; a better solution could be to split ControllerInfo up into a public interface that was exposed to client logic, and that they could extend, and a package-private implementation with internal fields like the callback. That's a much bigger change, however.
PiperOrigin-RevId: 491978830
Before this change, the bitmap loading result with mainHandler, in which we set the metadata to `MediaSessionCompat`. However, the `MediaSessionCompat` is not thread safe, all calls should be made from the same thread. In the other calls to `MediaSessionCompat`, we ensure that they are on the application thread (which may be or may not be main thread), so we should do the same for `setMetadata` when bitmap arrives.
Also removes a comment in `DefaultMediaNotificationProvider` as bitmap request caching is already moved to CacheBitmapLoader.
PiperOrigin-RevId: 490524209
This change includes a change in the `IMediaController.aidl` file and needs
to provide backwards compatibility for when a client connects that is of an older or
newer version of the current service implementation.
This CL proposes to create a new AIDL method `onPlayerInfoChangedWithExtensions`
that is easier to extend in the future because it does use an `Bundle` rather than
primitives. A `Bundle` can be changed in a backward/forwards compatible way
in case we need further changes.
The compatibility handling is provided in `MediaSessionStub` and `MediaControllerStub`. The approach is not based on specific AIDL/Binder features but implemented fully in application code.
Issue: androidx/media#102
#minor-release
PiperOrigin-RevId: 490483068
This avoids that apps have to depend on the legacy compat support
library when they want to make this conversion.
Also add a version to both helper methods that takes a Looper to
give apps the option to use an existing Looper, which should be
much faster than spinning up a new thread for every method call.
Issue: androidx/media#171
PiperOrigin-RevId: 490441913
When receiving the `onTimelineChanged` callback, we convert the timeline to the list of `QueueItem`s, where decoding a bitmap is needed for building each of the `QueueItem`s. The strategy is similar to what we did in <unknown commit> for list of `MediaBrowserCompat.MediaItem` - set the queue item list until the bitmaps decoding for all the `MediaItem`s are completed.
PiperOrigin-RevId: 490283587
The only reason this is required at the moment is to set the
process UID field in the token, that is supposed to make it easier
for controller apps to identify the session. However, if this
visibility is not provided, it shouldn't stop us from creating
the controller for this session.
Also docuement more clearly what UID means in this context.
PiperOrigin-RevId: 490184508
When we currently call SessionToken.createSessionToken with a legacy
token, we call the package manager to get the process UID. This
requires visiblity to the target package, which may not be available
unless the target runs a service known to the controller app.
However, when connecting to a Media3, this UID doesn't have to be
known, so we can move the call closer to where it's needed to
avoid the unncessary visibility check.
In addition, a legacy session may reply with unknown result code
to the session token request, which we should handle as well.
One of the constructor can be removed since it was only used from
a test.
PiperOrigin-RevId: 489917706
* Transforms the `ListenableFuture<LibraryResult<MediaItem>>` and `ListenableFuture<LibraryResult<List<MediaItem>>>` to `ListenableFuture<MediaBrowserCompat.MediaItem>` and `ListenableFuture<List<MediaBrowserCompat.MediaItem>>`, and the result will be sent out when `ListenableFuture` the `MediaBrowserCompat.MediaItem` (or the list of it) is fulfilled.
* Add `artworkData` to the tests in `MediaBrowserCompatWithMediaLibraryServiceTest`.
PiperOrigin-RevId: 489205547
The MediaControllerImplBase listener invocations currently use the
class member state that can change if one of the listener method
implementations changes the state recursively.
Updating the listener invocations to use a final local variable
ensures all listeners get consistent updates.
PiperOrigin-RevId: 487503373
Creating the PlaybackStateCompat from a media3 Player state is done
already by the MediaSessionConnector (and used widely). The media3
session module should set the same states under the same circumstances
to ensure compatiblity and consistency.
PlaybackStateCompat changes made in media3 session:
- Use STATE_STOPPED when player is ended instead of STATE_PAUSED
- Use STATE_PLAYING when playback is suppressed temporarily.
- Set the playback speed to 0 if the player is not playing.
- Add extras for mediaId and user-set playback speed.
Part of the problem was that Player.isPlaying() was used to check
the state. Unfortunately, MockPlayer.isPlaying() is implemented in
a way that makes it hard to test these changes, because the value
is set independently of playbackState, playWhenReady and suppression
reason. To be able to write consistent, logical tests, this change
also removes the independent setting of isPlaying in MockPlayer to
align it better with a real player. This requires to update some
other tests to use alternative methods.
PiperOrigin-RevId: 487500859
The connection to a legacy MediaSession may receive additional
onSessionReady callbacks that are treated as additional state updates.
We currently also set the "notifyConnected" flag for these updates
even though we are connected already, causing an IllegalStateException.
Fix the exception by not setting this flag.
We can also remove the wording about "locked" updates since this class
operates everything on a single application thread.
Issue: androidx/media#49
PiperOrigin-RevId: 487487286
Accepting a PlayerInfo while the MediaController is masking its state
means we are reverting all masking changes we've made earlier. This
only makes sense if the update already contains the masked operation.
If multiple operations are in flight (or are sent from the session
while they are in flight), we need to wait until all of them are
handled before accepting new updates.
In cases where a new update from the session excludes the Timeline
and the masked state is incompatible with the new update, we also
risk an exception if we accept the update too early.
PiperOrigin-RevId: 487266899
This is the follow-up commit where the onEvents callback
raised by MediaController contains the missing events, for the
case where MediaController is connected to a legacy MediaSession.
#minor-release
PiperOrigin-RevId: 487231996
The method getCurrentPosition() may return a lesser position during pause than the previous retrieved value due to ipc call delay in playerInfo update. Users see track position jump backwards at pause. Fixed to return last estimated position while paused if have not received updated playerInfo. Code is deduped to point getContentPosition() to getCurrentPosition() when !isPlayingAd.
PiperOrigin-RevId: 486617341
Notification buttons for next/previous should change based on the new index of the currently played media item after another media item is added or removed from a playlist.
Issue: androidx/media#130
PiperOrigin-RevId: 485869144
* Add `Listener` in `MediaSession` with method `onNotificationRefreshRequired(MediaSession)`.
* Add `MediaSessionService` as the listener of the `MediaSession` when `MediaSession` is added to `MediaSessionService`
* Load bitmap when update metadata in `MediaSessionLegacyStub` and call `onNotificationRefreshRequired` when bitmap asynchronously arrives.
PiperOrigin-RevId: 485376145
This root extra needs to be set by apps manually in media1 and we
can do that automatically in Media3 based on the available session
commands.
#minor-release
PiperOrigin-RevId: 484286833
In Media3 there is the useful concept of connection hints that a
client can set when building the session and that are sent to the
service and passed to the `Callback.onConnect()` method when the
browser connects.
These connection hints are then included in the `ControllerInfo`
object that later will be passed to every callback method and the
implementor can then take decisions specific to these connection
hints.
These connection hints are not available in media1. However, when
an app creates a `MediaBrowserCompat` object, the constructor takes
a rootHint object that is sent to
`MediaBrowserServiceCompat.onGetRoot()`.
This change uses the browser rootHints as the connection hints when
creating the `ControllerInfo` for legacy browsers and makes them
available to the `MediaLibrarySession.Callback` domain methods in
the same way as connection hints of a Media3 browser.
PiperOrigin-RevId: 484220748
* Add `BitmapLoader` in `MediaSession.Builder` and `MediaLibrarySession.Builder`.
* Pass `BitmapLoader` into the constructor of `MediaSession`, `MediaSessionImpl`, `MediaLibrarySession` and `MediaLibrarySessionImpl`.
* Add an interface method `loadBitmapFromMetadata(MediaMetadata)` in `BitmapLoader`.
* Remove the reference of `BitmapLoader` in `DefaultMediaNotificationProvider`.
PiperOrigin-RevId: 483654596
* Add `CacheBitmapLoader`.
* Add `CacheBitmapLoaderTest`.
* Remove the `BitmapLoadRequest` and some bitmap caching logic in `DefaultMediaNotificationProvider` since we moved all of them in `CacheBitmapLoader`.
* Modify `DefaultMediaNotificationProviderTest`.
PiperOrigin-RevId: 482787445
The MediaNotficationManager stops the service from the foreground
calling Service.stopForeground(boolean) which is deprecated in API 33.
This change calls Service.stopForeground(int), which was added in API
24.
#minor-release
PiperOrigin-RevId: 482190332
This is the first commit out of two. This change adds the missing event
flags for the onEvents() callback when MediaController is connected to a
media3 session (see MediaControllerImplBase). I updated the
MediaControllerListenerTest and MediaControllerStateMaskingTest with
assertions that on onEvents() is called alongside individual
Player.Listener callbacks.
There will be a follow-up change for the case where a MediaController is
connected to a legacy MediaSession (MediaControllerImplLegacy). I've
split this in two separate changes to make the size of the commit
manageable for reviewing.
#minor-release
PiperOrigin-RevId: 481933437
Adds root extras and metadata extras to MockMediaLibraryService and MockMediaBrowserCompatService and completed test cases for asserting
interoperability with a media1 or Media3 browser.
PiperOrigin-RevId: 480854842
When a media service currently produces multiple media sessions, the notification of the second session overwrites the notification of the first one, because all sessions use the same notification ID. When we use different notification IDs for different sessions, multiple media notifications can be up at the same time, which means that they can both be controlled at the same time.
PiperOrigin-RevId: 478709069
`PlaybackStateCompat.toKeyCode(command)` was replaced by our
own implementation of `toKeyCode()`. The legacy implementation used PLAY and PAUSE, while the new implementation uses PLAY_PAUSE. This made `pause` a pending intent that attempt to start the service in the foreground, but `service.startForeground()` won't be called in `MediaNotificationManager.updateNotificationInternal` when paused.
PiperOrigin-RevId: 476895752
Calling maybeUpdateLegacyErrorState potentially creates a new legacy playback
state which involves calling player methods. This change makes sure that the call
sites of `maybeUpdateLegacyErrorState` are called on the app thread as enforced by
the library.
PiperOrigin-RevId: 476406282
The exception fires when intent resolution fails to find a service which declares an appropriate intent-filter. The existing message is confusing; it's trying to say that the service couldn't be found but the double negative renders it incorrect.
#cleanup
#minor-release
PiperOrigin-RevId: 472736740
* Non-standard parameter comment; prefer `/* paramName= */ arg`
(see http://go/bugpattern/ParameterComment) (2 times)
* This catch block catches an exception and re-throws another, but swallows the caught exception rather than setting it as a cause. This can make debugging harder.
(see http://go/bugpattern/UnusedException)
* This comment contains Javadoc or HTML tags, but isn't started with a double asterisk (/**); is it meant to be Javadoc?
(see http://go/bugpattern/AlmostJavadoc)
This CL looks good? Just LGTM and Approve it!
This CL doesn’t look good? This is what you can do:
* Revert this CL, by replying "REVERT: <provide reason>"
* File a bug under go/error-prone-bug for category ErrorProneStyle if there's an issue with the CL content.
* File a bug under go/rosie-bug if there's an issue with how the CL was managed.
* Revert this CL and not get a CL that cleans up these paths in the future by
replying "BLOCKLIST: <provide reason>". This is not reversible! We recommend to
opt out the respective paths in your CL Robot configuration instead:
go/clrobot-opt-out.
This CL was generated by CL Robot - a tool that cleans up code findings
(go/clrobot). The affected code paths have been enabled for CL Robot in //depot/google3/java/com/google/android/libraries/media/METADATA which is reachable following include_presubmits from //depot/google3/third_party/java_src/android_libs/media/METADATA.
Anything wrong with the signup? File a bug at go/clrobot-bug.
#codehealth
Tested:
Local presubmit tests passed.
PiperOrigin-RevId: 472255768
The assertion asserts against a `Period` and an `AdPlaybackState` which actually
asserts against a resolved ad which is what `ExoPlayerImplInternal` does later and
what gives us a `SEEK_ADJUSTMENT`. However, this assertion is not required at the
moment of masking, because we are sure that the resolved seek results in a content
period and never an ad period.
#minor-release
Issue: androidx/media#122
PiperOrigin-RevId: 471827072
We create an empty CueGroup in many places as default or
where none is needed. Instead, we can define a constant
for this purpose and reuse it.
PiperOrigin-RevId: 467944841
When the player finishes playback and reaches the STATE_ENDED,
the notification remains visible with a pause button and the
service is kept in the foreground. This is a bug.
With this change, when the player reaches the STATE_ENDED, the
service is stopped from the foreground and a notification is shown
with a play button. If the play icon is tapped, the player will restart
playback of the last played item. Playing the last played item again
is the existing behavior when play/pause commands are received from
the legacy MediaSession (e.g. BT headset buttons).
#minor-release
Issue: google/ExoPlayer#112
PiperOrigin-RevId: 467231509
Once a controller bound to a MediaSessionService unbinds, this service
may be destroyed immediately if this was the last bound controller and
the service isn't started in the foreground.
At the time of releasing the controller, there may still be pending
commands that need to be handled by the session service. These commands
may cause the session service to post a foreground notification to
keep it alive. So to avoid the destruction of the service while these
commands are still handled, we need to keep the controller bound.
We also add a timeout in case the session tasks are never completed
by the session implementation. In case the controller is destroyed,
the unbinding happens automatically by the system.
PiperOrigin-RevId: 463802220
The DefaultMediaNotificationProvider caches the last loaded artwork
bitmap so that the bitmap isn't loaded again when the notification is
updated, e.g., the player is transiting from playing to paused. However,
loading bitmap requests for bitmaps that are already being loaded are
not suppressed. For example, if the notification is updated while the
artwork is still downloading, the same artwork might be downloaded
multiple times.
This change suppresses a bitmap load request if the same artwork is
still being loaded, to avoid additional artwork downloads.
#minor-release
PiperOrigin-RevId: 462572221
We currently clear all pending messages, including the one that flushes
pending commands to the MediaSession. To ensure all commands that have
been called before controller.release() are still sent, we can manually
trigger the flush message from the release call.
Related to handling the final flush because disconnecting the controller,
MediaSessionStub didn't post the removal of the controller to the
session thread, creating a race condition between removing the controller
and actually handling the flush.
Issue: androidx/media#99
PiperOrigin-RevId: 462342860
These constructors are currently very intertwined, passing `this`
references from the constructor of one to the constructor of another
before the first constructor is complete (and so the `this` reference
isn't really valid yet).
This change uses checker framework `@UnderInitialization` and
`@NotOnlyInitialized` annotations to make it more clear that the
references are not available yet. For the one 'direct' access needed
in the second constructor (calling `getApplicationLooper()`) we now
pass the `applicationLooper` directly alongside (to avoid needing to
dereference the reference 'too early').
This change also ensures that where a class hierarchy has a
'dependent' class hierarchy, the 'subclass' instance is always used
(by both subclass and superclass) without casting or manually hiding
the superclass field, by defining an overridable `getFoo()` method
instead and always using it.
#minor-release
PiperOrigin-RevId: 462335043
"seq" is not a well-defined abbreviation and the value is
also an integer, so sequenceNumber is better than just sequence.
PiperOrigin-RevId: 462129581
Add a Builder to constructor DefaultMediaNotificationProvider. The
Builder can also set the provider's:
- notification ID
- notification channel ID
- notification channel name
The change adds an API for apps to set the small icon in notifications.
#minor-release
Issue: androidx/media#104
PiperOrigin-RevId: 462111536
Some commands are run asynchronously and subsequent commands need
to wait until the previous one finished. This can be supported
by returning a Future for each command and using the existing
command execution logic to wait for each Future to complete.
As some MediaSessionStub code is now executed delayed to when it
was originally created, we also need to check if the session is
not released before triggering any actions or sending result codes.
Issue: androidx/media#85
PiperOrigin-RevId: 462101136
The commands currently use a task and a postTask that are chained
together manually. In some cases, e.g. when adding MediaItems,
the postTask is already a chain of commands in itself.
To allow using the entire command handling as a single task
(for simplified queueing), we can change the implementation to
always create a single task. If multiple subtasks need to be
chained together, we can do that by wrapping the method calls.
In case a task is asynchronous, we can also use Futures to
chain them together.
Overall, this is just a refactoring and changes no logic.
Issue: androidx/media#85
PiperOrigin-RevId: 462085724
Some commands may be asynchronous and subsequent commands need to
wait for them to complete before running. This change updates the
queue to use (and listen to) Futures instead of calling Runnables
directly. The commands are currently still added as Runanbles
though, so this change is a no-op.
Also moves the permission check in MediaSessionImpl to before
queueing the command because the permission should be check at
the time of calling the method.
When executing the comamnds in the queue, we need to be careful
to avoid recursion in the same thread (which happens when both
the Future is immediate and running on the correct thread already).
To avoid recursion, we detect this case and loop the commands
instead.
Issue: androidx/media#85
PiperOrigin-RevId: 461827264
After this change the current tracks are sent to the controller as part of
`PlayerInfo` and call `Listener.onTracksChanged()` in case of a change in tracks.
PiperOrigin-RevId: 461578695