fix(weather): keep met-weather DTOs from R8 so Gson works; add +4h reading (v0.3.8)
Some checks failed
Android Main Branch CI / Build, Sign & Upload (push) Has been cancelled
Update CHANGELOG.md / changelog (push) Has been cancelled
Validate Gradle Wrapper / Validation (push) Has been cancelled
Android Release CI / Build, Sign & Release (push) Has been cancelled
Nightly Release / release (push) Has been cancelled
Nightly Release / Build, Sign & Release (push) Has been cancelled
Delete Unused Caches / delete (push) Has been cancelled
Close Inactive Issues & Pull Requests / close-issues (push) Has been cancelled
GitHub Actions Version Updater / build (push) Has been cancelled

- ProGuard keep rule for dk.haugesenspil.metweather.** (R8 was obfuscating the
  Gson DTOs -> ClassCastException, weather glyph vanished).
- Home current-weather now shows 'current  ->  ~4h' (nearest MET hourly point),
  cached in met_weather_prefs.
release build v0.3.8
This commit is contained in:
2026-08-20 13:19:21 +02:00
parent 553d1802c7
commit 44d7e81801
10 changed files with 54 additions and 14 deletions

View File

@@ -38,7 +38,7 @@ export JAVA_HOME=~/jdk21 # Gradle 8.13, compileSdk/targetSdk 36, minSdk 2
## Release flow (see git history for the v0.3.x pattern)
1. Bump `versionCode`/`versionName` in `app/build.gradle.kts`
(currently 37 / 0.3.7).
(currently 38 / 0.3.8).
2. Build both flavors, then sign each:
```bash
@@ -46,9 +46,9 @@ PASS=$(cat ~/android-keystores/easylauncher-release.pass)
~/android-sdk/build-tools/36.0.0/apksigner sign --v4-signing-enabled true \
--ks ~/android-keystores/easylauncher-release.jks --ks-key-alias easylauncher \
--ks-pass "pass:$PASS" --key-pass "pass:$PASS" \
--out dist/EasyLauncher-Internet-v0.3.7-Signed.apk \
--out dist/EasyLauncher-Internet-v0.3.8-Signed.apk \
app/build/outputs/apk/withInternet/release/app.easy.launcher_v0.3.7-Release.apk
# repeat for withoutInternet -> dist/EasyLauncher-v0.3.7-Signed.apk
# repeat for withoutInternet -> dist/EasyLauncher-v0.3.8-Signed.apk
```
3. `apksigner verify --print-certs` should show the keystore SHA-256.

View File

