Many bug fixes, nearing release

This commit is contained in:
Henry Hiles 2023-12-27 08:31:49 -05:00
parent 92bad19a90
commit 985382fa1b
18 changed files with 164 additions and 129 deletions

View file

@ -1,5 +1,5 @@
plugins { plugins {
kotlin("plugin.serialization") version "1.9.21" kotlin("plugin.serialization")
id("com.android.application") id("com.android.application")
id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.android")
id("kotlin-kapt") id("kotlin-kapt")

View file

@ -6,7 +6,7 @@ import com.henryhiles.qweather.domain.geocoding.GeocodingData
fun GeocodingDto.toGeocodingData(): List<GeocodingData> { fun GeocodingDto.toGeocodingData(): List<GeocodingData> {
return results.map { return results.map {
GeocodingData( GeocodingData(
location = "${it.city}, ${it.admin}, ${it.country}", location = "${if(it.name == it.country) "" else "${it.name}, "}${if(it.admin == null) "" else "${it.admin}, "}${it.country}",
longitude = it.longitude, longitude = it.longitude,
latitude = it.latitude, latitude = it.latitude,
) )

View file

@ -35,7 +35,9 @@ fun DailyWeatherDataDto.toDailyWeatherData(): List<DailyWeatherData> {
temperatureMax = temperatureMax[index].roundToInt(), temperatureMax = temperatureMax[index].roundToInt(),
temperatureMin = temperatureMin[index].roundToInt(), temperatureMin = temperatureMin[index].roundToInt(),
precipitationProbabilityMax = precipitationProbabilityMax.getOrNull(index), precipitationProbabilityMax = precipitationProbabilityMax.getOrNull(index),
windSpeedMax = windSpeedMax[index].roundToInt() windSpeedMax = windSpeedMax[index].roundToInt(),
sunrise = LocalDateTime.parse(sunrise[index]),
sunset = LocalDateTime.parse(sunset[index]),
) )
} }
} }
@ -49,8 +51,5 @@ fun WeatherDto.toHourlyWeatherInfo(): HourlyWeatherInfo {
return HourlyWeatherInfo( return HourlyWeatherInfo(
weatherData = weatherDataMap, weatherData = weatherDataMap,
currentWeatherData = currentWeatherData, currentWeatherData = currentWeatherData,
highTemperature = weatherDataMap.maxBy { it.temperature }.temperature,
lowTemperature = weatherDataMap.minBy { it.temperature }.temperature,
precipitationProbability = weatherDataMap.maxBy { it.precipitationProbability ?: 0}.precipitationProbability
) )
} }

View file

