Make weather updates more reliable

This commit is contained in:
azuo 2021-10-15 14:42:38 +08:00
parent d8e204c5d9
commit fb3f28d035
10 changed files with 266 additions and 249 deletions

View File

@ -19,8 +19,15 @@ import com.tommasoberlose.anotherwidget.utils.isDarkTheme
object WeatherHelper { object WeatherHelper {
fun updateWeather(context: Context) { fun updateWeather(context: Context, force: Boolean = false) {
WeatherWorker.enqueue(context) 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) { fun removeWeather(context: Context) {

View File

@ -17,13 +17,11 @@ import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
import com.tommasoberlose.anotherwidget.network.repository.* import com.tommasoberlose.anotherwidget.network.repository.*
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget 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.lang.Exception
import java.text.SimpleDateFormat 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) { class WeatherNetworkApi(val context: Context) {
suspend fun updateWeather() { suspend fun updateWeather() {
@ -31,7 +29,7 @@ class WeatherNetworkApi(val context: Context) {
Preferences.weatherProviderError = "-" Preferences.weatherProviderError = "-"
Preferences.weatherProviderLocationError = "" Preferences.weatherProviderLocationError = ""
if (Preferences.showWeather && Preferences.customLocationLat != "" && Preferences.customLocationLon != "") { if (Preferences.customLocationLat != "" && Preferences.customLocationLon != "") {
when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) { when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
Constants.WeatherProvider.OPEN_WEATHER -> useOpenWeatherMap(context) Constants.WeatherProvider.OPEN_WEATHER -> useOpenWeatherMap(context)
Constants.WeatherProvider.WEATHER_GOV -> useWeatherGov(context) Constants.WeatherProvider.WEATHER_GOV -> useWeatherGov(context)
@ -42,46 +40,67 @@ class WeatherNetworkApi(val context: Context) {
Constants.WeatherProvider.YR -> useYrProvider(context) Constants.WeatherProvider.YR -> useYrProvider(context)
} }
} else { } else {
if (!Preferences.showWeather) Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_missing_location)
Preferences.weatherProviderError = context.getString(R.string.show_weather_not_visible) Preferences.weatherProviderError = ""
else {
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_missing_location)
Preferences.weatherProviderError = ""
}
WeatherHelper.removeWeather( WeatherHelper.removeWeather(
context context
) )
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} }
} }
private fun useOpenWeatherMap(context: Context) { private suspend fun useOpenWeatherMap(context: Context) {
if (Preferences.weatherProviderApiOpen != "") { if (Preferences.weatherProviderApiOpen != "") {
val helper = OpenWeatherMapHelper(Preferences.weatherProviderApiOpen) val helper = OpenWeatherMapHelper(Preferences.weatherProviderApiOpen)
helper.setUnits(if (Preferences.weatherTempUnit == "F") Units.IMPERIAL else Units.METRIC) helper.setUnits(if (Preferences.weatherTempUnit == "F") Units.IMPERIAL else Units.METRIC)
helper.getCurrentWeatherByGeoCoordinates(Preferences.customLocationLat.toDouble(), Preferences.customLocationLon.toDouble(), object : when (val response = suspendCancellableCoroutine<Any?> { continuation ->
CurrentWeatherCallback { helper.getCurrentWeatherByGeoCoordinates(Preferences.customLocationLat.toDouble(), Preferences.customLocationLon.toDouble(), object :
override fun onSuccess(currentWeather: CurrentWeather?) { CurrentWeatherCallback {
currentWeather?.let { override fun onSuccess(currentWeather: CurrentWeather?) {
Preferences.weatherTemp = currentWeather.main.temp.toFloat() continuation.resume(currentWeather)
Preferences.weatherIcon = currentWeather.weather[0].icon
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
MainWidget.updateWidget(context)
} }
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.weatherProviderError = ""
Preferences.weatherProviderLocationError = "" Preferences.weatherProviderLocationError = ""
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} }
is Throwable -> {
override fun onFailure(throwable: 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.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = "" Preferences.weatherProviderLocationError = ""
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} }
}) }
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} else { } else {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key) Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
Preferences.weatherProviderLocationError = "" Preferences.weatherProviderLocationError = ""

View File

@ -13,11 +13,8 @@ import com.tommasoberlose.anotherwidget.helpers.*
import com.tommasoberlose.anotherwidget.models.Event import com.tommasoberlose.anotherwidget.models.Event
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.setExactIfCanSchedule 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 java.util.*
import org.joda.time.Period
class UpdatesReceiver : BroadcastReceiver() { class UpdatesReceiver : BroadcastReceiver() {
@ -40,10 +37,6 @@ class UpdatesReceiver : BroadcastReceiver() {
CalendarHelper.updateEventList(context) 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 -> { Actions.ACTION_TIME_UPDATE -> {
MainWidget.updateWidget(context) MainWidget.updateWidget(context)
if (intent.hasExtra(EVENT_ID)) { 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 -> { Actions.ACTION_CLEAR_NOTIFICATION -> {
ActiveNotificationsHelper.clearLastNotification(context) ActiveNotificationsHelper.clearLastNotification(context)
} }

View File

@ -1,18 +1,12 @@
package com.tommasoberlose.anotherwidget.receivers package com.tommasoberlose.anotherwidget.receivers
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import com.tommasoberlose.anotherwidget.global.Actions import com.tommasoberlose.anotherwidget.global.Actions
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
import com.tommasoberlose.anotherwidget.services.WeatherWorker 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() { class WeatherReceiver : BroadcastReceiver() {
@ -22,10 +16,9 @@ class WeatherReceiver : BroadcastReceiver() {
Intent.ACTION_MY_PACKAGE_REPLACED, Intent.ACTION_MY_PACKAGE_REPLACED,
Intent.ACTION_TIMEZONE_CHANGED, Intent.ACTION_TIMEZONE_CHANGED,
Intent.ACTION_LOCALE_CHANGED, Intent.ACTION_LOCALE_CHANGED,
Intent.ACTION_TIME_CHANGED -> setUpdates(context) Intent.ACTION_TIME_CHANGED,
Actions.ACTION_WEATHER_UPDATE -> { Actions.ACTION_WEATHER_UPDATE -> {
WeatherWorker.enqueue(context) WeatherHelper.updateWeather(context)
} }
} }
} }
@ -33,21 +26,12 @@ class WeatherReceiver : BroadcastReceiver() {
companion object { companion object {
fun setUpdates(context: Context) { fun setUpdates(context: Context) {
if (Preferences.showWeather) { if (Preferences.showWeather) {
val interval = when (Preferences.weatherRefreshPeriod) { WeatherWorker.enqueueTrigger(context)
0 -> 30
1 -> 60
2 -> 60L * 3
3 -> 60L * 6
4 -> 60L * 12
5 -> 60L * 24
else -> 60
}
WeatherWorker.enqueuePeriodic(context, interval, TimeUnit.MINUTES)
} }
} }
fun removeUpdates(context: Context) { fun removeUpdates(context: Context) {
WeatherWorker.cancelPeriodic(context) WeatherWorker.cancelTrigger(context)
} }
} }
} }

View File

@ -5,10 +5,10 @@ import android.content.Context
import android.os.Build import android.os.Build
import android.provider.CalendarContract import android.provider.CalendarContract
import androidx.work.Constraints import androidx.work.Constraints
import androidx.work.CoroutineWorker
import androidx.work.ExistingWorkPolicy import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.tommasoberlose.anotherwidget.db.EventRepository import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Preferences 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.fragments.MainFragment
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import java.util.*
import me.everything.providers.android.calendar.CalendarProvider import me.everything.providers.android.calendar.CalendarProvider
import org.greenrobot.eventbus.EventBus 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) : class UpdateCalendarWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
CoroutineWorker(context, params) {
override suspend fun doWork(): Result { override fun doWork(): Result {
withContext(Dispatchers.IO) { val context = applicationContext
UpdatesReceiver.removeUpdates(context) UpdatesReceiver.removeUpdates(context)
val eventRepository = EventRepository(context) val eventRepository = EventRepository(context)
if (Preferences.showEvents) { if (Preferences.showEvents) {
if (!context.checkGrantedPermission(Manifest.permission.READ_CALENDAR)) { 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<Event>()
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 {
eventRepository.resetNextEventData() eventRepository.resetNextEventData()
eventRepository.clearEvents() 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<Event>()
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() } else {
eventRepository.resetNextEventData()
UpdatesReceiver.setUpdates(context) eventRepository.clearEvents()
MainWidget.updateWidget(context)
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} }
eventRepository.close()
UpdatesReceiver.setUpdates(context)
MainWidget.updateWidget(context)
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
if (Preferences.showEvents)
enqueueTrigger(context)
return Result.success() return Result.success()
} }
@ -169,9 +164,9 @@ class UpdateCalendarWorker(private val context: Context, params: WorkerParameter
fun enqueueTrigger(context: Context) { fun enqueueTrigger(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
WorkManager.getInstance(context).enqueueUniqueWork( WorkManager.getInstance(context).enqueueUniqueWork(
"updateEventListByTrigger", "updateEventListTrigger",
ExistingWorkPolicy.KEEP, ExistingWorkPolicy.KEEP,
OneTimeWorkRequestBuilder<UpdateCalendarWorker>().setConstraints( OneTimeWorkRequestBuilder<Trigger>().setConstraints(
Constraints.Builder().addContentUriTrigger( Constraints.Builder().addContentUriTrigger(
CalendarContract.CONTENT_URI, CalendarContract.CONTENT_URI,
true true
@ -184,9 +179,17 @@ class UpdateCalendarWorker(private val context: Context, params: WorkerParameter
fun cancelTrigger(context: Context) { fun cancelTrigger(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
WorkManager.getInstance(context).cancelUniqueWork( 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()
}
}
} }

View File

@ -5,100 +5,104 @@ import android.content.Context
import android.location.Location import android.location.Location
import android.location.LocationManager import android.location.LocationManager
import androidx.work.CoroutineWorker import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.ExistingWorkPolicy import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.location.LocationServices import com.google.android.gms.location.LocationServices
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import org.greenrobot.eventbus.EventBus
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class WeatherWorker(private val context: Context, params: WorkerParameters) : class WeatherWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
CoroutineWorker(context, params) {
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
when { val context = applicationContext
Preferences.customLocationAdd != "" -> { if (Preferences.customLocationAdd == "" &&
withContext(Dispatchers.IO) { context.checkGrantedPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
WeatherNetworkApi(context).updateWeather() ) {
} if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context)
} == ConnectionResult.SUCCESS
context.checkGrantedPermission(Manifest.permission.ACCESS_COARSE_LOCATION) -> { ) {
if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) suspendCancellableCoroutine { continuation ->
== ConnectionResult.SUCCESS LocationServices.getFusedLocationProviderClient(context).lastLocation.addOnCompleteListener {
) { continuation.resume(if (it.isSuccessful) it.result else null)
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()
} }
} }
} } else {
else -> { val lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_missing_location) var location: Location? = null
Preferences.weatherProviderError = "" for (provider in lm.getProviders(true)) {
WeatherHelper.removeWeather(context) lm.getLastKnownLocation(provider)?.let {
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) 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() return Result.success()
} }
companion object { companion object {
fun enqueue(context: Context) { fun enqueue(context: Context, replace: Boolean = false) {
WorkManager.getInstance(context).enqueueUniqueWork( WorkManager.getInstance(context).enqueueUniqueWork(
"updateWeather", "updateWeather",
ExistingWorkPolicy.REPLACE, if (replace) ExistingWorkPolicy.REPLACE else ExistingWorkPolicy.KEEP,
OneTimeWorkRequestBuilder<WeatherWorker>().build() OneTimeWorkRequestBuilder<WeatherWorker>().build()
) )
} }
fun enqueuePeriodic(context: Context, interval: Long, unit: TimeUnit) { fun enqueueTrigger(context: Context) {
WorkManager.getInstance(context).enqueueUniquePeriodicWork( val interval = when (Preferences.weatherRefreshPeriod) {
"updateWeatherPeriodically", 0 -> 30
ExistingPeriodicWorkPolicy.REPLACE, 1 -> 60
PeriodicWorkRequestBuilder<WeatherWorker>(interval, unit).build() 2 -> 60L * 3
3 -> 60L * 6
4 -> 60L * 12
5 -> 60L * 24
else -> 60
}
WorkManager.getInstance(context).enqueueUniqueWork(
"updateWeatherTrigger",
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequestBuilder<Trigger>().setInitialDelay(
interval, TimeUnit.MINUTES
).build()
) )
} }
fun cancelPeriodic(context: Context) { fun cancelTrigger(context: Context) {
WorkManager.getInstance(context).cancelUniqueWork( 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()
}
}
} }

View File

@ -57,7 +57,7 @@ class WeatherProviderActivity : AppCompatActivity() {
updateListItem() updateListItem()
binding.loader.isVisible = true binding.loader.isVisible = true
WeatherHelper.updateWeather(this@WeatherProviderActivity) WeatherHelper.updateWeather(this@WeatherProviderActivity, true)
} }
.clicked(R.id.radioButton) { .clicked(R.id.radioButton) {
if (Preferences.weatherProvider != provider.rawValue) { if (Preferences.weatherProvider != provider.rawValue) {
@ -70,7 +70,7 @@ class WeatherProviderActivity : AppCompatActivity() {
updateListItem() updateListItem()
binding.loader.isVisible = true binding.loader.isVisible = true
WeatherHelper.updateWeather(this@WeatherProviderActivity) WeatherHelper.updateWeather(this@WeatherProviderActivity, true)
} }
.checked(R.id.radioButton, provider.rawValue == Preferences.weatherProvider) .checked(R.id.radioButton, provider.rawValue == Preferences.weatherProvider)
.with<TextView>(R.id.text2) { .with<TextView>(R.id.text2) {
@ -89,7 +89,7 @@ class WeatherProviderActivity : AppCompatActivity() {
.clicked(R.id.action_configure) { .clicked(R.id.action_configure) {
BottomSheetWeatherProviderSettings(this) { BottomSheetWeatherProviderSettings(this) {
binding.loader.isVisible = true binding.loader.isVisible = true
WeatherHelper.updateWeather(this@WeatherProviderActivity) WeatherHelper.updateWeather(this@WeatherProviderActivity, true)
}.show() }.show()
} }
.visibility(R.id.action_configure, if (/*WeatherHelper.isKeyRequired(provider) && */provider.rawValue == Preferences.weatherProvider) View.VISIBLE else View.GONE) .visibility(R.id.action_configure, if (/*WeatherHelper.isKeyRequired(provider) && */provider.rawValue == Preferences.weatherProvider) View.VISIBLE else View.GONE)

View File

@ -23,6 +23,7 @@ import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog
import com.tommasoberlose.anotherwidget.databinding.FragmentPreferencesBinding import com.tommasoberlose.anotherwidget.databinding.FragmentPreferencesBinding
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
import com.tommasoberlose.anotherwidget.receivers.WeatherReceiver import com.tommasoberlose.anotherwidget.receivers.WeatherReceiver
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
@ -137,7 +138,7 @@ class PreferencesFragment : Fragment() {
if (enabled) { if (enabled) {
Preferences.weatherProviderError = "" Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = "" Preferences.weatherProviderLocationError = ""
WeatherReceiver.setUpdates(requireContext()) WeatherHelper.updateWeather(requireContext())
} else { } else {
WeatherReceiver.removeUpdates(requireContext()) WeatherReceiver.removeUpdates(requireContext())
} }

View File

@ -156,7 +156,6 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
addSource(Preferences.asLiveData(Preferences::showWeather)) { value = true } addSource(Preferences.asLiveData(Preferences::showWeather)) { value = true }
addSource(Preferences.asLiveData(Preferences::weatherProvider)) { 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::weatherIconPack)) { value = true }
addSource(Preferences.asLiveData(Preferences::customLocationLat)) { value = true } addSource(Preferences.asLiveData(Preferences::customLocationLat)) { value = true }
addSource(Preferences.asLiveData(Preferences::customLocationLon)) { value = true } addSource(Preferences.asLiveData(Preferences::customLocationLon)) { value = true }

View File

@ -32,7 +32,7 @@ class MainWidget : AppWidgetProvider() {
override fun onEnabled(context: Context) { override fun onEnabled(context: Context) {
CalendarHelper.updateEventList(context) CalendarHelper.updateEventList(context)
WeatherReceiver.setUpdates(context) WeatherHelper.updateWeather(context)
MediaPlayerHelper.updatePlayingMediaInfo(context) MediaPlayerHelper.updatePlayingMediaInfo(context)
} }