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:
parent
b6b7114c29
commit
4c713f118f
12 changed files with 290 additions and 31 deletions
|
|
@ -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
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
209
app/src/main/res/drawable/anim_waiting.xml
Normal file
209
app/src/main/res/drawable/anim_waiting.xml
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue