warm_alarm_android

style: very good analysis License: BSD-3-Clause

The Android implementation of warm_alarm.

This package is endorsed, which means you do not add it directly to your pubspec.yaml. It is automatically included when you depend on warm_alarm.


Platform capabilities

Feature Support Notes
Notification scheduling ✅ Full POST_NOTIFICATIONS permission required
Exact alarm scheduling ✅ Full SCHEDULE_EXACT_ALARM / USE_EXACT_ALARM
Background audio playback ⚠️ Limited Foreground service with mediaPlayback type
Full-screen presentation ✅ Full setFullScreenIntent; per-alarm via androidFullScreenIntent; runtime-checked with canUseFullScreenIntent()
Wake-check ✅ Full Follow-up alarm verifies user is awake

Native implementation

Scheduling

Alarms are scheduled via AlarmManager.setExactAndAllowWhileIdle() so they fire even when the device is in Doze mode. The exact-alarm permission (either USE_EXACT_ALARM for system apps or SCHEDULE_EXACT_ALARM with user approval) is declared in the manifest and checked at runtime through getPermissionState().

requestNotificationPermission() requests POST_NOTIFICATIONS on Android 13+. On Android 12 and earlier there is no runtime notification permission to request, so the call reports unsupported and the caller should open notification settings instead. openReadinessSettings(reason) opens notification, exact-alarm, or full-screen settings when the reported reason supports it. The settings action returns when Android accepts the deep link. Query readiness again after the app resumes.

Alarm delivery

When the scheduled time arrives WarmAlarmReceiver receives the com.andrew.alarm.ACTION_FIRE broadcast and starts WarmAlarmForegroundService. The service:

  • Plays audio via MediaPlayer (local file, asset, or system default ringtone)
  • Applies optional fade-in via a Handler-driven volume schedule
  • Posts the alarm notification with configurable Stop/Snooze actions
  • Optionally enforces volume if the device is set to silent

Wake-check

When WarmAlarmWakeCheck is configured, a secondary alarm is scheduled for checkDelay after the primary alarm fires. If ACTION_WAKE_CHECK_FIRE arrives without the user having dismissed the primary alarm, the alarm retriggers (up to maxRetriggers times at retriggerDelay intervals). ACTION_WAKE_CHECK_DISMISS cancels the follow-up chain.

A retrigger and the regular alarm are separate PendingIntents. AlarmManager matches on request code plus Intent.filterEquals, which ignores extras, so a shared identity let arming a retrigger re-target the pending next occurrence and let a dismiss cancel it. Only the retrigger carries a warm-alarm://<package>/alarm/<id>/retrigger data Uri; the regular alarm keeps the data-less identity it had before 0.1.2, so an alarm scheduled by an older install stays cancellable after an update. Finishing a wake check clears a recurring alarm's retrigger count and removes the stored schedule only for a one-shot alarm.

filterEquals cannot be exercised from the JVM unit tests, so verify it on a device: schedule a recurring alarm, let the wake check fire, tap the dismiss action, then confirm the next occurrence is still listed by adb shell dumpsys alarm | grep <package>.

Recurrence

Weekly recurrence is re-armed on fire: AlarmManager is single-shot, so when a recurring alarm fires, WarmAlarmReceiver computes the next matching weekday occurrence (WarmAlarmRecurrence.nextOccurrence, same time-of-day, strictly after now) and re-arms it, updating the stored scheduledAtMillis. The re-arm runs in the receiver without a Flutter engine, so the series survives even when the app process is dead. Dismissing an alarm ends only the current occurrence; cancelAlarm(id) tears down the series.

Boot persistence

WarmAlarmBootReceiver handles BOOT_COMPLETED and LOCKED_BOOT_COMPLETED broadcasts (via RECEIVE_BOOT_COMPLETED permission) to reschedule any alarms that were lost when the device restarted. It is directBootAware, so alarms scheduled before first unlock are restored from device-protected storage.

Kill warning

setKillWarning(title, body) posts a persistent, non-dismissible notification to discourage users from force-quitting the app before an alarm fires. Call clearKillWarning() to remove it.


Required permissions

The following permissions are declared in the plugin's AndroidManifest.xml and merged automatically into your app's manifest:

SCHEDULE_EXACT_ALARM       — exact alarm scheduling (user-granted on Android 12+)
USE_EXACT_ALARM            — exact alarm scheduling (system-granted for alarm-category apps)
RECEIVE_BOOT_COMPLETED     — reschedule alarms after device reboot
POST_NOTIFICATIONS         — show alarm and kill-warning notifications (Android 13+)
USE_FULL_SCREEN_INTENT     — launch the full-screen alarm UI over the lock screen (Android 14+)
VIBRATE                    — haptic feedback during alarm
FOREGROUND_SERVICE         — run WarmAlarmForegroundService
FOREGROUND_SERVICE_MEDIA_PLAYBACK — declare mediaPlayback foreground service type

Pigeon wire layer

All Dart ↔ Kotlin communication is generated by Pigeon. The schema lives in pigeons/messages.dart; generated outputs (Messages.g.kt, lib/src/messages.g.dart) are never hand-edited. Run melos run generate after any schema change.


License

BSD-3-Clause — Copyright (c) 2026, Dongmin Yu. See LICENSE for details.

Libraries

warm_alarm_android