[Android] Play queue: Prevent ANR

Geoffrey Métais git at videolan.org
Fri Dec 27 11:20:49 CET 2019


vlc-android | branch: 3.2.x | Geoffrey Métais <geoffrey.metais at gmail.com> | Thu Dec 26 11:30:11 2019 +0100| [4598db40d7ab72d063e030a1c045fd09b6344cb9] | committer: Geoffrey Métais

Play queue: Prevent ANR

canSwitchToVideo() calls getVideoTracksCount() which can block main
thread.

(cherry picked from commit 95c9aa0ae981675f4e0859d7ede1b8dc88c3e383)

> https://code.videolan.org/videolan/vlc-android/commit/4598db40d7ab72d063e030a1c045fd09b6344cb9
---

 .../src/org/videolan/vlc/gui/audio/AudioPlayer.kt  | 44 +++++++++++-----------
 .../vlc/gui/tv/audioplayer/AudioPlayerActivity.kt  | 13 +++----
 .../org/videolan/vlc/viewmodels/PlaylistModel.kt   |  6 ++-
 3 files changed, 31 insertions(+), 32 deletions(-)

diff --git a/vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayer.kt b/vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayer.kt
index d345bb644..eb3ae3fee 100644
--- a/vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayer.kt
+++ b/vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayer.kt
@@ -213,7 +213,7 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, CoroutineS
         showContext(activity, ctxReceiver, position, item?.title ?: "", flags)
     }
 
