Skip to content

Conversation

@t-regbs
Copy link
Owner

@t-regbs t-regbs commented Jan 3, 2026

No description provided.

This commit introduces a major refactoring to centralize and improve alarm scheduling logic. The core change is the introduction of the `AlarmTimeCalculator` in the domain layer, which is now responsible for all alarm time calculations.

Key changes include:

- **feat: Add `AlarmTimeCalculator`**
  - Introduced `AlarmTimeCalculator` interface and its implementation to handle the logic of calculating alarm trigger times, including for repeating alarms and considering future dates.
  - This moves complex time calculation logic out of the Android-specific `AlarmNotificationScheduler` and into the shared `core` module.

- **refactor: Simplify `AlarmInteractor` and `AlarmNotificationScheduler`**
  - `AlarmInteractor.schedule` method signature changed from `schedule(alarm, reschedule)` to `schedule(alarm, timeInMillis)`. The interactor now schedules an alarm for a specific time, removing its responsibility for calculating that time.
  - The Android `AlarmNotificationScheduler` is heavily simplified. It no longer calculates *when* an alarm should fire but only schedules a `PendingIntent` for a given `timeInMillis`.
  - Added an `update(alarm)` method to the `AlarmInteractor` interface and its platform implementations.

- **refactor: Update Use Cases**
  - Use cases like `ScheduleAlarm`, `ScheduleNextAlarm`, `RescheduleFutureAlarms`, and `SnoozeAlarm` are updated to use `AlarmTimeCalculator` to determine alarm times before passing them to the `AlarmInteractor`.

- **refactor: Decouple `AlarmPermission`**
  - Abstracted Android framework dependencies (`AlarmManager`, `Context`) out of `AlarmPermissionImpl` by introducing `PermissionChecker`, `ScreenNavigator`, and `AndroidVersion` interfaces. This improves testability.

- **refactor: Replace GlobalScope in `AlarmReceiver`**
  - Replaced `GlobalScope` with an injected `AppCoroutineScope` to follow structured concurrency best practices.

- **test: Enhance and Adapt Tests**
  - Updated existing tests for ViewModels, use cases, and interactors to accommodate the new `AlarmTimeCalculator` and simplified scheduling logic.
  - Added comprehensive tests for the new `AlarmPermissionImpl` abstractions.
This commit refactors the iOS application's startup sequence and alarm notification logic for better performance and reliability.

Key changes include:
- **Early Koin Initialization**: Dependency injection is now initialized in the Swift `App.init()` before the UI loads, ensuring services are available sooner.
- **Background Database Prewarming**: The Room database is now prewarmed on a background thread after Koin initialization to prevent UI lag on first access.
- **Deferred Notification Permissions**: The notification permission request is now deferred until after the main UI appears, preventing it from blocking the app's startup.
- **Refined Notification Handling**:
    - The `IosAlarmScheduler` now registers notification categories (`Snooze`, `Dismiss`) upon initialization.
    - Notification content creation is updated to use the `TimeSensitive` interruption level, which is better suited for alarms than the previously used `Critical` level that requires special entitlements.
    - A new `AlarmAudioController.swift` has been added to manage the playback of alarm sounds when a notification is received in the foreground or tapped, ensuring the alarm sound plays correctly.
    - The `UNUserNotificationCenter` delegate method now explicitly requests to play the system sound as a fallback, in addition to showing the banner and badge.
…r notifications

This commit introduces a foreground service for handling alarms on Android, making them more reliable and resilient to app closures. It also improves notification handling on both Android and iOS.

### Android Changes
- **Introduced `AlarmService`**: A new foreground service to manage alarm playback, ensuring alarms continue to ring even if the app is closed or killed.
- **`AlarmTimingController`**: Extracted alarm timing logic (ring/pause/restart cycle) into a separate, testable class.
- **Improved Notification Handling**:
    - Notifications are now ongoing and cannot be swiped away.
    - Full-screen intents are re-triggered if the user navigates away from the alarm screen.
- **`ActiveAlarmManager`**: Tracks the active alarm to re-show the notification if the app is force-closed.
- **`PermissionChecker` Abstraction**: Created interfaces for checking permissions and navigating to system settings to improve testability.

### iOS Changes
- **`NotificationActionDelegate`**: Handles notification actions (snooze, dismiss) in a dedicated class.
- **`IosAlarmNotification`**: Manages the display and dismissal of delivered notifications.
- **`AlarmAudioController`**: A new Swift class to handle alarm audio playback and vibrations when the app is in the foreground.

### Core & Common Changes
- **`AppCoroutineScope`**: Introduced a shared coroutine scope for background tasks, replacing `GlobalScope`.
- **`AudioPlayer` Interface**: Added an `isPlaying` property to the common `AudioPlayer` interface.
This commit updates the GitHub Actions workflow, adds Robolectric for robust Android testing, and enhances the test suites for snoozing and alarm scheduling.

- **CI:**
    - Replaced the deprecated `android_build.yml` with a new `build.yml` workflow.
    - Upgraded to `actions/checkout@v4`, `actions/setup-java@v4`, and `gradle/actions/setup-gradle@v4`.
    - Added separate steps for running tests on `core` and `app` modules.
    - Implemented Gradle caching to improve workflow speed.
    - Added a step to build and upload the debug APK as a build artifact.

- **Testing:**
    - Added Robolectric to enable instrumentation tests in a local JVM, and configured it in `app/build.gradle.kts`.
    - Rewrote `AlarmNotificationSchedulerTest` using Robolectric to properly test `AlarmManager` scheduling and cancellation logic.
    - Overhauled `SnoozeAlarmTest` to use a fake `DateTimeProvider`, allowing for deterministic tests covering time rollovers (hour, day) and custom durations.
    - Injected `DateTimeProvider` into `AlarmTimeCalculatorImpl` to make time-based calculations more testable.

- **UI:**
    - Set the `imeAction` to `Done` on the alarm label text field for a better user experience.
This commit introduces a comprehensive suite of unit and integration-style tests for critical alarm scheduling, calculation, and persistence logic.

Key additions include:
- **`AlarmTimeCalculatorImplTest`**: Unit tests for the alarm time calculation logic, covering various scenarios such as non-repeating alarms, repeating alarms, day/month/year rollovers, and scheduling for past vs. future times.
- **`BootPersistenceTest`**: Integration-style tests to verify that alarms are correctly rescheduled after device boot or app updates. It tests scenarios for one-time, repeating, and disabled alarms.
- **`AlarmManagerExtensionsTest`**: Robolectric tests for the `setExactAlarm` and `cancelAlarm` extension functions, ensuring correct behavior with Android's `AlarmManager`.
- **`AlarmReceiverTest`**: Robolectric tests to validate the correct creation of intents with various actions (`ALARM_ACTION`, `SNOOZE_ACTION`, etc.) and boot-related events.
- **Test Fakes and Infrastructure**:
    - Added `DateTimeProviderFake` to allow for deterministic time-based testing.
    - Created a `TestApplication` for Robolectric tests to prevent Koin initialization conflicts.
@t-regbs t-regbs merged commit 42b028d into main Jan 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants