Loop support, Improve add to queue behavior, home retry, fix history search pager, defaults progressbar

This commit is contained in:
Kelvin 2023-12-06 19:46:09 +01:00
commit b284176072
13 changed files with 123 additions and 7 deletions

View file

@ -181,7 +181,7 @@ class Settings : FragmentedStorageFileJson() {
@FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 6) @FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 6)
var progressBar: Boolean = false; var progressBar: Boolean = true;
@FormField(R.string.clear_hidden, FieldForm.BUTTON, R.string.clear_hidden_description, 8) @FormField(R.string.clear_hidden, FieldForm.BUTTON, R.string.clear_hidden_description, 8)
@ -212,7 +212,7 @@ class Settings : FragmentedStorageFileJson() {
var previewFeedItems: Boolean = true; var previewFeedItems: Boolean = true;
@FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 6) @FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 6)
var progressBar: Boolean = false; var progressBar: Boolean = true;
fun getSearchFeedStyle(): FeedStyle { fun getSearchFeedStyle(): FeedStyle {
@ -230,7 +230,7 @@ class Settings : FragmentedStorageFileJson() {
class ChannelSettings { class ChannelSettings {
@FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 6) @FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 6)
var progressBar: Boolean = false; var progressBar: Boolean = true;
} }
@FormField(R.string.subscriptions, "group", R.string.configure_how_your_subscriptions_works_and_feels, 4) @FormField(R.string.subscriptions, "group", R.string.configure_how_your_subscriptions_works_and_feels, 4)
@ -252,7 +252,7 @@ class Settings : FragmentedStorageFileJson() {
var previewFeedItems: Boolean = true; var previewFeedItems: Boolean = true;
@FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 6) @FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 6)
var progressBar: Boolean = false; var progressBar: Boolean = true;
@FormField(R.string.fetch_on_app_boot, FieldForm.TOGGLE, R.string.shortly_after_opening_the_app_start_fetching_subscriptions, 7) @FormField(R.string.fetch_on_app_boot, FieldForm.TOGGLE, R.string.shortly_after_opening_the_app_start_fetching_subscriptions, 7)
@Serializable(with = FlexibleBooleanSerializer::class) @Serializable(with = FlexibleBooleanSerializer::class)

View file

