diff --git a/docs/android_alarm_manager_plus/usage.mdx b/docs/android_alarm_manager_plus/usage.mdx index 7c80289f2f..c1b10c636d 100644 --- a/docs/android_alarm_manager_plus/usage.mdx +++ b/docs/android_alarm_manager_plus/usage.mdx @@ -10,6 +10,8 @@ After importing this plugin to your project as usual, add the following to your ```xml + + ``` Next, within the `` tags, add: @@ -24,14 +26,14 @@ Next, within the `` tags, add: android:exported="false"/> + android:enabled="false" + android:exported="false"> - + - -Check out our documentation website to learn more. [Plus plugins documentation](https://plus.fluttercommunity.dev/docs/overview) ``` +Check out our documentation website to learn more. [Plus plugins documentation](https://plus.fluttercommunity.dev/docs/overview) Then in Dart code add: @@ -45,13 +47,26 @@ void printHello() { } main() async { - final int helloAlarmID = 0; + // Be sure to add this line if initialize() call happens before runApp() + WidgetsFlutterBinding.ensureInitialized(); + await AndroidAlarmManager.initialize(); runApp(...); + final int helloAlarmID = 0; await AndroidAlarmManager.periodic(const Duration(minutes: 1), helloAlarmID, printHello); } ``` +:::note +If your app has targetSDK=31 (Android 12) and you would like to create alarms with `alarmClock=true` or `exact=true` +be aware that user or system might cancel such alarms by revoking `SCHEDULE_EXACT_ALARM` permission. +More info can be found in [the official documentation](https://developer.android.com/training/scheduling/alarms#exact-permission-declare) + +AndroidAlarmManagerPlus checks if the permission was revoked before scheduling exact alarms, so your app won't get `SecurityException`, +but will report the issue in logs. However, in case when user grants `SCHEDULE_EXACT_ALARM` permission again AndroidAlarmManagerPlus won't +reschedule canceled alarms automatically, so it is up to you to handle such cases. +::: + `printHello` will then run (roughly) every minute, even if the main app ends. However, `printHello` will not run in the same isolate as the main application. Unlike threads, isolates do not share memory and communication between isolates must be done via message passing (see more documentation on @@ -64,52 +79,6 @@ alarm manager plugin itself, it may be necessary to inform the background servic to initialize plugins depending on which Flutter Android embedding the application is using. -### Flutter Android Embedding V2 (Flutter Version >= 1.12) - -For the Flutter Android Embedding V2, plugins are registered with the background -isolate via reflection so `AlarmService.setPluginRegistrant` does not need to be -called. - -**NOTE: this plugin is not completely compatible with the V2 embedding on -Flutter versions < 1.12 as the background isolate will not automatically -register plugins. This can be resolved by running `flutter upgrade` to upgrade -to the latest Flutter version.** - -### Flutter Android Embedding V1 (DEPRECATED) - -For the Flutter Android Embedding V1, the background service must be provided a -callback to register plugins with the background isolate. This is done by giving -the `AlarmService` a callback to call the application's `onCreate` method. See the example's -[Application overrides](https://github.com/fluttercommunity/plus_plugins/tree/main/packages/android_alarm_manager_plus/example/android/app/src/main/java/io/flutter/plugins/androidalarmmanagerexample/Application.java). - -In particular, its `Application` class is as follows: - -```java -public class Application extends FlutterApplication implements PluginRegistrantCallback { - @Override - public void onCreate() { - super.onCreate(); - AlarmService.setPluginRegistrant(this); - } - - @Override - public void registerWith(PluginRegistry registry) { - GeneratedPluginRegistrant.registerWith(registry); - } -} -``` - -Which must be reflected in the application's `AndroidManifest.xml`. E.g.: - -```xml - + + ``` Next, within the `` tags, add: @@ -34,14 +36,15 @@ Next, within the `` tags, add: android:exported="false"/> + android:enabled="false" + android:exported="false"> - + -Check out our documentation website to learn more. [Plus plugins documentation](https://plus.fluttercommunity.dev/docs/overview) ``` +Check out our documentation website to learn more. [Plus plugins documentation](https://plus.fluttercommunity.dev/docs/overview) Then in Dart code add: diff --git a/packages/android_alarm_manager_plus/android/src/main/AndroidManifest.xml b/packages/android_alarm_manager_plus/android/src/main/AndroidManifest.xml index 2fc149503f..d46d3b33d3 100644 --- a/packages/android_alarm_manager_plus/android/src/main/AndroidManifest.xml +++ b/packages/android_alarm_manager_plus/android/src/main/AndroidManifest.xml @@ -1,2 +1 @@ - - + diff --git a/packages/android_alarm_manager_plus/android/src/main/java/dev/fluttercommunity/plus/androidalarmmanager/AlarmBroadcastReceiver.java b/packages/android_alarm_manager_plus/android/src/main/java/dev/fluttercommunity/plus/androidalarmmanager/AlarmBroadcastReceiver.java index 8567f5a26c..5391a2d6ca 100644 --- a/packages/android_alarm_manager_plus/android/src/main/java/dev/fluttercommunity/plus/androidalarmmanager/AlarmBroadcastReceiver.java +++ b/packages/android_alarm_manager_plus/android/src/main/java/dev/fluttercommunity/plus/androidalarmmanager/AlarmBroadcastReceiver.java @@ -19,7 +19,7 @@ public class AlarmBroadcastReceiver extends BroadcastReceiver { * offloading any work to {@link AlarmService#enqueueAlarmProcessing(Context, Intent)}. * *

This method is the beginning of an execution path that will eventually execute a desired - * Dart callback function, as registed by the Dart side of the android_alarm_manager plugin. + * Dart callback function, as registered by the Dart side of the android_alarm_manager plugin. * However, there may be asynchronous gaps between {@code onReceive()} and the eventual invocation * of the Dart callback because {@link AlarmService} may need to spin up a Flutter execution * context before the callback can be invoked. diff --git a/packages/android_alarm_manager_plus/android/src/main/java/dev/fluttercommunity/plus/androidalarmmanager/AlarmService.java b/packages/android_alarm_manager_plus/android/src/main/java/dev/fluttercommunity/plus/androidalarmmanager/AlarmService.java index 473fe1608a..378066a077 100644 --- a/packages/android_alarm_manager_plus/android/src/main/java/dev/fluttercommunity/plus/androidalarmmanager/AlarmService.java +++ b/packages/android_alarm_manager_plus/android/src/main/java/dev/fluttercommunity/plus/androidalarmmanager/AlarmService.java @@ -127,7 +127,7 @@ private static void scheduleAlarm( context, requestCode, alarm, - (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0) + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0) | PendingIntent.FLAG_UPDATE_CURRENT); // Use the appropriate clock. @@ -140,7 +140,11 @@ private static void scheduleAlarm( AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); if (alarmClock) { - AlarmManagerCompat.setAlarmClock(manager, startMillis, pendingIntent, pendingIntent); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !manager.canScheduleExactAlarms()) { + Log.e(TAG, "Can`t schedule exact alarm due to revoked SCHEDULE_EXACT_ALARM permission"); + } else { + AlarmManagerCompat.setAlarmClock(manager, startMillis, pendingIntent, pendingIntent); + } return; } @@ -148,10 +152,15 @@ private static void scheduleAlarm( if (repeating) { manager.setRepeating(clock, startMillis, intervalMillis, pendingIntent); } else { - if (allowWhileIdle) { - AlarmManagerCompat.setExactAndAllowWhileIdle(manager, clock, startMillis, pendingIntent); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !manager.canScheduleExactAlarms()) { + Log.e(TAG, "Can`t schedule exact alarm due to revoked SCHEDULE_EXACT_ALARM permission"); } else { - AlarmManagerCompat.setExact(manager, clock, startMillis, pendingIntent); + if (allowWhileIdle) { + AlarmManagerCompat.setExactAndAllowWhileIdle( + manager, clock, startMillis, pendingIntent); + } else { + AlarmManagerCompat.setExact(manager, clock, startMillis, pendingIntent); + } } } } else { @@ -215,7 +224,7 @@ public static void cancel(Context context, int requestCode) { context, requestCode, alarm, - (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0) + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0) | PendingIntent.FLAG_NO_CREATE); if (existingIntent == null) { Log.i(TAG, "cancel: broadcast receiver not found"); diff --git a/packages/android_alarm_manager_plus/android/src/main/java/dev/fluttercommunity/plus/androidalarmmanager/RebootBroadcastReceiver.java b/packages/android_alarm_manager_plus/android/src/main/java/dev/fluttercommunity/plus/androidalarmmanager/RebootBroadcastReceiver.java index 81acc06ce7..f88489a051 100644 --- a/packages/android_alarm_manager_plus/android/src/main/java/dev/fluttercommunity/plus/androidalarmmanager/RebootBroadcastReceiver.java +++ b/packages/android_alarm_manager_plus/android/src/main/java/dev/fluttercommunity/plus/androidalarmmanager/RebootBroadcastReceiver.java @@ -14,8 +14,8 @@ /** * Reschedules background work after the Android device reboots. * - *

