dbff329384
WidgetRefreshSchedule: 30-min day / 120-min overnight (6am–11pm split). WidgetRefreshWorker: CoroutineWorker fetches via APILayer -> repo -> widget.update. WidgetUpdateManager: chained one-time enqueue pattern (WorkManager PeriodicWork can't vary cadence). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
63 lines
2.3 KiB
Kotlin
63 lines
2.3 KiB
Kotlin
package com.tt.honeyDue.widget
|
||
|
||
import kotlinx.datetime.DateTimeUnit
|
||
import kotlinx.datetime.LocalDateTime
|
||
import kotlinx.datetime.TimeZone
|
||
import kotlinx.datetime.plus
|
||
import kotlinx.datetime.toInstant
|
||
import kotlinx.datetime.toLocalDateTime
|
||
|
||
/**
|
||
* Pure-logic schedule for widget refresh cadence. Mirrors the iOS-parity
|
||
* split from the P3 parity plan:
|
||
*
|
||
* - 06:00 (inclusive) .. 23:00 (exclusive) local → refresh every 30 minutes
|
||
* - 23:00 (inclusive) .. 06:00 (exclusive) local → refresh every 120 minutes
|
||
*
|
||
* iOS ([BackgroundTaskManager.swift]) uses a random 12am–4am overnight
|
||
* BGAppRefreshTask window rather than a fixed cadence, because iOS
|
||
* `BGTaskScheduler` is coalesced by the system. Android's WorkManager runs
|
||
* user-defined intervals, so this file encodes the ios-parity cadence the
|
||
* plan specifies. The split 30/120 preserves the core intent: frequent
|
||
* while awake, sparse while the user is asleep.
|
||
*/
|
||
object WidgetRefreshSchedule {
|
||
|
||
private const val DAY_START_HOUR_INCLUSIVE = 6 // 06:00 local
|
||
private const val DAY_END_HOUR_EXCLUSIVE = 23 // 23:00 local
|
||
|
||
const val DAY_INTERVAL_MINUTES: Long = 30L
|
||
const val NIGHT_INTERVAL_MINUTES: Long = 120L
|
||
|
||
/**
|
||
* Returns the refresh interval (in minutes) for a wall-clock time.
|
||
*
|
||
* Hour bands:
|
||
* - [06:00, 23:00) → [DAY_INTERVAL_MINUTES] (30)
|
||
* - [23:00, 06:00) → [NIGHT_INTERVAL_MINUTES] (120)
|
||
*/
|
||
fun intervalMinutes(at: LocalDateTime): Long {
|
||
val hour = at.hour
|
||
return if (hour in DAY_START_HOUR_INCLUSIVE until DAY_END_HOUR_EXCLUSIVE) {
|
||
DAY_INTERVAL_MINUTES
|
||
} else {
|
||
NIGHT_INTERVAL_MINUTES
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Returns `now + intervalMinutes(now)` as a [LocalDateTime].
|
||
*
|
||
* Arithmetic is performed through [TimeZone.UTC] to avoid ambiguity
|
||
* around DST transitions in the local zone — the absolute minute offset
|
||
* is what WorkManager's `setInitialDelay` consumes, so the returned
|
||
* wall-clock value is for display/testing only.
|
||
*/
|
||
fun nextRefreshTime(now: LocalDateTime): LocalDateTime {
|
||
val interval = intervalMinutes(now)
|
||
val instant = now.toInstant(TimeZone.UTC)
|
||
val next = instant.plus(interval, DateTimeUnit.MINUTE)
|
||
return next.toLocalDateTime(TimeZone.UTC)
|
||
}
|
||
}
|