mirror of
https://github.com/AntennaPod/AntennaPod.git
synced 2026-08-18 02:53:10 +00:00
Fix android auto voice commands in media3 playback service (#8605)
### Description Fix android auto voice commands in media3 playback service. I hope this solves the Google Play rejection of new beta versions. ### Checklist <!-- To help us keep the issue tracker clean and work as efficient as possible, please make sure that you have done all of the following. You can tick the boxes below by placing an x inside the brackets like this: [x] --> - [x] I have read the contribution guidelines: https://github.com/AntennaPod/AntennaPod/blob/develop/CONTRIBUTING.md#submit-a-pull-request - [x] I have performed a self-review of my code, going through my changes line by line and carefully considering why this line change is necessary - [x] I have run the automated code checks using `./gradlew checkstyle lint` - [x] My code follows the style guidelines of the AntennaPod project: https://antennapod.org/contribute/develop/app/code-style - [x] I have mentioned the corresponding issue and the relevant keyword (e.g., "Closes: #xy") in the description (see https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) - [x] If it is a core feature, I have added automated tests
This commit is contained in:
committed by
GitHub
parent
bf5dcb96b0
commit
f267812d29
@ -40,6 +40,7 @@ import de.danoeh.antennapod.storage.preferences.PlaybackPreferences;
|
||||
import de.danoeh.antennapod.storage.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import io.reactivex.rxjava3.core.Maybe;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
@ -259,6 +260,38 @@ public class MediaLibrarySessionCallback implements MediaLibraryService.MediaLib
|
||||
return Futures.immediateFuture(new MediaSession.MediaItemsWithStartPosition(
|
||||
mediaItems, index, startPositionMs));
|
||||
}
|
||||
String searchQuery = mediaItems.get(index).requestMetadata.searchQuery;
|
||||
if (searchQuery != null) {
|
||||
if ("".equals(searchQuery)) {
|
||||
return onPlaybackResumption(mediaSession, controller); // "Play something" voice action
|
||||
}
|
||||
SettableFuture<MediaSession.MediaItemsWithStartPosition> future = SettableFuture.create();
|
||||
Maybe.fromCallable(() -> {
|
||||
List<FeedItem> results = DBReader.searchFeedItems(0, searchQuery, FeedItemFilter.unfiltered());
|
||||
for (FeedItem result : results) {
|
||||
if (result.getMedia() != null) {
|
||||
return result.getMedia();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(media -> {
|
||||
long startPosition = SkipUtils.skipIntroIfNecessary(context, media);
|
||||
startPosition = RewindAfterPauseUtils.calculatePositionWithRewind(
|
||||
(int) startPosition, media.getLastPlayedTimeStatistics());
|
||||
future.set(new MediaSession.MediaItemsWithStartPosition(
|
||||
Collections.singletonList(MediaItemAdapter.fromPlayable(context, media, false)),
|
||||
0, startPosition));
|
||||
}, error -> {
|
||||
Log.e(TAG, "Voice search failed", error);
|
||||
future.set(new MediaSession.MediaItemsWithStartPosition(
|
||||
Collections.emptyList(), index, startPositionMs));
|
||||
},
|
||||
() -> future.set(new MediaSession.MediaItemsWithStartPosition(
|
||||
Collections.emptyList(), index, startPositionMs)));
|
||||
return future;
|
||||
}
|
||||
SettableFuture<MediaSession.MediaItemsWithStartPosition> future = SettableFuture.create();
|
||||
Single.fromCallable(
|
||||
() -> {
|
||||
@ -473,6 +506,18 @@ public class MediaLibrarySessionCallback implements MediaLibraryService.MediaLib
|
||||
public ListenableFuture<LibraryResult<Void>> onSearch(@NonNull MediaLibraryService.MediaLibrarySession session,
|
||||
@NonNull MediaSession.ControllerInfo browser, @NonNull String query,
|
||||
@Nullable MediaLibraryService.LibraryParams params) {
|
||||
if (query.isEmpty()) {
|
||||
session.notifySearchResultChanged(browser, query, 0, params);
|
||||
return Futures.immediateFuture(LibraryResult.ofVoid());
|
||||
}
|
||||
Single.fromCallable(() -> DBReader.searchFeedItems(0, query, FeedItemFilter.unfiltered()))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(
|
||||
items -> session.notifySearchResultChanged(browser, query, items.size(), params),
|
||||
error -> {
|
||||
Log.e(TAG, "Search failed", error);
|
||||
session.notifySearchResultChanged(browser, query, 0, params);
|
||||
});
|
||||
return Futures.immediateFuture(LibraryResult.ofVoid());
|
||||
}
|
||||
|
||||
|
||||
@ -74,6 +74,36 @@ public class MediaLibrarySessionCallbackTest {
|
||||
assertEquals(String.valueOf(media.getId()), result.mediaItems.get(0).mediaId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAndroidAutoVoiceSearchQuery() throws Exception {
|
||||
long mediaId = seedEpisode().getId();
|
||||
MediaItem searchItem = MediaItem.EMPTY.buildUpon()
|
||||
.setRequestMetadata(new MediaItem.RequestMetadata.Builder().setSearchQuery(EPISODE_TITLE).build())
|
||||
.build();
|
||||
MediaSession.MediaItemsWithStartPosition result = callback.onSetMediaItems(session, controllerInfo,
|
||||
Collections.singletonList(searchItem), C.INDEX_UNSET, C.TIME_UNSET).get(5, TimeUnit.SECONDS);
|
||||
assertEquals(1, result.mediaItems.size());
|
||||
assertEquals(String.valueOf(mediaId), result.mediaItems.get(0).mediaId);
|
||||
|
||||
// No match: nothing to play
|
||||
searchItem = MediaItem.EMPTY.buildUpon()
|
||||
.setRequestMetadata(new MediaItem.RequestMetadata.Builder().setSearchQuery("Unrelated").build())
|
||||
.build();
|
||||
result = callback.onSetMediaItems(session, controllerInfo,
|
||||
Collections.singletonList(searchItem), C.INDEX_UNSET, C.TIME_UNSET).get(5, TimeUnit.SECONDS);
|
||||
assertEquals(0, result.mediaItems.size());
|
||||
|
||||
// Empty query ("play something"): fall back to playing something rather than nothing, per
|
||||
// Android Auto/Assistant voice action guidelines.
|
||||
searchItem = MediaItem.EMPTY.buildUpon()
|
||||
.setRequestMetadata(new MediaItem.RequestMetadata.Builder().setSearchQuery("").build())
|
||||
.build();
|
||||
result = callback.onSetMediaItems(session, controllerInfo,
|
||||
Collections.singletonList(searchItem), C.INDEX_UNSET, C.TIME_UNSET).get(5, TimeUnit.SECONDS);
|
||||
assertEquals(1, result.mediaItems.size());
|
||||
assertEquals(String.valueOf(mediaId), result.mediaItems.get(0).mediaId);
|
||||
}
|
||||
|
||||
private FeedMedia seedEpisode() {
|
||||
Feed feed = new Feed("url", null, null);
|
||||
feed.setItems(new ArrayList<>());
|
||||
|
||||
Reference in New Issue
Block a user