Skip to content

fix: implement notification channels on android and refactor interface#229

Merged
ahmet-cetinkaya merged 2 commits into
mainfrom
fix/android-notification-channels
Feb 6, 2026
Merged

fix: implement notification channels on android and refactor interface#229
ahmet-cetinkaya merged 2 commits into
mainfrom
fix/android-notification-channels

Conversation

@ahmet-cetinkaya

@ahmet-cetinkaya ahmet-cetinkaya commented Feb 6, 2026

Copy link
Copy Markdown
Owner

This PR fixes notification issues on modern Android devices (Galaxy S24+) by explicitly creating notification channels. It also refactors the INotificationService interface to use a NotificationOptions object for better maintainability and clean code.

Changes:

  • Android Notification Channels: Explicitly create 'Task Reminders' and 'Habit Reminders' channels.
  • Interface Refactor: Introduced 'NotificationOptions' to encapsulate platform-specific notification details.
  • Testability: Refactored 'MobileNotificationService' to allow dependency injection for robust unit testing.
  • Unit Tests: Added comprehensive tests for channel creation and usage.

Summary by Sourcery

Implement Android notification channels and refactor the notification service API for better platform handling and testability.

New Features:

  • Add explicit Android notification channels for task and habit reminders with high importance and alert settings.
  • Introduce NotificationOptions to encapsulate platform-specific notification configuration such as channel selection and action button text.

Enhancements:

  • Refactor MobileNotificationService to support dependency injection of the notifications plugin and platform flags, improving testability and platform-specific behavior.
  • Update notification permission checks to rely on injectable platform flags instead of direct Platform calls, enabling more flexible testing and platform overrides.

Tests:

  • Add unit tests to verify Android notification initialization, channel creation, and correct channel usage when showing notifications.
  • Extend MobileNotificationService tests to cover notification settings, permission flow, and channel selection for task vs habit notifications.

@ahmet-cetinkaya ahmet-cetinkaya added the bug Something isn't working label Feb 6, 2026
@ahmet-cetinkaya ahmet-cetinkaya self-assigned this Feb 6, 2026
@ahmet-cetinkaya ahmet-cetinkaya added the bug Something isn't working label Feb 6, 2026
@sourcery-ai

sourcery-ai Bot commented Feb 6, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements Android notification channels and a configurable notification options interface, while making MobileNotificationService more testable and adding unit tests for channel creation, channel selection, and existing task completion flows.

Sequence diagram for showing a notification with Android channels and options

sequenceDiagram
  actor AppLogic
  participant MobileNotificationService
  participant FlutterLocalNotificationsPlugin
  participant AndroidFlutterLocalNotificationsPlugin as AndroidPlugin

  AppLogic->>MobileNotificationService: show(title, body, payload, id, options)
  MobileNotificationService->>MobileNotificationService: isEnabled()
  alt notifications disabled
    MobileNotificationService-->>AppLogic: return
  else enabled
    MobileNotificationService->>MobileNotificationService: _checkPermission()
    alt permission not granted
      MobileNotificationService-->>AppLogic: return
    else permission granted
      MobileNotificationService->>MobileNotificationService: determine effectiveChannelId and effectiveChannelName
      MobileNotificationService->>FlutterLocalNotificationsPlugin: show(effectiveId, title, body, NotificationDetails(androidDetails))
      FlutterLocalNotificationsPlugin-->>MobileNotificationService: done
      MobileNotificationService-->>AppLogic: complete
    end
  end

  AppLogic->>MobileNotificationService: init()
  MobileNotificationService->>FlutterLocalNotificationsPlugin: initialize(InitializationSettings)
  MobileNotificationService->>MobileNotificationService: _createNotificationChannels()
  MobileNotificationService->>FlutterLocalNotificationsPlugin: resolvePlatformSpecificImplementation()
  FlutterLocalNotificationsPlugin-->>MobileNotificationService: AndroidPlugin
  MobileNotificationService->>AndroidPlugin: createNotificationChannel(taskChannel)
  MobileNotificationService->>AndroidPlugin: createNotificationChannel(habitChannel)
  AndroidPlugin-->>MobileNotificationService: channels created
  MobileNotificationService-->>AppLogic: init complete
Loading

Class diagram for updated notification services and options

