Skip to content

Commit

Permalink
Add lever to turn off iOS background silent audio (#312)
Browse files Browse the repository at this point in the history
* Add lever to turn off iOS background silent audio & fix time data type errors

* Revert temp changes in example
  • Loading branch information
orkun1675 authored Dec 30, 2024
1 parent e5ed54c commit 859357f
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ data class AlarmSettingsWire (
val vibrate: Boolean,
val warningNotificationOnKill: Boolean,
val androidFullScreenIntent: Boolean,
val allowAlarmOverlap: Boolean
val allowAlarmOverlap: Boolean,
val iOSBackgroundAudio: Boolean
)
{
companion object {
Expand All @@ -99,7 +100,8 @@ data class AlarmSettingsWire (
val warningNotificationOnKill = pigeonVar_list[7] as Boolean
val androidFullScreenIntent = pigeonVar_list[8] as Boolean
val allowAlarmOverlap = pigeonVar_list[9] as Boolean
return AlarmSettingsWire(id, millisecondsSinceEpoch, assetAudioPath, volumeSettings, notificationSettings, loopAudio, vibrate, warningNotificationOnKill, androidFullScreenIntent, allowAlarmOverlap)
val iOSBackgroundAudio = pigeonVar_list[10] as Boolean
return AlarmSettingsWire(id, millisecondsSinceEpoch, assetAudioPath, volumeSettings, notificationSettings, loopAudio, vibrate, warningNotificationOnKill, androidFullScreenIntent, allowAlarmOverlap, iOSBackgroundAudio)
}
}
fun toList(): List<Any?> {
Expand All @@ -114,6 +116,7 @@ data class AlarmSettingsWire (
warningNotificationOnKill,
androidFullScreenIntent,
allowAlarmOverlap,
iOSBackgroundAudio,
)
}
}
Expand Down
42 changes: 21 additions & 21 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ packages:
dependency: transitive
description:
name: collection
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.19.0"
version: "1.18.0"
cupertino_icons:
dependency: "direct main"
description:
Expand Down Expand Up @@ -123,18 +123,18 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06"
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
url: "https://pub.dev"
source: hosted
version: "10.0.7"
version: "10.0.5"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379"
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
url: "https://pub.dev"
source: hosted
version: "3.0.8"
version: "3.0.5"
leak_tracker_testing:
dependency: transitive
description:
Expand Down Expand Up @@ -267,26 +267,26 @@ packages:
dependency: transitive
description:
name: shared_preferences
sha256: "95f9997ca1fb9799d494d0cb2a780fd7be075818d59f00c43832ed112b158a82"
sha256: "3c7e73920c694a436afaf65ab60ce3453d91f84208d761fbd83fc21182134d93"
url: "https://pub.dev"
source: hosted
version: "2.3.3"
version: "2.3.4"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "7f172d1b06de5da47b6264c2692ee2ead20bbbc246690427cdb4fc301cd0c549"
sha256: "02a7d8a9ef346c9af715811b01fbd8e27845ad2c41148eefd31321471b41863d"
url: "https://pub.dev"
source: hosted
version: "2.3.4"
version: "2.4.0"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d"
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
url: "https://pub.dev"
source: hosted
version: "2.5.3"
version: "2.5.4"
shared_preferences_linux:
dependency: transitive
description:
Expand Down Expand Up @@ -323,7 +323,7 @@ packages:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
version: "0.0.99"
source_span:
dependency: transitive
description:
Expand All @@ -336,10 +336,10 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
source: hosted
version: "1.12.0"
version: "1.11.1"
stream_channel:
dependency: transitive
description:
Expand All @@ -352,10 +352,10 @@ packages:
dependency: transitive
description:
name: string_scanner
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3"
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "1.2.0"
term_glyph:
dependency: transitive
description:
Expand All @@ -368,10 +368,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
url: "https://pub.dev"
source: hosted
version: "0.7.3"
version: "0.7.2"
url_launcher:
dependency: "direct main"
description:
Expand Down Expand Up @@ -456,10 +456,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
url: "https://pub.dev"
source: hosted
version: "14.3.0"
version: "14.2.5"
web:
dependency: transitive
description:
Expand Down
16 changes: 11 additions & 5 deletions ios/Classes/api/AlarmApiImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public class AlarmApiImpl: NSObject, AlarmApi {

audioPlayer.prepareToPlay()

if !self.playSilent {
if !self.playSilent && alarmSettings.iOSBackgroundAudio {
self.startSilentSound()
}

Expand Down Expand Up @@ -312,7 +312,7 @@ public class AlarmApiImpl: NSObject, AlarmApi {

if !alarm.settings.loopAudio {
let audioDuration = audioPlayer.duration
DispatchQueue.main.asyncAfter(deadline: .now() + audioDuration) {
DispatchQueue.main.asyncAfter(deadline: .now() + audioDuration.toDispatchInterval()) {
self.stopAlarmInternal(id: id, cancelNotif: false)
}
}
Expand Down Expand Up @@ -349,7 +349,7 @@ public class AlarmApiImpl: NSObject, AlarmApi {
private func triggerVibrations() {
if !self.vibratingAlarms.isEmpty && self.isDevice {
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
self.triggerVibrations()
}
}
Expand All @@ -363,7 +363,7 @@ public class AlarmApiImpl: NSObject, AlarmApi {
public func setVolume(volume: Float, enable: Bool) {
let volumeView = MPVolumeView()

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .milliseconds(100)) {
if let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider {
self.previousVolume = enable ? slider.value : nil
print("[SwiftAlarmPlugin] Setting system volume to \(volume).")
Expand Down Expand Up @@ -394,7 +394,7 @@ public class AlarmApiImpl: NSObject, AlarmApi {
let targetVolume = Float(nextStep.volume)

// Schedule the fade using setVolume for a smooth transition
DispatchQueue.main.asyncAfter(deadline: now + startTime) {
DispatchQueue.main.asyncAfter(deadline: now + startTime.toDispatchInterval()) {
if !audioPlayer.isPlaying {
return
}
Expand Down Expand Up @@ -483,3 +483,9 @@ public class AlarmApiImpl: NSObject, AlarmApi {
}
}
}

extension TimeInterval {
func toDispatchInterval() -> DispatchTimeInterval {
return DispatchTimeInterval.milliseconds(Int(self * 1_000))
}
}
6 changes: 5 additions & 1 deletion ios/Classes/generated/FlutterBindings.g.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ struct AlarmSettingsWire {
var warningNotificationOnKill: Bool
var androidFullScreenIntent: Bool
var allowAlarmOverlap: Bool
var iOSBackgroundAudio: Bool


// swift-format-ignore: AlwaysUseLowerCamelCase
Expand All @@ -110,6 +111,7 @@ struct AlarmSettingsWire {
let warningNotificationOnKill = pigeonVar_list[7] as! Bool
let androidFullScreenIntent = pigeonVar_list[8] as! Bool
let allowAlarmOverlap = pigeonVar_list[9] as! Bool
let iOSBackgroundAudio = pigeonVar_list[10] as! Bool

return AlarmSettingsWire(
id: id,
Expand All @@ -121,7 +123,8 @@ struct AlarmSettingsWire {
vibrate: vibrate,
warningNotificationOnKill: warningNotificationOnKill,
androidFullScreenIntent: androidFullScreenIntent,
allowAlarmOverlap: allowAlarmOverlap
allowAlarmOverlap: allowAlarmOverlap,
iOSBackgroundAudio: iOSBackgroundAudio
)
}
func toList() -> [Any?] {
Expand All @@ -136,6 +139,7 @@ struct AlarmSettingsWire {
warningNotificationOnKill,
androidFullScreenIntent,
allowAlarmOverlap,
iOSBackgroundAudio,
]
}
}
Expand Down
16 changes: 12 additions & 4 deletions ios/Classes/models/AlarmSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ struct AlarmSettings: Codable {
let warningNotificationOnKill: Bool
let androidFullScreenIntent: Bool
let allowAlarmOverlap: Bool
let iOSBackgroundAudio: Bool

enum CodingKeys: String, CodingKey {
case id, dateTime, assetAudioPath, volumeSettings, notificationSettings,
loopAudio, vibrate, warningNotificationOnKill, androidFullScreenIntent,
allowAlarmOverlap, volume, fadeDuration, volumeEnforced
allowAlarmOverlap, iOSBackgroundAudio, volume, fadeDuration, volumeEnforced
}

/// Custom initializer to handle backward compatibility for older models
Expand All @@ -34,6 +35,9 @@ struct AlarmSettings: Codable {

// Backward compatibility for `allowAlarmOverlap`
allowAlarmOverlap = try container.decodeIfPresent(Bool.self, forKey: .allowAlarmOverlap) ?? false

// Backward compatibility for `iOSBackgroundAudio`
iOSBackgroundAudio = try container.decodeIfPresent(Bool.self, forKey: .iOSBackgroundAudio) ?? true

// Backward compatibility for `volumeSettings`
if let volumeSettingsDecoded = try? container.decode(VolumeSettings.self, forKey: .volumeSettings) {
Expand Down Expand Up @@ -68,6 +72,7 @@ struct AlarmSettings: Codable {
try container.encode(warningNotificationOnKill, forKey: .warningNotificationOnKill)
try container.encode(androidFullScreenIntent, forKey: .androidFullScreenIntent)
try container.encode(allowAlarmOverlap, forKey: .allowAlarmOverlap)
try container.encode(iOSBackgroundAudio, forKey: .iOSBackgroundAudio)
}

/// Memberwise initializer
Expand All @@ -81,7 +86,8 @@ struct AlarmSettings: Codable {
vibrate: Bool,
warningNotificationOnKill: Bool,
androidFullScreenIntent: Bool,
allowAlarmOverlap: Bool
allowAlarmOverlap: Bool,
iOSBackgroundAudio: Bool
) {
self.id = id
self.dateTime = dateTime
Expand All @@ -93,6 +99,7 @@ struct AlarmSettings: Codable {
self.warningNotificationOnKill = warningNotificationOnKill
self.androidFullScreenIntent = androidFullScreenIntent
self.allowAlarmOverlap = allowAlarmOverlap
self.iOSBackgroundAudio = iOSBackgroundAudio
}

/// Converts from wire model to `AlarmSettings`.
Expand All @@ -107,7 +114,8 @@ struct AlarmSettings: Codable {
vibrate: wire.vibrate,
warningNotificationOnKill: wire.warningNotificationOnKill,
androidFullScreenIntent: wire.androidFullScreenIntent,
allowAlarmOverlap: wire.allowAlarmOverlap
allowAlarmOverlap: wire.allowAlarmOverlap,
iOSBackgroundAudio: wire.iOSBackgroundAudio
)
}
}
}
29 changes: 22 additions & 7 deletions lib/model/alarm_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class AlarmSettings {
this.warningNotificationOnKill = true,
this.androidFullScreenIntent = true,
this.allowAlarmOverlap = false,
this.iOSBackgroundAudio = true,
});

/// Constructs an `AlarmSettings` instance from the given JSON data.
Expand Down Expand Up @@ -49,6 +50,9 @@ class AlarmSettings {
// Default `allowAlarmOverlap` to false for v4
json['allowAlarmOverlap'] = json['allowAlarmOverlap'] ?? false;

// Default `iOSBackgroundAudio` to true for v4
json['iOSBackgroundAudio'] = json['iOSBackgroundAudio'] ?? true;

// Adjust `dateTime` field for v4
if (json['dateTime'] != null) {
if (json['dateTime'] is int) {
Expand Down Expand Up @@ -83,7 +87,8 @@ class AlarmSettings {
vibrate = wire.vibrate,
warningNotificationOnKill = wire.warningNotificationOnKill,
androidFullScreenIntent = wire.androidFullScreenIntent,
allowAlarmOverlap = wire.allowAlarmOverlap;
allowAlarmOverlap = wire.allowAlarmOverlap,
iOSBackgroundAudio = wire.iOSBackgroundAudio;

/// Unique identifier associated with the alarm. Cannot be 0 or -1.
final int id;
Expand Down Expand Up @@ -153,6 +158,19 @@ class AlarmSettings {
/// Defaults to `false`.
final bool allowAlarmOverlap;

/// iOS apps are killed if they remain inactive in the background. Android
/// does not have this limitation due to native AlarmManager support.
///
/// This flag controls whether a silent audio player should start playing when
/// there is an active alarm. Apps that already have background activity can
/// set this to `false` to conserve battery.
///
/// DO NOT set this to `false` unless you are certain. Otherwise your alarms
/// may not ring!
///
/// Defaults to `true`. Has no effect on Android.
final bool iOSBackgroundAudio;

/// Converts the `AlarmSettings` instance to a JSON object.
Map<String, dynamic> toJson() => _$AlarmSettingsToJson(this);

Expand All @@ -168,6 +186,7 @@ class AlarmSettings {
warningNotificationOnKill: warningNotificationOnKill,
androidFullScreenIntent: androidFullScreenIntent,
allowAlarmOverlap: allowAlarmOverlap,
iOSBackgroundAudio: iOSBackgroundAudio,
);

/// Creates a copy of `AlarmSettings` but with the given fields replaced with
Expand All @@ -190,6 +209,7 @@ class AlarmSettings {
bool? warningNotificationOnKill,
bool? androidFullScreenIntent,
bool? allowAlarmOverlap,
bool? iOSBackgroundAudio,
}) {
return AlarmSettings(
id: id ?? this.id,
Expand All @@ -204,12 +224,7 @@ class AlarmSettings {
androidFullScreenIntent:
androidFullScreenIntent ?? this.androidFullScreenIntent,
allowAlarmOverlap: allowAlarmOverlap ?? this.allowAlarmOverlap,
iOSBackgroundAudio: iOSBackgroundAudio ?? this.iOSBackgroundAudio,
);
}

static DateTime _dateTimeFromJson(int millisecondsSinceEpoch) =>
DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch);

static int _dateTimeToJson(DateTime dateTime) =>
dateTime.millisecondsSinceEpoch;
}
Loading

0 comments on commit 859357f

Please sign in to comment.