audio_service icon indicating copy to clipboard operation
audio_service copied to clipboard

Fixed playFromSearch in audio_service.dart

Open Ruchit2759 opened this issue 2 years ago • 2 comments

While developing the play from search functionality in my app, I discovered a bug. Actually, I'd like Google Assistant support for my app because it's compatible with Android Auto. So the playFromSearch method returns the Google assistance query. But I also want extras from the method, but I'm getting a null value from extras. So I resolved that problem and now it is giving me the extras.

Actual Behaviour: Getting null value from extras in playFromSearch method.

Expected Behaviour: In the playFromSearch method, extras should contain value as a map.

Ruchit2759 avatar May 01 '23 05:05 Ruchit2759

Thanks for addressing the formatting changes. And I can see clearly now what your PR does. That's actually quite straightforward. I will try to get to this over the weekend.

ryanheise avatar Jul 21 '23 09:07 ryanheise

Thanks for addressing the formatting changes. And I can see clearly now what your PR does. That's actually quite straightforward. I will try to get to this over the weekend.

Hello @ryanheise, Friendly reminder regarding my PR. Your review and merge would be much appreciated. Thanks!"

Ruchit2759 avatar Aug 09 '23 12:08 Ruchit2759

Another friendly bump :) We're trying to get Finamp set up for Android Auto support and can already play songs via voice search, but it would be nice if we could access the extras to play other media items too :D

Chaphasilor avatar Feb 06 '24 09:02 Chaphasilor

I have just merged this, and noticed that the extras parameter was also missing from a number of similar callbacks which I've fixed.

To ensure nothing breaks in your apps, please test the minor branch and let me know if it works before I roll out the next release.

ryanheise avatar Feb 06 '24 14:02 ryanheise

I have just merged this, and noticed that the extras parameter was also missing from a number of similar callbacks which I've fixed.

To ensure nothing breaks in your apps, please test the minor branch and let me know if it works before I roll out the next release.

Thanks for merging my PR. I will acknowledge you regarding the minor branch in short time.

Ruchit2759 avatar Feb 06 '24 18:02 Ruchit2759

@ryanheise extras are being passed properly for playFromSearch now. However, I'm unsure about

Future<List<MediaItem>> search(String query, [Map<String, dynamic>? extras])

because I'm getting an empty map as the extras. The method is used for regular keyboard search and also to let the user select from alternative search results after a voice search. The voice search I did yielded extras with the title and artist set in the playFromSearch() method, but when I clicked on the "Search Results" button afterwards, the search() method got called the empty map for extras. It could simply be that Google forgets the extras related to the voice search (which would be sad, because they do add some useful metadata), or that audio_service is not properly forwarding the data.

playFromSearch:
query: Dark Side Alan Walker ; extras: {android.intent.extra.artist: Alan Walker, INTENT_EXTRA_INTENT_PKG_SENDER: aap/com.google.android.googlequicksearchbox, query: Dark Side Alan Walker, android.intent.extra.title: Darkside, android.intent.extra.album: The Dome Vol.87, android.intent.extra.REFERRER_NAME: android-app://com.google.android.googlequicksearchbox/https/www.google.com, opa_allow_launch_intent_on_lockscreen: true, android.intent.extra.focus: vnd.android.cursor.item/audio, android.intent.extra.START_PLAYBACK: true, com.google.android.projection.gearhead.ignore_original_pkg: false}

search:
query: Dark Side Alan Walker ; extras: {}
This is the button I'm referring to (top right)

image

Chaphasilor avatar Feb 07 '24 20:02 Chaphasilor

It's possible this is Android's behaviour. Can you spot anything wrong with the Java implementation?

In AudioServicePlugin.java:

    @Override
    public void onSearch(String query, Bundle extras, Result<List<MediaBrowserCompat.MediaItem>> result) {
        if (listener == null) {
            result.sendResult(new ArrayList<>());
            return;
        }
        listener.onSearch(query, extras, result);
    }

In AudioService.java:

        @Override
        public void onSearch(String query, Bundle extras, final MediaBrowserServiceCompat.Result<List<MediaBrowserCompat.MediaItem>> result) {
            if (audioHandlerInterface != null) {
                Map<String, Object> args = new HashMap<>();
                args.put("query", query);
                args.put("extras", bundleToMap(extras));
                audioHandlerInterface.invokeMethod("search", args, new MethodChannel.Result() {
                    @Override
                    public void error(String errorCode, String errorMessage, Object errorDetails) {
                        result.sendError(new Bundle());
                    }

                    @Override
                    public void notImplemented() {
                        result.sendError(new Bundle());
                    }

                    @Override
                    public void success(Object obj) {
                        Map<?, ?> response = (Map<?, ?>)obj;
                        @SuppressWarnings("unchecked") List<Map<?, ?>> rawMediaItems = (List<Map<?, ?>>)response.get("mediaItems");
                        List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
                        for (Map<?, ?> rawMediaItem : rawMediaItems) {
                            mediaItems.add(rawToMediaItem(rawMediaItem));
                        }
                        result.sendResult(mediaItems);
                    }
                });
            }
            result.detach();
        }

In method_channel_audio_service.dart:

      case 'search':
        return (await callbacks.search(SearchRequest(
                query: call.arguments['query'] as String,
                extras: _castMap(
                    call.arguments['extras'] as Map<dynamic, dynamic>?))))
            .toMap();

It seems the extras are passed through as-is from Android.

ryanheise avatar Feb 08 '24 01:02 ryanheise

Yeah, it seems like the implementation is fine. I also didn't manage to find any information about what kind of extras are potentially passed to onSearch. It sucks because it breaks our search (if media items are detected by Assistant, both the title and artist are provided as a query, with no way to tell where the title ends and the artist name starts, and our search can't handle searching for title and artist in a single query), but it seems like there's no way to fix that. If anyone does know a way to get that information, I would appreciate a response here :)
Thanks for taking the time to look into it Ryan!

Chaphasilor avatar Feb 09 '24 14:02 Chaphasilor

I will probably release within the next day.

@Ruchit2759 did you have a chance to test the minor branch?

ryanheise avatar Feb 13 '24 14:02 ryanheise

I will probably release within the next day.

@Ruchit2759 did you have a chance to test the minor branch?

HI @ryanheise Due to a busy schedule, I was unable to test a minor branch. I'll give it a test shortly.

Ruchit2759 avatar Feb 14 '24 18:02 Ruchit2759