359 Commits

Author SHA1 Message Date
bachinger
141f9b760d Return null if media notification controller Future is not done
When the media notification controller is requested for a session
with `getConnectedControllerForSession` and the `Future` is not null
but not yet completed, the `Future` was returned either way. This was
reported as creating a race condition between the notification
being requested for update the very first time, and the media
notification controller having completed connecting to the session.

Returning null from `getConnectedControllerForSession` when the
`Future` is available but not yet done fixes the problem. This is
safe because for the case when a notification update is dropped,
the media notification controller will trigger the update as soon
as the connection completes.

Issue: androidx/media#917
#minor-release
PiperOrigin-RevId: 595699929
(cherry picked from commit 5c50b27e8fce878bc24852498ae42298613d5ad2)
2024-01-09 12:17:49 +00:00
bachinger
d1b882ae56 Don't set negative values to setWhen()
When the 'when' timer of the notification is disabled
`DefaultMediaNotificationProvider` may set `C.TIME_UNSET`
as the time. Users reported problems on some devices with
this and the docs ask for an event time that probably
shouldn't be a negative number.

This change sets `0L` instead of `C.TIME_UNSET` when the
timer is disabled.

Issue: androidx/media#903

#minor-release

PiperOrigin-RevId: 594451074
(cherry picked from commit 426bc940905237accf902f42d63b3777c11ff72e)
2024-01-09 12:17:48 +00:00
bachinger
09d30a160e Fix broadcasting notifyChildrenChanged for legacy controllers
When broadcasting a notifyChildrenChanged event, the task for legacy
controllers was sent to the broadcasting callback. This would
technically work, but because the subscription list is maintained
with specific controllers, the broadcast controller isn't subscribed
and hence the call wasn't executed.

This change calls the overloaded method for a specific controller
for each connected controller. Making sure (only) subscribed
controllers are notified.

Issue: androidx/media#644
PiperOrigin-RevId: 590904037
(cherry picked from commit 4974f960e7b8a34a37c2206b87973339c0e81cef)
2024-01-09 12:17:48 +00:00
Googler
c314123298 Fix nullability issue in MediaControllerImplLegacy
PiperOrigin-RevId: 588035411
(cherry picked from commit e0f1783a54867ee81d6f9fb93ffdcee8451f2118)
2024-01-09 11:03:33 +00:00
tonihei
4234bc7091 Remove redundant ) in Javadoc
PiperOrigin-RevId: 584910697
(cherry picked from commit 85a54e2e190b705367b0d2dd4d7fcd71900b3fdd)
2024-01-08 15:31:29 +00:00
bachinger
5e86c4c20b Add session extras to the state of the controller
This change adds `MediaController.getSessionExtras()` through
which a controller can access the session extras.

The session extras can be set for the entire session when
building the session. This can be overridden for specific
controllers in `MediaSession.Callback.onConnect`.

PiperOrigin-RevId: 584430419
(cherry picked from commit a063d137b4307348a140ec6a2b6d254db294395e)
2024-01-08 15:26:08 +00:00
rohks
aab1c9ada1 Populate MediaMetadata.extras to MediaMetadataCompat
Ensures backward compatibility.

Issue: androidx/media#802
PiperOrigin-RevId: 583425114
(cherry picked from commit 6df240877c30aedb271c2bc74c54fc2bab0e4cbf)
2024-01-08 15:18:38 +00:00
Googler
2db9da5c1d Don't crash when receiving a bad playback state
PiperOrigin-RevId: 580942377
(cherry picked from commit e79809616cd0ecb6f39cbeffdaaf143c260f64e6)
2024-01-08 14:54:21 +00:00
ibaker
2f4d475b5c Split media1/media3 conversion methods out of MediaUtils
Android Studio removed some nested imports, but I think the extra
qualification at the usage site is actually mostly helpful, so I'm
leaving it as-is.

