[Android] Pause podcasts instead of ducking

Robert Stone git at videolan.org
Tue May 30 09:37:05 UTC 2023


vlc-android | branch: master | Robert Stone <rhstone at gmail.com> | Mon May 22 20:37:13 2023 -0700| [9d32eafe6c065687251dfccb271a9b71d517f00c] | committer: Robert Stone

Pause podcasts instead of ducking

Fixes #2927

> https://code.videolan.org/videolan/vlc-android/commit/9d32eafe6c065687251dfccb271a9b71d517f00c
---

 .../src/org/videolan/vlc/PlaybackService.kt        |  8 +++-
 .../org/videolan/vlc/util/VLCAudioFocusHelper.kt   | 54 ++++++++++++++++++----
 2 files changed, 51 insertions(+), 11 deletions(-)

diff --git a/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt b/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt
index 32f9962322..e6d75d6476 100644
--- a/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt
+++ b/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt
@@ -272,7 +272,11 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner, CoroutineSc
 
     val isPodcastMode: Boolean
         @MainThread
-        get() = playlistManager.getMediaListSize() == 1 && playlistManager.getCurrentMedia()?.isPodcast == true
+        get() = playlistManager.getMediaListSize() == 1 && isPodcastPlaying
+
+    val isPodcastPlaying: Boolean
+        @MainThread
+        get() = playlistManager.getCurrentMedia()?.isPodcast == true
 
     val speed: Float
         @MainThread
@@ -1091,7 +1095,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner, CoroutineSc
         pscb.setState(state, time, playlistManager.player.getRate())
         pscb.setActiveQueueItemId(playlistManager.currentIndex.toLong())
         val repeatType = playlistManager.repeating
-        val podcastMode = playlistManager.getMediaListSize() == 1 && playlistManager.getCurrentMedia()?.isPodcast == true
+        val podcastMode = isPodcastMode
         if (repeatType != PlaybackStateCompat.REPEAT_MODE_NONE || hasNext())
             actions = actions or PlaybackStateCompat.ACTION_SKIP_TO_NEXT
         if (repeatType != PlaybackStateCompat.REPEAT_MODE_NONE || hasPrevious() || (isSeekable && !podcastMode))
diff --git a/application/vlc-android/src/org/videolan/vlc/util/VLCAudioFocusHelper.kt b/application/vlc-android/src/org/videolan/vlc/util/VLCAudioFocusHelper.kt
index dba0930e76..7b74d9e76c 100644
--- a/application/vlc-android/src/org/videolan/vlc/util/VLCAudioFocusHelper.kt
+++ b/application/vlc-android/src/org/videolan/vlc/util/VLCAudioFocusHelper.kt
@@ -28,6 +28,8 @@ import android.media.AudioAttributes
 import android.media.AudioFocusRequest
 import android.media.AudioManager
 import android.os.Build
+import android.os.Handler
+import android.os.Looper
 import android.util.Log
 import androidx.core.content.getSystemService
 import org.videolan.libvlc.util.AndroidUtil
