From 5facb079ea7a870b695f3aa58e17359a53abde93 Mon Sep 17 00:00:00 2001 From: Yuri Volkov Date: Wed, 8 Jan 2025 15:37:44 +0300 Subject: [PATCH] #140 Start hour of a day option --- .../org/andstatus/todoagenda/BirthdayTest.kt | 41 +- .../todoagenda/DuplicateEventsTest.kt | 4 +- .../todoagenda/MultidayAllDayEventTest.kt | 5 +- .../todoagenda/RecurringEventsTest.kt | 7 +- .../todoagenda/StartHourOfDayTest.kt | 56 + .../provider/FakeCalendarContentProvider.kt | 8 +- .../res/raw/start_hour_of_day.json | 1636 +++++++++++++++++ .../todoagenda/RemoteViewsFactory.kt | 32 +- .../todoagenda/calendar/CalendarEvent.kt | 2 +- .../calendar/CalendarEventProvider.kt | 2 +- .../calendar/CalendarEventVisualizer.kt | 57 +- .../andstatus/todoagenda/prefs/AllSettings.kt | 2 +- .../prefs/ApplicationPreferences.kt | 1 + .../todoagenda/prefs/InstanceSettings.kt | 15 +- .../prefs/OtherPreferencesFragment.kt | 19 +- .../provider/QueryResultsStorage.kt | 37 +- .../org/andstatus/todoagenda/util/DateUtil.kt | 3 - .../org/andstatus/todoagenda/util/MyClock.kt | 82 +- .../todoagenda/widget/CalendarEntry.kt | 2 +- .../andstatus/todoagenda/widget/LastEntry.kt | 4 +- .../todoagenda/widget/WidgetEntry.kt | 45 +- app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/preferences_other.xml | 20 +- 24 files changed, 1937 insertions(+), 145 deletions(-) create mode 100644 app/src/androidTest/kotlin/org/andstatus/todoagenda/StartHourOfDayTest.kt create mode 100644 app/src/androidTest/res/raw/start_hour_of_day.json diff --git a/app/src/androidTest/kotlin/org/andstatus/todoagenda/BirthdayTest.kt b/app/src/androidTest/kotlin/org/andstatus/todoagenda/BirthdayTest.kt index f5bf62cd..c5a138a1 100644 --- a/app/src/androidTest/kotlin/org/andstatus/todoagenda/BirthdayTest.kt +++ b/app/src/androidTest/kotlin/org/andstatus/todoagenda/BirthdayTest.kt @@ -1,7 +1,6 @@ package org.andstatus.todoagenda import org.andstatus.todoagenda.prefs.AllDayEventsPlacement -import org.andstatus.todoagenda.prefs.ApplicationPreferences import org.andstatus.todoagenda.prefs.EndedSomeTimeAgo import org.andstatus.todoagenda.prefs.dateformat.DateFormatType import org.andstatus.todoagenda.prefs.dateformat.DateFormatValue @@ -21,16 +20,13 @@ class BirthdayTest : BaseWidgetTest() { val inputs = provider.loadResultsAndSettings( org.andstatus.todoagenda.test.R.raw.birthday ) - provider.startEditingPreferences() - ApplicationPreferences.setWidgetHeaderDateFormat( - provider.context, - DateFormatValue.of(DateFormatType.CUSTOM, "YYYY-MM-dd") + provider.settings = settings.copy( + widgetHeaderDateFormat = DateFormatValue.of(DateFormatType.CUSTOM, "YYYY-MM-dd"), + allDayEventsPlacement = AllDayEventsPlacement.TOP_DAY, + eventsEnded = EndedSomeTimeAgo.NONE, + showPastEventsWithDefaultColor = false, + eventRange = 30 ) - ApplicationPreferences.setAllDayEventsPlacement(provider.context, AllDayEventsPlacement.TOP_DAY) - ApplicationPreferences.setEventsEnded(provider.context, EndedSomeTimeAgo.NONE) - ApplicationPreferences.setShowPastEventsWithDefaultColor(provider.context, false) - ApplicationPreferences.setEventRange(provider.context, 30) - provider.savePreferences() playAtOneTime(inputs, dateTime(2015, 8, 1, 17, 0), 0) playAtOneTime(inputs, dateTime(2015, 8, 9, 23, 59), 0) playAtOneTime(inputs, dateTime(2015, 8, 10, 0, 0).plusMillis(1), 2) @@ -43,28 +39,33 @@ class BirthdayTest : BaseWidgetTest() { playAtOneTime(inputs, dateTime(2015, 9, 9, 23, 30), 2) playAtOneTime(inputs, dateTime(2015, 9, 10, 0, 30), 0) playAtOneTime(inputs, dateTime(2015, 9, 10, 11, 0), 0) - ApplicationPreferences.setEventsEnded(provider.context, EndedSomeTimeAgo.ONE_HOUR) - provider.savePreferences() + provider.settings = settings.copy( + eventsEnded = EndedSomeTimeAgo.ONE_HOUR + ) playAtOneTime(inputs, dateTime(2015, 9, 10, 0, 30), 2) playAtOneTime(inputs, dateTime(2015, 9, 10, 1, 30), 0) - ApplicationPreferences.setEventsEnded(provider.context, EndedSomeTimeAgo.TODAY) - provider.savePreferences() + provider.settings = settings.copy( + eventsEnded = EndedSomeTimeAgo.TODAY + ) playAtOneTime(inputs, dateTime(2015, 9, 10, 1, 30), 0) - ApplicationPreferences.setEventsEnded(provider.context, EndedSomeTimeAgo.FOUR_HOURS) - provider.savePreferences() + provider.settings = settings.copy( + eventsEnded = EndedSomeTimeAgo.FOUR_HOURS + ) playAtOneTime(inputs, dateTime(2015, 9, 10, 1, 30), 2) playAtOneTime(inputs, dateTime(2015, 9, 10, 3, 59), 2) playAtOneTime(inputs, dateTime(2015, 9, 10, 4, 0), 0) - ApplicationPreferences.setEventsEnded(provider.context, EndedSomeTimeAgo.YESTERDAY) - provider.savePreferences() + provider.settings = settings.copy( + eventsEnded = EndedSomeTimeAgo.YESTERDAY + ) playAtOneTime(inputs, dateTime(2015, 9, 10, 4, 0), 2) playAtOneTime(inputs, dateTime(2015, 9, 10, 11, 0), 2) playAtOneTime(inputs, dateTime(2015, 9, 10, 17, 0), 2) playAtOneTime(inputs, dateTime(2015, 9, 10, 23, 30), 2) playAtOneTime(inputs, dateTime(2015, 9, 11, 0, 0), 0) playAtOneTime(inputs, dateTime(2015, 9, 11, 0, 30), 0) - ApplicationPreferences.setShowPastEventsWithDefaultColor(provider.context, true) - provider.savePreferences() + provider.settings = settings.copy( + showPastEventsWithDefaultColor = true + ) playAtOneTime(inputs, dateTime(2015, 9, 11, 0, 30), 0) } diff --git a/app/src/androidTest/kotlin/org/andstatus/todoagenda/DuplicateEventsTest.kt b/app/src/androidTest/kotlin/org/andstatus/todoagenda/DuplicateEventsTest.kt index aa940189..bad98ba3 100644 --- a/app/src/androidTest/kotlin/org/andstatus/todoagenda/DuplicateEventsTest.kt +++ b/app/src/androidTest/kotlin/org/andstatus/todoagenda/DuplicateEventsTest.kt @@ -13,10 +13,10 @@ class DuplicateEventsTest : BaseWidgetTest() { @Test fun testIssue354() { val method = "testIssue354" - val inputs = provider!!.loadResultsAndSettings( + val inputs = provider.loadResultsAndSettings( org.andstatus.todoagenda.test.R.raw.duplicates ) - provider!!.addResults(inputs) + provider.addResults(inputs) playResults(method) Assert.assertEquals("Number of entries", 40, factory.widgetEntries.size.toLong()) } diff --git a/app/src/androidTest/kotlin/org/andstatus/todoagenda/MultidayAllDayEventTest.kt b/app/src/androidTest/kotlin/org/andstatus/todoagenda/MultidayAllDayEventTest.kt index bed94210..78cf8835 100644 --- a/app/src/androidTest/kotlin/org/andstatus/todoagenda/MultidayAllDayEventTest.kt +++ b/app/src/androidTest/kotlin/org/andstatus/todoagenda/MultidayAllDayEventTest.kt @@ -1,6 +1,5 @@ package org.andstatus.todoagenda -import org.andstatus.todoagenda.prefs.ApplicationPreferences import org.andstatus.todoagenda.widget.DayHeader import org.andstatus.todoagenda.widget.LastEntry import org.joda.time.DateTime @@ -21,9 +20,7 @@ class MultidayAllDayEventTest : BaseWidgetTest() { inputs.executedAt.set(now) provider.addResults(inputs) val dateRange = 30 - provider.startEditingPreferences() - ApplicationPreferences.setEventRange(provider.context, dateRange) - provider.savePreferences() + provider.settings = settings.copy(eventRange = dateRange) playResults(method) val today = now.withTimeAtStartOfDay() val endOfRangeTime = today.plusDays(dateRange) diff --git a/app/src/androidTest/kotlin/org/andstatus/todoagenda/RecurringEventsTest.kt b/app/src/androidTest/kotlin/org/andstatus/todoagenda/RecurringEventsTest.kt index e497273b..3b21d273 100644 --- a/app/src/androidTest/kotlin/org/andstatus/todoagenda/RecurringEventsTest.kt +++ b/app/src/androidTest/kotlin/org/andstatus/todoagenda/RecurringEventsTest.kt @@ -1,6 +1,5 @@ package org.andstatus.todoagenda -import org.andstatus.todoagenda.prefs.ApplicationPreferences import org.andstatus.todoagenda.provider.QueryRow import org.andstatus.todoagenda.widget.CalendarEntry import org.junit.Assert @@ -21,9 +20,9 @@ class RecurringEventsTest : BaseWidgetTest() { fun testShowRecurringEvents() { generateEventInstances() Assert.assertEquals("Entries: " + factory.widgetEntries.size, 15, countCalendarEntries().toLong()) - provider.startEditingPreferences() - ApplicationPreferences.setShowOnlyClosestInstanceOfRecurringEvent(provider.context, true) - provider.savePreferences() + provider.settings = settings.copy( + showOnlyClosestInstanceOfRecurringEvent = true + ) generateEventInstances() Assert.assertEquals("Entries: " + factory.widgetEntries.size, 1, countCalendarEntries().toLong()) } diff --git a/app/src/androidTest/kotlin/org/andstatus/todoagenda/StartHourOfDayTest.kt b/app/src/androidTest/kotlin/org/andstatus/todoagenda/StartHourOfDayTest.kt new file mode 100644 index 00000000..690230f3 --- /dev/null +++ b/app/src/androidTest/kotlin/org/andstatus/todoagenda/StartHourOfDayTest.kt @@ -0,0 +1,56 @@ +package org.andstatus.todoagenda + +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertTrue +import org.andstatus.todoagenda.provider.QueryResultsStorage +import org.andstatus.todoagenda.widget.CalendarEntry +import org.joda.time.DateTime +import org.joda.time.DateTimeZone +import org.junit.Test + +/** + * @author yvolk@yurivolkov.com + */ +class StartHourOfDayTest : BaseWidgetTest() { + @Test + fun startHourOfDayTest() { + val method = "startHourOfDayTest" + provider.loadResultsAndSettings(org.andstatus.todoagenda.test.R.raw.start_hour_of_day) + + assertSplitEvent(method, DateTime(2024, 12, 22, 23, 30, DateTimeZone.UTC)) + + provider.settings = settings.copy(startHourOfDayIn = 4) + assertSplitEvent(method, DateTime(2024, 12, 23, 3, 30, DateTimeZone.UTC)) + } + + private fun assertSplitEvent( + methodIn: String, + entryStartDate: DateTime + ) { + val method = "$methodIn $entryStartDate" + val inputs: QueryResultsStorage = settings.resultsStorage!! + val thisDay: DateTime = inputs.executedAt.get().withTimeAtStartOfDay() + + provider.addResults(inputs) + playResults(method) + val indLastDec22 = factory.widgetEntries.indexOfLast { + it.entryDay == thisDay && it is CalendarEntry + } + assertTrue( + "Should exist Dec 22 entry ${factory.widgetEntries}", + indLastDec22 > 0 + ) + val lastDec22Entry: CalendarEntry = factory.widgetEntries[indLastDec22] as CalendarEntry + assertEquals( + "Last day entry start date $lastDec22Entry", entryStartDate, lastDec22Entry.event.startDate + ) + assertTrue( + "Entry should continue on the next day ${lastDec22Entry.eventTimeString}, $lastDec22Entry", + lastDec22Entry.eventTimeString.endsWith(" →") + ) + val firstDec23Entry: CalendarEntry = factory.widgetEntries[indLastDec22 + 2] as CalendarEntry + assertEquals( + "Next day entry start date $firstDec23Entry", entryStartDate, firstDec23Entry.event.startDate + ) + } +} diff --git a/app/src/androidTest/kotlin/org/andstatus/todoagenda/provider/FakeCalendarContentProvider.kt b/app/src/androidTest/kotlin/org/andstatus/todoagenda/provider/FakeCalendarContentProvider.kt index 1782a211..affd6de6 100644 --- a/app/src/androidTest/kotlin/org/andstatus/todoagenda/provider/FakeCalendarContentProvider.kt +++ b/app/src/androidTest/kotlin/org/andstatus/todoagenda/provider/FakeCalendarContentProvider.kt @@ -32,7 +32,7 @@ class FakeCalendarContentProvider private constructor(val context: Context) { ?: throw java.lang.IllegalStateException("No settings stored") set(value) { settingsRef.set(value) - AllSettings.addNew(TAG, context, value) + AllSettings.addOrReplace(TAG, context, value) } init { @@ -51,10 +51,11 @@ class FakeCalendarContentProvider private constructor(val context: Context) { fun updateAppSettings(tag: String) { settings = settings.copy( + logEvents = true, resultsStorage = results, snapshotModeIn = SnapshotMode.SNAPSHOT_TIME ) - AllSettings.addNew(tag, context, settings) + AllSettings.addOrReplace(tag, context, settings) if (results.results.isNotEmpty()) { Log.d(tag, "Results executed at " + settings.clock.now()) } @@ -136,7 +137,8 @@ class FakeCalendarContentProvider private constructor(val context: Context) { val widgetData = WidgetData.fromJson(json) settings = widgetData.getSettingsForWidget( context, - settings, widgetId + settings, + widgetId ) return settings.resultsStorage ?: throw IllegalStateException("No results storage") } catch (e: Exception) { diff --git a/app/src/androidTest/res/raw/start_hour_of_day.json b/app/src/androidTest/res/raw/start_hour_of_day.json new file mode 100644 index 00000000..e26aedc7 --- /dev/null +++ b/app/src/androidTest/res/raw/start_hour_of_day.json @@ -0,0 +1,1636 @@ +{ + "deviceInfo": { + "versionCode": 35, + "versionRelease": "15", + "versionCodename": "REL", + "buildManufacturer": "Google", + "buildBrand": "google", + "buildModel": "sdk_gphone64_x86_64" + }, + "applicationInfo": { + "versionName": "4.10.2", + "versionCode": 708 + }, + "settings": { + "widgetId": 2, + "compactLayout": false, + "widgetHeaderLayout": "ONE_ROW", + "widgetHeaderDateFormat": "defaultWeekday:", + "showDayHeaders": true, + "dayHeaderDateFormat": "defaultWeekday:", + "showPastEventsUnderOneHeader": false, + "dayHeaderAlignment": "RIGHT", + "horizontalLineBelowDayHeader": false, + "showDaysWithoutEvents": false, + "eventEntryLayout": "DEFAULT", + "showEventIcon": true, + "entryDateFormat": "hidden:", + "multiline_title": false, + "maxLinesTitle": 5, + "multiline_details": false, + "maxLinesDetails": 5, + "widgetHeaderBackgroundColor": 0, + "pastEventsBackgroundColor": -1082623956, + "todaysEventsBackgroundColor": -620756993, + "backgroundColor": -2147483648, + "textColorSource": "shading", + "headerTheme": "DARK", + "widgetHeaderTextColor": -1694498817, + "dayHeaderThemePast": "DARK", + "dayHeaderTextColorPast": -3355444, + "entryThemePast": "BLACK", + "eventTextColorPast": -1, + "dayHeaderTheme": "LIGHT", + "dayHeaderTextColorToday": -8947849, + "entryTheme": "WHITE", + "eventTextColorToday": -16777216, + "dayHeaderThemeFuture": "DARK", + "dayHeaderTextColorFuture": -3355444, + "entryThemeFuture": "BLACK", + "eventTextColorFuture": -1, + "textShadow": "no", + "showEndTime": true, + "showLocation": true, + "showDescription": false, + "fillAllDay": true, + "indicateAlerts": true, + "indicateRecurring": false, + "eventsEnded": "ONE_WEEK", + "showPastEventsWithDefaultColor": false, + "eventRange": 7, + "hideBasedOnKeywords": "", + "showBasedOnKeywords": "", + "showOnlyClosestInstanceOfRecurringEvent": false, + "hideDuplicates": false, + "allDayEventsPlacement": "top_day", + "taskScheduling": "date_due", + "taskWithoutDates": "end_of_list", + "filterMode": "normal", + "activeSources": [ + { + "providerType": 1, + "id": 2, + "title": "andstatus@gmail.com", + "summary": "andstatus@gmail.com", + "color": -6299161, + "isAvailable": true + }, + { + "providerType": 1, + "id": 3, + "title": "ToDo Agenda Shared", + "summary": "calendar.widget.android@gmail.com", + "color": -6644481, + "isAvailable": true + } + ], + "widgetInstanceName": "Birthday test - Test replay", + "textSizeScale": "1.0", + "dateFormat": "auto", + "lockedTimeZoneId": "", + "startHourOfDay": 0, + "snapshotMode": "live_data", + "refreshPeriodMinutes": 10 + }, + "resultsVersion": 3, + "results": [ + { + "providerType": 1, + "executedAt": 1734877251426, + "timeZoneId": "UTC", + "millisOffsetUtcToLocal": 0, + "standardMillisOffsetUtcToLocal": 0, + "uri": "content:\/\/com.android.calendar\/instances\/when\/1734048000000\/1735482051416", + "projection": [ + "calendar_id", + "event_id", + "eventStatus", + "title", + "begin", + "end", + "allDay", + "eventLocation", + "description", + "hasAlarm", + "rrule", + "displayColor", + "calendar_color" + ], + "selection": "selfAttendeeStatus!=2 AND (calendar_id = 2 OR calendar_id = 3 )", + "selectionArgs": [], + "sortOrder": "startDay ASC, allDay DESC, begin ASC ", + "rows": [ + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "All day weekly event1" + }, + "hasAlarm": { + "type": 1, + "value": 0 + }, + "allDay": { + "type": 1, + "value": 1 + }, + "rrule": { + "type": 3, + "value": "FREQ=WEEKLY;WKST=TU" + }, + "event_id": { + "type": 1, + "value": 14 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734220800000 + }, + "displayColor": { + "type": 1, + "value": -8722497 + }, + "begin": { + "type": 1, + "value": 1734134400000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 2 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "" + }, + "hasAlarm": { + "type": 1, + "value": 0 + }, + "allDay": { + "type": 1, + "value": 1 + }, + "event_id": { + "type": 1, + "value": 9 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734307200000 + }, + "displayColor": { + "type": 1, + "value": -6299161 + }, + "begin": { + "type": 1, + "value": 1734220800000 + }, + "calendar_color": { + "type": 1, + "value": -6299161 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "Secret cafe, Rhodyho, 4220 Bardejov, Slovakia" + }, + "description": { + "type": 3, + "value": "Description of the event" + }, + "title": { + "type": 3, + "value": "Test event 2 that is a recurring all day event and has a reminder" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 1 + }, + "rrule": { + "type": 3, + "value": "FREQ=MONTHLY;WKST=SU" + }, + "event_id": { + "type": 1, + "value": 18 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734566400000 + }, + "displayColor": { + "type": 1, + "value": -8722497 + }, + "begin": { + "type": 1, + "value": 1734480000000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "One AM whole week" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "rrule": { + "type": 3, + "value": "FREQ=DAILY;WKST=MO" + }, + "event_id": { + "type": 1, + "value": 38 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734573600000 + }, + "displayColor": { + "type": 1, + "value": -272549 + }, + "begin": { + "type": 1, + "value": 1734570000000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "One AM whole week" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "rrule": { + "type": 3, + "value": "FREQ=DAILY;WKST=MO" + }, + "event_id": { + "type": 1, + "value": 38 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734660000000 + }, + "displayColor": { + "type": 1, + "value": -272549 + }, + "begin": { + "type": 1, + "value": 1734656400000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "All day weekly event1" + }, + "hasAlarm": { + "type": 1, + "value": 0 + }, + "allDay": { + "type": 1, + "value": 1 + }, + "rrule": { + "type": 3, + "value": "FREQ=WEEKLY;WKST=TU" + }, + "event_id": { + "type": 1, + "value": 14 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734825600000 + }, + "displayColor": { + "type": 1, + "value": -8722497 + }, + "begin": { + "type": 1, + "value": 1734739200000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "One AM whole week" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "rrule": { + "type": 3, + "value": "FREQ=DAILY;WKST=MO" + }, + "event_id": { + "type": 1, + "value": 38 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734746400000 + }, + "displayColor": { + "type": 1, + "value": -272549 + }, + "begin": { + "type": 1, + "value": 1734742800000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "Nine PM yesterday" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "event_id": { + "type": 1, + "value": 28 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734818400000 + }, + "displayColor": { + "type": 1, + "value": -12134693 + }, + "begin": { + "type": 1, + "value": 1734814800000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "Nine thirty PM Yesterday" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "event_id": { + "type": 1, + "value": 35 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734820200000 + }, + "displayColor": { + "type": 1, + "value": -2350809 + }, + "begin": { + "type": 1, + "value": 1734816600000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "Ten PM yesterday" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "event_id": { + "type": 1, + "value": 25 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734822300000 + }, + "displayColor": { + "type": 1, + "value": -8722497 + }, + "begin": { + "type": 1, + "value": 1734818700000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "Last Midnight" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "event_id": { + "type": 1, + "value": 26 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734826800000 + }, + "displayColor": { + "type": 1, + "value": -6644481 + }, + "begin": { + "type": 1, + "value": 1734825600000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "One AM whole week" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "rrule": { + "type": 3, + "value": "FREQ=DAILY;WKST=MO" + }, + "event_id": { + "type": 1, + "value": 38 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734832800000 + }, + "displayColor": { + "type": 1, + "value": -272549 + }, + "begin": { + "type": 1, + "value": 1734829200000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "Four AM today" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "event_id": { + "type": 1, + "value": 27 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734843600000 + }, + "displayColor": { + "type": 1, + "value": -30596 + }, + "begin": { + "type": 1, + "value": 1734840000000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "Ten PM today" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "event_id": { + "type": 1, + "value": 29 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734908400000 + }, + "displayColor": { + "type": 1, + "value": -6644481 + }, + "begin": { + "type": 1, + "value": 1734904800000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "Eleven PM Today" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "event_id": { + "type": 1, + "value": 30 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734912000000 + }, + "displayColor": { + "type": 1, + "value": -272549 + }, + "begin": { + "type": 1, + "value": 1734908400000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "Eleven thirty PM Today" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "event_id": { + "type": 1, + "value": 34 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734913800000 + }, + "displayColor": { + "type": 1, + "value": -11421879 + }, + "begin": { + "type": 1, + "value": 1734910200000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "Next Midnight" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "event_id": { + "type": 1, + "value": 31 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734915600000 + }, + "displayColor": { + "type": 1, + "value": -6644481 + }, + "begin": { + "type": 1, + "value": 1734912000000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "One AM whole week" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "rrule": { + "type": 3, + "value": "FREQ=DAILY;WKST=MO" + }, + "event_id": { + "type": 1, + "value": 38 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734919200000 + }, + "displayColor": { + "type": 1, + "value": -272549 + }, + "begin": { + "type": 1, + "value": 1734915600000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "Three thirty Tomorrow" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "event_id": { + "type": 1, + "value": 33 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734928200000 + }, + "displayColor": { + "type": 1, + "value": -2380289 + }, + "begin": { + "type": 1, + "value": 1734924600000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "Noon Tomorrow" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "event_id": { + "type": 1, + "value": 32 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734958800000 + }, + "displayColor": { + "type": 1, + "value": -18312 + }, + "begin": { + "type": 1, + "value": 1734955200000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "Six PM Tomorrow" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "event_id": { + "type": 1, + "value": 36 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1734980400000 + }, + "displayColor": { + "type": 1, + "value": -6644481 + }, + "begin": { + "type": 1, + "value": 1734976800000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "One AM whole week" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "rrule": { + "type": 3, + "value": "FREQ=DAILY;WKST=MO" + }, + "event_id": { + "type": 1, + "value": 38 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1735005600000 + }, + "displayColor": { + "type": 1, + "value": -272549 + }, + "begin": { + "type": 1, + "value": 1735002000000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "Two AM day after Tomorrow" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "event_id": { + "type": 1, + "value": 37 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1735009200000 + }, + "displayColor": { + "type": 1, + "value": -6644481 + }, + "begin": { + "type": 1, + "value": 1735005600000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "One AM whole week" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "rrule": { + "type": 3, + "value": "FREQ=DAILY;WKST=MO" + }, + "event_id": { + "type": 1, + "value": 38 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1735092000000 + }, + "displayColor": { + "type": 1, + "value": -272549 + }, + "begin": { + "type": 1, + "value": 1735088400000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "One AM whole week" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "rrule": { + "type": 3, + "value": "FREQ=DAILY;WKST=MO" + }, + "event_id": { + "type": 1, + "value": 38 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1735178400000 + }, + "displayColor": { + "type": 1, + "value": -272549 + }, + "begin": { + "type": 1, + "value": 1735174800000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "One AM whole week" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "rrule": { + "type": 3, + "value": "FREQ=DAILY;WKST=MO" + }, + "event_id": { + "type": 1, + "value": 38 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1735264800000 + }, + "displayColor": { + "type": 1, + "value": -272549 + }, + "begin": { + "type": 1, + "value": 1735261200000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "All day weekly event1" + }, + "hasAlarm": { + "type": 1, + "value": 0 + }, + "allDay": { + "type": 1, + "value": 1 + }, + "rrule": { + "type": 3, + "value": "FREQ=WEEKLY;WKST=TU" + }, + "event_id": { + "type": 1, + "value": 14 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1735430400000 + }, + "displayColor": { + "type": 1, + "value": -8722497 + }, + "begin": { + "type": 1, + "value": 1735344000000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "One AM whole week" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "rrule": { + "type": 3, + "value": "FREQ=DAILY;WKST=MO" + }, + "event_id": { + "type": 1, + "value": 38 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1735351200000 + }, + "displayColor": { + "type": 1, + "value": -272549 + }, + "begin": { + "type": 1, + "value": 1735347600000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + }, + { + "calendar_id": { + "type": 1, + "value": 3 + }, + "eventLocation": { + "type": 3, + "value": "" + }, + "description": { + "type": 3, + "value": "" + }, + "title": { + "type": 3, + "value": "One AM whole week" + }, + "hasAlarm": { + "type": 1, + "value": 1 + }, + "allDay": { + "type": 1, + "value": 0 + }, + "rrule": { + "type": 3, + "value": "FREQ=DAILY;WKST=MO" + }, + "event_id": { + "type": 1, + "value": 38 + }, + "eventStatus": { + "type": 1, + "value": 1 + }, + "end": { + "type": 1, + "value": 1735437600000 + }, + "displayColor": { + "type": 1, + "value": -272549 + }, + "begin": { + "type": 1, + "value": 1735434000000 + }, + "calendar_color": { + "type": 1, + "value": -6644481 + } + } + ] + } + ] +} diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/RemoteViewsFactory.kt b/app/src/main/kotlin/org/andstatus/todoagenda/RemoteViewsFactory.kt index 674c7fe8..cf39baad 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/RemoteViewsFactory.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/RemoteViewsFactory.kt @@ -33,7 +33,6 @@ import org.andstatus.todoagenda.widget.WidgetLayout import org.joda.time.DateTime import java.util.Locale import java.util.concurrent.ConcurrentHashMap -import java.util.stream.Collectors import kotlin.concurrent.Volatile class RemoteViewsFactory(val context: Context, private val widgetId: Int, createdByLauncher: Boolean) : @@ -41,14 +40,14 @@ class RemoteViewsFactory(val context: Context, private val widgetId: Int, create val instanceId = InstanceId.next() @Volatile - var widgetEntries: MutableList> = ArrayList() + var widgetEntries: List> = ArrayList() @Volatile private var visualizers: MutableList>> = ArrayList() init { visualizers.add(LastEntryVisualizer(context, widgetId)) - widgetEntries.add(LastEntry(settings, LastEntryType.NOT_LOADED, settings.clock.now())) + widgetEntries = listOf(LastEntry(settings, LastEntryType.NOT_LOADED, settings.clock.now())) logEvent("Init" + if (createdByLauncher) " by Launcher" else "") } @@ -143,19 +142,31 @@ class RemoteViewsFactory(val context: Context, private val widgetId: Int, create return if (widgetEntries.isNotEmpty()) 0 else -1 } - private fun queryWidgetEntries(settings: InstanceSettings): MutableList> { + private fun queryWidgetEntries(settings: InstanceSettings): List> { val eventEntries: MutableList> = ArrayList() for (visualizer in visualizers) { - eventEntries.addAll(visualizer.queryEventEntries()) + visualizer.queryEventEntries().let { + eventEntries.addAll(it) + if (settings.logEvents) { + Log.i("visualizer_query", "${visualizer}") + it.forEachIndexed { index, entry -> + Log.i("visualizer_query", "${index+1}. $entry") + } + } + } + } + if (settings.logEvents) { + eventEntries.forEachIndexed { index, entry -> + Log.i("queryWidgetEntries", "${index+1}. $entry") + } } eventEntries.sort() - val noHidden: MutableList> = - eventEntries.stream().filter { obj: WidgetEntry<*>? -> obj!!.notHidden() }.collect(Collectors.toList()) + val noHidden: List> = eventEntries.filter { it.notHidden() } val deduplicated = if (settings.hideDuplicates) filterOutDuplicates(noHidden) else noHidden - val widgetEntries: MutableList> = + val widgetEntries: List> = if (settings.showDayHeaders) addDayHeaders(deduplicated) else deduplicated - LastEntry.addLast(settings, widgetEntries) - return widgetEntries + val withLast = LastEntry.addLast(settings, widgetEntries) + return withLast } private fun filterOutDuplicates(inputEntries: List>): MutableList> { @@ -179,7 +190,6 @@ class RemoteViewsFactory(val context: Context, private val widgetId: Int, create private fun addDayHeaders(listIn: List>): MutableList> { val listOut: MutableList> = ArrayList() if (listIn.isNotEmpty()) { - val settings = settings var curDayBucket = DayHeader(settings, WidgetEntryPosition.DAY_HEADER, MyClock.DATETIME_MIN) var pastEventsHeaderAdded = false var endOfListHeaderAdded = false diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/calendar/CalendarEvent.kt b/app/src/main/kotlin/org/andstatus/todoagenda/calendar/CalendarEvent.kt index caab1cd1..52be276f 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/calendar/CalendarEvent.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/calendar/CalendarEvent.kt @@ -178,7 +178,7 @@ class CalendarEvent( return startDate.isBefore(now) && endDate.isAfter(now) } val isPartOfMultiDayEvent: Boolean - get() = endDate.withTimeAtStartOfDay().isAfter(startDate.withTimeAtStartOfDay()) + get() = settings.clock.dayOf(endDate).isAfter(settings.clock.dayOf(startDate)) companion object { @Volatile diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/calendar/CalendarEventProvider.kt b/app/src/main/kotlin/org/andstatus/todoagenda/calendar/CalendarEventProvider.kt index a64d1ef1..de70c402 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/calendar/CalendarEventProvider.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/calendar/CalendarEventProvider.kt @@ -116,7 +116,7 @@ class CalendarEventProvider(type: EventProviderType, context: Context, widgetId: ", got " + eventList.size + " events" ) val factory: RemoteViewsFactory? = RemoteViewsFactory.factories.get(widgetId) - if (factory != null) { + if (factory != null && settings.logEvents) { factory.logWidgetEntries(tag) } } diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/calendar/CalendarEventVisualizer.kt b/app/src/main/kotlin/org/andstatus/todoagenda/calendar/CalendarEventVisualizer.kt index ab26c8d7..27a838cd 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/calendar/CalendarEventVisualizer.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/calendar/CalendarEventVisualizer.kt @@ -1,6 +1,7 @@ package org.andstatus.todoagenda.calendar import android.content.Intent +import android.util.Log import android.view.View import android.widget.RemoteViews import org.andstatus.todoagenda.R @@ -95,47 +96,67 @@ class CalendarEventVisualizer(eventProvider: EventProvider) : WidgetEntryVisuali } private fun toCalendarEntryList(eventList: List?): List { - val fillAllDayEvents = settings.fillAllDayEvents val entryList: MutableList = ArrayList() for (event in eventList!!) { - val dayOneEntry = getDayOneEntry(event) + val dayOneEntry = getFirstDayEntry(event) entryList.add(dayOneEntry) - if (fillAllDayEvents) { - addEntriesToFillAllDayEvents(entryList, dayOneEntry) + if (settings.logEvents) { + Log.i("toCalendarEntryList", "event: $event\nentry: $dayOneEntry") + } + if (settings.fillAllDayEvents) { + addEntriesToFillAllEventDays(entryList, dayOneEntry) + } + } + if (settings.logEvents) { + entryList.forEachIndexed { index, entry -> + Log.i("toCalendarEntryLAll", "${index + 1}. $entry") } } return entryList } - private fun getDayOneEntry(event: CalendarEvent): CalendarEntry { + private fun getFirstDayEntry(event: CalendarEvent): CalendarEntry { var firstDate = event.startDate - val dayOfStartOfTimeRange = calendarEventProvider.startOfTimeRange - .withTimeAtStartOfDay() + val timeRangeStartDay = settings.clock.dayOf(calendarEventProvider.startOfTimeRange) if (!event.hasDefaultCalendarColor() // ??? TODO: fix logic && firstDate.isBefore(calendarEventProvider.startOfTimeRange) && event.endDate.isAfter(calendarEventProvider.startOfTimeRange) ) { - if (event.isAllDay || firstDate.isBefore(dayOfStartOfTimeRange)) { - firstDate = dayOfStartOfTimeRange + if (event.isAllDay || firstDate.isBefore(timeRangeStartDay)) { + firstDate = settings.clock.withTimeAtStartHourOfDay(timeRangeStartDay) } } - val today = settings.clock.now(event.startDate.zone).withTimeAtStartOfDay() + val today = settings.clock.startOfToday() if (event.isActive && firstDate.isBefore(today)) { firstDate = today } return CalendarEntry.fromEvent(settings, event, firstDate) } - private fun addEntriesToFillAllDayEvents(entryList: MutableList, dayOneEntry: CalendarEntry) { - var endDate = dayOneEntry.event.endDate - if (endDate.isAfter(calendarEventProvider.endOfTimeRange)) { - endDate = calendarEventProvider.endOfTimeRange + private fun addEntriesToFillAllEventDays(entryList: MutableList, dayOneEntry: CalendarEntry) { + var endDay = if (dayOneEntry.event.endDate.isAfter(calendarEventProvider.endOfTimeRange)) { + calendarEventProvider.endOfTimeRange + } else { + dayOneEntry.event.endDate + }.let { + dayOneEntry.calcEntryDay(it.minusSeconds(1)) } - var thisDay = dayOneEntry.entryDay.plusDays(1).withTimeAtStartOfDay() - while (thisDay.isBefore(endDate)) { - val nextEntry: CalendarEntry = CalendarEntry.fromEvent(settings, dayOneEntry.event, thisDay) + var nextDay = dayOneEntry.entryDay.plusDays(1) + var i = 1 + if (settings.logEvents) { + Log.i("addEntriesToFillAllEventDays", "$i. endDay: $endDay, thisDay: $nextDay, $dayOneEntry") + } + while (!nextDay.isAfter(endDay)) { + val nextEntry: CalendarEntry = CalendarEntry.fromEvent( + settings, dayOneEntry.event, + entryDate = settings.clock.withTimeAtStartHourOfDay(nextDay) + ) + if (settings.logEvents) { + i++ + Log.i("addEntriesToFillAllEventDays", "$i. thisDay: $nextDay, $nextEntry") + } entryList.add(nextEntry) - thisDay = thisDay.plusDays(1) + nextDay = nextDay.plusDays(1) } } } diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/prefs/AllSettings.kt b/app/src/main/kotlin/org/andstatus/todoagenda/prefs/AllSettings.kt index 3d963345..5ba30667 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/prefs/AllSettings.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/prefs/AllSettings.kt @@ -78,7 +78,7 @@ object AllSettings { } } - fun addNew(tag: String, context: Context, settings: InstanceSettings) { + fun addOrReplace(tag: String, context: Context, settings: InstanceSettings) { save(tag, "addNew", settings) } diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/prefs/ApplicationPreferences.kt b/app/src/main/kotlin/org/andstatus/todoagenda/prefs/ApplicationPreferences.kt index 5fae292c..ed667bce 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/prefs/ApplicationPreferences.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/prefs/ApplicationPreferences.kt @@ -100,6 +100,7 @@ object ApplicationPreferences { setString(context, InstanceSettings.PREF_TEXT_SIZE_SCALE, settings.textSizeScale.preferenceValue) setString(context, InstanceSettings.PREF_TIME_FORMAT, settings.timeFormat) setLockedTimeZoneId(context, settings.lockedTimeZoneId) + setString(context, InstanceSettings.PREF_START_HOUR_OF_DAY, settings.startHourOfDay.toString()) setString(context, InstanceSettings.PREF_SNAPSHOT_MODE, settings.snapshotMode.value) setRefreshPeriodMinutes(context, settings.refreshPeriodMinutes) } diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/prefs/InstanceSettings.kt b/app/src/main/kotlin/org/andstatus/todoagenda/prefs/InstanceSettings.kt index e778fb2d..b3504993 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/prefs/InstanceSettings.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/prefs/InstanceSettings.kt @@ -27,6 +27,8 @@ import org.json.JSONObject import java.io.IOException import java.util.concurrent.CopyOnWriteArrayList import java.util.stream.Collectors +import kotlin.Int +import kotlin.math.abs /** * Loaded settings of one Widget @@ -36,6 +38,7 @@ import java.util.stream.Collectors data class InstanceSettings( private val contextIn: Context?, val widgetId: Int, + val logEvents: Boolean = false, // ---------------------------------------------------------------------------------- // Layout @@ -99,6 +102,7 @@ data class InstanceSettings( val textSizeScale: TextSizeScale = TextSizeScale.MEDIUM, val timeFormat: String = PREF_TIME_FORMAT_DEFAULT, private val lockedTimeZoneIdIn: String = "", + private val startHourOfDayIn: Int = 0, private val snapshotModeIn: SnapshotMode = SnapshotMode.Companion.defaultValue, val resultsStorage: QueryResultsStorage? = null, private val refreshPeriodMinutesIn: Int = PREF_REFRESH_PERIOD_MINUTES_DEFAULT, @@ -111,6 +115,7 @@ data class InstanceSettings( proposedInstanceName ) val lockedTimeZoneId: String = DateUtil.validatedTimeZoneId(lockedTimeZoneIdIn) + val startHourOfDay: Int = startHourOfDayIn.takeIf { abs(it) < 13 } ?: 0 val hasResults: Boolean get() = resultsStorage.hasResults() val snapshotMode: SnapshotMode = if (snapshotModeIn.isSnapshotMode && !hasResults) { SnapshotMode.LIVE_DATA @@ -141,6 +146,7 @@ data class InstanceSettings( snapshotMode = snapshotMode, snapshotDate = snapshotDate, timeZone = timeZone, + startHourOfDay = startHourOfDay, ) val isEmpty: Boolean @@ -228,6 +234,7 @@ data class InstanceSettings( put(PREF_TEXT_SIZE_SCALE, textSizeScale.preferenceValue) put(PREF_TIME_FORMAT, timeFormat) put(PREF_LOCKED_TIME_ZONE_ID, lockedTimeZoneId) + put(PREF_START_HOUR_OF_DAY, startHourOfDay) put(PREF_SNAPSHOT_MODE, snapshotMode.value) put(PREF_REFRESH_PERIOD_MINUTES, refreshPeriodMinutes) if (resultsStorage.hasResults()) { @@ -247,7 +254,8 @@ data class InstanceSettings( } val endOfTimeRange: DateTime - get() = (if (eventRange > 0) clock.now().plusDays(eventRange) else clock.now().withTimeAtStartOfDay() + get() = (if (eventRange > 0) clock.now().plusDays(eventRange) + else clock.startOfToday() .plusDays(1 - eventRange)) .minusMillis(1) val startOfTimeRange: DateTime? @@ -421,6 +429,7 @@ data class InstanceSettings( const val PREF_TIME_FORMAT_DEFAULT = "auto" const val PREF_LOCK_TIME_ZONE = "lockTimeZone" const val PREF_LOCKED_TIME_ZONE_ID = "lockedTimeZoneId" + const val PREF_START_HOUR_OF_DAY = "startHourOfDay" const val PREF_SNAPSHOT_MODE = "snapshotMode" const val PREF_RESULTS_STORAGE = "resultsStorage" const val PREF_REFRESH_PERIOD_MINUTES = "refreshPeriodMinutes" @@ -557,6 +566,9 @@ data class InstanceSettings( lockedTimeZoneIdIn = if (json.has(PREF_LOCKED_TIME_ZONE_ID)) { json.getString(PREF_LOCKED_TIME_ZONE_ID) } else EMPTY.lockedTimeZoneId, + startHourOfDayIn = if (json.has(PREF_START_HOUR_OF_DAY)) { + json.getInt(PREF_START_HOUR_OF_DAY) + } else EMPTY.startHourOfDay, refreshPeriodMinutesIn = if (json.has(PREF_REFRESH_PERIOD_MINUTES)) { json.getInt(PREF_REFRESH_PERIOD_MINUTES) } else EMPTY.refreshPeriodMinutes, @@ -739,6 +751,7 @@ data class InstanceSettings( ), timeFormat = ApplicationPreferences.getTimeFormat(context), lockedTimeZoneIdIn = ApplicationPreferences.getLockedTimeZoneId(context), + startHourOfDayIn = ApplicationPreferences.getIntStoredAsString(context, PREF_START_HOUR_OF_DAY, 0), snapshotModeIn = ApplicationPreferences.getSnapshotMode(context), resultsStorage = resultsStorage, refreshPeriodMinutesIn = ApplicationPreferences.getRefreshPeriodMinutes(context), diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/prefs/OtherPreferencesFragment.kt b/app/src/main/kotlin/org/andstatus/todoagenda/prefs/OtherPreferencesFragment.kt index 6e698a07..2149bec1 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/prefs/OtherPreferencesFragment.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/prefs/OtherPreferencesFragment.kt @@ -27,6 +27,7 @@ class OtherPreferencesFragment : MyPreferenceFragment(), OnSharedPreferenceChang showSnapshotMode() setLockTimeZone() showLockTimeZone() + showStartHourOfDay() showRefreshPeriod() preferenceManager.sharedPreferences!!.registerOnSharedPreferenceChangeListener(this) } @@ -55,6 +56,19 @@ class OtherPreferencesFragment : MyPreferenceFragment(), OnSharedPreferenceChang ) } + private fun showStartHourOfDay() { + val preference = + findPreference(InstanceSettings.PREF_START_HOUR_OF_DAY) as EditTextPreference? + if (preference != null) { + val value = ApplicationPreferences.getIntStoredAsString( + requireActivity(), + InstanceSettings.PREF_START_HOUR_OF_DAY, + 0 + ) + preference.summary = value.toString() + } + } + private fun showSnapshotMode() { val preference = findPreference(InstanceSettings.PREF_SNAPSHOT_MODE) ?: return @@ -104,7 +118,7 @@ class OtherPreferencesFragment : MyPreferenceFragment(), OnSharedPreferenceChang timeZone ) settings.copy(lockedTimeZoneIdIn = timeZone) - .let { AllSettings.addNew(TAG, requireActivity(), it) } + .let { AllSettings.addOrReplace(TAG, requireActivity(), it) } showLockTimeZone() } @@ -131,6 +145,7 @@ class OtherPreferencesFragment : MyPreferenceFragment(), OnSharedPreferenceChang ) } + InstanceSettings.PREF_START_HOUR_OF_DAY -> showStartHourOfDay() InstanceSettings.PREF_REFRESH_PERIOD_MINUTES -> showRefreshPeriod() InstanceSettings.PREF_SNAPSHOT_MODE -> { val snapshotMode = ApplicationPreferences.getSnapshotMode(requireActivity()) @@ -143,7 +158,7 @@ class OtherPreferencesFragment : MyPreferenceFragment(), OnSharedPreferenceChang } ) settings.save(key, "newResultsForSnapshotMode") - AllSettings.addNew(TAG, requireActivity(), settings) + AllSettings.addOrReplace(TAG, requireActivity(), settings) showSnapshotMode() setLockTimeZone() showLockTimeZone() diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/provider/QueryResultsStorage.kt b/app/src/main/kotlin/org/andstatus/todoagenda/provider/QueryResultsStorage.kt index 680107aa..5de6a4a5 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/provider/QueryResultsStorage.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/provider/QueryResultsStorage.kt @@ -2,7 +2,6 @@ package org.andstatus.todoagenda.provider import android.content.Context import android.content.Intent -import android.text.TextUtils import android.util.Log import org.andstatus.todoagenda.R import org.andstatus.todoagenda.RemoteViewsFactory @@ -156,24 +155,26 @@ class QueryResultsStorage { val method = "shareEventsForDebugging" Log.i(TAG, "$method started") val settings = AllSettings.instanceFromId(context, widgetId) - val storage = if (settings.isSnapshotMode) settings.resultsStorage else getNewResults(context, widgetId) - val results = storage!!.toJsonString(context, widgetId) - if (TextUtils.isEmpty(results)) { - Log.i(TAG, "$method; Nothing to share") - } else { - val fileName = (settings.widgetInstanceName + "-" + context.getText(R.string.app_name)) - .replace("\\W+".toRegex(), "-") + - "-shareEvents-" + DateUtil.formatLogDateTime(System.currentTimeMillis()) + - ".json" - val intent = Intent(Intent.ACTION_SEND) - intent.setType("application/json") - intent.putExtra(Intent.EXTRA_SUBJECT, fileName) - intent.putExtra(Intent.EXTRA_TEXT, results) - context.startActivity( - Intent.createChooser(intent, context.getText(R.string.share_events_for_debugging_title)) - ) - Log.i(TAG, "$method; Shared $results") + val storage: QueryResultsStorage? = + if (settings.isSnapshotMode) settings.resultsStorage else getNewResults(context, widgetId) + if (storage == null || !storage.hasResults()) { + Log.w(TAG, "$method; Nothing to share") + return } + val results = storage.toJsonString(context, widgetId) + val fileName = (settings.widgetInstanceName + "-" + context.getText(R.string.app_name)) + .replace("\\W+".toRegex(), "-") + + "-shareEvents-" + DateUtil.formatLogDateTime(System.currentTimeMillis()) + + ".json" + Log.d(TAG, "$method; Sharing ${results.length} bytes to $fileName") + val intent = Intent(Intent.ACTION_SEND) + intent.setType("application/json") + intent.putExtra(Intent.EXTRA_SUBJECT, fileName) + intent.putExtra(Intent.EXTRA_TEXT, results) + context.startActivity( + Intent.createChooser(intent, context.getText(R.string.share_events_for_debugging_title)) + ) + Log.i(TAG, "$method; Shared $results") } fun getNewResults(context: Context, widgetId: Int): QueryResultsStorage? { diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/util/DateUtil.kt b/app/src/main/kotlin/org/andstatus/todoagenda/util/DateUtil.kt index 97ca638c..72d3589b 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/util/DateUtil.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/util/DateUtil.kt @@ -18,9 +18,6 @@ object DateUtil { private const val TWELVE = "12" private const val AUTO = "auto" const val EMPTY_STRING = "" - fun isMidnight(date: DateTime): Boolean { - return date.isEqual(date.withTimeAtStartOfDay()) - } fun formatTime(settingsSupplier: Supplier, time: DateTime?): String { if (time == null || !MyClock.isDateDefined(time)) return EMPTY_STRING diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/util/MyClock.kt b/app/src/main/kotlin/org/andstatus/todoagenda/util/MyClock.kt index ca1dddcf..cf35aece 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/util/MyClock.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/util/MyClock.kt @@ -6,7 +6,6 @@ import org.joda.time.DateTimeZone import org.joda.time.Days import org.joda.time.Minutes import java.util.TimeZone -import java.util.concurrent.atomic.AtomicInteger import kotlin.concurrent.Volatile /** @@ -18,15 +17,11 @@ class MyClock( private val snapshotMode: SnapshotMode, private val snapshotDate: DateTime?, private val timeZone: DateTimeZone, + private val startHourOfDay: Int, ) { private val snapshotDateSetAt: DateTime = DateTime.now() - private val startHourOfDayRef: AtomicInteger = AtomicInteger(0) - var startHourOfDay: Int - get() = startHourOfDayRef.get() - set(value) { - startHourOfDayRef.set(value) - } + fun today(timeZone: DateTimeZone? = this.timeZone): DateTime = dayOf(now(timeZone)) /** * Usually returns real "now", but may be #setNow to some other time for testing purposes @@ -40,27 +35,31 @@ class MyClock( } } - private fun getTimeMachineDate(zone: DateTimeZone?): DateTime { + private fun getTimeMachineDate(timeZone: DateTimeZone?): DateTime { return if (snapshotDate == null) { - DateTime.now(zone) + DateTime.now(timeZone) } else { val millisElapsed = DateTime.now().millis - snapshotDateSetAt.millis - DateTime(snapshotDate, zone).plusMillis(millisElapsed.toInt()) + DateTime(snapshotDate, timeZone).plusMillis(millisElapsed.toInt()) } } + fun isDayToday(date: DateTime?): Boolean = isDateDefined(date) && date!!.isEqual(today(date.zone)) + fun isToday(date: DateTime?): Boolean { - return isDateDefined(date) && !isBeforeToday(date) && date!!.isBefore( - now(date.zone).plusDays(1).withTimeAtStartOfDay() - ) + return isDateDefined(date) && dayOf(date!!).isEqual(today(date.zone)) + } + + fun isDayBeforeToday(date: DateTime?): Boolean { + return isDateDefined(date) && date!!.isBefore(today(date.zone)) } fun isBeforeToday(date: DateTime?): Boolean { - return isDateDefined(date) && date!!.isBefore(now(date.zone).withTimeAtStartOfDay()) + return isDateDefined(date) && date!!.isBefore(today(date.zone).withTimeAtStartHourOfDayInner()) } fun isAfterToday(date: DateTime?): Boolean { - return isDateDefined(date) && !date!!.isBefore(now(date.zone).withTimeAtStartOfDay().plusDays(1)) + return isDateDefined(date) && !date!!.isBefore(today(date.zone).withTimeAtStartHourOfDayInner().plusDays(1)) } fun isBeforeNow(date: DateTime?): Boolean { @@ -81,7 +80,54 @@ class MyClock( } fun startOfTomorrow(): DateTime { - return startOfNextDay(now(timeZone)) + return startOfToday().plusDays(1) + } + + fun startOfToday(): DateTime { + return dayOf(now()).withTimeAtStartHourOfDayInner() + } + + fun dayOf(date: DateTime): DateTime { + return when { + startHourOfDay == 0 -> date.withTimeAtStartOfDay() + startHourOfDay > 0 -> { + val startOfThisDay = date.withTimeAtStartHourOfDayInner() + if (startOfThisDay.isAfter(date)) { + date.minusDays(1).withTimeAtStartOfDay() + } else { + date.withTimeAtStartOfDay() + } + } + + else -> { + val startOfNextDay = date.withTimeAtStartHourOfDayInner().plusDays(1) + if (date.isBefore(startOfNextDay)) { + date.withTimeAtStartOfDay() + } else { + date.plusDays(1).withTimeAtStartOfDay() + } + } + } + } + + fun isStartOfDay(date: DateTime): Boolean { + return date.isEqual(startOfThisDay(date)) + } + + fun startOfNextDay(date: DateTime): DateTime { + return startOfThisDay(date).plusDays(1) + } + + fun startOfThisDay(date: DateTime): DateTime { + return dayOf(date).withTimeAtStartHourOfDayInner() + } + + fun withTimeAtStartHourOfDay(date: DateTime): DateTime { + return date.withTimeAtStartHourOfDayInner() + } + + private fun DateTime.withTimeAtStartHourOfDayInner(): DateTime { + return withTimeAtStartOfDay().plusHours(startHourOfDay) } companion object { @@ -92,10 +138,6 @@ class MyClock( var myDefaultTimeZone: DateTimeZone? = null val defaultTimeZone: DateTimeZone get() = myDefaultTimeZone ?: DateTimeZone.forTimeZone(TimeZone.getDefault()) - fun startOfNextDay(date: DateTime): DateTime { - return date.plusDays(1).withTimeAtStartOfDay() - } - fun isDateDefined(dateTime: DateTime?): Boolean { return dateTime != null && dateTime.isAfter(DATETIME_MIN) && dateTime.isBefore(DATETIME_MAX) } diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/widget/CalendarEntry.kt b/app/src/main/kotlin/org/andstatus/todoagenda/widget/CalendarEntry.kt index 8da29169..48f297c1 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/widget/CalendarEntry.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/widget/CalendarEntry.kt @@ -64,7 +64,7 @@ class CalendarEntry private constructor( val startStr: String? val endStr: String? var separator = SPACE_DASH_SPACE - if (!MyClock.isDateDefined(entryDate) || (isPartOfMultiDayEvent && DateUtil.isMidnight(entryDate) + if (!MyClock.isDateDefined(entryDate) || (isPartOfMultiDayEvent && settings.clock.isStartOfDay(entryDate) && !isStartOfMultiDayEvent) ) { startStr = ARROW diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/widget/LastEntry.kt b/app/src/main/kotlin/org/andstatus/todoagenda/widget/LastEntry.kt index 9cb436ab..476f88e7 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/widget/LastEntry.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/widget/LastEntry.kt @@ -26,13 +26,13 @@ class LastEntry(settings: InstanceSettings, val type: LastEntryType, date: DateT return LastEntry(settings, entryType, settings.clock.now()) } - fun addLast(settings: InstanceSettings, widgetEntries: MutableList>) { + fun addLast(settings: InstanceSettings, widgetEntries: List>): List> { val entry = if (widgetEntries.isEmpty()) forEmptyList(settings) else LastEntry( settings, LastEntryType.LAST, widgetEntries[widgetEntries.size - 1].entryDate ) - widgetEntries.add(entry) + return widgetEntries + entry } } } diff --git a/app/src/main/kotlin/org/andstatus/todoagenda/widget/WidgetEntry.kt b/app/src/main/kotlin/org/andstatus/todoagenda/widget/WidgetEntry.kt index 30c38a47..30f25831 100644 --- a/app/src/main/kotlin/org/andstatus/todoagenda/widget/WidgetEntry.kt +++ b/app/src/main/kotlin/org/andstatus/todoagenda/widget/WidgetEntry.kt @@ -24,14 +24,14 @@ abstract class WidgetEntry> protected constructor( init { this.entryDate = fixEntryDate(entryPosition, entryDate) - entryDay = calcEntryDay(settings, entryPosition, this.entryDate) + entryDay = calcEntryDay(entryDate) timeSection = calcTimeSection(settings, entryPosition, entryDay, endDate) } val isLastEntryOfEvent: Boolean get() = endDate == null || !entryPosition.entryDateIsRequired || - endDate.isBefore(MyClock.startOfNextDay(entryDate)) + !endDate.isAfter(settings.clock.startOfNextDay(entryDate)) open val eventTimeString: String get() = "" abstract val source: OrderedEventSource @@ -108,14 +108,24 @@ abstract class WidgetEntry> protected constructor( return entryPosition != WidgetEntryPosition.HIDDEN } + fun calcEntryDay(entryDate: DateTime): DateTime { + return when (entryPosition) { + WidgetEntryPosition.START_OF_TODAY, WidgetEntryPosition.END_OF_TODAY -> settings.clock.now() + .withTimeAtStartOfDay() + + WidgetEntryPosition.DAY_HEADER -> entryDate.withTimeAtStartOfDay() + + else -> if (allDay) entryDate.withTimeAtStartOfDay() else settings.clock.dayOf(entryDate) + } + } + companion object { val EXTRA_WIDGET_ENTRY_ID: String = RemoteViewsFactory.PACKAGE + ".extra.WIDGET_ENTRY_ID" private val idGenerator = AtomicLong(0) private fun fixEntryDate(entryPosition: WidgetEntryPosition, entryDate: DateTime?): DateTime { return when (entryPosition) { WidgetEntryPosition.ENTRY_DATE -> { - throwIfNull(entryPosition, entryDate) - entryDate!! + requireNotNull(entryPosition, entryDate) } WidgetEntryPosition.PAST_AND_DUE_HEADER, @@ -124,13 +134,11 @@ abstract class WidgetEntry> protected constructor( WidgetEntryPosition.HIDDEN -> entryDate ?: MyClock.DATETIME_MIN WidgetEntryPosition.DAY_HEADER, WidgetEntryPosition.START_OF_DAY -> { - throwIfNull(entryPosition, entryDate) - entryDate!!.withTimeAtStartOfDay() + requireNotNull(entryPosition, entryDate).withTimeAtStartOfDay() } WidgetEntryPosition.END_OF_DAY -> { - throwIfNull(entryPosition, entryDate) - entryDate!!.withTimeAtStartOfDay().plusDays(1).minusMillis(1) + requireNotNull(entryPosition, entryDate).withTimeAtStartOfDay().plusDays(1).minusMillis(1) } WidgetEntryPosition.END_OF_TODAY, @@ -142,19 +150,6 @@ abstract class WidgetEntry> protected constructor( } } - private fun calcEntryDay( - settings: InstanceSettings, - entryPosition: WidgetEntryPosition?, - entryDate: DateTime? - ): DateTime { - return when (entryPosition) { - WidgetEntryPosition.START_OF_TODAY, WidgetEntryPosition.END_OF_TODAY -> settings.clock.now() - .withTimeAtStartOfDay() - - else -> entryDate!!.withTimeAtStartOfDay() - } - } - private fun calcTimeSection( settings: InstanceSettings, entryPosition: WidgetEntryPosition?, entryDay: DateTime, endDate: DateTime? @@ -165,19 +160,19 @@ abstract class WidgetEntry> protected constructor( WidgetEntryPosition.END_OF_TODAY, WidgetEntryPosition.END_OF_LIST_HEADER, WidgetEntryPosition.END_OF_LIST, WidgetEntryPosition.LIST_FOOTER -> return TimeSection.FUTURE else -> {} } - if (settings.clock.isToday(entryDay)) { + if (settings.clock.isDayToday(entryDay)) { if (entryPosition == WidgetEntryPosition.DAY_HEADER) return TimeSection.TODAY return if (settings.clock.isToday(endDate)) { if (settings.clock.isBeforeNow(endDate)) TimeSection.PAST else TimeSection.TODAY } else TimeSection.TODAY } - return if (settings.clock.isBeforeToday(entryDay)) TimeSection.PAST else if (settings.clock + return if (settings.clock.isDayBeforeToday(entryDay)) TimeSection.PAST else if (settings.clock .isToday(endDate) ) TimeSection.TODAY else TimeSection.FUTURE } - private fun throwIfNull(entryPosition: WidgetEntryPosition, entryDate: DateTime?) { - requireNotNull(entryDate) { "Invalid entry date: $entryDate at position $entryPosition" } + private fun requireNotNull(entryPosition: WidgetEntryPosition, entryDate: DateTime?): DateTime { + return requireNotNull(entryDate) { "Invalid entry date: $entryDate at position $entryPosition" } } fun getEntryPosition( diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ea1c8d1e..547afdf9 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -223,6 +223,7 @@ Разблокирован, текущий часовой пояс: %s Заблокирован на %s Блокировка часового пояса + Час начала дня Заблокировать изменения списка событий (Режим снимка) Живые данные из реальных источников Снимок, как он был виден тогда, когда он был сделан: %s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 09041432..4fffd647 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -261,6 +261,7 @@ Unlocked, current zone: %s Locked at %s Lock time zone + Start hour of day Lock list of events (Snapshot mode) Live data from real sources Snapshot as seen at the time, when it was taken: %s diff --git a/app/src/main/res/xml/preferences_other.xml b/app/src/main/res/xml/preferences_other.xml index a6977d11..4b2dcb53 100644 --- a/app/src/main/res/xml/preferences_other.xml +++ b/app/src/main/res/xml/preferences_other.xml @@ -2,42 +2,46 @@ + android:title="@string/widget_instance_name" /> + + + android:singleLine="true" + android:title="@string/refresh_period_minutes" />