From fb3f28d03595a1c8c38f0c4f9ffbdbffc9fba1bd Mon Sep 17 00:00:00 2001 From: azuo Date: Fri, 15 Oct 2021 14:42:38 +0800 Subject: [PATCH] Make weather updates more reliable --- .../anotherwidget/helpers/WeatherHelper.kt | 11 +- .../network/WeatherNetworkApi.kt | 73 +++-- .../receivers/UpdatesReceiver.kt | 16 +- .../receivers/WeatherReceiver.kt | 26 +- .../services/UpdateCalendarWorker.kt | 259 +++++++++--------- .../anotherwidget/services/WeatherWorker.kt | 118 ++++---- .../tabs/WeatherProviderActivity.kt | 6 +- .../ui/fragments/tabs/PreferencesFragment.kt | 3 +- .../ui/viewmodels/MainViewModel.kt | 1 - .../anotherwidget/ui/widgets/MainWidget.kt | 2 +- 10 files changed, 266 insertions(+), 249 deletions(-) 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 d37172e..c0efdb2 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WeatherHelper.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WeatherHelper.kt @@ -19,8 +19,15 @@ import com.tommasoberlose.anotherwidget.utils.isDarkTheme object WeatherHelper { - fun updateWeather(context: Context) { - WeatherWorker.enqueue(context) + fun updateWeather(context: Context, force: Boolean = false) { + if (Preferences.showWeather || force) + WeatherWorker.enqueue(context, replace = force) + else { + removeWeather(context) + org.greenrobot.eventbus.EventBus.getDefault().post( + com.tommasoberlose.anotherwidget.ui.fragments.MainFragment.UpdateUiMessageEvent() + ) + } } fun removeWeather(context: Context) { 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 8880a0b..903c8b1 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/network/WeatherNetworkApi.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/network/WeatherNetworkApi.kt @@ -17,13 +17,11 @@ import com.tommasoberlose.anotherwidget.helpers.WeatherHelper import com.tommasoberlose.anotherwidget.network.repository.* import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import org.greenrobot.eventbus.EventBus import java.lang.Exception import java.text.SimpleDateFormat -import java.util.* +import kotlin.coroutines.resume +import kotlinx.coroutines.suspendCancellableCoroutine +import org.greenrobot.eventbus.EventBus class WeatherNetworkApi(val context: Context) { suspend fun updateWeather() { @@ -31,7 +29,7 @@ class WeatherNetworkApi(val context: Context) { Preferences.weatherProviderError = "-" Preferences.weatherProviderLocationError = "" - if (Preferences.showWeather && Preferences.customLocationLat != "" && Preferences.customLocationLon != "") { + if (Preferences.customLocationLat != "" && Preferences.customLocationLon != "") { when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) { Constants.WeatherProvider.OPEN_WEATHER -> useOpenWeatherMap(context) Constants.WeatherProvider.WEATHER_GOV -> useWeatherGov(context) @@ -42,46 +40,67 @@ class WeatherNetworkApi(val context: Context) { Constants.WeatherProvider.YR -> useYrProvider(context) } } else { - if (!Preferences.showWeather) - Preferences.weatherProviderError = context.getString(R.string.show_weather_not_visible) - else { - Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_missing_location) - Preferences.weatherProviderError = "" - } + Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_missing_location) + Preferences.weatherProviderError = "" WeatherHelper.removeWeather( context ) - EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) } } - private fun useOpenWeatherMap(context: Context) { + private suspend fun useOpenWeatherMap(context: Context) { if (Preferences.weatherProviderApiOpen != "") { val helper = OpenWeatherMapHelper(Preferences.weatherProviderApiOpen) helper.setUnits(if (Preferences.weatherTempUnit == "F") Units.IMPERIAL else Units.METRIC) - helper.getCurrentWeatherByGeoCoordinates(Preferences.customLocationLat.toDouble(), Preferences.customLocationLon.toDouble(), object : - CurrentWeatherCallback { - override fun onSuccess(currentWeather: CurrentWeather?) { - currentWeather?.let { - Preferences.weatherTemp = currentWeather.main.temp.toFloat() - Preferences.weatherIcon = currentWeather.weather[0].icon - Preferences.weatherRealTempUnit = Preferences.weatherTempUnit - MainWidget.updateWidget(context) + when (val response = suspendCancellableCoroutine { continuation -> + helper.getCurrentWeatherByGeoCoordinates(Preferences.customLocationLat.toDouble(), Preferences.customLocationLon.toDouble(), object : + CurrentWeatherCallback { + override fun onSuccess(currentWeather: CurrentWeather?) { + continuation.resume(currentWeather) } + override fun onFailure(throwable: Throwable?) { + continuation.resume(throwable) + } + }) + }) { + is CurrentWeather -> { + Preferences.weatherTemp = response.main.temp.toFloat() + Preferences.weatherIcon = response.weather[0].icon + Preferences.weatherRealTempUnit = Preferences.weatherTempUnit + MainWidget.updateWidget(context) + Preferences.weatherProviderError = "" Preferences.weatherProviderLocationError = "" - EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) } - - override fun onFailure(throwable: Throwable?) { + is Throwable -> { + if (response.javaClass == Throwable::class.java) { + // server error, see [OpenWeatherMapHelper.handleCurrentWeatherResponse] + if (response.message?.startsWith("UnAuthorized") == true) { + Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key) + Preferences.weatherProviderLocationError = "" + } + else { + Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic) + Preferences.weatherProviderLocationError = "" + } + WeatherHelper.removeWeather( + context + ) + } + else { + Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection) + Preferences.weatherProviderLocationError = "" + } + } + else -> { Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic) Preferences.weatherProviderLocationError = "" - EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) } - }) + } + EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) } else { Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key) Preferences.weatherProviderLocationError = "" diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/UpdatesReceiver.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/UpdatesReceiver.kt index c599294..82fccc4 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/UpdatesReceiver.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/UpdatesReceiver.kt @@ -13,11 +13,8 @@ import com.tommasoberlose.anotherwidget.helpers.* import com.tommasoberlose.anotherwidget.models.Event import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.utils.setExactIfCanSchedule -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.joda.time.Period import java.util.* +import org.joda.time.Period class UpdatesReceiver : BroadcastReceiver() { @@ -40,10 +37,6 @@ class UpdatesReceiver : BroadcastReceiver() { CalendarHelper.updateEventList(context) } - "com.sec.android.widgetapp.APPWIDGET_RESIZE", - AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED, - Actions.ACTION_ALARM_UPDATE, - Actions.ACTION_UPDATE_GREETINGS, Actions.ACTION_TIME_UPDATE -> { MainWidget.updateWidget(context) if (intent.hasExtra(EVENT_ID)) { @@ -51,6 +44,13 @@ class UpdatesReceiver : BroadcastReceiver() { } } + "com.sec.android.widgetapp.APPWIDGET_RESIZE", + AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED, + Actions.ACTION_ALARM_UPDATE, + Actions.ACTION_UPDATE_GREETINGS -> { + MainWidget.updateWidget(context) + } + Actions.ACTION_CLEAR_NOTIFICATION -> { ActiveNotificationsHelper.clearLastNotification(context) } diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/WeatherReceiver.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/WeatherReceiver.kt index 49c1c7a..6901ca6 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/WeatherReceiver.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/WeatherReceiver.kt @@ -1,18 +1,12 @@ package com.tommasoberlose.anotherwidget.receivers -import android.app.AlarmManager -import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import com.tommasoberlose.anotherwidget.global.Actions import com.tommasoberlose.anotherwidget.global.Preferences +import com.tommasoberlose.anotherwidget.helpers.WeatherHelper import com.tommasoberlose.anotherwidget.services.WeatherWorker -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import java.util.concurrent.TimeUnit - class WeatherReceiver : BroadcastReceiver() { @@ -22,10 +16,9 @@ class WeatherReceiver : BroadcastReceiver() { Intent.ACTION_MY_PACKAGE_REPLACED, Intent.ACTION_TIMEZONE_CHANGED, Intent.ACTION_LOCALE_CHANGED, - Intent.ACTION_TIME_CHANGED -> setUpdates(context) - + Intent.ACTION_TIME_CHANGED, Actions.ACTION_WEATHER_UPDATE -> { - WeatherWorker.enqueue(context) + WeatherHelper.updateWeather(context) } } } @@ -33,21 +26,12 @@ class WeatherReceiver : BroadcastReceiver() { companion object { fun setUpdates(context: Context) { if (Preferences.showWeather) { - val interval = when (Preferences.weatherRefreshPeriod) { - 0 -> 30 - 1 -> 60 - 2 -> 60L * 3 - 3 -> 60L * 6 - 4 -> 60L * 12 - 5 -> 60L * 24 - else -> 60 - } - WeatherWorker.enqueuePeriodic(context, interval, TimeUnit.MINUTES) + WeatherWorker.enqueueTrigger(context) } } fun removeUpdates(context: Context) { - WeatherWorker.cancelPeriodic(context) + WeatherWorker.cancelTrigger(context) } } } diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/services/UpdateCalendarWorker.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/services/UpdateCalendarWorker.kt index a8a3ac4..6e8144d 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/services/UpdateCalendarWorker.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/services/UpdateCalendarWorker.kt @@ -5,10 +5,10 @@ import android.content.Context import android.os.Build import android.provider.CalendarContract import androidx.work.Constraints -import androidx.work.CoroutineWorker import androidx.work.ExistingWorkPolicy import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager +import androidx.work.Worker import androidx.work.WorkerParameters import com.tommasoberlose.anotherwidget.db.EventRepository import com.tommasoberlose.anotherwidget.global.Preferences @@ -20,140 +20,135 @@ import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission +import java.util.* import me.everything.providers.android.calendar.CalendarProvider import org.greenrobot.eventbus.EventBus -import java.util.* -import kotlin.collections.ArrayList -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -class UpdateCalendarWorker(private val context: Context, params: WorkerParameters) : - CoroutineWorker(context, params) { +class UpdateCalendarWorker(context: Context, params: WorkerParameters) : Worker(context, params) { - override suspend fun doWork(): Result { - withContext(Dispatchers.IO) { - UpdatesReceiver.removeUpdates(context) - val eventRepository = EventRepository(context) + override fun doWork(): Result { + val context = applicationContext + UpdatesReceiver.removeUpdates(context) + val eventRepository = EventRepository(context) - if (Preferences.showEvents) { - if (!context.checkGrantedPermission(Manifest.permission.READ_CALENDAR)) { - eventRepository.resetNextEventData() - eventRepository.clearEvents() - } else { - // fetch all events from now to next ACTION_CALENDAR_UPDATE + limit - val now = Calendar.getInstance() - val limit = Calendar.getInstance().apply { - set(Calendar.MILLISECOND, 0) - set(Calendar.SECOND, 0) - set(Calendar.MINUTE, 0) - set(Calendar.HOUR_OF_DAY, 0) - add(Calendar.DATE, 1) - when (Preferences.showUntil) { - 0 -> add(Calendar.HOUR, 3) - 1 -> add(Calendar.HOUR, 6) - 2 -> add(Calendar.HOUR, 12) - 3 -> add(Calendar.DAY_OF_MONTH, 1) - 4 -> add(Calendar.DAY_OF_MONTH, 3) - 5 -> add(Calendar.DAY_OF_MONTH, 7) - 6 -> add(Calendar.MINUTE, 30) - 7 -> add(Calendar.HOUR, 1) - else -> add(Calendar.HOUR, 6) - } - } - - try { - val eventList = ArrayList() - val provider = CalendarProvider(context) - // apply time zone offset to correctly fetch all-day events - val data = provider.getInstances( - now.timeInMillis + now.timeZone.getOffset(now.timeInMillis).coerceAtMost(0), - limit.timeInMillis + limit.timeZone.getOffset(limit.timeInMillis).coerceAtLeast(0) - ) - if (data != null) { - val instances = data.list - for (instance in instances) { - try { - val e = provider.getEvent(instance.eventId) - if (e == null || e.deleted || CalendarHelper.getFilteredCalendarIdList().contains(e.calendarId)) - continue - if (e.allDay) { - val start = Calendar.getInstance() - start.timeInMillis = instance.begin - val end = Calendar.getInstance() - end.timeInMillis = instance.end - instance.begin = - start.timeInMillis - start.timeZone.getOffset(start.timeInMillis) - instance.end = - end.timeInMillis - end.timeZone.getOffset(end.timeInMillis) - } - if (instance.begin <= limit.timeInMillis && now.timeInMillis < instance.end) { - /* Following check may result in "fake" all-day events with - * non-UTC start/end time, and therefore cannot be found by - * Calendar when tapped to open details. - // Check all day events - val startDate = Calendar.getInstance() - startDate.timeInMillis = instance.begin - val endDate = Calendar.getInstance() - endDate.timeInMillis = instance.end - - val isAllDay = e.allDay || ( - startDate.get(Calendar.MILLISECOND) == 0 - && startDate.get(Calendar.SECOND) == 0 - && startDate.get(Calendar.MINUTE) == 0 - && startDate.get(Calendar.HOUR_OF_DAY) == 0 - && endDate.get(Calendar.MILLISECOND) == 0 - && endDate.get(Calendar.SECOND) == 0 - && endDate.get(Calendar.MINUTE) == 0 - && endDate.get(Calendar.HOUR_OF_DAY) == 0 - ) - */ - - eventList.add( - Event( - id = instance.id, - eventID = e.id, - title = e.title ?: "", - startDate = instance.begin, - endDate = instance.end, - calendarID = e.calendarId, - allDay = e.allDay, - address = e.eventLocation ?: "", - selfAttendeeStatus = e.selfAttendeeStatus.toInt(), - availability = e.availability - ) - ) - } - } catch (ignored: Exception) { - } - } - } - - val sortedEvents = eventList.sortEvents() - val filteredEventList = sortedEvents.applyFilters() - - if (filteredEventList.isEmpty()) { - eventRepository.resetNextEventData() - eventRepository.clearEvents() - } else { - eventRepository.saveEvents(sortedEvents) - //eventRepository.saveNextEventData(filteredEventList.first()) - } - } catch (ignored: java.lang.Exception) { - } - } - enqueueTrigger(context) - } else { + if (Preferences.showEvents) { + if (!context.checkGrantedPermission(Manifest.permission.READ_CALENDAR)) { eventRepository.resetNextEventData() eventRepository.clearEvents() + } else { + // fetch all events from now to next ACTION_CALENDAR_UPDATE + limit + val now = Calendar.getInstance() + val limit = Calendar.getInstance().apply { + set(Calendar.MILLISECOND, 0) + set(Calendar.SECOND, 0) + set(Calendar.MINUTE, 0) + set(Calendar.HOUR_OF_DAY, 0) + add(Calendar.DATE, 1) + when (Preferences.showUntil) { + 0 -> add(Calendar.HOUR, 3) + 1 -> add(Calendar.HOUR, 6) + 2 -> add(Calendar.HOUR, 12) + 3 -> add(Calendar.DAY_OF_MONTH, 1) + 4 -> add(Calendar.DAY_OF_MONTH, 3) + 5 -> add(Calendar.DAY_OF_MONTH, 7) + 6 -> add(Calendar.MINUTE, 30) + 7 -> add(Calendar.HOUR, 1) + else -> add(Calendar.HOUR, 6) + } + } + + try { + val eventList = ArrayList() + val provider = CalendarProvider(context) + // apply time zone offset to correctly fetch all-day events + val data = provider.getInstances( + now.timeInMillis + now.timeZone.getOffset(now.timeInMillis).coerceAtMost(0), + limit.timeInMillis + limit.timeZone.getOffset(limit.timeInMillis).coerceAtLeast(0) + ) + if (data != null) { + val instances = data.list + for (instance in instances) { + try { + val e = provider.getEvent(instance.eventId) + if (e == null || e.deleted || CalendarHelper.getFilteredCalendarIdList().contains(e.calendarId)) + continue + if (e.allDay) { + val start = Calendar.getInstance() + start.timeInMillis = instance.begin + val end = Calendar.getInstance() + end.timeInMillis = instance.end + instance.begin = + start.timeInMillis - start.timeZone.getOffset(start.timeInMillis) + instance.end = + end.timeInMillis - end.timeZone.getOffset(end.timeInMillis) + } + if (instance.begin <= limit.timeInMillis && now.timeInMillis < instance.end) { + /* Following check may result in "fake" all-day events with + * non-UTC start/end time, and therefore cannot be found by + * Calendar when tapped to open details. + // Check all day events + val startDate = Calendar.getInstance() + startDate.timeInMillis = instance.begin + val endDate = Calendar.getInstance() + endDate.timeInMillis = instance.end + + val isAllDay = e.allDay || ( + startDate.get(Calendar.MILLISECOND) == 0 + && startDate.get(Calendar.SECOND) == 0 + && startDate.get(Calendar.MINUTE) == 0 + && startDate.get(Calendar.HOUR_OF_DAY) == 0 + && endDate.get(Calendar.MILLISECOND) == 0 + && endDate.get(Calendar.SECOND) == 0 + && endDate.get(Calendar.MINUTE) == 0 + && endDate.get(Calendar.HOUR_OF_DAY) == 0 + ) + */ + + eventList.add( + Event( + id = instance.id, + eventID = e.id, + title = e.title ?: "", + startDate = instance.begin, + endDate = instance.end, + calendarID = e.calendarId, + allDay = e.allDay, + address = e.eventLocation ?: "", + selfAttendeeStatus = e.selfAttendeeStatus.toInt(), + availability = e.availability + ) + ) + } + } catch (ignored: Exception) { + } + } + } + + val sortedEvents = eventList.sortEvents() + val filteredEventList = sortedEvents.applyFilters() + + if (filteredEventList.isEmpty()) { + eventRepository.resetNextEventData() + eventRepository.clearEvents() + } else { + eventRepository.saveEvents(sortedEvents) + eventRepository.saveNextEventData(filteredEventList.first()) + } + } catch (ignored: java.lang.Exception) { + } } - eventRepository.close() - - UpdatesReceiver.setUpdates(context) - MainWidget.updateWidget(context) - - EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) + } else { + eventRepository.resetNextEventData() + eventRepository.clearEvents() } + eventRepository.close() + UpdatesReceiver.setUpdates(context) + MainWidget.updateWidget(context) + EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) + + if (Preferences.showEvents) + enqueueTrigger(context) return Result.success() } @@ -169,9 +164,9 @@ class UpdateCalendarWorker(private val context: Context, params: WorkerParameter fun enqueueTrigger(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { WorkManager.getInstance(context).enqueueUniqueWork( - "updateEventListByTrigger", + "updateEventListTrigger", ExistingWorkPolicy.KEEP, - OneTimeWorkRequestBuilder().setConstraints( + OneTimeWorkRequestBuilder().setConstraints( Constraints.Builder().addContentUriTrigger( CalendarContract.CONTENT_URI, true @@ -184,9 +179,17 @@ class UpdateCalendarWorker(private val context: Context, params: WorkerParameter fun cancelTrigger(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { WorkManager.getInstance(context).cancelUniqueWork( - "updateEventListByTrigger" + "updateEventListTrigger" ) } } } + + class Trigger(context: Context, params: WorkerParameters) : Worker(context, params) { + override fun doWork(): Result { + if (Preferences.showEvents && !isStopped) + enqueue(applicationContext) + return Result.success() + } + } } diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/services/WeatherWorker.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/services/WeatherWorker.kt index 3f3edbb..91761a1 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/services/WeatherWorker.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/services/WeatherWorker.kt @@ -5,100 +5,104 @@ import android.content.Context import android.location.Location import android.location.LocationManager import androidx.work.CoroutineWorker -import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.ExistingWorkPolicy import androidx.work.OneTimeWorkRequestBuilder -import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager +import androidx.work.Worker import androidx.work.WorkerParameters import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability import com.google.android.gms.location.LocationServices -import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.global.Preferences -import com.tommasoberlose.anotherwidget.helpers.WeatherHelper import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi -import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission -import org.greenrobot.eventbus.EventBus import java.util.concurrent.TimeUnit import kotlin.coroutines.resume import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext -class WeatherWorker(private val context: Context, params: WorkerParameters) : - CoroutineWorker(context, params) { +class WeatherWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { - when { - Preferences.customLocationAdd != "" -> { - withContext(Dispatchers.IO) { - WeatherNetworkApi(context).updateWeather() - } - } - context.checkGrantedPermission(Manifest.permission.ACCESS_COARSE_LOCATION) -> { - if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) - == ConnectionResult.SUCCESS - ) { - suspendCancellableCoroutine { continuation -> - LocationServices.getFusedLocationProviderClient(context).lastLocation.addOnCompleteListener { - continuation.resume(if (it.isSuccessful) it.result else null) - } - } - } else { - val lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager - var location: Location? = null - for (provider in lm.getProviders(true)) { - lm.getLastKnownLocation(provider)?.let { - if (location == null || - it.time - location!!.time > 2 * 60 * 1000 || - (it.time - location!!.time > -2 * 60 * 1000 && it.accuracy < location!!.accuracy)) - location = it - } - } - location - }.let { location -> - if (location != null) { - Preferences.customLocationLat = location.latitude.toString() - Preferences.customLocationLon = location.longitude.toString() - } - withContext(Dispatchers.IO) { - WeatherNetworkApi(context).updateWeather() + val context = applicationContext + if (Preferences.customLocationAdd == "" && + context.checkGrantedPermission(Manifest.permission.ACCESS_COARSE_LOCATION) + ) { + if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) + == ConnectionResult.SUCCESS + ) { + suspendCancellableCoroutine { continuation -> + LocationServices.getFusedLocationProviderClient(context).lastLocation.addOnCompleteListener { + continuation.resume(if (it.isSuccessful) it.result else null) } } - } - else -> { - Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_missing_location) - Preferences.weatherProviderError = "" - WeatherHelper.removeWeather(context) - EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) + } else { + val lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager + var location: Location? = null + for (provider in lm.getProviders(true)) { + lm.getLastKnownLocation(provider)?.let { + if (location == null || + it.time - location!!.time > 2 * 60 * 1000 || + (it.time - location!!.time > -2 * 60 * 1000 && it.accuracy < location!!.accuracy)) + location = it + } + } + location + }?.let { location -> + Preferences.customLocationLat = location.latitude.toString() + Preferences.customLocationLon = location.longitude.toString() } } + withContext(Dispatchers.IO) { + WeatherNetworkApi(context).updateWeather() + } + + if (Preferences.showWeather) + enqueueTrigger(context) return Result.success() } companion object { - fun enqueue(context: Context) { + fun enqueue(context: Context, replace: Boolean = false) { WorkManager.getInstance(context).enqueueUniqueWork( "updateWeather", - ExistingWorkPolicy.REPLACE, + if (replace) ExistingWorkPolicy.REPLACE else ExistingWorkPolicy.KEEP, OneTimeWorkRequestBuilder().build() ) } - fun enqueuePeriodic(context: Context, interval: Long, unit: TimeUnit) { - WorkManager.getInstance(context).enqueueUniquePeriodicWork( - "updateWeatherPeriodically", - ExistingPeriodicWorkPolicy.REPLACE, - PeriodicWorkRequestBuilder(interval, unit).build() + fun enqueueTrigger(context: Context) { + val interval = when (Preferences.weatherRefreshPeriod) { + 0 -> 30 + 1 -> 60 + 2 -> 60L * 3 + 3 -> 60L * 6 + 4 -> 60L * 12 + 5 -> 60L * 24 + else -> 60 + } + WorkManager.getInstance(context).enqueueUniqueWork( + "updateWeatherTrigger", + ExistingWorkPolicy.REPLACE, + OneTimeWorkRequestBuilder().setInitialDelay( + interval, TimeUnit.MINUTES + ).build() ) } - fun cancelPeriodic(context: Context) { + fun cancelTrigger(context: Context) { WorkManager.getInstance(context).cancelUniqueWork( - "updateWeatherPeriodically" + "updateWeatherTrigger" ) } } + + class Trigger(context: Context, params: WorkerParameters) : Worker(context, params) { + override fun doWork(): Result { + if (Preferences.showWeather && !isStopped) + enqueue(applicationContext) + return Result.success() + } + } } diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/tabs/WeatherProviderActivity.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/tabs/WeatherProviderActivity.kt index 5296a44..217950a 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/tabs/WeatherProviderActivity.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/tabs/WeatherProviderActivity.kt @@ -57,7 +57,7 @@ class WeatherProviderActivity : AppCompatActivity() { updateListItem() binding.loader.isVisible = true - WeatherHelper.updateWeather(this@WeatherProviderActivity) + WeatherHelper.updateWeather(this@WeatherProviderActivity, true) } .clicked(R.id.radioButton) { if (Preferences.weatherProvider != provider.rawValue) { @@ -70,7 +70,7 @@ class WeatherProviderActivity : AppCompatActivity() { updateListItem() binding.loader.isVisible = true - WeatherHelper.updateWeather(this@WeatherProviderActivity) + WeatherHelper.updateWeather(this@WeatherProviderActivity, true) } .checked(R.id.radioButton, provider.rawValue == Preferences.weatherProvider) .with(R.id.text2) { @@ -89,7 +89,7 @@ class WeatherProviderActivity : AppCompatActivity() { .clicked(R.id.action_configure) { BottomSheetWeatherProviderSettings(this) { binding.loader.isVisible = true - WeatherHelper.updateWeather(this@WeatherProviderActivity) + WeatherHelper.updateWeather(this@WeatherProviderActivity, true) }.show() } .visibility(R.id.action_configure, if (/*WeatherHelper.isKeyRequired(provider) && */provider.rawValue == Preferences.weatherProvider) View.VISIBLE else View.GONE) diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/tabs/PreferencesFragment.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/tabs/PreferencesFragment.kt index 6295a55..89bf214 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/tabs/PreferencesFragment.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/tabs/PreferencesFragment.kt @@ -23,6 +23,7 @@ import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog import com.tommasoberlose.anotherwidget.databinding.FragmentPreferencesBinding import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.helpers.CalendarHelper +import com.tommasoberlose.anotherwidget.helpers.WeatherHelper import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver import com.tommasoberlose.anotherwidget.receivers.WeatherReceiver import com.tommasoberlose.anotherwidget.ui.activities.MainActivity @@ -137,7 +138,7 @@ class PreferencesFragment : Fragment() { if (enabled) { Preferences.weatherProviderError = "" Preferences.weatherProviderLocationError = "" - WeatherReceiver.setUpdates(requireContext()) + WeatherHelper.updateWeather(requireContext()) } else { WeatherReceiver.removeUpdates(requireContext()) } diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/viewmodels/MainViewModel.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/viewmodels/MainViewModel.kt index 872fa15..74b68a1 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/viewmodels/MainViewModel.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/viewmodels/MainViewModel.kt @@ -156,7 +156,6 @@ class MainViewModel(context: Application) : AndroidViewModel(context) { addSource(Preferences.asLiveData(Preferences::showWeather)) { value = true } addSource(Preferences.asLiveData(Preferences::weatherProvider)) { value = true } - addSource(Preferences.asLiveData(Preferences::weatherTempUnit)) { value = true } addSource(Preferences.asLiveData(Preferences::weatherIconPack)) { value = true } addSource(Preferences.asLiveData(Preferences::customLocationLat)) { value = true } addSource(Preferences.asLiveData(Preferences::customLocationLon)) { value = true } diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/MainWidget.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/MainWidget.kt index cd70e38..fb58d2a 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/MainWidget.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/MainWidget.kt @@ -32,7 +32,7 @@ class MainWidget : AppWidgetProvider() { override fun onEnabled(context: Context) { CalendarHelper.updateEventList(context) - WeatherReceiver.setUpdates(context) + WeatherHelper.updateWeather(context) MediaPlayerHelper.updatePlayingMediaInfo(context) }