Skip to content

Commit 32dada1

Browse files
authored
fix: event lookahead milestone one fast follow p3 (#177)
* fix: cuttoff become day boundary * fix: s/cutoff/dayboundary/g * fix: bug bot ignore old banner settings im ok with people seeing it one more time now that it actually goes to the right place * fix: fully replace cuttoff with day boundary
1 parent 589fadd commit 32dada1

7 files changed

Lines changed: 331 additions & 234 deletions

File tree

android/app/src/main/java/com/github/quarck/calnotify/Settings.kt

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -450,32 +450,23 @@ class Settings(context: Context) : PersistentStorageBase(context), SettingsInter
450450
get() = getBoolean(USE_NEW_NAVIGATION_UI_KEY, true)
451451
set(value) = setBoolean(USE_NEW_NAVIGATION_UI_KEY, value)
452452

453-
/** Whether the "new UI" info banner has been dismissed (legacy, kept for compatibility) */
454-
var newUIBannerDismissed: Boolean
455-
get() = getBoolean(NEW_UI_BANNER_DISMISSED_KEY, false)
456-
set(value) = setBoolean(NEW_UI_BANNER_DISMISSED_KEY, value)
457-
458-
/** Whether to show the "new UI" info banner on Active events screen.
459-
* Migrates from old newUIBannerDismissed preference if user previously dismissed banner. */
453+
/** Whether to show the "new UI" info banner on Active events screen */
460454
var showNewUIBanner: Boolean
461-
get() {
462-
// Migration: if user dismissed banner with old preference, respect that
463-
if (newUIBannerDismissed) return false
464-
return getBoolean(SHOW_NEW_UI_BANNER_KEY, true)
465-
}
455+
get() = getBoolean(SHOW_NEW_UI_BANNER_KEY, true)
466456
set(value) = setBoolean(SHOW_NEW_UI_BANNER_KEY, value)
467457

468-
/** Lookahead mode: "cutoff" = next morning cutoff, "fixed" = fixed hours */
458+
/** Lookahead mode: "fixed" = fixed hours ahead (default), "day_boundary" = until day boundary.*/
469459
var upcomingEventsMode: String
470-
get() = getString(UPCOMING_EVENTS_MODE_KEY, "cutoff")
460+
get() = getString(UPCOMING_EVENTS_MODE_KEY, "fixed")
471461
set(value) = setString(UPCOMING_EVENTS_MODE_KEY, value)
472462

473-
/** Hour of day for morning cutoff (6-12, default 10). Bounded to prevent date rollover. */
474-
var upcomingEventsCutoffHour: Int
475-
get() = (getString(UPCOMING_EVENTS_CUTOFF_HOUR_KEY, DEFAULT_UPCOMING_EVENTS_CUTOFF_HOUR.toString())
476-
.toIntOrNull() ?: DEFAULT_UPCOMING_EVENTS_CUTOFF_HOUR)
477-
.coerceIn(MIN_CUTOFF_HOUR, MAX_CUTOFF_HOUR)
478-
set(value) = setString(UPCOMING_EVENTS_CUTOFF_HOUR_KEY, value.coerceIn(MIN_CUTOFF_HOUR, MAX_CUTOFF_HOUR).toString())
463+
/** Hour when "new day" begins (0-10, default 4am). Before this hour, show until today's boundary;
464+
* after, show until tomorrow's boundary. */
465+
var upcomingEventsDayBoundaryHour: Int
466+
get() = (getString(UPCOMING_EVENTS_DAY_BOUNDARY_HOUR_KEY, DEFAULT_UPCOMING_EVENTS_DAY_BOUNDARY_HOUR.toString())
467+
.toIntOrNull() ?: DEFAULT_UPCOMING_EVENTS_DAY_BOUNDARY_HOUR)
468+
.coerceIn(MIN_DAY_BOUNDARY_HOUR, MAX_DAY_BOUNDARY_HOUR)
469+
set(value) = setString(UPCOMING_EVENTS_DAY_BOUNDARY_HOUR_KEY, value.coerceIn(MIN_DAY_BOUNDARY_HOUR, MAX_DAY_BOUNDARY_HOUR).toString())
479470

480471
/** Fixed hours lookahead (1-48, default 8). Bounded to prevent misconfiguration. */
481472
var upcomingEventsFixedHours: Int
@@ -577,10 +568,9 @@ class Settings(context: Context) : PersistentStorageBase(context), SettingsInter
577568

578569
// Upcoming events / navigation settings
579570
private const val USE_NEW_NAVIGATION_UI_KEY = "use_new_navigation_ui"
580-
private const val NEW_UI_BANNER_DISMISSED_KEY = "new_ui_banner_dismissed"
581571
private const val SHOW_NEW_UI_BANNER_KEY = "show_new_ui_banner"
582572
private const val UPCOMING_EVENTS_MODE_KEY = "upcoming_events_mode"
583-
private const val UPCOMING_EVENTS_CUTOFF_HOUR_KEY = "upcoming_events_cutoff_hour"
573+
private const val UPCOMING_EVENTS_DAY_BOUNDARY_HOUR_KEY = "upcoming_events_day_boundary_hour"
584574
private const val UPCOMING_EVENTS_FIXED_HOURS_KEY = "upcoming_events_fixed_hours"
585575

586576
// Default values
@@ -594,10 +584,10 @@ class Settings(context: Context) : PersistentStorageBase(context), SettingsInter
594584
internal const val DEFAULT_KEEP_HISTORY_DAYS = 14
595585

596586
// Upcoming events defaults and bounds
597-
internal const val DEFAULT_UPCOMING_EVENTS_CUTOFF_HOUR = 10
587+
internal const val DEFAULT_UPCOMING_EVENTS_DAY_BOUNDARY_HOUR = 4 // 4am - when "new day" begins
598588
internal const val DEFAULT_UPCOMING_EVENTS_FIXED_HOURS = 8
599-
internal const val MIN_CUTOFF_HOUR = 6
600-
internal const val MAX_CUTOFF_HOUR = 12
589+
internal const val MIN_DAY_BOUNDARY_HOUR = 0 // Midnight (no slack)
590+
internal const val MAX_DAY_BOUNDARY_HOUR = 10 // 10am (max slack for night owls)
601591
internal const val MIN_FIXED_HOURS = 1
602592
internal const val MAX_FIXED_HOURS = 48
603593
}

