diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/di/network/ApiModule.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/di/network/ApiModule.kt index cab4665c3..24dd3ddb9 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/core/di/network/ApiModule.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/di/network/ApiModule.kt @@ -2,9 +2,6 @@ package com.pranshulgg.weather_master_app.core.di.network import com.pranshulgg.weather_master_app.core.network.github.GithubApi import com.pranshulgg.weather_master_app.core.network.sources.address.nominatim.NominatimApi -import com.pranshulgg.weather_master_app.core.network.sources.airquality.accu.AccuAqiApi -import com.pranshulgg.weather_master_app.core.network.sources.airquality.openmeteo.OpenMeteoAqiApi -import com.pranshulgg.weather_master_app.core.network.sources.alerts.accu.AlertsAccuApi import com.pranshulgg.weather_master_app.core.network.sources.alerts.fpas.FpasApi import com.pranshulgg.weather_master_app.core.network.sources.alerts.weatherapi.AlertsWeatherApi import com.pranshulgg.weather_master_app.core.network.sources.alerts.wmosevereweather.WmoSevereWeatherApi @@ -29,6 +26,7 @@ import com.pranshulgg.weather_master_app.core.network.sources.weather.metnorway. import com.pranshulgg.weather_master_app.core.network.sources.weather.metoffice.MetOfficeApi import com.pranshulgg.weather_master_app.core.network.sources.weather.nws.NwsApi import com.pranshulgg.weather_master_app.core.network.sources.weather.openmeteo.OpenMeteoApi +import com.pranshulgg.weather_master_app.core.network.sources.weather.openmeteo.airquality.OpenMeteoAqiApi import com.pranshulgg.weather_master_app.core.network.sources.weather.pirateweather.PirateWeatherApi import com.pranshulgg.weather_master_app.core.network.sources.weather.smhi.SmhiApi import dagger.Module @@ -113,14 +111,6 @@ object ApiModule { @Singleton fun provideMeteoamApi(): MeteoamApi = MeteoamApi.create() - @Provides - @Singleton - fun provideAccuAqiApi(): AccuAqiApi = AccuAqiApi.create() - - @Provides - @Singleton - fun provideAlertsAccuApi(): AlertsAccuApi = AlertsAccuApi.create() - @Provides @Singleton fun provideAlertsWeatherApi(): AlertsWeatherApi = AlertsWeatherApi.create() diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/di/weather/WeatherRepositoryModule.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/di/weather/WeatherRepositoryModule.kt index a0158ca14..a2e0fca22 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/core/di/weather/WeatherRepositoryModule.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/di/weather/WeatherRepositoryModule.kt @@ -1,17 +1,6 @@ package com.pranshulgg.weather_master_app.core.di.weather -import com.pranshulgg.weather_master_app.core.network.sources.airquality.accu.AccuAqiApi -import com.pranshulgg.weather_master_app.core.network.sources.airquality.accu.AccuAqiRepository -import com.pranshulgg.weather_master_app.core.network.sources.airquality.openmeteo.OpenMeteoAqiApi -import com.pranshulgg.weather_master_app.core.network.sources.airquality.openmeteo.OpenMeteoAqiRepository -import com.pranshulgg.weather_master_app.core.network.sources.alerts.accu.AlertsAccuApi -import com.pranshulgg.weather_master_app.core.network.sources.alerts.accu.AlertsAccuRepository -import com.pranshulgg.weather_master_app.core.network.sources.alerts.fpas.FpasApi -import com.pranshulgg.weather_master_app.core.network.sources.alerts.fpas.FpasRepository -import com.pranshulgg.weather_master_app.core.network.sources.alerts.weatherapi.AlertsWeatherApi -import com.pranshulgg.weather_master_app.core.network.sources.alerts.weatherapi.AlertsWeatherApiRepository -import com.pranshulgg.weather_master_app.core.network.sources.alerts.wmosevereweather.WmoSevereWeatherApi -import com.pranshulgg.weather_master_app.core.network.sources.alerts.wmosevereweather.WmoSevereWeatherRepository +import com.pranshulgg.weather_master_app.core.network.sources.weather.openmeteo.airquality.OpenMeteoAqiApi import com.pranshulgg.weather_master_app.core.network.sources.weather.accu.AccuApi import com.pranshulgg.weather_master_app.core.network.sources.weather.accu.AccuRepository import com.pranshulgg.weather_master_app.core.network.sources.weather.aemet.AemetApi @@ -71,16 +60,11 @@ object WeatherRepositoryModule { fun provideOpenMeteoRepository( dao: LocationsDao, api: OpenMeteoApi, - weatherDao: WeatherDao - ): OpenMeteoRepository = OpenMeteoRepository(dao, weatherDao, api) + weatherDao: WeatherDao, + airQualityDao: AirQualityDao, + airQualityApi: OpenMeteoAqiApi + ): OpenMeteoRepository = OpenMeteoRepository(dao, weatherDao, api, airQualityApi, airQualityDao) - @Provides - @Singleton - fun provideOpenMeteoAqiRepository( - api: OpenMeteoAqiApi, - dao: AirQualityDao - ): OpenMeteoAqiRepository = - OpenMeteoAqiRepository(api, dao) @Provides @Singleton @@ -161,8 +145,19 @@ object WeatherRepositoryModule { dao: LocationsDao, api: AccuApi, weatherDao: WeatherDao, - locationKeysDao: LocationKeysDao - ): AccuRepository = AccuRepository(dao, weatherDao, api, locationKeysDao) + locationKeysDao: LocationKeysDao, + airQualityDao: AirQualityDao, + alertsDao: AlertsDao, + locationsDao: LocationsDao + ): AccuRepository = AccuRepository( + dao, + weatherDao, + api, + locationKeysDao, + airQualityDao, + alertsDao, + locationsDao + ) @Provides @Singleton @@ -172,33 +167,6 @@ object WeatherRepositoryModule { weatherDao: WeatherDao ): MeteoamRepository = MeteoamRepository(dao, weatherDao, api) - @Provides - @Singleton - fun provideAccuAqiRepository( - api: AccuAqiApi, - dao: AirQualityDao, - locationKeysDao: LocationKeysDao, - accuApi: AccuApi - ): AccuAqiRepository = AccuAqiRepository(api, dao, locationKeysDao, accuApi) - - @Provides - @Singleton - fun provideAccuAlertsRepository( - api: AlertsAccuApi, - dao: AlertsDao, - locationKeysDao: LocationKeysDao, - accuApi: AccuApi, - locationsDao: LocationsDao - ): AlertsAccuRepository = AlertsAccuRepository(api, dao, locationKeysDao, accuApi, locationsDao) - - @Provides - @Singleton - fun provideWeatherApiAlertsRepository( - api: AlertsWeatherApi, - dao: AlertsDao, - locationsDao: LocationsDao - ): AlertsWeatherApiRepository = AlertsWeatherApiRepository(api, dao, locationsDao) - @Provides @Singleton fun provideIpmaRepository( @@ -208,21 +176,6 @@ object WeatherRepositoryModule { locationKeysDao: LocationKeysDao ): IpmaRepository = IpmaRepository(dao, weatherDao, api, locationKeysDao) - @Provides - @Singleton - fun provideWmoSevereWeatherAlertsRepository( - api: WmoSevereWeatherApi, - dao: AlertsDao, - locationsDao: LocationsDao - ): WmoSevereWeatherRepository = WmoSevereWeatherRepository(api, dao, locationsDao) - - @Provides - @Singleton - fun provideFpasAlertsRepository( - api: FpasApi, - dao: AlertsDao, - locationsDao: LocationsDao - ): FpasRepository = FpasRepository(api, dao, locationsDao) @Provides @Singleton @@ -266,8 +219,10 @@ object WeatherRepositoryModule { dao: LocationsDao, api: PirateWeatherApi, weatherDao: WeatherDao, - apiKeysDao: ApiKeysDao - ): PirateWeatherRepository = PirateWeatherRepository(dao, weatherDao, api, apiKeysDao) + apiKeysDao: ApiKeysDao, + alertsDao: AlertsDao + ): PirateWeatherRepository = + PirateWeatherRepository(dao, weatherDao, api, apiKeysDao, alertsDao) @Provides @Singleton diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/domain/location/Location.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/domain/location/Location.kt index e748b2a6b..933233e76 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/domain/location/Location.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/domain/location/Location.kt @@ -1,8 +1,6 @@ package com.pranshulgg.weather_master_app.core.model.domain.location -import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource -import com.pranshulgg.weather_master_app.core.model.sources.AlertSource -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.model.weather.openmeteo.OpenMeteoModel data class Location( @@ -14,13 +12,13 @@ data class Location( val timezone: String, val countryCode: String?, val state: String, - val source: WeatherSource = WeatherSource.OPEN_METEO, + val source: Source = Source.OPEN_METEO, val isFavorite: Boolean = false, val isPinned: Boolean = false, val isDefault: Boolean, val isDeviceLocation: Boolean = false, - val alertSource: AlertSource = AlertSource.NONE, - val airQualitySource: AirQualitySource = AirQualitySource.OPEN_METEO, + val alertSource: Source = Source.NONE, + val airQualitySource: Source = Source.OPEN_METEO, val customName: String? = null, val openMeteoModel: OpenMeteoModel = OpenMeteoModel.BEST_MATCH, val alertsLastFetchedAt: Long? = null diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/sources/Source.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/sources/Source.kt new file mode 100644 index 000000000..8d8261daf --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/sources/Source.kt @@ -0,0 +1,252 @@ +package com.pranshulgg.weather_master_app.core.model.sources + +import com.pranshulgg.weather_master_app.R + + +// TODO: implement alerts / air quality for sources that support it +enum class Source( + val displayName: String, + val hourlyAggregationLimitHours: Int = 24, // stupid and should be removed + val displayLink: String, + val fullName: String, + val countryNameRes: Int? = null, + val requiresUserApiKey: Boolean = false, // Source must not be selectable until the user has provided their API key + val regionalButWorldwideSupport: Boolean = false, + val capabilities: Set, +// val providesAlerts: Boolean = false, +// val providesAirQuality: Boolean = false, +) { + OPEN_METEO( + displayName = "Open Meteo", + fullName = "Open Meteo", + displayLink = "https://open-meteo.com/", + capabilities = setOf(Capability.WEATHER, Capability.AIR_QUALITY) + ), + NWS( + displayName = "NWS", + fullName = "National Weather Service", + displayLink = "https://www.weather.gov/documentation/services-web-api", + countryNameRes = R.string.country_usa, + capabilities = setOf(Capability.WEATHER) + ), + SMHI( + displayName = "SMHI", + fullName = "Swedish Meteorological and Hydrological Institute", + displayLink = "https://opendata.smhi.se", + countryNameRes = R.string.country_sweden, + capabilities = setOf(Capability.WEATHER) + ), + DWD( + displayName = "DWD", + fullName = "Bright Sky DWD", + displayLink = "https://brightsky.dev", + countryNameRes = R.string.country_germany, + capabilities = setOf(Capability.WEATHER) + ), + METEO_FRANCE( + displayName = "Météo-France", + fullName = "Météo-France", + displayLink = "https://meteofrance.com/", + regionalButWorldwideSupport = true, + capabilities = setOf(Capability.WEATHER) + ), + ECCC( + displayName = "ECCC", + fullName = "Environment and Climate Change Canada", + displayLink = "https://app.weather.gc.ca/", + countryNameRes = R.string.country_canada, + capabilities = setOf(Capability.WEATHER) + ), + FMI( + displayName = "FMI", + fullName = "Finnish Meteorological Institute", + displayLink = "https://en.ilmatieteenlaitos.fi/", + countryNameRes = R.string.country_finland, + capabilities = setOf(Capability.WEATHER) + ), + CHINA( + displayName = "China", + fullName = "China National Environmental Monitoring Centre", + displayLink = "https://www.cnemc.cn/", + capabilities = setOf(Capability.WEATHER) + ), + BMKG( + displayName = "BMKG", + fullName = "Badan Meteorologi, Klimatologi, dan Geofisika", + displayLink = "https://www.bmkg.go.id/", + countryNameRes = R.string.country_indonesia, + capabilities = setOf(Capability.WEATHER) + ), + ACCU_WEATHER( + displayName = "AccuWeather", + fullName = "AccuWeather", + displayLink = "https://www.accuweather.com/", + capabilities = setOf(Capability.WEATHER, Capability.ALERTS, Capability.AIR_QUALITY) + ), + METEO_AM( + displayName = "Meteo AM", + fullName = "Meteorologia Aeronautica Militare", + displayLink = "https://www.meteoam.it/", + countryNameRes = R.string.country_italy, + capabilities = setOf(Capability.WEATHER) + ), + IPMA( + displayName = "IPMA", + fullName = "Instituto Português do Mar e da Atmosfera", + displayLink = "https://api.ipma.pt/", + countryNameRes = R.string.country_portugal, + capabilities = setOf(Capability.WEATHER) + ), + GISMETEO( + displayName = "Gismeteo", + fullName = "Gismeteo", + displayLink = "https://www.gismeteo.ru/", + countryNameRes = R.string.country_russia, + regionalButWorldwideSupport = true, + capabilities = setOf(Capability.WEATHER) + ), + MET_OFFICE( + displayName = "Met Office", + fullName = "Meteorological Office", + displayLink = "https://www.metoffice.gov.uk/", + countryNameRes = R.string.country_united_kingdom, + requiresUserApiKey = true, + regionalButWorldwideSupport = true, + capabilities = setOf(Capability.WEATHER) + ), + PIRATE_WEATHER( + displayName = "Pirate Weather", + fullName = "Pirate Weather", + displayLink = "https://pirateweather.net/", + requiresUserApiKey = true, + regionalButWorldwideSupport = false, + capabilities = setOf(Capability.WEATHER, Capability.ALERTS) + ), + IMD( + displayName = "IMD", + fullName = "India Meteorological Department", + displayLink = "https://mausam.imd.gov.in/", + countryNameRes = R.string.country_india, + capabilities = setOf(Capability.WEATHER) + ), + MET_NORWAY( + displayName = "Met Norway", + fullName = "Met Norway", + displayLink = "https://api.met.no/", + regionalButWorldwideSupport = true, + capabilities = setOf(Capability.WEATHER) + ), + AEMET( + displayName = "AEMET", + fullName = "Agencia Estatal de Meteorología", + displayLink = "https://opendata.aemet.es/centrodedescargas/altaUsuario", + countryNameRes = R.string.country_spain, + requiresUserApiKey = true, + capabilities = setOf(Capability.WEATHER) + ), + + WEATHER_API( + displayName = "WeatherApi", + fullName = "WeatherApi.com", + displayLink = "https://www.weatherapi.com/", + capabilities = setOf(Capability.ALERTS) + ), + WMO_SEVERE_WEATHER( + displayName = "WMO Severe Weather", + fullName = "WMO Severe Weather Information Centre", + displayLink = "https://www.wmo.int/", + capabilities = setOf(Capability.ALERTS) + ), + FPAS( + displayName = "FOSS Public Alert Server", + fullName = "FOSS Public Alert Server", + displayLink = "https://invent.kde.org/webapps/foss-public-alert-server", + capabilities = setOf(Capability.ALERTS) + ), + NONE( + displayName = "None", + fullName = "", + displayLink = "", + capabilities = setOf(Capability.ALERTS, Capability.AIR_QUALITY) + ), + CWA( + displayName = "CWA", + fullName = "Central Weather Administration", + displayLink = "https://opendata.cwa.gov.tw/", + countryNameRes = R.string.country_taiwan, + requiresUserApiKey = true, + capabilities = setOf(Capability.WEATHER) + ); + + // Sources that provide snow/rain as precipitation + fun providesSnowFall(): Boolean { + return when (this) { + MET_NORWAY -> false + DWD -> false + CHINA -> false + BMKG -> false + IMD -> false + CWA -> false + else -> true + } + } +} + + +// WE MAP EVERY WEATHER SOURCE HERE, AS THEY GET ADDED + +private val sourcesByCountry = buildMap { + put("US", listOf(Source.NWS)) + put("SE", listOf(Source.SMHI)) + put("DE", listOf(Source.DWD)) + put("CA", listOf(Source.ECCC)) + put("FI", listOf(Source.FMI)) + put("CN", listOf(Source.CHINA)) + put("ID", listOf(Source.BMKG)) + listOf("IT", "VA").forEach { put(it, listOf(Source.METEO_AM)) } + put("PT", listOf(Source.IPMA)) + put("RU", listOf(Source.GISMETEO)) + listOf("GB", "UK").forEach { put(it, listOf(Source.MET_OFFICE)) } + put("NO", listOf(Source.MET_NORWAY)) + put("FR", listOf(Source.METEO_FRANCE)) + put("ES", listOf(Source.AEMET)) + put("IN", listOf(Source.IMD)) + put("TW", listOf(Source.CWA)) + +} + +fun getSourcesForCountry(countryCode: String?): List { + return sourcesByCountry[countryCode] ?: emptyList() +} + + +// GLOBAL SOURCES +private val sourcesGlobal = listOf( + Source.OPEN_METEO, + Source.MET_NORWAY, + Source.METEO_FRANCE, + Source.ACCU_WEATHER, + Source.GISMETEO, + Source.MET_OFFICE, + Source.PIRATE_WEATHER +) + +fun getSourcesGlobal(): List { + return sourcesGlobal +} + +fun Source.isSourceSupportedFor(countryCode: String?): Boolean { + val source = sourcesByCountry[countryCode] ?: return false + + return this in source +} + +fun Source.isGlobal(): Boolean { + return this in sourcesGlobal +} + +enum class Capability { + WEATHER, + ALERTS, + AIR_QUALITY +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/sources/WeatherSource.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/sources/WeatherSource.kt deleted file mode 100644 index 40bb17a6e..000000000 --- a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/sources/WeatherSource.kt +++ /dev/null @@ -1,198 +0,0 @@ -package com.pranshulgg.weather_master_app.core.model.sources - -import android.content.Context -import com.pranshulgg.weather_master_app.R - -enum class WeatherSource( - val displayName: String, - val hourlyAggregationLimitHours: Int = 24, // stupid and should be removed - val displayLink: String, - val fullName: String, - val countryNameRes: Int? = null, - val requiresUserApiKey: Boolean = false, // Source must not be selectable until the user has provided their API key - val regionalButWorldwideSupport: Boolean = false -) { - OPEN_METEO( - displayName = "Open Meteo", - fullName = "Open Meteo", - displayLink = "https://open-meteo.com/" - ), - NWS( - displayName = "NWS", - fullName = "National Weather Service", - displayLink = "https://www.weather.gov/documentation/services-web-api", - countryNameRes = R.string.country_usa - ), - SMHI( - displayName = "SMHI", - fullName = "Swedish Meteorological and Hydrological Institute", - displayLink = "https://opendata.smhi.se", - countryNameRes = R.string.country_sweden - ), - DWD( - displayName = "DWD", - fullName = "Bright Sky DWD", - displayLink = "https://brightsky.dev", - countryNameRes = R.string.country_germany - ), - METEO_FRANCE( - displayName = "Météo-France", - fullName = "Météo-France", - displayLink = "https://meteofrance.com/", - regionalButWorldwideSupport = true - ), - ECCC( - displayName = "ECCC", - fullName = "Environment and Climate Change Canada", - displayLink = "https://app.weather.gc.ca/", - countryNameRes = R.string.country_canada - ), - FMI( - displayName = "FMI", - fullName = "Finnish Meteorological Institute", - displayLink = "https://en.ilmatieteenlaitos.fi/", - countryNameRes = R.string.country_finland - ), - CHINA( - displayName = "China", - fullName = "China National Environmental Monitoring Centre", - displayLink = "https://www.cnemc.cn/" - ), - BMKG( - displayName = "BMKG", - fullName = "Badan Meteorologi, Klimatologi, dan Geofisika", - displayLink = "https://www.bmkg.go.id/", - countryNameRes = R.string.country_indonesia - ), - ACCU_WEATHER( - displayName = "AccuWeather", - fullName = "AccuWeather", - displayLink = "https://www.accuweather.com/" - ), - METEO_AM( - displayName = "Meteo AM", - fullName = "Meteorologia Aeronautica Militare", - displayLink = "https://www.meteoam.it/", - countryNameRes = R.string.country_italy - ), - IPMA( - displayName = "IPMA", - fullName = "Instituto Português do Mar e da Atmosfera", - displayLink = "https://api.ipma.pt/", - countryNameRes = R.string.country_portugal - ), - GISMETEO( - displayName = "Gismeteo", - fullName = "Gismeteo", - displayLink = "https://www.gismeteo.ru/", - countryNameRes = R.string.country_russia, - regionalButWorldwideSupport = true - ), - MET_OFFICE( - displayName = "Met Office", - fullName = "Meteorological Office", - displayLink = "https://www.metoffice.gov.uk/", - countryNameRes = R.string.country_united_kingdom, - requiresUserApiKey = true, - regionalButWorldwideSupport = true - ), - PIRATE_WEATHER( - displayName = "Pirate Weather", - fullName = "Pirate Weather", - displayLink = "https://pirateweather.net/", - requiresUserApiKey = true, - regionalButWorldwideSupport = false - ), - IMD( - displayName = "IMD", - fullName = "India Meteorological Department", - displayLink = "https://mausam.imd.gov.in/", - countryNameRes = R.string.country_india - ), - MET_NORWAY( - displayName = "Met Norway", - fullName = "Met Norway", - displayLink = "https://api.met.no/", - regionalButWorldwideSupport = true - ), - AEMET( - displayName = "AEMET", - fullName = "Agencia Estatal de Meteorología", - displayLink = "https://opendata.aemet.es/centrodedescargas/altaUsuario", - countryNameRes = R.string.country_spain, - requiresUserApiKey = true - ), - CWA( - displayName = "CWA", - fullName = "Central Weather Administration", - displayLink = "https://opendata.cwa.gov.tw/", - countryNameRes = R.string.country_taiwan, - requiresUserApiKey = true - ); - - // Sources that provide snow/rain as precipitation - fun providesSnowFall(): Boolean { - return when (this) { - MET_NORWAY -> false - DWD -> false - CHINA -> false - BMKG -> false - IMD -> false - CWA -> false - else -> true - } - } -} - - -// WE MAP EVERY WEATHER SOURCE HERE, AS THEY GET ADDED - -private val weatherSourcesByCountry = buildMap { - put("US", listOf(WeatherSource.NWS)) - put("SE", listOf(WeatherSource.SMHI)) - put("DE", listOf(WeatherSource.DWD)) - put("CA", listOf(WeatherSource.ECCC)) - put("FI", listOf(WeatherSource.FMI)) - put("CN", listOf(WeatherSource.CHINA)) - put("ID", listOf(WeatherSource.BMKG)) - listOf("IT", "VA").forEach { put(it, listOf(WeatherSource.METEO_AM)) } - put("PT", listOf(WeatherSource.IPMA)) - put("RU", listOf(WeatherSource.GISMETEO)) - listOf("GB", "UK").forEach { put(it, listOf(WeatherSource.MET_OFFICE)) } - put("NO", listOf(WeatherSource.MET_NORWAY)) - put("FR", listOf(WeatherSource.METEO_FRANCE)) - put("ES", listOf(WeatherSource.AEMET)) - put("IN", listOf(WeatherSource.IMD)) - put("TW", listOf(WeatherSource.CWA)) - -} - -fun getWeatherSourcesForCountry(countryCode: String?): List { - return weatherSourcesByCountry[countryCode] ?: emptyList() -} - - -// GLOBAL SOURCES -private val weatherSourcesGlobal = listOf( - WeatherSource.OPEN_METEO, - WeatherSource.MET_NORWAY, - WeatherSource.METEO_FRANCE, - WeatherSource.ACCU_WEATHER, - WeatherSource.GISMETEO, - WeatherSource.MET_OFFICE, - WeatherSource.PIRATE_WEATHER -) - -fun getWeatherSourcesGlobal(): List { - return weatherSourcesGlobal -} - -fun WeatherSource.isSourceSupportedFor(countryCode: String?): Boolean { - val source = weatherSourcesByCountry[countryCode] ?: return false - - return this in source -} - -fun WeatherSource.isGlobal(): Boolean { - return this in weatherSourcesGlobal -} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/weather/WeatherResult.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/weather/WeatherResult.kt index 46dd79268..bcf76f55f 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/weather/WeatherResult.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/weather/WeatherResult.kt @@ -1,12 +1,16 @@ package com.pranshulgg.weather_master_app.core.model.weather +import com.pranshulgg.weather_master_app.core.model.domain.alerts.Alert import com.pranshulgg.weather_master_app.core.model.domain.weather.Weather sealed class WeatherResult { - data class Success(val weather: Weather) : WeatherResult() + data class Success(val weather: Weather, val alerts: List = emptyList()) : + WeatherResult() + data object RefreshNotAvailable : WeatherResult() data class Error(val exception: Exception, val cacheWeather: Weather? = null) : WeatherResult() + } enum class WeatherResultType { diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/search/geonames/GeoNamesSearchRepository.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/search/geonames/GeoNamesSearchRepository.kt index b77ced6d3..212b37e5c 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/search/geonames/GeoNamesSearchRepository.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/search/geonames/GeoNamesSearchRepository.kt @@ -2,7 +2,7 @@ package com.pranshulgg.weather_master_app.core.network.sources.search.geonames import com.pranshulgg.weather_master_app.core.model.domain.location.Location import com.pranshulgg.weather_master_app.data.local.mapper.locations.toDomain -import com.pranshulgg.weather_master_app.data.repository.SearchRepository +import com.pranshulgg.weather_master_app.data.repository.data.SearchRepository import javax.inject.Inject diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/utils/weather/forecast/ForecastMatching.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/utils/weather/forecast/ForecastMatching.kt index 15fa5d2eb..6bc9a0afd 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/core/utils/weather/forecast/ForecastMatching.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/utils/weather/forecast/ForecastMatching.kt @@ -3,7 +3,7 @@ package com.pranshulgg.weather_master_app.core.utils.weather.forecast import android.util.Log import com.pranshulgg.weather_master_app.core.model.domain.weather.WeatherDaily import com.pranshulgg.weather_master_app.core.model.domain.weather.WeatherHourly -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.utils.formatters.safeZoneId import java.time.Instant import java.time.ZoneId @@ -34,7 +34,7 @@ fun findMatchingDaily( fun findMatchingHourly( data: List, currentMilli: Long, - source: WeatherSource, + source: Source, zoneId: String, alwaysReturn24Hrs: Boolean = false, keepPastHour: Boolean = false diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/WeatherMasterDatabase.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/WeatherMasterDatabase.kt index d8c48a4fd..6ca418959 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/WeatherMasterDatabase.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/WeatherMasterDatabase.kt @@ -47,7 +47,7 @@ import com.pranshulgg.weather_master_app.data.local.entity.weather.units.AppWeat AlertEntity::class, ApiKeyEntity::class ], - version = 56, + version = 57, autoMigrations = [ AutoMigration(from = 39, to = 40), AutoMigration(from = 42, to = 43), @@ -59,7 +59,8 @@ import com.pranshulgg.weather_master_app.data.local.entity.weather.units.AppWeat AutoMigration(from = 50, to = 51), AutoMigration(from = 52, to = 53), AutoMigration(from = 53, to = 54), - AutoMigration(from = 54, to = 55) + AutoMigration(from = 54, to = 55), + AutoMigration(from = 56, to = 57) ] ) abstract class WeatherMasterDatabase : RoomDatabase() { diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/location/LocationsDao.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/location/LocationsDao.kt index 0796a4268..8129df56d 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/location/LocationsDao.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/location/LocationsDao.kt @@ -5,11 +5,8 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction -import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource -import com.pranshulgg.weather_master_app.core.model.sources.AlertSource -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.model.weather.openmeteo.OpenMeteoModel -import com.pranshulgg.weather_master_app.data.local.entity.alerts.AlertEntity import com.pranshulgg.weather_master_app.data.local.entity.location.WeatherLocationEntity import com.pranshulgg.weather_master_app.data.local.entity.weather.WeatherWithRelations import kotlinx.coroutines.flow.Flow @@ -63,7 +60,7 @@ interface LocationsDao { fun getAllLocationsCurrentWeather(): Flow> @Query("UPDATE weather_locations SET source = :source WHERE id = :id") - suspend fun updateSourceForLocation(id: String, source: WeatherSource) + suspend fun updateSourceForLocation(id: String, source: Source) @Query("DELETE FROM weather_daily WHERE locationId = :id") suspend fun deleteDailyDataForLocation(id: String) @@ -81,10 +78,10 @@ interface LocationsDao { suspend fun getLocationForId(id: String): WeatherLocationEntity @Query("UPDATE weather_locations SET airQualitySource = :source WHERE id = :id") - suspend fun updateAirQualitySourceForLocation(id: String, source: AirQualitySource) + suspend fun updateAirQualitySourceForLocation(id: String, source: Source) @Query("UPDATE weather_locations SET alertSource = :source WHERE id = :id") - suspend fun updateAlertSourceForLocation(id: String, source: AlertSource) + suspend fun updateAlertSourceForLocation(id: String, source: Source) @Query("UPDATE weather_locations SET customName = :name WHERE id = :id") diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/weather/WeatherDao.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/weather/WeatherDao.kt index 37d1cc532..b743de2a0 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/weather/WeatherDao.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/weather/WeatherDao.kt @@ -5,8 +5,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource -import com.pranshulgg.weather_master_app.data.local.entity.weather.ApiKeyEntity +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.data.local.entity.weather.CurrentWeatherEntity import com.pranshulgg.weather_master_app.data.local.entity.weather.DailyWeatherEntity import com.pranshulgg.weather_master_app.data.local.entity.weather.HourlyWeatherEntity @@ -54,7 +53,7 @@ interface WeatherDao { ) suspend fun getHourlyDataForLocation( locationId: String, - source: WeatherSource + source: Source ): List } \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/location/WeatherLocationEntity.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/location/WeatherLocationEntity.kt index 6c538b7b0..7c8572286 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/location/WeatherLocationEntity.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/location/WeatherLocationEntity.kt @@ -3,9 +3,7 @@ package com.pranshulgg.weather_master_app.data.local.entity.location import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey -import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource -import com.pranshulgg.weather_master_app.core.model.sources.AlertSource -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.model.weather.openmeteo.OpenMeteoModel import kotlin.uuid.ExperimentalUuidApi @@ -20,15 +18,15 @@ data class WeatherLocationEntity( val lat: Double, val lon: Double, val timezone: String, - val source: WeatherSource, + val source: Source, val state: String? = null, val isFavorite: Boolean = false, val isPinned: Boolean = false, val countryCode: String? = null, val isDefault: Boolean = false, val isDeviceLocation: Boolean = false, - val alertSource: AlertSource, - val airQualitySource: AirQualitySource, + val alertSource: Source, + val airQualitySource: Source, val customName: String? = null, @ColumnInfo(name = "openMeteoModel", defaultValue = "BEST_MATCH") diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/weather/HourlyWeatherEntity.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/weather/HourlyWeatherEntity.kt index fa1f2b623..82aa2c8e8 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/weather/HourlyWeatherEntity.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/weather/HourlyWeatherEntity.kt @@ -1,10 +1,9 @@ package com.pranshulgg.weather_master_app.data.local.entity.weather -import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.Index -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.model.weather.WeatherCondition import com.pranshulgg.weather_master_app.core.model.weather.wind.WindDirection import com.pranshulgg.weather_master_app.data.local.entity.location.WeatherLocationEntity @@ -39,5 +38,5 @@ data class HourlyWeatherEntity( val time: Long, val precipitationProbability: Int?, - val cachedSource: WeatherSource? = null + val cachedSource: Source? = null ) \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/provider/SearchRepositoryProvider.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/provider/SearchRepositoryProvider.kt index af3eff07e..cd7416e78 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/provider/SearchRepositoryProvider.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/provider/SearchRepositoryProvider.kt @@ -4,7 +4,7 @@ import com.pranshulgg.weather_master_app.core.model.sources.SearchSource import com.pranshulgg.weather_master_app.core.network.sources.search.accu.AccuSearchRepository import com.pranshulgg.weather_master_app.core.network.sources.search.geonames.GeoNamesSearchRepository import com.pranshulgg.weather_master_app.core.network.sources.search.openmeteo.OpenMeteoSearchRepository -import com.pranshulgg.weather_master_app.data.repository.SearchRepository +import com.pranshulgg.weather_master_app.data.repository.data.SearchRepository import javax.inject.Inject class SearchRepositoryProvider @Inject constructor( diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/provider/WeatherRepositoryProvider.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/provider/SourceRepositoryProvider.kt similarity index 56% rename from app/src/main/java/com/pranshulgg/weather_master_app/data/provider/WeatherRepositoryProvider.kt rename to app/src/main/java/com/pranshulgg/weather_master_app/data/provider/SourceRepositoryProvider.kt index 05914e589..39601fe33 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/provider/WeatherRepositoryProvider.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/provider/SourceRepositoryProvider.kt @@ -1,6 +1,9 @@ package com.pranshulgg.weather_master_app.data.provider -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source +import com.pranshulgg.weather_master_app.core.network.sources.alerts.fpas.FpasRepository +import com.pranshulgg.weather_master_app.core.network.sources.alerts.weatherapi.AlertsWeatherApiRepository +import com.pranshulgg.weather_master_app.core.network.sources.alerts.wmosevereweather.WmoSevereWeatherRepository import com.pranshulgg.weather_master_app.core.network.sources.weather.accu.AccuRepository import com.pranshulgg.weather_master_app.core.network.sources.weather.aemet.AemetRepository import com.pranshulgg.weather_master_app.core.network.sources.weather.bmkg.BmkgRepository @@ -20,10 +23,12 @@ import com.pranshulgg.weather_master_app.core.network.sources.weather.nws.NwsRep import com.pranshulgg.weather_master_app.core.network.sources.weather.openmeteo.OpenMeteoRepository import com.pranshulgg.weather_master_app.core.network.sources.weather.pirateweather.PirateWeatherRepository import com.pranshulgg.weather_master_app.core.network.sources.weather.smhi.SmhiRepository -import com.pranshulgg.weather_master_app.data.repository.WeatherRepository +import com.pranshulgg.weather_master_app.data.repository.data.AirQualityRepository +import com.pranshulgg.weather_master_app.data.repository.data.AlertRepository +import com.pranshulgg.weather_master_app.data.repository.data.WeatherRepository import javax.inject.Inject -class WeatherRepositoryProvider @Inject constructor( +class SourceRepositoryProvider @Inject constructor( private val openMeteoRepository: OpenMeteoRepository, private val nwsRepository: NwsRepository, private val metNorwayRepository: MetNorwayRepository, @@ -42,30 +47,55 @@ class WeatherRepositoryProvider @Inject constructor( private val aemetRepository: AemetRepository, private val imdRepository: ImdRepository, private val pirateWeatherRepository: PirateWeatherRepository, - private val cwaRepository: CwaRepository -) { + private val cwaRepository: CwaRepository, - fun getRepository(source: WeatherSource): WeatherRepository { - return when (source) { - WeatherSource.OPEN_METEO -> openMeteoRepository - WeatherSource.NWS -> nwsRepository - WeatherSource.MET_NORWAY -> metNorwayRepository - WeatherSource.SMHI -> smhiRepository - WeatherSource.DWD -> dwdRepository - WeatherSource.METEO_FRANCE -> meteoFranceRepository - WeatherSource.ECCC -> ecccRepository - WeatherSource.FMI -> fmiRepository - WeatherSource.CHINA -> chinaRepository - WeatherSource.BMKG -> bmkgRepository - WeatherSource.ACCU_WEATHER -> accuRepository - WeatherSource.METEO_AM -> meteoamRepository - WeatherSource.IPMA -> ipmaRepository - WeatherSource.GISMETEO -> gismeteoRepository - WeatherSource.MET_OFFICE -> metOfficeRepository - WeatherSource.AEMET -> aemetRepository - WeatherSource.IMD -> imdRepository - WeatherSource.PIRATE_WEATHER -> pirateWeatherRepository - WeatherSource.CWA -> cwaRepository + // ALERTS + private val alertsWeatherApiRepository: AlertsWeatherApiRepository, + private val wmoSevereWeatherRepository: WmoSevereWeatherRepository, + private val fpasRepository: FpasRepository, + + ) { + + val repositories = listOf( + openMeteoRepository, + nwsRepository, + metNorwayRepository, + smhiRepository, + dwdRepository, + meteoFranceRepository, + ecccRepository, + fmiRepository, + chinaRepository, + bmkgRepository, + accuRepository, + meteoamRepository, + ipmaRepository, + gismeteoRepository, + metOfficeRepository, + aemetRepository, + imdRepository, + pirateWeatherRepository, + cwaRepository, + alertsWeatherApiRepository, + wmoSevereWeatherRepository, + fpasRepository, + ) + + fun getWeatherRepository(source: Source): WeatherRepository { + return repositories.filterIsInstance().firstOrNull { + it.weatherSource == source + } ?: openMeteoRepository + } + + fun getAlertRepository(source: Source): AlertRepository? { + return repositories.filterIsInstance().firstOrNull { + it.alertSource == source + } + } + + fun getAirQualityRepository(source: Source): AirQualityRepository? { + return repositories.filterIsInstance().firstOrNull { + it.airQualitySource == source } } diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/LocationsRepository.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/LocationsRepository.kt index 527911c1b..ddfba8fc1 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/LocationsRepository.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/LocationsRepository.kt @@ -4,18 +4,14 @@ import android.content.Context import androidx.room.Transaction import com.pranshulgg.weather_master_app.core.model.domain.AppException import com.pranshulgg.weather_master_app.core.model.domain.airquality.AirQuality -import com.pranshulgg.weather_master_app.core.model.domain.alerts.Alert import com.pranshulgg.weather_master_app.core.model.domain.location.Location import com.pranshulgg.weather_master_app.core.model.domain.weather.Weather -import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource -import com.pranshulgg.weather_master_app.core.model.sources.AlertSource -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.model.weather.openmeteo.OpenMeteoModel import com.pranshulgg.weather_master_app.core.network.sources.address.nominatim.json.NominatimRepository import com.pranshulgg.weather_master_app.data.local.dao.airquality.AirQualityDao import com.pranshulgg.weather_master_app.data.local.dao.location.LocationsDao import com.pranshulgg.weather_master_app.data.local.mapper.airquality.toDomain -import com.pranshulgg.weather_master_app.data.local.mapper.alerts.toDomain import com.pranshulgg.weather_master_app.data.local.mapper.locations.toDomain import com.pranshulgg.weather_master_app.data.local.mapper.locations.toEntity import com.pranshulgg.weather_master_app.data.local.mapper.weather.toDomain @@ -72,7 +68,7 @@ class LocationsRepository @Inject constructor( } - suspend fun updateSourceForLocation(id: String, source: WeatherSource) { + suspend fun updateSourceForLocation(id: String, source: Source) { dao.updateSourceForLocation(id, source) } @@ -80,11 +76,11 @@ class LocationsRepository @Inject constructor( dao.updateOpenMeteoModelForLocation(id, model) } - suspend fun updateAirQualitySourceForLocation(id: String, source: AirQualitySource) { + suspend fun updateAirQualitySourceForLocation(id: String, source: Source) { dao.updateAirQualitySourceForLocation(id, source) } - suspend fun updateAlertSourceForLocation(id: String, source: AlertSource) { + suspend fun updateAlertSourceForLocation(id: String, source: Source) { dao.updateAlertSourceForLocation(id, source) } diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/WeatherDataReconcilerRepository.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/WeatherDataReconcilerRepository.kt index cc432ca47..89fc60290 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/WeatherDataReconcilerRepository.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/WeatherDataReconcilerRepository.kt @@ -1,8 +1,7 @@ package com.pranshulgg.weather_master_app.data.repository -import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource -import com.pranshulgg.weather_master_app.core.model.sources.AlertSource -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.domain.location.Location +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.data.local.dao.airquality.AirQualityDao import com.pranshulgg.weather_master_app.data.local.dao.alerts.AlertsDao import com.pranshulgg.weather_master_app.data.local.dao.location.LocationKeysDao @@ -25,55 +24,85 @@ class WeatherDataReconcilerRepository @Inject constructor( * NWS might have saved grid points * which are important to be removed from the DB so they don't end up stale */ - suspend fun cleanUpStaleData( - previousSource: WeatherSource, + suspend fun reconcileSourceChange( + previous: Location, + updated: Location + ) { + if (previous.source != updated.source) { + cleanUpStaleWeatherData( + previousSource = previous.source, + locationId = previous.id, + airQualitySource = updated.airQualitySource, + currentAlertSource = updated.alertSource + ) + } + + if (previous.airQualitySource != updated.airQualitySource) { + cleanUpStaleAirQualityData( + locationId = previous.id, + currentWeatherSource = updated.source, + currentAlertSource = updated.alertSource + ) + } + + if (previous.alertSource != updated.alertSource) { + cleanUpStaleAlertsData( + locationId = previous.id, + currentWeatherSource = updated.source, + airQualitySource = updated.airQualitySource + ) + } + } + + private suspend fun cleanUpStaleWeatherData( + previousSource: Source, locationId: String, - airQualitySource: AirQualitySource, - currentAlertSource: AlertSource + airQualitySource: Source, + currentAlertSource: Source ) { when (previousSource) { - WeatherSource.NWS -> nwsDao.deleteGridPointsForLocation(locationId) - WeatherSource.ACCU_WEATHER -> cleanAccuWeather( + Source.NWS -> nwsDao.deleteGridPointsForLocation(locationId) + + Source.ACCU_WEATHER -> cleanAccuWeather( locationId, airQualitySource, currentAlertSource ) - WeatherSource.IPMA -> locationKeysDao.getCityKeyForLocation(locationId) else -> {} } } private suspend fun cleanAccuWeather( - locationId: String, airQualitySource: AirQualitySource, - currentAlertSource: AlertSource + locationId: String, airQualitySource: Source, + currentAlertSource: Source ) { - if (airQualitySource != AirQualitySource.ACCU_WEATHER || currentAlertSource != AlertSource.ACCU_WEATHER) { + if (airQualitySource != Source.ACCU_WEATHER || currentAlertSource != Source.ACCU_WEATHER) { locationKeysDao.deleteCityKeyForLocation(locationId) } } - suspend fun cleanUpStaleAirQualityData( + private suspend fun cleanUpStaleAirQualityData( locationId: String, - currentWeatherSource: WeatherSource, - currentAlertSource: AlertSource + currentWeatherSource: Source, + currentAlertSource: Source ) { airQualityDao.deleteCurrentAirQuality(locationId) airQualityDao.deleteHourlyAirQuality(locationId) - if (currentWeatherSource != WeatherSource.ACCU_WEATHER || currentAlertSource != AlertSource.ACCU_WEATHER) { + if (currentWeatherSource != Source.ACCU_WEATHER || currentAlertSource != Source.ACCU_WEATHER) { locationKeysDao.deleteCityKeyForLocation(locationId) } } - suspend fun cleanUpStaleAlertsData( + private suspend fun cleanUpStaleAlertsData( locationId: String, - currentWeatherSource: WeatherSource, - airQualitySource: AirQualitySource, + currentWeatherSource: Source, + airQualitySource: Source, ) { alertsDao.deleteAlertsForLocation(locationId) - if (currentWeatherSource != WeatherSource.ACCU_WEATHER || airQualitySource != AirQualitySource.ACCU_WEATHER) { + if (currentWeatherSource != Source.ACCU_WEATHER || airQualitySource != Source.ACCU_WEATHER) { locationKeysDao.deleteCityKeyForLocation(locationId) } } diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/WeatherRepository.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/WeatherRepository.kt deleted file mode 100644 index 8e7159dfb..000000000 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/WeatherRepository.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.pranshulgg.weather_master_app.data.repository - -import com.pranshulgg.weather_master_app.core.model.weather.WeatherResult -import com.pranshulgg.weather_master_app.core.model.domain.location.Location - -interface WeatherRepository { - suspend fun getWeather( - location: Location, - isManualRefresh: Boolean = false, - isForceRefresh: Boolean - ): WeatherResult - -} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/SearchRepository.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/data/SearchRepository.kt similarity index 71% rename from app/src/main/java/com/pranshulgg/weather_master_app/data/repository/SearchRepository.kt rename to app/src/main/java/com/pranshulgg/weather_master_app/data/repository/data/SearchRepository.kt index 8995db515..cd0378160 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/SearchRepository.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/data/SearchRepository.kt @@ -1,8 +1,8 @@ -package com.pranshulgg.weather_master_app.data.repository +package com.pranshulgg.weather_master_app.data.repository.data import com.pranshulgg.weather_master_app.core.model.domain.location.Location interface SearchRepository { suspend fun search(query: String): List -} +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/data/SourceDataRepository.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/data/SourceDataRepository.kt new file mode 100644 index 000000000..8e0ebf8cc --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/data/SourceDataRepository.kt @@ -0,0 +1,105 @@ +package com.pranshulgg.weather_master_app.data.repository.data + +import com.pranshulgg.weather_master_app.core.model.domain.location.Location +import com.pranshulgg.weather_master_app.core.model.sources.Source +import com.pranshulgg.weather_master_app.core.model.weather.WeatherResult +import com.pranshulgg.weather_master_app.core.model.weather.airquality.AirQualityResult +import com.pranshulgg.weather_master_app.core.model.weather.alerts.AlertResult +import com.pranshulgg.weather_master_app.data.provider.SourceRepositoryProvider +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import javax.inject.Inject + +class SourceDataRepository @Inject constructor( + private val sourceRepositoryProvider: SourceRepositoryProvider +) { + suspend fun getData( + location: Location, + isManualRefresh: Boolean = false, + isForceRefresh: Boolean = false, + isForceRefreshForAirQuality: Boolean = false, + isForceRefreshForAlerts: Boolean = false, + onWeather: suspend (WeatherResult) -> Unit, + onAlerts: suspend (AlertResult) -> Unit, + onAirQuality: suspend (AirQualityResult) -> Unit, + ) = coroutineScope { + + val weatherSource = location.source + val alertSource = location.alertSource + val airQualitySource = location.airQualitySource + + val weatherRepo = sourceRepositoryProvider.getWeatherRepository(weatherSource) + + val weatherJob = async { + + + weatherRepo.getWeather( + location, + isManualRefresh, + isForceRefresh + ) + } + + if (alertSource == weatherSource && weatherRepo.providesAlerts) { + + launch { + weatherJob.await() + + val repo = sourceRepositoryProvider.getAlertRepository(alertSource) + + if (repo != null) { + onAlerts(repo.getAlerts(location = location)) + } + } + + } else { + launch { + val repo = sourceRepositoryProvider.getAlertRepository(alertSource) + + if (repo != null) { + onAlerts( + repo.getAlerts( + location = location, + isManualRefresh = isManualRefresh, + isForceRefresh = isForceRefreshForAlerts + ) + ) + } + } + } + + if (airQualitySource == weatherSource && weatherRepo.providesAirQuality) { + launch { + weatherJob.await() + + val repo = sourceRepositoryProvider.getAirQualityRepository(airQualitySource) + + if (repo != null) { + onAirQuality(repo.getAirQuality(location = location)) + } + } + + } else { + launch { + val repo = sourceRepositoryProvider.getAirQualityRepository(airQualitySource) + + if (repo != null) { + onAirQuality( + repo.getAirQuality( + location = location, + isManualRefresh = isManualRefresh, + isForceRefresh = isForceRefreshForAirQuality + ) + ) + } + } + } + + launch { + onWeather(weatherJob.await()) + } + + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/data/WeatherRepository.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/data/WeatherRepository.kt new file mode 100644 index 000000000..ba5bd259e --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/data/WeatherRepository.kt @@ -0,0 +1,24 @@ +package com.pranshulgg.weather_master_app.data.repository.data + +import com.pranshulgg.weather_master_app.core.model.domain.location.Location +import com.pranshulgg.weather_master_app.core.model.sources.Source +import com.pranshulgg.weather_master_app.core.model.weather.WeatherResult + +interface WeatherRepository { + val weatherSource: Source + + // Keep them true if air quality or alerts are provided along with the weather response + // rather than a separate API endpoint + val providesAlerts: Boolean + get() = false + + val providesAirQuality: Boolean + get() = false + + suspend fun getWeather( + location: Location, + isManualRefresh: Boolean = false, + isForceRefresh: Boolean + ): WeatherResult + +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/worker/WeatherWorker.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/worker/WeatherWorker.kt index f104ae6fa..fd0dde8b4 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/worker/WeatherWorker.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/worker/WeatherWorker.kt @@ -2,8 +2,6 @@ package com.pranshulgg.weather_master_app.data.worker import android.Manifest import android.content.Context -import android.content.SharedPreferences -import android.util.Log import androidx.annotation.RequiresPermission import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker @@ -12,7 +10,7 @@ import com.pranshulgg.weather_master_app.core.model.domain.weather.Weather import com.pranshulgg.weather_master_app.core.model.domain.weather.WeatherUnits import com.pranshulgg.weather_master_app.core.model.weather.WeatherResult import com.pranshulgg.weather_master_app.core.prefs.helper.PreferencesHelper -import com.pranshulgg.weather_master_app.data.provider.WeatherRepositoryProvider +import com.pranshulgg.weather_master_app.data.provider.SourceRepositoryProvider import com.pranshulgg.weather_master_app.data.repository.LocationsRepository import com.pranshulgg.weather_master_app.data.repository.WeatherUnitsRepository import com.pranshulgg.weather_master_app.data.worker.gadgetbridge.sendGadgetBridgeWeatherData @@ -22,13 +20,12 @@ import com.pranshulgg.weather_master_app.data.worker.widgets.WeatherWidgetUpdate import com.pranshulgg.weather_master_app.data.worker.widgets.widgetWeatherMapper import dagger.assisted.Assisted import dagger.assisted.AssistedInject -import kotlinx.coroutines.delay @HiltWorker class WeatherWorker @AssistedInject constructor( @Assisted context: Context, @Assisted params: WorkerParameters, - private val repositoryProvider: WeatherRepositoryProvider, + private val repositoryProvider: SourceRepositoryProvider, private val locationsRepository: LocationsRepository, private val appVisibility: AppVisibility, private val weatherUnitsRepository: WeatherUnitsRepository @@ -64,7 +61,7 @@ class WeatherWorker @AssistedInject constructor( // Get the repository - val repo = repositoryProvider.getRepository(default.source) + val repo = repositoryProvider.getWeatherRepository(default.source) val result = repo.getWeather( location = default, diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/worker/widgets/WidgetReload.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/worker/widgets/WidgetReload.kt index e37a304ea..f0f7b6d77 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/worker/widgets/WidgetReload.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/worker/widgets/WidgetReload.kt @@ -1,16 +1,15 @@ package com.pranshulgg.weather_master_app.data.worker.widgets import android.content.Context -import android.content.SharedPreferences import com.pranshulgg.weather_master_app.core.model.weather.WeatherResult import com.pranshulgg.weather_master_app.core.prefs.helper.PreferencesHelper -import com.pranshulgg.weather_master_app.data.provider.WeatherRepositoryProvider +import com.pranshulgg.weather_master_app.data.provider.SourceRepositoryProvider import com.pranshulgg.weather_master_app.data.repository.LocationsRepository import com.pranshulgg.weather_master_app.data.repository.WeatherUnitsRepository import jakarta.inject.Inject class WidgetReload @Inject constructor( - private val repositoryProvider: WeatherRepositoryProvider, + private val repositoryProvider: SourceRepositoryProvider, private val locationsRepository: LocationsRepository, private val weatherUnitsRepository: WeatherUnitsRepository ) { @@ -24,7 +23,7 @@ class WidgetReload @Inject constructor( if (defaultLocation == null || units == null) return - val repo = repositoryProvider.getRepository(defaultLocation.source) + val repo = repositoryProvider.getWeatherRepository(defaultLocation.source) val result = repo.getWeather( location = defaultLocation, diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/blocks/BlocksScreenViewModel.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/blocks/BlocksScreenViewModel.kt index 9cb9cca47..6dbdb7d62 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/blocks/BlocksScreenViewModel.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/blocks/BlocksScreenViewModel.kt @@ -5,7 +5,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.pranshulgg.weather_master_app.core.model.domain.weather.WeatherUnits -import com.pranshulgg.weather_master_app.core.network.sources.airquality.openmeteo.OpenMeteoAqiRepository import com.pranshulgg.weather_master_app.data.repository.LocationsRepository import com.pranshulgg.weather_master_app.data.repository.WeatherUnitsRepository import dagger.hilt.android.lifecycle.HiltViewModel diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationScreen.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationScreen.kt index 750342569..7ae30fa2d 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationScreen.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationScreen.kt @@ -1,8 +1,6 @@ package com.pranshulgg.weather_master_app.feature.editlocation -import android.util.Log import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize @@ -22,7 +20,6 @@ import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SheetValue import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.material3.rememberBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -39,9 +36,7 @@ import androidx.navigation.NavController import com.pranshulgg.weather_master_app.R import com.pranshulgg.weather_master_app.core.model.domain.location.Location import com.pranshulgg.weather_master_app.core.model.domain.weather.ApiKey -import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource -import com.pranshulgg.weather_master_app.core.model.sources.AlertSource -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.model.weather.openmeteo.OpenMeteoModel import com.pranshulgg.weather_master_app.core.ui.components.Gap import com.pranshulgg.weather_master_app.core.ui.components.LargeTopBarScaffold @@ -55,15 +50,14 @@ import com.pranshulgg.weather_master_app.feature.editlocation.ui.EditLocationBot import com.pranshulgg.weather_master_app.feature.editlocation.ui.EditLocationScreenDialogs import com.pranshulgg.weather_master_app.feature.shared.WeatherViewModel import com.pranshulgg.weather_master_app.feature.shared.ui.SharedBottomSheet -import com.pranshulgg.weather_master_app.feature.shared.ui.SharedDialogs data class EditLocationScreenUiState( val location: Location? = null, val isWeatherSourcesForLocationSheetOpen: Boolean = false, - val selectedWeatherSource: WeatherSource? = null, - val selectedAlertSource: AlertSource? = null, - val selectedAirQualitySource: AirQualitySource? = null, + val selectedWeatherSource: Source? = null, + val selectedAlertSource: Source? = null, + val selectedAirQualitySource: Source? = null, val isAlertSourcesSheetOpen: Boolean = false, val isAirQualitySourcesSheetOpen: Boolean = false, val isEditLocationNameSheetOpen: Boolean = false, @@ -115,28 +109,25 @@ fun EditLocationScreen( var currentLocationName by remember { mutableStateOf(locationName) } + val effectiveWeatherSource = + uiState.selectedWeatherSource ?: uiState.location.source + + val effectiveOpenMeteoModel = + uiState.selectedOpenMeteoModel + ?: uiState.location.openMeteoModel + val selectedWeatherSourceString = buildString { - if (uiState.selectedWeatherSource != null) { - append(uiState.selectedWeatherSource.displayName) - } else { - append(uiState.location.source.displayName) + append(effectiveWeatherSource.displayName) + + effectiveWeatherSource.countryNameRes?.let { + append(" (${stringResource(it)})") } - if (uiState.selectedWeatherSource?.countryNameRes != null) { - append(" (${stringResource(uiState.selectedWeatherSource.countryNameRes)})") - } else if (uiState.location.source.countryNameRes != null && uiState.selectedWeatherSource == null) { - append(" (${stringResource(uiState.location.source.countryNameRes)})") + if ( + effectiveWeatherSource == Source.OPEN_METEO) { + append(" (${effectiveOpenMeteoModel.displayName})") } - - if (uiState.selectedWeatherSource == WeatherSource.OPEN_METEO) { - append(" (${uiState.selectedOpenMeteoModel?.displayName})") - } else if (uiState.location.source == WeatherSource.OPEN_METEO && uiState.selectedWeatherSource == null) { - append(" (${uiState.location.openMeteoModel.displayName})") - } - } - - LargeTopBarScaffold( title = stringResource(R.string.location_edit), navigationIcon = { NavigateUpBtn(navController) }, @@ -189,8 +180,8 @@ fun EditLocationScreen( }, trailing = { val showButton = if (uiState.selectedWeatherSource != null) - uiState.selectedWeatherSource == WeatherSource.OPEN_METEO else - uiState.location.source == WeatherSource.OPEN_METEO + uiState.selectedWeatherSource == Source.OPEN_METEO else + uiState.location.source == Source.OPEN_METEO if (showButton) { IconButton( @@ -285,7 +276,7 @@ fun EditLocationScreen( selectedSource = uiState.selectedWeatherSource ?: uiState.location.source, onSave = { viewModel.updateSelectedWeatherSource(it) - if (it == WeatherSource.OPEN_METEO) { + if (it == Source.OPEN_METEO) { viewModel.updateSelectedOpenMeteoModel( uiState.selectedOpenMeteoModel ?: uiState.location.openMeteoModel ) diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationViewModel.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationViewModel.kt index 7423aee98..46d788863 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationViewModel.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationViewModel.kt @@ -4,12 +4,8 @@ import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.pranshulgg.weather_master_app.R -import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource -import com.pranshulgg.weather_master_app.core.model.sources.AlertSource -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.model.weather.openmeteo.OpenMeteoModel -import com.pranshulgg.weather_master_app.core.ui.snackbar.SnackbarManager import com.pranshulgg.weather_master_app.data.repository.ApiKeysRepository import com.pranshulgg.weather_master_app.data.repository.LocationsRepository import dagger.hilt.android.lifecycle.HiltViewModel @@ -54,15 +50,15 @@ class EditLocationViewModel @Inject constructor( _uiState.value = _uiState.value.copy(isWeatherSourcesForLocationSheetOpen = false) } - fun updateSelectedWeatherSource(source: WeatherSource) { + fun updateSelectedWeatherSource(source: Source) { _uiState.value = _uiState.value.copy(selectedWeatherSource = source) } - fun updateSelectedAlertSource(source: AlertSource) { + fun updateSelectedAlertSource(source: Source) { _uiState.value = _uiState.value.copy(selectedAlertSource = source) } - fun updateSelectedAirQualitySource(source: AirQualitySource) { + fun updateSelectedAirQualitySource(source: Source) { _uiState.value = _uiState.value.copy(selectedAirQualitySource = source) } diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/ui/EditLocationBottomSheet.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/ui/EditLocationBottomSheet.kt index db6e3b773..7a5c59c56 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/ui/EditLocationBottomSheet.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/ui/EditLocationBottomSheet.kt @@ -24,13 +24,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.pranshulgg.weather_master_app.R -import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource -import com.pranshulgg.weather_master_app.core.model.sources.AlertSource -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource -import com.pranshulgg.weather_master_app.core.model.sources.getWeatherSourcesForCountry -import com.pranshulgg.weather_master_app.core.model.sources.getWeatherSourcesGlobal +import com.pranshulgg.weather_master_app.core.model.sources.Capability +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.model.weather.openmeteo.OpenMeteoModel -import com.pranshulgg.weather_master_app.core.model.weather.openmeteo.OpenMeteoModelType import com.pranshulgg.weather_master_app.core.ui.components.ActionBottomSheet import com.pranshulgg.weather_master_app.core.ui.components.Gap import com.pranshulgg.weather_master_app.core.ui.components.SettingSection @@ -78,8 +74,8 @@ object EditLocationBottomSheet { fun AlertSourcesSheet( show: Boolean, sheetState: SheetState, - selectedSource: AlertSource = AlertSource.NONE, - onSave: (AlertSource) -> Unit, + selectedSource: Source = Source.NONE, + onSave: (Source) -> Unit, onDismiss: () -> Unit ) { if (show) { @@ -91,6 +87,9 @@ object EditLocationBottomSheet { mutableStateOf(selectedSource) } + val sources = Source.entries.filter { + Capability.ALERTS in it.capabilities + } ActionBottomSheet( sheetState = sheetState, onCancel = { onDismiss() }, @@ -100,7 +99,7 @@ object EditLocationBottomSheet { ) { SettingSection( title = stringResource(R.string.global_sources), - tiles = AlertSource.entries.map { source -> + tiles = sources.map { source -> val isSelected = currentSelectedSource == source SettingTile.ActionTile( @@ -129,8 +128,8 @@ object EditLocationBottomSheet { fun AirQualitySourcesSheet( show: Boolean, sheetState: SheetState, - selectedSource: AirQualitySource = AirQualitySource.OPEN_METEO, - onSave: (AirQualitySource) -> Unit, + selectedSource: Source = Source.OPEN_METEO, + onSave: (Source) -> Unit, onDismiss: () -> Unit ) { if (show) { @@ -142,6 +141,9 @@ object EditLocationBottomSheet { mutableStateOf(selectedSource) } + val sources = Source.entries.filter { + Capability.AIR_QUALITY in it.capabilities + } ActionBottomSheet( sheetState = sheetState, onCancel = { onDismiss() }, @@ -151,7 +153,7 @@ object EditLocationBottomSheet { ) { SettingSection( title = stringResource(R.string.global_sources), - tiles = AirQualitySource.entries.map { source -> + tiles = sources.map { source -> val isSelected = currentSelectedSource == source SettingTile.ActionTile( diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/intro/IntroScreen.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/intro/IntroScreen.kt index c63ec4290..b9777de0b 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/intro/IntroScreen.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/intro/IntroScreen.kt @@ -39,7 +39,7 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.navigation.NavController import com.pranshulgg.weather_master_app.R import com.pranshulgg.weather_master_app.core.model.domain.location.Address -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.model.domain.location.Location import com.pranshulgg.weather_master_app.core.ui.components.Gap import com.pranshulgg.weather_master_app.core.ui.components.Symbol @@ -279,7 +279,7 @@ fun DeviceLocation.toDomain(context: Context): Location { timezone = ZoneId.systemDefault().id, countryCode = "", state = "", - source = WeatherSource.OPEN_METEO, + source = Source.OPEN_METEO, isFavorite = false, isPinned = false, isDefault = false, // Repository can handle it diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/locations/LocationsScreenViewModel.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/locations/LocationsScreenViewModel.kt index 4f17d397a..94f476c14 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/locations/LocationsScreenViewModel.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/locations/LocationsScreenViewModel.kt @@ -1,11 +1,9 @@ package com.pranshulgg.weather_master_app.feature.locations -import android.util.Log import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import coil.util.Logger import com.pranshulgg.weather_master_app.core.model.domain.AppException import com.pranshulgg.weather_master_app.core.model.domain.location.Location import com.pranshulgg.weather_master_app.core.model.domain.toMessageRes diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/MainScreen.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/MainScreen.kt index 907dd1ff4..4d39d9156 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/MainScreen.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/MainScreen.kt @@ -1,7 +1,5 @@ package com.pranshulgg.weather_master_app.feature.main -import android.util.Log -import android.widget.Toast import androidx.activity.compose.BackHandler import androidx.compose.material3.DrawerValue import androidx.compose.material3.ExperimentalMaterial3Api @@ -9,11 +7,9 @@ import androidx.compose.material3.SheetValue import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.rememberBottomSheetState import androidx.compose.material3.rememberDrawerState -import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity @@ -30,7 +26,6 @@ import com.pranshulgg.weather_master_app.core.model.domain.location.Location import com.pranshulgg.weather_master_app.core.model.domain.weather.Weather import com.pranshulgg.weather_master_app.core.model.domain.weather.WeatherBlock import com.pranshulgg.weather_master_app.core.model.domain.weather.WeatherUnits -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource import com.pranshulgg.weather_master_app.core.prefs.helper.PreferencesHelper import com.pranshulgg.weather_master_app.core.ui.navigation.NavRoutes import com.pranshulgg.weather_master_app.core.ui.snackbar.SnackbarManager @@ -42,7 +37,6 @@ import com.pranshulgg.weather_master_app.feature.main.ui.MainScreenDialogs import com.pranshulgg.weather_master_app.feature.main.ui.NavigationDrawer import com.pranshulgg.weather_master_app.feature.shared.WeatherViewModel import com.pranshulgg.weather_master_app.feature.shared.ui.SharedBottomSheet -import com.pranshulgg.weather_master_app.feature.shared.ui.SharedDialogs import kotlinx.coroutines.launch data class MainScreenWeatherUiState( diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/components/CreditsBottomSection.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/components/CreditsBottomSection.kt index 60fc4e98c..ab0228750 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/components/CreditsBottomSection.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/components/CreditsBottomSection.kt @@ -14,11 +14,11 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import com.pranshulgg.weather_master_app.core.model.domain.weather.Weather -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource import com.pranshulgg.weather_master_app.core.ui.components.Gap import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.ui.res.stringResource import com.pranshulgg.weather_master_app.R +import com.pranshulgg.weather_master_app.core.model.sources.Source @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable @@ -27,7 +27,7 @@ fun CreditsBottomSection(weather: Weather?, onClick: () -> Unit) { val source = weather?.location?.source val sourceString = source?.displayName - ?: WeatherSource.OPEN_METEO.displayName + ?: Source.OPEN_METEO.displayName val countryString = buildString { if (source?.countryNameRes != null) { diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/data/WeatherAutoRefreshForeground.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/data/WeatherAutoRefreshForeground.kt index d1663bb0a..21a9c2280 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/data/WeatherAutoRefreshForeground.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/data/WeatherAutoRefreshForeground.kt @@ -28,12 +28,12 @@ fun WeatherAutoRefreshForeground( // isInitialized guard avoids duplicating setActiveLocation()'s // cold-start fetch (Android replays ON_START on resubscribe, // which happens the moment `location` first becomes non-null). - if (weatherViewModel.uiState.value.isInitialized) { - weatherViewModel.getWeather( - location = it, - source = it.source - ) - } +// if (weatherViewModel.uiState.value.isInitialized) { +// weatherViewModel.getWeather( +// location = it, +// source = it.source +// ) +// } } } diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/ui/MainScreenBottomSheets.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/ui/MainScreenBottomSheets.kt index 5e38f84c3..84e3f6e9f 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/ui/MainScreenBottomSheets.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/ui/MainScreenBottomSheets.kt @@ -1,35 +1,19 @@ package com.pranshulgg.weather_master_app.feature.main.ui -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ListItem -import androidx.compose.material3.ListItemDefaults -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SheetState -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.platform.UriHandler import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.pranshulgg.weather_master_app.R import com.pranshulgg.weather_master_app.core.model.domain.location.Location -import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource -import com.pranshulgg.weather_master_app.core.model.sources.AlertSource -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.ui.components.ActionBottomSheet -import com.pranshulgg.weather_master_app.core.ui.components.DialogBasic import com.pranshulgg.weather_master_app.core.ui.components.Gap import com.pranshulgg.weather_master_app.core.ui.components.SettingSection import com.pranshulgg.weather_master_app.core.ui.components.SettingTile import com.pranshulgg.weather_master_app.core.ui.components.SettingsTileIcon -import com.pranshulgg.weather_master_app.core.ui.components.Symbol import com.pranshulgg.weather_master_app.feature.main.MainScreenViewModel object MainScreenBottomSheets { @@ -57,7 +41,7 @@ object MainScreenBottomSheets { append(" (${stringResource(location.source.countryNameRes)})") } - if (location.source == WeatherSource.OPEN_METEO) { + if (location.source == Source.OPEN_METEO) { append(" (${location.openMeteoModel.displayName})") } @@ -86,7 +70,7 @@ object MainScreenBottomSheets { ) ) ) - if (location.airQualitySource != AirQualitySource.NONE) { + if (location.airQualitySource != Source.NONE) { Gap(8.dp) SettingSection( title = stringResource(R.string.weather_air_quality), @@ -102,7 +86,7 @@ object MainScreenBottomSheets { ) ) } - if (location.alertSource != AlertSource.NONE) { + if (location.alertSource != Source.NONE) { Gap(8.dp) SettingSection( title = "Alerts", diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/search/SearchScreen.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/search/SearchScreen.kt index 039a4b8b2..214946d76 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/search/SearchScreen.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/search/SearchScreen.kt @@ -20,7 +20,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SheetValue import androidx.compose.material3.Text import androidx.compose.material3.rememberBottomSheetState -import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -37,9 +36,8 @@ import com.pranshulgg.weather_master_app.R import com.pranshulgg.weather_master_app.core.model.domain.location.Location import com.pranshulgg.weather_master_app.core.model.domain.weather.ApiKey import com.pranshulgg.weather_master_app.core.model.sources.SearchSource -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.prefs.LocalAppPrefs -import com.pranshulgg.weather_master_app.core.utils.formatters.toTitleCase import com.pranshulgg.weather_master_app.core.ui.components.EmptyContainerPlaceholder import com.pranshulgg.weather_master_app.core.ui.components.Gap import com.pranshulgg.weather_master_app.core.ui.components.LargeTopBarScaffold @@ -49,6 +47,7 @@ import com.pranshulgg.weather_master_app.core.ui.components.SettingSection import com.pranshulgg.weather_master_app.core.ui.components.SettingTile import com.pranshulgg.weather_master_app.core.ui.components.Symbol import com.pranshulgg.weather_master_app.core.ui.navigation.NavRoutes +import com.pranshulgg.weather_master_app.core.utils.formatters.toTitleCase import com.pranshulgg.weather_master_app.feature.search.ui.SearchFloatingToolbar import com.pranshulgg.weather_master_app.feature.search.ui.SearchScreenBottomSheets import com.pranshulgg.weather_master_app.feature.shared.ui.SharedBottomSheet @@ -174,7 +173,7 @@ fun SearchScreen(navController: NavController) { SharedBottomSheet.WeatherSourcesForLocationSheet( countryCode = selectedLocation?.countryCode, show = uiState.isWeatherSourcesForLocationSheetOpen, - selectedSource = selectedLocation?.source ?: WeatherSource.OPEN_METEO, + selectedSource = selectedLocation?.source ?: Source.OPEN_METEO, onSave = { viewModel.hideWeatherSourcesForLocationSheet() viewModel.saveLocation( diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/search/SearchScreenViewModel.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/search/SearchScreenViewModel.kt index dbdf8b5bc..a338d88a5 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/search/SearchScreenViewModel.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/search/SearchScreenViewModel.kt @@ -11,7 +11,7 @@ import com.pranshulgg.weather_master_app.core.model.sources.SearchSource import com.pranshulgg.weather_master_app.core.model.domain.location.Location import com.pranshulgg.weather_master_app.core.model.domain.toAppException import com.pranshulgg.weather_master_app.core.model.domain.toMessageRes -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.network.sources.search.geonames.GeoNamesTimezoneRepository import com.pranshulgg.weather_master_app.core.prefs.AppPrefsState import com.pranshulgg.weather_master_app.core.ui.snackbar.SnackbarManager @@ -69,7 +69,7 @@ class SearchScreenViewModel @Inject constructor( location: Location?, onBack: () -> Unit, onReset: () -> Unit, - weatherSource: WeatherSource + weatherSource: Source ) { if (location == null) return diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/settings/sources/WeatherSourcesScreen.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/settings/sources/WeatherSourcesScreen.kt index eb92e396e..4d7fa0600 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/settings/sources/WeatherSourcesScreen.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/settings/sources/WeatherSourcesScreen.kt @@ -18,8 +18,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.navigation.NavController import com.pranshulgg.weather_master_app.R -import com.pranshulgg.weather_master_app.core.model.sources.AlertSource -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.ui.components.Gap import com.pranshulgg.weather_master_app.core.ui.components.LargeTopBarScaffold import com.pranshulgg.weather_master_app.core.ui.components.NavigateUpBtn @@ -29,38 +28,11 @@ import com.pranshulgg.weather_master_app.core.ui.components.SettingsTileIcon import com.pranshulgg.weather_master_app.core.ui.navigation.NavRoutes -private data class WeatherSourceScreen( - val displayName: String, - val fullName: String, - val displayLink: String, - val countryNameRes: Int? = null -) - @Composable fun WeatherSourcesScreen(navController: NavController) { val uriHandler = LocalUriHandler.current - val alertSources = - AlertSource.entries.filter { it.displayName !in WeatherSource.entries.map { source -> source.displayName } } - .filter { it != AlertSource.NONE } - .map { - WeatherSourceScreen( - displayName = it.displayName, - fullName = it.fullName, - displayLink = it.displayLink, - ) - } - - val weatherSources = WeatherSource.entries.map { - WeatherSourceScreen( - displayName = it.displayName, - fullName = it.fullName, - displayLink = it.displayLink, - countryNameRes = it.countryNameRes - ) - } - - val sources = weatherSources + alertSources + val sources = Source.entries LargeTopBarScaffold( title = stringResource(R.string.weather_sources), diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/shared/WeatherViewModel.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/shared/WeatherViewModel.kt index 5b85d9ffe..7aa0d74c3 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/shared/WeatherViewModel.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/shared/WeatherViewModel.kt @@ -1,7 +1,6 @@ package com.pranshulgg.weather_master_app.feature.shared import android.content.Context -import android.util.Log import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel @@ -11,24 +10,19 @@ import com.pranshulgg.weather_master_app.core.model.domain.location.Location import com.pranshulgg.weather_master_app.core.model.domain.toAppException import com.pranshulgg.weather_master_app.core.model.domain.toMessageRes import com.pranshulgg.weather_master_app.core.model.domain.weather.WeatherBlock -import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource -import com.pranshulgg.weather_master_app.core.model.sources.AlertSource -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.Source import com.pranshulgg.weather_master_app.core.model.sources.isGlobal import com.pranshulgg.weather_master_app.core.model.sources.isSourceSupportedFor import com.pranshulgg.weather_master_app.core.model.weather.WeatherResult import com.pranshulgg.weather_master_app.core.model.weather.airquality.AirQualityResult import com.pranshulgg.weather_master_app.core.model.weather.alerts.AlertResult import com.pranshulgg.weather_master_app.core.model.weather.openmeteo.OpenMeteoModel -import com.pranshulgg.weather_master_app.core.network.sources.airquality.openmeteo.OpenMeteoAqiRepository import com.pranshulgg.weather_master_app.core.ui.snackbar.SnackbarManager -import com.pranshulgg.weather_master_app.data.provider.AirQualityRepositoryProvider -import com.pranshulgg.weather_master_app.data.provider.AlertsRepositoryProvider -import com.pranshulgg.weather_master_app.data.provider.WeatherRepositoryProvider import com.pranshulgg.weather_master_app.data.repository.LocationsRepository import com.pranshulgg.weather_master_app.data.repository.WeatherBlocksRepository import com.pranshulgg.weather_master_app.data.repository.WeatherDataReconcilerRepository import com.pranshulgg.weather_master_app.data.repository.WeatherUnitsRepository +import com.pranshulgg.weather_master_app.data.repository.data.SourceDataRepository import com.pranshulgg.weather_master_app.data.worker.WeatherUpdateScheduler import com.pranshulgg.weather_master_app.feature.main.MainScreenWeatherUiState import dagger.hilt.android.lifecycle.HiltViewModel @@ -43,19 +37,15 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import javax.inject.Inject -import kotlin.coroutines.cancellation.CancellationException import kotlin.time.Duration.Companion.minutes @HiltViewModel class WeatherViewModel @Inject constructor( - private val repo: WeatherRepositoryProvider, private val locationsRepo: LocationsRepository, appWeatherUnitsRepo: WeatherUnitsRepository, private val weatherBlocksRepository: WeatherBlocksRepository, - private val openMeteoAqiRepository: OpenMeteoAqiRepository, private val weatherDataReconcilerRepository: WeatherDataReconcilerRepository, - private val airQualityRepositoryProvider: AirQualityRepositoryProvider, - private val alertRepositoryProvider: AlertsRepositoryProvider, + private val sourceDataRepository: SourceDataRepository, @ApplicationContext private val context: Context ) : ViewModel() { @@ -116,7 +106,7 @@ class WeatherViewModel @Inject constructor( fun getWeather( location: Location, - source: WeatherSource, + source: Source, isManualRefresh: Boolean = false, isForceRefresh: Boolean = false, isForceRefreshForAirQuality: Boolean = false, @@ -125,7 +115,11 @@ class WeatherViewModel @Inject constructor( setLoading(true) weatherJob?.cancel() val startTime = System.currentTimeMillis() - _uiState.value = _uiState.value.copy(isError = false, isUnsupportedSource = false) + _uiState.value = _uiState.value.copy( + isError = false, + isUnsupportedSource = false, + isAirQualityLoading = true + ) weatherJob = viewModelScope.launch { @@ -151,17 +145,51 @@ class WeatherViewModel @Inject constructor( } } - // Run separately - if (!_uiState.value.isError) { - launch { - handleAirQuality(effectiveLocation, isManualRefresh, effectiveForceRefreshForAirQuality) - } - launch { - handleAlerts(effectiveLocation, isManualRefresh, effectiveForceRefreshForAlerts) - } - } - handleWeatherData(source, effectiveLocation, isManualRefresh, effectiveForceRefresh) + sourceDataRepository.getData( + location = effectiveLocation, + isManualRefresh = isManualRefresh, + isForceRefresh = effectiveForceRefresh, + isForceRefreshForAirQuality = effectiveForceRefreshForAirQuality, + isForceRefreshForAlerts = effectiveForceRefreshForAlerts, + onWeather = { result -> + handleWeatherData(result, effectiveLocation) + }, + onAlerts = { result -> + handleAlerts(result) + }, + onAirQuality = { result -> + handleAirQuality(result) + }, + ) +// handleWeatherData(source, effectiveLocation, isManualRefresh, effectiveForceRefresh) + + // Run separately +// if (!_uiState.value.isError) { +// launch { +// handleAirQuality( +// effectiveLocation, +// isManualRefresh, +// effectiveForceRefreshForAirQuality +// ) +// } +// + + // Only run separately if the alert source is a different API of its own + // If not, then run after the weather has fetched +// if (!source.providesAlerts && effectiveLocation.alertSource.name != source.name) { +// launch { +// handleAlerts( +// effectiveLocation, +// isManualRefresh, +// effectiveForceRefreshForAlerts +// ) +// } +// } else { +// handleAlertsFromWeatherSource(effectiveLocation) +// } +// } + val elapsed = System.currentTimeMillis() - startTime val minLoadingTime = 1000L // 1s @@ -203,9 +231,9 @@ class WeatherViewModel @Inject constructor( fun handleSourceChangeForWeather( location: Location, - source: WeatherSource, - airQualitySource: AirQualitySource, - alertSource: AlertSource, + source: Source, + airQualitySource: Source, + alertSource: Source, openMeteoModel: OpenMeteoModel ) { val updatedLocation = location.copy( @@ -227,32 +255,11 @@ class WeatherViewModel @Inject constructor( val allowForceRefreshForAirQuality = location.airQualitySource != airQualitySource val allowForceRefreshForAlerts = location.alertSource != alertSource - - if (allowForceRefreshForWeather) { - weatherDataReconcilerRepository.cleanUpStaleData( - location.source, - location.id, - airQualitySource, - alertSource - ) - } - if (allowForceRefreshForAirQuality) { - weatherDataReconcilerRepository.cleanUpStaleAirQualityData( - location.id, - source, - alertSource - ) - } - if (allowForceRefreshForAlerts) { - weatherDataReconcilerRepository.cleanUpStaleAlertsData( - location.id, - source, - airQualitySource - ) - } - _uiState.value = _uiState.value.copy( - activeLocation = updatedLocation + weatherDataReconcilerRepository.reconcileSourceChange( + previous = location, + updated = updatedLocation ) + _uiState.value = _uiState.value.copy(activeLocation = updatedLocation) getWeather( updatedLocation, source, @@ -260,8 +267,6 @@ class WeatherViewModel @Inject constructor( isForceRefreshForAirQuality = allowForceRefreshForAirQuality, isForceRefreshForAlerts = allowForceRefreshForAlerts ) - - } } @@ -297,18 +302,10 @@ class WeatherViewModel @Inject constructor( return locationsRepo.updateDeviceLocationPosition() } - private suspend fun handleWeatherData( - source: WeatherSource, - location: Location, - isManualRefresh: Boolean, - isForceRefresh: Boolean - ) { - - val repo = repo.getRepository(source) + private suspend fun handleWeatherData(result: WeatherResult, location: Location) { - when (val result = repo.getWeather(location, isManualRefresh, isForceRefresh)) { - + when (result) { is WeatherResult.Success -> { _uiState.value = _uiState.value.copy(weather = result.weather, isInitialized = true) @@ -319,8 +316,6 @@ class WeatherViewModel @Inject constructor( val appExpectation = result.exception.toAppException() SnackbarManager.show(appExpectation.toMessageRes()) - - _uiState.value = _uiState.value.copy( isError = true, weather = result.cacheWeather, @@ -345,27 +340,11 @@ class WeatherViewModel @Inject constructor( } } - private suspend fun handleAirQuality( - location: Location, - isManualRefresh: Boolean, - isForceRefresh: Boolean + private fun handleAirQuality( + result: AirQualityResult ) { - if (_uiState.value.isAirQualityLoading) return - - val repoAir = airQualityRepositoryProvider.getRepository(location.airQualitySource) - - - if (repoAir == null) { - _uiState.value = _uiState.value.copy(airQuality = null, isAirQualityLoading = false) - return - } - - _uiState.value = _uiState.value.copy(isAirQualityLoading = true) - - - - when (val result = repoAir.getAirQuality(location, isManualRefresh, isForceRefresh)) { + when (result) { is AirQualityResult.Success -> { _uiState.value = _uiState.value.copy( @@ -387,30 +366,14 @@ class WeatherViewModel @Inject constructor( } - private suspend fun handleAlerts( - location: Location, - isManualRefresh: Boolean, - isForceRefresh: Boolean - ) { + private fun handleAlerts(result: AlertResult) { - - val repoAlert = alertRepositoryProvider.getRepository(location.alertSource) - - - if (repoAlert == null) { - _uiState.value = _uiState.value.copy(alerts = emptyList()) - return - } - - - when (val result = repoAlert.getAlerts(location, isManualRefresh, isForceRefresh)) { + when (result) { is AlertResult.Success -> { _uiState.value = _uiState.value.copy( alerts = result.alerts ) - - } // Fail silently, we just won't show the Air quality in the UI @@ -426,7 +389,7 @@ class WeatherViewModel @Inject constructor( fun startAutoRefresh( location: Location, - source: WeatherSource + source: Source ) { if (autoRefreshJob?.isActive == true) return diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/shared/ui/SharedBottomSheets.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/shared/ui/SharedBottomSheets.kt index 0778bae6c..3bbdbbffe 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/shared/ui/SharedBottomSheets.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/shared/ui/SharedBottomSheets.kt @@ -1,40 +1,25 @@ package com.pranshulgg.weather_master_app.feature.shared.ui -import android.util.Log -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.IconButton -import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.SheetState -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.pranshulgg.weather_master_app.R import com.pranshulgg.weather_master_app.core.model.domain.weather.ApiKey -import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource -import com.pranshulgg.weather_master_app.core.model.sources.getWeatherSourcesForCountry -import com.pranshulgg.weather_master_app.core.model.sources.getWeatherSourcesGlobal +import com.pranshulgg.weather_master_app.core.model.sources.Source +import com.pranshulgg.weather_master_app.core.model.sources.getSourcesForCountry +import com.pranshulgg.weather_master_app.core.model.sources.getSourcesGlobal import com.pranshulgg.weather_master_app.core.ui.components.ActionBottomSheet -import com.pranshulgg.weather_master_app.core.ui.components.DialogBasic import com.pranshulgg.weather_master_app.core.ui.components.Gap -import com.pranshulgg.weather_master_app.core.ui.components.RadioRow import com.pranshulgg.weather_master_app.core.ui.components.SettingSection import com.pranshulgg.weather_master_app.core.ui.components.SettingTile -import com.pranshulgg.weather_master_app.core.ui.components.SettingsTileIcon import com.pranshulgg.weather_master_app.core.ui.components.Symbol -import com.pranshulgg.weather_master_app.core.ui.theme.ShapeRadius import com.pranshulgg.weather_master_app.feature.shared.components.ChangelogContent object SharedBottomSheet { @@ -44,18 +29,18 @@ object SharedBottomSheet { countryCode: String?, show: Boolean, sheetState: SheetState, - selectedSource: WeatherSource = WeatherSource.OPEN_METEO, + selectedSource: Source = Source.OPEN_METEO, isEditing: Boolean = false, - onSave: (WeatherSource) -> Unit, + onSave: (Source) -> Unit, onDismiss: () -> Unit, onClickApiConfig: () -> Unit, apiKeys: List, ) { if (show) { - val recommendedSources = getWeatherSourcesForCountry(countryCode?.uppercase()) - val globalSources = getWeatherSourcesGlobal() + val recommendedSources = getSourcesForCountry(countryCode?.uppercase()) + val globalSources = getSourcesGlobal() - val isApiKeyAvailable: (WeatherSource) -> Boolean = { source -> + val isApiKeyAvailable: (Source) -> Boolean = { source -> if (source.requiresUserApiKey) { apiKeys.isNotEmpty() && apiKeys @@ -78,6 +63,22 @@ object SharedBottomSheet { ) } + val handeSelection: (Source) -> Unit = { + if (!it.requiresUserApiKey) { + currentSelectedSource = it + } else if (!isApiKeyAvailable(it)) { + onClickApiConfig() + } else { + currentSelectedSource = it + } + } + + val description: (Source) -> String? = { + if (it.requiresUserApiKey && + !isApiKeyAvailable(it) + ) "Requires API key" else null + } + ActionBottomSheet( @@ -106,20 +107,11 @@ object SharedBottomSheet { ) }, title = source.displayName + countryString, - description = if (source.requiresUserApiKey && !isApiKeyAvailable( - source - ) - ) "Requires API key" else null, + description = description(source), colorDesc = MaterialTheme.colorScheme.error, selected = isSelected, onClick = { - if (!source.requiresUserApiKey) { - currentSelectedSource = source - } else if (!isApiKeyAvailable(source)) { - onClickApiConfig() - } else { - currentSelectedSource = source - } + handeSelection(source) } ) } @@ -143,8 +135,9 @@ object SharedBottomSheet { }, title = source.displayName + countryString, selected = isSelected, + description = description(source), onClick = { - currentSelectedSource = source + handeSelection(source) }, ) } @@ -169,19 +162,10 @@ object SharedBottomSheet { }, title = source.displayName + countryString, selected = isSelected, - description = if (source.requiresUserApiKey && !isApiKeyAvailable( - source - ) - ) "Requires API key" else null, + description = description(source), colorDesc = MaterialTheme.colorScheme.error, onClick = { - if (!source.requiresUserApiKey) { - currentSelectedSource = source - } else if (!isApiKeyAvailable(source)) { - onClickApiConfig() - } else { - currentSelectedSource = source - } + handeSelection(source) }, ) }