mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-09-27 11:49:03 +00:00
Merge branch 'master' of gitlab.futo.org:videostreaming/grayjay
This commit is contained in:
commit
cf95791dcc
17 changed files with 210 additions and 65 deletions
|
@ -29,6 +29,7 @@ import com.futo.platformplayer.states.StateUpdate
|
||||||
import com.futo.platformplayer.stores.FragmentedStorage
|
import com.futo.platformplayer.stores.FragmentedStorage
|
||||||
import com.futo.platformplayer.stores.FragmentedStorageFileJson
|
import com.futo.platformplayer.stores.FragmentedStorageFileJson
|
||||||
import com.futo.platformplayer.views.FeedStyle
|
import com.futo.platformplayer.views.FeedStyle
|
||||||
|
import com.futo.platformplayer.views.fields.AdvancedField
|
||||||
import com.futo.platformplayer.views.fields.DropdownFieldOptionsId
|
import com.futo.platformplayer.views.fields.DropdownFieldOptionsId
|
||||||
import com.futo.platformplayer.views.fields.FieldForm
|
import com.futo.platformplayer.views.fields.FieldForm
|
||||||
import com.futo.platformplayer.views.fields.FormField
|
import com.futo.platformplayer.views.fields.FormField
|
||||||
|
@ -175,6 +176,10 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
@FormField(R.string.advanced_settings, FieldForm.TOGGLE, R.string.advanced_settings_description, -1, "advancedSettings")
|
||||||
|
var advancedSettings: Boolean = false;
|
||||||
|
|
||||||
@FormField(R.string.language, "group", -1, 0)
|
@FormField(R.string.language, "group", -1, 0)
|
||||||
var language = LanguageSettings();
|
var language = LanguageSettings();
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@ -221,10 +226,11 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
@FormField(R.string.show_home_filters_plugin_names, FieldForm.TOGGLE, R.string.show_home_filters_plugin_names_description, 5)
|
@FormField(R.string.show_home_filters_plugin_names, FieldForm.TOGGLE, R.string.show_home_filters_plugin_names_description, 5)
|
||||||
var showHomeFiltersPluginNames: Boolean = false;
|
var showHomeFiltersPluginNames: Boolean = false;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.preview_feed_items, FieldForm.TOGGLE, R.string.preview_feed_items_description, 6)
|
@FormField(R.string.preview_feed_items, FieldForm.TOGGLE, R.string.preview_feed_items_description, 6)
|
||||||
var previewFeedItems: Boolean = true;
|
var previewFeedItems: Boolean = true;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@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 = true;
|
var progressBar: Boolean = true;
|
||||||
|
|
||||||
|
@ -253,9 +259,11 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
@DropdownFieldOptionsId(R.array.feed_style)
|
@DropdownFieldOptionsId(R.array.feed_style)
|
||||||
var searchFeedStyle: Int = 1;
|
var searchFeedStyle: Int = 1;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.preview_feed_items, FieldForm.TOGGLE, R.string.preview_feed_items_description, 5)
|
@FormField(R.string.preview_feed_items, FieldForm.TOGGLE, R.string.preview_feed_items_description, 5)
|
||||||
var previewFeedItems: Boolean = true;
|
var previewFeedItems: Boolean = true;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@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 = true;
|
var progressBar: Boolean = true;
|
||||||
|
|
||||||
|
@ -277,6 +285,7 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
@Serializable
|
@Serializable
|
||||||
class ChannelSettings {
|
class ChannelSettings {
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@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 = true;
|
var progressBar: Boolean = true;
|
||||||
}
|
}
|
||||||
|
@ -302,16 +311,20 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
@FormField(R.string.use_subscription_exchange, FieldForm.TOGGLE, R.string.use_subscription_exchange_description, 6)
|
@FormField(R.string.use_subscription_exchange, FieldForm.TOGGLE, R.string.use_subscription_exchange_description, 6)
|
||||||
var useSubscriptionExchange: Boolean = false;
|
var useSubscriptionExchange: Boolean = false;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.preview_feed_items, FieldForm.TOGGLE, R.string.preview_feed_items_description, 6)
|
@FormField(R.string.preview_feed_items, FieldForm.TOGGLE, R.string.preview_feed_items_description, 6)
|
||||||
var previewFeedItems: Boolean = true;
|
var previewFeedItems: Boolean = true;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 7)
|
@FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 7)
|
||||||
var progressBar: Boolean = true;
|
var progressBar: Boolean = true;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.fetch_on_app_boot, FieldForm.TOGGLE, R.string.shortly_after_opening_the_app_start_fetching_subscriptions, 8)
|
@FormField(R.string.fetch_on_app_boot, FieldForm.TOGGLE, R.string.shortly_after_opening_the_app_start_fetching_subscriptions, 8)
|
||||||
@Serializable(with = FlexibleBooleanSerializer::class)
|
@Serializable(with = FlexibleBooleanSerializer::class)
|
||||||
var fetchOnAppBoot: Boolean = true;
|
var fetchOnAppBoot: Boolean = true;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.fetch_on_tab_opened, FieldForm.TOGGLE, R.string.fetch_on_tab_opened_description, 9)
|
@FormField(R.string.fetch_on_tab_opened, FieldForm.TOGGLE, R.string.fetch_on_tab_opened_description, 9)
|
||||||
var fetchOnTabOpen: Boolean = true;
|
var fetchOnTabOpen: Boolean = true;
|
||||||
|
|
||||||
|
@ -342,13 +355,16 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
@FormField(R.string.show_watch_metrics, FieldForm.TOGGLE, R.string.show_watch_metrics_description, 12)
|
@FormField(R.string.show_watch_metrics, FieldForm.TOGGLE, R.string.show_watch_metrics_description, 12)
|
||||||
var showWatchMetrics: Boolean = false;
|
var showWatchMetrics: Boolean = false;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.track_playtime_locally, FieldForm.TOGGLE, R.string.track_playtime_locally_description, 13)
|
@FormField(R.string.track_playtime_locally, FieldForm.TOGGLE, R.string.track_playtime_locally_description, 13)
|
||||||
var allowPlaytimeTracking: Boolean = true;
|
var allowPlaytimeTracking: Boolean = true;
|
||||||
|
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.always_reload_from_cache, FieldForm.TOGGLE, R.string.always_reload_from_cache_description, 14)
|
@FormField(R.string.always_reload_from_cache, FieldForm.TOGGLE, R.string.always_reload_from_cache_description, 14)
|
||||||
var alwaysReloadFromCache: Boolean = false;
|
var alwaysReloadFromCache: Boolean = false;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.peek_channel_contents, FieldForm.TOGGLE, R.string.peek_channel_contents_description, 15)
|
@FormField(R.string.peek_channel_contents, FieldForm.TOGGLE, R.string.peek_channel_contents_description, 15)
|
||||||
var peekChannelContents: Boolean = false;
|
var peekChannelContents: Boolean = false;
|
||||||
|
|
||||||
|
@ -425,9 +441,11 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
var preferredPreviewQuality: Int = 5;
|
var preferredPreviewQuality: Int = 5;
|
||||||
fun getPreferredPreviewQualityPixelCount(): Int = preferedQualityToPixels(preferredPreviewQuality);
|
fun getPreferredPreviewQualityPixelCount(): Int = preferedQualityToPixels(preferredPreviewQuality);
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.simplify_sources, FieldForm.TOGGLE, R.string.simplify_sources_description, 4)
|
@FormField(R.string.simplify_sources, FieldForm.TOGGLE, R.string.simplify_sources_description, 4)
|
||||||
var simplifySources: Boolean = true;
|
var simplifySources: Boolean = true;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.always_allow_reverse_landscape_auto_rotate, FieldForm.TOGGLE, R.string.always_allow_reverse_landscape_auto_rotate_description, 5)
|
@FormField(R.string.always_allow_reverse_landscape_auto_rotate, FieldForm.TOGGLE, R.string.always_allow_reverse_landscape_auto_rotate_description, 5)
|
||||||
var alwaysAllowReverseLandscapeAutoRotate: Boolean = true
|
var alwaysAllowReverseLandscapeAutoRotate: Boolean = true
|
||||||
|
|
||||||
|
@ -438,6 +456,7 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
fun isBackgroundContinue() = backgroundPlay == 1;
|
fun isBackgroundContinue() = backgroundPlay == 1;
|
||||||
fun isBackgroundPictureInPicture() = backgroundPlay == 2;
|
fun isBackgroundPictureInPicture() = backgroundPlay == 2;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.resume_after_preview, FieldForm.DROPDOWN, R.string.when_watching_a_video_in_preview_mode_resume_at_the_position_when_opening_the_video_code, 7)
|
@FormField(R.string.resume_after_preview, FieldForm.DROPDOWN, R.string.when_watching_a_video_in_preview_mode_resume_at_the_position_when_opening_the_video_code, 7)
|
||||||
@DropdownFieldOptionsId(R.array.resume_after_preview)
|
@DropdownFieldOptionsId(R.array.resume_after_preview)
|
||||||
var resumeAfterPreview: Int = 1;
|
var resumeAfterPreview: Int = 1;
|
||||||
|
@ -464,14 +483,10 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.live_chat_webview, FieldForm.TOGGLE, R.string.use_the_live_chat_web_window_when_available_over_native_implementation, 9)
|
@FormField(R.string.live_chat_webview, FieldForm.TOGGLE, R.string.use_the_live_chat_web_window_when_available_over_native_implementation, 9)
|
||||||
var useLiveChatWindow: Boolean = true;
|
var useLiveChatWindow: Boolean = true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@FormField(R.string.background_switch_audio, FieldForm.TOGGLE, R.string.background_switch_audio_description, 10)
|
|
||||||
var backgroundSwitchToAudio: Boolean = true;
|
|
||||||
|
|
||||||
@FormField(R.string.restart_after_audio_focus_loss, FieldForm.DROPDOWN, R.string.restart_playback_when_gaining_audio_focus_after_a_loss, 11)
|
@FormField(R.string.restart_after_audio_focus_loss, FieldForm.DROPDOWN, R.string.restart_playback_when_gaining_audio_focus_after_a_loss, 11)
|
||||||
@DropdownFieldOptionsId(R.array.restart_playback_after_loss)
|
@DropdownFieldOptionsId(R.array.restart_playback_after_loss)
|
||||||
var restartPlaybackAfterLoss: Int = 1;
|
var restartPlaybackAfterLoss: Int = 1;
|
||||||
|
@ -497,6 +512,7 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
@FormField(R.string.autoplay, FieldForm.TOGGLE, R.string.autoplay, 21)
|
@FormField(R.string.autoplay, FieldForm.TOGGLE, R.string.autoplay, 21)
|
||||||
var autoplay: Boolean = false;
|
var autoplay: Boolean = false;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.delete_watchlist_on_finish, FieldForm.TOGGLE, R.string.delete_watchlist_on_finish_description, 22)
|
@FormField(R.string.delete_watchlist_on_finish, FieldForm.TOGGLE, R.string.delete_watchlist_on_finish_description, 22)
|
||||||
var deleteFromWatchLaterAuto: Boolean = true;
|
var deleteFromWatchLaterAuto: Boolean = true;
|
||||||
|
|
||||||
|
@ -530,6 +546,7 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
@FormField(R.string.default_recommendations, FieldForm.TOGGLE, R.string.default_recommendations_description, 0)
|
@FormField(R.string.default_recommendations, FieldForm.TOGGLE, R.string.default_recommendations_description, 0)
|
||||||
var recommendationsDefault: Boolean = false;
|
var recommendationsDefault: Boolean = false;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.hide_recommendations, FieldForm.TOGGLE, R.string.hide_recommendations_description, 0)
|
@FormField(R.string.hide_recommendations, FieldForm.TOGGLE, R.string.hide_recommendations_description, 0)
|
||||||
var hideRecommendations: Boolean = false;
|
var hideRecommendations: Boolean = false;
|
||||||
|
|
||||||
|
@ -566,10 +583,12 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
var preferredAudioQuality: Int = 1;
|
var preferredAudioQuality: Int = 1;
|
||||||
fun isHighBitrateDefault(): Boolean = preferredAudioQuality > 0;
|
fun isHighBitrateDefault(): Boolean = preferredAudioQuality > 0;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.byte_range_download, FieldForm.TOGGLE, R.string.attempt_to_utilize_byte_ranges, 4)
|
@FormField(R.string.byte_range_download, FieldForm.TOGGLE, R.string.attempt_to_utilize_byte_ranges, 4)
|
||||||
@Serializable(with = FlexibleBooleanSerializer::class)
|
@Serializable(with = FlexibleBooleanSerializer::class)
|
||||||
var byteRangeDownload: Boolean = true;
|
var byteRangeDownload: Boolean = true;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.byte_range_concurrency, FieldForm.DROPDOWN, R.string.number_of_concurrent_threads_to_multiply_download_speeds_from_throttled_sources, 5)
|
@FormField(R.string.byte_range_concurrency, FieldForm.DROPDOWN, R.string.number_of_concurrent_threads_to_multiply_download_speeds_from_throttled_sources, 5)
|
||||||
@DropdownFieldOptionsId(R.array.thread_count)
|
@DropdownFieldOptionsId(R.array.thread_count)
|
||||||
var byteRangeConcurrency: Int = 3;
|
var byteRangeConcurrency: Int = 3;
|
||||||
|
@ -599,11 +618,12 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
@Serializable(with = FlexibleBooleanSerializer::class)
|
@Serializable(with = FlexibleBooleanSerializer::class)
|
||||||
var keepScreenOn: Boolean = true;
|
var keepScreenOn: Boolean = true;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.always_proxy_requests, FieldForm.TOGGLE, R.string.always_proxy_requests_description, 3)
|
@FormField(R.string.always_proxy_requests, FieldForm.TOGGLE, R.string.always_proxy_requests_description, 3)
|
||||||
@Serializable(with = FlexibleBooleanSerializer::class)
|
@Serializable(with = FlexibleBooleanSerializer::class)
|
||||||
var alwaysProxyRequests: Boolean = false;
|
var alwaysProxyRequests: Boolean = false;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.allow_ipv6, FieldForm.TOGGLE, R.string.allow_ipv6_description, 4)
|
@FormField(R.string.allow_ipv6, FieldForm.TOGGLE, R.string.allow_ipv6_description, 4)
|
||||||
@Serializable(with = FlexibleBooleanSerializer::class)
|
@Serializable(with = FlexibleBooleanSerializer::class)
|
||||||
var allowIpv6: Boolean = true;
|
var allowIpv6: Boolean = true;
|
||||||
|
@ -675,9 +695,11 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
@Serializable
|
@Serializable
|
||||||
class Plugins {
|
class Plugins {
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.check_disabled_plugin_updates, FieldForm.TOGGLE, R.string.check_disabled_plugin_updates_description, -1)
|
@FormField(R.string.check_disabled_plugin_updates, FieldForm.TOGGLE, R.string.check_disabled_plugin_updates_description, -1)
|
||||||
var checkDisabledPluginsForUpdates: Boolean = false;
|
var checkDisabledPluginsForUpdates: Boolean = false;
|
||||||
|
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.clear_cookies_on_logout, FieldForm.TOGGLE, R.string.clears_cookies_when_you_log_out, 0)
|
@FormField(R.string.clear_cookies_on_logout, FieldForm.TOGGLE, R.string.clears_cookies_when_you_log_out, 0)
|
||||||
var clearCookiesOnLogout: Boolean = true;
|
var clearCookiesOnLogout: Boolean = true;
|
||||||
|
|
||||||
|
@ -896,8 +918,10 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
var other = Other();
|
var other = Other();
|
||||||
@Serializable
|
@Serializable
|
||||||
class Other {
|
class Other {
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.playlist_delete_confirmation, FieldForm.TOGGLE, R.string.playlist_delete_confirmation_description, 2)
|
@FormField(R.string.playlist_delete_confirmation, FieldForm.TOGGLE, R.string.playlist_delete_confirmation_description, 2)
|
||||||
var playlistDeleteConfirmation: Boolean = true;
|
var playlistDeleteConfirmation: Boolean = true;
|
||||||
|
@AdvancedField
|
||||||
@FormField(R.string.playlist_allow_dups, FieldForm.TOGGLE, R.string.playlist_allow_dups_description, 3)
|
@FormField(R.string.playlist_allow_dups, FieldForm.TOGGLE, R.string.playlist_allow_dups_description, 3)
|
||||||
var playlistAllowDups: Boolean = true;
|
var playlistAllowDups: Boolean = true;
|
||||||
|
|
||||||
|
|
|
@ -150,7 +150,7 @@ class DownloadsFragment : MainFragment() {
|
||||||
spinnerSortBy.adapter = ArrayAdapter(context, R.layout.spinner_item_simple, resources.getStringArray(R.array.downloads_sortby_array)).also {
|
spinnerSortBy.adapter = ArrayAdapter(context, R.layout.spinner_item_simple, resources.getStringArray(R.array.downloads_sortby_array)).also {
|
||||||
it.setDropDownViewResource(R.layout.spinner_dropdownitem_simple);
|
it.setDropDownViewResource(R.layout.spinner_dropdownitem_simple);
|
||||||
};
|
};
|
||||||
val options = listOf("nameAsc", "nameDesc", "downloadDateAsc", "downloadDateDesc", "releasedAsc", "releasedDesc");
|
val options = listOf("nameAsc", "nameDesc", "downloadDateAsc", "downloadDateDesc", "releasedAsc", "releasedDesc", "sizeAsc", "sizeDesc");
|
||||||
spinnerSortBy.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
spinnerSortBy.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onItemSelected(parent: AdapterView<*>, view: View?, pos: Int, id: Long) {
|
override fun onItemSelected(parent: AdapterView<*>, view: View?, pos: Int, id: Long) {
|
||||||
when(pos) {
|
when(pos) {
|
||||||
|
@ -160,6 +160,8 @@ class DownloadsFragment : MainFragment() {
|
||||||
3 -> ordering.setAndSave("downloadDateDesc")
|
3 -> ordering.setAndSave("downloadDateDesc")
|
||||||
4 -> ordering.setAndSave("releasedAsc")
|
4 -> ordering.setAndSave("releasedAsc")
|
||||||
5 -> ordering.setAndSave("releasedDesc")
|
5 -> ordering.setAndSave("releasedDesc")
|
||||||
|
6 -> ordering.setAndSave("sizeAsc")
|
||||||
|
7 -> ordering.setAndSave("sizeDesc")
|
||||||
else -> ordering.setAndSave("")
|
else -> ordering.setAndSave("")
|
||||||
}
|
}
|
||||||
updateContentFilters()
|
updateContentFilters()
|
||||||
|
@ -257,6 +259,8 @@ class DownloadsFragment : MainFragment() {
|
||||||
"nameDesc" -> vidsToReturn.sortedByDescending { it.name.lowercase() }
|
"nameDesc" -> vidsToReturn.sortedByDescending { it.name.lowercase() }
|
||||||
"releasedAsc" -> vidsToReturn.sortedBy { it.datetime ?: OffsetDateTime.MAX }
|
"releasedAsc" -> vidsToReturn.sortedBy { it.datetime ?: OffsetDateTime.MAX }
|
||||||
"releasedDesc" -> vidsToReturn.sortedByDescending { it.datetime ?: OffsetDateTime.MIN }
|
"releasedDesc" -> vidsToReturn.sortedByDescending { it.datetime ?: OffsetDateTime.MIN }
|
||||||
|
"sizeAsc" -> vidsToReturn.sortedBy { it.videoSource.sumOf { it.fileSize } + it.audioSource.sumOf { it.fileSize } }
|
||||||
|
"sizeDesc" -> vidsToReturn.sortedByDescending { it.videoSource.sumOf { it.fileSize } + it.audioSource.sumOf { it.fileSize } }
|
||||||
else -> vidsToReturn
|
else -> vidsToReturn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1115,7 +1115,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
when (Settings.instance.playback.backgroundPlay) {
|
when (Settings.instance.playback.backgroundPlay) {
|
||||||
0 -> handlePause();
|
0 -> handlePause();
|
||||||
1 -> {
|
1 -> {
|
||||||
if(!(video?.isLive ?: false) && Settings.instance.playback.backgroundSwitchToAudio)
|
if(!(video?.isLive ?: false))
|
||||||
_player.switchToAudioMode();
|
_player.switchToAudioMode();
|
||||||
StatePlayer.instance.startOrUpdateMediaSession(context, video);
|
StatePlayer.instance.startOrUpdateMediaSession(context, video);
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,8 +131,13 @@ class StateHistory {
|
||||||
fun getHistoryPager(): IPager<HistoryVideo> {
|
fun getHistoryPager(): IPager<HistoryVideo> {
|
||||||
return _historyDBStore.getObjectPager();
|
return _historyDBStore.getObjectPager();
|
||||||
}
|
}
|
||||||
fun getHistorySearchPager(query: String): IPager<HistoryVideo> {
|
fun getHistorySearchPager(query: String, withAuthor: Boolean = false): IPager<HistoryVideo> {
|
||||||
return _historyDBStore.queryLikeObjectPager(DBHistory.Index::name, "%${query}%", 10);
|
return if(!withAuthor)
|
||||||
|
_historyDBStore.queryLikeObjectPager(DBHistory.Index::name, "%${query}%", 10)
|
||||||
|
else
|
||||||
|
_historyDBStore.queryLikeObjectPager(DBHistory.Index::name, "%${query}%", 10)
|
||||||
|
//_historyDBStore.queryLike2ObjectPager(DBHistory.Index::name, DBHistory.Index::auth,"%${query}%", 10)
|
||||||
|
//TODO: See if we can include author name?
|
||||||
}
|
}
|
||||||
fun getHistoryIndexByUrl(url: String): DBHistory.Index? {
|
fun getHistoryIndexByUrl(url: String): DBHistory.Index? {
|
||||||
return historyIndex[url];
|
return historyIndex[url];
|
||||||
|
|
|
@ -423,17 +423,25 @@ class StatePlaylists {
|
||||||
class PlaylistBackup: ReconstructStore<Playlist>() {
|
class PlaylistBackup: ReconstructStore<Playlist>() {
|
||||||
override fun toReconstruction(obj: Playlist): String {
|
override fun toReconstruction(obj: Playlist): String {
|
||||||
val items = ArrayList<String>();
|
val items = ArrayList<String>();
|
||||||
items.add(obj.name);
|
items.add(obj.name + ":::" + obj.id);
|
||||||
items.addAll(obj.videos.map { it.url });
|
items.addAll(obj.videos.map { it.url });
|
||||||
return items.map { it.replace("\n","") }.joinToString("\n");
|
return items.map { it.replace("\n","") }.joinToString("\n");
|
||||||
}
|
}
|
||||||
override suspend fun toObject(id: String, backup: String, reconstructionBuilder: Builder, importCache: ImportCache?): Playlist {
|
override suspend fun toObject(id: String, backup: String, reconstructionBuilder: Builder, importCache: ImportCache?): Playlist {
|
||||||
|
var idToUse = id;
|
||||||
val items = backup.split("\n");
|
val items = backup.split("\n");
|
||||||
if(items.size <= 0) {
|
if(items.size <= 0) {
|
||||||
throw IllegalStateException("Cannot reconstructor playlist ${id}");
|
throw IllegalStateException("Cannot reconstructor playlist ${id}");
|
||||||
}
|
}
|
||||||
|
|
||||||
val name = items[0];
|
var name = items[0];
|
||||||
|
if(name.contains(":::")){
|
||||||
|
val splitIndex = name.indexOf(":::");
|
||||||
|
val foundId = name.substring(splitIndex + 3);
|
||||||
|
if(!foundId.isNullOrEmpty())
|
||||||
|
idToUse = foundId;
|
||||||
|
name = name.substring(0, splitIndex);
|
||||||
|
}
|
||||||
val videos = items.drop(1).filter { it.isNotEmpty() }.map {
|
val videos = items.drop(1).filter { it.isNotEmpty() }.map {
|
||||||
try {
|
try {
|
||||||
val videoUrl = it;
|
val videoUrl = it;
|
||||||
|
@ -465,7 +473,7 @@ class StatePlaylists {
|
||||||
throw ReconstructionException(name, "${name}:[${it}] ${ex.message}", ex);
|
throw ReconstructionException(name, "${name}:[${it}] ${ex.message}", ex);
|
||||||
}
|
}
|
||||||
}.filter { it != null }.map { it!! }
|
}.filter { it != null }.map { it!! }
|
||||||
return Playlist(id, name, videos);
|
return Playlist(idToUse, name, videos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -329,8 +329,19 @@ class StateSubscriptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(StateSubscriptionGroups.instance.hasSubscriptionGroup(sub.channel.url))
|
if(StateSubscriptionGroups.instance.hasSubscriptionGroup(sub.channel.url)) {
|
||||||
getSubscriptionOtherOrCreate(sub.channel.url, sub.channel.name, sub.channel.thumbnail);
|
val subGroups = StateSubscriptionGroups.instance.getSubscriptionGroups().filter { it.urls.contains(sub.channel.url) };
|
||||||
|
for(group in subGroups) {
|
||||||
|
group.urls.remove(sub.channel.url);
|
||||||
|
StateSubscriptionGroups.instance.updateSubscriptionGroup(group);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
getSubscriptionOtherOrCreate(
|
||||||
|
sub.channel.url,
|
||||||
|
sub.channel.name,
|
||||||
|
sub.channel.thumbnail
|
||||||
|
); */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return sub;
|
return sub;
|
||||||
}
|
}
|
||||||
|
|
|
@ -236,6 +236,11 @@ class StateSync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val _lockSubscriptions = Any();
|
||||||
|
private val _lockSubscriptionGroups = Any();
|
||||||
|
private val _lockPlaylists = Any();
|
||||||
|
private val _lockWatchlater = Any();
|
||||||
|
|
||||||
private fun handleData(session: SyncSession, opcode: UByte, subOpcode: UByte, data: ByteBuffer) {
|
private fun handleData(session: SyncSession, opcode: UByte, subOpcode: UByte, data: ByteBuffer) {
|
||||||
val remotePublicKey = session.remotePublicKey
|
val remotePublicKey = session.remotePublicKey
|
||||||
when (subOpcode) {
|
when (subOpcode) {
|
||||||
|
@ -319,7 +324,9 @@ class StateSync {
|
||||||
data.get(dataBody);
|
data.get(dataBody);
|
||||||
val json = String(dataBody, Charsets.UTF_8);
|
val json = String(dataBody, Charsets.UTF_8);
|
||||||
val subPackage = Serializer.json.decodeFromString<SyncSubscriptionsPackage>(json);
|
val subPackage = Serializer.json.decodeFromString<SyncSubscriptionsPackage>(json);
|
||||||
|
synchronized(_lockSubscriptions) {
|
||||||
handleSyncSubscriptionPackage(session, subPackage);
|
handleSyncSubscriptionPackage(session, subPackage);
|
||||||
|
}
|
||||||
|
|
||||||
if(subPackage.subscriptions.size > 0) {
|
if(subPackage.subscriptions.size > 0) {
|
||||||
val newestSub = subPackage.subscriptions.maxOf { it.creationTime };
|
val newestSub = subPackage.subscriptions.maxOf { it.creationTime };
|
||||||
|
@ -339,6 +346,7 @@ class StateSync {
|
||||||
val pack = Serializer.json.decodeFromString<SyncSubscriptionGroupsPackage>(json);
|
val pack = Serializer.json.decodeFromString<SyncSubscriptionGroupsPackage>(json);
|
||||||
|
|
||||||
var lastSubgroupChange = OffsetDateTime.MIN;
|
var lastSubgroupChange = OffsetDateTime.MIN;
|
||||||
|
synchronized(_lockSubscriptionGroups) {
|
||||||
for(group in pack.groups){
|
for(group in pack.groups){
|
||||||
if(group.lastChange > lastSubgroupChange)
|
if(group.lastChange > lastSubgroupChange)
|
||||||
lastSubgroupChange = group.lastChange;
|
lastSubgroupChange = group.lastChange;
|
||||||
|
@ -356,6 +364,7 @@ class StateSync {
|
||||||
StateSubscriptionGroups.instance.updateSubscriptionGroup(existing, false, true);
|
StateSubscriptionGroups.instance.updateSubscriptionGroup(existing, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for(removal in pack.groupRemovals) {
|
for(removal in pack.groupRemovals) {
|
||||||
val creation = StateSubscriptionGroups.instance.getSubscriptionGroup(removal.key);
|
val creation = StateSubscriptionGroups.instance.getSubscriptionGroup(removal.key);
|
||||||
val removalTime = removal.value.sToOffsetDateTimeUTC();
|
val removalTime = removal.value.sToOffsetDateTimeUTC();
|
||||||
|
@ -370,6 +379,7 @@ class StateSync {
|
||||||
val json = String(dataBody, Charsets.UTF_8);
|
val json = String(dataBody, Charsets.UTF_8);
|
||||||
val pack = Serializer.json.decodeFromString<SyncPlaylistsPackage>(json);
|
val pack = Serializer.json.decodeFromString<SyncPlaylistsPackage>(json);
|
||||||
|
|
||||||
|
synchronized(_lockPlaylists) {
|
||||||
for(playlist in pack.playlists) {
|
for(playlist in pack.playlists) {
|
||||||
val existing = StatePlaylists.instance.getPlaylist(playlist.id);
|
val existing = StatePlaylists.instance.getPlaylist(playlist.id);
|
||||||
|
|
||||||
|
@ -384,6 +394,7 @@ class StateSync {
|
||||||
StatePlaylists.instance.createOrUpdatePlaylist(existing, false);
|
StatePlaylists.instance.createOrUpdatePlaylist(existing, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for(removal in pack.playlistRemovals) {
|
for(removal in pack.playlistRemovals) {
|
||||||
val creation = StatePlaylists.instance.getPlaylist(removal.key);
|
val creation = StatePlaylists.instance.getPlaylist(removal.key);
|
||||||
val removalTime = removal.value.sToOffsetDateTimeUTC();
|
val removalTime = removal.value.sToOffsetDateTimeUTC();
|
||||||
|
@ -402,6 +413,7 @@ class StateSync {
|
||||||
Logger.i(TAG, "SyncWatchLater received ${pack.videos.size} (${pack.videoAdds?.size}, ${pack.videoRemovals?.size})");
|
Logger.i(TAG, "SyncWatchLater received ${pack.videos.size} (${pack.videoAdds?.size}, ${pack.videoRemovals?.size})");
|
||||||
|
|
||||||
val allExisting = StatePlaylists.instance.getWatchLater();
|
val allExisting = StatePlaylists.instance.getWatchLater();
|
||||||
|
synchronized(_lockWatchlater) {
|
||||||
for(video in pack.videos) {
|
for(video in pack.videos) {
|
||||||
val existing = allExisting.firstOrNull { it.url == video.url };
|
val existing = allExisting.firstOrNull { it.url == video.url };
|
||||||
val time = if(pack.videoAdds != null && pack.videoAdds.containsKey(video.url)) (pack.videoAdds[video.url] ?: 0).sToOffsetDateTimeUTC() else OffsetDateTime.MIN;
|
val time = if(pack.videoAdds != null && pack.videoAdds.containsKey(video.url)) (pack.videoAdds[video.url] ?: 0).sToOffsetDateTimeUTC() else OffsetDateTime.MIN;
|
||||||
|
@ -412,6 +424,7 @@ class StateSync {
|
||||||
StatePlaylists.instance.setWatchLaterAddTime(video.url, time);
|
StatePlaylists.instance.setWatchLaterAddTime(video.url, time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for(removal in pack.videoRemovals) {
|
for(removal in pack.videoRemovals) {
|
||||||
val watchLater = allExisting.firstOrNull { it.url == removal.key } ?: continue;
|
val watchLater = allExisting.firstOrNull { it.url == removal.key } ?: continue;
|
||||||
val creation = StatePlaylists.instance.getWatchLaterRemovalTime(watchLater.url) ?: OffsetDateTime.MIN;
|
val creation = StatePlaylists.instance.getWatchLaterRemovalTime(watchLater.url) ?: OffsetDateTime.MIN;
|
||||||
|
|
|
@ -274,10 +274,17 @@ class ManagedDBStore<I: ManagedDBIndex<T>, T, D: ManagedDBDatabase<T, I, DA>, DA
|
||||||
val queryStr = "SELECT * FROM ${descriptor.table_name} WHERE ${field} LIKE ? ${_orderSQL} LIMIT ? OFFSET ?";
|
val queryStr = "SELECT * FROM ${descriptor.table_name} WHERE ${field} LIKE ? ${_orderSQL} LIMIT ? OFFSET ?";
|
||||||
val query = SimpleSQLiteQuery(queryStr, arrayOf(obj, pageSize, page * pageSize));
|
val query = SimpleSQLiteQuery(queryStr, arrayOf(obj, pageSize, page * pageSize));
|
||||||
return deserializeIndexes(dbDaoBase.getMultiple(query));
|
return deserializeIndexes(dbDaoBase.getMultiple(query));
|
||||||
|
}fun queryLike2Page(field: String, field2: String, obj: String, page: Int, pageSize: Int): List<I> {
|
||||||
|
val queryStr = "SELECT * FROM ${descriptor.table_name} WHERE ${field} LIKE ? OR ${field2} LIKE ? ${_orderSQL} LIMIT ? OFFSET ?";
|
||||||
|
val query = SimpleSQLiteQuery(queryStr, arrayOf(obj, obj, pageSize, page * pageSize));
|
||||||
|
return deserializeIndexes(dbDaoBase.getMultiple(query));
|
||||||
}
|
}
|
||||||
fun queryLikeObjectPage(field: String, obj: String, page: Int, pageSize: Int): List<T> {
|
fun queryLikeObjectPage(field: String, obj: String, page: Int, pageSize: Int): List<T> {
|
||||||
return convertObjects(queryLikePage(field, obj, page, pageSize));
|
return convertObjects(queryLikePage(field, obj, page, pageSize));
|
||||||
}
|
}
|
||||||
|
fun queryLike2ObjectPage(field: String, field2: String, obj: String, page: Int, pageSize: Int): List<T> {
|
||||||
|
return convertObjects(queryLike2Page(field, field2, obj, page, pageSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//Query Page Objects
|
//Query Page Objects
|
||||||
|
@ -336,6 +343,13 @@ class ManagedDBStore<I: ManagedDBIndex<T>, T, D: ManagedDBDatabase<T, I, DA>, DA
|
||||||
queryLikePage(field, obj, it - 1, pageSize);
|
queryLikePage(field, obj, it - 1, pageSize);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
fun queryLike2Pager(field: KProperty<*>, field2: KProperty<*>, obj: String, pageSize: Int): IPager<I> = queryLike2Pager(validateFieldName(field), validateFieldName(field2), obj, pageSize);
|
||||||
|
fun queryLike2Pager(field: String, field2: String, obj: String, pageSize: Int): IPager<I> {
|
||||||
|
return AdhocPager({
|
||||||
|
Logger.i("ManagedDBStore", "Next Page [query: ${obj}](${it}) ${pageSize}");
|
||||||
|
queryLike2Page(field, field2, obj, it - 1, pageSize);
|
||||||
|
});
|
||||||
|
}
|
||||||
fun queryLikeObjectPager(field: KProperty<*>, obj: String, pageSize: Int): IPager<T> = queryLikeObjectPager(validateFieldName(field), obj, pageSize);
|
fun queryLikeObjectPager(field: KProperty<*>, obj: String, pageSize: Int): IPager<T> = queryLikeObjectPager(validateFieldName(field), obj, pageSize);
|
||||||
fun queryLikeObjectPager(field: String, obj: String, pageSize: Int): IPager<T> {
|
fun queryLikeObjectPager(field: String, obj: String, pageSize: Int): IPager<T> {
|
||||||
return AdhocPager({
|
return AdhocPager({
|
||||||
|
@ -344,6 +358,14 @@ class ManagedDBStore<I: ManagedDBIndex<T>, T, D: ManagedDBDatabase<T, I, DA>, DA
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun queryLike2ObjectPager(field: KProperty<*>, field2: KProperty<*>, obj: String, pageSize: Int): IPager<T> = queryLike2ObjectPager(validateFieldName(field), validateFieldName(field2), obj, pageSize);
|
||||||
|
fun queryLike2ObjectPager(field: String, field2: String, obj: String, pageSize: Int): IPager<T> {
|
||||||
|
return AdhocPager({
|
||||||
|
Logger.i("ManagedDBStore", "Next Page [query: ${obj}](${it}) ${pageSize}");
|
||||||
|
queryLike2ObjectPage(field, field2, obj, it - 1, pageSize);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//Query Pager with convert
|
//Query Pager with convert
|
||||||
fun <X> queryPager(field: KProperty<*>, obj: Any, pageSize: Int, convert: (I)->X): IPager<X> = queryPager(validateFieldName(field), obj, pageSize, convert);
|
fun <X> queryPager(field: KProperty<*>, obj: Any, pageSize: Int, convert: (I)->X): IPager<X> = queryPager(validateFieldName(field), obj, pageSize, convert);
|
||||||
fun <X> queryPager(field: String, obj: Any, pageSize: Int, convert: (I)->X): IPager<X> {
|
fun <X> queryPager(field: String, obj: Any, pageSize: Int, convert: (I)->X): IPager<X> {
|
||||||
|
|
|
@ -41,6 +41,8 @@ class ButtonField : BigButton, IField {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
override var isAdvanced: Boolean = false;
|
||||||
|
|
||||||
//private val _title : TextView;
|
//private val _title : TextView;
|
||||||
//private val _subtitle : TextView;
|
//private val _subtitle : TextView;
|
||||||
|
|
||||||
|
@ -89,7 +91,7 @@ class ButtonField : BigButton, IField {
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
override fun fromField(obj : Any, field : Field, formField: FormField?) : ButtonField {
|
override fun fromField(obj : Any, field : Field, formField: FormField?, advanced: Boolean) : ButtonField {
|
||||||
throw IllegalStateException("ButtonField should only be used for methods");
|
throw IllegalStateException("ButtonField should only be used for methods");
|
||||||
}
|
}
|
||||||
override fun setField() {
|
override fun setField() {
|
||||||
|
|
|
@ -40,6 +40,8 @@ class DropdownField : TableRow, IField {
|
||||||
|
|
||||||
override var reference: Any? = null;
|
override var reference: Any? = null;
|
||||||
|
|
||||||
|
override var isAdvanced: Boolean = false;
|
||||||
|
|
||||||
override val onChanged = Event3<IField, Any, Any>();
|
override val onChanged = Event3<IField, Any, Any>();
|
||||||
|
|
||||||
override val value: Any? get() = _selected;
|
override val value: Any? get() = _selected;
|
||||||
|
@ -112,7 +114,7 @@ class DropdownField : TableRow, IField {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fromField(obj: Any, field: Field, formField: FormField?) : DropdownField {
|
override fun fromField(obj: Any, field: Field, formField: FormField?, advanced: Boolean) : DropdownField {
|
||||||
this._field = field;
|
this._field = field;
|
||||||
this._obj = obj;
|
this._obj = obj;
|
||||||
|
|
||||||
|
@ -133,6 +135,9 @@ class DropdownField : TableRow, IField {
|
||||||
_description.visibility = View.GONE;
|
_description.visibility = View.GONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val advancedFieldAttr = field.getAnnotation(AdvancedField::class.java)
|
||||||
|
if(advancedFieldAttr != null || advanced)
|
||||||
|
isAdvanced = true;
|
||||||
|
|
||||||
_options = (field.getAnnotation(DropdownFieldOptions::class.java)?.options ?:
|
_options = (field.getAnnotation(DropdownFieldOptions::class.java)?.options ?:
|
||||||
field.getAnnotation(DropdownFieldOptionsId::class.java)?.optionsId?.let { resources.getStringArray(it) } ?:
|
field.getAnnotation(DropdownFieldOptionsId::class.java)?.optionsId?.let { resources.getStringArray(it) } ?:
|
||||||
|
|
|
@ -4,6 +4,10 @@ import com.futo.platformplayer.constructs.Event2
|
||||||
import com.futo.platformplayer.constructs.Event3
|
import com.futo.platformplayer.constructs.Event3
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class AdvancedField();
|
||||||
|
|
||||||
|
|
||||||
@Target(AnnotationTarget.FIELD, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
@ -22,6 +26,8 @@ interface IField {
|
||||||
val obj : Any?;
|
val obj : Any?;
|
||||||
val field : Field?;
|
val field : Field?;
|
||||||
|
|
||||||
|
val isAdvanced: Boolean;
|
||||||
|
|
||||||
val value: Any?;
|
val value: Any?;
|
||||||
val onChanged : Event3<IField, Any, Any>;
|
val onChanged : Event3<IField, Any, Any>;
|
||||||
|
|
||||||
|
@ -29,7 +35,7 @@ interface IField {
|
||||||
|
|
||||||
val searchContent: String?;
|
val searchContent: String?;
|
||||||
|
|
||||||
fun fromField(obj : Any, field : Field, formField: FormField? = null) : IField;
|
fun fromField(obj : Any, field : Field, formField: FormField? = null, advanced: Boolean = false) : IField;
|
||||||
fun setField();
|
fun setField();
|
||||||
|
|
||||||
fun setValue(value: Any);
|
fun setValue(value: Any);
|
||||||
|
|
|
@ -37,6 +37,8 @@ class FieldForm : LinearLayout {
|
||||||
|
|
||||||
private var _fields : List<IField> = arrayListOf();
|
private var _fields : List<IField> = arrayListOf();
|
||||||
|
|
||||||
|
private var _showAdvancedSettings: Boolean = false;
|
||||||
|
|
||||||
constructor(context : Context, attrs : AttributeSet? = null) : super(context, attrs) {
|
constructor(context : Context, attrs : AttributeSet? = null) : super(context, attrs) {
|
||||||
inflate(context, R.layout.field_form, this);
|
inflate(context, R.layout.field_form, this);
|
||||||
_containerSearch = findViewById(R.id.container_search);
|
_containerSearch = findViewById(R.id.container_search);
|
||||||
|
@ -58,6 +60,11 @@ class FieldForm : LinearLayout {
|
||||||
if(field is GroupField) {
|
if(field is GroupField) {
|
||||||
updateSettingsVisibility(field);
|
updateSettingsVisibility(field);
|
||||||
} else if(field is View && field.descriptor != null) {
|
} else if(field is View && field.descriptor != null) {
|
||||||
|
if(field.isAdvanced && !_showAdvancedSettings)
|
||||||
|
{
|
||||||
|
field.visibility = View.GONE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
val txt = field.searchContent?.lowercase();
|
val txt = field.searchContent?.lowercase();
|
||||||
if (txt != null) {
|
if (txt != null) {
|
||||||
val visible = isGroupMatch || txt.contains(query);
|
val visible = isGroupMatch || txt.contains(query);
|
||||||
|
@ -66,11 +73,16 @@ class FieldForm : LinearLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if(group != null) {
|
if(group != null) {
|
||||||
group.visibility = if (groupVisible) View.VISIBLE else View.GONE;
|
group.visibility = if (groupVisible) View.VISIBLE else View.GONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setShowAdvancedSettings(show: Boolean) {
|
||||||
|
_showAdvancedSettings = show;
|
||||||
|
updateSettingsVisibility();
|
||||||
|
}
|
||||||
fun setSearchQuery(query: String) {
|
fun setSearchQuery(query: String) {
|
||||||
_editSearch.setText(query);
|
_editSearch.setText(query);
|
||||||
updateSettingsVisibility();
|
updateSettingsVisibility();
|
||||||
|
@ -92,13 +104,22 @@ class FieldForm : LinearLayout {
|
||||||
throw java.lang.IllegalStateException("Only views can be IFields");
|
throw java.lang.IllegalStateException("Only views can be IFields");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(field is ToggleField && field.descriptor?.id == "advancedSettings") {
|
||||||
|
_showAdvancedSettings = field.value as Boolean;
|
||||||
|
}
|
||||||
|
|
||||||
_fieldsContainer.addView(field as View);
|
_fieldsContainer.addView(field as View);
|
||||||
field.onChanged.subscribe { a1, a2, _ ->
|
field.onChanged.subscribe { a1, a2, _ ->
|
||||||
|
if(field is ToggleField && field.descriptor?.id == "advancedSettings") {
|
||||||
|
setShowAdvancedSettings((a2 as Boolean));
|
||||||
|
}
|
||||||
|
|
||||||
onChanged.emit(a1, a2);
|
onChanged.emit(a1, a2);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_fields = newFields;
|
_fields = newFields;
|
||||||
|
|
||||||
|
updateSettingsVisibility();
|
||||||
onLoaded?.invoke();
|
onLoaded?.invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,10 +288,12 @@ class FieldForm : LinearLayout {
|
||||||
for(prop in objFields) {
|
for(prop in objFields) {
|
||||||
prop.first.javaField!!.isAccessible = true;
|
prop.first.javaField!!.isAccessible = true;
|
||||||
|
|
||||||
|
val advanced = prop.first.hasAnnotation<AdvancedField>();
|
||||||
|
|
||||||
val field = when(prop.second.type) {
|
val field = when(prop.second.type) {
|
||||||
GROUP -> GroupField(context).fromField(obj, prop.first.javaField!!, prop.second);
|
GROUP -> GroupField(context).fromField(obj, prop.first.javaField!!, prop.second);
|
||||||
DROPDOWN -> DropdownField(context).fromField(obj, prop.first.javaField!!, prop.second);
|
DROPDOWN -> DropdownField(context).fromField(obj, prop.first.javaField!!, prop.second, advanced);
|
||||||
TOGGLE -> ToggleField(context).fromField(obj, prop.first.javaField!!, prop.second);
|
TOGGLE -> ToggleField(context).fromField(obj, prop.first.javaField!!, prop.second, advanced);
|
||||||
READONLYTEXT -> ReadOnlyTextField(context).fromField(obj, prop.first.javaField!!, prop.second);
|
READONLYTEXT -> ReadOnlyTextField(context).fromField(obj, prop.first.javaField!!, prop.second);
|
||||||
else -> throw java.lang.IllegalStateException("Unknown field type ${prop.second.type} for ${prop.second.title}")
|
else -> throw java.lang.IllegalStateException("Unknown field type ${prop.second.type} for ${prop.second.title}")
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ class GroupField : LinearLayout, IField {
|
||||||
private val _container : LinearLayout;
|
private val _container : LinearLayout;
|
||||||
|
|
||||||
override var reference: Any? = null;
|
override var reference: Any? = null;
|
||||||
|
override var isAdvanced: Boolean = false;
|
||||||
|
|
||||||
override val value: Any? = null;
|
override val value: Any? = null;
|
||||||
|
|
||||||
|
@ -100,7 +101,7 @@ class GroupField : LinearLayout, IField {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fromField(obj: Any, field: Field, formField: FormField?) : GroupField {
|
override fun fromField(obj: Any, field: Field, formField: FormField?, advanced: Boolean) : GroupField {
|
||||||
this._field = field;
|
this._field = field;
|
||||||
this._obj = obj;
|
this._obj = obj;
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ class ReadOnlyTextField : TableRow, IField {
|
||||||
override val onChanged = Event3<IField, Any, Any>();
|
override val onChanged = Event3<IField, Any, Any>();
|
||||||
|
|
||||||
override var reference: Any? = null;
|
override var reference: Any? = null;
|
||||||
|
override var isAdvanced: Boolean = false;
|
||||||
|
|
||||||
override val value: Any? = null;
|
override val value: Any? = null;
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ class ReadOnlyTextField : TableRow, IField {
|
||||||
|
|
||||||
override fun setValue(value: Any) {}
|
override fun setValue(value: Any) {}
|
||||||
|
|
||||||
override fun fromField(obj : Any, field : Field, formField: FormField?) : ReadOnlyTextField {
|
override fun fromField(obj : Any, field : Field, formField: FormField?, advanced: Boolean) : ReadOnlyTextField {
|
||||||
this._field = field;
|
this._field = field;
|
||||||
this._obj = obj;
|
this._obj = obj;
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ class ToggleField : TableRow, IField {
|
||||||
private var _lastValue: Boolean = false;
|
private var _lastValue: Boolean = false;
|
||||||
|
|
||||||
override var reference: Any? = null;
|
override var reference: Any? = null;
|
||||||
|
override var isAdvanced: Boolean = false;
|
||||||
|
|
||||||
override val onChanged = Event3<IField, Any, Any>();
|
override val onChanged = Event3<IField, Any, Any>();
|
||||||
|
|
||||||
|
@ -75,7 +76,7 @@ class ToggleField : TableRow, IField {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fromField(obj : Any, field : Field, formField: FormField?) : ToggleField {
|
override fun fromField(obj : Any, field : Field, formField: FormField?, advanced: Boolean) : ToggleField {
|
||||||
this._field = field;
|
this._field = field;
|
||||||
this._obj = obj;
|
this._obj = obj;
|
||||||
|
|
||||||
|
@ -87,6 +88,12 @@ class ToggleField : TableRow, IField {
|
||||||
else
|
else
|
||||||
_title.text = field.name;
|
_title.text = field.name;
|
||||||
|
|
||||||
|
val advancedFieldAttr = field.getAnnotation(AdvancedField::class.java)
|
||||||
|
if(advancedFieldAttr != null || advanced) {
|
||||||
|
Logger.w("ToggleField", "Found advanced field: " + field.name);
|
||||||
|
isAdvanced = true;
|
||||||
|
}
|
||||||
|
|
||||||
if(attrField == null || attrField.subtitle == -1)
|
if(attrField == null || attrField.subtitle == -1)
|
||||||
_description.visibility = View.GONE;
|
_description.visibility = View.GONE;
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -252,12 +252,22 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
|
||||||
fun switchToVideoMode() {
|
fun switchToVideoMode() {
|
||||||
Logger.i(TAG, "Switching to Video Mode");
|
Logger.i(TAG, "Switching to Video Mode");
|
||||||
isAudioMode = false;
|
isAudioMode = false;
|
||||||
loadSelectedSources(playing, true);
|
val player = exoPlayer ?: return
|
||||||
|
player.player.trackSelectionParameters =
|
||||||
|
player.player.trackSelectionParameters
|
||||||
|
.buildUpon()
|
||||||
|
.setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, isAudioMode)
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
fun switchToAudioMode() {
|
fun switchToAudioMode() {
|
||||||
Logger.i(TAG, "Switching to Audio Mode");
|
Logger.i(TAG, "Switching to Audio Mode");
|
||||||
isAudioMode = true;
|
isAudioMode = true;
|
||||||
loadSelectedSources(playing, true);
|
val player = exoPlayer ?: return
|
||||||
|
player.player.trackSelectionParameters =
|
||||||
|
player.player.trackSelectionParameters
|
||||||
|
.buildUpon()
|
||||||
|
.setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, isAudioMode)
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun seekTo(ms: Long) {
|
fun seekTo(ms: Long) {
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
<string name="channel">Channel</string>
|
<string name="channel">Channel</string>
|
||||||
<string name="home">Home</string>
|
<string name="home">Home</string>
|
||||||
<string name="progress_bar">Progress Bar</string>
|
<string name="progress_bar">Progress Bar</string>
|
||||||
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
|
<string name="advanced_settings_description">If advanced settings should be shown, this exposes additional settings to finetune your experience.</string>
|
||||||
<string name="progress_bar_description">If a historical progress bar should be shown</string>
|
<string name="progress_bar_description">If a historical progress bar should be shown</string>
|
||||||
<string name="hide_hidden_from_search">Hide hidden from home in search</string>
|
<string name="hide_hidden_from_search">Hide hidden from home in search</string>
|
||||||
<string name="hide_hidden_from_search_description">Hide videos and creators hidden from home also in search results</string>
|
<string name="hide_hidden_from_search_description">Hide videos and creators hidden from home also in search results</string>
|
||||||
|
@ -427,7 +429,6 @@
|
||||||
<string name="seek_offset">Seek duration</string>
|
<string name="seek_offset">Seek duration</string>
|
||||||
<string name="seek_offset_description">Fast-Forward / Fast-Rewind duration</string>
|
<string name="seek_offset_description">Fast-Forward / Fast-Rewind duration</string>
|
||||||
<string name="background_switch_audio">Switch to Audio in Background</string>
|
<string name="background_switch_audio">Switch to Audio in Background</string>
|
||||||
<string name="background_switch_audio_description">Optimize bandwidth usage by switching to audio-only stream in background if available, may cause stutter</string>
|
|
||||||
<string name="subscription_group_menu">Groups</string>
|
<string name="subscription_group_menu">Groups</string>
|
||||||
<string name="show_subscription_group">Show Subscription Groups</string>
|
<string name="show_subscription_group">Show Subscription Groups</string>
|
||||||
<string name="use_subscription_exchange">Use Subscription Exchange (Experimental)</string>
|
<string name="use_subscription_exchange">Use Subscription Exchange (Experimental)</string>
|
||||||
|
@ -994,6 +995,8 @@
|
||||||
<item>Download Date (Newest)</item>
|
<item>Download Date (Newest)</item>
|
||||||
<item>Release Date (Oldest)</item>
|
<item>Release Date (Oldest)</item>
|
||||||
<item>Release Date (Newest)</item>
|
<item>Release Date (Newest)</item>
|
||||||
|
<item>Size (Smallest)</item>
|
||||||
|
<item>Size (Largest)</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="playlists_sortby_array">
|
<string-array name="playlists_sortby_array">
|
||||||
<item>Name (Ascending)</item>
|
<item>Name (Ascending)</item>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue