diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 981990a..66c1536 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("kotlin-kapt") + kotlin("plugin.serialization") version "1.8.10" } android { @@ -72,7 +73,7 @@ dependencies { implementation("androidx.compose.material3:material3:1.1.0-rc01") implementation("androidx.activity:activity-compose:1.7.1") implementation("androidx.core:core-ktx:1.10.0") - + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") implementation("com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.3") coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3") @@ -110,7 +111,7 @@ dependencies { // Retrofit val retrofitVersion = "2.9.0" implementation("com.squareup.retrofit2:retrofit:$retrofitVersion") - implementation("com.squareup.retrofit2:converter-moshi:$retrofitVersion") + implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0") // Accompanist val accompanistVersion = "0.30.0" diff --git a/app/src/main/java/com/henryhiles/qweather/di/AppModule.kt b/app/src/main/java/com/henryhiles/qweather/di/AppModule.kt index 5ffa516..8421870 100644 --- a/app/src/main/java/com/henryhiles/qweather/di/AppModule.kt +++ b/app/src/main/java/com/henryhiles/qweather/di/AppModule.kt @@ -5,13 +5,15 @@ import android.net.ConnectivityManager import android.net.NetworkCapabilities import com.henryhiles.qweather.domain.remote.GeocodingApi import com.henryhiles.qweather.domain.remote.WeatherApi +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import kotlinx.serialization.json.Json import okhttp3.Cache import okhttp3.Interceptor +import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient.Builder import org.koin.core.module.dsl.singleOf import org.koin.dsl.module import retrofit2.Retrofit -import retrofit2.converter.moshi.MoshiConverterFactory import retrofit2.create private fun isNetworkAvailable(context: Context): Boolean { @@ -29,6 +31,9 @@ private fun isNetworkAvailable(context: Context): Boolean { } } +private val contentType = "application/json".toMediaType() +private val json = Json { ignoreUnknownKeys = true } + val appModule = module { fun provideWeatherApi(context: Context): WeatherApi { val cacheControlInterceptor = Interceptor { chain -> @@ -56,7 +61,7 @@ val appModule = module { return Retrofit.Builder() .baseUrl("https://api.open-meteo.com") .client(okHttpClient) - .addConverterFactory(MoshiConverterFactory.create()) + .addConverterFactory(json.asConverterFactory(contentType)) .build() .create() } @@ -64,7 +69,7 @@ val appModule = module { fun provideGeocodingApi(): GeocodingApi { return Retrofit.Builder() .baseUrl("https://geocoding-api.open-meteo.com") - .addConverterFactory(MoshiConverterFactory.create()) + .addConverterFactory(json.asConverterFactory(contentType)) .build() .create() } diff --git a/app/src/main/java/com/henryhiles/qweather/domain/geocoding/GeocodingData.kt b/app/src/main/java/com/henryhiles/qweather/domain/geocoding/GeocodingData.kt new file mode 100644 index 0000000..03e95f5 --- /dev/null +++ b/app/src/main/java/com/henryhiles/qweather/domain/geocoding/GeocodingData.kt @@ -0,0 +1,10 @@ +package com.henryhiles.qweather.domain.geocoding + +import kotlinx.serialization.Serializable + +@Serializable +data class GeocodingData( + val location: String, + val longitude: Float, + val latitude: Float +) diff --git a/app/src/main/java/com/henryhiles/qweather/domain/manager/BasePreferenceManager.kt b/app/src/main/java/com/henryhiles/qweather/domain/manager/BasePreferenceManager.kt index 0f15d35..2f3a8de 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/manager/BasePreferenceManager.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/manager/BasePreferenceManager.kt @@ -41,9 +41,7 @@ abstract class BasePreferenceManager( getter: (key: String, defaultValue: T) -> T, private val setter: (key: String, newValue: T) -> Unit ) { - @Suppress("RedundantSetter") - var value by mutableStateOf(getter(key, defaultValue)) - private set + private var value by mutableStateOf(getter(key, defaultValue)) operator fun getValue(thisRef: Any?, property: KProperty<*>) = value operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) { @@ -102,7 +100,6 @@ abstract class BasePreferenceManager( setter = ::putColor ) - protected inline fun > enumPreference( key: String, defaultValue: E diff --git a/app/src/main/java/com/henryhiles/qweather/domain/mappers/GeocodingMappers.kt b/app/src/main/java/com/henryhiles/qweather/domain/mappers/GeocodingMappers.kt new file mode 100644 index 0000000..ab53a1f --- /dev/null +++ b/app/src/main/java/com/henryhiles/qweather/domain/mappers/GeocodingMappers.kt @@ -0,0 +1,14 @@ +package com.henryhiles.qweather.domain.mappers + +import com.henryhiles.qweather.domain.remote.GeocodingDto +import com.henryhiles.qweather.domain.geocoding.GeocodingData + +fun GeocodingDto.toGeocodingData(): List { + return results.map { + GeocodingData( + location = "${it.city}, ${it.admin}, ${it.country}", + longitude = it.longitude, + latitude = it.latitude, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/mappers/WeatherMappers.kt b/app/src/main/java/com/henryhiles/qweather/domain/mappers/WeatherMappers.kt index 2cc9695..88c7505 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/mappers/WeatherMappers.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/mappers/WeatherMappers.kt @@ -12,7 +12,7 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter import kotlin.math.roundToInt -fun HourlyWeatherDataDto.toHourlyWeatherDataMap(): List { +fun HourlyWeatherDataDto.toHourlyWeatherData(): List { return time.subList(0, 24).mapIndexed { index, time -> HourlyWeatherData( time = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME), @@ -20,12 +20,12 @@ fun HourlyWeatherDataDto.toHourlyWeatherDataMap(): List { apparentTemperature = apparentTemperature[index].roundToInt(), windSpeed = windSpeed[index].roundToInt(), precipitationProbability = precipitationProbability.getOrNull(index), - weatherType = WeatherType.fromWMO(weatherCode[index]) + weatherType = WeatherType.fromWMO(weatherCode[index]), ) } } -fun DailyWeatherDataDto.toDailyWeatherDataMap(): List { +fun DailyWeatherDataDto.toDailyWeatherData(): List { return date.mapIndexed { index, date -> DailyWeatherData( date = LocalDate.parse(date, DateTimeFormatter.ISO_DATE), @@ -41,13 +41,16 @@ fun DailyWeatherDataDto.toDailyWeatherDataMap(): List { } fun WeatherDto.toHourlyWeatherInfo(): HourlyWeatherInfo { - val weatherDataMap = hourlyWeatherData.toHourlyWeatherDataMap() + val weatherDataMap = hourlyWeatherData.toHourlyWeatherData() val now = LocalDateTime.now() val currentWeatherData = weatherDataMap.find { it.time.hour == now.hour } return HourlyWeatherInfo( 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 ) } \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/remote/DailyWeatherDataDto.kt b/app/src/main/java/com/henryhiles/qweather/domain/remote/DailyWeatherDataDto.kt index a0180d7..437b76c 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/remote/DailyWeatherDataDto.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/DailyWeatherDataDto.kt @@ -1,24 +1,26 @@ package com.henryhiles.qweather.domain.remote -import com.squareup.moshi.Json +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class DailyWeatherDataDto( - @field:Json(name = "time") + @SerialName("time") val date: List, - @field:Json(name = "weathercode") + @SerialName("weathercode") val weatherCode: List, - @field:Json(name = "precipitation_probability_max") - val precipitationProbabilityMax: List, - @field:Json(name = "precipitation_sum") + @SerialName("precipitation_probability_max") + val precipitationProbabilityMax: List, + @SerialName("precipitation_sum") val precipitationSum: List, - @field:Json(name = "windspeed_10m_max") + @SerialName("windspeed_10m_max") val windSpeedMax: List, - @field:Json(name = "temperature_2m_max") + @SerialName("temperature_2m_max") val temperatureMax: List, - @field:Json(name = "temperature_2m_min") + @SerialName("temperature_2m_min") val temperatureMin: List, - @field:Json(name = "apparent_temperature_max") + @SerialName("apparent_temperature_max") val apparentTemperatureMax: List, - @field:Json(name = "apparent_temperature_min") + @SerialName("apparent_temperature_min") val apparentTemperatureMin: List ) \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingApi.kt b/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingApi.kt index 1d98a79..f3d819d 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingApi.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingApi.kt @@ -4,8 +4,9 @@ import retrofit2.http.GET import retrofit2.http.Query interface GeocodingApi { - @GET("v1/search?count=10") + @GET("v1/search") suspend fun getGeocodingData( @Query("name") location: String, + @Query("count") count: Int = 10 ): GeocodingDto } \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingDto.kt b/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingDto.kt index e0e323b..554dfd6 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingDto.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingDto.kt @@ -1,8 +1,8 @@ package com.henryhiles.qweather.domain.remote -import com.squareup.moshi.Json +import kotlinx.serialization.Serializable +@Serializable data class GeocodingDto( - @field:Json(name = "results") - val results: List + val results: List = listOf() ) \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingLocationDto.kt b/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingLocationDto.kt index 6c8143b..4b166a8 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingLocationDto.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingLocationDto.kt @@ -1,16 +1,15 @@ package com.henryhiles.qweather.domain.remote -import com.squareup.moshi.Json +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class GeocodingLocationDto( - @field:Json(name = "name") + @SerialName("name") val city: String, - @field:Json(name = "country") val country: String, - @field:Json(name = "admin1") + @SerialName("admin1") val admin: String, - @field:Json(name = "latitude") val latitude: Float, - @field:Json(name = "longitude") val longitude: Float ) diff --git a/app/src/main/java/com/henryhiles/qweather/domain/remote/HourlyWeatherDataDto.kt b/app/src/main/java/com/henryhiles/qweather/domain/remote/HourlyWeatherDataDto.kt index 9f0072d..541e19e 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/remote/HourlyWeatherDataDto.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/HourlyWeatherDataDto.kt @@ -1,18 +1,19 @@ package com.henryhiles.qweather.domain.remote -import com.squareup.moshi.Json +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class HourlyWeatherDataDto( - @field:Json(name = "time") val time: List, - @field:Json(name = "temperature_2m") + @SerialName("temperature_2m") val temperature: List, - @field:Json(name = "apparent_temperature") + @SerialName("apparent_temperature") val apparentTemperature: List, - @field:Json(name = "weathercode") + @SerialName("weathercode") val weatherCode: List, - @field:Json(name = "precipitation_probability") - val precipitationProbability: List, - @field:Json(name = "windspeed_10m") + @SerialName("precipitation_probability") + val precipitationProbability: List, + @SerialName("windspeed_10m") val windSpeed: List, ) \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherDto.kt b/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherDto.kt index 88783a9..2ec4aac 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherDto.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherDto.kt @@ -1,11 +1,13 @@ package com.henryhiles.qweather.domain.remote -import com.squareup.moshi.Json +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class WeatherDto( - @field:Json(name = "hourly") + @SerialName("hourly") val hourlyWeatherData: HourlyWeatherDataDto, - @field:Json(name = "daily") + @SerialName("daily") val dailyWeatherData: DailyWeatherDataDto ) \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/repository/GeocodingRepository.kt b/app/src/main/java/com/henryhiles/qweather/domain/repository/GeocodingRepository.kt index 11986cd..48cd09f 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/repository/GeocodingRepository.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/repository/GeocodingRepository.kt @@ -1,14 +1,15 @@ package com.henryhiles.qweather.domain.repository +import com.henryhiles.qweather.domain.mappers.toGeocodingData import com.henryhiles.qweather.domain.remote.GeocodingApi -import com.henryhiles.qweather.domain.remote.GeocodingLocationDto import com.henryhiles.qweather.domain.util.Resource +import com.henryhiles.qweather.domain.geocoding.GeocodingData class GeocodingRepository(private val api: GeocodingApi) { - suspend fun getGeocodingData(location: String): Resource> { + suspend fun getGeocodingData(location: String): Resource> { return try { Resource.Success( - data = api.getGeocodingData(location = location).results + data = api.getGeocodingData(location = location).toGeocodingData() ) } catch (e: Exception) { e.printStackTrace() diff --git a/app/src/main/java/com/henryhiles/qweather/domain/repository/WeatherRepository.kt b/app/src/main/java/com/henryhiles/qweather/domain/repository/WeatherRepository.kt index b8a010a..0239b06 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/repository/WeatherRepository.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/repository/WeatherRepository.kt @@ -1,6 +1,6 @@ package com.henryhiles.qweather.domain.repository -import com.henryhiles.qweather.domain.mappers.toDailyWeatherDataMap +import com.henryhiles.qweather.domain.mappers.toDailyWeatherData import com.henryhiles.qweather.domain.mappers.toHourlyWeatherInfo import com.henryhiles.qweather.domain.remote.WeatherApi import com.henryhiles.qweather.domain.util.Resource @@ -43,7 +43,7 @@ class WeatherRepository(private val api: WeatherApi) { ) else api.getWeatherDataWithoutCache( lat = lat, long = long - )).dailyWeatherData.toDailyWeatherDataMap() + )).dailyWeatherData.toDailyWeatherData() ) } catch (e: Exception) { e.printStackTrace() diff --git a/app/src/main/java/com/henryhiles/qweather/domain/weather/HourlyWeatherInfo.kt b/app/src/main/java/com/henryhiles/qweather/domain/weather/HourlyWeatherInfo.kt index 22ba71b..344e229 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/weather/HourlyWeatherInfo.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/weather/HourlyWeatherInfo.kt @@ -2,5 +2,8 @@ package com.henryhiles.qweather.domain.weather data class HourlyWeatherInfo( val weatherData: List, - val currentWeatherData: HourlyWeatherData? + val currentWeatherData: HourlyWeatherData?, + val highTemperature: Int, + val lowTemperature: Int, + val precipitationProbability: Int? ) \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/QWeatherActivity.kt b/app/src/main/java/com/henryhiles/qweather/presentation/QWeatherActivity.kt index 7604304..3ee1759 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/QWeatherActivity.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/QWeatherActivity.kt @@ -7,7 +7,6 @@ import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Surface -import androidx.compose.material3.Text import androidx.compose.ui.Modifier import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.transitions.SlideTransition @@ -32,11 +31,10 @@ class QWeatherActivity : ComponentActivity() { Theme.LIGHT -> false Theme.DARK -> true } - val isLocationSet = location.location != "" + val isLocationSet = location.getLocations().isNotEmpty() WeatherAppTheme(darkTheme = isDark, monet = prefs.monet) { Surface(modifier = Modifier.fillMaxSize()) { - Text(text = location.location) Navigator( screen = if (isLocationSet) MainScreen() else LocationPickerScreen(), onBackPressed = { diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/RadioController.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/RadioController.kt index 5827c72..4833142 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/RadioController.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/RadioController.kt @@ -10,7 +10,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext @Composable inline fun > EnumRadioController( @@ -19,7 +18,6 @@ inline fun > EnumRadioController( crossinline onChoiceSelected: (E) -> Unit ) { var choice by remember { mutableStateOf(default) } - val ctx = LocalContext.current Column { enumValues().forEach { diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/VerticalDivider.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/VerticalDivider.kt new file mode 100644 index 0000000..0517abe --- /dev/null +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/VerticalDivider.kt @@ -0,0 +1,18 @@ +package com.henryhiles.qweather.presentation.components + +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Divider +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun VerticalDivider(modifier: Modifier = Modifier) { + Divider( + modifier = modifier + .fillMaxHeight() + .width(1.dp) + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/location/LocationsDrawer.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/location/LocationsDrawer.kt new file mode 100644 index 0000000..b1caecc --- /dev/null +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/location/LocationsDrawer.kt @@ -0,0 +1,71 @@ +package com.henryhiles.qweather.presentation.components.location + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.navigator.LocalNavigator +import com.henryhiles.qweather.R +import com.henryhiles.qweather.presentation.screen.LocationPickerScreen +import com.henryhiles.qweather.presentation.screenmodel.LocationPreferenceManager +import org.koin.androidx.compose.get + +@Composable +fun LocationsDrawer(drawerState: DrawerState, children: @Composable () -> Unit) { + val location: LocationPreferenceManager = get() + val navigator = LocalNavigator.current?.parent + + ModalNavigationDrawer(drawerContent = { + ModalDrawerSheet { + Column(modifier = Modifier.padding(16.dp)) { + val locations = location.getLocations() + + Text( + text = stringResource(id = R.string.locations), + style = MaterialTheme.typography.headlineSmall + ) + Spacer(modifier = Modifier.height(16.dp)) + locations.forEachIndexed { index, data -> + NavigationDrawerItem( + label = { Text(text = data.location) }, + selected = index == location.selectedLocation, + onClick = { location.selectedLocation = index }, + badge = { + IconButton(onClick = { location.removeLocation(data) }) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = stringResource( + id = R.string.action_delete + ) + ) + } + } + ) + } + + Spacer(modifier = Modifier.weight(1f)) + NavigationDrawerItem( + label = { Text(text = stringResource(id = R.string.location_add)) }, + icon = { + Icon( + imageVector = Icons.Default.Add, + contentDescription = stringResource(id = R.string.location_add) + ) + }, + selected = true, + onClick = { navigator?.push(LocationPickerScreen()) }, + ) + } + } + }, drawerState = drawerState) { + children() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/navigation/SmallToolbar.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/navigation/SmallToolbar.kt index b7f20d0..e506496 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/navigation/SmallToolbar.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/navigation/SmallToolbar.kt @@ -7,13 +7,14 @@ import androidx.compose.runtime.Composable @Composable @OptIn(ExperimentalMaterial3Api::class) fun SmallToolbar( + backButton: Boolean = true, title: @Composable () -> Unit, actions: @Composable RowScope.() -> Unit = {}, - backButton: Boolean = true + navigationIcon: @Composable () -> Unit = { if (backButton) BackButton() }, ) { TopAppBar( title = title, - navigationIcon = { if (backButton) BackButton() }, + navigationIcon = navigationIcon, actions = actions, ) } \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherCard.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherCard.kt index 022f303..fb6daaf 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherCard.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherCard.kt @@ -6,19 +6,18 @@ import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Thermostat +import androidx.compose.material.icons.outlined.Thermostat +import androidx.compose.material.icons.outlined.WaterDrop +import androidx.compose.material.icons.outlined.WindPower import androidx.compose.material3.Card import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.henryhiles.qweather.R import com.henryhiles.qweather.domain.weather.HourlyWeatherData import java.time.format.DateTimeFormatter @@ -31,7 +30,7 @@ fun WeatherCard(hour: HourlyWeatherData?, location: String, modifier: Modifier = } Card( shape = RoundedCornerShape(8.dp), - modifier = modifier.padding(16.dp) + modifier = modifier ) { Column( modifier = Modifier @@ -58,7 +57,7 @@ fun WeatherCard(hour: HourlyWeatherData?, location: String, modifier: Modifier = Image( painter = painterResource(id = it.weatherType.iconRes), contentDescription = "Image of ${it.weatherType.weatherDesc}", - modifier = Modifier.width(200.dp) + modifier = Modifier.height(152.dp) ) Spacer(modifier = Modifier.height(16.dp)) Text(text = "${it.temperature}°C", fontSize = 50.sp) @@ -72,19 +71,19 @@ fun WeatherCard(hour: HourlyWeatherData?, location: String, modifier: Modifier = WeatherDataDisplay( value = it.apparentTemperature, unit = "°C", - icon = Icons.Default.Thermostat, + icon = Icons.Outlined.Thermostat, description = "Feels like", ) WeatherDataDisplay( value = it.precipitationProbability, unit = "%", - icon = ImageVector.vectorResource(id = R.drawable.ic_drop), + icon = Icons.Outlined.WaterDrop, description = "Chance of precipitation" ) WeatherDataDisplay( value = it.windSpeed, unit = "km/h", - icon = ImageVector.vectorResource(id = R.drawable.ic_wind), + icon = Icons.Outlined.WindPower, description = "Wind Speed", ) } diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherDay.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherDay.kt index 8fcafa6..a0ec069 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherDay.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherDay.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Water import androidx.compose.material.icons.outlined.WaterDrop +import androidx.compose.material.icons.outlined.WindPower import androidx.compose.material3.Card import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -15,11 +16,8 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp -import com.henryhiles.qweather.R import com.henryhiles.qweather.domain.weather.DailyWeatherData import java.time.format.DateTimeFormatter @@ -71,7 +69,7 @@ fun WeatherDay(dailyWeatherData: DailyWeatherData, expanded: Boolean, onExpand: Row( modifier = Modifier .fillMaxWidth() - .padding(16.dp, 0.dp, 16.dp, 16.dp), + .padding(start = 16.dp, end = 16.dp, bottom = 16.dp), horizontalArrangement = Arrangement.Center ) { WeatherDataDisplay( @@ -80,7 +78,6 @@ fun WeatherDay(dailyWeatherData: DailyWeatherData, expanded: Boolean, onExpand: icon = Icons.Outlined.WaterDrop, description = "Chance of rain" ) - Spacer(modifier = Modifier.width(16.dp)) WeatherDataDisplay( value = dailyWeatherData.windSpeedMax, @@ -88,12 +85,11 @@ fun WeatherDay(dailyWeatherData: DailyWeatherData, expanded: Boolean, onExpand: icon = Icons.Outlined.Water, description = "Precipitation Amount" ) - Spacer(modifier = Modifier.width(16.dp)) WeatherDataDisplay( value = dailyWeatherData.windSpeedMax, unit = "km/h", - icon = ImageVector.vectorResource(id = R.drawable.ic_wind), + icon = Icons.Outlined.WindPower, description = "Wind Speed" ) } diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherForecast.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherForecast.kt index f96a7bd..db499a7 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherForecast.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherForecast.kt @@ -1,14 +1,12 @@ package com.henryhiles.qweather.presentation.components.weather -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherState import java.time.LocalDateTime @@ -19,24 +17,15 @@ fun WeatherForecast( onChangeSelected: (Int) -> Unit ) { state.hourlyWeatherInfo?.weatherData?.let { - Column( - modifier = modifier - .fillMaxWidth() - .padding(horizontal = 16.dp) - ) { - Text(text = "Today", fontSize = 20.sp) - Spacer(modifier = Modifier.height(16.dp)) - val rowState = rememberLazyListState(LocalDateTime.now().hour) - - LazyRow(state = rowState) { - itemsIndexed(it) { index, data -> - WeatherHour( - data = data, - modifier = Modifier - .padding(horizontal = 8.dp), - onChangeSelected = { onChangeSelected(index) } - ) - } + val rowState = rememberLazyListState(LocalDateTime.now().hour) + LazyRow(state = rowState, modifier = modifier) { + itemsIndexed(it) { index, data -> + WeatherHour( + data = data, + modifier = Modifier + .padding(horizontal = 8.dp), + onChangeSelected = { onChangeSelected(index) } + ) } } } diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherHour.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherHour.kt index 5435f9c..21e598d 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherHour.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherHour.kt @@ -37,13 +37,11 @@ fun WeatherHour( horizontalAlignment = CenterHorizontally ) { Text(text = formattedTime) - Image( painter = painterResource(id = it.weatherType.iconRes), contentDescription = "Image of ${it.weatherType.weatherDesc}", modifier = Modifier.width(40.dp) ) - Text(text = "${it.temperature}°C") } } diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherToday.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherToday.kt new file mode 100644 index 0000000..c1f0fcc --- /dev/null +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherToday.kt @@ -0,0 +1,43 @@ +package com.henryhiles.qweather.presentation.components.weather + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.henryhiles.qweather.R +import com.henryhiles.qweather.presentation.components.VerticalDivider +import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherState + +@Composable +fun WeatherToday(state: HourlyWeatherState) { + state.hourlyWeatherInfo?.let { + Row( + modifier = Modifier + .height(24.dp) + .fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + + Text( + text = stringResource(R.string.weather_high, it.highTemperature), + ) + VerticalDivider(modifier = Modifier.padding(horizontal = 8.dp)) + Text( + text = stringResource(id = R.string.weather_low, it.lowTemperature) + ) + VerticalDivider(modifier = Modifier.padding(horizontal = 8.dp)) + Text( + text = it.precipitationProbability?.let { + stringResource( + id = R.string.weather_precipitation, + it + ) + } ?: stringResource( + id = R.string.unknown + ) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/screen/LocationPickerScreen.kt b/app/src/main/java/com/henryhiles/qweather/presentation/screen/LocationPickerScreen.kt index 883f3b8..08ace3c 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/screen/LocationPickerScreen.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screen/LocationPickerScreen.kt @@ -15,7 +15,6 @@ import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization @@ -24,6 +23,7 @@ import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.koin.getScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import com.henryhiles.qweather.R +import com.henryhiles.qweather.domain.geocoding.GeocodingData import com.henryhiles.qweather.presentation.components.navigation.SmallToolbar import com.henryhiles.qweather.presentation.screenmodel.LocationPickerScreenModel @@ -32,24 +32,20 @@ class LocationPickerScreen : Screen { @Composable override fun Content() { val screenModel: LocationPickerScreenModel = getScreenModel() - var latitude by remember { mutableStateOf(screenModel.prefs.latitude) } - var longitude by remember { mutableStateOf(screenModel.prefs.longitude) } - var location by remember { mutableStateOf(screenModel.prefs.location) } + var location by remember { + mutableStateOf(null) + } var locationSearch by remember { mutableStateOf("") } var isAboutOpen by remember { mutableStateOf(false) } val navigator = LocalNavigator.current - val context = LocalContext.current Scaffold(modifier = Modifier.imePadding(), floatingActionButton = { FloatingActionButton(onClick = { - if (location == "") isAboutOpen = true - else { - screenModel.prefs.location = location - screenModel.prefs.latitude = latitude - screenModel.prefs.longitude = longitude + location?.let { + screenModel.prefs.addLocation(it) navigator?.push(MainScreen()) - } + } ?: kotlin.run { isAboutOpen = true } }) { Icon( imageVector = Icons.Default.Check, @@ -57,32 +53,32 @@ class LocationPickerScreen : Screen { ) } }) { - screenModel.state.error?.let { - AlertDialog( - onDismissRequest = {}, - confirmButton = {}, - title = { Text(text = stringResource(id = R.string.error)) }, - text = { - SelectionContainer { - Text( - text = it, + Column { + SmallToolbar( + title = { Text(text = stringResource(id = R.string.location_choose)) }, + actions = { + IconButton( + onClick = { isAboutOpen = true }) { + Icon( + imageVector = Icons.Outlined.Info, + contentDescription = stringResource(id = R.string.help_screen) ) } - }, - ) - } ?: kotlin.run { - Column { - SmallToolbar( - title = { Text(text = stringResource(id = R.string.location_choose)) }, - actions = { - IconButton( - onClick = { isAboutOpen = true }) { - Icon( - imageVector = Icons.Outlined.Info, - contentDescription = stringResource(id = R.string.help_screen) + }) + screenModel.state.error?.let { + AlertDialog( + onDismissRequest = {}, + confirmButton = {}, + title = { Text(text = stringResource(id = R.string.error)) }, + text = { + SelectionContainer { + Text( + text = it, ) } - }) + }, + ) + } ?: kotlin.run { Column(modifier = Modifier.padding(16.dp)) { if (isAboutOpen) AlertDialog( title = { Text(text = stringResource(id = R.string.location_choose)) }, @@ -134,27 +130,16 @@ class LocationPickerScreen : Screen { ) else screenModel.state.locations?.let { LazyColumn { items(it) { - val locationText by remember { - mutableStateOf( - context.getString( - R.string.location_string, - it.city, it.admin, it.country - ) - ) - } + val selected = it == location Spacer(modifier = Modifier.height(8.dp)) - Card(modifier = Modifier.clickable { - location = locationText - longitude = it.longitude - latitude = it.latitude - }) { + Card(modifier = Modifier.clickable { location = it }) { Row( modifier = Modifier .fillMaxWidth() .padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { - if (location == locationText) { + if (selected) { Icon( imageVector = Icons.Default.Check, contentDescription = stringResource( @@ -164,7 +149,7 @@ class LocationPickerScreen : Screen { ) Spacer(modifier = Modifier.width(8.dp)) } - Text(text = locationText) + Text(text = it.location) } } } diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/screen/MainScreen.kt b/app/src/main/java/com/henryhiles/qweather/presentation/screen/MainScreen.kt index cc3bf30..cf30e89 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/screen/MainScreen.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screen/MainScreen.kt @@ -1,38 +1,67 @@ package com.henryhiles.qweather.presentation.screen import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Menu +import androidx.compose.material3.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.navigator.CurrentScreen +import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.tab.TabNavigator +import com.henryhiles.qweather.R import com.henryhiles.qweather.domain.util.NavigationTab +import com.henryhiles.qweather.presentation.components.location.LocationsDrawer import com.henryhiles.qweather.presentation.components.navigation.BottomBar import com.henryhiles.qweather.presentation.components.navigation.SmallToolbar +import com.henryhiles.qweather.presentation.screenmodel.LocationPreferenceManager import com.henryhiles.qweather.presentation.tabs.TodayTab +import kotlinx.coroutines.launch +import org.koin.androidx.compose.get class MainScreen : Screen { @Composable override fun Content() { + val drawerState = + rememberDrawerState(initialValue = DrawerValue.Closed) + val coroutineScope = rememberCoroutineScope() TabNavigator(tab = TodayTab) { - Scaffold( - topBar = { - SmallToolbar( - title = { Text(text = "QWeather") }, - actions = { - (it.current as? NavigationTab)?.Actions() + LocationsDrawer(drawerState = drawerState) { + Scaffold( + topBar = { + SmallToolbar( + title = { Text(text = stringResource(R.string.app_name)) }, + actions = { + (it.current as? NavigationTab)?.Actions() + } + ) { + IconButton(onClick = { + coroutineScope.launch { + with(drawerState) { if (isOpen) close() else open() } + } + }) { + Icon( + imageVector = Icons.Default.Menu, + contentDescription = stringResource(id = R.string.location_picker_open) + ) + } } - ) - }, - bottomBar = { - BottomBar(navigator = it) - } - ) { padding -> - Box(modifier = Modifier.padding(padding)) { - CurrentScreen() + }, + bottomBar = { + BottomBar(navigator = it) + } + ) { padding -> + Box(modifier = Modifier.padding(padding)) { + CurrentScreen() + } } } } diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/DailyWeatherScreenModel.kt b/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/DailyWeatherScreenModel.kt index 7bde18d..fddf9c2 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/DailyWeatherScreenModel.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/DailyWeatherScreenModel.kt @@ -19,20 +19,20 @@ data class DailyWeatherState( class DailyWeatherScreenModel( private val repository: WeatherRepository, - private val location: LocationPreferenceManager + locationPreferenceManager: LocationPreferenceManager ) : ScreenModel { var state by mutableStateOf(DailyWeatherState()) private set + val location = locationPreferenceManager.getSelectedLocation() fun loadWeatherInfo(cache: Boolean = true) { coroutineScope.launch { state = state.copy(isLoading = true, error = null) - state = when (val result = - repository.getDailyWeatherData( - lat = location.latitude, - long = location.longitude, - cache = cache - )) { + state = when (val result = repository.getDailyWeatherData( + lat = location.latitude, + long = location.longitude, + cache = cache + )) { is Resource.Success -> { state.copy( dailyWeatherData = result.data, diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/HourlyWeatherScreenModel.kt b/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/HourlyWeatherScreenModel.kt index e7c4edc..a04c7d4 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/HourlyWeatherScreenModel.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/HourlyWeatherScreenModel.kt @@ -19,11 +19,13 @@ data class HourlyWeatherState( class HourlyWeatherScreenModel( private val repository: WeatherRepository, - val location: LocationPreferenceManager, + locationPreferenceManager: LocationPreferenceManager, ) : ScreenModel { var state by mutableStateOf(HourlyWeatherState()) private set + val location = locationPreferenceManager.getSelectedLocation() + fun loadWeatherInfo(cache: Boolean = true) { coroutineScope.launch { state = state.copy(isLoading = true, error = null, selected = null) diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/LocationPickerScreenModel.kt b/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/LocationPickerScreenModel.kt index b946c8c..5d00302 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/LocationPickerScreenModel.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/LocationPickerScreenModel.kt @@ -6,23 +6,46 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import cafe.adriel.voyager.core.model.ScreenModel import cafe.adriel.voyager.core.model.coroutineScope +import com.henryhiles.qweather.domain.geocoding.GeocodingData import com.henryhiles.qweather.domain.manager.BasePreferenceManager -import com.henryhiles.qweather.domain.remote.GeocodingLocationDto import com.henryhiles.qweather.domain.repository.GeocodingRepository import com.henryhiles.qweather.domain.util.Resource import kotlinx.coroutines.launch +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json data class LocationPickerState( - val locations: List? = null, + val locations: List? = null, val isLoading: Boolean = false, val error: String? = null, ) class LocationPreferenceManager(context: Context) : BasePreferenceManager(context.getSharedPreferences("location", Context.MODE_PRIVATE)) { - var latitude by floatPreference("lat", 0f) - var longitude by floatPreference("long", 0f) - var location by stringPreference("string") + private var locations by stringPreference( + "locations", + Json.encodeToString(value = listOf()) + ) + var selectedLocation by intPreference("selected_location", 0) + + fun getSelectedLocation(): GeocodingData { + return getLocations()[selectedLocation] + } + + fun getLocations(): List { + return Json.decodeFromString(string = locations) + } + + fun addLocation(location: GeocodingData) { + val currentLocations = getLocations() + locations = Json.encodeToString(value = currentLocations + location) + } + + fun removeLocation(location: GeocodingData) { + val currentLocations = getLocations() + locations = Json.encodeToString(value = currentLocations - location) + } } class LocationPickerScreenModel( diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/tabs/TodayTab.kt b/app/src/main/java/com/henryhiles/qweather/presentation/tabs/TodayTab.kt index 67b7f9d..5fd0591 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/tabs/TodayTab.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/tabs/TodayTab.kt @@ -20,6 +20,7 @@ import com.henryhiles.qweather.R import com.henryhiles.qweather.domain.util.NavigationTab import com.henryhiles.qweather.presentation.components.weather.WeatherCard import com.henryhiles.qweather.presentation.components.weather.WeatherForecast +import com.henryhiles.qweather.presentation.components.weather.WeatherToday import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherScreenModel object TodayTab : NavigationTab { @@ -77,6 +78,7 @@ object TodayTab : NavigationTab { Column( modifier = Modifier .fillMaxSize() + .padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { WeatherCard( hour = weatherViewModel.state.selected?.let { @@ -84,7 +86,7 @@ object TodayTab : NavigationTab { } ?: weatherViewModel.state.hourlyWeatherInfo?.currentWeatherData, location = weatherViewModel.location.location ) - Spacer(modifier = Modifier.height(16.dp)) + WeatherToday(state = weatherViewModel.state) WeatherForecast( state = weatherViewModel.state ) { weatherViewModel.setSelected(it) } diff --git a/app/src/main/res/drawable/ic_drop.xml b/app/src/main/res/drawable/ic_drop.xml deleted file mode 100644 index 7ef4816..0000000 --- a/app/src/main/res/drawable/ic_drop.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_pressure.xml b/app/src/main/res/drawable/ic_pressure.xml deleted file mode 100644 index 80aa4b7..0000000 --- a/app/src/main/res/drawable/ic_pressure.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_wind.xml b/app/src/main/res/drawable/ic_wind.xml deleted file mode 100644 index f9028cc..0000000 --- a/app/src/main/res/drawable/ic_wind.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c2fc7e0..967d0f6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,11 +8,12 @@ Apply Confirm About + Delete Search Reload Try Again - Selectedx + Selected How do I use this screen? Please search a location, then tap a result. Then tap the apply button in the bottom left corner. @@ -27,16 +28,21 @@ Location to fetch data from Location - %1$s, %2$s, %3$s - Auto-pick location + Locations + Add Location + Open location picker Choose a Location System Light Dark + High: %1$d°C + Low: %1$d°C + Precipitation: %1$d﹪ + Unknown An error occurred - Couldn\'t retrieve location. Make sure to grant permission and enable GPS. + "Couldn't retrieve location. Make sure to grant permission and enable GPS." \ No newline at end of file