diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser
index 6db82f3..005fbc3 100644
Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 3cc336b..6da653d 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -17,6 +17,7 @@
+
diff --git a/app/build.gradle b/app/build.gradle
index fbd0a37..43043a1 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -123,6 +123,7 @@ dependencies {
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0'
+ implementation "com.github.haroldadmin:NetworkResponseAdapter:4.0.1"
//Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5'
diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WeatherHelper.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WeatherHelper.kt
index 0504759..5c60b3d 100644
--- a/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WeatherHelper.kt
+++ b/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WeatherHelper.kt
@@ -44,6 +44,7 @@ object WeatherHelper {
fun removeWeather(context: Context) {
Preferences.remove(Preferences::weatherTemp)
Preferences.remove(Preferences::weatherRealTempUnit)
+ Preferences.remove(Preferences::weatherIcon)
MainWidget.updateWidget(context)
}
@@ -80,6 +81,40 @@ object WeatherHelper {
})
}
+ fun getProviderInfoTitle(context: Context): String {
+ return context.getString(when(Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
+ Constants.WeatherProvider.OPEN_WEATHER -> R.string.weather_provider_info_open_weather_title
+ Constants.WeatherProvider.WEATHER_BIT -> R.string.weather_provider_info_weatherbit_title
+ Constants.WeatherProvider.FORECA -> R.string.weather_provider_info_foreca_title
+ Constants.WeatherProvider.HERE -> R.string.weather_provider_info_here_title
+ Constants.WeatherProvider.ACCUWEATHER -> R.string.weather_provider_info_accuweather_title
+ Constants.WeatherProvider.WEATHER_GOV -> R.string.weather_provider_info_weather_gov_title
+ Constants.WeatherProvider.YR -> R.string.weather_provider_info_yr_title
+ Constants.WeatherProvider.SMHI -> R.string.weather_provider_info_smhi_title
+ Constants.WeatherProvider.WEATHER_CA -> R.string.weather_provider_info_weather_ca_title
+ Constants.WeatherProvider.BOM -> R.string.weather_provider_info_bom_title
+ Constants.WeatherProvider.METEOFRANCE -> R.string.weather_provider_info_meteofrance_title
+ else -> R.string.nothing
+ })
+ }
+
+ fun getProviderInfoSubtitle(context: Context): String {
+ return context.getString(when(Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
+ Constants.WeatherProvider.OPEN_WEATHER -> R.string.weather_provider_info_open_weather_subtitle
+ Constants.WeatherProvider.WEATHER_BIT -> R.string.weather_provider_info_weatherbit_subtitle
+ Constants.WeatherProvider.FORECA -> R.string.weather_provider_info_foreca_subtitle
+ Constants.WeatherProvider.HERE -> R.string.weather_provider_info_here_subtitle
+ Constants.WeatherProvider.ACCUWEATHER -> R.string.weather_provider_info_accuweather_subtitle
+ Constants.WeatherProvider.WEATHER_GOV -> R.string.weather_provider_info_weather_gov_subtitle
+ Constants.WeatherProvider.YR -> R.string.weather_provider_info_yr_subtitle
+ Constants.WeatherProvider.SMHI -> R.string.weather_provider_info_smhi_subtitle
+ Constants.WeatherProvider.WEATHER_CA -> R.string.weather_provider_info_weather_ca_subtitle
+ Constants.WeatherProvider.BOM -> R.string.weather_provider_info_bom_subtitle
+ Constants.WeatherProvider.METEOFRANCE -> R.string.weather_provider_info_meteofrance_subtitle
+ else -> R.string.nothing
+ })
+ }
+
fun getProviderLink(): String {
return when(Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
Constants.WeatherProvider.OPEN_WEATHER -> "https://home.openweathermap.org/users/sign_up"
@@ -315,4 +350,43 @@ object WeatherHelper {
}
}
}
+
+ fun getWeatherGovIcon(iconString: String, isDaytime: Boolean): String = when {
+ iconString.contains("skc") -> "01"
+ iconString.contains("few") -> "02"
+ iconString.contains("sct") -> "03"
+ iconString.contains("bkn") -> "04"
+ iconString.contains("ovc") -> "04"
+ iconString.contains("wind_skc") -> "01"
+ iconString.contains("wind_few") -> "02"
+ iconString.contains("wind_sct") -> "03"
+ iconString.contains("wind_bkn") -> "04"
+ iconString.contains("wind_ovc") -> "04"
+ iconString.contains("snow") -> "13"
+ iconString.contains("rain_snow") -> "81"
+ iconString.contains("rain_sleet") -> "81"
+ iconString.contains("snow_sleet") -> "81"
+ iconString.contains("fzra") -> "81"
+ iconString.contains("rain_fzra") -> "81"
+ iconString.contains("snow_fzra") -> "81"
+ iconString.contains("sleet") -> "81"
+ iconString.contains("rain") -> "10"
+ iconString.contains("rain_showers") -> "10"
+ iconString.contains("rain_showers_hi") -> "10"
+ iconString.contains("tsra") -> "82"
+ iconString.contains("tsra_sct") -> "82"
+ iconString.contains("tsra_hi") -> "82"
+ iconString.contains("tornado") -> "80"
+ iconString.contains("hurricane") -> "80"
+ iconString.contains("tropical_storm") -> "09"
+ iconString.contains("dust") -> "Dust"
+ iconString.contains("smoke") -> "Smoke"
+ iconString.contains("haze") -> "50"
+ iconString.contains("hot") -> "01"
+ iconString.contains("cold") -> "13"
+ iconString.contains("blizzard") -> "80"
+ iconString.contains("fog") -> "82"
+ else -> ""
+ } + if (isDaytime) "d" else "n"
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/network/WeatherNetworkApi.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/network/WeatherNetworkApi.kt
index d839c9d..c01d796 100644
--- a/app/src/main/java/com/tommasoberlose/anotherwidget/network/WeatherNetworkApi.kt
+++ b/app/src/main/java/com/tommasoberlose/anotherwidget/network/WeatherNetworkApi.kt
@@ -3,10 +3,13 @@ package com.tommasoberlose.anotherwidget.network
import android.content.Context
import android.util.Log
import com.google.gson.internal.LinkedTreeMap
+import com.haroldadmin.cnradapter.NetworkResponse
+import com.haroldadmin.cnradapter.executeWithRetry
import com.kwabenaberko.openweathermaplib.constants.Units
import com.kwabenaberko.openweathermaplib.implementation.OpenWeatherMapHelper
import com.kwabenaberko.openweathermaplib.implementation.callbacks.CurrentWeatherCallback
import com.kwabenaberko.openweathermaplib.models.currentweather.CurrentWeather
+import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
@@ -22,6 +25,9 @@ import java.lang.Exception
class WeatherNetworkApi(val context: Context) {
fun updateWeather() {
+ Preferences.weatherProviderError = ""
+ Preferences.weatherProviderLocationError = ""
+
if (Preferences.showWeather && Preferences.customLocationLat != "" && Preferences.customLocationLon != "") {
when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
Constants.WeatherProvider.OPEN_WEATHER -> useOpenWeatherMap(context)
@@ -56,6 +62,9 @@ class WeatherNetworkApi(val context: Context) {
})
} else {
+ Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
+ Preferences.weatherProviderLocationError = ""
+
WeatherHelper.removeWeather(
context
)
@@ -65,23 +74,65 @@ class WeatherNetworkApi(val context: Context) {
private fun useWeatherGov(context: Context) {
CoroutineScope(Dispatchers.IO).launch {
val repository = WeatherGovRepository()
- try {
- val points = repository.getGridPoints(
+ val pointsResponse = executeWithRetry(times = 5) {
+ repository.getGridPoints(
Preferences.customLocationLat,
Preferences.customLocationLon
)
-
- val pp = points["properties"] as LinkedTreeMap<*,*>
- val gridId = pp["gridId"] as String
- val gridX = pp["gridX"] as Double
- val gridY = pp["gridY"] as Double
-
- val weather = repository.getWeather(gridId, gridX, gridY)
-
- Log.d("ciao", weather.toString())
- } catch (ex: Exception) {
- Log.d("ciao", ex.localizedMessage)
}
+
+ when (pointsResponse) {
+ is NetworkResponse.Success -> {
+ try {
+ val pp = pointsResponse.body["properties"] as LinkedTreeMap<*, *>
+ val gridId = pp["gridId"] as String
+ val gridX = pp["gridX"] as Double
+ val gridY = pp["gridY"] as Double
+
+ when (val weatherResponse = repository.getWeather(
+ gridId,
+ gridX,
+ gridY,
+ if (Preferences.weatherTempUnit == "F") "us" else "si"
+ )) {
+ is NetworkResponse.Success -> {
+ try {
+ val props =
+ weatherResponse.body["properties"] as LinkedTreeMap<*, *>
+ val periods = props["periods"] as List<*>
+ val now = periods[0] as LinkedTreeMap<*, *>
+
+ val temp = now["temperature"] as Double
+ val fullIcon = now["icon"] as String
+ val isDaytime = now["isDaytime"] as Boolean
+
+ Preferences.weatherTemp = temp.toFloat()
+ Preferences.weatherIcon = WeatherHelper.getWeatherGovIcon(fullIcon, isDaytime)
+ Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
+
+ MainWidget.updateWidget(context)
+
+ EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
+ } catch (ex: Exception) {
+ }
+ }
+ }
+ } catch(ex: Exception) {
+
+ }
+ }
+ is NetworkResponse.ServerError -> {
+ if (pointsResponse.body?.containsKey("status") == true && (pointsResponse.body?.get("status") as Double).toInt() == 404) {
+ Preferences.weatherProviderError = ""
+ Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_wrong_location)
+
+ WeatherHelper.removeWeather(
+ context
+ )
+ }
+ }
+ }
+
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/network/api/WeatherGovApiService.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/network/api/WeatherGovApiService.kt
index ec20295..2fd9dd0 100644
--- a/app/src/main/java/com/tommasoberlose/anotherwidget/network/api/WeatherGovApiService.kt
+++ b/app/src/main/java/com/tommasoberlose/anotherwidget/network/api/WeatherGovApiService.kt
@@ -1,12 +1,13 @@
package com.tommasoberlose.anotherwidget.network.api
-import retrofit2.http.GET
-import retrofit2.http.Path
+import com.haroldadmin.cnradapter.NetworkResponse
+import retrofit2.http.*
interface WeatherGovApiService {
@GET("points/{latitude},{longitude}")
- suspend fun getGridPoints(@Path("latitude") latitude: String, @Path("longitude") longitude: String): HashMap
+ suspend fun getGridPoints(@Path("latitude") latitude: String, @Path("longitude") longitude: String): NetworkResponse, HashMap>
+ @Headers("User-Agent: (Another Widget, tommaso.berlose@gmail.com)")
@GET("gridpoints/{gridId}/{gridX},{gridY}/forecast")
- suspend fun getWeather(@Path("gridId") gridId: String, @Path("gridX") gridX: Int, @Path("gridY") gridY: Int): HashMap
+ suspend fun getWeather(@Path("gridId") gridId: String, @Path("gridX") gridX: Int, @Path("gridY") gridY: Int, @Query("units") unit: String): NetworkResponse, HashMap>
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/network/repository/WeatherGovRepository.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/network/repository/WeatherGovRepository.kt
index f21c44b..6c0bf11 100644
--- a/app/src/main/java/com/tommasoberlose/anotherwidget/network/repository/WeatherGovRepository.kt
+++ b/app/src/main/java/com/tommasoberlose/anotherwidget/network/repository/WeatherGovRepository.kt
@@ -1,15 +1,16 @@
package com.tommasoberlose.anotherwidget.network.repository
+import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
import com.tommasoberlose.anotherwidget.network.api.WeatherGovApiService
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
-class WeatherGovRepository() {
+class WeatherGovRepository {
private val apiService: WeatherGovApiService = getRetrofit().create(WeatherGovApiService::class.java)
suspend fun getGridPoints(latitude: String, longitude: String) = apiService.getGridPoints(latitude, longitude)
- suspend fun getWeather(gridId: String, gridX: Double, gridY: Double) = apiService.getWeather(gridId, gridX.toInt(), gridY.toInt())
+ suspend fun getWeather(gridId: String, gridX: Double, gridY: Double, unit: String) = apiService.getWeather(gridId, gridX.toInt(), gridY.toInt(), unit)
companion object {
private const val BASE_URL = "https://api.weather.gov/"
@@ -17,6 +18,7 @@ class WeatherGovRepository() {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
+ .addCallAdapterFactory(NetworkResponseAdapterFactory())
.build()
}
}
diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/WeatherProviderActivity.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/WeatherProviderActivity.kt
index 3538573..49ac40b 100644
--- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/WeatherProviderActivity.kt
+++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/WeatherProviderActivity.kt
@@ -49,11 +49,22 @@ class WeatherProviderActivity : AppCompatActivity() {
viewModel.weatherProvider.observe(this, Observer {
weather_provider.editText?.setText(WeatherHelper.getProviderName(this, Constants.WeatherProvider.fromInt(Preferences.weatherProvider)!!).split("\n").first())
- info_container.isVisible = WeatherHelper.isKeyRequired()
api_key_container.isVisible = WeatherHelper.isKeyRequired()
+ WeatherHelper.getProviderInfoTitle(this).let {
+ info_title.text = it
+ info_title.isVisible = it != ""
+ }
+
+ WeatherHelper.getProviderInfoSubtitle(this).let {
+ info_subtitle.text = it
+ info_subtitle.isVisible = it != ""
+ }
+
action_open_provider.text = WeatherHelper.getProviderLinkName(this)
})
+
+ viewModel.weatherProviderApi
}
private fun setListener() {
@@ -80,7 +91,8 @@ class WeatherProviderActivity : AppCompatActivity() {
}
api_key.editText?.addTextChangedListener {
- Preferences.weatherProviderApi = it?.toString() ?: ""
+ val key = it?.toString() ?: ""
+ Preferences.weatherProviderApi = key
}
}
diff --git a/app/src/main/res/layout/activity_weather_provider.xml b/app/src/main/res/layout/activity_weather_provider.xml
index bb6f886..0ffdc74 100644
--- a/app/src/main/res/layout/activity_weather_provider.xml
+++ b/app/src/main/res/layout/activity_weather_provider.xml
@@ -92,6 +92,8 @@
android:clickable="false"
android:id="@+id/weather_provider_inner"
android:textDirection="locale"
+ android:textStyle="bold"
+ android:cursorVisible="false"
android:fontFamily="@font/google_sans"
android:gravity="center_vertical|start" />
@@ -151,8 +153,9 @@
android:layout_height="wrap_content"
style="@style/AnotherWidget.Settings.Title"
android:gravity="start"
+ android:id="@+id/info_title"
android:textAlignment="viewStart"
- android:text="This weather provider needs\nan API key to work correctly."/>
+ android:text="@string/weather_provider_info_open_weather_title"/>
+ android:text="@string/weather_provider_info_open_weather_subtitle"/>
diff --git a/app/src/main/res/layout/fragment_weather_settings.xml b/app/src/main/res/layout/fragment_weather_settings.xml
index d8bab8d..0091a7b 100644
--- a/app/src/main/res/layout/fragment_weather_settings.xml
+++ b/app/src/main/res/layout/fragment_weather_settings.xml
@@ -175,7 +175,7 @@
android:id="@+id/weather_provider_error"
android:textColor="@color/errorColorText"
android:letterSpacing="0"
- android:fontFamily="@font/google_sans"
+ android:fontFamily="@font/google_sans_bold"
android:textStyle="bold"
android:textAppearance="@style/TextAppearance.MaterialComponents.Button"
app:textAllCaps="false" />
@@ -245,7 +245,7 @@
android:id="@+id/weather_provider_location_error"
android:textColor="@color/errorColorText"
android:letterSpacing="0"
- android:fontFamily="@font/google_sans"
+ android:fontFamily="@font/google_sans_bold"
android:textStyle="bold"
android:textAppearance="@style/TextAppearance.MaterialComponents.Button"
app:textAllCaps="false" />
@@ -254,7 +254,7 @@
android:layout_height="wrap_content"
android:id="@+id/background_location_warning"
android:paddingStart="64dp"
- android:paddingEnd="16dp"
+ android:paddingEnd="8dp"
android:paddingBottom="8dp"
android:orientation="horizontal">
#E93B3B
#FB8C00
#efefef
+ #43A047
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bcdd229..3f3e341 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -129,6 +129,7 @@
Icon pack %d
We will collect location data to update the weather information even when the app is closed or not in use.\nWe will not use these data otherwise.
Weather provider
+
Open Weather Map
Weatherbit.io
Foreca.com
@@ -140,6 +141,7 @@
Weather.gc.ca (Canada)
BOM (Australia)\nPowered by Australia\'s national weather
Meteofrance.com (France)
+
Open openweathermap.com
Open weatherbit.io
Open foreca.com
@@ -152,6 +154,36 @@
Open bom.gov.au
Open meteofrance.com
+ This weather provider needs\nan API key to work correctly.
+ Weatherbit.io
+ Foreca.com
+ Here.com
+ Accuweather.com
+ This weather provider works only\nfor locations inside the US.
+ YR.no/Met.no\nPowered by Meteorological Institute
+ Smhi.se (Swedish)\nPowered by Swedish Meteorological
+ Weather.gc.ca (Canada)
+ BOM (Australia)\nPowered by Australia\'s national weather
+ Meteofrance.com (France)
+
+ Please, open the provider website, create a personal account and copy here the default key.
+ Weatherbit.io
+ Foreca.com
+ Here.com
+ Accuweather.com
+
+ YR.no/Met.no\nPowered by Meteorological Institute
+ Smhi.se (Swedish)\nPowered by Swedish Meteorological
+ Weather.gc.ca (Canada)
+ BOM (Australia)\nPowered by Australia\'s national weather
+ Meteofrance.com (France)
+
+ The weather provider that you\'ve selected requires an API key.
+ The current location is not supported.
+ Please select a valid location.
+ It seems that your API key has expired.
+ The weather provider is wrongly configured.
+
Clock
Tap on clock opens