Skip to content

Commit

Permalink
Implement stopAll natively
Browse files Browse the repository at this point in the history
  • Loading branch information
orkun1675 committed Dec 10, 2024
1 parent 767380f commit eda2149
Show file tree
Hide file tree
Showing 15 changed files with 162 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Add support for fading the alarm volume using a staircase function.
* Fixes a bug where `isRinging` might return FALSE immediately after alarm starts to ring.
* Handles alarm events on the platform side, increasing efficiency.
* Handles `stopAll` on the platform side for improved reliability.

## 4.1.1
* [Android] Show app on lock screen when alarm rings.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ class AlarmApiImpl(private val context: Context) : AlarmApi {
}
}

override fun stopAll() {
for (alarm in AlarmStorage(context).getSavedAlarms()) {
stopAlarm(alarm.id.toLong())
}
for (alarmId in alarmIds.toList()) {
stopAlarm(alarmId.toLong())
}
}

override fun isRinging(alarmId: Long?): Boolean {
val ringingAlarmIds = AlarmService.ringingAlarmIds
if (alarmId == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// Autogenerated from Pigeon (v22.6.3), do not edit directly.
// Autogenerated from Pigeon (v22.7.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")


import android.util.Log
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMethodCodec
import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
Expand Down Expand Up @@ -248,6 +250,7 @@ private open class FlutterBindingsPigeonCodec : StandardMessageCodec() {
interface AlarmApi {
fun setAlarm(alarmSettings: AlarmSettingsWire)
fun stopAlarm(alarmId: Long)
fun stopAll()
fun isRinging(alarmId: Long?): Boolean
fun setWarningNotificationOnKill(title: String, body: String)
fun disableWarningNotificationOnKill()
Expand Down Expand Up @@ -297,6 +300,22 @@ interface AlarmApi {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.alarm.AlarmApi.stopAll$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
val wrapped: List<Any?> = try {
api.stopAll()
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.alarm.AlarmApi.isRinging$separatedMessageChannelSuffix", codec)
if (api != null) {
Expand Down
6 changes: 6 additions & 0 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ PODS:
- Flutter (1.0.0)
- flutter_fgbg (0.0.1):
- Flutter
- package_info_plus (0.4.5):
- Flutter
- permission_handler_apple (9.3.0):
- Flutter
- shared_preferences_foundation (0.0.1):
Expand All @@ -16,6 +18,7 @@ DEPENDENCIES:
- alarm (from `.symlinks/plugins/alarm/ios`)
- Flutter (from `Flutter`)
- flutter_fgbg (from `.symlinks/plugins/flutter_fgbg/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
Expand All @@ -27,6 +30,8 @@ EXTERNAL SOURCES:
:path: Flutter
flutter_fgbg:
:path: ".symlinks/plugins/flutter_fgbg/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
shared_preferences_foundation:
Expand All @@ -38,6 +43,7 @@ SPEC CHECKSUMS:
alarm: 6c1f6a9688f94cd6bf8f104c67cc26e78c9d8d13
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_fgbg: 31c0d1140a131daea2d342121808f6aa0dcd879d
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
Expand Down
10 changes: 10 additions & 0 deletions example/lib/screens/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,16 @@ class _ExampleAlarmHomeScreenState extends State<ExampleAlarmHomeScreen> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ExampleAlarmHomeShortcutButton(refreshAlarms: loadAlarms),
const FloatingActionButton(
onPressed: Alarm.stopAll,
backgroundColor: Colors.red,
heroTag: null,
child: Text(
'STOP ALL',
textScaler: TextScaler.linear(0.9),
textAlign: TextAlign.center,
),
),
FloatingActionButton(
onPressed: () => navigateToAlarmScreen(null),
child: const Icon(Icons.alarm_add_rounded, size: 33),
Expand Down
8 changes: 6 additions & 2 deletions example/lib/screens/shortcut_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,13 @@ class _ExampleAlarmHomeShortcutButtonState
},
child: FloatingActionButton(
onPressed: () => onPressButton(0),
backgroundColor: Colors.red,
backgroundColor: Colors.green[700],
heroTag: null,
child: const Text('RING NOW', textAlign: TextAlign.center),
child: const Text(
'RING NOW',
textScaler: TextScaler.linear(0.9),
textAlign: TextAlign.center,
),
),
),
if (showMenu)
Expand Down
36 changes: 36 additions & 0 deletions ios/Classes/api/AlarmApiImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,42 @@ public class AlarmApiImpl: NSObject, AlarmApi {
func stopAlarm(alarmId: Int64) throws {
self.stopAlarmInternal(id: Int(truncatingIfNeeded: alarmId), cancelNotif: true)
}

func stopAll() throws {
NotificationManager.shared.removeAllNotifications()

self.mixOtherAudios()

self.vibratingAlarms.removeAll()

if let previousVolume = self.previousVolume {
self.setVolume(volume: previousVolume, enable: false)
}

let alarmIds = self.alarms.keys

for (_, alarm) in self.alarms {
alarm.timer?.invalidate()
alarm.task?.cancel()
alarm.audioPlayer?.stop()
alarm.volumeEnforcementTimer?.invalidate()
}
self.alarms.removeAll()

self.stopSilentSound()
self.stopNotificationOnKillService()

for (id) in alarmIds {
// Inform the Flutter plugin that the alarm was stopped
SwiftAlarmPlugin.alarmTriggerApi?.alarmStopped(alarmId: Int64(id), completion: { result in
if case .success = result {
NSLog("[SwiftAlarmPlugin] Alarm stopped notification for \(id) was processed successfully by Flutter.")
} else {
NSLog("[SwiftAlarmPlugin] Alarm stopped notification for \(id) encountered error in Flutter.")
}
})
}
}

func isRinging(alarmId: Int64?) throws -> Bool {
if let alarmId = alarmId {
Expand Down
20 changes: 15 additions & 5 deletions ios/Classes/generated/FlutterBindings.g.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.6.3), do not edit directly.
// Autogenerated from Pigeon (v22.7.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon

import Foundation
Expand Down Expand Up @@ -97,7 +97,6 @@ struct AlarmSettingsWire {
var androidFullScreenIntent: Bool



// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> AlarmSettingsWire? {
let id = pigeonVar_list[0] as! Int64
Expand Down Expand Up @@ -145,7 +144,6 @@ struct VolumeSettingsWire {
var volumeEnforced: Bool



// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> VolumeSettingsWire? {
let volume: Double? = nilOrValue(pigeonVar_list[0])
Expand Down Expand Up @@ -176,7 +174,6 @@ struct VolumeFadeStepWire {
var volume: Double



// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> VolumeFadeStepWire? {
let timeMillis = pigeonVar_list[0] as! Int64
Expand All @@ -203,7 +200,6 @@ struct NotificationSettingsWire {
var icon: String? = nil



// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> NotificationSettingsWire? {
let title = pigeonVar_list[0] as! String
Expand Down Expand Up @@ -292,6 +288,7 @@ class FlutterBindingsPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendab
protocol AlarmApi {
func setAlarm(alarmSettings: AlarmSettingsWire) throws
func stopAlarm(alarmId: Int64) throws
func stopAll() throws
func isRinging(alarmId: Int64?) throws -> Bool
func setWarningNotificationOnKill(title: String, body: String) throws
func disableWarningNotificationOnKill() throws
Expand Down Expand Up @@ -333,6 +330,19 @@ class AlarmApiSetup {
} else {
stopAlarmChannel.setMessageHandler(nil)
}
let stopAllChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.alarm.AlarmApi.stopAll\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
stopAllChannel.setMessageHandler { _, reply in
do {
try api.stopAll()
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
}
}
} else {
stopAllChannel.setMessageHandler(nil)
}
let isRingingChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.alarm.AlarmApi.isRinging\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
isRingingChannel.setMessageHandler { message, reply in
Expand Down
5 changes: 5 additions & 0 deletions ios/Classes/services/NotificationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ class NotificationManager: NSObject, UNUserNotificationCenterDelegate {
let notificationIdentifier = "alarm-\(id)"
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [notificationIdentifier])
}

func removeAllNotifications() {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
}

func handleAction(withIdentifier identifier: String?, for notification: UNNotification) {
guard let identifier = identifier else { return }
Expand Down
6 changes: 5 additions & 1 deletion lib/alarm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,12 @@ class Alarm {
static Future<void> stopAll() async {
final alarms = await getAlarms();

iOS ? await IOSAlarm.stopAll() : await AndroidAlarm.stopAll();

await AlarmStorage.unsaveAll();

for (final alarm in alarms) {
await stop(alarm.id);
updateStream.add(alarm.id);
}
}

Expand Down
12 changes: 12 additions & 0 deletions lib/service/alarm_storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ class AlarmStorage {
await _prefs.remove('$prefix$id');
}

/// Removes all alarms from local storage.
static Future<void> unsaveAll() async {
await _waitUntilInitialized();

final keys = _prefs.getKeys();
for (final key in keys) {
if (key.startsWith(prefix)) {
await _prefs.remove(key);
}
}
}

/// Whether at least one alarm is set.
static Future<bool> hasAlarm() async {
await _waitUntilInitialized();
Expand Down
5 changes: 5 additions & 0 deletions lib/src/android_alarm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class AndroidAlarm {
}
}

/// Calls the native `stopAll` function.
static Future<void> stopAll() async {
return _api.stopAll().catchError(AlarmExceptionHandlers.catchError<void>);
}

/// Checks whether an alarm or any alarm (if id is null) is ringing.
static Future<bool> isRinging([int? id]) async {
try {
Expand Down
26 changes: 25 additions & 1 deletion lib/src/generated/platform_bindings.g.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.6.3), do not edit directly.
// Autogenerated from Pigeon (v22.7.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers

Expand Down Expand Up @@ -314,6 +314,30 @@ class AlarmApi {
}
}

Future<void> stopAll() async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.alarm.AlarmApi.stopAll$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(null) as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return;
}
}

Future<bool> isRinging({required int? alarmId}) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.alarm.AlarmApi.isRinging$pigeonVar_messageChannelSuffix';
Expand Down
6 changes: 5 additions & 1 deletion lib/src/ios_alarm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class IOSAlarm {
return true;
}

/// Disposes timer and FGBG subscription
/// and calls the native `stopAlarm` function.
static Future<bool> stopAlarm(int id) async {
try {
Expand All @@ -46,6 +45,11 @@ class IOSAlarm {
}
}

/// Calls the native `stopAll` function.
static Future<void> stopAll() async {
return _api.stopAll().catchError(AlarmExceptionHandlers.catchError<void>);
}

/// Checks whether an alarm or any alarm (if id is null) is ringing.
static Future<bool> isRinging([int? id]) async {
try {
Expand Down
2 changes: 2 additions & 0 deletions pigeons/alarm_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ abstract class AlarmApi {

void stopAlarm({required int alarmId});

void stopAll();

bool isRinging({required int? alarmId});

void setWarningNotificationOnKill({
Expand Down

0 comments on commit eda2149

Please sign in to comment.