From 29f1bef0997e4b0a17fb457643aff398b878fb76 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Wed, 4 Jun 2025 20:43:37 +0200 Subject: [PATCH] Advanced settings option, Playlist id saving for exports and backups, Sync synchronization to prevent dups --- .../java/com/futo/platformplayer/Settings.kt | 31 +++++++- .../platformplayer/states/StatePlaylists.kt | 14 +++- .../futo/platformplayer/states/StateSync.kt | 79 +++++++++++-------- .../views/fields/ButtonField.kt | 4 +- .../views/fields/DropdownField.kt | 7 +- .../futo/platformplayer/views/fields/Field.kt | 8 +- .../platformplayer/views/fields/FieldForm.kt | 37 +++++++-- .../platformplayer/views/fields/GroupField.kt | 3 +- .../views/fields/ReadOnlyTextField.kt | 3 +- .../views/fields/ToggleField.kt | 9 ++- app/src/main/res/values/strings.xml | 2 + 11 files changed, 146 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/Settings.kt b/app/src/main/java/com/futo/platformplayer/Settings.kt index 7f827f66..363eb917 100644 --- a/app/src/main/java/com/futo/platformplayer/Settings.kt +++ b/app/src/main/java/com/futo/platformplayer/Settings.kt @@ -29,6 +29,7 @@ import com.futo.platformplayer.states.StateUpdate import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.FragmentedStorageFileJson 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.FieldForm 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) var language = LanguageSettings(); @Serializable @@ -224,7 +229,7 @@ class Settings : FragmentedStorageFileJson() { @FormField(R.string.preview_feed_items, FieldForm.TOGGLE, R.string.preview_feed_items_description, 6) var previewFeedItems: Boolean = true; - + @AdvancedField @FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 6) var progressBar: Boolean = true; @@ -256,6 +261,7 @@ class Settings : FragmentedStorageFileJson() { @FormField(R.string.preview_feed_items, FieldForm.TOGGLE, R.string.preview_feed_items_description, 5) var previewFeedItems: Boolean = true; + @AdvancedField @FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 6) var progressBar: Boolean = true; @@ -277,6 +283,7 @@ class Settings : FragmentedStorageFileJson() { @Serializable class ChannelSettings { + @AdvancedField @FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 6) var progressBar: Boolean = true; } @@ -302,16 +309,20 @@ class Settings : FragmentedStorageFileJson() { @FormField(R.string.use_subscription_exchange, FieldForm.TOGGLE, R.string.use_subscription_exchange_description, 6) var useSubscriptionExchange: Boolean = false; + @AdvancedField @FormField(R.string.preview_feed_items, FieldForm.TOGGLE, R.string.preview_feed_items_description, 6) var previewFeedItems: Boolean = true; + @AdvancedField @FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 7) 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) @Serializable(with = FlexibleBooleanSerializer::class) var fetchOnAppBoot: Boolean = true; + @AdvancedField @FormField(R.string.fetch_on_tab_opened, FieldForm.TOGGLE, R.string.fetch_on_tab_opened_description, 9) var fetchOnTabOpen: Boolean = true; @@ -342,13 +353,16 @@ class Settings : FragmentedStorageFileJson() { @FormField(R.string.show_watch_metrics, FieldForm.TOGGLE, R.string.show_watch_metrics_description, 12) var showWatchMetrics: Boolean = false; + @AdvancedField @FormField(R.string.track_playtime_locally, FieldForm.TOGGLE, R.string.track_playtime_locally_description, 13) var allowPlaytimeTracking: Boolean = true; + @AdvancedField @FormField(R.string.always_reload_from_cache, FieldForm.TOGGLE, R.string.always_reload_from_cache_description, 14) var alwaysReloadFromCache: Boolean = false; + @AdvancedField @FormField(R.string.peek_channel_contents, FieldForm.TOGGLE, R.string.peek_channel_contents_description, 15) var peekChannelContents: Boolean = false; @@ -425,9 +439,11 @@ class Settings : FragmentedStorageFileJson() { var preferredPreviewQuality: Int = 5; fun getPreferredPreviewQualityPixelCount(): Int = preferedQualityToPixels(preferredPreviewQuality); + @AdvancedField @FormField(R.string.simplify_sources, FieldForm.TOGGLE, R.string.simplify_sources_description, 4) 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) var alwaysAllowReverseLandscapeAutoRotate: Boolean = true @@ -438,6 +454,7 @@ class Settings : FragmentedStorageFileJson() { fun isBackgroundContinue() = backgroundPlay == 1; 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) @DropdownFieldOptionsId(R.array.resume_after_preview) var resumeAfterPreview: Int = 1; @@ -464,6 +481,7 @@ 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) var useLiveChatWindow: Boolean = true; @@ -497,6 +515,7 @@ class Settings : FragmentedStorageFileJson() { @FormField(R.string.autoplay, FieldForm.TOGGLE, R.string.autoplay, 21) var autoplay: Boolean = false; + @AdvancedField @FormField(R.string.delete_watchlist_on_finish, FieldForm.TOGGLE, R.string.delete_watchlist_on_finish_description, 22) var deleteFromWatchLaterAuto: Boolean = true; @@ -530,6 +549,7 @@ class Settings : FragmentedStorageFileJson() { @FormField(R.string.default_recommendations, FieldForm.TOGGLE, R.string.default_recommendations_description, 0) var recommendationsDefault: Boolean = false; + @AdvancedField @FormField(R.string.hide_recommendations, FieldForm.TOGGLE, R.string.hide_recommendations_description, 0) var hideRecommendations: Boolean = false; @@ -566,10 +586,12 @@ class Settings : FragmentedStorageFileJson() { var preferredAudioQuality: Int = 1; fun isHighBitrateDefault(): Boolean = preferredAudioQuality > 0; + @AdvancedField @FormField(R.string.byte_range_download, FieldForm.TOGGLE, R.string.attempt_to_utilize_byte_ranges, 4) @Serializable(with = FlexibleBooleanSerializer::class) 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) @DropdownFieldOptionsId(R.array.thread_count) var byteRangeConcurrency: Int = 3; @@ -599,11 +621,12 @@ class Settings : FragmentedStorageFileJson() { @Serializable(with = FlexibleBooleanSerializer::class) var keepScreenOn: Boolean = true; + @AdvancedField @FormField(R.string.always_proxy_requests, FieldForm.TOGGLE, R.string.always_proxy_requests_description, 3) @Serializable(with = FlexibleBooleanSerializer::class) var alwaysProxyRequests: Boolean = false; - + @AdvancedField @FormField(R.string.allow_ipv6, FieldForm.TOGGLE, R.string.allow_ipv6_description, 4) @Serializable(with = FlexibleBooleanSerializer::class) var allowIpv6: Boolean = true; @@ -675,9 +698,11 @@ class Settings : FragmentedStorageFileJson() { @Serializable class Plugins { + @AdvancedField @FormField(R.string.check_disabled_plugin_updates, FieldForm.TOGGLE, R.string.check_disabled_plugin_updates_description, -1) var checkDisabledPluginsForUpdates: Boolean = false; + @AdvancedField @FormField(R.string.clear_cookies_on_logout, FieldForm.TOGGLE, R.string.clears_cookies_when_you_log_out, 0) var clearCookiesOnLogout: Boolean = true; @@ -896,8 +921,10 @@ class Settings : FragmentedStorageFileJson() { var other = Other(); @Serializable class Other { + @AdvancedField @FormField(R.string.playlist_delete_confirmation, FieldForm.TOGGLE, R.string.playlist_delete_confirmation_description, 2) var playlistDeleteConfirmation: Boolean = true; + @AdvancedField @FormField(R.string.playlist_allow_dups, FieldForm.TOGGLE, R.string.playlist_allow_dups_description, 3) var playlistAllowDups: Boolean = true; diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlaylists.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlaylists.kt index c20375f2..6ebf7be6 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StatePlaylists.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StatePlaylists.kt @@ -423,17 +423,25 @@ class StatePlaylists { class PlaylistBackup: ReconstructStore() { override fun toReconstruction(obj: Playlist): String { val items = ArrayList(); - items.add(obj.name); + items.add(obj.name + ":::" + obj.id); items.addAll(obj.videos.map { it.url }); return items.map { it.replace("\n","") }.joinToString("\n"); } override suspend fun toObject(id: String, backup: String, reconstructionBuilder: Builder, importCache: ImportCache?): Playlist { + var idToUse = id; val items = backup.split("\n"); if(items.size <= 0) { 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 { try { val videoUrl = it; @@ -465,7 +473,7 @@ class StatePlaylists { throw ReconstructionException(name, "${name}:[${it}] ${ex.message}", ex); } }.filter { it != null }.map { it!! } - return Playlist(id, name, videos); + return Playlist(idToUse, name, videos); } } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/states/StateSync.kt b/app/src/main/java/com/futo/platformplayer/states/StateSync.kt index fd08165c..3cc52a2d 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StateSync.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StateSync.kt @@ -224,6 +224,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) { val remotePublicKey = session.remotePublicKey when (subOpcode) { @@ -307,7 +312,9 @@ class StateSync { data.get(dataBody); val json = String(dataBody, Charsets.UTF_8); val subPackage = Serializer.json.decodeFromString(json); - handleSyncSubscriptionPackage(session, subPackage); + synchronized(_lockSubscriptions) { + handleSyncSubscriptionPackage(session, subPackage); + } if(subPackage.subscriptions.size > 0) { val newestSub = subPackage.subscriptions.maxOf { it.creationTime }; @@ -327,21 +334,23 @@ class StateSync { val pack = Serializer.json.decodeFromString(json); var lastSubgroupChange = OffsetDateTime.MIN; - for(group in pack.groups){ - if(group.lastChange > lastSubgroupChange) - lastSubgroupChange = group.lastChange; + synchronized(_lockSubscriptionGroups) { + for(group in pack.groups){ + if(group.lastChange > lastSubgroupChange) + lastSubgroupChange = group.lastChange; - val existing = StateSubscriptionGroups.instance.getSubscriptionGroup(group.id); + val existing = StateSubscriptionGroups.instance.getSubscriptionGroup(group.id); - if(existing == null) - StateSubscriptionGroups.instance.updateSubscriptionGroup(group, false, true); - else if(existing.lastChange < group.lastChange) { - existing.name = group.name; - existing.urls = group.urls; - existing.image = group.image; - existing.priority = group.priority; - existing.lastChange = group.lastChange; - StateSubscriptionGroups.instance.updateSubscriptionGroup(existing, false, true); + if(existing == null) + StateSubscriptionGroups.instance.updateSubscriptionGroup(group, false, true); + else if(existing.lastChange < group.lastChange) { + existing.name = group.name; + existing.urls = group.urls; + existing.image = group.image; + existing.priority = group.priority; + existing.lastChange = group.lastChange; + StateSubscriptionGroups.instance.updateSubscriptionGroup(existing, false, true); + } } } for(removal in pack.groupRemovals) { @@ -358,18 +367,20 @@ class StateSync { val json = String(dataBody, Charsets.UTF_8); val pack = Serializer.json.decodeFromString(json); - for(playlist in pack.playlists) { - val existing = StatePlaylists.instance.getPlaylist(playlist.id); + synchronized(_lockPlaylists) { + for(playlist in pack.playlists) { + val existing = StatePlaylists.instance.getPlaylist(playlist.id); - if(existing == null) - StatePlaylists.instance.createOrUpdatePlaylist(playlist, false); - else if(existing.dateUpdate < playlist.dateUpdate) { - existing.dateUpdate = playlist.dateUpdate; - existing.name = playlist.name; - existing.videos = playlist.videos; - existing.dateCreation = playlist.dateCreation; - existing.datePlayed = playlist.datePlayed; - StatePlaylists.instance.createOrUpdatePlaylist(existing, false); + if(existing == null) + StatePlaylists.instance.createOrUpdatePlaylist(playlist, false); + else if(existing.dateUpdate < playlist.dateUpdate) { + existing.dateUpdate = playlist.dateUpdate; + existing.name = playlist.name; + existing.videos = playlist.videos; + existing.dateCreation = playlist.dateCreation; + existing.datePlayed = playlist.datePlayed; + StatePlaylists.instance.createOrUpdatePlaylist(existing, false); + } } } for(removal in pack.playlistRemovals) { @@ -390,14 +401,16 @@ class StateSync { Logger.i(TAG, "SyncWatchLater received ${pack.videos.size} (${pack.videoAdds?.size}, ${pack.videoRemovals?.size})"); val allExisting = StatePlaylists.instance.getWatchLater(); - for(video in pack.videos) { - 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 removalTime = StatePlaylists.instance.getWatchLaterRemovalTime(video.url) ?: OffsetDateTime.MIN; - if(existing == null && time > removalTime) { - StatePlaylists.instance.addToWatchLater(video, false); - if(time > OffsetDateTime.MIN) - StatePlaylists.instance.setWatchLaterAddTime(video.url, time); + synchronized(_lockWatchlater) { + for(video in pack.videos) { + 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 removalTime = StatePlaylists.instance.getWatchLaterRemovalTime(video.url) ?: OffsetDateTime.MIN; + if(existing == null && time > removalTime) { + StatePlaylists.instance.addToWatchLater(video, false); + if(time > OffsetDateTime.MIN) + StatePlaylists.instance.setWatchLaterAddTime(video.url, time); + } } } for(removal in pack.videoRemovals) { diff --git a/app/src/main/java/com/futo/platformplayer/views/fields/ButtonField.kt b/app/src/main/java/com/futo/platformplayer/views/fields/ButtonField.kt index 03af1ee8..a7e142f8 100644 --- a/app/src/main/java/com/futo/platformplayer/views/fields/ButtonField.kt +++ b/app/src/main/java/com/futo/platformplayer/views/fields/ButtonField.kt @@ -41,6 +41,8 @@ class ButtonField : BigButton, IField { return null; }; + override var isAdvanced: Boolean = false; + //private val _title : TextView; //private val _subtitle : TextView; @@ -89,7 +91,7 @@ class ButtonField : BigButton, IField { 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"); } override fun setField() { diff --git a/app/src/main/java/com/futo/platformplayer/views/fields/DropdownField.kt b/app/src/main/java/com/futo/platformplayer/views/fields/DropdownField.kt index 590335d3..4f01d4d5 100644 --- a/app/src/main/java/com/futo/platformplayer/views/fields/DropdownField.kt +++ b/app/src/main/java/com/futo/platformplayer/views/fields/DropdownField.kt @@ -40,6 +40,8 @@ class DropdownField : TableRow, IField { override var reference: Any? = null; + override var isAdvanced: Boolean = false; + override val onChanged = Event3(); override val value: Any? get() = _selected; @@ -112,7 +114,7 @@ class DropdownField : TableRow, IField { 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._obj = obj; @@ -133,6 +135,9 @@ class DropdownField : TableRow, IField { _description.visibility = View.GONE; } + val advancedFieldAttr = field.getAnnotation(AdvancedField::class.java) + if(advancedFieldAttr != null || advanced) + isAdvanced = true; _options = (field.getAnnotation(DropdownFieldOptions::class.java)?.options ?: field.getAnnotation(DropdownFieldOptionsId::class.java)?.optionsId?.let { resources.getStringArray(it) } ?: diff --git a/app/src/main/java/com/futo/platformplayer/views/fields/Field.kt b/app/src/main/java/com/futo/platformplayer/views/fields/Field.kt index 9439d46a..b11ce641 100644 --- a/app/src/main/java/com/futo/platformplayer/views/fields/Field.kt +++ b/app/src/main/java/com/futo/platformplayer/views/fields/Field.kt @@ -4,6 +4,10 @@ import com.futo.platformplayer.constructs.Event2 import com.futo.platformplayer.constructs.Event3 import java.lang.reflect.Field +@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.RUNTIME) +annotation class AdvancedField(); + @Target(AnnotationTarget.FIELD, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY) @Retention(AnnotationRetention.RUNTIME) @@ -22,6 +26,8 @@ interface IField { val obj : Any?; val field : Field?; + val isAdvanced: Boolean; + val value: Any?; val onChanged : Event3; @@ -29,7 +35,7 @@ interface IField { 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 setValue(value: Any); diff --git a/app/src/main/java/com/futo/platformplayer/views/fields/FieldForm.kt b/app/src/main/java/com/futo/platformplayer/views/fields/FieldForm.kt index 1262e345..566a5024 100644 --- a/app/src/main/java/com/futo/platformplayer/views/fields/FieldForm.kt +++ b/app/src/main/java/com/futo/platformplayer/views/fields/FieldForm.kt @@ -37,6 +37,8 @@ class FieldForm : LinearLayout { private var _fields : List = arrayListOf(); + private var _showAdvancedSettings: Boolean = false; + constructor(context : Context, attrs : AttributeSet? = null) : super(context, attrs) { inflate(context, R.layout.field_form, this); _containerSearch = findViewById(R.id.container_search); @@ -58,11 +60,17 @@ class FieldForm : LinearLayout { if(field is GroupField) { updateSettingsVisibility(field); } else if(field is View && field.descriptor != null) { - val txt = field.searchContent?.lowercase(); - if(txt != null) { - val visible = isGroupMatch || txt.contains(query); - field.visibility = if (visible) View.VISIBLE else View.GONE; - groupVisible = groupVisible || visible; + if(field.isAdvanced && !_showAdvancedSettings) + { + field.visibility = View.GONE; + } + else { + val txt = field.searchContent?.lowercase(); + if (txt != null) { + val visible = isGroupMatch || txt.contains(query); + field.visibility = if (visible) View.VISIBLE else View.GONE; + groupVisible = groupVisible || visible; + } } } } @@ -71,6 +79,10 @@ class FieldForm : LinearLayout { } } + fun setShowAdvancedSettings(show: Boolean) { + _showAdvancedSettings = show; + updateSettingsVisibility(); + } fun setSearchQuery(query: String) { _editSearch.setText(query); updateSettingsVisibility(); @@ -92,13 +104,22 @@ class FieldForm : LinearLayout { 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); field.onChanged.subscribe { a1, a2, _ -> + if(field is ToggleField && field.descriptor?.id == "advancedSettings") { + setShowAdvancedSettings((a2 as Boolean)); + } + onChanged.emit(a1, a2); }; } _fields = newFields; + updateSettingsVisibility(); onLoaded?.invoke(); } } @@ -267,10 +288,12 @@ class FieldForm : LinearLayout { for(prop in objFields) { prop.first.javaField!!.isAccessible = true; + val advanced = prop.first.hasAnnotation(); + val field = when(prop.second.type) { GROUP -> GroupField(context).fromField(obj, prop.first.javaField!!, prop.second); - DROPDOWN -> DropdownField(context).fromField(obj, prop.first.javaField!!, prop.second); - TOGGLE -> ToggleField(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, advanced); 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}") } diff --git a/app/src/main/java/com/futo/platformplayer/views/fields/GroupField.kt b/app/src/main/java/com/futo/platformplayer/views/fields/GroupField.kt index 133fb788..9621285a 100644 --- a/app/src/main/java/com/futo/platformplayer/views/fields/GroupField.kt +++ b/app/src/main/java/com/futo/platformplayer/views/fields/GroupField.kt @@ -34,6 +34,7 @@ class GroupField : LinearLayout, IField { private val _container : LinearLayout; override var reference: Any? = null; + override var isAdvanced: Boolean = false; override val value: Any? = null; @@ -100,7 +101,7 @@ class GroupField : LinearLayout, IField { 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._obj = obj; diff --git a/app/src/main/java/com/futo/platformplayer/views/fields/ReadOnlyTextField.kt b/app/src/main/java/com/futo/platformplayer/views/fields/ReadOnlyTextField.kt index d0cfb7dc..3fb78aeb 100644 --- a/app/src/main/java/com/futo/platformplayer/views/fields/ReadOnlyTextField.kt +++ b/app/src/main/java/com/futo/platformplayer/views/fields/ReadOnlyTextField.kt @@ -31,6 +31,7 @@ class ReadOnlyTextField : TableRow, IField { override val onChanged = Event3(); override var reference: Any? = null; + override var isAdvanced: Boolean = false; override val value: Any? = null; @@ -45,7 +46,7 @@ class ReadOnlyTextField : TableRow, IField { 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._obj = obj; diff --git a/app/src/main/java/com/futo/platformplayer/views/fields/ToggleField.kt b/app/src/main/java/com/futo/platformplayer/views/fields/ToggleField.kt index 8e1cfbbb..c419092c 100644 --- a/app/src/main/java/com/futo/platformplayer/views/fields/ToggleField.kt +++ b/app/src/main/java/com/futo/platformplayer/views/fields/ToggleField.kt @@ -33,6 +33,7 @@ class ToggleField : TableRow, IField { private var _lastValue: Boolean = false; override var reference: Any? = null; + override var isAdvanced: Boolean = false; override val onChanged = Event3(); @@ -75,7 +76,7 @@ class ToggleField : TableRow, IField { 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._obj = obj; @@ -87,6 +88,12 @@ class ToggleField : TableRow, IField { else _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) _description.visibility = View.GONE; else { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f81f1642..d382860d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,6 +12,8 @@ Channel Home Progress Bar + Advanced Settings + If advanced settings should be shown, this exposes additional settings to finetune your experience. If a historical progress bar should be shown Hide hidden from home in search Hide videos and creators hidden from home also in search results