classDiagram
  direction LR

  class INotificationService {
    <<interface>>
    +Future~void~ init()
    +Future~void~ show(title String, body String, payload String, id int, options NotificationOptions)
    +Future~void~ clearAll()
    +Future~bool~ isEnabled()
    +Future~void~ handleNotificationTaskCompletion(String taskId)
    +Future~void~ handleNotificationHabitCompletion(String habitId)
    +Future~bool~ checkPermissionStatus()
    +Future~bool~ requestPermission()
  }

  class NotificationOptions {
    +String actionButtonText
    +String channelId
    +NotificationOptions(actionButtonText String, channelId String)
  }

  class MobileNotificationService {
    -Mediator _mediator
    -FlutterLocalNotificationsPlugin _flutterLocalNotifications
    -bool _isAndroid
    -bool _isIOS
    +MobileNotificationService(Mediator mediator, FlutterLocalNotificationsPlugin flutterLocalNotifications, bool isAndroid, bool isIOS)
    +Future~void~ init()
    -Future~void~ _createNotificationChannels()
    +Future~void~ show(title String, body String, payload String, id int, options NotificationOptions)
    -Future~bool~ _checkPermission()
    +Future~bool~ checkPermissionStatus()
    +Future~bool~ requestPermission()
  }

  class DesktopNotificationService {
    +Future~void~ init()
    +Future~void~ show(title String, body String, payload String, id int, options NotificationOptions)
    +Future~void~ clearAll()
    +Future~bool~ isEnabled()
    +Future~void~ handleNotificationTaskCompletion(String taskId)
    +Future~void~ handleNotificationHabitCompletion(String habitId)
    +Future~bool~ checkPermissionStatus()
    +Future~bool~ requestPermission()
  }

  class FlutterLocalNotificationsPlugin {
    +Future~bool~ initialize(InitializationSettings settings)
    +Future~void~ show(int id, String title, String body, NotificationDetails details)
    +T resolvePlatformSpecificImplementation()
  }

  class AndroidFlutterLocalNotificationsPlugin {
    +Future~bool~ requestNotificationsPermission()
    +Future~void~ createNotificationChannel(AndroidNotificationChannel channel)
    +Future~NotificationAppLaunchDetails~ getNotificationAppLaunchDetails()
  }

  class AndroidNotificationChannel {
    +String id
    +String name
    +Importance importance
    +bool playSound
    +bool enableVibration
    +bool enableLights
  }

  class AndroidAppConstants_notificationChannels {
    +String taskChannelId
    +String taskChannelName
    +String habitChannelId
    +String habitChannelName
  }

  INotificationService <|.. MobileNotificationService
  INotificationService <|.. DesktopNotificationService

  MobileNotificationService --> FlutterLocalNotificationsPlugin : uses
  MobileNotificationService --> AndroidFlutterLocalNotificationsPlugin : resolves
  MobileNotificationService --> AndroidNotificationChannel : creates
  MobileNotificationService --> AndroidAppConstants_notificationChannels : reads ids and names
  MobileNotificationService --> NotificationOptions : reads options

  DesktopNotificationService --> NotificationOptions : reads options
Loading

File-Level Changes

Change Details Files
Add explicit Android notification channel creation and selection in MobileNotificationService, with platform flags to improve testability.
  • Extend MobileNotificationService constructor to accept an optional FlutterLocalNotificationsPlugin and explicit isAndroid/isIOS flags for dependency injection and tests.
  • Add init() flow to call a new _createNotificationChannels() helper that defines and registers task and habit AndroidNotificationChannel instances using AndroidAppConstants identifiers and names.
  • Update show() to compute an effective channelId/channelName (defaulting to the task channel, switching to habit channel when specified) and use these for AndroidNotificationDetails instead of the generic AppInfo name.
  • Refactor _checkPermission(), checkPermissionStatus(), and requestPermission() to use injected platform flags instead of direct Platform checks, with adjusted non‑mobile short‑circuit logic.
src/lib/infrastructure/mobile/features/notification/mobile_notification_service.dart
Refactor notification service interface to use NotificationOptions and propagate it to platform-specific implementations.
  • Change INotificationService.show() to accept a NotificationOptions? parameter instead of a raw actionButtonText argument and document it as platform-specific options.
  • Introduce NotificationOptions class with optional actionButtonText and channelId fields for cross‑platform configuration.
  • Update DesktopNotificationService.show() signature to use NotificationOptions and drive Linux action button configuration from options.actionButtonText when present.
src/lib/presentation/ui/shared/services/abstraction/i_notification_service.dart
src/lib/infrastructure/desktop/features/notification/desktop_notification_service.dart
Add and update unit tests to cover Android channel initialization, channel selection in show(), and existing task completion behavior.
  • Extend @GenerateMocks to include FlutterLocalNotificationsPlugin and AndroidFlutterLocalNotificationsPlugin and create corresponding mock fields in tests.
  • Add init group tests that verify initialization calls FlutterLocalNotificationsPlugin.initialize and creates both task and habit channels with correct IDs, names, and Importance.max.
  • Add show group tests that stub settings, permissions, and plugin behavior, and verify default task channel usage and explicit habit channel usage via NotificationOptions.channelId.
  • Retain and slightly simplify handleNotificationTaskCompletion tests to focus on mediator invocation while keeping error‑handling coverage minimal.
