Fixed playback controls.

This commit is contained in:
Koen 2023-12-13 17:14:40 +01:00
commit bf6e61ed90
4 changed files with 171 additions and 86 deletions

View file

@ -13,7 +13,6 @@ import java.text.DecimalFormat
import java.time.OffsetDateTime
import java.time.temporal.ChronoUnit
import kotlin.math.abs
import kotlin.time.toDuration
//Long
@ -228,6 +227,14 @@ fun String.fixHtmlWhitespace(): Spanned {
return Html.fromHtml(replace("\n", "<br />"), HtmlCompat.FROM_HTML_MODE_LEGACY);
}
fun Long.formatDuration(): String {
val hours = this / 3600000
val minutes = (this % 3600000) / 60000
val seconds = (this % 60000) / 1000
return String.format("%02d:%02d:%02d", hours, minutes, seconds)
}
fun String.fixHtmlLinks(): Spanned {
//TODO: Properly fix whitespace handling.
val doc = Jsoup.parse(replace("\n", "<br />"));

View file

@ -16,6 +16,14 @@ import android.widget.TextView
import androidx.annotation.OptIn
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.setMargins
import androidx.media3.common.PlaybackParameters
import androidx.media3.common.VideoSize
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerControlView
import androidx.media3.ui.PlayerView
import androidx.media3.ui.TimeBar
import com.futo.platformplayer.R
import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs
@ -26,18 +34,11 @@ import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.constructs.Event2
import com.futo.platformplayer.constructs.Event3
import com.futo.platformplayer.formatDuration
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 androidx.media3.common.PlaybackParameters
import androidx.media3.common.VideoSize
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerControlView
import androidx.media3.ui.PlayerView
import androidx.media3.ui.TimeBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -74,7 +75,10 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
private val _control_loop: ImageButton;
private val _control_cast: ImageButton;
private val _control_play: ImageButton;
private val _control_pause: ImageButton;
private val _control_chapter: TextView;
private val _control_time: TextView;
private val _control_duration: TextView;
private val _time_bar: TimeBar;
private val _buttonPrevious: ImageButton;
private val _buttonNext: ImageButton;
@ -91,6 +95,9 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
private val _control_chapter_fullscreen: TextView;
private val _buttonPrevious_fullscreen: ImageButton;
private val _buttonNext_fullscreen: ImageButton;
private val _control_time_fullscreen: TextView;
private val _control_duration_fullscreen: TextView;
private val _control_pause_fullscreen: ImageButton;
private val _title_fullscreen: TextView;
private val _author_fullscreen: TextView;
@ -134,30 +141,36 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
_videoView = findViewById(R.id.video_player);
videoControls = findViewById(R.id.video_player_controller);
_control_fullscreen = videoControls.findViewById(R.id.exo_fullscreen);
_control_videosettings = videoControls.findViewById(R.id.exo_settings);
_control_minimize = videoControls.findViewById(R.id.exo_minimize);
_control_rotate_lock = videoControls.findViewById(R.id.exo_rotate_lock);
_control_loop = videoControls.findViewById(R.id.exo_loop);
_control_cast = videoControls.findViewById(R.id.exo_cast);
_control_play = videoControls.findViewById(androidx.media3.ui.R.id.exo_play);
_time_bar = videoControls.findViewById(androidx.media3.ui.R.id.exo_progress);
_control_fullscreen = videoControls.findViewById(R.id.button_fullscreen);
_control_videosettings = videoControls.findViewById(R.id.button_settings);
_control_minimize = videoControls.findViewById(R.id.button_minimize);
_control_rotate_lock = videoControls.findViewById(R.id.button_rotate_lock);
_control_loop = videoControls.findViewById(R.id.button_loop);
_control_cast = videoControls.findViewById(R.id.button_cast);
_control_play = videoControls.findViewById(R.id.button_play);
_control_pause = videoControls.findViewById(R.id.button_pause);
_time_bar = videoControls.findViewById(R.id.time_progress);
_control_chapter = videoControls.findViewById(R.id.text_chapter_current);
_buttonNext = videoControls.findViewById(R.id.button_next);
_buttonPrevious = videoControls.findViewById(R.id.button_previous);
_control_time = videoControls.findViewById(R.id.text_position);
_control_duration = videoControls.findViewById(R.id.text_duration);
_videoControls_fullscreen = findViewById(R.id.video_player_controller_fullscreen);
_control_fullscreen_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_fullscreen);
_control_minimize_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_minimize);
_control_videosettings_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_settings);
_control_rotate_lock_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_rotate_lock);
_control_loop_fullscreen = videoControls.findViewById(R.id.exo_loop);
_control_cast_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_cast);
_control_play_fullscreen = videoControls.findViewById(androidx.media3.ui.R.id.exo_play);
_control_fullscreen_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_fullscreen);
_control_minimize_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_minimize);
_control_videosettings_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_settings);
_control_rotate_lock_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_rotate_lock);
_control_loop_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_loop);
_control_cast_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_cast);
_control_play_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_play);
_control_chapter_fullscreen = _videoControls_fullscreen.findViewById(R.id.text_chapter_current);
_time_bar_fullscreen = _videoControls_fullscreen.findViewById(androidx.media3.ui.R.id.exo_progress);
_time_bar_fullscreen = _videoControls_fullscreen.findViewById(R.id.time_progress);
_buttonPrevious_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_previous);
_buttonNext_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_next);
_control_time_fullscreen = _videoControls_fullscreen.findViewById(R.id.text_position);
_control_duration_fullscreen = _videoControls_fullscreen.findViewById(R.id.text_duration);
_control_pause_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_pause);
val castVisibility = if (Settings.instance.casting.enabled) View.VISIBLE else View.GONE
_control_cast.visibility = castVisibility
@ -167,11 +180,53 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
_buttonNext.setOnClickListener { onNext.emit() };
_buttonPrevious_fullscreen.setOnClickListener { onPrevious.emit() };
_buttonNext_fullscreen.setOnClickListener { onNext.emit() };
_control_play.setOnClickListener {
exoPlayer?.player?.let {
if (it.contentPosition >= it.duration) {
it.seekTo(0)
}
exoPlayer?.player?.play();
}
updatePlayPause();
};
_control_play_fullscreen.setOnClickListener {
exoPlayer?.player?.let {
if (it.contentPosition >= it.duration) {
it.seekTo(0)
}
exoPlayer?.player?.play();
}
updatePlayPause();
};
_control_pause.setOnClickListener {
exoPlayer?.player?.pause();
updatePlayPause();
};
_control_pause_fullscreen.setOnClickListener {
exoPlayer?.player?.pause();
updatePlayPause();
};
val scrubListener = object : TimeBar.OnScrubListener {
override fun onScrubStart(timeBar: TimeBar, position: Long) {
exoPlayer?.player?.seekTo(position);
}
override fun onScrubMove(timeBar: TimeBar, position: Long) {
exoPlayer?.player?.seekTo(position);
}
override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) {
exoPlayer?.player?.seekTo(position);
}
};
_time_bar.addListener(scrubListener)
_time_bar_fullscreen.addListener(scrubListener)
_overlay_brightness = findViewById(R.id.overlay_brightness);
_title_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_title);
_author_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_author);
_title_fullscreen = _videoControls_fullscreen.findViewById(R.id.text_title);
_author_fullscreen = _videoControls_fullscreen.findViewById(R.id.text_author);
background = findViewById(R.id.layout_controls_background);
_layoutControls = findViewById(R.id.layout_controls);
@ -303,6 +358,19 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
};
videoControls.setProgressUpdateListener { position, bufferedPosition ->
val currentTime = position.formatDuration()
val currentDuration = duration.formatDuration()
_control_time.text = currentTime;
_control_time_fullscreen.text = currentTime;
_control_duration.text = currentDuration;
_control_duration_fullscreen.text = currentDuration;
_time_bar_fullscreen.setDuration(duration);
_time_bar.setDuration(duration);
_time_bar_fullscreen.setPosition(position);
_time_bar.setPosition(position);
_time_bar_fullscreen.setBufferedPosition(bufferedPosition);
_time_bar.setBufferedPosition(bufferedPosition);
onTimeBarChanged.emit(position, bufferedPosition);
if(!_currentChapterLoopActive)
@ -512,8 +580,24 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
onSourceChanged.emit(videoSource, audioSource, resume);
}
private fun updatePlayPause() {
if (exoPlayer?.player?.isPlaying == true) {
_control_pause.visibility = View.VISIBLE
_control_play.visibility = View.GONE
_control_pause_fullscreen.visibility = View.VISIBLE
_control_play_fullscreen.visibility = View.GONE
} else {
_control_pause.visibility = View.GONE
_control_play.visibility = View.VISIBLE
_control_pause_fullscreen.visibility = View.GONE
_control_play_fullscreen.visibility = View.VISIBLE
}
}
override fun onPlaybackStateChanged(playbackState: Int) {
Logger.v(TAG, "onPlaybackStateChanged $playbackState");
updatePlayPause()
if (playbackState == ExoPlayer.STATE_ENDED) {
if (abs(position - duration) < 2000) {
onSourceEnded.emit();

View file

@ -11,7 +11,7 @@
tools:targetApi="28">
<ImageButton
android:id="@+id/exo_minimize"
android:id="@+id/button_minimize"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
@ -30,7 +30,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent">
<ImageButton
android:id="@+id/exo_cast"
android:id="@+id/button_cast"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
@ -38,7 +38,7 @@
android:padding="12dp"
app:srcCompat="@drawable/ic_cast" />
<ImageButton
android:id="@+id/exo_rotate_lock"
android:id="@+id/button_rotate_lock"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
@ -46,7 +46,7 @@
android:padding="12dp"
app:srcCompat="@drawable/ic_screen_lock_rotation" />
<ImageButton
android:id="@+id/exo_loop"
android:id="@+id/button_loop"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
@ -54,7 +54,7 @@
android:padding="12dp"
app:srcCompat="@drawable/ic_loop" />
<ImageButton
android:id="@+id/exo_settings"
android:id="@+id/button_settings"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
@ -64,7 +64,7 @@
</LinearLayout>
<ImageButton
android:id="@id/button_previous"
android:id="@+id/button_previous"
android:layout_width="60dp"
android:layout_height="60dp"
android:scaleType="centerCrop"
@ -86,7 +86,7 @@
app:layout_constraintRight_toRightOf="parent">
<ImageButton
android:id="@id/exo_play"
android:id="@+id/button_play"
android:layout_width="60dp"
android:layout_height="60dp"
android:padding="10dp"
@ -94,7 +94,7 @@
app:srcCompat="@drawable/ic_play_white_nopad" />
<ImageButton
android:id="@id/exo_pause"
android:id="@+id/button_pause"
android:layout_width="60dp"
android:layout_height="60dp"
android:padding="10dp"
@ -107,7 +107,7 @@
</FrameLayout>
<ImageButton
android:id="@id/button_next"
android:id="@+id/button_next"
android:layout_width="60dp"
android:layout_height="60dp"
android:clickable="true"
@ -120,7 +120,7 @@
app:layout_constraintBottom_toBottomOf="parent" />
<ImageButton
android:id="@+id/exo_fullscreen"
android:id="@+id/button_fullscreen"
android:layout_width="55dp"
android:layout_height="40dp"
android:clickable="true"
@ -134,7 +134,7 @@
android:layout_marginBottom="18dp" />
<TextView
android:id="@id/exo_position"
android:id="@+id/text_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
@ -147,7 +147,7 @@
android:textStyle="normal"
android:layout_marginBottom="3dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toTopOf="@id/exo_progress" />
app:layout_constraintBottom_toTopOf="@id/time_progress" />
<TextView
android:id="@+id/text_divider"
@ -157,13 +157,13 @@
android:textSize="13sp"
android:layout_gravity="bottom"
android:text="/"
app:layout_constraintLeft_toRightOf="@id/exo_position"
app:layout_constraintTop_toTopOf="@id/exo_position"
app:layout_constraintBottom_toBottomOf="@id/exo_position"
app:layout_constraintLeft_toRightOf="@id/text_position"
app:layout_constraintTop_toTopOf="@id/text_position"
app:layout_constraintBottom_toBottomOf="@id/text_position"
tools:ignore="HardcodedText" />
<TextView
android:id="@id/exo_duration"
android:id="@+id/text_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
@ -175,8 +175,8 @@
android:layout_gravity="bottom"
android:textStyle="normal"
app:layout_constraintLeft_toRightOf="@id/text_divider"
app:layout_constraintTop_toTopOf="@id/exo_position"
app:layout_constraintBottom_toBottomOf="@id/exo_position"/>
app:layout_constraintTop_toTopOf="@id/text_position"
app:layout_constraintBottom_toBottomOf="@id/text_position"/>
<TextView
android:id="@+id/text_chapter_current"
@ -187,24 +187,18 @@
android:paddingRight="10dp"
android:textSize="11sp"
android:gravity="left"
app:layout_constraintLeft_toRightOf="@id/exo_duration"
app:layout_constraintTop_toTopOf="@id/exo_duration"
app:layout_constraintBottom_toBottomOf="@id/exo_duration"
app:layout_constraintRight_toLeftOf="@id/exo_fullscreen"
app:layout_constraintLeft_toRightOf="@id/text_duration"
app:layout_constraintTop_toTopOf="@id/text_duration"
app:layout_constraintBottom_toBottomOf="@id/text_duration"
app:layout_constraintRight_toLeftOf="@id/button_fullscreen"
android:ellipsize="end"
android:maxLines="1"
android:text="">
</TextView>
<androidx.media3.ui.SubtitleView
android:id="@id/exo_subtitles"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<androidx.media3.ui.DefaultTimeBar
android:id="@id/exo_progress"
android:id="@+id/time_progress"
android:layout_width="match_parent"
android:layout_height="16dp"
android:layout_marginBottom="-2dp"

View file

@ -10,7 +10,7 @@
tools:targetApi="28">
<ImageButton
android:id="@+id/exo_minimize"
android:id="@+id/button_minimize"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
@ -24,7 +24,7 @@
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/exo_title"
android:id="@+id/text_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
tools:text="Long title name"
@ -32,14 +32,14 @@
android:textSize="15dp"
android:textColor="@color/white"
android:fontFamily="@font/inter_bold"
app:layout_constraintTop_toTopOf="@id/exo_minimize"
app:layout_constraintLeft_toRightOf="@id/exo_minimize"
app:layout_constraintBottom_toTopOf="@id/exo_author"
app:layout_constraintTop_toTopOf="@id/button_minimize"
app:layout_constraintLeft_toRightOf="@id/button_minimize"
app:layout_constraintBottom_toTopOf="@id/text_author"
app:layout_constraintRight_toLeftOf="@id/button_container"
android:layout_marginTop="7dp"/>
<TextView
android:id="@+id/exo_author"
android:id="@+id/text_author"
android:layout_width="0dp"
android:layout_height="wrap_content"
tools:text="Author"
@ -47,8 +47,8 @@
android:textSize="10dp"
android:textColor="@color/gray_c3"
android:fontFamily="@font/inter_medium"
app:layout_constraintTop_toBottomOf="@id/exo_title"
app:layout_constraintLeft_toRightOf="@id/exo_minimize"
app:layout_constraintTop_toBottomOf="@id/text_title"
app:layout_constraintLeft_toRightOf="@id/button_minimize"
app:layout_constraintRight_toLeftOf="@id/button_container" />
<LinearLayout
@ -58,7 +58,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent">
<ImageButton
android:id="@+id/exo_cast"
android:id="@+id/button_cast"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
@ -66,7 +66,7 @@
android:padding="12dp"
app:srcCompat="@drawable/ic_cast" />
<ImageButton
android:id="@+id/exo_rotate_lock"
android:id="@+id/button_rotate_lock"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
@ -74,7 +74,7 @@
android:padding="12dp"
app:srcCompat="@drawable/ic_screen_lock_rotation" />
<ImageButton
android:id="@+id/exo_loop"
android:id="@+id/button_loop"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
@ -82,7 +82,7 @@
android:padding="12dp"
app:srcCompat="@drawable/ic_loop" />
<ImageButton
android:id="@+id/exo_settings"
android:id="@+id/button_settings"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
@ -92,7 +92,7 @@
</LinearLayout>
<ImageButton
android:id="@id/button_previous"
android:id="@+id/button_previous"
android:layout_width="60dp"
android:layout_height="60dp"
android:scaleType="centerCrop"
@ -114,7 +114,7 @@
app:layout_constraintRight_toRightOf="parent">
<ImageButton
android:id="@id/exo_play"
android:id="@+id/button_play"
android:layout_width="60dp"
android:layout_height="60dp"
android:padding="10dp"
@ -122,7 +122,7 @@
app:srcCompat="@drawable/ic_play_white_nopad" />
<ImageButton
android:id="@id/exo_pause"
android:id="@+id/button_pause"
android:layout_width="60dp"
android:layout_height="60dp"
android:padding="10dp"
@ -135,7 +135,7 @@
</FrameLayout>
<ImageButton
android:id="@id/button_next"
android:id="@+id/button_next"
android:layout_width="60dp"
android:layout_height="60dp"
android:clickable="true"
@ -148,7 +148,7 @@
app:layout_constraintBottom_toBottomOf="parent" />
<ImageButton
android:id="@+id/exo_fullscreen"
android:id="@+id/button_fullscreen"
android:layout_width="55dp"
android:layout_height="40dp"
android:clickable="true"
@ -163,7 +163,7 @@
android:layout_marginBottom="38dp" />
<TextView
android:id="@id/exo_position"
android:id="@+id/text_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
@ -176,7 +176,7 @@
android:textStyle="normal"
android:layout_marginBottom="3dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toTopOf="@id/exo_progress" />
app:layout_constraintBottom_toTopOf="@id/time_progress" />
<TextView
android:id="@+id/text_divider"
@ -186,13 +186,13 @@
android:textSize="13sp"
android:layout_gravity="bottom"
android:text="/"
app:layout_constraintLeft_toRightOf="@id/exo_position"
app:layout_constraintTop_toTopOf="@id/exo_position"
app:layout_constraintBottom_toBottomOf="@id/exo_position"
app:layout_constraintLeft_toRightOf="@id/text_position"
app:layout_constraintTop_toTopOf="@id/text_position"
app:layout_constraintBottom_toBottomOf="@id/text_position"
tools:ignore="HardcodedText" />
<TextView
android:id="@id/exo_duration"
android:id="@+id/text_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
@ -204,8 +204,8 @@
android:layout_gravity="bottom"
android:textStyle="normal"
app:layout_constraintLeft_toRightOf="@id/text_divider"
app:layout_constraintTop_toTopOf="@id/exo_position"
app:layout_constraintBottom_toBottomOf="@id/exo_position"/>
app:layout_constraintTop_toTopOf="@id/text_position"
app:layout_constraintBottom_toBottomOf="@id/text_position"/>
<TextView
@ -217,10 +217,10 @@
android:layout_marginTop="-2dp"
android:textSize="11sp"
android:gravity="left"
app:layout_constraintLeft_toRightOf="@id/exo_duration"
app:layout_constraintTop_toTopOf="@id/exo_duration"
app:layout_constraintBottom_toBottomOf="@id/exo_duration"
app:layout_constraintRight_toLeftOf="@id/exo_fullscreen"
app:layout_constraintLeft_toRightOf="@id/text_duration"
app:layout_constraintTop_toTopOf="@id/text_duration"
app:layout_constraintBottom_toBottomOf="@id/text_duration"
app:layout_constraintRight_toLeftOf="@id/button_fullscreen"
android:ellipsize="end"
android:maxLines="1"
android:text="">
@ -228,7 +228,7 @@
</TextView>
<androidx.media3.ui.DefaultTimeBar
android:id="@id/exo_progress"
android:id="@+id/time_progress"
android:layout_width="match_parent"
android:layout_height="12dp"
android:layout_marginStart="20dp"