-    private fun doUpdate() {
+    private suspend fun doUpdate() {
         if (activity === null || (isVisible && playlistModel.switchToVideo())) return
         binding.playlistPlayasaudioOff.visibility = if (playlistModel.videoTrackCount > 0) View.VISIBLE else View.GONE
         binding.audioMediaSwitcher.updateMedia(this, playlistModel.service)
@@ -291,28 +291,26 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, CoroutineS
     }
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    private fun updateBackground() {
+    private suspend fun updateBackground() {
         if (settings.getBoolean("blurred_cover_background", true)) {
-            launch {
-                val mw = playlistModel.currentMediaWrapper
-                if (!isStarted() || mw === null || TextUtils.equals(currentCoverArt, mw.artworkMrl)) return at launch
-                currentCoverArt = mw.artworkMrl
-                if (TextUtils.isEmpty(mw.artworkMrl)) {
-                    setDefaultBackground()
-                } else {
-                    val width = if (binding.contentLayout.width > 0) binding.contentLayout.width else activity?.getScreenWidth()
-                            ?: return at launch
-                    val blurredCover = withContext(Dispatchers.IO) { UiTools.blurBitmap(AudioUtil.readCoverBitmap(Uri.decode(mw.artworkMrl), width)) }
-                    if (!isStarted()) return at launch
-                    if (blurredCover !== null) {
-                        val activity = activity as? AudioPlayerContainerActivity ?: return at launch
-                        binding.backgroundView.setColorFilter(UiTools.getColorFromAttribute(activity, R.attr.audio_player_background_tint))
-                        binding.backgroundView.setImageBitmap(blurredCover)
-                        binding.backgroundView.visibility = View.VISIBLE
-                        binding.songsList.setBackgroundResource(0)
-                        binding.header.setBackgroundColor(UiTools.getColorFromAttribute(requireContext(), R.attr.audio_player_transparent))
-                    } else setDefaultBackground()
-                }
+            val mw = playlistModel.currentMediaWrapper
+            if (!isStarted() || mw === null || TextUtils.equals(currentCoverArt, mw.artworkMrl)) return
+            currentCoverArt = mw.artworkMrl
+            if (TextUtils.isEmpty(mw.artworkMrl)) {
+                setDefaultBackground()
+            } else {
+                val width = if (binding.contentLayout.width > 0) binding.contentLayout.width else activity?.getScreenWidth()
+                        ?: return
+                val blurredCover = withContext(Dispatchers.IO) { UiTools.blurBitmap(AudioUtil.readCoverBitmap(Uri.decode(mw.artworkMrl), width)) }
+                if (!isStarted()) return
+                if (blurredCover !== null) {
+                    val activity = activity as? AudioPlayerContainerActivity ?: return
+                    binding.backgroundView.setColorFilter(UiTools.getColorFromAttribute(activity, R.attr.audio_player_background_tint))
+                    binding.backgroundView.setImageBitmap(blurredCover)
+                    binding.backgroundView.visibility = View.VISIBLE
+                    binding.songsList.setBackgroundResource(0)
+                    binding.header.setBackgroundColor(UiTools.getColorFromAttribute(requireContext(), R.attr.audio_player_transparent))
+                } else setDefaultBackground()
             }
         }
     }
@@ -383,7 +381,7 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, CoroutineS
                     it.uri, playlistModel.currentMediaPosition)
             else if (hasMedia()) {
                 it.removeFlags(AbstractMediaWrapper.MEDIA_FORCE_AUDIO)
-                playlistModel.switchToVideo()
+                launch(start = CoroutineStart.UNDISPATCHED) { playlistModel.switchToVideo() }
             }
         }
     }
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/AudioPlayerActivity.kt b/vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/AudioPlayerActivity.kt
index 119deb750..c23805a12 100644
--- a/vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/AudioPlayerActivity.kt
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/AudioPlayerActivity.kt
@@ -109,21 +109,20 @@ class AudioPlayerActivity : BaseTvActivity() {
 
         wasPlaying = state.playing
 
-        val mw = model.currentMediaWrapper
-        if (mw != null && !mw.hasFlag(AbstractMediaWrapper.MEDIA_FORCE_AUDIO) && model.canSwitchToVideo()) {
-            model.switchToVideo()
-            finish()
-            return
-        }
         binding.mediaTitle.text = state.title
         binding.mediaArtist.text = state.artist
         binding.buttonShuffle.setImageResource(if (shuffling)
             R.drawable.ic_shuffle_on
         else
             R.drawable.ic_shuffle)
+        val mw = model.currentMediaWrapper
         if (mw == null || TextUtils.equals(currentCoverArt, mw.artworkMrl)) return
         currentCoverArt = mw.artworkMrl
-        updateBackground()
+        launch {
+            if (model.switchToVideo()) finish()
+            else updateBackground()
+        }
+
     }
 
     private fun updateBackground() = launch {
diff --git a/vlc-android/src/org/videolan/vlc/viewmodels/PlaylistModel.kt b/vlc-android/src/org/videolan/vlc/viewmodels/PlaylistModel.kt
index 0f288768b..ad9f93800 100644
--- a/vlc-android/src/org/videolan/vlc/viewmodels/PlaylistModel.kt
+++ b/vlc-android/src/org/videolan/vlc/viewmodels/PlaylistModel.kt
@@ -26,9 +26,11 @@ import androidx.lifecycle.MediatorLiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.Observer
 import androidx.lifecycle.ViewModelProviders
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.ObsoleteCoroutinesApi
 import kotlinx.coroutines.channels.actor
+import kotlinx.coroutines.withContext
 import org.videolan.medialibrary.Tools
 import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
 import org.videolan.vlc.PlaybackService
@@ -185,7 +187,7 @@ class PlaylistModel : ScopedModel(), PlaybackService.Callback by EmptyPBSCallbac
 
     fun load(medialist: List<AbstractMediaWrapper>, position: Int) = service?.load(medialist, position)
 
-    fun switchToVideo() : Boolean {
+    suspend fun switchToVideo() : Boolean {
         service?.apply {
             if (PlaylistManager.hasMedia() && !isVideoPlaying && !hasRenderer()) {
                 currentMediaWrapper?.run {
@@ -199,7 +201,7 @@ class PlaylistModel : ScopedModel(), PlaybackService.Callback by EmptyPBSCallbac
         return false
     }
 
-    fun canSwitchToVideo() = service?.playlistManager?.player?.canSwitchToVideo() ?: false
+    suspend fun canSwitchToVideo() = withContext(Dispatchers.IO) { service?.playlistManager?.player?.canSwitchToVideo() ?: false }
 
     fun toggleABRepeat() = service?.playlistManager?.toggleABRepeat()
 



More information about the Android mailing list