@@ -43,9 +45,11 @@ private const val TAG = "VLCAudioFocusHelper"
 class VLCAudioFocusHelper(private val service: PlaybackService) {
 
     private lateinit var audioManager: AudioManager
-    private lateinit var audioFocusRequest : AudioFocusRequest
+    private lateinit var audioFocusRequest: AudioFocusRequest
     private var hasAudioFocus = false
     @Volatile
+    private var podcastPlaying = false
+    @Volatile
     internal var lossTransient = false
 
     private val audioFocusListener = createOnAudioFocusChangeListener()
@@ -54,7 +58,8 @@ class VLCAudioFocusHelper(private val service: PlaybackService) {
         if (!this::audioManager.isInitialized) audioManager = service.getSystemService() ?: return
 
         if (acquire && !service.hasRenderer()) {
-            if (!hasAudioFocus) {
+            podcastPlaying = service.isPodcastPlaying
+            if (!hasAudioFocus || podcastPlaying) {
                 val result = requestAudioFocus()
                 if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
                     audioManager.setParameters("bgm_state=true")
@@ -82,6 +87,7 @@ class VLCAudioFocusHelper(private val service: PlaybackService) {
                 .build()
         audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                 .setOnAudioFocusChangeListener(audioFocusListener)
+                .setWillPauseWhenDucked(podcastPlaying)
                 .setAudioAttributes(attributes)
                 .build()
         audioManager.requestAudioFocus(audioFocusRequest)
@@ -92,7 +98,9 @@ class VLCAudioFocusHelper(private val service: PlaybackService) {
 
     private fun createOnAudioFocusChangeListener(): AudioManager.OnAudioFocusChangeListener {
         return object : AudioManager.OnAudioFocusChangeListener {
+            private val handler by lazy(LazyThreadSafetyMode.NONE) { Handler(Looper.getMainLooper()) }
             private var lossTransientVolume = -1
+            private var lossTime: Long = 0
             private var wasPlaying = false
 
             override fun onAudioFocusChange(focusChange: Int) {
@@ -106,24 +114,27 @@ class VLCAudioFocusHelper(private val service: PlaybackService) {
                         changeAudioFocus(false)
                         service.pause()
                     }
+
                     AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
                         if (BuildConfig.DEBUG) Log.i(TAG, "AUDIOFOCUS_LOSS_TRANSIENT")
                         // Pause playback
                         pausePlayback()
                     }
+
                     AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
                         if (BuildConfig.DEBUG) Log.i(TAG, "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK")
                         // Lower the volume
                         if (service.isPlaying) {
-                            if (AndroidDevices.isAmazon) {
+                            if (AndroidDevices.isAmazon || podcastPlaying) {
                                 pausePlayback()
                             } else if (service.settings.getBoolean(AUDIO_DUCKING, true)) {
                                 val volume = service.volume
                                 lossTransientVolume = volume
-                                service.setVolume(volume/3)
+                                service.setVolume(volume / 3)
                             }
                         }
                     }
+
                     AudioManager.AUDIOFOCUS_GAIN -> {
                         if (BuildConfig.DEBUG) Log.i(TAG, "AUDIOFOCUS_GAIN: ")
                         // Resume playback
@@ -131,10 +142,15 @@ class VLCAudioFocusHelper(private val service: PlaybackService) {
                             service.setVolume(lossTransientVolume)
                             lossTransientVolume = -1
                         }
-                        if (lossTransient) {
-                            if (wasPlaying && service.settings.getBoolean(RESUME_PLAYBACK, true))
-                                service.play()
-                            lossTransient = false
+
+                        if (lossTransient && wasPlaying) {
+                            if (podcastPlaying) {
+                                // If the interruption was short just restart playback
+                                val deltaTime = System.currentTimeMillis() - lossTime
+                                if (deltaTime > 1_000L) schedulePlayback(2_000L) else resumePlayback()
+                            } else if (service.settings.getBoolean(RESUME_PLAYBACK, true)) {
+                                resumePlayback()
+                            }
                         }
                     }
                 }
@@ -144,7 +160,27 @@ class VLCAudioFocusHelper(private val service: PlaybackService) {
                 if (lossTransient) return
                 lossTransient = true
                 wasPlaying = service.isPlaying
-                if (wasPlaying) service.pause()
+                if (wasPlaying) {
+                    service.pause()
+                    lossTime = System.currentTimeMillis()
+                }
+            }
+
+            private fun schedulePlayback(delay: Long) {
+                handler.removeCallbacks(delayedPodcastRunnable)
+                handler.postDelayed(delayedPodcastRunnable, delay)
+            }
+
+            private val delayedPodcastRunnable = Runnable {
+                val position = (service.getTime() - 5_000L).coerceAtLeast(0)
+                resumePlayback()
+                service.seek(position, fromUser = true)
+                service.playlistManager.player.updateProgress(position)
+            }
+
+            private fun resumePlayback() {
+                service.play()
+                lossTransient = false
             }
         }
     }



More information about the Android mailing list