From 753285275f668506ea149390c9f3b8cf9eab4deb Mon Sep 17 00:00:00 2001 From: "Cuong M. Tran" Date: Wed, 12 Jun 2024 18:22:44 +0700 Subject: [PATCH] Improve Theme cover based (#81) * Support Pure dark mode * Tag using elevated style (non pure-dark-mode) and more vibrant outline color (pure-dark-mode) * More color for various icons * Color theme for Metadata screen * Always animated color change * Always/Only use dominant color for library/update/upcoming cover's background * Don't always generate color each time cover loaded (library/browse), only update color when new cover changed on detail page * Fix the spinner background in Edit Manga Info * Improve manga's backdrop color Detail: * Change Cover's color & ratio preference key to AppState * using ElevatedSuggestionChip with color * more icon color * color theme for MetadataViewScreen * enable animation by default * Fix: can't turn off theme cover based * Won't check pref for theme enabled everytime, instead always generate cover's color to run faster later * Skip generate cover's vibrant color for browsing mangas if already generated, also won't even trying to read ratio for favorite mangas * Only generate new seed color when cover loaded in detail page * only use dominantColor for cover when cover is unable to load * Don't use elevatedSuggestionChip for darkmode and change border color * EditMangaInfo: Set Spinner's selected item's background color to transparent * update chip color * Colors Palette screen * Support Amoled Dark Mode by upgrading Material-Kolor to 1.7.0 * improve manga's backdrop color * only generate color while browsing if option is enabled, aslo fixed the case won't update dominant color if cover changed * always using theme animation * update translation --- .../java/eu/kanade/domain/ui/UiPreferences.kt | 4 +- .../history/components/HistoryItem.kt | 8 +- .../library/components/CommonMangaItem.kt | 42 ++-- .../components/LibraryComfortableGrid.kt | 1 + .../library/components/LibraryCompactGrid.kt | 1 + .../library/components/LibraryList.kt | 1 + .../kanade/presentation/manga/MangaScreen.kt | 15 ++ .../components/ChapterDownloadIndicator.kt | 6 +- .../manga/components/ChapterHeader.kt | 40 ++-- .../manga/components/MangaCover.kt | 22 +- .../manga/components/MangaInfoHeader.kt | 35 +++- .../manga/components/MangaToolbar.kt | 11 +- .../manga/components/NamespaceTags.kt | 73 +++++-- .../screen/SettingsAppearanceScreen.kt | 11 +- .../presentation/updates/UpdatesUiItem.kt | 8 +- app/src/main/java/eu/kanade/tachiyomi/App.kt | 2 +- .../tachiyomi/data/coil/MangaCoverFetcher.kt | 13 +- .../tachiyomi/data/coil/MangaCoverMetadata.kt | 55 +++-- .../tachiyomi/ui/manga/EditMangaDialog.kt | 24 +-- .../kanade/tachiyomi/ui/manga/MangaScreen.kt | 20 +- .../tachiyomi/ui/manga/MangaScreenModel.kt | 81 +++---- .../tachiyomi/ui/manga/PaletteScreen.kt | 197 ++++++++++++++++++ .../exh/ui/metadata/MetadataViewScreen.kt | 25 ++- .../adapters/EHentaiDescriptionAdapter.kt | 4 +- .../adapters/EightMusesDescriptionAdapter.kt | 2 +- .../adapters/HBrowseDescriptionAdapter.kt | 2 +- .../adapters/MangaDexDescriptionAdapter.kt | 2 +- .../adapters/NHentaiDescriptionAdapter.kt | 2 +- .../adapters/PururinDescriptionAdapter.kt | 2 +- .../adapters/TsuminoDescriptionAdapter.kt | 2 +- .../upcoming/components/UpcomingItem.kt | 8 +- .../library/service/LibraryPreferences.kt | 10 +- gradle/libs.versions.toml | 6 +- .../commonMain/resources/MR/ar/strings.xml | 3 +- .../commonMain/resources/MR/base/strings.xml | 3 +- .../commonMain/resources/MR/ca/strings.xml | 3 +- .../commonMain/resources/MR/cs/strings.xml | 3 +- .../commonMain/resources/MR/da/strings.xml | 3 +- .../commonMain/resources/MR/de/strings.xml | 3 +- .../commonMain/resources/MR/el/strings.xml | 3 +- .../commonMain/resources/MR/es/strings.xml | 3 +- .../commonMain/resources/MR/fi/strings.xml | 3 +- .../commonMain/resources/MR/fil/strings.xml | 3 +- .../commonMain/resources/MR/fr/strings.xml | 3 +- .../commonMain/resources/MR/he/strings.xml | 3 +- .../commonMain/resources/MR/hi/strings.xml | 3 +- .../commonMain/resources/MR/hu/strings.xml | 3 +- .../commonMain/resources/MR/id/strings.xml | 3 +- .../commonMain/resources/MR/it/strings.xml | 3 +- .../commonMain/resources/MR/ja/strings.xml | 5 +- .../commonMain/resources/MR/ko/strings.xml | 3 +- .../commonMain/resources/MR/nl/strings.xml | 3 +- .../commonMain/resources/MR/pl/strings.xml | 3 +- .../resources/MR/pt-rBR/strings.xml | 3 +- .../commonMain/resources/MR/pt/strings.xml | 3 +- .../commonMain/resources/MR/ro/strings.xml | 3 +- .../commonMain/resources/MR/ru/strings.xml | 3 +- .../commonMain/resources/MR/sr/strings.xml | 3 +- .../commonMain/resources/MR/sv/strings.xml | 3 +- .../commonMain/resources/MR/th/strings.xml | 3 +- .../commonMain/resources/MR/tr/strings.xml | 3 +- .../commonMain/resources/MR/uk/strings.xml | 3 +- .../commonMain/resources/MR/vi/strings.xml | 3 +- .../resources/MR/zh-rCN/strings.xml | 3 +- .../resources/MR/zh-rTW/strings.xml | 3 +- 65 files changed, 558 insertions(+), 275 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/manga/PaletteScreen.kt diff --git a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt index 02161458b..4512e9ad8 100644 --- a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt +++ b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt @@ -25,12 +25,10 @@ class UiPreferences( fun themeDarkAmoled() = preferenceStore.getBoolean("pref_theme_dark_amoled_key", false) - fun detailsPageThemeCoverBased() = preferenceStore.getBoolean("pref_details_page_theme_cover_based_key", true) + fun themeCoverBased() = preferenceStore.getBoolean("pref_theme_cover_based_key", true) fun themeCoverBasedStyle() = preferenceStore.getEnum("pref_theme_cover_based_style_key", PaletteStyle.Vibrant) - fun themeCoverBasedAnimate() = preferenceStore.getBoolean("pref_theme_cover_based_animate_key", false) - fun relativeTime() = preferenceStore.getBoolean("relative_time_v2", true) fun dateFormat() = preferenceStore.getString("app_date_format", "") diff --git a/app/src/main/java/eu/kanade/presentation/history/components/HistoryItem.kt b/app/src/main/java/eu/kanade/presentation/history/components/HistoryItem.kt index 4298ba43c..ab6ff8004 100644 --- a/app/src/main/java/eu/kanade/presentation/history/components/HistoryItem.kt +++ b/app/src/main/java/eu/kanade/presentation/history/components/HistoryItem.kt @@ -17,6 +17,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewLightDark @@ -48,10 +49,15 @@ fun HistoryItem( .padding(horizontal = MaterialTheme.padding.medium, vertical = MaterialTheme.padding.small), verticalAlignment = Alignment.CenterVertically, ) { + val mangaCover = history.coverData + val bgColor = mangaCover.dominantCoverColors?.first?.let { Color(it) } + val onBgColor = mangaCover.dominantCoverColors?.second MangaCover.Book( modifier = Modifier.fillMaxHeight(), - data = history.coverData, + data = mangaCover, onClick = onClickCover, + bgColor = bgColor, + tint = onBgColor, ) Column( modifier = Modifier diff --git a/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt b/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt index bb0c5f944..f7cba2b21 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt @@ -27,18 +27,15 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shadow -import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import eu.kanade.presentation.manga.components.CoverPlaceholderColor import eu.kanade.presentation.manga.components.MangaCover import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.BadgeGroup @@ -74,7 +71,10 @@ fun MangaCompactGridItem( coverAlpha: Float = 1f, coverBadgeStart: @Composable (RowScope.() -> Unit)? = null, coverBadgeEnd: @Composable (RowScope.() -> Unit)? = null, + libraryColored: Boolean? = null, ) { + val bgColor = libraryColored?.let { coverData.dominantCoverColors?.first?.let { Color(it) } } + val onBgColor = libraryColored?.let { coverData.dominantCoverColors?.second } GridItemSelectable( isSelected = isSelected, onClick = onClick, @@ -84,15 +84,11 @@ fun MangaCompactGridItem( cover = { MangaCover.Book( modifier = Modifier - .fillMaxWidth() - .background( - color = DomainMangaCover.coverColorMap[coverData.mangaId]?.first?.let { Color(it) } - ?: MaterialTheme.colorScheme.surface - ), + .fillMaxWidth(), alpha = if (isSelected) GridSelectedCoverAlpha else coverAlpha, data = coverData, - tint = DomainMangaCover.coverColorMap[coverData.mangaId]?.second - ?: MaterialTheme.colorScheme.onSurface.toArgb(), + bgColor = bgColor, + tint = onBgColor, ) }, badgesStart = coverBadgeStart, @@ -182,7 +178,10 @@ fun MangaComfortableGridItem( coverBadgeStart: (@Composable RowScope.() -> Unit)? = null, coverBadgeEnd: (@Composable RowScope.() -> Unit)? = null, onClickContinueReading: (() -> Unit)? = null, + libraryColored: Boolean? = null, ) { + val bgColor = libraryColored?.let { coverData.dominantCoverColors?.first?.let { Color(it) } } + val onBgColor = libraryColored?.let { coverData.dominantCoverColors?.second } GridItemSelectable( isSelected = isSelected, onClick = onClick, @@ -193,15 +192,11 @@ fun MangaComfortableGridItem( cover = { MangaCover.Book( modifier = Modifier - .fillMaxWidth() - .background( - color = DomainMangaCover.coverColorMap[coverData.mangaId]?.first?.let { Color(it) } - ?: MaterialTheme.colorScheme.surface - ), + .fillMaxWidth(), alpha = if (isSelected) GridSelectedCoverAlpha else coverAlpha, data = coverData, - tint = DomainMangaCover.coverColorMap[coverData.mangaId]?.second - ?: MaterialTheme.colorScheme.onSurface.toArgb(), + bgColor = bgColor, + tint = onBgColor, ) }, badgesStart = coverBadgeStart, @@ -339,7 +334,10 @@ fun MangaListItem( isSelected: Boolean = false, coverAlpha: Float = 1f, onClickContinueReading: (() -> Unit)? = null, + libraryColored: Boolean? = null, ) { + val bgColor = libraryColored?.let { coverData.dominantCoverColors?.first?.let { Color(it) } } + val onBgColor = libraryColored?.let { coverData.dominantCoverColors?.second } Row( modifier = Modifier .selectedBackground(isSelected) @@ -353,15 +351,11 @@ fun MangaListItem( ) { MangaCover.Square( modifier = Modifier - .fillMaxHeight() - .background( - color = DomainMangaCover.coverColorMap[coverData.mangaId]?.first?.let { Color(it) } - ?: MaterialTheme.colorScheme.surface - ), + .fillMaxHeight(), alpha = coverAlpha, data = coverData, - tint = DomainMangaCover.coverColorMap[coverData.mangaId]?.second - ?: MaterialTheme.colorScheme.onSurface.toArgb(), + bgColor = bgColor, + tint = onBgColor, ) Text( text = title, diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryComfortableGrid.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryComfortableGrid.kt index dd209ce40..ee81c440c 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryComfortableGrid.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryComfortableGrid.kt @@ -61,6 +61,7 @@ internal fun LibraryComfortableGrid( } else { null }, + libraryColored = true, ) } } diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryCompactGrid.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryCompactGrid.kt index 23cd51cfe..93887d0a9 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryCompactGrid.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryCompactGrid.kt @@ -62,6 +62,7 @@ internal fun LibraryCompactGrid( } else { null }, + libraryColored = true, ) } } diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryList.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryList.kt index 4f20f0274..982e6e4c5 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryList.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryList.kt @@ -69,6 +69,7 @@ internal fun LibraryList( } else { null }, + libraryColored = true, ) } } diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index 035d489fd..65d801750 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -101,6 +101,7 @@ import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.service.missingChaptersCount import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.manga.model.Manga +import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.source.model.StubSource import tachiyomi.i18n.MR import tachiyomi.i18n.kmk.KMR @@ -183,6 +184,8 @@ fun MangaScreen( onRelatedMangaClick: (Manga) -> Unit, onRelatedMangaLongClick: (Manga) -> Unit, onSourceClick: () -> Unit, + onCoverLoaded: (MangaCover) -> Unit, + onPaletteScreenClick: () -> Unit, // KMK <-- ) { val context = LocalContext.current @@ -243,6 +246,8 @@ fun MangaScreen( onRelatedMangaClick = onRelatedMangaClick, onRelatedMangaLongClick = onRelatedMangaLongClick, onSourceClick = onSourceClick, + onCoverLoaded = onCoverLoaded, + onPaletteScreenClick = onPaletteScreenClick, // KMK <-- ) } else { @@ -296,6 +301,8 @@ fun MangaScreen( onRelatedMangaClick = onRelatedMangaClick, onRelatedMangaLongClick = onRelatedMangaLongClick, onSourceClick = onSourceClick, + onCoverLoaded = onCoverLoaded, + onPaletteScreenClick = onPaletteScreenClick, // KMK <-- ) } @@ -366,6 +373,8 @@ private fun MangaScreenSmallImpl( onRelatedMangaClick: (Manga) -> Unit, onRelatedMangaLongClick: (Manga) -> Unit, onSourceClick: () -> Unit, + onCoverLoaded: (MangaCover) -> Unit, + onPaletteScreenClick: () -> Unit, // KMK <-- ) { val chapterListState = rememberLazyListState() @@ -437,6 +446,7 @@ private fun MangaScreenSmallImpl( actionModeCounter = selectedChapterCount, onSelectAll = { onAllChapterSelected(true) }, onInvertSelection = { onInvertSelection() }, + onPaletteScreenClick = onPaletteScreenClick, ) }, bottomBar = { @@ -520,6 +530,7 @@ private fun MangaScreenSmallImpl( doSearch = onSearch, // KMK --> onSourceClick = onSourceClick, + onCoverLoaded = onCoverLoaded, // KMK <-- ) } @@ -747,6 +758,8 @@ private fun MangaScreenLargeImpl( onRelatedMangaClick: (Manga) -> Unit, onRelatedMangaLongClick: (Manga) -> Unit, onSourceClick: () -> Unit, + onCoverLoaded: (MangaCover) -> Unit, + onPaletteScreenClick: () -> Unit, // KMK <-- ) { val layoutDirection = LocalLayoutDirection.current @@ -809,6 +822,7 @@ private fun MangaScreenLargeImpl( actionModeCounter = selectedChapterCount, onSelectAll = { onAllChapterSelected(true) }, onInvertSelection = { onInvertSelection() }, + onPaletteScreenClick = onPaletteScreenClick, ) }, bottomBar = { @@ -893,6 +907,7 @@ private fun MangaScreenLargeImpl( doSearch = onSearch, // KMK --> onSourceClick = onSourceClick, + onCoverLoaded = onCoverLoaded, // KMK <-- ) MangaActionRow( diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt index 0f04aab96..4b5c4532a 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt @@ -102,7 +102,7 @@ private fun NotDownloadedIndicator( painter = painterResource(R.drawable.ic_download_chapter_24dp), contentDescription = stringResource(MR.strings.manga_download), modifier = Modifier.size(IndicatorSize), - tint = MaterialTheme.colorScheme.onSurfaceVariant, + tint = MaterialTheme.colorScheme.primary, ) } } @@ -128,7 +128,7 @@ private fun DownloadingIndicator( contentAlignment = Alignment.Center, ) { val arrowColor: Color - val strokeColor = MaterialTheme.colorScheme.onSurfaceVariant + val strokeColor = MaterialTheme.colorScheme.primary val downloadProgress = downloadProgressProvider() val indeterminate = downloadState == Download.State.QUEUE || (downloadState == Download.State.DOWNLOADING && downloadProgress == 0) @@ -208,7 +208,7 @@ private fun DownloadedIndicator( imageVector = Icons.Filled.CheckCircle, contentDescription = null, modifier = Modifier.size(IndicatorSize), - tint = MaterialTheme.colorScheme.onSurfaceVariant, + tint = MaterialTheme.colorScheme.primary, ) DropdownMenu(expanded = isMenuExpanded, onDismissRequest = { isMenuExpanded = false }) { DropdownMenuItem( diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterHeader.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterHeader.kt index 99ad1b37a..79d580044 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterHeader.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterHeader.kt @@ -3,11 +3,16 @@ package eu.kanade.presentation.manga.components import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.FilterList +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -25,7 +30,7 @@ fun ChapterHeader( onClick: () -> Unit, modifier: Modifier = Modifier, ) { - Column( + Row( modifier = modifier .fillMaxWidth() .clickable( @@ -33,19 +38,30 @@ fun ChapterHeader( onClick = onClick, ) .padding(horizontal = 16.dp, vertical = 4.dp), - verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, ) { - Text( - text = if (chapterCount == null) { - stringResource(MR.strings.chapters) - } else { - pluralStringResource(MR.plurals.manga_num_chapters, count = chapterCount, chapterCount) - }, - style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onBackground, - ) + Column( + verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall), + ) { + Text( + text = if (chapterCount == null) { + stringResource(MR.strings.chapters) + } else { + pluralStringResource(MR.plurals.manga_num_chapters, count = chapterCount, chapterCount) + }, + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onBackground, + ) - MissingChaptersWarning(missingChapterCount) + MissingChaptersWarning(missingChapterCount) + } + + Icon( + imageVector = Icons.Outlined.FilterList, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + ) } } diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt index fbd1f1a04..105278b61 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt @@ -4,6 +4,7 @@ import androidx.annotation.ColorInt import androidx.compose.animation.graphics.res.animatedVectorResource import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter import androidx.compose.animation.graphics.vector.AnimatedImageVector +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.aspectRatio import androidx.compose.material3.MaterialTheme @@ -16,6 +17,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.semantics.Role @@ -23,6 +25,9 @@ import coil3.compose.AsyncImage import eu.kanade.presentation.util.rememberResourceBitmapPainter import eu.kanade.tachiyomi.R import kotlinx.coroutines.delay +import tachiyomi.domain.manga.model.Manga +import tachiyomi.domain.manga.model.asMangaCover +import tachiyomi.domain.manga.model.MangaCover as DomainMangaCover enum class MangaCover(val ratio: Float) { Square(1f / 1f), @@ -36,8 +41,10 @@ enum class MangaCover(val ratio: Float) { contentDescription: String = "", shape: Shape = MaterialTheme.shapes.extraSmall, onClick: (() -> Unit)? = null, - @ColorInt tint: Int = CoverPlaceholderColor, alpha: Float = 1f, + bgColor: Color? = null, + @ColorInt tint: Int? = null, + onCoverLoaded: ((DomainMangaCover) -> Unit)? = null, ) { val animatedImageVector = AnimatedImageVector.animatedVectorResource(R.drawable.anim_waiting) var atEnd by remember { mutableStateOf(false) } @@ -60,12 +67,21 @@ enum class MangaCover(val ratio: Float) { placeholder = rememberAnimatedVectorPainter(animatedImageVector = animatedImageVector, atEnd = atEnd), error = rememberResourceBitmapPainter(id = R.drawable.cover_error, tint), fallback = rememberResourceBitmapPainter(id = R.drawable.cover_error, tint), - onSuccess = { succeed = true }, + onSuccess = { + succeed = true + if (onCoverLoaded != null) { + when (data) { + is Manga -> onCoverLoaded(data.asMangaCover()) + is DomainMangaCover -> onCoverLoaded(data) + } + } + }, contentDescription = contentDescription, modifier = modifier .aspectRatio(ratio) .clip(shape) .alpha(if (succeed) alpha else 1f) + .background(bgColor ?: Color.Transparent) .then( if (onClick != null) { Modifier.clickable( @@ -81,6 +97,4 @@ enum class MangaCover(val ratio: Float) { } } -@ColorInt -internal val CoverPlaceholderColor = 0x7F888888 const val LoadingAnimatedDuration = 600L diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt index 293632aa9..0549ec5ad 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt @@ -32,7 +32,6 @@ import androidx.compose.material.icons.filled.PersonOutline import androidx.compose.material.icons.filled.Warning import androidx.compose.material.icons.outlined.AttachMoney import androidx.compose.material.icons.outlined.Block -import androidx.compose.material.icons.outlined.CallMerge import androidx.compose.material.icons.outlined.Close import androidx.compose.material.icons.outlined.Done import androidx.compose.material.icons.outlined.DoneAll @@ -74,7 +73,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import coil3.compose.AsyncImage -import com.materialkolor.ktx.blend +import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.components.DropdownMenu import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.SManga @@ -88,9 +87,12 @@ import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.clickableNoIndication import tachiyomi.presentation.core.util.secondaryItemAlpha +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import java.time.Instant import java.time.temporal.ChronoUnit import kotlin.math.roundToInt +import tachiyomi.domain.manga.model.MangaCover as DomainMangaCover private val whitespaceLineRegex = Regex("[\\r\\n]{2,}", setOf(RegexOption.MULTILINE)) @@ -110,6 +112,7 @@ fun MangaInfoBox( modifier: Modifier = Modifier, // KMK --> onSourceClick: () -> Unit, + onCoverLoaded: (DomainMangaCover) -> Unit, // KMK <-- ) { Box(modifier = modifier) { @@ -127,11 +130,14 @@ fun MangaInfoBox( .drawWithContent { drawContent() drawRect( - brush = Brush.verticalGradient(colors = backdropGradientColors), + brush = Brush.verticalGradient( + colors = backdropGradientColors, + startY = size.height / 2, + ), ) } - .background(MaterialTheme.colorScheme.inversePrimary.copy(alpha = 0.2f)) - .blur(4.dp) + .background(MaterialTheme.colorScheme.surfaceTint.copy(alpha = 0.4f)) + .blur(7.dp) .alpha(0.2f), ) @@ -151,6 +157,7 @@ fun MangaInfoBox( isStubSource = isStubSource, // KMK --> onSourceClick = onSourceClick, + onCoverLoaded = onCoverLoaded, // KMK <-- ) } else { @@ -167,6 +174,7 @@ fun MangaInfoBox( isStubSource = isStubSource, // KMK --> onSourceClick = onSourceClick, + onCoverLoaded = onCoverLoaded, // KMK <-- ) } @@ -226,7 +234,11 @@ fun MangaActionRow( ) }, icon = Icons.Default.HourglassEmpty, - color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor, + color = if (isUserIntervalMode || nextUpdateDays?.let { it <= 1 } == true) { + MaterialTheme.colorScheme.primary + } else { + defaultActionButtonColor + }, onClick = { onEditIntervalClicked?.invoke() }, ) MangaActionButton( @@ -274,6 +286,8 @@ fun ExpandableMangaDescription( // SY <-- modifier: Modifier = Modifier, ) { + val uiPreferences = Injekt.get() + val pureDarkMode = uiPreferences.themeDarkAmoled().get() Column(modifier = modifier) { val (expanded, onExpanded) = rememberSaveable { mutableStateOf(defaultExpandState) @@ -341,6 +355,7 @@ fun ExpandableMangaDescription( tagSelected = it showMenu = true }, + pureDarkMode = pureDarkMode, ) } else { // SY <-- @@ -356,6 +371,7 @@ fun ExpandableMangaDescription( tagSelected = it showMenu = true }, + pureDarkMode = pureDarkMode, ) } } @@ -373,6 +389,7 @@ fun ExpandableMangaDescription( tagSelected = it showMenu = true }, + pureDarkMode = pureDarkMode, ) } } @@ -396,6 +413,7 @@ private fun MangaAndSourceTitlesLarge( isStubSource: Boolean, // KMK --> onSourceClick: () -> Unit, + onCoverLoaded: (DomainMangaCover) -> Unit, // KMK <-- ) { Column( @@ -409,6 +427,7 @@ private fun MangaAndSourceTitlesLarge( data = coverDataProvider(), contentDescription = stringResource(MR.strings.manga_cover), onClick = onCoverClick, + onCoverLoaded = { mangaCover -> onCoverLoaded(mangaCover) }, ) Spacer(modifier = Modifier.height(16.dp)) MangaContentInfo( @@ -441,6 +460,7 @@ private fun MangaAndSourceTitlesSmall( isStubSource: Boolean, // KMK --> onSourceClick: () -> Unit, + onCoverLoaded: (DomainMangaCover) -> Unit, // KMK <-- ) { Row( @@ -457,6 +477,7 @@ private fun MangaAndSourceTitlesSmall( data = coverDataProvider(), contentDescription = stringResource(MR.strings.manga_cover), onClick = onCoverClick, + onCoverLoaded = { mangaCover -> onCoverLoaded(mangaCover) }, ) Column( verticalArrangement = Arrangement.spacedBy(2.dp), @@ -674,7 +695,7 @@ private fun MangaSummary( contentDescription = stringResource( if (expanded) MR.strings.manga_info_collapse else MR.strings.manga_info_expand, ), - tint = MaterialTheme.colorScheme.onBackground, + tint = MaterialTheme.colorScheme.primary, modifier = Modifier.background(Brush.radialGradient(colors = colors.asReversed())), ) } diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaToolbar.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaToolbar.kt index 7210355e2..d1b645782 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaToolbar.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import dev.icerock.moko.graphics.BuildConfig import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.DownloadDropdownMenu @@ -59,6 +60,7 @@ fun MangaToolbar( modifier: Modifier = Modifier, backgroundAlphaProvider: () -> Float = titleAlphaProvider, + onPaletteScreenClick: () -> Unit, ) { Column( modifier = modifier, @@ -191,6 +193,14 @@ fun MangaToolbar( } // SY <-- // KMK --> + if (BuildConfig.DEBUG) { + add( + AppBar.OverflowAction( + title = "Colors Palette", + onClick = onPaletteScreenClick, + ), + ) + } // add( // AppBar.OverflowAction( // title = stringResource(MR.strings.pref_invalidate_download_cache), @@ -204,7 +214,6 @@ fun MangaToolbar( // ), // ) // KMK <-- - } .build(), ) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/NamespaceTags.kt b/app/src/main/java/eu/kanade/presentation/manga/components/NamespaceTags.kt index 29a9209f0..2b552f4c6 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/NamespaceTags.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/NamespaceTags.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ElevatedSuggestionChip import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SuggestionChip @@ -98,6 +99,7 @@ value class SearchMetadataChips( fun NamespaceTags( tags: SearchMetadataChips, onClick: (item: String) -> Unit, + pureDarkMode: Boolean = false, ) { Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(4.dp)) { tags.tags.forEach { (namespace, tags) -> @@ -107,6 +109,7 @@ fun NamespaceTags( modifier = Modifier.padding(top = 4.dp), text = namespace, onClick = null, + pureDarkMode = pureDarkMode, ) } FlowRow( @@ -120,11 +123,23 @@ fun NamespaceTags( text = text, onClick = { onClick(search) }, border = borderDp?.let { - SuggestionChipDefaults.suggestionChipBorder(borderWidth = it) - } ?: SuggestionChipDefaults.suggestionChipBorder(), + SuggestionChipDefaults.suggestionChipBorder( + borderWidth = it, + borderColor = MaterialTheme.colorScheme.primary, + ) + } ?: SuggestionChipDefaults.suggestionChipBorder( + borderColor = MaterialTheme.colorScheme.primary, + ), borderM3 = borderDp?.let { - SuggestionChipDefaultsM3.suggestionChipBorder(enabled = true, borderWidth = it) - } ?: SuggestionChipDefaultsM3.suggestionChipBorder(enabled = true), + SuggestionChipDefaultsM3.suggestionChipBorder( + enabled = true, + borderWidth = it, + borderColor = MaterialTheme.colorScheme.primary, + ) + } ?: SuggestionChipDefaultsM3.suggestionChipBorder( + enabled = true, + borderColor = MaterialTheme.colorScheme.primary, + ), ) } } @@ -139,23 +154,45 @@ fun TagsChip( onClick: (() -> Unit)?, modifier: Modifier = Modifier, border: ChipBorder? = SuggestionChipDefaults.suggestionChipBorder(), - borderM3: BorderStroke? = SuggestionChipDefaultsM3.suggestionChipBorder(enabled = true), + borderM3: BorderStroke? = null, + pureDarkMode: Boolean = false, ) { CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) { if (onClick != null) { - SuggestionChip( - modifier = modifier, - onClick = onClick, - label = { - Text( - text = text, - style = MaterialTheme.typography.bodySmall, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - }, - border = borderM3, - ) + if (borderM3 != null || pureDarkMode) { + SuggestionChip( + modifier = modifier, + onClick = onClick, + label = { + Text( + text = text, + style = MaterialTheme.typography.bodySmall, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + }, + border = borderM3 ?: SuggestionChipDefaultsM3.suggestionChipBorder( + enabled = true, + borderColor = MaterialTheme.colorScheme.primary, + ), + ) + } else { + ElevatedSuggestionChip( + modifier = modifier, + onClick = onClick, + label = { + Text( + text = text, + style = MaterialTheme.typography.bodySmall, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + }, + colors = SuggestionChipDefaultsM3.elevatedSuggestionChipColors( + containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, + ), + ) + } } else { SuggestionChip( modifier = modifier, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt index 7a8a020e2..34a7c7a46 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt @@ -112,14 +112,14 @@ object SettingsAppearanceScreen : SearchableSettings { ): Preference.PreferenceGroup { val scope = rememberCoroutineScope() val detailsPageThemeCoverBased by remember { - Injekt.get().detailsPageThemeCoverBased().asState(scope) + Injekt.get().themeCoverBased().asState(scope) } return Preference.PreferenceGroup( title = stringResource(KMR.strings.pref_details_page_theme), preferenceItems = persistentListOf( Preference.PreferenceItem.SwitchPreference( - pref = uiPreferences.detailsPageThemeCoverBased(), - title = stringResource(KMR.strings.pref_details_page_theme_cover_based), + pref = uiPreferences.themeCoverBased(), + title = stringResource(KMR.strings.pref_theme_cover_based), ), Preference.PreferenceItem.ListPreference( pref = uiPreferences.themeCoverBasedStyle(), @@ -129,11 +129,6 @@ object SettingsAppearanceScreen : SearchableSettings { .associateWith { it.name } .toImmutableMap(), ), - Preference.PreferenceItem.SwitchPreference( - pref = uiPreferences.themeCoverBasedAnimate(), - title = stringResource(KMR.strings.pref_theme_cover_based_animate), - enabled = detailsPageThemeCoverBased, - ), ), ) } diff --git a/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt b/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt index 3735ad5c7..c700beae0 100644 --- a/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt +++ b/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt @@ -26,6 +26,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalHapticFeedback @@ -168,12 +169,17 @@ private fun UpdatesUiItem( .padding(horizontal = MaterialTheme.padding.medium), verticalAlignment = Alignment.CenterVertically, ) { + val mangaCover = update.coverData + val bgColor = mangaCover.dominantCoverColors?.first?.let { Color(it) } + val onBgColor = mangaCover.dominantCoverColors?.second MangaCover.Square( modifier = Modifier .padding(vertical = 6.dp) .fillMaxHeight(), - data = update.coverData, + data = mangaCover, onClick = onClickCover, + bgColor = bgColor, + tint = onBgColor, ) Column( diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index b0a4a89b0..da82cffc8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -42,6 +42,7 @@ import eu.kanade.tachiyomi.crash.GlobalExceptionHandler 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.MangaCoverMetadata import eu.kanade.tachiyomi.data.coil.MangaKeyer import eu.kanade.tachiyomi.data.coil.PagePreviewFetcher import eu.kanade.tachiyomi.data.coil.PagePreviewKeyer @@ -54,7 +55,6 @@ import eu.kanade.tachiyomi.di.SYPreferenceModule import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkPreferences import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate -import eu.kanade.tachiyomi.data.coil.MangaCoverMetadata import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.WebViewUtil import eu.kanade.tachiyomi.util.system.animatorDurationScale 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 22a1f2733..33b9d8c33 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 @@ -68,8 +68,8 @@ class MangaCoverFetcher( private val imageLoader: ImageLoader, ) : Fetcher { - private val uiPreferences: UiPreferences = Injekt.get() - private val fileScope = CoroutineScope(Job() + Dispatchers.IO) + private val fileScope by lazy { CoroutineScope(Job() + Dispatchers.IO) } + private val uiPreferences = Injekt.get() private val diskCacheKey: String get() = diskCacheKeyLazy.value @@ -325,18 +325,17 @@ class MangaCoverFetcher( * @param bufferedSource if not null then it will load bitmap from [BufferedSource], regardless of [ogFile] * @param ogFile if not null then it will load bitmap from [File]. If it's null then it will try to load bitmap * from [CoverCache] using either [CoverCache.customCoverCacheDir] or [CoverCache.cacheDir] - * @param force if true (default) then it will always re-calculate ratio & color for favorite mangas. - * This is useful when a favorite manga updates/changes its cover. If false then it will only update ratio. + * @param force if true then it will always re-calculate ratio & color for favorite mangas. */ private fun setRatioAndColorsInScope( mangaCover: MangaCover, bufferedSource: BufferedSource? = null, ogFile: File? = null, - force: Boolean = true + onlyFavorite: Boolean = !uiPreferences.themeCoverBased().get(), + force: Boolean = false, ) { - if (!uiPreferences.detailsPageThemeCoverBased().get()) return fileScope.launch { - MangaCoverMetadata.setRatioAndColors(mangaCover, bufferedSource, ogFile, force) + MangaCoverMetadata.setRatioAndColors(mangaCover, bufferedSource, ogFile, onlyFavorite, force) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverMetadata.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverMetadata.kt index 4caff1e8b..e59dc2037 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverMetadata.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverMetadata.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.coil import android.graphics.BitmapFactory import androidx.palette.graphics.Palette import eu.kanade.tachiyomi.data.cache.CoverCache +import eu.kanade.tachiyomi.ui.manga.MangaScreenModel import okio.BufferedSource import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.manga.model.MangaCover @@ -65,37 +66,57 @@ object MangaCoverMetadata { * @param bufferedSource if not null then it will load bitmap from [BufferedSource], regardless of [ogFile] * @param ogFile if not null then it will load bitmap from [File]. If it's null then it will try to load bitmap * from [CoverCache] using either [CoverCache.customCoverCacheDir] or [CoverCache.cacheDir] - * @param force if true (default) then it will always re-calculate ratio & color for favorite mangas. - * This is useful when a favorite manga updates/changes its cover. If false then it will only update ratio. + * @param force if true then it will always re-calculate ratio & color for favorite mangas. * - * @author Jays2Kings + * This is only for loading color first time it appears on Library/Browse. Any new colors caused by loading new + * cover when open a manga detail or change cover will be updated separately on [MangaScreenModel.setPaletteColor]. */ fun setRatioAndColors( mangaCover: MangaCover, bufferedSource: BufferedSource? = null, ogFile: File? = null, - force: Boolean = true, + onlyDominantColor: Boolean = true, + force: Boolean = false, ) { if (!mangaCover.isMangaFavorite) { mangaCover.remove() + if (mangaCover.vibrantCoverColor != null) return } + + if (mangaCover.isMangaFavorite && onlyDominantColor && mangaCover.dominantCoverColors != null) return + + val options = BitmapFactory.Options() + + val updateColors = mangaCover.isMangaFavorite && mangaCover.dominantCoverColors == null || + !onlyDominantColor && mangaCover.vibrantCoverColor == null || force + + if (updateColors) { + /** + * + Manga is Favorite & doesn't have dominant color + * For non-favorite, it doesn't care if dominant is there or not, if it has vibrant color then it will + * already be returned from beginning. + * + [onlyDominantColor] = false + * - Manga doesn't have vibrant color + */ + options.inSampleSize = 4 + } else { + /** + * + [onlyDominantColor] = true + * - Manga is Favorite & already have dominant color + * + [onlyDominantColor] = false + * - Manga is Favorite & already have dominant color & vibrant color + * - Manga is not Favorite & already have vibrant color (already skip at beginning) + */ + // Just trying to update ratio without actual reading bitmap (bitmap will be null) + options.inJustDecodeBounds = true + // Don't even need to update ratio because we don't use it yet. + return + } + val file = ogFile ?: coverCache.getCustomCoverFile(mangaCover.mangaId).takeIf { it.exists() } ?: coverCache.getCoverFile(mangaCover.url) - val options = BitmapFactory.Options() - val hasVibrantColor = if (mangaCover.isMangaFavorite) mangaCover.vibrantCoverColor != null else true - // If dominantCoverColors is not null, it means that color is restored from Prefs - // and also has vibrantCoverColor (e.g. new color caused by updated cover) - val updateRatioOnly = mangaCover.dominantCoverColors != null && hasVibrantColor && !force - if (updateRatioOnly) { - // Just trying to update ratio without actual reading bitmap (bitmap will be null) - // This is often when open a favorite manga - options.inJustDecodeBounds = true - } else { - options.inSampleSize = 4 - } - val bitmap = when { bufferedSource != null -> BitmapFactory.decodeStream(bufferedSource.inputStream(), null, options) // if the file exists and the there was still an error then the file is corrupted diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt index cf9cda52a..c93a719c3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt @@ -6,6 +6,7 @@ import android.os.Build import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.TextView import androidx.annotation.ColorInt @@ -82,7 +83,6 @@ fun EditMangaDialog( textHighlightColor = MaterialTheme.colorScheme.outline.toArgb(), iconColor = MaterialTheme.colorScheme.primary.toArgb(), tagColor = MaterialTheme.colorScheme.outlineVariant.toArgb(), - tagFocusColor = MaterialTheme.colorScheme.outline.toArgb(), tagTextColor = MaterialTheme.colorScheme.onSurfaceVariant.toArgb(), btnTextColor = MaterialTheme.colorScheme.onPrimary.toArgb(), btnBgColor = MaterialTheme.colorScheme.surfaceTint.toArgb(), @@ -159,7 +159,6 @@ class EditMangaDialogColors( @ColorInt val textHighlightColor: Int, @ColorInt val iconColor: Int, @ColorInt val tagColor: Int, - @ColorInt val tagFocusColor: Int, @ColorInt val tagTextColor: Int, @ColorInt val btnTextColor: Int, @ColorInt val btnBgColor: Int, @@ -207,6 +206,14 @@ private fun onViewCreated( ) } + // Set Spinner's selected item's background color to transparent + binding.status.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>, view: View?, pos: Int, id: Long) { + if (view != null) (view as TextView).setBackgroundColor(0x00000000) + } + override fun onNothingSelected(parent: AdapterView<*>?) = Unit + } + // Set Spinner's dropdown caret color binding.status.backgroundTintList = ColorStateList.valueOf(colors.iconColor) @@ -346,18 +353,7 @@ private fun ChipGroup.setChips( ) { removeAllViews() - val colorStateList = ColorStateList( - arrayOf( - intArrayOf(android.R.attr.state_focused), - intArrayOf(android.R.attr.state_pressed), - intArrayOf(-android.R.attr.state_active), - ), - intArrayOf( - colors.tagFocusColor, - colors.tagFocusColor, - colors.tagColor, - ), - ) + val colorStateList = ColorStateList.valueOf(colors.tagColor) items.asSequence().map { item -> Chip(context).apply { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index 5c03946fc..46b77709c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -29,7 +29,6 @@ import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.materialkolor.DynamicMaterialTheme -import com.materialkolor.rememberDynamicColorScheme import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.domain.manga.model.toSManga import eu.kanade.domain.ui.UiPreferences @@ -140,24 +139,17 @@ class MangaScreen( val seedColorState = rememberUpdatedState(newValue = successState.seedColor) val uiPreferences = remember { Injekt.get() } - if (uiPreferences.themeCoverBasedAnimate().get()) { + if (uiPreferences.themeCoverBased().get()) { DynamicMaterialTheme( seedColor = seedColorState.value ?: MaterialTheme.colorScheme.primary, useDarkTheme = isSystemInDarkTheme(), + withAmoled = uiPreferences.themeDarkAmoled().get(), style = uiPreferences.themeCoverBasedStyle().get(), - animate = uiPreferences.themeCoverBasedAnimate().get(), + animate = true, content = { MaterialThemeContent(context, screenModel, successState) }, ) } else { - val colorScheme = rememberDynamicColorScheme( - seedColor = seedColorState.value ?: MaterialTheme.colorScheme.primary, - isDark = isSystemInDarkTheme(), - style = uiPreferences.themeCoverBasedStyle().get(), - ) - MaterialTheme( - colorScheme = colorScheme, - content = { MaterialThemeContent(context, screenModel, successState) }, - ) + MaterialThemeContent(context, screenModel, successState) } } @@ -262,7 +254,7 @@ class MangaScreen( previewsRowCount = successState.previewsRowCount, // SY --> onMigrateClicked = { migrateManga(navigator, screenModel.manga!!) }.takeIf { successState.manga.favorite }, - onMetadataViewerClicked = { openMetadataViewer(navigator, successState.manga) }, + onMetadataViewerClicked = { openMetadataViewer(navigator, successState.manga, successState.seedColor) }, onEditInfoClicked = screenModel::showEditMangaInfoDialog, onRecommendClicked = { openRecommends(context, navigator, screenModel.source?.getMainSource(), successState.manga) }, onMergedSettingsClicked = screenModel::showEditMergedSettingsDialog, @@ -313,6 +305,8 @@ class MangaScreen( navigator.push(ExtensionsScreen(searchSource = successState.source.name)) } }, + onCoverLoaded = screenModel::setPaletteColor, + onPaletteScreenClick = { navigator.push(PaletteScreen(successState.seedColor)) } // KMK <-- ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index f709bb6ac..4e52167af 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -15,7 +15,6 @@ import androidx.palette.graphics.Palette import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope import coil3.Image -import coil3.executeBlocking import coil3.imageLoader import coil3.request.ImageRequest import coil3.request.allowHardware @@ -45,6 +44,7 @@ import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.manga.DownloadAction import eu.kanade.presentation.manga.components.ChapterDownloadAction import eu.kanade.presentation.util.formattedMessage +import eu.kanade.tachiyomi.data.coil.getBestColor import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.model.Download @@ -64,7 +64,6 @@ import eu.kanade.tachiyomi.ui.manga.RelatedManga.Companion.sorted import eu.kanade.tachiyomi.ui.manga.track.TrackItem import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.util.chapter.getNextUnread -import eu.kanade.tachiyomi.data.coil.getBestColor import eu.kanade.tachiyomi.util.removeCovers import eu.kanade.tachiyomi.util.shouldDownloadNewChapters import eu.kanade.tachiyomi.util.system.getBitmapOrNull @@ -481,7 +480,7 @@ class MangaScreenModel( /** * Get the color of the manga cover by loading cover with ImageRequest directly from network. */ - private suspend fun setPaletteColor(model: Any, method: ImageRequestType = ImageRequestType.Enqueue) { + fun setPaletteColor(model: Any) { if (model is ImageRequest && model.defined.sizeResolver != null) return val imageRequestBuilder = if (model is ImageRequest) { @@ -494,58 +493,41 @@ class MangaScreenModel( val generatePalette: (Image) -> Unit = { image -> val bitmap = image.asDrawable(context.resources).getBitmapOrNull() if (bitmap != null) { - Palette.from(bitmap).generate { palette -> + Palette.from(bitmap).generate { screenModelScope.launchUI { - palette?.getBestColor()?.let { vibrantColor -> - when (model) { - is Manga -> model.asMangaCover().vibrantCoverColor = vibrantColor - is MangaCover -> model.vibrantCoverColor = vibrantColor + if (it == null) return@launchUI + val mangaCover = when (model) { + is Manga -> model.asMangaCover() + is MangaCover -> model + else -> return@launchUI + } + if (mangaCover.isMangaFavorite) { + it.dominantSwatch?.let { swatch -> + mangaCover.dominantCoverColors = swatch.rgb to swatch.titleTextColor } } + val vibrantColor = it.getBestColor() ?: return@launchUI + mangaCover.vibrantCoverColor = vibrantColor } } } } - when (method) { - ImageRequestType.Enqueue -> { - context.imageLoader.enqueue( - imageRequestBuilder - .target( - onSuccess = generatePalette, - onError = { - // TODO: handle error - // val file = coverCache.getCoverFile(manga!!) - // if (file.exists()) { - // file.delete() - // setPaletteColor() - // } - }, - ) - .build() + context.imageLoader.enqueue( + imageRequestBuilder + .target( + onSuccess = generatePalette, + onError = { + // TODO: handle error + // val file = coverCache.getCoverFile(manga!!) + // if (file.exists()) { + // file.delete() + // setPaletteColor() + // } + }, ) - } - ImageRequestType.Execute -> { - context.imageLoader.execute(imageRequestBuilder.build()) - .image?.let { image -> - generatePalette(image) - } - } - ImageRequestType.ExecuteBlocking -> { - context.imageLoader.executeBlocking(imageRequestBuilder.build()) - .image?.let { image -> - generatePalette(image) - } - } - ImageRequestType.IOContext -> { - withIOContext { - context.imageLoader.execute(imageRequestBuilder.build()) - .image?.let { image -> - generatePalette(image) - } - } - } - } + .build() + ) } // KMK --> @@ -2079,10 +2061,3 @@ sealed interface RelatedManga { } } // KMK <-- - -enum class ImageRequestType { - IOContext, - Enqueue, - Execute, - ExecuteBlocking, -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/PaletteScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/PaletteScreen.kt new file mode 100644 index 000000000..e9c44fdd4 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/PaletteScreen.kt @@ -0,0 +1,197 @@ +package eu.kanade.tachiyomi.ui.manga + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.contentColorFor +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import com.materialkolor.DynamicMaterialTheme +import eu.kanade.domain.ui.UiPreferences +import eu.kanade.presentation.components.AppBar +import eu.kanade.presentation.util.Screen +import tachiyomi.presentation.core.components.material.Button +import tachiyomi.presentation.core.components.material.ButtonDefaults +import tachiyomi.presentation.core.components.material.Scaffold +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +/** + * A screen that displays a colors palette of current theme. + */ +class PaletteScreen( + private val seedColor: Color?, +) : Screen() { + + @Composable + override fun Content() { + val uiPreferences = remember { Injekt.get() } + + if (uiPreferences.themeCoverBased().get()) { + DynamicMaterialTheme( + seedColor = seedColor ?: MaterialTheme.colorScheme.primary, + useDarkTheme = isSystemInDarkTheme(), + withAmoled = uiPreferences.themeDarkAmoled().get(), + style = uiPreferences.themeCoverBasedStyle().get(), + animate = true, + content = { MaterialThemeContent() }, + ) + } else { + MaterialThemeContent() + } + } + + @Composable + fun MaterialThemeContent() { + val navigator = LocalNavigator.currentOrThrow + + Scaffold( + topBar = { scrollBehavior -> + AppBar( + title = "Colors Palette", + navigateUp = navigator::pop, + scrollBehavior = scrollBehavior, + ) + }, + ) { contentPadding -> + Column( + modifier = Modifier.padding(contentPadding), + ) { + ButtonsColor( + "accent & onPrimary", + seedColor ?: MaterialTheme.colorScheme.primary, + "accent & contentColor", + seedColor ?: MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.onPrimary, + ) + + ButtonsColor( + "primary", + MaterialTheme.colorScheme.primary, + "primaryContainer", + MaterialTheme.colorScheme.primaryContainer + ) + ButtonsColor( + "secondary", + MaterialTheme.colorScheme.secondary, + "secondaryContainer", + MaterialTheme.colorScheme.secondaryContainer + ) + ButtonsColor( + "tertiary", + MaterialTheme.colorScheme.tertiary, + "tertiaryContainer", + MaterialTheme.colorScheme.tertiaryContainer + ) + ButtonsColor( + "surface", + MaterialTheme.colorScheme.surface, + "surfaceVariant", + MaterialTheme.colorScheme.surfaceVariant + ) + ButtonsColor( + "inverseSurface", + MaterialTheme.colorScheme.inverseSurface, + "surfaceTint", + MaterialTheme.colorScheme.surfaceTint + ) + ButtonsColor( + "inversePrimary", + MaterialTheme.colorScheme.inversePrimary, + "background", + MaterialTheme.colorScheme.background + ) + ButtonsColor( + "error", + MaterialTheme.colorScheme.error, + "errorContainer", + MaterialTheme.colorScheme.errorContainer + ) + ButtonsColor( + "outline", + MaterialTheme.colorScheme.outline, + "outlineVariant", + MaterialTheme.colorScheme.outlineVariant + ) + ButtonsColor( + "scrim", + MaterialTheme.colorScheme.scrim, + "surfaceBright", + MaterialTheme.colorScheme.surfaceBright + ) + ButtonsColor( + "surfaceDim", + MaterialTheme.colorScheme.surfaceDim, + "surfaceContainer", + MaterialTheme.colorScheme.surfaceContainer + ) + ButtonsColor( + "surfaceContainerHigh", + MaterialTheme.colorScheme.surfaceContainerHigh, + "surfaceContainerHighest", + MaterialTheme.colorScheme.surfaceContainerHighest + ) + ButtonsColor( + "surfaceContainerLow", + MaterialTheme.colorScheme.surfaceContainerLow, + "surfaceContainerLowest", + MaterialTheme.colorScheme.surfaceContainerLowest + ) + } + } + } +} + +@Composable +fun ButtonsColor( + name1: String, + color1: Color, + name2: String, + color2: Color, + contentColor1: Color = contentColorFor(backgroundColor = color1), + contentColor2: Color = contentColorFor(backgroundColor = color2), +) { + Row( + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth() + ) { + Button( + onClick = { }, + Modifier + .weight(1f) + .padding(horizontal = 16.dp, vertical = 4.dp), + colors = ButtonDefaults.buttonColors( + containerColor = color1, + contentColor = contentColor1, + ) + ) { + Text( + text = name1, + ) + } + Button( + onClick = { }, + Modifier + .weight(1f) + .padding(horizontal = 16.dp, vertical = 4.dp), + colors = ButtonDefaults.buttonColors( + containerColor = color2, + contentColor = contentColor2, + ), + ) { + Text( + text = name2 + ) + } + } +} diff --git a/app/src/main/java/exh/ui/metadata/MetadataViewScreen.kt b/app/src/main/java/exh/ui/metadata/MetadataViewScreen.kt index b0178548d..5fb7fe119 100644 --- a/app/src/main/java/exh/ui/metadata/MetadataViewScreen.kt +++ b/app/src/main/java/exh/ui/metadata/MetadataViewScreen.kt @@ -24,7 +24,7 @@ import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow import com.materialkolor.DynamicMaterialTheme -import com.materialkolor.PaletteStyle +import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.util.system.copyToClipboard @@ -36,6 +36,8 @@ import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.util.clickableNoIndication import tachiyomi.presentation.core.util.plus +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get class MetadataViewScreen( private val mangaId: Long, @@ -107,12 +109,19 @@ class MetadataViewScreen( } } - DynamicMaterialTheme( - seedColor = seedColor ?: MaterialTheme.colorScheme.primary, - useDarkTheme = isSystemInDarkTheme(), - style = PaletteStyle.Vibrant, - animate = true, - content = { content() } - ) + val uiPreferences = remember { Injekt.get() } + + if (uiPreferences.themeCoverBased().get()) { + DynamicMaterialTheme( + seedColor = seedColor ?: MaterialTheme.colorScheme.primary, + useDarkTheme = isSystemInDarkTheme(), + withAmoled = uiPreferences.themeDarkAmoled().get(), + style = uiPreferences.themeCoverBasedStyle().get(), + animate = true, + content = { content() }, + ) + } else { + content() + } } } diff --git a/app/src/main/java/exh/ui/metadata/adapters/EHentaiDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/EHentaiDescriptionAdapter.kt index 5317a2171..0e77d9226 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/EHentaiDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/EHentaiDescriptionAdapter.kt @@ -29,8 +29,8 @@ fun EHentaiDescription( search: (String) -> Unit, ) { val context = LocalContext.current - val textColor = MaterialTheme.colorScheme.onBackground.toArgb() - val iconColor = MaterialTheme.colorScheme.secondary.toArgb() + val textColor = MaterialTheme.colorScheme.secondary.toArgb() + val iconColor = MaterialTheme.colorScheme.primary.toArgb() val ratingBarSecondaryColor = MaterialTheme.colorScheme.outlineVariant.toArgb() AndroidView( modifier = Modifier.fillMaxWidth(), diff --git a/app/src/main/java/exh/ui/metadata/adapters/EightMusesDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/EightMusesDescriptionAdapter.kt index 8eaa295ac..481620272 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/EightMusesDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/EightMusesDescriptionAdapter.kt @@ -20,7 +20,7 @@ import tachiyomi.i18n.MR @Composable fun EightMusesDescription(state: State.Success, openMetadataViewer: () -> Unit) { val context = LocalContext.current - val textColor = MaterialTheme.colorScheme.onBackground.toArgb() + val textColor = MaterialTheme.colorScheme.secondary.toArgb() val iconColor = MaterialTheme.colorScheme.primary.toArgb() AndroidView( modifier = Modifier.fillMaxWidth(), diff --git a/app/src/main/java/exh/ui/metadata/adapters/HBrowseDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/HBrowseDescriptionAdapter.kt index de4d2f2bb..2a519fbe4 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/HBrowseDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/HBrowseDescriptionAdapter.kt @@ -20,7 +20,7 @@ import tachiyomi.i18n.sy.SYMR @Composable fun HBrowseDescription(state: State.Success, openMetadataViewer: () -> Unit) { val context = LocalContext.current - val textColor = MaterialTheme.colorScheme.onBackground.toArgb() + val textColor = MaterialTheme.colorScheme.secondary.toArgb() val iconColor = MaterialTheme.colorScheme.primary.toArgb() AndroidView( modifier = Modifier.fillMaxWidth(), diff --git a/app/src/main/java/exh/ui/metadata/adapters/MangaDexDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/MangaDexDescriptionAdapter.kt index 5c7cc5b5b..05b00792f 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/MangaDexDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/MangaDexDescriptionAdapter.kt @@ -23,7 +23,7 @@ import kotlin.math.round @Composable fun MangaDexDescription(state: State.Success, openMetadataViewer: () -> Unit) { val context = LocalContext.current - val textColor = MaterialTheme.colorScheme.onBackground.toArgb() + val textColor = MaterialTheme.colorScheme.secondary.toArgb() val iconColor = MaterialTheme.colorScheme.primary.toArgb() val ratingBarSecondaryColor = MaterialTheme.colorScheme.outlineVariant.toArgb() AndroidView( diff --git a/app/src/main/java/exh/ui/metadata/adapters/NHentaiDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/NHentaiDescriptionAdapter.kt index 85326077b..654fe8941 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/NHentaiDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/NHentaiDescriptionAdapter.kt @@ -27,7 +27,7 @@ import java.time.ZonedDateTime @Composable fun NHentaiDescription(state: State.Success, openMetadataViewer: () -> Unit) { val context = LocalContext.current - val textColor = MaterialTheme.colorScheme.onBackground.toArgb() + val textColor = MaterialTheme.colorScheme.secondary.toArgb() val iconColor = MaterialTheme.colorScheme.primary.toArgb() AndroidView( modifier = Modifier.fillMaxWidth(), diff --git a/app/src/main/java/exh/ui/metadata/adapters/PururinDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/PururinDescriptionAdapter.kt index 81f91262c..d35988b09 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/PururinDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/PururinDescriptionAdapter.kt @@ -25,7 +25,7 @@ import kotlin.math.round @Composable fun PururinDescription(state: State.Success, openMetadataViewer: () -> Unit) { val context = LocalContext.current - val textColor = MaterialTheme.colorScheme.onBackground.toArgb() + val textColor = MaterialTheme.colorScheme.secondary.toArgb() val iconColor = MaterialTheme.colorScheme.primary.toArgb() val ratingBarSecondaryColor = MaterialTheme.colorScheme.outlineVariant.toArgb() AndroidView( diff --git a/app/src/main/java/exh/ui/metadata/adapters/TsuminoDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/TsuminoDescriptionAdapter.kt index 0830283d4..a52006dad 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/TsuminoDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/TsuminoDescriptionAdapter.kt @@ -26,7 +26,7 @@ import kotlin.math.round @Composable fun TsuminoDescription(state: State.Success, openMetadataViewer: () -> Unit) { val context = LocalContext.current - val textColor = MaterialTheme.colorScheme.onBackground.toArgb() + val textColor = MaterialTheme.colorScheme.secondary.toArgb() val iconColor = MaterialTheme.colorScheme.primary.toArgb() val ratingBarSecondaryColor = MaterialTheme.colorScheme.outlineVariant.toArgb() AndroidView( diff --git a/app/src/main/java/mihon/feature/upcoming/components/UpcomingItem.kt b/app/src/main/java/mihon/feature/upcoming/components/UpcomingItem.kt index bf82c73cd..19e301517 100644 --- a/app/src/main/java/mihon/feature/upcoming/components/UpcomingItem.kt +++ b/app/src/main/java/mihon/feature/upcoming/components/UpcomingItem.kt @@ -11,6 +11,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -38,9 +39,14 @@ fun UpcomingItem( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.large), ) { + val mangaCover = upcoming.asMangaCover() + val bgColor = mangaCover.dominantCoverColors?.first?.let { Color(it) } + val onBgColor = mangaCover.dominantCoverColors?.second MangaCover.Book( modifier = Modifier.fillMaxHeight(), - data = upcoming.asMangaCover(), + data = mangaCover, + bgColor = bgColor, + tint = onBgColor, ) Text( modifier = Modifier.weight(1f), diff --git a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt index 30f2f09c9..d097c32a5 100644 --- a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt @@ -35,9 +35,15 @@ class LibraryPreferences( fun lastUpdatedTimestamp() = preferenceStore.getLong(Preference.appStateKey("library_update_last_timestamp"), 0L) fun autoUpdateInterval() = preferenceStore.getInt("pref_library_update_interval_key", 0) - fun coverRatios() = preferenceStore.getStringSet("pref_library_cover_ratios_key", emptySet()) + fun coverRatios() = preferenceStore.getStringSet( + Preference.appStateKey("pref_library_cover_ratios_key"), + emptySet(), + ) - fun coverColors() = preferenceStore.getStringSet("pref_library_cover_colors_key", emptySet()) + fun coverColors() = preferenceStore.getStringSet( + Preference.appStateKey("pref_library_cover_colors_key"), + emptySet(), + ) fun autoUpdateDeviceRestrictions() = preferenceStore.getStringSet( "library_update_restriction", diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1faf399ee..3cc461bf9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,8 +10,6 @@ sqlite = "2.4.0" voyager = "1.0.0" detekt = "1.23.6" detektCompose = "0.3.12" -paletteKtxVersion = "1.0.0" -materialKolorVersion = "1.6.1" [libraries] desugar = "com.android.tools:desugar_jdk_libs:2.0.4" @@ -70,8 +68,8 @@ insetter = "dev.chrisbanes.insetter:insetter:0.6.1" compose-materialmotion = "io.github.fornewid:material-motion-compose-core:2.0.0" compose-webview = "io.github.kevinnzou:compose-webview:0.33.6" compose-grid = "io.woong.compose.grid:grid:1.2.2" -palette-ktx = { group = "androidx.palette", name = "palette-ktx", version.ref = "paletteKtxVersion" } -material-kolor = { module = "com.materialkolor:material-kolor", version.ref = "materialKolorVersion" } +palette-ktx = "androidx.palette:palette-ktx:1.0.0" +material-kolor = "com.materialkolor:material-kolor:1.7.0" swipe = "me.saket.swipe:swipe:1.3.0" diff --git a/i18n-kmk/src/commonMain/resources/MR/ar/strings.xml b/i18n-kmk/src/commonMain/resources/MR/ar/strings.xml index feba8ded4..f5ce405de 100644 --- a/i18n-kmk/src/commonMain/resources/MR/ar/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/ar/strings.xml @@ -5,9 +5,8 @@ وضع التحديد بالجملة الأسئلة الشائعة والأدلة صفحة التفاصيل - تفاصيل سمة الصفحة استنادًا إلى الغلاف + السمة المبنية على الغلاف نمط السمة - الرسوم المتحركة للسمة غير رسمي وهذا التمديد ليس من المستودع الرسمي. الفاصل الزمني لتجديد التحميل diff --git a/i18n-kmk/src/commonMain/resources/MR/base/strings.xml b/i18n-kmk/src/commonMain/resources/MR/base/strings.xml index 233c9ddb2..173c5e44f 100644 --- a/i18n-kmk/src/commonMain/resources/MR/base/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/base/strings.xml @@ -9,9 +9,8 @@ Details page - Details page theme based on cover + Theme based on cover Theme style - Theme animate Expand Related titles If turned off, related titles won\'t automatically load & show expanded diff --git a/i18n-kmk/src/commonMain/resources/MR/ca/strings.xml b/i18n-kmk/src/commonMain/resources/MR/ca/strings.xml index a299ca39e..a7e873280 100644 --- a/i18n-kmk/src/commonMain/resources/MR/ca/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/ca/strings.xml @@ -5,9 +5,8 @@ Bulk selection mode FAQ and Guides Details page - Details page theme based on cover + Theme based on cover Theme style - Theme animate Unofficial This extension is not from the official repo. Download cache renew interval diff --git a/i18n-kmk/src/commonMain/resources/MR/cs/strings.xml b/i18n-kmk/src/commonMain/resources/MR/cs/strings.xml index a3ffa3e78..0cd0257bc 100644 --- a/i18n-kmk/src/commonMain/resources/MR/cs/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/cs/strings.xml @@ -5,9 +5,8 @@ Režim hromadného výběru FAQ a návody Stránka s podrobnostmi - Šablona stránky s podrobnostmi na základě obalu + Šablona založená na obálce Styl motivu - Animace motivu Neoficiální Toto rozšíření není z oficiálního repozitáře. Interval obnovení mezipaměti diff --git a/i18n-kmk/src/commonMain/resources/MR/da/strings.xml b/i18n-kmk/src/commonMain/resources/MR/da/strings.xml index 7f2c3051c..3270d26b0 100644 --- a/i18n-kmk/src/commonMain/resources/MR/da/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/da/strings.xml @@ -5,9 +5,8 @@ Masse valg tilstand Ofte stillede spørgsmål og vejledninger Detaljer side - Detaljer sidetema baseret på omslag + Tema baseret på omslag Tema stil - Tema animere Uofficiel Denne udvidelse er ikke fra den officielle repo. Download cache forny interval diff --git a/i18n-kmk/src/commonMain/resources/MR/de/strings.xml b/i18n-kmk/src/commonMain/resources/MR/de/strings.xml index 4045af229..3a38e0f7e 100644 --- a/i18n-kmk/src/commonMain/resources/MR/de/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/de/strings.xml @@ -5,9 +5,8 @@ Massenauswahlmodus FAQ und Anleitungen Detailseite - Detailseitenthema basierend auf Cover + Theme basierend auf Cover Design-Stil - Theme animieren Ungültig Diese Erweiterung ist nicht vom offiziellen Repo. Download-Cache Erneuerungsintervall diff --git a/i18n-kmk/src/commonMain/resources/MR/el/strings.xml b/i18n-kmk/src/commonMain/resources/MR/el/strings.xml index cca7f278f..ea02b816a 100644 --- a/i18n-kmk/src/commonMain/resources/MR/el/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/el/strings.xml @@ -5,9 +5,8 @@ Λειτουργία μαζικής επιλογής Συχνές ερωτήσεις και οδηγοί Σελίδα λεπτομερειών - Θέμα λεπτομερειών σελίδας με βάση το εξώφυλλο + Θέμα βασισμένο στο εξώφυλλο Στυλ θέματος - Εφέ κίνησης θέματος Ανεπίσημη Η επέκταση αυτή δεν προέρχεται από την επίσημη αποχώρηση. Χρονικό διάστημα ανανέωσης προσωρινής μνήμης diff --git a/i18n-kmk/src/commonMain/resources/MR/es/strings.xml b/i18n-kmk/src/commonMain/resources/MR/es/strings.xml index b1ed78a8d..c35b94a4a 100644 --- a/i18n-kmk/src/commonMain/resources/MR/es/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/es/strings.xml @@ -5,9 +5,8 @@ Modo de selección masiva Preguntas frecuentes y guías Página de detalles - Tema de página de detalles basado en portada + Tema basado en portada Estilo del tema - Animar tema No oficial Esta extensión no es del repositorio oficial. Descargar el intervalo de renovación de caché diff --git a/i18n-kmk/src/commonMain/resources/MR/fi/strings.xml b/i18n-kmk/src/commonMain/resources/MR/fi/strings.xml index 5522602c5..09a24c51d 100644 --- a/i18n-kmk/src/commonMain/resources/MR/fi/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/fi/strings.xml @@ -5,9 +5,8 @@ Bulk valintatila UKK ja oppaat Yksityiskohdat sivu - Yksityiskohtaiset tiedot sivun teema kannen perusteella + Teema perustuu kanteen Teeman tyyli - Teema animoitu Epävirallinen Tämä laajennus ei ole virallisesta reposta. Lataa välimuistin uusimisväli diff --git a/i18n-kmk/src/commonMain/resources/MR/fil/strings.xml b/i18n-kmk/src/commonMain/resources/MR/fil/strings.xml index a299ca39e..a7e873280 100644 --- a/i18n-kmk/src/commonMain/resources/MR/fil/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/fil/strings.xml @@ -5,9 +5,8 @@ Bulk selection mode FAQ and Guides Details page - Details page theme based on cover + Theme based on cover Theme style - Theme animate Unofficial This extension is not from the official repo. Download cache renew interval diff --git a/i18n-kmk/src/commonMain/resources/MR/fr/strings.xml b/i18n-kmk/src/commonMain/resources/MR/fr/strings.xml index 20fb95b5e..60729352f 100644 --- a/i18n-kmk/src/commonMain/resources/MR/fr/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/fr/strings.xml @@ -5,9 +5,8 @@ Mode de sélection en bloc FAQ et tutoriels Page de détails - Thème de la page de détails basé sur la couverture + Thème basé sur la couverture Style du thème - Animation du thème Non officiel Cette extension ne provient pas du dépôt officiel. Intervalle de renouvellement du cache de téléchargement diff --git a/i18n-kmk/src/commonMain/resources/MR/he/strings.xml b/i18n-kmk/src/commonMain/resources/MR/he/strings.xml index a299ca39e..a7e873280 100644 --- a/i18n-kmk/src/commonMain/resources/MR/he/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/he/strings.xml @@ -5,9 +5,8 @@ Bulk selection mode FAQ and Guides Details page - Details page theme based on cover + Theme based on cover Theme style - Theme animate Unofficial This extension is not from the official repo. Download cache renew interval diff --git a/i18n-kmk/src/commonMain/resources/MR/hi/strings.xml b/i18n-kmk/src/commonMain/resources/MR/hi/strings.xml index a299ca39e..a7e873280 100644 --- a/i18n-kmk/src/commonMain/resources/MR/hi/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/hi/strings.xml @@ -5,9 +5,8 @@ Bulk selection mode FAQ and Guides Details page - Details page theme based on cover + Theme based on cover Theme style - Theme animate Unofficial This extension is not from the official repo. Download cache renew interval diff --git a/i18n-kmk/src/commonMain/resources/MR/hu/strings.xml b/i18n-kmk/src/commonMain/resources/MR/hu/strings.xml index a299ca39e..a7e873280 100644 --- a/i18n-kmk/src/commonMain/resources/MR/hu/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/hu/strings.xml @@ -5,9 +5,8 @@ Bulk selection mode FAQ and Guides Details page - Details page theme based on cover + Theme based on cover Theme style - Theme animate Unofficial This extension is not from the official repo. Download cache renew interval diff --git a/i18n-kmk/src/commonMain/resources/MR/id/strings.xml b/i18n-kmk/src/commonMain/resources/MR/id/strings.xml index a299ca39e..a7e873280 100644 --- a/i18n-kmk/src/commonMain/resources/MR/id/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/id/strings.xml @@ -5,9 +5,8 @@ Bulk selection mode FAQ and Guides Details page - Details page theme based on cover + Theme based on cover Theme style - Theme animate Unofficial This extension is not from the official repo. Download cache renew interval diff --git a/i18n-kmk/src/commonMain/resources/MR/it/strings.xml b/i18n-kmk/src/commonMain/resources/MR/it/strings.xml index c42fb704e..401f3785c 100644 --- a/i18n-kmk/src/commonMain/resources/MR/it/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/it/strings.xml @@ -5,9 +5,8 @@ Modalità selezione Bulk FAQ e guide Pagina dettagli - Dettagli tema pagina basato sulla copertina + Tema basato sulla copertina Stile tema - Animazione tema Non Ufficiale Questa estensione non proviene dal repo ufficiale. Intervallo di rinnovo download cache diff --git a/i18n-kmk/src/commonMain/resources/MR/ja/strings.xml b/i18n-kmk/src/commonMain/resources/MR/ja/strings.xml index 1af453d99..6eed6debf 100644 --- a/i18n-kmk/src/commonMain/resources/MR/ja/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/ja/strings.xml @@ -5,10 +5,9 @@ 一括選択モード FAQ とガイド 詳細ページ - カバーに基づく詳細ページのテーマ + カバーに基づくテーマ テーマのスタイル - テーマのアニメーション - <unk> + Unofficial この拡張機能は公式リポジトリからのものではありません。 キャッシュの更新間隔をダウンロード マニュアル diff --git a/i18n-kmk/src/commonMain/resources/MR/ko/strings.xml b/i18n-kmk/src/commonMain/resources/MR/ko/strings.xml index a299ca39e..a7e873280 100644 --- a/i18n-kmk/src/commonMain/resources/MR/ko/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/ko/strings.xml @@ -5,9 +5,8 @@ Bulk selection mode FAQ and Guides Details page - Details page theme based on cover + Theme based on cover Theme style - Theme animate Unofficial This extension is not from the official repo. Download cache renew interval diff --git a/i18n-kmk/src/commonMain/resources/MR/nl/strings.xml b/i18n-kmk/src/commonMain/resources/MR/nl/strings.xml index 2aaf9eaca..d748a0d19 100644 --- a/i18n-kmk/src/commonMain/resources/MR/nl/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/nl/strings.xml @@ -5,9 +5,8 @@ Bulk selectiemodus FAQ en handleidingen Details pagina - Details pagina thema gebaseerd op cover + Thema gebaseerd op cover Thema stijl - Thema animeren Onofficiële Deze uitbreiding komt niet uit de officiële repo. Download de cache vernieuwingsinterval diff --git a/i18n-kmk/src/commonMain/resources/MR/pl/strings.xml b/i18n-kmk/src/commonMain/resources/MR/pl/strings.xml index 83e4611df..b496dd59b 100644 --- a/i18n-kmk/src/commonMain/resources/MR/pl/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/pl/strings.xml @@ -5,9 +5,8 @@ Tryb masowego zaznaczenia FAQ i przewodniki Strona szczegółów - Szczegółowy motyw strony oparty na okładce + Motyw oparty na okładce Styl motywu - Animacja motywu Nieoficjalne To rozszerzenie nie pochodzi z oficjalnego repozytorium. Częstotliwość odnawiania pamięci podręcznej pobierania diff --git a/i18n-kmk/src/commonMain/resources/MR/pt-rBR/strings.xml b/i18n-kmk/src/commonMain/resources/MR/pt-rBR/strings.xml index 4a9bfc44b..c9d896410 100644 --- a/i18n-kmk/src/commonMain/resources/MR/pt-rBR/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/pt-rBR/strings.xml @@ -5,9 +5,8 @@ Modo de seleção em massa FAQ e Guias Página de detalhes - Detalhe o tema da página baseado na capa + Tema baseado na capa Estilo do tema - Animação do tema Não-oficial Essa extensão não é do repositório oficial. Baixar intervalo de renovação do cache diff --git a/i18n-kmk/src/commonMain/resources/MR/pt/strings.xml b/i18n-kmk/src/commonMain/resources/MR/pt/strings.xml index 4a9bfc44b..c9d896410 100644 --- a/i18n-kmk/src/commonMain/resources/MR/pt/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/pt/strings.xml @@ -5,9 +5,8 @@ Modo de seleção em massa FAQ e Guias Página de detalhes - Detalhe o tema da página baseado na capa + Tema baseado na capa Estilo do tema - Animação do tema Não-oficial Essa extensão não é do repositório oficial. Baixar intervalo de renovação do cache diff --git a/i18n-kmk/src/commonMain/resources/MR/ro/strings.xml b/i18n-kmk/src/commonMain/resources/MR/ro/strings.xml index c22fa63a3..1411b85c9 100644 --- a/i18n-kmk/src/commonMain/resources/MR/ro/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/ro/strings.xml @@ -5,9 +5,8 @@ Mod selecţie în masă Întrebări frecvente și ghiduri Pagina de detalii - Tema paginii cu detalii bazate pe copertă + Temă bazată pe copertă Stil temă - Animare temă Neoficial. Această extensie nu este din repo-ul oficial. Interval reînnoire descărcare geocutie diff --git a/i18n-kmk/src/commonMain/resources/MR/ru/strings.xml b/i18n-kmk/src/commonMain/resources/MR/ru/strings.xml index 35e6b99f6..269184542 100644 --- a/i18n-kmk/src/commonMain/resources/MR/ru/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/ru/strings.xml @@ -5,9 +5,8 @@ Режим массового выбора FAQ и руководства Детали - Детали темы на основе обложки + Тема на основе обложки Стиль темы - Тема анимации Неофициальный Это расширение не из официального репозитория. Загрузить интервал обновления кэша diff --git a/i18n-kmk/src/commonMain/resources/MR/sr/strings.xml b/i18n-kmk/src/commonMain/resources/MR/sr/strings.xml index a299ca39e..a7e873280 100644 --- a/i18n-kmk/src/commonMain/resources/MR/sr/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/sr/strings.xml @@ -5,9 +5,8 @@ Bulk selection mode FAQ and Guides Details page - Details page theme based on cover + Theme based on cover Theme style - Theme animate Unofficial This extension is not from the official repo. Download cache renew interval diff --git a/i18n-kmk/src/commonMain/resources/MR/sv/strings.xml b/i18n-kmk/src/commonMain/resources/MR/sv/strings.xml index 094c3bf45..7381c5103 100644 --- a/i18n-kmk/src/commonMain/resources/MR/sv/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/sv/strings.xml @@ -5,9 +5,8 @@ Bulk markeringsläge Vanliga frågor och guider Detaljer sida - Detaljer sida tema baserat på omslag + Tema baserat på omslag Tema stil - Tema animerat Inofficiellt Denna förlängning kommer inte från den officiella rapo. Hämta cache förnyelseintervall diff --git a/i18n-kmk/src/commonMain/resources/MR/th/strings.xml b/i18n-kmk/src/commonMain/resources/MR/th/strings.xml index a299ca39e..a7e873280 100644 --- a/i18n-kmk/src/commonMain/resources/MR/th/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/th/strings.xml @@ -5,9 +5,8 @@ Bulk selection mode FAQ and Guides Details page - Details page theme based on cover + Theme based on cover Theme style - Theme animate Unofficial This extension is not from the official repo. Download cache renew interval diff --git a/i18n-kmk/src/commonMain/resources/MR/tr/strings.xml b/i18n-kmk/src/commonMain/resources/MR/tr/strings.xml index a299ca39e..a7e873280 100644 --- a/i18n-kmk/src/commonMain/resources/MR/tr/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/tr/strings.xml @@ -5,9 +5,8 @@ Bulk selection mode FAQ and Guides Details page - Details page theme based on cover + Theme based on cover Theme style - Theme animate Unofficial This extension is not from the official repo. Download cache renew interval diff --git a/i18n-kmk/src/commonMain/resources/MR/uk/strings.xml b/i18n-kmk/src/commonMain/resources/MR/uk/strings.xml index 64c749e3c..c2edf052f 100644 --- a/i18n-kmk/src/commonMain/resources/MR/uk/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/uk/strings.xml @@ -5,9 +5,8 @@ Масовий режим вибору ЧаПи та керівництва Сторінка подробиць - Тема сторінки подробиць на основі обкладинки + Тему на основі обкладинки Стиль теми - Анімація теми Неофіційна Це розширення не з офіційного репозиторію. Завантажити інтервал оновлення кешу diff --git a/i18n-kmk/src/commonMain/resources/MR/vi/strings.xml b/i18n-kmk/src/commonMain/resources/MR/vi/strings.xml index a299ca39e..a7e873280 100644 --- a/i18n-kmk/src/commonMain/resources/MR/vi/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/vi/strings.xml @@ -5,9 +5,8 @@ Bulk selection mode FAQ and Guides Details page - Details page theme based on cover + Theme based on cover Theme style - Theme animate Unofficial This extension is not from the official repo. Download cache renew interval diff --git a/i18n-kmk/src/commonMain/resources/MR/zh-rCN/strings.xml b/i18n-kmk/src/commonMain/resources/MR/zh-rCN/strings.xml index 58f22e242..972d2d3bf 100644 --- a/i18n-kmk/src/commonMain/resources/MR/zh-rCN/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/zh-rCN/strings.xml @@ -5,9 +5,8 @@ 批量选择模式 常见问题和指南 详细信息页面 - 基于封面的详细页面主题 + 基于封面的主题 主题样式 - 主题动画 非官方的 这个扩展不是官方仓库的扩展。 下载缓存续间隔 diff --git a/i18n-kmk/src/commonMain/resources/MR/zh-rTW/strings.xml b/i18n-kmk/src/commonMain/resources/MR/zh-rTW/strings.xml index cbe6ad750..238db3b13 100644 --- a/i18n-kmk/src/commonMain/resources/MR/zh-rTW/strings.xml +++ b/i18n-kmk/src/commonMain/resources/MR/zh-rTW/strings.xml @@ -5,9 +5,8 @@ 批量選擇模式 常見問題和指南 詳情頁面 - 基於封面的詳細資訊頁面主題 + 基於封面的主題 主題風格 - 主題動畫 非官方版本 此擴充功能不是來自官方儲存庫 下載快取更新間隔