PiperOrigin-RevId: 578518880
(cherry picked from commit 72b7019578f3051e1bec826cf0ac401a86d818dc)
2024-01-08 14:51:05 +00:00
tianyifeng
2237f4902d Put the custom keys in MediaMetadataCompat to MediaMetadata.extras
PiperOrigin-RevId: 578473874
(cherry picked from commit 84022eacc560b90cf34253b2470aabdf4a4b767d)
2024-01-08 14:51:05 +00:00
bachinger
f2cf43ccd5 Use MediaSessionImpl.onMediaButtonEvent() to dispatch key events
This change moves the handling of any media button event into
`MediaSessionImpl.onMediaButtonEvent(intent)`. This includes
the double click handling from `MediaSessionLegacyStub`.

The advantage is that everything is in one place which allows
to offer `MediaSession.Callback.onMediaButtonEvent` with which
an app can override the default implementation and handle media
buttons in a custom way.

Media button events can originate from various places:

- Delivered to `MediaSessionService.onStartCommand(Intent)`
  - A `PendingIntent` from the notification below API 33
  - An `Intent` sent to the `MediaButtonReceiver` by the system dispatched
    to the service
- Delivered to `MediaSessionCompat.Callback.onMediaButtonEvent(Intent)`
  implemented by `MediaSessionLegacyStub` during the session is active
  - Bluetooth (headset/remote control)
  - Apps/system using `AudioManager.dispatchKeyEvent(KeyEvent)`
  - Apps/system using `MediaControllerCompat.dispatchKeyEvent(keyEvent)`

Issue: androidx/media#12
Issue: androidx/media#159
Issue: androidx/media#216
Issue: androidx/media#249

#minor-release

PiperOrigin-RevId: 575231251
(cherry picked from commit a79d44edc5c7fdc81120dbc9b2c89b9799b14031)
2023-10-20 08:52:55 -07:00
tonihei
e1ad106453 Add missing command checks to playback resumption flow
Player methods shouldn't be called if they are not available and the
entry point to the playback resumption flow only checks
COMMAND_PLAY_PAUSE.

#minor-release

PiperOrigin-RevId: 574834148
(cherry picked from commit bfd1a2724c660de0df3c13f8394238ac6aa26e68)
2023-10-19 05:36:49 -07:00
bachinger
a8b0bd712a Do not hide System UI when app rejects connection
If an app rejects the connection of the internal media notification manager
the session should behave like without the the media notification controller.
The legacy System UI controller should not be hidden or even rejected to
connect in such a case.

#minor-release

PiperOrigin-RevId: 574807901
(cherry picked from commit 54d5810fc353a9e7133ef929ab2f822d921070b1)
2023-10-19 03:48:16 -07:00
tonihei
2ae6b1e832 Rollback of 64bd3bcad3
PiperOrigin-RevId: 574766164
(cherry picked from commit f0cab4d03ecfa7fbd48262c332d85329736224af)
2023-10-19 01:03:49 -07:00
tonihei
21cb3a6bea Rollback of 4ebe630a80
PiperOrigin-RevId: 574530273
(cherry picked from commit c0759a4e62466bbc0816737e28adf1b4a513016c)
2023-10-23 13:15:17 +01:00
tonihei
fe60d0d7b4 Use DataSourceBitmapLoader by default
This replaces the SimpleBitmapLoader that can now be deprecated
as it's fully unused and doesn't provide any additional functionality.

#minor-release

PiperOrigin-RevId: 574454636
(cherry picked from commit db86932781b4a5f377d1f4c1414c3d6a74ede174)
2023-10-23 13:15:17 +01:00
Googler
9ace4855e0 Rollback of 4ebe630a80
PiperOrigin-RevId: 574308136
(cherry picked from commit ff330bd8e9e925987396597cfa25f5455e1d4048)
2023-10-23 13:15:17 +01:00
Googler
fcb3315edc Rollback of 64bd3bcad3
PiperOrigin-RevId: 574290408
(cherry picked from commit 1a43aa3602075cc88c902de293cb025ff3d619cc)
2023-10-23 13:15:17 +01:00
bachinger
8e48433be3 Send media button events from service directly using MediaSessionImpl
Media button event coming from the `MediaSessionService` are delegated
to the `MediaSessionImpl` and then sent to the session by using the
`MediaSessionStub` directly instead of using the `MediaController`
API.

Splitting the `MediaController.Listener` and `Player.Listener` in
`MediaNotificationManager` got reverted, and both listener are set to the
controller as before. This reverts the change that introduced a
different timing behaviour. It still holds, that a listener
registered on a `MediaController` that calls a method like `play()` is
called immediately and before the call has arrived at the player. This
change works around this behaviour from the library side by calling
`MediaSessionStub` directly with a `ControllerInfo`.

#minor-release

PiperOrigin-RevId: 573918850
(cherry picked from commit 64bd3bcad3fa4b0e433b16d583456920afad3ce2)
2023-10-23 13:15:17 +01:00
bachinger
85167f175a Send ConnectionState as in-process bundle if possible
#minor-release

PiperOrigin-RevId: 573849858
(cherry picked from commit d5f093f43cc2bda763436d4ecf32c38c76b9418e)
2023-10-23 13:15:17 +01:00
bachinger
386d2239eb Only set the queue when COMMAND_GET_TIMELINE is available
Android Auto shows a queue button when the queue is not empty.
Apps were able to remove this queue button with the legacy API
by not setting the queue of the session.

After this change, removing `COMMAND_GET_TIMELINE` from the commands
of the media notification controller or the session player sets the
queue in the platform session to null.

#minor-release
Issue: androidx/media#339
PiperOrigin-RevId: 573813558
(cherry picked from commit f53e1bc6f63caba7774c35aeb663b9178941faf5)
2023-10-23 13:15:17 +01:00
tonihei
d7044c7b83 Avoid bundling PlayerInfo for in-process calls
PlayerInfo bundling is costly and we can add a shortcut for
in-process binder calls where we store the direct object
reference in a live Binder object that can be written to the
Bundle instead of the individual data fields.

#minor-release

PiperOrigin-RevId: 572816784
(cherry picked from commit d1fc15f2075dd5c130a12420889fd83bd6517a08)
2023-10-23 13:14:42 +01:00
tonihei
7d6146170f Split available command filtering and bundling
A few methods in PlayerInfo and related classes combine filtering
information with bundling in one method. This makes it impossible
to use just the filtering for example and it's also easier to reason
about than two dedicated methods. This change splits these methods
into two parts accordingly.

PiperOrigin-RevId: 572592458
(cherry picked from commit 4ebe630a80296cbb4437336a50abccb39da978f7)
2023-10-23 13:14:42 +01:00
bachinger
0255f3be16 Check whether a session is still managed before removing
When the controller of the `MediaNotificationManager` is disconnected,
the session is removed from the service without checking whether the
session hasn't already been removed. This caused flakiness in `MediaSessionServiceTest.addSession()`.

Because there is a public API `MediaSessionService.removeSession()`,
the controller can't make an assumption whether the session is still
contained in the service when being disconnected.

#minor-release

PiperOrigin-RevId: 572568350
(cherry picked from commit 7fdc5b22bac1af6fd074df38bb6b98c921e713a1)
2023-10-23 13:14:42 +01:00
bachinger
4b85207621 Add MediaSession.Builder().setPeriodicPositionUpdateEnabled()
This allows to disable periodic position updates when building
the session.

#minor-release

PiperOrigin-RevId: 572531837
(cherry picked from commit 4dc3db4da3da486b9c9ec1780aa595da8de5330c)
2023-10-23 13:14:41 +01:00
tonihei
7254f5aca5 Do not interrupt controller thread without a good reason
Interrupting the main thread in particular may be dangerous
as the flag is not cleared after handling the current message.

#minor-release

PiperOrigin-RevId: 572259422
(cherry picked from commit 846117399ff87dc025c355639444de2e54430b18)
2023-10-23 13:14:41 +01:00
tonihei
7d063e9aab Add missing Future cancellation checks
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
(cherry picked from commit fe7c62afe0b39f8d6617cf610dbdccc9e6adcfb4)
2023-10-23 13:13:46 +01:00
bachinger
0b0d02c3e4 Add isAutomotiveController and isAutoCompanionController
Issue: androidx/media#561
Issue: androidx/media#644
Issue: androidx/media#645
PiperOrigin-RevId: 568948230
2023-09-27 13:45:17 -07:00
bachinger
99086b4007 Add default implementation of Callback.onSubscribe
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
2023-09-27 12:19:34 -07:00
tonihei
77ba0292ad Add position interpolation to MediaControllerImplLegacy
Without this, the position won't udpate until the session sends
a new playback state.

