From 3ab42fd1633852aef42016f70751e08cf6de3bcf Mon Sep 17 00:00:00 2001 From: azuo Date: Fri, 3 Sep 2021 18:20:01 +0800 Subject: [PATCH] Correct and improve calendar events updating algorithm. 1. UpdateCalendarService will fetch all events from now to the next fetch time + Preferences.showUntil, so that EventRepository holds all events need to be shown before the next fetch; 2. Every event is updated 1 minute after the corresponding time span, to make the actual time difference consistent with the displayed getRelativeTimeSpanString; 3. Update events only when necessary, eliminate redundant alarms; cancel all update alarms when showEvents is turned off. --- .../anotherwidget/db/EventRepository.kt | 4 +- .../helpers/SettingsStringHelper.kt | 32 +--- .../receivers/NewCalendarEventReceiver.kt | 3 +- .../receivers/UpdatesReceiver.kt | 177 ++++++++---------- .../receivers/WeatherReceiver.kt | 2 - .../services/UpdateCalendarService.kt | 15 +- .../ui/fragments/tabs/PreferencesFragment.kt | 2 + 7 files changed, 102 insertions(+), 133 deletions(-) diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/db/EventRepository.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/db/EventRepository.kt index 342d107..9811857 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/db/EventRepository.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/db/EventRepository.kt @@ -64,7 +64,7 @@ class EventRepository(val context: Context) { else -> add(Calendar.HOUR, 6) } } - val event = if (nextEvent != null && nextEvent.endDate > now && nextEvent.startDate < limit.timeInMillis) { + val event = if (nextEvent != null && nextEvent.endDate > now && nextEvent.startDate <= limit.timeInMillis) { nextEvent } else { val events = getEvents() @@ -105,7 +105,6 @@ class EventRepository(val context: Context) { } else { resetNextEventData() } - UpdatesReceiver.setUpdates(context) MainWidget.updateWidget(context) } @@ -121,7 +120,6 @@ class EventRepository(val context: Context) { } else { resetNextEventData() } - UpdatesReceiver.setUpdates(context) MainWidget.updateWidget(context) } diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/SettingsStringHelper.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/SettingsStringHelper.kt index 3402c91..6f511fa 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/SettingsStringHelper.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/SettingsStringHelper.kt @@ -88,19 +88,17 @@ object SettingsStringHelper { fun getDifferenceText(context: Context, now: Long, start: Long): String { val nowDate = DateTime(now) val eventDate = DateTime(start) - - var difference = start - now - difference += 60 * 1000 - (difference % (60 * 1000)) + val difference = start - now when { difference <= 0 -> { return "" } - TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.HIGH.rawValue && TimeUnit.MILLISECONDS.toMinutes(difference) > 5 -> { - return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - 1 - (TimeUnit.MILLISECONDS.toMinutes(difference) - 1) % 5), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString() + TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.HIGH.rawValue && TimeUnit.MILLISECONDS.toMinutes(difference) >= 5 -> { + return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - TimeUnit.MILLISECONDS.toMinutes(difference) % 5), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString() } - TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.DEFAULT.rawValue && TimeUnit.MILLISECONDS.toMinutes(difference) > 15 -> { - return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - 1 - (TimeUnit.MILLISECONDS.toMinutes(difference) - 1) % 15), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString() + TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.DEFAULT.rawValue && TimeUnit.MILLISECONDS.toMinutes(difference) >= 15 -> { + return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - TimeUnit.MILLISECONDS.toMinutes(difference) % 15), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString() } TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.LOW.rawValue -> { return context.getString(R.string.soon) @@ -109,22 +107,7 @@ object SettingsStringHelper { return context.getString(R.string.now) } TimeUnit.MILLISECONDS.toHours(difference) < 12 -> { - val minutes = TimeUnit.MILLISECONDS.toMinutes(difference) - 60 * TimeUnit.MILLISECONDS.toHours(difference) - return if (minutes < 1 || minutes > 30) { - DateUtils.getRelativeTimeSpanString( - start, - now - 1000 * 60 * 40, - DateUtils.HOUR_IN_MILLIS, - DateUtils.FORMAT_ABBREV_RELATIVE - ).toString() - } else { - DateUtils.getRelativeTimeSpanString( - start, - now, - DateUtils.HOUR_IN_MILLIS, - DateUtils.FORMAT_ABBREV_RELATIVE - ).toString() - } + return DateUtils.getRelativeTimeSpanString(start, now, DateUtils.HOUR_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString() } eventDate.dayOfYear == nowDate.plusDays(1).dayOfYear -> { return String.format("%s", context.getString(R.string.tomorrow)) @@ -143,9 +126,6 @@ object SettingsStringHelper { val nowDate = DateTime(now) val eventDate = DateTime(start) - var difference = start - now - difference += 60 * 1000 - (difference % (60 * 1000)) - return when (eventDate.dayOfYear) { nowDate.dayOfYear -> { "" diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/NewCalendarEventReceiver.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/NewCalendarEventReceiver.kt index 3ff5e57..b807020 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/NewCalendarEventReceiver.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/NewCalendarEventReceiver.kt @@ -13,8 +13,7 @@ class NewCalendarEventReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val eventRepository = EventRepository(context) when (intent.action) { - Intent.ACTION_PROVIDER_CHANGED, - Intent.ACTION_TIME_CHANGED -> { + Intent.ACTION_PROVIDER_CHANGED -> { CalendarHelper.updateEventList(context) } Actions.ACTION_GO_TO_NEXT_EVENT -> { 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 36330f4..b1dc382 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/UpdatesReceiver.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/UpdatesReceiver.kt @@ -38,6 +38,7 @@ class UpdatesReceiver : BroadcastReceiver() { "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)) { @@ -49,9 +50,6 @@ class UpdatesReceiver : BroadcastReceiver() { ActiveNotificationsHelper.clearLastNotification(context) MainWidget.updateWidget(context) } - Actions.ACTION_UPDATE_GREETINGS -> { - MainWidget.updateWidget(context) - } Actions.ACTION_REFRESH -> { GlobalScope.launch(Dispatchers.IO) { @@ -69,7 +67,25 @@ class UpdatesReceiver : BroadcastReceiver() { fun setUpdates(context: Context, eventId: Long? = null) { val eventRepository = EventRepository(context) if (eventId == null) { - removeUpdates(context) + with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) { + setExact( + AlarmManager.RTC, + 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) + }.timeInMillis, + PendingIntent.getBroadcast( + context, + 0, + Intent(context, UpdatesReceiver::class.java).apply { + action = Actions.ACTION_CALENDAR_UPDATE + }, + 0) + ) + } eventRepository.getFutureEvents().forEach { event -> setEventUpdate(context, event) @@ -84,109 +100,80 @@ class UpdatesReceiver : BroadcastReceiver() { } private fun setEventUpdate(context: Context, event: Event) { - with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) { - val now = Calendar.getInstance().apply { - set(Calendar.SECOND, 0) - set(Calendar.MILLISECOND, 0) - } - val diff = Period(now.timeInMillis, event.startDate) - val limit = when (Preferences.showUntil) { - 0 -> 1000 * 60 * 60 * 3 - 1 -> 1000 * 60 * 60 * 6 - 2 -> 1000 * 60 * 60 * 12 - 3 -> 1000 * 60 * 60 * 24 - 4 -> 1000 * 60 * 60 * 24 * 3 - 5 -> 1000 * 60 * 60 * 24 * 7 - 6 -> 1000 * 60 * 30 - 7 -> 1000 * 60 * 60 - else -> 1000 * 60 * 60 * 6 - } - if (event.startDate <= limit) { - if (event.startDate > now.timeInMillis) { - // Update the widget every hour till the event - if (diff.hours == 0) { - var minutes = 0 - when (Preferences.widgetUpdateFrequency) { - Constants.WidgetUpdateFrequency.DEFAULT.rawValue -> { - minutes = when { - diff.minutes > 50 -> 50 - diff.minutes > 30 -> 30 - diff.minutes > 15 -> 15 - else -> 0 - } - } - Constants.WidgetUpdateFrequency.HIGH.rawValue -> { - minutes = diff.minutes - (diff.minutes % 5) - } + val now = Calendar.getInstance().apply { + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + } + val diff = Period(now.timeInMillis, event.startDate, org.joda.time.PeriodType.time()) + val limit = when (Preferences.showUntil) { + 0 -> 1000 * 60 * 60 * 3 + 1 -> 1000 * 60 * 60 * 6 + 2 -> 1000 * 60 * 60 * 12 + 3 -> 1000 * 60 * 60 * 24 + 4 -> 1000 * 60 * 60 * 24 * 3 + 5 -> 1000 * 60 * 60 * 24 * 7 + 6 -> 1000 * 60 * 30 + 7 -> 1000 * 60 * 60 + else -> 1000 * 60 * 60 * 6 + } + val fireTime = when { + event.startDate <= now.timeInMillis + -> event.endDate + event.startDate > now.timeInMillis + limit + -> event.startDate - limit + event.allDay + -> event.startDate + diff.hours > 12 + -> event.startDate - 12 * 1000 * 60 * 60 + 1000 * 60 + diff.hours > 0 + -> event.startDate - diff.hours * 1000 * 60 * 60 + 1000 * 60 + else + -> event.startDate - 1000 * 60 * when (Preferences.widgetUpdateFrequency) { + Constants.WidgetUpdateFrequency.DEFAULT.rawValue -> { + when { + diff.minutes >= 45 -> 44 + diff.minutes >= 30 -> 29 + diff.minutes >= 15 -> 14 + else -> 0 } - setExact( - AlarmManager.RTC, - if (event.startDate - minutes * 1000 * 60 > (now.timeInMillis + 120 * 1000)) event.startDate - 60 * 1000 * minutes else now.timeInMillis + 120000, - PendingIntent.getBroadcast( - context, - event.eventID.toInt(), - Intent(context, UpdatesReceiver::class.java).apply { - action = Actions.ACTION_TIME_UPDATE - putExtra(EVENT_ID, event.eventID) - }, - PendingIntent.FLAG_UPDATE_CURRENT - ) - ) - } else { - setExact( - AlarmManager.RTC, - event.startDate - diff.hours * 1000 * 60 * 60 + if (diff.minutes > 30) (-30) else (+30), - PendingIntent.getBroadcast( - context, - event.eventID.toInt(), - Intent(context, UpdatesReceiver::class.java).apply { - action = Actions.ACTION_TIME_UPDATE - putExtra(EVENT_ID, event.eventID) - }, - PendingIntent.FLAG_UPDATE_CURRENT - ) - ) } - } else { - // Update the widget one second after the event is finished - val fireTime = - if (event.endDate > now.timeInMillis + 120 * 1000) event.endDate else now.timeInMillis + 120000 - setExact( - AlarmManager.RTC, - fireTime, - PendingIntent.getBroadcast( - context, - event.eventID.toInt(), - Intent(context, UpdatesReceiver::class.java).apply { - action = Actions.ACTION_TIME_UPDATE - }, - 0 - ) - ) + Constants.WidgetUpdateFrequency.HIGH.rawValue -> { + when { + diff.minutes >= 5 -> diff.minutes - diff.minutes % 5 - 1 + else -> 0 + } + } + else -> 0 } - } else { - setExact( - AlarmManager.RTC, - if (event.startDate - limit > now.timeInMillis + 120 * 1000) event.startDate - limit else now.timeInMillis + 120000, - PendingIntent.getBroadcast( - context, - event.eventID.toInt(), - Intent(context, UpdatesReceiver::class.java).apply { - action = Actions.ACTION_TIME_UPDATE + } + with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) { + setExact( + AlarmManager.RTC, + fireTime.coerceAtLeast(now.timeInMillis + 1000 * 60), + PendingIntent.getBroadcast( + context, + event.eventID.toInt(), + Intent(context, UpdatesReceiver::class.java).apply { + action = Actions.ACTION_TIME_UPDATE + if (event.startDate > now.timeInMillis) putExtra(EVENT_ID, event.eventID) - }, - PendingIntent.FLAG_UPDATE_CURRENT - ) + }, + PendingIntent.FLAG_UPDATE_CURRENT ) - } + ) } } fun removeUpdates(context: Context) { with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) { + cancel(PendingIntent.getBroadcast(context, 0, Intent(context, UpdatesReceiver::class.java).apply { + action = Actions.ACTION_CALENDAR_UPDATE + }, 0)) val eventRepository = EventRepository(context) eventRepository.getFutureEvents().forEach { - cancel(PendingIntent.getBroadcast(context, it.eventID.toInt(), Intent(context, UpdatesReceiver::class.java), 0)) + cancel(PendingIntent.getBroadcast(context, it.eventID.toInt(), Intent(context, UpdatesReceiver::class.java).apply { + action = Actions.ACTION_TIME_UPDATE + }, 0)) } eventRepository.close() } 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 4b6f6eb..92f068b 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/WeatherReceiver.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/receivers/WeatherReceiver.kt @@ -35,8 +35,6 @@ class WeatherReceiver : BroadcastReceiver() { companion object { private const val MINUTE = 60 * 1000L fun setUpdates(context: Context) { - removeUpdates(context) - if (Preferences.showWeather) { val interval = MINUTE * when (Preferences.weatherRefreshPeriod) { 0 -> 30 diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/services/UpdateCalendarService.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/services/UpdateCalendarService.kt index c584cb6..b69f0b9 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/services/UpdateCalendarService.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/services/UpdateCalendarService.kt @@ -55,18 +55,20 @@ class UpdateCalendarService : Service() { job?.cancel() job = GlobalScope.launch(Dispatchers.IO) { + UpdatesReceiver.removeUpdates(this@UpdateCalendarService) + val eventRepository = EventRepository(this@UpdateCalendarService) if (Preferences.showEvents) { val eventList = ArrayList() + // fetch all events from now to next ACTION_CALENDAR_UPDATE + limit val now = Calendar.getInstance() - val begin = Calendar.getInstance().apply { + val limit = Calendar.getInstance().apply { set(Calendar.MILLISECOND, 0) set(Calendar.SECOND, 0) set(Calendar.MINUTE, 0) set(Calendar.HOUR_OF_DAY, 0) - } - val limit = Calendar.getInstance().apply { + add(Calendar.DATE, 1) when (Preferences.showUntil) { 0 -> add(Calendar.HOUR, 3) 1 -> add(Calendar.HOUR, 6) @@ -85,11 +87,13 @@ class UpdateCalendarService : Service() { ) ) { eventRepository.resetNextEventData() + eventRepository.clearEvents() } else { try { val provider = CalendarProvider(this@UpdateCalendarService) + // apply time zone offset to correctly fetch all-day events val data = provider.getInstances( - begin.timeInMillis + begin.timeZone.getOffset(begin.timeInMillis).coerceAtMost(0), + now.timeInMillis + now.timeZone.getOffset(now.timeInMillis).coerceAtMost(0), limit.timeInMillis + limit.timeZone.getOffset(limit.timeInMillis).coerceAtLeast(0) ) if (data != null) { @@ -170,13 +174,14 @@ class UpdateCalendarService : Service() { } } else { eventRepository.resetNextEventData() + eventRepository.clearEvents() } + eventRepository.close() UpdatesReceiver.setUpdates(this@UpdateCalendarService) MainWidget.updateWidget(this@UpdateCalendarService) EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) - eventRepository.close() stopSelf() } 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 3c6c9d2..74a4c97 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.receivers.UpdatesReceiver import com.tommasoberlose.anotherwidget.receivers.WeatherReceiver import com.tommasoberlose.anotherwidget.ui.activities.MainActivity import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel @@ -123,6 +124,7 @@ class PreferencesFragment : Fragment() { requireCalendarPermission() } else { Preferences.showEvents = enabled + UpdatesReceiver.removeUpdates(requireContext()) } }