Use 1.x preference abstraction (#8020)

* Use 1.x preference abstraction

- Uses SharedPreferences compared to 1.x impl which uses DataStore but it breaks all settings screens currently
- Move PreferencesHelper to new PreferenceStore
  - PreferencesHelper should be split into smaller preference stores and be in core or domain
- Remove flow preferences as new PreferenceStore handles changes for us

Co-authored-by: inorichi <3521738+inorichi@users.noreply.github.com>

* Fix PreferenceMutableState not updating

* Fix changes not emitting on first subscription

Co-authored-by: inorichi <3521738+inorichi@users.noreply.github.com>
This commit is contained in:
Andreas 2022-09-17 17:48:24 +02:00 committed by GitHub
parent bc8c45832e
commit 0086743a53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 698 additions and 340 deletions

View File

@ -226,7 +226,6 @@ dependencies {
// Preferences
implementation(libs.preferencektx)
implementation(libs.flowpreferences)
// Model View Presenter
implementation(libs.bundles.nucleus)

View File

@ -2,9 +2,8 @@ package eu.kanade.core.prefs
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import com.fredporciuncula.flow.preferences.Preference
import eu.kanade.tachiyomi.core.preference.Preference
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@ -16,8 +15,7 @@ class PreferenceMutableState<T>(
private val state = mutableStateOf(preference.get())
init {
preference.asFlow()
.distinctUntilChanged()
preference.changes()
.onEach { state.value = it }
.launchIn(scope)
}

View File

@ -52,7 +52,7 @@ class SetReadStatus(
return@withContext Result.InternalError(e)
}
if (read && preferences.removeAfterMarkedAsRead()) {
if (read && preferences.removeAfterMarkedAsRead().get()) {
manga.forEach {
deleteDownload.awaitAll(
manga = it,

View File

@ -12,7 +12,7 @@ class GetExtensionLanguages(
) {
fun subscribe(): Flow<List<String>> {
return combine(
preferences.enabledLanguages().asFlow(),
preferences.enabledLanguages().changes(),
extensionManager.getAvailableExtensionsFlow(),
) { enabledLanguage, availableExtensions ->
availableExtensions

View File

@ -16,7 +16,7 @@ class GetExtensionSources(
val isMultiLangSingleSource =
isMultiSource && extension.sources.map { it.name }.distinct().size == 1
return preferences.disabledSources().asFlow().map { disabledSources ->
return preferences.disabledSources().changes().map { disabledSources ->
fun Source.isEnabled() = id.toString() !in disabledSources
extension.sources

View File

@ -16,7 +16,7 @@ class GetExtensionsByType(
val showNsfwSources = preferences.showNsfwSource().get()
return combine(
preferences.enabledLanguages().asFlow(),
preferences.enabledLanguages().changes(),
extensionManager.getInstalledExtensionsFlow(),
extensionManager.getUntrustedExtensionsFlow(),
extensionManager.getAvailableExtensionsFlow(),

View File

@ -17,10 +17,10 @@ class GetEnabledSources(
fun subscribe(): Flow<List<Source>> {
return combine(
preferences.pinnedSources().asFlow(),
preferences.enabledLanguages().asFlow(),
preferences.disabledSources().asFlow(),
preferences.lastUsedSource().asFlow(),
preferences.pinnedSources().changes(),
preferences.enabledLanguages().changes(),
preferences.disabledSources().changes(),
preferences.lastUsedSource().changes(),
repository.getSources(),
) { pinnedSourceIds, enabledLanguages, disabledSources, lastUsedSource, sources ->
val duplicatePins = preferences.duplicatePinnedSources().get()

View File

@ -14,8 +14,8 @@ class GetLanguagesWithSources(
fun subscribe(): Flow<Map<String, List<Source>>> {
return combine(
preferences.enabledLanguages().asFlow(),
preferences.disabledSources().asFlow(),
preferences.enabledLanguages().changes(),
preferences.disabledSources().changes(),
repository.getOnlineSources(),
) { enabledLanguage, disabledSource, onlineSources ->
val sortedSources = onlineSources.sortedWith(

View File

@ -16,8 +16,8 @@ class GetSourcesWithFavoriteCount(
fun subscribe(): Flow<List<Pair<Source, Long>>> {
return combine(
preferences.migrationSortingDirection().asFlow(),
preferences.migrationSortingMode().asFlow(),
preferences.migrationSortingDirection().changes(),
preferences.migrationSortingMode().changes(),
repository.getSourcesWithFavoriteCount(),
) { direction, mode, list ->
list.sortedWith(sortFn(direction, mode))

View File

@ -1,5 +1,6 @@
package eu.kanade.domain.source.interactor
import eu.kanade.tachiyomi.core.preference.getAndSet
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.util.preference.minusAssign
import eu.kanade.tachiyomi.util.preference.plusAssign
@ -9,11 +10,9 @@ class ToggleLanguage(
) {
fun await(language: String) {
val enabled = language in preferences.enabledLanguages().get()
if (enabled) {
preferences.enabledLanguages() -= language
} else {
preferences.enabledLanguages() += language
val isEnabled = language in preferences.enabledLanguages().get()
preferences.enabledLanguages().getAndSet { enabled ->
if (isEnabled) enabled.minus(language) else enabled.plus(language)
}
}
}

View File

@ -1,6 +1,7 @@
package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.model.Source
import eu.kanade.tachiyomi.core.preference.getAndSet
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.util.preference.minusAssign
import eu.kanade.tachiyomi.util.preference.plusAssign
@ -14,10 +15,8 @@ class ToggleSource(
}
fun await(sourceId: Long, enable: Boolean = sourceId.toString() in preferences.disabledSources().get()) {
if (enable) {
preferences.disabledSources() -= sourceId.toString()
} else {
preferences.disabledSources() += sourceId.toString()
preferences.disabledSources().getAndSet { disabled ->
if (enable) disabled.minus("$sourceId") else disabled.plus("$sourceId")
}
}
}

View File

@ -1,6 +1,7 @@
package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.model.Source
import eu.kanade.tachiyomi.core.preference.getAndSet
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.util.preference.minusAssign
import eu.kanade.tachiyomi.util.preference.plusAssign
@ -11,10 +12,8 @@ class ToggleSourcePin(
fun await(source: Source) {
val isPinned = source.id.toString() in preferences.pinnedSources().get()
if (isPinned) {
preferences.pinnedSources() -= source.id.toString()
} else {
preferences.pinnedSources() += source.id.toString()
preferences.pinnedSources().getAndSet { pinned ->
if (isPinned) pinned.minus("${source.id}") else pinned.plus("${source.id}")
}
}
}

View File

@ -37,6 +37,7 @@ import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.glance.UpdatesGridGlanceWidget
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
import eu.kanade.tachiyomi.util.preference.asHotFlow
import eu.kanade.tachiyomi.util.system.WebViewUtil
@ -63,6 +64,7 @@ import java.security.Security
class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
private val preferences: PreferencesHelper by injectLazy()
private val networkPreferences: NetworkPreferences by injectLazy()
private val disableIncognitoReceiver = DisableIncognitoReceiver()
@ -82,6 +84,7 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
}
Injekt.importModule(AppModule(this))
Injekt.importModule(PreferenceModule(this))
Injekt.importModule(DomainModule())
setupAcra()
@ -90,7 +93,7 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
// Show notification to disable Incognito Mode when it's enabled
preferences.incognitoMode().asFlow()
preferences.incognitoMode().changes()
.onEach { enabled ->
val notificationManager = NotificationManagerCompat.from(this)
if (enabled) {
@ -141,7 +144,7 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
}
.launchIn(ProcessLifecycleOwner.get().lifecycleScope)
if (!LogcatLogger.isInstalled && preferences.verboseLogging()) {
if (!LogcatLogger.isInstalled && networkPreferences.verboseLogging().get()) {
LogcatLogger.install(AndroidLogcatLogger(LogPriority.VERBOSE))
}
}
@ -168,7 +171,7 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
diskCache(diskCacheInit)
crossfade((300 * this@App.animatorDurationScale).toInt())
allowRgb565(getSystemService<ActivityManager>()!!.isLowRamDevice)
if (preferences.verboseLogging()) logger(DebugLogger())
if (networkPreferences.verboseLogging().get()) logger(DebugLogger())
}.build()
}

View File

@ -13,6 +13,8 @@ import eu.kanade.data.AndroidDatabaseHandler
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.dateAdapter
import eu.kanade.data.listOfStringsAdapter
import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore
import eu.kanade.tachiyomi.core.preference.PreferenceStore
import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.download.DownloadManager
@ -22,7 +24,9 @@ import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.job.DelayedTrackingStore
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.util.system.isDevFlavor
import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.api.InjektModule
@ -84,8 +88,6 @@ class AppModule(val app: Application) : InjektModule {
}
}
addSingletonFactory { PreferencesHelper(app) }
addSingletonFactory { ChapterCache(app) }
addSingletonFactory { CoverCache(app) }
@ -106,8 +108,6 @@ class AppModule(val app: Application) : InjektModule {
// Asynchronously init expensive components for a faster cold start
ContextCompat.getMainExecutor(app).execute {
get<PreferencesHelper>()
get<NetworkHelper>()
get<SourceManager>()
@ -118,3 +118,23 @@ class AppModule(val app: Application) : InjektModule {
}
}
}
class PreferenceModule(val application: Application) : InjektModule {
override fun InjektRegistrar.registerInjectables() {
addSingletonFactory<PreferenceStore> {
AndroidPreferenceStore(application)
}
addSingletonFactory {
NetworkPreferences(
preferenceStore = get(),
verboseLogging = isDevFlavor,
)
}
addSingletonFactory {
PreferencesHelper(
context = application,
preferenceStore = get(),
)
}
}
}

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi
import android.content.Context
import android.os.Build
import androidx.core.content.edit
import androidx.preference.PreferenceManager
@ -12,6 +13,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.updater.AppUpdateJob
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.util.preference.minusAssign
@ -31,9 +33,11 @@ object Migrations {
* @param preferences Preferences of the application.
* @return true if a migration is performed, false otherwise.
*/
fun upgrade(preferences: PreferencesHelper): Boolean {
val context = preferences.context
fun upgrade(
context: Context,
preferences: PreferencesHelper,
networkPreferences: NetworkPreferences,
): Boolean {
val oldVersion = preferences.lastVersionCode().get()
if (oldVersion < BuildConfig.VERSION_CODE) {
preferences.lastVersionCode().set(BuildConfig.VERSION_CODE)
@ -143,7 +147,7 @@ object Migrations {
val wasDohEnabled = prefs.getBoolean("enable_doh", false)
if (wasDohEnabled) {
prefs.edit {
putInt(PreferenceKeys.dohProvider, PREF_DOH_CLOUDFLARE)
putInt(networkPreferences.dohProvider().key(), PREF_DOH_CLOUDFLARE)
remove("enable_doh")
}
}

View File

@ -84,7 +84,7 @@ class BackupNotifier(private val context: Context) {
val builder = with(progressNotificationBuilder) {
setContentTitle(context.getString(R.string.restoring_backup))
if (!preferences.hideNotificationContent()) {
if (!preferences.hideNotificationContent().get()) {
setContentText(content)
}

View File

@ -47,7 +47,7 @@ class DownloadCache(
private var rootDir = RootDirectory(getDirectoryFromPreference())
init {
preferences.downloadsDirectory().asFlow()
preferences.downloadsDirectory().changes()
.onEach {
lastRenew = 0L // invalidate cache
rootDir = RootDirectory(getDirectoryFromPreference())

View File

@ -414,7 +414,7 @@ class DownloadManager(
return if (categoriesForManga.intersect(categoriesToExclude).isNotEmpty()) {
chapters.filterNot { it.read }
} else if (!preferences.removeBookmarkedChapters()) {
} else if (!preferences.removeBookmarkedChapters().get()) {
chapters.filterNot { it.bookmark }
} else {
chapters

View File

@ -104,7 +104,7 @@ internal class DownloadNotifier(private val context: Context) {
download.pages!!.size,
)
if (preferences.hideNotificationContent()) {
if (preferences.hideNotificationContent().get()) {
setContentTitle(downloadingProgressText)
setContentText(null)
} else {

View File

@ -39,7 +39,7 @@ class DownloadProvider(private val context: Context) {
}
init {
preferences.downloadsDirectory().asFlow()
preferences.downloadsDirectory().changes()
.onEach { downloadsDir = UniFile.fromUri(context, it.toUri()) }
.launchIn(scope)
}

View File

@ -164,7 +164,7 @@ class DownloadService : Service() {
*/
private fun onNetworkStateChanged() {
if (isOnline()) {
if (preferences.downloadOnlyOverWifi() && !isConnectedToWifi()) {
if (preferences.downloadOnlyOverWifi().get() && !isConnectedToWifi()) {
stopDownloads(R.string.download_notifier_text_only_wifi)
} else {
val started = downloadManager.startDownloads()

View File

@ -71,7 +71,7 @@ class LibraryUpdateNotifier(private val context: Context) {
* @param total the total progress.
*/
fun showProgressNotification(manga: List<Manga>, current: Int, total: Int) {
if (preferences.hideNotificationContent()) {
if (preferences.hideNotificationContent().get()) {
progressNotificationBuilder
.setContentTitle(context.getString(R.string.notification_check_updates))
.setContentText("($current/$total)")
@ -167,12 +167,12 @@ class LibraryUpdateNotifier(private val context: Context) {
Notifications.ID_NEW_CHAPTERS,
context.notification(Notifications.CHANNEL_NEW_CHAPTERS) {
setContentTitle(context.getString(R.string.notification_new_chapters))
if (updates.size == 1 && !preferences.hideNotificationContent()) {
if (updates.size == 1 && !preferences.hideNotificationContent().get()) {
setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN))
} else {
setContentText(context.resources.getQuantityString(R.plurals.notification_new_chapters_summary, updates.size, updates.size))
if (!preferences.hideNotificationContent()) {
if (!preferences.hideNotificationContent().get()) {
setStyle(
NotificationCompat.BigTextStyle().bigText(
updates.joinToString("\n") {
@ -197,7 +197,7 @@ class LibraryUpdateNotifier(private val context: Context) {
)
// Per-manga notification
if (!preferences.hideNotificationContent()) {
if (!preferences.hideNotificationContent().get()) {
launchUI {
updates.forEach { (manga, chapters) ->
notify(manga.id.hashCode(), createNewChaptersNotification(manga, chapters))

View File

@ -380,7 +380,7 @@ class LibraryUpdateService(
failedUpdates.add(mangaWithNotif to errorMessage)
}
if (preferences.autoUpdateTrackers()) {
if (preferences.autoUpdateTrackers().get()) {
updateTrackings(mangaWithNotif, loggedServices)
}
}
@ -430,7 +430,7 @@ class LibraryUpdateService(
val source = sourceManager.getOrStub(manga.source)
// Update manga metadata if needed
if (preferences.autoUpdateMetadata()) {
if (preferences.autoUpdateMetadata().get()) {
val networkManga = source.getMangaDetails(manga.toSManga())
updateManga.awaitUpdateFromSource(manga, networkManga, manualFetch = false, coverCache)
}

View File

@ -248,7 +248,7 @@ class NotificationReceiver : BroadcastReceiver() {
val toUpdate = chapterUrls.mapNotNull { getChapter.await(it, mangaId) }
.map {
val chapter = it.copy(read = true)
if (preferences.removeAfterMarkedAsRead()) {
if (preferences.removeAfterMarkedAsRead().get()) {
val manga = getManga.await(mangaId)
if (manga != null) {
val source = sourceManager.get(manga.source)

View File

@ -56,10 +56,6 @@ object PreferenceKeys {
const val searchPinnedSourcesOnly = "search_pinned_sources_only"
const val dohProvider = "doh_provider"
const val defaultUserAgent = "default_user_agent"
const val defaultChapterFilterByRead = "default_chapter_filter_by_read"
const val defaultChapterFilterByDownloaded = "default_chapter_filter_by_downloaded"
@ -72,8 +68,6 @@ object PreferenceKeys {
const val defaultChapterDisplayByNameOrNumber = "default_chapter_display_by_name_or_number"
const val verboseLogging = "verbose_logging"
const val autoClearChapterCache = "auto_clear_chapter_cache"
fun trackUsername(syncId: Long) = "pref_mangasync_username_$syncId"

View File

@ -3,12 +3,11 @@ package eu.kanade.tachiyomi.data.preference
import android.content.Context
import android.os.Build
import android.os.Environment
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.preference.PreferenceManager
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.core.preference.PreferenceStore
import eu.kanade.tachiyomi.core.preference.getEnum
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.anilist.Anilist
@ -18,7 +17,6 @@ import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.system.isDevFlavor
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
import eu.kanade.tachiyomi.widget.ExtendedNavigationView
import java.io.File
@ -29,10 +27,10 @@ import eu.kanade.domain.manga.model.Manga as DomainManga
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values
class PreferencesHelper(val context: Context) {
private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
private val flowPrefs = FlowSharedPreferences(prefs)
class PreferencesHelper(
val context: Context,
private val preferenceStore: PreferenceStore,
) {
private val defaultDownloadsDir = File(
Environment.getExternalStorageDirectory().absolutePath + File.separator +
@ -46,300 +44,290 @@ class PreferencesHelper(val context: Context) {
"backup",
).toUri()
fun confirmExit() = prefs.getBoolean(Keys.confirmExit, false)
fun confirmExit() = this.preferenceStore.getBoolean(Keys.confirmExit, false)
fun sideNavIconAlignment() = flowPrefs.getInt("pref_side_nav_icon_alignment", 0)
fun sideNavIconAlignment() = this.preferenceStore.getInt("pref_side_nav_icon_alignment", 0)
fun useAuthenticator() = flowPrefs.getBoolean("use_biometric_lock", false)
fun useAuthenticator() = this.preferenceStore.getBoolean("use_biometric_lock", false)
fun lockAppAfter() = flowPrefs.getInt("lock_app_after", 0)
fun lockAppAfter() = this.preferenceStore.getInt("lock_app_after", 0)
/**
* For app lock. Will be set when there is a pending timed lock.
* Otherwise this pref should be deleted.
*/
fun lastAppClosed() = flowPrefs.getLong("last_app_closed", 0)
fun lastAppClosed() = this.preferenceStore.getLong("last_app_closed", 0)
fun secureScreen() = flowPrefs.getEnum("secure_screen_v2", Values.SecureScreenMode.INCOGNITO)
fun secureScreen() = this.preferenceStore.getEnum("secure_screen_v2", Values.SecureScreenMode.INCOGNITO)
fun hideNotificationContent() = prefs.getBoolean(Keys.hideNotificationContent, false)
fun hideNotificationContent() = this.preferenceStore.getBoolean(Keys.hideNotificationContent, false)
fun autoUpdateMetadata() = prefs.getBoolean(Keys.autoUpdateMetadata, false)
fun autoUpdateMetadata() = this.preferenceStore.getBoolean(Keys.autoUpdateMetadata, false)
fun autoUpdateTrackers() = prefs.getBoolean(Keys.autoUpdateTrackers, false)
fun autoUpdateTrackers() = this.preferenceStore.getBoolean(Keys.autoUpdateTrackers, false)
fun themeMode() = flowPrefs.getEnum(
fun themeMode() = this.preferenceStore.getEnum(
"pref_theme_mode_key",
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { Values.ThemeMode.system } else { Values.ThemeMode.light },
)
fun appTheme() = flowPrefs.getEnum(
fun appTheme() = this.preferenceStore.getEnum(
"pref_app_theme",
if (DeviceUtil.isDynamicColorAvailable) { Values.AppTheme.MONET } else { Values.AppTheme.DEFAULT },
)
fun themeDarkAmoled() = flowPrefs.getBoolean("pref_theme_dark_amoled_key", false)
fun themeDarkAmoled() = this.preferenceStore.getBoolean("pref_theme_dark_amoled_key", false)
fun pageTransitions() = flowPrefs.getBoolean("pref_enable_transitions_key", true)
fun pageTransitions() = this.preferenceStore.getBoolean("pref_enable_transitions_key", true)
fun doubleTapAnimSpeed() = flowPrefs.getInt("pref_double_tap_anim_speed", 500)
fun doubleTapAnimSpeed() = this.preferenceStore.getInt("pref_double_tap_anim_speed", 500)
fun showPageNumber() = flowPrefs.getBoolean("pref_show_page_number_key", true)
fun showPageNumber() = this.preferenceStore.getBoolean("pref_show_page_number_key", true)
fun dualPageSplitPaged() = flowPrefs.getBoolean("pref_dual_page_split", false)
fun dualPageSplitPaged() = this.preferenceStore.getBoolean("pref_dual_page_split", false)
fun dualPageInvertPaged() = flowPrefs.getBoolean("pref_dual_page_invert", false)
fun dualPageInvertPaged() = this.preferenceStore.getBoolean("pref_dual_page_invert", false)
fun dualPageSplitWebtoon() = flowPrefs.getBoolean("pref_dual_page_split_webtoon", false)
fun dualPageSplitWebtoon() = this.preferenceStore.getBoolean("pref_dual_page_split_webtoon", false)
fun dualPageInvertWebtoon() = flowPrefs.getBoolean("pref_dual_page_invert_webtoon", false)
fun dualPageInvertWebtoon() = this.preferenceStore.getBoolean("pref_dual_page_invert_webtoon", false)
fun longStripSplitWebtoon() = flowPrefs.getBoolean("pref_long_strip_split_webtoon", true)
fun longStripSplitWebtoon() = this.preferenceStore.getBoolean("pref_long_strip_split_webtoon", true)
fun showReadingMode() = prefs.getBoolean(Keys.showReadingMode, true)
fun showReadingMode() = this.preferenceStore.getBoolean(Keys.showReadingMode, true)
fun trueColor() = flowPrefs.getBoolean("pref_true_color_key", false)
fun trueColor() = this.preferenceStore.getBoolean("pref_true_color_key", false)
fun fullscreen() = flowPrefs.getBoolean("fullscreen", true)
fun fullscreen() = this.preferenceStore.getBoolean("fullscreen", true)
fun cutoutShort() = flowPrefs.getBoolean("cutout_short", true)
fun cutoutShort() = this.preferenceStore.getBoolean("cutout_short", true)
fun keepScreenOn() = flowPrefs.getBoolean("pref_keep_screen_on_key", true)
fun keepScreenOn() = this.preferenceStore.getBoolean("pref_keep_screen_on_key", true)
fun customBrightness() = flowPrefs.getBoolean("pref_custom_brightness_key", false)
fun customBrightness() = this.preferenceStore.getBoolean("pref_custom_brightness_key", false)
fun customBrightnessValue() = flowPrefs.getInt("custom_brightness_value", 0)
fun customBrightnessValue() = this.preferenceStore.getInt("custom_brightness_value", 0)
fun colorFilter() = flowPrefs.getBoolean("pref_color_filter_key", false)
fun colorFilter() = this.preferenceStore.getBoolean("pref_color_filter_key", false)
fun colorFilterValue() = flowPrefs.getInt("color_filter_value", 0)
fun colorFilterValue() = this.preferenceStore.getInt("color_filter_value", 0)
fun colorFilterMode() = flowPrefs.getInt("color_filter_mode", 0)
fun colorFilterMode() = this.preferenceStore.getInt("color_filter_mode", 0)
fun grayscale() = flowPrefs.getBoolean("pref_grayscale", false)
fun grayscale() = this.preferenceStore.getBoolean("pref_grayscale", false)
fun invertedColors() = flowPrefs.getBoolean("pref_inverted_colors", false)
fun invertedColors() = this.preferenceStore.getBoolean("pref_inverted_colors", false)
fun defaultReadingMode() = prefs.getInt(Keys.defaultReadingMode, ReadingModeType.RIGHT_TO_LEFT.flagValue)
fun defaultReadingMode() = this.preferenceStore.getInt(Keys.defaultReadingMode, ReadingModeType.RIGHT_TO_LEFT.flagValue)
fun defaultOrientationType() = prefs.getInt(Keys.defaultOrientationType, OrientationType.FREE.flagValue)
fun defaultOrientationType() = this.preferenceStore.getInt(Keys.defaultOrientationType, OrientationType.FREE.flagValue)
fun imageScaleType() = flowPrefs.getInt("pref_image_scale_type_key", 1)
fun imageScaleType() = this.preferenceStore.getInt("pref_image_scale_type_key", 1)
fun zoomStart() = flowPrefs.getInt("pref_zoom_start_key", 1)
fun zoomStart() = this.preferenceStore.getInt("pref_zoom_start_key", 1)
fun readerTheme() = flowPrefs.getInt("pref_reader_theme_key", 1)
fun readerTheme() = this.preferenceStore.getInt("pref_reader_theme_key", 1)
fun alwaysShowChapterTransition() = flowPrefs.getBoolean("always_show_chapter_transition", true)
fun alwaysShowChapterTransition() = this.preferenceStore.getBoolean("always_show_chapter_transition", true)
fun cropBorders() = flowPrefs.getBoolean("crop_borders", false)
fun cropBorders() = this.preferenceStore.getBoolean("crop_borders", false)
fun navigateToPan() = flowPrefs.getBoolean("navigate_pan", true)
fun navigateToPan() = this.preferenceStore.getBoolean("navigate_pan", true)
fun landscapeZoom() = flowPrefs.getBoolean("landscape_zoom", true)
fun landscapeZoom() = this.preferenceStore.getBoolean("landscape_zoom", true)
fun cropBordersWebtoon() = flowPrefs.getBoolean("crop_borders_webtoon", false)
fun cropBordersWebtoon() = this.preferenceStore.getBoolean("crop_borders_webtoon", false)
fun webtoonSidePadding() = flowPrefs.getInt("webtoon_side_padding", 0)
fun webtoonSidePadding() = this.preferenceStore.getInt("webtoon_side_padding", 0)
fun pagerNavInverted() = flowPrefs.getEnum("reader_tapping_inverted", Values.TappingInvertMode.NONE)
fun pagerNavInverted() = this.preferenceStore.getEnum("reader_tapping_inverted", Values.TappingInvertMode.NONE)
fun webtoonNavInverted() = flowPrefs.getEnum("reader_tapping_inverted_webtoon", Values.TappingInvertMode.NONE)
fun webtoonNavInverted() = this.preferenceStore.getEnum("reader_tapping_inverted_webtoon", Values.TappingInvertMode.NONE)
fun readWithLongTap() = flowPrefs.getBoolean("reader_long_tap", true)
fun readWithLongTap() = this.preferenceStore.getBoolean("reader_long_tap", true)
fun readWithVolumeKeys() = flowPrefs.getBoolean("reader_volume_keys", false)
fun readWithVolumeKeys() = this.preferenceStore.getBoolean("reader_volume_keys", false)
fun readWithVolumeKeysInverted() = flowPrefs.getBoolean("reader_volume_keys_inverted", false)
fun readWithVolumeKeysInverted() = this.preferenceStore.getBoolean("reader_volume_keys_inverted", false)
fun navigationModePager() = flowPrefs.getInt("reader_navigation_mode_pager", 0)
fun navigationModePager() = this.preferenceStore.getInt("reader_navigation_mode_pager", 0)
fun navigationModeWebtoon() = flowPrefs.getInt("reader_navigation_mode_webtoon", 0)
fun navigationModeWebtoon() = this.preferenceStore.getInt("reader_navigation_mode_webtoon", 0)
fun showNavigationOverlayNewUser() = flowPrefs.getBoolean("reader_navigation_overlay_new_user", true)
fun showNavigationOverlayNewUser() = this.preferenceStore.getBoolean("reader_navigation_overlay_new_user", true)
fun showNavigationOverlayOnStart() = flowPrefs.getBoolean("reader_navigation_overlay_on_start", false)
fun showNavigationOverlayOnStart() = this.preferenceStore.getBoolean("reader_navigation_overlay_on_start", false)
fun readerHideThreshold() = flowPrefs.getEnum("reader_hide_threshold", Values.ReaderHideThreshold.LOW)
fun readerHideThreshold() = this.preferenceStore.getEnum("reader_hide_threshold", Values.ReaderHideThreshold.LOW)
fun portraitColumns() = flowPrefs.getInt("pref_library_columns_portrait_key", 0)
fun portraitColumns() = this.preferenceStore.getInt("pref_library_columns_portrait_key", 0)
fun landscapeColumns() = flowPrefs.getInt("pref_library_columns_landscape_key", 0)
fun landscapeColumns() = this.preferenceStore.getInt("pref_library_columns_landscape_key", 0)
fun autoUpdateTrack() = prefs.getBoolean(Keys.autoUpdateTrack, true)
fun autoUpdateTrack() = this.preferenceStore.getBoolean(Keys.autoUpdateTrack, true)
fun lastUsedSource() = flowPrefs.getLong("last_catalogue_source", -1)
fun lastUsedSource() = this.preferenceStore.getLong("last_catalogue_source", -1)
fun lastUsedCategory() = flowPrefs.getInt("last_used_category", 0)
fun lastUsedCategory() = this.preferenceStore.getInt("last_used_category", 0)
fun lastVersionCode() = flowPrefs.getInt("last_version_code", 0)
fun lastVersionCode() = this.preferenceStore.getInt("last_version_code", 0)
fun sourceDisplayMode() = flowPrefs.getObject("pref_display_mode_catalogue", LibraryDisplayMode.Serializer, LibraryDisplayMode.default)
fun sourceDisplayMode() = this.preferenceStore.getObject("pref_display_mode_catalogue", LibraryDisplayMode.default, LibraryDisplayMode.Serializer::serialize, LibraryDisplayMode.Serializer::deserialize)
fun enabledLanguages() = flowPrefs.getStringSet("source_languages", LocaleHelper.getDefaultEnabledLanguages())
fun enabledLanguages() = this.preferenceStore.getStringSet("source_languages", LocaleHelper.getDefaultEnabledLanguages())
fun trackUsername(sync: TrackService) = prefs.getString(Keys.trackUsername(sync.id), "")
fun trackUsername(sync: TrackService) = this.preferenceStore.getString(Keys.trackUsername(sync.id), "")
fun trackPassword(sync: TrackService) = prefs.getString(Keys.trackPassword(sync.id), "")
fun trackPassword(sync: TrackService) = this.preferenceStore.getString(Keys.trackPassword(sync.id), "")
fun setTrackCredentials(sync: TrackService, username: String, password: String) {
prefs.edit {
putString(Keys.trackUsername(sync.id), username)
putString(Keys.trackPassword(sync.id), password)
}
trackUsername(sync).set(username)
trackPassword(sync).set(password)
}
fun trackToken(sync: TrackService) = flowPrefs.getString(Keys.trackToken(sync.id), "")
fun trackToken(sync: TrackService) = this.preferenceStore.getString(Keys.trackToken(sync.id), "")
fun anilistScoreType() = flowPrefs.getString("anilist_score_type", Anilist.POINT_10)
fun anilistScoreType() = this.preferenceStore.getString("anilist_score_type", Anilist.POINT_10)
fun backupsDirectory() = flowPrefs.getString("backup_directory", defaultBackupDir.toString())
fun backupsDirectory() = this.preferenceStore.getString("backup_directory", defaultBackupDir.toString())
fun relativeTime() = flowPrefs.getInt("relative_time", 7)
fun relativeTime() = this.preferenceStore.getInt("relative_time", 7)
fun dateFormat(format: String = flowPrefs.getString(Keys.dateFormat, "").get()): DateFormat = when (format) {
fun dateFormat(format: String = this.preferenceStore.getString(Keys.dateFormat, "").get()): DateFormat = when (format) {
"" -> DateFormat.getDateInstance(DateFormat.SHORT)
else -> SimpleDateFormat(format, Locale.getDefault())
}
fun downloadsDirectory() = flowPrefs.getString("download_directory", defaultDownloadsDir.toString())
fun downloadsDirectory() = this.preferenceStore.getString("download_directory", defaultDownloadsDir.toString())
fun downloadOnlyOverWifi() = prefs.getBoolean(Keys.downloadOnlyOverWifi, true)
fun downloadOnlyOverWifi() = this.preferenceStore.getBoolean(Keys.downloadOnlyOverWifi, true)
fun saveChaptersAsCBZ() = flowPrefs.getBoolean("save_chapter_as_cbz", true)
fun saveChaptersAsCBZ() = this.preferenceStore.getBoolean("save_chapter_as_cbz", true)
fun splitTallImages() = flowPrefs.getBoolean("split_tall_images", false)
fun splitTallImages() = this.preferenceStore.getBoolean("split_tall_images", false)
fun folderPerManga() = prefs.getBoolean(Keys.folderPerManga, false)
fun folderPerManga() = this.preferenceStore.getBoolean(Keys.folderPerManga, false)
fun numberOfBackups() = flowPrefs.getInt("backup_slots", 2)
fun numberOfBackups() = this.preferenceStore.getInt("backup_slots", 2)
fun backupInterval() = flowPrefs.getInt("backup_interval", 12)
fun backupInterval() = this.preferenceStore.getInt("backup_interval", 12)
fun removeAfterReadSlots() = prefs.getInt(Keys.removeAfterReadSlots, -1)
fun removeAfterReadSlots() = this.preferenceStore.getInt(Keys.removeAfterReadSlots, -1)
fun removeAfterMarkedAsRead() = prefs.getBoolean(Keys.removeAfterMarkedAsRead, false)
fun removeAfterMarkedAsRead() = this.preferenceStore.getBoolean(Keys.removeAfterMarkedAsRead, false)
fun removeBookmarkedChapters() = prefs.getBoolean(Keys.removeBookmarkedChapters, false)
fun removeBookmarkedChapters() = this.preferenceStore.getBoolean(Keys.removeBookmarkedChapters, false)
fun removeExcludeCategories() = flowPrefs.getStringSet("remove_exclude_categories", emptySet())
fun removeExcludeCategories() = this.preferenceStore.getStringSet("remove_exclude_categories", emptySet())
fun libraryUpdateInterval() = flowPrefs.getInt("pref_library_update_interval_key", 24)
fun libraryUpdateLastTimestamp() = flowPrefs.getLong("library_update_last_timestamp", 0L)
fun libraryUpdateInterval() = this.preferenceStore.getInt("pref_library_update_interval_key", 24)
fun libraryUpdateLastTimestamp() = this.preferenceStore.getLong("library_update_last_timestamp", 0L)
fun libraryUpdateDeviceRestriction() = flowPrefs.getStringSet("library_update_restriction", setOf(DEVICE_ONLY_ON_WIFI))
fun libraryUpdateMangaRestriction() = flowPrefs.getStringSet("library_update_manga_restriction", setOf(MANGA_HAS_UNREAD, MANGA_NON_COMPLETED, MANGA_NON_READ))
fun libraryUpdateDeviceRestriction() = this.preferenceStore.getStringSet("library_update_restriction", setOf(DEVICE_ONLY_ON_WIFI))
fun libraryUpdateMangaRestriction() = this.preferenceStore.getStringSet("library_update_manga_restriction", setOf(MANGA_HAS_UNREAD, MANGA_NON_COMPLETED, MANGA_NON_READ))
fun showUpdatesNavBadge() = flowPrefs.getBoolean("library_update_show_tab_badge", false)
fun unreadUpdatesCount() = flowPrefs.getInt("library_unread_updates_count", 0)
fun showUpdatesNavBadge() = this.preferenceStore.getBoolean("library_update_show_tab_badge", false)
fun unreadUpdatesCount() = this.preferenceStore.getInt("library_unread_updates_count", 0)
fun libraryUpdateCategories() = flowPrefs.getStringSet("library_update_categories", emptySet())
fun libraryUpdateCategoriesExclude() = flowPrefs.getStringSet("library_update_categories_exclude", emptySet())
fun libraryUpdateCategories() = this.preferenceStore.getStringSet("library_update_categories", emptySet())
fun libraryUpdateCategoriesExclude() = this.preferenceStore.getStringSet("library_update_categories_exclude", emptySet())
fun libraryDisplayMode() = flowPrefs.getObject("pref_display_mode_library", LibraryDisplayMode.Serializer, LibraryDisplayMode.default)
fun libraryDisplayMode() = this.preferenceStore.getObject("pref_display_mode_library", LibraryDisplayMode.default, LibraryDisplayMode.Serializer::serialize, LibraryDisplayMode.Serializer::deserialize)
fun downloadBadge() = flowPrefs.getBoolean("display_download_badge", false)
fun downloadBadge() = this.preferenceStore.getBoolean("display_download_badge", false)
fun localBadge() = flowPrefs.getBoolean("display_local_badge", true)
fun localBadge() = this.preferenceStore.getBoolean("display_local_badge", true)
fun downloadedOnly() = flowPrefs.getBoolean("pref_downloaded_only", false)
fun downloadedOnly() = this.preferenceStore.getBoolean("pref_downloaded_only", false)
fun unreadBadge() = flowPrefs.getBoolean("display_unread_badge", true)
fun unreadBadge() = this.preferenceStore.getBoolean("display_unread_badge", true)
fun languageBadge() = flowPrefs.getBoolean("display_language_badge", false)
fun languageBadge() = this.preferenceStore.getBoolean("display_language_badge", false)
fun categoryTabs() = flowPrefs.getBoolean("display_category_tabs", true)
fun categoryTabs() = this.preferenceStore.getBoolean("display_category_tabs", true)
fun categoryNumberOfItems() = flowPrefs.getBoolean("display_number_of_items", false)
fun categoryNumberOfItems() = this.preferenceStore.getBoolean("display_number_of_items", false)
fun filterDownloaded() = flowPrefs.getInt(Keys.filterDownloaded, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterDownloaded() = this.preferenceStore.getInt(Keys.filterDownloaded, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterUnread() = flowPrefs.getInt(Keys.filterUnread, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterUnread() = this.preferenceStore.getInt(Keys.filterUnread, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterStarted() = flowPrefs.getInt(Keys.filterStarted, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterStarted() = this.preferenceStore.getInt(Keys.filterStarted, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterCompleted() = flowPrefs.getInt(Keys.filterCompleted, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterCompleted() = this.preferenceStore.getInt(Keys.filterCompleted, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterTracking(name: Int) = flowPrefs.getInt("${Keys.filterTracked}_$name", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterTracking(name: Int) = this.preferenceStore.getInt("${Keys.filterTracked}_$name", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun librarySortingMode() = flowPrefs.getObject(Keys.librarySortingMode, LibrarySort.Serializer, LibrarySort.default)
fun librarySortingMode() = this.preferenceStore.getObject(Keys.librarySortingMode, LibrarySort.default, LibrarySort.Serializer::serialize, LibrarySort.Serializer::deserialize)
fun migrationSortingMode() = flowPrefs.getEnum(Keys.migrationSortingMode, SetMigrateSorting.Mode.ALPHABETICAL)
fun migrationSortingDirection() = flowPrefs.getEnum(Keys.migrationSortingDirection, SetMigrateSorting.Direction.ASCENDING)
fun migrationSortingMode() = this.preferenceStore.getEnum(Keys.migrationSortingMode, SetMigrateSorting.Mode.ALPHABETICAL)
fun migrationSortingDirection() = this.preferenceStore.getEnum(Keys.migrationSortingDirection, SetMigrateSorting.Direction.ASCENDING)
fun automaticExtUpdates() = flowPrefs.getBoolean("automatic_ext_updates", true)
fun automaticExtUpdates() = this.preferenceStore.getBoolean("automatic_ext_updates", true)
fun showNsfwSource() = flowPrefs.getBoolean("show_nsfw_source", true)
fun showNsfwSource() = this.preferenceStore.getBoolean("show_nsfw_source", true)
fun extensionUpdatesCount() = flowPrefs.getInt("ext_updates_count", 0)
fun extensionUpdatesCount() = this.preferenceStore.getInt("ext_updates_count", 0)
fun lastAppCheck() = flowPrefs.getLong("last_app_check", 0)
fun lastExtCheck() = flowPrefs.getLong("last_ext_check", 0)
fun lastAppCheck() = this.preferenceStore.getLong("last_app_check", 0)
fun lastExtCheck() = this.preferenceStore.getLong("last_ext_check", 0)
fun searchPinnedSourcesOnly() = prefs.getBoolean(Keys.searchPinnedSourcesOnly, false)
fun searchPinnedSourcesOnly() = this.preferenceStore.getBoolean(Keys.searchPinnedSourcesOnly, false)
fun disabledSources() = flowPrefs.getStringSet("hidden_catalogues", emptySet())
fun disabledSources() = this.preferenceStore.getStringSet("hidden_catalogues", emptySet())
fun pinnedSources() = flowPrefs.getStringSet("pinned_catalogues", emptySet())
fun pinnedSources() = this.preferenceStore.getStringSet("pinned_catalogues", emptySet())
fun downloadNewChapters() = flowPrefs.getBoolean("download_new", false)
fun downloadNewChapters() = this.preferenceStore.getBoolean("download_new", false)
fun downloadNewChapterCategories() = flowPrefs.getStringSet("download_new_categories", emptySet())
fun downloadNewChapterCategoriesExclude() = flowPrefs.getStringSet("download_new_categories_exclude", emptySet())
fun downloadNewChapterCategories() = this.preferenceStore.getStringSet("download_new_categories", emptySet())
fun downloadNewChapterCategoriesExclude() = this.preferenceStore.getStringSet("download_new_categories_exclude", emptySet())
fun autoDownloadWhileReading() = flowPrefs.getInt("auto_download_while_reading", 0)
fun autoDownloadWhileReading() = this.preferenceStore.getInt("auto_download_while_reading", 0)
fun defaultCategory() = prefs.getInt(Keys.defaultCategory, -1)
fun defaultCategory() = this.preferenceStore.getInt(Keys.defaultCategory, -1)
fun categorizedDisplaySettings() = flowPrefs.getBoolean("categorized_display", false)
fun categorizedDisplaySettings() = this.preferenceStore.getBoolean("categorized_display", false)
fun skipRead() = prefs.getBoolean(Keys.skipRead, false)
fun skipRead() = this.preferenceStore.getBoolean(Keys.skipRead, false)
fun skipFiltered() = prefs.getBoolean(Keys.skipFiltered, true)
fun skipFiltered() = this.preferenceStore.getBoolean(Keys.skipFiltered, true)
fun migrateFlags() = flowPrefs.getInt("migrate_flags", Int.MAX_VALUE)
fun migrateFlags() = this.preferenceStore.getInt("migrate_flags", Int.MAX_VALUE)
fun trustedSignatures() = flowPrefs.getStringSet("trusted_signatures", emptySet())
fun trustedSignatures() = this.preferenceStore.getStringSet("trusted_signatures", emptySet())
fun dohProvider() = prefs.getInt(Keys.dohProvider, -1)
fun filterChapterByRead() = this.preferenceStore.getInt(Keys.defaultChapterFilterByRead, DomainManga.SHOW_ALL.toInt())
fun defaultUserAgent() = flowPrefs.getString(Keys.defaultUserAgent, "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0")
fun filterChapterByDownloaded() = this.preferenceStore.getInt(Keys.defaultChapterFilterByDownloaded, DomainManga.SHOW_ALL.toInt())
fun filterChapterByRead() = prefs.getInt(Keys.defaultChapterFilterByRead, DomainManga.SHOW_ALL.toInt())
fun filterChapterByBookmarked() = this.preferenceStore.getInt(Keys.defaultChapterFilterByBookmarked, DomainManga.SHOW_ALL.toInt())
fun filterChapterByDownloaded() = prefs.getInt(Keys.defaultChapterFilterByDownloaded, DomainManga.SHOW_ALL.toInt())
fun sortChapterBySourceOrNumber() = this.preferenceStore.getInt(Keys.defaultChapterSortBySourceOrNumber, DomainManga.CHAPTER_SORTING_SOURCE.toInt())
fun filterChapterByBookmarked() = prefs.getInt(Keys.defaultChapterFilterByBookmarked, DomainManga.SHOW_ALL.toInt())
fun displayChapterByNameOrNumber() = this.preferenceStore.getInt(Keys.defaultChapterDisplayByNameOrNumber, DomainManga.CHAPTER_DISPLAY_NAME.toInt())
fun sortChapterBySourceOrNumber() = prefs.getInt(Keys.defaultChapterSortBySourceOrNumber, DomainManga.CHAPTER_SORTING_SOURCE.toInt())
fun sortChapterByAscendingOrDescending() = this.preferenceStore.getInt(Keys.defaultChapterSortByAscendingOrDescending, DomainManga.CHAPTER_SORT_DESC.toInt())
fun displayChapterByNameOrNumber() = prefs.getInt(Keys.defaultChapterDisplayByNameOrNumber, DomainManga.CHAPTER_DISPLAY_NAME.toInt())
fun incognitoMode() = this.preferenceStore.getBoolean("incognito_mode", false)
fun sortChapterByAscendingOrDescending() = prefs.getInt(Keys.defaultChapterSortByAscendingOrDescending, DomainManga.CHAPTER_SORT_DESC.toInt())
fun tabletUiMode() = this.preferenceStore.getEnum("tablet_ui_mode", Values.TabletUiMode.AUTOMATIC)
fun incognitoMode() = flowPrefs.getBoolean("incognito_mode", false)
fun tabletUiMode() = flowPrefs.getEnum("tablet_ui_mode", Values.TabletUiMode.AUTOMATIC)
fun extensionInstaller() = flowPrefs.getEnum(
fun extensionInstaller() = this.preferenceStore.getEnum(
"extension_installer",
if (DeviceUtil.isMiui) Values.ExtensionInstaller.LEGACY else Values.ExtensionInstaller.PACKAGEINSTALLER,
)
fun verboseLogging() = prefs.getBoolean(Keys.verboseLogging, isDevFlavor)
fun autoClearChapterCache() = this.preferenceStore.getBoolean(Keys.autoClearChapterCache, false)
fun autoClearChapterCache() = prefs.getBoolean(Keys.autoClearChapterCache, false)
fun duplicatePinnedSources() = flowPrefs.getBoolean("duplicate_pinned_sources", false)
fun duplicatePinnedSources() = this.preferenceStore.getBoolean("duplicate_pinned_sources", false)
fun setChapterSettingsDefault(manga: Manga) {
prefs.edit {
putInt(Keys.defaultChapterFilterByRead, manga.readFilter)
putInt(Keys.defaultChapterFilterByDownloaded, manga.downloadedFilter)
putInt(Keys.defaultChapterFilterByBookmarked, manga.bookmarkedFilter)
putInt(Keys.defaultChapterSortBySourceOrNumber, manga.sorting)
putInt(Keys.defaultChapterDisplayByNameOrNumber, manga.displayMode)
putInt(Keys.defaultChapterSortByAscendingOrDescending, if (manga.sortDescending()) DomainManga.CHAPTER_SORT_DESC.toInt() else DomainManga.CHAPTER_SORT_ASC.toInt())
}
filterChapterByRead().set(manga.readFilter)
filterChapterByDownloaded().set(manga.downloadedFilter)
filterChapterByBookmarked().set(manga.bookmarkedFilter)
sortChapterBySourceOrNumber().set(manga.sorting)
displayChapterByNameOrNumber().set(manga.displayMode)
sortChapterByAscendingOrDescending().set(if (manga.sortDescending()) DomainManga.CHAPTER_SORT_DESC.toInt() else DomainManga.CHAPTER_SORT_ASC.toInt())
}
}

View File

@ -69,9 +69,9 @@ abstract class TrackService(val id: Long) {
get() = getUsername().isNotEmpty() &&
getPassword().isNotEmpty()
fun getUsername() = preferences.trackUsername(this)!!
fun getUsername() = preferences.trackUsername(this).get()
fun getPassword() = preferences.trackPassword(this)!!
fun getPassword() = preferences.trackPassword(this).get()
fun saveCredentials(username: String, password: String) {
preferences.setTrackCredentials(this, username, password)

View File

@ -99,6 +99,6 @@ class MangaUpdates(private val context: Context, id: Long) : TrackService(id) {
}
fun restoreSession(): String? {
return preferences.trackPassword(this)
return preferences.trackPassword(this).get()
}
}

View File

@ -193,7 +193,7 @@ class ExtensionManager(
.map(AvailableSources::lang)
val deviceLanguage = Locale.getDefault().language
val defaultLanguages = preferences.enabledLanguages().defaultValue
val defaultLanguages = preferences.enabledLanguages().defaultValue()
val languagesToEnable = availableLanguages.filter {
it != deviceLanguage && it.startsWith(deviceLanguage)
}

View File

@ -82,8 +82,8 @@ class SecureActivityDelegateImpl : SecureActivityDelegate, DefaultLifecycleObser
}
private fun setSecureScreen() {
val secureScreenFlow = preferences.secureScreen().asFlow()
val incognitoModeFlow = preferences.incognitoMode().asFlow()
val secureScreenFlow = preferences.secureScreen().changes()
val incognitoModeFlow = preferences.incognitoMode().changes()
combine(secureScreenFlow, incognitoModeFlow) { secureScreen, incognitoMode ->
secureScreen == PreferenceValues.SecureScreenMode.ALWAYS ||
secureScreen == PreferenceValues.SecureScreenMode.INCOGNITO && incognitoMode

View File

@ -1,8 +1,8 @@
package eu.kanade.tachiyomi.ui.base.presenter
import android.os.Bundle
import com.fredporciuncula.flow.preferences.Preference
import eu.kanade.core.prefs.PreferenceMutableState
import eu.kanade.tachiyomi.core.preference.Preference
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel

View File

@ -123,7 +123,7 @@ class ExtensionsPresenter(
presenterScope.launchIO { findAvailableExtensions() }
preferences.extensionUpdatesCount().asFlow()
preferences.extensionUpdatesCount().changes()
.onEach { state.updates = it }
.launchIn(presenterScope)
}

View File

@ -42,11 +42,11 @@ class MigrationSourcesPresenter(
}
}
preferences.migrationSortingDirection().asFlow()
preferences.migrationSortingDirection().changes()
.onEach { state.sortingDirection = it }
.launchIn(presenterScope)
preferences.migrationSortingMode().asFlow()
preferences.migrationSortingMode().changes()
.onEach { state.sortingMode = it }
.launchIn(presenterScope)
}

View File

@ -111,7 +111,7 @@ open class BrowseSourcePresenter(
val isLandscape = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE
return produceState<GridCells>(initialValue = GridCells.Adaptive(128.dp), isLandscape) {
(if (isLandscape) preferences.landscapeColumns() else preferences.portraitColumns())
.asFlow()
.changes()
.collectLatest { columns ->
value = if (columns == 0) GridCells.Adaptive(128.dp) else GridCells.Fixed(columns)
}
@ -257,7 +257,7 @@ open class BrowseSourcePresenter(
fun addFavorite(manga: DomainManga) {
presenterScope.launch {
val categories = getCategories()
val defaultCategoryId = preferences.defaultCategory()
val defaultCategoryId = preferences.defaultCategory().get()
val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() }
when {

View File

@ -174,7 +174,7 @@ open class GlobalSearchController(
* @param searchResult result of search.
*/
fun setItems(searchResult: List<GlobalSearchItem>) {
if (searchResult.isEmpty() && preferences.searchPinnedSourcesOnly()) {
if (searchResult.isEmpty() && preferences.searchPinnedSourcesOnly().get()) {
binding.emptyView.show(R.string.no_pinned_sources)
} else {
binding.emptyView.hide()

View File

@ -123,7 +123,7 @@ open class GlobalSearchPresenter(
return filteredSources
}
val onlyPinnedSources = preferences.searchPinnedSourcesOnly()
val onlyPinnedSources = preferences.searchPinnedSourcesOnly().get()
val pinnedSourceIds = preferences.pinnedSources().get()
return enabledSources

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.ui.library.setting
import eu.kanade.domain.category.model.Category
import com.fredporciuncula.flow.preferences.Serializer as PreferencesSerializer
sealed class LibraryDisplayMode(
override val flag: Long,
@ -14,12 +13,12 @@ sealed class LibraryDisplayMode(
object List : LibraryDisplayMode(0b00000010)
object CoverOnlyGrid : LibraryDisplayMode(0b00000011)
object Serializer : PreferencesSerializer<LibraryDisplayMode> {
override fun deserialize(serialized: String): LibraryDisplayMode {
object Serializer {
fun deserialize(serialized: String): LibraryDisplayMode {
return LibraryDisplayMode.deserialize(serialized)
}
override fun serialize(value: LibraryDisplayMode): String {
fun serialize(value: LibraryDisplayMode): String {
return value.serialize()
}
}

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.ui.library.setting
import eu.kanade.domain.category.model.Category
import com.fredporciuncula.flow.preferences.Serializer as PreferencesSerializer
data class LibrarySort(
val type: Type,
@ -57,12 +56,12 @@ data class LibrarySort(
}
}
object Serializer : PreferencesSerializer<LibrarySort> {
override fun deserialize(serialized: String): LibrarySort {
object Serializer {
fun deserialize(serialized: String): LibrarySort {
return LibrarySort.deserialize(serialized)
}
override fun serialize(value: LibrarySort): String {
fun serialize(value: LibrarySort): String {
return value.serialize()
}
}

View File

@ -72,6 +72,8 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import logcat.LogPriority
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
class MainActivity : BaseActivity() {
@ -105,7 +107,15 @@ class MainActivity : BaseActivity() {
super.onCreate(savedInstanceState)
val didMigration = if (savedInstanceState == null) Migrations.upgrade(preferences) else false
val didMigration = if (savedInstanceState == null) {
Migrations.upgrade(
context = applicationContext,
preferences = preferences,
networkPreferences = Injekt.get(),
)
} else {
false
}
binding = MainActivityBinding.inflate(layoutInflater)
@ -240,7 +250,7 @@ class MainActivity : BaseActivity() {
}
}
merge(preferences.showUpdatesNavBadge().asFlow(), preferences.unreadUpdatesCount().asFlow())
merge(preferences.showUpdatesNavBadge().changes(), preferences.unreadUpdatesCount().changes())
.onEach { setUnreadUpdatesBadge() }
.launchIn(lifecycleScope)
@ -253,7 +263,7 @@ class MainActivity : BaseActivity() {
.launchIn(lifecycleScope)
binding.incognitoMode.isVisible = preferences.incognitoMode().get()
preferences.incognitoMode().asFlow()
preferences.incognitoMode().changes()
.drop(1)
.onEach {
binding.incognitoMode.isVisible = it
@ -490,7 +500,7 @@ class MainActivity : BaseActivity() {
lifecycleScope.launchUI { resetExitConfirmation() }
} else if (backstackSize == 1 || !router.handleBack()) {
// Regular back (i.e. closing the app)
if (preferences.autoClearChapterCache()) {
if (preferences.autoClearChapterCache().get()) {
chapterCache.clear()
}
super.onBackPressed()
@ -534,7 +544,7 @@ class MainActivity : BaseActivity() {
private fun shouldHandleExitConfirmation(): Boolean {
return router.backstackSize == 1 &&
router.getControllerWithTag("$startScreenId") != null &&
preferences.confirmExit() &&
preferences.confirmExit().get() &&
!isConfirmingExit
}

View File

@ -283,7 +283,7 @@ class MangaPresenter(
// Now check if user previously set categories, when available
val categories = getCategories()
val defaultCategoryId = preferences.defaultCategory().toLong()
val defaultCategoryId = preferences.defaultCategory().get().toLong()
val defaultCategory = categories.find { it.id == defaultCategoryId }
when {
// Default category set

View File

@ -194,7 +194,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
initializeMenu()
// Finish when incognito mode is disabled
preferences.incognitoMode().asFlow()
preferences.incognitoMode().changes()
.drop(1)
.onEach { if (!it) finish() }
.launchIn(lifecycleScope)
@ -446,7 +446,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
presenter.setMangaReadingMode(newReadingMode.flagValue)
menuToggleToast?.cancel()
if (!preferences.showReadingMode()) {
if (!preferences.showReadingMode().get()) {
menuToggleToast = toast(newReadingMode.stringRes)
}
@ -480,7 +480,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
updateCropBordersShortcut()
listOf(preferences.cropBorders(), preferences.cropBordersWebtoon())
.forEach { pref ->
pref.asFlow()
pref.changes()
.onEach { updateCropBordersShortcut() }
.launchIn(lifecycleScope)
}
@ -493,7 +493,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
popupMenu(
items = OrientationType.values().map { it.flagValue to it.stringRes },
selectedItemId = presenter.manga?.orientationType
?: preferences.defaultOrientationType(),
?: preferences.defaultOrientationType().get(),
) {
val newOrientation = OrientationType.fromPreference(itemId)
@ -635,7 +635,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
updateViewerInset(preferences.fullscreen().get())
binding.viewerContainer.addView(newViewer.getView())
if (preferences.showReadingMode()) {
if (preferences.showReadingMode().get()) {
showReadingModeToast(presenter.getMangaReadingMode())
}
@ -949,7 +949,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
* Initializes the reader subscriptions.
*/
init {
preferences.readerTheme().asFlow()
preferences.readerTheme().changes()
.onEach {
binding.readerContainer.setBackgroundResource(
when (preferences.readerTheme().get()) {
@ -962,41 +962,41 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
}
.launchIn(lifecycleScope)
preferences.showPageNumber().asFlow()
preferences.showPageNumber().changes()
.onEach { setPageNumberVisibility(it) }
.launchIn(lifecycleScope)
preferences.trueColor().asFlow()
preferences.trueColor().changes()
.onEach { setTrueColor(it) }
.launchIn(lifecycleScope)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
preferences.cutoutShort().asFlow()
preferences.cutoutShort().changes()
.onEach { setCutoutShort(it) }
.launchIn(lifecycleScope)
}
preferences.keepScreenOn().asFlow()
preferences.keepScreenOn().changes()
.onEach { setKeepScreenOn(it) }
.launchIn(lifecycleScope)
preferences.customBrightness().asFlow()
preferences.customBrightness().changes()
.onEach { setCustomBrightness(it) }
.launchIn(lifecycleScope)
preferences.colorFilter().asFlow()
preferences.colorFilter().changes()
.onEach { setColorFilter(it) }
.launchIn(lifecycleScope)
preferences.colorFilterMode().asFlow()
preferences.colorFilterMode().changes()
.onEach { setColorFilter(preferences.colorFilter().get()) }
.launchIn(lifecycleScope)
merge(preferences.grayscale().asFlow(), preferences.invertedColors().asFlow())
merge(preferences.grayscale().changes(), preferences.invertedColors().changes())
.onEach { setLayerPaint(preferences.grayscale().get(), preferences.invertedColors().get()) }
.launchIn(lifecycleScope)
preferences.fullscreen().asFlow()
preferences.fullscreen().changes()
.onEach {
WindowCompat.setDecorFitsSystemWindows(window, !it)
updateViewerInset(it)
@ -1060,7 +1060,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
*/
private fun setCustomBrightness(enabled: Boolean) {
if (enabled) {
preferences.customBrightnessValue().asFlow()
preferences.customBrightnessValue().changes()
.sample(100)
.onEach { setCustomBrightnessValue(it) }
.launchIn(lifecycleScope)
@ -1074,7 +1074,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
*/
private fun setColorFilter(enabled: Boolean) {
if (enabled) {
preferences.colorFilterValue().asFlow()
preferences.colorFilterValue().changes()
.sample(100)
.onEach { setColorFilterValue(it) }
.launchIn(lifecycleScope)

View File

@ -140,11 +140,11 @@ class ReaderPresenter(
?: error("Requested chapter of id $chapterId not found in chapter list")
val chaptersForReader = when {
(preferences.skipRead() || preferences.skipFiltered()) -> {
(preferences.skipRead().get() || preferences.skipFiltered().get()) -> {
val filteredChapters = chapters.filterNot {
when {
preferences.skipRead() && it.read -> true
preferences.skipFiltered() -> {
preferences.skipRead().get() && it.read -> true
preferences.skipFiltered().get() -> {
(manga.readFilter == DomainManga.CHAPTER_SHOW_READ.toInt() && !it.read) ||
(manga.readFilter == DomainManga.CHAPTER_SHOW_UNREAD.toInt() && it.read) ||
(manga.downloadedFilter == DomainManga.CHAPTER_SHOW_DOWNLOADED.toInt() && !downloadManager.isChapterDownloaded(it.name, it.scanlator, manga.title, manga.source)) ||
@ -502,7 +502,7 @@ class ReaderPresenter(
private fun deleteChapterIfNeeded(currentChapter: ReaderChapter) {
// Determine which chapter should be deleted and enqueue
val currentChapterPosition = chapterList.indexOf(currentChapter)
val removeAfterReadSlots = preferences.removeAfterReadSlots()
val removeAfterReadSlots = preferences.removeAfterReadSlots().get()
val chapterToDelete = chapterList.getOrNull(currentChapterPosition - removeAfterReadSlots)
if (removeAfterReadSlots != 0 && chapterDownload != null) {
@ -619,7 +619,7 @@ class ReaderPresenter(
* Returns the viewer position used by this manga or the default one.
*/
fun getMangaReadingMode(resolveDefault: Boolean = true): Int {
val default = preferences.defaultReadingMode()
val default = preferences.defaultReadingMode().get()
val readingMode = ReadingModeType.fromPreference(manga?.readingModeType)
return when {
resolveDefault && readingMode == ReadingModeType.DEFAULT -> default
@ -656,7 +656,7 @@ class ReaderPresenter(
* Returns the orientation type used by this manga or the default one.
*/
fun getMangaOrientationType(resolveDefault: Boolean = true): Int {
val default = preferences.defaultOrientationType()
val default = preferences.defaultOrientationType().get()
val orientation = OrientationType.fromPreference(manga?.orientationType)
return when {
resolveDefault && orientation == OrientationType.DEFAULT -> default
@ -714,7 +714,7 @@ class ReaderPresenter(
val filename = generateFilename(manga, page)
// Pictures directory.
val relativePath = if (preferences.folderPerManga()) DiskUtil.buildValidFilename(manga.title) else ""
val relativePath = if (preferences.folderPerManga().get()) DiskUtil.buildValidFilename(manga.title) else ""
// Copy file in background.
try {
@ -818,7 +818,7 @@ class ReaderPresenter(
* will run in a background thread and errors are ignored.
*/
private fun updateTrackChapterRead(readerChapter: ReaderChapter) {
if (!preferences.autoUpdateTrack()) return
if (!preferences.autoUpdateTrack().get()) return
val manga = manga ?: return
val chapterRead = readerChapter.chapter.chapter_number.toDouble()

View File

@ -32,15 +32,15 @@ class ReaderColorFilterSettings @JvmOverloads constructor(context: Context, attr
init {
addView(binding.root)
preferences.colorFilter().asFlow()
preferences.colorFilter().changes()
.onEach { setColorFilter(it) }
.launchIn((context as ReaderActivity).lifecycleScope)
preferences.colorFilterMode().asFlow()
preferences.colorFilterMode().changes()
.onEach { setColorFilter(preferences.colorFilter().get()) }
.launchIn(context.lifecycleScope)
preferences.customBrightness().asFlow()
preferences.customBrightness().changes()
.onEach { setCustomBrightness(it) }
.launchIn(context.lifecycleScope)
@ -139,7 +139,7 @@ class ReaderColorFilterSettings @JvmOverloads constructor(context: Context, attr
*/
private fun setCustomBrightness(enabled: Boolean) {
if (enabled) {
preferences.customBrightnessValue().asFlow()
preferences.customBrightnessValue().changes()
.sample(100)
.onEach { setCustomBrightnessValue(it) }
.launchIn((context as ReaderActivity).lifecycleScope)
@ -167,7 +167,7 @@ class ReaderColorFilterSettings @JvmOverloads constructor(context: Context, attr
*/
private fun setColorFilter(enabled: Boolean) {
if (enabled) {
preferences.colorFilterValue().asFlow()
preferences.colorFilterValue().changes()
.sample(100)
.onEach { setColorFilterValue(it) }
.launchIn((context as ReaderActivity).lifecycleScope)

View File

@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.ui.reader.viewer
import com.fredporciuncula.flow.preferences.Preference
import eu.kanade.tachiyomi.core.preference.Preference
import eu.kanade.tachiyomi.data.preference.PreferenceValues.TappingInvertMode
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import kotlinx.coroutines.CoroutineScope
@ -80,7 +80,7 @@ abstract class ViewerConfig(preferences: PreferencesHelper, private val scope: C
valueAssignment: (T) -> Unit,
onChanged: (T) -> Unit = {},
) {
asFlow()
changes()
.onEach { valueAssignment(it) }
.distinctUntilChanged()
.onEach { onChanged(it) }

View File

@ -78,7 +78,7 @@ class PagerConfig(
preferences.pagerNavInverted()
.register({ tappingInverted = it }, { navigator.invertMode = it })
preferences.pagerNavInverted().asFlow()
preferences.pagerNavInverted().changes()
.drop(1)
.onEach { navigationModeChangedListener?.invoke() }
.launchIn(scope)

View File

@ -51,7 +51,7 @@ class WebtoonConfig(
preferences.webtoonNavInverted()
.register({ tappingInverted = it }, { navigator.invertMode = it })
preferences.webtoonNavInverted().asFlow()
preferences.webtoonNavInverted().changes()
.drop(1)
.onEach { navigationModeChangedListener?.invoke() }
.launchIn(scope)
@ -71,7 +71,7 @@ class WebtoonConfig(
},
)
preferences.readerTheme().asFlow()
preferences.readerTheme().changes()
.drop(1)
.distinctUntilChanged()
.onEach { themeChangedListener?.invoke() }

View File

@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Target
import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.network.PREF_DOH_360
import eu.kanade.tachiyomi.network.PREF_DOH_ADGUARD
import eu.kanade.tachiyomi.network.PREF_DOH_ALIDNS
@ -69,6 +70,7 @@ class SettingsAdvancedController(
private val network: NetworkHelper by injectLazy()
private val chapterCache: ChapterCache by injectLazy()
private val trackManager: TrackManager by injectLazy()
private val networkPreferences: NetworkPreferences by injectLazy()
@SuppressLint("BatteryLife")
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
@ -96,7 +98,7 @@ class SettingsAdvancedController(
}
switchPreference {
key = Keys.verboseLogging
key = networkPreferences.verboseLogging().key()
titleRes = R.string.pref_verbose_logging
summaryRes = R.string.pref_verbose_logging_summary
defaultValue = isDevFlavor
@ -189,7 +191,7 @@ class SettingsAdvancedController(
onClick { clearWebViewData() }
}
intListPreference {
key = Keys.dohProvider
key = networkPreferences.dohProvider().key()
titleRes = R.string.pref_dns_over_https
entries = arrayOf(
context.getString(R.string.disabled),
@ -227,10 +229,11 @@ class SettingsAdvancedController(
true
}
}
val defaultUserAgent = networkPreferences.defaultUserAgent()
editTextPreference {
key = Keys.defaultUserAgent
key = defaultUserAgent.key()
titleRes = R.string.pref_user_agent_string
text = preferences.defaultUserAgent().get()
text = defaultUserAgent.get()
summary = network.defaultUserAgent
onChange {
@ -247,10 +250,10 @@ class SettingsAdvancedController(
key = "pref_reset_user_agent"
titleRes = R.string.pref_reset_user_agent_string
visibleIf(preferences.defaultUserAgent()) { it != preferences.defaultUserAgent().defaultValue }
visibleIf(defaultUserAgent) { it != defaultUserAgent.defaultValue() }
onClick {
preferences.defaultUserAgent().delete()
defaultUserAgent.delete()
activity?.toast(R.string.requires_app_restart)
}
}

View File

@ -136,7 +136,7 @@ class SettingsBackupController : SettingsController() {
}
}
preferences.backupsDirectory().asFlow()
preferences.backupsDirectory().changes()
.onEach { path ->
val dir = UniFile.fromUri(context, path.toUri())
summary = dir.filePath + "/automatic"

View File

@ -130,7 +130,7 @@ abstract class SettingsController : PreferenceController() {
(activity as? AppCompatActivity)?.supportActionBar?.title = getTitle()
}
inline fun <T> Preference.visibleIf(preference: com.fredporciuncula.flow.preferences.Preference<T>, crossinline block: (T) -> Boolean) {
inline fun <T> Preference.visibleIf(preference: eu.kanade.tachiyomi.core.preference.Preference<T>, crossinline block: (T) -> Boolean) {
preference.asHotFlow { isVisible = block(it) }
.launchIn(viewScope)
}

View File

@ -58,7 +58,7 @@ class SettingsDownloadController : SettingsController() {
ctrl.showDialog(router)
}
preferences.downloadsDirectory().asFlow()
preferences.downloadsDirectory().changes()
.onEach { path ->
val dir = UniFile.fromUri(context, path.toUri())
summary = dir.filePath ?: path
@ -114,7 +114,7 @@ class SettingsDownloadController : SettingsController() {
entries = categories.map { it.visualName(context) }.toTypedArray()
entryValues = categories.map { it.id.toString() }.toTypedArray()
preferences.removeExcludeCategories().asFlow()
preferences.removeExcludeCategories().changes()
.onEach { mutable ->
val selected = mutable
.mapNotNull { id -> categories.find { it.id == id.toLong() } }
@ -171,10 +171,10 @@ class SettingsDownloadController : SettingsController() {
}
}
preferences.downloadNewChapterCategories().asFlow()
preferences.downloadNewChapterCategories().changes()
.onEach { updateSummary() }
.launchIn(viewScope)
preferences.downloadNewChapterCategoriesExclude().asFlow()
preferences.downloadNewChapterCategoriesExclude().changes()
.onEach { updateSummary() }
.launchIn(viewScope)
}

View File

@ -79,7 +79,7 @@ class SettingsLibraryController : SettingsController() {
}
}
combine(preferences.portraitColumns().asFlow(), preferences.landscapeColumns().asFlow()) { portraitCols, landscapeCols -> Pair(portraitCols, landscapeCols) }
combine(preferences.portraitColumns().changes(), preferences.landscapeColumns().changes()) { portraitCols, landscapeCols -> Pair(portraitCols, landscapeCols) }
.onEach { (portraitCols, landscapeCols) ->
val portrait = getColumnValue(portraitCols)
val landscape = getColumnValue(landscapeCols)
@ -114,7 +114,7 @@ class SettingsLibraryController : SettingsController() {
entryValues = arrayOf("-1") + allCategories.map { it.id.toString() }.toTypedArray()
defaultValue = "-1"
val selectedCategory = allCategories.find { it.id == preferences.defaultCategory().toLong() }
val selectedCategory = allCategories.find { it.id == preferences.defaultCategory().get().toLong() }
summary = selectedCategory?.visualName(context)
?: context.getString(R.string.default_category_summary)
onChange { newValue ->
@ -129,7 +129,7 @@ class SettingsLibraryController : SettingsController() {
bindTo(preferences.categorizedDisplaySettings())
titleRes = R.string.categorized_display_settings
preferences.categorizedDisplaySettings().asFlow()
preferences.categorizedDisplaySettings().changes()
.onEach {
if (it.not()) {
resetCategoryFlags.await()
@ -197,7 +197,7 @@ class SettingsLibraryController : SettingsController() {
summary = context.getString(R.string.restrictions, restrictionsText)
}
preferences.libraryUpdateDeviceRestriction().asFlow()
preferences.libraryUpdateDeviceRestriction().changes()
.onEach { updateSummary() }
.launchIn(viewScope)
}
@ -226,7 +226,7 @@ class SettingsLibraryController : SettingsController() {
summary = restrictionsText
}
preferences.libraryUpdateMangaRestriction().asFlow()
preferences.libraryUpdateMangaRestriction().changes()
.onEach { updateSummary() }
.launchIn(viewScope)
}
@ -269,10 +269,10 @@ class SettingsLibraryController : SettingsController() {
}
}
preferences.libraryUpdateCategories().asFlow()
preferences.libraryUpdateCategories().changes()
.onEach { updateSummary() }
.launchIn(viewScope)
preferences.libraryUpdateCategoriesExclude().asFlow()
preferences.libraryUpdateCategoriesExclude().changes()
.onEach { updateSummary() }
.launchIn(viewScope)
}

View File

@ -27,12 +27,12 @@ object ChapterSettingsHelper {
suspend fun applySettingDefaults(mangaId: Long) {
setMangaChapterFlags.awaitSetAllFlags(
mangaId = mangaId,
unreadFilter = preferences.filterChapterByRead().toLong(),
downloadedFilter = preferences.filterChapterByDownloaded().toLong(),
bookmarkedFilter = preferences.filterChapterByBookmarked().toLong(),
sortingMode = preferences.sortChapterBySourceOrNumber().toLong(),
sortingDirection = preferences.sortChapterByAscendingOrDescending().toLong(),
displayMode = preferences.displayChapterByNameOrNumber().toLong(),
unreadFilter = preferences.filterChapterByRead().get().toLong(),
downloadedFilter = preferences.filterChapterByDownloaded().get().toLong(),
bookmarkedFilter = preferences.filterChapterByBookmarked().get().toLong(),
sortingMode = preferences.sortChapterBySourceOrNumber().get().toLong(),
sortingDirection = preferences.sortChapterByAscendingOrDescending().get().toLong(),
displayMode = preferences.displayChapterByNameOrNumber().get().toLong(),
)
}
@ -45,12 +45,12 @@ object ChapterSettingsHelper {
.map { manga ->
setMangaChapterFlags.awaitSetAllFlags(
mangaId = manga.id,
unreadFilter = preferences.filterChapterByRead().toLong(),
downloadedFilter = preferences.filterChapterByDownloaded().toLong(),
bookmarkedFilter = preferences.filterChapterByBookmarked().toLong(),
sortingMode = preferences.sortChapterBySourceOrNumber().toLong(),
sortingDirection = preferences.sortChapterByAscendingOrDescending().toLong(),
displayMode = preferences.displayChapterByNameOrNumber().toLong(),
unreadFilter = preferences.filterChapterByRead().get().toLong(),
downloadedFilter = preferences.filterChapterByDownloaded().get().toLong(),
bookmarkedFilter = preferences.filterChapterByBookmarked().get().toLong(),
sortingMode = preferences.sortChapterBySourceOrNumber().get().toLong(),
sortingDirection = preferences.sortChapterByAscendingOrDescending().get().toLong(),
displayMode = preferences.displayChapterByNameOrNumber().get().toLong(),
)
}
}

View File

@ -106,15 +106,14 @@ inline fun <P : Preference> PreferenceGroup.addThenInit(p: P, block: P.() -> Uni
}
}
inline fun <T> Preference.bindTo(preference: com.fredporciuncula.flow.preferences.Preference<T>) {
key = preference.key
defaultValue = preference.defaultValue
inline fun <T> Preference.bindTo(preference: eu.kanade.tachiyomi.core.preference.Preference<T>) {
key = preference.key()
defaultValue = preference.defaultValue()
}
inline fun <T> ListPreference.bindTo(preference: com.fredporciuncula.flow.preferences.Preference<T>) {
key = preference.key
// ListPreferences persist values as strings, even when we're using our IntListPreference
defaultValue = preference.defaultValue.toString()
inline fun <T> ListPreference.bindTo(preference: eu.kanade.tachiyomi.core.preference.Preference<T>) {
key = preference.key()
defaultValue = preference.defaultValue().toString()
}
inline fun Preference.onClick(crossinline block: () -> Unit) {

View File

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.util.preference
import android.widget.CompoundButton
import com.fredporciuncula.flow.preferences.Preference
import eu.kanade.tachiyomi.core.preference.Preference
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.onEach
@ -15,7 +15,7 @@ fun CompoundButton.bindToPreference(pref: Preference<Boolean>) {
fun <T> Preference<T>.asHotFlow(block: (T) -> Unit): Flow<T> {
block(get())
return asFlow()
return changes()
.onEach { block(it) }
}

View File

@ -14,8 +14,8 @@ import androidx.appcompat.widget.PopupMenu
import androidx.core.content.withStyledAttributes
import androidx.core.view.forEach
import androidx.core.view.get
import com.fredporciuncula.flow.preferences.Preference
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.core.preference.Preference
import eu.kanade.tachiyomi.databinding.PrefSpinnerBinding
import eu.kanade.tachiyomi.util.system.getResourceColor

View File

@ -31,13 +31,15 @@ class TachiyomiSearchView @JvmOverloads constructor(
override fun onAttachedToWindow() {
super.onAttachedToWindow()
scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
Injekt.get<PreferencesHelper>().incognitoMode().asHotFlow {
imeOptions = if (it) {
imeOptions or EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING
} else {
imeOptions and EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING.inv()
Injekt.get<PreferencesHelper>().incognitoMode()
.asHotFlow {
imeOptions = if (it) {
imeOptions or EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING
} else {
imeOptions and EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING.inv()
}
}
}.launchIn(scope!!)
.launchIn(scope!!)
}
override fun setOnQueryTextListener(listener: OnQueryTextListener?) {

View File

@ -49,13 +49,15 @@ class TachiyomiTextInputEditText @JvmOverloads constructor(
* if [PreferencesHelper.incognitoMode] is true. Some IMEs may not respect this flag.
*/
fun EditText.setIncognito(viewScope: CoroutineScope) {
Injekt.get<PreferencesHelper>().incognitoMode().asHotFlow {
imeOptions = if (it) {
imeOptions or EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING
} else {
imeOptions and EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING.inv()
Injekt.get<PreferencesHelper>().incognitoMode()
.asHotFlow {
imeOptions = if (it) {
imeOptions or EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING
} else {
imeOptions and EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING.inv()
}
}
}.launchIn(viewScope)
.launchIn(viewScope)
}
}
}

View File

@ -0,0 +1,178 @@
package eu.kanade.tachiyomi.core.preference
import android.content.SharedPreferences
import android.content.SharedPreferences.Editor
import androidx.core.content.edit
import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
sealed class AndroidPreference<T>(
private val preferences: SharedPreferences,
private val keyFlow: Flow<String?>,
private val key: String,
private val defaultValue: T,
) : Preference<T> {
abstract fun read(preferences: SharedPreferences, key: String, defaultValue: T): T
abstract fun write(key: String, value: T): Editor.() -> Unit
override fun key(): String {
return key
}
override fun get(): T {
return read(preferences, key, defaultValue)
}
override fun set(value: T) {
preferences.edit(action = write(key, value))
}
override fun isSet(): Boolean {
return preferences.contains(key)
}
override fun delete() {
preferences.edit {
remove(key)
}
}
override fun defaultValue(): T {
return defaultValue
}
override fun changes(): Flow<T> {
return keyFlow
.filter { it == key || it == null }
.onStart { emit("ignition") }
.map { get() }
.conflate()
}
override fun stateIn(scope: CoroutineScope): StateFlow<T> {
return changes().stateIn(scope, SharingStarted.Eagerly, get())
}
class StringPrimitive(
preferences: SharedPreferences,
keyFlow: Flow<String?>,
key: String,
defaultValue: String
) : AndroidPreference<String>(preferences, keyFlow, key, defaultValue) {
override fun read(preferences: SharedPreferences, key: String, defaultValue: String): String {
return preferences.getString(key, defaultValue) ?: defaultValue
}
override fun write(key: String, value: String): Editor.() -> Unit = {
putString(key, value)
}
}
class LongPrimitive(
preferences: SharedPreferences,
keyFlow: Flow<String?>,
key: String,
defaultValue: Long
) : AndroidPreference<Long>(preferences, keyFlow, key, defaultValue) {
override fun read(preferences: SharedPreferences, key: String, defaultValue: Long): Long {
return preferences.getLong(key, defaultValue)
}
override fun write(key: String, value: Long): Editor.() -> Unit = {
putLong(key, value)
}
}
class IntPrimitive(
preferences: SharedPreferences,
keyFlow: Flow<String?>,
key: String,
defaultValue: Int
) : AndroidPreference<Int>(preferences, keyFlow, key, defaultValue) {
override fun read(preferences: SharedPreferences, key: String, defaultValue: Int): Int {
return preferences.getInt(key, defaultValue)
}
override fun write(key: String, value: Int): Editor.() -> Unit = {
putInt(key, value)
}
}
class FloatPrimitive(
preferences: SharedPreferences,
keyFlow: Flow<String?>,
key: String,
defaultValue: Float
) : AndroidPreference<Float>(preferences, keyFlow, key, defaultValue) {
override fun read(preferences: SharedPreferences, key: String, defaultValue: Float): Float {
return preferences.getFloat(key, defaultValue)
}
override fun write(key: String, value: Float): Editor.() -> Unit = {
putFloat(key, value)
}
}
class BooleanPrimitive(
preferences: SharedPreferences,
keyFlow: Flow<String?>,
key: String,
defaultValue: Boolean
) : AndroidPreference<Boolean>(preferences, keyFlow, key, defaultValue) {
override fun read(preferences: SharedPreferences, key: String, defaultValue: Boolean): Boolean {
return preferences.getBoolean(key, defaultValue)
}
override fun write(key: String, value: Boolean): Editor.() -> Unit = {
putBoolean(key, value)
}
}
class StringSetPrimitive(
preferences: SharedPreferences,
keyFlow: Flow<String?>,
key: String,
defaultValue: Set<String>
) : AndroidPreference<Set<String>>(preferences, keyFlow, key, defaultValue) {
override fun read(preferences: SharedPreferences, key: String, defaultValue: Set<String>): Set<String> {
return preferences.getStringSet(key, defaultValue) ?: defaultValue
}
override fun write(key: String, value: Set<String>): Editor.() -> Unit = {
putStringSet(key, value)
}
}
class Object<T>(
preferences: SharedPreferences,
keyFlow: Flow<String?>,
key: String,
defaultValue: T,
val serializer: (T) -> String,
val deserializer: (String) -> T
) : AndroidPreference<T>(preferences, keyFlow, key, defaultValue) {
override fun read(preferences: SharedPreferences, key: String, defaultValue: T): T {
return try {
preferences.getString(key, null)?.let(deserializer) ?: defaultValue
} catch (e: Exception) {
defaultValue
}
}
override fun write(key: String, value: T): Editor.() -> Unit = {
putString(key, serializer(value))
}
}
}

View File

@ -0,0 +1,72 @@
package eu.kanade.tachiyomi.core.preference
import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import eu.kanade.tachiyomi.core.preference.AndroidPreference.BooleanPrimitive
import eu.kanade.tachiyomi.core.preference.AndroidPreference.FloatPrimitive
import eu.kanade.tachiyomi.core.preference.AndroidPreference.IntPrimitive
import eu.kanade.tachiyomi.core.preference.AndroidPreference.LongPrimitive
import eu.kanade.tachiyomi.core.preference.AndroidPreference.Object
import eu.kanade.tachiyomi.core.preference.AndroidPreference.StringPrimitive
import eu.kanade.tachiyomi.core.preference.AndroidPreference.StringSetPrimitive
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
class AndroidPreferenceStore(
context: Context
) : PreferenceStore {
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
private val keyFlow = sharedPreferences.keyFlow
override fun getString(key: String, defaultValue: String): Preference<String> {
return StringPrimitive(sharedPreferences, keyFlow, key, defaultValue)
}
override fun getLong(key: String, defaultValue: Long): Preference<Long> {
return LongPrimitive(sharedPreferences, keyFlow,key, defaultValue)
}
override fun getInt(key: String, defaultValue: Int): Preference<Int> {
return IntPrimitive(sharedPreferences, keyFlow,key, defaultValue)
}
override fun getFloat(key: String, defaultValue: Float): Preference<Float> {
return FloatPrimitive(sharedPreferences, keyFlow,key, defaultValue)
}
override fun getBoolean(key: String, defaultValue: Boolean): Preference<Boolean> {
return BooleanPrimitive(sharedPreferences, keyFlow, key, defaultValue)
}
override fun getStringSet(key: String, defaultValue: Set<String>): Preference<Set<String>> {
return StringSetPrimitive(sharedPreferences, keyFlow, key, defaultValue)
}
override fun <T> getObject(
key: String,
defaultValue: T,
serializer: (T) -> String,
deserializer: (String) -> T,
): Preference<T> {
return Object(
preferences = sharedPreferences,
keyFlow = keyFlow,
key = key,
defaultValue = defaultValue,
serializer = serializer,
deserializer = deserializer
)
}
}
private val SharedPreferences.keyFlow
get() = callbackFlow {
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key: String? -> trySend(key) }
registerOnSharedPreferenceChangeListener(listener)
awaitClose {
unregisterOnSharedPreferenceChangeListener(listener)
}
}

View File

@ -0,0 +1,27 @@
package eu.kanade.tachiyomi.core.preference
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
interface Preference<T> {
fun key(): String
fun get(): T
fun set(value: T)
fun isSet(): Boolean
fun delete()
fun defaultValue(): T
fun changes(): Flow<T>
fun stateIn(scope: CoroutineScope): StateFlow<T>
}
inline fun <reified T, R : T> Preference<T>.getAndSet(crossinline block: (T) -> R) = set(block(get()))

View File

@ -0,0 +1,42 @@
package eu.kanade.tachiyomi.core.preference
interface PreferenceStore {
fun getString(key: String, defaultValue: String = ""): Preference<String>
fun getLong(key: String, defaultValue: Long = 0): Preference<Long>
fun getInt(key: String, defaultValue: Int = 0): Preference<Int>
fun getFloat(key: String, defaultValue: Float = 0f): Preference<Float>
fun getBoolean(key: String, defaultValue: Boolean = false): Preference<Boolean>
fun getStringSet(key: String, defaultValue: Set<String> = emptySet()): Preference<Set<String>>
fun <T> getObject(
key: String,
defaultValue: T,
serializer: (T) -> String,
deserializer: (String) -> T
): Preference<T>
}
inline fun <reified T : Enum<T>> PreferenceStore.getEnum(
key: String,
defaultValue: T
) : Preference<T> {
return getObject(
key = key,
defaultValue = defaultValue,
serializer = { it.name },
deserializer = {
try {
enumValueOf(it)
} catch (e: IllegalArgumentException) {
defaultValue
}
}
)
}

View File

@ -8,13 +8,13 @@ import eu.kanade.tachiyomi.network.interceptor.UserAgentInterceptor
import okhttp3.Cache
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.util.concurrent.TimeUnit
class NetworkHelper(context: Context) {
// TODO: Abstract preferences similar to 1.x
private val preferences = PreferenceManager.getDefaultSharedPreferences(context)
private val preferences: NetworkPreferences by injectLazy()
private val cacheDir = File(context.cacheDir, "network_cache")
private val cacheSize = 5L * 1024 * 1024 // 5 MiB
@ -36,14 +36,14 @@ class NetworkHelper(context: Context) {
.addInterceptor(userAgentInterceptor)
.addNetworkInterceptor(http103Interceptor)
if (preferences.getBoolean("verbose_logging", false)) {
if (preferences.verboseLogging().get()) {
val httpLoggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.HEADERS
}
builder.addNetworkInterceptor(httpLoggingInterceptor)
}
when (preferences.getInt("doh_provider", -1)) {
when (preferences.dohProvider().get()) {
PREF_DOH_CLOUDFLARE -> builder.dohCloudflare()
PREF_DOH_GOOGLE -> builder.dohGoogle()
PREF_DOH_ADGUARD -> builder.dohAdGuard()
@ -70,6 +70,6 @@ class NetworkHelper(context: Context) {
}
val defaultUserAgent by lazy {
preferences.getString("default_user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0")!!
preferences.defaultUserAgent().get()
}
}

View File

@ -0,0 +1,23 @@
package eu.kanade.tachiyomi.network
import eu.kanade.tachiyomi.core.preference.Preference
import eu.kanade.tachiyomi.core.preference.PreferenceStore
class NetworkPreferences(
private val preferenceStore: PreferenceStore,
private val verboseLogging: Boolean = false
) {
fun verboseLogging(): Preference<Boolean> {
return preferenceStore.getBoolean("verbose_logging", verboseLogging)
}
fun dohProvider(): Preference<Int> {
return preferenceStore.getInt("doh_provider", -1)
}
fun defaultUserAgent(): Preference<String> {
return preferenceStore.getString("default_user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0")
}
}

View File

@ -37,7 +37,6 @@ sqlitektx = "androidx.sqlite:sqlite-ktx:2.3.0-alpha05"
sqlite-android = "com.github.requery:sqlite-android:3.36.0"
preferencektx = "androidx.preference:preference-ktx:1.2.0"
flowpreferences = "com.fredporciuncula:flow-preferences:1.8.0"
nucleus-core = { module = "info.android15.nucleus:nucleus", version.ref = "nucleus_version" }
nucleus-supportv7 = { module = "info.android15.nucleus:nucleus-support-v7", version.ref = "nucleus_version" }