From 376f28dc9d09b36e6ef01f3e7a8cefbdee24b407 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 16 Apr 2023 16:28:40 -0400 Subject: [PATCH] not working --- .../com/henryhiles/qweather/di/AppModule.kt | 22 ++- .../henryhiles/qweather/di/ManagerModule.kt | 3 + .../qweather/di/RepositoryModule.kt | 2 + .../qweather/di/ScreenModelModule.kt | 2 + .../domain/location/LocationTracker.kt | 2 +- .../domain/remote/DailyWeatherDataDto.kt | 12 +- .../qweather/domain/remote/GeocodingApi.kt | 11 ++ .../qweather/domain/remote/GeocodingDto.kt | 8 + .../domain/remote/GeocodingLocationDto.kt | 16 ++ .../domain/remote/HourlyWeatherDataDto.kt | 6 +- .../qweather/domain/remote/WeatherApi.kt | 8 +- .../domain/repository/GeocodingRepository.kt | 18 ++ .../domain/repository/WeatherRepository.kt | 13 +- .../qweather/presentation/QWeatherActivity.kt | 12 +- .../components/weather/WeatherHour.kt | 20 +-- .../screen/AppearanceSettingsScreen.kt | 10 +- .../screen/LocationPickerScreen.kt | 167 ++++++++++++++++++ .../screenmodel/DailyWeatherScreenModel.kt | 6 +- .../screenmodel/HourlyWeatherScreenModel.kt | 6 +- .../screenmodel/LocationPickerScreenModel.kt | 64 +++++++ app/src/main/res/values/strings.xml | 9 + 21 files changed, 360 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingApi.kt create mode 100644 app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingDto.kt create mode 100644 app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingLocationDto.kt create mode 100644 app/src/main/java/com/henryhiles/qweather/domain/repository/GeocodingRepository.kt create mode 100644 app/src/main/java/com/henryhiles/qweather/presentation/screen/LocationPickerScreen.kt create mode 100644 app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/LocationPickerScreenModel.kt 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 3f95b22..5ffa516 100644 --- a/app/src/main/java/com/henryhiles/qweather/di/AppModule.kt +++ b/app/src/main/java/com/henryhiles/qweather/di/AppModule.kt @@ -3,6 +3,7 @@ package com.henryhiles.qweather.di import android.content.Context import android.net.ConnectivityManager import android.net.NetworkCapabilities +import com.henryhiles.qweather.domain.remote.GeocodingApi import com.henryhiles.qweather.domain.remote.WeatherApi import okhttp3.Cache import okhttp3.Interceptor @@ -30,7 +31,6 @@ private fun isNetworkAvailable(context: Context): Boolean { val appModule = module { fun provideWeatherApi(context: Context): WeatherApi { - val cacheControlInterceptor = Interceptor { chain -> val originalResponse = chain.proceed(chain.request()) if (isNetworkAvailable(context)) { @@ -50,13 +50,25 @@ val appModule = module { val cache = Cache(context.cacheDir, cacheSize.toLong()) val builder = Builder() .cache(cache) - builder.networkInterceptors() - .add(cacheControlInterceptor) + builder.networkInterceptors().add(cacheControlInterceptor) val okHttpClient = builder.build() - return Retrofit.Builder().baseUrl("https://api.open-meteo.com").client(okHttpClient) - .addConverterFactory(MoshiConverterFactory.create()).build().create() + return Retrofit.Builder() + .baseUrl("https://api.open-meteo.com") + .client(okHttpClient) + .addConverterFactory(MoshiConverterFactory.create()) + .build() + .create() + } + + fun provideGeocodingApi(): GeocodingApi { + return Retrofit.Builder() + .baseUrl("https://geocoding-api.open-meteo.com") + .addConverterFactory(MoshiConverterFactory.create()) + .build() + .create() } singleOf(::provideWeatherApi) + singleOf(::provideGeocodingApi) } \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/di/ManagerModule.kt b/app/src/main/java/com/henryhiles/qweather/di/ManagerModule.kt index 64ab3b5..9225691 100644 --- a/app/src/main/java/com/henryhiles/qweather/di/ManagerModule.kt +++ b/app/src/main/java/com/henryhiles/qweather/di/ManagerModule.kt @@ -1,9 +1,12 @@ package com.henryhiles.qweather.di import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferenceManager +import com.henryhiles.qweather.presentation.screenmodel.LocationPreferenceManager + import org.koin.core.module.dsl.singleOf import org.koin.dsl.module val managerModule = module { singleOf(::AppearancePreferenceManager) + singleOf(::LocationPreferenceManager) } \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/di/RepositoryModule.kt b/app/src/main/java/com/henryhiles/qweather/di/RepositoryModule.kt index 0410ead..0fc7f78 100644 --- a/app/src/main/java/com/henryhiles/qweather/di/RepositoryModule.kt +++ b/app/src/main/java/com/henryhiles/qweather/di/RepositoryModule.kt @@ -1,9 +1,11 @@ package com.henryhiles.qweather.di +import com.henryhiles.qweather.domain.repository.GeocodingRepository import com.henryhiles.qweather.domain.repository.WeatherRepository import org.koin.core.module.dsl.singleOf import org.koin.dsl.module val repositoryModule = module { singleOf(::WeatherRepository) + singleOf(::GeocodingRepository) } \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/di/ScreenModelModule.kt b/app/src/main/java/com/henryhiles/qweather/di/ScreenModelModule.kt index 80daadb..5f4f491 100644 --- a/app/src/main/java/com/henryhiles/qweather/di/ScreenModelModule.kt +++ b/app/src/main/java/com/henryhiles/qweather/di/ScreenModelModule.kt @@ -3,11 +3,13 @@ package com.henryhiles.qweather.di import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferencesScreenModel import com.henryhiles.qweather.presentation.screenmodel.DailyWeatherScreenModel import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherScreenModel +import com.henryhiles.qweather.presentation.screenmodel.LocationPickerScreenModel import org.koin.core.module.dsl.factoryOf import org.koin.dsl.module val screenModelModule = module { factoryOf(::AppearancePreferencesScreenModel) + factoryOf(::LocationPickerScreenModel) factoryOf(::HourlyWeatherScreenModel) factoryOf(::DailyWeatherScreenModel) } \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/location/LocationTracker.kt b/app/src/main/java/com/henryhiles/qweather/domain/location/LocationTracker.kt index 6c21a6c..747a18e 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/location/LocationTracker.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/location/LocationTracker.kt @@ -8,7 +8,7 @@ import android.location.Location import android.location.LocationManager import androidx.core.content.ContextCompat -class LocationTracker constructor( +class LocationTracker( private val application: Application ) { fun getCurrentLocation(): Location? { 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 336e5b6..a0180d7 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 @@ -10,15 +10,15 @@ data class DailyWeatherDataDto( @field:Json(name = "precipitation_probability_max") val precipitationProbabilityMax: List, @field:Json(name = "precipitation_sum") - val precipitationSum: List, + val precipitationSum: List, @field:Json(name = "windspeed_10m_max") - val windSpeedMax: List, + val windSpeedMax: List, @field:Json(name = "temperature_2m_max") - val temperatureMax: List, + val temperatureMax: List, @field:Json(name = "temperature_2m_min") - val temperatureMin: List, + val temperatureMin: List, @field:Json(name = "apparent_temperature_max") - val apparentTemperatureMax: List, + val apparentTemperatureMax: List, @field:Json(name = "apparent_temperature_min") - val apparentTemperatureMin: List + 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 new file mode 100644 index 0000000..da15a7c --- /dev/null +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingApi.kt @@ -0,0 +1,11 @@ +package com.henryhiles.qweather.domain.remote + +import retrofit2.http.GET +import retrofit2.http.Query + +interface GeocodingApi { + @GET("v1/search?count=4") + suspend fun getGeocodingData( + @Query("name") location: String, + ): 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 new file mode 100644 index 0000000..e0e323b --- /dev/null +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingDto.kt @@ -0,0 +1,8 @@ +package com.henryhiles.qweather.domain.remote + +import com.squareup.moshi.Json + +data class GeocodingDto( + @field:Json(name = "results") + val results: List +) \ 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 new file mode 100644 index 0000000..6c8143b --- /dev/null +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingLocationDto.kt @@ -0,0 +1,16 @@ +package com.henryhiles.qweather.domain.remote + +import com.squareup.moshi.Json + +data class GeocodingLocationDto( + @field:Json(name = "name") + val city: String, + @field:Json(name = "country") + val country: String, + @field:Json(name = "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 2b2a8fd..9f0072d 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 @@ -6,13 +6,13 @@ data class HourlyWeatherDataDto( @field:Json(name = "time") val time: List, @field:Json(name = "temperature_2m") - val temperature: List, + val temperature: List, @field:Json(name = "apparent_temperature") - val apparentTemperature: List, + val apparentTemperature: List, @field:Json(name = "weathercode") val weatherCode: List, @field:Json(name = "precipitation_probability") val precipitationProbability: List, @field:Json(name = "windspeed_10m") - val windSpeed: List, + val windSpeed: List, ) \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherApi.kt b/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherApi.kt index 7958a0c..59fd151 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherApi.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherApi.kt @@ -15,14 +15,14 @@ const val URL = "v1/forecast?$HOURLY&$DAILY&$TIMEZONE&$FORECAST_DAYS" interface WeatherApi { @GET(URL) suspend fun getWeatherData( - @Query("latitude") lat: Double, - @Query("longitude") long: Double, + @Query("latitude") lat: Float, + @Query("longitude") long: Float, ): WeatherDto @Headers("Cache-Control: no-cache") @GET(URL) suspend fun getWeatherDataWithoutCache( - @Query("latitude") lat: Double, - @Query("longitude") long: Double, + @Query("latitude") lat: Float, + @Query("longitude") long: Float, ): WeatherDto } \ 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 new file mode 100644 index 0000000..11986cd --- /dev/null +++ b/app/src/main/java/com/henryhiles/qweather/domain/repository/GeocodingRepository.kt @@ -0,0 +1,18 @@ +package com.henryhiles.qweather.domain.repository + +import com.henryhiles.qweather.domain.remote.GeocodingApi +import com.henryhiles.qweather.domain.remote.GeocodingLocationDto +import com.henryhiles.qweather.domain.util.Resource + +class GeocodingRepository(private val api: GeocodingApi) { + suspend fun getGeocodingData(location: String): Resource> { + return try { + Resource.Success( + data = api.getGeocodingData(location = location).results + ) + } catch (e: Exception) { + e.printStackTrace() + Resource.Error(e.message ?: "An unknown error occurred.") + } + } +} \ No newline at end of file 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 94c5a70..b8a010a 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 @@ -7,10 +7,10 @@ import com.henryhiles.qweather.domain.util.Resource import com.henryhiles.qweather.domain.weather.DailyWeatherData import com.henryhiles.qweather.domain.weather.HourlyWeatherInfo -class WeatherRepository constructor(private val api: WeatherApi) { +class WeatherRepository(private val api: WeatherApi) { suspend fun getHourlyWeatherData( - lat: Double, - long: Double, + lat: Float, + long: Float, cache: Boolean = true ): Resource { return try { @@ -31,8 +31,8 @@ class WeatherRepository constructor(private val api: WeatherApi) { } suspend fun getDailyWeatherData( - lat: Double, - long: Double, + lat: Float, + long: Float, cache: Boolean = true ): Resource> { return try { @@ -43,8 +43,7 @@ class WeatherRepository constructor(private val api: WeatherApi) { ) else api.getWeatherDataWithoutCache( lat = lat, long = long - )) - .dailyWeatherData.toDailyWeatherDataMap() + )).dailyWeatherData.toDailyWeatherDataMap() ) } catch (e: Exception) { e.printStackTrace() 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 61763c0..1112294 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/QWeatherActivity.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/QWeatherActivity.kt @@ -5,17 +5,23 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent 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 +import com.henryhiles.qweather.presentation.screen.LocationPickerScreen import com.henryhiles.qweather.presentation.screen.MainScreen import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferenceManager +import com.henryhiles.qweather.presentation.screenmodel.LocationPreferenceManager import com.henryhiles.qweather.presentation.screenmodel.Theme import com.henryhiles.qweather.presentation.ui.theme.WeatherAppTheme import org.koin.android.ext.android.inject class QWeatherActivity : ComponentActivity() { private val prefs: AppearancePreferenceManager by inject() + private val location: LocationPreferenceManager by inject() @OptIn(ExperimentalAnimationApi::class) override fun onCreate(savedInstanceState: Bundle?) { @@ -26,10 +32,12 @@ class QWeatherActivity : ComponentActivity() { Theme.LIGHT -> false Theme.DARK -> true } + val isLocationSet = location.location != "" WeatherAppTheme(darkTheme = isDark, monet = prefs.monet) { - Surface { - Navigator(screen = MainScreen()) { + Surface(modifier = Modifier.fillMaxSize()) { + Text(text = location.location) + Navigator(screen = if (isLocationSet) MainScreen() else LocationPickerScreen()) { SlideTransition(it) } } 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 13f92a4..5435f9c 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 @@ -6,6 +6,8 @@ import androidx.compose.foundation.layout.* import androidx.compose.material3.Card import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Modifier @@ -21,8 +23,8 @@ fun WeatherHour( onChangeSelected: () -> Unit ) { data.let { - val formattedTime = remember(it) { - it.time.format(DateTimeFormatter.ofPattern("HH:mm")) + val formattedTime by remember { + derivedStateOf { it.time.format(DateTimeFormatter.ofPattern("HH:mm")) } } Card(modifier = modifier.clickable { onChangeSelected() @@ -44,21 +46,7 @@ fun WeatherHour( Text(text = "${it.temperature}°C") } - } -// Column( -// horizontalAlignment = Alignment.CenterHorizontally, -// verticalArrangement = Arrangement.SpaceBetween, -// modifier = modifier -// ) { -// 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") -// } } } \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/screen/AppearanceSettingsScreen.kt b/app/src/main/java/com/henryhiles/qweather/presentation/screen/AppearanceSettingsScreen.kt index 7c5a3fc..84f7e7e 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/screen/AppearanceSettingsScreen.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screen/AppearanceSettingsScreen.kt @@ -20,14 +20,10 @@ import com.henryhiles.qweather.presentation.components.settings.SettingsSwitch import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferencesScreenModel class AppearanceSettingsScreen : Screen { - @Composable - override fun Content() = Screen() - - @Composable - private fun Screen() { + override fun Content() { val screenModel: AppearancePreferencesScreenModel = getScreenModel() - val ctx = LocalContext.current + val context = LocalContext.current Scaffold(topBar = { SmallToolbar( @@ -51,7 +47,7 @@ class AppearanceSettingsScreen : Screen { SettingsItemChoice( label = stringResource(R.string.appearance_theme), pref = screenModel.prefs.theme, - labelFactory = { ctx.getString(it.label) } + labelFactory = { context.getString(it.label) } ) { screenModel.prefs.theme = it } } 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 new file mode 100644 index 0000000..62ac6be --- /dev/null +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screen/LocationPickerScreen.kt @@ -0,0 +1,167 @@ +package com.henryhiles.qweather.presentation.screen + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.outlined.MyLocation +import androidx.compose.material.icons.outlined.Search +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 +import androidx.compose.ui.unit.dp +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.presentation.screenmodel.LocationPickerScreenModel + +class LocationPickerScreen : Screen { + @Composable + override fun Content() { + val screenModel: LocationPickerScreenModel = getScreenModel() + var latitude by remember { mutableStateOf(0f) } + var longitude by remember { mutableStateOf(0f) } + var location by remember { mutableStateOf("") } + var locationSearch by remember { mutableStateOf("") } + val navigator = LocalNavigator.current + val context = LocalContext.current + + Box(modifier = Modifier.fillMaxSize()) { + screenModel.state.error?.let { + AlertDialog( + onDismissRequest = {}, + confirmButton = {}, + title = { Text(text = stringResource(id = R.string.error)) }, + text = { + SelectionContainer { + Text( + text = it, + ) + } + }, + ) + } ?: AlertDialog( + onDismissRequest = {}, + confirmButton = { + TextButton( + onClick = { + screenModel.prefs.location = location + screenModel.prefs.latitude = latitude + screenModel.prefs.longitude = longitude + navigator?.push(MainScreen()) + }, + enabled = location != "" + ) { + Text(text = stringResource(id = R.string.action_apply)) + } + }, + title = { Text(text = stringResource(id = R.string.location_choose)) }, + text = { + Column { + OutlinedTextField( + label = { Text(text = stringResource(id = R.string.location)) }, + keyboardOptions = KeyboardOptions( + capitalization = KeyboardCapitalization.Sentences, + imeAction = ImeAction.Search + ), + keyboardActions = KeyboardActions(onSearch = { + screenModel.loadGeolocationInfo( + locationSearch + ) + }), + maxLines = 1, + value = locationSearch, + onValueChange = { + locationSearch = it + }, + trailingIcon = { + if (locationSearch == "") + IconButton(onClick = { + + }) { + Icon( + imageVector = Icons.Outlined.MyLocation, + contentDescription = stringResource(id = R.string.location_auto_pick) + ) + } + else + IconButton(onClick = { + screenModel.loadGeolocationInfo( + locationSearch + ) + }) { + Icon( + imageVector = Icons.Outlined.Search, + contentDescription = stringResource(id = R.string.action_search) + ) + } + } + ) + Spacer(modifier = Modifier.height(16.dp)) + Text(text = "${screenModel.state.locations != null}") + screenModel.state.locations?.let { + Text( + text = "hi" + ) + } + + if (screenModel.state.isLoading) CircularProgressIndicator( + modifier = Modifier.align( + Alignment.CenterHorizontally + ) + ) 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 + ) + ) + } + Spacer(modifier = Modifier.height(8.dp)) + Card(modifier = Modifier.clickable { + location = locationText + longitude = it.longitude + latitude = it.latitude + }) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + if (location == locationText) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = stringResource( + id = R.string.selected + ), + modifier = Modifier.height(16.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + } + Text(text = locationText) + } + } + } + } + } + } + } + ) + } + } +} 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 48c312a..9e177c3 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,7 +19,7 @@ data class DailyWeatherState( val expanded: Int? = null ) -class DailyWeatherScreenModel constructor( +class DailyWeatherScreenModel( private val repository: WeatherRepository, private val locationTracker: LocationTracker, ) : ScreenModel { @@ -34,8 +34,8 @@ class DailyWeatherScreenModel constructor( currentLocation?.let { location -> state = when (val result = repository.getDailyWeatherData( - lat = location.latitude, - long = location.longitude, + lat = location.latitude.toFloat(), + long = location.longitude.toFloat(), cache = cache )) { is Resource.Success -> { 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 a0cc0e8..0e90ee6 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 @@ -20,7 +20,7 @@ data class HourlyWeatherState( val selected: Int? = null ) -class HourlyWeatherScreenModel constructor( +class HourlyWeatherScreenModel( private val repository: WeatherRepository, private val locationTracker: LocationTracker, private val context: Context @@ -35,8 +35,8 @@ class HourlyWeatherScreenModel constructor( currentLocation?.let { location -> state = when (val result = repository.getHourlyWeatherData( - lat = location.latitude, - long = location.longitude, + lat = location.latitude.toFloat(), + long = location.longitude.toFloat(), cache = cache )) { is Resource.Success -> { 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 new file mode 100644 index 0000000..d9d94cf --- /dev/null +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/LocationPickerScreenModel.kt @@ -0,0 +1,64 @@ +package com.henryhiles.qweather.presentation.screenmodel + +import android.content.Context +import androidx.compose.runtime.getValue +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.location.LocationTracker +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 + +data class LocationPickerState( + 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") +} + +class LocationPickerScreenModel( + val prefs: LocationPreferenceManager, + private val repository: GeocodingRepository, + private val locationTracker: LocationTracker, + private val context: Context +) : ScreenModel { + var state by mutableStateOf(LocationPickerState()) + private set + + fun loadGeolocationInfo(location: String) { + coroutineScope.launch { + state = state.copy(isLoading = true, error = null) + + state = when (val result = + repository.getGeocodingData( + location = location + )) { + is Resource.Success -> { + state.copy( + locations = result.data, + isLoading = false, + error = null, + ) + } + + is Resource.Error -> { + state.copy( + locations = null, + isLoading = false, + error = result.message + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 21e8835..264a2a3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,11 +5,15 @@ Settings Back + Apply Confirm About + Search Reload Try Again + Selected + Theme Dynamic Theme Available on Android 12+ @@ -17,6 +21,11 @@ Appearance Theme, code style + Location + %1$s, %2$s, %3$s + Auto-pick location + Choose a Location + System Light Dark