-
Notifications
You must be signed in to change notification settings - Fork 406
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support media resumption with session demo app #27
Comments
I understand that media resumption is still using a legacy This corresponds to the documentation of If this is the case I would expect that such a legacy call needs to be delegated to the Media3 Can you clarify whether you found that |
The Media Resumption Steps (source)
Note: At this point, the system shows the static placeholder notification with the play button. If the user taps on the play button:
At first I thought that the System play call (step 7) would include the After reading the documentation and the UAMP implementation (I've checked the
Sorry if it's still not clear. My main doubt was to know where should I retrieve and prepare the |
Thanks for the detailed answer! I think best is to add media resumption to the session demo app (see docs). I renamed the issue accordingly and it's kept as an I can confirm the protocol you are describing with the 3 calls from systemui to the service and the
The first two calls to the service are used to created the notification. The service is not started by intent, and when systemui unbinds the service is stopped. Then after potentially a longer duration the user finds the notification and taps play. Now systemui calls the service again to start the service and call The service can recognize that systemui is the caller by its package name and also gets the |
I can't get this working at all and the notification is always unresponsive on Android 13. Pocketcasts added a workaround here: Maybe that's a fix that can / should be incorporated into media3? @marcbaechinger @ashiagr did you investigate into a proper solution after your temporary fix? |
I'm not sure how this is related. As far as I can see the code there is using the legacy I meant this works with UAMP in the media3 branch after I implemented it. I can test again when I have some cycles. |
Hey @PaulWoitaschek 👋 I wasn't able to investigate a proper solution yet. I upgraded ExoPlayer media library to 2.18.2 which corresponds to Media3 1.0.0-beta03 release but it didn't help. As a workaround, I used legacy flags with |
You are correct @marcbaechinger , with uamp it's working. I'll investigate more, thanks 🙏 If someone else is trying this out, here is a patch to update to rc1 |
I found the issue! And possibly also a better fix for pocket casts. The main difference between my implementation and the one of the media3 uamp branch is that I don't have that This was causing a dead notification which is hanging. When clicking on the play button in logcat you can see a To reproduce it, remove these lines in releaseMediaSession()
stopSelf()
Additionally a fix for pocketcasts might be to call |
Already tried this @PaulWoitaschek, it doesn't seem to be working for us. Thanks so much for taking a look. 🙏 @marcbaechinger, is there anything you can suggest or things we should be checking to make it work with ExoPlayer's latest version? |
The dead notification is a know issue I think that is caused by the app not stopping the service properly. See this comment also.
Disclaimer: I see that the commit in I'm not sure if calling A controller that is still connected to the session is such a case. In the cases when the controller has connected by using a service connection (which is probably the most common case when building a controller with a token that points to the service component), the controller has bound to the service. Calling When I put log statements in some of the involved components in the session demo app, I see that |
I can confirm that! Initially when studying the UAMP implementation I also thought: What is @marcbaechinger doing there, that's totally unnecessary to tear down the stuff there because onDestroy will be called anyways. |
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: #167 Issue: #27 Issue: #314 PiperOrigin-RevId: 523638051
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`. Issue: #167 Issue: #27 Issue: #314 PiperOrigin-RevId: 523638051 (cherry picked from commit e54a934)
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: #355 Issue: #167 Issue: #27 See https://developer.android.com/guide/topics/media/media-controls#mediabrowserservice_implementation PiperOrigin-RevId: 527276529
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: #355 Issue: #167 Issue: #27 See https://developer.android.com/guide/topics/media/media-controls#mediabrowserservice_implementation PiperOrigin-RevId: 527276529 (cherry picked from commit 7938978)
Hello, I'm currently using a MediaLibraryService and implementing MediaResumption based on the provided documentation Uamp and this topic. However, I'm encountering two issues. Here are some notes related to the implementation:
Issue 1: I expected this behaviour to occur when the notification is triggered. It's puzzling because the MediaItems we're returning will quickly become outdated as we load other items. Issue 2: However, after rebooting the device and selecting play on the Notification, I receive the following error: Reason: Context.startForegroundService() did not then call Service.startForeground():. It's worth mentioning that I'm not using a Bluetooth controller and I'm testing this on a Pixel 3 device running Android 12. |
This documentation is outdated I'm afraid. Please accept my apologies. Unfortunately, we haven't yet published the documentation for the Media3 session module. And yes, I share your sentiments about this :( . We will take action now to improve this situation (see docs below).
That's how it works. This needs to be understood as that System UI asking your app whether you support playback resumption. It will call you once when you post the first notification and you can return whatever you want but it must be at least one playable item (see here). You only need to return real data during boot time. Please see the documentation below. With Media3 you don't have to implement this library service workflow with System UI yourself. Media3 is doing this for you. You only have to follow the two steps below.
The system knows the last item that you played because this is reflected in the session at the time the last notification was posted by the app. No need to call you again - all information available.
Indeed.
With 1.1.0-rc01 you can override
We wrote a page about this. I'm not sure whether this ends up on DAC in this form, but it may be helpful for you. The following is possible with 1.1.0-rc01. Please file bugs if not: Playback resumption with Media3When using a MediaSession, a media app is advertising media playback to the operating system. Being aware of media playback this way, Android can provide means for a user to resume playback after an app has terminated and even after the device has been restarted. Media3 provides a Add the MediaButtonReceiverTo opt-in to playback resumption, add the Media3 <receiver android:name="androidx.media3.session.MediaButtonReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver> Implement Callback.onPlaybackResumption()Then implement When playback resumption is requested by either a Bluetooth device, or the System UI playback resumption notification, Media3 calls override fun onPlaybackResumption(
mediaSession: MediaSession,
controller: ControllerInfo
): ListenableFuture<MediaItemsWithStartPosition> {
val settable = SettableFuture.create<MediaItemsWithStartPosition>()
scope.launch { settable.set(restorePlaylist()) }
return settable
} An app should aim for completing the In case you've stored further parameters like the playback speed, the repeat mode or whether the shuffle mode is enabled, Playback resumption turned off by defaultBy default, meaning when no In this mode, the session receives media button events only while the session is alive and the user can't resume playback once your session is released. |
I can't get this to working at all. Here I've implemented what I think should be correct: Tested on Pixel 7, Android 33 Scenario A: Media Button
Scenario B: Notification
|
Thank you for your quick and clear response! I appreciate it. I will definitely try using the If that's the case, I would be delighted to implement it, as I believe it would greatly enhance the code's readability and descriptiveness. |
Yes. Media3 is doing a root media item for recent items for you under the hood. When you have added the When System UI calls
Yes, this is the intention. The intention is that an app for supporting playback resumption with Media3 has to do two things:
All implementation details are done by Media3. If this is not the case, please file a bug. I will implement this in UAMP as soon as possible. I hope I can provide this very soon as part of the media3 branch of UAMP or a PR of this branch. |
Do you think I have a flaw in my implementation or do you think there is a bug in media3? |
I think what you have added to the Manifest differs from the snippet above. I have just tested the new approach with |
Yeah, not sure, sorry. Can you try to remove <action android:name="android.intent.action.MEDIA_BUTTON" /> from the service and have it only in the receiver? That's a long shot though. For Media3 it shouldn't make a difference, just to remove to be sure no one else sees this. The other actions your having declared for the service are sufficient for the Media3 library to see your service. Can you when the app is running and after terminating the app, do
? If the
Without a receiver in the manifest Media3 registers the service as the receiver during the life-time of the session, but clears it out after release. In this case the the Last MediaButtonReceiver is a foreground service intent
when the app is terminated this is cleared out, because playback resumption is not supported. The |
Ha, we found out in the very same moment that it's related to play pause (85). |
Thanks for the stack trace! From that it looks like it doesn't go the expected code path. But I think it goes a path that Media3 should avoid when playback resumption is requested by Bluetooth with a Thanks for reporting! I filed #493 If you want to verify, you can copy the Then add the following on line 140, the last line of the if branch
If you now use your own |
I tried that but it doesn't have an effect. I also tried to copy more infos from the KeyEvent. I am actually not sure if this is a real world issue of if this is not just an artificial scenarios that only might be seen by users who use stuff like tasker etc. |
Small question corresponding the new implementation. Is there a way to optionally allow media resumption? Lets say i only want to support and show the Notification if the user is logged in. Previously we where able to do this in |
Try to subclass the receiver and only conditionally call super.onReceive |
The receiver is only called when playback resumption is requested by a Bluetooth head set. The System UI resumption notification is starting the service directly.
This is currently not possible I'm afraid. Would you want to allow BT resumption but not the notification or wouldn't this be to be separated? |
I don't think we need it to be separated. In our use case it would be pretty straight forward, we just want to allow our users to play our content while logged in with a valid account. Meaning that if we store a list of media items to resume before a user logs out we don't allow him to continue playing afterwards. We could just fix it with what we return in onPlaybackResumption, but the prettiest solution would be to hide the media notification :) . |
For my use case, I also need to prevent playback resumption conditionally. For instance if the user removed his audiobook, there is nothing I can play when a user requests resumption. |
Once you tell System UI you want a notification when playback starts, you can't get back and change that decision. System UI doesn't give you a second chance. This would work for BT resumption but as far as I can tell not with the System UI notification. How did you implement this with the old API and the System UI notification? |
I don't think I handled it correctly 🙈 The BT resumption dismissal could be fixed by subclassing the receiver and intercepting onReceives already right now if I understand correctly. |
Technically, yes. But then you tell the system that you want to do playback resumption and then when you get it you don't. That's not good citizenship, because the system may want to give the playback resumption rights to something else if your app does not request that. If not yet, then you may break a smart system strategy the system has in the future, to benefit the user. If you say you do playback resumption you should do playback resumption. I think we need to provide something in the API but it's not clear what this is given the APIs we are having:
|
just in case someone has the same issue (or maybe i just haven't read documentation properly), while bluetooth resumption works, for systemui to show resumption music player, you need to use MediaLibraryService and NOT MediaSessionService |
Hi! I would like to permanently disable SystemUI-based playback resumption but still want to respond to media button events (e.g. bluetooth headsets). Apparently, the new SystemUI media resumption feature is confusing to some users. I took a peak at your linked code and media3 is indeed automatically enabling playback resumption if the media button receiver is added: media/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java Lines 146 to 165 in 3f36610
media/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java Lines 223 to 240 in 3f36610
I attempted to disable the connection requests from It looks like Possible solution: Can we rewrite |
It's currently not possible to disable the playback resumption notification when requesting playback resumption by adding the receiver to the manifest. You get all or nothing with the current version.
That would technically work, but I think it isn't a good idea to conflate the receiver with the resumption notification some further, because they actually are two separate things. The receiver is actually not involved when System UI requests playback resumption with the notification; only BT is using the receiver. I think it's valuable for apps to be able to switch off the notification though, so we should think about how to do this. There are various options, we can think about. |
To support the "Media Resumption" feature introduced in Android 11, we need to "implement a MediaSession callback for onPlay()".
With media3, the
MediaLibrarySessionCallback
is replacingonPrepareFromMediaId
,onPrepareFromSearch
,onPrepareFromUri
,onPlayFromMediaId
,onPlayFromSearch
,onPlayFromUri
withonSetMediaUri
, but the simpleonPlay
with no input is not included and I can't find an easy way to implement it.Edit: Is not included in the documentation, but the "Media Resumption" feature is also calling onPrepare() and, after reading through
MediaSessionLegacyStub
, I was able to "fake" an onPrepare() by catchingCOMMAND_PREPARE
(only dispatched inside onPrepare with no input). I couldn't do the same to fake theonPlay
since it uses theCOMMAND_PLAY_PAUSE
and that command could meanonPlay
oronPause
, but the onPrepare replacement seems to be enough to support the feature for now.The text was updated successfully, but these errors were encountered: