From b0072f0fc2d70fc68c1dc48c4bc84dc9fba77880 Mon Sep 17 00:00:00 2001 From: Jonas Haugesen Date: Sat, 12 Sep 2026 15:43:10 +0200 Subject: [PATCH] Key the weather worker's location skip on the provider, not on a setting typing.8 checked WeatherSettingsData.managedLocation, but that only exists once the user has explicitly picked "managed location" for the provider, so weatherProviderSettings stayed empty and the guard never fired: the worker kept requesting a location and retrying every 30s. The real signal is the provider's own capability. BreezyWeatherProvider.getWeatherData() is a hardcoded noop that also manages its own location, so add WeatherProvider.isPushBased and check that instead. Also stop treating a managed location as "pull nothing": those providers still have data to fetch, they just resolve the position themselves, so they now take the WeatherLocation path instead of the auto-location one. Bump to 1.40.2-typing.9 (versionCode 2026091202). --- app/app/build.gradle.kts | 4 +- .../mm20/launcher2/weather/WeatherProvider.kt | 8 +++ .../launcher2/weather/WeatherRepository.kt | 57 ++++++++++--------- .../weather/breezy/BreezyWeatherProvider.kt | 3 + 4 files changed, 43 insertions(+), 29 deletions(-) diff --git a/app/app/build.gradle.kts b/app/app/build.gradle.kts index f48015c2c..56a0ddecb 100644 --- a/app/app/build.gradle.kts +++ b/app/app/build.gradle.kts @@ -34,8 +34,8 @@ android { applicationId = "de.mm20.launcher2" minSdk = libs.versions.minSdk.get().toInt() targetSdk = libs.versions.targetSdk.get().toInt() - versionCode = System.getenv("VERSION_CODE_OVERRIDE")?.toIntOrNull() ?: 2026091201 - versionName = "1.40.2-typing.8" + versionCode = System.getenv("VERSION_CODE_OVERRIDE")?.toIntOrNull() ?: 2026091202 + versionName = "1.40.2-typing.9" signingConfig = signingConfigs.getByName("debug") } diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherProvider.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherProvider.kt index 0a88d9582..c9aa45a36 100644 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherProvider.kt +++ b/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherProvider.kt @@ -11,6 +11,14 @@ interface WeatherProvider { return 1000 * 60 * 60L } + /** + * Whether this provider pushes weather updates on its own instead of being polled, and + * resolves its location itself. [WeatherUpdateWorker] then has nothing to pull and must not + * request a location for it. + */ + val isPushBased: Boolean + get() = false + suspend fun getWeatherData(location: WeatherLocation): List? suspend fun getWeatherData(lat: Double, lon: Double): List? suspend fun findLocation(query: String): List diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherRepository.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherRepository.kt index 2170037f9..b43e830b8 100644 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherRepository.kt +++ b/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherRepository.kt @@ -79,13 +79,12 @@ internal class WeatherRepositoryImpl( } scope.launch { settings.collectLatest { - if (it.managedLocation) { - // The provider resolves its own location and pushes weather updates, so there - // is nothing to poll for. + val provider = WeatherProvider.getInstance(it.provider) + if (provider.isPushBased) { + // The provider sends its updates itself, so there is nothing to poll for. WorkManager.getInstance(context).cancelUniqueWork("weather") return@collectLatest } - val provider = WeatherProvider.getInstance(it.provider) val weatherRequest = PeriodicWorkRequestBuilder(Duration.ofMillis(provider.getUpdateInterval())) .build() @@ -218,20 +217,19 @@ class WeatherUpdateWorker( override suspend fun doWork(): Result { Log.d("WeatherUpdateWorker", "Requesting weather data") val settingsData = settings.first() + val provider = WeatherProvider.getInstance(settingsData.provider) - // A provider with a managed location resolves its position itself and pushes weather - // updates instead of being polled (Breezy Weather does both). There is nothing to pull, - // and its getWeatherData() always returns null - which used to make this worker request a - // location (holding GPS and network listeners for up to ten minutes at a time) and then - // return Result.retry() forever, without ever setting lastUpdate, so the update interval - // check below could never short-circuit it either. - if (settingsData.managedLocation) { - Log.d("WeatherUpdateWorker", "Provider manages its own location, nothing to pull") + // A push-based provider sends its updates on its own and resolves its own location + // (Breezy Weather does both), so there is nothing to pull here. Its getWeatherData() + // always returns null, which used to make this worker request a location - registering + // GPS and network listeners and holding them for up to ten minutes - and then return + // Result.retry() forever, without ever setting lastUpdate, so the interval check below + // could never short-circuit it either. + if (provider.isPushBased) { + Log.d("WeatherUpdateWorker", "Provider pushes updates, nothing to pull") return Result.success() } - val provider = WeatherProvider.getInstance(settingsData.provider) - val updateInterval = provider.getUpdateInterval() val lastUpdate = settingsData.lastUpdate @@ -240,21 +238,26 @@ class WeatherUpdateWorker( return Result.failure() } - val weatherData = if (settingsData.autoLocation) { - val latLon = getLastKnownLocation() ?: settingsData.lastLocation - if (latLon == null) { - Log.e("WeatherUpdateWorker", "Could not get location") - return Result.failure() + val weatherData = when { + // A managed location means the provider resolves the position itself, so requesting + // one here would be wasted work that holds the location stack open. + settingsData.managedLocation, !settingsData.autoLocation -> { + val location = settings.location.first() + if (location == null) { + Log.e("WeatherUpdateWorker", "Location not set") + return Result.failure() + } + provider.getWeatherData(location) } - settings.setLastLocation(latLon) - provider.getWeatherData(latLon.lat, latLon.lon) - } else { - val location = settings.location.first() - if (location == null) { - Log.e("WeatherUpdateWorker", "Location not set") - return Result.failure() + else -> { + val latLon = getLastKnownLocation() ?: settingsData.lastLocation + if (latLon == null) { + Log.e("WeatherUpdateWorker", "Could not get location") + return Result.failure() + } + settings.setLastLocation(latLon) + provider.getWeatherData(latLon.lat, latLon.lon) } - provider.getWeatherData(location) } return if (weatherData == null) { diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/breezy/BreezyWeatherProvider.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/breezy/BreezyWeatherProvider.kt index 22c504a60..01f864a52 100644 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/breezy/BreezyWeatherProvider.kt +++ b/data/weather/src/main/java/de/mm20/launcher2/weather/breezy/BreezyWeatherProvider.kt @@ -20,6 +20,9 @@ class BreezyWeatherProvider( ) : WeatherProvider, KoinComponent { private val database: AppDatabase by inject() + // Breezy sends its weather data to us (see pushWeatherData) and manages its own location. + override val isPushBased: Boolean = true + override suspend fun getWeatherData(location: WeatherLocation): List? { // Noop implementation, because Breezy weather is handled in a special way return null