Rename weather icons, add monochrome variant

This commit is contained in:
MM20
2026-02-13 11:16:17 +01:00
parent d8e95e9f64
commit de8e83aa38
13 changed files with 759 additions and 727 deletions

View File

@@ -3,13 +3,35 @@ package de.mm20.launcher2.ui.component.weather
import androidx.compose.animation.Crossfade
import androidx.compose.animation.animateColor
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.*
import androidx.compose.animation.core.InfiniteRepeatableSpec
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.animateOffset
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
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.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
@@ -41,9 +63,7 @@ fun AnimatedWeatherIcon(
SunMoon(icon, night, colors)
Precipitation(icon, colors)
LightningBolt(icon, colors)
LightningBolt2(icon, colors)
Cloud1(icon, colors)
Wind2(icon, colors)
Cloud2(icon, colors)
Cloud3(icon, colors)
Fog(icon, colors)
@@ -63,18 +83,16 @@ private fun SunMoon(icon: WeatherIcon, night: Boolean, colors: WeatherIconColors
when (it) {
WeatherIcon.Clear,
WeatherIcon.PartlyCloudy,
WeatherIcon.BrokenClouds,
WeatherIcon.Haze -> 1f
WeatherIcon.MostlyCloudy -> 0.8f
else -> 0f
}
}
val offset by transition.animateOffset(label = "sunOffset") {
when (it) {
WeatherIcon.Clear,
WeatherIcon.BrokenClouds,
WeatherIcon.Haze -> Offset.Zero
WeatherIcon.MostlyCloudy -> Offset(6f, -4f)
WeatherIcon.PartlyCloudy -> Offset(3f, -2f)
else -> Offset.Zero
}
@@ -97,19 +115,17 @@ private fun LightningBolt(icon: WeatherIcon, colors: WeatherIconColors) {
val scale by transition.animateFloat(label = "scale") {
when (it) {
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.HeavyThunderstorm -> 0.6f
WeatherIcon.Thunder,
WeatherIcon.Thunderstorm -> 0.6f
else -> 0f
}
}
val offset by transition.animateOffset(label = "offset") {
when (it) {
WeatherIcon.ThunderstormWithRain,
WeatherIcon.Thunder,
WeatherIcon.Thunderstorm -> Offset(1f, 8f)
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.HeavyThunderstorm -> Offset(6f, 8f)
else -> Offset.Zero
}
}
@@ -125,35 +141,6 @@ private fun LightningBolt(icon: WeatherIcon, colors: WeatherIconColors) {
)
}
@Composable
private fun LightningBolt2(icon: WeatherIcon, colors: WeatherIconColors) {
val transition = updateTransition(targetState = icon, "AnimatedWeatherIconBolt2")
val scale by transition.animateFloat(label = "scale") {
when (it) {
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.HeavyThunderstorm -> 0.5f
else -> 0f
}
}
val offset by transition.animateOffset(label = "offset") {
when (it) {
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.HeavyThunderstorm -> Offset(-9f, 5f)
else -> Offset.Zero
}
}
Icon(
painter = painterResource(R.drawable.bolt_24px_filled),
null,
modifier = Modifier
.size(32.dp)
.offset(offset.x.dp, offset.y.dp)
.scale(scale),
tint = colors.lightningBolt
)
}
@Composable
private fun Cloud1(icon: WeatherIcon, colors: WeatherIconColors) {
@@ -162,64 +149,60 @@ private fun Cloud1(icon: WeatherIcon, colors: WeatherIconColors) {
val scale by transition.animateFloat(label = "scale") {
when (it) {
WeatherIcon.Clear -> 0f
WeatherIcon.Thunder,
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.LightRain,
WeatherIcon.Rain,
WeatherIcon.HeavyRain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Wind,
WeatherIcon.Cloudy,
WeatherIcon.Storm,
WeatherIcon.Overcast,
WeatherIcon.Wind,
WeatherIcon.Fog -> 1.4f
WeatherIcon.MostlyCloudy,
WeatherIcon.PartlyCloudy -> 1f
WeatherIcon.BrokenClouds -> 0.9f
else -> 0f
}
}
val offset by transition.animateOffset(label = "offset") {
when (it) {
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.LightRain,
WeatherIcon.Rain,
WeatherIcon.HeavyRain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain -> Offset(0f, -14f)
WeatherIcon.Cloudy,
WeatherIcon.Thunder,
WeatherIcon.Thunderstorm -> Offset(0f, -14f)
WeatherIcon.Overcast,
WeatherIcon.Wind,
WeatherIcon.Storm,
WeatherIcon.Fog -> Offset(0f, -5f)
WeatherIcon.MostlyCloudy -> Offset(-5f, 0f)
WeatherIcon.PartlyCloudy -> Offset(-3f, 4f)
WeatherIcon.BrokenClouds -> Offset(-5f, 7f)
else -> Offset.Zero
}
}
val color by transition.animateColor(label = "color") {
when (it) {
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.Storm -> colors.cloudDark2
WeatherIcon.Showers,
WeatherIcon.Thunder,
WeatherIcon.Wind -> colors.cloudDark2
WeatherIcon.Rain,
WeatherIcon.HeavyRain,
WeatherIcon.Sleet,
WeatherIcon.Hail,
WeatherIcon.Cloudy,
WeatherIcon.Overcast,
WeatherIcon.Wind,
WeatherIcon.Fog -> colors.cloudDark1
WeatherIcon.Drizzle,
WeatherIcon.LightRain,
WeatherIcon.Snow -> colors.cloudMedium2
WeatherIcon.MostlyCloudy -> colors.cloudLight2
WeatherIcon.PartlyCloudy,
WeatherIcon.BrokenClouds -> colors.cloudLight1
WeatherIcon.PartlyCloudy -> colors.cloudLight1
else -> colors.cloudMedium2
}
}
@@ -241,61 +224,55 @@ private fun Cloud2(icon: WeatherIcon, colors: WeatherIconColors) {
val scale by transition.animateFloat(label = "scale") {
when (it) {
WeatherIcon.Thunder,
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.MostlyCloudy,
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.Rain,
WeatherIcon.HeavyRain,
WeatherIcon.LightRain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Cloudy,
WeatherIcon.Overcast,
WeatherIcon.Wind,
WeatherIcon.Storm,
WeatherIcon.Fog -> 1.1f
WeatherIcon.BrokenClouds -> 0.7f
else -> 0f
}
}
val offset by transition.animateOffset(label = "offset") {
when (it) {
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.Rain,
WeatherIcon.LightRain,
WeatherIcon.HeavyRain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain -> Offset(-6f, -6f)
WeatherIcon.Cloudy,
WeatherIcon.Thunder -> Offset(-6f, -6f)
WeatherIcon.Overcast,
WeatherIcon.Wind,
WeatherIcon.Storm,
WeatherIcon.Fog -> Offset(-6f, 3f)
WeatherIcon.BrokenClouds -> Offset(9f, -9f)
WeatherIcon.MostlyCloudy -> Offset(4f, 5f)
else -> Offset.Zero
}
}
val color by transition.animateColor(label = "color") {
when (it) {
WeatherIcon.BrokenClouds -> colors.cloudLight2
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.Storm -> colors.cloudMedium2
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.Thunder,
WeatherIcon.Wind -> colors.cloudMedium2
WeatherIcon.Rain,
WeatherIcon.HeavyRain,
WeatherIcon.LightRain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Cloudy,
WeatherIcon.Overcast,
WeatherIcon.Wind,
WeatherIcon.Fog -> colors.cloudMedium1
WeatherIcon.MostlyCloudy -> colors.cloudMedium1
else -> colors.cloudMedium2
}
}
@@ -317,52 +294,53 @@ private fun Cloud3(icon: WeatherIcon, colors: WeatherIconColors) {
val scale by transition.animateFloat(label = "scale") {
when (it) {
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.Rain,
WeatherIcon.LightRain,
WeatherIcon.HeavyRain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.Cloudy,
WeatherIcon.Wind,
WeatherIcon.Storm -> 1f
WeatherIcon.Thunder,
WeatherIcon.Overcast,
WeatherIcon.Wind -> 1f
else -> 0f
}
}
val offset by transition.animateOffset(label = "offset") {
when (it) {
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.LightRain,
WeatherIcon.HeavyRain,
WeatherIcon.Rain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain -> Offset(6f, -4f)
WeatherIcon.Cloudy,
WeatherIcon.Wind,
WeatherIcon.Storm -> Offset(6f, 5f)
WeatherIcon.Thunder -> Offset(6f, -4f)
WeatherIcon.Overcast,
WeatherIcon.Wind -> Offset(6f, 5f)
else -> Offset.Zero
}
}
val color by transition.animateColor(label = "color") {
when (it) {
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.Storm -> colors.cloudDark1
WeatherIcon.Showers,
WeatherIcon.Thunder,
WeatherIcon.Wind -> colors.cloudDark1
WeatherIcon.Rain,
WeatherIcon.HeavyRain,
WeatherIcon.Sleet,
WeatherIcon.Hail,
WeatherIcon.Cloudy,
WeatherIcon.Overcast,
WeatherIcon.Wind -> colors.cloudMedium2
WeatherIcon.Drizzle,
WeatherIcon.LightRain,
WeatherIcon.Snow -> colors.cloudLight2
else -> colors.cloudMedium2
}
}
@@ -381,7 +359,7 @@ private fun Cloud3(icon: WeatherIcon, colors: WeatherIconColors) {
@Composable
private fun Hot(icon: WeatherIcon, colors: WeatherIconColors) {
val scale by animateFloatAsState(
if (icon == WeatherIcon.Hot) {
if (icon == WeatherIcon.ExtremeHeat) {
1f
} else {
0f
@@ -400,7 +378,7 @@ private fun Hot(icon: WeatherIcon, colors: WeatherIconColors) {
@Composable
private fun Cold(icon: WeatherIcon, colors: WeatherIconColors) {
val scale by animateFloatAsState(
if (icon == WeatherIcon.Cold) {
if (icon == WeatherIcon.ExtremeCold) {
1f
} else {
0f
@@ -419,7 +397,7 @@ private fun Cold(icon: WeatherIcon, colors: WeatherIconColors) {
@Composable
private fun Wind1(icon: WeatherIcon, colors: WeatherIconColors) {
val scale by animateFloatAsState(
if (icon == WeatherIcon.Wind || icon == WeatherIcon.Storm) {
if (icon == WeatherIcon.Wind) {
0.6f
} else {
0f
@@ -436,38 +414,38 @@ private fun Wind1(icon: WeatherIcon, colors: WeatherIconColors) {
)
}
@Composable
private fun Wind2(icon: WeatherIcon, colors: WeatherIconColors) {
val scale by animateFloatAsState(
if (icon == WeatherIcon.Storm) {
0.6f
} else {
0f
}
)
Icon(
painterResource(R.drawable.air_20px),
null,
modifier = Modifier
.scale(scale)
.offset(8.dp, -1.dp)
.size(32.dp),
tint = colors.windDark
)
}
@Preview
@Composable
fun AnimatedWeatherIconTestPanel() {
var icon by remember { mutableStateOf(WeatherIcon.ThunderstormWithRain) }
var icon by remember { mutableStateOf(WeatherIcon.Thunderstorm) }
var night by remember { mutableStateOf(false) }
Row {
val icons = WeatherIcon.values()
val icons = WeatherIcon.entries
Column(
modifier = Modifier.weight(1f)
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
AnimatedWeatherIcon(icon = icon, night = night)
WeatherIcon(icon = icon, night = night)
val monochromeColors = WeatherIconDefaults.monochromeColors(
MaterialTheme.colorScheme.secondary
)
AnimatedWeatherIcon(
modifier = Modifier.background(MaterialTheme.colorScheme.secondary),
icon = icon,
night = night,
colors = monochromeColors
)
WeatherIcon(
modifier = Modifier.background(MaterialTheme.colorScheme.secondary),
icon = icon,
night = night,
colors = monochromeColors
)
}
Column {
@@ -510,7 +488,7 @@ private fun Precipitation(icon: WeatherIcon, colors: WeatherIconColors) {
) {
Crossfade(icon) {
when (it) {
WeatherIcon.Drizzle -> {
WeatherIcon.LightRain, WeatherIcon.Rain -> {
val anim = rememberInfiniteTransition()
val animProgress by anim.animateFloat(
initialValue = 0f, targetValue = 1f, animationSpec = InfiniteRepeatableSpec(
@@ -526,6 +504,7 @@ private fun Precipitation(icon: WeatherIcon, colors: WeatherIconColors) {
tint = colors.rain
)
}
WeatherIcon.Hail -> {
val anim = rememberInfiniteTransition()
val animProgress by anim.animateFloat(
@@ -542,6 +521,7 @@ private fun Precipitation(icon: WeatherIcon, colors: WeatherIconColors) {
tint = colors.hail,
)
}
WeatherIcon.Snow -> {
val anim = rememberInfiniteTransition()
val animProgress by anim.animateFloat(
@@ -561,9 +541,9 @@ private fun Precipitation(icon: WeatherIcon, colors: WeatherIconColors) {
tint = colors.snow,
)
}
WeatherIcon.Showers,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstormWithRain -> {
WeatherIcon.HeavyRain,
WeatherIcon.Thunderstorm -> {
val anim = rememberInfiniteTransition()
val animProgress by anim.animateFloat(
initialValue = 0f, targetValue = 1f, animationSpec = InfiniteRepeatableSpec(
@@ -579,6 +559,7 @@ private fun Precipitation(icon: WeatherIcon, colors: WeatherIconColors) {
tint = colors.rain,
)
}
WeatherIcon.Sleet -> {
val anim = rememberInfiniteTransition()
val animProgress by anim.animateFloat(
@@ -603,6 +584,7 @@ private fun Precipitation(icon: WeatherIcon, colors: WeatherIconColors) {
tint = colors.snow,
)
}
else -> {}
}
}
@@ -625,25 +607,36 @@ private fun Fog(icon: WeatherIcon, colors: WeatherIconColors) {
}
enum class WeatherIcon {
None,
Unknown,
Clear,
Cloudy,
Cold,
Drizzle,
Haze,
Fog,
Hail,
HeavyThunderstorm,
HeavyThunderstormWithRain,
Hot,
MostlyCloudy,
PartlyCloudy,
Showers,
Sleet,
Snow,
Storm,
Overcast,
LightRain,
Rain,
HeavyRain,
/**
* Thunder and lightning without rain
*/
Thunder,
/**
* Thunder and lightning with rain
*/
Thunderstorm,
ThunderstormWithRain,
Snow,
Sleet,
Hail,
ExtremeHeat,
ExtremeCold,
Wind,
BrokenClouds
Fog,
Haze,
}

View File

@@ -43,10 +43,7 @@ private fun SunMoon(icon: WeatherIcon, night: Boolean, colors: WeatherIconColors
when (icon) {
WeatherIcon.Clear,
WeatherIcon.PartlyCloudy,
WeatherIcon.BrokenClouds,
WeatherIcon.Haze,
WeatherIcon.MostlyCloudy -> {
}
WeatherIcon.Haze, -> {}
else -> return
}
@@ -56,15 +53,10 @@ private fun SunMoon(icon: WeatherIcon, night: Boolean, colors: WeatherIconColors
val scale = when (icon) {
WeatherIcon.Clear,
WeatherIcon.PartlyCloudy,
WeatherIcon.BrokenClouds,
WeatherIcon.Haze -> 1f
WeatherIcon.MostlyCloudy -> 0.8f
else -> 0f
}
val offset = when (icon) {
WeatherIcon.MostlyCloudy -> DpOffset(2.dp, -3.dp)
WeatherIcon.PartlyCloudy -> DpOffset(2.dp, -2.dp)
else -> DpOffset.Zero
}
@@ -85,19 +77,16 @@ private fun Cloud1(icon: WeatherIcon, colors: WeatherIconColors) {
when (icon) {
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.Thunder,
WeatherIcon.Rain,
WeatherIcon.LightRain,
WeatherIcon.HeavyRain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Cloudy,
WeatherIcon.Overcast,
WeatherIcon.Fog,
WeatherIcon.MostlyCloudy,
WeatherIcon.PartlyCloudy,
WeatherIcon.BrokenClouds -> {
WeatherIcon.PartlyCloudy -> {
}
else -> return
@@ -105,62 +94,48 @@ private fun Cloud1(icon: WeatherIcon, colors: WeatherIconColors) {
val scale = when (icon) {
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.Thunder,
WeatherIcon.Rain,
WeatherIcon.LightRain,
WeatherIcon.HeavyRain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Cloudy,
WeatherIcon.Overcast,
WeatherIcon.Fog -> 1.4f
WeatherIcon.MostlyCloudy,
WeatherIcon.PartlyCloudy -> 1f
WeatherIcon.BrokenClouds -> 0.9f
else -> 0f
}
val offset = when (icon) {
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.HeavyRain,
WeatherIcon.Rain,
WeatherIcon.LightRain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain -> DpOffset(0.dp, -7.dp)
WeatherIcon.Thunder -> DpOffset(0.dp, -7.dp)
WeatherIcon.Cloudy,
WeatherIcon.Overcast,
WeatherIcon.Fog -> DpOffset(0.dp, -2.5.dp)
WeatherIcon.MostlyCloudy -> DpOffset(-2.5.dp, 0.dp)
WeatherIcon.PartlyCloudy -> DpOffset(-1.5.dp, 2.dp)
WeatherIcon.BrokenClouds -> DpOffset(-2.5.dp, 3.5.dp)
else -> DpOffset.Zero
}
val color = when (icon) {
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain -> colors.cloudDark2
WeatherIcon.Thunder -> colors.cloudDark2
WeatherIcon.Showers,
WeatherIcon.Rain,
WeatherIcon.HeavyRain,
WeatherIcon.Sleet,
WeatherIcon.Hail,
WeatherIcon.Cloudy,
WeatherIcon.Overcast,
WeatherIcon.Fog -> colors.cloudDark1
WeatherIcon.Drizzle,
WeatherIcon.LightRain,
WeatherIcon.Snow -> colors.cloudMedium2
WeatherIcon.MostlyCloudy -> colors.cloudLight2
WeatherIcon.PartlyCloudy,
WeatherIcon.BrokenClouds -> colors.cloudLight1
else -> colors.cloudMedium2
WeatherIcon.PartlyCloudy -> colors.cloudLight1
}
Icon(
@@ -179,18 +154,15 @@ private fun Cloud2(icon: WeatherIcon, colors: WeatherIconColors) {
when (icon) {
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.MostlyCloudy,
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.Thunder,
WeatherIcon.Rain,
WeatherIcon.HeavyRain,
WeatherIcon.LightRain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Cloudy,
WeatherIcon.Fog,
WeatherIcon.BrokenClouds -> {
WeatherIcon.Overcast,
WeatherIcon.Fog -> {
}
else -> return
@@ -198,56 +170,41 @@ private fun Cloud2(icon: WeatherIcon, colors: WeatherIconColors) {
val scale = when (icon) {
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.MostlyCloudy,
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.Thunder,
WeatherIcon.Rain,
WeatherIcon.LightRain,
WeatherIcon.HeavyRain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Cloudy,
WeatherIcon.Overcast,
WeatherIcon.Fog -> 1.1f
WeatherIcon.BrokenClouds -> 0.7f
else -> 0f
}
val offset = when (icon) {
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.Rain,
WeatherIcon.HeavyRain,
WeatherIcon.LightRain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain -> DpOffset(-3.dp, -3.dp)
WeatherIcon.Thunder -> DpOffset(-3.dp, -3.dp)
WeatherIcon.Cloudy,
WeatherIcon.Overcast,
WeatherIcon.Fog -> DpOffset(-3.dp, 1.5.dp)
WeatherIcon.BrokenClouds -> DpOffset(4.5.dp, -4.5.dp)
WeatherIcon.MostlyCloudy -> DpOffset(2.dp, 2.5.dp)
else -> DpOffset.Zero
}
val color = when (icon) {
WeatherIcon.BrokenClouds -> colors.cloudLight2
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain -> colors.cloudMedium2
WeatherIcon.Thunder -> colors.cloudMedium2
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.Rain,
WeatherIcon.HeavyRain,
WeatherIcon.LightRain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Cloudy,
WeatherIcon.Overcast,
WeatherIcon.Fog -> colors.cloudMedium1
WeatherIcon.MostlyCloudy -> colors.cloudMedium1
else -> colors.cloudMedium2
}
Icon(
@@ -264,52 +221,41 @@ private fun Cloud2(icon: WeatherIcon, colors: WeatherIconColors) {
@Composable
private fun Cloud3(icon: WeatherIcon, colors: WeatherIconColors) {
when (icon) {
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.LightRain,
WeatherIcon.HeavyRain,
WeatherIcon.Rain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain,
WeatherIcon.Cloudy -> {
}
WeatherIcon.Thunder,
WeatherIcon.Overcast -> {}
else -> return
}
val offset = when (icon) {
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.LightRain,
WeatherIcon.HeavyRain,
WeatherIcon.Rain,
WeatherIcon.Sleet,
WeatherIcon.Snow,
WeatherIcon.Hail,
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain -> DpOffset(3.dp, -2.dp)
WeatherIcon.Cloudy,
WeatherIcon.Fog -> DpOffset(3.dp, 2.5.dp)
else -> DpOffset.Zero
WeatherIcon.Thunder -> DpOffset(3.dp, -2.dp)
WeatherIcon.Overcast -> DpOffset(3.dp, 2.5.dp)
}
val color = when (icon) {
WeatherIcon.Thunderstorm,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstorm,
WeatherIcon.HeavyThunderstormWithRain -> colors.cloudDark1
WeatherIcon.Thunder -> colors.cloudDark1
WeatherIcon.Showers,
WeatherIcon.Rain,
WeatherIcon.HeavyRain,
WeatherIcon.Sleet,
WeatherIcon.Hail,
WeatherIcon.Cloudy -> colors.cloudMedium2
WeatherIcon.Overcast -> colors.cloudMedium2
WeatherIcon.Drizzle,
WeatherIcon.LightRain,
WeatherIcon.Snow -> colors.cloudLight2
else -> colors.cloudMedium2
}
Icon(
@@ -345,30 +291,26 @@ private fun Precipitation(icon: WeatherIcon, colors: WeatherIconColors) {
}
when (icon) {
WeatherIcon.Showers,
WeatherIcon.Drizzle,
WeatherIcon.Rain,
WeatherIcon.LightRain,
WeatherIcon.HeavyRain,
WeatherIcon.Hail,
WeatherIcon.Snow,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstormWithRain -> {
WeatherIcon.Thunderstorm -> {
}
else -> return
}
val vector = when (icon) {
WeatherIcon.Drizzle -> WeatherLightRain
WeatherIcon.Showers,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstormWithRain -> WeatherRain
WeatherIcon.LightRain, WeatherIcon.Rain -> WeatherLightRain
WeatherIcon.HeavyRain,
WeatherIcon.Thunderstorm -> WeatherRain
WeatherIcon.Hail, WeatherIcon.Snow -> WeatherHail
else -> WeatherLightRain
}
val color = when (icon) {
WeatherIcon.Drizzle,
WeatherIcon.Showers,
WeatherIcon.ThunderstormWithRain,
WeatherIcon.HeavyThunderstormWithRain -> colors.rain
WeatherIcon.LightRain,
WeatherIcon.Rain,
WeatherIcon.HeavyRain,
WeatherIcon.Thunderstorm -> colors.rain
WeatherIcon.Hail -> colors.hail
WeatherIcon.Snow -> colors.snow
@@ -386,7 +328,7 @@ private fun Precipitation(icon: WeatherIcon, colors: WeatherIconColors) {
@Composable
private fun HotCold(icon: WeatherIcon, colors: WeatherIconColors) {
if (icon == WeatherIcon.Hot) {
if (icon == WeatherIcon.ExtremeHeat) {
Icon(
modifier = Modifier
.size(32.dp),
@@ -395,7 +337,7 @@ private fun HotCold(icon: WeatherIcon, colors: WeatherIconColors) {
tint = colors.hot
)
}
if (icon == WeatherIcon.Cold) {
if (icon == WeatherIcon.ExtremeCold) {
Icon(
modifier = Modifier
.size(32.dp),
@@ -408,20 +350,10 @@ private fun HotCold(icon: WeatherIcon, colors: WeatherIconColors) {
@Composable
private fun Wind(icon: WeatherIcon, colors: WeatherIconColors) {
if (icon == WeatherIcon.Storm) {
if (icon == WeatherIcon.Wind) {
Icon(
modifier = Modifier
.offset(x = 4.dp, y = 0.dp)
.size(24.dp),
painter = painterResource(R.drawable.air_24px),
contentDescription = null,
tint = colors.windDark
)
}
if (icon == WeatherIcon.Wind || icon == WeatherIcon.Storm) {
Icon(
modifier = Modifier
.size(if (icon == WeatherIcon.Wind) 32.dp else 24.dp),
.size(32.dp),
painter = painterResource(R.drawable.air_24px),
contentDescription = null,
tint = colors.wind
@@ -449,34 +381,18 @@ private fun Fog(icon: WeatherIcon, colors: WeatherIconColors) {
@Composable
private fun LightningBolts(icon: WeatherIcon, colors: WeatherIconColors) {
if (icon != WeatherIcon.ThunderstormWithRain
&& icon != WeatherIcon.HeavyThunderstormWithRain
&& icon != WeatherIcon.Thunderstorm
&& icon != WeatherIcon.HeavyThunderstorm
if (icon != WeatherIcon.Thunderstorm
&& icon != WeatherIcon.Thunder
) {
return
}
val isHeavy =
icon == WeatherIcon.HeavyThunderstormWithRain || icon == WeatherIcon.HeavyThunderstorm
Icon(
modifier = Modifier
.size(12.dp)
.offset(x = if (isHeavy) 4.dp else 1.dp, y = 6.dp),
.offset(x = 1.dp, y = 6.dp),
painter = painterResource(R.drawable.bolt_24px_filled),
contentDescription = null,
tint = colors.lightningBolt
)
if (icon == WeatherIcon.HeavyThunderstorm || icon == WeatherIcon.HeavyThunderstormWithRain) {
Icon(
modifier = Modifier
.size(10.dp)
.offset(x = -3.dp, y = 6.dp),
painter = painterResource(R.drawable.bolt_24px_filled),
contentDescription = null,
tint = colors.lightningBolt
)
}
}

View File

@@ -1,8 +1,6 @@
package de.mm20.launcher2.ui.component.weather
import android.content.Context
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@@ -10,9 +8,9 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import blend.Blend
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.ktx.atTone
import de.mm20.launcher2.ui.locals.LocalDarkTheme
import hct.Hct
data class WeatherIconColors(
@@ -38,38 +36,138 @@ data class WeatherIconColors(
object WeatherIconDefaults {
@Composable
fun colors(): WeatherIconColors {
val darkTheme = LocalDarkTheme.current
if (darkTheme) {
return darkColors()
} else {
return lightColors()
}
}
@Composable
fun invertedColors(): WeatherIconColors {
val darkTheme = LocalDarkTheme.current
if (!darkTheme) {
return darkColors()
} else {
return lightColors()
}
}
@Composable
fun darkColors(): WeatherIconColors {
val context = LocalContext.current
val themeColor = MaterialTheme.colorScheme.primary
val darkTheme = LocalDarkTheme.current
val neutral1 = MaterialTheme.colorScheme.outline
val neutral2 = MaterialTheme.colorScheme.outline
return remember(themeColor) {
WeatherIconColors(
sun = harmonize(context, 0xFFFFB300.toInt(), themeColor),
moon = harmonize(context, 0xFF9E9E9E.toInt(), themeColor),
cloudDark1 = harmonize(context, neutral1.atTone(if (darkTheme) 40 else 30).toArgb(), themeColor),
cloudDark2 = harmonize(context, neutral1.atTone(if (darkTheme) 30 else 20).toArgb(), themeColor),
cloudMedium1 = harmonize(context, neutral1.atTone(if (darkTheme) 60 else 50).toArgb(), themeColor),
cloudMedium2 = harmonize(context, neutral1.atTone(if (darkTheme) 50 else 40).toArgb(), themeColor),
cloudLight1 = harmonize(context, neutral1.atTone(if (darkTheme) 95 else 85).toArgb(), themeColor),
cloudLight2 = harmonize(context, neutral1.atTone(if (darkTheme) 85 else 75).toArgb(), themeColor),
rain = harmonize(context, if (darkTheme) 0xFF64B5F6.toInt() else 0xFF1E88E5.toInt(), themeColor),
snow = harmonize(context, if (darkTheme) 0xFFF5F5F5.toInt() else 0xFFE0E0E0.toInt(), themeColor),
hail = harmonize(context, if (darkTheme) 0xFFF5F5F5.toInt() else 0xFFE0E0E0.toInt(), themeColor),
fog = harmonize(context, neutral1.atTone(if (darkTheme) 95 else 85).toArgb(), themeColor),
wind = harmonize(context, neutral2.atTone(if (darkTheme) 70 else 75).toArgb(), themeColor),
windDark = harmonize(context, neutral2.atTone(if (darkTheme) 40 else 45).toArgb(), themeColor),
lightningBolt = harmonize(context, 0xFFFFB300.toInt(), themeColor),
hot = harmonize(context, if (darkTheme) 0xFFE57373.toInt() else 0xFFE53935.toInt(), themeColor),
cold = harmonize(context, if (darkTheme) 0xFF4FC3F7.toInt() else 0xFF039BE5.toInt(), themeColor),
sun = harmonize(0xFFFFB300.toInt(), themeColor),
moon = harmonize(0xFF9E9E9E.toInt(), themeColor),
cloudDark1 = harmonize(neutral1.atTone(40).toArgb(), themeColor),
cloudDark2 = harmonize(neutral1.atTone(30).toArgb(), themeColor),
cloudMedium1 = harmonize(neutral1.atTone(60).toArgb(), themeColor),
cloudMedium2 = harmonize(neutral1.atTone(50).toArgb(), themeColor),
cloudLight1 = harmonize(neutral1.atTone(95).toArgb(), themeColor),
cloudLight2 = harmonize(neutral1.atTone(85).toArgb(), themeColor),
rain = harmonize(0xFF64B5F6.toInt(), themeColor),
snow = harmonize(0xFFF5F5F5.toInt(), themeColor),
hail = harmonize(0xFFF5F5F5.toInt(), themeColor),
fog = harmonize(neutral1.atTone(95).toArgb(), themeColor),
wind = harmonize(neutral2.atTone(70).toArgb(), themeColor),
windDark = harmonize(neutral2.atTone(40).toArgb(), themeColor),
lightningBolt = harmonize(0xFFFFB300.toInt(), themeColor),
hot = harmonize(0xFFE57373.toInt(), themeColor),
cold = harmonize(0xFF4FC3F7.toInt(), themeColor),
)
}
}
@Composable
fun lightColors(): WeatherIconColors {
val context = LocalContext.current
val themeColor = MaterialTheme.colorScheme.primary
val neutral1 = MaterialTheme.colorScheme.outline
val neutral2 = MaterialTheme.colorScheme.outline
return remember(themeColor) {
WeatherIconColors(
sun = harmonize(0xFFFFB300.toInt(), themeColor),
moon = harmonize(0xFF9E9E9E.toInt(), themeColor),
cloudDark1 = harmonize(neutral1.atTone(30).toArgb(), themeColor),
cloudDark2 = harmonize(neutral1.atTone(20).toArgb(), themeColor),
cloudMedium1 = harmonize(neutral1.atTone(50).toArgb(), themeColor),
cloudMedium2 = harmonize(neutral1.atTone(40).toArgb(), themeColor),
cloudLight1 = harmonize(neutral1.atTone(85).toArgb(), themeColor),
cloudLight2 = harmonize(neutral1.atTone(75).toArgb(), themeColor),
rain = harmonize(0xFF1E88E5.toInt(), themeColor),
snow = harmonize(0xFFE0E0E0.toInt(), themeColor),
hail = harmonize(0xFFE0E0E0.toInt(), themeColor),
fog = harmonize(neutral1.atTone(85).toArgb(), themeColor),
wind = harmonize(neutral2.atTone(75).toArgb(), themeColor),
windDark = harmonize(neutral2.atTone(45).toArgb(), themeColor),
lightningBolt = harmonize(0xFFFFB300.toInt(), themeColor),
hot = harmonize(0xFFE53935.toInt(), themeColor),
cold = harmonize(0xFF039BE5.toInt(), themeColor),
)
}
}
@Composable
fun monochromeColors(baseColor: Color): WeatherIconColors {
val baseTone = Hct.fromInt(baseColor.toArgb()).tone.toInt()
return remember(baseColor) {
/**
* Adjusts the tone of a color.
* The tone is adjusted up or down to not overlap with the base color's tone.
*
* @param tone The tone to adjust.
* @return The adjusted tone.
*/
fun t(tone: Int): Int {
val upper = (baseTone + 15f).coerceAtMost(100f)
val lower = (baseTone - 15f).coerceAtLeast(0f)
val diff = upper - lower
val scale = (100f - diff) / 100f
val tn = tone * scale
if (tn > lower) {
return (tn + diff).toInt()
}
return tn.toInt()
}
WeatherIconColors(
sun = baseColor.atTone(t(90)),
moon = baseColor.atTone(t(90)),
cloudDark1 = baseColor.atTone(t(25)),
cloudDark2 = baseColor.atTone(t(15)),
cloudMedium1 = baseColor.atTone(t(45)),
cloudMedium2 = baseColor.atTone(t(35)),
cloudLight1 = baseColor.atTone(t(65)),
cloudLight2 = baseColor.atTone(t(55)),
rain = baseColor.atTone(t(55)),
snow = baseColor.atTone(t(100)),
hail = baseColor.atTone(t(90)),
fog = baseColor.atTone(t(60)),
wind = baseColor.atTone(t(65)),
windDark = baseColor.atTone(t(45)),
lightningBolt = baseColor.atTone(t(90)),
hot = baseColor.atTone(t(55)),
cold = baseColor.atTone(t(55)),
)
}
}
}
private fun harmonize(context: Context, @ColorInt baseColor: Int, themeColor: Color): Color {
private fun harmonize(@ColorInt baseColor: Int, themeColor: Color): Color {
return Color(Blend.harmonize(baseColor, themeColor.toArgb()))
}

View File

@@ -6,12 +6,14 @@ import android.content.Intent
import android.net.Uri
import android.text.format.DateUtils
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.expandHorizontally
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkHorizontally
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@@ -79,6 +81,8 @@ import de.mm20.launcher2.ui.component.MissingPermissionBanner
import de.mm20.launcher2.ui.component.Tooltip
import de.mm20.launcher2.ui.component.weather.AnimatedWeatherIcon
import de.mm20.launcher2.ui.component.weather.WeatherIcon
import de.mm20.launcher2.ui.component.weather.WeatherIconDefaults
import de.mm20.launcher2.ui.ktx.animateShapeAsState
import de.mm20.launcher2.ui.locals.LocalMeasurementSystem
import de.mm20.launcher2.ui.locals.LocalTimeFormat
import de.mm20.launcher2.ui.theme.transparency.transparency
@@ -187,21 +191,32 @@ fun WeatherWidget(widget: WeatherWidget) {
Surface(
color = MaterialTheme.colorScheme.surfaceContainer.copy(alpha = MaterialTheme.transparency.surface),
modifier = Modifier.fillMaxWidth()
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
shape = MaterialTheme.shapes.small,
) {
Column(
modifier = Modifier.padding(top = 12.dp, bottom = 12.dp)
modifier = Modifier.padding(top = 8.dp, bottom = 8.dp)
) {
WeatherTimeSelector(
forecasts = currentDayForecasts,
selectedForecast = forecast,
measurementSystem = measurementSystem,
timeFormat = timeFormat,
onTimeSelected = {
viewModel.selectForecast(it)
},
modifier = Modifier.padding(bottom = 12.dp)
)
AnimatedContent(
currentDayForecasts to forecast,
contentKey = { it.first },
transitionSpec = {
fadeIn() togetherWith fadeOut()
}
) { (fcs, fc) ->
WeatherTimeSelector(
forecasts = fcs,
selectedForecast = fc,
measurementSystem = measurementSystem,
timeFormat = timeFormat,
onTimeSelected = {
viewModel.selectForecast(it)
},
modifier = Modifier.padding(bottom = 8.dp)
)
}
selectedDayForecast?.let {
WeatherDaySelector(
days = dailyForecasts,
@@ -469,12 +484,16 @@ fun WeatherTimeSelector(
val context = LocalContext.current
val listState = rememberLazyListState()
val colors = WeatherIconDefaults.colors()
val selectedColors = WeatherIconDefaults.monochromeColors(MaterialTheme.colorScheme.secondary)
LazyRow(
state = listState,
modifier = modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(2.dp),
contentPadding = PaddingValues(start = 12.dp, end = 12.dp),
contentPadding = PaddingValues(start = 8.dp, end = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
itemsIndexed(forecasts, key = { idx, _ -> idx }) { idx, fc ->
@@ -482,22 +501,26 @@ fun WeatherTimeSelector(
val sm = MaterialTheme.shapes.small
val xs = MaterialTheme.shapes.extraSmall
Surface(
shape = when (idx) {
0 -> xs.copy(
topStart = sm.topStart,
bottomStart = sm.bottomStart
)
shape = animateShapeAsState(
when {
selected -> sm
idx == 0 -> xs.copy(
topStart = sm.topStart,
bottomStart = sm.bottomStart
)
forecasts.lastIndex -> xs.copy(
topEnd = sm.topEnd,
bottomEnd = sm.bottomEnd
)
idx == forecasts.lastIndex -> xs.copy(
topEnd = sm.topEnd,
bottomEnd = sm.bottomEnd
)
else -> MaterialTheme.shapes.extraSmall
},
else -> MaterialTheme.shapes.extraSmall
}
).value,
modifier = Modifier
.widthIn(min = 60.dp),
color = MaterialTheme.colorScheme.surface,
color = if (selected) MaterialTheme.colorScheme.secondary else MaterialTheme.colorScheme.surfaceBright,
contentColor = if (selected) MaterialTheme.colorScheme.onSecondary else MaterialTheme.colorScheme.onSurfaceVariant,
) {
Column(
modifier = Modifier
@@ -513,12 +536,12 @@ fun WeatherTimeSelector(
}
.padding(bottom = 4.dp),
icon = weatherIconById(fc.icon),
night = fc.night
night = fc.night,
colors = if (selected) selectedColors else colors
)
Text(
text = formatTime(context, fc.timestamp, timeFormat),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
softWrap = false,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
@@ -536,13 +559,15 @@ fun WeatherTimeSelector(
),
softWrap = false,
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Box(
modifier = Modifier
.alpha(1f - alpha)
.requiredSize(8.dp)
.background(MaterialTheme.colorScheme.primary, CircleShape)
.background(
MaterialTheme.colorScheme.onSecondary,
CircleShape
)
)
}
}
@@ -562,6 +587,9 @@ fun WeatherDaySelector(
val dateFormat = SimpleDateFormat("EEE")
val context = LocalContext.current
val colors = WeatherIconDefaults.colors()
val selectedColors = WeatherIconDefaults.monochromeColors(MaterialTheme.colorScheme.secondary)
val listState = rememberLazyListState()
LazyRow(
state = listState,
@@ -569,7 +597,7 @@ fun WeatherDaySelector(
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(2.dp),
verticalAlignment = Alignment.CenterVertically,
contentPadding = PaddingValues(start = 12.dp, end = 12.dp),
contentPadding = PaddingValues(start = 8.dp, end = 8.dp),
) {
itemsIndexed(days, key = { idx, _ -> idx }) { idx, day ->
val selected = day == selectedDay
@@ -577,20 +605,24 @@ fun WeatherDaySelector(
val sm = MaterialTheme.shapes.small
val xs = MaterialTheme.shapes.extraSmall
Surface(
shape = when (idx) {
0 -> xs.copy(
topStart = sm.topStart,
bottomStart = sm.bottomStart
)
shape = animateShapeAsState(
when {
selected -> sm
idx == 0 -> xs.copy(
topStart = sm.topStart,
bottomStart = sm.bottomStart
)
days.lastIndex -> xs.copy(
topEnd = sm.topEnd,
bottomEnd = sm.bottomEnd
)
idx == days.lastIndex -> xs.copy(
topEnd = sm.topEnd,
bottomEnd = sm.bottomEnd
)
else -> MaterialTheme.shapes.extraSmall
},
color = MaterialTheme.colorScheme.surface,
else -> MaterialTheme.shapes.extraSmall
}
).value,
color = if (selected) MaterialTheme.colorScheme.secondary else MaterialTheme.colorScheme.surfaceBright,
contentColor = if (selected) MaterialTheme.colorScheme.onSecondary else MaterialTheme.colorScheme.onSurfaceVariant,
) {
Row(
modifier = Modifier
@@ -598,7 +630,10 @@ fun WeatherDaySelector(
.padding(top = 4.dp, bottom = 4.dp, start = 4.dp, end = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
WeatherIcon(icon = weatherIconById(day.icon))
WeatherIcon(
icon = weatherIconById(day.icon),
colors = if (selected) selectedColors else colors
)
Text(
modifier = Modifier.padding(start = 8.dp),
text = "${dateFormat.format(day.timestamp)} " +
@@ -616,7 +651,6 @@ fun WeatherDaySelector(
),
softWrap = false,
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
val spec = MaterialTheme.motionScheme.fastSpatialSpec<IntSize>()
AnimatedVisibility(
@@ -628,7 +662,10 @@ fun WeatherDaySelector(
modifier = Modifier
.padding(start = 8.dp)
.size(8.dp)
.background(MaterialTheme.colorScheme.primary, CircleShape)
.background(
MaterialTheme.colorScheme.onSecondary,
CircleShape
)
)
}
}
@@ -678,26 +715,27 @@ private fun windDirectionAsWord(direction: Double): String {
private fun weatherIconById(id: Int): WeatherIcon {
return when (id) {
Forecast.CLEAR -> WeatherIcon.Clear
Forecast.CLOUDY -> WeatherIcon.Cloudy
Forecast.COLD -> WeatherIcon.Cold
Forecast.DRIZZLE -> WeatherIcon.Drizzle
Forecast.OVERCAST -> WeatherIcon.Overcast
Forecast.EXTREME_COLD -> WeatherIcon.ExtremeHeat
Forecast.LIGHT_RAIN -> WeatherIcon.LightRain
Forecast.HAZE -> WeatherIcon.Haze
Forecast.FOG -> WeatherIcon.Fog
Forecast.HAIL -> WeatherIcon.Hail
Forecast.HEAVY_THUNDERSTORM -> WeatherIcon.HeavyThunderstorm
Forecast.HEAVY_THUNDERSTORM_WITH_RAIN -> WeatherIcon.HeavyThunderstormWithRain
Forecast.HOT -> WeatherIcon.Hot
Forecast.MOSTLY_CLOUDY -> WeatherIcon.MostlyCloudy
Forecast.EXTREME_HEAT -> WeatherIcon.ExtremeHeat
Forecast.PARTLY_CLOUDY -> WeatherIcon.PartlyCloudy
Forecast.SHOWERS -> WeatherIcon.Showers
Forecast.RAIN -> WeatherIcon.Rain
Forecast.HEAVY_RAIN -> WeatherIcon.HeavyRain
Forecast.SLEET -> WeatherIcon.Sleet
Forecast.SNOW -> WeatherIcon.Snow
Forecast.STORM -> WeatherIcon.Storm
Forecast.THUNDER -> WeatherIcon.Thunder
Forecast.THUNDERSTORM -> WeatherIcon.Thunderstorm
Forecast.THUNDERSTORM_WITH_RAIN -> WeatherIcon.ThunderstormWithRain
Forecast.WIND -> WeatherIcon.Wind
Forecast.BROKEN_CLOUDS -> WeatherIcon.BrokenClouds
else -> WeatherIcon.None
Forecast.MOSTLY_CLOUDY -> WeatherIcon.PartlyCloudy
Forecast.STORM -> WeatherIcon.Wind
Forecast.BROKEN_CLOUDS -> WeatherIcon.PartlyCloudy
Forecast.HEAVY_THUNDERSTORM -> WeatherIcon.Thunder
Forecast.HEAVY_THUNDERSTORM_WITH_RAIN -> WeatherIcon.Thunderstorm
else -> WeatherIcon.Unknown
}
}