509 Commits

Author SHA1 Message Date
tonihei
f533f55cd6 Add missing type cast
(required due to not cherry-picking 6b1a2aff98)
2025-04-14 09:33:02 +01:00
tonihei
24a8185f4d Ensure media notification provider can be updated
Some interactions create a default notification provider if
no custom one is set yet (e.g. setForegroundServiceTimeoutMs).
This means a later call to setMediaNotificationProvider will
silently fail to apply the new provider.

This can be fixed by making the media notification provider
updatable.

Issue: androidx/media#2305
PiperOrigin-RevId: 746428193
(cherry picked from commit 9ca8540f85fba2ff5c8ff151706f36e7b30b46e6)
2025-04-12 21:10:54 +01:00
bachinger
75e2522862 Always return empty timeline when wrapped player is empty
In `PlayerWrapper.getCurrentTimelineWithCommandCheck()` we
always return `new CurrentMediaItemOnlyTimeline(this)` in
case the wrapped player doesn't have `COMMAND_GET_TIMELINE`
available but has `COMMAND_GET_CURRENT_MEDIA_ITEM`. This is
emulating a single item timeline with a static window count
of 1 which isn't correct when the wrapped player is empty.

Instead, when the wrapped player is empty we need to return
an empty timeline to match the wrapped player.

Issue: androidx/media#2320
PiperOrigin-RevId: 746071237
(cherry picked from commit 5f940af3df5e644b33d45c78cd5103f78566efd0)
2025-04-12 21:10:54 +01:00
tonihei
157fd8a260 Remember explicit notification dismissal
Currently, a notification may be recreated even if a user dismissed
it as long as the standard conditions for a notification are true.

To avoid this effect, we plumb the dismissal event to the notification
controller, so that it can override its `shouldShowNotification`
decision. The plumbing sets an extra on the media key intent, which
the session forwards as a custom event to the media notification
controller if connected.

Issue: androidx/media#2302
PiperOrigin-RevId: 745989590
(cherry picked from commit f672590b2deeffb06435eb542cfe0d0630894e92)
2025-04-12 21:10:54 +01:00
bachinger
ce0c98c4d4 Make sure subtitle is used without display title being set
PiperOrigin-RevId: 745616511
(cherry picked from commit cb0ea7fc95ffb1160cfd7904ee5c548d8f0a68cb)
2025-04-12 21:10:51 +01:00
bachinger
5b4ca08ca0 Adjust AGGREGATES_CALLBACKS_WITHIN_TIMEOUT_MS and make it configurable
PiperOrigin-RevId: 745585764
(cherry picked from commit 1b3658e357adad6f12fd7cc864f0b0d64f38fb3c)
2025-04-12 21:09:13 +01:00
tonihei
23468ed55c Clarify that method can only be called after onCreate
And improve error message if access to the notification controller
happens without a base context.

#cherrypick

PiperOrigin-RevId: 736545574
(cherry picked from commit 41722be02e34199543dc92c44e12ff95a35cb378)
2025-03-17 14:02:19 +00:00
tonihei
b215670445 Make foreground service timeout configurable
This allows apps to set a shorter timeout if so desired.

Issue: androidx/media#2206

#cherrypick

PiperOrigin-RevId: 735360459
(cherry picked from commit 222950cfd1bdc942b90e0991e6b96a3f2c86518a)
2025-03-17 14:01:30 +00:00
tonihei
67ec5b76ad Ensure notification is removed when shouldShowNotification==false
We currently combine stopping the FGS and optionally removing the
notification in one method, which unnecessarily gates its logic on
checking the desired foreground state again. This causes a bug where
the notification should be removed (because shouldShowNotification
returns false), but stays visible because the service is allowed
to stay in the foreground and the notification removal code is not
triggered.

Issue: androidx/media#2211
PiperOrigin-RevId: 735126704
(cherry picked from commit 91ecc16198bbb48d114a6d581669a9e670c161da)
2025-03-17 14:01:27 +00:00
tonihei
766dbd497e Add util to obtain AudioManager
AudioManager internally assumes the thread is was created on can be
used as a callback thread. Since the AudioManager is only created
once in the lifetime of an app, we need to make sure its obtained
on a thread that stays alive.

#cherrypick

