Better indicators

This commit is contained in:
Damontecres 2026-01-06 15:28:15 -05:00
parent ee640f82db
commit f89eec622a
No known key found for this signature in database
4 changed files with 170 additions and 48 deletions

View file

@ -21,18 +21,23 @@ val LocalImageUrlService =
/** /**
* Colors not associated with the theme * Colors not associated with the theme
*/ */
sealed class AppColors private constructor() { object AppColors {
companion object { val TransparentBlack25 = Color(0x40000000)
val TransparentBlack25 = Color(0x40000000) val TransparentBlack50 = Color(0x80000000)
val TransparentBlack50 = Color(0x80000000) val TransparentBlack75 = Color(0xBF000000)
val TransparentBlack75 = Color(0xBF000000)
val DarkGreen = Color(0xFF114000) val DarkGreen = Color(0xFF114000)
val DarkRed = Color(0xFF400000) val DarkRed = Color(0xFF400000)
val DarkCyan = Color(0xFF21556E) val DarkCyan = Color(0xFF21556E)
val DarkPurple = Color(0xFF261370) val DarkPurple = Color(0xFF261370)
val GoldenYellow = Color(0xFFDAB440) val GoldenYellow = Color(0xFFDAB440)
object Discover {
val Blue = Color(37, 99, 235)
val Purple = Color(147, 51, 234)
val Green = Color(74, 222, 128)
val Yellow = Color(234, 179, 8)
} }
} }

View file

