From 343e9d0e26058c68c4112fd7378836270244a24a Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Wed, 13 Oct 2021 23:51:09 +0300 Subject: [PATCH 1/7] (android_alarm_manager_plus) Update README, fix typos, add new permission declaration --- packages/android_alarm_manager_plus/README.md | 9 ++++++--- .../android/src/main/AndroidManifest.xml | 3 +-- .../plus/androidalarmmanager/AlarmBroadcastReceiver.java | 2 +- .../androidalarmmanager/RebootBroadcastReceiver.java | 2 +- .../example/android/app/src/main/AndroidManifest.xml | 7 ++++--- .../example/ios/Flutter/Debug.xcconfig | 1 + .../example/ios/Flutter/Release.xcconfig | 1 + .../android_alarm_manager_plus/example/lib/main.dart | 2 -- 8 files changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/android_alarm_manager_plus/README.md b/packages/android_alarm_manager_plus/README.md index 3ab84776dd..9711ad9940 100644 --- a/packages/android_alarm_manager_plus/README.md +++ b/packages/android_alarm_manager_plus/README.md @@ -20,6 +20,8 @@ After importing this plugin to your project as usual, add the following to your ```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/RebootBroadcastReceiver.java b/packages/android_alarm_manager_plus/android/src/main/java/dev/fluttercommunity/plus/androidalarmmanager/RebootBroadcastReceiver.java index 81acc06ce7..078508c92a 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,7 +14,7 @@ /** * Reschedules background work after the Android device reboots. * - *

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

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 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 From bfd6107ae5fe38e8d16b32477a9d4e85f80b0cf7 Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Mon, 18 Oct 2021 09:17:58 +0300 Subject: [PATCH 2/7] (android_alarm_manager) Update usage docs, add info about need to check new permission --- docs/android_alarm_manager_plus/usage.mdx | 63 ++++--------------- .../lib/android_alarm_manager_plus.dart | 9 +++ 2 files changed, 20 insertions(+), 52 deletions(-) diff --git a/docs/android_alarm_manager_plus/usage.mdx b/docs/android_alarm_manager_plus/usage.mdx index 7c80289f2f..53471a6d01 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,9 +47,12 @@ 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); } ``` @@ -64,52 +69,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 - Date: Sun, 24 Oct 2021 19:32:15 +0200 Subject: [PATCH 3/7] (android_alarm_manager) add check for exact alarms permission before scheduling such --- .../androidalarmmanager/AlarmService.java | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) 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..ef233824ff 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,15 @@ 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) { + if (manager.canScheduleExactAlarms()) { + AlarmManagerCompat.setAlarmClock(manager, startMillis, pendingIntent, pendingIntent); + } else { + 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 +156,22 @@ 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) { + if (manager.canScheduleExactAlarms()) { + if (allowWhileIdle) { + AlarmManagerCompat.setExactAndAllowWhileIdle(manager, clock, startMillis, pendingIntent); + } else { + AlarmManagerCompat.setExact(manager, clock, startMillis, pendingIntent); + } + } else { + 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 +235,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"); From 6c06efb2dfa6925b14d2ab7c95b1d620c8e275f5 Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Mon, 25 Oct 2021 18:27:16 +0200 Subject: [PATCH 4/7] (android_alarm_manager) add info about Android 12 exact permissions, simplify check for exact alarm scheduling permission --- docs/android_alarm_manager_plus/usage.mdx | 9 +++++++++ .../androidalarmmanager/AlarmService.java | 20 ++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/docs/android_alarm_manager_plus/usage.mdx b/docs/android_alarm_manager_plus/usage.mdx index 53471a6d01..e9fffda40b 100644 --- a/docs/android_alarm_manager_plus/usage.mdx +++ b/docs/android_alarm_manager_plus/usage.mdx @@ -57,6 +57,15 @@ main() async { } ``` +:::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. +::: + `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 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 ef233824ff..25207c5bad 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 @@ -140,12 +140,8 @@ private static void scheduleAlarm( AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); if (alarmClock) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - if (manager.canScheduleExactAlarms()) { - AlarmManagerCompat.setAlarmClock(manager, startMillis, pendingIntent, pendingIntent); - } else { - Log.e(TAG, "Can`t schedule exact alarm due to revoked SCHEDULE_EXACT_ALARM permission"); - } + 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); } @@ -156,16 +152,8 @@ private static void scheduleAlarm( if (repeating) { manager.setRepeating(clock, startMillis, intervalMillis, pendingIntent); } else { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - if (manager.canScheduleExactAlarms()) { - if (allowWhileIdle) { - AlarmManagerCompat.setExactAndAllowWhileIdle(manager, clock, startMillis, pendingIntent); - } else { - AlarmManagerCompat.setExact(manager, clock, startMillis, pendingIntent); - } - } else { - Log.e(TAG, "Can`t schedule exact alarm due to revoked SCHEDULE_EXACT_ALARM permission"); - } + 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 { if (allowWhileIdle) { AlarmManagerCompat.setExactAndAllowWhileIdle(manager, clock, startMillis, pendingIntent); From bb13ab07c37e4e100ebc3abff4f88ed682d3cce6 Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Mon, 25 Oct 2021 18:35:26 +0200 Subject: [PATCH 5/7] Bump version, update CHANGELOG --- packages/android_alarm_manager_plus/CHANGELOG.md | 5 +++++ packages/android_alarm_manager_plus/pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/android_alarm_manager_plus/CHANGELOG.md b/packages/android_alarm_manager_plus/CHANGELOG.md index 46e379605a..98c4d274e8 100644 --- a/packages/android_alarm_manager_plus/CHANGELOG.md +++ b/packages/android_alarm_manager_plus/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.3 + +- Handle Android 12 behavior changes for exact alarms scheduling +- Update plugin documentation + ## 2.0.2 - Clean up plugin code and fix potential issues with scheduling persistent alarms 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/ From 3606950455deb92c38141159dfdce38c3ced222b Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Mon, 25 Oct 2021 18:41:20 +0200 Subject: [PATCH 6/7] Add mention of not handling exact_permission permission granting --- docs/android_alarm_manager_plus/usage.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/android_alarm_manager_plus/usage.mdx b/docs/android_alarm_manager_plus/usage.mdx index e9fffda40b..c1b10c636d 100644 --- a/docs/android_alarm_manager_plus/usage.mdx +++ b/docs/android_alarm_manager_plus/usage.mdx @@ -63,7 +63,8 @@ be aware that user or system might cancel such alarms by revoking `SCHEDULE_EXAC 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. +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` From 477b41aedc48cea65a0dcdd1c65e291447cbfac8 Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Mon, 25 Oct 2021 19:17:08 +0200 Subject: [PATCH 7/7] Fix java formatting --- .../plus/androidalarmmanager/AlarmService.java | 3 ++- .../plus/androidalarmmanager/RebootBroadcastReceiver.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) 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 25207c5bad..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 @@ -156,7 +156,8 @@ private static void scheduleAlarm( Log.e(TAG, "Can`t schedule exact alarm due to revoked SCHEDULE_EXACT_ALARM permission"); } else { if (allowWhileIdle) { - AlarmManagerCompat.setExactAndAllowWhileIdle(manager, clock, startMillis, pendingIntent); + AlarmManagerCompat.setExactAndAllowWhileIdle( + manager, clock, startMillis, pendingIntent); } else { AlarmManagerCompat.setExact(manager, clock, startMillis, pendingIntent); } 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 078508c92a..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 android.app.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