PiperOrigin-RevId: 732072255
(cherry picked from commit 2088697a19ac85feb26ba2521a70647803246571)
2025-03-03 16:27:28 +00:00
Copybara-Service
a578d43324 Merge pull request #2115 from MGaetan89:use_objects_equals
PiperOrigin-RevId: 730860597
(cherry picked from commit cc44de8757501cab7e4bb70fd0b090c6dc9799e1)
2025-03-03 16:26:31 +00:00
tonihei
1f691106f1 Clarify that multiple LibraryParams fields can be set
Issue: androidx/media#2159
PiperOrigin-RevId: 730805485
(cherry picked from commit 3f493eaf9e01909094e99c173cec96b1256f5ca4)
2025-03-03 16:26:31 +00:00
bachinger
bb358241b9 Make setSessionActivity accept null
Issue: androidx/media#2109
PiperOrigin-RevId: 728160580
(cherry picked from commit 2b8700beaaabd51365daf9099c42bfa64a2dd4fe)
2025-02-18 14:11:51 +00:00
tonihei
1ceafc7a61 Add timeout to foreground service handling
Currently, as soon as the playback is considered disengaged (not
ready and playing), the foreground service is stopped.
This causes problems if the app or the user attempts to restart
playback after a short amount of time, where apps may run into
foreground service start restrictions.

Almost all of these short-term interaction issues can be solved
by keeping the foreground service running for an additional
timeout period, which is chosen to be 10 minutes to match the
behavior of future Android system enforcements. For any longer
term interactions, apps need to implement playback resumption
paths that can restart the service with the previous playback.

One caveat is that we currently use player.pause() as a way to
stop the foreground service in onTaskRemoved() if the app wants
to abandon playback at this point. With the timeout, the service
can no longer be stopped immediately just by calling pause(),
so we need to explicitly disable the timeout in the corresponding
helper method.

Issue: androidx/media#1928
Issue: androidx/media#111
PiperOrigin-RevId: 726942625
(cherry picked from commit 8a888d0d1801ce018b5bca5dbab78be44507286e)
2025-02-14 18:41:15 +00:00
tonihei
2a3e733161 Post initial media button preferences update
Controller and browsers are typically obtained with
Futures.addCallback(future, getMainExecutor()), which triggers
the onSuccess callback with a message post. We currently send
the initial media button preferences inline, causing the callback
in MediaController.Listener.onMediaButtonPreferencesChanged to
arrive before the FutureCallback.onSuccess callback.

In the test controller app, which causes crashed when
connecting to existing sessions for example. We can make this
more robust by also posting the initial update of the media button
preferences.

