diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index d1cc818..3acaa76 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
+
@@ -53,7 +54,7 @@
diff --git a/android/app/src/main/kotlin/es/freetimelab/pluriwave/AlarmScheduler.kt b/android/app/src/main/kotlin/es/freetimelab/pluriwave/AlarmScheduler.kt
index 093ce16..a0fbf62 100644
--- a/android/app/src/main/kotlin/es/freetimelab/pluriwave/AlarmScheduler.kt
+++ b/android/app/src/main/kotlin/es/freetimelab/pluriwave/AlarmScheduler.kt
@@ -36,7 +36,10 @@ class AlarmScheduler(private val context: Context) {
snoozeOriginMillis: Long? = null,
lastHandledAtMillis: Long? = null,
soundOnVacation: Boolean = true,
- snoozeMinutes: Int = 5
+ snoozeMinutes: Int = 5,
+ fallbackStationName: String? = null,
+ fallbackStationUrl: String? = null,
+ fadeInSegundos: Int = 0
): Boolean {
val existing = readSpec(id)
val preservedSnooze = preserveNativeSnooze(
@@ -62,8 +65,11 @@ class AlarmScheduler(private val context: Context) {
snoozeMinutes = sanitizeSnoozeMinutes(snoozeMinutes),
stationName = stationName,
stationUrl = stationUrl,
+ fallbackStationName = fallbackStationName,
+ fallbackStationUrl = fallbackStationUrl,
fallbackSound = fallbackSound,
volume = volume.coerceIn(0f, 1f),
+ fadeInSegundos = fadeInSegundos.coerceIn(0, 60),
timezoneId = TimeZone.getDefault().id
)
return scheduleSpec(spec, persistOnSuccess = true)
@@ -251,17 +257,36 @@ class AlarmScheduler(private val context: Context) {
}
}
- fun snooze(id: String, minutes: Int) {
- val spec = readSpec(id) ?: return
+ /**
+ * Snoozes using the SAME anchor as [postponeNext] (Design 2.2): the
+ * occurrence time + minutes, clamped to now + minutes when the target is
+ * already past. Returns the resulting snooze so the caller can report it
+ * back to Flutter (single source of truth), or null if the spec is gone.
+ */
+ fun snooze(id: String, minutes: Int): NativeSnoozeResult? {
+ val spec = readSpec(id) ?: return null
val safeMinutes = sanitizeSnoozeMinutes(minutes)
- val snoozeUntil = System.currentTimeMillis() + safeMinutes * 60_000L
+ val occurrenceAt = spec.snoozeOriginMillis ?: spec.triggerAtMillis
+ val target = occurrenceAt + safeMinutes * 60_000L
+ val now = System.currentTimeMillis()
+ val snoozeUntil = if (target > now) target else now + safeMinutes * 60_000L
+ Log.d(
+ tag,
+ "alarm.snooze id=$id minutes=$safeMinutes occurrence=$occurrenceAt until=$snoozeUntil"
+ )
scheduleSpec(
spec.copy(
snoozeUntilMillis = snoozeUntil,
- snoozeOriginMillis = spec.snoozeOriginMillis ?: spec.triggerAtMillis
+ snoozeOriginMillis = occurrenceAt,
+ snoozeMinutes = safeMinutes
),
persistOnSuccess = true
)
+ return NativeSnoozeResult(
+ snoozeUntilMillis = snoozeUntil,
+ occurrenceAtMillis = occurrenceAt,
+ title = spec.title
+ )
}
fun postponeNext(id: String, minutes: Int): Long? {
@@ -339,6 +364,26 @@ class AlarmScheduler(private val context: Context) {
)
}
+ /**
+ * Active (future) native snoozes from the persisted specs, used by the
+ * Flutter cold-start sync (Decision 2.1, engine-dead case) so a snooze
+ * performed while the engine was down is imported on the next launch.
+ */
+ fun nativeSnoozeStates(): List