@ -2,6 +2,7 @@ package com.github.damontecres.wholphin.ui.cards
import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -24,20 +25,27 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.Card import androidx.tv.material3.Card
import androidx.tv.material3.CardDefaults import androidx.tv.material3.CardDefaults
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.DiscoverItem import com.github.damontecres.wholphin.data.model.DiscoverItem
import com.github.damontecres.wholphin.data.model.SeerrAvailability import com.github.damontecres.wholphin.data.model.SeerrAvailability
import com.github.damontecres.wholphin.data.model.SeerrItemType import com.github.damontecres.wholphin.data.model.SeerrItemType
import com.github.damontecres.wholphin.ui.AppColors
import com.github.damontecres.wholphin.ui.AspectRatios import com.github.damontecres.wholphin.ui.AspectRatios
import com.github.damontecres.wholphin.ui.Cards import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.FontAwesome
import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.enableMarquee import com.github.damontecres.wholphin.ui.enableMarquee
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@Composable @Composable
@ -106,49 +114,50 @@ fun DiscoverItemCard(
Modifier Modifier
.fillMaxSize(), .fillMaxSize(),
) )
val availabilityColor = when (item?.availability) {
when (item?.availability) { SeerrAvailability.PENDING,
SeerrAvailability.PENDING, SeerrAvailability.PROCESSING,
SeerrAvailability.PROCESSING, -> {
-> Color.Yellow PendingIndicator(Modifier.align(Alignment.TopEnd))
SeerrAvailability.PARTIALLY_AVAILABLE,
SeerrAvailability.AVAILABLE,
-> Color.Green
else -> null
} }
availabilityColor?.let {
Box( SeerrAvailability.PARTIALLY_AVAILABLE -> {
modifier = PartiallyAvailableIndicator(Modifier.align(Alignment.TopEnd))
Modifier }
.padding(4.dp)
.background( SeerrAvailability.AVAILABLE,
color = it, -> {
shape = CircleShape, AvailableIndicator(Modifier.align(Alignment.TopEnd))
).size(12.dp) }
.align(Alignment.TopEnd),
) else -> {}
} }
if (showOverlay) { if (showOverlay) {
// TODO better colors
val color = val color =
when (item?.type) { remember(item?.type) {
SeerrItemType.MOVIE -> Color.Blue when (item?.type) {
SeerrItemType.TV -> Color.Red SeerrItemType.MOVIE -> AppColors.Discover.Blue
SeerrItemType.PERSON -> Color.Green SeerrItemType.TV -> AppColors.Discover.Purple
SeerrItemType.UNKNOWN -> Color.Black SeerrItemType.PERSON -> AppColors.Discover.Green
null -> Color.Black SeerrItemType.UNKNOWN -> Color.Black
}.copy(alpha = .5f) null -> Color.Black
when (item?.type) { }.copy(alpha = .8f)
SeerrItemType.MOVIE -> R.string.movies }
SeerrItemType.TV -> R.string.tv_shows val text =
SeerrItemType.PERSON -> R.string.people remember(item?.type) {
SeerrItemType.UNKNOWN -> null when (item?.type) {
null -> null SeerrItemType.MOVIE -> R.plurals.movies
}?.let { SeerrItemType.TV -> R.plurals.tv_shows
SeerrItemType.PERSON -> R.plurals.people
SeerrItemType.UNKNOWN -> null
null -> null
}
}
text?.let {
Text( Text(
text = stringResource(it), text = pluralStringResource(it, 1),
style = MaterialTheme.typography.bodySmall,
fontSize = 10.sp,
modifier = modifier =
Modifier Modifier
.align(Alignment.TopStart) .align(Alignment.TopStart)
@ -192,3 +201,93 @@ fun DiscoverItemCard(
} }
} }
} }
@Composable
fun PendingIndicator(modifier: Modifier = Modifier) {
Box(
modifier =
modifier
.padding(4.dp)
.border(
width = .5.dp,
color = AppColors.Discover.Yellow,
shape = CircleShape,
).background(
color = Color.White.copy(alpha = .85f),
shape = CircleShape,
).size(16.dp),
) {
Text(
text = stringResource(R.string.fa_bell),
fontFamily = FontAwesome,
fontSize = 10.sp,
color = AppColors.Discover.Yellow,
modifier = Modifier.align(Alignment.Center),
)
}
}
@Composable
fun AvailableIndicator(modifier: Modifier = Modifier) {
Box(
modifier =
modifier
.padding(4.dp)
.border(
width = .5.dp,
color = Color.White,
shape = CircleShape,
).background(
color = AppColors.Discover.Green.copy(alpha = .85f),
shape = CircleShape,
).size(16.dp),
) {
Text(
text = stringResource(R.string.fa_check),
fontFamily = FontAwesome,
fontSize = 10.sp,
color = Color.White,
modifier = Modifier.align(Alignment.Center),
)
}
}
@Composable
fun PartiallyAvailableIndicator(modifier: Modifier = Modifier) {
Box(
modifier =
modifier
.padding(4.dp)
.border(
width = .5.dp,
color = Color.White,
shape = CircleShape,
).background(
color = AppColors.Discover.Green.copy(alpha = .85f),
shape = CircleShape,
).size(16.dp),
) {
Box(
modifier =
Modifier
.align(Alignment.Center)
.size(width = 10.dp, height = 2.dp)
.background(
color = Color.White,
shape = CircleShape,
),
)
}
}
@PreviewTvSpec
@Composable
private fun Preview() {
WholphinTheme {
Column {
PendingIndicator()
AvailableIndicator()
PartiallyAvailableIndicator()
}
}
}

View file

@ -47,4 +47,6 @@
<string name="fa_sliders" translatable="false">&#xf1de;</string> <string name="fa_sliders" translatable="false">&#xf1de;</string>
<string name="fa_download" translatable="false">&#xf019;</string> <string name="fa_download" translatable="false">&#xf019;</string>
<string name="fa_clock" translatable="false">&#xf017;</string> <string name="fa_clock" translatable="false">&#xf017;</string>
<string name="fa_bell" translatable="false">&#xf0f3;</string>
<string name="fa_check" translatable="false">&#xf00c;</string>
</resources> </resources>

View file

@ -252,6 +252,22 @@
<item quantity="other">%d seconds</item> <item quantity="other">%d seconds</item>
</plurals> </plurals>
<plurals name="movies">
<item quantity="zero">Movies</item>
<item quantity="one">Movie</item>
<item quantity="other">Movies</item>
</plurals>
<plurals name="tv_shows">
<item quantity="zero">TV Shows</item>
<item quantity="one">TV Show</item>
<item quantity="other">TV Shows</item>
</plurals>
<plurals name="people">
<item quantity="zero">People</item>
<item quantity="one">Person</item>
<item quantity="other">People</item>
</plurals>
<!-- Preferences/Setting specific strings --> <!-- Preferences/Setting specific strings -->
<string name="pref_key_update_last_check_threshold" translatable="false">preference.update.threshold</string> <string name="pref_key_update_last_check_threshold" translatable="false">preference.update.threshold</string>
<string name="pref_key_update_last_check" translatable="false">preference.update.lastTimestamp</string> <string name="pref_key_update_last_check" translatable="false">preference.update.lastTimestamp</string>