Implemented FutoVideoPlayer loader.

This commit is contained in:
Koen J
2025-07-04 08:09:24 +02:00
parent 2c463dd5a1
commit 86a4cf8d84
3 changed files with 113 additions and 0 deletions
@@ -1,5 +1,6 @@
package com.futo.platformplayer.views.video
import android.animation.ValueAnimator
import android.content.Context
import android.content.Intent
import android.content.res.Resources
@@ -44,8 +45,11 @@ import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.views.behavior.GestureControlView
import com.futo.platformplayer.views.others.ProgressBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.util.concurrent.Executors
@@ -150,6 +154,11 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
val onChapterClicked = Event1<IChapter>();
private val loaderOverlay: FrameLayout
private val loaderIndeterminate: android.widget.ProgressBar
private val loaderDeterminate: android.widget.ProgressBar
private var determinateAnimator: ValueAnimator? = null
@OptIn(UnstableApi::class)
constructor(context: Context, attrs: AttributeSet? = null) : super(PLAYER_STATE_NAME, context, attrs) {
LayoutInflater.from(context).inflate(R.layout.video_view, this, true);
@@ -190,6 +199,14 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
_control_duration_fullscreen = _videoControls_fullscreen.findViewById(R.id.text_duration);
_control_pause_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_pause);
loaderOverlay = findViewById(R.id.loader_overlay)
loaderIndeterminate = findViewById(R.id.loader_indeterminate)
loaderDeterminate = findViewById(R.id.loader_determinate)
loaderOverlay.visibility = View.GONE
loaderIndeterminate.visibility = View.GONE
loaderDeterminate.visibility = View.GONE
_control_chapter.setOnClickListener {
_currentChapter?.let {
onChapterClicked.emit(it);
@@ -865,4 +882,35 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
override fun onSurfaceSizeChanged(width: Int, height: Int) {
gestureControl.resetZoomPan()
}
override fun setLoading(isLoading: Boolean) {
determinateAnimator?.cancel()
if (isLoading) {
loaderOverlay.visibility = View.VISIBLE
loaderIndeterminate.visibility = View.VISIBLE
loaderDeterminate.visibility = View.GONE
} else {
loaderOverlay.visibility = View.GONE
loaderIndeterminate.visibility = View.GONE
loaderDeterminate.visibility = View.GONE
}
}
override fun setLoading(expectedDurationMs: Int) {
determinateAnimator?.cancel()
loaderOverlay.visibility = View.VISIBLE
loaderIndeterminate.visibility = View.GONE
loaderDeterminate.visibility = View.VISIBLE
loaderDeterminate.max = expectedDurationMs
loaderDeterminate.progress = 0
determinateAnimator = ValueAnimator.ofInt(0, expectedDurationMs).apply {
duration = expectedDurationMs.toLong()
addUpdateListener { anim ->
loaderDeterminate.progress = anim.animatedValue as Int
}
start()
}
}
}
@@ -65,6 +65,8 @@ import com.futo.platformplayer.views.video.datasources.JSHttpDataSource
import getHttpDataSourceFactory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.ByteArrayInputStream
@@ -573,7 +575,17 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
val plugin = videoSource.getUnderlyingPlugin() ?: return@launch;
startId = plugin.getUnderlyingPlugin()?.runtimeId ?: -1;
val generatedDef = plugin.busy { videoSource.generateAsync(scope); };
withContext(Dispatchers.Main) {
if (generatedDef.estDuration >= 0) {
setLoading(generatedDef.estDuration)
} else {
setLoading(true)
}
}
val generated = generatedDef.await();
withContext(Dispatchers.Main) {
setLoading(false)
}
if (generated != null) {
withContext(Dispatchers.Main) {
val dataSource = if(videoSource is JSSource && (videoSource.requiresCustomDatasource))
@@ -610,6 +622,10 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
}
catch(ex: Throwable) {
Logger.e(TAG, "DashRaw generator failed", ex);
} finally {
withContext(Dispatchers.Main) {
setLoading(false)
}
}
}
return false;
@@ -702,7 +718,17 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
val plugin = audioSource.getUnderlyingPlugin() ?: return@launch;
startId = audioSource.getUnderlyingPlugin()?.getUnderlyingPlugin()?.runtimeId ?: -1;
val generatedDef = plugin.busy { audioSource.generateAsync(scope); }
withContext(Dispatchers.Main) {
if (generatedDef.estDuration >= 0) {
setLoading(generatedDef.estDuration)
} else {
setLoading(true)
}
}
val generated = generatedDef.await();
withContext(Dispatchers.Main) {
setLoading(false)
}
if(generated != null) {
val dataSource = if(audioSource is JSSource && (audioSource.requiresCustomDatasource))
audioSource.getHttpDataSourceFactory()
@@ -729,6 +755,10 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
}
catch(ex: Throwable) {
} finally {
withContext(Dispatchers.Main) {
setLoading(false)
}
}
}
return false;
@@ -938,6 +968,9 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
}
}
protected open fun setLoading(isLoading: Boolean) { }
protected open fun setLoading(expectedDurationMs: Int) { }
companion object {
val DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0";
+32
View File
@@ -64,4 +64,36 @@
app:controller_layout_id="@layout/video_player_ui_fullscreen"
android:visibility="gone" />
</FrameLayout>
<FrameLayout
android:id="@+id/loader_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="6dp"
android:background="@color/black"
android:clickable="true"
android:focusable="true"
android:visibility="gone">
<ProgressBar
android:id="@+id/loader_indeterminate"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
android:visibility="gone" />
<ProgressBar
android:id="@+id/loader_determinate"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="false"
android:max="100"
android:progress="0"
android:visibility="gone"/>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>