Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 21 additions & 52 deletions docs/android_alarm_manager_plus/usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ After importing this plugin to your project as usual, add the following to your
```xml
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- For apps with targetSDK=31 (Android 12) -->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
```

Next, within the `<application></application>` tags, add:
Expand All @@ -24,14 +26,14 @@ Next, within the `<application></application>` tags, add:
android:exported="false"/>
<receiver
android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
android:enabled="false">
android:enabled="false"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>

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:

Expand All @@ -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
Expand All @@ -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
<application
android:name=".Application"
...
```

**Note:** Not calling `AlarmService.setPluginRegistrant` will result in an exception being
thrown when an alarm eventually fires.

## Plugin Development

### Running Flutter unit tests
Expand Down Expand Up @@ -140,4 +109,4 @@ To run the Flutter Driver tests, cd into `example` and run:

```
flutter driver test_driver/android_alarm_manager_e2e.dart
```
```
5 changes: 5 additions & 0 deletions packages/android_alarm_manager_plus/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
9 changes: 6 additions & 3 deletions packages/android_alarm_manager_plus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ After importing this plugin to your project as usual, add the following to your
```xml
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- For apps with targetSDK=31 (Android 12) -->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
```

Next, within the `<application></application>` tags, add:
Expand All @@ -34,14 +36,15 @@ Next, within the `<application></application>` tags, add:
android:exported="false"/>
<receiver
android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
android:enabled="false">
android:enabled="false"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>

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:

Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
<manifest package="dev.fluttercommunity.plus.androidalarmmanager">
</manifest>
<manifest package="dev.fluttercommunity.plus.androidalarmmanager" />
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class AlarmBroadcastReceiver extends BroadcastReceiver {
* offloading any work to {@link AlarmService#enqueueAlarmProcessing(Context, Intent)}.
*
* <p>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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -140,18 +140,27 @@ 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;
}

if (exact) {
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 {
Expand Down Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
/**
* Reschedules background work after the Android device reboots.
*
* <p>When an Android device reboots, all previously scheduled {@link AlarmManager} timers are
* cleared.
* <p>When an Android device reboots, all previously scheduled {@link android.app.AlarmManager}
* timers are cleared.
*
* <p>Timer callbacks registered with the android_alarm_manager plugin can be designated
* "persistent" and therefore, upon device reboot, should be rescheduled for execution. To
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<uses-permission android:name="android.permission.INTERNET" />

<!-- Start Alarm Manager -->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- End Alarm Manager -->
Expand All @@ -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">
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
Expand Down Expand Up @@ -65,9 +66,9 @@
<receiver
android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
android:enabled="false"
android:exported="true">
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- End Alarm Manager -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
2 changes: 0 additions & 2 deletions packages/android_alarm_manager_plus/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ final ReceivePort port = ReceivePort();
SharedPreferences prefs;

Future<void> main() async {
// ignore: todo
// TODO(bkonyi): uncomment
WidgetsFlutterBinding.ensureInitialized();

// Register the UI isolate's SendPort to allow for communication from the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/android_alarm_manager_plus/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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/

Expand Down