Compare commits

..

30 Commits

Author SHA1 Message Date
Jobobby04 509ace1a38 Revert "Reapply "Fix thread starvation caused by not yielding or using an inappropriate thread pool (#2955)""
This reverts commit 9c01119d24.

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
2026-04-08 15:02:37 -04:00
Jobobby04 170358f88e Revert "Reapply "Fix cache invalidation isn't done at startup (#2970)""
This reverts commit c17e9573b7.
2026-04-08 14:52:15 -04:00
Jobobby04 5c14879c12 Fix build 2026-04-06 16:25:25 -04:00
ArthurKun 1682962703 Add gradle.properties to build-logic synced with root gradle.properties (#3159)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
2026-04-06 16:22:14 -04:00
Mend Renovate a1daf53188 Update dependency com.gradleup.tapmoc:tapmoc-gradle-plugin to v0.4.1 (#3173) 2026-04-06 16:03:22 -04:00
Mend Renovate b0bdee8c72 Update dependency io.kotest:kotest-assertions-core to v6.1.10 (#3172) 2026-04-06 16:03:16 -04:00
MajorTanya 72c091d317 Add informative error for unapproved MAL titles (#3155)
MAL has a concept of "titles waiting for approval". These titles
cannot be added to user lists, but they do show up on the website and
crucially, in search results.

However, trying to add such an "unapproved" title will return a 400
error response with the error "invalid_content".

Previously, the awaitSuccess() call would mean the generic "HTTP 400"
toast would be shown. Now, a dedicated informative error message is
shown instead.
# Conflicts:
#	CHANGELOG.md
2026-04-06 16:02:51 -04:00
Mend Renovate 1cc796a62e Update dependency androidx.compose:compose-bom-beta to v2026.03.01 (#3147) 2026-04-06 16:02:30 -04:00
Mend Renovate cd40c0fb32 Update dependency androidx.work:work-runtime to v2.11.2 (#3146) 2026-04-06 16:02:15 -04:00
Christos 88edd18d02 Use plural forms for update error notification (#3090)
* Use plural forms for update error notification

Replace the hardcoded 'X update(s) failed' string with Android
plural resources so the notification correctly shows '1 update failed'
vs '2 updates failed', enabling proper localization.

Fixes #3051

* Address review: rename plural key and remove comment

Rename plural key to notification_update_errors to avoid potential
conflicts with existing translated string keys. Remove leftover
comment from strings.xml.

* Revert key rename per review
2026-04-06 16:02:10 -04:00
Mend Renovate ba9d010f78 Update dependency io.kotest:kotest-assertions-core to v6.1.9 (#3137) 2026-04-06 16:02:02 -04:00
Mend Renovate 1ed375968e Update dependency io.kotest:kotest-assertions-core to v6.1.8 (#3132) 2026-04-06 16:01:55 -04:00
MajorTanya 9e525515ea Address Gradle warning about Task.project (#3118)
Apparently, this is all that's needed to replace the forbidden
`Task.project` accessor, which would be an error in Gradle v10 (it's
been deprecated for a while)

This will also allow us to use the Configuration Cache if we wanted
to.
2026-04-06 16:01:47 -04:00
AntsyLich bd7201cfb9 Fix app crash on startup on some Android TV
Why do you guys even exist

# Conflicts:
#	CHANGELOG.md
2026-04-06 16:01:40 -04:00
AntsyLich d9ca2b69e8 Fix occasional crash when mass installing/uninstalling extension using PackageManager
# Conflicts:
#	CHANGELOG.md
2026-04-06 16:01:20 -04:00
AntsyLich 925fb118af Potentially fix 'database is locked' crash
# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt
2026-04-06 15:59:05 -04:00
AntsyLich 4552221020 Fix wrong exception being caught after 8c480c6355 migration
`android.database.sqlite.SQLiteException` instead of `android.database.SQLException`

# Conflicts:
#	CHANGELOG.md
2026-04-06 15:54:09 -04:00
MajorTanya 24b66b7030 Address CancellableContinuation.resume deprecation (#3115)
According to the source docs, this has been a warning since
kotlinx.coroutines 1.9.0.
2026-04-06 15:52:45 -04:00
Mend Renovate 58decd076c Update Gradle to v9 (#2333) 2026-04-06 15:50:18 -04:00
AntsyLich a7d93ae751 Cleanup and rework build logic (#3113)
# Conflicts:
#	app/build.gradle.kts
#	app/shortcuts.xml
#	app/src/main/res/xml/shortcuts.xml
#	app/src/main/shortcuts.xml
#	build.gradle.kts
#	buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt
#	core/archive/build.gradle.kts
#	core/common/build.gradle.kts
#	gradle/build-logic/src/main/kotlin/mihon/gradle/BuildConfig.kt
#	settings.gradle.kts
#	source-api/build.gradle.kts
#	source-local/build.gradle.kts
#	telemetry/build.gradle.kts
2026-04-06 15:50:09 -04:00
Weblate (bot) d225b7c586 Translations update from Hosted Weblate (#3003)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/be/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/fi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/be/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ko/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/lv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ta/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

Co-authored-by: A <ogloppi@mailbox.org>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Kehribar <103407696+dpentx@users.noreply.github.com>
Co-authored-by: Lucas Correia <anicetoclucas@gmail.com>
Co-authored-by: ScratchBuild <foobarbuzz@gmail.com>
Co-authored-by: Siebrenvde <siebren@siebrenvde.dev>
Co-authored-by: lilp <felipegabriel.avila6@gmail.com>
Co-authored-by: nadevko <ormak@protonmail.com>
Co-authored-by: தமிழ்நேரம் <tamilneram247@gmail.com>
Co-authored-by: ℂ𝕠𝕠𝕠𝕝 (𝕘𝕚𝕥𝕙𝕦𝕓.𝕔𝕠𝕞/ℂ𝕠𝕠𝕠𝕝) <coool@mail.lv>
Co-authored-by: 안세훈 <on9686@gmail.com>
# Conflicts:
#	i18n/src/commonMain/moko-resources/be/strings.xml
2026-04-06 15:27:10 -04:00
AntsyLich 4c51d01236 Merge and cleanup version catalogs (#3103)
# Conflicts:
#	app/build.gradle.kts
#	core/archive/build.gradle.kts
#	gradle/libs.versions.toml
#	i18n/build.gradle.kts
#	settings.gradle.kts
#	source-api/build.gradle.kts
2026-04-06 15:26:05 -04:00
Mend Renovate 4d46e84f54 Update dependency com.google.firebase:firebase-bom to v34.11.0 (#3094) 2026-04-06 15:06:15 -04:00
AntsyLich af51607053 Replace preference getter functions with properties (#3091)
# Conflicts:
#	app/src/main/java/eu/kanade/domain/source/interactor/GetEnabledSources.kt
#	app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt
#	app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt
#	app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
#	app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt
#	app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt
#	app/src/main/java/eu/kanade/tachiyomi/App.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt
#	app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt
#	app/src/main/java/eu/kanade/tachiyomi/source/SourceExtensions.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt
#	app/src/main/java/mihon/feature/migration/list/MigrationListScreenModel.kt
#	core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt
#	domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt
2026-04-06 15:06:05 -04:00
Leodyver Semilla 1c8e6dcd6f Fix extension actions disappearing after installing and uninstalling in same session (#3049)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
# Conflicts:
#	CHANGELOG.md
2026-04-06 14:20:49 -04:00
AntsyLich 7a398dabba Make retry in reader redownload image (#3089)
# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt
2026-04-06 14:18:57 -04:00
Leodyver Semilla b7fcf7ccda Fix WebView JavaScript dialogs popup after screen is closed (#3041)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
# Conflicts:
#	CHANGELOG.md
2026-04-06 14:13:39 -04:00
Leodyver Semilla acbda604cc MangaUpdates API content-type heade (#3021)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
# Conflicts:
#	CHANGELOG.md
2026-04-06 14:13:18 -04:00
Leodyver Semilla 97f3dd3b25 Fix tracker-induced duplicate key crash in duplicate detection (#3040)
# Conflicts:
#	CHANGELOG.md
2026-04-06 14:12:59 -04:00
Mend Renovate 60758f89ed Update dependency com.diffplug.spotless:spotless-plugin-gradle to v8.4.0 (#3086) 2026-04-06 14:12:41 -04:00
317 changed files with 3539 additions and 2712 deletions
+102 -84
View File
@@ -1,16 +1,19 @@
@file:Suppress("ChromeOsAbiSupport")
import mihon.buildlogic.getBuildTime
import mihon.buildlogic.getCommitCount
import mihon.buildlogic.getGitSha
import mihon.gradle.getBuildTime
import mihon.gradle.getLatestCommitCount
import mihon.gradle.getLatestCommitSha
import mihon.gradle.tasks.ReplaceShortcutsPlaceholderTask
plugins {
id("mihon.android.application")
id("mihon.android.application.compose")
alias(mihonx.plugins.android.application)
alias(mihonx.plugins.compose)
alias(mihonx.plugins.spotless)
kotlin("plugin.parcelize")
kotlin("plugin.serialization")
// id("com.github.zellius.shortcut-helper")
alias(libs.plugins.aboutLibraries)
alias(libs.plugins.kotlin.serialization)
id("com.github.ben-manes.versions")
}
@@ -21,10 +24,6 @@ if (gradle.startParameter.taskRequests.toString().contains("Standard")) {
}
}
// shortcutHelper.setFilePath("./shortcuts.xml")
val supportedAbis = setOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
android {
namespace = "eu.kanade.tachiyomi"
@@ -34,29 +33,17 @@ android {
versionCode = 77
versionName = "1.12.0"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLastCommitTime = false)}\"")
buildConfigField("String", "COMMIT_COUNT", "\"${getLatestCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getLatestCommitSha()}\"")
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLatestCommitTime = false)}\"")
buildConfigField("boolean", "INCLUDE_UPDATER", "false")
ndk {
abiFilters += supportedAbis
}
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
splits {
abi {
isEnable = true
reset()
include(*supportedAbis.toTypedArray())
isUniversalApk = true
}
}
buildTypes {
named("debug") {
versionNameSuffix = "-${getCommitCount()}"
versionNameSuffix = "-${getLatestCommitCount()}"
applicationIdSuffix = ".debug"
isPseudoLocalesEnabled = true
}
@@ -72,7 +59,7 @@ android {
isShrinkResources = true
setProguardFiles(listOf(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"))
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLastCommitTime = true)}\"")
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLatestCommitTime = true)}\"")
}
create("benchmark") {
initWith(getByName("release"))
@@ -90,6 +77,15 @@ android {
getByName("benchmark").res.srcDirs("src/debug/res")
}
splits {
abi {
isEnable = true
isUniversalApk = true
reset()
include("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
}
}
flavorDimensions.add("default")
productFlavors {
@@ -106,20 +102,31 @@ android {
}
packaging {
resources.excludes.addAll(
listOf(
jniLibs {
keepDebugSymbols += listOf(
"libandroidx.graphics.path",
"libarchive-jni",
"libconscrypt_jni",
"libimagedecoder",
"libquickjs",
"libsqlite3x",
)
.map { "**/$it.so" }
}
resources {
excludes += setOf(
"kotlin-tooling-metadata.json",
"META-INF/DEPENDENCIES",
"LICENSE.txt",
"META-INF/LICENSE",
"META-INF/**/*.properties",
"META-INF/**/LICENSE.txt",
"META-INF/*.properties",
"META-INF/**/*.properties",
"META-INF/README.md",
"META-INF/NOTICE",
"META-INF/*.version",
),
)
"META-INF/DEPENDENCIES",
"META-INF/LICENSE",
"META-INF/NOTICE",
"META-INF/README.md",
)
}
}
dependenciesInfo {
@@ -177,94 +184,92 @@ dependencies {
implementation(projects.presentationWidget)
// Compose
implementation(compose.activity)
implementation(compose.foundation)
implementation(compose.material3.core)
implementation(compose.material.icons)
implementation(compose.animation)
implementation(compose.animation.graphics)
debugImplementation(compose.ui.tooling)
implementation(compose.ui.tooling.preview)
implementation(compose.ui.util)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.compose.foundation)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.materialIcons)
implementation(libs.androidx.compose.animation)
implementation(libs.androidx.compose.animationGraphics)
debugImplementation(libs.androidx.compose.uiTooling)
implementation(libs.androidx.compose.uiToolingPreview)
implementation(libs.androidx.compose.uiUtil)
implementation(androidx.interpolator)
implementation(libs.androidx.interpolator)
implementation(androidx.paging.runtime)
implementation(androidx.paging.compose)
implementation(libs.androidx.paging.runtime)
implementation(libs.androidx.paging.compose)
implementation(androidx.sqlite.bundled)
implementation(libs.androidx.sqlite.bundled)
// SY -->
implementation(sylibs.sqlcipher)
// SY <--
implementation(kotlinx.reflect)
implementation(kotlinx.immutables)
implementation(libs.kotlin.reflect)
implementation(libs.kotlinx.collections.immutable)
implementation(platform(kotlinx.coroutines.bom))
implementation(kotlinx.bundles.coroutines)
implementation(libs.bundles.kotlinx.coroutines)
// AndroidX libraries
implementation(androidx.annotation)
implementation(androidx.appcompat)
implementation(androidx.biometricktx)
implementation(androidx.constraintlayout)
implementation(androidx.corektx)
implementation(androidx.splashscreen)
implementation(androidx.recyclerview)
implementation(androidx.viewpager)
implementation(androidx.profileinstaller)
implementation(libs.androidx.annotation)
implementation(libs.androidx.appCompat)
implementation(libs.androidx.biometric)
implementation(libs.androidx.constraintLayout)
implementation(libs.androidx.core)
implementation(libs.androidx.coreSplashScreen)
implementation(libs.androidx.recyclerView)
implementation(libs.androidx.viewPager)
implementation(libs.androidx.profileInstaller)
implementation(androidx.bundles.lifecycle)
implementation(libs.bundles.androidx.lifecycle)
// Job scheduling
implementation(androidx.workmanager)
implementation(libs.androidx.work)
// RxJava
implementation(libs.rxjava)
implementation(libs.rxJava)
// Networking
implementation(libs.bundles.okhttp)
implementation(libs.okio)
implementation(libs.conscrypt.android) // TLS 1.3 support for Android < 10
implementation(libs.conscrypt) // TLS 1.3 support for Android < 10
// Data serialization (JSON, protobuf, xml)
implementation(kotlinx.bundles.serialization)
implementation(libs.bundles.serialization)
// HTML parser
implementation(libs.jsoup)
// Disk
implementation(libs.disklrucache)
implementation(libs.diskLruCache)
implementation(libs.unifile)
// Preferences
implementation(libs.preferencektx)
implementation(libs.androidx.preference)
// Dependency injection
implementation(libs.injekt)
// Image loading
implementation(platform(libs.coil.bom))
implementation(libs.bundles.coil)
implementation(libs.subsamplingscaleimageview) {
implementation(libs.subsamplingScaleImageView) {
exclude(module = "image-decoder")
}
implementation(libs.image.decoder)
// UI libraries
implementation(libs.material)
implementation(libs.flexible.adapter.core)
implementation(libs.photoview)
implementation(libs.directionalviewpager) {
implementation(libs.flexibleAdapter)
implementation(libs.photoView)
implementation(libs.directionalViewPager) {
exclude(group = "androidx.viewpager", module = "viewpager")
}
implementation(libs.richeditor.compose)
implementation(libs.composeRichEditor)
implementation(libs.aboutLibraries.compose)
implementation(libs.bundles.voyager)
implementation(libs.compose.materialmotion)
implementation(libs.composeMaterialMotion)
implementation(libs.swipe)
implementation(libs.compose.webview)
implementation(libs.compose.grid)
implementation(libs.composeWebview)
implementation(libs.composeGrid)
implementation(libs.reorderable)
implementation(libs.bundles.markdown)
implementation(libs.materialKolor)
@@ -288,10 +293,10 @@ dependencies {
testRuntimeOnly(libs.junit.platform.launcher)
// For detecting memory leaks; see https://square.github.io/leakcanary/
// debugImplementation(libs.leakcanary.android)
implementation(libs.leakcanary.plumber)
// debugImplementation(libs.leakCanary.android)
implementation(libs.leakCanary.plumber)
testImplementation(kotlinx.coroutines.test)
testImplementation(libs.kotlinx.coroutines.test)
// SY -->
// Firebase (EH)
@@ -319,6 +324,19 @@ dependencies {
}
androidComponents {
onVariants { variant ->
val resSource = variant.sources.res ?: return@onVariants
val variantName = variant.name.replaceFirstChar { it.uppercase() }
val replaceShortcutsPlaceholderTask = tasks.register<ReplaceShortcutsPlaceholderTask>(
"replace${variantName}ShortcutPlaceholder",
) {
applicationId.set(variant.applicationId)
shortcutsFile.set(projectDir.resolve("src/main/shortcuts.xml"))
}
resSource.addGeneratedSourceDirectory(replaceShortcutsPlaceholderTask) { it.outputDir }
}
onVariants(selector().withFlavor("default" to "standard")) {
// Only excluding in standard flavor because this breaks
// Layout Inspector's Compose tree
@@ -328,6 +346,6 @@ androidComponents {
buildscript {
dependencies {
classpath(kotlinx.gradle)
classpath(libs.kotlin.gradle)
}
}
@@ -9,19 +9,22 @@ import tachiyomi.i18n.MR
class BasePreferences(
val context: Context,
private val preferenceStore: PreferenceStore,
preferenceStore: PreferenceStore,
) {
fun downloadedOnly() = preferenceStore.getBoolean(
val downloadedOnly: Preference<Boolean> = preferenceStore.getBoolean(
Preference.appStateKey("pref_downloaded_only"),
false,
)
fun incognitoMode() = preferenceStore.getBoolean(Preference.appStateKey("incognito_mode"), false)
val incognitoMode: Preference<Boolean> = preferenceStore.getBoolean(Preference.appStateKey("incognito_mode"), false)
fun extensionInstaller() = ExtensionInstallerPreference(context, preferenceStore)
val extensionInstaller: ExtensionInstallerPreference = ExtensionInstallerPreference(context, preferenceStore)
fun shownOnboardingFlow() = preferenceStore.getBoolean(Preference.appStateKey("onboarding_complete"), false)
val shownOnboardingFlow: Preference<Boolean> = preferenceStore.getBoolean(
Preference.appStateKey("onboarding_complete"),
false,
)
enum class ExtensionInstaller(val titleRes: StringResource, val requiresSystemPermission: Boolean) {
LEGACY(MR.strings.ext_installer_legacy, true),
@@ -30,11 +33,17 @@ class BasePreferences(
PRIVATE(MR.strings.ext_installer_private, false),
}
fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "")
val displayProfile: Preference<String> = preferenceStore.getString("pref_display_profile_key", "")
fun hardwareBitmapThreshold() = preferenceStore.getInt("pref_hardware_bitmap_threshold", GLUtil.SAFE_TEXTURE_LIMIT)
val hardwareBitmapThreshold: Preference<Int> = preferenceStore.getInt(
"pref_hardware_bitmap_threshold",
GLUtil.SAFE_TEXTURE_LIMIT,
)
fun alwaysDecodeLongStripWithSSIV() = preferenceStore.getBoolean("pref_always_decode_long_strip_with_ssiv", false)
val alwaysDecodeLongStripWithSSIV: Preference<Boolean> = preferenceStore.getBoolean(
"pref_always_decode_long_strip_with_ssiv",
false,
)
fun installationId() = preferenceStore.getString(Preference.appStateKey("installation_id"), "")
val installationId: Preference<String> = preferenceStore.getString(Preference.appStateKey("installation_id"), "")
}
@@ -51,7 +51,7 @@ class SetReadStatus(
return@withNonCancellableContext Result.InternalError(e)
}
if (read && downloadPreferences.removeAfterMarkedAsRead().get()) {
if (read && downloadPreferences.removeAfterMarkedAsRead.get()) {
chaptersToUpdate
.groupBy { it.mangaId }
.forEach { (mangaId, chapters) ->
@@ -176,7 +176,7 @@ class SyncChaptersWithSource(
val deletedChapterNumberDateFetchMap = removedChapters.sortedByDescending { it.dateFetch }
.associate { it.chapterNumber to it.dateFetch }
val markDuplicateAsRead = libraryPreferences.markDuplicateReadChapterAsRead().get()
val markDuplicateAsRead = libraryPreferences.markDuplicateReadChapterAsRead.get()
.contains(LibraryPreferences.MARK_DUPLICATE_CHAPTER_READ_NEW)
// Date fetch is set in such a way that the upper ones will have bigger value than the lower ones
@@ -12,7 +12,7 @@ class GetExtensionLanguages(
) {
fun subscribe(): Flow<List<String>> {
return combine(
preferences.enabledLanguages().changes(),
preferences.enabledLanguages.changes(),
extensionManager.availableExtensionsFlow,
) { enabledLanguage, availableExtensions ->
availableExtensions
@@ -15,7 +15,7 @@ class GetExtensionSources(
val isMultiLangSingleSource =
isMultiSource && extension.sources.map { it.name }.distinct().size == 1
return preferences.disabledSources().changes().map { disabledSources ->
return preferences.disabledSources.changes().map { disabledSources ->
fun Source.isEnabled() = id.toString() !in disabledSources
extension.sources
@@ -13,10 +13,10 @@ class GetExtensionsByType(
) {
fun subscribe(): Flow<Extensions> {
val showNsfwSources = preferences.showNsfwSource().get()
val showNsfwSources = preferences.showNsfwSource.get()
return combine(
preferences.enabledLanguages().changes(),
preferences.enabledLanguages.changes(),
extensionManager.installedExtensionsFlow,
extensionManager.untrustedExtensionsFlow,
extensionManager.availableExtensionsFlow,
@@ -14,11 +14,11 @@ class TrustExtension(
suspend fun isTrusted(pkgInfo: PackageInfo, fingerprints: List<String>): Boolean {
val trustedFingerprints = extensionRepoRepository.getAll().map { it.signingKeyFingerprint }.toHashSet()
val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:${fingerprints.last()}"
return trustedFingerprints.any { fingerprints.contains(it) } || key in preferences.trustedExtensions().get()
return trustedFingerprints.any { fingerprints.contains(it) } || key in preferences.trustedExtensions.get()
}
fun trust(pkgName: String, versionCode: Long, signatureHash: String) {
preferences.trustedExtensions().getAndSet { exts ->
preferences.trustedExtensions.getAndSet { exts ->
// Remove previously trusted versions
val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet()
@@ -27,6 +27,6 @@ class TrustExtension(
}
fun revokeAll() {
preferences.trustedExtensions().delete()
preferences.trustedExtensions.delete()
}
}
@@ -15,9 +15,9 @@ class CreateSortTag(
return Result.TagExists
}
val size = preferences.sortTagsForLibrary().get().size
val size = preferences.sortTagsForLibrary.get().size
preferences.sortTagsForLibrary() += encodeTag(size, tag)
preferences.sortTagsForLibrary += encodeTag(size, tag)
return Result.Success
}
@@ -8,7 +8,7 @@ class DeleteSortTag(
) {
fun await(tag: String) {
preferences.sortTagsForLibrary().set(
preferences.sortTagsForLibrary.set(
(getSortTag.await() - tag).mapIndexed { index, s ->
CreateSortTag.encodeTag(index, s)
}.toSet(),
@@ -7,14 +7,14 @@ import tachiyomi.domain.library.service.LibraryPreferences
class GetSortTag(private val preferences: LibraryPreferences) {
fun subscribe(): Flow<List<String>> {
return preferences.sortTagsForLibrary().changes()
return preferences.sortTagsForLibrary.changes()
.map(::mapSortTags)
}
fun await() = getSortTags(preferences).let(::mapSortTags)
companion object {
fun getSortTags(preferences: LibraryPreferences) = preferences.sortTagsForLibrary().get()
fun getSortTags(preferences: LibraryPreferences) = preferences.sortTagsForLibrary.get()
fun mapSortTags(tags: Set<String>) = tags.mapNotNull {
val index = it.indexOf('|')
@@ -23,7 +23,7 @@ class ReorderSortTag(
val reorderedTag = reorderedTags.removeAt(currentIndex)
reorderedTags.add(newPosition, reorderedTag)
preferences.sortTagsForLibrary().set(
preferences.sortTagsForLibrary.set(
reorderedTags.mapIndexed { index, s ->
CreateSortTag.encodeTag(index, s)
}.toSet(),
@@ -44,7 +44,7 @@ class UpdateManga(
// if the manga isn't a favorite (or 'update titles' preference is enabled), set its title from source and update in db
val title =
if (remoteTitle.isNotEmpty() && (!localManga.favorite || libraryPreferences.updateMangaTitles().get())) {
if (remoteTitle.isNotEmpty() && (!localManga.favorite || libraryPreferences.updateMangaTitles.get())) {
remoteTitle
} else {
null
@@ -23,7 +23,7 @@ val Manga.readerOrientation: Long
val Manga.downloadedFilter: TriState
get() {
if (Injekt.get<BasePreferences>().downloadedOnly().get()) return TriState.ENABLED_IS
if (Injekt.get<BasePreferences>().downloadedOnly.get()) return TriState.ENABLED_IS
return when (downloadedFilterRaw) {
Manga.CHAPTER_SHOW_DOWNLOADED -> TriState.ENABLED_IS
Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT
@@ -11,7 +11,7 @@ class CreateSourceCategory(private val preferences: SourcePreferences) {
}
// Create category.
preferences.sourcesTabCategories() += category
preferences.sourcesTabCategories += category
return Result.Success
}
@@ -7,9 +7,9 @@ import tachiyomi.core.common.preference.minusAssign
class DeleteSourceCategory(private val preferences: SourcePreferences) {
fun await(category: String) {
preferences.sourcesTabSourcesInCategories().getAndSet { sourcesInCategories ->
preferences.sourcesTabSourcesInCategories.getAndSet { sourcesInCategories ->
sourcesInCategories.filterNot { it.substringAfter("|") == category }.toSet()
}
preferences.sourcesTabCategories() -= category
preferences.sourcesTabCategories -= category
}
}
@@ -18,17 +18,17 @@ class GetEnabledSources(
fun subscribe(): Flow<List<Source>> {
return combine(
preferences.pinnedSources().changes(),
preferences.pinnedSources.changes(),
combine(
preferences.enabledLanguages().changes(),
preferences.disabledSources().changes(),
preferences.lastUsedSource().changes(),
preferences.enabledLanguages.changes(),
preferences.disabledSources.changes(),
preferences.lastUsedSource.changes(),
) { a, b, c -> Triple(a, b, c) },
// SY -->
combine(
preferences.dataSaverExcludedSources().changes(),
preferences.sourcesTabSourcesInCategories().changes(),
preferences.sourcesTabCategoriesFilter().changes(),
preferences.dataSaverExcludedSources.changes(),
preferences.sourcesTabSourcesInCategories.changes(),
preferences.sourcesTabCategoriesFilter.changes(),
) { a, b, c -> Triple(a, b, c) },
// SY <--
repository.getSources(),
@@ -13,19 +13,19 @@ class GetIncognitoState(
private val extensionManager: ExtensionManager,
) {
fun await(sourceId: Long?): Boolean {
if (basePreferences.incognitoMode().get()) return true
if (basePreferences.incognitoMode.get()) return true
if (sourceId == null) return false
val extensionPackage = extensionManager.getExtensionPackage(sourceId) ?: return false
return extensionPackage in sourcePreferences.incognitoExtensions().get()
return extensionPackage in sourcePreferences.incognitoExtensions.get()
}
fun subscribe(sourceId: Long?): Flow<Boolean> {
if (sourceId == null) return basePreferences.incognitoMode().changes()
if (sourceId == null) return basePreferences.incognitoMode.changes()
return combine(
basePreferences.incognitoMode().changes(),
sourcePreferences.incognitoExtensions().changes(),
basePreferences.incognitoMode.changes(),
sourcePreferences.incognitoExtensions.changes(),
extensionManager.getExtensionPackageAsFlow(sourceId),
) { incognito, incognitoExtensions, extensionPackage ->
incognito || (extensionPackage in incognitoExtensions)
@@ -16,8 +16,8 @@ class GetLanguagesWithSources(
fun subscribe(): Flow<SortedMap<String, List<Source>>> {
return combine(
preferences.enabledLanguages().changes(),
preferences.disabledSources().changes(),
preferences.enabledLanguages.changes(),
preferences.disabledSources.changes(),
repository.getOnlineSources(),
) { enabledLanguage, disabledSource, onlineSources ->
val sortedSources = onlineSources.filterNot { it.id in BlacklistedSources.HIDDEN_SOURCES }.sortedWith(
@@ -9,7 +9,7 @@ class GetShowLatest(
) {
fun subscribe(hasSmartSearchConfig: Boolean): Flow<Boolean> {
return preferences.useNewSourceNavigation().changes()
return preferences.useNewSourceNavigation.changes()
.map {
!hasSmartSearchConfig && !it
}
@@ -9,6 +9,6 @@ class GetSourceCategories(
) {
fun subscribe(): Flow<List<String>> {
return preferences.sourcesTabCategories().changes().map { it.sortedWith(String.CASE_INSENSITIVE_ORDER) }
return preferences.sourcesTabCategories.changes().map { it.sortedWith(String.CASE_INSENSITIVE_ORDER) }
}
}
@@ -16,8 +16,8 @@ class GetSourcesWithFavoriteCount(
fun subscribe(): Flow<List<Pair<Source, Long>>> {
return combine(
preferences.migrationSortingDirection().changes(),
preferences.migrationSortingMode().changes(),
preferences.migrationSortingDirection.changes(),
preferences.migrationSortingMode.changes(),
repository.getSourcesWithFavoriteCount(),
) { direction, mode, list ->
list
@@ -14,7 +14,7 @@ class RenameSourceCategory(
CreateSourceCategory.Result.Success -> {}
}
preferences.sourcesTabSourcesInCategories().getAndSet { sourcesInCategories ->
preferences.sourcesTabSourcesInCategories.getAndSet { sourcesInCategories ->
sourcesInCategories.map {
val index = it.indexOf('|')
if (index != -1 && it.substring(index + 1) == categoryOld) {
@@ -24,7 +24,7 @@ class RenameSourceCategory(
}
}.toSet()
}
preferences.sourcesTabCategories().getAndSet {
preferences.sourcesTabCategories.getAndSet {
it.minus(categoryOld).plus(categoryNew)
}
@@ -7,8 +7,8 @@ class SetMigrateSorting(
) {
fun await(mode: Mode, direction: Direction) {
preferences.migrationSortingMode().set(mode)
preferences.migrationSortingDirection().set(direction)
preferences.migrationSortingMode.set(mode)
preferences.migrationSortingDirection.set(direction)
}
enum class Mode {
@@ -10,7 +10,7 @@ class SetSourceCategories(
fun await(source: Source, sourceCategories: List<String>) {
val sourceIdString = source.id.toString()
preferences.sourcesTabSourcesInCategories().getAndSet { sourcesInCategories ->
preferences.sourcesTabSourcesInCategories.getAndSet { sourcesInCategories ->
val currentSourceCategories = sourcesInCategories.filterNot {
it.substringBefore('|') == sourceIdString
}
@@ -9,7 +9,7 @@ class ToggleExcludeFromDataSaver(
) {
fun await(source: Source) {
preferences.dataSaverExcludedSources().getAndSet {
preferences.dataSaverExcludedSources.getAndSet {
if (source.id.toString() in it) {
it - source.id.toString()
} else {
@@ -7,7 +7,7 @@ class ToggleIncognito(
private val preferences: SourcePreferences,
) {
fun await(extensions: String, enable: Boolean) {
preferences.incognitoExtensions().getAndSet {
preferences.incognitoExtensions.getAndSet {
if (enable) it.plus(extensions) else it.minus(extensions)
}
}
@@ -8,8 +8,8 @@ class ToggleLanguage(
) {
fun await(language: String) {
val isEnabled = language in preferences.enabledLanguages().get()
preferences.enabledLanguages().getAndSet { enabled ->
val isEnabled = language in preferences.enabledLanguages.get()
preferences.enabledLanguages.getAndSet { enabled ->
if (isEnabled) enabled.minus(language) else enabled.plus(language)
}
}
@@ -13,19 +13,19 @@ class ToggleSource(
}
fun await(sourceId: Long, enable: Boolean = isEnabled(sourceId)) {
preferences.disabledSources().getAndSet { disabled ->
preferences.disabledSources.getAndSet { disabled ->
if (enable) disabled.minus("$sourceId") else disabled.plus("$sourceId")
}
}
fun await(sourceIds: List<Long>, enable: Boolean) {
val transformedSourceIds = sourceIds.map { it.toString() }
preferences.disabledSources().getAndSet { disabled ->
preferences.disabledSources.getAndSet { disabled ->
if (enable) disabled.minus(transformedSourceIds) else disabled.plus(transformedSourceIds)
}
}
private fun isEnabled(sourceId: Long): Boolean {
return sourceId.toString() in preferences.disabledSources().get()
return sourceId.toString() in preferences.disabledSources.get()
}
}
@@ -9,8 +9,8 @@ class ToggleSourcePin(
) {
fun await(source: Source) {
val isPinned = source.id.toString() in preferences.pinnedSources().get()
preferences.pinnedSources().getAndSet { pinned ->
val isPinned = source.id.toString() in preferences.pinnedSources.get()
preferences.pinnedSources.getAndSet { pinned ->
if (isPinned) pinned.minus("${source.id}") else pinned.plus("${source.id}")
}
}
@@ -10,80 +10,86 @@ import tachiyomi.core.common.preference.getLongArray
import tachiyomi.domain.library.model.LibraryDisplayMode
class SourcePreferences(
private val preferenceStore: PreferenceStore,
preferenceStore: PreferenceStore,
) {
fun sourceDisplayMode() = preferenceStore.getObjectFromString(
val sourceDisplayMode: Preference<LibraryDisplayMode> = preferenceStore.getObjectFromString(
"pref_display_mode_catalogue",
LibraryDisplayMode.default,
LibraryDisplayMode.Serializer::serialize,
LibraryDisplayMode.Serializer::deserialize,
)
fun enabledLanguages() = preferenceStore.getStringSet("source_languages", LocaleHelper.getDefaultEnabledLanguages())
val enabledLanguages: Preference<Set<String>> = preferenceStore.getStringSet(
"source_languages",
LocaleHelper.getDefaultEnabledLanguages(),
)
fun disabledSources() = preferenceStore.getStringSet("hidden_catalogues", emptySet())
val disabledSources: Preference<Set<String>> = preferenceStore.getStringSet("hidden_catalogues", emptySet())
fun incognitoExtensions() = preferenceStore.getStringSet("incognito_extensions", emptySet())
val incognitoExtensions: Preference<Set<String>> = preferenceStore.getStringSet("incognito_extensions", emptySet())
fun pinnedSources() = preferenceStore.getStringSet("pinned_catalogues", emptySet())
val pinnedSources: Preference<Set<String>> = preferenceStore.getStringSet("pinned_catalogues", emptySet())
fun lastUsedSource() = preferenceStore.getLong(
val lastUsedSource: Preference<Long> = preferenceStore.getLong(
Preference.appStateKey("last_catalogue_source"),
-1,
)
fun showNsfwSource() = preferenceStore.getBoolean("show_nsfw_source", true)
val showNsfwSource: Preference<Boolean> = preferenceStore.getBoolean("show_nsfw_source", true)
fun migrationSortingMode() = preferenceStore.getEnum("pref_migration_sorting", SetMigrateSorting.Mode.ALPHABETICAL)
val migrationSortingMode: Preference<SetMigrateSorting.Mode> = preferenceStore.getEnum(
"pref_migration_sorting",
SetMigrateSorting.Mode.ALPHABETICAL,
)
fun migrationSortingDirection() = preferenceStore.getEnum(
val migrationSortingDirection: Preference<SetMigrateSorting.Direction> = preferenceStore.getEnum(
"pref_migration_direction",
SetMigrateSorting.Direction.ASCENDING,
)
fun hideInLibraryItems() = preferenceStore.getBoolean("browse_hide_in_library_items", false)
val hideInLibraryItems: Preference<Boolean> = preferenceStore.getBoolean("browse_hide_in_library_items", false)
fun extensionRepos() = preferenceStore.getStringSet("extension_repos", emptySet())
val extensionRepos: Preference<Set<String>> = preferenceStore.getStringSet("extension_repos", emptySet())
fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0)
val extensionUpdatesCount: Preference<Int> = preferenceStore.getInt("ext_updates_count", 0)
fun trustedExtensions() = preferenceStore.getStringSet(
val trustedExtensions: Preference<Set<String>> = preferenceStore.getStringSet(
Preference.appStateKey("trusted_extensions"),
emptySet(),
)
fun globalSearchFilterState() = preferenceStore.getBoolean(
val globalSearchFilterState: Preference<Boolean> = preferenceStore.getBoolean(
Preference.appStateKey("has_filters_toggle_state"),
false,
)
// SY -->
fun enableSourceBlacklist() = preferenceStore.getBoolean("eh_enable_source_blacklist", true)
val enableSourceBlacklist: Preference<Boolean> = preferenceStore.getBoolean("eh_enable_source_blacklist", true)
fun sourcesTabCategories() = preferenceStore.getStringSet("sources_tab_categories", mutableSetOf())
val sourcesTabCategories: Preference<Set<String>> = preferenceStore.getStringSet("sources_tab_categories", mutableSetOf())
fun sourcesTabCategoriesFilter() = preferenceStore.getBoolean("sources_tab_categories_filter", false)
val sourcesTabCategoriesFilter: Preference<Boolean> = preferenceStore.getBoolean("sources_tab_categories_filter", false)
fun sourcesTabSourcesInCategories() = preferenceStore.getStringSet("sources_tab_source_categories", mutableSetOf())
val sourcesTabSourcesInCategories: Preference<Set<String>> = preferenceStore.getStringSet("sources_tab_source_categories", mutableSetOf())
fun dataSaver() = preferenceStore.getEnum("data_saver", DataSaver.NONE)
val dataSaver: Preference<DataSaver> = preferenceStore.getEnum("data_saver", DataSaver.NONE)
fun dataSaverIgnoreJpeg() = preferenceStore.getBoolean("ignore_jpeg", false)
val dataSaverIgnoreJpeg: Preference<Boolean> = preferenceStore.getBoolean("ignore_jpeg", false)
fun dataSaverIgnoreGif() = preferenceStore.getBoolean("ignore_gif", true)
val dataSaverIgnoreGif: Preference<Boolean> = preferenceStore.getBoolean("ignore_gif", true)
fun dataSaverImageQuality() = preferenceStore.getInt("data_saver_image_quality", 80)
val dataSaverImageQuality: Preference<Int> = preferenceStore.getInt("data_saver_image_quality", 80)
fun dataSaverImageFormatJpeg() = preferenceStore.getBoolean("data_saver_image_format_jpeg", false)
val dataSaverImageFormatJpeg: Preference<Boolean> = preferenceStore.getBoolean("data_saver_image_format_jpeg", false)
fun dataSaverServer() = preferenceStore.getString("data_saver_server", "")
val dataSaverServer: Preference<String> = preferenceStore.getString("data_saver_server", "")
fun dataSaverColorBW() = preferenceStore.getBoolean("data_saver_color_bw", false)
val dataSaverColorBW: Preference<Boolean> = preferenceStore.getBoolean("data_saver_color_bw", false)
fun dataSaverExcludedSources() = preferenceStore.getStringSet("data_saver_excluded", emptySet())
val dataSaverExcludedSources: Preference<Set<String>> = preferenceStore.getStringSet("data_saver_excluded", emptySet())
fun dataSaverDownloader() = preferenceStore.getBoolean("data_saver_downloader", true)
val dataSaverDownloader: Preference<Boolean> = preferenceStore.getBoolean("data_saver_downloader", true)
enum class DataSaver {
NONE,
@@ -91,32 +97,38 @@ class SourcePreferences(
WSRV_NL,
}
fun allowLocalSourceHiddenFolders() = preferenceStore.getBoolean("allow_local_source_hidden_folders", false)
val allowLocalSourceHiddenFolders: Preference<Boolean> = preferenceStore.getBoolean("allow_local_source_hidden_folders", false)
fun preferredMangaDexId() = preferenceStore.getString("preferred_mangaDex_id", "0")
val preferredMangaDexId: Preference<String> = preferenceStore.getString("preferred_mangaDex_id", "0")
fun mangadexSyncToLibraryIndexes() = preferenceStore.getStringSet(
val mangadexSyncToLibraryIndexes: Preference<Set<String>> = preferenceStore.getStringSet(
"pref_mangadex_sync_to_library_indexes",
emptySet(),
)
fun recommendationSearchFlags() = preferenceStore.getInt("rec_search_flags", Int.MAX_VALUE)
val recommendationSearchFlags: Preference<Int> = preferenceStore.getInt("rec_search_flags", Int.MAX_VALUE)
// SY <--
fun migrationSources() = preferenceStore.getLongArray("migration_sources", emptyList())
val migrationSources: Preference<List<Long>> = preferenceStore.getLongArray("migration_sources", emptyList())
fun migrationFlags() = preferenceStore.getObjectFromInt(
val migrationFlags: Preference<Set<MigrationFlag>> = preferenceStore.getObjectFromInt(
key = "migration_flags",
defaultValue = MigrationFlag.entries.toSet(),
serializer = { MigrationFlag.toBit(it) },
deserializer = { value: Int -> MigrationFlag.fromBit(value) },
)
fun migrationDeepSearchMode() = preferenceStore.getBoolean("migration_deep_search", false)
val migrationDeepSearchMode: Preference<Boolean> = preferenceStore.getBoolean("migration_deep_search", false)
fun migrationPrioritizeByChapters() = preferenceStore.getBoolean("migration_prioritize_by_chapters", false)
val migrationPrioritizeByChapters: Preference<Boolean> = preferenceStore.getBoolean(
"migration_prioritize_by_chapters",
false,
)
fun migrationHideUnmatched() = preferenceStore.getBoolean("migration_hide_unmatched", false)
val migrationHideUnmatched: Preference<Boolean> = preferenceStore.getBoolean("migration_hide_unmatched", false)
fun migrationHideWithoutUpdates() = preferenceStore.getBoolean("migration_hide_without_updates", false)
val migrationHideWithoutUpdates: Preference<Boolean> = preferenceStore.getBoolean(
"migration_hide_without_updates",
false,
)
}
@@ -9,21 +9,21 @@ import java.util.UUID
class SyncPreferences(
private val preferenceStore: PreferenceStore,
) {
fun clientHost() = preferenceStore.getString("sync_client_host", "https://sync.tachiyomi.org")
fun clientAPIKey() = preferenceStore.getString("sync_client_api_key", "")
fun lastSyncTimestamp() = preferenceStore.getLong(Preference.appStateKey("last_sync_timestamp"), 0L)
val clientHost: Preference<String> = preferenceStore.getString("sync_client_host", "https://sync.tachiyomi.org")
val clientAPIKey: Preference<String> = preferenceStore.getString("sync_client_api_key", "")
val lastSyncTimestamp: Preference<Long> = preferenceStore.getLong(Preference.appStateKey("last_sync_timestamp"), 0L)
fun lastSyncEtag() = preferenceStore.getString("sync_etag", "")
val lastSyncEtag: Preference<String> = preferenceStore.getString("sync_etag", "")
fun syncInterval() = preferenceStore.getInt("sync_interval", 0)
fun syncService() = preferenceStore.getInt("sync_service", 0)
val syncInterval: Preference<Int> = preferenceStore.getInt("sync_interval", 0)
val syncService: Preference<Int> = preferenceStore.getInt("sync_service", 0)
fun googleDriveAccessToken() = preferenceStore.getString(
val googleDriveAccessToken: Preference<String> = preferenceStore.getString(
Preference.appStateKey("google_drive_access_token"),
"",
)
fun googleDriveRefreshToken() = preferenceStore.getString(
val googleDriveRefreshToken: Preference<String> = preferenceStore.getString(
Preference.appStateKey("google_drive_refresh_token"),
"",
)
@@ -42,7 +42,7 @@ class SyncPreferences(
}
fun isSyncEnabled(): Boolean {
return syncService().get() != 0
return syncService.get() != 0
}
fun getSyncSettings(): SyncSettings {
@@ -34,17 +34,17 @@ class TrackPreferences(
fun trackToken(tracker: Tracker) = preferenceStore.getString(Preference.privateKey("track_token_${tracker.id}"), "")
fun anilistScoreType() = preferenceStore.getString("anilist_score_type", Anilist.POINT_10)
val anilistScoreType: Preference<String> = preferenceStore.getString("anilist_score_type", Anilist.POINT_10)
fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true)
val autoUpdateTrack: Preference<Boolean> = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true)
fun autoUpdateTrackOnMarkRead() = preferenceStore.getEnum(
val autoUpdateTrackOnMarkRead: Preference<AutoTrackState> = preferenceStore.getEnum(
"pref_auto_update_manga_on_mark_read",
AutoTrackState.ALWAYS,
)
// SY -->
fun resolveUsingSourceMetadata() = preferenceStore.getBoolean(
val resolveUsingSourceMetadata: Preference<Boolean> = preferenceStore.getBoolean(
"pref_resolve_using_source_metadata_key",
true,
)
@@ -6,6 +6,7 @@ import eu.kanade.domain.ui.model.TabletUiMode
import eu.kanade.domain.ui.model.ThemeMode
import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.preference.getEnum
import java.time.format.DateTimeFormatter
@@ -13,10 +14,10 @@ import java.time.format.FormatStyle
import java.util.Locale
class UiPreferences(
private val preferenceStore: PreferenceStore,
preferenceStore: PreferenceStore,
) {
fun themeMode() = preferenceStore.getEnum(
val themeMode = preferenceStore.getEnum(
"pref_theme_mode_key",
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ThemeMode.SYSTEM
@@ -25,7 +26,7 @@ class UiPreferences(
},
)
fun appTheme() = preferenceStore.getEnum(
val appTheme: Preference<AppTheme> = preferenceStore.getEnum(
"pref_app_theme",
if (DeviceUtil.isDynamicColorAvailable) {
AppTheme.MONET
@@ -34,37 +35,37 @@ class UiPreferences(
},
)
fun themeDarkAmoled() = preferenceStore.getBoolean("pref_theme_dark_amoled_key", false)
val themeDarkAmoled: Preference<Boolean> = preferenceStore.getBoolean("pref_theme_dark_amoled_key", false)
fun relativeTime() = preferenceStore.getBoolean("relative_time_v2", true)
val relativeTime: Preference<Boolean> = preferenceStore.getBoolean("relative_time_v2", true)
fun dateFormat() = preferenceStore.getString("app_date_format", "")
val dateFormat: Preference<String> = preferenceStore.getString("app_date_format", "")
fun tabletUiMode() = preferenceStore.getEnum("tablet_ui_mode", TabletUiMode.AUTOMATIC)
val tabletUiMode: Preference<TabletUiMode> = preferenceStore.getEnum("tablet_ui_mode", TabletUiMode.AUTOMATIC)
fun imagesInDescription() = preferenceStore.getBoolean("pref_render_images_description", true)
val imagesInDescription: Preference<Boolean> = preferenceStore.getBoolean("pref_render_images_description", true)
// SY -->
fun expandFilters() = preferenceStore.getBoolean("eh_expand_filters", false)
val expandFilters: Preference<Boolean> = preferenceStore.getBoolean("eh_expand_filters", false)
fun hideFeedTab() = preferenceStore.getBoolean("hide_latest_tab", false)
val hideFeedTab: Preference<Boolean> = preferenceStore.getBoolean("hide_latest_tab", false)
fun feedTabInFront() = preferenceStore.getBoolean("latest_tab_position", false)
val feedTabInFront: Preference<Boolean> = preferenceStore.getBoolean("latest_tab_position", false)
fun recommendsInOverflow() = preferenceStore.getBoolean("recommends_in_overflow", false)
val recommendsInOverflow: Preference<Boolean> = preferenceStore.getBoolean("recommends_in_overflow", false)
fun mergeInOverflow() = preferenceStore.getBoolean("merge_in_overflow", true)
val mergeInOverflow: Preference<Boolean> = preferenceStore.getBoolean("merge_in_overflow", true)
fun previewsRowCount() = preferenceStore.getInt("pref_previews_row_count", 4)
val previewsRowCount: Preference<Int> = preferenceStore.getInt("pref_previews_row_count", 4)
fun useNewSourceNavigation() = preferenceStore.getBoolean("use_new_source_navigation", true)
val useNewSourceNavigation: Preference<Boolean> = preferenceStore.getBoolean("use_new_source_navigation", true)
fun bottomBarLabels() = preferenceStore.getBoolean("pref_show_bottom_bar_labels", true)
val bottomBarLabels: Preference<Boolean> = preferenceStore.getBoolean("pref_show_bottom_bar_labels", true)
fun showNavUpdates() = preferenceStore.getBoolean("pref_show_updates_button", true)
val showNavUpdates: Preference<Boolean> = preferenceStore.getBoolean("pref_show_updates_button", true)
fun showNavHistory() = preferenceStore.getBoolean("pref_show_history_button", true)
val showNavHistory: Preference<Boolean> = preferenceStore.getBoolean("pref_show_history_button", true)
// SY <--
@@ -33,8 +33,8 @@ fun relativeDateText(
val context = LocalContext.current
val preferences = remember { Injekt.get<UiPreferences>() }
val relativeTime = remember { preferences.relativeTime().get() }
val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
val relativeTime = remember { preferences.relativeTime.get() }
val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat.get()) }
return localDate?.toRelativeString(
context = context,
@@ -100,9 +100,9 @@ fun LibrarySettingsDialog(
private fun ColumnScope.FilterPage(
screenModel: LibrarySettingsScreenModel,
) {
val filterDownloaded by screenModel.libraryPreferences.filterDownloaded().collectAsState()
val downloadedOnly by screenModel.preferences.downloadedOnly().collectAsState()
val autoUpdateMangaRestrictions by screenModel.libraryPreferences.autoUpdateMangaRestrictions().collectAsState()
val filterDownloaded by screenModel.libraryPreferences.filterDownloaded.collectAsState()
val downloadedOnly by screenModel.preferences.downloadedOnly.collectAsState()
val autoUpdateMangaRestrictions by screenModel.libraryPreferences.autoUpdateMangaRestrictions.collectAsState()
TriStateItem(
label = stringResource(MR.strings.label_downloaded),
@@ -114,25 +114,25 @@ private fun ColumnScope.FilterPage(
enabled = !downloadedOnly,
onClick = { screenModel.toggleFilter(LibraryPreferences::filterDownloaded) },
)
val filterUnread by screenModel.libraryPreferences.filterUnread().collectAsState()
val filterUnread by screenModel.libraryPreferences.filterUnread.collectAsState()
TriStateItem(
label = stringResource(MR.strings.action_filter_unread),
state = filterUnread,
onClick = { screenModel.toggleFilter(LibraryPreferences::filterUnread) },
)
val filterStarted by screenModel.libraryPreferences.filterStarted().collectAsState()
val filterStarted by screenModel.libraryPreferences.filterStarted.collectAsState()
TriStateItem(
label = stringResource(MR.strings.label_started),
state = filterStarted,
onClick = { screenModel.toggleFilter(LibraryPreferences::filterStarted) },
)
val filterBookmarked by screenModel.libraryPreferences.filterBookmarked().collectAsState()
val filterBookmarked by screenModel.libraryPreferences.filterBookmarked.collectAsState()
TriStateItem(
label = stringResource(MR.strings.action_filter_bookmarked),
state = filterBookmarked,
onClick = { screenModel.toggleFilter(LibraryPreferences::filterBookmarked) },
)
val filterCompleted by screenModel.libraryPreferences.filterCompleted().collectAsState()
val filterCompleted by screenModel.libraryPreferences.filterCompleted.collectAsState()
TriStateItem(
label = stringResource(MR.strings.completed),
state = filterCompleted,
@@ -143,7 +143,7 @@ private fun ColumnScope.FilterPage(
(isDevFlavor || isPreviewBuildType) &&
LibraryPreferences.MANGA_OUTSIDE_RELEASE_PERIOD in autoUpdateMangaRestrictions
) {
val filterIntervalCustom by screenModel.libraryPreferences.filterIntervalCustom().collectAsState()
val filterIntervalCustom by screenModel.libraryPreferences.filterIntervalCustom.collectAsState()
TriStateItem(
label = stringResource(MR.strings.action_filter_interval_custom),
state = filterIntervalCustom,
@@ -151,7 +151,7 @@ private fun ColumnScope.FilterPage(
)
}
// SY -->
val filterLewd by screenModel.libraryPreferences.filterLewd().collectAsState()
val filterLewd by screenModel.libraryPreferences.filterLewd.collectAsState()
TriStateItem(
label = stringResource(SYMR.strings.lewd),
state = filterLewd,
@@ -194,7 +194,7 @@ private fun ColumnScope.SortPage(
) {
val trackers by screenModel.trackersFlow.collectAsState()
// SY -->
val globalSortMode by screenModel.libraryPreferences.sortingMode().collectAsState()
val globalSortMode by screenModel.libraryPreferences.sortingMode.collectAsState()
val sortingMode = if (screenModel.grouping == LibraryGroup.BY_DEFAULT) {
category.sort.type
} else {
@@ -206,9 +206,9 @@ private fun ColumnScope.SortPage(
!globalSortMode.isAscending
}
val hasSortTags by remember {
screenModel.libraryPreferences.sortTagsForLibrary().changes()
screenModel.libraryPreferences.sortTagsForLibrary.changes()
.map { it.isNotEmpty() }
}.collectAsState(initial = screenModel.libraryPreferences.sortTagsForLibrary().get().isNotEmpty())
}.collectAsState(initial = screenModel.libraryPreferences.sortTagsForLibrary.get().isNotEmpty())
// SY <--
val options = remember(trackers.isEmpty()/* SY --> */, hasSortTags/* SY <-- */) {
@@ -287,7 +287,7 @@ private val displayModes = listOf(
private fun ColumnScope.DisplayPage(
screenModel: LibrarySettingsScreenModel,
) {
val displayMode by screenModel.libraryPreferences.displayMode().collectAsState()
val displayMode by screenModel.libraryPreferences.displayMode.collectAsState()
SettingsChipRow(MR.strings.action_display_mode) {
displayModes.map { (titleRes, mode) ->
FilterChip(
@@ -302,9 +302,9 @@ private fun ColumnScope.DisplayPage(
val configuration = LocalConfiguration.current
val columnPreference = remember {
if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
screenModel.libraryPreferences.landscapeColumns()
screenModel.libraryPreferences.landscapeColumns
} else {
screenModel.libraryPreferences.portraitColumns()
screenModel.libraryPreferences.portraitColumns
}
}
@@ -326,33 +326,33 @@ private fun ColumnScope.DisplayPage(
HeadingItem(MR.strings.overlay_header)
CheckboxItem(
label = stringResource(MR.strings.action_display_download_badge),
pref = screenModel.libraryPreferences.downloadBadge(),
pref = screenModel.libraryPreferences.downloadBadge,
)
CheckboxItem(
label = stringResource(MR.strings.action_display_unread_badge),
pref = screenModel.libraryPreferences.unreadBadge(),
pref = screenModel.libraryPreferences.unreadBadge,
)
CheckboxItem(
label = stringResource(MR.strings.action_display_local_badge),
pref = screenModel.libraryPreferences.localBadge(),
pref = screenModel.libraryPreferences.localBadge,
)
CheckboxItem(
label = stringResource(MR.strings.action_display_language_badge),
pref = screenModel.libraryPreferences.languageBadge(),
pref = screenModel.libraryPreferences.languageBadge,
)
CheckboxItem(
label = stringResource(MR.strings.action_display_show_continue_reading_button),
pref = screenModel.libraryPreferences.showContinueReadingButton(),
pref = screenModel.libraryPreferences.showContinueReadingButton,
)
HeadingItem(MR.strings.tabs_header)
CheckboxItem(
label = stringResource(MR.strings.action_display_show_tabs),
pref = screenModel.libraryPreferences.categoryTabs(),
pref = screenModel.libraryPreferences.categoryTabs,
)
CheckboxItem(
label = stringResource(MR.strings.action_display_show_number_of_items),
pref = screenModel.libraryPreferences.categoryNumberOfItems(),
pref = screenModel.libraryPreferences.categoryNumberOfItems,
)
}
@@ -66,7 +66,7 @@ fun ChapterSettingsDialog(
)
}
val downloadedOnly = remember { Injekt.get<BasePreferences>().downloadedOnly().get() }
val downloadedOnly = remember { Injekt.get<BasePreferences>().downloadedOnly.get() }
TabbedDialog(
onDismissRequest = onDismissRequest,
@@ -656,7 +656,7 @@ private fun MangaSummary(
modifier: Modifier = Modifier,
) {
val preferences = remember { Injekt.get<UiPreferences>() }
val loadImages = remember { preferences.imagesInDescription().get() }
val loadImages = remember { preferences.imagesInDescription.get() }
val animProgress by animateFloatAsState(
targetValue = if (expanded) 1f else 0f,
label = "summary",
@@ -122,7 +122,7 @@ internal class PermissionStep : OnboardingStep {
color = MaterialTheme.colorScheme.onPrimaryContainer,
)
val crashlyticsPref = privacyPreferences.crashlytics()
val crashlyticsPref = privacyPreferences.crashlytics
val crashlytics by crashlyticsPref.collectAsState()
PermissionSwitch(
title = stringResource(MR.strings.onboarding_permission_crashlytics),
@@ -131,7 +131,7 @@ internal class PermissionStep : OnboardingStep {
onToggleChange = crashlyticsPref::set,
)
val analyticsPref = privacyPreferences.analytics()
val analyticsPref = privacyPreferences.analytics
val analytics by analyticsPref.collectAsState()
PermissionSwitch(
title = stringResource(MR.strings.onboarding_permission_analytics),
@@ -30,7 +30,7 @@ import uy.kohesive.injekt.api.get
internal class StorageStep : OnboardingStep {
private val storagePref = Injekt.get<StoragePreferences>().baseStorageDirectory()
private val storagePref = Injekt.get<StoragePreferences>().baseStorageDirectory
private var _isComplete by mutableStateOf(false)
@@ -19,13 +19,13 @@ internal class ThemeStep : OnboardingStep {
@Composable
override fun Content() {
val themeModePref = uiPreferences.themeMode()
val themeModePref = uiPreferences.themeMode
val themeMode by themeModePref.collectAsState()
val appThemePref = uiPreferences.appTheme()
val appThemePref = uiPreferences.appTheme
val appTheme by appThemePref.collectAsState()
val amoledPref = uiPreferences.themeDarkAmoled()
val amoledPref = uiPreferences.themeDarkAmoled
val amoled by amoledPref.collectAsState()
Column {
@@ -38,7 +38,7 @@ fun ConfigureExhDialog(run: Boolean, onRunning: () -> Unit) {
LaunchedEffect(run) {
if (run) {
if (exhPreferences.exhShowSettingsUploadWarning().get()) {
if (exhPreferences.exhShowSettingsUploadWarning.get()) {
warnDialogOpen = true
} else {
configureDialogOpen = true
@@ -57,7 +57,7 @@ fun ConfigureExhDialog(run: Boolean, onRunning: () -> Unit) {
confirmButton = {
TextButton(
onClick = {
exhPreferences.exhShowSettingsUploadWarning().set(false)
exhPreferences.exhShowSettingsUploadWarning.set(false)
configureDialogOpen = true
warnDialogOpen = false
},
@@ -131,7 +131,7 @@ object SettingsAdvancedScreen : SearchableSettings {
},
),
/* SY --> Preference.PreferenceItem.SwitchPreference(
preference = networkPreferences.verboseLogging(),
preference = networkPreferences.verboseLogging,
title = stringResource(MR.strings.pref_verbose_logging),
subtitle = stringResource(MR.strings.pref_verbose_logging_summary),
onValueChanged = {
@@ -223,7 +223,6 @@ object SettingsAdvancedScreen : SearchableSettings {
private fun getDataGroup(): Preference.PreferenceGroup {
val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow
val scope = rememberCoroutineScope()
return Preference.PreferenceGroup(
title = stringResource(MR.strings.label_data),
@@ -232,10 +231,8 @@ object SettingsAdvancedScreen : SearchableSettings {
title = stringResource(MR.strings.pref_invalidate_download_cache),
subtitle = stringResource(MR.strings.pref_invalidate_download_cache_summary),
onClick = {
scope.launch {
Injekt.get<DownloadCache>().invalidateCache()
context.toast(MR.strings.download_cache_invalidated)
}
Injekt.get<DownloadCache>().invalidateCache()
context.toast(MR.strings.download_cache_invalidated)
},
),
Preference.PreferenceItem.TextPreference(
@@ -254,7 +251,7 @@ object SettingsAdvancedScreen : SearchableSettings {
val context = LocalContext.current
val networkHelper = remember { Injekt.get<NetworkHelper>() }
val userAgentPref = networkPreferences.defaultUserAgent()
val userAgentPref = networkPreferences.defaultUserAgent
val userAgent by userAgentPref.collectAsState()
return Preference.PreferenceGroup(
@@ -290,7 +287,7 @@ object SettingsAdvancedScreen : SearchableSettings {
},
),
Preference.PreferenceItem.ListPreference(
preference = networkPreferences.dohProvider(),
preference = networkPreferences.dohProvider,
entries = persistentMapOf(
-1 to stringResource(MR.strings.disabled),
PREF_DOH_CLOUDFLARE to "Cloudflare",
@@ -371,12 +368,12 @@ object SettingsAdvancedScreen : SearchableSettings {
},
),
Preference.PreferenceItem.SwitchPreference(
preference = libraryPreferences.updateMangaTitles(),
preference = libraryPreferences.updateMangaTitles,
title = stringResource(MR.strings.pref_update_library_manga_titles),
subtitle = stringResource(MR.strings.pref_update_library_manga_titles_summary),
),
Preference.PreferenceItem.SwitchPreference(
preference = libraryPreferences.disallowNonAsciiFilenames(),
preference = libraryPreferences.disallowNonAsciiFilenames,
title = stringResource(MR.strings.pref_disallow_non_ascii_filenames),
subtitle = stringResource(MR.strings.pref_disallow_non_ascii_filenames_details),
),
@@ -393,7 +390,7 @@ object SettingsAdvancedScreen : SearchableSettings {
title = stringResource(MR.strings.pref_category_downloads),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = downloadPreferences.includeChapterUrlHash(),
preference = downloadPreferences.includeChapterUrlHash,
title = stringResource(SYMR.strings.pref_include_chapter_url_hash),
subtitle = stringResource(SYMR.strings.pref_include_chapter_url_hash_desc),
),
@@ -413,14 +410,14 @@ object SettingsAdvancedScreen : SearchableSettings {
uri?.let {
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(uri, flags)
basePreferences.displayProfile().set(uri.toString())
basePreferences.displayProfile.set(uri.toString())
}
}
return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_reader),
preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference(
preference = basePreferences.hardwareBitmapThreshold(),
preference = basePreferences.hardwareBitmapThreshold,
entries = GLUtil.CUSTOM_TEXTURE_LIMIT_OPTIONS
.mapIndexed { index, option ->
val display = if (index == 0) {
@@ -440,13 +437,13 @@ object SettingsAdvancedScreen : SearchableSettings {
GLUtil.DEVICE_TEXTURE_LIMIT > GLUtil.SAFE_TEXTURE_LIMIT,
),
Preference.PreferenceItem.SwitchPreference(
preference = basePreferences.alwaysDecodeLongStripWithSSIV(),
preference = basePreferences.alwaysDecodeLongStripWithSSIV,
title = stringResource(MR.strings.pref_always_decode_long_strip_with_ssiv_2),
subtitle = stringResource(MR.strings.pref_always_decode_long_strip_with_ssiv_summary),
),
Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_display_profile),
subtitle = basePreferences.displayProfile().get(),
subtitle = basePreferences.displayProfile.get(),
onClick = {
chooseColorProfile.launch(arrayOf("*/*"))
},
@@ -461,7 +458,7 @@ object SettingsAdvancedScreen : SearchableSettings {
): Preference.PreferenceGroup {
val context = LocalContext.current
val uriHandler = LocalUriHandler.current
val extensionInstallerPref = basePreferences.extensionInstaller()
val extensionInstallerPref = basePreferences.extensionInstaller
var shizukuMissing by rememberSaveable { mutableStateOf(false) }
val trustExtension = remember { Injekt.get<TrustExtension>() }
@@ -661,12 +658,12 @@ object SettingsAdvancedScreen : SearchableSettings {
@Composable
private fun getDataSaverGroup(): Preference.PreferenceGroup {
val sourcePreferences = remember { Injekt.get<SourcePreferences>() }
val dataSaver by sourcePreferences.dataSaver().collectAsState()
val dataSaver by sourcePreferences.dataSaver.collectAsState()
return Preference.PreferenceGroup(
title = stringResource(SYMR.strings.data_saver),
preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference(
preference = sourcePreferences.dataSaver(),
preference = sourcePreferences.dataSaver,
title = stringResource(SYMR.strings.data_saver),
subtitle = stringResource(SYMR.strings.data_saver_summary),
entries = persistentMapOf(
@@ -676,28 +673,28 @@ object SettingsAdvancedScreen : SearchableSettings {
),
),
Preference.PreferenceItem.EditTextPreference(
preference = sourcePreferences.dataSaverServer(),
preference = sourcePreferences.dataSaverServer,
title = stringResource(SYMR.strings.bandwidth_data_saver_server),
subtitle = stringResource(SYMR.strings.data_saver_server_summary),
enabled = dataSaver == DataSaver.BANDWIDTH_HERO,
),
Preference.PreferenceItem.SwitchPreference(
preference = sourcePreferences.dataSaverDownloader(),
preference = sourcePreferences.dataSaverDownloader,
title = stringResource(SYMR.strings.data_saver_downloader),
enabled = dataSaver != DataSaver.NONE,
),
Preference.PreferenceItem.SwitchPreference(
preference = sourcePreferences.dataSaverIgnoreJpeg(),
preference = sourcePreferences.dataSaverIgnoreJpeg,
title = stringResource(SYMR.strings.data_saver_ignore_jpeg),
enabled = dataSaver != DataSaver.NONE,
),
Preference.PreferenceItem.SwitchPreference(
preference = sourcePreferences.dataSaverIgnoreGif(),
preference = sourcePreferences.dataSaverIgnoreGif,
title = stringResource(SYMR.strings.data_saver_ignore_gif),
enabled = dataSaver != DataSaver.NONE,
),
Preference.PreferenceItem.ListPreference(
preference = sourcePreferences.dataSaverImageQuality(),
preference = sourcePreferences.dataSaverImageQuality,
title = stringResource(SYMR.strings.data_saver_image_quality),
subtitle = stringResource(SYMR.strings.data_saver_image_quality_summary),
entries = listOf(
@@ -713,10 +710,10 @@ object SettingsAdvancedScreen : SearchableSettings {
enabled = dataSaver != DataSaver.NONE,
),
kotlin.run {
val dataSaverImageFormatJpeg by sourcePreferences.dataSaverImageFormatJpeg()
val dataSaverImageFormatJpeg by sourcePreferences.dataSaverImageFormatJpeg
.collectAsState()
Preference.PreferenceItem.SwitchPreference(
preference = sourcePreferences.dataSaverImageFormatJpeg(),
preference = sourcePreferences.dataSaverImageFormatJpeg,
title = stringResource(SYMR.strings.data_saver_image_format),
subtitle = if (dataSaverImageFormatJpeg) {
stringResource(SYMR.strings.data_saver_image_format_summary_on)
@@ -727,7 +724,7 @@ object SettingsAdvancedScreen : SearchableSettings {
)
},
Preference.PreferenceItem.SwitchPreference(
preference = sourcePreferences.dataSaverColorBW(),
preference = sourcePreferences.dataSaverColorBW,
title = stringResource(SYMR.strings.data_saver_color_bw),
enabled = dataSaver == DataSaver.BANDWIDTH_HERO,
),
@@ -747,7 +744,7 @@ object SettingsAdvancedScreen : SearchableSettings {
title = stringResource(SYMR.strings.developer_tools),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = exhPreferences.isHentaiEnabled(),
preference = exhPreferences.isHentaiEnabled,
title = stringResource(SYMR.strings.toggle_hentai_features),
subtitle = stringResource(SYMR.strings.toggle_hentai_features_summary),
onValueChanged = {
@@ -762,7 +759,7 @@ object SettingsAdvancedScreen : SearchableSettings {
},
),
Preference.PreferenceItem.SwitchPreference(
preference = delegateSourcePreferences.delegateSources(),
preference = delegateSourcePreferences.delegateSources,
title = stringResource(SYMR.strings.toggle_delegated_sources),
subtitle = stringResource(
SYMR.strings.toggle_delegated_sources_summary,
@@ -772,7 +769,7 @@ object SettingsAdvancedScreen : SearchableSettings {
),
),
Preference.PreferenceItem.ListPreference(
preference = exhPreferences.logLevel(),
preference = exhPreferences.logLevel,
title = stringResource(SYMR.strings.log_level),
subtitle = stringResource(SYMR.strings.log_level_summary),
entries = EHLogLevel.entries.mapIndexed { index, ehLogLevel ->
@@ -782,7 +779,7 @@ object SettingsAdvancedScreen : SearchableSettings {
}.toMap().toImmutableMap(),
),
Preference.PreferenceItem.SwitchPreference(
preference = sourcePreferences.enableSourceBlacklist(),
preference = sourcePreferences.enableSourceBlacklist,
title = stringResource(SYMR.strings.enable_source_blacklist),
subtitle = stringResource(
SYMR.strings.enable_source_blacklist_summary,
@@ -816,7 +813,7 @@ object SettingsAdvancedScreen : SearchableSettings {
TextButton(
onClick = {
dismiss()
securityPreferences.encryptDatabase().set(true)
securityPreferences.encryptDatabase.set(true)
},
) {
Text(text = stringResource(MR.strings.action_ok))
@@ -826,7 +823,7 @@ object SettingsAdvancedScreen : SearchableSettings {
}
Preference.PreferenceItem.SwitchPreference(
title = stringResource(SYMR.strings.encrypt_database),
preference = securityPreferences.encryptDatabase(),
preference = securityPreferences.encryptDatabase,
subtitle = stringResource(SYMR.strings.encrypt_database_subtitle),
onValueChanged = {
if (it) {
@@ -56,13 +56,13 @@ object SettingsAppearanceScreen : SearchableSettings {
): Preference.PreferenceGroup {
val context = LocalContext.current
val themeModePref = uiPreferences.themeMode()
val themeModePref = uiPreferences.themeMode
val themeMode by themeModePref.collectAsState()
val appThemePref = uiPreferences.appTheme()
val appThemePref = uiPreferences.appTheme
val appTheme by appThemePref.collectAsState()
val amoledPref = uiPreferences.themeDarkAmoled()
val amoledPref = uiPreferences.themeDarkAmoled
val amoled by amoledPref.collectAsState()
return Preference.PreferenceGroup(
@@ -109,7 +109,7 @@ object SettingsAppearanceScreen : SearchableSettings {
val now = remember { LocalDate.now() }
val dateFormat by uiPreferences.dateFormat().collectAsState()
val dateFormat by uiPreferences.dateFormat.collectAsState()
val formattedNow = remember(dateFormat) {
UiPreferences.dateFormat(dateFormat).format(now)
}
@@ -122,7 +122,7 @@ object SettingsAppearanceScreen : SearchableSettings {
onClick = { navigator.push(AppLanguageScreen()) },
),
Preference.PreferenceItem.ListPreference(
preference = uiPreferences.tabletUiMode(),
preference = uiPreferences.tabletUiMode,
entries = TabletUiMode.entries
.associateWith { stringResource(it.titleRes) }
.toImmutableMap(),
@@ -133,7 +133,7 @@ object SettingsAppearanceScreen : SearchableSettings {
},
),
Preference.PreferenceItem.ListPreference(
preference = uiPreferences.dateFormat(),
preference = uiPreferences.dateFormat,
entries = DateFormats
.associateWith {
val formattedDate = UiPreferences.dateFormat(it).format(now)
@@ -143,7 +143,7 @@ object SettingsAppearanceScreen : SearchableSettings {
title = stringResource(MR.strings.pref_date_format),
),
Preference.PreferenceItem.SwitchPreference(
preference = uiPreferences.relativeTime(),
preference = uiPreferences.relativeTime,
title = stringResource(MR.strings.pref_relative_format),
subtitle = stringResource(
MR.strings.pref_relative_format_summary,
@@ -152,7 +152,7 @@ object SettingsAppearanceScreen : SearchableSettings {
),
),
Preference.PreferenceItem.SwitchPreference(
preference = uiPreferences.imagesInDescription(),
preference = uiPreferences.imagesInDescription,
title = stringResource(MR.strings.pref_display_images_description),
),
),
@@ -162,22 +162,22 @@ object SettingsAppearanceScreen : SearchableSettings {
// SY -->
@Composable
fun getForkGroup(uiPreferences: UiPreferences): Preference.PreferenceGroup {
val previewsRowCount by uiPreferences.previewsRowCount().collectAsState()
val previewsRowCount by uiPreferences.previewsRowCount.collectAsState()
return Preference.PreferenceGroup(
stringResource(SYMR.strings.pref_category_fork),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = uiPreferences.expandFilters(),
preference = uiPreferences.expandFilters,
title = stringResource(SYMR.strings.toggle_expand_search_filters),
),
Preference.PreferenceItem.SwitchPreference(
preference = uiPreferences.recommendsInOverflow(),
preference = uiPreferences.recommendsInOverflow,
title = stringResource(SYMR.strings.put_recommends_in_overflow),
subtitle = stringResource(SYMR.strings.put_recommends_in_overflow_summary),
),
Preference.PreferenceItem.SwitchPreference(
preference = uiPreferences.mergeInOverflow(),
preference = uiPreferences.mergeInOverflow,
title = stringResource(SYMR.strings.put_merge_in_overflow),
subtitle = stringResource(SYMR.strings.put_merge_in_overflow_summary),
),
@@ -195,7 +195,7 @@ object SettingsAppearanceScreen : SearchableSettings {
},
valueRange = 0..10,
onValueChanged = {
uiPreferences.previewsRowCount().set(it)
uiPreferences.previewsRowCount.set(it)
true
},
),
@@ -209,15 +209,15 @@ object SettingsAppearanceScreen : SearchableSettings {
stringResource(SYMR.strings.pref_category_navbar),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = uiPreferences.showNavUpdates(),
preference = uiPreferences.showNavUpdates,
title = stringResource(SYMR.strings.pref_hide_updates_button),
),
Preference.PreferenceItem.SwitchPreference(
preference = uiPreferences.showNavHistory(),
preference = uiPreferences.showNavHistory,
title = stringResource(SYMR.strings.pref_hide_history_button),
),
Preference.PreferenceItem.SwitchPreference(
preference = uiPreferences.bottomBarLabels(),
preference = uiPreferences.bottomBarLabels,
title = stringResource(SYMR.strings.pref_show_bottom_bar_labels),
),
),
@@ -46,7 +46,7 @@ object SettingsBrowseScreen : SearchableSettings {
// SY -->
val scope = rememberCoroutineScope()
val hideFeedTab by remember { Injekt.get<UiPreferences>().hideFeedTab().asState(scope) }
val hideFeedTab by remember { Injekt.get<UiPreferences>().hideFeedTab.asState(scope) }
val uiPreferences = remember { Injekt.get<UiPreferences>() }
// SY <--
return listOf(
@@ -55,7 +55,7 @@ object SettingsBrowseScreen : SearchableSettings {
title = stringResource(MR.strings.label_sources),
preferenceItems = persistentListOf(
kotlin.run {
val count by sourcePreferences.sourcesTabCategories().collectAsState()
val count by sourcePreferences.sourcesTabCategories.collectAsState()
Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.action_edit_categories),
subtitle = pluralStringResource(MR.plurals.num_categories, count.size, count.size),
@@ -65,17 +65,17 @@ object SettingsBrowseScreen : SearchableSettings {
)
},
Preference.PreferenceItem.SwitchPreference(
preference = sourcePreferences.sourcesTabCategoriesFilter(),
preference = sourcePreferences.sourcesTabCategoriesFilter,
title = stringResource(SYMR.strings.pref_source_source_filtering),
subtitle = stringResource(SYMR.strings.pref_source_source_filtering_summery),
),
Preference.PreferenceItem.SwitchPreference(
preference = uiPreferences.useNewSourceNavigation(),
preference = uiPreferences.useNewSourceNavigation,
title = stringResource(SYMR.strings.pref_source_navigation),
subtitle = stringResource(SYMR.strings.pref_source_navigation_summery),
),
Preference.PreferenceItem.SwitchPreference(
preference = sourcePreferences.allowLocalSourceHiddenFolders(),
preference = sourcePreferences.allowLocalSourceHiddenFolders,
title = stringResource(SYMR.strings.pref_local_source_hidden_folders),
subtitle = stringResource(SYMR.strings.pref_local_source_hidden_folders_summery),
),
@@ -85,11 +85,11 @@ object SettingsBrowseScreen : SearchableSettings {
title = stringResource(SYMR.strings.feed),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = uiPreferences.hideFeedTab(),
preference = uiPreferences.hideFeedTab,
title = stringResource(SYMR.strings.pref_hide_feed),
),
Preference.PreferenceItem.SwitchPreference(
preference = uiPreferences.feedTabInFront(),
preference = uiPreferences.feedTabInFront,
title = stringResource(SYMR.strings.pref_feed_position),
subtitle = stringResource(SYMR.strings.pref_feed_position_summery),
enabled = hideFeedTab.not(),
@@ -101,7 +101,7 @@ object SettingsBrowseScreen : SearchableSettings {
title = stringResource(MR.strings.label_sources),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = sourcePreferences.hideInLibraryItems(),
preference = sourcePreferences.hideInLibraryItems,
title = stringResource(MR.strings.pref_hide_in_library_items),
),
Preference.PreferenceItem.TextPreference(
@@ -117,7 +117,7 @@ object SettingsBrowseScreen : SearchableSettings {
title = stringResource(MR.strings.pref_category_nsfw_content),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = sourcePreferences.showNsfwSource(),
preference = sourcePreferences.showNsfwSource,
title = stringResource(MR.strings.pref_show_nsfw_source),
subtitle = stringResource(MR.strings.requires_app_restart),
onValueChanged = {
@@ -119,7 +119,7 @@ object SettingsDataScreen : SearchableSettings {
val storagePreferences = Injekt.get<StoragePreferences>()
val syncPreferences = remember { Injekt.get<SyncPreferences>() }
val syncService by syncPreferences.syncService().collectAsState()
val syncService by syncPreferences.syncService.collectAsState()
return persistentListOf(
getStorageLocationPref(storagePreferences = storagePreferences),
@@ -185,11 +185,11 @@ object SettingsDataScreen : SearchableSettings {
storagePreferences: StoragePreferences,
): Preference.PreferenceItem.TextPreference {
val context = LocalContext.current
val pickStorageLocation = storageLocationPicker(storagePreferences.baseStorageDirectory())
val pickStorageLocation = storageLocationPicker(storagePreferences.baseStorageDirectory)
return Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_storage_location),
subtitle = storageLocationText(storagePreferences.baseStorageDirectory()),
subtitle = storageLocationText(storagePreferences.baseStorageDirectory),
onClick = {
try {
pickStorageLocation.launch(null)
@@ -205,7 +205,7 @@ object SettingsDataScreen : SearchableSettings {
val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow
val lastAutoBackup by backupPreferences.lastAutoBackupTimestamp().collectAsState()
val lastAutoBackup by backupPreferences.lastAutoBackupTimestamp.collectAsState()
val chooseBackup = rememberLauncherForActivityResult(
object : ActivityResultContracts.GetContent() {
@@ -272,7 +272,7 @@ object SettingsDataScreen : SearchableSettings {
// Automatic backups
Preference.PreferenceItem.ListPreference(
preference = backupPreferences.backupInterval(),
preference = backupPreferences.backupInterval,
entries = persistentMapOf(
0 to stringResource(MR.strings.off),
6 to stringResource(MR.strings.update_6hour),
@@ -303,10 +303,7 @@ object SettingsDataScreen : SearchableSettings {
val chapterCache = remember { Injekt.get<ChapterCache>() }
var cacheReadableSizeSema by remember { mutableIntStateOf(0) }
var cacheReadableSize by remember { mutableStateOf(context.stringResource(MR.strings.calculating)) }
LaunchedEffect(cacheReadableSizeSema) {
cacheReadableSize = chapterCache.getReadableSize()
}
val cacheReadableSize = remember(cacheReadableSizeSema) { chapterCache.readableSize }
// SY -->
val pagePreviewCache = remember { Injekt.get<PagePreviewCache>() }
@@ -368,7 +365,7 @@ object SettingsDataScreen : SearchableSettings {
),
// SY <--
Preference.PreferenceItem.SwitchPreference(
preference = libraryPreferences.autoClearChapterCache(),
preference = libraryPreferences.autoClearChapterCache,
title = stringResource(MR.strings.pref_auto_clear_chapter_cache),
),
),
@@ -520,7 +517,7 @@ object SettingsDataScreen : SearchableSettings {
title = stringResource(SYMR.strings.pref_sync_service_category),
preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference(
preference = syncPreferences.syncService(),
preference = syncPreferences.syncService,
title = stringResource(SYMR.strings.pref_sync_service),
entries = persistentMapOf(
SyncManager.SyncService.NONE.value to stringResource(MR.strings.off),
@@ -663,7 +660,7 @@ object SettingsDataScreen : SearchableSettings {
val qrScanLauncher = rememberLauncherForActivityResult(ScanContract()) {
if (it.contents != null && it.contents.isNotEmpty()) {
syncPreferences.clientAPIKey().set(it.contents)
syncPreferences.clientAPIKey.set(it.contents)
}
}
val context = LocalContext.current
@@ -680,13 +677,13 @@ object SettingsDataScreen : SearchableSettings {
Preference.PreferenceItem.EditTextPreference(
title = stringResource(SYMR.strings.pref_sync_host),
subtitle = stringResource(SYMR.strings.pref_sync_host_summ),
preference = syncPreferences.clientHost(),
preference = syncPreferences.clientHost,
onValueChanged = { newValue ->
scope.launch {
// Trim spaces at the beginning and end, then remove trailing slash if present
val trimmedValue = newValue.trim()
val modifiedValue = trimmedValue.trimEnd { it == '/' }
syncPreferences.clientHost().set(modifiedValue)
syncPreferences.clientHost.set(modifiedValue)
}
true
},
@@ -694,12 +691,12 @@ object SettingsDataScreen : SearchableSettings {
Preference.PreferenceItem.CustomPreference(
title = stringResource(SYMR.strings.pref_sync_api_key),
) {
val values by syncPreferences.clientAPIKey().collectAsState()
val values by syncPreferences.clientAPIKey.collectAsState()
EditTextPreferenceWidget(
title = stringResource(SYMR.strings.pref_sync_api_key),
subtitle = stringResource(SYMR.strings.pref_sync_api_key_summ),
onConfirm = {
syncPreferences.clientAPIKey().set(it)
syncPreferences.clientAPIKey.set(it)
true
},
icon = null,
@@ -755,8 +752,8 @@ object SettingsDataScreen : SearchableSettings {
@Composable
private fun getAutomaticSyncGroup(syncPreferences: SyncPreferences): Preference.PreferenceGroup {
val context = LocalContext.current
val syncIntervalPref = syncPreferences.syncInterval()
val lastSync by syncPreferences.lastSyncTimestamp().collectAsState()
val syncIntervalPref = syncPreferences.syncInterval
val lastSync by syncPreferences.lastSyncTimestamp.collectAsState()
return Preference.PreferenceGroup(
title = stringResource(SYMR.strings.pref_sync_automatic_category),
@@ -37,19 +37,19 @@ object SettingsDownloadScreen : SearchableSettings {
val allCategories by getCategories.subscribe().collectAsState(initial = emptyList())
val downloadPreferences = remember { Injekt.get<DownloadPreferences>() }
val parallelSourceLimit by downloadPreferences.parallelSourceLimit().collectAsState()
val parallelPageLimit by downloadPreferences.parallelPageLimit().collectAsState()
val parallelSourceLimit by downloadPreferences.parallelSourceLimit.collectAsState()
val parallelPageLimit by downloadPreferences.parallelPageLimit.collectAsState()
return listOf(
Preference.PreferenceItem.SwitchPreference(
preference = downloadPreferences.downloadOnlyOverWifi(),
preference = downloadPreferences.downloadOnlyOverWifi,
title = stringResource(MR.strings.connected_to_wifi),
),
Preference.PreferenceItem.SwitchPreference(
preference = downloadPreferences.saveChaptersAsCBZ(),
preference = downloadPreferences.saveChaptersAsCBZ,
title = stringResource(MR.strings.save_chapter_as_cbz),
),
Preference.PreferenceItem.SwitchPreference(
preference = downloadPreferences.splitTallImages(),
preference = downloadPreferences.splitTallImages,
title = stringResource(MR.strings.split_tall_images),
subtitle = stringResource(MR.strings.split_tall_images_summary),
),
@@ -57,14 +57,14 @@ object SettingsDownloadScreen : SearchableSettings {
value = parallelSourceLimit,
valueRange = 1..10,
title = stringResource(MR.strings.pref_download_concurrent_sources),
onValueChanged = { downloadPreferences.parallelSourceLimit().set(it) },
onValueChanged = { downloadPreferences.parallelSourceLimit.set(it) },
),
Preference.PreferenceItem.SliderPreference(
value = parallelPageLimit,
valueRange = 1..15,
title = stringResource(MR.strings.pref_download_concurrent_pages),
subtitle = stringResource(MR.strings.pref_download_concurrent_pages_summary),
onValueChanged = { downloadPreferences.parallelPageLimit().set(it) },
onValueChanged = { downloadPreferences.parallelPageLimit.set(it) },
),
getDeleteChaptersGroup(
downloadPreferences = downloadPreferences,
@@ -87,11 +87,11 @@ object SettingsDownloadScreen : SearchableSettings {
title = stringResource(MR.strings.pref_category_delete_chapters),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = downloadPreferences.removeAfterMarkedAsRead(),
preference = downloadPreferences.removeAfterMarkedAsRead,
title = stringResource(MR.strings.pref_remove_after_marked_as_read),
),
Preference.PreferenceItem.ListPreference(
preference = downloadPreferences.removeAfterReadSlots(),
preference = downloadPreferences.removeAfterReadSlots,
entries = persistentMapOf(
-1 to stringResource(MR.strings.disabled),
0 to stringResource(MR.strings.last_read_chapter),
@@ -103,7 +103,7 @@ object SettingsDownloadScreen : SearchableSettings {
title = stringResource(MR.strings.pref_remove_after_read),
),
Preference.PreferenceItem.SwitchPreference(
preference = downloadPreferences.removeBookmarkedChapters(),
preference = downloadPreferences.removeBookmarkedChapters,
title = stringResource(MR.strings.pref_remove_bookmarked_chapters),
),
getExcludedCategoriesPreference(
@@ -120,7 +120,7 @@ object SettingsDownloadScreen : SearchableSettings {
categories: () -> List<Category>,
): Preference.PreferenceItem.MultiSelectListPreference {
return Preference.PreferenceItem.MultiSelectListPreference(
preference = downloadPreferences.removeExcludeCategories(),
preference = downloadPreferences.removeExcludeCategories,
entries = categories()
.associate { it.id.toString() to it.visualName }
.toImmutableMap(),
@@ -133,10 +133,10 @@ object SettingsDownloadScreen : SearchableSettings {
downloadPreferences: DownloadPreferences,
allCategories: List<Category>,
): Preference.PreferenceGroup {
val downloadNewChaptersPref = downloadPreferences.downloadNewChapters()
val downloadNewUnreadChaptersOnlyPref = downloadPreferences.downloadNewUnreadChaptersOnly()
val downloadNewChapterCategoriesPref = downloadPreferences.downloadNewChapterCategories()
val downloadNewChapterCategoriesExcludePref = downloadPreferences.downloadNewChapterCategoriesExclude()
val downloadNewChaptersPref = downloadPreferences.downloadNewChapters
val downloadNewUnreadChaptersOnlyPref = downloadPreferences.downloadNewUnreadChaptersOnly
val downloadNewChapterCategoriesPref = downloadPreferences.downloadNewChapterCategories
val downloadNewChapterCategoriesExcludePref = downloadPreferences.downloadNewChapterCategoriesExclude
val downloadNewChapters by downloadNewChaptersPref.collectAsState()
@@ -194,7 +194,7 @@ object SettingsDownloadScreen : SearchableSettings {
title = stringResource(MR.strings.download_ahead),
preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference(
preference = downloadPreferences.autoDownloadWhileReading(),
preference = downloadPreferences.autoDownloadWhileReading,
entries = listOf(0, 2, 3, 5, 10)
.associateWith {
if (it == 0) {
@@ -88,7 +88,7 @@ object SettingsEhScreen : SearchableSettings {
@Composable
override fun getTitleRes() = SYMR.strings.pref_category_eh
override fun isEnabled(): Boolean = Injekt.get<ExhPreferences>().isHentaiEnabled().get()
override fun isEnabled(): Boolean = Injekt.get<ExhPreferences>().isHentaiEnabled.get()
@Composable
fun Reconfigure(
@@ -96,14 +96,14 @@ object SettingsEhScreen : SearchableSettings {
openWarnConfigureDialogController: () -> Unit,
) {
var initialLoadGuard by remember { mutableStateOf(false) }
val useHentaiAtHome by exhPreferences.useHentaiAtHome().collectAsState()
val useJapaneseTitle by exhPreferences.useJapaneseTitle().collectAsState()
val useOriginalImages by exhPreferences.exhUseOriginalImages().collectAsState()
val ehTagFilterValue by exhPreferences.ehTagFilterValue().collectAsState()
val ehTagWatchingValue by exhPreferences.ehTagWatchingValue().collectAsState()
val settingsLanguages by exhPreferences.exhSettingsLanguages().collectAsState()
val enabledCategories by exhPreferences.exhEnabledCategories().collectAsState()
val imageQuality by exhPreferences.imageQuality().collectAsState()
val useHentaiAtHome by exhPreferences.useHentaiAtHome.collectAsState()
val useJapaneseTitle by exhPreferences.useJapaneseTitle.collectAsState()
val useOriginalImages by exhPreferences.exhUseOriginalImages.collectAsState()
val ehTagFilterValue by exhPreferences.ehTagFilterValue.collectAsState()
val ehTagWatchingValue by exhPreferences.ehTagWatchingValue.collectAsState()
val settingsLanguages by exhPreferences.exhSettingsLanguages.collectAsState()
val enabledCategories by exhPreferences.exhEnabledCategories.collectAsState()
val imageQuality by exhPreferences.imageQuality.collectAsState()
DisposableEffect(
useHentaiAtHome,
useJapaneseTitle,
@@ -128,7 +128,7 @@ object SettingsEhScreen : SearchableSettings {
val getFlatMetadataById: GetFlatMetadataById = remember { Injekt.get() }
val deleteFavoriteEntries: DeleteFavoriteEntries = remember { Injekt.get() }
val getExhFavoriteMangaWithMetadata: GetExhFavoriteMangaWithMetadata = remember { Injekt.get() }
val exhentaiEnabled by exhPreferences.enableExhentai().collectAsState()
val exhentaiEnabled by exhPreferences.enableExhentai.collectAsState()
var runConfigureDialog by remember { mutableStateOf(false) }
val openWarnConfigureDialogController = { runConfigureDialog = true }
@@ -191,9 +191,9 @@ object SettingsEhScreen : SearchableSettings {
}
}
val context = LocalContext.current
val value by exhPreferences.enableExhentai().collectAsState()
val value by exhPreferences.enableExhentai.collectAsState()
return Preference.PreferenceItem.SwitchPreference(
preference = exhPreferences.enableExhentai(),
preference = exhPreferences.enableExhentai,
title = stringResource(SYMR.strings.enable_exhentai),
subtitle = if (!value) {
stringResource(SYMR.strings.requires_login)
@@ -202,7 +202,7 @@ object SettingsEhScreen : SearchableSettings {
},
onValueChanged = { newVal ->
if (!newVal) {
exhPreferences.enableExhentai().set(false)
exhPreferences.enableExhentai.set(false)
true
} else {
activityResultContract.launch(EhLoginActivity.newIntent(context))
@@ -218,7 +218,7 @@ object SettingsEhScreen : SearchableSettings {
exhPreferences: ExhPreferences,
): Preference.PreferenceItem.ListPreference<Int> {
return Preference.PreferenceItem.ListPreference(
preference = exhPreferences.useHentaiAtHome(),
preference = exhPreferences.useHentaiAtHome,
title = stringResource(SYMR.strings.use_hentai_at_home),
subtitle = stringResource(SYMR.strings.use_hentai_at_home_summary),
entries = persistentMapOf(
@@ -234,9 +234,9 @@ object SettingsEhScreen : SearchableSettings {
exhentaiEnabled: Boolean,
exhPreferences: ExhPreferences,
): Preference.PreferenceItem.SwitchPreference {
val value by exhPreferences.useJapaneseTitle().collectAsState()
val value by exhPreferences.useJapaneseTitle.collectAsState()
return Preference.PreferenceItem.SwitchPreference(
preference = exhPreferences.useJapaneseTitle(),
preference = exhPreferences.useJapaneseTitle,
title = stringResource(SYMR.strings.show_japanese_titles),
subtitle = if (value) {
stringResource(SYMR.strings.show_japanese_titles_option_1)
@@ -252,9 +252,9 @@ object SettingsEhScreen : SearchableSettings {
exhentaiEnabled: Boolean,
exhPreferences: ExhPreferences,
): Preference.PreferenceItem.SwitchPreference {
val value by exhPreferences.exhUseOriginalImages().collectAsState()
val value by exhPreferences.exhUseOriginalImages.collectAsState()
return Preference.PreferenceItem.SwitchPreference(
preference = exhPreferences.exhUseOriginalImages(),
preference = exhPreferences.exhUseOriginalImages,
title = stringResource(SYMR.strings.use_original_images),
subtitle = if (value) {
stringResource(SYMR.strings.use_original_images_on)
@@ -353,7 +353,7 @@ object SettingsEhScreen : SearchableSettings {
exhentaiEnabled: Boolean,
exhPreferences: ExhPreferences,
): Preference.PreferenceItem.TextPreference {
val value by exhPreferences.ehTagFilterValue().collectAsState()
val value by exhPreferences.ehTagFilterValue.collectAsState()
var dialogOpen by remember { mutableStateOf(false) }
if (dialogOpen) {
TagThresholdDialog(
@@ -364,7 +364,7 @@ object SettingsEhScreen : SearchableSettings {
outsideRangeError = stringResource(SYMR.strings.tag_filtering_threshhold_error),
onValueChange = {
dialogOpen = false
exhPreferences.ehTagFilterValue().set(it)
exhPreferences.ehTagFilterValue.set(it)
},
)
}
@@ -383,7 +383,7 @@ object SettingsEhScreen : SearchableSettings {
exhentaiEnabled: Boolean,
exhPreferences: ExhPreferences,
): Preference.PreferenceItem.TextPreference {
val value by exhPreferences.ehTagWatchingValue().collectAsState()
val value by exhPreferences.ehTagWatchingValue.collectAsState()
var dialogOpen by remember { mutableStateOf(false) }
if (dialogOpen) {
TagThresholdDialog(
@@ -394,7 +394,7 @@ object SettingsEhScreen : SearchableSettings {
outsideRangeError = stringResource(SYMR.strings.tag_watching_threshhold_error),
onValueChange = {
dialogOpen = false
exhPreferences.ehTagWatchingValue().set(it)
exhPreferences.ehTagWatchingValue.set(it)
},
)
}
@@ -606,7 +606,7 @@ object SettingsEhScreen : SearchableSettings {
exhentaiEnabled: Boolean,
exhPreferences: ExhPreferences,
): Preference.PreferenceItem.TextPreference {
val value by exhPreferences.exhSettingsLanguages().collectAsState()
val value by exhPreferences.exhSettingsLanguages.collectAsState()
var dialogOpen by remember { mutableStateOf(false) }
if (dialogOpen) {
LanguagesDialog(
@@ -614,7 +614,7 @@ object SettingsEhScreen : SearchableSettings {
initialValue = value,
onValueChange = {
dialogOpen = false
exhPreferences.exhSettingsLanguages().set(it)
exhPreferences.exhSettingsLanguages.set(it)
},
)
}
@@ -772,7 +772,7 @@ object SettingsEhScreen : SearchableSettings {
exhentaiEnabled: Boolean,
exhPreferences: ExhPreferences,
): Preference.PreferenceItem.TextPreference {
val value by exhPreferences.exhEnabledCategories().collectAsState()
val value by exhPreferences.exhEnabledCategories.collectAsState()
var dialogOpen by remember { mutableStateOf(false) }
if (dialogOpen) {
FrontPageCategoriesDialog(
@@ -780,7 +780,7 @@ object SettingsEhScreen : SearchableSettings {
initialValue = value,
onValueChange = {
dialogOpen = false
exhPreferences.exhEnabledCategories().set(it)
exhPreferences.exhEnabledCategories.set(it)
},
)
}
@@ -800,7 +800,7 @@ object SettingsEhScreen : SearchableSettings {
exhPreferences: ExhPreferences,
): Preference.PreferenceItem.SwitchPreference {
return Preference.PreferenceItem.SwitchPreference(
preference = exhPreferences.exhWatchedListDefaultState(),
preference = exhPreferences.exhWatchedListDefaultState,
title = stringResource(SYMR.strings.watched_list_default),
subtitle = stringResource(SYMR.strings.watched_list_state_summary),
enabled = exhentaiEnabled,
@@ -813,7 +813,7 @@ object SettingsEhScreen : SearchableSettings {
exhPreferences: ExhPreferences,
): Preference.PreferenceItem.ListPreference<String> {
return Preference.PreferenceItem.ListPreference(
preference = exhPreferences.imageQuality(),
preference = exhPreferences.imageQuality,
title = stringResource(SYMR.strings.eh_image_quality_summary),
subtitle = stringResource(SYMR.strings.eh_image_quality),
entries = persistentMapOf(
@@ -831,7 +831,7 @@ object SettingsEhScreen : SearchableSettings {
@Composable
fun enhancedEhentaiView(exhPreferences: ExhPreferences): Preference.PreferenceItem.SwitchPreference {
return Preference.PreferenceItem.SwitchPreference(
preference = exhPreferences.enhancedEHentaiView(),
preference = exhPreferences.enhancedEHentaiView,
title = stringResource(SYMR.strings.pref_enhanced_e_hentai_view),
subtitle = stringResource(SYMR.strings.pref_enhanced_e_hentai_view_summary),
)
@@ -840,7 +840,7 @@ object SettingsEhScreen : SearchableSettings {
@Composable
fun readOnlySync(exhPreferences: ExhPreferences): Preference.PreferenceItem.SwitchPreference {
return Preference.PreferenceItem.SwitchPreference(
preference = exhPreferences.exhReadOnlySync(),
preference = exhPreferences.exhReadOnlySync,
title = stringResource(SYMR.strings.disable_favorites_uploading),
subtitle = stringResource(SYMR.strings.disable_favorites_uploading_summary),
)
@@ -865,7 +865,7 @@ object SettingsEhScreen : SearchableSettings {
@Composable
fun lenientSync(exhPreferences: ExhPreferences): Preference.PreferenceItem.SwitchPreference {
return Preference.PreferenceItem.SwitchPreference(
preference = exhPreferences.exhLenientSync(),
preference = exhPreferences.exhLenientSync,
title = stringResource(SYMR.strings.ignore_sync_errors),
subtitle = stringResource(SYMR.strings.ignore_sync_errors_summary),
)
@@ -937,10 +937,10 @@ object SettingsEhScreen : SearchableSettings {
fun updateCheckerFrequency(
exhPreferences: ExhPreferences,
): Preference.PreferenceItem.ListPreference<Int> {
val value by exhPreferences.exhAutoUpdateFrequency().collectAsState()
val value by exhPreferences.exhAutoUpdateFrequency.collectAsState()
val context = LocalContext.current
return Preference.PreferenceItem.ListPreference(
preference = exhPreferences.exhAutoUpdateFrequency(),
preference = exhPreferences.exhAutoUpdateFrequency,
title = stringResource(SYMR.strings.time_between_batches),
subtitle = if (value == 0) {
stringResource(SYMR.strings.time_between_batches_summary_1, stringResource(MR.strings.app_name))
@@ -973,10 +973,10 @@ object SettingsEhScreen : SearchableSettings {
fun autoUpdateRequirements(
exhPreferences: ExhPreferences,
): Preference.PreferenceItem.MultiSelectListPreference {
val value by exhPreferences.exhAutoUpdateRequirements().collectAsState()
val value by exhPreferences.exhAutoUpdateRequirements.collectAsState()
val context = LocalContext.current
return Preference.PreferenceItem.MultiSelectListPreference(
preference = exhPreferences.exhAutoUpdateRequirements(),
preference = exhPreferences.exhAutoUpdateRequirements,
title = stringResource(SYMR.strings.auto_update_restrictions),
subtitle = remember(value) {
context.stringResource(
@@ -1150,7 +1150,7 @@ object SettingsEhScreen : SearchableSettings {
value = withIOContext {
try {
val stats =
exhPreferences.exhAutoUpdateStats().get().nullIfBlank()?.let {
exhPreferences.exhAutoUpdateStats.get().nullIfBlank()?.let {
Json.decodeFromString<EHentaiUpdaterStats>(it)
}
@@ -79,7 +79,7 @@ object SettingsLibraryScreen : SearchableSettings {
val userCategoriesCount = allCategories.filterNot(Category::isSystemCategory).size
// For default category
val ids = listOf(libraryPreferences.defaultCategory().defaultValue()) +
val ids = listOf(libraryPreferences.defaultCategory.defaultValue()) +
allCategories.fastMap { it.id.toInt() }
val labels = listOf(stringResource(MR.strings.default_category_summary)) +
allCategories.fastMap { it.visualName }
@@ -97,12 +97,12 @@ object SettingsLibraryScreen : SearchableSettings {
onClick = { navigator.push(CategoryScreen()) },
),
Preference.PreferenceItem.ListPreference(
preference = libraryPreferences.defaultCategory(),
preference = libraryPreferences.defaultCategory,
entries = ids.zip(labels).toMap().toImmutableMap(),
title = stringResource(MR.strings.default_category),
),
Preference.PreferenceItem.SwitchPreference(
preference = libraryPreferences.categorizedDisplaySettings(),
preference = libraryPreferences.categorizedDisplaySettings,
title = stringResource(MR.strings.categorized_display_settings),
onValueChanged = {
if (!it) {
@@ -124,9 +124,9 @@ object SettingsLibraryScreen : SearchableSettings {
): Preference.PreferenceGroup {
val context = LocalContext.current
val autoUpdateIntervalPref = libraryPreferences.autoUpdateInterval()
val autoUpdateCategoriesPref = libraryPreferences.updateCategories()
val autoUpdateCategoriesExcludePref = libraryPreferences.updateCategoriesExclude()
val autoUpdateIntervalPref = libraryPreferences.autoUpdateInterval
val autoUpdateCategoriesPref = libraryPreferences.updateCategories
val autoUpdateCategoriesExcludePref = libraryPreferences.updateCategoriesExclude
val autoUpdateInterval by autoUpdateIntervalPref.collectAsState()
@@ -170,7 +170,7 @@ object SettingsLibraryScreen : SearchableSettings {
},
),
Preference.PreferenceItem.MultiSelectListPreference(
preference = libraryPreferences.autoUpdateDeviceRestrictions(),
preference = libraryPreferences.autoUpdateDeviceRestrictions,
entries = persistentMapOf(
DEVICE_ONLY_ON_WIFI to stringResource(MR.strings.connected_to_wifi),
DEVICE_NETWORK_NOT_METERED to stringResource(MR.strings.network_not_metered),
@@ -196,7 +196,7 @@ object SettingsLibraryScreen : SearchableSettings {
),
// SY -->
Preference.PreferenceItem.ListPreference(
preference = libraryPreferences.groupLibraryUpdateType(),
preference = libraryPreferences.groupLibraryUpdateType,
title = stringResource(SYMR.strings.library_group_updates),
entries = persistentMapOf(
GroupLibraryMode.GLOBAL to stringResource(SYMR.strings.library_group_updates_global),
@@ -207,12 +207,12 @@ object SettingsLibraryScreen : SearchableSettings {
),
// SY <--
Preference.PreferenceItem.SwitchPreference(
preference = libraryPreferences.autoUpdateMetadata(),
preference = libraryPreferences.autoUpdateMetadata,
title = stringResource(MR.strings.pref_library_update_refresh_metadata),
subtitle = stringResource(MR.strings.pref_library_update_refresh_metadata_summary),
),
Preference.PreferenceItem.MultiSelectListPreference(
preference = libraryPreferences.autoUpdateMangaRestrictions(),
preference = libraryPreferences.autoUpdateMangaRestrictions,
entries = persistentMapOf(
MANGA_HAS_UNREAD to stringResource(MR.strings.pref_update_only_completely_read),
MANGA_NON_READ to stringResource(MR.strings.pref_update_only_started),
@@ -222,7 +222,7 @@ object SettingsLibraryScreen : SearchableSettings {
title = stringResource(MR.strings.pref_library_update_smart_update),
),
Preference.PreferenceItem.SwitchPreference(
preference = libraryPreferences.newShowUpdatesCount(),
preference = libraryPreferences.newShowUpdatesCount,
title = stringResource(MR.strings.pref_library_update_show_tab_badge),
),
),
@@ -237,7 +237,7 @@ object SettingsLibraryScreen : SearchableSettings {
title = stringResource(MR.strings.pref_behavior),
preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference(
preference = libraryPreferences.swipeToStartAction(),
preference = libraryPreferences.swipeToStartAction,
entries = persistentMapOf(
LibraryPreferences.ChapterSwipeAction.Disabled to
stringResource(MR.strings.disabled),
@@ -251,7 +251,7 @@ object SettingsLibraryScreen : SearchableSettings {
title = stringResource(MR.strings.pref_chapter_swipe_start),
),
Preference.PreferenceItem.ListPreference(
preference = libraryPreferences.swipeToEndAction(),
preference = libraryPreferences.swipeToEndAction,
entries = persistentMapOf(
LibraryPreferences.ChapterSwipeAction.Disabled to
stringResource(MR.strings.disabled),
@@ -265,7 +265,7 @@ object SettingsLibraryScreen : SearchableSettings {
title = stringResource(MR.strings.pref_chapter_swipe_end),
),
Preference.PreferenceItem.MultiSelectListPreference(
preference = libraryPreferences.markDuplicateReadChapterAsRead(),
preference = libraryPreferences.markDuplicateReadChapterAsRead,
entries = persistentMapOf(
MARK_DUPLICATE_CHAPTER_READ_EXISTING to
stringResource(MR.strings.pref_mark_duplicate_read_chapter_read_existing),
@@ -281,7 +281,7 @@ object SettingsLibraryScreen : SearchableSettings {
// SY -->
@Composable
fun getSortingCategory(navigator: Navigator, libraryPreferences: LibraryPreferences): Preference.PreferenceGroup {
val tagCount by libraryPreferences.sortTagsForLibrary().collectAsState()
val tagCount by libraryPreferences.sortTagsForLibrary.collectAsState()
return Preference.PreferenceGroup(
stringResource(SYMR.strings.pref_sorting_settings),
preferenceItems = persistentListOf(
@@ -175,7 +175,7 @@ object SettingsMangadexScreen : SearchableSettings {
sourcePreferences: SourcePreferences,
): Preference.PreferenceItem.ListPreference<String> {
return Preference.PreferenceItem.ListPreference(
preference = sourcePreferences.preferredMangaDexId(),
preference = sourcePreferences.preferredMangaDexId,
title = stringResource(SYMR.strings.mangadex_preffered_source),
subtitle = stringResource(SYMR.strings.mangadex_preffered_source_summary),
entries = MdUtil.getEnabledMangaDexs(sourcePreferences)
@@ -255,7 +255,7 @@ object SettingsMangadexScreen : SearchableSettings {
onDismissRequest = { dialogOpen = false },
onSelectionConfirmed = { items ->
dialogOpen = false
sourcePreferences.mangadexSyncToLibraryIndexes().set(
sourcePreferences.mangadexSyncToLibraryIndexes.set(
List(items.size) { index -> (index + 1).toString() }.toSet(),
)
LibraryUpdateJob.startNow(
@@ -34,19 +34,19 @@ object SettingsReaderScreen : SearchableSettings {
override fun getPreferences(): List<Preference> {
val readerPref = remember { Injekt.get<ReaderPreferences>() }
// SY -->
val forceHorizontalSeekbar by readerPref.forceHorizontalSeekbar().collectAsState()
val forceHorizontalSeekbar by readerPref.forceHorizontalSeekbar.collectAsState()
// SY <--
return listOf(
Preference.PreferenceItem.ListPreference(
preference = readerPref.defaultReadingMode(),
preference = readerPref.defaultReadingMode,
entries = ReadingMode.entries.drop(1)
.associate { it.flagValue to stringResource(it.stringRes) }
.toImmutableMap(),
title = stringResource(MR.strings.pref_viewer_type),
),
Preference.PreferenceItem.ListPreference(
preference = readerPref.doubleTapAnimSpeed(),
preference = readerPref.doubleTapAnimSpeed,
entries = persistentMapOf(
1 to stringResource(MR.strings.double_tap_anim_speed_0),
500 to stringResource(MR.strings.double_tap_anim_speed_normal),
@@ -55,29 +55,29 @@ object SettingsReaderScreen : SearchableSettings {
title = stringResource(MR.strings.pref_double_tap_anim_speed),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPref.showReadingMode(),
preference = readerPref.showReadingMode,
title = stringResource(MR.strings.pref_show_reading_mode),
subtitle = stringResource(MR.strings.pref_show_reading_mode_summary),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPref.showNavigationOverlayOnStart(),
preference = readerPref.showNavigationOverlayOnStart,
title = stringResource(MR.strings.pref_show_navigation_mode),
subtitle = stringResource(MR.strings.pref_show_navigation_mode_summary),
),
// SY -->
Preference.PreferenceItem.SwitchPreference(
preference = readerPref.forceHorizontalSeekbar(),
preference = readerPref.forceHorizontalSeekbar,
title = stringResource(SYMR.strings.pref_force_horz_seekbar),
subtitle = stringResource(SYMR.strings.pref_force_horz_seekbar_summary),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPref.landscapeVerticalSeekbar(),
preference = readerPref.landscapeVerticalSeekbar,
title = stringResource(SYMR.strings.pref_show_vert_seekbar_landscape),
subtitle = stringResource(SYMR.strings.pref_show_vert_seekbar_landscape_summary),
enabled = !forceHorizontalSeekbar,
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPref.leftVerticalSeekbar(),
preference = readerPref.leftVerticalSeekbar,
title = stringResource(SYMR.strings.pref_left_handed_vertical_seekbar),
subtitle = stringResource(SYMR.strings.pref_left_handed_vertical_seekbar_summary),
enabled = !forceHorizontalSeekbar,
@@ -85,7 +85,7 @@ object SettingsReaderScreen : SearchableSettings {
// SY <--
/* SY -->
Preference.PreferenceItem.SwitchPreference(
preference = readerPref.pageTransitions(),
preference = readerPref.pageTransitions,
title = stringResource(MR.strings.pref_page_transitions),
),
SY <-- */
@@ -108,20 +108,20 @@ object SettingsReaderScreen : SearchableSettings {
@Composable
private fun getDisplayGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
val fullscreenPref = readerPreferences.fullscreen()
val fullscreenPref = readerPreferences.fullscreen
val fullscreen by fullscreenPref.collectAsState()
return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_display),
preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference(
preference = readerPreferences.defaultOrientationType(),
preference = readerPreferences.defaultOrientationType,
entries = ReaderOrientation.entries.drop(1)
.associate { it.flagValue to stringResource(it.stringRes) }
.toImmutableMap(),
title = stringResource(MR.strings.pref_rotation_type),
),
Preference.PreferenceItem.ListPreference(
preference = readerPreferences.readerTheme(),
preference = readerPreferences.readerTheme,
entries = persistentMapOf(
1 to stringResource(MR.strings.black_background),
2 to stringResource(MR.strings.gray_background),
@@ -135,16 +135,16 @@ object SettingsReaderScreen : SearchableSettings {
title = stringResource(MR.strings.pref_fullscreen),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.drawUnderCutout(),
preference = readerPreferences.drawUnderCutout,
title = stringResource(MR.strings.pref_cutout_short),
enabled = LocalView.current.hasDisplayCutout() && fullscreen,
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.keepScreenOn(),
preference = readerPreferences.keepScreenOn,
title = stringResource(MR.strings.pref_keep_screen_on),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.showPageNumber(),
preference = readerPreferences.showPageNumber,
title = stringResource(MR.strings.pref_show_page_number),
),
),
@@ -153,21 +153,21 @@ object SettingsReaderScreen : SearchableSettings {
@Composable
private fun getEInkGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
val flashPageState by readerPreferences.flashOnPageChange().collectAsState()
val flashPageState by readerPreferences.flashOnPageChange.collectAsState()
val flashMillisPref = readerPreferences.flashDurationMillis()
val flashMillisPref = readerPreferences.flashDurationMillis
val flashMillis by flashMillisPref.collectAsState()
val flashIntervalPref = readerPreferences.flashPageInterval()
val flashIntervalPref = readerPreferences.flashPageInterval
val flashInterval by flashIntervalPref.collectAsState()
val flashColorPref = readerPreferences.flashColor()
val flashColorPref = readerPreferences.flashColor
return Preference.PreferenceGroup(
title = "E-Ink",
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.flashOnPageChange(),
preference = readerPreferences.flashOnPageChange,
title = stringResource(MR.strings.pref_flash_page),
subtitle = stringResource(MR.strings.pref_flash_page_summ),
),
@@ -208,19 +208,19 @@ object SettingsReaderScreen : SearchableSettings {
title = stringResource(MR.strings.pref_category_reading),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.skipRead(),
preference = readerPreferences.skipRead,
title = stringResource(MR.strings.pref_skip_read_chapters),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.skipFiltered(),
preference = readerPreferences.skipFiltered,
title = stringResource(MR.strings.pref_skip_filtered_chapters),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.skipDupe(),
preference = readerPreferences.skipDupe,
title = stringResource(MR.strings.pref_skip_dupe_chapters),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.alwaysShowChapterTransition(),
preference = readerPreferences.alwaysShowChapterTransition,
title = stringResource(MR.strings.pref_always_show_chapter_transition),
),
),
@@ -229,10 +229,10 @@ object SettingsReaderScreen : SearchableSettings {
@Composable
private fun getPagedGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
val navModePref = readerPreferences.navigationModePager()
val imageScaleTypePref = readerPreferences.imageScaleType()
val dualPageSplitPref = readerPreferences.dualPageSplitPaged()
val rotateToFitPref = readerPreferences.dualPageRotateToFit()
val navModePref = readerPreferences.navigationModePager
val imageScaleTypePref = readerPreferences.imageScaleType
val dualPageSplitPref = readerPreferences.dualPageSplitPaged
val rotateToFitPref = readerPreferences.dualPageRotateToFit
val navMode by navModePref.collectAsState()
val imageScaleType by imageScaleTypePref.collectAsState()
@@ -251,7 +251,7 @@ object SettingsReaderScreen : SearchableSettings {
title = stringResource(MR.strings.pref_viewer_nav),
),
Preference.PreferenceItem.ListPreference(
preference = readerPreferences.pagerNavInverted(),
preference = readerPreferences.pagerNavInverted,
entries = persistentListOf(
ReaderPreferences.TappingInvertMode.NONE,
ReaderPreferences.TappingInvertMode.HORIZONTAL,
@@ -272,7 +272,7 @@ object SettingsReaderScreen : SearchableSettings {
title = stringResource(MR.strings.pref_image_scale_type),
),
Preference.PreferenceItem.ListPreference(
preference = readerPreferences.zoomStart(),
preference = readerPreferences.zoomStart,
entries = ReaderPreferences.ZoomStart
.mapIndexed { index, it -> index + 1 to stringResource(it) }
.toMap()
@@ -280,22 +280,22 @@ object SettingsReaderScreen : SearchableSettings {
title = stringResource(MR.strings.pref_zoom_start),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.cropBorders(),
preference = readerPreferences.cropBorders,
title = stringResource(MR.strings.pref_crop_borders),
),
// SY -->
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.pageTransitionsPager(),
preference = readerPreferences.pageTransitionsPager,
title = stringResource(MR.strings.pref_page_transitions),
),
// SY <--
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.landscapeZoom(),
preference = readerPreferences.landscapeZoom,
title = stringResource(MR.strings.pref_landscape_zoom),
enabled = imageScaleType == 1,
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.navigateToPan(),
preference = readerPreferences.navigateToPan,
title = stringResource(MR.strings.pref_navigate_pan),
enabled = navMode != 5,
),
@@ -308,7 +308,7 @@ object SettingsReaderScreen : SearchableSettings {
},
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.dualPageInvertPaged(),
preference = readerPreferences.dualPageInvertPaged,
title = stringResource(MR.strings.pref_dual_page_invert),
subtitle = stringResource(MR.strings.pref_dual_page_invert_summary),
enabled = dualPageSplit,
@@ -322,7 +322,7 @@ object SettingsReaderScreen : SearchableSettings {
},
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.dualPageRotateToFitInvert(),
preference = readerPreferences.dualPageRotateToFitInvert,
title = stringResource(MR.strings.pref_page_rotate_invert),
enabled = rotateToFit,
),
@@ -334,10 +334,10 @@ object SettingsReaderScreen : SearchableSettings {
private fun getWebtoonGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
val numberFormat = remember { NumberFormat.getPercentInstance() }
val navModePref = readerPreferences.navigationModeWebtoon()
val dualPageSplitPref = readerPreferences.dualPageSplitWebtoon()
val rotateToFitPref = readerPreferences.dualPageRotateToFitWebtoon()
val webtoonSidePaddingPref = readerPreferences.webtoonSidePadding()
val navModePref = readerPreferences.navigationModeWebtoon
val dualPageSplitPref = readerPreferences.dualPageSplitWebtoon
val rotateToFitPref = readerPreferences.dualPageRotateToFitWebtoon
val webtoonSidePaddingPref = readerPreferences.webtoonSidePadding
val navMode by navModePref.collectAsState()
val dualPageSplit by dualPageSplitPref.collectAsState()
@@ -356,7 +356,7 @@ object SettingsReaderScreen : SearchableSettings {
title = stringResource(MR.strings.pref_viewer_nav),
),
Preference.PreferenceItem.ListPreference(
preference = readerPreferences.webtoonNavInverted(),
preference = readerPreferences.webtoonNavInverted,
entries = persistentListOf(
ReaderPreferences.TappingInvertMode.NONE,
ReaderPreferences.TappingInvertMode.HORIZONTAL,
@@ -378,7 +378,7 @@ object SettingsReaderScreen : SearchableSettings {
onValueChanged = { webtoonSidePaddingPref.set(it) },
),
Preference.PreferenceItem.ListPreference(
preference = readerPreferences.readerHideThreshold(),
preference = readerPreferences.readerHideThreshold,
entries = persistentMapOf(
ReaderPreferences.ReaderHideThreshold.HIGHEST to stringResource(MR.strings.pref_highest),
ReaderPreferences.ReaderHideThreshold.HIGH to stringResource(MR.strings.pref_high),
@@ -388,7 +388,7 @@ object SettingsReaderScreen : SearchableSettings {
title = stringResource(MR.strings.pref_hide_threshold),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.cropBordersWebtoon(),
preference = readerPreferences.cropBordersWebtoon,
title = stringResource(MR.strings.pref_crop_borders),
),
Preference.PreferenceItem.SwitchPreference(
@@ -400,7 +400,7 @@ object SettingsReaderScreen : SearchableSettings {
},
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.dualPageInvertWebtoon(),
preference = readerPreferences.dualPageInvertWebtoon,
title = stringResource(MR.strings.pref_dual_page_invert),
subtitle = stringResource(MR.strings.pref_dual_page_invert_summary),
enabled = dualPageSplit,
@@ -414,21 +414,21 @@ object SettingsReaderScreen : SearchableSettings {
},
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.dualPageRotateToFitInvertWebtoon(),
preference = readerPreferences.dualPageRotateToFitInvertWebtoon,
title = stringResource(MR.strings.pref_page_rotate_invert),
enabled = rotateToFit,
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.webtoonDoubleTapZoomEnabled(),
preference = readerPreferences.webtoonDoubleTapZoomEnabled,
title = stringResource(MR.strings.pref_double_tap_zoom),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.webtoonDisableZoomOut(),
preference = readerPreferences.webtoonDisableZoomOut,
title = stringResource(MR.strings.pref_webtoon_disable_zoom_out),
),
// SY -->
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.pageTransitionsWebtoon(),
preference = readerPreferences.pageTransitionsWebtoon,
title = stringResource(MR.strings.pref_page_transitions),
),
// SY <--
@@ -443,12 +443,12 @@ object SettingsReaderScreen : SearchableSettings {
title = stringResource(MR.strings.vertical_plus_viewer),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.continuousVerticalTappingByPage(),
preference = readerPreferences.continuousVerticalTappingByPage,
title = stringResource(SYMR.strings.tap_scroll_page),
subtitle = stringResource(SYMR.strings.tap_scroll_page_summary),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.cropBordersContinuousVertical(),
preference = readerPreferences.cropBordersContinuousVertical,
title = stringResource(MR.strings.pref_crop_borders),
),
),
@@ -458,7 +458,7 @@ object SettingsReaderScreen : SearchableSettings {
@Composable
private fun getNavigationGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
val readWithVolumeKeysPref = readerPreferences.readWithVolumeKeys()
val readWithVolumeKeysPref = readerPreferences.readWithVolumeKeys
val readWithVolumeKeys by readWithVolumeKeysPref.collectAsState()
return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_reader_navigation),
@@ -468,7 +468,7 @@ object SettingsReaderScreen : SearchableSettings {
title = stringResource(MR.strings.pref_read_with_volume_keys),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.readWithVolumeKeysInverted(),
preference = readerPreferences.readWithVolumeKeysInverted,
title = stringResource(MR.strings.pref_read_with_volume_keys_inverted),
enabled = readWithVolumeKeys,
),
@@ -482,11 +482,11 @@ object SettingsReaderScreen : SearchableSettings {
title = stringResource(MR.strings.pref_reader_actions),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.readWithLongTap(),
preference = readerPreferences.readWithLongTap,
title = stringResource(MR.strings.pref_read_with_long_tap),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.folderPerManga(),
preference = readerPreferences.folderPerManga,
title = stringResource(MR.strings.pref_create_folder_per_manga),
subtitle = stringResource(MR.strings.pref_create_folder_per_manga_summary),
),
@@ -501,7 +501,7 @@ object SettingsReaderScreen : SearchableSettings {
title = stringResource(SYMR.strings.page_downloading),
preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference(
preference = readerPreferences.preloadSize(),
preference = readerPreferences.preloadSize,
title = stringResource(SYMR.strings.reader_preload_amount),
subtitle = stringResource(SYMR.strings.reader_preload_amount_summary),
entries = persistentMapOf(
@@ -516,13 +516,13 @@ object SettingsReaderScreen : SearchableSettings {
),
),
Preference.PreferenceItem.ListPreference(
preference = readerPreferences.readerThreads(),
preference = readerPreferences.readerThreads,
title = stringResource(SYMR.strings.download_threads),
subtitle = stringResource(SYMR.strings.download_threads_summary),
entries = List(5) { it }.associateWith { it.toString() }.toImmutableMap(),
),
Preference.PreferenceItem.ListPreference(
preference = readerPreferences.cacheSize(),
preference = readerPreferences.cacheSize,
title = stringResource(SYMR.strings.reader_cache_size),
subtitle = stringResource(SYMR.strings.reader_cache_size_summary),
entries = persistentMapOf(
@@ -545,7 +545,7 @@ object SettingsReaderScreen : SearchableSettings {
),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.aggressivePageLoading(),
preference = readerPreferences.aggressivePageLoading,
title = stringResource(SYMR.strings.aggressively_load_pages),
subtitle = stringResource(SYMR.strings.aggressively_load_pages_summary),
),
@@ -555,26 +555,26 @@ object SettingsReaderScreen : SearchableSettings {
@Composable
private fun getForkSettingsGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
val pageLayout by readerPreferences.pageLayout().collectAsState()
val pageLayout by readerPreferences.pageLayout.collectAsState()
return Preference.PreferenceGroup(
title = stringResource(SYMR.strings.pref_category_fork),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.readerInstantRetry(),
preference = readerPreferences.readerInstantRetry,
title = stringResource(SYMR.strings.skip_queue_on_retry),
subtitle = stringResource(SYMR.strings.skip_queue_on_retry_summary),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.preserveReadingPosition(),
preference = readerPreferences.preserveReadingPosition,
title = stringResource(SYMR.strings.preserve_reading_position),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.useAutoWebtoon(),
preference = readerPreferences.useAutoWebtoon,
title = stringResource(SYMR.strings.auto_webtoon_mode),
subtitle = stringResource(SYMR.strings.auto_webtoon_mode_summary),
),
Preference.PreferenceItem.MultiSelectListPreference(
preference = readerPreferences.readerBottomButtons(),
preference = readerPreferences.readerBottomButtons,
title = stringResource(SYMR.strings.reader_bottom_buttons),
subtitle = stringResource(SYMR.strings.reader_bottom_buttons_summary),
entries = ReaderBottomButton.entries
@@ -582,7 +582,7 @@ object SettingsReaderScreen : SearchableSettings {
.toImmutableMap(),
),
Preference.PreferenceItem.ListPreference(
preference = readerPreferences.pageLayout(),
preference = readerPreferences.pageLayout,
title = stringResource(SYMR.strings.page_layout),
subtitle = stringResource(SYMR.strings.automatic_can_still_switch),
entries = ReaderPreferences.PageLayouts
@@ -591,12 +591,12 @@ object SettingsReaderScreen : SearchableSettings {
.toImmutableMap(),
),
Preference.PreferenceItem.SwitchPreference(
preference = readerPreferences.invertDoublePages(),
preference = readerPreferences.invertDoublePages,
title = stringResource(SYMR.strings.invert_double_pages),
enabled = pageLayout != PagerConfig.PageLayout.SINGLE_PAGE,
),
Preference.PreferenceItem.ListPreference(
preference = readerPreferences.centerMarginType(),
preference = readerPreferences.centerMarginType,
title = stringResource(SYMR.strings.center_margin),
subtitle = stringResource(SYMR.strings.pref_center_margin_summary),
entries = ReaderPreferences.CenterMarginTypes
@@ -605,7 +605,7 @@ object SettingsReaderScreen : SearchableSettings {
.toImmutableMap(),
),
Preference.PreferenceItem.ListPreference(
preference = readerPreferences.archiveReaderMode(),
preference = readerPreferences.archiveReaderMode,
title = stringResource(SYMR.strings.pref_archive_reader_mode),
subtitle = stringResource(SYMR.strings.pref_archive_reader_mode_summary),
entries = ReaderPreferences.archiveModeTypes
@@ -85,12 +85,12 @@ object SettingsSecurityScreen : SearchableSettings {
): Preference.PreferenceGroup {
val context = LocalContext.current
val authSupported = remember { context.isAuthenticationSupported() }
val useAuthPref = securityPreferences.useAuthenticator()
val useAuthPref = securityPreferences.useAuthenticator
val useAuth by useAuthPref.collectAsState()
val scope = rememberCoroutineScope()
val isCbzPasswordSet by remember { CbzCrypto.isPasswordSetState(scope) }.collectAsState()
val passwordProtectDownloads by securityPreferences.passwordProtectDownloads().collectAsState()
val passwordProtectDownloads by securityPreferences.passwordProtectDownloads.collectAsState()
return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_security),
@@ -106,7 +106,7 @@ object SettingsSecurityScreen : SearchableSettings {
},
),
Preference.PreferenceItem.ListPreference(
preference = securityPreferences.lockAppAfter(),
preference = securityPreferences.lockAppAfter,
entries = LockAfterValues
.associateWith {
when (it) {
@@ -125,11 +125,11 @@ object SettingsSecurityScreen : SearchableSettings {
},
),
Preference.PreferenceItem.SwitchPreference(
preference = securityPreferences.hideNotificationContent(),
preference = securityPreferences.hideNotificationContent,
title = stringResource(MR.strings.hide_notification_content),
),
Preference.PreferenceItem.ListPreference(
preference = securityPreferences.secureScreen(),
preference = securityPreferences.secureScreen,
entries = SecurityPreferences.SecureScreenMode.entries
.associateWith { stringResource(it.titleRes) }
.toImmutableMap(),
@@ -137,13 +137,13 @@ object SettingsSecurityScreen : SearchableSettings {
),
// SY -->
Preference.PreferenceItem.SwitchPreference(
preference = securityPreferences.passwordProtectDownloads(),
preference = securityPreferences.passwordProtectDownloads,
title = stringResource(SYMR.strings.password_protect_downloads),
subtitle = stringResource(SYMR.strings.password_protect_downloads_summary),
enabled = isCbzPasswordSet,
),
Preference.PreferenceItem.ListPreference(
preference = securityPreferences.encryptionType(),
preference = securityPreferences.encryptionType,
title = stringResource(SYMR.strings.encryption_type),
entries = SecurityPreferences.EncryptionType.entries
.associateWith { stringResource(it.titleRes) }
@@ -160,7 +160,7 @@ object SettingsSecurityScreen : SearchableSettings {
dialogOpen = false
CbzCrypto.deleteKeyCbz()
securityPreferences.cbzPassword().set(CbzCrypto.encryptCbz(password.replace("\n", "")))
securityPreferences.cbzPassword.set(CbzCrypto.encryptCbz(password.replace("\n", "")))
},
)
}
@@ -175,13 +175,13 @@ object SettingsSecurityScreen : SearchableSettings {
title = stringResource(SYMR.strings.delete_cbz_archive_password),
onClick = {
CbzCrypto.deleteKeyCbz()
securityPreferences.cbzPassword().set("")
securityPreferences.cbzPassword.set("")
},
enabled = isCbzPasswordSet,
),
kotlin.run {
val navigator = LocalNavigator.currentOrThrow
val count by securityPreferences.authenticatorTimeRanges().collectAsState()
val count by securityPreferences.authenticatorTimeRanges.collectAsState()
Preference.PreferenceItem.TextPreference(
title = stringResource(SYMR.strings.action_edit_biometric_lock_times),
subtitle = pluralStringResource(
@@ -196,7 +196,7 @@ object SettingsSecurityScreen : SearchableSettings {
)
},
kotlin.run {
val selection by securityPreferences.authenticatorDays().collectAsState()
val selection by securityPreferences.authenticatorDays.collectAsState()
var dialogOpen by remember { mutableStateOf(false) }
if (dialogOpen) {
SetLockedDaysDialog(
@@ -204,7 +204,7 @@ object SettingsSecurityScreen : SearchableSettings {
initialSelection = selection,
onDaysSelected = {
dialogOpen = false
securityPreferences.authenticatorDays().set(it)
securityPreferences.authenticatorDays.set(it)
},
)
}
@@ -384,12 +384,12 @@ object SettingsSecurityScreen : SearchableSettings {
title = stringResource(MR.strings.pref_firebase),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = privacyPreferences.crashlytics(),
preference = privacyPreferences.crashlytics,
title = stringResource(MR.strings.onboarding_permission_crashlytics),
subtitle = stringResource(MR.strings.onboarding_permission_crashlytics_description),
),
Preference.PreferenceItem.SwitchPreference(
preference = privacyPreferences.analytics(),
preference = privacyPreferences.analytics,
title = stringResource(MR.strings.onboarding_permission_analytics),
subtitle = stringResource(MR.strings.onboarding_permission_analytics_description),
),
@@ -91,7 +91,7 @@ object SettingsTrackingScreen : SearchableSettings {
val trackPreferences = remember { Injekt.get<TrackPreferences>() }
val trackerManager = remember { Injekt.get<TrackerManager>() }
val sourceManager = remember { Injekt.get<SourceManager>() }
val autoTrackStatePref = trackPreferences.autoUpdateTrackOnMarkRead()
val autoTrackStatePref = trackPreferences.autoUpdateTrackOnMarkRead
var dialog by remember { mutableStateOf<Any?>(null) }
dialog?.run {
@@ -129,11 +129,11 @@ object SettingsTrackingScreen : SearchableSettings {
return listOf(
Preference.PreferenceItem.SwitchPreference(
preference = trackPreferences.autoUpdateTrack(),
preference = trackPreferences.autoUpdateTrack,
title = stringResource(MR.strings.pref_auto_update_manga_sync),
),
Preference.PreferenceItem.ListPreference(
preference = trackPreferences.autoUpdateTrackOnMarkRead(),
preference = trackPreferences.autoUpdateTrackOnMarkRead,
entries = AutoTrackState.entries
.associateWith { stringResource(it.titleRes) }
.toPersistentMap(),
@@ -141,7 +141,7 @@ object SettingsTrackingScreen : SearchableSettings {
),
// SY -->
Preference.PreferenceItem.SwitchPreference(
preference = trackPreferences.resolveUsingSourceMetadata(),
preference = trackPreferences.resolveUsingSourceMetadata,
title = stringResource(SYMR.strings.pref_tracker_resolve_using_source_metadata),
subtitle = stringResource(SYMR.strings.pref_tracker_resolve_using_source_metadata_summary),
),
@@ -297,7 +297,7 @@ object AboutScreen : Screen() {
)
.toDateTimestampString(
UiPreferences.dateFormat(
Injekt.get<UiPreferences>().dateFormat().get(),
Injekt.get<UiPreferences>().dateFormat.get(),
),
)
} catch (e: Exception) {
@@ -9,18 +9,12 @@ import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.util.storage.DiskUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource
@@ -51,24 +45,10 @@ private fun StorageInfo(
) {
val context = LocalContext.current
var available by remember(file) { mutableStateOf(-1L) }
var total by remember(file) { mutableStateOf(-1L) }
LaunchedEffect(file) {
available = withContext(Dispatchers.IO) { DiskUtil.getAvailableStorageSpace(file) }
total = withContext(Dispatchers.IO) { DiskUtil.getTotalStorageSpace(file) }
}
val availableText = if (available == -1L) {
stringResource(MR.strings.calculating)
} else {
Formatter.formatFileSize(context, available)
}
val totalText = if (total == -1L) {
stringResource(MR.strings.calculating)
} else {
Formatter.formatFileSize(context, total)
}
val available = remember(file) { DiskUtil.getAvailableStorageSpace(file) }
val availableText = remember(available) { Formatter.formatFileSize(context, available) }
val total = remember(file) { DiskUtil.getTotalStorageSpace(file) }
val totalText = remember(total) { Formatter.formatFileSize(context, total) }
Column(
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
@@ -78,15 +58,13 @@ private fun StorageInfo(
style = MaterialTheme.typography.header,
)
if (total > 0) {
LinearProgressIndicator(
modifier = Modifier
.clip(MaterialTheme.shapes.small)
.fillMaxWidth()
.height(12.dp),
progress = { (1 - (available / total.toFloat())) },
)
}
LinearProgressIndicator(
modifier = Modifier
.clip(MaterialTheme.shapes.small)
.fillMaxWidth()
.height(12.dp),
progress = { (1 - (available / total.toFloat())) },
)
Text(
text = stringResource(MR.strings.available_disk_space_info, availableText, totalText),
@@ -64,7 +64,7 @@ class DebugInfoScreen : Screen() {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val installationIdPref = remember { Injekt.get<BasePreferences>().installationId() }
val installationIdPref = remember { Injekt.get<BasePreferences>().installationId }
val installationId by installationIdPref.collectAsState()
return Preference.PreferenceGroup(
@@ -156,7 +156,7 @@ class WorkerInfoScreen : Screen() {
)
.toDateTimestampString(
UiPreferences.dateFormat(
Injekt.get<UiPreferences>().dateFormat().get(),
Injekt.get<UiPreferences>().dateFormat.get(),
),
)
appendLine("Next scheduled run: $timestamp")
@@ -24,10 +24,10 @@ class DisplayRefreshHost {
internal var currentDisplayRefresh by mutableStateOf(false)
private val readerPreferences = Injekt.get<ReaderPreferences>()
internal val flashMillis = readerPreferences.flashDurationMillis()
internal val flashMode = readerPreferences.flashColor()
internal val flashMillis = readerPreferences.flashDurationMillis
internal val flashMode = readerPreferences.flashColor
internal val flashIntervalPref = readerPreferences.flashPageInterval()
internal val flashIntervalPref = readerPreferences.flashPageInterval
// Internal State for Flash
private var flashInterval = flashIntervalPref.get()
@@ -22,10 +22,10 @@ import tachiyomi.presentation.core.util.collectAsState
@Composable
internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel) {
val customBrightness by screenModel.preferences.customBrightness().collectAsState()
val customBrightness by screenModel.preferences.customBrightness.collectAsState()
CheckboxItem(
label = stringResource(MR.strings.pref_custom_brightness),
pref = screenModel.preferences.customBrightness(),
pref = screenModel.preferences.customBrightness,
)
/*
@@ -35,31 +35,31 @@ internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel)
* 0 sets system brightness and hides the overlay.
*/
if (customBrightness) {
val customBrightnessValue by screenModel.preferences.customBrightnessValue().collectAsState()
val customBrightnessValue by screenModel.preferences.customBrightnessValue.collectAsState()
SliderItem(
value = customBrightnessValue,
valueRange = -75..100,
steps = 0,
label = stringResource(MR.strings.pref_custom_brightness),
onChange = { screenModel.preferences.customBrightnessValue().set(it) },
onChange = { screenModel.preferences.customBrightnessValue.set(it) },
pillColor = MaterialTheme.colorScheme.surfaceContainerHighest,
)
}
val colorFilter by screenModel.preferences.colorFilter().collectAsState()
val colorFilter by screenModel.preferences.colorFilter.collectAsState()
CheckboxItem(
label = stringResource(MR.strings.pref_custom_color_filter),
pref = screenModel.preferences.colorFilter(),
pref = screenModel.preferences.colorFilter,
)
if (colorFilter) {
val colorFilterValue by screenModel.preferences.colorFilterValue().collectAsState()
val colorFilterValue by screenModel.preferences.colorFilterValue.collectAsState()
SliderItem(
value = colorFilterValue.red,
valueRange = 0..255,
steps = 0,
label = stringResource(MR.strings.color_filter_r_value),
onChange = { newRValue ->
screenModel.preferences.colorFilterValue().getAndSet {
screenModel.preferences.colorFilterValue.getAndSet {
getColorValue(it, newRValue, RED_MASK, 16)
}
},
@@ -71,7 +71,7 @@ internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel)
steps = 0,
label = stringResource(MR.strings.color_filter_g_value),
onChange = { newGValue ->
screenModel.preferences.colorFilterValue().getAndSet {
screenModel.preferences.colorFilterValue.getAndSet {
getColorValue(it, newGValue, GREEN_MASK, 8)
}
},
@@ -83,7 +83,7 @@ internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel)
steps = 0,
label = stringResource(MR.strings.color_filter_b_value),
onChange = { newBValue ->
screenModel.preferences.colorFilterValue().getAndSet {
screenModel.preferences.colorFilterValue.getAndSet {
getColorValue(it, newBValue, BLUE_MASK, 0)
}
},
@@ -95,19 +95,19 @@ internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel)
steps = 0,
label = stringResource(MR.strings.color_filter_a_value),
onChange = { newAValue ->
screenModel.preferences.colorFilterValue().getAndSet {
screenModel.preferences.colorFilterValue.getAndSet {
getColorValue(it, newAValue, ALPHA_MASK, 24)
}
},
pillColor = MaterialTheme.colorScheme.surfaceContainerHighest,
)
val colorFilterMode by screenModel.preferences.colorFilterMode().collectAsState()
val colorFilterMode by screenModel.preferences.colorFilterMode.collectAsState()
SettingsChipRow(MR.strings.pref_color_filter_mode) {
ColorFilterMode.mapIndexed { index, it ->
FilterChip(
selected = colorFilterMode == index,
onClick = { screenModel.preferences.colorFilterMode().set(index) },
onClick = { screenModel.preferences.colorFilterMode.set(index) },
label = { Text(stringResource(it.first)) },
)
}
@@ -116,11 +116,11 @@ internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel)
CheckboxItem(
label = stringResource(MR.strings.pref_grayscale),
pref = screenModel.preferences.grayscale(),
pref = screenModel.preferences.grayscale,
)
CheckboxItem(
label = stringResource(MR.strings.pref_inverted_colors),
pref = screenModel.preferences.invertedColors(),
pref = screenModel.preferences.invertedColors,
)
}
@@ -34,24 +34,24 @@ private val flashColors = listOf(
@Composable
internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
val readerTheme by screenModel.preferences.readerTheme().collectAsState()
val readerTheme by screenModel.preferences.readerTheme.collectAsState()
val flashPageState by screenModel.preferences.flashOnPageChange().collectAsState()
val flashPageState by screenModel.preferences.flashOnPageChange.collectAsState()
val flashMillisPref = screenModel.preferences.flashDurationMillis()
val flashMillisPref = screenModel.preferences.flashDurationMillis
val flashMillis by flashMillisPref.collectAsState()
val flashIntervalPref = screenModel.preferences.flashPageInterval()
val flashIntervalPref = screenModel.preferences.flashPageInterval
val flashInterval by flashIntervalPref.collectAsState()
val flashColorPref = screenModel.preferences.flashColor()
val flashColorPref = screenModel.preferences.flashColor
val flashColor by flashColorPref.collectAsState()
SettingsChipRow(MR.strings.pref_reader_theme) {
themes.map { (labelRes, value) ->
FilterChip(
selected = readerTheme == value,
onClick = { screenModel.preferences.readerTheme().set(value) },
onClick = { screenModel.preferences.readerTheme.set(value) },
label = { Text(stringResource(labelRes)) },
)
}
@@ -59,66 +59,66 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
CheckboxItem(
label = stringResource(MR.strings.pref_show_page_number),
pref = screenModel.preferences.showPageNumber(),
pref = screenModel.preferences.showPageNumber,
)
// SY -->
val forceHorizontalSeekbar by screenModel.preferences.forceHorizontalSeekbar().collectAsState()
val forceHorizontalSeekbar by screenModel.preferences.forceHorizontalSeekbar.collectAsState()
CheckboxItem(
label = stringResource(SYMR.strings.pref_force_horz_seekbar),
pref = screenModel.preferences.forceHorizontalSeekbar(),
pref = screenModel.preferences.forceHorizontalSeekbar,
)
if (!forceHorizontalSeekbar) {
CheckboxItem(
label = stringResource(SYMR.strings.pref_show_vert_seekbar_landscape),
pref = screenModel.preferences.landscapeVerticalSeekbar(),
pref = screenModel.preferences.landscapeVerticalSeekbar,
)
CheckboxItem(
label = stringResource(SYMR.strings.pref_left_handed_vertical_seekbar),
pref = screenModel.preferences.leftVerticalSeekbar(),
pref = screenModel.preferences.leftVerticalSeekbar,
)
}
// SY <--
CheckboxItem(
label = stringResource(MR.strings.pref_fullscreen),
pref = screenModel.preferences.fullscreen(),
pref = screenModel.preferences.fullscreen,
)
val isFullscreen by screenModel.preferences.fullscreen().collectAsState()
val isFullscreen by screenModel.preferences.fullscreen.collectAsState()
if (LocalActivity.current?.hasDisplayCutout() == true && isFullscreen) {
CheckboxItem(
label = stringResource(MR.strings.pref_cutout_short),
pref = screenModel.preferences.drawUnderCutout(),
pref = screenModel.preferences.drawUnderCutout,
)
}
CheckboxItem(
label = stringResource(MR.strings.pref_keep_screen_on),
pref = screenModel.preferences.keepScreenOn(),
pref = screenModel.preferences.keepScreenOn,
)
CheckboxItem(
label = stringResource(MR.strings.pref_read_with_long_tap),
pref = screenModel.preferences.readWithLongTap(),
pref = screenModel.preferences.readWithLongTap,
)
CheckboxItem(
label = stringResource(MR.strings.pref_always_show_chapter_transition),
pref = screenModel.preferences.alwaysShowChapterTransition(),
pref = screenModel.preferences.alwaysShowChapterTransition,
)
// SY -->
/*CheckboxItem(
label = stringResource(MR.strings.pref_page_transitions),
pref = screenModel.preferences.pageTransitions(),
pref = screenModel.preferences.pageTransitions,
) SY <-- */
CheckboxItem(
label = stringResource(MR.strings.pref_flash_page),
pref = screenModel.preferences.flashOnPageChange(),
pref = screenModel.preferences.flashOnPageChange,
)
if (flashPageState) {
SliderItem(
@@ -153,7 +153,7 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
// SY -->
CheckboxItem(
label = stringResource(SYMR.strings.auto_webtoon_mode),
pref = screenModel.preferences.useAutoWebtoon(),
pref = screenModel.preferences.useAutoWebtoon,
)
// SY <--
}
@@ -67,44 +67,44 @@ internal fun ColumnScope.ReadingModePage(screenModel: ReaderSettingsScreenModel)
private fun ColumnScope.PagerViewerSettings(screenModel: ReaderSettingsScreenModel) {
HeadingItem(MR.strings.pager_viewer)
val navigationModePager by screenModel.preferences.navigationModePager().collectAsState()
val pagerNavInverted by screenModel.preferences.pagerNavInverted().collectAsState()
val navigationModePager by screenModel.preferences.navigationModePager.collectAsState()
val pagerNavInverted by screenModel.preferences.pagerNavInverted.collectAsState()
TapZonesItems(
selected = navigationModePager,
onSelect = screenModel.preferences.navigationModePager()::set,
onSelect = screenModel.preferences.navigationModePager::set,
invertMode = pagerNavInverted,
onSelectInvertMode = screenModel.preferences.pagerNavInverted()::set,
onSelectInvertMode = screenModel.preferences.pagerNavInverted::set,
)
val imageScaleType by screenModel.preferences.imageScaleType().collectAsState()
val imageScaleType by screenModel.preferences.imageScaleType.collectAsState()
SettingsChipRow(MR.strings.pref_image_scale_type) {
ReaderPreferences.ImageScaleType.mapIndexed { index, it ->
FilterChip(
selected = imageScaleType == index + 1,
onClick = { screenModel.preferences.imageScaleType().set(index + 1) },
onClick = { screenModel.preferences.imageScaleType.set(index + 1) },
label = { Text(stringResource(it)) },
)
}
}
val zoomStart by screenModel.preferences.zoomStart().collectAsState()
val zoomStart by screenModel.preferences.zoomStart.collectAsState()
SettingsChipRow(MR.strings.pref_zoom_start) {
ReaderPreferences.ZoomStart.mapIndexed { index, it ->
FilterChip(
selected = zoomStart == index + 1,
onClick = { screenModel.preferences.zoomStart().set(index + 1) },
onClick = { screenModel.preferences.zoomStart.set(index + 1) },
label = { Text(stringResource(it)) },
)
}
}
// SY -->
val pageLayout by screenModel.preferences.pageLayout().collectAsState()
val pageLayout by screenModel.preferences.pageLayout.collectAsState()
SettingsChipRow(SYMR.strings.page_layout) {
ReaderPreferences.PageLayouts.mapIndexed { index, it ->
FilterChip(
selected = pageLayout == index,
onClick = { screenModel.preferences.pageLayout().set(index) },
onClick = { screenModel.preferences.pageLayout.set(index) },
label = { Text(stringResource(it)) },
)
}
@@ -113,62 +113,62 @@ private fun ColumnScope.PagerViewerSettings(screenModel: ReaderSettingsScreenMod
CheckboxItem(
label = stringResource(MR.strings.pref_crop_borders),
pref = screenModel.preferences.cropBorders(),
pref = screenModel.preferences.cropBorders,
)
CheckboxItem(
label = stringResource(MR.strings.pref_landscape_zoom),
pref = screenModel.preferences.landscapeZoom(),
pref = screenModel.preferences.landscapeZoom,
)
CheckboxItem(
label = stringResource(MR.strings.pref_navigate_pan),
pref = screenModel.preferences.navigateToPan(),
pref = screenModel.preferences.navigateToPan,
)
val dualPageSplitPaged by screenModel.preferences.dualPageSplitPaged().collectAsState()
val dualPageSplitPaged by screenModel.preferences.dualPageSplitPaged.collectAsState()
CheckboxItem(
label = stringResource(MR.strings.pref_dual_page_split),
pref = screenModel.preferences.dualPageSplitPaged(),
pref = screenModel.preferences.dualPageSplitPaged,
)
if (dualPageSplitPaged) {
CheckboxItem(
label = stringResource(MR.strings.pref_dual_page_invert),
pref = screenModel.preferences.dualPageInvertPaged(),
pref = screenModel.preferences.dualPageInvertPaged,
)
}
val dualPageRotateToFit by screenModel.preferences.dualPageRotateToFit().collectAsState()
val dualPageRotateToFit by screenModel.preferences.dualPageRotateToFit.collectAsState()
CheckboxItem(
label = stringResource(MR.strings.pref_page_rotate),
pref = screenModel.preferences.dualPageRotateToFit(),
pref = screenModel.preferences.dualPageRotateToFit,
)
if (dualPageRotateToFit) {
CheckboxItem(
label = stringResource(MR.strings.pref_page_rotate_invert),
pref = screenModel.preferences.dualPageRotateToFitInvert(),
pref = screenModel.preferences.dualPageRotateToFitInvert,
)
}
// SY -->
CheckboxItem(
label = stringResource(MR.strings.pref_page_transitions),
pref = screenModel.preferences.pageTransitionsPager(),
pref = screenModel.preferences.pageTransitionsPager,
)
CheckboxItem(
label = stringResource(SYMR.strings.invert_double_pages),
pref = screenModel.preferences.invertDoublePages(),
pref = screenModel.preferences.invertDoublePages,
)
val centerMarginType by screenModel.preferences.centerMarginType().collectAsState()
val centerMarginType by screenModel.preferences.centerMarginType.collectAsState()
SettingsChipRow(SYMR.strings.pref_center_margin) {
ReaderPreferences.CenterMarginTypes.mapIndexed { index, it ->
FilterChip(
selected = centerMarginType == index,
onClick = { screenModel.preferences.centerMarginType().set(index) },
onClick = { screenModel.preferences.centerMarginType.set(index) },
label = { Text(stringResource(it)) },
)
}
@@ -182,77 +182,77 @@ private fun ColumnScope.WebtoonViewerSettings(screenModel: ReaderSettingsScreenM
HeadingItem(MR.strings.webtoon_viewer)
val navigationModeWebtoon by screenModel.preferences.navigationModeWebtoon().collectAsState()
val webtoonNavInverted by screenModel.preferences.webtoonNavInverted().collectAsState()
val navigationModeWebtoon by screenModel.preferences.navigationModeWebtoon.collectAsState()
val webtoonNavInverted by screenModel.preferences.webtoonNavInverted.collectAsState()
TapZonesItems(
selected = navigationModeWebtoon,
onSelect = screenModel.preferences.navigationModeWebtoon()::set,
onSelect = screenModel.preferences.navigationModeWebtoon::set,
invertMode = webtoonNavInverted,
onSelectInvertMode = screenModel.preferences.webtoonNavInverted()::set,
onSelectInvertMode = screenModel.preferences.webtoonNavInverted::set,
)
val webtoonSidePadding by screenModel.preferences.webtoonSidePadding().collectAsState()
val webtoonSidePadding by screenModel.preferences.webtoonSidePadding.collectAsState()
SliderItem(
value = webtoonSidePadding,
valueRange = ReaderPreferences.let { it.WEBTOON_PADDING_MIN..it.WEBTOON_PADDING_MAX },
label = stringResource(MR.strings.pref_webtoon_side_padding),
valueString = numberFormat.format(webtoonSidePadding / 100f),
onChange = {
screenModel.preferences.webtoonSidePadding().set(it)
screenModel.preferences.webtoonSidePadding.set(it)
},
pillColor = MaterialTheme.colorScheme.surfaceContainerHighest,
)
CheckboxItem(
label = stringResource(MR.strings.pref_crop_borders),
pref = screenModel.preferences.cropBordersWebtoon(),
pref = screenModel.preferences.cropBordersWebtoon,
)
// SY -->
CheckboxItem(
label = stringResource(SYMR.strings.pref_smooth_scroll),
pref = screenModel.preferences.smoothAutoScroll(),
pref = screenModel.preferences.smoothAutoScroll,
)
CheckboxItem(
label = stringResource(MR.strings.pref_page_transitions),
pref = screenModel.preferences.pageTransitionsWebtoon(),
pref = screenModel.preferences.pageTransitionsWebtoon,
)
// SY <--
val dualPageSplitWebtoon by screenModel.preferences.dualPageSplitWebtoon().collectAsState()
val dualPageSplitWebtoon by screenModel.preferences.dualPageSplitWebtoon.collectAsState()
CheckboxItem(
label = stringResource(MR.strings.pref_dual_page_split),
pref = screenModel.preferences.dualPageSplitWebtoon(),
pref = screenModel.preferences.dualPageSplitWebtoon,
)
if (dualPageSplitWebtoon) {
CheckboxItem(
label = stringResource(MR.strings.pref_dual_page_invert),
pref = screenModel.preferences.dualPageInvertWebtoon(),
pref = screenModel.preferences.dualPageInvertWebtoon,
)
}
val dualPageRotateToFitWebtoon by screenModel.preferences.dualPageRotateToFitWebtoon().collectAsState()
val dualPageRotateToFitWebtoon by screenModel.preferences.dualPageRotateToFitWebtoon.collectAsState()
CheckboxItem(
label = stringResource(MR.strings.pref_page_rotate),
pref = screenModel.preferences.dualPageRotateToFitWebtoon(),
pref = screenModel.preferences.dualPageRotateToFitWebtoon,
)
if (dualPageRotateToFitWebtoon) {
CheckboxItem(
label = stringResource(MR.strings.pref_page_rotate_invert),
pref = screenModel.preferences.dualPageRotateToFitInvertWebtoon(),
pref = screenModel.preferences.dualPageRotateToFitInvertWebtoon,
)
}
CheckboxItem(
label = stringResource(MR.strings.pref_double_tap_zoom),
pref = screenModel.preferences.webtoonDoubleTapZoomEnabled(),
pref = screenModel.preferences.webtoonDoubleTapZoomEnabled,
)
CheckboxItem(
label = stringResource(MR.strings.pref_webtoon_disable_zoom_out),
pref = screenModel.preferences.webtoonDisableZoomOut(),
pref = screenModel.preferences.webtoonDisableZoomOut,
)
}
@@ -263,7 +263,7 @@ private fun ColumnScope.WebtoonWithGapsViewerSettings(screenModel: ReaderSetting
CheckboxItem(
label = stringResource(MR.strings.pref_crop_borders),
pref = screenModel.preferences.cropBordersContinuousVertical(),
pref = screenModel.preferences.cropBordersContinuousVertical,
)
}
// SY <--
@@ -35,8 +35,8 @@ fun TachiyomiTheme(
) {
val uiPreferences = Injekt.get<UiPreferences>()
BaseTachiyomiTheme(
appTheme = appTheme ?: uiPreferences.appTheme().get(),
isAmoled = amoled ?: uiPreferences.themeDarkAmoled().get(),
appTheme = appTheme ?: uiPreferences.appTheme.get(),
isAmoled = amoled ?: uiPreferences.themeDarkAmoled.get(),
content = content,
)
}
@@ -55,28 +55,28 @@ fun UpdatesFilterDialog(
private fun ColumnScope.FilterSheet(
screenModel: UpdatesSettingsScreenModel,
) {
val filterDownloaded by screenModel.updatesPreferences.filterDownloaded().collectAsState()
val filterDownloaded by screenModel.updatesPreferences.filterDownloaded.collectAsState()
TriStateItem(
label = stringResource(MR.strings.label_downloaded),
state = filterDownloaded,
onClick = { screenModel.toggleFilter(UpdatesPreferences::filterDownloaded) },
)
val filterUnread by screenModel.updatesPreferences.filterUnread().collectAsState()
val filterUnread by screenModel.updatesPreferences.filterUnread.collectAsState()
TriStateItem(
label = stringResource(MR.strings.action_filter_unread),
state = filterUnread,
onClick = { screenModel.toggleFilter(UpdatesPreferences::filterUnread) },
)
val filterStarted by screenModel.updatesPreferences.filterStarted().collectAsState()
val filterStarted by screenModel.updatesPreferences.filterStarted.collectAsState()
TriStateItem(
label = stringResource(MR.strings.label_started),
state = filterStarted,
onClick = { screenModel.toggleFilter(UpdatesPreferences::filterStarted) },
)
val filterBookmarked by screenModel.updatesPreferences.filterBookmarked().collectAsState()
val filterBookmarked by screenModel.updatesPreferences.filterBookmarked.collectAsState()
TriStateItem(
label = stringResource(MR.strings.action_filter_bookmarked),
state = filterBookmarked,
@@ -85,9 +85,9 @@ private fun ColumnScope.FilterSheet(
HorizontalDivider(modifier = Modifier.padding(MaterialTheme.padding.small))
val filterExcludedScanlators by screenModel.updatesPreferences.filterExcludedScanlators().collectAsState()
val filterExcludedScanlators by screenModel.updatesPreferences.filterExcludedScanlators.collectAsState()
fun toggleScanlatorFilter() = screenModel.updatesPreferences.filterExcludedScanlators().getAndSet { !it }
fun toggleScanlatorFilter() = screenModel.updatesPreferences.filterExcludedScanlators.getAndSet { !it }
Row(
modifier = Modifier
@@ -3,6 +3,8 @@ package eu.kanade.presentation.webview
import android.content.pm.ApplicationInfo
import android.graphics.Bitmap
import android.os.Message
import android.webkit.JsPromptResult
import android.webkit.JsResult
import android.webkit.WebResourceRequest
import android.webkit.WebView
import androidx.activity.compose.BackHandler
@@ -20,6 +22,7 @@ import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
@@ -95,6 +98,11 @@ fun WebViewScreenContent(
var currentUrl by remember { mutableStateOf(url) }
var showCloudflareHelp by remember { mutableStateOf(false) }
var isActive by remember { mutableStateOf(true) }
DisposableEffect(Unit) {
onDispose { isActive = false }
}
val webClient = remember {
object : AccompanistWebViewClient() {
@@ -163,6 +171,36 @@ fun WebViewScreenContent(
}
return false
}
override fun onJsAlert(view: WebView, url: String?, message: String?, result: JsResult): Boolean {
if (!isActive) {
result.confirm()
return true
}
return super.onJsAlert(view, url, message, result)
}
override fun onJsConfirm(view: WebView, url: String?, message: String?, result: JsResult): Boolean {
if (!isActive) {
result.cancel()
return true
}
return super.onJsConfirm(view, url, message, result)
}
override fun onJsPrompt(
view: WebView,
url: String?,
message: String?,
defaultValue: String?,
result: JsPromptResult,
): Boolean {
if (!isActive) {
result.cancel()
return true
}
return super.onJsPrompt(view, url, message, defaultValue, result)
}
}
}
+8 -8
View File
@@ -141,7 +141,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
val scope = ProcessLifecycleOwner.get().lifecycleScope
// Show notification to disable Incognito Mode when it's enabled
basePreferences.incognitoMode().changes()
basePreferences.incognitoMode.changes()
.onEach { enabled ->
if (enabled) {
disableIncognitoReceiver.register()
@@ -169,25 +169,25 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
}
.launchIn(scope)
privacyPreferences.analytics()
privacyPreferences.analytics
.changes()
.onEach(FirebaseConfig::setAnalyticsEnabled)
.launchIn(scope)
privacyPreferences.crashlytics()
privacyPreferences.crashlytics
.changes()
.onEach(FirebaseConfig::setCrashlyticsEnabled)
.launchIn(scope)
basePreferences.hardwareBitmapThreshold().let { preference ->
basePreferences.hardwareBitmapThreshold.let { preference ->
if (!preference.isSet()) preference.set(GLUtil.DEVICE_TEXTURE_LIMIT)
}
basePreferences.hardwareBitmapThreshold().changes()
basePreferences.hardwareBitmapThreshold.changes()
.onEach { ImageUtil.hardwareBitmapThreshold = it }
.launchIn(scope)
setAppCompatDelegateThemeMode(Injekt.get<UiPreferences>().themeMode().get())
setAppCompatDelegateThemeMode(Injekt.get<UiPreferences>().themeMode.get())
// Updates widget update
WidgetManager(Injekt.get(), Injekt.get()).apply { init(scope) }
@@ -256,7 +256,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
crossfade((300 * this@App.animatorDurationScale).toInt())
allowRgb565(DeviceUtil.isLowRamDevice(this@App))
if (networkPreferences.verboseLogging().get()) logger(DebugLogger())
if (networkPreferences.verboseLogging.get()) logger(DebugLogger())
// Coil spawns a new thread for every image load by default
fetcherCoroutineContext(Dispatchers.IO.limitedParallelism(8))
@@ -376,7 +376,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
private var registered = false
override fun onReceive(context: Context, intent: Intent) {
basePreferences.incognitoMode().set(false)
basePreferences.incognitoMode.set(false)
}
fun register() {
@@ -101,7 +101,7 @@ class BackupNotifier(private val context: Context) {
}
setContentTitle(contentTitle)
if (!preferences.hideNotificationContent().get()) {
if (!preferences.hideNotificationContent.get()) {
setContentText(content)
}
@@ -89,7 +89,7 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
fun setupTask(context: Context, prefInterval: Int? = null) {
val backupPreferences = Injekt.get<BackupPreferences>()
val interval = prefInterval ?: backupPreferences.backupInterval().get()
val interval = prefInterval ?: backupPreferences.backupInterval.get()
if (interval > 0) {
val constraints = Constraints(
requiresBatteryNotLow = true,
@@ -124,7 +124,7 @@ class BackupCreator(
BackupFileValidator(context).validate(fileUri)
if (isAutoBackup) {
backupPreferences.lastAutoBackupTimestamp().set(Instant.now().toEpochMilli())
backupPreferences.lastAutoBackupTimestamp.set(Instant.now().toEpochMilli())
}
return fileUri.toString()
@@ -75,7 +75,7 @@ class CategoriesRestorer(
}
// SY <--
libraryPreferences.categorizedDisplaySettings().set(
libraryPreferences.categorizedDisplaySettings.set(
(dbCategories + categories)
.distinctBy { it.flags }
.size > 1,
@@ -13,7 +13,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import logcat.LogPriority
import okhttp3.Response
@@ -44,10 +43,10 @@ class ChapterCache(
private val scope = CoroutineScope(Job() + Dispatchers.Main)
/** Cache class used for cache management. */
private var diskCache = setupDiskCache(readerPreferences.cacheSize().get().toLong())
private var diskCache = setupDiskCache(readerPreferences.cacheSize.get().toLong())
init {
readerPreferences.cacheSize().changes()
readerPreferences.cacheSize.changes()
.drop(1)
.onEach {
// Save old cache for destruction later
@@ -64,13 +63,17 @@ class ChapterCache(
*/
private val cacheDir: File = diskCache.directory
/**
* Returns real size of directory.
*/
private val realSize: Long
get() = DiskUtil.getDirectorySize(cacheDir)
/**
* Returns real size of directory in human readable format.
*/
suspend fun getReadableSize(): String = withContext(Dispatchers.IO) {
val size = DiskUtil.getDirectorySize(cacheDir)
Formatter.formatFileSize(context, size)
}
val readableSize: String
get() = Formatter.formatFileSize(context, realSize)
// --> EH
// Cache size is in MB
@@ -12,17 +12,14 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.receiveAsFlow
@@ -112,19 +109,13 @@ class DownloadCache(
ProtoBuf.decodeFromByteArray<RootDirectory>(it.readBytes())
}
rootDownloadsDir = diskCache
lastRenew = System.currentTimeMillis()
}
} catch (e: Throwable) {
logcat(LogPriority.ERROR, e) { "Failed to initialize from disk cache" }
diskCacheFile.delete()
}
}
sourceManager.catalogueSources
.map { sources -> sources.map { it.id }.toSet() }
.distinctUntilChanged()
.collect {
restartRenewal()
}
}
storageManager.changes
@@ -362,34 +353,19 @@ class DownloadCache(
notifyChanges()
}
suspend fun invalidateCache() {
renewalJob?.cancelAndJoin()
diskCacheFile.delete()
fun invalidateCache() {
lastRenew = 0L
renewCache(forceRenew = true)
}
/**
* Safely cancels any in-progress renewal job, resets the last-renew timestamp, and
* immediately starts a new renewal, bypassing the time-based throttle.
*/
private fun restartRenewal() {
renewalJob?.cancel()
lastRenew = 0L
renewCache(forceRenew = true)
diskCacheFile.delete()
renewCache()
}
/**
* Renews the downloads cache.
*
* @param forceRenew when `true`, the time-based throttle is bypassed. Use this after
* explicitly cancelling the previous job to avoid a race where the cancelled job's
* [invokeOnCompletion] handler sets [lastRenew] after the reset but before the new
* job's guard check.
*/
private fun renewCache(forceRenew: Boolean = false) {
private fun renewCache() {
// Avoid renewing cache if in the process nor too often
if ((!forceRenew && lastRenew + renewInterval >= System.currentTimeMillis()) || renewalJob?.isActive == true) {
if (lastRenew + renewInterval >= System.currentTimeMillis() || renewalJob?.isActive == true) {
return
}
@@ -400,14 +376,15 @@ class DownloadCache(
// Try to wait until extensions and sources have loaded
// SY -->
var sources = emptyList<Source>()
withTimeoutOrNull(30.seconds) {
// SY <--
sourceManager.catalogueSources.first { it.isNotEmpty() }
// SY -->
extensionManager.isInitialized.first { it }
sourceManager.isInitialized.first { it }
sources = getSources()
}
// SY <--
val sources = getSources()
val sourceMap = sources.associate { provider.getSourceDirName(it).lowercase() to it.id }
rootDownloadsDirMutex.withLock {
@@ -482,9 +459,8 @@ class DownloadCache(
private var updateDiskCacheJob: Job? = null
private fun updateDiskCache() {
val previousJob = updateDiskCacheJob
updateDiskCacheJob?.cancel()
updateDiskCacheJob = scope.launchIO {
previousJob?.cancelAndJoin()
delay(1000)
ensureActive()
val bytes = ProtoBuf.encodeToByteArray(rootDownloadsDir)
@@ -56,7 +56,7 @@ class DownloadJob(context: Context, workerParams: WorkerParameters) : CoroutineW
override suspend fun doWork(): Result {
var networkCheck = checkNetworkState(
applicationContext.activeNetworkState(),
downloadPreferences.downloadOnlyOverWifi().get(),
downloadPreferences.downloadOnlyOverWifi.get(),
)
var active = networkCheck && downloadManager.downloaderStart()
@@ -69,7 +69,7 @@ class DownloadJob(context: Context, workerParams: WorkerParameters) : CoroutineW
coroutineScope {
combineTransform(
applicationContext.networkStateFlow(),
downloadPreferences.downloadOnlyOverWifi().changes(),
downloadPreferences.downloadOnlyOverWifi.changes(),
transform = { a, b -> emit(checkNetworkState(a, b)) },
)
.onEach { networkCheck = it }
@@ -109,10 +109,10 @@ class DownloadManager(
return queueState.value.find { it.chapter.id == chapterId }
}
suspend fun startDownloadNow(chapterId: Long) {
fun startDownloadNow(chapterId: Long) {
val existingDownload = getQueuedDownloadOrNull(chapterId)
// If not in queue try to start a new download
val toAdd = existingDownload ?: Download.fromChapterId(chapterId) ?: return
val toAdd = existingDownload ?: runBlocking { Download.fromChapterId(chapterId) } ?: return
queueState.value.toMutableList().apply {
existingDownload?.let { remove(it) }
add(0, toAdd)
@@ -470,7 +470,7 @@ class DownloadManager(
private suspend fun getChaptersToDelete(chapters: List<Chapter>, manga: Manga): List<Chapter> {
// Retrieve the categories that are set to exclude from being deleted on read
val categoriesToExclude = downloadPreferences.removeExcludeCategories().get().map(String::toLong)
val categoriesToExclude = downloadPreferences.removeExcludeCategories.get().map(String::toLong)
val categoriesForManga = getCategories.await(manga.id)
.map { it.id }
@@ -481,7 +481,7 @@ class DownloadManager(
chapters
}
return if (!downloadPreferences.removeBookmarkedChapters().get()) {
return if (!downloadPreferences.removeBookmarkedChapters.get()) {
filteredCategoryManga.filterNot { it.bookmark }
} else {
filteredCategoryManga
@@ -96,7 +96,7 @@ internal class DownloadNotifier(private val context: Context) {
download.pages!!.size,
)
if (preferences.hideNotificationContent().get()) {
if (preferences.hideNotificationContent.get()) {
setContentTitle(downloadingProgressText)
setContentText(null)
} else {
@@ -164,7 +164,7 @@ class DownloadProvider(
fun getSourceDirName(source: Source): String {
return DiskUtil.buildValidFilename(
source.toString(),
disallowNonAscii = libraryPreferences.disallowNonAsciiFilenames().get(),
disallowNonAscii = libraryPreferences.disallowNonAsciiFilenames.get(),
)
}
@@ -176,7 +176,7 @@ class DownloadProvider(
fun getMangaDirName(mangaTitle: String): String {
return DiskUtil.buildValidFilename(
mangaTitle,
disallowNonAscii = libraryPreferences.disallowNonAsciiFilenames().get(),
disallowNonAscii = libraryPreferences.disallowNonAsciiFilenames.get(),
)
}
@@ -191,8 +191,8 @@ class DownloadProvider(
chapterName: String,
chapterScanlator: String?,
chapterUrl: String,
disallowNonAsciiFilenames: Boolean = libraryPreferences.disallowNonAsciiFilenames().get(),
includeChapterUrlHash: Boolean = downloadPreferences.includeChapterUrlHash().get(),
disallowNonAsciiFilenames: Boolean = libraryPreferences.disallowNonAsciiFilenames.get(),
includeChapterUrlHash: Boolean = downloadPreferences.includeChapterUrlHash.get(),
): String {
var dirName = sanitizeChapterName(chapterName)
if (!chapterScanlator.isNullOrBlank()) {
@@ -235,8 +235,8 @@ class DownloadProvider(
chapterName,
chapterScanlator,
chapterUrl,
!libraryPreferences.disallowNonAsciiFilenames().get(),
!downloadPreferences.includeChapterUrlHash().get(),
!libraryPreferences.disallowNonAsciiFilenames.get(),
!downloadPreferences.includeChapterUrlHash.get(),
)
return buildList(2) {
@@ -89,7 +89,7 @@ class DownloadStore(
/**
* Returns the list of downloads to restore. It should be called in a background thread.
*/
suspend fun restore(): List<Download> {
fun restore(): List<Download> {
val objs = preferences.all
.mapNotNull { it.value as? String }
.mapNotNull { deserialize(it) }
@@ -100,10 +100,10 @@ class DownloadStore(
val cachedManga = mutableMapOf<Long, Manga?>()
for ((mangaId, chapterId) in objs) {
val manga = cachedManga.getOrPut(mangaId) {
getManga.await(mangaId)
runBlocking { getManga.await(mangaId) }
} ?: continue
val source = sourceManager.get(manga.source) as? HttpSource ?: continue
val chapter = getChapter.await(chapterId) ?: continue
val chapter = runBlocking { getChapter.await(chapterId) } ?: continue
downloads.add(Download(source, manga, chapter))
}
}
@@ -121,9 +121,9 @@ class Downloader(
var isPaused: Boolean = false
init {
scope.launch {
val chapters = store.restore()
addAllToQueue(chapters)
launchNow {
val chapters = async { store.restore() }
addAllToQueue(chapters.await())
}
}
@@ -203,7 +203,7 @@ class Downloader(
downloaderJob = scope.launch {
val activeDownloadsFlow = combine(
queueState,
downloadPreferences.parallelSourceLimit().changes(),
downloadPreferences.parallelSourceLimit.changes(),
) { a, b -> a to b }.transformLatest { (queue, parallelCount) ->
while (true) {
val activeDownloads = queue.asSequence()
@@ -380,7 +380,7 @@ class Downloader(
reIndexedPages
}
val dataSaver = if (sourcePreferences.dataSaverDownloader().get()) {
val dataSaver = if (sourcePreferences.dataSaverDownloader.get()) {
DataSaver(download.source, sourcePreferences)
} else {
DataSaver.NoOp
@@ -394,7 +394,7 @@ class Downloader(
download.status = Download.State.DOWNLOADING
// Start downloading images, consider we can have downloaded images already
pageList.asFlow().flatMapMerge(concurrency = downloadPreferences.parallelPageLimit().get()) { page ->
pageList.asFlow().flatMapMerge(concurrency = downloadPreferences.parallelPageLimit.get()) { page ->
flow {
// Fetch image URL if necessary
if (page.imageUrl.isNullOrEmpty()) {
@@ -431,7 +431,7 @@ class Downloader(
)
// Only rename the directory if it's downloaded
if (downloadPreferences.saveChaptersAsCBZ().get()) {
if (downloadPreferences.saveChaptersAsCBZ.get()) {
archiveChapter(mangaDir, chapterDirname, tmpDir)
} else {
tmpDir.renameTo(chapterDirname)
@@ -579,7 +579,7 @@ class Downloader(
}
private fun splitTallImageIfNeeded(page: Page, tmpDir: UniFile) {
if (!downloadPreferences.splitTallImages().get()) return
if (!downloadPreferences.splitTallImages.get()) return
try {
val filenamePrefix = "%03d".format(Locale.ENGLISH, page.number)
@@ -138,7 +138,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
if (tags.contains(WORK_NAME_AUTO)) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
val preferences = Injekt.get<LibraryPreferences>()
val restrictions = preferences.autoUpdateDeviceRestrictions().get()
val restrictions = preferences.autoUpdateDeviceRestrictions.get()
if ((DEVICE_ONLY_ON_WIFI in restrictions) && !context.isConnectedToWifi()) {
return Result.retry()
}
@@ -157,7 +157,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
// If this is a chapter update, set the last update time to now
if (target == Target.CHAPTERS) {
libraryPreferences.lastUpdatedTimestamp().set(Instant.now().toEpochMilli())
libraryPreferences.lastUpdatedTimestamp.set(Instant.now().toEpochMilli())
}
val categoryId = inputData.getLong(KEY_CATEGORY, -1L)
@@ -213,7 +213,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
private suspend fun addMangaToQueue(categoryId: Long, group: Int, groupExtra: String?) {
val libraryManga = getLibraryManga.await()
// SY -->
val groupLibraryUpdateType = libraryPreferences.groupLibraryUpdateType().get()
val groupLibraryUpdateType = libraryPreferences.groupLibraryUpdateType.get()
// SY <--
val listToUpdate = if (categoryId != -1L) {
@@ -225,8 +225,8 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
(groupLibraryUpdateType == GroupLibraryMode.ALL_BUT_UNGROUPED && group == LibraryGroup.UNGROUPED)
) {
// SY <--
val includedCategories = libraryPreferences.updateCategories().get().map { it.toLong() }.toSet()
val excludedCategories = libraryPreferences.updateCategoriesExclude().get().map { it.toLong() }.toSet()
val includedCategories = libraryPreferences.updateCategories.get().map { it.toLong() }.toSet()
val excludedCategories = libraryPreferences.updateCategoriesExclude.get().map { it.toLong() }.toSet()
libraryManga.filter {
val included = includedCategories.isEmpty() || it.categories.intersect(includedCategories).isNotEmpty()
@@ -271,7 +271,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
// SY <--
}
val restrictions = libraryPreferences.autoUpdateMangaRestrictions().get()
val restrictions = libraryPreferences.autoUpdateMangaRestrictions.get()
val skippedUpdates = mutableListOf<Pair<Manga, String?>>()
val (_, fetchWindowUpperBound) = fetchInterval.getWindow(ZonedDateTime.now())
@@ -406,7 +406,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
hasDownloads.store(true)
}
libraryPreferences.newUpdatesCount().getAndSet { it + newChapters.size }
libraryPreferences.newUpdatesCount.getAndSet { it + newChapters.size }
// Convert to the manga that contains new chapters
newUpdates.add(manga to newChapters.toTypedArray())
@@ -482,7 +482,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
val source = sourceManager.getOrStub(manga.source)
// Update manga metadata if needed
if (libraryPreferences.autoUpdateMetadata().get()) {
if (libraryPreferences.autoUpdateMetadata.get()) {
val networkManga = source.getMangaDetails(manga.toSManga())
updateManga.awaitUpdateFromSource(manga, networkManga, manualFetch = false, coverCache)
}
@@ -559,7 +559,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
var count = 0
val mangaDex = MdUtil.getEnabledMangaDex(preferences, sourceManager = sourceManager)
?: return@coroutineScope
val syncFollowStatusInts = preferences.mangadexSyncToLibraryIndexes().get().map { it.toInt() }
val syncFollowStatusInts = preferences.mangadexSyncToLibraryIndexes.get().map { it.toInt() }
val size: Int
mangaDex.fetchAllFollows()
@@ -740,9 +740,9 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
prefInterval: Int? = null,
) {
val preferences = Injekt.get<LibraryPreferences>()
val interval = prefInterval ?: preferences.autoUpdateInterval().get()
val interval = prefInterval ?: preferences.autoUpdateInterval.get()
if (interval > 0) {
val restrictions = preferences.autoUpdateDeviceRestrictions().get()
val restrictions = preferences.autoUpdateDeviceRestrictions.get()
val networkType = if (DEVICE_NETWORK_NOT_METERED in restrictions) {
NetworkType.UNMETERED
} else {
@@ -100,7 +100,7 @@ class LibraryUpdateNotifier(
),
)
if (!securityPreferences.hideNotificationContent().get()) {
if (!securityPreferences.hideNotificationContent.get()) {
val updatingText = manga.joinToString("\n") { it.title.chop(40) }
progressNotificationBuilder.setStyle(NotificationCompat.BigTextStyle().bigText(updatingText))
}
@@ -155,7 +155,7 @@ class LibraryUpdateNotifier(
Notifications.ID_LIBRARY_ERROR,
Notifications.CHANNEL_LIBRARY_ERROR,
) {
setContentTitle(context.stringResource(MR.strings.notification_update_error, failed))
setContentTitle(context.pluralStringResource(MR.plurals.notification_update_error, failed, failed))
setContentText(context.stringResource(MR.strings.action_show_errors))
setSmallIcon(R.drawable.ic_tachi)
@@ -175,7 +175,7 @@ class LibraryUpdateNotifier(
Notifications.CHANNEL_NEW_CHAPTERS,
) {
setContentTitle(context.stringResource(MR.strings.notification_new_chapters))
if (updates.size == 1 && !securityPreferences.hideNotificationContent().get()) {
if (updates.size == 1 && !securityPreferences.hideNotificationContent.get()) {
setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN))
} else {
setContentText(
@@ -186,7 +186,7 @@ class LibraryUpdateNotifier(
),
)
if (!securityPreferences.hideNotificationContent().get()) {
if (!securityPreferences.hideNotificationContent.get()) {
setStyle(
NotificationCompat.BigTextStyle().bigText(
updates.joinToString("\n") {
@@ -210,7 +210,7 @@ class LibraryUpdateNotifier(
}
// Per-manga notification
if (!securityPreferences.hideNotificationContent().get()) {
if (!securityPreferences.hideNotificationContent.get()) {
launchUI {
context.notify(
updates.map { (manga, chapters) ->
@@ -23,7 +23,6 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.runBlocking
import tachiyomi.core.common.Constants
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.withUIContext
import tachiyomi.domain.chapter.interactor.GetChapter
import tachiyomi.domain.chapter.interactor.UpdateChapter
import tachiyomi.domain.chapter.model.Chapter
@@ -85,18 +84,11 @@ class NotificationReceiver : BroadcastReceiver() {
ACTION_CANCEL_APP_UPDATE_DOWNLOAD -> cancelDownloadAppUpdate(context)
// Open reader activity
ACTION_OPEN_CHAPTER -> {
val pendingResult = goAsync()
launchIO {
try {
openChapter(
context,
intent.getLongExtra(EXTRA_MANGA_ID, -1),
intent.getLongExtra(EXTRA_CHAPTER_ID, -1),
)
} finally {
pendingResult.finish()
}
}
openChapter(
context,
intent.getLongExtra(EXTRA_MANGA_ID, -1),
intent.getLongExtra(EXTRA_CHAPTER_ID, -1),
)
}
// Mark updated manga chapters as read
ACTION_MARK_AS_READ -> {
@@ -161,18 +153,16 @@ class NotificationReceiver : BroadcastReceiver() {
* @param mangaId id of manga
* @param chapterId id of chapter
*/
private suspend fun openChapter(context: Context, mangaId: Long, chapterId: Long) {
val manga = getManga.await(mangaId)
val chapter = getChapter.await(chapterId)
withUIContext {
if (manga != null && chapter != null) {
val intent = ReaderActivity.newIntent(context, manga.id, chapter.id).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
}
context.startActivity(intent)
} else {
context.toast(MR.strings.chapter_error)
private fun openChapter(context: Context, mangaId: Long, chapterId: Long) {
val manga = runBlocking { getManga.await(mangaId) }
val chapter = runBlocking { getChapter.await(chapterId) }
if (manga != null && chapter != null) {
val intent = ReaderActivity.newIntent(context, manga.id, chapter.id).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
}
context.startActivity(intent)
} else {
context.toast(MR.strings.chapter_error)
}
}
@@ -226,7 +216,7 @@ class NotificationReceiver : BroadcastReceiver() {
val toUpdate = chapterUrls.mapNotNull { getChapter.await(it, mangaId) }
.map {
val chapter = it.copy(read = true)
if (downloadPreferences.removeAfterMarkedAsRead().get()) {
if (downloadPreferences.removeAfterMarkedAsRead.get()) {
val manga = getManga.await(mangaId)
if (manga != null) {
val source = sourceManager.get(manga.source)
@@ -78,7 +78,7 @@ class SyncDataJob(private val context: Context, workerParams: WorkerParameters)
fun setupTask(context: Context, prefInterval: Int? = null) {
val syncPreferences = Injekt.get<SyncPreferences>()
val interval = prefInterval ?: syncPreferences.syncInterval().get()
val interval = prefInterval ?: syncPreferences.syncInterval.get()
if (interval > 0) {
val request = PeriodicWorkRequestBuilder<SyncDataJob>(
@@ -120,7 +120,7 @@ class SyncManager(
)
// Handle sync based on the selected service
val syncService = when (val syncService = SyncService.fromInt(syncPreferences.syncService().get())) {
val syncService = when (val syncService = SyncService.fromInt(syncPreferences.syncService.get())) {
SyncService.SYNCYOMI -> {
SyncYomiSyncService(
context,
@@ -151,7 +151,7 @@ class SyncManager(
if (remoteBackup === syncData.backup) {
// nothing changed
logcat(LogPriority.DEBUG) { "Skip restore due to remote was overwrite from local" }
syncPreferences.lastSyncTimestamp().set(Date().time)
syncPreferences.lastSyncTimestamp.set(Date().time)
notifier.showSyncSuccess("Sync completed successfully")
return
}
@@ -163,9 +163,9 @@ class SyncManager(
}
// Check if it's first sync based on lastSyncTimestamp
if (syncPreferences.lastSyncTimestamp().get() == 0L && databaseManga.isNotEmpty()) {
if (syncPreferences.lastSyncTimestamp.get() == 0L && databaseManga.isNotEmpty()) {
// It's first sync no need to restore data. (just update remote data)
syncPreferences.lastSyncTimestamp().set(Date().time)
syncPreferences.lastSyncTimestamp.set(Date().time)
notifier.showSyncSuccess("Updated remote data successfully")
return
}
@@ -199,7 +199,7 @@ class SyncManager(
!hasExtensionRepoChanges && !hasSavedSearchChanges
) {
// update the sync timestamp
syncPreferences.lastSyncTimestamp().set(Date().time)
syncPreferences.lastSyncTimestamp.set(Date().time)
notifier.showSyncSuccess("Sync completed successfully")
return
}
@@ -240,7 +240,7 @@ class SyncManager(
)
// update the sync timestamp
syncPreferences.lastSyncTimestamp().set(Date().time)
syncPreferences.lastSyncTimestamp.set(Date().time)
} else {
logcat(LogPriority.ERROR) { "Failed to write sync data to file" }
}
@@ -42,7 +42,7 @@ class SyncNotifier(private val context: Context) {
val builder = with(progressNotificationBuilder) {
setContentTitle(context.getString(R.string.syncing_library))
if (!preferences.hideNotificationContent().get()) {
if (!preferences.hideNotificationContent.get()) {
setContentText(content)
}
@@ -259,8 +259,8 @@ class GoogleDriveService(private val context: Context) {
* and setting up the service using the obtained tokens.
*/
private fun initGoogleDriveService() {
val accessToken = syncPreferences.googleDriveAccessToken().get()
val refreshToken = syncPreferences.googleDriveRefreshToken().get()
val accessToken = syncPreferences.googleDriveAccessToken.get()
val refreshToken = syncPreferences.googleDriveRefreshToken.get()
if (accessToken == "" || refreshToken == "") {
driveService = null
@@ -312,7 +312,7 @@ class GoogleDriveService(private val context: Context) {
.build()
}
internal suspend fun refreshToken() = withIOContext {
val refreshToken = syncPreferences.googleDriveRefreshToken().get()
val refreshToken = syncPreferences.googleDriveRefreshToken.get()
val jsonFactory: JsonFactory = JacksonFactory.getDefaultInstance()
val secrets = GoogleClientSecrets.load(
@@ -336,7 +336,7 @@ class GoogleDriveService(private val context: Context) {
credential.refreshToken()
val newAccessToken = credential.accessToken
// Save the new access token
syncPreferences.googleDriveAccessToken().set(newAccessToken)
syncPreferences.googleDriveAccessToken.set(newAccessToken)
setupGoogleDriveService(newAccessToken, credential.refreshToken)
} catch (e: TokenResponseException) {
if (e.details.error == "invalid_grant") {
@@ -425,8 +425,8 @@ class GoogleDriveService(private val context: Context) {
val refreshToken = tokenResponse.refreshToken
// Save the tokens to SyncPreferences
syncPreferences.googleDriveAccessToken().set(accessToken)
syncPreferences.googleDriveRefreshToken().set(refreshToken)
syncPreferences.googleDriveAccessToken.set(accessToken)
syncPreferences.googleDriveRefreshToken.set(refreshToken)
setupGoogleDriveService(accessToken, refreshToken)
initGoogleDriveService()
@@ -136,7 +136,7 @@ abstract class SyncService(
"Starting merge. Local list size: ${localMangaListSafe.size}, Remote list size: ${remoteMangaListSafe.size}"
}
val lastSyncTime = syncPreferences.lastSyncTimestamp().get().milliseconds.inWholeSeconds
val lastSyncTime = syncPreferences.lastSyncTimestamp.get().milliseconds.inWholeSeconds
val syncOptions = syncPreferences.getSyncSettings()
val mergedList = (localMangaMap.keys + remoteMangaMap.keys).distinct().mapNotNull { compositeKey ->
@@ -319,7 +319,7 @@ abstract class SyncService(
val localMapByUid = localCategoriesList.filter { it.uid != 0L }.associateBy { it.uid }
val localMapByName = localCategoriesList.associateBy { it.name }
val lastSyncTime = syncPreferences.lastSyncTimestamp().get()
val lastSyncTime = syncPreferences.lastSyncTimestamp.get()
remoteCategoriesList.forEach { remote ->
var localMatch: BackupCategory? = null
@@ -99,12 +99,12 @@ class SyncYomiSyncService(
}
private suspend fun pullSyncData(): Pair<SyncData?, String> {
val host = syncPreferences.clientHost().get()
val apiKey = syncPreferences.clientAPIKey().get()
val host = syncPreferences.clientHost.get()
val apiKey = syncPreferences.clientAPIKey.get()
val downloadUrl = "$host/api/sync/content"
val headersBuilder = Headers.Builder().add("X-API-Token", apiKey)
val lastETag = syncPreferences.lastSyncEtag().get()
val lastETag = syncPreferences.lastSyncEtag.get()
if (lastETag != "") {
headersBuilder.add("If-None-Match", lastETag)
}
@@ -163,8 +163,8 @@ class SyncYomiSyncService(
private suspend fun pushSyncData(syncData: SyncData, eTag: String): Boolean {
val backup = syncData.backup ?: return true
val host = syncPreferences.clientHost().get()
val apiKey = syncPreferences.clientAPIKey().get()
val host = syncPreferences.clientHost.get()
val apiKey = syncPreferences.clientAPIKey.get()
val uploadUrl = "$host/api/sync/content"
val timeout = 30L
@@ -198,7 +198,7 @@ class SyncYomiSyncService(
if (response.isSuccessful) {
val newETag = response.headers["ETag"]
.takeIf { it?.isNotEmpty() == true } ?: throw SyncYomiException("Missing ETag")
syncPreferences.lastSyncEtag().set(newETag)
syncPreferences.lastSyncEtag.set(newETag)
logcat(LogPriority.DEBUG) { "SyncYomi sync completed" }
return true
} else if (response.code == HttpStatus.SC_PRECONDITION_FAILED) {
@@ -216,8 +216,8 @@ class SyncYomiSyncService(
private suspend fun reportSyncEvent(event: SyncEventStatus, message: String? = null) {
withContext(NonCancellable) {
try {
val host = syncPreferences.clientHost().get()
val apiKey = syncPreferences.clientAPIKey().get()
val host = syncPreferences.clientHost.get()
val apiKey = syncPreferences.clientAPIKey.get()
val url = "$host/api/sync/event"
val headersBuilder = Headers.Builder().add("X-API-Token", apiKey)
@@ -44,7 +44,7 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
override val supportsPrivateTracking: Boolean = true
private val scorePreference = trackPreferences.anilistScoreType()
private val scorePreference = trackPreferences.anilistScoreType
init {
// If the preference is an int from APIv1, logout user to force using APIv2
@@ -17,7 +17,7 @@ fun Track.toApiStatus() = when (status) {
private val preferences: TrackPreferences by injectLazy()
fun DomainTrack.toApiScore(): String = when (preferences.anilistScoreType().get()) {
fun DomainTrack.toApiScore(): String = when (preferences.anilistScoreType.get()) {
// 10 point
"POINT_10" -> (score.toInt() / 10).toString()
// 100 point
@@ -224,6 +224,6 @@ class MangaUpdatesApi(
companion object {
private const val BASE_URL = "https://api.mangaupdates.com"
private val CONTENT_TYPE = "application/vnd.api+json".toMediaType()
private val CONTENT_TYPE = "application/json".toMediaType()
}
}
@@ -14,7 +14,9 @@ import eu.kanade.tachiyomi.data.track.myanimelist.dto.MALSearchResult
import eu.kanade.tachiyomi.data.track.myanimelist.dto.MALUser
import eu.kanade.tachiyomi.network.DELETE
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.HttpException
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.network.parseAs
import eu.kanade.tachiyomi.util.PkceUtil
@@ -124,8 +126,23 @@ class MyAnimeListApi(
.put(formBodyBuilder.build())
.build()
with(json) {
authClient.newCall(request)
.awaitSuccess()
val response = authClient
.newCall(request)
.await()
if (!response.isSuccessful) {
if (response.body.string().contains("invalid_content")) {
// MAL returns unapproved titles in search but does not allow adding them to the list
// returns 400 with this body: {"message":"Invalid content","error":"invalid_content"}
// These unapproved titles cannot be filtered out in search and are also returned by the
// endpoint we use for id prefix search
throw MALTitleNotApproved()
} else {
throw HttpException(response.code)
}
}
response
.parseAs<MALListItemStatus>()
.let { parseMangaItem(it, track) }
}
@@ -80,5 +80,6 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor
}
}
class MALTitleNotApproved : IOException("MAL: This title can't be added because it is waiting for approval.")
class MALTokenRefreshFailed : IOException("MAL: Failed to refresh account token")
class MALTokenExpired : IOException("MAL: Login has expired")
@@ -53,40 +53,49 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.lang.ref.WeakReference
private val lock = Any()
class AppModule(val app: Application) : InjektModule {
// SY -->
private val securityPreferences: SecurityPreferences by injectLazy()
// SY <--
private var sqlDriverRef: WeakReference<SqlDriver>? = null
override fun InjektRegistrar.registerInjectables() {
addSingleton(app)
addSingletonFactory<SqlDriver> {
// SY -->
if (securityPreferences.encryptDatabase().get()) {
System.loadLibrary("sqlcipher")
synchronized(lock) {
sqlDriverRef?.get()?.let { return@synchronized it }
return@addSingletonFactory AndroidSqliteDriver(
schema = Database.Schema,
context = app,
name = CbzCrypto.DATABASE_NAME,
factory = SupportOpenHelperFactory(CbzCrypto.getDecryptedPasswordSql(), null, false, 25),
callback = object : AndroidSqliteDriver.Callback(Database.Schema) {
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
setPragma(db, "foreign_keys = ON")
setPragma(db, "journal_mode = WAL")
setPragma(db, "synchronous = NORMAL")
}
// SY -->
if (securityPreferences.encryptDatabase.get()) {
System.loadLibrary("sqlcipher")
private fun setPragma(db: SupportSQLiteDatabase, pragma: String) {
val cursor = db.query("PRAGMA $pragma")
cursor.moveToFirst()
cursor.close()
}
},
)
return@synchronized AndroidSqliteDriver(
schema = Database.Schema,
context = app,
name = CbzCrypto.DATABASE_NAME,
factory = SupportOpenHelperFactory(CbzCrypto.getDecryptedPasswordSql(), null, false, 25),
callback = object : AndroidSqliteDriver.Callback(Database.Schema) {
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
setPragma(db, "foreign_keys = ON")
setPragma(db, "journal_mode = WAL")
setPragma(db, "synchronous = NORMAL")
}
private fun setPragma(db: SupportSQLiteDatabase, pragma: String) {
val cursor = db.query("PRAGMA $pragma")
cursor.moveToFirst()
cursor.close()
}
},
).also { sqlDriverRef = WeakReference(it) }
}
}
// SY <--
@@ -97,7 +106,7 @@ class AppModule(val app: Application) : InjektModule {
configuration = AndroidxSqliteConfiguration(
isForeignKeyConstraintsEnabled = true,
),
)
).also { sqlDriverRef = WeakReference(it) }
}
addSingletonFactory {
Database(
@@ -30,7 +30,7 @@ class PreferenceModule(val app: Application) : InjektModule {
addSingletonFactory {
NetworkPreferences(
preferenceStore = get(),
verboseLogging = isDevFlavor,
verboseLoggingDefault = isDevFlavor,
)
}
addSingletonFactory {
@@ -30,7 +30,6 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import logcat.LogPriority
import tachiyomi.core.common.util.lang.withUIContext
import tachiyomi.core.common.util.system.logcat
@@ -88,7 +87,7 @@ class ExtensionManager(
ExtensionInstallReceiver(InstallationListener()).register(context)
}
private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet()
private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages.isSet()
fun getExtensionPackage(sourceId: Long): String? {
return installedExtensionsFlow.value.find { extension ->
@@ -141,27 +140,25 @@ class ExtensionManager(
* Loads and registers the installed extensions.
*/
private fun initExtensions() {
scope.launch {
val extensions = ExtensionLoader.loadExtensions(context)
val extensions = ExtensionLoader.loadExtensions(context)
installedExtensionMapFlow.value = extensions
.filterIsInstance<LoadResult.Success>()
.associate { it.extension.pkgName to it.extension }
installedExtensionMapFlow.value = extensions
.filterIsInstance<LoadResult.Success>()
.associate { it.extension.pkgName to it.extension }
untrustedExtensionMapFlow.value = extensions
.filterIsInstance<LoadResult.Untrusted>()
.associate { it.extension.pkgName to it.extension }
// SY -->
.filterNotBlacklisted()
// SY <--
untrustedExtensionMapFlow.value = extensions
.filterIsInstance<LoadResult.Untrusted>()
.associate { it.extension.pkgName to it.extension }
// SY -->
.filterNotBlacklisted()
// SY <--
_isInitialized.value = true
}
_isInitialized.value = true
}
// EXH -->
private fun <T : Extension> Map<String, T>.filterNotBlacklisted(): Map<String, T> {
val blacklistEnabled = preferences.enableSourceBlacklist().get()
val blacklistEnabled = preferences.enableSourceBlacklist.get()
return filterNot { (_, extension) ->
extension.isBlacklisted(blacklistEnabled)
.also {
@@ -170,7 +167,7 @@ class ExtensionManager(
}
}
private fun Extension.isBlacklisted(blacklistEnabled: Boolean = preferences.enableSourceBlacklist().get()): Boolean {
private fun Extension.isBlacklisted(blacklistEnabled: Boolean = preferences.enableSourceBlacklist.get()): Boolean {
return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled
}
// EXH <--
@@ -215,12 +212,12 @@ class ExtensionManager(
.map(Extension.Available.Source::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)
}
preferences.enabledLanguages().set(defaultLanguages + languagesToEnable)
preferences.enabledLanguages.set(defaultLanguages + languagesToEnable)
subLanguagesEnabledOnFirstRun = true
}
@@ -231,7 +228,7 @@ class ExtensionManager(
*/
private fun updatedInstalledExtensionsStatuses(availableExtensions: List<Extension.Available>) {
if (availableExtensions.isEmpty()) {
preferences.extensionUpdatesCount().set(0)
preferences.extensionUpdatesCount.set(0)
return
}
@@ -429,7 +426,7 @@ class ExtensionManager(
private fun updatePendingUpdatesCount() {
val pendingUpdateCount = installedExtensionMapFlow.value.values.count { it.hasUpdate }
preferences.extensionUpdatesCount().set(pendingUpdateCount)
preferences.extensionUpdatesCount.set(pendingUpdateCount)
if (pendingUpdateCount == 0) {
ExtensionUpdateNotifier(context).dismiss()
}
@@ -93,7 +93,7 @@ internal class ExtensionApi {
}
// SY -->
val blacklistEnabled = sourcePreferences.enableSourceBlacklist().get()
val blacklistEnabled = sourcePreferences.enableSourceBlacklist.get()
// SY <--
val installedExtensions = ExtensionLoader.loadExtensions(context)
@@ -155,7 +155,7 @@ internal class ExtensionApi {
// SY -->
private fun Extension.isBlacklisted(
blacklistEnabled: Boolean = sourcePreferences.enableSourceBlacklist().get(),
blacklistEnabled: Boolean = sourcePreferences.enableSourceBlacklist.get(),
): Boolean {
return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled
}
@@ -29,7 +29,7 @@ class ExtensionUpdateNotifier(
names.size,
),
)
if (!securityPreferences.hideNotificationContent().get()) {
if (!securityPreferences.hideNotificationContent.get()) {
val extNames = names.joinToString(", ")
setContentText(extNames)
setStyle(NotificationCompat.BigTextStyle().bigText(extNames))
@@ -106,8 +106,13 @@ class PackageInstallerInstaller(private val service: Service) : Installer(servic
override fun cancelEntry(entry: Entry): Boolean {
activeSession?.let { (activeEntry, sessionId) ->
if (activeEntry == entry) {
packageInstaller.abandonSession(sessionId)
return false
return try {
packageInstaller.abandonSession(sessionId)
false
} catch (_: SecurityException) {
// Highly likely the session has succeeded
true
}
}
}
return true
@@ -39,7 +39,7 @@ internal class ExtensionInstaller(
private val scope = CoroutineScope(Dispatchers.IO)
private val activeJobs = mutableMapOf<String, Job>()
private val activeSteps = mutableMapOf<Long, MutableStateFlow<InstallStep>>()
private val extensionInstaller = Injekt.get<BasePreferences>().extensionInstaller()
private val extensionInstaller = Injekt.get<BasePreferences>().extensionInstaller
private val httpClient: OkHttpClient = Injekt.get<NetworkHelper>().client
@@ -18,7 +18,6 @@ import eu.kanade.tachiyomi.util.storage.copyAndSetReadOnlyTo
import eu.kanade.tachiyomi.util.system.ChildFirstPathClassLoader
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.runBlocking
import logcat.LogPriority
import tachiyomi.core.common.util.system.logcat
@@ -44,7 +43,7 @@ internal object ExtensionLoader {
private val preferences: SourcePreferences by injectLazy()
private val trustExtension: TrustExtension by injectLazy()
private val loadNsfwSource by lazy {
preferences.showNsfwSource().get()
preferences.showNsfwSource.get()
}
private const val EXTENSION_FEATURE = "tachiyomi.extension"
@@ -115,7 +114,7 @@ internal object ExtensionLoader {
*
* @param context The application context.
*/
suspend fun loadExtensions(context: Context): List<LoadResult> {
fun loadExtensions(context: Context): List<LoadResult> {
val pkgManager = context.packageManager
val installedPkgs = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
@@ -161,10 +160,11 @@ internal object ExtensionLoader {
if (extPkgs.isEmpty()) return emptyList()
// Load each extension concurrently and wait for completion
return coroutineScope {
extPkgs.map {
return runBlocking {
val deferred = extPkgs.map {
async { loadExtension(context, it) }
}.awaitAll()
}
deferred.awaitAll()
}
}
@@ -79,23 +79,25 @@ class AndroidSourceManager(
scope.launch {
extensionManager.installedExtensionsFlow
// SY -->
.combine(exhPreferences.enableExhentai().changes()) { extensions, enableExhentai ->
.combine(exhPreferences.enableExhentai.changes()) { extensions, enableExhentai ->
extensions to enableExhentai
}
// SY <--
.collectLatest { (extensions, enableExhentai) ->
val mutableMap = ConcurrentHashMap<Long, Source>(
val mutableMap: ConcurrentHashMap<Long, Source> = ConcurrentHashMap<Long, Source>(
mapOf(
LocalSource.ID to LocalSource(
context,
Injekt.get(),
Injekt.get(),
// SY -->
sourcePreferences.allowLocalSourceHiddenFolders()::get,
sourcePreferences.allowLocalSourceHiddenFolders::get,
// SY <--
),
),
).apply {
)
mutableMap.apply {
// SY -->
put(EH_SOURCE_ID, EHentai(EH_SOURCE_ID, false, context))
if (enableExhentai) {
@@ -104,6 +106,7 @@ class AndroidSourceManager(
put(MERGED_SOURCE_ID, MergedSource())
// SY <--
}
extensions.forEach { extension ->
extension.sources.mapNotNull { it.toInternalSource() }.forEach {
mutableMap[it.id] = it

Some files were not shown because too many files have changed in this diff Show More