P3 Stream L: widget refresh scheduler (WorkManager, iOS cadence)
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>
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user