PiperOrigin-RevId: 726923498
(cherry picked from commit d5df227b3ae4d87ec4876cc19ecca814c91c5fd7)
2025-02-14 18:41:15 +00:00
tonihei
22e5f25648 Refine logic to set and interpret ACTION_PAUSE/ACTION_PLAY
We currently set both actions depending on the playWhenReady state
and we require both actions when converting a platform session to
a Media3 session (if ACTION_PLAY_PAUSE isn't set anyway).

This causes problems in two situations:
 - A controller using the platform ACTION_PAUSE/ACTION_PLAY to
   determine which button to show in a UI. This needs to be aligned
   to the existing Util.shouldShowPlayButton we already use when
   setting the PlaybackStateCompat state.
 - A session only setting either ACTION_PAUSE or ACTION_PLAY
   depending on its state. We should check if the action triggered
   by setPlayWhenReady(...) is possible and allow COMMAND_PLAY_PAUSE
   accordingly.

PiperOrigin-RevId: 726916720
(cherry picked from commit 28bfb27fb59ceb0cd209ac1eebd803d31f78afa4)
2025-02-14 18:41:15 +00:00
tonihei
71a2b7d72d Refine conversion logic from platform to Media3 state for STATE_ENDED
When converting to platform states, STATE_ENDED is mapped to
STATE_STOPPED, but the reverse mapping always assumes STATE_STOPPED
means STATE_IDLE at the moment. We can use the same logic we already
apply for STATE_PAUSED to figure out if we are ended.

PiperOrigin-RevId: 726902064
(cherry picked from commit 982403a0ccab31de1f293fb0b8591e2132e11dd2)
2025-02-14 18:41:15 +00:00
tonihei
89b5420bc8 Show notification even in STATE_IDLE
Currently the notification disappears immediately when the player
enters an error or stopped state, but still has its media and
could resume on user request.

This can be fixed by only checking the existence of media and not
the state when deciding to show a notification.

PiperOrigin-RevId: 726901050
(cherry picked from commit f0da364d3fdd8574c8506f0682a7d2ecf8135ad8)
2025-02-14 18:41:15 +00:00
tonihei
dfd2b75720 Stablize command button icons
These are the preferred replacement for custom icon res ids.

PiperOrigin-RevId: 726821168
2025-02-14 01:24:18 -08:00
Googler
f5e583332b Copy the isPlaceholder flag into CurrentMediaItemOnlyTimeline
PiperOrigin-RevId: 726405776
2025-02-13 03:32:08 -08:00
bachinger
813973bb58 Explicitly test custom extra entries for legacy conversions
Issue: androidx/media#2127
PiperOrigin-RevId: 725984132
2025-02-12 03:25:22 -08:00
ibaker
5e6fb88372 Add missing LINT.If/ThenChange comments
PiperOrigin-RevId: 724291745
2025-02-07 04:10:41 -08:00
ibaker
097771306d Remove LINT.IfChange comments with no following LINT.ThenChange
PiperOrigin-RevId: 723918659
2025-02-06 06:58:11 -08:00
tonihei
ddcf455d03 Convert context dependent TrackSelectionParameters to boolean
This avoids that these settings have to be resolved inline,
potentially blocking the main thread. They can be resolved at
the time of track selection on a background thread instead.

As a side effect, we can also remove the context parameter from
the Builder. Having the Context in the Builder is also a bad sign
in the first place because it implies the potentially blocking
calls can happen.

PiperOrigin-RevId: 720523139
2025-01-28 04:26:15 -08:00
bachinger
ec50358784 Do not override pending info when masking
PiperOrigin-RevId: 712919739
2025-01-07 08:26:19 -08:00
siroberts
16dcf91c1c Reduce log severity to info for unhandled media buttons
PiperOrigin-RevId: 707485315
2024-12-18 03:47:58 -08:00
rohks
4b26eb2800 Fix lint errors
- Add required mutability flag to `ConnectionStateTest` to fix
  `Missing PendingIntent mutability flag`.
- Suppress `WrongConstant` lint error caused by Kotlin's spread operator
  not propagating the `@IntDef` annotation.

#cherrypick

PiperOrigin-RevId: 707061163
2024-12-17 05:53:19 -08:00
ibaker
36466cf883 Add some session methods to the stable API
This change adds a new stable overload of
`SessionToken.createSessionToken` which takes the platform `Token` type
(instead of `Parcelable`). It also stabilises
`MediaController.getCustomLayout` & the corresponding listener.

PiperOrigin-RevId: 705917161
2024-12-13 10:03:46 -08:00
tonihei
3bce3af1a3 Switch play FGS exemption to use custom action instead of commands
Custom actions are more naturally associated with a user intent
than commands (that are meant to be used for automated inter-app
communication without user interaction).

PiperOrigin-RevId: 705797057
2024-12-13 01:43:21 -08:00
tonihei
684273e4e1 Add utilities to resolve button preferences to display constraints
PiperOrigin-RevId: 705491402
2024-12-12 07:08:43 -08:00
tonihei
b34a5b4e5d Align logic to ignore disabled and non-custom buttons in custom layout
The legacy custom layout doesn't support disabled buttons or buttons
with non-custom actions. These are currently filtered out, but in
inconsistent ways:
 - MediaControllerImplBase doesn't filter at all.
 - DefaultNotificationProvider filters before converting
   mediaButtonPreferences, PlayerWrapper filters after the conversion.
 - PlayerWrapper doesn't disable buttons that are unavailable.

To ensure it's consistent, we can add these checks to the existing util
method in CommandButton and also make sure PlayerWrapper disables
unavailable buttons before triggering the util method.

This also means we can simplify some of the tests that rely on the
mediaButtonPreference to customLayout conversion in MediaController.

This change also includes two bug fixes in PlayerWrapper that
became evident in the tests: The extras need to be copied to
avoid modifying a Bundle instance that be used elsewhere and
we need to update the custom layout and potentially the session
extras when the available commands change, as they depend on them.

PiperOrigin-RevId: 704678404
2024-12-10 06:13:54 -08:00
tonihei
46ab6cf030 Fix wrong copy-pasted comment
PiperOrigin-RevId: 702647886
2024-12-04 02:38:11 -08:00
tonihei
da4376d48d Avoid modifying legacy custom layout in MediaControllerImplBase
When a session sets a custom layout, we currently convert it to the
media button preferences in MediaControllerImplBase and convert it back
to a custom layout for consumers of MediaController.getCustomLayout.

To avoid unnecessary conversions and potential changes, we can
directly use the provided custom layout to get the same logic as
before. This also means we avoid notifying a change to the custom layout
if only the implicit slots changed that weren't relevant before
introducing them (=we can remove some tests for this behavior).

PiperOrigin-RevId: 701903475
2024-12-02 03:19:51 -08:00
ibaker
77d33645cc Clarify CommandButton javadoc around iconResId and ICON_UNDEFINED
PiperOrigin-RevId: 701898620
2024-12-02 02:59:32 -08:00
bachinger
9ae136becb Add context as a parameter to shouldStartForegroundService
Issue: androidx/media#1887
PiperOrigin-RevId: 697650546
2024-11-18 09:15:46 -08:00
ibaker
286273c10e Enable lint in tests for modules that require no fixes
PiperOrigin-RevId: 693313908
2024-11-05 05:40:03 -08:00
tonihei
12cb803486 Compat logic for MediaController.getCustomLayout
When a new media session sets media button preferences, we need to
"translate" them back to a custom layout to ensure the user preferences
are represented as closely as possible when the controller uses the
old button placement rules.

PiperOrigin-RevId: 693306153
2024-11-05 05:06:47 -08:00
tonihei
9fb4ed91b6 Make getCustomLayoutFromMediaButtonPreferences side-effect free
The method currently modifies the input Bundle, but it's easier to
reason about it if the method is side-effect free and the places
that need to modify a Bundle do this after calling the method.

PiperOrigin-RevId: 693288031
2024-11-05 03:54:40 -08:00
ibaker
6f81b5792b Add 'session extras' and MediaMetadata.durationMs to the stable API
PiperOrigin-RevId: 693279664
2024-11-05 03:17:06 -08:00
ibaker
af1c13524c Enable lint in lib-common tests
Also move the `lint.xml` config which disables the `NewApi` check from
`lib-session` to the existing top-level file, and limit it to cover all
Robolectric tests by path matching.

Follow-up to 76db936d68

PiperOrigin-RevId: 692913646
2024-11-04 04:16:39 -08:00
tonihei
a15571c8ee Add logic to convert button preferences to custom layout in sessions
This ensures that media button preferences with slots for BACK,
FORWARD and OVERFLOW are converted to the legacy custom layout
according to the implicit placement rules. This has to happen
when populating the MediaSessionCompat and when generating the
notification for older APIs. Sending these preferences to older
Media3 controllers can filter them down to the custom layout the
session would have used previously, but there is no need to adjust
the reservation extras because the older Media3 custom layout has
no concept of these extras.

PiperOrigin-RevId: 689761637
2024-10-25 06:09:21 -07:00
tonihei
8811b454bb Use slots to fill in default compact view indices
This is equivalent to the current logic, but also allows to easily pick
up other buttons that use the BACK, CENTRAL or FORWARD slots for the
compact view by default.

Also fix the test to actually assert the output order.

PiperOrigin-RevId: 689747844
2024-10-25 05:12:07 -07:00
tonihei
aeb9d12a16 Add logic to convert custom layout to button preferences in controller
This ensures that the slots are set according to the implicit placement
rules for the legacy custom layouts. This has to be done when receiving
custom actions from a MediaControllerCompat and for Media3 sessions
setting the custom layout field.

PiperOrigin-RevId: 689737951
2024-10-25 04:29:05 -07:00
tonihei
21526588be Removed unused constructor
PiperOrigin-RevId: 688960856
2024-10-23 07:50:31 -07:00
tonihei
e43720b83a Remove deprecated MediaSession.getSessionCompatToken
And also the deprecated override of
ControllerInfo.createTestOnlyControllerInfo using the
androidx.media classes.

PiperOrigin-RevId: 688625484
2024-10-22 11:30:28 -07:00
tonihei
d9ca3c734a Ensure session extras Bundle is copied at least once
If not copied, the extras Bundle can be accidentally changed by the
app if it modifies the instance passed into MediaSession. On the flip
side, the Bundle should be modifiable once created so that the session
can amend the extras if needed without crashing.

PiperOrigin-RevId: 688485963
2024-10-22 04:06:06 -07:00
tonihei
5fffe03312 Remove unneeded @Nullable from PlayerWrapper.legacyExtras
All values passed in via the constructor or setLegacyExtras
are guaranteed to be non-null.

PiperOrigin-RevId: 687245475
2024-10-18 04:04:32 -07:00
bachinger
3e556a4b53 Accept resource URIs for command buttons
PiperOrigin-RevId: 687239119
2024-10-18 03:35:50 -07:00
bachinger
08e6f30b68 Look for METADATA_KEY_ART_URI for legacy media items
PiperOrigin-RevId: 687222830
2024-10-18 02:26:47 -07:00
tonihei
627b7a3e56 Add media button preferences
This adds the API surface for media button preferences in MediaSession
and MediaController. It closely mimics the existing custom layout
infrastructure (which it will replace eventually).

Compat logic:
 - Session:
     - When converting to platform custom actions, prefer to use
       media button preferences if both are set.
     - When connecting to an older Media3 controller, send the
       media button preferences as custom layout instead.
 - Controller:
     - Maintain a single resolved media button preferences field.
     - For Media3 controller receiving both values, prefer media
       button preferences over custom layouts.

Missing functionality:
 - The conversion from/to custom layout and platform custom actions
   does not take the slot preferences into account yet.

PiperOrigin-RevId: 686950100
2024-10-17 09:54:36 -07:00