Issues
+
-1. **Before reporting a new issue, take a look at the already opened and closed [issues](https://github.com/aniyomiorg/aniyomi/issues), and [recent release version changes](https://aniyomi.org/changelogs/).**
-2. If you are unsure, ask on Discord: [![Discord](https://img.shields.io/discord/841701076242530374?label=discord&labelColor=7289da&color=2c2f33&style=flat)](https://discord.gg/F32UjdJZrR)
+* Local reading and watching of content.
+* A configurable reader with multiple viewers, reading directions and other settings.
+* A configurable player built on mpv-android with multiple options and settings.
+* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.io/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), [Simkl](https://simkl.com/), and [Bangumi](https://bgm.tv/) support.
+* Categories to organize your library.
+* Light and dark themes.
+* Schedule updating your library for new chapters/episodes.
+* Create backups locally to read/watch offline or to your desired cloud service.
+* Plus much more...
-
+
-Bugs
+## Contributing
-* Include version (More → About → Version)
- * If not latest, try updating, it may have already been solved
- * Preview version is equal to the number of commits as seen on the main page
-* Include steps to reproduce (if not obvious from description)
-* Include screenshot (if needed)
-* If it could be device-dependent, try reproducing on another device (if possible)
-* Don't group unrelated requests into one issue
+[Code of conduct](./CODE_OF_CONDUCT.md) · [Contributing guide](./CONTRIBUTING.md)
-
+Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
-Feature Requests
+Before reporting a new issue, take a look at the [FAQ](https://aniyomi.org/docs/faq/general), the [changelog](https://aniyomi.org/changelogs/) and the already opened [issues](https://github.com/aniyomiorg/aniyomi/issues); if you got any questions, join our [Discord server](https://discord.gg/F32UjdJZrR).
-* Write a detailed issue, explaining what it should do or how. Avoid writing just "like X app does"
-* Include screenshot (if needed)
+### Repositories
-Contributing
+[![aniyomiorg/aniyomi-website - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=aniyomiorg&repo=aniyomi-website&bg_color=161B22&text_color=c9d1d9&title_color=818cf8&icon_color=818cf8&border_radius=8&hide_border=true&description_lines_count=2)](https://github.com/aniyomiorg/aniyomi-website/)
+[![aniyomiorg/aniyomi-mpv-lib - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=aniyomiorg&repo=aniyomi-mpv-lib&bg_color=161B22&text_color=c9d1d9&title_color=818cf8&icon_color=818cf8&border_radius=8&hide_border=true&description_lines_count=2)](https://github.com/aniyomiorg/aniyomi-mpv-lib/)
-See [CONTRIBUTING.md](./CONTRIBUTING.md).
-
+### Credits
-Code of Conduct
+Thank you to all the people who have contributed!
-See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
-
+
+
+
+### Disclaimer
-## License
+The developer(s) of this application does not have any affiliation with the content providers available, and this application hosts zero content.
- Copyright 2015 Javier Tomás
+### License
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
+
+Copyright © 2015 Javier Tomás
+Copyright © 2024 The Mihon Open Source Project
+Copyright © 2024 The Aniyomi Open Source Project
- http://www.apache.org/licenses/LICENSE-2.0
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+http://www.apache.org/licenses/LICENSE-2.0
-## Disclaimer
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
-The developer of this application does not have any affiliation with the content providers available.
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index dc602dc8f6..b2e4166479 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -9,7 +9,6 @@ plugins {
id("mihon.android.application")
id("mihon.android.application.compose")
id("com.mikepenz.aboutlibraries.plugin")
- kotlin("plugin.compose") // TODO(kotlin2): Remove
id("com.github.zellius.shortcut-helper")
kotlin("plugin.serialization")
}
@@ -162,7 +161,6 @@ dependencies {
implementation(compose.activity)
implementation(compose.foundation)
implementation(compose.material3.core)
- implementation(compose.material.core)
implementation(compose.material.icons)
implementation(compose.animation)
implementation(compose.animation.graphics)
@@ -171,6 +169,8 @@ dependencies {
implementation(compose.ui.util)
implementation(compose.accompanist.systemuicontroller)
+ implementation(androidx.interpolator)
+
implementation(androidx.paging.runtime)
implementation(androidx.paging.compose)
@@ -216,7 +216,6 @@ dependencies {
// Disk
implementation(libs.disklrucache)
implementation(libs.unifile)
- implementation(libs.bundles.archive)
// Preferences
implementation(libs.preferencektx)
@@ -310,7 +309,6 @@ tasks {
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
"-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi",
"-opt-in=coil3.annotation.ExperimentalCoilApi",
- "-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=kotlinx.coroutines.FlowPreview",
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 1d0bdb05a1..4f47aa94ce 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -48,6 +48,10 @@
-dontnote rx.internal.util.PlatformDependent
##---------------End: proguard configuration for RxJava 1.x ----------
+##---------------Begin: proguard configuration for okhttp ----------
+-keepclasseswithmembers class okhttp3.MultipartBody$Builder { *; }
+##---------------End: proguard configuration for okhttp ----------
+
##---------------Begin: proguard configuration for kotlinx.serialization ----------
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.** # core serialization annotations
@@ -75,7 +79,4 @@
##---------------End: proguard configuration for kotlinx.serialization ----------
# XmlUtil
--keep public enum nl.adaptivity.xmlutil.EventType { *; }
-
-# Apache Commons Compress
--keep class * extends org.apache.commons.compress.archivers.zip.ZipExtraField { (); }
\ No newline at end of file
+-keep public enum nl.adaptivity.xmlutil.EventType { *; }
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt
index e3e1767704..c54db27683 100644
--- a/app/src/main/java/eu/kanade/domain/DomainModule.kt
+++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt
@@ -345,8 +345,8 @@ class DomainModule : InjektModule {
addFactory { ToggleLanguage(get()) }
addFactory { ToggleMangaSource(get()) }
addFactory { ToggleMangaSourcePin(get()) }
- addFactory { TrustAnimeExtension(get()) }
- addFactory { TrustMangaExtension(get()) }
+ addFactory { TrustAnimeExtension(get(), get()) }
+ addFactory { TrustMangaExtension(get(), get()) }
addFactory { ExtensionRepoService(get(), get()) }
diff --git a/app/src/main/java/eu/kanade/domain/extension/anime/interactor/GetAnimeExtensionsByType.kt b/app/src/main/java/eu/kanade/domain/extension/anime/interactor/GetAnimeExtensionsByType.kt
index 62d38df6e7..86f898f26e 100644
--- a/app/src/main/java/eu/kanade/domain/extension/anime/interactor/GetAnimeExtensionsByType.kt
+++ b/app/src/main/java/eu/kanade/domain/extension/anime/interactor/GetAnimeExtensionsByType.kt
@@ -20,7 +20,7 @@ class GetAnimeExtensionsByType(
extensionManager.installedExtensionsFlow,
extensionManager.untrustedExtensionsFlow,
extensionManager.availableExtensionsFlow,
- ) { _activeLanguages, _installed, _untrusted, _available ->
+ ) { enabledLanguages, _installed, _untrusted, _available ->
val (updates, installed) = _installed
.filter { (showNsfwSources || !it.isNsfw) }
.sortedWith(
@@ -40,9 +40,9 @@ class GetAnimeExtensionsByType(
}
.flatMap { ext ->
if (ext.sources.isEmpty()) {
- return@flatMap if (ext.lang in _activeLanguages) listOf(ext) else emptyList()
+ return@flatMap if (ext.lang in enabledLanguages) listOf(ext) else emptyList()
}
- ext.sources.filter { it.lang in _activeLanguages }
+ ext.sources.filter { it.lang in enabledLanguages }
.map {
ext.copy(
name = it.name,
diff --git a/app/src/main/java/eu/kanade/domain/extension/anime/interactor/TrustAnimeExtension.kt b/app/src/main/java/eu/kanade/domain/extension/anime/interactor/TrustAnimeExtension.kt
index 2cf6cd99d8..5e8643a62b 100644
--- a/app/src/main/java/eu/kanade/domain/extension/anime/interactor/TrustAnimeExtension.kt
+++ b/app/src/main/java/eu/kanade/domain/extension/anime/interactor/TrustAnimeExtension.kt
@@ -3,16 +3,18 @@ package eu.kanade.domain.extension.anime.interactor
import android.content.pm.PackageInfo
import androidx.core.content.pm.PackageInfoCompat
import eu.kanade.domain.source.service.SourcePreferences
+import mihon.domain.extensionrepo.anime.repository.AnimeExtensionRepoRepository
import tachiyomi.core.common.preference.getAndSet
class TrustAnimeExtension(
+ private val animeExtensionRepoRepository: AnimeExtensionRepoRepository,
private val preferences: SourcePreferences,
) {
- fun isTrusted(pkgInfo: PackageInfo, signatureHash: String): Boolean {
- val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:$signatureHash"
- return key in preferences.trustedExtensions().get() ||
- signatureHash == officialSignature
+ suspend fun isTrusted(pkgInfo: PackageInfo, fingerprints: List): Boolean {
+ val trustedFingerprints = animeExtensionRepoRepository.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()
}
fun trust(pkgName: String, versionCode: Long, signatureHash: String) {
@@ -20,9 +22,7 @@ class TrustAnimeExtension(
// Remove previously trusted versions
val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet()
- removed.also {
- it += "$pkgName:$versionCode:$signatureHash"
- }
+ removed.also { it += "$pkgName:$versionCode:$signatureHash" }
}
}
@@ -30,6 +30,3 @@ class TrustAnimeExtension(
preferences.trustedExtensions().delete()
}
}
-
-// jmir1's key
-private const val officialSignature = "50ab1d1e3a20d204d0ad6d334c7691c632e41b98dfa132bf385695fdfa63839c"
diff --git a/app/src/main/java/eu/kanade/domain/extension/manga/interactor/GetMangaExtensionsByType.kt b/app/src/main/java/eu/kanade/domain/extension/manga/interactor/GetMangaExtensionsByType.kt
index f035338048..408b2fa643 100644
--- a/app/src/main/java/eu/kanade/domain/extension/manga/interactor/GetMangaExtensionsByType.kt
+++ b/app/src/main/java/eu/kanade/domain/extension/manga/interactor/GetMangaExtensionsByType.kt
@@ -20,7 +20,7 @@ class GetMangaExtensionsByType(
extensionManager.installedExtensionsFlow,
extensionManager.untrustedExtensionsFlow,
extensionManager.availableExtensionsFlow,
- ) { _activeLanguages, _installed, _untrusted, _available ->
+ ) { enabledLanguages, _installed, _untrusted, _available ->
val (updates, installed) = _installed
.filter { (showNsfwSources || !it.isNsfw) }
.sortedWith(
@@ -40,9 +40,9 @@ class GetMangaExtensionsByType(
}
.flatMap { ext ->
if (ext.sources.isEmpty()) {
- return@flatMap if (ext.lang in _activeLanguages) listOf(ext) else emptyList()
+ return@flatMap if (ext.lang in enabledLanguages) listOf(ext) else emptyList()
}
- ext.sources.filter { it.lang in _activeLanguages }
+ ext.sources.filter { it.lang in enabledLanguages }
.map {
ext.copy(
name = it.name,
diff --git a/app/src/main/java/eu/kanade/domain/extension/manga/interactor/TrustMangaExtension.kt b/app/src/main/java/eu/kanade/domain/extension/manga/interactor/TrustMangaExtension.kt
index daa5fb8631..006f650bad 100644
--- a/app/src/main/java/eu/kanade/domain/extension/manga/interactor/TrustMangaExtension.kt
+++ b/app/src/main/java/eu/kanade/domain/extension/manga/interactor/TrustMangaExtension.kt
@@ -3,16 +3,18 @@ package eu.kanade.domain.extension.manga.interactor
import android.content.pm.PackageInfo
import androidx.core.content.pm.PackageInfoCompat
import eu.kanade.domain.source.service.SourcePreferences
+import mihon.domain.extensionrepo.manga.repository.MangaExtensionRepoRepository
import tachiyomi.core.common.preference.getAndSet
class TrustMangaExtension(
+ private val mangaExtensionRepoRepository: MangaExtensionRepoRepository,
private val preferences: SourcePreferences,
) {
- fun isTrusted(pkgInfo: PackageInfo, signatureHash: String): Boolean {
- val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:$signatureHash"
- return key in preferences.trustedExtensions().get() ||
- signatureHash == officialSignature
+ suspend fun isTrusted(pkgInfo: PackageInfo, fingerprints: List): Boolean {
+ val trustedFingerprints = mangaExtensionRepoRepository.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()
}
fun trust(pkgName: String, versionCode: Long, signatureHash: String) {
@@ -20,9 +22,7 @@ class TrustMangaExtension(
// Remove previously trusted versions
val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet()
- removed.also {
- it += "$pkgName:$versionCode:$signatureHash"
- }
+ removed.also { it += "$pkgName:$versionCode:$signatureHash" }
}
}
@@ -30,6 +30,3 @@ class TrustMangaExtension(
preferences.trustedExtensions().delete()
}
}
-
-// inorichi's key
-private const val officialSignature = "7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23"
diff --git a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt
index 5385716786..f910b53db7 100644
--- a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt
+++ b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt
@@ -46,6 +46,11 @@ class SourcePreferences(
emptySet(),
)
+ fun globalSearchFilterState() = preferenceStore.getBoolean(
+ Preference.appStateKey("has_filters_toggle_state"),
+ false,
+ )
+
// Mixture Sources
fun disabledAnimeSources() = preferenceStore.getStringSet("hidden_anime_catalogues", emptySet())
diff --git a/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchResultItems.kt b/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchResultItems.kt
index b489f9bb58..afda5497ab 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchResultItems.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchResultItems.kt
@@ -32,9 +32,10 @@ fun GlobalSearchResultItem(
title: String,
subtitle: String,
onClick: () -> Unit,
+ modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
- Column {
+ Column(modifier = modifier) {
Row(
modifier = Modifier
.padding(
diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionDetailsScreen.kt
index 6b88b69438..0ed3010298 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionDetailsScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionDetailsScreen.kt
@@ -5,7 +5,6 @@ import android.net.Uri
import android.provider.Settings
import android.util.DisplayMetrics
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@@ -190,7 +189,7 @@ private fun AnimeExtensionDetails(
key = { it.source.id },
) { source ->
SourceSwitchPreference(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
source = source,
onClickSourcePreferences = onClickSourcePreferences,
onClickSource = onClickSource,
@@ -355,10 +354,8 @@ private fun InfoText(
primaryTextStyle: TextStyle = MaterialTheme.typography.bodyLarge,
onClick: (() -> Unit)? = null,
) {
- val interactionSource = remember { MutableInteractionSource() }
-
val clickableModifier = if (onClick != null) {
- Modifier.clickable(interactionSource, indication = null) { onClick() }
+ Modifier.clickable(interactionSource = null, indication = null, onClick = onClick)
} else {
Modifier
}
diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionFilterScreen.kt
index 048d772d24..de8e87eb5f 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionFilterScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionFilterScreen.kt
@@ -58,7 +58,7 @@ private fun AnimeExtensionFilterContent(
) {
items(state.languages) { language ->
SwitchPreferenceWidget(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
title = LocaleHelper.getSourceDisplayName(language, context),
checked = language in state.enabledLanguages,
onCheckedChanged = { onClickLang(language) },
diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionsScreen.kt
index d4d48b2e44..bc0ae578dd 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionsScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionsScreen.kt
@@ -88,7 +88,7 @@ fun AnimeExtensionScreen(
PullRefresh(
refreshing = state.isRefreshing,
onRefresh = onRefresh,
- enabled = { !state.isLoading },
+ enabled = !state.isLoading,
) {
when {
state.isLoading -> LoadingScreen(Modifier.padding(contentPadding))
@@ -185,14 +185,14 @@ private fun AnimeExtensionContent(
}
ExtensionHeader(
textRes = header.textRes,
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
action = action,
)
}
is AnimeExtensionUiModel.Header.Text -> {
ExtensionHeader(
text = header.text,
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
)
}
}
@@ -211,7 +211,7 @@ private fun AnimeExtensionContent(
) { item ->
AnimeExtensionItem(
item = item,
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
onClickItem = {
when (it) {
is AnimeExtension.Available -> onInstallExtension(it)
diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeSourcesFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeSourcesFilterScreen.kt
index 2606b68336..8aafc1a4e2 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeSourcesFilterScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeSourcesFilterScreen.kt
@@ -68,7 +68,7 @@ private fun AnimeSourcesFilterContent(
contentType = "source-filter-header",
) {
AnimeSourcesFilterHeader(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
language = language,
enabled = enabled,
onClickItem = onClickLanguage,
@@ -81,7 +81,7 @@ private fun AnimeSourcesFilterContent(
contentType = { "source-filter-item" },
) { source ->
AnimeSourcesFilterItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
source = source,
isEnabled = "${source.id}" !in state.disabledSources,
onClickItem = onClickSource,
diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeSourcesScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeSourcesScreen.kt
index 3fb20cb6d4..d2ce30aa4f 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeSourcesScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeSourcesScreen.kt
@@ -74,12 +74,12 @@ fun AnimeSourcesScreen(
when (model) {
is AnimeSourceUiModel.Header -> {
AnimeSourceHeader(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
language = model.language,
)
}
is AnimeSourceUiModel.Item -> AnimeSourceItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
source = model.source,
onClickItem = onClickItem,
onLongClickItem = onLongClickItem,
diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt
index 54c87954a4..8b8eba66f5 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt
@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
+import androidx.compose.ui.Modifier
import eu.kanade.presentation.browse.GlobalSearchErrorResultItem
import eu.kanade.presentation.browse.GlobalSearchLoadingResultItem
import eu.kanade.presentation.browse.GlobalSearchResultItem
@@ -79,6 +80,7 @@ internal fun GlobalSearchContent(
} ?: source.name,
subtitle = LocaleHelper.getLocalizedDisplayName(source.lang),
onClick = { onClickSource(source) },
+ modifier = Modifier.animateItem(),
) {
when (result) {
AnimeSearchItemResult.Loading -> {
diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSourceScreen.kt
index 3cf8d3161b..c0e4454a06 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSourceScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSourceScreen.kt
@@ -133,7 +133,7 @@ private fun MigrateAnimeSourceList(
key = { (source, _) -> "migrate-${source.id}" },
) { (source, count) ->
MigrateAnimeSourceItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
source = source,
count = count,
onClickItem = { onClickItem(source) },
diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt
index 68be3ddf6e..89f1602197 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt
@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
+import androidx.compose.ui.Modifier
import eu.kanade.presentation.browse.GlobalSearchErrorResultItem
import eu.kanade.presentation.browse.GlobalSearchLoadingResultItem
import eu.kanade.presentation.browse.GlobalSearchResultItem
@@ -79,6 +80,7 @@ internal fun GlobalSearchContent(
} ?: source.name,
subtitle = LocaleHelper.getLocalizedDisplayName(source.lang),
onClick = { onClickSource(source) },
+ modifier = Modifier.animateItem(),
) {
when (result) {
MangaSearchItemResult.Loading -> {
diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionDetailsScreen.kt
index 7b9a755354..92423923c6 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionDetailsScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionDetailsScreen.kt
@@ -5,7 +5,6 @@ import android.net.Uri
import android.provider.Settings
import android.util.DisplayMetrics
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@@ -191,7 +190,7 @@ private fun ExtensionDetails(
key = { it.source.id },
) { source ->
SourceSwitchPreference(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
source = source,
onClickSourcePreferences = onClickSourcePreferences,
onClickSource = onClickSource,
@@ -354,10 +353,8 @@ private fun InfoText(
primaryTextStyle: TextStyle = MaterialTheme.typography.bodyLarge,
onClick: (() -> Unit)? = null,
) {
- val interactionSource = remember { MutableInteractionSource() }
-
val clickableModifier = if (onClick != null) {
- Modifier.clickable(interactionSource, indication = null) { onClick() }
+ Modifier.clickable(interactionSource = null, indication = null, onClick = onClick)
} else {
Modifier
}
diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionFilterScreen.kt
index 53263a4d6c..eb0f2903c0 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionFilterScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionFilterScreen.kt
@@ -58,7 +58,7 @@ private fun ExtensionFilterContent(
) {
items(state.languages) { language ->
SwitchPreferenceWidget(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
title = LocaleHelper.getSourceDisplayName(language, context),
checked = language in state.enabledLanguages,
onCheckedChanged = { onClickLang(language) },
diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionsScreen.kt
index b7d3abfc71..f3862c14c6 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionsScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionsScreen.kt
@@ -90,7 +90,7 @@ fun MangaExtensionScreen(
PullRefresh(
refreshing = state.isRefreshing,
onRefresh = onRefresh,
- enabled = { !state.isLoading },
+ enabled = !state.isLoading,
) {
when {
state.isLoading -> LoadingScreen(Modifier.padding(contentPadding))
@@ -187,14 +187,14 @@ private fun ExtensionContent(
}
ExtensionHeader(
textRes = header.textRes,
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
action = action,
)
}
is MangaExtensionUiModel.Header.Text -> {
ExtensionHeader(
text = header.text,
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
)
}
}
@@ -212,7 +212,7 @@ private fun ExtensionContent(
},
) { item ->
ExtensionItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
item = item,
onClickItem = {
when (it) {
diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaSourcesFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaSourcesFilterScreen.kt
index 4455d37cff..b61f9c4106 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaSourcesFilterScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaSourcesFilterScreen.kt
@@ -68,7 +68,7 @@ private fun SourcesFilterContent(
contentType = "source-filter-header",
) {
SourcesFilterHeader(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
language = language,
enabled = enabled,
onClickItem = onClickLanguage,
@@ -81,7 +81,7 @@ private fun SourcesFilterContent(
contentType = { "source-filter-item" },
) { source ->
SourcesFilterItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
source = source,
enabled = "${source.id}" !in state.disabledSources,
onClickItem = onClickSource,
diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaSourcesScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaSourcesScreen.kt
index 181c98c84c..8640bf6bc8 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaSourcesScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaSourcesScreen.kt
@@ -74,12 +74,12 @@ fun MangaSourcesScreen(
when (model) {
is MangaSourceUiModel.Header -> {
SourceHeader(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
language = model.language,
)
}
is MangaSourceUiModel.Item -> SourceItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
source = model.source,
onClickItem = onClickItem,
onLongClickItem = onLongClickItem,
diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSourceScreen.kt
index 0c126ed9f4..e68142bb42 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSourceScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSourceScreen.kt
@@ -133,7 +133,7 @@ private fun MigrateSourceList(
key = { (source, _) -> "migrate-${source.id}" },
) { (source, count) ->
MigrateSourceItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
source = source,
count = count,
onClickItem = { onClickItem(source) },
diff --git a/app/src/main/java/eu/kanade/presentation/category/AnimeCategoryScreen.kt b/app/src/main/java/eu/kanade/presentation/category/AnimeCategoryScreen.kt
index 97ae103ab2..5184eaa22f 100644
--- a/app/src/main/java/eu/kanade/presentation/category/AnimeCategoryScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/category/AnimeCategoryScreen.kt
@@ -84,7 +84,7 @@ private fun CategoryContent(
key = { _, category -> "category-${category.id}" },
) { index, category ->
CategoryListItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
category = category,
canMoveUp = index != 0,
canMoveDown = index != categories.lastIndex,
diff --git a/app/src/main/java/eu/kanade/presentation/category/MangaCategoryScreen.kt b/app/src/main/java/eu/kanade/presentation/category/MangaCategoryScreen.kt
index a93c3897e5..459c9ad41e 100644
--- a/app/src/main/java/eu/kanade/presentation/category/MangaCategoryScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/category/MangaCategoryScreen.kt
@@ -84,7 +84,7 @@ private fun CategoryContent(
key = { _, category -> "category-${category.id}" },
) { index, category ->
CategoryListItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
category = category,
canMoveUp = index != 0,
canMoveDown = index != categories.lastIndex,
diff --git a/app/src/main/java/eu/kanade/presentation/category/components/CategoryFloatingActionButton.kt b/app/src/main/java/eu/kanade/presentation/category/components/CategoryFloatingActionButton.kt
index 17ceff1a30..e3fcadc150 100644
--- a/app/src/main/java/eu/kanade/presentation/category/components/CategoryFloatingActionButton.kt
+++ b/app/src/main/java/eu/kanade/presentation/category/components/CategoryFloatingActionButton.kt
@@ -10,8 +10,7 @@ import androidx.compose.ui.Modifier
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
import tachiyomi.presentation.core.i18n.stringResource
-import tachiyomi.presentation.core.util.isScrolledToEnd
-import tachiyomi.presentation.core.util.isScrollingUp
+import tachiyomi.presentation.core.util.shouldExpandFAB
@Composable
fun CategoryFloatingActionButton(
@@ -23,7 +22,7 @@ fun CategoryFloatingActionButton(
text = { Text(text = stringResource(MR.strings.action_add)) },
icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = "") },
onClick = onCreate,
- expanded = lazyListState.isScrollingUp() || lazyListState.isScrolledToEnd(),
+ expanded = lazyListState.shouldExpandFAB(),
modifier = modifier,
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt b/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt
index fac8bb989c..30ae43a43d 100644
--- a/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt
+++ b/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt
@@ -7,8 +7,6 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.core.view.WindowInsetsControllerCompat
@@ -25,7 +23,6 @@ import tachiyomi.presentation.core.components.AdaptiveSheet as AdaptiveSheetImpl
@Composable
fun NavigatorAdaptiveSheet(
screen: Screen,
- tonalElevation: Dp = 1.dp,
enableSwipeDismiss: (Navigator) -> Boolean = { true },
onDismissRequest: () -> Unit,
) {
@@ -33,7 +30,6 @@ fun NavigatorAdaptiveSheet(
screen = screen,
content = { sheetNavigator ->
AdaptiveSheet(
- tonalElevation = tonalElevation,
enableSwipeDismiss = enableSwipeDismiss(sheetNavigator),
onDismissRequest = onDismissRequest,
) {
@@ -76,7 +72,6 @@ fun AdaptiveSheet(
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
hideSystemBars: Boolean = false,
- tonalElevation: Dp = 1.dp,
enableSwipeDismiss: Boolean = true,
content: @Composable () -> Unit,
) {
@@ -95,7 +90,6 @@ fun AdaptiveSheet(
AdaptiveSheetImpl(
modifier = modifier,
isTabletUi = isTabletUi,
- tonalElevation = tonalElevation,
enableSwipeDismiss = enableSwipeDismiss,
onDismissRequest = onDismissRequest,
) {
diff --git a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt
index 8e743ebf5f..071711e1d1 100644
--- a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt
+++ b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt
@@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.outlined.Close
@@ -24,6 +23,7 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PlainTooltip
import androidx.compose.material3.Text
+import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TooltipBox
import androidx.compose.material3.TooltipDefaults
import androidx.compose.material3.TopAppBar
@@ -338,7 +338,7 @@ fun SearchToolbar(
visualTransformation = visualTransformation,
interactionSource = interactionSource,
decorationBox = { innerTextField ->
- TextFieldDefaults.TextFieldDecorationBox(
+ TextFieldDefaults.DecorationBox(
value = searchQuery,
innerTextField = innerTextField,
enabled = true,
@@ -361,6 +361,7 @@ fun SearchToolbar(
),
)
},
+ container = {},
)
},
)
diff --git a/app/src/main/java/eu/kanade/presentation/components/ItemDownloadIndicator.kt b/app/src/main/java/eu/kanade/presentation/components/ItemDownloadIndicator.kt
index ffecc3815c..57b8c297e5 100644
--- a/app/src/main/java/eu/kanade/presentation/components/ItemDownloadIndicator.kt
+++ b/app/src/main/java/eu/kanade/presentation/components/ItemDownloadIndicator.kt
@@ -3,7 +3,7 @@ package eu.kanade.presentation.components
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.material.ripple
+import androidx.compose.material3.ripple
import androidx.compose.ui.Modifier
import androidx.compose.ui.hapticfeedback.HapticFeedback
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt
index 5978727870..b9f7f0e204 100644
--- a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt
@@ -63,6 +63,7 @@ fun TabbedDialog(
PrimaryTabRow(
modifier = Modifier.weight(1f),
selectedTabIndex = pagerState.currentPage,
+ containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
divider = {},
) {
tabTitles.fastForEachIndexed { index, tab ->
diff --git a/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt b/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt
index 4fb22201a5..153d311a74 100644
--- a/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt
@@ -40,7 +40,7 @@ fun CrashScreen(
acceptText = stringResource(MR.strings.pref_dump_crash_logs),
onAcceptClick = {
scope.launch {
- CrashLogUtil(context).dumpLogs()
+ CrashLogUtil(context).dumpLogs(exception)
}
},
rejectText = stringResource(MR.strings.crash_screen_restart_application),
diff --git a/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt b/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt
index c24dc654bd..25cc09595d 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt
@@ -88,8 +88,7 @@ import tachiyomi.presentation.core.components.material.ExtendedFloatingActionBut
import tachiyomi.presentation.core.components.material.PullRefresh
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource
-import tachiyomi.presentation.core.util.isScrolledToEnd
-import tachiyomi.presentation.core.util.isScrollingUp
+import tachiyomi.presentation.core.util.shouldExpandFAB
import tachiyomi.source.local.entries.anime.isLocal
import java.time.Instant
import java.util.concurrent.TimeUnit
@@ -393,7 +392,7 @@ private fun AnimeScreenSmallImpl(
)
},
onClick = onContinueWatching,
- expanded = episodeListState.isScrollingUp() || episodeListState.isScrolledToEnd(),
+ expanded = episodeListState.shouldExpandFAB(),
)
}
},
@@ -403,7 +402,7 @@ private fun AnimeScreenSmallImpl(
PullRefresh(
refreshing = state.isRefreshingData,
onRefresh = onRefresh,
- enabled = { !isAnySelected },
+ enabled = !isAnySelected,
indicatorPadding = PaddingValues(top = topPadding),
) {
val layoutDirection = LocalLayoutDirection.current
@@ -680,7 +679,7 @@ fun AnimeScreenLargeImpl(
},
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
onClick = onContinueWatching,
- expanded = episodeListState.isScrollingUp() || episodeListState.isScrolledToEnd(),
+ expanded = episodeListState.shouldExpandFAB(),
)
}
},
@@ -688,7 +687,7 @@ fun AnimeScreenLargeImpl(
PullRefresh(
refreshing = state.isRefreshingData,
onRefresh = onRefresh,
- enabled = { !isAnySelected },
+ enabled = !isAnySelected,
indicatorPadding = PaddingValues(
start = insetPadding.calculateStartPadding(layoutDirection),
top = with(density) { topBarHeight.toDp() },
diff --git a/app/src/main/java/eu/kanade/presentation/entries/anime/components/AnimeCoverDialog.kt b/app/src/main/java/eu/kanade/presentation/entries/anime/components/AnimeCoverDialog.kt
index 06ac19b41b..901235c98f 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/anime/components/AnimeCoverDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/anime/components/AnimeCoverDialog.kt
@@ -37,6 +37,7 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.core.view.updatePadding
+import coil3.asDrawable
import coil3.imageLoader
import coil3.request.CachePolicy
import coil3.request.ImageRequest
diff --git a/app/src/main/java/eu/kanade/presentation/entries/anime/components/AnimeEpisodeListItem.kt b/app/src/main/java/eu/kanade/presentation/entries/anime/components/AnimeEpisodeListItem.kt
index 7afedad0b0..14393baa31 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/anime/components/AnimeEpisodeListItem.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/anime/components/AnimeEpisodeListItem.kt
@@ -69,9 +69,6 @@ fun AnimeEpisodeListItem(
onEpisodeSwipe: (LibraryPreferences.EpisodeSwipeAction) -> Unit,
modifier: Modifier = Modifier,
) {
- val textAlpha = if (seen) ReadItemAlpha else 1f
- val textSubtitleAlpha = if (seen) ReadItemAlpha else SecondaryItemAlpha
-
val start = getSwipeAction(
action = episodeSwipeStartAction,
seen = seen,
@@ -136,15 +133,20 @@ fun AnimeEpisodeListItem(
Text(
text = title,
style = MaterialTheme.typography.bodyMedium,
- color = LocalContentColor.current.copy(alpha = textAlpha),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
onTextLayout = { textHeight = it.size.height },
+ color = LocalContentColor.current.copy(alpha = if (seen) ReadItemAlpha else 1f),
)
}
- Row(modifier = Modifier.alpha(textSubtitleAlpha)) {
- ProvideTextStyle(value = MaterialTheme.typography.bodySmall) {
+ Row {
+ val subtitleStyle = MaterialTheme.typography.bodySmall
+ .merge(
+ color = LocalContentColor.current
+ .copy(alpha = if (seen) ReadItemAlpha else SecondaryItemAlpha)
+ )
+ ProvideTextStyle(value = subtitleStyle) {
if (date != null) {
Text(
text = date,
diff --git a/app/src/main/java/eu/kanade/presentation/entries/anime/components/AnimeInfoHeader.kt b/app/src/main/java/eu/kanade/presentation/entries/anime/components/AnimeInfoHeader.kt
index d143b98369..0d2c9317bc 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/anime/components/AnimeInfoHeader.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/anime/components/AnimeInfoHeader.kt
@@ -23,7 +23,6 @@ import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.selection.SelectionContainer
-import androidx.compose.material.LocalMinimumInteractiveComponentEnforcement
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Brush
import androidx.compose.material.icons.filled.Favorite
@@ -43,6 +42,7 @@ import androidx.compose.material.icons.outlined.Sync
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.LocalMinimumInteractiveComponentSize
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
@@ -651,7 +651,7 @@ private fun TagsChip(
modifier: Modifier = Modifier,
onClick: () -> Unit,
) {
- CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) {
+ CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
SuggestionChip(
modifier = modifier,
onClick = onClick,
diff --git a/app/src/main/java/eu/kanade/presentation/entries/components/EntryBottomActionMenu.kt b/app/src/main/java/eu/kanade/presentation/entries/components/EntryBottomActionMenu.kt
index bd12639d39..ad21f681e2 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/components/EntryBottomActionMenu.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/components/EntryBottomActionMenu.kt
@@ -8,7 +8,6 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.combinedClickable
-import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -32,11 +31,11 @@ import androidx.compose.material.icons.outlined.Download
import androidx.compose.material.icons.outlined.Input
import androidx.compose.material.icons.outlined.OpenInNew
import androidx.compose.material.icons.outlined.RemoveDone
-import androidx.compose.material.ripple
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
+import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
@@ -94,7 +93,7 @@ fun EntryBottomActionMenu(
bottomEnd = ZeroCornerSize,
bottomStart = ZeroCornerSize,
),
- tonalElevation = 3.dp,
+ color = MaterialTheme.colorScheme.surfaceContainerHigh,
) {
val haptic = LocalHapticFeedback.current
val confirm = remember { mutableStateListOf(false, false, false, false, false, false, false, false, false) }
@@ -234,7 +233,7 @@ private fun RowScope.Button(
.size(48.dp)
.weight(animatedWeight)
.combinedClickable(
- interactionSource = remember { MutableInteractionSource() },
+ interactionSource = null,
indication = ripple(bounded = false),
onLongClick = onLongClick,
onClick = onClick,
@@ -285,7 +284,7 @@ fun LibraryBottomActionMenu(
bottomEnd = ZeroCornerSize,
bottomStart = ZeroCornerSize,
),
- tonalElevation = 3.dp,
+ color = MaterialTheme.colorScheme.surfaceContainerHigh,
) {
val haptic = LocalHapticFeedback.current
val confirm = remember { mutableStateListOf(false, false, false, false, false) }
diff --git a/app/src/main/java/eu/kanade/presentation/entries/components/ItemsDialogs.kt b/app/src/main/java/eu/kanade/presentation/entries/components/ItemsDialogs.kt
index 4ea417e903..20906d6b8d 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/components/ItemsDialogs.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/components/ItemsDialogs.kt
@@ -110,9 +110,18 @@ fun SetIntervalDialog(
),
),
)
-
- Spacer(Modifier.height(MaterialTheme.padding.small))
+ } else {
+ Text(
+ stringResource(
+ if (isManga) {
+ MR.strings.manga_interval_expected_update_null
+ } else {
+ MR.strings.anime_interval_expected_update_null
+ },
+ ),
+ )
}
+ Spacer(Modifier.height(MaterialTheme.padding.small))
if (onValueChanged != null && (isDevFlavor || isPreviewBuildType)) {
Text(stringResource(MR.strings.manga_interval_custom_amount))
diff --git a/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt
index d20848fc58..ce0041890c 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt
@@ -81,8 +81,7 @@ import tachiyomi.presentation.core.components.material.ExtendedFloatingActionBut
import tachiyomi.presentation.core.components.material.PullRefresh
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource
-import tachiyomi.presentation.core.util.isScrolledToEnd
-import tachiyomi.presentation.core.util.isScrollingUp
+import tachiyomi.presentation.core.util.shouldExpandFAB
import tachiyomi.source.local.entries.manga.isLocal
import java.time.Instant
@@ -363,7 +362,7 @@ private fun MangaScreenSmallImpl(
},
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
onClick = onContinueReading,
- expanded = chapterListState.isScrollingUp() || chapterListState.isScrolledToEnd(),
+ expanded = chapterListState.shouldExpandFAB(),
)
}
},
@@ -373,7 +372,7 @@ private fun MangaScreenSmallImpl(
PullRefresh(
refreshing = state.isRefreshingData,
onRefresh = onRefresh,
- enabled = { !isAnySelected },
+ enabled = !isAnySelected,
indicatorPadding = PaddingValues(top = topPadding),
) {
val layoutDirection = LocalLayoutDirection.current
@@ -616,7 +615,7 @@ fun MangaScreenLargeImpl(
},
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
onClick = onContinueReading,
- expanded = chapterListState.isScrollingUp() || chapterListState.isScrolledToEnd(),
+ expanded = chapterListState.shouldExpandFAB(),
)
}
},
@@ -624,7 +623,7 @@ fun MangaScreenLargeImpl(
PullRefresh(
refreshing = state.isRefreshingData,
onRefresh = onRefresh,
- enabled = { !isAnySelected },
+ enabled = !isAnySelected,
indicatorPadding = PaddingValues(
start = insetPadding.calculateStartPadding(layoutDirection),
top = with(density) { topBarHeight.toDp() },
diff --git a/app/src/main/java/eu/kanade/presentation/entries/manga/components/MangaChapterListItem.kt b/app/src/main/java/eu/kanade/presentation/entries/manga/components/MangaChapterListItem.kt
index 3b66d35721..79a1da0705 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/manga/components/MangaChapterListItem.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/manga/components/MangaChapterListItem.kt
@@ -30,7 +30,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
@@ -67,9 +66,6 @@ fun MangaChapterListItem(
onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit,
modifier: Modifier = Modifier,
) {
- val textAlpha = if (read) ReadItemAlpha else 1f
- val textSubtitleAlpha = if (read) ReadItemAlpha else SecondaryItemAlpha
-
val start = getSwipeAction(
action = chapterSwipeStartAction,
read = read,
@@ -135,15 +131,20 @@ fun MangaChapterListItem(
Text(
text = title,
style = MaterialTheme.typography.bodyMedium,
- color = LocalContentColor.current.copy(alpha = textAlpha),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
onTextLayout = { textHeight = it.size.height },
+ color = LocalContentColor.current.copy(alpha = if (read) ReadItemAlpha else 1f),
)
}
- Row(modifier = Modifier.alpha(textSubtitleAlpha)) {
- ProvideTextStyle(value = MaterialTheme.typography.bodySmall) {
+ Row {
+ val subtitleStyle = MaterialTheme.typography.bodySmall
+ .merge(
+ color = LocalContentColor.current
+ .copy(alpha = if (read) ReadItemAlpha else SecondaryItemAlpha)
+ )
+ ProvideTextStyle(value = subtitleStyle) {
if (date != null) {
Text(
text = date,
diff --git a/app/src/main/java/eu/kanade/presentation/entries/manga/components/MangaCoverDialog.kt b/app/src/main/java/eu/kanade/presentation/entries/manga/components/MangaCoverDialog.kt
index a7ff5a563a..eabe209ce2 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/manga/components/MangaCoverDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/manga/components/MangaCoverDialog.kt
@@ -37,6 +37,7 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.core.view.updatePadding
+import coil3.asDrawable
import coil3.imageLoader
import coil3.request.CachePolicy
import coil3.request.ImageRequest
diff --git a/app/src/main/java/eu/kanade/presentation/entries/manga/components/MangaInfoHeader.kt b/app/src/main/java/eu/kanade/presentation/entries/manga/components/MangaInfoHeader.kt
index 2080940cd7..56d79df5f1 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/manga/components/MangaInfoHeader.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/manga/components/MangaInfoHeader.kt
@@ -23,7 +23,6 @@ import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.selection.SelectionContainer
-import androidx.compose.material.LocalMinimumInteractiveComponentEnforcement
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Brush
import androidx.compose.material.icons.filled.Favorite
@@ -43,6 +42,7 @@ import androidx.compose.material.icons.outlined.Sync
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.LocalMinimumInteractiveComponentSize
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
@@ -653,7 +653,7 @@ private fun TagsChip(
modifier: Modifier = Modifier,
onClick: () -> Unit,
) {
- CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) {
+ CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
SuggestionChip(
modifier = modifier,
onClick = onClick,
diff --git a/app/src/main/java/eu/kanade/presentation/entries/manga/components/ScanlatorFilterDialog.kt b/app/src/main/java/eu/kanade/presentation/entries/manga/components/ScanlatorFilterDialog.kt
index d773ef7572..aef644c6c0 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/manga/components/ScanlatorFilterDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/manga/components/ScanlatorFilterDialog.kt
@@ -31,8 +31,6 @@ import androidx.compose.ui.window.DialogProperties
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource
-import tachiyomi.presentation.core.util.isScrolledToEnd
-import tachiyomi.presentation.core.util.isScrolledToStart
@Composable
fun ScanlatorFilterDialog(
@@ -96,8 +94,8 @@ fun ScanlatorFilterDialog(
}
}
}
- if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
- if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
+ if (state.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
+ if (state.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
}
},
properties = DialogProperties(
diff --git a/app/src/main/java/eu/kanade/presentation/history/anime/AnimeHistoryScreen.kt b/app/src/main/java/eu/kanade/presentation/history/anime/AnimeHistoryScreen.kt
index 8f1add91b7..5aa141c742 100644
--- a/app/src/main/java/eu/kanade/presentation/history/anime/AnimeHistoryScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/history/anime/AnimeHistoryScreen.kt
@@ -84,14 +84,14 @@ private fun AnimeHistoryScreenContent(
when (item) {
is AnimeHistoryUiModel.Header -> {
ListGroupHeader(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
text = relativeDateText(item.date),
)
}
is AnimeHistoryUiModel.Item -> {
val value = item.item
AnimeHistoryItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
history = value,
onClickCover = { onClickCover(value) },
onClickResume = { onClickResume(value) },
diff --git a/app/src/main/java/eu/kanade/presentation/history/manga/MangaHistoryScreen.kt b/app/src/main/java/eu/kanade/presentation/history/manga/MangaHistoryScreen.kt
index 7734578b90..eceb1bd695 100644
--- a/app/src/main/java/eu/kanade/presentation/history/manga/MangaHistoryScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/history/manga/MangaHistoryScreen.kt
@@ -84,14 +84,14 @@ private fun MangaHistoryScreenContent(
when (item) {
is MangaHistoryUiModel.Header -> {
ListGroupHeader(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
text = relativeDateText(item.date),
)
}
is MangaHistoryUiModel.Item -> {
val value = item.item
MangaHistoryItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
history = value,
onClickCover = { onClickCover(value) },
onClickResume = { onClickResume(value) },
diff --git a/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibraryContent.kt b/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibraryContent.kt
index a0762420e1..035c3ccfdc 100644
--- a/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibraryContent.kt
+++ b/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibraryContent.kt
@@ -94,7 +94,7 @@ fun AnimeLibraryContent(
isRefreshing = false
}
},
- enabled = { notSelectionMode },
+ enabled = notSelectionMode,
) {
AnimeLibraryPager(
state = pagerState,
diff --git a/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibrarySettingsDialog.kt
index 5059ee4bfc..3172cf6f93 100644
--- a/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibrarySettingsDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibrarySettingsDialog.kt
@@ -9,6 +9,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@@ -125,7 +126,7 @@ private fun ColumnScope.FilterPage(
)
}
- val trackers = remember { screenModel.trackers }
+ val trackers by screenModel.trackersFlow.collectAsState()
when (trackers.size) {
0 -> {
// No trackers
@@ -162,15 +163,15 @@ private fun ColumnScope.SortPage(
category: Category?,
screenModel: AnimeLibrarySettingsScreenModel,
) {
+ val trackers by screenModel.trackersFlow.collectAsState()
val sortingMode = category.sort.type
val sortDescending = !category.sort.isAscending
- val trackerSortOption =
- if (screenModel.trackers.isEmpty()) {
- emptyList()
- } else {
- listOf(MR.strings.action_sort_tracker_score to AnimeLibrarySort.Type.TrackerMean)
- }
+ val trackerSortOption = if (trackers.isEmpty()) {
+ emptyList()
+ } else {
+ listOf(MR.strings.action_sort_tracker_score to AnimeLibrarySort.Type.TrackerMean)
+ }
listOf(
MR.strings.action_sort_alpha to AnimeLibrarySort.Type.Alphabetical,
diff --git a/app/src/main/java/eu/kanade/presentation/library/components/CommonEntryItem.kt b/app/src/main/java/eu/kanade/presentation/library/components/CommonEntryItem.kt
index 9738a8bb47..43c9ecd4c6 100644
--- a/app/src/main/java/eu/kanade/presentation/library/components/CommonEntryItem.kt
+++ b/app/src/main/java/eu/kanade/presentation/library/components/CommonEntryItem.kt
@@ -35,23 +35,30 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import eu.kanade.presentation.entries.components.ItemCover
-import tachiyomi.domain.entries.EntryCover
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.BadgeGroup
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.selectedBackground
+import tachiyomi.domain.entries.EntryCover as EntryCoverModel
object CommonEntryItemDefaults {
val GridHorizontalSpacer = 4.dp
val GridVerticalSpacer = 4.dp
+ @Suppress("ConstPropertyName")
const val BrowseFavoriteCoverAlpha = 0.34f
}
-private val ContinueViewingButtonSize = 28.dp
+private val ContinueViewingButtonSizeSmall = 28.dp
+private val ContinueViewingButtonSizeLarge = 32.dp
+
+private val ContinueViewingButtonIconSizeSmall = 16.dp
+private val ContinueViewingButtonIconSizeLarge = 20.dp
+
private val ContinueViewingButtonGridPadding = 6.dp
private val ContinueViewingButtonListSpacing = 8.dp
@@ -63,7 +70,7 @@ private const val GridSelectedCoverAlpha = 0.76f
*/
@Composable
fun EntryCompactGridItem(
- coverData: EntryCover,
+ coverData: EntryCoverModel,
onClick: () -> Unit,
onLongClick: () -> Unit,
isSelected: Boolean = false,
@@ -97,10 +104,12 @@ fun EntryCompactGridItem(
)
} else if (onClickContinueViewing != null) {
ContinueViewingButton(
+ size = ContinueViewingButtonSizeLarge,
+ iconSize = ContinueViewingButtonIconSizeLarge,
+ onClick = onClickContinueViewing,
modifier = Modifier
.padding(ContinueViewingButtonGridPadding)
.align(Alignment.BottomEnd),
- onClickContinueViewing = onClickContinueViewing,
)
}
},
@@ -149,11 +158,13 @@ private fun BoxScope.CoverTextOverlay(
)
if (onClickContinueViewing != null) {
ContinueViewingButton(
+ size = ContinueViewingButtonSizeSmall,
+ iconSize = ContinueViewingButtonIconSizeSmall,
+ onClick = onClickContinueViewing,
modifier = Modifier.padding(
end = ContinueViewingButtonGridPadding,
bottom = ContinueViewingButtonGridPadding,
),
- onClickContinueViewing = onClickContinueViewing,
)
}
}
@@ -169,7 +180,7 @@ fun EntryComfortableGridItem(
onClick: () -> Unit,
onLongClick: () -> Unit,
titleMaxLines: Int = 2,
- coverData: EntryCover,
+ coverData: EntryCoverModel,
coverAlpha: Float = 1f,
coverBadgeStart: (@Composable RowScope.() -> Unit)? = null,
coverBadgeEnd: (@Composable RowScope.() -> Unit)? = null,
@@ -195,10 +206,12 @@ fun EntryComfortableGridItem(
content = {
if (onClickContinueViewing != null) {
ContinueViewingButton(
+ size = ContinueViewingButtonSizeLarge,
+ iconSize = ContinueViewingButtonIconSizeLarge,
+ onClick = onClickContinueViewing,
modifier = Modifier
.padding(ContinueViewingButtonGridPadding)
.align(Alignment.BottomEnd),
- onClickContinueViewing = onClickContinueViewing,
)
}
},
@@ -310,7 +323,7 @@ private fun GridItemSelectable(
private fun Modifier.selectedOutline(
isSelected: Boolean,
color: Color,
-) = this then drawBehind { if (isSelected) drawRect(color = color) }
+) = drawBehind { if (isSelected) drawRect(color = color) }
/**
* Layout of list item.
@@ -319,7 +332,7 @@ private fun Modifier.selectedOutline(
fun EntryListItem(
isSelected: Boolean = false,
title: String,
- coverData: EntryCover,
+ coverData: EntryCoverModel,
coverAlpha: Float = 1f,
onLongClick: () -> Unit,
onClick: () -> Unit,
@@ -355,8 +368,10 @@ fun EntryListItem(
BadgeGroup(content = badge)
if (onClickContinueViewing != null) {
ContinueViewingButton(
+ size = ContinueViewingButtonSizeSmall,
+ iconSize = ContinueViewingButtonIconSizeSmall,
+ onClick = onClickContinueViewing,
modifier = Modifier.padding(start = ContinueViewingButtonListSpacing),
- onClickContinueViewing = onClickContinueViewing,
)
}
}
@@ -364,23 +379,25 @@ fun EntryListItem(
@Composable
private fun ContinueViewingButton(
+ size: Dp,
+ iconSize: Dp,
+ onClick: () -> Unit,
modifier: Modifier = Modifier,
- onClickContinueViewing: () -> Unit,
) {
Box(modifier = modifier) {
FilledIconButton(
- onClick = onClickContinueViewing,
- modifier = Modifier.size(ContinueViewingButtonSize),
+ onClick = onClick,
shape = MaterialTheme.shapes.small,
colors = IconButtonDefaults.filledIconButtonColors(
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.9f),
contentColor = contentColorFor(MaterialTheme.colorScheme.primaryContainer),
),
+ modifier = Modifier.size(size)
) {
Icon(
imageVector = Icons.Filled.PlayArrow,
contentDescription = stringResource(MR.strings.action_resume),
- modifier = Modifier.size(16.dp),
+ modifier = Modifier.size(iconSize),
)
}
}
diff --git a/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibraryContent.kt b/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibraryContent.kt
index 9fb0d7c3d9..1ddb4a26d4 100644
--- a/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibraryContent.kt
+++ b/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibraryContent.kt
@@ -94,7 +94,7 @@ fun MangaLibraryContent(
isRefreshing = false
}
},
- enabled = { notSelectionMode },
+ enabled = notSelectionMode,
) {
MangaLibraryPager(
state = pagerState,
diff --git a/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibrarySettingsDialog.kt
index 9e0a59f431..d3ecf57858 100644
--- a/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibrarySettingsDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibrarySettingsDialog.kt
@@ -9,6 +9,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@@ -125,7 +126,7 @@ private fun ColumnScope.FilterPage(
)
}
- val trackers = remember { screenModel.trackers }
+ val trackers by screenModel.trackersFlow.collectAsState()
when (trackers.size) {
0 -> {
// No trackers
@@ -162,15 +163,15 @@ private fun ColumnScope.SortPage(
category: Category?,
screenModel: MangaLibrarySettingsScreenModel,
) {
+ val trackers by screenModel.trackersFlow.collectAsState()
val sortingMode = category.sort.type
val sortDescending = !category.sort.isAscending
- val trackerSortOption =
- if (screenModel.trackers.isEmpty()) {
- emptyList()
- } else {
- listOf(MR.strings.action_sort_tracker_score to MangaLibrarySort.Type.TrackerMean)
- }
+ val trackerSortOption = if (trackers.isEmpty()) {
+ emptyList()
+ } else {
+ listOf(MR.strings.action_sort_tracker_score to MangaLibrarySort.Type.TrackerMean)
+ }
listOf(
MR.strings.action_sort_alpha to MangaLibrarySort.Type.Alphabetical,
diff --git a/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt
index 79e45159f9..8b3d9c07bc 100644
--- a/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt
@@ -28,11 +28,11 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.unit.dp
import androidx.core.content.getSystemService
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.compose.LocalLifecycleOwner
import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState
import eu.kanade.tachiyomi.util.system.launchRequestPackageInstallsPermission
import tachiyomi.i18n.MR
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt
index e428b2288c..9af40d8f1d 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt
@@ -7,12 +7,12 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.unit.dp
-import eu.kanade.domain.track.service.TrackPreferences
import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget
import eu.kanade.presentation.more.settings.widget.InfoWidget
import eu.kanade.presentation.more.settings.widget.ListPreferenceWidget
@@ -23,8 +23,6 @@ import eu.kanade.presentation.more.settings.widget.TrackingPreferenceWidget
import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.SliderItem
import tachiyomi.presentation.core.util.collectAsState
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
val LocalPreferenceHighlighted = compositionLocalOf(structuralEqualityPolicy()) { false }
val LocalPreferenceMinHeight = compositionLocalOf(structuralEqualityPolicy()) { 56.dp }
@@ -172,16 +170,14 @@ internal fun PreferenceItem(
)
}
is Preference.PreferenceItem.TrackerPreference -> {
- val uName by Injekt.get()
- .trackUsername(item.tracker)
- .collectAsState()
- item.tracker.run {
- TrackingPreferenceWidget(
- tracker = this,
- checked = uName.isNotEmpty(),
- onClick = { if (isLoggedIn) item.logout() else item.login() },
- )
+ val isLoggedIn by item.tracker.let { tracker ->
+ tracker.isLoggedInFlow.collectAsState(tracker.isLoggedIn)
}
+ TrackingPreferenceWidget(
+ tracker = item.tracker,
+ checked = isLoggedIn,
+ onClick = { if (isLoggedIn) item.logout() else item.login() },
+ )
}
is Preference.PreferenceItem.InfoPreference -> {
InfoWidget(text = item.title)
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt
index 422bd3ae34..d15d6ccf5c 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt
@@ -113,7 +113,17 @@ object SettingsDataScreen : SearchableSettings {
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- context.contentResolver.takePersistableUriPermission(uri, flags)
+ // For some reason InkBook devices do not implement the SAF properly. Persistable URI grants do not
+ // work. However, simply retrieving the URI and using it works fine for these devices. Access is not
+ // revoked after the app is closed or the device is restarted.
+ // This also holds for some Samsung devices. Thus, we simply execute inside of a try-catch block and
+ // ignore the exception if it is thrown.
+ try {
+ context.contentResolver.takePersistableUriPermission(uri, flags)
+ } catch (e: SecurityException) {
+ logcat(LogPriority.ERROR, e)
+ context.toast(MR.strings.file_picker_uri_permission_unsupported)
+ }
UniFile.fromUri(context, uri)?.let {
storageDirPref.set(it.uri.toString())
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt
index 56dc2dbfc0..e88d66757f 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt
@@ -14,6 +14,7 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toImmutableMap
import tachiyomi.i18n.MR
+import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
@@ -61,12 +62,8 @@ object SettingsReaderScreen : SearchableSettings {
pref = readerPref.pageTransitions(),
title = stringResource(MR.strings.pref_page_transitions),
),
- Preference.PreferenceItem.SwitchPreference(
- pref = readerPref.flashOnPageChange(),
- title = stringResource(MR.strings.pref_flash_page),
- subtitle = stringResource(MR.strings.pref_flash_page_summ),
- ),
getDisplayGroup(readerPreferences = readerPref),
+ getEInkGroup(readerPreferences = readerPref),
getReadingGroup(readerPreferences = readerPref),
getPagedGroup(readerPreferences = readerPref),
getWebtoonGroup(readerPreferences = readerPref),
@@ -122,6 +119,65 @@ object SettingsReaderScreen : SearchableSettings {
)
}
+ @Composable
+ private fun getEInkGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
+ val flashPageState by readerPreferences.flashOnPageChange().collectAsState()
+
+ val flashMillisPref = readerPreferences.flashDurationMillis()
+ val flashMillis by flashMillisPref.collectAsState()
+
+ val flashIntervalPref = readerPreferences.flashPageInterval()
+ val flashInterval by flashIntervalPref.collectAsState()
+
+ val flashColorPref = readerPreferences.flashColor()
+
+ return Preference.PreferenceGroup(
+ title = "E-Ink",
+ preferenceItems = persistentListOf(
+ Preference.PreferenceItem.SwitchPreference(
+ pref = readerPreferences.flashOnPageChange(),
+ title = stringResource(MR.strings.pref_flash_page),
+ subtitle = stringResource(MR.strings.pref_flash_page_summ),
+ ),
+ Preference.PreferenceItem.SliderPreference(
+ value = flashMillis / ReaderPreferences.MILLI_CONVERSION,
+ min = 1,
+ max = 15,
+ title = stringResource(MR.strings.pref_flash_duration),
+ subtitle = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
+ onValueChanged = {
+ flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION)
+ true
+ },
+ enabled = flashPageState,
+ ),
+ Preference.PreferenceItem.SliderPreference(
+ value = flashInterval,
+ min = 1,
+ max = 10,
+ title = stringResource(MR.strings.pref_flash_page_interval),
+ subtitle = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
+ onValueChanged = {
+ flashIntervalPref.set(it)
+ true
+ },
+ enabled = flashPageState,
+ ),
+ Preference.PreferenceItem.ListPreference(
+ pref = flashColorPref,
+ title = stringResource(MR.strings.pref_flash_with),
+ entries = persistentMapOf(
+ ReaderPreferences.FlashColor.BLACK to stringResource(MR.strings.pref_flash_style_black),
+ ReaderPreferences.FlashColor.WHITE to stringResource(MR.strings.pref_flash_style_white),
+ ReaderPreferences.FlashColor.WHITE_BLACK
+ to stringResource(MR.strings.pref_flash_style_white_black),
+ ),
+ enabled = flashPageState,
+ ),
+ ),
+ )
+ }
+
@Composable
private fun getReadingGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
return Preference.PreferenceGroup(
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt
index 20a924d112..e6b2a227f6 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt
@@ -46,7 +46,7 @@ fun ExtensionReposContent(
repos.forEach {
item {
ExtensionRepoListItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
repo = it,
onOpenWebsite = { onOpenWebsite(it) },
onDelete = { onClickDelete(it.baseUrl) },
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt
index 5022f44f05..239f917d0b 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt
@@ -1,6 +1,7 @@
package eu.kanade.presentation.more.settings.screen.browse.components
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
@@ -14,6 +15,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.text.input.KeyboardType
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.coroutines.delay
import mihon.domain.extensionrepo.model.ExtensionRepo
@@ -74,6 +76,7 @@ fun ExtensionRepoCreateDialog(
Text(text = stringResource(msgRes))
},
isError = name.isNotEmpty() && nameAlreadyExists,
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri),
singleLine = true,
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt
index 773a3d4fb8..c1b94764ec 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt
@@ -223,13 +223,12 @@ fun AppThemePreviewItem(
contentAlignment = Alignment.BottomCenter,
) {
Surface(
- tonalElevation = 3.dp,
+ color = MaterialTheme.colorScheme.surfaceContainer,
) {
Row(
modifier = Modifier
.height(32.dp)
.fillMaxWidth()
- .background(MaterialTheme.colorScheme.surfaceVariant)
.padding(horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt
index afd4bc45b8..7daf651688 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt
@@ -6,13 +6,13 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.selection.selectable
-import androidx.compose.material.minimumInteractiveComponentSize
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
+import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -26,8 +26,6 @@ import androidx.compose.ui.unit.dp
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.i18n.stringResource
-import tachiyomi.presentation.core.util.isScrolledToEnd
-import tachiyomi.presentation.core.util.isScrolledToStart
@Composable
fun ListPreferenceWidget(
@@ -69,16 +67,8 @@ fun ListPreferenceWidget(
}
}
}
- if (!state.isScrolledToStart()) {
- HorizontalDivider(
- modifier = Modifier.align(Alignment.TopCenter),
- )
- }
- if (!state.isScrolledToEnd()) {
- HorizontalDivider(
- modifier = Modifier.align(Alignment.BottomCenter),
- )
- }
+ if (state.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
+ if (state.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
}
},
confirmButton = {
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt
index 4366806a8c..5f30ae245a 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt
@@ -31,8 +31,6 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
-import tachiyomi.presentation.core.util.isScrolledToEnd
-import tachiyomi.presentation.core.util.isScrolledToStart
private enum class State {
CHECKED, INVERSED, UNCHECKED
@@ -117,16 +115,8 @@ fun TriStateListDialog(
}
}
- if (!listState.isScrolledToStart()) {
- HorizontalDivider(
- modifier = Modifier.align(Alignment.TopCenter),
- )
- }
- if (!listState.isScrolledToEnd()) {
- HorizontalDivider(
- modifier = Modifier.align(Alignment.BottomCenter),
- )
- }
+ if (listState.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
+ if (listState.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
}
}
},
diff --git a/app/src/main/java/eu/kanade/presentation/reader/DisplayRefreshHost.kt b/app/src/main/java/eu/kanade/presentation/reader/DisplayRefreshHost.kt
index 82d3cad0fa..ecf26119d0 100644
--- a/app/src/main/java/eu/kanade/presentation/reader/DisplayRefreshHost.kt
+++ b/app/src/main/java/eu/kanade/presentation/reader/DisplayRefreshHost.kt
@@ -7,19 +7,42 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
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.graphics.Color
+import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import kotlinx.coroutines.delay
-import kotlin.time.Duration.Companion.seconds
+import tachiyomi.presentation.core.util.collectAsState
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+import kotlin.time.Duration.Companion.milliseconds
@Stable
class DisplayRefreshHost {
internal var currentDisplayRefresh by mutableStateOf(false)
+ private val readerPreferences = Injekt.get()
+
+ internal val flashMillis = readerPreferences.flashDurationMillis()
+ internal val flashMode = readerPreferences.flashColor()
+
+ internal val flashIntervalPref = readerPreferences.flashPageInterval()
+
+ // Internal State for Flash
+ private var flashInterval = flashIntervalPref.get()
+ private var timesCalled = 0
fun flash() {
- currentDisplayRefresh = true
+ if (timesCalled % flashInterval == 0) {
+ currentDisplayRefresh = true
+ }
+ timesCalled += 1
+ }
+
+ fun setInterval(interval: Int) {
+ flashInterval = interval
+ timesCalled = 0
}
}
@@ -29,18 +52,39 @@ fun DisplayRefreshHost(
modifier: Modifier = Modifier,
) {
val currentDisplayRefresh = hostState.currentDisplayRefresh
+ val refreshDuration by hostState.flashMillis.collectAsState()
+ val flashMode by hostState.flashMode.collectAsState()
+ val flashInterval by hostState.flashIntervalPref.collectAsState()
+
+ var currentColor by remember { mutableStateOf(null) }
+
LaunchedEffect(currentDisplayRefresh) {
- if (currentDisplayRefresh) {
- delay(1.5.seconds)
- hostState.currentDisplayRefresh = false
+ if (!currentDisplayRefresh) {
+ currentColor = null
+ return@LaunchedEffect
+ }
+
+ val refreshDurationHalf = refreshDuration.milliseconds / 2
+ currentColor = if (flashMode == ReaderPreferences.FlashColor.BLACK) {
+ Color.Black
+ } else {
+ Color.White
+ }
+ delay(refreshDurationHalf)
+ if (flashMode == ReaderPreferences.FlashColor.WHITE_BLACK) {
+ currentColor = Color.Black
}
+ delay(refreshDurationHalf)
+ hostState.currentDisplayRefresh = false
+ }
+
+ LaunchedEffect(flashInterval) {
+ hostState.setInterval(flashInterval)
}
Canvas(
modifier = modifier.fillMaxSize(),
) {
- if (currentDisplayRefresh) {
- drawRect(Color.Black)
- }
+ currentColor?.let { drawRect(it) }
}
}
diff --git a/app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt b/app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt
index 70cc58f207..b83e8d6adf 100644
--- a/app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt
@@ -33,9 +33,7 @@ fun ReaderPageActionsDialog(
) {
var showSetCoverDialog by remember { mutableStateOf(false) }
- AdaptiveSheet(
- onDismissRequest = onDismissRequest,
- ) {
+ AdaptiveSheet(onDismissRequest = onDismissRequest) {
Row(
modifier = Modifier.padding(vertical = 16.dp),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt
index 2f2832feb5..0bb2da6cc0 100644
--- a/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt
+++ b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt
@@ -5,10 +5,13 @@ import androidx.compose.material3.FilterChip
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.SettingsChipRow
+import tachiyomi.presentation.core.components.SliderItem
+import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState
@@ -19,9 +22,27 @@ private val themes = listOf(
MR.strings.automatic_background to 3,
)
+private val flashColors = listOf(
+ MR.strings.pref_flash_style_black to ReaderPreferences.FlashColor.BLACK,
+ MR.strings.pref_flash_style_white to ReaderPreferences.FlashColor.WHITE,
+ MR.strings.pref_flash_style_white_black to ReaderPreferences.FlashColor.WHITE_BLACK,
+)
+
@Composable
internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
val readerTheme by screenModel.preferences.readerTheme().collectAsState()
+
+ val flashPageState by screenModel.preferences.flashOnPageChange().collectAsState()
+
+ val flashMillisPref = screenModel.preferences.flashDurationMillis()
+ val flashMillis by flashMillisPref.collectAsState()
+
+ val flashIntervalPref = screenModel.preferences.flashPageInterval()
+ val flashInterval by flashIntervalPref.collectAsState()
+
+ val flashColorPref = screenModel.preferences.flashColor()
+ val flashColor by flashColorPref.collectAsState()
+
SettingsChipRow(MR.strings.pref_reader_theme) {
themes.map { (labelRes, value) ->
FilterChip(
@@ -73,4 +94,33 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
label = stringResource(MR.strings.pref_flash_page),
pref = screenModel.preferences.flashOnPageChange(),
)
+ if (flashPageState) {
+ SliderItem(
+ value = flashMillis / ReaderPreferences.MILLI_CONVERSION,
+ label = stringResource(MR.strings.pref_flash_duration),
+ valueText = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
+ onChange = { flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION) },
+ min = 1,
+ max = 15,
+ )
+ SliderItem(
+ value = flashInterval,
+ label = stringResource(MR.strings.pref_flash_page_interval),
+ valueText = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
+ onChange = {
+ flashIntervalPref.set(it)
+ },
+ min = 1,
+ max = 10,
+ )
+ SettingsChipRow(MR.strings.pref_flash_with) {
+ flashColors.map { (labelRes, value) ->
+ FilterChip(
+ selected = flashColor == value,
+ onClick = { flashColorPref.set(value) },
+ label = { Text(stringResource(labelRes)) },
+ )
+ }
+ }
+ }
}
diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/BaseColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/BaseColorScheme.kt
index 97455fd9e4..22dd9a0a79 100644
--- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/BaseColorScheme.kt
+++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/BaseColorScheme.kt
@@ -8,6 +8,12 @@ internal abstract class BaseColorScheme {
abstract val darkScheme: ColorScheme
abstract val lightScheme: ColorScheme
+ // Cannot be pure black as there's content scrolling behind it
+ // https://m3.material.io/components/navigation-bar/guidelines#90615a71-607e-485e-9e09-778bfc080563
+ private val surfaceContainer = Color(0xFF0C0C0C)
+ private val surfaceContainerHigh = Color(0xFF131313)
+ private val surfaceContainerHighest = Color(0xFF1B1B1B)
+
fun getColorScheme(isDark: Boolean, isAmoled: Boolean): ColorScheme {
if (!isDark) return lightScheme
@@ -18,6 +24,12 @@ internal abstract class BaseColorScheme {
onBackground = Color.White,
surface = Color.Black,
onSurface = Color.White,
+ surfaceVariant = surfaceContainer, // Navigation bar background (ThemePrefWidget)
+ surfaceContainerLowest = surfaceContainer,
+ surfaceContainerLow = surfaceContainer,
+ surfaceContainer = surfaceContainer, // Navigation bar background
+ surfaceContainerHigh = surfaceContainerHigh,
+ surfaceContainerHighest = surfaceContainerHighest,
)
}
}
diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/GreenAppleColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/GreenAppleColorScheme.kt
index 354faed5f2..566cce91bc 100644
--- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/GreenAppleColorScheme.kt
+++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/GreenAppleColorScheme.kt
@@ -19,53 +19,77 @@ internal object GreenAppleColorScheme : BaseColorScheme() {
override val darkScheme = darkColorScheme(
primary = Color(0xFF7ADB8F),
- onPrimary = Color(0xFF003915),
- primaryContainer = Color(0xFF005322),
- onPrimaryContainer = Color(0xFF96F8A9),
- inversePrimary = Color(0xFF006D2F),
- secondary = Color(0xFF7ADB8F),
- onSecondary = Color(0xFF003915),
- secondaryContainer = Color(0xFF005322),
- onSecondaryContainer = Color(0xFF96F8A9),
- tertiary = Color(0xFFFFB3AA),
- onTertiary = Color(0xFF680006),
- tertiaryContainer = Color(0xFF93000D),
- onTertiaryContainer = Color(0xFFFFDAD5),
- background = Color(0xFF1A1C19),
- onBackground = Color(0xFFE1E3DD),
- surface = Color(0xFF1A1C19),
- onSurface = Color(0xFFE1E3DD),
- surfaceVariant = Color(0xFF414941),
- onSurfaceVariant = Color(0xFFC1C8BE),
- surfaceTint = Color(0xFF7ADB8F),
- inverseSurface = Color(0xFFE1E3DD),
- inverseOnSurface = Color(0xFF1A1C19),
- outline = Color(0xFF8B9389),
+ onPrimary = Color(0xFF003917),
+ primaryContainer = Color(0xFF017737),
+ onPrimaryContainer = Color(0xFFFFFFFF),
+ secondary = Color(0xFF7ADB8F), // Unread badge
+ onSecondary = Color(0xFF003917), // Unread badge text
+ secondaryContainer = Color(0xFF017737), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFFFFFFFF), // Navigation bar selected icon
+ tertiary = Color(0xFFFFB3AC), // Downloaded badge
+ onTertiary = Color(0xFF680008), // Downloaded badge text
+ tertiaryContainer = Color(0xFFC7282A),
+ onTertiaryContainer = Color(0xFFFFFFFF),
+ error = Color(0xFFFFB4AB),
+ onError = Color(0xFF690005),
+ errorContainer = Color(0xFF93000A),
+ onErrorContainer = Color(0xFFFFDAD6),
+ background = Color(0xFF0F1510),
+ onBackground = Color(0xFFDFE4DB),
+ surface = Color(0xFF0F1510),
+ onSurface = Color(0xFFDFE4DB),
+ surfaceVariant = Color(0xFF3F493F), // Navigation bar background (ThemePrefWidget)
+ onSurfaceVariant = Color(0xFFBECABC),
+ outline = Color(0xFF889487),
+ outlineVariant = Color(0xFF3F493F),
+ scrim = Color(0xFF000000),
+ inverseSurface = Color(0xFFDFE4DB),
+ inverseOnSurface = Color(0xFF2C322C),
+ inversePrimary = Color(0xFF006D32),
+ surfaceDim = Color(0xFF0F1510),
+ surfaceBright = Color(0xFF353B35),
+ surfaceContainerLowest = Color(0xFF0A0F0B),
+ surfaceContainerLow = Color(0xFF181D18),
+ surfaceContainer = Color(0xFF1C211C), // Navigation bar background
+ surfaceContainerHigh = Color(0xFF262B26),
+ surfaceContainerHighest = Color(0xFF313630),
)
override val lightScheme = lightColorScheme(
- primary = Color(0xFF006D2F),
+ primary = Color(0xFF005927),
onPrimary = Color(0xFFFFFFFF),
- primaryContainer = Color(0xFF96F8A9),
- onPrimaryContainer = Color(0xFF002109),
+ primaryContainer = Color(0xFF188140),
+ onPrimaryContainer = Color(0xFFFFFFFF),
+ secondary = Color(0xFF005927), // Unread badge
+ onSecondary = Color(0xFFFFFFFF), // Unread badge text
+ secondaryContainer = Color(0xFF97f7a9), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFF000000), // Navigation bar selected icon
+ tertiary = Color(0xFF9D0012), // Downloaded badge
+ onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
+ tertiaryContainer = Color(0xFFD33131),
+ onTertiaryContainer = Color(0xFFFFFFFF),
+ error = Color(0xFFBA1A1A),
+ onError = Color(0xFFFFFFFF),
+ errorContainer = Color(0xFFFFDAD6),
+ onErrorContainer = Color(0xFF410002),
+ background = Color(0xFFF6FBF2),
+ onBackground = Color(0xFF181D18),
+ surface = Color(0xFFF6FBF2),
+ onSurface = Color(0xFF181D18),
+ surfaceVariant = Color(0xFFDAE6D7), // Navigation bar background (ThemePrefWidget)
+ onSurfaceVariant = Color(0xFF3F493F),
+ outline = Color(0xFF6F7A6E),
+ outlineVariant = Color(0xFFBECABC),
+ scrim = Color(0xFF000000),
+ inverseSurface = Color(0xFF2C322C),
+ inverseOnSurface = Color(0xFFEDF2E9),
inversePrimary = Color(0xFF7ADB8F),
- secondary = Color(0xFF006D2F),
- onSecondary = Color(0xFFFFFFFF),
- secondaryContainer = Color(0xFF96F8A9),
- onSecondaryContainer = Color(0xFF002109),
- tertiary = Color(0xFFB91D22),
- onTertiary = Color(0xFFFFFFFF),
- tertiaryContainer = Color(0xFFFFDAD5),
- onTertiaryContainer = Color(0xFF410003),
- background = Color(0xFFFBFDF7),
- onBackground = Color(0xFF1A1C19),
- surface = Color(0xFFFBFDF7),
- onSurface = Color(0xFF1A1C19),
- surfaceVariant = Color(0xFFDDE5DA),
- onSurfaceVariant = Color(0xFF414941),
- surfaceTint = Color(0xFF006D2F),
- inverseSurface = Color(0xFF2F312E),
- inverseOnSurface = Color(0xFFF0F2EC),
- outline = Color(0xFF717970),
+ surfaceDim = Color(0xFFD6DCD3),
+ surfaceBright = Color(0xFFF6FBF2),
+ surfaceContainerLowest = Color(0xFFFFFFFF),
+ surfaceContainerLow = Color(0xFFF0F5EC),
+ surfaceContainer = Color(0xFFEAEFE6), // Navigation bar background
+ surfaceContainerHigh = Color(0xFFE4EAE1),
+ surfaceContainerHighest = Color(0xFFDFE4DB),
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/LavenderColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/LavenderColorScheme.kt
index 70a9bd196f..e1bb6b3ee4 100644
--- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/LavenderColorScheme.kt
+++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/LavenderColorScheme.kt
@@ -18,53 +18,77 @@ internal object LavenderColorScheme : BaseColorScheme() {
override val darkScheme = darkColorScheme(
primary = Color(0xFFA177FF),
- onPrimary = Color(0xFF111129),
+ onPrimary = Color(0xFF3D0090),
primaryContainer = Color(0xFFA177FF),
- onPrimaryContainer = Color(0xFF111129),
- inversePrimary = Color(0xFF006D2F),
- secondary = Color(0xFFA177FF),
- onSecondary = Color(0xFF111129),
- secondaryContainer = Color(0xFFA177FF),
- onSecondaryContainer = Color(0xFF111129),
- tertiary = Color(0xFF5E25E1),
- onTertiary = Color(0xFFE8E8E8),
- tertiaryContainer = Color(0xFF111129),
- onTertiaryContainer = Color(0xFFDEE8FF),
+ onPrimaryContainer = Color(0xFFFFFFFF),
+ secondary = Color(0xFFA177FF), // Unread badge
+ onSecondary = Color(0xFFFFFFFF), // Unread badge text
+ secondaryContainer = Color(0xFF423271), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFFA177FF), // Navigation bar selected icon
+ tertiary = Color(0xFFCDBDFF), // Downloaded badge
+ onTertiary = Color(0xFF360096), // Downloaded badge text
+ tertiaryContainer = Color(0xFF5512D8),
+ onTertiaryContainer = Color(0xFFEFE6FF),
+ error = Color(0xFFFFB4AB),
+ onError = Color(0xFF690005),
+ errorContainer = Color(0xFF93000A),
+ onErrorContainer = Color(0xFFFFDAD6),
background = Color(0xFF111129),
- onBackground = Color(0xFFDEE8FF),
+ onBackground = Color(0xFFE7E0EC),
surface = Color(0xFF111129),
- onSurface = Color(0xFFDEE8FF),
- surfaceVariant = Color(0x2CB6B6B6),
- onSurfaceVariant = Color(0xFFE8E8E8),
- surfaceTint = Color(0xFFA177FF),
- inverseSurface = Color(0xFF221247),
- inverseOnSurface = Color(0xFFDEE8FF),
- outline = Color(0xA8905FFF),
+ onSurface = Color(0xFFE7E0EC),
+ surfaceVariant = Color(0xFF3D2F6B), // Navigation bar background (ThemePrefWidget)
+ onSurfaceVariant = Color(0xFFCBC3D6),
+ outline = Color(0xFF958E9F),
+ outlineVariant = Color(0xFF4A4453),
+ scrim = Color(0xFF000000),
+ inverseSurface = Color(0xFFE7E0EC),
+ inverseOnSurface = Color(0xFF322F38),
+ inversePrimary = Color(0xFF6D41C8),
+ surfaceDim = Color(0xFF111129),
+ surfaceBright = Color(0xFF3B3841),
+ surfaceContainerLowest = Color(0xFF15132d),
+ surfaceContainerLow = Color(0xFF171531),
+ surfaceContainer = Color(0xFF1D193B), // Navigation bar background
+ surfaceContainerHigh = Color(0xFF241f41),
+ surfaceContainerHighest = Color(0xFF282446),
)
override val lightScheme = lightColorScheme(
- primary = Color(0xFF7B46AF),
- onPrimary = Color(0xFFEDE2FF),
+ primary = Color(0xFF6D41C8),
+ onPrimary = Color(0xFFFFFFFF),
primaryContainer = Color(0xFF7B46AF),
- onPrimaryContainer = Color(0xFFEDE2FF),
- inversePrimary = Color(0xFFD6BAFF),
- secondary = Color(0xFF7B46AF),
- onSecondary = Color(0xFFEDE2FF),
- secondaryContainer = Color(0xFF7B46AF),
- onSecondaryContainer = Color(0xFFEDE2FF),
- tertiary = Color(0xFFEDE2FF),
- onTertiary = Color(0xFF7B46AF),
- tertiaryContainer = Color(0xFFEDE2FF),
- onTertiaryContainer = Color(0xFF7B46AF),
+ onPrimaryContainer = Color(0xFF130038),
+ secondary = Color(0xFF7B46AF), // Unread badge
+ onSecondary = Color(0xFFEDE2FF), // Unread badge text
+ secondaryContainer = Color(0xFFC9B0E6), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFF7B46AF), // Navigation bar selector icon
+ tertiary = Color(0xFFEDE2FF), // Downloaded badge
+ onTertiary = Color(0xFF7B46AF), // Downloaded badge text
+ tertiaryContainer = Color(0xFF6D3BF0),
+ onTertiaryContainer = Color(0xFFFFFFFF),
+ error = Color(0xFFBA1A1A),
+ onError = Color(0xFFFFFFFF),
+ errorContainer = Color(0xFFFFDAD6),
+ onErrorContainer = Color(0xFF410002),
background = Color(0xFFEDE2FF),
- onBackground = Color(0xFF1B1B22),
+ onBackground = Color(0xFF1D1A22),
surface = Color(0xFFEDE2FF),
- onSurface = Color(0xFF1B1B22),
- surfaceVariant = Color(0xFFB9B0CC),
- onSurfaceVariant = Color(0xD849454E),
- surfaceTint = Color(0xFF7B46AF),
- inverseSurface = Color(0xFF313033),
- inverseOnSurface = Color(0xFFF3EFF4),
- outline = Color(0xFF7B46AF),
+ onSurface = Color(0xFF1D1A22),
+ surfaceVariant = Color(0xFFE4D5F8), // Navigation bar background (ThemePrefWidget)
+ onSurfaceVariant = Color(0xFF4A4453),
+ outline = Color(0xFF7B7485),
+ outlineVariant = Color(0xFFCBC3D6),
+ scrim = Color(0xFF000000),
+ inverseSurface = Color(0xFF322F38),
+ inverseOnSurface = Color(0xFFF5EEFA),
+ inversePrimary = Color(0xFFA177FF),
+ surfaceDim = Color(0xFFDED7E3),
+ surfaceBright = Color(0xFFEDE2FF),
+ surfaceContainerLowest = Color(0xFFDACCEC),
+ surfaceContainerLow = Color(0xFFDED0F1),
+ surfaceContainer = Color(0xFFE4D5F8), // Navigation bar background
+ surfaceContainerHigh = Color(0xFFEADCFD),
+ surfaceContainerHighest = Color(0xFFEEE2FF),
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/MidnightDuskColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/MidnightDuskColorScheme.kt
index 7feaae333a..5ae86aa34f 100644
--- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/MidnightDuskColorScheme.kt
+++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/MidnightDuskColorScheme.kt
@@ -23,24 +23,29 @@ internal object MidnightDuskColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFBD1C5C),
onPrimaryContainer = Color(0xFFFFFFFF),
inversePrimary = Color(0xFFF02475),
- secondary = Color(0xFFF02475),
- onSecondary = Color(0xFFFFFFFF),
- secondaryContainer = Color(0xFFF02475),
- onSecondaryContainer = Color(0xFFFFFFFF),
- tertiary = Color(0xFF55971C),
- onTertiary = Color(0xFFFFFFFF),
+ secondary = Color(0xFFF02475), // Unread badge
+ onSecondary = Color(0xFF16151D), // Unread badge text
+ secondaryContainer = Color(0xFF66183C), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFFF02475), // Navigation bar selector icon
+ tertiary = Color(0xFF55971C), // Downloaded badge
+ onTertiary = Color(0xFF16151D), // Downloaded badge text
tertiaryContainer = Color(0xFF386412),
onTertiaryContainer = Color(0xFFE5E1E5),
background = Color(0xFF16151D),
onBackground = Color(0xFFE5E1E5),
surface = Color(0xFF16151D),
onSurface = Color(0xFFE5E1E5),
- surfaceVariant = Color(0xFF524346),
+ surfaceVariant = Color(0xFF281624), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFD6C1C4),
surfaceTint = Color(0xFFF02475),
inverseSurface = Color(0xFF333043),
inverseOnSurface = Color(0xFFFFFFFF),
outline = Color(0xFF9F8C8F),
+ surfaceContainerLowest = Color(0xFF221320),
+ surfaceContainerLow = Color(0xFF251522),
+ surfaceContainer = Color(0xFF281624), // Navigation bar background
+ surfaceContainerHigh = Color(0xFF2D1C2A),
+ surfaceContainerHighest = Color(0xFF2F1F2C),
)
override val lightScheme = lightColorScheme(
@@ -49,23 +54,28 @@ internal object MidnightDuskColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFFFD9E1),
onPrimaryContainer = Color(0xFF3F0017),
inversePrimary = Color(0xFFFFB1C4),
- secondary = Color(0xFFBB0054),
- onSecondary = Color(0xFFFFFFFF),
- secondaryContainer = Color(0xFFFFD9E1),
- onSecondaryContainer = Color(0xFF3F0017),
- tertiary = Color(0xFF006638),
- onTertiary = Color(0xFFFFFFFF),
+ secondary = Color(0xFFBB0054), // Unread badge
+ onSecondary = Color(0xFFFFFFFF), // Unread badge text
+ secondaryContainer = Color(0xFFEFBAD4), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFFD1377C), // Navigation bar selector icon
+ tertiary = Color(0xFF006638), // Downloaded badge
+ onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFF00894b),
onTertiaryContainer = Color(0xFF2D1600),
background = Color(0xFFFFFBFF),
onBackground = Color(0xFF1C1B1F),
surface = Color(0xFFFFFBFF),
onSurface = Color(0xFF1C1B1F),
- surfaceVariant = Color(0xFFF3DDE0),
+ surfaceVariant = Color(0xFFF9E6F1), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF524346),
surfaceTint = Color(0xFFBB0054),
inverseSurface = Color(0xFF313033),
inverseOnSurface = Color(0xFFF4F0F4),
outline = Color(0xFF847376),
+ surfaceContainerLowest = Color(0xFFDAC0CD),
+ surfaceContainerLow = Color(0xFFE8D1DD),
+ surfaceContainer = Color(0xFFF9E6F1), // Navigation bar background
+ surfaceContainerHigh = Color(0xFFFCF3F8),
+ surfaceContainerHighest = Color(0xFFFEF9FC),
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/NordColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/NordColorScheme.kt
index d493e2d626..84fe7b49fa 100644
--- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/NordColorScheme.kt
+++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/NordColorScheme.kt
@@ -17,19 +17,19 @@ internal object NordColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF88C0D0),
onPrimaryContainer = Color(0xFF2E3440),
inversePrimary = Color(0xFF397E91),
- secondary = Color(0xFF81A1C1),
- onSecondary = Color(0xFF2E3440),
- secondaryContainer = Color(0xFF81A1C1),
- onSecondaryContainer = Color(0xFF2E3440),
- tertiary = Color(0xFF5E81AC),
- onTertiary = Color(0xFF000000),
+ secondary = Color(0xFF81A1C1), // Unread badge
+ onSecondary = Color(0xFF2E3440), // Unread badge text
+ secondaryContainer = Color(0xFF506275), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFF88C0D0), // Navigation bar selector icon
+ tertiary = Color(0xFF5E81AC), // Downloaded badge
+ onTertiary = Color(0xFF000000), // Downloaded badge text
tertiaryContainer = Color(0xFF5E81AC),
onTertiaryContainer = Color(0xFF000000),
background = Color(0xFF2E3440),
onBackground = Color(0xFFECEFF4),
- surface = Color(0xFF3B4252),
+ surface = Color(0xFF2E3440),
onSurface = Color(0xFFECEFF4),
- surfaceVariant = Color(0xFF2E3440),
+ surfaceVariant = Color(0xFF414C5C), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFECEFF4),
surfaceTint = Color(0xFF88C0D0),
inverseSurface = Color(0xFFD8DEE9),
@@ -39,6 +39,11 @@ internal object NordColorScheme : BaseColorScheme() {
onError = Color(0xFF2E3440),
errorContainer = Color(0xFFBF616A),
onErrorContainer = Color(0xFF000000),
+ surfaceContainerLowest = Color(0xFF373F4D),
+ surfaceContainerLow = Color(0xFF3E4756),
+ surfaceContainer = Color(0xFF414C5C),
+ surfaceContainerHigh = Color(0xFF4E5766),
+ surfaceContainerHighest = Color(0xFF505968), // Navigation bar background
)
override val lightScheme = lightColorScheme(
@@ -47,19 +52,19 @@ internal object NordColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF5E81AC),
onPrimaryContainer = Color(0xFF000000),
inversePrimary = Color(0xFF8CA8CD),
- secondary = Color(0xFF81A1C1),
- onSecondary = Color(0xFF2E3440),
- secondaryContainer = Color(0xFF81A1C1),
- onSecondaryContainer = Color(0xFF2E3440),
- tertiary = Color(0xFF88C0D0),
- onTertiary = Color(0xFF2E3440),
+ secondary = Color(0xFF81A1C1), // Unread badge
+ onSecondary = Color(0xFF2E3440), // Unread badge text
+ secondaryContainer = Color(0xFF91B4D7), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFF2E3440), // Navigation bar selector icon
+ tertiary = Color(0xFF88C0D0), // Downloaded badge
+ onTertiary = Color(0xFF2E3440), // Downloaded badge text
tertiaryContainer = Color(0xFF88C0D0),
onTertiaryContainer = Color(0xFF2E3440),
background = Color(0xFFECEFF4),
onBackground = Color(0xFF2E3440),
surface = Color(0xFFE5E9F0),
onSurface = Color(0xFF2E3440),
- surfaceVariant = Color(0xFFffffff),
+ surfaceVariant = Color(0xFFDAE0EA), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF2E3440),
surfaceTint = Color(0xFF5E81AC),
inverseSurface = Color(0xFF3B4252),
@@ -68,5 +73,10 @@ internal object NordColorScheme : BaseColorScheme() {
onError = Color(0xFFECEFF4),
errorContainer = Color(0xFFBF616A),
onErrorContainer = Color(0xFF000000),
+ surfaceContainerLowest = Color(0xFFD1D7E0),
+ surfaceContainerLow = Color(0xFFD6DCE6),
+ surfaceContainer = Color(0xFFDAE0EA), // Navigation bar background
+ surfaceContainerHigh = Color(0xFFE9EDF3),
+ surfaceContainerHighest = Color(0xFFF2F4F8),
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/StrawberryColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/StrawberryColorScheme.kt
index 98417e3364..e1b096e259 100644
--- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/StrawberryColorScheme.kt
+++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/StrawberryColorScheme.kt
@@ -18,54 +18,78 @@ import androidx.compose.ui.graphics.Color
internal object StrawberryColorScheme : BaseColorScheme() {
override val darkScheme = darkColorScheme(
- primary = Color(0xFFFFB2B9),
- onPrimary = Color(0xFF67001B),
- primaryContainer = Color(0xFF91002A),
- onPrimaryContainer = Color(0xFFFFDADD),
- inversePrimary = Color(0xFFB61E40),
- secondary = Color(0xFFFFB2B9),
- onSecondary = Color(0xFF67001B),
- secondaryContainer = Color(0xFF91002A),
- onSecondaryContainer = Color(0xFFFFDADD),
- tertiary = Color(0xFFE8C08E),
- onTertiary = Color(0xFF432C06),
- tertiaryContainer = Color(0xFF5D421B),
- onTertiaryContainer = Color(0xFFFFDDB1),
+ primary = Color(0xFFFFB2B8),
+ onPrimary = Color(0xFF67001D),
+ primaryContainer = Color(0xFFD53855),
+ onPrimaryContainer = Color(0xFFFFFFFF),
+ secondary = Color(0xFFED4A65), // Unread badge
+ onSecondary = Color(0xFF201A1A), // Unread badge text
+ secondaryContainer = Color(0xFF91002A), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFFFFFFFF), // Navigation bar selector icon
+ tertiary = Color(0xFFE8C08E), // Downloaded badge
+ onTertiary = Color(0xFF201A1A), // Downloaded badge text
+ tertiaryContainer = Color(0xFF775930),
+ onTertiaryContainer = Color(0xFFFFF7F1),
+ error = Color(0xFFFFB4AB),
+ onError = Color(0xFF690005),
+ errorContainer = Color(0xFF93000A),
+ onErrorContainer = Color(0xFFFFDAD6),
background = Color(0xFF201A1A),
- onBackground = Color(0xFFECDFDF),
+ onBackground = Color(0xFFF7DCDD),
surface = Color(0xFF201A1A),
- onSurface = Color(0xFFECDFDF),
- surfaceVariant = Color(0xFF534344),
- onSurfaceVariant = Color(0xFFD7C1C2),
- surfaceTint = Color(0xFFFFB2B9),
- inverseSurface = Color(0xFFECDFDF),
- inverseOnSurface = Color(0xFF201A1A),
- outline = Color(0xFFA08C8D),
+ onSurface = Color(0xFFF7DCDD),
+ surfaceVariant = Color(0xFF322727), // Navigation bar background (ThemePrefWidget)
+ onSurfaceVariant = Color(0xFFE1BEC0),
+ outline = Color(0xFFA9898B),
+ outlineVariant = Color(0xFF594042),
+ scrim = Color(0xFF000000),
+ inverseSurface = Color(0xFFF7DCDD),
+ inverseOnSurface = Color(0xFF3D2C2D),
+ inversePrimary = Color(0xFFB61F40),
+ surfaceDim = Color(0xFF1D1011),
+ surfaceBright = Color(0xFF463536),
+ surfaceContainerLowest = Color(0xFF2C2222),
+ surfaceContainerLow = Color(0xFF302525),
+ surfaceContainer = Color(0xFF322727), // Navigation bar background
+ surfaceContainerHigh = Color(0xFF3C2F2F),
+ surfaceContainerHighest = Color(0xFF463737),
)
override val lightScheme = lightColorScheme(
- primary = Color(0xFFB61E40),
+ primary = Color(0xFFA10833),
onPrimary = Color(0xFFFFFFFF),
- primaryContainer = Color(0xFFFFDADD),
- onPrimaryContainer = Color(0xFF40000D),
- inversePrimary = Color(0xFFFFB2B9),
- secondary = Color(0xFFB61E40),
- onSecondary = Color(0xFFFFFFFF),
- secondaryContainer = Color(0xFFFFDADD),
- onSecondaryContainer = Color(0xFF40000D),
- tertiary = Color(0xFF775930),
- onTertiary = Color(0xFFFFFFFF),
- tertiaryContainer = Color(0xFFFFDDB1),
- onTertiaryContainer = Color(0xFF2A1800),
- background = Color(0xFFFCFCFC),
- onBackground = Color(0xFF201A1A),
- surface = Color(0xFFFCFCFC),
- onSurface = Color(0xFF201A1A),
- surfaceVariant = Color(0xFFF4DDDD),
- onSurfaceVariant = Color(0xFF534344),
- surfaceTint = Color(0xFFB61E40),
- inverseSurface = Color(0xFF362F2F),
- inverseOnSurface = Color(0xFFFBEDED),
- outline = Color(0xFF857374),
+ primaryContainer = Color(0xFFD53855),
+ onPrimaryContainer = Color(0xFFFFFFFF),
+ secondary = Color(0xFFA10833), // Unread badge
+ onSecondary = Color(0xFFFFFFFF), // Unread badge text
+ secondaryContainer = Color(0xFFD53855), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFFF6EAED), // Navigation bar selector icon
+ tertiary = Color(0xFF5F441D), // Downloaded badge
+ onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
+ tertiaryContainer = Color(0xFF87683D),
+ onTertiaryContainer = Color(0xFFFFFFFF),
+ error = Color(0xFFBA1A1A),
+ onError = Color(0xFFFFFFFF),
+ errorContainer = Color(0xFFFFDAD6),
+ onErrorContainer = Color(0xFF410002),
+ background = Color(0xFFFAFAFA),
+ onBackground = Color(0xFF261819),
+ surface = Color(0xFFFAFAFA),
+ onSurface = Color(0xFF261819),
+ surfaceVariant = Color(0xFFF6EAED), // Navigation bar background (ThemePrefWidget)
+ onSurfaceVariant = Color(0xFF594042),
+ outline = Color(0xFF8D7071),
+ outlineVariant = Color(0xFFE1BEC0),
+ scrim = Color(0xFF000000),
+ inverseSurface = Color(0xFF3D2C2D),
+ inverseOnSurface = Color(0xFFFFECED),
+ inversePrimary = Color(0xFFFFB2B8),
+ surfaceDim = Color(0xFFEED4D5),
+ surfaceBright = Color(0xFFFFF8F7),
+ surfaceContainerLowest = Color(0xFFF7DCDD),
+ surfaceContainerLow = Color(0xFFFDE2E3),
+ surfaceContainer = Color(0xFFF6EAED), // Navigation bar background
+ surfaceContainerHigh = Color(0xFFFFF0F0),
+ surfaceContainerHighest = Color(0xFFFFFFFF),
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TachiyomiColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TachiyomiColorScheme.kt
index 974d5f22d3..5faeb7df01 100644
--- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TachiyomiColorScheme.kt
+++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TachiyomiColorScheme.kt
@@ -22,19 +22,19 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF00429B),
onPrimaryContainer = Color(0xFFD9E2FF),
inversePrimary = Color(0xFF0058CA),
- secondary = Color(0xFFB0C6FF),
- onSecondary = Color(0xFF002D6E),
- secondaryContainer = Color(0xFF00429B),
- onSecondaryContainer = Color(0xFFD9E2FF),
- tertiary = Color(0xFF7ADC77),
- onTertiary = Color(0xFF003909),
+ secondary = Color(0xFFB0C6FF), // Unread badge
+ onSecondary = Color(0xFF002D6E), // Unread badge text
+ secondaryContainer = Color(0xFF00429B), // Navigation bar selector pill & pro
+ onSecondaryContainer = Color(0xFFD9E2FF), // Navigation bar selector icon
+ tertiary = Color(0xFF7ADC77), // Downloaded badge
+ onTertiary = Color(0xFF003909), // Downloaded badge text
tertiaryContainer = Color(0xFF005312),
onTertiaryContainer = Color(0xFF95F990),
background = Color(0xFF1B1B1F),
onBackground = Color(0xFFE3E2E6),
surface = Color(0xFF1B1B1F),
onSurface = Color(0xFFE3E2E6),
- surfaceVariant = Color(0xFF44464F),
+ surfaceVariant = Color(0xFF211F26), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFC5C6D0),
surfaceTint = Color(0xFFB0C6FF),
inverseSurface = Color(0xFFE3E2E6),
@@ -45,6 +45,11 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
onErrorContainer = Color(0xFFFFDAD6),
outline = Color(0xFF8F9099),
outlineVariant = Color(0xFF44464F),
+ surfaceContainerLowest = Color(0xFF1A181D),
+ surfaceContainerLow = Color(0xFF1E1C22),
+ surfaceContainer = Color(0xFF211F26), // Navigation bar background
+ surfaceContainerHigh = Color(0xFF292730),
+ surfaceContainerHighest = Color(0xFF302E38),
)
override val lightScheme = lightColorScheme(
@@ -53,19 +58,19 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFD9E2FF),
onPrimaryContainer = Color(0xFF001945),
inversePrimary = Color(0xFFB0C6FF),
- secondary = Color(0xFF0058CA),
- onSecondary = Color(0xFFFFFFFF),
- secondaryContainer = Color(0xFFD9E2FF),
- onSecondaryContainer = Color(0xFF001945),
- tertiary = Color(0xFF006E1B),
- onTertiary = Color(0xFFFFFFFF),
+ secondary = Color(0xFF0058CA), // Unread badge
+ onSecondary = Color(0xFFFFFFFF), // Unread badge text
+ secondaryContainer = Color(0xFFD9E2FF), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFF001945), // Navigation bar selector icon
+ tertiary = Color(0xFF006E1B), // Downloaded badge
+ onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFF95F990),
onTertiaryContainer = Color(0xFF002203),
background = Color(0xFFFEFBFF),
onBackground = Color(0xFF1B1B1F),
surface = Color(0xFFFEFBFF),
onSurface = Color(0xFF1B1B1F),
- surfaceVariant = Color(0xFFE1E2EC),
+ surfaceVariant = Color(0xFFF3EDF7), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF44464F),
surfaceTint = Color(0xFF0058CA),
inverseSurface = Color(0xFF303034),
@@ -76,5 +81,10 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
onErrorContainer = Color(0xFF410002),
outline = Color(0xFF757780),
outlineVariant = Color(0xFFC5C6D0),
+ surfaceContainerLowest = Color(0xFFF5F1F8),
+ surfaceContainerLow = Color(0xFFF7F2FA),
+ surfaceContainer = Color(0xFFF3EDF7), // Navigation bar background
+ surfaceContainerHigh = Color(0xFFFCF7FF),
+ surfaceContainerHighest = Color(0xFFFCF7FF),
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TakoColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TakoColorScheme.kt
index 244e769d4c..7988129638 100644
--- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TakoColorScheme.kt
+++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TakoColorScheme.kt
@@ -23,24 +23,29 @@ internal object TakoColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFF3B375),
onPrimaryContainer = Color(0xFF38294E),
inversePrimary = Color(0xFF84531E),
- secondary = Color(0xFFF3B375),
- onSecondary = Color(0xFF38294E),
- secondaryContainer = Color(0xFFF3B375),
- onSecondaryContainer = Color(0xFF38294E),
- tertiary = Color(0xFF66577E),
- onTertiary = Color(0xFFF3B375),
+ secondary = Color(0xFFF3B375), // Unread badge
+ onSecondary = Color(0xFF38294E), // Unread badge text
+ secondaryContainer = Color(0xFF5C4D4B), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFFF3B375), // Navigation bar selector icon
+ tertiary = Color(0xFF66577E), // Downloaded badge
+ onTertiary = Color(0xFFF3B375), // Downloaded badge text
tertiaryContainer = Color(0xFF4E4065),
onTertiaryContainer = Color(0xFFEDDCFF),
background = Color(0xFF21212E),
onBackground = Color(0xFFE3E0F2),
surface = Color(0xFF21212E),
onSurface = Color(0xFFE3E0F2),
- surfaceVariant = Color(0xFF49454E),
+ surfaceVariant = Color(0xFF2A2A3C), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFCBC4CE),
surfaceTint = Color(0xFF66577E),
inverseSurface = Color(0xFFE5E1E6),
inverseOnSurface = Color(0xFF1B1B1E),
outline = Color(0xFF958F99),
+ surfaceContainerLowest = Color(0xFF20202E),
+ surfaceContainerLow = Color(0xFF262636),
+ surfaceContainer = Color(0xFF2A2A3C), // Navigation bar background
+ surfaceContainerHigh = Color(0xFF303044),
+ surfaceContainerHighest = Color(0xFF36364D),
)
override val lightScheme = lightColorScheme(
@@ -49,23 +54,28 @@ internal object TakoColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF66577E),
onPrimaryContainer = Color(0xFFF3B375),
inversePrimary = Color(0xFFD6BAFF),
- secondary = Color(0xFF66577E),
- onSecondary = Color(0xFFF3B375),
- secondaryContainer = Color(0xFF66577E),
- onSecondaryContainer = Color(0xFFF3B375),
- tertiary = Color(0xFFF3B375),
- onTertiary = Color(0xFF574360),
+ secondary = Color(0xFF66577E), // Unread badge
+ onSecondary = Color(0xFFF3B375), // Unread badge text
+ secondaryContainer = Color(0xFFC8BED0), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFF66577E), // Navigation bar selector icon
+ tertiary = Color(0xFFF3B375), // Downloaded badge
+ onTertiary = Color(0xFF574360), // Downloaded badge text
tertiaryContainer = Color(0xFFFDD6B0),
onTertiaryContainer = Color(0xFF221437),
background = Color(0xFFF7F5FF),
onBackground = Color(0xFF1B1B22),
surface = Color(0xFFF7F5FF),
onSurface = Color(0xFF1B1B22),
- surfaceVariant = Color(0xFFE8E0EB),
+ surfaceVariant = Color(0xFFE8E0EB), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF49454E),
surfaceTint = Color(0xFF66577E),
inverseSurface = Color(0xFF313033),
inverseOnSurface = Color(0xFFF3EFF4),
outline = Color(0xFF7A757E),
+ surfaceContainerLowest = Color(0xFFD7D0DA),
+ surfaceContainerLow = Color(0xFFDFD8E2),
+ surfaceContainer = Color(0xFFE8E0EB), // Navigation bar background
+ surfaceContainerHigh = Color(0xFFEEE6F1),
+ surfaceContainerHighest = Color(0xFFF7EEFA),
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TealTurqoiseColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TealTurqoiseColorScheme.kt
index e914b49fcb..28811fa6a3 100644
--- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TealTurqoiseColorScheme.kt
+++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TealTurqoiseColorScheme.kt
@@ -15,24 +15,29 @@ internal object TealTurqoiseColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF40E0D0),
onPrimaryContainer = Color(0xFF000000),
inversePrimary = Color(0xFF008080),
- secondary = Color(0xFF40E0D0),
- onSecondary = Color(0xFF000000),
- secondaryContainer = Color(0xFF18544E),
- onSecondaryContainer = Color(0xFF40E0D0),
- tertiary = Color(0xFFBF1F2F),
- onTertiary = Color(0xFFFFFFFF),
+ secondary = Color(0xFF40E0D0), // Unread badge
+ onSecondary = Color(0xFF000000), // Unread badge text
+ secondaryContainer = Color(0xFF18544E), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFF40E0D0), // Navigation bar selector icon
+ tertiary = Color(0xFFBF1F2F), // Downloaded badge
+ onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFF200508),
onTertiaryContainer = Color(0xFFBF1F2F),
background = Color(0xFF202125),
onBackground = Color(0xFFDFDEDA),
surface = Color(0xFF202125),
onSurface = Color(0xFFDFDEDA),
- surfaceVariant = Color(0xFF3F4947),
+ surfaceVariant = Color(0xFF233133), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFDFDEDA),
surfaceTint = Color(0xFF40E0D0),
inverseSurface = Color(0xFFDFDEDA),
inverseOnSurface = Color(0xFF202125),
outline = Color(0xFF899391),
+ surfaceContainerLowest = Color(0xFF202C2E),
+ surfaceContainerLow = Color(0xFF222F31),
+ surfaceContainer = Color(0xFF233133), // Navigation bar background
+ surfaceContainerHigh = Color(0xFF28383A),
+ surfaceContainerHighest = Color(0xFF2F4244),
)
override val lightScheme = lightColorScheme(
@@ -41,23 +46,28 @@ internal object TealTurqoiseColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF008080),
onPrimaryContainer = Color(0xFFFFFFFF),
inversePrimary = Color(0xFF40E0D0),
- secondary = Color(0xFF008080),
- onSecondary = Color(0xFFFFFFFF),
- secondaryContainer = Color(0xFFBFDFDF),
- onSecondaryContainer = Color(0xFF008080),
- tertiary = Color(0xFFFF7F7F),
- onTertiary = Color(0xFF000000),
+ secondary = Color(0xFF008080), // Unread badge text
+ onSecondary = Color(0xFFFFFFFF), // Unread badge text
+ secondaryContainer = Color(0xFFCFE5E4), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFF008080), // Navigation bar selector icon
+ tertiary = Color(0xFFFF7F7F), // Downloaded badge
+ onTertiary = Color(0xFF000000), // Downloaded badge text
tertiaryContainer = Color(0xFF2A1616),
onTertiaryContainer = Color(0xFFFF7F7F),
background = Color(0xFFFAFAFA),
onBackground = Color(0xFF050505),
surface = Color(0xFFFAFAFA),
onSurface = Color(0xFF050505),
- surfaceVariant = Color(0xFFDAE5E2),
+ surfaceVariant = Color(0xFFEBF3F1), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF050505),
surfaceTint = Color(0xFFBFDFDF),
inverseSurface = Color(0xFF050505),
inverseOnSurface = Color(0xFFFAFAFA),
outline = Color(0xFF6F7977),
+ surfaceContainerLowest = Color(0xFFE1E9E7),
+ surfaceContainerLow = Color(0xFFE6EEEC),
+ surfaceContainer = Color(0xFFEBF3F1), // Navigation bar background
+ surfaceContainerHigh = Color(0xFFF0F8F6),
+ surfaceContainerHighest = Color(0xFFF7FFFD),
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TidalWaveColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TidalWaveColorScheme.kt
index c56a1fa57e..09dc248c0a 100644
--- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TidalWaveColorScheme.kt
+++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TidalWaveColorScheme.kt
@@ -22,24 +22,29 @@ internal object TidalWaveColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF004d61),
onPrimaryContainer = Color(0xFFb8eaff),
inversePrimary = Color(0xFFa12b03),
- secondary = Color(0xFF5ed4fc),
- onSecondary = Color(0xFF003544),
- secondaryContainer = Color(0xFF004d61),
- onSecondaryContainer = Color(0xFFb8eaff),
- tertiary = Color(0xFF92f7bc),
- onTertiary = Color(0xFF001c3b),
+ secondary = Color(0xFF5ed4fc), // Unread badge
+ onSecondary = Color(0xFF003544), // Unread badge text
+ secondaryContainer = Color(0xFF004d61), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFFb8eaff), // Navigation bar selector icon
+ tertiary = Color(0xFF92f7bc), // Downloaded badge
+ onTertiary = Color(0xFF001c3b), // Downloaded badge text
tertiaryContainer = Color(0xFFc3fada),
onTertiaryContainer = Color(0xFF78ffd6),
background = Color(0xFF001c3b),
onBackground = Color(0xFFd5e3ff),
surface = Color(0xFF001c3b),
onSurface = Color(0xFFd5e3ff),
- surfaceVariant = Color(0xFF40484c),
+ surfaceVariant = Color(0xFF082b4b), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFbfc8cc),
surfaceTint = Color(0xFF5ed4fc),
inverseSurface = Color(0xFFffe3c4),
inverseOnSurface = Color(0xFF001c3b),
outline = Color(0xFF8a9296),
+ surfaceContainerLowest = Color(0xFF072642),
+ surfaceContainerLow = Color(0xFF072947),
+ surfaceContainer = Color(0xFF082b4b), // Navigation bar background
+ surfaceContainerHigh = Color(0xFF093257),
+ surfaceContainerHighest = Color(0xFF0A3861),
)
override val lightScheme = lightColorScheme(
@@ -48,23 +53,28 @@ internal object TidalWaveColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFB4D4DF),
onPrimaryContainer = Color(0xFF001f28),
inversePrimary = Color(0xFFff987f),
- secondary = Color(0xFF006780),
- onSecondary = Color(0xFFffffff),
- secondaryContainer = Color(0xFFb8eaff),
- onSecondaryContainer = Color(0xFF001f28),
- tertiary = Color(0xFF92f7bc),
- onTertiary = Color(0xFF001c3b),
+ secondary = Color(0xFF006780), // Unread badge
+ onSecondary = Color(0xFFffffff), // Unread badge text
+ secondaryContainer = Color(0xFF9AE1FF), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFF001f28), // Navigation bar selector icon
+ tertiary = Color(0xFF92f7bc), // Downloaded badge
+ onTertiary = Color(0xFF001c3b), // Downloaded badge text
tertiaryContainer = Color(0xFFc3fada),
onTertiaryContainer = Color(0xFF78ffd6),
background = Color(0xFFfdfbff),
onBackground = Color(0xFF001c3b),
surface = Color(0xFFfdfbff),
onSurface = Color(0xFF001c3b),
- surfaceVariant = Color(0xFFdce4e8),
+ surfaceVariant = Color(0xFFe8eff5), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF40484c),
surfaceTint = Color(0xFF006780),
inverseSurface = Color(0xFF020400),
inverseOnSurface = Color(0xFFffe3c4),
outline = Color(0xFF70787c),
+ surfaceContainerLowest = Color(0xFFe2e8ec),
+ surfaceContainerLow = Color(0xFFe5ecf1),
+ surfaceContainer = Color(0xFFe8eff5), // Navigation bar background
+ surfaceContainerHigh = Color(0xFFedf4fA),
+ surfaceContainerHighest = Color(0xFFf5faff),
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YinYangColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YinYangColorScheme.kt
index da9dee4241..1e9b129785 100644
--- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YinYangColorScheme.kt
+++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YinYangColorScheme.kt
@@ -17,24 +17,29 @@ internal object YinYangColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFFFFFFF),
onPrimaryContainer = Color(0xFF000000),
inversePrimary = Color(0xFFCECECE),
- secondary = Color(0xFFFFFFFF),
- onSecondary = Color(0xFF5A5A5A),
- secondaryContainer = Color(0xFF717171),
- onSecondaryContainer = Color(0xFFE4E4E4),
- tertiary = Color(0xFF000000),
- onTertiary = Color(0xFFFFFFFF),
+ secondary = Color(0xFFFFFFFF), // Unread badge
+ onSecondary = Color(0xFF5A5A5A), // Unread badge text
+ secondaryContainer = Color(0xFF717171), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFFE4E4E4), // Navigation bar selector icon
+ tertiary = Color(0xFF000000), // Downloaded badge
+ onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFF00419E),
onTertiaryContainer = Color(0xFFD8E2FF),
background = Color(0xFF1E1E1E),
onBackground = Color(0xFFE6E6E6),
surface = Color(0xFF1E1E1E),
onSurface = Color(0xFFE6E6E6),
- surfaceVariant = Color(0xFF4E4E4E),
+ surfaceVariant = Color(0xFF313131), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFD1D1D1),
surfaceTint = Color(0xFFFFFFFF),
inverseSurface = Color(0xFFE6E6E6),
inverseOnSurface = Color(0xFF1E1E1E),
outline = Color(0xFF999999),
+ surfaceContainerLowest = Color(0xFF2A2A2A),
+ surfaceContainerLow = Color(0xFF2D2D2D),
+ surfaceContainer = Color(0xFF313131), // Navigation bar background
+ surfaceContainerHigh = Color(0xFF383838),
+ surfaceContainerHighest = Color(0xFF3F3F3F),
)
override val lightScheme = lightColorScheme(
@@ -43,23 +48,28 @@ internal object YinYangColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF000000),
onPrimaryContainer = Color(0xFFFFFFFF),
inversePrimary = Color(0xFFA6A6A6),
- secondary = Color(0xFF000000),
- onSecondary = Color(0xFFFFFFFF),
- secondaryContainer = Color(0xFFDDDDDD),
- onSecondaryContainer = Color(0xFF0C0C0C),
- tertiary = Color(0xFFFFFFFF),
- onTertiary = Color(0xFF000000),
+ secondary = Color(0xFF000000), // Unread badge
+ onSecondary = Color(0xFFFFFFFF), // Unread badge text
+ secondaryContainer = Color(0xFFDDDDDD), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFF0C0C0C), // Navigation bar selector icon
+ tertiary = Color(0xFFFFFFFF), // Downloaded badge
+ onTertiary = Color(0xFF000000), // Downloaded badge text
tertiaryContainer = Color(0xFFD8E2FF),
onTertiaryContainer = Color(0xFF001947),
background = Color(0xFFFDFDFD),
onBackground = Color(0xFF222222),
surface = Color(0xFFFDFDFD),
onSurface = Color(0xFF222222),
- surfaceVariant = Color(0xFFEDEDED),
+ surfaceVariant = Color(0xFFE8E8E8), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF515151),
surfaceTint = Color(0xFF000000),
inverseSurface = Color(0xFF333333),
inverseOnSurface = Color(0xFFF4F4F4),
outline = Color(0xFF838383),
+ surfaceContainerLowest = Color(0xFFCFCFCF),
+ surfaceContainerLow = Color(0xFFDADADA),
+ surfaceContainer = Color(0xFFE8E8E8), // Navigation bar background
+ surfaceContainerHigh = Color(0xFFECECEC),
+ surfaceContainerHighest = Color(0xFFEFEFEF),
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YotsubaColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YotsubaColorScheme.kt
index fdda6b7dca..58007a6802 100644
--- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YotsubaColorScheme.kt
+++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YotsubaColorScheme.kt
@@ -23,24 +23,29 @@ internal object YotsubaColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF862200),
onPrimaryContainer = Color(0xFFFFDBCF),
inversePrimary = Color(0xFFAE3200),
- secondary = Color(0xFFFFB59D),
- onSecondary = Color(0xFF5F1600),
- secondaryContainer = Color(0xFF862200),
- onSecondaryContainer = Color(0xFFFFDBCF),
- tertiary = Color(0xFFD7C68D),
- onTertiary = Color(0xFF3A2F05),
+ secondary = Color(0xFFFFB59D), // Unread badge
+ onSecondary = Color(0xFF5F1600), // Unread badge text
+ secondaryContainer = Color(0xFF862200), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFFFFDBCF), // Navigation bar selector icon
+ tertiary = Color(0xFFD7C68D), // Downloaded badge
+ onTertiary = Color(0xFF3A2F05), // Downloaded badge text
tertiaryContainer = Color(0xFF524619),
onTertiaryContainer = Color(0xFFF5E2A7),
background = Color(0xFF211A18),
onBackground = Color(0xFFEDE0DD),
surface = Color(0xFF211A18),
onSurface = Color(0xFFEDE0DD),
- surfaceVariant = Color(0xFF53433F),
+ surfaceVariant = Color(0xFF332723), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFD8C2BC),
surfaceTint = Color(0xFFFFB59D),
inverseSurface = Color(0xFFEDE0DD),
inverseOnSurface = Color(0xFF211A18),
outline = Color(0xFFA08C87),
+ surfaceContainerLowest = Color(0xFF2E221F),
+ surfaceContainerLow = Color(0xFF312521),
+ surfaceContainer = Color(0xFF332723), // Navigation bar background
+ surfaceContainerHigh = Color(0xFF413531),
+ surfaceContainerHighest = Color(0xFF4C403D),
)
override val lightScheme = lightColorScheme(
@@ -49,23 +54,28 @@ internal object YotsubaColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFFFDBCF),
onPrimaryContainer = Color(0xFF3B0A00),
inversePrimary = Color(0xFFFFB59D),
- secondary = Color(0xFFAE3200),
- onSecondary = Color(0xFFFFFFFF),
- secondaryContainer = Color(0xFFFFDBCF),
- onSecondaryContainer = Color(0xFF3B0A00),
- tertiary = Color(0xFF6B5E2F),
- onTertiary = Color(0xFFFFFFFF),
+ secondary = Color(0xFFAE3200), // Unread badge
+ onSecondary = Color(0xFFFFFFFF), // Unread badge text
+ secondaryContainer = Color(0xFFEBCDC2), // Navigation bar selector pill & progress indicator (remaining)
+ onSecondaryContainer = Color(0xFF3B0A00), // Navigation bar selector icon
+ tertiary = Color(0xFF6B5E2F), // Downloaded badge
+ onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFFF5E2A7),
onTertiaryContainer = Color(0xFF231B00),
background = Color(0xFFFCFCFC),
onBackground = Color(0xFF211A18),
surface = Color(0xFFFCFCFC),
onSurface = Color(0xFF211A18),
- surfaceVariant = Color(0xFFF5DED8),
+ surfaceVariant = Color(0xFFF6EBE7), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF53433F),
surfaceTint = Color(0xFFAE3200),
inverseSurface = Color(0xFF362F2D),
inverseOnSurface = Color(0xFFFBEEEB),
outline = Color(0xFF85736E),
+ surfaceContainerLowest = Color(0xFFECE3E0),
+ surfaceContainerLow = Color(0xFFF1E7E4),
+ surfaceContainer = Color(0xFFF6EBE7), // Navigation bar background
+ surfaceContainerHigh = Color(0xFFFAF4F2),
+ surfaceContainerHighest = Color(0xFFFBF6F4),
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt
index b415dedcb5..a96885186f 100644
--- a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt
+++ b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt
@@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.minimumInteractiveComponentSize
import androidx.compose.material3.DatePicker
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
@@ -24,6 +23,7 @@ import androidx.compose.material3.SelectableDates
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
+import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.material3.rememberDatePickerState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -43,8 +43,6 @@ import tachiyomi.presentation.core.components.WheelTextPicker
import tachiyomi.presentation.core.components.material.AlertDialogContent
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource
-import tachiyomi.presentation.core.util.isScrolledToEnd
-import tachiyomi.presentation.core.util.isScrolledToStart
@Composable
fun TrackStatusSelector(
@@ -86,16 +84,8 @@ fun TrackStatusSelector(
}
}
}
- if (!state.isScrolledToStart()) {
- HorizontalDivider(
- modifier = Modifier.align(Alignment.TopCenter),
- )
- }
- if (!state.isScrolledToEnd()) {
- HorizontalDivider(
- modifier = Modifier.align(Alignment.BottomCenter),
- )
- }
+ if (state.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
+ if (state.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
},
onConfirm = onConfirm,
onDismissRequest = onDismissRequest,
diff --git a/app/src/main/java/eu/kanade/presentation/track/anime/AnimeTrackInfoDialogHome.kt b/app/src/main/java/eu/kanade/presentation/track/anime/AnimeTrackInfoDialogHome.kt
index e6fa4ac694..dbe0d4ad3d 100644
--- a/app/src/main/java/eu/kanade/presentation/track/anime/AnimeTrackInfoDialogHome.kt
+++ b/app/src/main/java/eu/kanade/presentation/track/anime/AnimeTrackInfoDialogHome.kt
@@ -178,7 +178,7 @@ private fun TrackInfoItem(
modifier = Modifier
.padding(top = 12.dp)
.clip(MaterialTheme.shapes.medium)
- .background(MaterialTheme.colorScheme.surface)
+ .background(MaterialTheme.colorScheme.surfaceContainerHighest)
.padding(8.dp)
.clip(RoundedCornerShape(6.dp)),
) {
diff --git a/app/src/main/java/eu/kanade/presentation/track/manga/MangaTrackInfoDialogHome.kt b/app/src/main/java/eu/kanade/presentation/track/manga/MangaTrackInfoDialogHome.kt
index a6545d5fce..d0e630122e 100644
--- a/app/src/main/java/eu/kanade/presentation/track/manga/MangaTrackInfoDialogHome.kt
+++ b/app/src/main/java/eu/kanade/presentation/track/manga/MangaTrackInfoDialogHome.kt
@@ -189,7 +189,7 @@ private fun TrackInfoItem(
modifier = Modifier
.padding(top = 12.dp)
.clip(MaterialTheme.shapes.medium)
- .background(MaterialTheme.colorScheme.surface)
+ .background(MaterialTheme.colorScheme.surfaceContainerHighest)
.padding(8.dp)
.clip(RoundedCornerShape(6.dp)),
) {
diff --git a/app/src/main/java/eu/kanade/presentation/updates/anime/AnimeUpdatesScreen.kt b/app/src/main/java/eu/kanade/presentation/updates/anime/AnimeUpdatesScreen.kt
index 91ceed2f30..36428dfc5d 100644
--- a/app/src/main/java/eu/kanade/presentation/updates/anime/AnimeUpdatesScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/updates/anime/AnimeUpdatesScreen.kt
@@ -86,7 +86,7 @@ fun AnimeUpdateScreen(
isRefreshing = false
}
},
- enabled = { !state.selectionMode },
+ enabled = !state.selectionMode,
indicatorPadding = contentPadding,
) {
FastScrollLazyColumn(
diff --git a/app/src/main/java/eu/kanade/presentation/updates/anime/AnimeUpdatesUiItem.kt b/app/src/main/java/eu/kanade/presentation/updates/anime/AnimeUpdatesUiItem.kt
index 6b876a9605..d42157e139 100644
--- a/app/src/main/java/eu/kanade/presentation/updates/anime/AnimeUpdatesUiItem.kt
+++ b/app/src/main/java/eu/kanade/presentation/updates/anime/AnimeUpdatesUiItem.kt
@@ -55,7 +55,7 @@ internal fun LazyListScope.animeUpdatesLastUpdatedItem(
item(key = "animeUpdates-lastUpdated") {
Box(
modifier = Modifier
- .animateItemPlacement()
+ .animateItem()
.padding(
horizontal = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
@@ -95,14 +95,14 @@ internal fun LazyListScope.animeUpdatesUiItems(
when (item) {
is AnimeUpdatesUiModel.Header -> {
ListGroupHeader(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
text = relativeDateText(item.date),
)
}
is AnimeUpdatesUiModel.Item -> {
val updatesItem = item.item
AnimeUpdatesUiItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
update = updatesItem.update,
selected = updatesItem.selected,
watchProgress = updatesItem.update.lastSecondSeen
diff --git a/app/src/main/java/eu/kanade/presentation/updates/manga/MangaUpdatesScreen.kt b/app/src/main/java/eu/kanade/presentation/updates/manga/MangaUpdatesScreen.kt
index 3f5d5ce030..818f38bb91 100644
--- a/app/src/main/java/eu/kanade/presentation/updates/manga/MangaUpdatesScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/updates/manga/MangaUpdatesScreen.kt
@@ -82,7 +82,7 @@ fun MangaUpdateScreen(
isRefreshing = false
}
},
- enabled = { !state.selectionMode },
+ enabled = !state.selectionMode,
indicatorPadding = contentPadding,
) {
FastScrollLazyColumn(
diff --git a/app/src/main/java/eu/kanade/presentation/updates/manga/MangaUpdatesUiItem.kt b/app/src/main/java/eu/kanade/presentation/updates/manga/MangaUpdatesUiItem.kt
index 4a9ba796c4..546af1f1e1 100644
--- a/app/src/main/java/eu/kanade/presentation/updates/manga/MangaUpdatesUiItem.kt
+++ b/app/src/main/java/eu/kanade/presentation/updates/manga/MangaUpdatesUiItem.kt
@@ -54,7 +54,7 @@ internal fun LazyListScope.mangaUpdatesLastUpdatedItem(
item(key = "mangaUpdates-lastUpdated") {
Box(
modifier = Modifier
- .animateItemPlacement()
+ .animateItem()
.padding(horizontal = MaterialTheme.padding.medium, vertical = MaterialTheme.padding.small),
) {
Text(
@@ -91,14 +91,14 @@ internal fun LazyListScope.mangaUpdatesUiItems(
when (item) {
is MangaUpdatesUiModel.Header -> {
ListGroupHeader(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
text = relativeDateText(item.date),
)
}
is MangaUpdatesUiModel.Item -> {
val updatesItem = item.item
MangaUpdatesUiItem(
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
update = updatesItem.update,
selected = updatesItem.selected,
readProgress = updatesItem.update.lastPageRead
diff --git a/app/src/main/java/eu/kanade/presentation/util/Permissions.kt b/app/src/main/java/eu/kanade/presentation/util/Permissions.kt
index 6bee9203e8..80c2f90b92 100644
--- a/app/src/main/java/eu/kanade/presentation/util/Permissions.kt
+++ b/app/src/main/java/eu/kanade/presentation/util/Permissions.kt
@@ -7,9 +7,9 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.compose.LocalLifecycleOwner
@Composable
fun rememberRequestPackageInstallsPermissionState(initialValue: Boolean = false): Boolean {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt
index ac7f890f77..25aec7c193 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/App.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt
@@ -32,6 +32,7 @@ import eu.kanade.tachiyomi.crash.GlobalExceptionHandler
import eu.kanade.tachiyomi.data.coil.AnimeCoverFetcher
import eu.kanade.tachiyomi.data.coil.AnimeCoverKeyer
import eu.kanade.tachiyomi.data.coil.AnimeKeyer
+import eu.kanade.tachiyomi.data.coil.BufferedSourceFetcher
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer
import eu.kanade.tachiyomi.data.coil.MangaKeyer
@@ -173,29 +174,37 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
)
}
+ @Suppress("MagicNumber")
override fun newImageLoader(context: Context): ImageLoader {
return ImageLoader.Builder(this).apply {
val callFactoryLazy = lazy { Injekt.get().client }
components {
+ // NetworkFetcher.Factory
add(OkHttpNetworkFetcherFactory(callFactoryLazy::value))
+ // Decoder.Factory
add(TachiyomiImageDecoder.Factory())
+ // Fetcher.Factory
+ add(BufferedSourceFetcher.Factory())
add(MangaCoverFetcher.MangaFactory(callFactoryLazy))
+ add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy))
add(AnimeCoverFetcher.AnimeFactory(callFactoryLazy))
add(AnimeCoverFetcher.AnimeCoverFactory(callFactoryLazy))
+ // Keyer
add(AnimeKeyer())
- add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy))
add(MangaKeyer())
add(AnimeCoverKeyer())
add(MangaCoverKeyer())
}
+
crossfade((300 * this@App.animatorDurationScale).toInt())
allowRgb565(DeviceUtil.isLowRamDevice(this@App))
if (networkPreferences.verboseLogging().get()) logger(DebugLogger())
// Coil spawns a new thread for every image load by default
- fetcherDispatcher(Dispatchers.IO.limitedParallelism(8))
- decoderDispatcher(Dispatchers.IO.limitedParallelism(2))
- }.build()
+ fetcherCoroutineContext(Dispatchers.IO.limitedParallelism(8))
+ decoderCoroutineContext(Dispatchers.IO.limitedParallelism(3))
+ }
+ .build()
}
override fun onStart(owner: LifecycleOwner) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupDecoder.kt
index e33572caf9..34f862548f 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupDecoder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupDecoder.kt
@@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.backup
import android.content.Context
import android.net.Uri
import eu.kanade.tachiyomi.data.backup.models.Backup
-import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
import kotlinx.serialization.protobuf.ProtoBuf
import okio.buffer
import okio.gzip
@@ -33,7 +32,7 @@ class BackupDecoder(
source
}.use { it.readByteArray() }
- parser.decodeFromByteArray(BackupSerializer, backupString)
+ parser.decodeFromByteArray(Backup.serializer(), backupString)
}
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt
index a90ecde496..b4c20c06d1 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt
@@ -18,7 +18,6 @@ import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.backup.models.BackupExtension
import eu.kanade.tachiyomi.data.backup.models.BackupManga
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
-import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
import eu.kanade.tachiyomi.data.backup.models.BackupSource
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
import kotlinx.serialization.protobuf.ProtoBuf
@@ -99,7 +98,7 @@ class BackupCreator(
backupExtensions = backupExtensions(options),
)
- val byteArray = parser.encodeToByteArray(BackupSerializer, backup)
+ val byteArray = parser.encodeToByteArray(Backup.serializer(), backup)
if (byteArray.isEmpty()) {
throw IllegalStateException(context.stringResource(MR.strings.empty_backup_error))
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/models/BackupSerializer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/models/BackupSerializer.kt
deleted file mode 100644
index 55b1c6afc6..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/models/BackupSerializer.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package eu.kanade.tachiyomi.data.backup.full.models
-
-import kotlinx.serialization.Serializer
-
-@Serializer(forClass = Backup::class)
-object BackupSerializer
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt
index 9319854433..67ea342fa0 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt
@@ -1,12 +1,8 @@
package eu.kanade.tachiyomi.data.backup.models
import kotlinx.serialization.Serializable
-import kotlinx.serialization.Serializer
import kotlinx.serialization.protobuf.ProtoNumber
-@Serializer(forClass = Backup::class)
-object BackupSerializer
-
@Serializable
data class Backup(
@ProtoNumber(1) val backupManga: List = emptyList(),
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt
index 88f3122099..b889f63971 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt
@@ -21,14 +21,20 @@ class CategoriesRestorer(
if (backupCategories.isNotEmpty()) {
val dbCategories = getAnimeCategories.await()
val dbCategoriesByName = dbCategories.associateBy { it.name }
+ var nextOrder = dbCategories.maxOfOrNull { it.order }?.plus(1) ?: 0
- val categories = backupCategories.map {
- dbCategoriesByName[it.name]
- ?: animeHandler.awaitOneExecutable {
- categoriesQueries.insert(it.name, it.order, it.flags)
+ val categories = backupCategories
+ .sortedBy { it.order }
+ .map {
+ val dbCategory = dbCategoriesByName[it.name]
+ if (dbCategory != null) return@map dbCategory
+ val order = nextOrder++
+ animeHandler.awaitOneExecutable {
+ categoriesQueries.insert(it.name, order, it.flags)
categoriesQueries.selectLastInsertedRowId()
- }.let { id -> it.toCategory(id) }
- }
+ }
+ .let { id -> it.toCategory(id).copy(order = order) }
+ }
libraryPreferences.categorizedDisplaySettings().set(
(dbCategories + categories)
@@ -42,14 +48,20 @@ class CategoriesRestorer(
if (backupCategories.isNotEmpty()) {
val dbCategories = getMangaCategories.await()
val dbCategoriesByName = dbCategories.associateBy { it.name }
+ var nextOrder = dbCategories.maxOfOrNull { it.order }?.plus(1) ?: 0
- val categories = backupCategories.map {
- dbCategoriesByName[it.name]
- ?: mangaHandler.awaitOneExecutable {
- categoriesQueries.insert(it.name, it.order, it.flags)
+ val categories = backupCategories
+ .sortedBy { it.order }
+ .map {
+ val dbCategory = dbCategoriesByName[it.name]
+ if (dbCategory != null) return@map dbCategory
+ val order = nextOrder++
+ mangaHandler.awaitOneExecutable {
+ categoriesQueries.insert(it.name, order, it.flags)
categoriesQueries.selectLastInsertedRowId()
- }.let { id -> it.toCategory(id) }
- }
+ }
+ .let { id -> it.toCategory(id).copy(order = order) }
+ }
libraryPreferences.categorizedDisplaySettings().set(
(dbCategories + categories)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/AnimeCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/AnimeCoverFetcher.kt
index 588378ec73..12c6b1e6d4 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/AnimeCoverFetcher.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/AnimeCoverFetcher.kt
@@ -34,7 +34,6 @@ import tachiyomi.domain.source.anime.service.AnimeSourceManager
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.io.IOException
-import java.net.HttpURLConnection.HTTP_NOT_MODIFIED
/**
* A [Fetcher] that fetches cover image for [Anime] object.
@@ -344,5 +343,7 @@ class AnimeCoverFetcher(
private val CACHE_CONTROL_NO_STORE = CacheControl.Builder().noStore().build()
private val CACHE_CONTROL_NO_NETWORK_NO_CACHE = CacheControl.Builder().noCache().onlyIfCached().build()
+
+ private const val HTTP_NOT_MODIFIED = 304
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/BufferedSourceFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/BufferedSourceFetcher.kt
new file mode 100644
index 0000000000..4bee925ed5
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/BufferedSourceFetcher.kt
@@ -0,0 +1,38 @@
+package eu.kanade.tachiyomi.data.coil
+
+import coil3.ImageLoader
+import coil3.decode.DataSource
+import coil3.decode.ImageSource
+import coil3.fetch.FetchResult
+import coil3.fetch.Fetcher
+import coil3.fetch.SourceFetchResult
+import coil3.request.Options
+import okio.BufferedSource
+
+class BufferedSourceFetcher(
+ private val data: BufferedSource,
+ private val options: Options,
+) : Fetcher {
+
+ override suspend fun fetch(): FetchResult {
+ return SourceFetchResult(
+ source = ImageSource(
+ source = data,
+ fileSystem = options.fileSystem,
+ ),
+ mimeType = null,
+ dataSource = DataSource.MEMORY,
+ )
+ }
+
+ class Factory : Fetcher.Factory {
+
+ override fun create(
+ data: BufferedSource,
+ options: Options,
+ imageLoader: ImageLoader,
+ ): Fetcher {
+ return BufferedSourceFetcher(data, options)
+ }
+ }
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt
index a05540c125..e959c3e6c1 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt
@@ -34,7 +34,6 @@ import tachiyomi.domain.source.manga.service.MangaSourceManager
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.io.IOException
-import java.net.HttpURLConnection.HTTP_NOT_MODIFIED
/**
* A [Fetcher] that fetches cover image for [Manga] object.
@@ -344,5 +343,7 @@ class MangaCoverFetcher(
private val CACHE_CONTROL_NO_STORE = CacheControl.Builder().noStore().build()
private val CACHE_CONTROL_NO_NETWORK_NO_CACHE = CacheControl.Builder().noCache().onlyIfCached().build()
+
+ private const val HTTP_NOT_MODIFIED = 304
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt
index 2f0c3df493..6403f760bd 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt
@@ -1,12 +1,16 @@
package eu.kanade.tachiyomi.data.coil
+import android.graphics.Bitmap
import coil3.ImageLoader
-import coil3.asCoilImage
+import coil3.asImage
import coil3.decode.DecodeResult
+import coil3.decode.DecodeUtils
import coil3.decode.Decoder
import coil3.decode.ImageSource
import coil3.fetch.SourceFetchResult
import coil3.request.Options
+import coil3.request.bitmapConfig
+import eu.kanade.tachiyomi.util.system.GLUtil
import okio.BufferedSource
import tachiyomi.core.common.util.system.ImageUtil
import tachiyomi.decoder.ImageDecoder
@@ -18,27 +22,55 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
override suspend fun decode(): DecodeResult {
val decoder = resources.sourceOrNull()?.use {
- ImageDecoder.newInstance(it.inputStream())
+ ImageDecoder.newInstance(it.inputStream(), options.cropBorders, displayProfile)
}
check(decoder != null && decoder.width > 0 && decoder.height > 0) { "Failed to initialize decoder" }
- val bitmap = decoder.decode()
+ val srcWidth = decoder.width
+ val srcHeight = decoder.height
+
+ val dstWidth = options.size.widthPx(options.scale) { srcWidth }
+ val dstHeight = options.size.heightPx(options.scale) { srcHeight }
+
+ val sampleSize = DecodeUtils.calculateInSampleSize(
+ srcWidth = srcWidth,
+ srcHeight = srcHeight,
+ dstWidth = dstWidth,
+ dstHeight = dstHeight,
+ scale = options.scale,
+ )
+
+ var bitmap = decoder.decode(sampleSize = sampleSize)
decoder.recycle()
check(bitmap != null) { "Failed to decode image" }
+ if (
+ options.bitmapConfig == Bitmap.Config.HARDWARE &&
+ maxOf(bitmap.width, bitmap.height) <= GLUtil.maxTextureSize
+ ) {
+ val hwBitmap = bitmap.copy(Bitmap.Config.HARDWARE, false)
+ if (hwBitmap != null) {
+ bitmap.recycle()
+ bitmap = hwBitmap
+ }
+ }
+
return DecodeResult(
- image = bitmap.asCoilImage(),
- isSampled = false,
+ image = bitmap.asImage(),
+ isSampled = sampleSize > 1,
)
}
class Factory : Decoder.Factory {
override fun create(result: SourceFetchResult, options: Options, imageLoader: ImageLoader): Decoder? {
- if (!isApplicable(result.source.source())) return null
- return TachiyomiImageDecoder(result.source, options)
+ return if (options.customDecoder || isApplicable(result.source.source())) {
+ TachiyomiImageDecoder(result.source, options)
+ } else {
+ null
+ }
}
private fun isApplicable(source: BufferedSource): Boolean {
@@ -55,4 +87,8 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
override fun hashCode() = javaClass.hashCode()
}
+
+ companion object {
+ var displayProfile: ByteArray? = null
+ }
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/Utils.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/Utils.kt
new file mode 100644
index 0000000000..7a920bf398
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/Utils.kt
@@ -0,0 +1,44 @@
+package eu.kanade.tachiyomi.data.coil
+
+import coil3.Extras
+import coil3.getExtra
+import coil3.request.ImageRequest
+import coil3.request.Options
+import coil3.size.Dimension
+import coil3.size.Scale
+import coil3.size.Size
+import coil3.size.isOriginal
+import coil3.size.pxOrElse
+
+internal inline fun Size.widthPx(scale: Scale, original: () -> Int): Int {
+ return if (isOriginal) original() else width.toPx(scale)
+}
+
+internal inline fun Size.heightPx(scale: Scale, original: () -> Int): Int {
+ return if (isOriginal) original() else height.toPx(scale)
+}
+
+internal fun Dimension.toPx(scale: Scale): Int = pxOrElse {
+ when (scale) {
+ Scale.FILL -> Int.MIN_VALUE
+ Scale.FIT -> Int.MAX_VALUE
+ }
+}
+
+fun ImageRequest.Builder.cropBorders(enable: Boolean) = apply {
+ extras[cropBordersKey] = enable
+}
+
+val Options.cropBorders: Boolean
+ get() = getExtra(cropBordersKey)
+
+private val cropBordersKey = Extras.Key(default = false)
+
+fun ImageRequest.Builder.customDecoder(enable: Boolean) = apply {
+ extras[customDecoderKey] = enable
+}
+
+val Options.customDecoder: Boolean
+ get() = getExtra(customDecoderKey)
+
+private val customDecoderKey = Extras.Key(default = false)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadProvider.kt
index 52ddf08f3d..215692097f 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadProvider.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadProvider.kt
@@ -57,7 +57,7 @@ class AnimeDownloadProvider(
* @param source the source to query.
*/
fun findSourceDir(source: AnimeSource): UniFile? {
- return downloadsDir?.findFile(getSourceDirName(source), true)
+ return downloadsDir?.findFile(getSourceDirName(source))
}
/**
@@ -68,7 +68,7 @@ class AnimeDownloadProvider(
*/
fun findAnimeDir(animeTitle: String, source: AnimeSource): UniFile? {
val sourceDir = findSourceDir(source)
- return sourceDir?.findFile(getAnimeDirName(animeTitle), true)
+ return sourceDir?.findFile(getAnimeDirName(animeTitle))
}
/**
@@ -87,7 +87,7 @@ class AnimeDownloadProvider(
): UniFile? {
val animeDir = findAnimeDir(animeTitle, source)
return getValidEpisodeDirNames(episodeName, episodeScanlator).asSequence()
- .mapNotNull { animeDir?.findFile(it, true) }
+ .mapNotNull { animeDir?.findFile(it) }
.firstOrNull()
}
@@ -102,7 +102,7 @@ class AnimeDownloadProvider(
val animeDir = findAnimeDir(anime.title, source) ?: return null to emptyList()
return animeDir to episodes.mapNotNull { episode ->
getValidEpisodeDirNames(episode.name, episode.scanlator).asSequence()
- .mapNotNull { animeDir.findFile(it, true) }
+ .mapNotNull { animeDir.findFile(it) }
.firstOrNull()
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadManager.kt
index 1922e78f07..381ca11983 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadManager.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadManager.kt
@@ -18,6 +18,7 @@ import logcat.LogPriority
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.storage.extension
import tachiyomi.core.common.util.lang.launchIO
+import tachiyomi.core.common.util.system.ImageUtil
import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.category.manga.interactor.GetMangaCategories
import tachiyomi.domain.download.service.DownloadPreferences
@@ -171,7 +172,7 @@ class MangaDownloadManager(
source,
)
val files = chapterDir?.listFiles().orEmpty()
- .filter { "image" in it.type.orEmpty() }
+ .filter { it.isFile && ImageUtil.isImage(it.name) { it.openInputStream() } }
if (files.isEmpty()) {
throw Exception(context.stringResource(MR.strings.page_list_empty_error))
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadProvider.kt
index ac562b3464..d1b6b53007 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadProvider.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadProvider.kt
@@ -57,7 +57,7 @@ class MangaDownloadProvider(
* @param source the source to query.
*/
fun findSourceDir(source: MangaSource): UniFile? {
- return downloadsDir?.findFile(getSourceDirName(source), true)
+ return downloadsDir?.findFile(getSourceDirName(source))
}
/**
@@ -68,7 +68,7 @@ class MangaDownloadProvider(
*/
fun findMangaDir(mangaTitle: String, source: MangaSource): UniFile? {
val sourceDir = findSourceDir(source)
- return sourceDir?.findFile(getMangaDirName(mangaTitle), true)
+ return sourceDir?.findFile(getMangaDirName(mangaTitle))
}
/**
@@ -87,7 +87,7 @@ class MangaDownloadProvider(
): UniFile? {
val mangaDir = findMangaDir(mangaTitle, source)
return getValidChapterDirNames(chapterName, chapterScanlator).asSequence()
- .mapNotNull { mangaDir?.findFile(it, true) }
+ .mapNotNull { mangaDir?.findFile(it) }
.firstOrNull()
}
@@ -102,7 +102,7 @@ class MangaDownloadProvider(
val mangaDir = findMangaDir(manga.title, source) ?: return null to emptyList()
return mangaDir to chapters.mapNotNull { chapter ->
getValidChapterDirNames(chapter.name, chapter.scanlator).asSequence()
- .mapNotNull { mangaDir.findFile(it, true) }
+ .mapNotNull { mangaDir.findFile(it) }
.firstOrNull()
}
}
@@ -165,21 +165,12 @@ class MangaDownloadProvider(
*/
fun getValidChapterDirNames(chapterName: String, chapterScanlator: String?): List {
val chapterDirName = getChapterDirName(chapterName, chapterScanlator)
- return buildList(4) {
+ return buildList(2) {
// Folder of images
add(chapterDirName)
// Archived chapters
add("$chapterDirName.cbz")
-
- if (chapterScanlator.isNullOrBlank()) {
- // Previously null scanlator fields were converted to "" due to a bug
- add("_$chapterDirName")
- add("_$chapterDirName.cbz")
- } else {
- // Legacy chapter directory name used in v0.9.2 and before
- add(DiskUtil.buildValidFilename(chapterName))
- }
}
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloader.kt
index 254b2ebd89..f13b9a7049 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloader.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloader.kt
@@ -41,6 +41,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import logcat.LogPriority
+import mihon.core.common.archive.ZipWriter
import nl.adaptivity.xmlutil.serialization.XML
import okhttp3.Response
import okio.Throttler
@@ -63,12 +64,8 @@ import tachiyomi.domain.track.manga.interactor.GetMangaTracks
import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
-import java.io.BufferedOutputStream
import java.io.File
import java.util.Locale
-import java.util.zip.CRC32
-import java.util.zip.ZipEntry
-import java.util.zip.ZipOutputStream
/**
* This class is the one in charge of downloading chapters.
@@ -577,14 +574,8 @@ class MangaDownloader(
* @param file the file where the image is already downloaded.
*/
private fun getImageExtension(response: Response, file: UniFile): String {
- // Read content type if available.
val mime = response.body.contentType()?.run { if (type == "image") "image/$subtype" else null }
- // Else guess from the uri.
- ?: context.contentResolver.getType(file.uri)
- // Else read magic numbers.
- ?: ImageUtil.findImageType { file.openInputStream() }?.mime
-
- return ImageUtil.getExtensionFromMimeType(mime)
+ return ImageUtil.getExtensionFromMimeType(mime) { file.openInputStream() }
}
private fun splitTallImageIfNeeded(page: Page, tmpDir: UniFile) {
@@ -643,25 +634,9 @@ class MangaDownloader(
tmpDir: UniFile,
) {
val zip = mangaDir.createFile("$dirname.cbz$TMP_DIR_SUFFIX")!!
- ZipOutputStream(BufferedOutputStream(zip.openOutputStream())).use { zipOut ->
- zipOut.setMethod(ZipEntry.STORED)
-
- tmpDir.listFiles()?.forEach { img ->
- img.openInputStream().use { input ->
- val data = input.readBytes()
- val size = img.length()
- val entry = ZipEntry(img.name).apply {
- val crc = CRC32().apply {
- update(data)
- }
- setCrc(crc.value)
-
- compressedSize = size
- setSize(size)
- }
- zipOut.putNextEntry(entry)
- zipOut.write(data)
- }
+ ZipWriter(context, zip).use { writer ->
+ tmpDir.listFiles()?.forEach { file ->
+ writer.write(file)
}
}
zip.renameTo("$dirname.cbz")
@@ -694,7 +669,7 @@ class MangaDownloader(
)
// Remove the old file
- dir.findFile(COMIC_INFO_FILE, true)?.delete()
+ dir.findFile(COMIC_INFO_FILE)?.delete()
dir.createFile(COMIC_INFO_FILE)!!.openOutputStream().use {
val comicInfoString = xml.encodeToString(ComicInfo.serializer(), comicInfo)
it.write(comicInfoString.toByteArray())
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateNotifier.kt
index 2f9a9f6491..63212ede39 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateNotifier.kt
@@ -9,6 +9,7 @@ import android.graphics.BitmapFactory
import android.net.Uri
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
+import coil3.asDrawable
import coil3.imageLoader
import coil3.request.ImageRequest
import coil3.request.transformations
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateNotifier.kt
index 3dad875db6..7990d13e58 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateNotifier.kt
@@ -9,6 +9,7 @@ import android.graphics.BitmapFactory
import android.net.Uri
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
+import coil3.asDrawable
import coil3.imageLoader
import coil3.request.ImageRequest
import coil3.request.transformations
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt
index b6c3c307ba..cf906b40ba 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt
@@ -7,6 +7,7 @@ import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
+import android.webkit.MimeTypeMap
import androidx.annotation.RequiresApi
import androidx.core.content.contentValuesOf
import androidx.core.net.toUri
@@ -65,21 +66,26 @@ class ImageSaver(
filename: String,
data: () -> InputStream,
): Uri {
- val pictureDir =
+ val isMimeTypeSupported = MimeTypeMap.getSingleton().hasMimeType(type.mime)
+
+ val pictureDir = if (isMimeTypeSupported) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
+ } else {
+ MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
+ }
val imageLocation = (image.location as Location.Pictures).relativePath
val relativePath = listOf(
- Environment.DIRECTORY_PICTURES,
+ if (isMimeTypeSupported) Environment.DIRECTORY_PICTURES else Environment.DIRECTORY_DOCUMENTS,
context.stringResource(MR.strings.app_name),
imageLocation,
).joinToString(File.separator)
val contentValues = contentValuesOf(
- MediaStore.Images.Media.RELATIVE_PATH to relativePath,
- MediaStore.Images.Media.DISPLAY_NAME to image.name,
- MediaStore.Images.Media.MIME_TYPE to type.mime,
- MediaStore.Images.Media.DATE_MODIFIED to Instant.now().epochSecond,
+ MediaStore.MediaColumns.RELATIVE_PATH to relativePath,
+ MediaStore.MediaColumns.DISPLAY_NAME to if (isMimeTypeSupported) image.name else filename,
+ MediaStore.MediaColumns.MIME_TYPE to type.mime,
+ MediaStore.MediaColumns.DATE_MODIFIED to Instant.now().epochSecond,
)
val picture = findUriOrDefault(relativePath, filename) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt
index 7c126e9691..3eb9978d02 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt
@@ -3,6 +3,8 @@ package eu.kanade.tachiyomi.data.track
import androidx.annotation.CallSuper
import eu.kanade.domain.track.service.TrackPreferences
import eu.kanade.tachiyomi.network.NetworkHelper
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import okhttp3.OkHttpClient
import uy.kohesive.injekt.injectLazy
@@ -29,6 +31,15 @@ abstract class BaseTracker(
get() = getUsername().isNotEmpty() &&
getPassword().isNotEmpty()
+ override val isLoggedInFlow: Flow by lazy {
+ combine(
+ trackPreferences.trackUsername(this).changes(),
+ trackPreferences.trackPassword(this).changes(),
+ ) { username, password ->
+ username.isNotEmpty() && password.isNotEmpty()
+ }
+ }
+
override fun getUsername() = trackPreferences.trackUsername(this).get()
override fun getPassword() = trackPreferences.trackPassword(this).get()
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt
index 0f08150436..67ec1886a9 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt
@@ -5,6 +5,7 @@ import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import dev.icerock.moko.resources.StringResource
import kotlinx.collections.immutable.ImmutableList
+import kotlinx.coroutines.flow.Flow
import okhttp3.OkHttpClient
interface Tracker {
@@ -37,6 +38,8 @@ interface Tracker {
val isLoggedIn: Boolean
+ val isLoggedInFlow: Flow
+
fun getUsername(): String
fun getPassword(): String
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt
index b4fa422e36..08a4c1df46 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt
@@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.data.track.myanimelist.MyAnimeList
import eu.kanade.tachiyomi.data.track.shikimori.Shikimori
import eu.kanade.tachiyomi.data.track.simkl.Simkl
import eu.kanade.tachiyomi.data.track.suwayomi.Suwayomi
+import kotlinx.coroutines.flow.combine
class TrackerManager(context: Context) {
@@ -42,5 +43,13 @@ class TrackerManager(context: Context) {
fun loggedInTrackers() = trackers.filter { it.isLoggedIn }
+ fun loggedInTrackersFlow() = combine(trackers.map { it.isLoggedInFlow }) {
+ it.mapIndexedNotNull { index, isLoggedIn ->
+ if (isLoggedIn) trackers[index] else null
+ }
+ }
+
fun get(id: Long) = trackers.find { it.id == id }
+
+ fun getAll(ids: Set) = trackers.filter { it.id in ids }
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/anime/AnimeExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/anime/AnimeExtensionManager.kt
index e2bf607937..502e85fe3b 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/extension/anime/AnimeExtensionManager.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/extension/anime/AnimeExtensionManager.kt
@@ -13,14 +13,17 @@ import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionInstallReceiver
import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionInstaller
import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionLoader
import eu.kanade.tachiyomi.util.system.toast
-import kotlinx.coroutines.async
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
import logcat.LogPriority
-import tachiyomi.core.common.util.lang.launchNow
import tachiyomi.core.common.util.lang.withUIContext
import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.source.anime.model.StubAnimeSource
@@ -45,6 +48,8 @@ class AnimeExtensionManager(
private val trustExtension: TrustAnimeExtension = Injekt.get(),
) {
+ val scope = CoroutineScope(SupervisorJob())
+
private val _isInitialized = MutableStateFlow(false)
val isInitialized: StateFlow = _isInitialized.asStateFlow()
@@ -60,29 +65,36 @@ class AnimeExtensionManager(
private val iconMap = mutableMapOf()
- private val _installedAnimeExtensionsFlow = MutableStateFlow(
- emptyList(),
- )
- val installedExtensionsFlow = _installedAnimeExtensionsFlow.asStateFlow()
+ private val _installedExtensionsMapFlow = MutableStateFlow(emptyMap())
+ val installedExtensionsFlow = _installedExtensionsMapFlow.mapExtensions(scope)
+
+ private val _availableExtensionsMapFlow = MutableStateFlow(emptyMap())
+ val availableExtensionsFlow = _availableExtensionsMapFlow.mapExtensions(scope)
+
+ private val _untrustedExtensionsMapFlow = MutableStateFlow(emptyMap())
+ val untrustedExtensionsFlow = _untrustedExtensionsMapFlow.mapExtensions(scope)
+
+ init {
+ initAnimeExtensions()
+ AnimeExtensionInstallReceiver(AnimeInstallationListener()).register(context)
+ }
private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet()
fun getAppIconForSource(sourceId: Long): Drawable? {
- val pkgName = _installedAnimeExtensionsFlow.value.find { ext -> ext.sources.any { it.id == sourceId } }?.pkgName
- if (pkgName != null) {
- return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) {
- AnimeExtensionLoader.getAnimeExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo
- .loadIcon(context.packageManager)
+ val pkgName = _installedExtensionsMapFlow.value.values
+ .find { ext ->
+ ext.sources.any { it.id == sourceId }
}
+ ?.pkgName
+ ?: return null
+
+ return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) {
+ AnimeExtensionLoader.getAnimeExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo
+ .loadIcon(context.packageManager)
}
- return null
}
- private val _availableExtensionsFlow = MutableStateFlow(
- emptyList(),
- )
- val availableExtensionsFlow = _availableExtensionsFlow.asStateFlow()
-
private var availableAnimeExtensionsSourcesData: Map = emptyMap()
private fun setupAvailableAnimeExtensionsSourcesDataMap(
@@ -96,35 +108,25 @@ class AnimeExtensionManager(
fun getSourceData(id: Long) = availableAnimeExtensionsSourcesData[id]
- private val _untrustedExtensionsFlow = MutableStateFlow(
- emptyList(),
- )
- val untrustedExtensionsFlow = _untrustedExtensionsFlow.asStateFlow()
-
- init {
- initAnimeExtensions()
- AnimeExtensionInstallReceiver(AnimeInstallationListener()).register(context)
- }
-
/**
* Loads and registers the installed animeextensions.
*/
private fun initAnimeExtensions() {
val animeextensions = AnimeExtensionLoader.loadExtensions(context)
- _installedAnimeExtensionsFlow.value = animeextensions
+ _installedExtensionsMapFlow.value = animeextensions
.filterIsInstance()
- .map { it.extension }
+ .associate { it.extension.pkgName to it.extension }
- _untrustedExtensionsFlow.value = animeextensions
+ _untrustedExtensionsMapFlow.value = animeextensions
.filterIsInstance()
- .map { it.extension }
+ .associate { it.extension.pkgName to it.extension }
_isInitialized.value = true
}
/**
- * Finds the available anime extensions in the [api] and updates [availableExtensions].
+ * Finds the available anime extensions in the [api] and updates [_availableExtensionsMapFlow].
*/
suspend fun findAvailableExtensions() {
val extensions: List = try {
@@ -137,7 +139,7 @@ class AnimeExtensionManager(
enableAdditionalSubLanguages(extensions)
- _availableExtensionsFlow.value = extensions
+ _availableExtensionsMapFlow.value = extensions.associateBy { it.pkgName }
updatedInstalledAnimeExtensionsStatuses(extensions)
setupAvailableAnimeExtensionsSourcesDataMap(extensions)
}
@@ -185,35 +187,32 @@ class AnimeExtensionManager(
return
}
- val mutInstalledExtensions = _installedAnimeExtensionsFlow.value.toMutableList()
+ val installedExtensionsMap = _installedExtensionsMapFlow.value.toMutableMap()
var changed = false
- for ((index, installedExt) in mutInstalledExtensions.withIndex()) {
- val pkgName = installedExt.pkgName
+ for ((pkgName, extension) in installedExtensionsMap) {
val availableExt = availableExtensions.find { it.pkgName == pkgName }
- if (availableExt == null && !installedExt.isObsolete) {
- mutInstalledExtensions[index] = installedExt.copy(isObsolete = true)
+ if (availableExt == null && !extension.isObsolete) {
+ installedExtensionsMap[pkgName] = extension.copy(isObsolete = true)
changed = true
} else if (availableExt != null) {
- val hasUpdate = installedExt.updateExists(availableExt)
-
- if (installedExt.hasUpdate != hasUpdate) {
- mutInstalledExtensions[index] = installedExt.copy(
+ val hasUpdate = extension.updateExists(availableExt)
+ if (extension.hasUpdate != hasUpdate) {
+ installedExtensionsMap[pkgName] = extension.copy(
hasUpdate = hasUpdate,
repoUrl = availableExt.repoUrl,
)
- changed = true
} else {
- mutInstalledExtensions[index] = installedExt.copy(
+ installedExtensionsMap[pkgName] = extension.copy(
repoUrl = availableExt.repoUrl,
)
- changed = true
}
+ changed = true
}
}
if (changed) {
- _installedAnimeExtensionsFlow.value = mutInstalledExtensions
+ _installedExtensionsMapFlow.value = installedExtensionsMap
}
updatePendingUpdatesCount()
}
@@ -237,8 +236,7 @@ class AnimeExtensionManager(
* @param extension The anime extension to be updated.
*/
fun updateExtension(extension: AnimeExtension.Installed): Flow {
- val availableExt = _availableExtensionsFlow.value.find { it.pkgName == extension.pkgName }
- ?: return emptyFlow()
+ val availableExt = _availableExtensionsMapFlow.value[extension.pkgName] ?: return emptyFlow()
return installExtension(availableExt)
}
@@ -274,29 +272,16 @@ class AnimeExtensionManager(
*
* @param extension the extension to trust
*/
- fun trust(extension: AnimeExtension.Untrusted) {
- val untrustedPkgNames = _untrustedExtensionsFlow.value.map { it.pkgName }.toSet()
- if (extension.pkgName !in untrustedPkgNames) return
+ suspend fun trust(extension: AnimeExtension.Untrusted) {
+ _untrustedExtensionsMapFlow.value[extension.pkgName] ?: return
trustExtension.trust(extension.pkgName, extension.versionCode, extension.signatureHash)
- val nowTrustedExtensions = _untrustedExtensionsFlow.value
- .filter { it.pkgName == extension.pkgName && it.versionCode == extension.versionCode }
- _untrustedExtensionsFlow.value -= nowTrustedExtensions
-
- launchNow {
- nowTrustedExtensions
- .map { extension ->
- async {
- AnimeExtensionLoader.loadExtensionFromPkgName(
- context,
- extension.pkgName,
- )
- }.await()
- }
- .filterIsInstance()
- .forEach { registerNewExtension(it.extension) }
- }
+ _untrustedExtensionsMapFlow.value -= extension.pkgName
+
+ AnimeExtensionLoader.loadExtensionFromPkgName(context, extension.pkgName)
+ .let { it as? AnimeLoadResult.Success }
+ ?.let { registerNewExtension(it.extension) }
}
/**
@@ -305,7 +290,7 @@ class AnimeExtensionManager(
* @param extension The anime extension to be registered.
*/
private fun registerNewExtension(extension: AnimeExtension.Installed) {
- _installedAnimeExtensionsFlow.value += extension
+ _installedExtensionsMapFlow.value += extension
}
/**
@@ -315,13 +300,7 @@ class AnimeExtensionManager(
* @param extension The anime extension to be registered.
*/
private fun registerUpdatedExtension(extension: AnimeExtension.Installed) {
- val mutInstalledAnimeExtensions = _installedAnimeExtensionsFlow.value.toMutableList()
- val oldAnimeExtension = mutInstalledAnimeExtensions.find { it.pkgName == extension.pkgName }
- if (oldAnimeExtension != null) {
- mutInstalledAnimeExtensions -= oldAnimeExtension
- }
- mutInstalledAnimeExtensions += extension
- _installedAnimeExtensionsFlow.value = mutInstalledAnimeExtensions
+ _installedExtensionsMapFlow.value += extension
}
/**
@@ -331,14 +310,8 @@ class AnimeExtensionManager(
* @param pkgName The package name of the uninstalled application.
*/
private fun unregisterAnimeExtension(pkgName: String) {
- val installedAnimeExtension = _installedAnimeExtensionsFlow.value.find { it.pkgName == pkgName }
- if (installedAnimeExtension != null) {
- _installedAnimeExtensionsFlow.value -= installedAnimeExtension
- }
- val untrustedAnimeExtension = _untrustedExtensionsFlow.value.find { it.pkgName == pkgName }
- if (untrustedAnimeExtension != null) {
- _untrustedExtensionsFlow.value -= untrustedAnimeExtension
- }
+ _installedExtensionsMapFlow.value -= pkgName
+ _untrustedExtensionsMapFlow.value -= pkgName
}
/**
@@ -357,14 +330,9 @@ class AnimeExtensionManager(
}
override fun onExtensionUntrusted(extension: AnimeExtension.Untrusted) {
- val installedExtension = _installedAnimeExtensionsFlow.value
- .find { it.pkgName == extension.pkgName }
-
- if (installedExtension != null) {
- _installedAnimeExtensionsFlow.value -= installedExtension
- } else {
- _untrustedExtensionsFlow.value += extension
- }
+ _installedExtensionsMapFlow.value -= extension.pkgName
+ _untrustedExtensionsMapFlow.value += extension
+ updatePendingUpdatesCount()
}
override fun onPackageUninstalled(pkgName: String) {
@@ -388,17 +356,26 @@ class AnimeExtensionManager(
private fun AnimeExtension.Installed.updateExists(
availableExtension: AnimeExtension.Available? = null,
): Boolean {
- val availableExt = availableExtension ?: _availableExtensionsFlow.value.find { it.pkgName == pkgName }
+ val availableExt = availableExtension
+ ?: _availableExtensionsMapFlow.value[pkgName]
?: return false
return (availableExt.versionCode > versionCode || availableExt.libVersion > libVersion)
}
private fun updatePendingUpdatesCount() {
- val pendingUpdateCount = _installedAnimeExtensionsFlow.value.count { it.hasUpdate }
+ val pendingUpdateCount = _installedExtensionsMapFlow.value.values.count { it.hasUpdate }
preferences.animeExtensionUpdatesCount().set(pendingUpdateCount)
if (pendingUpdateCount == 0) {
ExtensionUpdateNotifier(context).dismiss()
}
}
+
+ private operator fun Map.plus(extension: T) = plus(extension.pkgName to extension)
+
+ private fun StateFlow