[Android] PlayAll util method to load large collections
Geoffrey Métais
git at videolan.org
Wed Sep 26 17:48:07 CEST 2018
vlc-android | branch: master | Geoffrey Métais <geoffrey.metais at gmail.com> | Wed Sep 26 17:45:51 2018 +0200| [a6206d9614fe84b20ac386b06471c31eb3b9880e] | committer: Geoffrey Métais
PlayAll util method to load large collections
Use the paged loading to load large collections of tracks
> https://code.videolan.org/videolan/vlc-android/commit/a6206d9614fe84b20ac386b06471c31eb3b9880e
---
.../src/org/videolan/vlc/media/MediaUtils.kt | 92 ++++++++++++++++++++--
1 file changed, 87 insertions(+), 5 deletions(-)
diff --git a/vlc-android/src/org/videolan/vlc/media/MediaUtils.kt b/vlc-android/src/org/videolan/vlc/media/MediaUtils.kt
index 289c6a0c1..7f9e07c77 100644
--- a/vlc-android/src/org/videolan/vlc/media/MediaUtils.kt
+++ b/vlc-android/src/org/videolan/vlc/media/MediaUtils.kt
@@ -10,9 +10,11 @@ import android.os.Looper
import android.provider.MediaStore
import android.provider.OpenableColumns
import android.text.TextUtils
-import kotlinx.coroutines.experimental.CoroutineStart
+import kotlinx.coroutines.experimental.*
+import kotlinx.coroutines.experimental.android.Main
import kotlinx.coroutines.experimental.android.UI
-import kotlinx.coroutines.experimental.launch
+import kotlinx.coroutines.experimental.channels.Channel
+import kotlinx.coroutines.experimental.channels.actor
import org.videolan.libvlc.util.AndroidUtil
import org.videolan.medialibrary.Tools
import org.videolan.medialibrary.media.MediaWrapper
@@ -24,9 +26,14 @@ import org.videolan.vlc.util.FileUtils
import org.videolan.vlc.util.SubtitlesDownloader
import org.videolan.vlc.util.Util
import org.videolan.vlc.util.getFromMl
+import org.videolan.vlc.viewmodels.paged.MLPagedModel
+import java.util.*
+import kotlin.math.min
private const val TAG = "VLC/MediaUtils"
+private const val PAGE_SIZE = 1000
+
object MediaUtils {
private val subtitlesDownloader by lazy { SubtitlesDownloader() }
@@ -116,6 +123,30 @@ object MediaUtils {
fun openArray(context: Context, array: Array<MediaWrapper>, position: Int) = openList(context, array.toList(), position)
+ fun playAll(context: Context?, model: MLPagedModel<MediaWrapper>, position: Int, shuffle: Boolean) {
+ if (context == null) return
+ SuspendDialogCallback(context) { service ->
+ val count = withContext(Dispatchers.IO) { model.getTotalCount() }
+ when (count) {
+ 0 -> null
+ in 1..PAGE_SIZE -> withContext(Dispatchers.IO) { model.getAll()?.toList() }
+ else -> withContext(Dispatchers.IO) {
+ val tracks = mutableListOf<MediaWrapper>()
+ var index = 0
+ while (index < count) {
+ val pageCount = min(PAGE_SIZE, count-index)
+ tracks.addAll(withContext(Dispatchers.IO) { model.getPage(pageCount, index) })
+ index += pageCount
+ }
+ tracks
+ }
+ }?.let { list ->
+ service.load(list, if (shuffle) Random().nextInt(count) else position)
+ if (shuffle && !service.isShuffling) service.shuffle()
+ }
+ }
+ }
+
@JvmOverloads
fun openList(context: Context?, list: List<MediaWrapper>, position: Int, shuffle: Boolean = false) {
if (Util.isListEmpty(list) || context == null) return
@@ -212,8 +243,9 @@ object MediaUtils {
handler.postDelayed({
dialog = ProgressDialog.show(
context,
- context.applicationContext.getString(R.string.loading) + "…",
- context.applicationContext.getString(R.string.please_wait), true)
+ "${context.applicationContext.getString(R.string.loading)} … ",
+ context.applicationContext.getString(R.string.please_wait),
+ true)
dialog.setCancelable(true)
dialog.setOnCancelListener(object : DialogInterface.OnCancelListener {
override fun onCancel(dialog: DialogInterface) {
@@ -243,6 +275,51 @@ object MediaUtils {
}
}
+ private class SuspendDialogCallback (context: Context, private val task: suspend (service: PlaybackService) -> Unit) : BaseCallBack(), CoroutineScope {
+ override val coroutineContext = Dispatchers.Main.immediate
+ private lateinit var dialog: ProgressDialog
+ var job = Job()
+ val actor = actor<Action>(capacity = Channel.UNLIMITED) {
+ for (action in channel) when (action) {
+ Connect -> client.connect()
+ Disconnect -> client.disconnect()
+ is Task -> {
+ action.task.invoke(action.service)
+ job.cancel()
+ dismiss()
+ client.disconnect()
+ }
+ }
+ }
+
+ init {
+ client = PlaybackService.Client(context, this)
+ job = launch{
+ delay(300)
+ dialog = ProgressDialog.show(
+ context,
+ "${context.applicationContext.getString(R.string.loading)}…",
+ context.applicationContext.getString(R.string.please_wait), true)
+ dialog.setCancelable(true)
+ dialog.setOnCancelListener { actor.offer(Disconnect) }
+ }
+ actor.offer(Connect)
+ }
+
+ override fun onConnected(service: PlaybackService) {
+ actor.offer(Task(service, task))
+ dismiss()
+ }
+
+ override fun onDisconnected() {
+ dismiss()
+ }
+
+ private fun dismiss() {
+ if (this::dialog.isInitialized) dialog.dismiss()
+ }
+ }
+
fun retrieveMediaTitle(mw: MediaWrapper) = try {
VLCApplication.getAppContext().contentResolver.query(mw.uri, null, null, null, null)?.use {
val nameIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
@@ -253,5 +330,10 @@ object MediaUtils {
}
} catch (ignored: UnsupportedOperationException) {}
- fun deletePlaylist(playlist: Playlist) = launch { playlist.delete() }
+ fun deletePlaylist(playlist: Playlist) = GlobalScope.launch(Dispatchers.IO) { playlist.delete() }
}
+
+private sealed class Action
+private object Connect : Action()
+private object Disconnect : Action()
+private class Task(val service : PlaybackService, val task: suspend (service: PlaybackService) -> Unit): Action()
More information about the Android
mailing list