When an Android device reboots, all previously scheduled {@link AlarmManager} timers are - * cleared. + *

When an Android device reboots, all previously scheduled {@link android.app.AlarmManager} + * timers are cleared. * *

Timer callbacks registered with the android_alarm_manager plugin can be designated * "persistent" and therefore, upon device reboot, should be rescheduled for execution. To diff --git a/packages/android_alarm_manager_plus/example/android/app/src/main/AndroidManifest.xml b/packages/android_alarm_manager_plus/example/android/app/src/main/AndroidManifest.xml index 39ead379b9..2fdfe25df7 100644 --- a/packages/android_alarm_manager_plus/example/android/app/src/main/AndroidManifest.xml +++ b/packages/android_alarm_manager_plus/example/android/app/src/main/AndroidManifest.xml @@ -10,6 +10,7 @@ + @@ -19,7 +20,7 @@ android:name="io.flutter.app.FlutterApplication" android:icon="@mipmap/ic_launcher" android:usesCleartextTraffic="true" - android:label="example"> + android:label="Alarm manager example"> + android:exported="false"> - + diff --git a/packages/android_alarm_manager_plus/example/ios/Flutter/Debug.xcconfig b/packages/android_alarm_manager_plus/example/ios/Flutter/Debug.xcconfig index 592ceee85b..ec97fc6f30 100644 --- a/packages/android_alarm_manager_plus/example/ios/Flutter/Debug.xcconfig +++ b/packages/android_alarm_manager_plus/example/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/packages/android_alarm_manager_plus/example/ios/Flutter/Release.xcconfig b/packages/android_alarm_manager_plus/example/ios/Flutter/Release.xcconfig index 592ceee85b..c4855bfe20 100644 --- a/packages/android_alarm_manager_plus/example/ios/Flutter/Release.xcconfig +++ b/packages/android_alarm_manager_plus/example/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/packages/android_alarm_manager_plus/example/lib/main.dart b/packages/android_alarm_manager_plus/example/lib/main.dart index 5ecfb4ded0..e360aca632 100644 --- a/packages/android_alarm_manager_plus/example/lib/main.dart +++ b/packages/android_alarm_manager_plus/example/lib/main.dart @@ -26,8 +26,6 @@ final ReceivePort port = ReceivePort(); SharedPreferences prefs; Future main() async { - // ignore: todo - // TODO(bkonyi): uncomment WidgetsFlutterBinding.ensureInitialized(); // Register the UI isolate's SendPort to allow for communication from the diff --git a/packages/android_alarm_manager_plus/lib/android_alarm_manager_plus.dart b/packages/android_alarm_manager_plus/lib/android_alarm_manager_plus.dart index f341779d2d..8ffb06d937 100644 --- a/packages/android_alarm_manager_plus/lib/android_alarm_manager_plus.dart +++ b/packages/android_alarm_manager_plus/lib/android_alarm_manager_plus.dart @@ -130,6 +130,9 @@ class AndroidAlarmManager { /// If `exact` is passed as `true`, the timer will be created with Android's /// `AlarmManagerCompat.setExact`. When `exact` is `false` (the default), the /// timer will be created with `AlarmManager.set`. + /// For apps with `targetSDK=31` before scheduling an exact alarm a check for + /// `SCHEDULE_EXACT_ALARM` permission is required. Otherwise, an exeption will + /// be thrown and alarm won't schedule. /// /// If `wakeup` is passed as `true`, the device will be woken up when the /// alarm fires. If `wakeup` is false (the default), the device will not be @@ -188,6 +191,9 @@ class AndroidAlarmManager { /// If `exact` is passed as `true`, the timer will be created with Android's /// `AlarmManagerCompat.setExact`. When `exact` is `false` (the default), the /// timer will be created with `AlarmManager.set`. + /// For apps with `targetSDK=31` before scheduling an exact alarm a check for + /// `SCHEDULE_EXACT_ALARM` permission is required. Otherwise, an exeption will + /// be thrown and alarm won't schedule. /// /// If `wakeup` is passed as `true`, the device will be woken up when the /// alarm fires. If `wakeup` is false (the default), the device will not be @@ -256,6 +262,9 @@ class AndroidAlarmManager { /// If `exact` is passed as `true`, the timer will be created with Android's /// `AlarmManager.setRepeating`. When `exact` is `false` (the default), the /// timer will be created with `AlarmManager.setInexactRepeating`. + /// For apps with `targetSDK=31` before scheduling an exact alarm a check for + /// `SCHEDULE_EXACT_ALARM` permission is required. Otherwise, an exeption will + /// be thrown and alarm won't schedule. /// /// If `wakeup` is passed as `true`, the device will be woken up when the /// alarm fires. If `wakeup` is false (the default), the device will not be diff --git a/packages/android_alarm_manager_plus/pubspec.yaml b/packages/android_alarm_manager_plus/pubspec.yaml index d863458ec1..4cd4c5bd54 100644 --- a/packages/android_alarm_manager_plus/pubspec.yaml +++ b/packages/android_alarm_manager_plus/pubspec.yaml @@ -1,7 +1,7 @@ name: android_alarm_manager_plus description: Flutter plugin for accessing the Android AlarmManager service, and running Dart code in the background when alarms fire. -version: 2.0.2 +version: 2.0.3 homepage: https://plus.fluttercommunity.dev/ repository: https://github.com/fluttercommunity/plus_plugins/tree/main/packages/