[Android] Add notifications for MediaBrowser clients on medialibrary updates

Robert Stone git at videolan.org
Mon Dec 20 07:38:11 UTC 2021


vlc-android | branch: master | Robert Stone <rhstone at gmail.com> | Sun Dec 12 22:32:41 2021 -0800| [5e76ec34b1d7ef901d0b9c3479ae8fab406ac0b1] | committer: Nicolas Pomepuy

Add notifications for MediaBrowser clients on medialibrary updates

> https://code.videolan.org/videolan/vlc-android/commit/5e76ec34b1d7ef901d0b9c3479ae8fab406ac0b1
---

 .../src/org/videolan/vlc/MediaBrowserCallback.kt   | 150 +++++++++++++++++++++
 .../src/org/videolan/vlc/PlaybackService.kt        |  12 ++
 .../org/videolan/vlc/media/MediaSessionBrowser.kt  |   2 +-
 3 files changed, 163 insertions(+), 1 deletion(-)

diff --git a/application/vlc-android/src/org/videolan/vlc/MediaBrowserCallback.kt b/application/vlc-android/src/org/videolan/vlc/MediaBrowserCallback.kt
new file mode 100644
index 000000000..44992b3e7
--- /dev/null
+++ b/application/vlc-android/src/org/videolan/vlc/MediaBrowserCallback.kt
@@ -0,0 +1,150 @@
+/*
+ *****************************************************************************
+ * MediaBrowserCallback.kt
+ *****************************************************************************
+ * Copyright © 2021 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+package org.videolan.vlc
+
+import androidx.lifecycle.lifecycleScope
+import kotlinx.coroutines.channels.SendChannel
+import org.videolan.medialibrary.interfaces.Medialibrary
+import org.videolan.tools.conflatedActor
+import org.videolan.tools.safeOffer
+
+/**
+ * @see org.videolan.vlc.viewmodels.ICallBackHandler
+ */
+interface IMediaBrowserCallback {
+    fun registerHistoryCallback(function: () -> Unit)
+    fun registerMediaCallback(function: () -> Unit)
+    fun removeCallbacks()
+}
+
+/**
+ *@see org.videolan.vlc.viewmodels.CallBackDelegate
+ */
+class MediaBrowserCallback(private val playbackService: PlaybackService) : IMediaBrowserCallback,
+        Medialibrary.MediaCb,
+        Medialibrary.ArtistsCb,
+        Medialibrary.AlbumsCb,
+        Medialibrary.GenresCb,
+        Medialibrary.PlaylistsCb,
+        Medialibrary.HistoryCb {
+
+    private val medialibrary = Medialibrary.getInstance()
+    private lateinit var historyActor: SendChannel<Unit>
+    private lateinit var refreshActor: SendChannel<Unit>
+
+    override fun registerHistoryCallback(refresh: () -> Unit) {
+        historyActor = playbackService.lifecycleScope.conflatedActor { refresh() }
+        medialibrary.addHistoryCb(this)
+    }
+
+    override fun onHistoryModified() {
+        historyActor.safeOffer(Unit)
+    }
+
+    override fun registerMediaCallback(refresh: () -> Unit) {
+        refreshActor = playbackService.lifecycleScope.conflatedActor { refresh() }
+        medialibrary.addMediaCb(this)
+        medialibrary.addArtistsCb(this)
+        medialibrary.addAlbumsCb(this)
+        medialibrary.addGenreCb(this)
+        medialibrary.addPlaylistCb(this)
+    }
+
+    override fun onMediaAdded() {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onMediaDeleted(id: LongArray?) {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onMediaModified() {
+        /* Intentionally Ignored */
+    }
+
+    override fun onMediaConvertedToExternal(id: LongArray?) {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onArtistsAdded() {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onArtistsModified() {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onArtistsDeleted() {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onAlbumsAdded() {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onAlbumsModified() {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onAlbumsDeleted() {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onGenresAdded() {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onGenresModified() {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onGenresDeleted() {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onPlaylistsAdded() {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onPlaylistsModified() {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun onPlaylistsDeleted() {
+        refreshActor.safeOffer(Unit)
+    }
+
+    override fun removeCallbacks() {
+        if (::refreshActor.isInitialized) {
+            medialibrary.removeMediaCb(this)
+            medialibrary.removeArtistsCb(this)
+            medialibrary.removeAlbumsCb(this)
+            medialibrary.removeGenreCb(this)
+            medialibrary.removePlaylistCb(this)
+            refreshActor.close()
+        }
+        if (::historyActor.isInitialized) {
+            medialibrary.removeHistoryCb(this)
+            historyActor.close()
+        }
+    }
+}
\ No newline at end of file
diff --git a/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt b/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt
index f2b7def4d..0dc5f8ed6 100644
--- a/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt
+++ b/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt
@@ -109,6 +109,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
     private var detectHeadset = true
     private lateinit var wakeLock: PowerManager.WakeLock
     private val audioFocusHelper by lazy { VLCAudioFocusHelper(this) }
+    private lateinit var browserCallback: MediaBrowserCallback
 
     // Playback management
     internal lateinit var mediaSession: MediaSessionCompat
@@ -119,6 +120,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
     private var lastLength = 0L
     private var lastChapter = 0
     private var lastChaptersCount = 0
+    private var lastParentId = ""
     private var widget = 0
     /**
      * Last widget position update timestamp
@@ -551,6 +553,14 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
         medialibrary = Medialibrary.getInstance()
         artworkMap = HashMap<String,Uri>()
 
+        browserCallback = MediaBrowserCallback(this)
+        browserCallback.registerMediaCallback { if (lastParentId.isNotEmpty()) notifyChildrenChanged(lastParentId) }
+        browserCallback.registerHistoryCallback {
+            when (lastParentId) {
+                MediaSessionBrowser.ID_HOME, MediaSessionBrowser.ID_HISTORY -> notifyChildrenChanged(lastParentId)
+            }
+        }
+
         detectHeadset = settings.getBoolean("enable_headset_detection", true)
 
         // Make sure the audio player will acquire a wake-lock while playing. If we don't do
@@ -651,6 +661,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
         dispatcher.onServicePreSuperOnDestroy()
         super.onDestroy()
         handler.removeCallbacksAndMessages(null)
+        browserCallback.removeCallbacks()
         if (this::mediaSession.isInitialized) mediaSession.release()
         //Call it once mediaSession is null, to not publish playback state
         stop(systemExit = true)
@@ -1611,6 +1622,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
 
     override fun onLoadChildren(parentId: String, result: Result<List<MediaBrowserCompat.MediaItem>>) {
         result.detach()
+        lastParentId = parentId
         lifecycleScope.launch(start = CoroutineStart.UNDISPATCHED) {
             awaitMedialibraryStarted()
             lifecycleScope.launch(Dispatchers.IO) {
diff --git a/application/vlc-android/src/org/videolan/vlc/media/MediaSessionBrowser.kt b/application/vlc-android/src/org/videolan/vlc/media/MediaSessionBrowser.kt
index c2a098a48..f22f82c6d 100644
--- a/application/vlc-android/src/org/videolan/vlc/media/MediaSessionBrowser.kt
+++ b/application/vlc-android/src/org/videolan/vlc/media/MediaSessionBrowser.kt
@@ -160,7 +160,7 @@ class MediaSessionBrowser : ExtensionManagerActivity {
         const val ID_NO_PLAYLIST = "$ID_ROOT/error/playlist"
 
         // Top-level menu
-        private const val ID_HOME = "$ID_ROOT/home"
+        const val ID_HOME = "$ID_ROOT/home"
         const val ID_PLAYLIST = "$ID_ROOT/playlist"
         private const val ID_LIBRARY = "$ID_ROOT/l"
         const val ID_STREAM = "$ID_ROOT/stream"



More information about the Android mailing list