src/test/infrastructure/mobile/features/notification/mobile_notification_service_test.dart

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @ahmet-cetinkaya, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves critical notification delivery issues on modern Android devices by implementing explicit notification channels. It also significantly refactors the notification service interface and its mobile implementation to enhance maintainability, support platform-specific configurations more cleanly, and drastically improve the testability of notification-related logic through dependency injection.

Highlights

  • Android Notification Channels: Explicitly created 'Task Reminders' and 'Habit Reminders' channels with high importance, play sound, vibration, and lights enabled to address notification issues on modern Android devices.
  • Interface Refactoring: The INotificationService interface now uses a NotificationOptions object to encapsulate platform-specific notification details like actionButtonText and channelId, improving maintainability and clean code.
  • Improved Testability: The MobileNotificationService has been refactored to allow dependency injection for FlutterLocalNotificationsPlugin and platform flags (isAndroid, isIOS), enabling robust unit testing and flexible platform overrides.
  • New Unit Tests: Comprehensive unit tests have been added to verify notification initialization, correct channel creation, and proper channel usage when showing notifications, covering both default and explicit channel selections.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/lib/infrastructure/desktop/features/notification/desktop_notification_service.dart
    • Updated the show method signature to accept a NotificationOptions object instead of a direct actionButtonText parameter.
    • Modified the Linux notification details to conditionally use the actionButtonText from the new NotificationOptions object.
  • src/lib/infrastructure/mobile/features/notification/mobile_notification_service.dart
    • Refactored the constructor to allow dependency injection for FlutterLocalNotificationsPlugin, isAndroid, and isIOS flags, improving testability.
    • Introduced a new private method _createNotificationChannels to explicitly create 'Task Reminders' and 'Habit Reminders' Android notification channels during service initialization.
    • Updated the show method to utilize the new NotificationOptions object and dynamically select the appropriate Android notification channel (task or habit) based on the provided channelId, defaulting to the task channel if none is specified.
    • Modified permission checking methods (_checkPermission, checkPermissionStatus, requestPermission) to use the injected platform flags (_isAndroid, _isIOS) instead of direct Platform calls, further enhancing testability.
  • src/lib/presentation/ui/shared/services/abstraction/i_notification_service.dart
    • Updated the show method signature in the INotificationService interface to accept a NotificationOptions object.
    • Defined a new NotificationOptions class to encapsulate optional notification parameters such as actionButtonText and channelId.
  • src/test/infrastructure/mobile/features/notification/mobile_notification_service_test.dart
    • Added new mocks for FlutterLocalNotificationsPlugin and AndroidFlutterLocalNotificationsPlugin to facilitate comprehensive testing.
    • Introduced new test cases within the init group to verify the successful initialization of notifications and the creation of both 'Task Reminders' and 'Habit Reminders' Android notification channels.
    • Added new test cases within the show group to confirm that notifications correctly utilize the default task channel when no specific channel is provided, and the habit channel when explicitly requested via NotificationOptions.
Activity
  • No specific review activity or comments have been recorded in the provided context.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In MobileNotificationService.requestPermission, the early-return condition still uses Platform.isIOS instead of the injected _isIOS flag, which undermines the testability / platform override pattern you introduced elsewhere; consider using the injected flags consistently for all platform checks.
  • In MobileNotificationService.show, you unconditionally derive effectiveChannelId/effectiveChannelName from AndroidAppConstants and always construct AndroidNotificationDetails; it would be cleaner (and safer for future platforms) to guard this Android-specific logic behind _isAndroid and keep the non-Android path free of Android constants/types.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `MobileNotificationService.requestPermission`, the early-return condition still uses `Platform.isIOS` instead of the injected `_isIOS` flag, which undermines the testability / platform override pattern you introduced elsewhere; consider using the injected flags consistently for all platform checks.
- In `MobileNotificationService.show`, you unconditionally derive `effectiveChannelId`/`effectiveChannelName` from `AndroidAppConstants` and always construct `AndroidNotificationDetails`; it would be cleaner (and safer for future platforms) to guard this Android-specific logic behind `_isAndroid` and keep the non-Android path free of Android constants/types.

## Individual Comments

