From 9a2f3909cc83b2c12ffa8d2b32d2c21e6520fb3e Mon Sep 17 00:00:00 2001 From: Gautier de Lataillade <32983806+gdelataillade@users.noreply.github.com> Date: Fri, 13 Dec 2024 18:47:02 +0100 Subject: [PATCH] [Feature] Add allowAlarmOverlap parameter (#303) * Add allowAlarmOverlap param * Fix android check if alarm is already ringing --- .../gdelataillade/alarm/alarm/AlarmService.kt | 2 +- .../alarm/generated/FlutterBindings.g.kt | 7 ++- .../alarm/models/AlarmSettings.kt | 4 +- example/ios/Runner.xcodeproj/project.pbxproj | 6 +-- example/lib/screens/edit_alarm.dart | 1 + ios/Classes/api/AlarmApiImpl.swift | 8 +-- ios/Classes/generated/FlutterBindings.g.swift | 6 ++- ios/Classes/models/AlarmSettings.swift | 54 +++++++++++++++++-- lib/model/alarm_settings.dart | 12 ++++- lib/src/generated/platform_bindings.g.dart | 5 ++ pigeons/alarm_api.dart | 2 + 11 files changed, 90 insertions(+), 17 deletions(-) diff --git a/android/src/main/kotlin/com/gdelataillade/alarm/alarm/AlarmService.kt b/android/src/main/kotlin/com/gdelataillade/alarm/alarm/AlarmService.kt index 4efc5f2c..0ccf5786 100644 --- a/android/src/main/kotlin/com/gdelataillade/alarm/alarm/AlarmService.kt +++ b/android/src/main/kotlin/com/gdelataillade/alarm/alarm/AlarmService.kt @@ -112,7 +112,7 @@ class AlarmService : Service() { } // Check if an alarm is already ringing - if (ringingAlarmIds.isNotEmpty() && action != "STOP_ALARM") { + if (!alarmSettings.allowAlarmOverlap && ringingAlarmIds.isNotEmpty() && action != "STOP_ALARM") { Log.d("AlarmService", "An alarm is already ringing. Ignoring new alarm with id: $id") unsaveAlarm(id) return START_NOT_STICKY diff --git a/android/src/main/kotlin/com/gdelataillade/alarm/generated/FlutterBindings.g.kt b/android/src/main/kotlin/com/gdelataillade/alarm/generated/FlutterBindings.g.kt index 5138d6df..94b4591a 100644 --- a/android/src/main/kotlin/com/gdelataillade/alarm/generated/FlutterBindings.g.kt +++ b/android/src/main/kotlin/com/gdelataillade/alarm/generated/FlutterBindings.g.kt @@ -83,7 +83,8 @@ data class AlarmSettingsWire ( val loopAudio: Boolean, val vibrate: Boolean, val warningNotificationOnKill: Boolean, - val androidFullScreenIntent: Boolean + val androidFullScreenIntent: Boolean, + val allowAlarmOverlap: Boolean ) { companion object { @@ -97,7 +98,8 @@ data class AlarmSettingsWire ( val vibrate = pigeonVar_list[6] as Boolean val warningNotificationOnKill = pigeonVar_list[7] as Boolean val androidFullScreenIntent = pigeonVar_list[8] as Boolean - return AlarmSettingsWire(id, millisecondsSinceEpoch, assetAudioPath, volumeSettings, notificationSettings, loopAudio, vibrate, warningNotificationOnKill, androidFullScreenIntent) + val allowAlarmOverlap = pigeonVar_list[9] as Boolean + return AlarmSettingsWire(id, millisecondsSinceEpoch, assetAudioPath, volumeSettings, notificationSettings, loopAudio, vibrate, warningNotificationOnKill, androidFullScreenIntent, allowAlarmOverlap) } } fun toList(): List { @@ -111,6 +113,7 @@ data class AlarmSettingsWire ( vibrate, warningNotificationOnKill, androidFullScreenIntent, + allowAlarmOverlap, ) } } diff --git a/android/src/main/kotlin/com/gdelataillade/alarm/models/AlarmSettings.kt b/android/src/main/kotlin/com/gdelataillade/alarm/models/AlarmSettings.kt index 4ba3a903..dd147b8c 100644 --- a/android/src/main/kotlin/com/gdelataillade/alarm/models/AlarmSettings.kt +++ b/android/src/main/kotlin/com/gdelataillade/alarm/models/AlarmSettings.kt @@ -20,7 +20,8 @@ data class AlarmSettings( val loopAudio: Boolean, val vibrate: Boolean, val warningNotificationOnKill: Boolean, - val androidFullScreenIntent: Boolean + val androidFullScreenIntent: Boolean, + val allowAlarmOverlap: Boolean = false ) { companion object { fun fromWire(e: AlarmSettingsWire): AlarmSettings { @@ -34,6 +35,7 @@ data class AlarmSettings( e.vibrate, e.warningNotificationOnKill, e.androidFullScreenIntent, + e.allowAlarmOverlap ) } } diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index f9809977..a06ba510 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -378,7 +378,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = WQ65PJ26MP; + DEVELOPMENT_TEAM = 63LD84R3KS; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -514,7 +514,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = WQ65PJ26MP; + DEVELOPMENT_TEAM = 63LD84R3KS; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -542,7 +542,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = WQ65PJ26MP; + DEVELOPMENT_TEAM = 63LD84R3KS; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/example/lib/screens/edit_alarm.dart b/example/lib/screens/edit_alarm.dart index 6a3af1a2..11fd1733 100644 --- a/example/lib/screens/edit_alarm.dart +++ b/example/lib/screens/edit_alarm.dart @@ -123,6 +123,7 @@ class _ExampleAlarmEditScreenState extends State { assetAudioPath: assetAudio, warningNotificationOnKill: Platform.isIOS, volumeSettings: volumeSettings, + allowAlarmOverlap: true, notificationSettings: NotificationSettings( title: 'Alarm example', body: 'Your alarm ($id) is ringing', diff --git a/ios/Classes/api/AlarmApiImpl.swift b/ios/Classes/api/AlarmApiImpl.swift index 403e278c..59ae0efa 100644 --- a/ios/Classes/api/AlarmApiImpl.swift +++ b/ios/Classes/api/AlarmApiImpl.swift @@ -278,13 +278,13 @@ public class AlarmApiImpl: NSObject, AlarmApi { } private func handleAlarmAfterDelay(id: Int) { - if self.isAnyAlarmRingingExcept(id: id) { - NSLog("[SwiftAlarmPlugin] Ignoring alarm with id \(id) because another alarm is already ringing.") - self.unsaveAlarm(id: id) + guard let alarm = self.alarms[id], let audioPlayer = alarm.audioPlayer else { return } - guard let alarm = self.alarms[id], let audioPlayer = alarm.audioPlayer else { + if !alarm.settings.allowAlarmOverlap && self.isAnyAlarmRingingExcept(id: id) { + NSLog("[SwiftAlarmPlugin] Ignoring alarm with id \(id) because another alarm is already ringing.") + self.unsaveAlarm(id: id) return } diff --git a/ios/Classes/generated/FlutterBindings.g.swift b/ios/Classes/generated/FlutterBindings.g.swift index 6cc6eddc..7361fbed 100644 --- a/ios/Classes/generated/FlutterBindings.g.swift +++ b/ios/Classes/generated/FlutterBindings.g.swift @@ -95,6 +95,7 @@ struct AlarmSettingsWire { var vibrate: Bool var warningNotificationOnKill: Bool var androidFullScreenIntent: Bool + var allowAlarmOverlap: Bool // swift-format-ignore: AlwaysUseLowerCamelCase @@ -108,6 +109,7 @@ struct AlarmSettingsWire { let vibrate = pigeonVar_list[6] as! Bool let warningNotificationOnKill = pigeonVar_list[7] as! Bool let androidFullScreenIntent = pigeonVar_list[8] as! Bool + let allowAlarmOverlap = pigeonVar_list[9] as! Bool return AlarmSettingsWire( id: id, @@ -118,7 +120,8 @@ struct AlarmSettingsWire { loopAudio: loopAudio, vibrate: vibrate, warningNotificationOnKill: warningNotificationOnKill, - androidFullScreenIntent: androidFullScreenIntent + androidFullScreenIntent: androidFullScreenIntent, + allowAlarmOverlap: allowAlarmOverlap ) } func toList() -> [Any?] { @@ -132,6 +135,7 @@ struct AlarmSettingsWire { vibrate, warningNotificationOnKill, androidFullScreenIntent, + allowAlarmOverlap, ] } } diff --git a/ios/Classes/models/AlarmSettings.swift b/ios/Classes/models/AlarmSettings.swift index 7063d3ef..969b0b92 100644 --- a/ios/Classes/models/AlarmSettings.swift +++ b/ios/Classes/models/AlarmSettings.swift @@ -1,5 +1,3 @@ -import Foundation - struct AlarmSettings: Codable { let id: Int let dateTime: Date @@ -10,6 +8,53 @@ struct AlarmSettings: Codable { let vibrate: Bool let warningNotificationOnKill: Bool let androidFullScreenIntent: Bool + let allowAlarmOverlap: Bool + + enum CodingKeys: String, CodingKey { + case id, dateTime, assetAudioPath, volumeSettings, notificationSettings, + loopAudio, vibrate, warningNotificationOnKill, androidFullScreenIntent, + allowAlarmOverlap + } + + // Custom initializer to handle missing keys + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + id = try container.decode(Int.self, forKey: .id) + dateTime = try container.decode(Date.self, forKey: .dateTime) + assetAudioPath = try container.decode(String.self, forKey: .assetAudioPath) + volumeSettings = try container.decode(VolumeSettings.self, forKey: .volumeSettings) + notificationSettings = try container.decode(NotificationSettings.self, forKey: .notificationSettings) + loopAudio = try container.decode(Bool.self, forKey: .loopAudio) + vibrate = try container.decode(Bool.self, forKey: .vibrate) + warningNotificationOnKill = try container.decode(Bool.self, forKey: .warningNotificationOnKill) + androidFullScreenIntent = try container.decode(Bool.self, forKey: .androidFullScreenIntent) + allowAlarmOverlap = try container.decodeIfPresent(Bool.self, forKey: .allowAlarmOverlap) ?? false + } + + // Memberwise initializer + init( + id: Int, + dateTime: Date, + assetAudioPath: String, + volumeSettings: VolumeSettings, + notificationSettings: NotificationSettings, + loopAudio: Bool, + vibrate: Bool, + warningNotificationOnKill: Bool, + androidFullScreenIntent: Bool, + allowAlarmOverlap: Bool + ) { + self.id = id + self.dateTime = dateTime + self.assetAudioPath = assetAudioPath + self.volumeSettings = volumeSettings + self.notificationSettings = notificationSettings + self.loopAudio = loopAudio + self.vibrate = vibrate + self.warningNotificationOnKill = warningNotificationOnKill + self.androidFullScreenIntent = androidFullScreenIntent + self.allowAlarmOverlap = allowAlarmOverlap + } static func from(wire: AlarmSettingsWire) -> AlarmSettings { return AlarmSettings( @@ -21,7 +66,8 @@ struct AlarmSettings: Codable { loopAudio: wire.loopAudio, vibrate: wire.vibrate, warningNotificationOnKill: wire.warningNotificationOnKill, - androidFullScreenIntent: wire.androidFullScreenIntent + androidFullScreenIntent: wire.androidFullScreenIntent, + allowAlarmOverlap: wire.allowAlarmOverlap ) } -} +} \ No newline at end of file diff --git a/lib/model/alarm_settings.dart b/lib/model/alarm_settings.dart index 21656d6a..88d6bff4 100644 --- a/lib/model/alarm_settings.dart +++ b/lib/model/alarm_settings.dart @@ -20,6 +20,7 @@ class AlarmSettings { this.vibrate = true, this.warningNotificationOnKill = true, this.androidFullScreenIntent = true, + this.allowAlarmOverlap = false, }); /// Constructs an `AlarmSettings` instance from the given JSON data. @@ -38,7 +39,8 @@ class AlarmSettings { loopAudio = wire.loopAudio, vibrate = wire.vibrate, warningNotificationOnKill = wire.warningNotificationOnKill, - androidFullScreenIntent = wire.androidFullScreenIntent; + androidFullScreenIntent = wire.androidFullScreenIntent, + allowAlarmOverlap = false; /// Unique identifier assiocated with the alarm. Cannot be 0 or -1; final int id; @@ -103,6 +105,11 @@ class AlarmSettings { /// package. final bool androidFullScreenIntent; + /// Whether the alarm should ring if another alarm is already ringing. + /// + /// Defaults to `false`. + final bool allowAlarmOverlap; + /// Converts the `AlarmSettings` instance to a JSON object. Map toJson() => _$AlarmSettingsToJson(this); @@ -117,6 +124,7 @@ class AlarmSettings { vibrate: vibrate, warningNotificationOnKill: warningNotificationOnKill, androidFullScreenIntent: androidFullScreenIntent, + allowAlarmOverlap: allowAlarmOverlap, ); /// Creates a copy of `AlarmSettings` but with the given fields replaced with @@ -138,6 +146,7 @@ class AlarmSettings { String? notificationBody, bool? warningNotificationOnKill, bool? androidFullScreenIntent, + bool? allowAlarmOverlap, }) { return AlarmSettings( id: id ?? this.id, @@ -151,6 +160,7 @@ class AlarmSettings { warningNotificationOnKill ?? this.warningNotificationOnKill, androidFullScreenIntent: androidFullScreenIntent ?? this.androidFullScreenIntent, + allowAlarmOverlap: allowAlarmOverlap ?? this.allowAlarmOverlap, ); } } diff --git a/lib/src/generated/platform_bindings.g.dart b/lib/src/generated/platform_bindings.g.dart index 66bee744..0448ee15 100644 --- a/lib/src/generated/platform_bindings.g.dart +++ b/lib/src/generated/platform_bindings.g.dart @@ -57,6 +57,7 @@ class AlarmSettingsWire { this.vibrate = true, this.warningNotificationOnKill = true, this.androidFullScreenIntent = true, + this.allowAlarmOverlap = false, }); int id; @@ -77,6 +78,8 @@ class AlarmSettingsWire { bool androidFullScreenIntent; + bool allowAlarmOverlap; + Object encode() { return [ id, @@ -88,6 +91,7 @@ class AlarmSettingsWire { vibrate, warningNotificationOnKill, androidFullScreenIntent, + allowAlarmOverlap, ]; } @@ -103,6 +107,7 @@ class AlarmSettingsWire { vibrate: result[6]! as bool, warningNotificationOnKill: result[7]! as bool, androidFullScreenIntent: result[8]! as bool, + allowAlarmOverlap: result[9]! as bool, ); } } diff --git a/pigeons/alarm_api.dart b/pigeons/alarm_api.dart index 38e626b7..4188c62b 100644 --- a/pigeons/alarm_api.dart +++ b/pigeons/alarm_api.dart @@ -26,6 +26,7 @@ class AlarmSettingsWire { this.vibrate = true, this.warningNotificationOnKill = true, this.androidFullScreenIntent = true, + this.allowAlarmOverlap = false, }); final int id; @@ -37,6 +38,7 @@ class AlarmSettingsWire { final bool vibrate; final bool warningNotificationOnKill; final bool androidFullScreenIntent; + final bool allowAlarmOverlap; } class VolumeSettingsWire {