mirror of
https://github.com/PranshulGG/WeatherMaster.git
synced 2026-10-23 19:43:40 -05:00
implement WindScreen, add click navigation to WindBlock closes #878
This commit is contained in:
1 parent
d8b7d6b351
commit
798fa10171
3 files changed
+201
-4
No files matched your search
+195
@@ -0,0 +1,195 @@
|
||||
package com.pranshulgg.weather_master_app.feature.blocks.screens
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.pranshulgg.weather_master_app.R
|
||||
import com.pranshulgg.weather_master_app.core.model.weather.WindSpeedUnit
|
||||
import com.pranshulgg.weather_master_app.core.model.weather.toName
|
||||
import com.pranshulgg.weather_master_app.core.model.weather.wind.WindDirection
|
||||
import com.pranshulgg.weather_master_app.core.prefs.LocalAppPrefs
|
||||
import com.pranshulgg.weather_master_app.core.ui.components.Gap
|
||||
import com.pranshulgg.weather_master_app.core.ui.components.LargeTopBarScaffold
|
||||
import com.pranshulgg.weather_master_app.core.ui.components.NavigateUpBtn
|
||||
import com.pranshulgg.weather_master_app.core.utils.formatters.to12HourTimeString
|
||||
import com.pranshulgg.weather_master_app.core.utils.formatters.to24HourTimeString
|
||||
import com.pranshulgg.weather_master_app.core.utils.formatters.toDateString
|
||||
import com.pranshulgg.weather_master_app.core.utils.weather.forecast.findMatchingHourly
|
||||
import com.pranshulgg.weather_master_app.feature.blocks.BlocksScreenViewModel
|
||||
import com.pranshulgg.weather_master_app.feature.blocks.components.AboutCard
|
||||
import com.pranshulgg.weather_master_app.feature.blocks.components.AboutCardText
|
||||
import com.pranshulgg.weather_master_app.feature.blocks.components.MatBarChart
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
@Composable
|
||||
fun WindScreen(navController: NavController, index: Int = 0, locationId: String) {
|
||||
|
||||
val viewModel: BlocksScreenViewModel = hiltViewModel()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.getUnitsOnce()
|
||||
viewModel.getWeather(locationId)
|
||||
}
|
||||
|
||||
|
||||
val uiState = viewModel.uiState.value
|
||||
val weather = uiState.weather
|
||||
val hourly = weather?.hourly ?: return
|
||||
val units = uiState.units
|
||||
val context = LocalContext.current
|
||||
|
||||
|
||||
val fullDayHourly =
|
||||
findMatchingHourly(hourly, weather.daily[index].time, weather.location.source)
|
||||
|
||||
val date = toDateString(weather.daily[index].time, weather.location.timezone)
|
||||
|
||||
val maxWind = weather.daily[index].windSpeed
|
||||
val minWind = fullDayHourly.minOf { it.windSpeed ?: 0.0 }
|
||||
val dayDirection = weather.daily[index].windDirection
|
||||
|
||||
LargeTopBarScaffold(
|
||||
title = stringResource(R.string.weather_wind),
|
||||
navigationIcon = { NavigateUpBtn(navController) },
|
||||
actions = {
|
||||
Text(
|
||||
date,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(end = 16.dp)
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(paddingValues)
|
||||
) {
|
||||
WindBarChart(
|
||||
max = maxWind!!,
|
||||
min = minWind,
|
||||
times = fullDayHourly.map { it.time },
|
||||
values = fullDayHourly.map { it.windSpeed ?: 0.0 },
|
||||
directions = fullDayHourly.map { it.windDirection },
|
||||
zoneId = weather.location.timezone,
|
||||
unit = units.windUnit,
|
||||
context = context,
|
||||
dayDirection = dayDirection
|
||||
)
|
||||
Gap(14.dp)
|
||||
AboutCard {
|
||||
AboutCardText(stringResource(R.string.weather_about_windspeed))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun WindBarChart(
|
||||
max: Double,
|
||||
min: Double,
|
||||
times: List<Long>,
|
||||
values: List<Double>,
|
||||
directions: List<WindDirection?>,
|
||||
dayDirection: WindDirection?,
|
||||
zoneId: String,
|
||||
unit: WindSpeedUnit,
|
||||
context: Context
|
||||
) {
|
||||
|
||||
|
||||
val timeStartIndex = if (times.size == 12) 0 else 6
|
||||
val is24hr = LocalAppPrefs.current.is24HrTimeFormat
|
||||
|
||||
val bottomValues = times.slice(timeStartIndex..times.lastIndex step 6)
|
||||
|
||||
val barHeights = values.map {
|
||||
val percentage = ((it.minus(min)).div((max - min))).times(100)
|
||||
max((percentage.div(100)).times(170).roundToInt(), 5)
|
||||
}
|
||||
|
||||
val sideValues =
|
||||
(min.roundToInt() until max.roundToInt()).sortedByDescending { it }
|
||||
.map { "${WindSpeedUnit.KPH.convert(it.toDouble(), unit)?.roundToInt()}" }
|
||||
|
||||
val directions = directions.map {
|
||||
WindDirection.toDegrees(it ?: WindDirection.N)
|
||||
}
|
||||
|
||||
|
||||
val steps = 7
|
||||
|
||||
val topValues = (0 until steps).map { i ->
|
||||
val index = ((directions.size - 1) * i.toDouble() / (steps - 1)).toInt()
|
||||
directions[index]
|
||||
}
|
||||
|
||||
val barColor = values.map {
|
||||
when {
|
||||
it < 10 -> Color(0xFF42A5F5)
|
||||
it < 20 -> Color(0xFF66BB6A)
|
||||
it < 40 -> Color(0xFFFDD835)
|
||||
it < 60 -> Color(0xFFFB8C00)
|
||||
else -> Color(0xFFC62828)
|
||||
}
|
||||
}
|
||||
MatBarChart(
|
||||
topValues = topValues.map {
|
||||
{
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.weather_wind_arrow_dominant),
|
||||
contentDescription = "",
|
||||
modifier = Modifier
|
||||
.size(18.dp)
|
||||
.rotate(it.toFloat()),
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary)
|
||||
)
|
||||
}
|
||||
},
|
||||
bottomValues = bottomValues.map {
|
||||
{
|
||||
|
||||
val time = if (is24hr) to24HourTimeString(
|
||||
it,
|
||||
zoneId
|
||||
) else to12HourTimeString(it, zoneId)
|
||||
|
||||
|
||||
Text(time, style = MaterialTheme.typography.labelMedium)
|
||||
}
|
||||
},
|
||||
sideValues = sideValues,
|
||||
values = values,
|
||||
barHeights = barHeights,
|
||||
barColor = barColor,
|
||||
headerValue = "${WindSpeedUnit.KPH.convert(max, unit)?.roundToInt()}",
|
||||
headerSuffix = "${unit.toName(context, true)} • $dayDirection",
|
||||
headerTitle = stringResource(R.string.weather_max_for_the_day),
|
||||
chartHeight = 250.dp
|
||||
)
|
||||
}
|
||||
+2
-2
@@ -231,8 +231,8 @@ fun WeatherBlocks(
|
||||
context,
|
||||
isDaily,
|
||||
dailyIndex,
|
||||
units
|
||||
)
|
||||
units,
|
||||
onClickBlock = { onClickBlock(NavRoutes.WIND) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -41,7 +41,8 @@ fun WindBlock(
|
||||
context: Context,
|
||||
isDaily: Boolean,
|
||||
dailyIndex: Int,
|
||||
units: WeatherUnits
|
||||
units: WeatherUnits,
|
||||
onClickBlock: () -> Unit
|
||||
) {
|
||||
|
||||
val windDirection = if (isDaily) {
|
||||
@@ -57,7 +58,8 @@ fun WindBlock(
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
shape = RoundedCornerShape(ShapeRadius.Full),
|
||||
shadowElevation = ShadowElevation.level2
|
||||
shadowElevation = ShadowElevation.level2,
|
||||
onClick = onClickBlock
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
|
||||
Reference in new issue
Block a user