### Comment 1
<location> `src/lib/infrastructure/mobile/features/notification/mobile_notification_service.dart:33-34` </location>
<code_context>
+        _isAndroid = isAndroid ?? Platform.isAndroid,
+        _isIOS = isIOS ?? Platform.isIOS;

   @override
   Future<void> init() async {
</code_context>

<issue_to_address>
**issue (bug_risk):** Use `_isIOS` in `checkPermissionStatus` instead of `Platform.isIOS` for consistency with the injectable platform flags.

Since `checkPermissionStatus` now has injectable `_isAndroid`/`_isIOS` flags and the Android branch already uses `_isAndroid`, the iOS branch should use `_isIOS` instead of `Platform.isIOS`. Otherwise, overrides of `_isIOS` (e.g., in tests) won’t affect this code path, making behavior inconsistent with `_checkPermission` and `requestPermission`. Please switch the iOS check to `_isIOS` for consistent, predictable behavior when forcing platform flags.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request successfully implements Android notification channels and refactors the INotificationService interface to use a NotificationOptions object. This significantly improves the maintainability and clarity of the notification service, especially for handling platform-specific details. The introduction of dependency injection for platform flags in MobileNotificationService is a great step towards improving testability, and the new unit tests adequately cover the added functionality. Overall, the changes are well-structured and address the stated goals effectively.

Future<bool> checkPermissionStatus() async {
if (!PlatformUtils.isMobile) {
// Override platform check if flags are forced (testing)
if (!_isAndroid && !_isIOS && !PlatformUtils.isMobile) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The condition !_isAndroid && !_isIOS && !PlatformUtils.isMobile seems a bit redundant. If _isAndroid and _isIOS are false, and PlatformUtils.isMobile is also false, then it's already a non-mobile platform. You could simplify this to !PlatformUtils.isMobile if _isAndroid and _isIOS are guaranteed to be false when PlatformUtils.isMobile is false, or clarify the specific testing scenario where this full condition is necessary.

Suggested change
if (!_isAndroid && !_isIOS && !PlatformUtils.isMobile) {
if (!PlatformUtils.isMobile && !_isAndroid && !_isIOS) {

Future<bool> requestPermission() async {
if (!PlatformUtils.isMobile && !Platform.isIOS) {
// Override platform check if flags are forced (testing)
if (!_isAndroid && !_isIOS && !PlatformUtils.isMobile && !Platform.isIOS) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the checkPermissionStatus method, the condition !_isAndroid && !_isIOS && !PlatformUtils.isMobile && !Platform.isIOS is quite verbose. Given that _isIOS is already a flag for Platform.isIOS, the last !Platform.isIOS part is redundant. Consider simplifying this condition for better readability, while still maintaining the testability override.

Suggested change
if (!_isAndroid && !_isIOS && !PlatformUtils.isMobile && !Platform.isIOS) {
if (!PlatformUtils.isMobile && !_isAndroid && !_isIOS) {

@ahmet-cetinkaya

Copy link
Copy Markdown
Owner Author

PR Review: #229 - fix: implement notification channels on android and refactor interface

Overall Assessment

This PR implements Android notification channels correctly and follows Clean Architecture principles. The import data migration and command handling demonstrate excellent error handling practices. However, there are several critical issues in the notification services that need to be addressed before merging.

Recommendation: Request changes - Fix critical issues first


Critical Issues (Must Fix)

1. Silent Failures in isEnabled() Methods

Files:

  • lib/infrastructure/mobile/features/notification/mobile_notification_service.dart:191-204
  • lib/infrastructure/desktop/features/notification/desktop_notification_service.dart:182-197

Issue: Both isEnabled() methods return true when any error occurs, hiding database connection failures, mediator injection failures, and other critical errors.

} catch (e) {
  return true; // Default to true if error occurs - PROBLEMATIC!
}

Impact: Users expect notifications but don't receive them. No indication anything is wrong. App appears functional while silently failing.

Fix: Return false on error (safer default) and add proper error logging:

} catch (e, stackTrace) {
  Logger.error(
    '[notification_check_failed] Failed to check if notifications are enabled, defaulting to disabled',
    error: e,
    stackTrace: stackTrace,
  );
  return false; // Default to FALSE on error
}

2. No Error Handling in Notification Channel Creation

File: lib/infrastructure/mobile/features/notification/mobile_notification_service.dart:44-72

Issue: _createNotificationChannels() has no try-catch, no null check for androidImplementation, and no verification that channels were actually created.

Impact: Notifications may not appear on Android with no error logging to diagnose the issue.

Fix: Add comprehensive error handling with logging for each channel creation attempt.


3. Test Bug: Habit Channel Not Verified

File: test/infrastructure/mobile/features/notification/mobile_notification_service_test.dart:62-72

Issue: Copy-paste error - test verifies task channel twice instead of checking habit channel:

verify(mockAndroidFlutterLocalNotificationsPlugin.createNotificationChannel(argThat(
    predicate<AndroidNotificationChannel>((channel) =>
        channel.id == 'whph_task_reminders' &&  // BUG: Should be 'whph_habit_reminders'
        channel.name == 'Task Reminders')))).called(1);

Fix: Second verification should check for habit channel properties.


Important Issues (Should Fix)

4. Inconsistent Error Handling Between Desktop and Mobile

Files:

  • lib/infrastructure/desktop/features/notification/desktop_notification_service.dart:159-161
  • lib/infrastructure/mobile/features/notification/mobile_notification_service.dart

Issue: Desktop service catches errors in show() method but mobile service lets exceptions propagate.

Recommendation: Both should catch errors for better UX consistency.


5. Missing StackTrace in Error Logging

Multiple locations:

  • mobile_notification_service.dart:272-274
  • desktop_notification_service.dart:108-111, 223-225, 250-253

Issue: Using Logger.error('message: $e') without stackTrace parameter and without error: parameter.

Fix: Use proper Logger.error pattern:

Logger.error(
  '[permission_request_failed] Failed to request notification permission',
  error: e,
  stackTrace: stackTrace,
);

6. Unreachable Code in isEnabled()

File: lib/infrastructure/mobile/features/notification/mobile_notification_service.dart:242

Issue: Redundant platform check: !Platform.isIOS && !PlatformUtils.isMobile. If _isAndroid and _isIOS are both false (the condition before this check), then PlatformUtils.isMobile would also be false.


Type Design Issues

7. NotificationOptions Lacks Type Safety

File: lib/presentation/ui/shared/services/abstraction/i_notification_service.dart:51-59

Issue:

  • No validation that channelId matches registered channels
  • String-based channel selection is error-prone (typos possible)
  • Platform-specific coupling is implicit

Recommendation: Consider using an enum for channel IDs:

enum NotificationChannel {
  task('whph_task_reminders', 'Task Reminders'),
  habit('whph_habit_reminders', 'Habit Reminders');
  
  final String id;
  final String name;
  const NotificationChannel(this.id, this.name);
}

Test Coverage Gaps

Missing Test Scenarios:

  1. Channel creation failures on Android
  2. Permission denied flow
  3. Notifications disabled behavior
  4. iOS-specific notification behavior
  5. Foreign key violation detection during import
  6. Backup creation failure in production (currently untestable due to kDebugMode)

Positive Observations

Excellent aspects of this PR:

  1. ✅ Clean Architecture adherence - interface refactor properly maintains abstraction
  2. ✅ Comprehensive test additions for notification channels
  3. ✅ Constants usage - proper use of AndroidAppConstants for channel IDs
  4. ✅ Dependency injection - platform flags properly injected for testability
  5. Import data command error handling - exemplary data validation and error recovery
  6. Migration service - distinguishes expected vs unexpected errors properly
  7. Completion handlers - proper error IDs and stack traces (this pattern should be followed in notification services)

Recommended Action Plan

  1. Fix the test bug (habit channel verification)
  2. Fix isEnabled() to return false on error in both services
  3. Add error handling to _createNotificationChannels()
  4. Add stackTrace to all Logger.error calls
  5. Consider type-safe enum for channel IDs (can be follow-up)
  6. Add missing test scenarios (can be follow-up)

Files Requiring Changes

  • lib/infrastructure/mobile/features/notification/mobile_notification_service.dart
  • lib/infrastructure/desktop/features/notification/desktop_notification_service.dart
  • test/infrastructure/mobile/features/notification/mobile_notification_service_test.dart

…service

- Fix isEnabled() to return false on error instead of true (safer default)
- Add error handling to _createNotificationChannels() with proper logging
- Add stackTrace parameter to all Logger.error calls
- Use _isIOS consistently in checkPermissionStatus() for testability
- Simplify redundant platform checks in checkPermissionStatus() and requestPermission()
- Guard Android-specific notification logic behind _isAndroid in show()
- Add iOS permission check in checkPermissionStatus()

These changes improve error handling, consistency, and testability
of the mobile notification service.
@ahmet-cetinkaya ahmet-cetinkaya merged commit 7941ed6 into main Feb 6, 2026
4 checks passed
@ahmet-cetinkaya ahmet-cetinkaya deleted the fix/android-notification-channels branch May 22, 2026 20:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant