Skip to content

Commit 38072a2

Browse files
Android Auto: Add shuffle and play button for the playlist
Background: - While testing the Android auto app, I missed a quick way to shuffle and play the playlist. - This adds the support to shuffle and play the playlist from the Android Auto app. Changes: - The "Shuffle and play" button is added as the first item in the playlist view. - The button is only displayed for playlists that are not empty to prevent confusion.
1 parent 6e0b7be commit 38072a2

4 files changed

Lines changed: 57 additions & 2 deletions

File tree

app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserCommon.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ internal const val ID_STREAM = "stream"
1717
internal const val ID_PLAYLIST = "playlist"
1818
internal const val ID_CHANNEL = "channel"
1919

20+
internal const val ID_SHUFFLE = "ID_SHUFFLE"
21+
2022
internal fun infoItemTypeToString(type: InfoType): String {
2123
return when (type) {
2224
InfoType.STREAM -> ID_STREAM

app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserImpl.kt

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,28 @@ class MediaBrowserImpl(
257257
.build().toString()
258258
}
259259

260+
private fun createShuffleAndPlayMediaItem(
261+
isRemote: Boolean,
262+
playlistId: Long
263+
): MediaBrowserCompat.MediaItem {
264+
val resources = context.resources
265+
266+
val builder = MediaDescriptionCompat.Builder()
267+
.setMediaId(createMediaIdForPlaylistShuffle(isRemote, playlistId))
268+
.setTitle(resources.getString(R.string.shuffle_and_play))
269+
270+
builder.setIconUri(
271+
Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
272+
.authority(resources.getResourcePackageName(R.drawable.ic_shuffle))
273+
.appendPath(resources.getResourceTypeName(R.drawable.ic_shuffle))
274+
.appendPath(resources.getResourceEntryName(R.drawable.ic_shuffle)).build()
275+
)
276+
277+
return MediaBrowserCompat.MediaItem(
278+
builder.build(), MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
279+
)
280+
}
281+
260282
private fun createLocalPlaylistStreamMediaItem(
261283
playlistId: Long,
262284
item: PlaylistStreamEntry,
@@ -301,6 +323,15 @@ class MediaBrowserImpl(
301323
.build().toString()
302324
}
303325

326+
private fun createMediaIdForPlaylistShuffle(
327+
isRemote: Boolean,
328+
playlistId: Long,
329+
): String {
330+
return buildLocalPlaylistItemMediaId(isRemote, playlistId)
331+
.appendPath(ID_SHUFFLE)
332+
.build().toString()
333+
}
334+
304335
private fun createMediaIdForInfoItem(item: InfoItem): String {
305336
return buildInfoItemMediaId(item).build().toString()
306337
}
@@ -346,7 +377,10 @@ class MediaBrowserImpl(
346377
private fun populateLocalPlaylist(playlistId: Long): Single<List<MediaBrowserCompat.MediaItem>> {
347378
val playlist = LocalPlaylistManager(database).getPlaylistStreams(playlistId).firstOrError()
348379
return playlist.map { items ->
349-
items.mapIndexed { index, item ->
380+
val quickActions = if (items.isEmpty()) emptyList() else listOf(
381+
createShuffleAndPlayMediaItem(false, playlistId)
382+
)
383+
quickActions + items.mapIndexed { index, item ->
350384
createLocalPlaylistStreamMediaItem(playlistId, item, index)
351385
}
352386
}
@@ -356,9 +390,12 @@ class MediaBrowserImpl(
356390
return RemotePlaylistManager(database).getPlaylist(playlistId).firstOrError()
357391
.flatMap { ExtractorHelper.getPlaylistInfo(it.serviceId, it.url, false) }
358392
.map {
393+
val quickActions = if (it.relatedItems.isEmpty()) emptyList() else listOf(
394+
createShuffleAndPlayMediaItem(false, playlistId)
395+
)
359396
// ignore it.errors, i.e. ignore errors about specific items, since there would
360397
// be no way to show the error properly in Android Auto anyway
361-
it.relatedItems.mapIndexed { index, item ->
398+
quickActions + it.relatedItems.mapIndexed { index, item ->
362399
createRemotePlaylistStreamMediaItem(playlistId, item, index)
363400
}
364401
}

app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,17 @@ class MediaBrowserPlaybackPreparer(
138138
.map { info -> PlaylistPlayQueue(info, index) }
139139
}
140140

141+
private fun extractShufflePlayQueue(isRemote: Boolean, playlistId: Long): Single<PlayQueue> {
142+
return if (isRemote) {
143+
extractRemotePlayQueue(playlistId, 0)
144+
} else {
145+
extractLocalPlayQueue(playlistId, 0)
146+
}.map { playQueue ->
147+
playQueue.shuffle()
148+
playQueue
149+
}
150+
}
151+
141152
private fun extractPlayQueueFromMediaId(mediaId: String): Single<PlayQueue> {
142153
try {
143154
val mediaIdUri = mediaId.toUri()
@@ -184,6 +195,10 @@ class MediaBrowserPlaybackPreparer(
184195
throw parseError(mediaId)
185196
}
186197
val playlistId = path[0].toLong()
198+
if (ID_SHUFFLE == path[1]) {
199+
return extractShufflePlayQueue(playlistType == ID_REMOTE, playlistId)
200+
}
201+
187202
val index = path[1].toInt()
188203
return if (playlistType == ID_LOCAL)
189204
extractLocalPlayQueue(playlistId, index)

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,7 @@
666666
<string name="app_language_title">App language</string>
667667
<string name="systems_language">System default</string>
668668
<string name="remove_watched">Remove watched</string>
669+
<string name="shuffle_and_play">Shuffle and play</string>
669670
<string name="remove_watched_popup_title">Remove watched videos?</string>
670671
<string name="remove_duplicates">Remove duplicates</string>
671672
<string name="remove_duplicates_title">Remove duplicates?</string>

0 commit comments

Comments
 (0)