Adjust weather widget colors
This commit is contained in:
parent
9a9b13ae90
commit
a917a00eb1
@ -1,6 +1,7 @@
|
||||
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
|
||||
@ -10,6 +11,8 @@ 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
|
||||
|
||||
|
||||
data class WeatherIconColors(
|
||||
@ -38,31 +41,35 @@ object WeatherIconDefaults {
|
||||
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, R.color.weather_sun, themeColor),
|
||||
moon = harmonize(context, R.color.weather_moon, themeColor),
|
||||
cloudDark1 = harmonize(context, R.color.weather_cloud_dark_1, themeColor),
|
||||
cloudDark2 = harmonize(context, R.color.weather_cloud_dark_2, themeColor),
|
||||
cloudMedium1 = harmonize(context, R.color.weather_cloud_medium_1, themeColor),
|
||||
cloudMedium2 = harmonize(context, R.color.weather_cloud_medium_2, themeColor),
|
||||
cloudLight1 = harmonize(context, R.color.weather_cloud_light_1, themeColor),
|
||||
cloudLight2 = harmonize(context, R.color.weather_cloud_light_2, themeColor),
|
||||
rain = harmonize(context, R.color.weather_rain, themeColor),
|
||||
snow = harmonize(context, R.color.weather_snow, themeColor),
|
||||
hail = harmonize(context, R.color.weather_hail, themeColor),
|
||||
fog = harmonize(context, R.color.weather_fog, themeColor),
|
||||
wind = harmonize(context, R.color.weather_wind, themeColor),
|
||||
windDark = harmonize(context, R.color.weather_wind_dark, themeColor),
|
||||
lightningBolt = harmonize(context, R.color.weather_lightning_bolt, themeColor),
|
||||
hot = harmonize(context, R.color.weather_hot, themeColor),
|
||||
cold = harmonize(context, R.color.weather_cold, themeColor),
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun harmonize(context: Context, @ColorRes baseColor: Int, themeColor: Color): Color {
|
||||
return Color(Blend.harmonize(context.getColor(baseColor), themeColor.toArgb()))
|
||||
private fun harmonize(context: Context, @ColorInt baseColor: Int, themeColor: Color): Color {
|
||||
return Color(Blend.harmonize(baseColor, themeColor.toArgb()))
|
||||
}
|
||||
@ -8,22 +8,30 @@ import android.text.format.DateUtils
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
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.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.CornerSize
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.OpenInNew
|
||||
@ -35,7 +43,6 @@ import androidx.compose.material.icons.rounded.MyLocation
|
||||
import androidx.compose.material.icons.rounded.North
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
@ -50,16 +57,17 @@ 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.alpha
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.onPlaced
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
@ -78,7 +86,6 @@ 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.ktx.blendIntoViewScale
|
||||
import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme
|
||||
import de.mm20.launcher2.weather.DailyForecast
|
||||
import de.mm20.launcher2.weather.Forecast
|
||||
@ -180,11 +187,11 @@ fun WeatherWidget(widget: WeatherWidget) {
|
||||
val currentDayForecasts by viewModel.currentDayForecasts
|
||||
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = LocalTransparencyScheme.current.surface),
|
||||
color = MaterialTheme.colorScheme.surfaceContainer.copy(alpha = LocalTransparencyScheme.current.surface),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(top = 8.dp, bottom = 12.dp)
|
||||
modifier = Modifier.padding(top = 12.dp, bottom = 12.dp)
|
||||
) {
|
||||
WeatherTimeSelector(
|
||||
forecasts = currentDayForecasts,
|
||||
@ -193,11 +200,7 @@ fun WeatherWidget(widget: WeatherWidget) {
|
||||
onTimeSelected = {
|
||||
viewModel.selectForecast(it)
|
||||
},
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
Divider(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.12f),
|
||||
modifier = Modifier.padding(bottom = 12.dp)
|
||||
)
|
||||
selectedDayForecast?.let {
|
||||
WeatherDaySelector(
|
||||
@ -207,7 +210,6 @@ fun WeatherWidget(widget: WeatherWidget) {
|
||||
viewModel.selectDay(it)
|
||||
},
|
||||
imperialUnits = imperialUnits,
|
||||
modifier = Modifier.padding(top = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -453,49 +455,74 @@ fun WeatherTimeSelector(
|
||||
state = listState,
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
contentPadding = PaddingValues(start = 16.dp, end = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
contentPadding = PaddingValues(start = 12.dp, end = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
itemsIndexed(forecasts, key = { idx, _ -> idx }) { idx, fc ->
|
||||
val backgroundAlpha = if (fc == selectedForecast) 0.2f else 0.0f
|
||||
val selected = fc == selectedForecast
|
||||
val sm = MaterialTheme.shapes.small
|
||||
val xs = MaterialTheme.shapes.extraSmall
|
||||
Surface(
|
||||
shape = MaterialTheme.shapes.extraSmall,
|
||||
modifier = Modifier
|
||||
.widthIn(min = 56.dp)
|
||||
.graphicsLayer {
|
||||
alpha = listState.layoutInfo.blendIntoViewScale(idx, 2f)
|
||||
shape = when (idx) {
|
||||
0 -> xs.copy(
|
||||
topStart = sm.topStart,
|
||||
bottomStart = sm.bottomStart
|
||||
)
|
||||
|
||||
forecasts.lastIndex -> xs.copy(
|
||||
topEnd = sm.topEnd,
|
||||
bottomEnd = sm.bottomEnd
|
||||
)
|
||||
|
||||
else -> MaterialTheme.shapes.extraSmall
|
||||
},
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(backgroundAlpha)
|
||||
modifier = Modifier
|
||||
.widthIn(min = 60.dp),
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clickable { onTimeSelected(idx) }
|
||||
.padding(4.dp),
|
||||
.padding(start = 4.dp, end = 4.dp, top = 8.dp, bottom = 4.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
WeatherIcon(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.semantics {
|
||||
contentDescription = fc.condition
|
||||
},
|
||||
}
|
||||
.padding(bottom = 4.dp),
|
||||
icon = weatherIconById(fc.icon),
|
||||
night = fc.night
|
||||
)
|
||||
Text(
|
||||
text = dateFormat.format(fc.timestamp),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
softWrap = false,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
Box(
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
val alpha by animateFloatAsState(if (selected) 0f else 1f)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.alpha(alpha),
|
||||
text = "${convertTemperature(imperialUnits, fc.temperature)}°",
|
||||
softWrap = false,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.alpha(1f - alpha)
|
||||
.requiredSize(8.dp)
|
||||
.background(MaterialTheme.colorScheme.primary, CircleShape)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -517,44 +544,60 @@ fun WeatherDaySelector(
|
||||
state = listState,
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(start = 16.dp, end = 16.dp),
|
||||
contentPadding = PaddingValues(start = 12.dp, end = 12.dp),
|
||||
) {
|
||||
itemsIndexed(days, key = { idx, _ -> idx }) { idx, day ->
|
||||
val backgroundAlpha = if (day == selectedDay) 0.2f else 0.0f
|
||||
val selected = day == selectedDay
|
||||
|
||||
val sm = MaterialTheme.shapes.small
|
||||
val xs = MaterialTheme.shapes.extraSmall
|
||||
Surface(
|
||||
modifier = Modifier.graphicsLayer {
|
||||
alpha = listState.layoutInfo.blendIntoViewScale(idx, 0.5f)
|
||||
shape = when (idx) {
|
||||
0 -> xs.copy(
|
||||
topStart = sm.topStart,
|
||||
bottomStart = sm.bottomStart
|
||||
)
|
||||
|
||||
days.lastIndex -> xs.copy(
|
||||
topEnd = sm.topEnd,
|
||||
bottomEnd = sm.bottomEnd
|
||||
)
|
||||
|
||||
else -> MaterialTheme.shapes.extraSmall
|
||||
},
|
||||
shape = MaterialTheme.shapes.extraSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(backgroundAlpha)
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable { onDaySelected(idx) }
|
||||
.padding(4.dp),
|
||||
horizontalArrangement = Arrangement.SpaceAround,
|
||||
.padding(top = 4.dp, bottom = 4.dp, start = 4.dp, end = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
WeatherIcon(icon = weatherIconById(day.icon))
|
||||
Text(
|
||||
text = dateFormat.format(day.timestamp),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
softWrap = false,
|
||||
modifier = Modifier.padding(start = 12.dp, end = 6.dp)
|
||||
)
|
||||
Text(
|
||||
text = "${
|
||||
convertTemperature(
|
||||
imperialUnits,
|
||||
day.minTemp
|
||||
)
|
||||
}° / ${convertTemperature(imperialUnits, day.maxTemp)}°",
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
text = "${dateFormat.format(day.timestamp)} " +
|
||||
"${convertTemperature(imperialUnits, day.minTemp)}° / " +
|
||||
"${convertTemperature(imperialUnits, day.maxTemp)}°",
|
||||
softWrap = false,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
val spec = MaterialTheme.motionScheme.fastSpatialSpec<IntSize>()
|
||||
AnimatedVisibility(
|
||||
selected,
|
||||
enter = fadeIn() + expandHorizontally(spec),
|
||||
exit = fadeOut() + shrinkHorizontally(spec),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(start = 8.dp)
|
||||
.size(8.dp)
|
||||
.background(MaterialTheme.colorScheme.primary, CircleShape)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user