[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