PiperOrigin-RevId: 568889286
2023-09-27 10:23:27 -07:00
bachinger
ffd7bb5639 Resolve and dispatch media button events within Media3
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
2023-09-25 08:19:33 -07:00
bachinger
5e05e2ec22 Pass down ControllerInfo from service to session when connecting
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
2023-09-25 07:50:12 -07:00
bachinger
742410d517 Use proxy controller to maintain platform session and notification
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
2023-09-22 06:36:22 -07:00
ibaker
e357d9f218 Don't call MediaSession.Callback.onPostConnect if connecting failed
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
2023-09-19 09:29:40 -07:00
jbibik
39c1ac7f3a Move setAudioAttributes from ExoPlayer to Player
PiperOrigin-RevId: 566607528
2023-09-19 06:08:37 -07:00
christosts
c0e11e0edd Move bitmap loader tests in test
These tests were under androidTest because we needed a functional
BitmapFactory. Robolectric now supports decoding bitmaps so moving them
under tests.

PiperOrigin-RevId: 565181239
2023-09-13 15:40:55 -07:00
bachinger
8c0191d0cb Use media button broadcast receiver above Android 31
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
2023-09-05 14:24:06 -07:00
tianyifeng
32e367fabf Set groupKey for media notification in DefaultMediaNotificationProvider
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
2023-08-29 09:10:12 -07:00
bachinger
6bd3e53e8d Set notification foreground service behaviour starting with API 31
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
2023-08-18 15:09:59 +01:00
tonihei
597de8706d Show play button during playback suppression by default
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
2023-08-15 17:15:38 +01:00
jbibik
dedccc596e Fix PlayerWrapper's creation of VolumeProviderCompat
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/201546605

http://Issue: androidx/media#554

#minor-release

PiperOrigin-RevId: 554966361
2023-08-10 12:12:09 +00:00
bachinger
17ee5277d5 Only use result.sendError where supported by legacy media library
`MediaLibraryServiceLegacyStub` handles various edge cases by calling
`result.sendError(null)` with the intention to send back an error to
the legacy browser [1].

`MediaBrowserServiceCompat` of the legacy media1 Compat library has an
inner base class `Result` that has a default implementation of
`onErrorSent` that throws an `UnsupportedOperationException` [2].
However, most anonymous inner classes for `Result` created in
`MediaBrowserServiceCompat` do not override `onErrorSent` [3].

Hence Media3 must not call `sendError` in these cases. Instead we call
`sendResult(null)` according to what the default implementation of
the callbacks in `MediaBrowserServiceCompat` do ([4] as an example).

Issue: androidx/media#78
Issue: androidx/media#334

[1] https://github.com/androidx/media/blob/release/libraries/session/src/main/java/androidx/media3/session/MediaLibraryServiceLegacyStub.java#L200
[2] https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:media/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java;l=872
[3] https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:media/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java;l=578-604?q=MediaBrowserServiceCompat&ss=androidx
[4] https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:media/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java;l=1395

PiperOrigin-RevId: 551210137
2023-08-01 14:11:08 +01:00
bachinger
a90ec96425 Use MediaUtils.intersect in MediaControllerImplBase
PiperOrigin-RevId: 550566322
2023-08-01 13:54:11 +01:00
bachinger
2c19399d3e Move command button related helper methods to CommandButton
PiperOrigin-RevId: 550558261
2023-08-01 13:53:09 +01:00
bachinger
d658de5944 Mark media notification controller and filter command buttons
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
2023-08-01 13:44:12 +01:00
tonihei
8064c6df83 Declare foreground service type for DownloadService
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
2023-07-20 10:23:56 +01:00
Googler
46482e0384 Avoid resetting suppression reason with play/pause on MediaController
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
2023-07-20 09:57:51 +01:00
siroberts
ab904bde2d Prevent the creation of CommandButtons without commands.
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
2023-07-13 15:56:43 +01:00
bachinger
ea21d27a69 Add custom layout to the state of the MediaController
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
2023-07-13 15:47:50 +01:00
ibaker
eed0e42ff8 Remove javadoc @link from non-javadoc comments
Also update type names to match the current names for these types.

PiperOrigin-RevId: 546884049
2023-07-13 15:32:21 +01:00