android/app/src/main/java/com/github/quarck/calnotify/upcoming/UpcomingEventsLookahead.kt

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,74 +25,75 @@ import com.github.quarck.calnotify.utils.CNPlusClockInterface
2525
import java.util.Calendar
2626

2727
/**
28-
* Calculates the cutoff time for the upcoming events lookahead window.
28+
* Calculates the end time for the upcoming events lookahead window.
2929
*
3030
* Supports two modes:
31-
* - "cutoff": Shows events until the next morning cutoff time (e.g., 10 AM tomorrow)
32-
* - "fixed": Shows events for a fixed number of hours ahead
31+
* - "fixed" (default): Shows events for a fixed number of hours ahead
32+
* - "day_boundary": Shows events until the next day boundary (e.g., 4 AM)
3333
*
34-
* The cutoff mode is useful for seeing late-night events without showing all of tomorrow's events.
34+
* The day boundary mode is for night owls who think in terms of "today" vs "tomorrow"
35+
* rather than fixed hours. Before the boundary hour, you're still mentally in "yesterday";
36+
* after it, you're planning "today".
3537
*/
3638
class UpcomingEventsLookahead(
3739
private val settings: Settings,
3840
private val clock: CNPlusClockInterface
3941
) {
4042

4143
/**
42-
* Get the cutoff time for the upcoming events window.
43-
* Events with alertTime between now and this cutoff will be shown.
44+
* Get the end time for the upcoming events lookahead window.
45+
* Events with alertTime between now and this end time will be shown.
4446
*
45-
* @return The cutoff time in milliseconds since epoch
47+
* @return The lookahead end time in milliseconds since epoch
4648
*/
47-
fun getCutoffTime(): Long {
49+
fun getLookaheadEndTime(): Long {
4850
val now = clock.currentTimeMillis()
4951

5052
return when (settings.upcomingEventsMode) {
51-
MODE_FIXED -> calculateFixedCutoff(now)
52-
MODE_CUTOFF -> calculateMorningCutoff(now)
53-
else -> calculateMorningCutoff(now) // Default to cutoff mode
53+
MODE_DAY_BOUNDARY -> calculateDayBoundaryEndTime(now)
54+
else -> calculateFixedEndTime(now) // Default to fixed mode
5455
}
5556
}
5657

5758
/**
5859
* Fixed hours lookahead: now + configured hours
5960
*/
60-
private fun calculateFixedCutoff(now: Long): Long {
61+
private fun calculateFixedEndTime(now: Long): Long {
6162
val fixedHours = settings.upcomingEventsFixedHours
6263
return now + (fixedHours * Consts.HOUR_IN_MILLISECONDS)
6364
}
6465

6566
/**
66-
* Next morning cutoff: Shows events until the cutoff hour the next morning.
67+
* Day boundary mode: before the boundary hour, show until today's boundary;
68+
* after the boundary hour, show until tomorrow's boundary.
6769
*
68-
* If current time is before the cutoff hour, use today's cutoff.
69-
* If current time is at or after the cutoff hour, use tomorrow's cutoff.
70-
*
71-
* Example with cutoff at 10 AM:
72-
* - At 2 AM: show events until 10 AM today
73-
* - At 11 PM: show events until 10 AM tomorrow
70+
* Example with boundary at 4 AM:
71+
* - At 1 AM: show until 4 AM today (3 hours) - "still yesterday"
72+
* - At 5 AM: show until 4 AM tomorrow (23 hours) - "today has begun"
73+
* - At 10 PM: show until 4 AM tomorrow (6 hours) - "winding down"
7474
*/
75-
private fun calculateMorningCutoff(now: Long): Long {
76-
val cutoffHour = settings.upcomingEventsCutoffHour
75+
private fun calculateDayBoundaryEndTime(now: Long): Long {
76+
val boundaryHour = settings.upcomingEventsDayBoundaryHour
77+
7778
val calendar = Calendar.getInstance().apply { timeInMillis = now }
7879
val currentHour = calendar.get(Calendar.HOUR_OF_DAY)
7980

80-
// Set to today at cutoff hour
81-
calendar.set(Calendar.HOUR_OF_DAY, cutoffHour)
81+
// Set to today at boundary hour
82+
calendar.set(Calendar.HOUR_OF_DAY, boundaryHour)
8283
calendar.set(Calendar.MINUTE, 0)
8384
calendar.set(Calendar.SECOND, 0)
8485
calendar.set(Calendar.MILLISECOND, 0)
8586

86-
// If we're past the cutoff already, move to tomorrow
87-
if (currentHour >= cutoffHour) {
87+
// If we're at or past the boundary, use tomorrow's boundary
88+
if (currentHour >= boundaryHour) {
8889
calendar.add(Calendar.DAY_OF_YEAR, 1)
8990
}
9091

9192
return calendar.timeInMillis
9293
}
9394

9495
companion object {
95-
const val MODE_CUTOFF = "cutoff"
9696
const val MODE_FIXED = "fixed"
97+
const val MODE_DAY_BOUNDARY = "day_boundary"
9798
}
9899
}

android/app/src/main/java/com/github/quarck/calnotify/upcoming/UpcomingEventsProvider.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import com.github.quarck.calnotify.utils.CNPlusClockInterface
3232
*
3333
* "Upcoming" events are alerts that:
3434
* - Have not been handled yet (wasHandled = false)
35-
* - Have alertTime between now and the lookahead cutoff
35+
* - Have alertTime within the configured lookahead window
3636
*
3737
* This class is designed to be testable with injected dependencies.
3838
*/
@@ -53,12 +53,12 @@ class UpcomingEventsProvider(
5353
*/
5454
fun getUpcomingEvents(): List<EventAlertRecord> {
5555
val now = clock.currentTimeMillis()
56-
val cutoffTime = lookahead.getCutoffTime()
56+
val endTime = lookahead.getLookaheadEndTime()
5757

58-
DevLog.debug(LOG_TAG, "getUpcomingEvents: now=$now, cutoff=$cutoffTime")
58+
DevLog.debug(LOG_TAG, "getUpcomingEvents: now=$now, endTime=$endTime")
5959

6060
// Fetch alerts in the time range
61-
val alerts = monitorStorage.getAlertsForAlertRange(now, cutoffTime)
61+
val alerts = monitorStorage.getAlertsForAlertRange(now, endTime)
6262
.filter { !it.wasHandled }
6363
.sortedBy { it.alertTime }
6464

android/app/src/main/res/values/strings.xml

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -602,33 +602,42 @@
602602

603603
<!-- Upcoming Events Settings -->
604604
<string-array name="upcoming_events_mode_entries">
605-
<item>@string/upcoming_events_mode_cutoff</item>
606605
<item>@string/upcoming_events_mode_fixed</item>
606+
<item>@string/upcoming_events_mode_day_boundary</item>
607607
</string-array>
608608

609609
<string-array name="upcoming_events_mode_values">
610-
<item>cutoff</item>
611610
<item>fixed</item>
611+
<item>day_boundary</item>
612612
</string-array>
613613

614-
<string-array name="upcoming_events_cutoff_hour_entries">
614+
<!-- Day boundary hour: 0 (midnight) to 10 AM, default 4 AM -->
615+
<string-array name="upcoming_events_day_boundary_hour_entries">
616+
<item>12 AM (midnight)</item>
617+
<item>1 AM</item>
618+
<item>2 AM</item>
619+
<item>3 AM</item>
620+
<item>4 AM</item>
621+
<item>5 AM</item>
615622
<item>6 AM</item>
616623
<item>7 AM</item>
617624
<item>8 AM</item>
618625
<item>9 AM</item>
619626
<item>10 AM</item>
620-
<item>11 AM</item>
621-
<item>12 PM</item>
622627
</string-array>
623628

624-
<string-array name="upcoming_events_cutoff_hour_values">
629+
<string-array name="upcoming_events_day_boundary_hour_values">
630+
<item>0</item>
631+
<item>1</item>
632+
<item>2</item>
633+
<item>3</item>
634+
<item>4</item>
635+
<item>5</item>
625636
<item>6</item>
626637
<item>7</item>
627638
<item>8</item>
628639
<item>9</item>
629640
<item>10</item>
630-
<item>11</item>
631-
<item>12</item>
632641
</string-array>
633642

634643
<string-array name="upcoming_events_fixed_hours_entries">
@@ -707,9 +716,9 @@
707716
<!-- Upcoming events settings -->
708717
<string name="upcoming_events_category">Upcoming Events</string>
709718
<string name="upcoming_events_mode_title">Lookahead Mode</string>
710-
<string name="upcoming_events_mode_cutoff">Next morning cutoff</string>
711719
<string name="upcoming_events_mode_fixed">Fixed hours</string>
712-
<string name="upcoming_events_cutoff_hour_title">Morning Cutoff Time</string>
720+
<string name="upcoming_events_mode_day_boundary">Day boundary</string>
721+
<string name="upcoming_events_day_boundary_hour_title">Day Boundary Hour</string>
713722
<string name="upcoming_events_fixed_hours_title">Hours to Look Ahead</string>
714723
<string name="navigation_settings_title">Navigation &amp; UI</string>
715724
<string name="use_new_navigation_ui_title">New Navigation UI</string>

android/app/src/main/res/xml/navigation_preferences.xml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@
2424
android:dialogTitle="@string/upcoming_events_mode_title"
2525
android:entries="@array/upcoming_events_mode_entries"
2626
android:entryValues="@array/upcoming_events_mode_values"
27-
android:defaultValue="cutoff" />
27+
android:defaultValue="fixed" />
2828

2929
<ListPreference
30-
android:key="upcoming_events_cutoff_hour"
31-
android:title="@string/upcoming_events_cutoff_hour_title"
32-
android:dialogTitle="@string/upcoming_events_cutoff_hour_title"
33-
android:entries="@array/upcoming_events_cutoff_hour_entries"
34-
android:entryValues="@array/upcoming_events_cutoff_hour_values"
35-
android:defaultValue="10" />
30+
android:key="upcoming_events_day_boundary_hour"
31+
android:title="@string/upcoming_events_day_boundary_hour_title"
32+
android:dialogTitle="@string/upcoming_events_day_boundary_hour_title"
33+
android:entries="@array/upcoming_events_day_boundary_hour_entries"
34+
android:entryValues="@array/upcoming_events_day_boundary_hour_values"
35+
android:defaultValue="4" />
3636

3737
<ListPreference
3838
android:key="upcoming_events_fixed_hours"

0 commit comments

Comments
 (0)