Cover's styling: color & loading animation (#42)

* use dominantCoverColors as background for cover so it's less tinted by theme color, also if CoverCache is corrupted, favorite entries can still show color

* add loading animation while cover is fetching image

* Bulk-selection using default isSelected style

* fix cover's background & tint when AsyncImage is Error/Fallback/PlaceHolder

* Widget's cover color when unable to load cover image
This commit is contained in:
Cuong M. Tran 2024-06-04 13:59:00 +07:00 committed by GitHub
parent b6b7114c29
commit 4c713f118f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 290 additions and 31 deletions

View file

@ -99,10 +99,8 @@ internal fun BrowseSourceComfortableGridItem(
ogUrl = manga.thumbnailUrl,
lastModified = manga.coverLastModified,
),
isSelected = isSelected,
coverAlpha = when {
// KMK -->
isSelected -> CommonMangaItemDefaults.BrowseSelectedCoverAlpha
// KMK <--
manga.favorite -> CommonMangaItemDefaults.BrowseFavoriteCoverAlpha
else -> 1f
},

View file

@ -99,10 +99,8 @@ internal fun BrowseSourceCompactGridItem(
ogUrl = manga.thumbnailUrl,
lastModified = manga.coverLastModified,
),
isSelected = isSelected,
coverAlpha = when {
// KMK -->
isSelected -> CommonMangaItemDefaults.BrowseSelectedCoverAlpha
// KMK <--
manga.favorite -> CommonMangaItemDefaults.BrowseFavoriteCoverAlpha
else -> 1f
},

View file

@ -92,10 +92,8 @@ internal fun BrowseSourceListItem(
ogUrl = manga.thumbnailUrl,
lastModified = manga.coverLastModified,
),
isSelected = isSelected,
coverAlpha = when {
// KMK -->
isSelected -> CommonMangaItemDefaults.BrowseSelectedCoverAlpha
// KMK <--
manga.favorite -> CommonMangaItemDefaults.BrowseFavoriteCoverAlpha
else -> 1f
},

View file

@ -78,10 +78,8 @@ internal fun MangaItem(
coverBadgeStart = {
InLibraryBadge(enabled = isFavorite)
},
isSelected = isSelected,
coverAlpha = when {
// KMK -->
isSelected -> CommonMangaItemDefaults.BrowseSelectedCoverAlpha
// KMK <--
isFavorite -> CommonMangaItemDefaults.BrowseFavoriteCoverAlpha
else -> 1f
},

View file

@ -33,25 +33,24 @@ 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
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.selectedBackground
import tachiyomi.domain.manga.model.MangaCover as DomainMangaCover
object CommonMangaItemDefaults {
val GridHorizontalSpacer = 4.dp
val GridVerticalSpacer = 4.dp
const val BrowseFavoriteCoverAlpha = 0.34f
// KMK -->
const val BrowseSelectedCoverAlpha = 0.17f
// KMK <--
}
private val ContinueReadingButtonSize = 28.dp
@ -66,7 +65,7 @@ private const val GridSelectedCoverAlpha = 0.76f
*/
@Composable
fun MangaCompactGridItem(
coverData: tachiyomi.domain.manga.model.MangaCover,
coverData: DomainMangaCover,
onClick: () -> Unit,
onLongClick: () -> Unit,
isSelected: Boolean = false,
@ -86,8 +85,14 @@ fun MangaCompactGridItem(
MangaCover.Book(
modifier = Modifier
.fillMaxWidth()
.alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha),
.background(
color = DomainMangaCover.coverColorMap[coverData.mangaId]?.first?.let { Color(it) }
?: MaterialTheme.colorScheme.surface
),
alpha = if (isSelected) GridSelectedCoverAlpha else coverAlpha,
data = coverData,
tint = DomainMangaCover.coverColorMap[coverData.mangaId]?.second
?: MaterialTheme.colorScheme.onSurface.toArgb(),
)
},
badgesStart = coverBadgeStart,
@ -167,7 +172,7 @@ private fun BoxScope.CoverTextOverlay(
*/
@Composable
fun MangaComfortableGridItem(
coverData: tachiyomi.domain.manga.model.MangaCover,
coverData: DomainMangaCover,
title: String,
onClick: () -> Unit,
onLongClick: () -> Unit,
@ -189,8 +194,14 @@ fun MangaComfortableGridItem(
MangaCover.Book(
modifier = Modifier
.fillMaxWidth()
.alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha),
.background(
color = DomainMangaCover.coverColorMap[coverData.mangaId]?.first?.let { Color(it) }
?: MaterialTheme.colorScheme.surface
),
alpha = if (isSelected) GridSelectedCoverAlpha else coverAlpha,
data = coverData,
tint = DomainMangaCover.coverColorMap[coverData.mangaId]?.second
?: MaterialTheme.colorScheme.onSurface.toArgb(),
)
},
badgesStart = coverBadgeStart,
@ -313,14 +324,14 @@ private fun GridItemSelectable(
private fun Modifier.selectedOutline(
isSelected: Boolean,
color: Color,
) = this then drawBehind { if (isSelected) drawRect(color = color) }
) = this.drawBehind { if (isSelected) drawRect(color = color) }
/**
* Layout of list item.
*/
@Composable
fun MangaListItem(
coverData: tachiyomi.domain.manga.model.MangaCover,
coverData: DomainMangaCover,
title: String,
onClick: () -> Unit,
onLongClick: () -> Unit,
@ -343,8 +354,14 @@ fun MangaListItem(
MangaCover.Square(
modifier = Modifier
.fillMaxHeight()
.alpha(coverAlpha),
.background(
color = DomainMangaCover.coverColorMap[coverData.mangaId]?.first?.let { Color(it) }
?: MaterialTheme.colorScheme.surface
),
alpha = coverAlpha,
data = coverData,
tint = DomainMangaCover.coverColorMap[coverData.mangaId]?.second
?: MaterialTheme.colorScheme.onSurface.toArgb(),
)
Text(
text = title,

View file

@ -1,19 +1,28 @@
package eu.kanade.presentation.manga.components
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.clickable
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.painter.ColorPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.semantics.Role
import coil3.compose.AsyncImage
import eu.kanade.presentation.util.rememberResourceBitmapPainter
import eu.kanade.tachiyomi.R
import kotlinx.coroutines.delay
enum class MangaCover(val ratio: Float) {
Square(1f / 1f),
@ -27,15 +36,36 @@ enum class MangaCover(val ratio: Float) {
contentDescription: String = "",
shape: Shape = MaterialTheme.shapes.extraSmall,
onClick: (() -> Unit)? = null,
@ColorInt tint: Int = CoverPlaceholderColor,
alpha: Float = 1f,
) {
val animatedImageVector = AnimatedImageVector.animatedVectorResource(R.drawable.anim_waiting)
var atEnd by remember { mutableStateOf(false) }
suspend fun runAnimation() {
while (true) {
delay(LoadingAnimatedDuration)
atEnd = !atEnd
}
}
LaunchedEffect(animatedImageVector) {
runAnimation()
}
var succeed by remember { mutableStateOf(false) }
AsyncImage(
model = data,
placeholder = ColorPainter(CoverPlaceholderColor),
error = rememberResourceBitmapPainter(id = R.drawable.cover_error),
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 },
contentDescription = contentDescription,
modifier = modifier
.aspectRatio(ratio)
.clip(shape)
.alpha(if (succeed) alpha else 1f)
.then(
if (onClick != null) {
Modifier.clickable(
@ -51,4 +81,6 @@ enum class MangaCover(val ratio: Float) {
}
}
private val CoverPlaceholderColor = Color(0x1F888888)
@ColorInt
internal val CoverPlaceholderColor = 0x7F888888
const val LoadingAnimatedDuration = 600L

View file

@ -1,6 +1,7 @@
package eu.kanade.presentation.util
import android.content.res.Resources
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@ -19,11 +20,12 @@ import androidx.core.graphics.drawable.toBitmap
* @return the bitmap associated with the resource
*/
@Composable
fun rememberResourceBitmapPainter(@DrawableRes id: Int): BitmapPainter {
fun rememberResourceBitmapPainter(@DrawableRes id: Int, @ColorInt tint: Int? = null): BitmapPainter {
val context = LocalContext.current
return remember(id) {
val drawable = ContextCompat.getDrawable(context, id)
?: throw Resources.NotFoundException()
tint?.let { drawable.setTint(it) }
BitmapPainter(drawable.toBitmap().asImageBitmap())
}
}

View file

@ -0,0 +1,209 @@
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
android:width="24dp"
android:height="24dp"
android:viewportWidth="4320"
android:viewportHeight="4320">
<group android:name="_SQ">
<path
android:fillAlpha="0.1"
android:fillColor="@color/cover_placeholder_loading"
android:pathData="M0,0h4320v4320H0z" />
</group>
<group android:name="_R_G"
android:translateX="1620"
android:translateY="1620">
<group android:name="_R_G_L_2_G">
<path
android:name="_R_G_L_2_G_D_0_P_0"
android:fillAlpha="1"
android:fillColor="@color/cover_placeholder_loading"
android:fillType="nonZero"
android:pathData=" M270.46 450.51 C220.99,450.51 180.52,490.99 180.52,540.46 C180.52,589.93 220.99,630.41 270.46,630.41 C319.93,630.41 360.41,589.93 360.41,540.46 C360.41,490.99 319.93,450.51 270.46,450.51c " />
</group>
<group android:name="_R_G_L_1_G">
<path
android:name="_R_G_L_1_G_D_0_P_0"
android:fillAlpha="1"
android:fillColor="@color/cover_placeholder_loading"
android:fillType="nonZero"
android:pathData=" M540.31 450.51 C490.84,450.51 450.36,490.99 450.36,540.46 C450.36,589.93 490.84,630.41 540.31,630.41 C589.78,630.41 630.26,589.93 630.26,540.46 C630.26,490.99 589.78,450.51 540.31,450.51c " />
</group>
<group android:name="_R_G_L_0_G">
<path
android:name="_R_G_L_0_G_D_0_P_0"
android:fillAlpha="1"
android:fillColor="@color/cover_placeholder_loading"
android:fillType="nonZero"
android:pathData=" M810.16 450.51 C760.68,450.51 720.21,490.99 720.21,540.46 C720.21,589.93 760.68,630.41 810.16,630.41 C859.63,630.41 900.1,589.93 900.1,540.46 C900.1,490.99 859.63,450.51 810.16,450.51c " />
</group>
</group>
<group android:name="time_group" />
</vector>
</aapt:attr>
<target android:name="_R_G_L_2_G_D_0_P_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
android:duration="83"
android:propertyName="pathData"
android:startOffset="0"
android:valueFrom="M270.46 450.51 C220.99,450.51 180.52,490.99 180.52,540.46 C180.52,589.93 220.99,630.41 270.46,630.41 C319.93,630.41 360.41,589.93 360.41,540.46 C360.41,490.99 319.93,450.51 270.46,450.51c "
android:valueTo="M270.46 450.51 C220.99,450.51 180.52,490.99 180.52,540.46 C180.52,589.93 220.99,630.41 270.46,630.41 C319.93,630.41 360.41,589.93 360.41,540.46 C360.41,490.99 319.93,450.51 270.46,450.51c "
android:valueType="pathType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
android:duration="100"
android:propertyName="pathData"
android:startOffset="83"
android:valueFrom="M270.46 450.51 C220.99,450.51 180.52,490.99 180.52,540.46 C180.52,589.93 220.99,630.41 270.46,630.41 C319.93,630.41 360.41,589.93 360.41,540.46 C360.41,490.99 319.93,450.51 270.46,450.51c "
android:valueTo="M272.31 322.51 C222.84,322.51 182.36,362.99 182.36,412.46 C182.36,461.93 222.84,502.41 272.31,502.41 C321.78,502.41 362.26,461.93 362.26,412.46 C362.26,362.99 321.78,322.51 272.31,322.51c "
android:valueType="pathType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
android:duration="133"
android:propertyName="pathData"
android:startOffset="183"
android:valueFrom="M272.31 322.51 C222.84,322.51 182.36,362.99 182.36,412.46 C182.36,461.93 222.84,502.41 272.31,502.41 C321.78,502.41 362.26,461.93 362.26,412.46 C362.26,362.99 321.78,322.51 272.31,322.51c "
android:valueTo="M270.46 486.51 C220.99,486.51 180.52,526.99 180.52,576.46 C180.52,625.93 220.99,666.41 270.46,666.41 C319.93,666.41 360.41,625.93 360.41,576.46 C360.41,526.99 319.93,486.51 270.46,486.51c "
android:valueType="pathType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
android:duration="67"
android:propertyName="pathData"
android:startOffset="317"
android:valueFrom="M270.46 486.51 C220.99,486.51 180.52,526.99 180.52,576.46 C180.52,625.93 220.99,666.41 270.46,666.41 C319.93,666.41 360.41,625.93 360.41,576.46 C360.41,526.99 319.93,486.51 270.46,486.51c "
android:valueTo="M270.46 450.51 C220.99,450.51 180.52,490.99 180.52,540.46 C180.52,589.93 220.99,630.41 270.46,630.41 C319.93,630.41 360.41,589.93 360.41,540.46 C360.41,490.99 319.93,450.51 270.46,450.51c "
android:valueType="pathType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
</set>
</aapt:attr>
</target>
<target android:name="_R_G_L_1_G_D_0_P_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
android:duration="150"
android:propertyName="pathData"
android:startOffset="0"
android:valueFrom="M540.31 450.51 C490.84,450.51 450.36,490.99 450.36,540.46 C450.36,589.93 490.84,630.41 540.31,630.41 C589.78,630.41 630.26,589.93 630.26,540.46 C630.26,490.99 589.78,450.51 540.31,450.51c "
android:valueTo="M540.31 450.51 C490.84,450.51 450.36,490.99 450.36,540.46 C450.36,589.93 490.84,630.41 540.31,630.41 C589.78,630.41 630.26,589.93 630.26,540.46 C630.26,490.99 589.78,450.51 540.31,450.51c "
android:valueType="pathType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
android:duration="100"
android:propertyName="pathData"
android:startOffset="150"
android:valueFrom="M540.31 450.51 C490.84,450.51 450.36,490.99 450.36,540.46 C450.36,589.93 490.84,630.41 540.31,630.41 C589.78,630.41 630.26,589.93 630.26,540.46 C630.26,490.99 589.78,450.51 540.31,450.51c "
android:valueTo="M542.16 322.51 C492.68,322.51 452.21,362.99 452.21,412.46 C452.21,461.93 492.68,502.41 542.16,502.41 C591.63,502.41 632.1,461.93 632.1,412.46 C632.1,362.99 591.63,322.51 542.16,322.51c "
android:valueType="pathType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
android:duration="133"
android:propertyName="pathData"
android:startOffset="250"
android:valueFrom="M542.16 322.51 C492.68,322.51 452.21,362.99 452.21,412.46 C452.21,461.93 492.68,502.41 542.16,502.41 C591.63,502.41 632.1,461.93 632.1,412.46 C632.1,362.99 591.63,322.51 542.16,322.51c "
android:valueTo="M540.31 486.51 C490.84,486.51 450.36,526.99 450.36,576.46 C450.36,625.93 490.84,666.41 540.31,666.41 C589.78,666.41 630.26,625.93 630.26,576.46 C630.26,526.99 589.78,486.51 540.31,486.51c "
android:valueType="pathType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
android:duration="67"
android:propertyName="pathData"
android:startOffset="383"
android:valueFrom="M540.31 486.51 C490.84,486.51 450.36,526.99 450.36,576.46 C450.36,625.93 490.84,666.41 540.31,666.41 C589.78,666.41 630.26,625.93 630.26,576.46 C630.26,526.99 589.78,486.51 540.31,486.51c "
android:valueTo="M540.31 450.51 C490.84,450.51 450.36,490.99 450.36,540.46 C450.36,589.93 490.84,630.41 540.31,630.41 C589.78,630.41 630.26,589.93 630.26,540.46 C630.26,490.99 589.78,450.51 540.31,450.51c "
android:valueType="pathType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
</set>
</aapt:attr>
</target>
<target android:name="_R_G_L_0_G_D_0_P_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
android:duration="217"
android:propertyName="pathData"
android:startOffset="0"
android:valueFrom="M810.16 450.51 C760.68,450.51 720.21,490.99 720.21,540.46 C720.21,589.93 760.68,630.41 810.16,630.41 C859.63,630.41 900.1,589.93 900.1,540.46 C900.1,490.99 859.63,450.51 810.16,450.51c "
android:valueTo="M810.16 450.51 C760.68,450.51 720.21,490.99 720.21,540.46 C720.21,589.93 760.68,630.41 810.16,630.41 C859.63,630.41 900.1,589.93 900.1,540.46 C900.1,490.99 859.63,450.51 810.16,450.51c "
android:valueType="pathType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
android:duration="100"
android:propertyName="pathData"
android:startOffset="217"
android:valueFrom="M810.16 450.51 C760.68,450.51 720.21,490.99 720.21,540.46 C720.21,589.93 760.68,630.41 810.16,630.41 C859.63,630.41 900.1,589.93 900.1,540.46 C900.1,490.99 859.63,450.51 810.16,450.51c "
android:valueTo="M812 322.51 C762.53,322.51 722.05,362.99 722.05,412.46 C722.05,461.93 762.53,502.41 812,502.41 C861.47,502.41 901.95,461.93 901.95,412.46 C901.95,362.99 861.47,322.51 812,322.51c "
android:valueType="pathType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
android:duration="133"
android:propertyName="pathData"
android:startOffset="317"
android:valueFrom="M812 322.51 C762.53,322.51 722.05,362.99 722.05,412.46 C722.05,461.93 762.53,502.41 812,502.41 C861.47,502.41 901.95,461.93 901.95,412.46 C901.95,362.99 861.47,322.51 812,322.51c "
android:valueTo="M810.16 486.51 C760.68,486.51 720.21,526.99 720.21,576.46 C720.21,625.93 760.68,666.41 810.16,666.41 C859.63,666.41 900.1,625.93 900.1,576.46 C900.1,526.99 859.63,486.51 810.16,486.51c "
android:valueType="pathType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
<objectAnimator
android:duration="67"
android:propertyName="pathData"
android:startOffset="450"
android:valueFrom="M810.16 486.51 C760.68,486.51 720.21,526.99 720.21,576.46 C720.21,625.93 760.68,666.41 810.16,666.41 C859.63,666.41 900.1,625.93 900.1,576.46 C900.1,526.99 859.63,486.51 810.16,486.51c "
android:valueTo="M810.16 450.51 C760.68,450.51 720.21,490.99 720.21,540.46 C720.21,589.93 760.68,630.41 810.16,630.41 C859.63,630.41 900.1,589.93 900.1,540.46 C900.1,490.99 859.63,450.51 810.16,450.51c "
android:valueType="pathType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.5,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
</set>
</aapt:attr>
</target>
<target android:name="time_group">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
android:duration="518"
android:propertyName="translateX"
android:startOffset="0"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType" />
</set>
</aapt:attr>
</target>
</animated-vector>

View file

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--suppress AndroidElementNotAllowed -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<item>

View file

@ -2,7 +2,8 @@
<resources>
<color name="splash">#EAEDE3</color>
<color name="cover_placeholder">#1F888888</color>
<color name="cover_placeholder">#2F888888</color>
<color name="cover_placeholder_loading">#888888</color>
<color name="error">#BA1B1B</color>
<color name="errorContainer">#FFDAD4</color>

View file

@ -2,10 +2,12 @@ package tachiyomi.presentation.widget.components
import android.graphics.Bitmap
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.glance.GlanceModifier
import androidx.glance.Image
import androidx.glance.ImageProvider
import androidx.glance.background
import androidx.glance.layout.Box
import androidx.glance.layout.ContentScale
import androidx.glance.layout.fillMaxSize
@ -20,10 +22,12 @@ val CoverHeight = 87.dp
fun UpdatesMangaCover(
cover: Bitmap?,
modifier: GlanceModifier = GlanceModifier,
color: Color? = null,
) {
Box(
modifier = modifier
.size(width = CoverWidth, height = CoverHeight)
.background(color ?: Color.Unspecified)
.appWidgetInnerRadius(),
) {
if (cover != null) {

View file

@ -3,6 +3,7 @@ package tachiyomi.presentation.widget.components
import android.content.Intent
import android.graphics.Bitmap
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.glance.GlanceModifier
@ -23,6 +24,7 @@ import androidx.glance.text.TextStyle
import androidx.glance.unit.ColorProvider
import kotlinx.collections.immutable.ImmutableList
import tachiyomi.core.common.Constants
import tachiyomi.domain.manga.model.MangaCover
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.widget.util.calculateRowAndColumnCount
@ -86,6 +88,7 @@ fun UpdatesWidget(
UpdatesMangaCover(
cover = cover,
modifier = GlanceModifier.clickable(actionStartActivity(intent)),
color = MangaCover.coverColorMap[mangaId]?.first?.let { Color(it) },
)
}
}