@@ -19,8 +19,8 @@ android {
applicationId = "app.easy.launcher"
minSdk = 24
targetSdk = 36
versionCode = 37
versionName = "0.3.7"
versionCode = 38
versionName = "0.3.8"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
manifestPlaceholders["internetPermission"] = "android.permission.INTERNET"

View File

@@ -26,5 +26,8 @@
-keep class com.github.droidworksstudio.launcher.helper.weather.Main { *; }
-keep class com.github.droidworksstudio.launcher.helper.weather.Weather { *; }
# Keep the shared met-weather library classes (Gson DTOs must keep field names)
-keep class dk.haugesenspil.metweather.** { *; }
# Keep all model classes and their fields that Gson might use for serialization/deserialization
-keep class com.github.droidworksstudio.launcher.helper.weather.** { *; }

View File

@@ -59,6 +59,7 @@ class AppHelper @Inject constructor() {
private companion object {
/** MET requires a descriptive User-Agent identifying the app + contact. */
const val MET_USER_AGENT = "app.easy.launcher (https://gitea.haugesenspil.dk/jonas/EasyLauncher)"
private const val FOUR_HOURS_MILLIS = 4L * 60 * 60 * 1000
}
@SuppressLint("WrongConstant", "PrivateApi")
@@ -562,7 +563,13 @@ class AppHelper @Inject constructor() {
data class CachedWeatherData(val timestamp: Long, val weatherResponse: WeatherResponse)
sealed class MetWeatherResult {
data class Success(val temperature: Int, val symbolCode: String) : MetWeatherResult()
data class Success(
val temperature: Int,
val symbolCode: String,
val temperature4h: Int? = null,
val symbol4h: String? = null,
) : MetWeatherResult()
data class Failure(val errorMessage: String) : MetWeatherResult()
}
@@ -592,9 +599,15 @@ class AppHelper @Inject constructor() {
return try {
val weather = MetWeatherClient(MET_USER_AGENT)
.fetch(latitude.toDouble(), longitude.toDouble())
val now = System.currentTimeMillis()
val plus4 = weather.hourly.minByOrNull {
kotlin.math.abs(it.epochMillis - (now + FOUR_HOURS_MILLIS))
}
val result = MetWeatherResult.Success(
weather.temperatureC.roundToInt(),
weather.symbolCode,
temperature = weather.temperatureC.roundToInt(),
symbolCode = weather.symbolCode,
temperature4h = plus4?.temperatureC?.roundToInt(),
symbol4h = plus4?.symbolCode,
)
context.cacheMetWeather(result)
result
@@ -605,11 +618,21 @@ class AppHelper @Inject constructor() {
}
/**
* Builds the home-screen weather text: nerd-font weather glyph followed
* by the temperature in Celsius, e.g. "\uE312 16°".
* Builds the home-screen weather text: the current nerd-font weather glyph
* and Celsius value, plus the reading ~4 hours out when available, e.g.
* "\uE302 16° → \uE31D 18°".
*/
fun buildCurrentWeatherText(temperature: Int, symbolCode: String): String {
return "${MetSymbolMapper.glyphFor(symbolCode)} $temperature°"
fun buildCurrentWeatherText(
temperature: Int,
symbolCode: String,
temperature4h: Int? = null,
symbol4h: String? = null,
): String {
val current = "${MetSymbolMapper.glyphFor(symbolCode)} $temperature°"
if (temperature4h != null && symbol4h != null) {
return "$current${MetSymbolMapper.glyphFor(symbol4h)} $temperature4h°"
}
return current
}
private fun Context.cacheMetWeather(result: MetWeatherResult.Success) {
@@ -618,6 +641,8 @@ class AppHelper @Inject constructor() {
.putLong(Constants.MET_WEATHER_TIMESTAMP, System.currentTimeMillis())
.putInt(Constants.MET_WEATHER_TEMPERATURE, result.temperature)
.putString(Constants.MET_WEATHER_SYMBOL, result.symbolCode)
.putInt(Constants.MET_WEATHER_TEMPERATURE_4H, result.temperature4h ?: Int.MIN_VALUE)
.putString(Constants.MET_WEATHER_SYMBOL_4H, result.symbol4h ?: "")
.apply()
}
@@ -627,6 +652,14 @@ class AppHelper @Inject constructor() {
if (timestamp == -1L) return null
val temperature = sharedPreferences.getInt(Constants.MET_WEATHER_TEMPERATURE, 0)
val symbol = sharedPreferences.getString(Constants.MET_WEATHER_SYMBOL, "cloudy") ?: "cloudy"
return timestamp to MetWeatherResult.Success(temperature, symbol)
val temp4h = sharedPreferences.getInt(Constants.MET_WEATHER_TEMPERATURE_4H, Int.MIN_VALUE)
val symbol4h = sharedPreferences.getString(Constants.MET_WEATHER_SYMBOL_4H, "") ?: ""
val result = MetWeatherResult.Success(
temperature = temperature,
symbolCode = symbol,
temperature4h = temp4h.takeIf { it != Int.MIN_VALUE },
symbol4h = symbol4h.takeIf { it.isNotEmpty() },
)
return timestamp to result
}
}

View File

@@ -393,7 +393,9 @@ class HomeFragment : Fragment(),
is AppHelper.MetWeatherResult.Success -> {
binding.currentWeather.text = appHelper.buildCurrentWeatherText(
result.temperature,
result.symbolCode
result.symbolCode,
result.temperature4h,
result.symbol4h,
)
binding.currentWeather.visibility = View.VISIBLE
}

View File

@@ -24,6 +24,8 @@ object Constants {
const val MET_WEATHER_TIMESTAMP = "MET_WEATHER_TIMESTAMP"
const val MET_WEATHER_TEMPERATURE = "MET_WEATHER_TEMPERATURE"
const val MET_WEATHER_SYMBOL = "MET_WEATHER_SYMBOL"
const val MET_WEATHER_TEMPERATURE_4H = "MET_WEATHER_TEMPERATURE_4H"
const val MET_WEATHER_SYMBOL_4H = "MET_WEATHER_SYMBOL_4H"
const val WEATHER_RESPONSE = "WEATHER_RESPONSE"
const val WEATHER_UNITS = "WEATHER_UNITS"
const val LATITUDE = "LATITUDE"

Binary file not shown.

Binary file not shown.

BIN
dist/EasyLauncher-v0.3.8-Signed.apk vendored Normal file

Binary file not shown.

Binary file not shown.