diff --git a/app/src/main/java/org/jellyfin/androidtv/preference/AuthenticationPreferences.kt b/app/src/main/java/org/jellyfin/androidtv/preference/AuthenticationPreferences.kt index 6a099d1d6..aad6c1e8a 100644 --- a/app/src/main/java/org/jellyfin/androidtv/preference/AuthenticationPreferences.kt +++ b/app/src/main/java/org/jellyfin/androidtv/preference/AuthenticationPreferences.kt @@ -3,25 +3,27 @@ package org.jellyfin.androidtv.preference import android.content.Context import org.jellyfin.androidtv.auth.model.AuthenticationSortBy import org.jellyfin.androidtv.preference.constant.UserSelectBehavior -import org.jellyfin.preference.Preference +import org.jellyfin.preference.booleanPreference +import org.jellyfin.preference.enumPreference import org.jellyfin.preference.store.SharedPreferenceStore +import org.jellyfin.preference.stringPreference class AuthenticationPreferences(context: Context) : SharedPreferenceStore( sharedPreferences = context.getSharedPreferences("authentication", Context.MODE_PRIVATE) ) { companion object { - val autoLoginUserBehavior = Preference.enum("auto_login_user_behavior", UserSelectBehavior.LAST_USER) - val autoLoginUserId = Preference.string("auto_login_user_id", "") + val autoLoginUserBehavior = enumPreference("auto_login_user_behavior", UserSelectBehavior.LAST_USER) + val autoLoginUserId = stringPreference("auto_login_user_id", "") - val systemUserBehavior = Preference.enum("system_user_behavior", UserSelectBehavior.LAST_USER) - val systemUserId = Preference.string("system_user_id", "") + val systemUserBehavior = enumPreference("system_user_behavior", UserSelectBehavior.LAST_USER) + val systemUserId = stringPreference("system_user_id", "") - val sortBy = Preference.enum("sort_by", AuthenticationSortBy.LAST_USE) - val alwaysAuthenticate = Preference.boolean("always_authenticate", false) + val sortBy = enumPreference("sort_by", AuthenticationSortBy.LAST_USE) + val alwaysAuthenticate = booleanPreference("always_authenticate", false) /** * Do not set directly, use [SessionRepository] instead. */ - val lastUserId = Preference.string("last_user_id", "") + val lastUserId = stringPreference("last_user_id", "") } } diff --git a/app/src/main/java/org/jellyfin/androidtv/preference/LibraryPreferences.kt b/app/src/main/java/org/jellyfin/androidtv/preference/LibraryPreferences.kt index 20050bf5c..98cf8ca64 100644 --- a/app/src/main/java/org/jellyfin/androidtv/preference/LibraryPreferences.kt +++ b/app/src/main/java/org/jellyfin/androidtv/preference/LibraryPreferences.kt @@ -6,7 +6,9 @@ import org.jellyfin.androidtv.constant.PosterSize import org.jellyfin.androidtv.preference.store.DisplayPreferencesStore import org.jellyfin.apiclient.model.entities.SortOrder import org.jellyfin.apiclient.model.querying.ItemSortBy -import org.jellyfin.preference.Preference +import org.jellyfin.preference.booleanPreference +import org.jellyfin.preference.enumPreference +import org.jellyfin.preference.stringPreference import org.jellyfin.sdk.api.client.ApiClient class LibraryPreferences( @@ -17,17 +19,17 @@ class LibraryPreferences( api = api, ) { companion object { - val posterSize = Preference.enum("PosterSize", PosterSize.AUTO) - val imageType = Preference.enum("ImageType", ImageType.DEFAULT) - val gridDirection = Preference.enum("GridDirection", GridDirection.HORIZONTAL) - val enableSmartScreen = Preference.boolean("SmartScreen", false) + val posterSize = enumPreference("PosterSize", PosterSize.AUTO) + val imageType = enumPreference("ImageType", ImageType.DEFAULT) + val gridDirection = enumPreference("GridDirection", GridDirection.HORIZONTAL) + val enableSmartScreen = booleanPreference("SmartScreen", false) // Filters - val filterFavoritesOnly = Preference.boolean("FilterFavoritesOnly", false) - val filterUnwatchedOnly = Preference.boolean("FilterUnwatchedOnly", false) + val filterFavoritesOnly = booleanPreference("FilterFavoritesOnly", false) + val filterUnwatchedOnly = booleanPreference("FilterUnwatchedOnly", false) // Item sorting - val sortBy = Preference.string("SortBy", ItemSortBy.SortName) - val sortOrder = Preference.enum("SortOrder", SortOrder.Ascending) + val sortBy = stringPreference("SortBy", ItemSortBy.SortName) + val sortOrder = enumPreference("SortOrder", SortOrder.Ascending) } } diff --git a/app/src/main/java/org/jellyfin/androidtv/preference/LiveTvPreferences.kt b/app/src/main/java/org/jellyfin/androidtv/preference/LiveTvPreferences.kt index 51430bbbd..8e52cf7cc 100644 --- a/app/src/main/java/org/jellyfin/androidtv/preference/LiveTvPreferences.kt +++ b/app/src/main/java/org/jellyfin/androidtv/preference/LiveTvPreferences.kt @@ -2,7 +2,8 @@ package org.jellyfin.androidtv.preference import org.jellyfin.androidtv.preference.store.DisplayPreferencesStore import org.jellyfin.apiclient.model.querying.ItemSortBy -import org.jellyfin.preference.Preference +import org.jellyfin.preference.booleanPreference +import org.jellyfin.preference.stringPreference import org.jellyfin.sdk.api.client.ApiClient class LiveTvPreferences( @@ -12,13 +13,13 @@ class LiveTvPreferences( api = api, ) { companion object { - val channelOrder = Preference.string("livetv-channelorder", ItemSortBy.DatePlayed) - val colorCodeGuide = Preference.boolean("guide-colorcodedbackgrounds", false) - val favsAtTop = Preference.boolean("livetv-favoritechannelsattop", true) - val showHDIndicator = Preference.boolean("guide-indicator-hd", false) - val showLiveIndicator = Preference.boolean("guide-indicator-live", true) - val showNewIndicator = Preference.boolean("guide-indicator-new", false) - val showPremiereIndicator = Preference.boolean("guide-indicator-premiere", true) - val showRepeatIndicator = Preference.boolean("guide-indicator-repeat", false) + val channelOrder = stringPreference("livetv-channelorder", ItemSortBy.DatePlayed) + val colorCodeGuide = booleanPreference("guide-colorcodedbackgrounds", false) + val favsAtTop = booleanPreference("livetv-favoritechannelsattop", true) + val showHDIndicator = booleanPreference("guide-indicator-hd", false) + val showLiveIndicator = booleanPreference("guide-indicator-live", true) + val showNewIndicator = booleanPreference("guide-indicator-new", false) + val showPremiereIndicator = booleanPreference("guide-indicator-premiere", true) + val showRepeatIndicator = booleanPreference("guide-indicator-repeat", false) } } diff --git a/app/src/main/java/org/jellyfin/androidtv/preference/PreferencesRepository.kt b/app/src/main/java/org/jellyfin/androidtv/preference/PreferencesRepository.kt index 8626411a3..d8d9b16d0 100644 --- a/app/src/main/java/org/jellyfin/androidtv/preference/PreferencesRepository.kt +++ b/app/src/main/java/org/jellyfin/androidtv/preference/PreferencesRepository.kt @@ -1,5 +1,6 @@ package org.jellyfin.androidtv.preference +import kotlinx.coroutines.runBlocking import org.jellyfin.sdk.api.client.ApiClient import kotlin.collections.set @@ -19,7 +20,7 @@ class PreferencesRepository( libraryPreferences[preferencesId] = store // FIXME: Make [getLibraryPreferences] suspended when usages are converted to Kotlin - if (store.shouldUpdate) store.updateBlocking() + if (store.shouldUpdate) runBlocking { store.update() } return store } diff --git a/app/src/main/java/org/jellyfin/androidtv/preference/SystemPreferences.kt b/app/src/main/java/org/jellyfin/androidtv/preference/SystemPreferences.kt index cac858b0c..40b2365ba 100644 --- a/app/src/main/java/org/jellyfin/androidtv/preference/SystemPreferences.kt +++ b/app/src/main/java/org/jellyfin/androidtv/preference/SystemPreferences.kt @@ -2,8 +2,10 @@ package org.jellyfin.androidtv.preference import android.content.Context import org.jellyfin.androidtv.preference.constant.PreferredVideoPlayer -import org.jellyfin.preference.Preference +import org.jellyfin.preference.booleanPreference +import org.jellyfin.preference.enumPreference import org.jellyfin.preference.store.SharedPreferenceStore +import org.jellyfin.preference.stringPreference /** * System preferences are not possible to modify by the user. @@ -19,48 +21,48 @@ class SystemPreferences(context: Context) : SharedPreferenceStore( /** * Stores the channel that was active before leaving the app */ - val liveTvLastChannel = Preference.string("sys_pref_last_tv_channel", "") + val liveTvLastChannel = stringPreference("sys_pref_last_tv_channel", "") /** * Also stores the channel that was active before leaving the app I think */ - val liveTvPrevChannel = Preference.string("sys_pref_prev_tv_channel", "") + val liveTvPrevChannel = stringPreference("sys_pref_prev_tv_channel", "") // Live TV - Guide Filters /** * Stores whether the kids filter is active in the channel guide or not */ - val liveTvGuideFilterKids = Preference.boolean("guide_filter_kids", false) + val liveTvGuideFilterKids = booleanPreference("guide_filter_kids", false) /** * Stores whether the movies filter is active in the channel guide or not */ - val liveTvGuideFilterMovies = Preference.boolean("guide_filter_movies", false) + val liveTvGuideFilterMovies = booleanPreference("guide_filter_movies", false) /** * Stores whether the news filter is active in the channel guide or not */ - val liveTvGuideFilterNews = Preference.boolean("guide_filter_news", false) + val liveTvGuideFilterNews = booleanPreference("guide_filter_news", false) /** * Stores whether the premiere filter is active in the channel guide or not */ - val liveTvGuideFilterPremiere = Preference.boolean("guide_filter_premiere", false) + val liveTvGuideFilterPremiere = booleanPreference("guide_filter_premiere", false) /** * Stores whether the series filter is active in the channel guide or not */ - val liveTvGuideFilterSeries = Preference.boolean("guide_filter_series", false) + val liveTvGuideFilterSeries = booleanPreference("guide_filter_series", false) /** * Stores whether the sports filter is active in the channel guide or not */ - val liveTvGuideFilterSports = Preference.boolean("guide_filter_sports", false) + val liveTvGuideFilterSports = booleanPreference("guide_filter_sports", false) // Other persistent variables /** * Chosen player for play with button. Changes every time user chooses a player with "play with" button. */ - var chosenPlayer = Preference.enum("chosen_player", PreferredVideoPlayer.VLC) + var chosenPlayer = enumPreference("chosen_player", PreferredVideoPlayer.VLC) } } diff --git a/app/src/main/java/org/jellyfin/androidtv/preference/UserPreferences.kt b/app/src/main/java/org/jellyfin/androidtv/preference/UserPreferences.kt index e2d668c48..361ce8957 100644 --- a/app/src/main/java/org/jellyfin/androidtv/preference/UserPreferences.kt +++ b/app/src/main/java/org/jellyfin/androidtv/preference/UserPreferences.kt @@ -13,9 +13,12 @@ import org.jellyfin.androidtv.preference.constant.RatingType import org.jellyfin.androidtv.preference.constant.WatchedIndicatorBehavior import org.jellyfin.androidtv.preference.constant.defaultAudioBehavior import org.jellyfin.androidtv.util.DeviceUtils -import org.jellyfin.preference.Preference +import org.jellyfin.preference.booleanPreference +import org.jellyfin.preference.enumPreference +import org.jellyfin.preference.intPreference import org.jellyfin.preference.migration.putEnum import org.jellyfin.preference.store.SharedPreferenceStore +import org.jellyfin.preference.stringPreference /** * User preferences are configurable by the user and change behavior of the application. @@ -36,170 +39,170 @@ class UserPreferences(context: Context) : SharedPreferenceStore( /** * Select the app theme */ - var appTheme = Preference.enum("app_theme", AppTheme.DARK) + var appTheme = enumPreference("app_theme", AppTheme.DARK) /** * Enable background images while browsing */ - var backdropEnabled = Preference.boolean("pref_show_backdrop", true) + var backdropEnabled = booleanPreference("pref_show_backdrop", true) /** * Show premieres on home screen */ - var premieresEnabled = Preference.boolean("pref_enable_premieres", false) + var premieresEnabled = booleanPreference("pref_enable_premieres", false) /** * Show a little notification to celebrate a set of holidays */ - var seasonalGreetingsEnabled = Preference.boolean("pref_enable_themes", true) + var seasonalGreetingsEnabled = booleanPreference("pref_enable_themes", true) /* Playback - General*/ /** * Maximum bitrate in megabit for playback. A value of [MAX_BITRATE_AUTO] is used when * the bitrate should be automatically detected. */ - var maxBitrate = Preference.string("pref_max_bitrate", "100") + var maxBitrate = stringPreference("pref_max_bitrate", "100") /** * Auto-play next item */ - var mediaQueuingEnabled = Preference.boolean("pref_enable_tv_queuing", true) + var mediaQueuingEnabled = booleanPreference("pref_enable_tv_queuing", true) /** * Enable the next up screen or not */ - var nextUpBehavior = Preference.enum("next_up_behavior", NextUpBehavior.EXTENDED) + var nextUpBehavior = enumPreference("next_up_behavior", NextUpBehavior.EXTENDED) /** * Next up timeout before playing next item * Stored in milliseconds */ - var nextUpTimeout = Preference.int("next_up_timeout", 1000 * 7) + var nextUpTimeout = intPreference("next_up_timeout", 1000 * 7) /** * Duration in seconds to subtract from resume time */ - var resumeSubtractDuration = Preference.string("pref_resume_preroll", "0") + var resumeSubtractDuration = stringPreference("pref_resume_preroll", "0") /** * Enable cinema mode */ - var cinemaModeEnabled = Preference.boolean("pref_enable_cinema_mode", true) + var cinemaModeEnabled = booleanPreference("pref_enable_cinema_mode", true) /* Playback - Video */ /** * Preferred video player. */ - var videoPlayer = Preference.enum("video_player", PreferredVideoPlayer.EXOPLAYER) + var videoPlayer = enumPreference("video_player", PreferredVideoPlayer.EXOPLAYER) /** * Enable refresh rate switching when device supports it */ - var refreshRateSwitchingEnabled = Preference.boolean("pref_refresh_switching", false) + var refreshRateSwitchingEnabled = booleanPreference("pref_refresh_switching", false) /** * Send a path instead to the external player */ - var externalVideoPlayerSendPath = Preference.boolean("pref_send_path_external", false) + var externalVideoPlayerSendPath = booleanPreference("pref_send_path_external", false) /* Playback - Audio related */ /** * Preferred behavior for audio streaming. */ - var audioBehaviour = Preference.enum("audio_behavior", defaultAudioBehavior) + var audioBehaviour = enumPreference("audio_behavior", defaultAudioBehavior) /** * Enable DTS */ - var dtsEnabled = Preference.boolean("pref_bitstream_dts", false) + var dtsEnabled = booleanPreference("pref_bitstream_dts", false) /** * Enable AC3 */ - var ac3Enabled = Preference.boolean("pref_bitstream_ac3", !DeviceUtils.isFireTvStickGen1()) + var ac3Enabled = booleanPreference("pref_bitstream_ac3", !DeviceUtils.isFireTvStickGen1()) /** * Default audio delay in milliseconds for libVLC */ - var libVLCAudioDelay = Preference.int("libvlc_audio_delay", 0) + var libVLCAudioDelay = intPreference("libvlc_audio_delay", 0) /* Live TV */ /** * Use direct play */ - var liveTvDirectPlayEnabled = Preference.boolean("pref_live_direct", true) + var liveTvDirectPlayEnabled = booleanPreference("pref_live_direct", true) /** * Preferred video player for live TV */ - var liveTvVideoPlayer = Preference.enum("live_tv_video_player", PreferredVideoPlayer.AUTO) + var liveTvVideoPlayer = enumPreference("live_tv_video_player", PreferredVideoPlayer.AUTO) /** * Shortcut used for changing the audio track */ - var shortcutAudioTrack = Preference.int("shortcut_audio_track", KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK) + var shortcutAudioTrack = intPreference("shortcut_audio_track", KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK) /** * Shortcut used for changing the subtitle track */ - var shortcutSubtitleTrack = Preference.int("shortcut_subtitle_track", KeyEvent.KEYCODE_CAPTIONS) + var shortcutSubtitleTrack = intPreference("shortcut_subtitle_track", KeyEvent.KEYCODE_CAPTIONS) /* Developer options */ /** * Show additional debug information */ - var debuggingEnabled = Preference.boolean("pref_enable_debug", false) + var debuggingEnabled = booleanPreference("pref_enable_debug", false) /** * Use playback rewrite module */ - var playbackRewriteEnabled = Preference.boolean("playback_new", false) + var playbackRewriteEnabled = booleanPreference("playback_new", false) /* ACRA */ /** * Enable ACRA crash reporting */ - var acraEnabled = Preference.boolean(ACRA.PREF_ENABLE_ACRA, true) + var acraEnabled = booleanPreference(ACRA.PREF_ENABLE_ACRA, true) /** * Never prompt to report crash logs */ - var acraNoPrompt = Preference.boolean(ACRA.PREF_ALWAYS_ACCEPT, false) + var acraNoPrompt = booleanPreference(ACRA.PREF_ALWAYS_ACCEPT, false) /** * Include system logs in crash reports */ - var acraIncludeSystemLogs = Preference.boolean(ACRA.PREF_ENABLE_SYSTEM_LOGS, true) + var acraIncludeSystemLogs = booleanPreference(ACRA.PREF_ENABLE_SYSTEM_LOGS, true) /** * When to show the clock. */ - var clockBehavior = Preference.enum("pref_clock_behavior", ClockBehavior.ALWAYS) + var clockBehavior = enumPreference("pref_clock_behavior", ClockBehavior.ALWAYS) /** * Set which ratings provider should show on MyImageCardViews */ - var defaultRatingType = Preference.enum("pref_rating_type", RatingType.RATING_TOMATOES) + var defaultRatingType = enumPreference("pref_rating_type", RatingType.RATING_TOMATOES) /** * Set when watched indicators should show on MyImageCardViews */ - var watchedIndicatorBehavior = Preference.enum("pref_watched_indicator_behavior", WatchedIndicatorBehavior.ALWAYS) + var watchedIndicatorBehavior = enumPreference("pref_watched_indicator_behavior", WatchedIndicatorBehavior.ALWAYS) /** * Enable series thumbnails in home screen rows */ - var seriesThumbnailsEnabled = Preference.boolean("pref_enable_series_thumbnails", true) + var seriesThumbnailsEnabled = booleanPreference("pref_enable_series_thumbnails", true) /** * Enable subtitles background */ - var subtitlesBackgroundEnabled = Preference.boolean("subtitles_background_enabled", true) + var subtitlesBackgroundEnabled = booleanPreference("subtitles_background_enabled", true) /** * Set default subtitles font size */ - var defaultSubtitlesSize = Preference.int("subtitles_size", 28) + var defaultSubtitlesSize = intPreference("subtitles_size", 28) } init { diff --git a/app/src/main/java/org/jellyfin/androidtv/preference/UserSettingPreferences.kt b/app/src/main/java/org/jellyfin/androidtv/preference/UserSettingPreferences.kt index 9b9a56c88..1600634c8 100644 --- a/app/src/main/java/org/jellyfin/androidtv/preference/UserSettingPreferences.kt +++ b/app/src/main/java/org/jellyfin/androidtv/preference/UserSettingPreferences.kt @@ -2,7 +2,8 @@ package org.jellyfin.androidtv.preference import org.jellyfin.androidtv.constant.HomeSectionType import org.jellyfin.androidtv.preference.store.DisplayPreferencesStore -import org.jellyfin.preference.Preference +import org.jellyfin.preference.enumPreference +import org.jellyfin.preference.intPreference import org.jellyfin.sdk.api.client.ApiClient class UserSettingPreferences( @@ -13,16 +14,16 @@ class UserSettingPreferences( app = "emby", ) { companion object { - val skipBackLength = Preference.int("skipBackLength", 10000) - val skipForwardLength = Preference.int("skipForwardLength", 30000) + val skipBackLength = intPreference("skipBackLength", 10000) + val skipForwardLength = intPreference("skipForwardLength", 30000) - val homesection0 = Preference.enum("homesection0", HomeSectionType.LIBRARY_TILES_SMALL) - val homesection1 = Preference.enum("homesection1", HomeSectionType.RESUME) - val homesection2 = Preference.enum("homesection2", HomeSectionType.RESUME_AUDIO) - val homesection3 = Preference.enum("homesection3", HomeSectionType.RESUME_BOOK) - val homesection4 = Preference.enum("homesection4", HomeSectionType.LIVE_TV) - val homesection5 = Preference.enum("homesection5", HomeSectionType.NEXT_UP) - val homesection6 = Preference.enum("homesection6", HomeSectionType.LATEST_MEDIA) + val homesection0 = enumPreference("homesection0", HomeSectionType.LIBRARY_TILES_SMALL) + val homesection1 = enumPreference("homesection1", HomeSectionType.RESUME) + val homesection2 = enumPreference("homesection2", HomeSectionType.RESUME_AUDIO) + val homesection3 = enumPreference("homesection3", HomeSectionType.RESUME_BOOK) + val homesection4 = enumPreference("homesection4", HomeSectionType.LIVE_TV) + val homesection5 = enumPreference("homesection5", HomeSectionType.NEXT_UP) + val homesection6 = enumPreference("homesection6", HomeSectionType.LATEST_MEDIA) } val homesections diff --git a/app/src/main/java/org/jellyfin/androidtv/preference/store/DisplayPreferencesStore.kt b/app/src/main/java/org/jellyfin/androidtv/preference/store/DisplayPreferencesStore.kt index dcf85b4a6..c6178228d 100644 --- a/app/src/main/java/org/jellyfin/androidtv/preference/store/DisplayPreferencesStore.kt +++ b/app/src/main/java/org/jellyfin/androidtv/preference/store/DisplayPreferencesStore.kt @@ -2,6 +2,7 @@ package org.jellyfin.androidtv.preference.store import org.jellyfin.preference.Preference import org.jellyfin.preference.PreferenceEnum +import org.jellyfin.preference.migration.MigrationContext import org.jellyfin.preference.store.AsyncPreferenceStore import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.exception.ApiClientException @@ -11,11 +12,12 @@ import org.jellyfin.sdk.model.api.ScrollDirection import org.jellyfin.sdk.model.api.SortOrder import timber.log.Timber +@Suppress("TooManyFunctions") abstract class DisplayPreferencesStore( protected var displayPreferencesId: String, protected var app: String = "jellyfin-androidtv", private val api: ApiClient, -) : AsyncPreferenceStore() { +) : AsyncPreferenceStore() { private var displayPreferencesDto: DisplayPreferencesDto? = null private var cachedPreferences: MutableMap = mutableMapOf() override val shouldUpdate: Boolean @@ -122,6 +124,10 @@ abstract class DisplayPreferencesStore( } ) + override fun runMigrations(body: MigrationContext.() -> Unit) { + TODO("The DisplayPreferencesStore does not support migrations") + } + /** * Create an empty [DisplayPreferencesDto] with default values. */ diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/DisplayPreferencesScreen.kt b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/DisplayPreferencesScreen.kt index 41411a489..fb1ce3dd1 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/DisplayPreferencesScreen.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/DisplayPreferencesScreen.kt @@ -22,7 +22,7 @@ class DisplayPreferencesScreen : OptionsFragment() { private val preferencesId by lazy { requireArguments().getString(ARG_PREFERENCES_ID) } private val allowViewSelection by lazy { requireArguments().getBoolean(ARG_ALLOW_VIEW_SELECTION) } - override val stores: Array + override val stores: Array> get() = arrayOf(libraryPreferences) override val screen by optionsScreen { diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/StdGridFragment.java b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/StdGridFragment.java index 61bff5507..8b9f978f1 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/StdGridFragment.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/StdGridFragment.java @@ -50,6 +50,7 @@ import org.jellyfin.androidtv.ui.presentation.HorizontalGridPresenter; import org.jellyfin.androidtv.ui.shared.BaseActivity; import org.jellyfin.androidtv.ui.shared.KeyListener; import org.jellyfin.androidtv.ui.shared.MessageListener; +import org.jellyfin.androidtv.util.CoroutineUtils; import org.jellyfin.androidtv.util.KeyProcessor; import org.jellyfin.androidtv.util.Utils; import org.jellyfin.apiclient.interaction.EmptyResponse; @@ -348,7 +349,7 @@ public class StdGridFragment extends GridFragment { libraryPreferences.set(LibraryPreferences.Companion.getFilterUnwatchedOnly(), mGridAdapter.getFilters().isUnwatchedOnly()); libraryPreferences.set(LibraryPreferences.Companion.getSortBy(), mGridAdapter.getSortBy()); libraryPreferences.set(LibraryPreferences.Companion.getSortOrder(), getSortOption(mGridAdapter.getSortBy()).order); - libraryPreferences.commitBlocking(); + CoroutineUtils.runBlocking((coroutineScope, continuation) -> libraryPreferences.commit(continuation)); } protected void addTools() { diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/livetv/LiveTvGuideActivity.java b/app/src/main/java/org/jellyfin/androidtv/ui/livetv/LiveTvGuideActivity.java index 56b899a4d..d62b9e138 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/livetv/LiveTvGuideActivity.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/livetv/LiveTvGuideActivity.java @@ -46,6 +46,7 @@ import org.jellyfin.androidtv.ui.ProgramGridCell; import org.jellyfin.androidtv.ui.ScrollViewListener; import org.jellyfin.androidtv.ui.shared.BaseActivity; import org.jellyfin.androidtv.ui.shared.MessageListener; +import org.jellyfin.androidtv.util.CoroutineUtils; import org.jellyfin.androidtv.util.ImageUtils; import org.jellyfin.androidtv.util.InfoLayoutHelper; import org.jellyfin.androidtv.util.TimeUtils; @@ -595,7 +596,7 @@ public class LiveTvGuideActivity extends BaseActivity implements LiveTvGuide { prefs.set(LiveTvPreferences.Companion.getShowRepeatIndicator(), mRepeat.isChecked()); prefs.set(LiveTvPreferences.Companion.getChannelOrder(), mCurrentSort); prefs.set(LiveTvPreferences.Companion.getShowLiveIndicator(), mLive.isChecked()); - prefs.commitBlocking(); + CoroutineUtils.runBlocking((coroutineScope, continuation) -> prefs.commit(continuation)); TvManager.clearCache(); diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/preference/dsl/OptionsFragment.kt b/app/src/main/java/org/jellyfin/androidtv/ui/preference/dsl/OptionsFragment.kt index 08462cda4..be10392ac 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/preference/dsl/OptionsFragment.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/preference/dsl/OptionsFragment.kt @@ -22,7 +22,7 @@ abstract class OptionsFragment : LeanbackPreferenceFragmentCompat() { * Preference stores used in current screen. Fragment will automatically call the update and * commit functions for all async stores. */ - protected open val stores: Array = emptyArray() + protected open val stores: Array> = emptyArray() // Used to not build twice during onCreate() private var skippedInitialResume = false @@ -31,8 +31,8 @@ abstract class OptionsFragment : LeanbackPreferenceFragmentCompat() { // Refresh all data in async stores runBlocking { stores - .filterIsInstance() - .map { async { it.update() } } + .filterIsInstance>() + .map { async { it.update() } } .awaitAll() } @@ -45,8 +45,8 @@ abstract class OptionsFragment : LeanbackPreferenceFragmentCompat() { // Save all data in async stores runBlocking { stores - .filterIsInstance() - .map { async { it.commit() } } + .filterIsInstance>() + .map { async { it.commit() } } .awaitAll() } } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/preference/dsl/OptionsItemEnum.kt b/app/src/main/java/org/jellyfin/androidtv/ui/preference/dsl/OptionsItemEnum.kt index 195831ce4..70d9c7ef4 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/preference/dsl/OptionsItemEnum.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/preference/dsl/OptionsItemEnum.kt @@ -18,7 +18,7 @@ class OptionsItemEnum>( // Add exact copy of the OptionsItemMutable.bind function so the correct // store getter and setter will be used. - override fun bind(store: PreferenceStore, preference: Preference) = bind { + override fun bind(store: PreferenceStore<*, *>, preference: Preference) = bind { get { store[preference] } set { store[preference] = it } default { store.getDefaultValue(preference) } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/preference/dsl/OptionsItemMutable.kt b/app/src/main/java/org/jellyfin/androidtv/ui/preference/dsl/OptionsItemMutable.kt index bdf1065ea..8f08621fd 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/preference/dsl/OptionsItemMutable.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/preference/dsl/OptionsItemMutable.kt @@ -11,7 +11,7 @@ abstract class OptionsItemMutable : OptionsItem { protected var dependencyCheckFun: () -> Boolean = { true } protected lateinit var binder: OptionsBinder - open fun bind(store: PreferenceStore, preference: Preference) = bind { + open fun bind(store: PreferenceStore<*, *>, preference: Preference) = bind { get { store[preference] } set { store[preference] = it } default { store.getDefaultValue(preference) } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/preference/screen/HomePreferencesScreen.kt b/app/src/main/java/org/jellyfin/androidtv/ui/preference/screen/HomePreferencesScreen.kt index a693fb591..da1099593 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/preference/screen/HomePreferencesScreen.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/preference/screen/HomePreferencesScreen.kt @@ -12,7 +12,7 @@ import org.koin.android.ext.android.inject class HomePreferencesScreen : OptionsFragment() { private val userSettingPreferences: UserSettingPreferences by inject() - override val stores: Array + override val stores: Array> get() = arrayOf(userSettingPreferences) override val screen by optionsScreen { diff --git a/app/src/main/java/org/jellyfin/androidtv/util/CoroutineUtils.kt b/app/src/main/java/org/jellyfin/androidtv/util/CoroutineUtils.kt new file mode 100644 index 000000000..378633489 --- /dev/null +++ b/app/src/main/java/org/jellyfin/androidtv/util/CoroutineUtils.kt @@ -0,0 +1,8 @@ +@file:JvmName("CoroutineUtils") +package org.jellyfin.androidtv.util + +import kotlinx.coroutines.CoroutineScope + +fun runBlocking(block: suspend CoroutineScope.() -> T) = kotlinx.coroutines.runBlocking { + block() +} diff --git a/preference/src/main/kotlin/Preference.kt b/preference/src/main/kotlin/Preference.kt index 1bdf369aa..9208b5074 100644 --- a/preference/src/main/kotlin/Preference.kt +++ b/preference/src/main/kotlin/Preference.kt @@ -6,12 +6,12 @@ data class Preference( val key: String, val defaultValue: T, val type: KClass -) { - companion object { - fun int(key: String, defaultValue: Int) = Preference(key, defaultValue, Int::class) - fun long(key: String, defaultValue: Long) = Preference(key, defaultValue, Long::class) - fun boolean(key: String, defaultValue: Boolean) = Preference(key, defaultValue, Boolean::class) - fun string(key: String, defaultValue: String) = Preference(key, defaultValue, String::class) - inline fun enum(key: String, defaultValue: T) = Preference(key, defaultValue, T::class) - } -} +) + +fun intPreference(key: String, defaultValue: Int) = Preference(key, defaultValue, Int::class) +fun longPreference(key: String, defaultValue: Long) = Preference(key, defaultValue, Long::class) +fun booleanPreference(key: String, defaultValue: Boolean) = Preference(key, defaultValue, Boolean::class) +fun stringPreference(key: String, defaultValue: String) = Preference(key, defaultValue, String::class) +fun enumPreference(key: String, defaultValue: T, type: KClass) = Preference(key, defaultValue, type) + +inline fun enumPreference(key: String, defaultValue: T) = enumPreference(key, defaultValue, T::class) diff --git a/preference/src/main/kotlin/migration/MigrationEditor.kt b/preference/src/main/kotlin/migration/MigrationEditor.kt index d0abd25cc..f3eb13d9d 100644 --- a/preference/src/main/kotlin/migration/MigrationEditor.kt +++ b/preference/src/main/kotlin/migration/MigrationEditor.kt @@ -2,8 +2,6 @@ package org.jellyfin.preference.migration import android.content.SharedPreferences -typealias MigrationEditor = SharedPreferences.Editor - -fun > MigrationEditor.putEnum(key: String, value: T) { +fun > SharedPreferences.Editor.putEnum(key: String, value: T) { putString(key, value.toString()) } diff --git a/preference/src/main/kotlin/store/AsyncPreferenceStore.kt b/preference/src/main/kotlin/store/AsyncPreferenceStore.kt index 30b614dbd..56d1d9dd5 100644 --- a/preference/src/main/kotlin/store/AsyncPreferenceStore.kt +++ b/preference/src/main/kotlin/store/AsyncPreferenceStore.kt @@ -1,8 +1,6 @@ package org.jellyfin.preference.store -import kotlinx.coroutines.runBlocking - -abstract class AsyncPreferenceStore : PreferenceStore() { +abstract class AsyncPreferenceStore : PreferenceStore() { abstract val shouldUpdate: Boolean /** @@ -34,21 +32,11 @@ abstract class AsyncPreferenceStore : PreferenceStore() { * } * ``` */ - suspend fun transaction(body: AsyncPreferenceStore.() -> Unit): Boolean { + suspend fun transaction(body: AsyncPreferenceStore.() -> Unit): Boolean { if (shouldUpdate) update() body() return commit() } - - /** - * Compatability with old Java classes. - */ - fun updateBlocking() = runBlocking { update() } - - /** - * Compatability with old Java classes. - */ - fun commitBlocking() = runBlocking { commit() } } diff --git a/preference/src/main/kotlin/store/PreferenceStore.kt b/preference/src/main/kotlin/store/PreferenceStore.kt index 5e43430c4..e14bc6d5a 100644 --- a/preference/src/main/kotlin/store/PreferenceStore.kt +++ b/preference/src/main/kotlin/store/PreferenceStore.kt @@ -1,6 +1,7 @@ package org.jellyfin.preference.store import org.jellyfin.preference.Preference +import org.jellyfin.preference.migration.MigrationContext /** * Abstract class defining the required functions for a preference store. @@ -8,7 +9,8 @@ import org.jellyfin.preference.Preference * This implements shared functionality (such as Enum handling), whilst allowing for * different backing stores. */ -abstract class PreferenceStore { +@Suppress("TooManyFunctions") +abstract class PreferenceStore { // val value = store[Preference.x] @Suppress("UNCHECKED_CAST") operator fun get(preference: Preference): T = @@ -62,4 +64,7 @@ abstract class PreferenceStore { protected abstract fun > getEnum(preference: Preference): T protected abstract fun > setEnum(preference: Preference<*>, value: Enum) + + // Migrations + protected abstract fun runMigrations(body: MigrationContext.() -> Unit) } diff --git a/preference/src/main/kotlin/store/SharedPreferenceStore.kt b/preference/src/main/kotlin/store/SharedPreferenceStore.kt index dc82fd04f..3f0c3fcf5 100644 --- a/preference/src/main/kotlin/store/SharedPreferenceStore.kt +++ b/preference/src/main/kotlin/store/SharedPreferenceStore.kt @@ -3,8 +3,8 @@ package org.jellyfin.preference.store import android.content.SharedPreferences import org.jellyfin.preference.Preference import org.jellyfin.preference.PreferenceEnum +import org.jellyfin.preference.intPreference import org.jellyfin.preference.migration.MigrationContext -import org.jellyfin.preference.migration.MigrationEditor import timber.log.Timber /** @@ -26,12 +26,13 @@ import timber.log.Timber * } * ``` */ +@Suppress("TooManyFunctions") abstract class SharedPreferenceStore( /** * SharedPreferences to read from and write to */ protected val sharedPreferences: SharedPreferences -) : PreferenceStore() { +) : PreferenceStore() { // Internal helpers private fun transaction(body: SharedPreferences.Editor.() -> Unit) { val editor = sharedPreferences.edit() @@ -79,8 +80,8 @@ abstract class SharedPreferenceStore( } // Migrations - protected fun runMigrations(body: MigrationContext.() -> Unit) { - val context = MigrationContext() + override fun runMigrations(body: MigrationContext.() -> Unit) { + val context = MigrationContext() context.body() this[VERSION] = context.applyMigrations(this[VERSION]) { migration -> @@ -95,6 +96,6 @@ abstract class SharedPreferenceStore( /** * Version of the preference store. Used for migration. */ - val VERSION = Preference.int("store_version", -1) + val VERSION = intPreference("store_version", -1) } } diff --git a/preference/src/test/kotlin/PreferenceStoreTests.kt b/preference/src/test/kotlin/PreferenceStoreTests.kt index 35e97a11e..583bf3db7 100644 --- a/preference/src/test/kotlin/PreferenceStoreTests.kt +++ b/preference/src/test/kotlin/PreferenceStoreTests.kt @@ -2,6 +2,7 @@ package org.jellyfin.preference import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe +import org.jellyfin.preference.migration.MigrationContext import org.jellyfin.preference.store.PreferenceStore /** @@ -17,14 +18,14 @@ class PreferenceStoreTests : FunSpec({ } test("Reading and writing primitives works correctly") { - verifySimpleType(1, Preference.int("key", 0)) - verifySimpleType(1L, Preference.long("key", 0L)) - verifySimpleType(true, Preference.boolean("key", false)) - verifySimpleType("string", Preference.string("key", "")) + verifySimpleType(1, intPreference("key", 0)) + verifySimpleType(1L, longPreference("key", 0L)) + verifySimpleType(true, booleanPreference("key", false)) + verifySimpleType("string", stringPreference("key", "")) } test("Reading and writing enums works correctly") { - val pref = Preference.enum("key", TestEnum.NOT_SET) + val pref = enumPreference("key", TestEnum.NOT_SET) val expectedVal = TestEnum.SET val instance = TestStub() instance[pref] = expectedVal @@ -33,7 +34,7 @@ class PreferenceStoreTests : FunSpec({ } }) -private class TestStub : PreferenceStore() { +private class TestStub : PreferenceStore() { var key: String? = null private var int: Int? = null private var long: Long? = null @@ -79,6 +80,10 @@ private class TestStub : PreferenceStore() { override fun delete(preference: Preference) { throw NotImplementedError("Not required for tests") } + + override fun runMigrations(body: MigrationContext.() -> Unit) { + throw NotImplementedError("Not required for tests") + } } private enum class TestEnum { NOT_SET, SET }