Skip to content

Commit c594e3d

Browse files
committed
Media browser interface to show playlists on Android Auto
1 parent a3e9df2 commit c594e3d

File tree

1 file changed

+160
-5
lines changed

1 file changed

+160
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,106 @@
11
package org.schabi.newpipe.player.mediabrowser;
22

3+
import android.net.Uri;
34
import android.os.Bundle;
4-
import android.support.v4.media.MediaBrowserCompat;
5+
import android.support.v4.media.MediaBrowserCompat.MediaItem;
6+
import android.support.v4.media.MediaDescriptionCompat;
7+
import android.support.v4.media.session.MediaSessionCompat;
58
import android.util.Log;
69

710
import androidx.annotation.NonNull;
811
import androidx.annotation.Nullable;
912
import androidx.media.MediaBrowserServiceCompat;
13+
import androidx.media.MediaBrowserServiceCompat.Result;
14+
import androidx.media.utils.MediaConstants;
1015

16+
import org.schabi.newpipe.NewPipeDatabase;
17+
import org.schabi.newpipe.R;
18+
import org.schabi.newpipe.database.AppDatabase;
19+
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
20+
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
21+
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
1122
import org.schabi.newpipe.player.PlayerService;
23+
import org.schabi.newpipe.player.playqueue.PlayQueue;
24+
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
25+
import org.schabi.newpipe.util.NavigationHelper;
1226

1327
import java.util.ArrayList;
1428
import java.util.List;
29+
import java.util.regex.Matcher;
30+
import java.util.regex.Pattern;
31+
import java.util.stream.Collectors;
1532

16-
public class MediaBrowserConnector {
33+
public class MediaBrowserConnector extends MediaSessionCompat.Callback {
1734
private static final String TAG = MediaBrowserConnector.class.getSimpleName();
1835

1936
private final PlayerService playerService;
37+
private AppDatabase database;
38+
private LocalPlaylistManager localPlaylistManager;
2039

2140
public MediaBrowserConnector(@NonNull final PlayerService playerService) {
2241
this.playerService = playerService;
2342
playerService.setSessionToken(playerService.getMediaSession().getSessionToken());
43+
playerService.getMediaSession().setCallback(this);
2444
}
2545

2646
@NonNull
2747
private static final String MY_MEDIA_ROOT_ID = "media_root_id";
48+
@NonNull
49+
private static final String MY_MEDIA_BOOKMARKS_ID = "media_playlists";
50+
@NonNull
51+
private static final String MY_MEDIA_HISTORY_ID = "media_history";
52+
@NonNull
53+
private static final String MY_MEDIA_BOOKMARKS_PREFIX = "media_playlist_";
54+
@NonNull
55+
private static final String MY_MEDIA_ITEM_PREFIX = "media_item_";
56+
57+
private MediaItem createRootMediaItem(final String mediaId, final String folderName) {
58+
final var builder = new MediaDescriptionCompat.Builder();
59+
builder.setMediaId(mediaId);
60+
builder.setTitle(folderName);
61+
62+
final var extras = new Bundle();
63+
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
64+
"NewPipe");
65+
builder.setExtras(extras);
66+
return new MediaItem(builder.build(), MediaItem.FLAG_BROWSABLE);
67+
}
68+
69+
private MediaItem createPlaylistMediaItem(final PlaylistMetadataEntry playlist) {
70+
final var builder = new MediaDescriptionCompat.Builder();
71+
builder.setMediaId(createMediaIdForPlaylist(playlist.uid))
72+
.setTitle(playlist.name)
73+
.setIconUri(Uri.parse(playlist.thumbnailUrl));
74+
75+
final var extras = new Bundle();
76+
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
77+
playerService.getResources().getString(R.string.tab_bookmarks));
78+
builder.setExtras(extras);
79+
return new MediaItem(builder.build(), MediaItem.FLAG_BROWSABLE);
80+
}
81+
82+
private String createMediaIdForPlaylist(final long playlistId) {
83+
return MY_MEDIA_BOOKMARKS_PREFIX + playlistId;
84+
}
85+
86+
private MediaItem createPlaylistStreamMediaItem(final PlaylistMetadataEntry playlist,
87+
final PlaylistStreamEntry item,
88+
final int index) {
89+
final var builder = new MediaDescriptionCompat.Builder();
90+
builder.setMediaId(createMediaIdForItem(playlist.uid, index))
91+
.setTitle(item.getStreamEntity().getTitle())
92+
.setIconUri(Uri.parse(item.getStreamEntity().getThumbnailUrl()));
93+
94+
return new MediaItem(builder.build(), MediaItem.FLAG_PLAYABLE);
95+
}
96+
97+
private String createMediaIdForItem(final long playlistId, final int index) {
98+
return MY_MEDIA_ITEM_PREFIX + playlistId + "_" + index;
99+
}
28100

29101
@Nullable
30-
public MediaBrowserServiceCompat.BrowserRoot onGetRoot(@NonNull final String clientPackageName, final int clientUid,
102+
public MediaBrowserServiceCompat.BrowserRoot onGetRoot(@NonNull final String clientPackageName,
103+
final int clientUid,
31104
@Nullable final Bundle rootHints) {
32105
Log.d(TAG, String.format("MediaBrowserService.onGetRoot(%s, %s, %s)",
33106
clientPackageName, clientUid, rootHints));
@@ -36,11 +109,93 @@ public MediaBrowserServiceCompat.BrowserRoot onGetRoot(@NonNull final String cli
36109
}
37110

38111
public void onLoadChildren(@NonNull final String parentId,
39-
@NonNull final MediaBrowserServiceCompat.Result<List<MediaBrowserCompat.MediaItem>> result) {
112+
@NonNull final Result<List<MediaItem>> result) {
40113
Log.d(TAG, String.format("MediaBrowserService.onLoadChildren(%s)", parentId));
41114

42-
final List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
115+
final List<MediaItem> mediaItems = new ArrayList<>();
43116

117+
if (parentId.equals(MY_MEDIA_ROOT_ID)) {
118+
mediaItems.add(
119+
createRootMediaItem(MY_MEDIA_BOOKMARKS_ID,
120+
playerService.getResources().getString(R.string.tab_bookmarks)));
121+
mediaItems.add(
122+
createRootMediaItem(MY_MEDIA_HISTORY_ID,
123+
playerService.getResources().getString(R.string.action_history)));
124+
} else if (parentId.startsWith(MY_MEDIA_BOOKMARKS_ID)) {
125+
populateBookmarks(mediaItems);
126+
} else if (parentId.startsWith(MY_MEDIA_BOOKMARKS_PREFIX)) {
127+
populatePlaylist(extractPlaylistId(parentId), mediaItems);
128+
}
44129
result.sendResult(mediaItems);
45130
}
131+
132+
private Long extractPlaylistId(final String mediaId) {
133+
return Long.valueOf(mediaId.substring(MY_MEDIA_BOOKMARKS_PREFIX.length()));
134+
}
135+
136+
private LocalPlaylistManager getPlaylistManager() {
137+
if (database == null) {
138+
database = NewPipeDatabase.getInstance(playerService);
139+
}
140+
if (localPlaylistManager == null) {
141+
localPlaylistManager = new LocalPlaylistManager(database);
142+
}
143+
return localPlaylistManager;
144+
}
145+
146+
private void populateBookmarks(final List<MediaItem> mediaItems) {
147+
// TODO async
148+
final var playlists = getPlaylistManager().getPlaylists().blockingFirst();
149+
mediaItems.addAll(playlists.stream().map(it -> createPlaylistMediaItem(it))
150+
.collect(Collectors.toList()));
151+
}
152+
153+
private void populatePlaylist(final Long playlistId, final List<MediaItem> mediaItems) {
154+
// TODO async
155+
getPlaylistManager().getPlaylists().blockingFirst()
156+
.stream().filter(it -> it.uid == playlistId).findFirst()
157+
.ifPresent(playlist -> {
158+
final var items = getPlaylistManager().getPlaylistStreams(playlist.uid)
159+
.blockingFirst();
160+
int index = 0;
161+
for (final var item : items) {
162+
mediaItems.add(createPlaylistStreamMediaItem(playlist, item, index));
163+
++index;
164+
}
165+
});
166+
}
167+
168+
@Override
169+
public void onPlay() {
170+
super.onPlay();
171+
NavigationHelper.playOnBackgroundPlayer(playerService, null, true);
172+
}
173+
174+
@Override
175+
public void onPlayFromMediaId(final String mediaId, final Bundle extras) {
176+
super.onPlayFromMediaId(mediaId, extras);
177+
final var playQueue = extractPlayQueueFromMediaId(mediaId);
178+
NavigationHelper.playOnBackgroundPlayer(playerService, playQueue, true);
179+
}
180+
181+
Pattern playlistMediaIdRe = Pattern.compile(MY_MEDIA_ITEM_PREFIX + "(\\d+)_(\\d+)");
182+
private PlayQueue extractPlayQueueFromMediaId(final String mediaId) {
183+
final Matcher m = playlistMediaIdRe.matcher(mediaId);
184+
if (!m.matches()) {
185+
return null;
186+
}
187+
final Long playlistId = Long.valueOf(m.group(1));
188+
final int index = Integer.valueOf(m.group(2));
189+
190+
final var items = getPlaylistManager().getPlaylists().blockingFirst()
191+
.stream().filter(it -> it.uid == playlistId).findFirst()
192+
.map(playlist -> {
193+
return getPlaylistManager().getPlaylistStreams(playlist.uid)
194+
.blockingFirst().stream()
195+
.map(it -> it.toStreamInfoItem())
196+
.collect(Collectors.toList());
197+
}).orElse(new ArrayList<>());
198+
199+
return new SinglePlayQueue(items, index);
200+
}
46201
}

0 commit comments

Comments
 (0)