diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/DataClasses.kt b/android/app/src/main/java/com/audiobookshelf/app/data/DataClasses.kt index 3e8c9bb5..55bf5607 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/DataClasses.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/DataClasses.kt @@ -1,5 +1,6 @@ package com.audiobookshelf.app.data +import android.content.Context import android.os.Bundle import android.support.v4.media.MediaDescriptionCompat import android.support.v4.media.MediaMetadataCompat @@ -237,8 +238,8 @@ data class PodcastEpisode( var serverEpisodeId:String? // For local podcasts to match with server podcasts ) { @JsonIgnore - fun getMediaDescription(libraryItem:LibraryItemWrapper, progress:MediaProgressWrapper?): MediaDescriptionCompat { - val coverUri = if(libraryItem is LocalLibraryItem) { + fun getMediaDescription(libraryItem:LibraryItemWrapper, progress:MediaProgressWrapper?, ctx: Context?): MediaDescriptionCompat { + val coverUri = if (libraryItem is LocalLibraryItem) { libraryItem.getCoverUri() } else { (libraryItem as LibraryItem).getCoverUri() @@ -266,22 +267,20 @@ data class PodcastEpisode( MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED ) } -// return MediaMetadataCompat.Builder().apply { -// putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id) -// putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, title) -// putString(MediaMetadataCompat.METADATA_KEY_TITLE, title) -// putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, podcast.metadata.getAuthorDisplayName()) -// putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, coverUri.toString()) -// -// }.build() - val libraryItemDescription = libraryItem.getMediaDescription(null) - return MediaDescriptionCompat.Builder() + + val libraryItemDescription = libraryItem.getMediaDescription(null, ctx) + val mediaDescriptionBuilder = MediaDescriptionCompat.Builder() .setMediaId(id) .setTitle(title) .setIconUri(coverUri) .setSubtitle(libraryItemDescription.title) .setExtras(extras) - .build() + + libraryItemDescription.iconBitmap?.let { + mediaDescriptionBuilder.setIconBitmap(it) + } + + return mediaDescriptionBuilder.build() } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt b/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt index 6574ee79..fdcff60a 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt @@ -1,5 +1,6 @@ package com.audiobookshelf.app.data +import android.content.Context import android.support.v4.media.MediaDescriptionCompat import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnoreProperties @@ -101,7 +102,7 @@ data class LocalFolder( ) open class LibraryItemWrapper(var id:String) { @JsonIgnore - open fun getMediaDescription(progress:MediaProgressWrapper?): MediaDescriptionCompat { return MediaDescriptionCompat.Builder().build() } + open fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context?): MediaDescriptionCompat { return MediaDescriptionCompat.Builder().build() } } @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/LibraryItem.kt b/android/app/src/main/java/com/audiobookshelf/app/data/LibraryItem.kt index 4166a6e3..4291a0f0 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/LibraryItem.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/LibraryItem.kt @@ -1,5 +1,6 @@ package com.audiobookshelf.app.data +import android.content.Context import android.net.Uri import android.os.Bundle import android.support.v4.media.MediaDescriptionCompat @@ -55,8 +56,9 @@ class LibraryItem( } @JsonIgnore - override fun getMediaDescription(progress:MediaProgressWrapper?): MediaDescriptionCompat { + override fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context?): MediaDescriptionCompat { val extras = Bundle() + if (progress != null) { if (progress.isFinished) { extras.putInt( diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/LocalLibraryItem.kt b/android/app/src/main/java/com/audiobookshelf/app/data/LocalLibraryItem.kt index 390cfd14..8d884c40 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/LocalLibraryItem.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/LocalLibraryItem.kt @@ -1,7 +1,12 @@ package com.audiobookshelf.app.data +import android.content.Context +import android.graphics.Bitmap +import android.graphics.ImageDecoder import android.net.Uri +import android.os.Build import android.os.Bundle +import android.provider.MediaStore import android.support.v4.media.MediaDescriptionCompat import android.util.Log import androidx.media.utils.MediaConstants @@ -96,9 +101,21 @@ class LocalLibraryItem( } @JsonIgnore - override fun getMediaDescription(progress:MediaProgressWrapper?): MediaDescriptionCompat { + override fun getMediaDescription(progress:MediaProgressWrapper?, ctx:Context?): MediaDescriptionCompat { val coverUri = getCoverUri() + var bitmap:Bitmap? = null + if (coverContentUrl != null) { + ctx?.let { + bitmap = if (Build.VERSION.SDK_INT < 28) { + MediaStore.Images.Media.getBitmap(it.contentResolver, coverUri) + } else { + val source: ImageDecoder.Source = ImageDecoder.createSource(it.contentResolver, coverUri) + ImageDecoder.decodeBitmap(source) + } + } + } + val extras = Bundle() if (progress != null) { if (progress.isFinished) { @@ -122,12 +139,17 @@ class LocalLibraryItem( ) } - return MediaDescriptionCompat.Builder() + val mediaDescriptionBuilder = MediaDescriptionCompat.Builder() .setMediaId(id) .setTitle(title) .setIconUri(coverUri) .setSubtitle(authorName) .setExtras(extras) - .build() + + bitmap?.let { + mediaDescriptionBuilder.setIconBitmap(bitmap) + } + + return mediaDescriptionBuilder.build() } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/PlaybackSession.kt b/android/app/src/main/java/com/audiobookshelf/app/data/PlaybackSession.kt index 0a9c0065..e2903bd9 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/PlaybackSession.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/PlaybackSession.kt @@ -2,7 +2,6 @@ package com.audiobookshelf.app.data import android.net.Uri import android.support.v4.media.MediaMetadataCompat -import com.audiobookshelf.app.R import com.audiobookshelf.app.device.DeviceManager import com.audiobookshelf.app.player.MediaProgressSyncData import com.fasterxml.jackson.annotation.JsonIgnore @@ -13,6 +12,7 @@ import com.google.android.gms.cast.MediaInfo import com.google.android.gms.cast.MediaQueueItem import com.google.android.gms.common.images.WebImage + // TODO: enum or something in kotlin? val PLAYMETHOD_DIRECTPLAY = 0 val PLAYMETHOD_DIRECTSTREAM = 1 @@ -105,9 +105,9 @@ class PlaybackSession( @JsonIgnore fun getCoverUri(): Uri { - if (localLibraryItem?.coverContentUrl != null) return Uri.parse(localLibraryItem?.coverContentUrl) ?: Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon) + if (localLibraryItem?.coverContentUrl != null) return Uri.parse(localLibraryItem?.coverContentUrl) ?: Uri.parse("android.resource://com.audiobookshelf.app/" + com.audiobookshelf.app.R.drawable.icon) - if (coverPath == null) return Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon) + if (coverPath == null) return Uri.parse("android.resource://com.audiobookshelf.app/" + com.audiobookshelf.app.R.drawable.icon) return Uri.parse("$serverAddress/api/items/$libraryItemId/cover?token=${DeviceManager.token}") } @@ -124,7 +124,12 @@ class PlaybackSession( .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, displayTitle) .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, displayAuthor) .putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, displayAuthor) + .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, displayAuthor) + .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, displayAuthor) + .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, displayAuthor) + .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION, displayAuthor) .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id) + .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getCoverUri().toString()) return metadataBuilder.build() } @@ -136,6 +141,8 @@ class PlaybackSession( .setArtist(displayAuthor) .setAlbumArtist(displayAuthor) .setSubtitle(displayAuthor) + .setAlbumTitle(displayAuthor) + .setDescription(displayAuthor) val contentUri = this.getContentUri(audioTrack) metadataBuilder.setMediaUri(contentUri) diff --git a/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt b/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt index 3199e8e8..d29695e1 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt @@ -121,7 +121,7 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) { } } - fun loadPodcastEpisodeMediaBrowserItems(libraryItemId:String, cb: (MutableList) -> Unit) { + fun loadPodcastEpisodeMediaBrowserItems(libraryItemId:String, ctx:Context, cb: (MutableList) -> Unit) { loadLibraryItem(libraryItemId) { libraryItemWrapper -> Log.d(tag, "Loaded Podcast library item $libraryItemWrapper") @@ -138,7 +138,7 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) { Log.d(tag, "Local Podcast Episode ${podcastEpisode.title} | ${podcastEpisode.id}") val progress = DeviceManager.dbManager.getLocalMediaProgress("${libraryItemWrapper.id}-${podcastEpisode.id}") - val description = podcastEpisode.getMediaDescription(libraryItemWrapper, progress) + val description = podcastEpisode.getMediaDescription(libraryItemWrapper, progress, ctx) MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) } children?.let { cb(children as MutableList) } ?: cb(mutableListOf()) @@ -157,7 +157,7 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) { val children = podcast.episodes?.map { podcastEpisode -> val progress = serverUserMediaProgress.find { it.libraryItemId == libraryItemWrapper.id && it.episodeId == podcastEpisode.id } - val description = podcastEpisode.getMediaDescription(libraryItemWrapper, progress) + val description = podcastEpisode.getMediaDescription(libraryItemWrapper, progress, ctx) MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) } children?.let { cb(children as MutableList) } ?: cb(mutableListOf()) diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt index 5d674b9a..f5ac838c 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt @@ -3,7 +3,9 @@ package com.audiobookshelf.app.player import android.app.* import android.content.Context import android.content.Intent +import android.graphics.Bitmap import android.graphics.Color +import android.graphics.ImageDecoder import android.hardware.Sensor import android.hardware.SensorManager import android.net.ConnectivityManager @@ -13,6 +15,7 @@ import android.net.NetworkRequest import android.os.* import android.support.v4.media.MediaBrowserCompat import android.support.v4.media.MediaDescriptionCompat +import android.support.v4.media.MediaMetadataCompat import android.support.v4.media.session.MediaControllerCompat import android.support.v4.media.session.MediaSessionCompat import android.support.v4.media.session.PlaybackStateCompat @@ -250,6 +253,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { playerNotificationManager.setPriority(NotificationCompat.PRIORITY_MAX) playerNotificationManager.setUseFastForwardActionInCompactView(true) playerNotificationManager.setUseRewindActionInCompactView(true) + playerNotificationManager.setSmallIcon(R.drawable.exo_icon_localaudio) // Unknown action playerNotificationManager.setBadgeIconType(NotificationCompat.BADGE_ICON_LARGE) @@ -282,11 +286,15 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { Log.e(tag, "Grant uri permission error $error") } - return MediaDescriptionCompat.Builder() - .setMediaId(currentPlaybackSession!!.id) + val extra = Bundle() + extra.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, currentPlaybackSession!!.displayAuthor) + + val mediaDescriptionBuilder = MediaDescriptionCompat.Builder() + .setExtras(extra) .setTitle(currentPlaybackSession!!.displayTitle) - .setSubtitle(currentPlaybackSession!!.displayAuthor) - .setIconUri(coverUri).build() + .setIconUri(coverUri) + + return mediaDescriptionBuilder.build() } } @@ -865,7 +873,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { if (parentMediaId.startsWith("li_") || parentMediaId.startsWith("local_")) { // Show podcast episodes Log.d(tag, "Loading podcast episodes") - mediaManager.loadPodcastEpisodeMediaBrowserItems(parentMediaId) { + mediaManager.loadPodcastEpisodeMediaBrowserItems(parentMediaId, ctx) { result.sendResult(it) } } else if (::browseTree.isInitialized && browseTree[parentMediaId] == null && mediaManager.getIsLibrary(parentMediaId)) { // Load library items for library @@ -873,11 +881,11 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { mediaManager.loadLibraryItemsWithAudio(parentMediaId) { libraryItems -> val children = libraryItems.map { libraryItem -> if (libraryItem.mediaType == "podcast") { // Podcasts are browseable - val mediaDescription = libraryItem.getMediaDescription(null) + val mediaDescription = libraryItem.getMediaDescription(null, ctx) MediaBrowserCompat.MediaItem(mediaDescription, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE) } else { val progress = mediaManager.serverUserMediaProgress.find { it.libraryItemId == libraryItem.id } - val description = libraryItem.getMediaDescription(progress) + val description = libraryItem.getMediaDescription(progress, ctx) MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) } } @@ -891,13 +899,13 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { localBooks.forEach { localLibraryItem -> val progress = DeviceManager.dbManager.getLocalMediaProgress(localLibraryItem.id) - val description = localLibraryItem.getMediaDescription(progress) + val description = localLibraryItem.getMediaDescription(progress, ctx) localBrowseItems += MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) } localPodcasts.forEach { localLibraryItem -> - val mediaDescription = localLibraryItem.getMediaDescription(null) + val mediaDescription = localLibraryItem.getMediaDescription(null, ctx) localBrowseItems += MediaBrowserCompat.MediaItem(mediaDescription, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE) } @@ -914,14 +922,14 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { } else { progress = mediaManager.serverUserMediaProgress.find { it.libraryItemId == itemInProgress.libraryItemWrapper.id && it.episodeId == itemInProgress.episode.id } } - mediaDescription = itemInProgress.episode.getMediaDescription(itemInProgress.libraryItemWrapper,progress) + mediaDescription = itemInProgress.episode.getMediaDescription(itemInProgress.libraryItemWrapper, progress, ctx) } else { if (itemInProgress.isLocal) { progress = DeviceManager.dbManager.getLocalMediaProgress(itemInProgress.libraryItemWrapper.id) } else { progress = mediaManager.serverUserMediaProgress.find { it.libraryItemId == itemInProgress.libraryItemWrapper.id } } - mediaDescription = itemInProgress.libraryItemWrapper.getMediaDescription(progress) + mediaDescription = itemInProgress.libraryItemWrapper.getMediaDescription(progress, ctx) } localBrowseItems += MediaBrowserCompat.MediaItem(mediaDescription, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) } diff --git a/components/app/AudioPlayer.vue b/components/app/AudioPlayer.vue index 01f84268..edd4fb3c 100644 --- a/components/app/AudioPlayer.vue +++ b/components/app/AudioPlayer.vue @@ -81,7 +81,7 @@
-
+

0:00