migrate timer to ForegroundService for background survival

Move CountDownTimer logic from a Dialog into a ForegroundService so
the timer survives when the user tabs away or the activity is
destroyed. The activity becomes a thin UI projection bound to the
service via TimerStateListener callbacks.

- Create CountdownService with notification, state persistence,
  and ToneGenerator/Vibrator ownership
- Persist timer state to SharedPreferences for process death recovery
- Add notification with progress bar and Stop/Restart actions
- Embed countdown UI in activity_main.xml alongside picker UI
- Delete CountdownDialog and dialog_countdown.xml
- Update AndroidManifest with foreground service permissions
This commit is contained in:
2026-06-23 01:49:11 +00:00
parent da29e51993
commit d7ce314c22
9 changed files with 697 additions and 438 deletions
+11 -8
View File
@@ -17,16 +17,19 @@
- **Android Patterns**: Use `findViewById` for view binding in this project to maintain consistency with existing simple architecture.
## Architecture
- **Pattern**: Simple Activity-Dialog architecture.
- **State**: The countdown state is managed locally within `CountdownDialog` using `android.os.CountDownTimer`.
- **Pattern**: Activity-Service architecture with `ForegroundService`.
- **State**: The countdown state is managed in `CountdownService` using `android.os.CountDownTimer`, persisted to `SharedPreferences` for process death recovery.
- **UI**: `MainActivity` binds to the service and acts as a thin UI projection, receiving state updates via `TimerStateListener` callback.
- **Dependency Management**: Gradle with Groovy DSL.
- **Key Constraints**:
- The `CountdownDialog` is explicitly set to `setCancelable(false)` and `setCanceledOnTouchOutside(false)` to ensure the timer completes.
- The notification is set to `setOngoing(true)` — cannot be swiped away.
- Timer state survives process death via `SharedPreferences` persistence.
## Critical Files Reference
- `app/src/main/java/com/example/countdowntimer/MainActivity.kt`: Entry point and trigger for the timer.
- `app/src/main/java/com/example/countdowntimer/CountdownDialog.kt`: Implementation of the countdown logic and `ToneGenerator` trigger.
- `app/src/main/java/com/example/countdowntimer/MainActivity.kt`: Entry point, binds to service, implements `TimerStateListener`.
- `app/src/main/java/com/example/countdowntimer/CountdownService.kt`: ForegroundService owning timer logic, `ToneGenerator`, `Vibrator`, notification, and state persistence.
- `app/src/main/java/com/example/countdowntimer/TimerStateListener.kt`: Callback interface for service-to-activity state updates.
- `app/src/main/java/com/example/countdowntimer/CountdownCanvasView.kt`: Custom view for the circular progress ring and countdown text.
- `app/src/main/res/layout/activity_main.xml`: Main screen layout.
- `app/src/main/res/layout/dialog_countdown.xml`: Timer dialog layout.
- `app/build.gradle`: Build configuration, SDK versions (minSdk 24, targetSdk 34), and dependencies.
- `app/src/main/res/layout/activity_main.xml`: Main screen layout (contains both picker and countdown UI).
- `app/src/main/AndroidManifest.xml`: Declares service and foreground service permissions.
- `app/build.gradle`: Build configuration, SDK versions (minSdk 34, targetSdk 34), and dependencies.