-
Notifications
You must be signed in to change notification settings - Fork 357
Expand file tree
/
Copy pathworkmanager_impl.dart
More file actions
326 lines (302 loc) · 13.4 KB
/
workmanager_impl.dart
File metadata and controls
326 lines (302 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
import 'dart:async';
import 'dart:io';
import 'package:flutter/widgets.dart';
import 'package:workmanager_platform_interface/workmanager_platform_interface.dart';
import 'package:workmanager_android/workmanager_android.dart';
import 'package:workmanager_apple/workmanager_apple.dart';
/// Function that executes your background work.
/// You should return whether the task ran successfully or not.
///
/// [taskName] Returns the value you provided when registering the task.
/// iOS will pass [Workmanager.iOSBackgroundTask] (for background-fetch) or
/// custom task IDs for BGTaskScheduler based tasks.
///
/// The behavior for retries is different on each platform:
/// - Android: return `false` from the this method will reschedule the work
/// based on the policy given in [Workmanager.registerOneOffTask], for example
/// - iOS: The return value is ignored, but if work has failed, you can schedule
/// another attempt using [Workmanager.registerOneOffTask]. This depends on
/// BGTaskScheduler being set up correctly. Please follow the README for
/// instructions.
typedef BackgroundTaskHandler = Future<bool> Function(
String taskName, Map<String, dynamic>? inputData);
/// Make sure you followed the platform setup steps first before trying to register any task.
///
/// Android:
/// - Custom Application class
///
/// iOS:
/// - Enabled the Background Fetch API
///
/// Inside your Dart code
///
/// Initialize the plugin first
///
/// ```
/// @pragma('vm:entry-point')
/// void callbackDispatcher() {
/// Workmanager().executeTask((taskName, inputData) {
/// switch(taskName) {
/// case "":
/// print("Replace this print statement with your code that should be executed in the background here");
/// break;
/// }
/// return Future.value(true);
/// });
/// }
///
/// void main() {
/// Workmanager().initialize(callbackDispatcher);
/// }
/// ```
///
/// ## You can schedule a specific iOS task using:
/// - `Workmanager().registerOneOffTask()`
/// Please read the documentation on limitations for background processing on iOS.
///
///
/// iOS periodic background fetch task is automatically scheduled if you setup the plugin properly for Background Fetch.
///
/// If you are targeting iOS 13+, you can use `Workmanager().registerPeriodicTask()`
///
/// Note: On iOS 13+, adding a BGTaskSchedulerPermittedIdentifiers key to the Info.plist
/// disables the performFetchWithCompletionHandler and setMinimumBackgroundFetchInterval
/// methods, which means you cannot use both old Background Fetch and new registerPeriodicTask
/// at the same time, you have to choose one based on your minimum iOS target version.
/// For details see [Using background tasks to update your app](https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/using_background_tasks_to_update_your_app/)
///
///
/// ## You can schedule Android tasks using:
/// - `Workmanager().registerOneOffTask()` or `Workmanager().registerPeriodicTask()`
class Workmanager {
factory Workmanager() => _instance;
Workmanager._internal() {
_ensurePlatformImplementation();
}
static final Workmanager _instance = Workmanager._internal();
static void _ensurePlatformImplementation() {
if (WorkmanagerPlatform.instance is! WorkmanagerAndroid &&
WorkmanagerPlatform.instance is! WorkmanagerApple) {
if (Platform.isAndroid) {
WorkmanagerPlatform.instance = WorkmanagerAndroid();
} else if (Platform.isIOS) {
WorkmanagerPlatform.instance = WorkmanagerApple();
}
}
}
/// Use this constant inside your callbackDispatcher to identify when an iOS Background Fetch occurred.
///
/// ```
/// @pragma('vm:entry-point')
/// void callbackDispatcher() {
/// Workmanager().executeTask((taskName, inputData) {
/// switch (taskName) {
/// case Workmanager.iOSBackgroundTask:
/// stderr.writeln("The iOS background fetch was triggered");
/// break;
/// }
///
/// return Future.value(true);
/// });
/// }
/// ```
static const String iOSBackgroundTask = "iOSPerformFetch";
static BackgroundTaskHandler? _backgroundTaskHandler;
static late final WorkmanagerFlutterApi _flutterApi;
/// Platform implementation
static WorkmanagerPlatform get _platform => WorkmanagerPlatform.instance;
/// Initialize the Workmanager with a [callbackDispatcher].
///
/// The [callbackDispatcher] is a top level function which will be invoked by Android or iOS whenever a scheduled task is due.
/// The [isInDebugMode] will post local notifications for every background worker that ran. This is very useful when trying to debug what's happening in the background.
Future<void> initialize(
Function callbackDispatcher, {
bool isInDebugMode = false,
}) async {
return _platform.initialize(callbackDispatcher,
isInDebugMode: isInDebugMode);
}
/// This method needs to be called from within your [callbackDispatcher].
///
/// [backgroundTaskHandler] is the callback that is provided when a background task is run.
///
/// This is used by iOS and Android to identify which task was selected to run in the background.
/// The [BackgroundTaskHandler] will provide you with the [taskName] and the [inputData].
/// The [taskName] will always be the value you provided when registering the task.
/// The [inputData] will contain all the data you registered the task with.
///
/// You need to return a [Future<bool>] that will tell the OS if the task was successful or not.
///
/// You can perfectly call other Flutter plugins inside this callback, as the callback is simply running within a Flutter background isolate.
///
/// Scheduling other background tasks inside the [BackgroundTaskHandler] is allowed.
void executeTask(BackgroundTaskHandler backgroundTaskHandler) async {
WidgetsFlutterBinding.ensureInitialized();
_backgroundTaskHandler = backgroundTaskHandler;
_flutterApi = _WorkmanagerFlutterApiImpl();
WorkmanagerFlutterApi.setUp(_flutterApi);
await _flutterApi.backgroundChannelInitialized();
}
/// Schedule a one-off task.
///
/// A [uniqueName] is required so only one task can be registered.
///
/// Calling this method again with the same [uniqueName] will update the current pending task, unless an [ExistingWorkPolicy] is provided.
///
/// - [taskName]: is the value that will be returned in the [BackgroundTaskHandler], ignored on iOS where you should use [uniqueName].
/// - [inputData]: is the input data for task. Valid value types are: int, bool, double, String and their list
/// - [initialDelay]: is an [Duration] after which the task will run. Ignored on iOS where you should schedule the task in AppDelegate.swift
/// - [constraints]: are the requirements that need to be met before the task runs.
/// - [backoffPolicy]: is the backoff policy to use when retrying work.
/// - [backoffPolicyDelay]: is the delay for the backoff policy.
/// - [tag]: is an optional tag that can be used to identify or cancel the task.
/// - [existingWorkPolicy]: is the policy to use when work with the same [uniqueName] already exists.
/// - [outOfQuotaPolicy]: is the policy to use when the device is out of quota. (Android only)
Future<void> registerOneOffTask(
String uniqueName,
String taskName, {
Map<String, dynamic>? inputData,
Duration? initialDelay,
Constraints? constraints,
ExistingWorkPolicy? existingWorkPolicy,
BackoffPolicy? backoffPolicy,
Duration? backoffPolicyDelay,
String? tag,
OutOfQuotaPolicy? outOfQuotaPolicy,
}) async {
return _platform.registerOneOffTask(
uniqueName,
taskName,
inputData: inputData,
initialDelay: initialDelay,
constraints: constraints,
existingWorkPolicy: existingWorkPolicy,
backoffPolicy: backoffPolicy,
backoffPolicyDelay: backoffPolicyDelay,
tag: tag,
outOfQuotaPolicy: outOfQuotaPolicy,
);
}
/// Schedules a periodic task that will run every provided [frequency].
///
/// On iOS it is not guaranteed when or how often it will run, iOS will schedule
/// it as per user's App usage pattern, iOS might terminate the task or throttle
/// it's frequency if it takes more than 30 seconds.
///
/// A [uniqueName] is required so only one task can be registered.
/// The [taskName] is the value that will be returned in the [BackgroundTaskHandler], ignored on iOS where you should use [uniqueName].
/// a [frequency] is not required and will be defaulted to 15 minutes if not provided.
/// a [frequency] has a minimum of 15 min. Android will automatically change your frequency to 15 min if you have configured a lower frequency.
/// the [flexInterval] If the nature of the work is time-sensitive, you can configure the PeriodicWorkRequest to run in a flexible period at each interval.
/// The [inputData] is the input data for task. Valid value types are: int, bool, double, String and their list
///
/// Unlike Android, you cannot set [frequency] for iOS here rather you have to set in `AppDelegate.swift` while registering the task.
/// The [inputData] is the input data for task. Valid value types are: int, bool, double, String and their list. It is not supported on iOS.
///
/// For iOS see Apple docs:
/// [iOS 13+ Using background tasks to update your app](https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/using_background_tasks_to_update_your_app/)
///
/// [iOS 13+ BGAppRefreshTask](https://developer.apple.com/documentation/backgroundtasks/bgapprefreshtask/)
Future<void> registerPeriodicTask(
String uniqueName,
String taskName, {
Duration? frequency,
Duration? flexInterval,
Map<String, dynamic>? inputData,
Duration? initialDelay,
Constraints? constraints,
ExistingWorkPolicy? existingWorkPolicy,
BackoffPolicy? backoffPolicy,
Duration? backoffPolicyDelay,
String? tag,
}) async {
return _platform.registerPeriodicTask(
uniqueName,
taskName,
frequency: frequency,
flexInterval: flexInterval,
inputData: inputData,
initialDelay: initialDelay,
constraints: constraints,
existingWorkPolicy: existingWorkPolicy,
backoffPolicy: backoffPolicy,
backoffPolicyDelay: backoffPolicyDelay,
tag: tag,
);
}
/// Checks whether a period task is scheduled by its [uniqueName].
///
/// Scheduled means the work state is either ENQUEUED or RUNNING
///
/// Only available on Android.
Future<bool> isScheduledByUniqueName(String uniqueName) async {
return _platform.isScheduledByUniqueName(uniqueName);
}
/// Schedule a background long running task, currently only available on iOS.
///
/// Processing tasks are for long processes like data processing and app maintenance.
/// Processing tasks can run for minutes, but the system can interrupt these.
/// Processing tasks run only when the device is idle. iOS might terminate any
/// running background processing tasks when the user starts using the device.
/// However background refresh tasks aren't affected.
///
/// For iOS see Apple docs:
/// [iOS 13+ Using background tasks to update your app](https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/using_background_tasks_to_update_your_app/)
///
/// [iOS 13+ BGProcessingTask](https://developer.apple.com/documentation/backgroundtasks/bgprocessingtask/)
Future<void> registerProcessingTask(
String uniqueName,
String taskName, {
Duration? initialDelay,
Map<String, dynamic>? inputData,
Constraints? constraints,
}) async {
return _platform.registerProcessingTask(
uniqueName,
taskName,
initialDelay: initialDelay,
inputData: inputData,
constraints: constraints,
);
}
/// Cancels task by [uniqueName]
Future<void> cancelByUniqueName(String uniqueName) async =>
_platform.cancelByUniqueName(uniqueName);
/// Cancels task by [tag]
Future<void> cancelByTag(String tag) async => _platform.cancelByTag(tag);
/// Cancels all tasks
Future<void> cancelAll() async => _platform.cancelAll();
/// Prints details of un-executed scheduled tasks to console. To be used during
/// development/debugging.
///
/// Currently only supported on iOS and only on iOS 13+.
/// Returns a string containing the scheduled tasks information.
Future<String> printScheduledTasks() async => _platform.printScheduledTasks();
}
/// Converts inputData from Pigeon format, filtering out null keys
@visibleForTesting
Map<String, dynamic>? convertPigeonInputData(Map<String?, Object?>? inputData) {
Map<String, dynamic>? convertedInputData;
if (inputData != null) {
convertedInputData = <String, dynamic>{};
for (final entry in inputData.entries) {
if (entry.key != null) {
convertedInputData[entry.key!] = entry.value;
}
}
}
return convertedInputData;
}
/// Implementation of WorkmanagerFlutterApi for handling background task execution
class _WorkmanagerFlutterApiImpl extends WorkmanagerFlutterApi {
@override
Future<void> backgroundChannelInitialized() async {}
@override
Future<bool> executeTask(
String taskName, Map<String?, Object?>? inputData) async {
final convertedInputData = convertPigeonInputData(inputData);
final result = await Workmanager._backgroundTaskHandler
?.call(taskName, convertedInputData);
return result ?? false;
}
}