Skip to content
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

Fix playback continuation on external controller command involving search (through the onPlayFromSearch) #2280

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

ASGusev
Copy link

@ASGusev ASGusev commented Jan 27, 2024

In #2215 I have mentioned a limitation of external controller support and described a possible solution. So, here I propose some changes to widen the support for external controllers.

This enables external controllers other than android auto
to resume the playback
After an external playback start, app launch and exit the session can
be left in the released state until process finish
@PaulWoitaschek
Copy link
Owner

Hmm, I'm not sure if this is the correct solution. The code is only a weird workaround. Usually connecting to a media controller should not prepare anything. Instead, the client connecting to a session should issue preparing of the corresponding media.

@ASGusev
Copy link
Author

ASGusev commented Feb 3, 2024

I am considering the scenario when the controller connects and issues a play command. Most apps continue to play the media they played last, Voice now picks a book from those which have not been started yet. Do you mean that there should be an extra action between the connection and the play command initiating loading the playback state? Or should it be loaded on service start?

@PaulWoitaschek
Copy link
Owner

I'm hesitant on this change; especially around re-injecting the service. I think this is not how playback resumption is mean to work with media3.

I've raised a question here: androidx/media#1064

Could you add the details from your original discussion to this issue so the information is less spread and media3 developers have an easier time?

Instead of loading the playback position on any external controller
connection it is now loaded on service creation
@ASGusev
Copy link
Author

ASGusev commented Feb 4, 2024

I have moved the position loading from onConnect to the PlaybackService's onCreate so that the service always has the position ready

@PaulWoitaschek
Copy link
Owner

I would assume that this would lead to a permanent notification which has been the case in the past.
Can you check that? (Ie there were a lot of users complaining that Voice is always there, even though the user did not launch it. )

@ASGusev
Copy link
Author

ASGusev commented Feb 4, 2024

I see no extra notifications on my phone

@ASGusev
Copy link
Author

ASGusev commented Feb 4, 2024

Well, now I see some notifications but not permanent: they sometimes appear on app launch (is is a problem?) and swiping it away from the recent app list (trying to figure out why it happens and how to filter it out).
Do you think it can be fine to check the player state before setting playWhenReady = true in VoicePlayer.play and load position if the state is idle? Or to load the position on service start and to prepare the player if it is asked to play from idle state?

@PaulWoitaschek
Copy link
Owner

Can you check Marcs answer? From what he explained it looks like this change should not be necessary and another controller should just connect and then call play()

Legacy controllers have onPlayFromSearch method that is called for play
button and expected to play something meaningful. For media3 sessions it
delegates to getSearchResult.
In this app search was already returning the last position for calls
with null as query, but in the mentioned scenario it is called with an
empty string, so, instead of manually loading the last book played it
is now returned for empty query searches
@ASGusev
Copy link
Author

ASGusev commented Feb 5, 2024

Yes, I have explored what's happening again and see another solution. SimpleWear, with which I face the issue, uses legacy controller's onPlayFromSearch method without query for which, according to android reference the player app is expected to make "a smart choice about what to play". By the media3 session this call gets delegated to getSearchResults. In Voice it is processed by BookSearchHandler.searchUnstructured. This method already returns the last played book when called with null as query, but during onPlayFromSearch processing it receives an empty string and returns an arbitrary book. So, now I suggest adjusting the check in searchUnstructured so that it also returns the last book when called with an empty string.

@@ -44,7 +44,7 @@ class BookSearchHandler

// Look for anything that might match the query
private suspend fun searchUnstructured(query: String?): Book? {
if (query != null) {
if (query != null && query != "") {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: you can use isNullOrBlank

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched

@PaulWoitaschek
Copy link
Owner

Very cool!

Could you merge main and update the PR title to describe what it fixed?

@ASGusev ASGusev changed the title Playback continuation from external controller Fix playback continuation on external controller command involving search (through the onPlayFromSearch) Feb 10, 2024
@ASGusev
Copy link
Author

ASGusev commented Feb 10, 2024

Will it do?

}
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession {
if (session.invokeIsReleased)
rootComponentAs<PlaybackComponent.Provider>()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And are you sure this is necessary? I'm hesitant to merge this because this will literally create new instances of the whole player stuff - while not releasing the old things.
I fear that this might lead to several players playing at the same time under some circumstances

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new session is only created in a situation when the service is not destroyed after a release call. I face this situation when I run Voice from SimpleWear and then close it. Now in such a situation playback cannot be started even by the play button in the player activity (or the play button in the books list). I think this behaviour is not more desirable.
By the way, in the comment in onTaskRemoved you have written that releasing the session is required to prevent showing a dead notification. Later, in a comment to my previous PR, you've mentioned having fixed the notification being unresponsive. So, is the dead notification still an issue, and is the release call in onTaskRemoved still required? It seems to me that only calling release from onDestroy would obviate the sole need to check if the session state.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And which conditions do you mean? Is that onGetSession being called from another thread than onCreate that does not see the session created there?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I don't quite understand what you mean by 'not releasing the old stuff' - a new session is only created if the old one has already been released.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants