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
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
While debugging various session tests failure I found that sometimes
this list was empty (e.g. when a controller had failed to connect, or
hadn't finished connecting yet) - resulting in these methods being
silent no-ops. I think it's basically never expected for there to be no
controllers connected when these methods are invoked, so this logs at
ERROR level to give a clue of what happened when looking at the test
logcat.
PiperOrigin-RevId: 564750969
This aligns the equivalent implementation in `MockMediaSessionService`,
and it looks more correct to me - but the tests seem to pass both with
and without this change (and it doesn't seem to affect flakiness
either).
PiperOrigin-RevId: 564689493
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
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
This removes some stack traces in unit tests when a
`RemoveMediaController` was created with `waitForConnection=false`
in which case the missing controller in the
`MediaControllerProviderService` caused a `NullPointerException`.
PiperOrigin-RevId: 546795181
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
*** Original commit ***
Rollback of 2a6f893fba
*** Original commit ***
Set video size to 0/0 when video render is disabled
In terms of MCVR wi...
***
PiperOrigin-RevId: 540525069
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
*** Original commit ***
BEGIN_PUBLIC
Set video size to 0/0 when video render is disabled
In terms of MCVR with a `VideoRendererEventListener`, the video size is set to
0/0 right after `onVideoDisabled()` is called and is set to the actual size as
soon as the video size is known after 'onVideoEnabled()`.
For ExoPlayer and in terms of the `Player` interface, `Player.getVideoSize()`
returns a video size of 0/0 when `Player.getCurrentTracks()` does not support
`C.TRACK_TYPE_VIDEO`. This is ensured by the masking behavior
***
PiperOrigin-RevId: 537938947
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
In terms of MCVR with a `VideoRendererEventListener`, the video size is set to
0/0 right after `onVideoDisabled()` is called and is set to the actual size as
soon as the video size is known after 'onVideoEnabled()`.
For ExoPlayer and in terms of the `Player` interface, `Player.getVideoSize()`
returns a video size of 0/0 when `Player.getCurrentTracks()` does not support
`C.TRACK_TYPE_VIDEO`. This is ensured by the masking behavior of
`ExoPlayerImpl` that sets an empty track selection result when the playing
period changes due to a seek or timeline removal.
When transitioning playback from a video media item to the next, or when
seeking within the same video media item, the renderer is not disabled.
#minor-release
PiperOrigin-RevId: 533479600
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
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
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
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
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
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
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
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
* 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
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
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
* 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
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
One of the tests in MediaBrowserListenerTest caused the remote
browser service to crash and then just timed out. As this asserts
nothing useful besides checking that the timeout method is working,
we can remove the test.
Crashing the remote browser service had the side effect of letting
subsequent tests in the same class fail because the previous session
was never released and was still present in the static MediaSession
SESSION_ID_TO_SESSION_MAP instance, which prevented the creation
of new sessions with the same id. This is only an issue in test
runs because a real process would also lose its static variables
when it crashes.
PiperOrigin-RevId: 476905337
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
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
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