@ -9,6 +9,8 @@ data class DailyWeatherDataDto(
val date: List<String>, val date: List<String>,
@SerialName("weathercode") @SerialName("weathercode")
val weatherCode: List<Int>, val weatherCode: List<Int>,
val sunrise: List<String>,
val sunset: List<String>,
@SerialName("precipitation_probability_max") @SerialName("precipitation_probability_max")
val precipitationProbabilityMax: List<Int?>, val precipitationProbabilityMax: List<Int?>,
@SerialName("precipitation_sum") @SerialName("precipitation_sum")

View file

@ -3,6 +3,7 @@ package com.henryhiles.qweather.domain.remote
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
data class GeocodingDto( data class
GeocodingDto(
val results: List<GeocodingLocationDto> = listOf() val results: List<GeocodingLocationDto> = listOf()
) )

View file

@ -5,11 +5,10 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class GeocodingLocationDto( data class GeocodingLocationDto(
@SerialName("name") val name: String,
val city: String,
val country: String, val country: String,
@SerialName("admin1") @SerialName("admin1")
val admin: String, val admin: String? = null,
val latitude: Float, val latitude: Float,
val longitude: Float val longitude: Float
) )

View file

@ -5,7 +5,7 @@ import retrofit2.http.Headers
import retrofit2.http.Query import retrofit2.http.Query
const val DAILY = const val DAILY =
"daily=weathercode,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min,precipitation_sum,precipitation_probability_max,windspeed_10m_max" "daily=weathercode,sunrise,sunset,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min,precipitation_sum,precipitation_probability_max,windspeed_10m_max"
const val HOURLY = const val HOURLY =
"hourly=temperature_2m,apparent_temperature,precipitation_probability,weathercode,windspeed_10m" "hourly=temperature_2m,apparent_temperature,precipitation_probability,weathercode,windspeed_10m"
const val TIMEZONE = "timezone=auto" const val TIMEZONE = "timezone=auto"

View file

@ -0,0 +1,14 @@
package com.henryhiles.qweather.domain.util
import com.henryhiles.qweather.domain.weather.DailyWeatherData
import com.henryhiles.qweather.domain.weather.HourlyWeatherData
fun getIcon(
data: HourlyWeatherData,
dailyData: DailyWeatherData,
): Int {
return if (data.time.isAfter(dailyData.sunrise) && data.time.isBefore(
dailyData.sunset
)
) data.weatherType.iconRes else data.weatherType.nightIconRes
}

View file

@ -1,10 +1,13 @@
package com.henryhiles.qweather.domain.weather package com.henryhiles.qweather.domain.weather
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime
data class DailyWeatherData( data class DailyWeatherData(
val date: LocalDate, val date: LocalDate,
val weatherType: WeatherType, val weatherType: WeatherType,
val sunrise: LocalDateTime,
val sunset: LocalDateTime,
val temperatureMax: Int, val temperatureMax: Int,
val temperatureMin: Int, val temperatureMin: Int,
val apparentTemperatureMax: Int, val apparentTemperatureMax: Int,

View file

@ -1,6 +1,5 @@
package com.henryhiles.qweather.domain.weather package com.henryhiles.qweather.domain.weather
import androidx.annotation.DrawableRes
import java.time.LocalDateTime import java.time.LocalDateTime
data class HourlyWeatherData( data class HourlyWeatherData(
@ -10,5 +9,4 @@ data class HourlyWeatherData(
val weatherType: WeatherType, val weatherType: WeatherType,
val precipitationProbability: Int?, val precipitationProbability: Int?,
val windSpeed: Int, val windSpeed: Int,
@DrawableRes val icon: Int = if(time.hour < 8 || time.hour >= 19) weatherType.nightIconRes else weatherType.iconRes
) )

View file

@ -3,7 +3,4 @@ package com.henryhiles.qweather.domain.weather
data class HourlyWeatherInfo( data class HourlyWeatherInfo(
val weatherData: List<HourlyWeatherData>, val weatherData: List<HourlyWeatherData>,
val currentWeatherData: HourlyWeatherData?, val currentWeatherData: HourlyWeatherData?,
val highTemperature: Int,
val lowTemperature: Int,
val precipitationProbability: Int?
) )

View file

@ -17,62 +17,62 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.henryhiles.qweather.domain.util.getIcon
import com.henryhiles.qweather.domain.weather.DailyWeatherData
import com.henryhiles.qweather.domain.weather.HourlyWeatherData import com.henryhiles.qweather.domain.weather.HourlyWeatherData
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@Composable @Composable
fun WeatherCard(hour: HourlyWeatherData?, modifier: Modifier = Modifier) { fun WeatherCard(hour: HourlyWeatherData, dailyData: DailyWeatherData, modifier: Modifier = Modifier) {
hour?.let { val formattedTime = remember(hour) {
val formattedTime = remember(it) { hour.time.format(DateTimeFormatter.ofPattern("HH:mm"))
it.time.format(DateTimeFormatter.ofPattern("HH:mm")) }
} Card(
Card( shape = RoundedCornerShape(8.dp),
shape = RoundedCornerShape(8.dp), modifier = modifier
modifier = modifier ) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Column( Row(
modifier = Modifier modifier = Modifier.fillMaxWidth()
.fillMaxWidth()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Row( Text(
modifier = Modifier.fillMaxWidth() text = formattedTime,
) { style = MaterialTheme.typography.headlineSmall,
Text( )
text = formattedTime, }
style = MaterialTheme.typography.headlineSmall, Spacer(modifier = Modifier.height(16.dp))
) Image(
} painter = painterResource(id = getIcon(hour, dailyData)),
Spacer(modifier = Modifier.height(16.dp)) contentDescription = "Image of ${hour.weatherType.weatherDesc}",
Image( modifier = Modifier.height(140.dp),
painter = painterResource(id = it.icon), contentScale = ContentScale.FillHeight
contentDescription = "Image of ${it.weatherType.weatherDesc}", )
modifier = Modifier.height(140.dp), Spacer(modifier = Modifier.height(16.dp))
contentScale = ContentScale.FillHeight Text(text = "${hour.temperature}°C", fontSize = 50.sp)
Spacer(modifier = Modifier.height(16.dp))
Text(text = "${hour.weatherType.weatherDesc} - Feels like ${hour.apparentTemperature}°C", fontSize = 20.sp)
Spacer(modifier = Modifier.height(32.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
WeatherDataDisplay(
value = hour.precipitationProbability,
unit = "%",
icon = Icons.Outlined.WaterDrop,
description = "Chance of precipitation"
)
WeatherDataDisplay(
value = hour.windSpeed,
unit = "km/h",
icon = Icons.Outlined.WindPower,
description = "Wind Speed",
) )
Spacer(modifier = Modifier.height(16.dp))
Text(text = "${it.temperature}°C", fontSize = 50.sp)
Spacer(modifier = Modifier.height(16.dp))
Text(text = "${it.weatherType.weatherDesc} - Feels like ${it.apparentTemperature}°C", fontSize = 20.sp)
Spacer(modifier = Modifier.height(32.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
WeatherDataDisplay(
value = it.precipitationProbability,
unit = "%",
icon = Icons.Outlined.WaterDrop,
description = "Chance of precipitation"
)
WeatherDataDisplay(
value = it.windSpeed,
unit = "km/h",
icon = Icons.Outlined.WindPower,
description = "Wind Speed",
)
}
} }
} }
} }

View file

@ -6,12 +6,14 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.henryhiles.qweather.domain.weather.DailyWeatherData
import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherState import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherState
import java.time.LocalDateTime import java.time.LocalDateTime
@Composable @Composable
fun WeatherForecast( fun WeatherForecast(
state: HourlyWeatherState, state: HourlyWeatherState,
dailyData: DailyWeatherData,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onChangeSelected: (Int) -> Unit onChangeSelected: (Int) -> Unit
) { ) {
@ -20,10 +22,12 @@ fun WeatherForecast(
items(it.subList(LocalDateTime.now().hour, it.size)) { items(it.subList(LocalDateTime.now().hour, it.size)) {
WeatherHour( WeatherHour(
data = it, data = it,
dailyData = dailyData,
modifier = Modifier modifier = Modifier
.padding(horizontal = 8.dp) .padding(horizontal = 8.dp)
) { onChangeSelected(it.time.hour) } ) { onChangeSelected(it.time.hour) }
} }
} }
} }
} }

View file

@ -13,12 +13,15 @@ import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.henryhiles.qweather.domain.util.getIcon
import com.henryhiles.qweather.domain.weather.DailyWeatherData
import com.henryhiles.qweather.domain.weather.HourlyWeatherData import com.henryhiles.qweather.domain.weather.HourlyWeatherData
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@Composable @Composable
fun WeatherHour( fun WeatherHour(
data: HourlyWeatherData, data: HourlyWeatherData,
dailyData: DailyWeatherData,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onChangeSelected: () -> Unit onChangeSelected: () -> Unit
) { ) {
@ -38,7 +41,9 @@ fun WeatherHour(
) { ) {
Text(text = formattedTime) Text(text = formattedTime)
Image( Image(
painter = painterResource(id = it.icon), painter = painterResource(
id = getIcon(it, dailyData)
),
contentDescription = "Image of ${it.weatherType.weatherDesc}", contentDescription = "Image of ${it.weatherType.weatherDesc}",
modifier = Modifier.width(40.dp) modifier = Modifier.width(40.dp)
) )

View file

@ -15,60 +15,58 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.henryhiles.qweather.R import com.henryhiles.qweather.R
import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherState import com.henryhiles.qweather.domain.weather.DailyWeatherData
import com.henryhiles.qweather.presentation.screenmodel.LocationPreferenceManager import com.henryhiles.qweather.presentation.screenmodel.LocationPreferenceManager
import org.koin.compose.koinInject import org.koin.compose.koinInject
@Composable @Composable
fun WeatherToday(state: HourlyWeatherState) { fun WeatherToday(data: DailyWeatherData) {
val locationPreferenceManager: LocationPreferenceManager = koinInject() val locationPreferenceManager: LocationPreferenceManager = koinInject()
state.hourlyWeatherInfo?.let { Card(
Card( shape = RoundedCornerShape(8.dp),
shape = RoundedCornerShape(8.dp), ) {
Column(
modifier = Modifier.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Column( Text(
modifier = Modifier.padding(8.dp), text = stringResource(id = R.string.today_in, with(locationPreferenceManager) {
horizontalAlignment = Alignment.CenterHorizontally locations.getOrNull(selectedIndex)?.location?.split(",")?.first()
?: stringResource(id = R.string.unknown)
}),
style = MaterialTheme.typography.headlineSmall
)
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier
.height(24.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly,
) { ) {
Text( WeatherDataDisplay(
text = stringResource(id = R.string.today_in, with(locationPreferenceManager) { value = data.temperatureMax,
locations.getOrNull(selectedIndex)?.location?.split(",")?.first() unit = "°C",
?: stringResource(id = R.string.unknown) icon = Icons.Default.ArrowUpward,
}), description = stringResource(R.string.weather_high, data.temperatureMax)
style = MaterialTheme.typography.headlineSmall )
WeatherDataDisplay(
value = data.temperatureMin,
unit = "°C",
icon = Icons.Default.ArrowDownward,
description = stringResource(id = R.string.weather_low, data.temperatureMin)
)
WeatherDataDisplay(
value = data.precipitationProbabilityMax,
unit = "%",
icon = Icons.Outlined.WaterDrop,
description = data.precipitationProbabilityMax?.let {
stringResource(
id = R.string.weather_precipitation,
it
)
} ?: stringResource(id = R.string.unknown)
) )
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier
.height(24.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly,
) {
WeatherDataDisplay(
value = it.highTemperature,
unit = "°C",
icon = Icons.Default.ArrowUpward,
description = stringResource(R.string.weather_high, it.highTemperature)
)
WeatherDataDisplay(
value = it.lowTemperature,
unit = "°C",
icon = Icons.Default.ArrowDownward,
description = stringResource(id = R.string.weather_low, it.lowTemperature)
)
WeatherDataDisplay(
value = it.precipitationProbability,
unit = "%",
icon = Icons.Outlined.WaterDrop,
description = it.precipitationProbability?.let {
stringResource(
id = R.string.weather_precipitation,
it
)
} ?: stringResource(id = R.string.unknown)
)
}
} }
} }
} }

View file

@ -21,6 +21,7 @@ import com.henryhiles.qweather.domain.util.NavigationTab
import com.henryhiles.qweather.presentation.components.weather.WeatherCard import com.henryhiles.qweather.presentation.components.weather.WeatherCard
import com.henryhiles.qweather.presentation.components.weather.WeatherForecast import com.henryhiles.qweather.presentation.components.weather.WeatherForecast
import com.henryhiles.qweather.presentation.components.weather.WeatherToday import com.henryhiles.qweather.presentation.components.weather.WeatherToday
import com.henryhiles.qweather.presentation.screenmodel.DailyWeatherScreenModel
import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherScreenModel import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherScreenModel
object TodayTab : NavigationTab { object TodayTab : NavigationTab {
@ -42,9 +43,11 @@ object TodayTab : NavigationTab {
@Composable @Composable
override fun Content() { override fun Content() {
val weatherViewModel = getScreenModel<HourlyWeatherScreenModel>() val weatherViewModel = getScreenModel<HourlyWeatherScreenModel>()
val dailyWeatherViewModel = getScreenModel<DailyWeatherScreenModel>()
LaunchedEffect(key1 = weatherViewModel.locationPreferenceManager.selectedIndex) { LaunchedEffect(key1 = weatherViewModel.locationPreferenceManager.selectedIndex) {
weatherViewModel.loadWeatherInfo() weatherViewModel.loadWeatherInfo()
dailyWeatherViewModel.loadWeatherInfo()
} }
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
@ -56,6 +59,7 @@ object TodayTab : NavigationTab {
) )
) )
} }
weatherViewModel.state.error != null -> { weatherViewModel.state.error != null -> {
AlertDialog( AlertDialog(
onDismissRequest = {}, onDismissRequest = {},
@ -74,6 +78,7 @@ object TodayTab : NavigationTab {
}, },
) )
} }
else -> { else -> {
Column( Column(
modifier = Modifier modifier = Modifier
@ -81,15 +86,24 @@ object TodayTab : NavigationTab {
.padding(16.dp), .padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp) verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
WeatherToday(state = weatherViewModel.state) dailyWeatherViewModel.state.dailyWeatherData?.get(0)
WeatherCard( ?.let { dailyWeatherData ->
hour = weatherViewModel.state.selected?.let { WeatherToday(data = dailyWeatherData)
weatherViewModel.state.hourlyWeatherInfo?.weatherData?.get(it) (weatherViewModel.state.selected?.let {
} ?: weatherViewModel.state.hourlyWeatherInfo?.currentWeatherData, weatherViewModel.state.hourlyWeatherInfo?.weatherData?.get(it)
) } ?: weatherViewModel.state.hourlyWeatherInfo?.currentWeatherData)
WeatherForecast( ?.let {
state = weatherViewModel.state WeatherCard(
) { weatherViewModel.setSelected(it) } hour = it,
dailyData = dailyWeatherData
)
}
WeatherForecast(
state = weatherViewModel.state,
dailyData = dailyWeatherData
) { weatherViewModel.setSelected(it) }
}
} }
} }
} }
@ -98,9 +112,14 @@ object TodayTab : NavigationTab {
@Composable @Composable
override fun Actions() { override fun Actions() {
val viewModel: HourlyWeatherScreenModel = getScreenModel() val weatherViewModel = getScreenModel<HourlyWeatherScreenModel>()
val dailyWeatherViewModel = getScreenModel<DailyWeatherScreenModel>()
IconButton(onClick = { viewModel.loadWeatherInfo(cache = false) }) {
IconButton(onClick = {
weatherViewModel.loadWeatherInfo(cache = false)
dailyWeatherViewModel.loadWeatherInfo(cache = false)
}) {
Icon( Icon(
imageVector = Icons.Filled.Refresh, imageVector = Icons.Filled.Refresh,
contentDescription = stringResource(R.string.action_reload) contentDescription = stringResource(R.string.action_reload)

View file

@ -27,7 +27,6 @@ import com.henryhiles.qweather.domain.util.NavigationTab
import com.henryhiles.qweather.presentation.components.weather.WeatherDay import com.henryhiles.qweather.presentation.components.weather.WeatherDay
import com.henryhiles.qweather.presentation.components.weather.WeatherToday import com.henryhiles.qweather.presentation.components.weather.WeatherToday
import com.henryhiles.qweather.presentation.screenmodel.DailyWeatherScreenModel import com.henryhiles.qweather.presentation.screenmodel.DailyWeatherScreenModel
import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherScreenModel
object WeekTab : NavigationTab { object WeekTab : NavigationTab {
override val options: TabOptions override val options: TabOptions
@ -47,12 +46,10 @@ object WeekTab : NavigationTab {
@Composable @Composable
override fun Content() { override fun Content() {
val hourlyWeatherViewModel = getScreenModel<HourlyWeatherScreenModel>()
val dailyWeatherViewModel = getScreenModel<DailyWeatherScreenModel>() val dailyWeatherViewModel = getScreenModel<DailyWeatherScreenModel>()
LaunchedEffect(key1 = dailyWeatherViewModel.locationPreferenceManager.selectedIndex) { LaunchedEffect(key1 = dailyWeatherViewModel.locationPreferenceManager.selectedIndex) {
dailyWeatherViewModel.loadWeatherInfo() dailyWeatherViewModel.loadWeatherInfo()
hourlyWeatherViewModel.loadWeatherInfo()
} }
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
@ -83,7 +80,7 @@ object WeekTab : NavigationTab {
else -> { else -> {
LazyColumn(contentPadding = PaddingValues(16.dp)) { LazyColumn(contentPadding = PaddingValues(16.dp)) {
dailyWeatherViewModel.state.dailyWeatherData?.let { data -> dailyWeatherViewModel.state.dailyWeatherData?.let { data ->
item { WeatherToday(state = hourlyWeatherViewModel.state) } item { WeatherToday(data = data[0]) }
items(data) { items(data) {
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
WeatherDay(dailyWeatherData = it) WeatherDay(dailyWeatherData = it)
@ -97,11 +94,9 @@ object WeekTab : NavigationTab {
@Composable @Composable
override fun Actions() { override fun Actions() {
val hourlyWeatherViewModel = getScreenModel<HourlyWeatherScreenModel>()
val dailyWeatherViewModel = getScreenModel<DailyWeatherScreenModel>() val dailyWeatherViewModel = getScreenModel<DailyWeatherScreenModel>()
IconButton(onClick = { IconButton(onClick = {
hourlyWeatherViewModel.loadWeatherInfo(cache = false)
dailyWeatherViewModel.loadWeatherInfo(cache = false) dailyWeatherViewModel.loadWeatherInfo(cache = false)
}) { }) {
Icon( Icon(

View file

@ -1,4 +1,5 @@
plugins { plugins {
kotlin("plugin.serialization") version "1.9.20" apply false
id("com.android.application") version "8.2.0" apply false id("com.android.application") version "8.2.0" apply false
id("com.android.library") version "8.2.0" apply false id("com.android.library") version "8.2.0" apply false
id("org.jetbrains.kotlin.android") version "1.9.20" apply false id("org.jetbrains.kotlin.android") version "1.9.20" apply false