@ -168,7 +168,7 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
protected open fun onContentClicked(content: IPlatformContent, time: Long) { protected open fun onContentClicked(content: IPlatformContent, time: Long) {
if(content is IPlatformVideo) { if(content is IPlatformVideo) {
if (StatePlayer.instance.hasQueue) { if (StatePlayer.instance.hasQueue) {
StatePlayer.instance.addToQueue(content) StatePlayer.instance.insertToQueue(content, true);
} else { } else {
if (Settings.instance.playback.shouldResumePreview(time)) if (Settings.instance.playback.shouldResumePreview(time))
fragment.navigate<VideoDetailFragment>(content.withTimestamp(time)).maximizeVideoDetail(); fragment.navigate<VideoDetailFragment>(content.withTimestamp(time)).maximizeVideoDetail();

View file

@ -18,6 +18,7 @@ import com.futo.platformplayer.*
import com.futo.platformplayer.api.media.models.contents.IPlatformContent import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.structures.IAsyncPager import com.futo.platformplayer.api.media.structures.IAsyncPager
import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.api.media.structures.IPager
import com.futo.platformplayer.api.media.structures.PlatformContentPager
import com.futo.platformplayer.constructs.TaskHandler import com.futo.platformplayer.constructs.TaskHandler
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.HistoryVideo import com.futo.platformplayer.models.HistoryVideo
@ -181,6 +182,7 @@ class HistoryFragment : MainFragment() {
val query = _editSearch.text.toString(); val query = _editSearch.text.toString();
if (_editSearch.text.isNotEmpty()) { if (_editSearch.text.isNotEmpty()) {
setPager(StateHistory.instance.getHistorySearchPager(query)); setPager(StateHistory.instance.getHistorySearchPager(query));
//setPager(StateHistory.instance.getHistorySearchPager(query));
} else { } else {
setPager(StateHistory.instance.getHistoryPager()); setPager(StateHistory.instance.getHistoryPager());
} }

View file

@ -100,7 +100,7 @@ class StateHistory {
return _historyDBStore.getObjectPager(); return _historyDBStore.getObjectPager();
} }
fun getHistorySearchPager(query: String): IPager<HistoryVideo> { fun getHistorySearchPager(query: String): IPager<HistoryVideo> {
return _historyDBStore.queryLikeObjectPager(DBHistory.Index::url, "%${query}%", 10); return _historyDBStore.queryLikeObjectPager(DBHistory.Index::name, "%${query}%", 10);
} }
fun getHistoryIndexByUrl(url: String): DBHistory.Index? { fun getHistoryIndexByUrl(url: String): DBHistory.Index? {
return historyIndex[url]; return historyIndex[url];

View file

@ -40,6 +40,7 @@ import com.futo.platformplayer.models.ImageVariable
import com.futo.platformplayer.stores.* import com.futo.platformplayer.stores.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import okhttp3.internal.concat import okhttp3.internal.concat
import java.lang.Thread.sleep
import java.time.OffsetDateTime import java.time.OffsetDateTime
import kotlin.reflect.jvm.internal.impl.builtins.jvm.JavaToKotlinClassMap.PlatformMutabilityMapping import kotlin.reflect.jvm.internal.impl.builtins.jvm.JavaToKotlinClassMap.PlatformMutabilityMapping
import kotlin.streams.asSequence import kotlin.streams.asSequence
@ -405,7 +406,12 @@ class StatePlatform {
val deferred: List<Pair<IPlatformClient, Deferred<IPager<IPlatformContent>?>>> = clients.map { val deferred: List<Pair<IPlatformClient, Deferred<IPager<IPlatformContent>?>>> = clients.map {
return@map Pair(it, scope.async(Dispatchers.IO) { return@map Pair(it, scope.async(Dispatchers.IO) {
try { try {
val searchResult = it.fromPool(_pagerClientPool).getHome(); var searchResult = it.fromPool(_pagerClientPool).getHome();
if(searchResult.getResults().size == 0) {
Logger.i(TAG, "No home results, retrying");
sleep(500);
searchResult = it.fromPool(_pagerClientPool).getHome();
}
return@async searchResult; return@async searchResult;
} catch(ex: Throwable) { } catch(ex: Throwable) {
Logger.e(TAG, "getHomeRefresh", ex); Logger.e(TAG, "getHomeRefresh", ex);

View file

@ -37,6 +37,7 @@ class StatePlayer {
//Video Status //Video Status
var rotationLock : Boolean = false; var rotationLock : Boolean = false;
var loopVideo : Boolean = false;
val isPlaying: Boolean get() = _exoplayer?.player?.playWhenReady ?: false; val isPlaying: Boolean get() = _exoplayer?.player?.playWhenReady ?: false;
@ -286,6 +287,31 @@ class StatePlayer {
} }
onQueueChanged.emit(true); onQueueChanged.emit(true);
} }
fun insertToQueue(video: IPlatformVideo, playNow: Boolean = false) {
synchronized(_queue) {
if(_queue.isEmpty()) {
setQueueType(TYPE_QUEUE);
currentVideo?.let {
_queue.add(it);
}
}
if(_queue.isEmpty())
_queue.add(video);
else
_queue.add(_queuePosition.coerceAtLeast(0).coerceAtMost(_queue.size - 1), video);
if (queueShuffle) {
addToShuffledQueue(video);
}
if (_queuePosition < 0) {
_queuePosition = 0;
}
}
onQueueChanged.emit(true);
if(playNow)
setQueuePosition(video);
}
fun setQueuePosition(video: IPlatformVideo) { fun setQueuePosition(video: IPlatformVideo) {
synchronized(_queue) { synchronized(_queue) {
if (getCurrentQueueItem() == video) { if (getCurrentQueueItem() == video) {
@ -348,6 +374,8 @@ class StatePlayer {
} }
fun getNextQueueItem() : IPlatformVideo? { fun getNextQueueItem() : IPlatformVideo? {
if(loopVideo)
return currentVideo;
synchronized(_queue) { synchronized(_queue) {
val shuffledQueue = _queueShuffled; val shuffledQueue = _queueShuffled;
val queue = if (queueShuffle && shuffledQueue != null) { val queue = if (queueShuffle && shuffledQueue != null) {
@ -375,6 +403,8 @@ class StatePlayer {
} }
}; };
fun nextQueueItem(withoutRemoval: Boolean = false) : IPlatformVideo? { fun nextQueueItem(withoutRemoval: Boolean = false) : IPlatformVideo? {
if(loopVideo)
return currentVideo;
synchronized(_queue) { synchronized(_queue) {
if (_queue.isEmpty()) if (_queue.isEmpty())
return null; return null;

View file

@ -26,6 +26,7 @@ class CastView : ConstraintLayout {
private val _thumbnail: ImageView; private val _thumbnail: ImageView;
private val _buttonMinimize: ImageButton; private val _buttonMinimize: ImageButton;
private val _buttonSettings: ImageButton; private val _buttonSettings: ImageButton;
private val _buttonLoop: ImageButton;
private val _buttonPlay: ImageButton; private val _buttonPlay: ImageButton;
private val _buttonPause: ImageButton; private val _buttonPause: ImageButton;
private val _buttonCast: CastButton; private val _buttonCast: CastButton;
@ -49,6 +50,7 @@ class CastView : ConstraintLayout {
_thumbnail = findViewById(R.id.image_thumbnail); _thumbnail = findViewById(R.id.image_thumbnail);
_buttonMinimize = findViewById(R.id.button_minimize); _buttonMinimize = findViewById(R.id.button_minimize);
_buttonSettings = findViewById(R.id.button_settings); _buttonSettings = findViewById(R.id.button_settings);
_buttonLoop = findViewById(R.id.button_loop);
_buttonPlay = findViewById(R.id.button_play); _buttonPlay = findViewById(R.id.button_play);
_buttonPause = findViewById(R.id.button_pause); _buttonPause = findViewById(R.id.button_pause);
_buttonCast = findViewById(R.id.button_cast); _buttonCast = findViewById(R.id.button_cast);
@ -65,6 +67,12 @@ class CastView : ConstraintLayout {
StateCasting.instance.videoSeekTo(d.expectedCurrentTime + it / 1000); StateCasting.instance.videoSeekTo(d.expectedCurrentTime + it / 1000);
}; };
_buttonLoop.setOnClickListener {
StatePlayer.instance.loopVideo = !StatePlayer.instance.loopVideo;
_buttonLoop.setImageResource(if(StatePlayer.instance.loopVideo) R.drawable.ic_loop_active else R.drawable.ic_loop);
}
_buttonLoop.setImageResource(if(StatePlayer.instance.loopVideo) R.drawable.ic_loop_active else R.drawable.ic_loop);
_timeBar.addListener(object : OnScrubListener { _timeBar.addListener(object : OnScrubListener {
override fun onScrubStart(timeBar: TimeBar, position: Long) { override fun onScrubStart(timeBar: TimeBar, position: Long) {
StateCasting.instance.videoSeekTo(position.toDouble()); StateCasting.instance.videoSeekTo(position.toDouble());

View file

@ -68,6 +68,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
private val _control_videosettings: ImageButton; private val _control_videosettings: ImageButton;
private val _control_minimize: ImageButton; private val _control_minimize: ImageButton;
private val _control_rotate_lock: ImageButton; private val _control_rotate_lock: ImageButton;
private val _control_loop: ImageButton;
private val _control_cast: ImageButton; private val _control_cast: ImageButton;
private val _control_play: ImageButton; private val _control_play: ImageButton;
private val _control_chapter: TextView; private val _control_chapter: TextView;
@ -77,6 +78,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
private val _control_videosettings_fullscreen: ImageButton; private val _control_videosettings_fullscreen: ImageButton;
private val _control_minimize_fullscreen: ImageButton; private val _control_minimize_fullscreen: ImageButton;
private val _control_rotate_lock_fullscreen: ImageButton; private val _control_rotate_lock_fullscreen: ImageButton;
private val _control_loop_fullscreen: ImageButton;
private val _control_cast_fullscreen: ImageButton; private val _control_cast_fullscreen: ImageButton;
private val _control_play_fullscreen: ImageButton; private val _control_play_fullscreen: ImageButton;
private val _time_bar_fullscreen: TimeBar; private val _time_bar_fullscreen: TimeBar;
@ -128,6 +130,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
_control_videosettings = videoControls.findViewById(R.id.exo_settings); _control_videosettings = videoControls.findViewById(R.id.exo_settings);
_control_minimize = videoControls.findViewById(R.id.exo_minimize); _control_minimize = videoControls.findViewById(R.id.exo_minimize);
_control_rotate_lock = videoControls.findViewById(R.id.exo_rotate_lock); _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_cast = videoControls.findViewById(R.id.exo_cast);
_control_play = videoControls.findViewById(com.google.android.exoplayer2.ui.R.id.exo_play); _control_play = videoControls.findViewById(com.google.android.exoplayer2.ui.R.id.exo_play);
_time_bar = videoControls.findViewById(com.google.android.exoplayer2.ui.R.id.exo_progress); _time_bar = videoControls.findViewById(com.google.android.exoplayer2.ui.R.id.exo_progress);
@ -138,6 +141,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
_control_minimize_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_minimize); _control_minimize_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_minimize);
_control_videosettings_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_settings); _control_videosettings_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_settings);
_control_rotate_lock_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_rotate_lock); _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_cast_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_cast);
_control_play_fullscreen = videoControls.findViewById(com.google.android.exoplayer2.ui.R.id.exo_play); _control_play_fullscreen = videoControls.findViewById(com.google.android.exoplayer2.ui.R.id.exo_play);
_control_chapter_fullscreen = _videoControls_fullscreen.findViewById(R.id.text_chapter_current); _control_chapter_fullscreen = _videoControls_fullscreen.findViewById(R.id.text_chapter_current);
@ -244,6 +248,16 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
UIDialogs.showCastingDialog(context); UIDialogs.showCastingDialog(context);
}; };
_control_loop.setOnClickListener {
StatePlayer.instance.loopVideo = !StatePlayer.instance.loopVideo;
updateLoopVideoUI();
}
_control_loop_fullscreen.setOnClickListener {
StatePlayer.instance.loopVideo = !StatePlayer.instance.loopVideo;
updateLoopVideoUI();
}
_control_minimize_fullscreen.setOnClickListener { _control_minimize_fullscreen.setOnClickListener {
onMinimize.emit(this); onMinimize.emit(this);
}; };
@ -273,6 +287,8 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
} }
} }
updateLoopVideoUI();
if(!isInEditMode) { if(!isInEditMode) {
gestureControl.hideControls(); gestureControl.hideControls();
} }
@ -555,6 +571,17 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
_control_rotate_lock.setImageResource(R.drawable.ic_screen_lock_rotation); _control_rotate_lock.setImageResource(R.drawable.ic_screen_lock_rotation);
} }
} }
fun updateLoopVideoUI() {
if(StatePlayer.instance.loopVideo) {
_control_loop.setImageResource(R.drawable.ic_loop_active);
_control_loop_fullscreen.setImageResource(R.drawable.ic_loop_active);
}
else {
_control_loop.setImageResource(R.drawable.ic_loop);
_control_loop_fullscreen.setImageResource(R.drawable.ic_loop);
}
}
fun setGestureSoundFactor(soundFactor: Float) { fun setGestureSoundFactor(soundFactor: Float) {
gestureControl.setSoundFactor(soundFactor); gestureControl.setSoundFactor(soundFactor);

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M292.31,840L160,707.69L292.31,575.39L320.61,604.15L237.08,687.69L692.31,687.69L692.31,527.69L732.31,527.69L732.31,727.69L237.08,727.69L320.61,811.23L292.31,840ZM227.69,432.31L227.69,232.31L722.92,232.31L639.39,148.77L667.69,120L800,252.31L667.69,384.61L639.39,355.85L722.92,272.31L267.69,272.31L267.69,432.31L227.69,432.31Z"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@color/colorPrimary"
android:pathData="M292.31,840L160,707.69L292.31,575.39L320.61,604.15L237.08,687.69L692.31,687.69L692.31,527.69L732.31,527.69L732.31,727.69L237.08,727.69L320.61,811.23L292.31,840ZM227.69,432.31L227.69,232.31L722.92,232.31L639.39,148.77L667.69,120L800,252.31L667.69,384.61L639.39,355.85L722.92,272.31L267.69,272.31L267.69,432.31L227.69,432.31Z"/>
</vector>

View file

@ -45,6 +45,14 @@
android:clickable="true" android:clickable="true"
android:padding="12dp" android:padding="12dp"
app:srcCompat="@drawable/ic_screen_lock_rotation" /> app:srcCompat="@drawable/ic_screen_lock_rotation" />
<ImageButton
android:id="@+id/exo_loop"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
android:clickable="true"
android:padding="12dp"
app:srcCompat="@drawable/ic_loop" />
<ImageButton <ImageButton
android:id="@+id/exo_settings" android:id="@+id/exo_settings"
android:layout_width="50dp" android:layout_width="50dp"

View file

@ -73,6 +73,14 @@
android:clickable="true" android:clickable="true"
android:padding="12dp" android:padding="12dp"
app:srcCompat="@drawable/ic_screen_lock_rotation" /> app:srcCompat="@drawable/ic_screen_lock_rotation" />
<ImageButton
android:id="@+id/exo_loop"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
android:clickable="true"
android:padding="12dp"
app:srcCompat="@drawable/ic_loop" />
<ImageButton <ImageButton
android:id="@+id/exo_settings" android:id="@+id/exo_settings"
android:layout_width="50dp" android:layout_width="50dp"

View file

@ -54,6 +54,15 @@
android:clickable="true" android:clickable="true"
android:padding="12dp" android:padding="12dp"
app:srcCompat="@drawable/ic_cast" /> app:srcCompat="@drawable/ic_cast" />
<ImageButton
android:id="@+id/button_loop"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
android:clickable="true"
android:padding="12dp"
app:srcCompat="@drawable/ic_loop" />
<ImageButton <ImageButton
android:id="@+id/button_settings" android:id="@+id/button_settings"
android:layout_width="50dp" android:layout_width="50dp"