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