| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:async'; |
| import 'dart:io'; |
| import 'dart:ui'; |
| |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/services.dart'; |
| |
| const String _backgroundName = |
| 'plugins.flutter.io/android_alarm_manager_background'; |
| |
| // This is the entrypoint for the background isolate. Since we can only enter |
| // an isolate once, we setup a MethodChannel to listen for method invokations |
| // from the native portion of the plugin. This allows for the plugin to perform |
| // any necessary processing in Dart (e.g., populating a custom object) before |
| // invoking the provided callback. |
| void _alarmManagerCallbackDispatcher() { |
| const MethodChannel _channel = |
| MethodChannel(_backgroundName, JSONMethodCodec()); |
| |
| // Setup Flutter state needed for MethodChannels. |
| WidgetsFlutterBinding.ensureInitialized(); |
| |
| // This is where the magic happens and we handle background events from the |
| // native portion of the plugin. |
| _channel.setMethodCallHandler((MethodCall call) async { |
| final dynamic args = call.arguments; |
| final CallbackHandle handle = CallbackHandle.fromRawHandle(args[0]); |
| |
| // PluginUtilities.getCallbackFromHandle performs a lookup based on the |
| // callback handle and returns a tear-off of the original callback. |
| final Function closure = PluginUtilities.getCallbackFromHandle(handle); |
| |
| if (closure == null) { |
| print('Fatal: could not find callback'); |
| exit(-1); |
| } |
| closure(); |
| }); |
| |
| // Once we've finished initializing, let the native portion of the plugin |
| // know that it can start scheduling alarms. |
| _channel.invokeMethod('AlarmService.initialized'); |
| } |
| |
| /// A Flutter plugin for registering Dart callbacks with the Android |
| /// AlarmManager service. |
| /// |
| /// See the example/ directory in this package for sample usage. |
| class AndroidAlarmManager { |
| static const String _channelName = 'plugins.flutter.io/android_alarm_manager'; |
| static const MethodChannel _channel = |
| MethodChannel(_channelName, JSONMethodCodec()); |
| |
| /// Starts the [AndroidAlarmManager] service. This must be called before |
| /// setting any alarms. |
| /// |
| /// Returns a [Future] that resolves to `true` on success and `false` on |
| /// failure. |
| static Future<bool> initialize() async { |
| final CallbackHandle handle = |
| PluginUtilities.getCallbackHandle(_alarmManagerCallbackDispatcher); |
| if (handle == null) { |
| return false; |
| } |
| final dynamic r = await _channel |
| .invokeMethod('AlarmService.start', <dynamic>[handle.toRawHandle()]); |
| return r ?? false; |
| } |
| |
| /// Schedules a one-shot timer to run `callback` after time `delay`. |
| /// |
| /// The `callback` will run whether or not the main application is running or |
| /// in the foreground. It will run in the Isolate owned by the |
| /// AndroidAlarmManager service. |
| /// |
| /// `callback` must be either a top-level function or a static method from a |
| /// class. |
| /// |
| /// The timer is uniquely identified by `id`. Calling this function again |
| /// again with the same `id` will cancel and replace the existing timer. |
| /// |
| /// 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`. |
| /// |
| /// 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 |
| /// woken up to service the alarm. |
| /// |
| /// Returns a [Future] that resolves to `true` on success and `false` on |
| /// failure. |
| static Future<bool> oneShot( |
| Duration delay, |
| int id, |
| dynamic Function() callback, { |
| bool exact = false, |
| bool wakeup = false, |
| }) async { |
| final int now = DateTime.now().millisecondsSinceEpoch; |
| final int first = now + delay.inMilliseconds; |
| final CallbackHandle handle = PluginUtilities.getCallbackHandle(callback); |
| if (handle == null) { |
| return false; |
| } |
| final dynamic r = await _channel.invokeMethod('Alarm.oneShot', <dynamic>[ |
| id, |
| exact, |
| wakeup, |
| first, |
| handle.toRawHandle(), |
| ]); |
| return (r == null) ? false : r; |
| } |
| |
| /// Schedules a repeating timer to run `callback` with period `duration`. |
| /// |
| /// The `callback` will run whether or not the main application is running or |
| /// in the foreground. It will run in the Isolate owned by the |
| /// AndroidAlarmManager service. |
| /// |
| /// `callback` must be either a top-level function or a static method from a |
| /// class. |
| /// |
| /// The repeating timer is uniquely identified by `id`. Calling this function |
| /// again with the same `id` will cancel and replace the existing timer. |
| /// |
| /// 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`. |
| /// |
| /// 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 |
| /// woken up to service the alarm. |
| /// |
| /// Returns a [Future] that resolves to `true` on success and `false` on |
| /// failure. |
| static Future<bool> periodic( |
| Duration duration, |
| int id, |
| dynamic Function() callback, { |
| bool exact = false, |
| bool wakeup = false, |
| }) async { |
| final int now = DateTime.now().millisecondsSinceEpoch; |
| final int period = duration.inMilliseconds; |
| final int first = now + period; |
| final CallbackHandle handle = PluginUtilities.getCallbackHandle(callback); |
| if (handle == null) { |
| return false; |
| } |
| final dynamic r = await _channel.invokeMethod('Alarm.periodic', |
| <dynamic>[id, exact, wakeup, first, period, handle.toRawHandle()]); |
| return (r == null) ? false : r; |
| } |
| |
| /// Cancels a timer. |
| /// |
| /// If a timer has been scheduled with `id`, then this function will cancel |
| /// it. |
| /// |
| /// Returns a [Future] that resolves to `true` on success and `false` on |
| /// failure. |
| static Future<bool> cancel(int id) async { |
| final dynamic r = |
| await _channel.invokeMethod('Alarm.cancel', <dynamic>[id]); |
| return (r == null) ? false : r; |
| } |
| } |