Rename weather icons, add monochrome variant
This commit is contained in:
@@ -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,
|
||||
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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()))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user