Line data Source code
1 : import 'package:flutter/foundation.dart';
2 :
3 : /// Backoff policy for retry behavior when task fails.
4 : ///
5 : /// **Android**: Determines retry behavior for failed WorkManager tasks.
6 : /// **iOS**: Not applicable (manual retry required).
7 : enum BackoffPolicy {
8 : /// Exponential backoff - Delay doubles after each retry.
9 : ///
10 : /// **Delay Pattern**: 30s, 60s, 120s, 240s, ...
11 : ///
12 : /// **Use Cases**: Network errors, server issues (gives server time to recover).
13 : exponential,
14 :
15 : /// Linear backoff - Constant delay between retries.
16 : ///
17 : /// **Delay Pattern**: 30s, 30s, 30s, 30s, ...
18 : ///
19 : /// **Use Cases**: Database locks, transient errors.
20 : linear,
21 : }
22 :
23 : /// Quality of Service (QoS) priority for task execution.
24 : ///
25 : /// **iOS**: Maps to DispatchQoS for task execution priority.
26 : /// **Android**: Ignored (WorkManager handles priority automatically).
27 : enum QoS {
28 : /// Low priority - User is not waiting.
29 : ///
30 : /// **Use Cases**: Prefetching, maintenance, non-urgent sync.
31 : utility,
32 :
33 : /// Default priority - Deferrable work.
34 : ///
35 : /// **Use Cases**: Most background tasks, indexing, cleanup.
36 : background,
37 :
38 : /// Important work - User may be waiting.
39 : ///
40 : /// **Use Cases**: Explicit user action, data refresh from user request.
41 : userInitiated,
42 :
43 : /// Critical work - User actively waiting.
44 : ///
45 : /// **Use Cases**: UI updates, immediate user-facing operations.
46 : /// **Note**: Avoid for background tasks (defeats purpose of background work).
47 : userInteractive,
48 : }
49 :
50 : /// iOS-specific behavior for exact time alarms.
51 : ///
52 : /// **Background**: iOS does not allow background code execution at exact times.
53 : /// This enum provides transparency and control over how exact alarms are handled on iOS.
54 : ///
55 : /// **Android**: Always executes worker code (this setting is ignored).
56 : enum ExactAlarmIOSBehavior {
57 : /// Show a local notification at the exact time (DEFAULT - Safe).
58 : ///
59 : /// **Guarantees**:
60 : /// - ✅ Notification appears at exact time (±seconds)
61 : /// - ✅ Works in Low Power Mode
62 : /// - ✅ Survives app termination
63 : ///
64 : /// **Use Cases**: Reminders, alarms, time-sensitive notifications.
65 : showNotification,
66 :
67 : /// Attempt background code execution (Best Effort - NOT GUARANTEED).
68 : ///
69 : /// **Limitations**:
70 : /// - ❌ NOT suitable for time-critical operations
71 : /// - ❌ Timing accuracy: ±minutes to ±hours
72 : /// - ❌ May not run in Low Power Mode
73 : ///
74 : /// **Use Cases**: Non-critical background sync with timing hint.
75 : attemptBackgroundRun,
76 :
77 : /// Throw exception immediately (Fail Fast - Development Safety).
78 : ///
79 : /// **Benefits**:
80 : /// - ✅ Immediate feedback during development
81 : /// - ✅ Prevents deploying code with wrong expectations
82 : /// - ✅ Forces platform-aware design
83 : ///
84 : /// **Use Cases**: Development/testing, critical operations that require exact timing.
85 : throwError,
86 : }
87 :
88 : /// System-level constraints for task execution (Android only).
89 : ///
90 : /// **New in KMP WorkManager 2.2.0+**: SystemConstraints provide a cleaner way to
91 : /// specify system-level requirements. These replace the deprecated trigger-based
92 : /// approach (TaskTrigger.batteryLow, etc.) and the individual boolean flags.
93 : ///
94 : /// **Platform Support**: Android only. iOS ignores these constraints.
95 : ///
96 : /// ## Basic Usage
97 : ///
98 : /// ```dart
99 : /// await NativeWorkManager.enqueue(
100 : /// taskId: 'maintenance-task',
101 : /// trigger: TaskTrigger.oneTime(),
102 : /// worker: DartWorker(callbackId: 'cleanup'),
103 : /// constraints: Constraints(
104 : /// systemConstraints: {
105 : /// SystemConstraint.deviceIdle, // Run when device is idle
106 : /// SystemConstraint.allowLowStorage, // OK to run on low storage
107 : /// },
108 : /// ),
109 : /// );
110 : /// ```
111 : ///
112 : /// ## Constraint Types
113 : ///
114 : /// **Storage Constraints:**
115 : /// - `allowLowStorage` - Task can run even when storage is low
116 : /// - Default behavior: Task waits for sufficient storage
117 : ///
118 : /// **Battery Constraints:**
119 : /// - `allowLowBattery` - Task can run even when battery is low
120 : /// - `requireBatteryNotLow` - Task requires battery level to not be low
121 : /// - Default behavior: No battery restriction
122 : ///
123 : /// **System State:**
124 : /// - `deviceIdle` - Task requires device to be idle (screen off, no user interaction)
125 : /// - Use for maintenance tasks that should not impact user experience
126 : ///
127 : /// ## Migration from Old API
128 : ///
129 : /// **Before (deprecated triggers):**
130 : /// ```dart
131 : /// // OLD - deprecated in v2.2.0
132 : /// trigger: TaskTrigger.storageLow, // or batteryLow, deviceIdle
133 : /// ```
134 : ///
135 : /// **After (SystemConstraints):**
136 : /// ```dart
137 : /// // NEW - recommended approach
138 : /// constraints: Constraints(
139 : /// systemConstraints: {SystemConstraint.allowLowStorage},
140 : /// )
141 : /// ```
142 : ///
143 : /// **Before (boolean flags):**
144 : /// ```dart
145 : /// // OLD - still works but less flexible
146 : /// constraints: Constraints(
147 : /// requiresStorageNotLow: true,
148 : /// requiresBatteryNotLow: true,
149 : /// requiresDeviceIdle: true,
150 : /// )
151 : /// ```
152 : ///
153 : /// **After (SystemConstraints - more explicit):**
154 : /// ```dart
155 : /// // NEW - clearer intent
156 : /// constraints: Constraints(
157 : /// systemConstraints: {
158 : /// SystemConstraint.deviceIdle,
159 : /// SystemConstraint.requireBatteryNotLow,
160 : /// },
161 : /// )
162 : /// ```
163 : ///
164 : /// ## Common Patterns
165 : ///
166 : /// **Maintenance Task (idle device, low priority):**
167 : /// ```dart
168 : /// Constraints(
169 : /// systemConstraints: {
170 : /// SystemConstraint.deviceIdle,
171 : /// SystemConstraint.allowLowStorage,
172 : /// SystemConstraint.allowLowBattery,
173 : /// },
174 : /// qos: QoS.utility,
175 : /// )
176 : /// ```
177 : ///
178 : /// **Critical Task (needs resources):**
179 : /// ```dart
180 : /// Constraints(
181 : /// systemConstraints: {
182 : /// SystemConstraint.requireBatteryNotLow,
183 : /// },
184 : /// requiresNetwork: true,
185 : /// requiresCharging: true,
186 : /// )
187 : /// ```
188 : ///
189 : /// **Background Sync (opportunistic):**
190 : /// ```dart
191 : /// Constraints(
192 : /// systemConstraints: {
193 : /// SystemConstraint.allowLowBattery, // Run even on low battery
194 : /// },
195 : /// requiresNetwork: true,
196 : /// )
197 : /// ```
198 : ///
199 : /// ## Platform Behavior
200 : ///
201 : /// **Android:**
202 : /// - Maps to WorkManager's SystemConstraint API
203 : /// - Enforced by Android WorkManager
204 : /// - Affects task scheduling and execution
205 : ///
206 : /// **iOS:**
207 : /// - Ignored (not applicable to iOS background tasks)
208 : /// - iOS has different constraint system
209 : /// - Use iOS-specific constraints like `requiresCharging` instead
210 : ///
211 : /// ## Best Practices
212 : ///
213 : /// ✅ **Do** use SystemConstraints for explicit intent
214 : /// ✅ **Do** combine with other constraints (network, charging)
215 : /// ✅ **Do** use deviceIdle for maintenance tasks
216 : /// ✅ **Do** use allowLowStorage/allowLowBattery for non-critical tasks
217 : ///
218 : /// ❌ **Don't** mix old triggers with new SystemConstraints
219 : /// ❌ **Don't** expect SystemConstraints to work on iOS
220 : /// ❌ **Don't** use deviceIdle for user-initiated tasks
221 : ///
222 : /// See also:
223 : /// - [Constraints] - Container for all constraint types
224 : /// - [TaskTrigger] - When tasks should execute
225 : enum SystemConstraint {
226 : /// Allow task to run even when storage is low (Android only).
227 : ///
228 : /// Use this for tasks that:
229 : /// - Don't require much storage
230 : /// - Can handle low-storage conditions gracefully
231 : /// - Are not storage-intensive
232 : ///
233 : /// **Example**: Small API sync, log cleanup.
234 : allowLowStorage,
235 :
236 : /// Allow task to run even when battery is low (Android only).
237 : ///
238 : /// Use this for tasks that:
239 : /// - Are lightweight and quick
240 : /// - Don't drain battery significantly
241 : /// - Can tolerate battery constraints
242 : ///
243 : /// **Example**: Quick sync, small uploads.
244 : allowLowBattery,
245 :
246 : /// Require battery level to not be low (Android only).
247 : ///
248 : /// Task will wait until battery is above low threshold (~15%).
249 : ///
250 : /// Use this for tasks that:
251 : /// - Are battery-intensive
252 : /// - Should not drain a low battery further
253 : /// - Can wait for charging
254 : ///
255 : /// **Example**: Large file processing, heavy computation.
256 : requireBatteryNotLow,
257 :
258 : /// Require device to be idle (Android only).
259 : ///
260 : /// Device is considered idle when:
261 : /// - Screen is off
262 : /// - No user interaction
263 : /// - Device has been idle for a period
264 : ///
265 : /// Use this for tasks that:
266 : /// - Are low priority
267 : /// - Should not impact user experience
268 : /// - Can run overnight or during idle periods
269 : ///
270 : /// **Example**: Database optimization, cache cleanup, maintenance.
271 : deviceIdle,
272 : }
273 :
274 : /// iOS background task type selection (iOS 13.0+ only).
275 : ///
276 : /// **New in KMP WorkManager 2.2.0+**: Control which iOS BGTask type is used
277 : /// for background execution. Each type has different time limits and capabilities.
278 : ///
279 : /// **Platform Support**: iOS only. Android ignores this setting.
280 : ///
281 : /// ## Task Types
282 : ///
283 : /// **BGAppRefreshTask (appRefresh):**
284 : /// - Time limit: ~30 seconds total
285 : /// - Task timeout: 20 seconds
286 : /// - Chain timeout: 50 seconds
287 : /// - Use case: Quick sync, small updates, lightweight operations
288 : /// - Frequency: System-determined (multiple times per day)
289 : ///
290 : /// **BGProcessingTask (processing):**
291 : /// - Time limit: 5-10 minutes
292 : /// - Task timeout: 120 seconds (2 minutes)
293 : /// - Chain timeout: 300 seconds (5 minutes)
294 : /// - Use case: Heavy processing, large downloads/uploads, data migration
295 : /// - Frequency: Less frequent (overnight, device charging)
296 : /// - Requires: `requiresCharging` or `requiresNetwork` in constraints
297 : ///
298 : /// ## Auto-Selection Behavior
299 : ///
300 : /// If `bgTaskType` is **not specified** (null), the system auto-selects based on:
301 : /// - `isHeavyTask = true` → BGProcessingTask
302 : /// - `isHeavyTask = false` → BGAppRefreshTask
303 : ///
304 : /// ## Basic Usage
305 : ///
306 : /// **Auto-Selection (Recommended):**
307 : /// ```dart
308 : /// // System chooses based on isHeavyTask
309 : /// await NativeWorkManager.enqueue(
310 : /// taskId: 'sync',
311 : /// trigger: TaskTrigger.oneTime(),
312 : /// worker: NativeWorker.httpSync(url: 'https://api.example.com/sync'),
313 : /// constraints: Constraints(
314 : /// isHeavyTask: false, // → BGAppRefreshTask
315 : /// ),
316 : /// );
317 : /// ```
318 : ///
319 : /// **Manual Selection:**
320 : /// ```dart
321 : /// // Force BGAppRefreshTask for quick sync
322 : /// await NativeWorkManager.enqueue(
323 : /// taskId: 'quick-sync',
324 : /// trigger: TaskTrigger.oneTime(),
325 : /// worker: NativeWorker.httpSync(url: 'https://api.example.com/sync'),
326 : /// constraints: Constraints(
327 : /// bgTaskType: BGTaskType.appRefresh, // Explicit
328 : /// requiresNetwork: true,
329 : /// ),
330 : /// );
331 : /// ```
332 : ///
333 : /// ## When to Use Each Type
334 : ///
335 : /// **Use appRefresh for:**
336 : /// - Quick API sync (<30s)
337 : /// - Small data uploads
338 : /// - Fast health checks
339 : /// - Lightweight maintenance
340 : /// - Frequent operations
341 : ///
342 : /// ```dart
343 : /// Constraints(
344 : /// bgTaskType: BGTaskType.appRefresh,
345 : /// requiresNetwork: true,
346 : /// )
347 : /// ```
348 : ///
349 : /// **Use processing for:**
350 : /// - Large file uploads/downloads (minutes)
351 : /// - Video/image processing
352 : /// - Database migrations
353 : /// - ML model inference
354 : /// - Batch operations
355 : ///
356 : /// ```dart
357 : /// Constraints(
358 : /// bgTaskType: BGTaskType.processing,
359 : /// requiresNetwork: true,
360 : /// requiresCharging: true, // Recommended
361 : /// isHeavyTask: true,
362 : /// )
363 : /// ```
364 : ///
365 : /// ## Time-Slicing (KMP 2.2.1+)
366 : ///
367 : /// For large queues, KMP WorkManager automatically:
368 : /// - Uses 85% of available time for tasks
369 : /// - Reserves 15% for cleanup
370 : /// - Stops early when time is insufficient
371 : /// - Schedules continuation for remaining tasks
372 : ///
373 : /// ## Info.plist Configuration
374 : ///
375 : /// Both task types must be declared in Info.plist:
376 : ///
377 : /// ```xml
378 : /// <key>BGTaskSchedulerPermittedIdentifiers</key>
379 : /// <array>
380 : /// <string>dev.brewkits.kmpworkmanager.refresh</string>
381 : /// <string>dev.brewkits.kmpworkmanager.processing</string>
382 : /// </array>
383 : /// ```
384 : ///
385 : /// ## Platform Behavior
386 : ///
387 : /// **iOS:**
388 : /// - Maps to BGTaskScheduler API
389 : /// - Time limits strictly enforced by iOS
390 : /// - Processing tasks run less frequently
391 : /// - System decides actual execution timing
392 : ///
393 : /// **Android:**
394 : /// - Setting is ignored (not applicable)
395 : /// - Android WorkManager uses different scheduling
396 : /// - Use `isHeavyTask` for foreground service on Android
397 : ///
398 : /// ## Common Pitfalls
399 : ///
400 : /// ❌ **Don't** use processing for quick operations (wastes battery)
401 : /// ❌ **Don't** use appRefresh for operations >30 seconds (will fail)
402 : /// ❌ **Don't** forget Info.plist configuration (tasks won't run)
403 : /// ❌ **Don't** rely on exact timing (iOS schedules opportunistically)
404 : ///
405 : /// ✅ **Do** prefer auto-selection (leave null, set isHeavyTask)
406 : /// ✅ **Do** handle task interruption gracefully
407 : /// ✅ **Do** use processing for long operations
408 : /// ✅ **Do** test with actual background conditions
409 : ///
410 : /// ## Best Practices
411 : ///
412 : /// 1. **Default to auto-selection**: Let `isHeavyTask` control type
413 : /// 2. **Test timeouts**: Verify operations complete within limits
414 : /// 3. **Handle interruption**: Save progress if task is stopped early
415 : /// 4. **Monitor metrics**: Use TaskEventBus for execution time tracking
416 : /// 5. **Optimize for appRefresh**: Most tasks should complete in 30s
417 : ///
418 : /// ## See Also
419 : ///
420 : /// - [Constraints.isHeavyTask] - Auto-selects BGProcessingTask
421 : /// - [TaskTrigger] - When tasks execute
422 : /// - KMP WorkManager: BGTaskType enum
423 : enum BGTaskType {
424 : /// BGAppRefreshTask - Quick operations (~30 seconds).
425 : ///
426 : /// **Limits**:
427 : /// - Total time: ~30 seconds
428 : /// - Task timeout: 20 seconds
429 : /// - Chain timeout: 50 seconds
430 : ///
431 : /// **Use Cases**: Quick sync, small uploads, health checks.
432 : ///
433 : /// **Frequency**: Multiple times per day (system-determined).
434 : appRefresh,
435 :
436 : /// BGProcessingTask - Heavy operations (5-10 minutes).
437 : ///
438 : /// **Limits**:
439 : /// - Total time: 5-10 minutes
440 : /// - Task timeout: 120 seconds
441 : /// - Chain timeout: 300 seconds
442 : ///
443 : /// **Use Cases**: Large downloads, video processing, migrations.
444 : ///
445 : /// **Frequency**: Less frequent (overnight, charging).
446 : ///
447 : /// **Requirements**: Needs `requiresCharging` or `requiresNetwork`.
448 : processing,
449 : }
450 :
451 : /// Android 14+ foreground service type for heavy tasks.
452 : ///
453 : /// **Available in native_workmanager 1.0.0+** (introduced in 0.8.0): Android 14 (API 34+)
454 : /// requires explicit foreground service types for apps targeting SDK 34+.
455 : ///
456 : /// **Platform Support**: Android 14+ only. iOS and Android <14 ignore this setting.
457 : ///
458 : /// **Applies To**: Only relevant when `isHeavyTask = true`. Heavy tasks run as
459 : /// foreground services with a persistent notification.
460 : ///
461 : /// ## Service Types
462 : ///
463 : /// Each type requires corresponding permissions in AndroidManifest.xml.
464 : ///
465 : /// **dataSync (default):**
466 : /// - Use case: Background data upload/download, API sync, database operations
467 : /// - Permissions: None (safe default)
468 : /// - Validation: Always passes on Chinese ROMs (FAIL OPEN strategy)
469 : ///
470 : /// **location:**
471 : /// - Use case: GPS tracking, location-based services
472 : /// - Permissions: `ACCESS_FINE_LOCATION` or `ACCESS_COARSE_LOCATION`
473 : /// - Manifest: `<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>`
474 : ///
475 : /// **mediaPlayback:**
476 : /// - Use case: Audio/video playback, media processing
477 : /// - Permissions: None
478 : /// - Manifest: `<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>`
479 : ///
480 : /// **camera:**
481 : /// - Use case: Camera operations, photo/video capture
482 : /// - Permissions: `CAMERA`
483 : /// - Manifest: `<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>`
484 : ///
485 : /// **microphone:**
486 : /// - Use case: Audio recording, voice processing
487 : /// - Permissions: `RECORD_AUDIO`
488 : /// - Manifest: `<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>`
489 : ///
490 : /// **health:**
491 : /// - Use case: Health/fitness data collection
492 : /// - Permissions: `BODY_SENSORS`, `HIGH_SAMPLING_RATE_SENSORS` (if applicable)
493 : /// - Manifest: `<uses-permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH"/>`
494 : ///
495 : /// ## Basic Usage
496 : ///
497 : /// **Default (dataSync):**
498 : /// ```dart
499 : /// await NativeWorkManager.enqueue(
500 : /// taskId: 'large-upload',
501 : /// trigger: TaskTrigger.oneTime(),
502 : /// worker: NativeWorker.httpUpload(
503 : /// url: 'https://cdn.example.com/upload',
504 : /// filePath: '/path/to/file.zip',
505 : /// ),
506 : /// constraints: Constraints(
507 : /// isHeavyTask: true, // Uses dataSync by default
508 : /// requiresNetwork: true,
509 : /// ),
510 : /// );
511 : /// ```
512 : ///
513 : /// **Location Tracking:**
514 : /// ```dart
515 : /// await NativeWorkManager.enqueue(
516 : /// taskId: 'gps-tracker',
517 : /// trigger: TaskTrigger.periodic(Duration(minutes: 15)),
518 : /// worker: DartWorker(callbackId: 'trackLocation'),
519 : /// constraints: Constraints(
520 : /// isHeavyTask: true,
521 : /// foregroundServiceType: ForegroundServiceType.location,
522 : /// requiresNetwork: true,
523 : /// ),
524 : /// );
525 : /// ```
526 : ///
527 : /// **Media Processing:**
528 : /// ```dart
529 : /// await NativeWorkManager.enqueue(
530 : /// taskId: 'video-encode',
531 : /// trigger: TaskTrigger.oneTime(),
532 : /// worker: DartWorker(callbackId: 'encodeVideo'),
533 : /// constraints: Constraints(
534 : /// isHeavyTask: true,
535 : /// foregroundServiceType: ForegroundServiceType.mediaPlayback,
536 : /// requiresCharging: true,
537 : /// ),
538 : /// );
539 : /// ```
540 : ///
541 : /// ## AndroidManifest.xml Configuration
542 : ///
543 : /// Add permission for the service type you're using:
544 : ///
545 : /// ```xml
546 : /// <manifest>
547 : /// <!-- For location tasks -->
548 : /// <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
549 : /// <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
550 : ///
551 : /// <!-- For media tasks -->
552 : /// <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
553 : ///
554 : /// <!-- For camera tasks -->
555 : /// <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>
556 : /// <uses-permission android:name="android.permission.CAMERA"/>
557 : ///
558 : /// <!-- For microphone tasks -->
559 : /// <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
560 : /// <uses-permission android:name="android.permission.RECORD_AUDIO"/>
561 : ///
562 : /// <!-- For health tasks -->
563 : /// <uses-permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH"/>
564 : /// <uses-permission android:name="android.permission.BODY_SENSORS"/>
565 : /// </manifest>
566 : /// ```
567 : ///
568 : /// ## Validation Strategy (KMP 2.1.2+)
569 : ///
570 : /// KMP WorkManager uses **FAIL OPEN** validation:
571 : /// - Validates permissions on Android 14+ (API 34+)
572 : /// - Falls back to `dataSync` if validation fails
573 : /// - Ensures compatibility with Chinese ROMs (Xiaomi, Oppo, etc.)
574 : /// - Never crashes due to missing permissions
575 : ///
576 : /// ## Platform Behavior
577 : ///
578 : /// **Android 14+ (API 34+):**
579 : /// - Foreground service type is required and validated
580 : /// - Missing permissions → fallback to dataSync
581 : /// - Notification shows during task execution
582 : ///
583 : /// **Android <14:**
584 : /// - Setting is ignored (not required)
585 : /// - Heavy tasks still run as foreground services
586 : /// - Any type works without validation
587 : ///
588 : /// **iOS:**
589 : /// - Setting is completely ignored
590 : /// - iOS uses BGTaskScheduler (no foreground services)
591 : /// - Use `bgTaskType` for iOS task type selection
592 : ///
593 : /// ## When to Use Each Type
594 : ///
595 : /// | Type | Use Case | Permissions Required |
596 : /// |------|----------|----------------------|
597 : /// | dataSync | Default, file upload/download | None |
598 : /// | location | GPS tracking, location services | FINE or COARSE location |
599 : /// | mediaPlayback | Audio/video playback | None |
600 : /// | camera | Camera operations | CAMERA |
601 : /// | microphone | Audio recording | RECORD_AUDIO |
602 : /// | health | Health/fitness data | BODY_SENSORS |
603 : ///
604 : /// ## Common Pitfalls
605 : ///
606 : /// ❌ **Don't** forget manifest permissions (task will fall back to dataSync)
607 : /// ❌ **Don't** use without `isHeavyTask = true` (no effect)
608 : /// ❌ **Don't** expect exact type on Chinese ROMs (FAIL OPEN)
609 : /// ❌ **Don't** use for tasks <30 seconds (foreground overhead)
610 : ///
611 : /// ✅ **Do** add manifest permissions for your service type
612 : /// ✅ **Do** use dataSync as safe default for uploads/downloads
613 : /// ✅ **Do** combine with appropriate constraints
614 : /// ✅ **Do** test on Android 14+ devices
615 : ///
616 : /// ## Best Practices
617 : ///
618 : /// 1. **Default to dataSync**: Safe for most heavy tasks
619 : /// 2. **Add manifest entries**: Always declare required permissions
620 : /// 3. **Test on Android 14+**: Verify behavior on modern devices
621 : /// 4. **Handle fallback**: App works even with wrong type (falls back to dataSync)
622 : /// 5. **Use specific types**: Only when actually accessing camera/location/etc.
623 : ///
624 : /// ## See Also
625 : ///
626 : /// - [Constraints.isHeavyTask] - Enables foreground service
627 : /// - [BGTaskType] - iOS equivalent for task type selection
628 : /// - KMP WorkManager: ForegroundServiceType and validation
629 : enum ForegroundServiceType {
630 : /// Data synchronization (DEFAULT - Safe for all heavy tasks).
631 : ///
632 : /// **Use Case**: File uploads/downloads, API sync, database operations.
633 : ///
634 : /// **Permissions**: None required.
635 : ///
636 : /// **Validation**: Always passes (FAIL OPEN strategy).
637 : ///
638 : /// **Best for**: Most heavy tasks (default choice).
639 : dataSync,
640 :
641 : /// Location tracking and GPS services.
642 : ///
643 : /// **Use Case**: GPS tracking, location-based services, geofencing.
644 : ///
645 : /// **Permissions Required**:
646 : /// - `ACCESS_FINE_LOCATION` or `ACCESS_COARSE_LOCATION`
647 : /// - `FOREGROUND_SERVICE_LOCATION` (Android 14+)
648 : ///
649 : /// **Manifest**:
650 : /// ```xml
651 : /// <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
652 : /// ```
653 : location,
654 :
655 : /// Audio/video playback and media processing.
656 : ///
657 : /// **Use Case**: Audio playback, video encoding, media streaming.
658 : ///
659 : /// **Permissions Required**:
660 : /// - `FOREGROUND_SERVICE_MEDIA_PLAYBACK` (Android 14+)
661 : ///
662 : /// **Manifest**:
663 : /// ```xml
664 : /// <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
665 : /// ```
666 : mediaPlayback,
667 :
668 : /// Camera operations and photo/video capture.
669 : ///
670 : /// **Use Case**: Camera capture, photo processing, AR applications.
671 : ///
672 : /// **Permissions Required**:
673 : /// - `CAMERA`
674 : /// - `FOREGROUND_SERVICE_CAMERA` (Android 14+)
675 : ///
676 : /// **Manifest**:
677 : /// ```xml
678 : /// <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>
679 : /// <uses-permission android:name="android.permission.CAMERA"/>
680 : /// ```
681 : camera,
682 :
683 : /// Microphone and audio recording.
684 : ///
685 : /// **Use Case**: Audio recording, voice recognition, speech processing.
686 : ///
687 : /// **Permissions Required**:
688 : /// - `RECORD_AUDIO`
689 : /// - `FOREGROUND_SERVICE_MICROPHONE` (Android 14+)
690 : ///
691 : /// **Manifest**:
692 : /// ```xml
693 : /// <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
694 : /// <uses-permission android:name="android.permission.RECORD_AUDIO"/>
695 : /// ```
696 : microphone,
697 :
698 : /// Health and fitness data collection.
699 : ///
700 : /// **Use Case**: Health tracking, fitness monitoring, sensor data.
701 : ///
702 : /// **Permissions Required**:
703 : /// - `BODY_SENSORS`
704 : /// - `HIGH_SAMPLING_RATE_SENSORS` (if applicable)
705 : /// - `FOREGROUND_SERVICE_HEALTH` (Android 14+)
706 : ///
707 : /// **Manifest**:
708 : /// ```xml
709 : /// <uses-permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH"/>
710 : /// <uses-permission android:name="android.permission.BODY_SENSORS"/>
711 : /// ```
712 : health,
713 : }
714 :
715 : /// Constraints that must be met before a task can run.
716 : ///
717 : /// Constraints optimize battery life and ensure tasks run under appropriate
718 : /// conditions. The OS will defer task execution until all constraints are met.
719 : ///
720 : /// ## Basic Examples
721 : ///
722 : /// **Network Required:**
723 : /// ```dart
724 : /// await NativeWorkManager.enqueue(
725 : /// taskId: 'api-sync',
726 : /// trigger: TaskTrigger.periodic(Duration(hours: 1)),
727 : /// worker: NativeWorker.httpSync(url: 'https://api.example.com/sync'),
728 : /// constraints: Constraints.networkRequired,
729 : /// );
730 : /// ```
731 : ///
732 : /// **WiFi + Charging (Heavy Task):**
733 : /// ```dart
734 : /// await NativeWorkManager.enqueue(
735 : /// taskId: 'video-upload',
736 : /// trigger: TaskTrigger.oneTime(),
737 : /// worker: NativeWorker.httpUpload(
738 : /// url: 'https://cdn.example.com/videos',
739 : /// filePath: '/path/to/video.mp4',
740 : /// ),
741 : /// constraints: Constraints.heavyTask,
742 : /// );
743 : /// ```
744 : ///
745 : /// **Custom Constraints:**
746 : /// ```dart
747 : /// await NativeWorkManager.enqueue(
748 : /// taskId: 'db-cleanup',
749 : /// trigger: TaskTrigger.periodic(Duration(days: 1)),
750 : /// worker: DartWorker(callbackId: 'cleanupDatabase'),
751 : /// constraints: Constraints(
752 : /// requiresDeviceIdle: true,
753 : /// requiresBatteryNotLow: true,
754 : /// requiresStorageNotLow: true,
755 : /// ),
756 : /// );
757 : /// ```
758 : ///
759 : /// ## Common Constraint Patterns
760 : ///
761 : /// **Data Sync (Network Required):**
762 : /// ```dart
763 : /// Constraints(requiresNetwork: true)
764 : /// ```
765 : ///
766 : /// **Large Upload (WiFi + Battery Safe):**
767 : /// ```dart
768 : /// Constraints(
769 : /// requiresUnmeteredNetwork: true,
770 : /// requiresCharging: true,
771 : /// isHeavyTask: true,
772 : /// )
773 : /// ```
774 : ///
775 : /// **Maintenance Task (Device Idle):**
776 : /// ```dart
777 : /// Constraints(
778 : /// requiresDeviceIdle: true,
779 : /// requiresBatteryNotLow: true,
780 : /// )
781 : /// ```
782 : ///
783 : /// **Critical Task (No Constraints):**
784 : /// ```dart
785 : /// Constraints.none // or Constraints()
786 : /// ```
787 : ///
788 : /// ## Static Helpers
789 : ///
790 : /// - [networkRequired] - Simple network requirement
791 : /// - [heavyTask] - WiFi + charging for large operations
792 : /// - [none] - No constraints (runs ASAP)
793 : ///
794 : /// ## When to Use Constraints
795 : ///
796 : /// ✅ **Use constraints for:**
797 : /// - Network-dependent operations (API calls, uploads)
798 : /// - Battery-intensive tasks (video processing)
799 : /// - Storage-intensive operations (downloads, caching)
800 : /// - Maintenance work (cleanup, optimization)
801 : ///
802 : /// ❌ **Don't use constraints for:**
803 : /// - Time-critical operations
804 : /// - User-initiated actions (use no constraints)
805 : /// - Tasks that must run immediately
806 : ///
807 : /// ## Battery Impact
808 : ///
809 : /// More constraints = Better battery life:
810 : /// - Tasks deferred until optimal conditions
811 : /// - OS can batch work together
812 : /// - Prevents running on cellular data
813 : /// - Avoids draining battery
814 : ///
815 : /// ## Platform Differences
816 : ///
817 : /// **Android:**
818 : /// - All constraints enforced by WorkManager
819 : /// - Very reliable constraint checking
820 : /// - Can combine multiple constraints
821 : ///
822 : /// **iOS:**
823 : /// - Constraints are advisory (not strictly enforced)
824 : /// - Best effort by BGTaskScheduler
825 : /// - Some constraints (requiresDeviceIdle) not available
826 : ///
827 : /// ## See Also
828 : ///
829 : /// - [BackoffPolicy] - Retry behavior on failure
830 : /// - [QoS] - iOS task priority
831 : /// - [ExactAlarmIOSBehavior] - iOS exact alarm handling
832 : @immutable
833 : class Constraints {
834 10 : const Constraints({
835 : this.requiresNetwork = false,
836 : this.requiresUnmeteredNetwork = false,
837 : this.requiresCharging = false,
838 : this.requiresDeviceIdle = false,
839 : this.requiresBatteryNotLow = false,
840 : this.requiresStorageNotLow = false,
841 : this.allowWhileIdle = false,
842 : this.isHeavyTask = false,
843 : this.qos = QoS.background,
844 : this.exactAlarmIOSBehavior = ExactAlarmIOSBehavior.showNotification,
845 : this.backoffPolicy = BackoffPolicy.exponential,
846 : this.backoffDelayMs = 30000,
847 : this.maxRetries = 3,
848 : this.systemConstraints = const {},
849 : this.bgTaskType,
850 : this.foregroundServiceType,
851 : });
852 :
853 : /// Task requires any network connection.
854 : final bool requiresNetwork;
855 :
856 : /// Task requires unmetered (WiFi) network.
857 : final bool requiresUnmeteredNetwork;
858 :
859 : /// Task requires device to be charging.
860 : final bool requiresCharging;
861 :
862 : /// Task requires device to be idle. (Android only)
863 : final bool requiresDeviceIdle;
864 :
865 : /// Task requires battery level to not be low. (Android only)
866 : final bool requiresBatteryNotLow;
867 :
868 : /// Task requires storage to not be low. (Android only)
869 : final bool requiresStorageNotLow;
870 :
871 : /// Allow task to run during Doze mode. (Android only)
872 : final bool allowWhileIdle;
873 :
874 : /// Indicates this is a long-running or heavy task requiring special handling.
875 : ///
876 : /// **Android**: Uses ForegroundService with persistent notification.
877 : /// - Task can run indefinitely while service is foreground
878 : /// - Prevents system from killing the task
879 : /// - Shows persistent notification to user
880 : ///
881 : /// **iOS**: Uses BGProcessingTask (≤60s) instead of BGAppRefreshTask (≤30s).
882 : /// - Double the execution time limit
883 : /// - Better for CPU-intensive work
884 : ///
885 : /// **Use Cases**: File upload, video processing, data migration.
886 : final bool isHeavyTask;
887 :
888 : /// Quality of Service hint for task priority (iOS only).
889 : ///
890 : /// **iOS**: Maps to DispatchQoS for task execution priority.
891 : /// **Android**: Ignored (WorkManager handles priority automatically).
892 : ///
893 : /// Default: [QoS.background]
894 : final QoS qos;
895 :
896 : /// iOS-specific behavior for exact time alarms.
897 : ///
898 : /// Determines how [TaskTrigger.exact()] is handled on iOS,
899 : /// since iOS does not support background code execution at exact times.
900 : ///
901 : /// **Android**: This field is ignored (Android always executes worker code).
902 : ///
903 : /// Default: [ExactAlarmIOSBehavior.showNotification]
904 : final ExactAlarmIOSBehavior exactAlarmIOSBehavior;
905 :
906 : /// Backoff policy when task fails and needs retry (Android only).
907 : ///
908 : /// **Android**: Determines retry behavior for failed WorkManager tasks.
909 : /// - [BackoffPolicy.exponential]: Delay doubles after each retry (30s, 60s, 120s, ...)
910 : /// - [BackoffPolicy.linear]: Constant delay between retries
911 : ///
912 : /// **iOS**: Not applicable (manual retry required).
913 : ///
914 : /// Default: [BackoffPolicy.exponential]
915 : final BackoffPolicy backoffPolicy;
916 :
917 : /// Initial backoff delay in milliseconds when task fails (Android only).
918 : ///
919 : /// **Android**: Starting delay before first retry.
920 : /// - Minimum: 10,000ms (10 seconds)
921 : /// - Subsequent retries follow [backoffPolicy]
922 : ///
923 : /// **iOS**: Not applicable.
924 : ///
925 : /// **Example**:
926 : /// ```dart
927 : /// Constraints(
928 : /// backoffPolicy: BackoffPolicy.exponential,
929 : /// backoffDelayMs: 30000, // Start with 30s, then 60s, 120s, ...
930 : /// )
931 : /// ```
932 : ///
933 : /// Default: 30,000ms (30 seconds)
934 : final int backoffDelayMs;
935 :
936 : /// Maximum number of retry attempts when a task fails.
937 : ///
938 : /// **Android**: Maps to WorkManager's `setInputMerger` / run-attempt cap.
939 : /// Retries follow [backoffPolicy] and [backoffDelayMs].
940 : ///
941 : /// **iOS**: Implemented natively in the plugin's execution layer.
942 : /// Each retry respects [backoffPolicy] and [backoffDelayMs].
943 : ///
944 : /// - `0` — no retry (fail immediately on first failure)
945 : /// - `1` — try once, retry once = up to 2 total attempts
946 : /// - `3` — try once, retry up to 3 times = up to 4 total attempts (default)
947 : ///
948 : /// Default: 3
949 : final int maxRetries;
950 :
951 : /// System-level constraints for task execution (Android only).
952 : ///
953 : /// **New in KMP WorkManager 2.2.0+**: Provides cleaner way to specify
954 : /// system-level requirements than individual boolean flags.
955 : ///
956 : /// **Platform Support**: Android only. iOS ignores these constraints.
957 : ///
958 : /// **Available Constraints**:
959 : /// - [SystemConstraint.allowLowStorage] - Run even when storage is low
960 : /// - [SystemConstraint.allowLowBattery] - Run even when battery is low
961 : /// - [SystemConstraint.requireBatteryNotLow] - Wait for battery to recover
962 : /// - [SystemConstraint.deviceIdle] - Run only when device is idle
963 : ///
964 : /// **Example**:
965 : /// ```dart
966 : /// Constraints(
967 : /// systemConstraints: {
968 : /// SystemConstraint.deviceIdle,
969 : /// SystemConstraint.allowLowStorage,
970 : /// },
971 : /// )
972 : /// ```
973 : ///
974 : /// **Migration**: Prefer this over deprecated boolean flags
975 : /// (`requiresDeviceIdle`, `requiresBatteryNotLow`, `requiresStorageNotLow`).
976 : ///
977 : /// Default: Empty set (no system constraints)
978 : final Set<SystemConstraint> systemConstraints;
979 :
980 : /// iOS background task type selection (iOS 13.0+ only).
981 : ///
982 : /// **New in KMP WorkManager 2.2.0+**: Controls which iOS BGTask type is used
983 : /// for background execution (BGAppRefreshTask vs BGProcessingTask).
984 : ///
985 : /// **Platform Support**: iOS only. Android ignores this setting.
986 : ///
987 : /// **Auto-Selection**: If null (default), type is selected based on [isHeavyTask]:
988 : /// - `isHeavyTask = true` → BGProcessingTask (5-10 min limit)
989 : /// - `isHeavyTask = false` → BGAppRefreshTask (~30s limit)
990 : ///
991 : /// **Time Limits**:
992 : /// - appRefresh: ~30 seconds total (20s task, 50s chain)
993 : /// - processing: 5-10 minutes total (120s task, 300s chain)
994 : ///
995 : /// **Example** (auto-selection):
996 : /// ```dart
997 : /// Constraints(
998 : /// isHeavyTask: true, // Selects BGProcessingTask
999 : /// requiresNetwork: true,
1000 : /// )
1001 : /// ```
1002 : ///
1003 : /// **Example** (manual selection):
1004 : /// ```dart
1005 : /// Constraints(
1006 : /// bgTaskType: BGTaskType.appRefresh, // Force quick task
1007 : /// requiresNetwork: true,
1008 : /// )
1009 : /// ```
1010 : ///
1011 : /// **Best Practice**: Prefer auto-selection (leave null) unless you have
1012 : /// specific timing requirements.
1013 : ///
1014 : /// Default: null (auto-select based on isHeavyTask)
1015 : final BGTaskType? bgTaskType;
1016 :
1017 : /// Android 14+ foreground service type for heavy tasks.
1018 : ///
1019 : /// **New in KMP WorkManager 2.1.2+**: Controls the foreground service type
1020 : /// for heavy tasks on Android 14 (API 34+).
1021 : ///
1022 : /// **Platform Support**: Android 14+ only. iOS and Android <14 ignore this.
1023 : ///
1024 : /// **Applies To**: Only effective when [isHeavyTask] is `true`. Heavy tasks
1025 : /// run as foreground services with persistent notifications.
1026 : ///
1027 : /// **Available Types**:
1028 : /// - [ForegroundServiceType.dataSync] (default) - File sync, uploads, downloads
1029 : /// - [ForegroundServiceType.location] - GPS tracking, location services
1030 : /// - [ForegroundServiceType.mediaPlayback] - Audio/video playback
1031 : /// - [ForegroundServiceType.camera] - Camera operations
1032 : /// - [ForegroundServiceType.microphone] - Audio recording
1033 : /// - [ForegroundServiceType.health] - Health/fitness data
1034 : ///
1035 : /// **Permissions**: Each type requires manifest permissions (see [ForegroundServiceType]).
1036 : ///
1037 : /// **Validation**: KMP WorkManager uses FAIL OPEN strategy - falls back to
1038 : /// dataSync if validation fails. Ensures compatibility with Chinese ROMs.
1039 : ///
1040 : /// **Example** (location tracking):
1041 : /// ```dart
1042 : /// Constraints(
1043 : /// isHeavyTask: true,
1044 : /// foregroundServiceType: ForegroundServiceType.location,
1045 : /// requiresNetwork: true,
1046 : /// )
1047 : /// ```
1048 : ///
1049 : /// **Example** (default data sync):
1050 : /// ```dart
1051 : /// Constraints(
1052 : /// isHeavyTask: true, // Uses dataSync by default
1053 : /// requiresNetwork: true,
1054 : /// )
1055 : /// ```
1056 : ///
1057 : /// **Manifest Required**:
1058 : /// ```xml
1059 : /// <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
1060 : /// ```
1061 : ///
1062 : /// Default: null (uses dataSync)
1063 : final ForegroundServiceType? foregroundServiceType;
1064 :
1065 : /// Preset for network-dependent tasks.
1066 : ///
1067 : /// Use this for any task that requires network connectivity (API calls,
1068 : /// uploads, downloads, sync operations).
1069 : ///
1070 : /// **Equivalent to:** `Constraints(requiresNetwork: true)`
1071 : ///
1072 : /// ```dart
1073 : /// await NativeWorkManager.enqueue(
1074 : /// taskId: 'api-sync',
1075 : /// trigger: TaskTrigger.periodic(Duration(hours: 1)),
1076 : /// worker: NativeWorker.httpSync(url: 'https://api.example.com/sync'),
1077 : /// constraints: Constraints.networkRequired,
1078 : /// );
1079 : /// ```
1080 : static const Constraints networkRequired = Constraints(requiresNetwork: true);
1081 :
1082 : /// Preset for heavy tasks (should run on WiFi + Charging).
1083 : ///
1084 : /// Use this for battery-intensive or data-heavy operations like video uploads,
1085 : /// large file downloads, or extensive data processing. Ensures task only runs
1086 : /// when device is charging and on WiFi (not cellular).
1087 : ///
1088 : /// **Equivalent to:**
1089 : /// ```dart
1090 : /// Constraints(
1091 : /// requiresUnmeteredNetwork: true,
1092 : /// requiresCharging: true,
1093 : /// )
1094 : /// ```
1095 : ///
1096 : /// **Use cases:**
1097 : /// - Video upload/download
1098 : /// - Batch photo backup
1099 : /// - Large database sync
1100 : /// - App data backup
1101 : ///
1102 : /// ```dart
1103 : /// await NativeWorkManager.enqueue(
1104 : /// taskId: 'video-backup',
1105 : /// trigger: TaskTrigger.oneTime(),
1106 : /// worker: NativeWorker.httpUpload(
1107 : /// url: 'https://cdn.example.com/videos',
1108 : /// filePath: '/path/to/large-video.mp4',
1109 : /// ),
1110 : /// constraints: Constraints.heavyTask,
1111 : /// );
1112 : /// ```
1113 : static const Constraints heavyTask = Constraints(
1114 : requiresUnmeteredNetwork: true,
1115 : requiresCharging: true,
1116 : );
1117 :
1118 : /// No constraints - task can run anytime.
1119 : ///
1120 : /// Use this for tasks that should run as soon as possible regardless of
1121 : /// network, battery, or charging state. Be cautious as this can impact
1122 : /// battery life and use cellular data.
1123 : ///
1124 : /// **Equivalent to:** `Constraints()`
1125 : ///
1126 : /// **When to use:**
1127 : /// - Critical user-initiated actions
1128 : /// - Time-sensitive operations
1129 : /// - Emergency tasks
1130 : /// - Local-only operations (no network needed)
1131 : ///
1132 : /// **When NOT to use:**
1133 : /// - Network-dependent tasks → Use `networkRequired`
1134 : /// - Large uploads/downloads → Use `heavyTask`
1135 : /// - Background maintenance → Use custom constraints
1136 : ///
1137 : /// ```dart
1138 : /// // Critical local operation - run immediately
1139 : /// await NativeWorkManager.enqueue(
1140 : /// taskId: 'emergency-save',
1141 : /// trigger: TaskTrigger.oneTime(),
1142 : /// worker: DartWorker(callbackId: 'saveToLocalDb'),
1143 : /// constraints: Constraints.none,
1144 : /// );
1145 : /// ```
1146 : static const Constraints none = Constraints();
1147 :
1148 : /// Convert to map for platform channel.
1149 12 : Map<String, dynamic> toMap() => {
1150 6 : 'requiresNetwork': requiresNetwork,
1151 6 : 'requiresUnmeteredNetwork': requiresUnmeteredNetwork,
1152 6 : 'requiresCharging': requiresCharging,
1153 6 : 'requiresDeviceIdle': requiresDeviceIdle,
1154 6 : 'requiresBatteryNotLow': requiresBatteryNotLow,
1155 6 : 'requiresStorageNotLow': requiresStorageNotLow,
1156 6 : 'allowWhileIdle': allowWhileIdle,
1157 6 : 'isHeavyTask': isHeavyTask,
1158 12 : 'qos': qos.name,
1159 12 : 'exactAlarmIOSBehavior': exactAlarmIOSBehavior.name,
1160 12 : 'backoffPolicy': backoffPolicy.name,
1161 6 : 'backoffDelayMs': backoffDelayMs,
1162 6 : 'maxRetries': maxRetries,
1163 20 : 'systemConstraints': systemConstraints.map((c) => c.name).toList(),
1164 7 : 'bgTaskType': bgTaskType?.name,
1165 7 : 'foregroundServiceType': foregroundServiceType?.name,
1166 : };
1167 :
1168 : /// Create from map.
1169 4 : factory Constraints.fromMap(Map<String, dynamic> map) => Constraints(
1170 2 : requiresNetwork: map['requiresNetwork'] as bool? ?? false,
1171 : requiresUnmeteredNetwork:
1172 2 : map['requiresUnmeteredNetwork'] as bool? ?? false,
1173 2 : requiresCharging: map['requiresCharging'] as bool? ?? false,
1174 2 : requiresDeviceIdle: map['requiresDeviceIdle'] as bool? ?? false,
1175 2 : requiresBatteryNotLow: map['requiresBatteryNotLow'] as bool? ?? false,
1176 2 : requiresStorageNotLow: map['requiresStorageNotLow'] as bool? ?? false,
1177 2 : allowWhileIdle: map['allowWhileIdle'] as bool? ?? false,
1178 2 : isHeavyTask: map['isHeavyTask'] as bool? ?? false,
1179 2 : qos: QoS.values.firstWhere(
1180 8 : (e) => e.name == map['qos'],
1181 0 : orElse: () => QoS.background,
1182 : ),
1183 2 : exactAlarmIOSBehavior: ExactAlarmIOSBehavior.values.firstWhere(
1184 8 : (e) => e.name == map['exactAlarmIOSBehavior'],
1185 0 : orElse: () => ExactAlarmIOSBehavior.showNotification,
1186 : ),
1187 2 : backoffPolicy: BackoffPolicy.values.firstWhere(
1188 8 : (e) => e.name == map['backoffPolicy'],
1189 0 : orElse: () => BackoffPolicy.exponential,
1190 : ),
1191 2 : backoffDelayMs: map['backoffDelayMs'] as int? ?? 30000,
1192 2 : maxRetries: map['maxRetries'] as int? ?? 3,
1193 2 : systemConstraints: (map['systemConstraints'] as List<dynamic>?)
1194 3 : ?.map((name) => SystemConstraint.values
1195 1 : .where(
1196 3 : (e) => e.name == name,
1197 : )
1198 1 : .firstOrNull)
1199 2 : .whereType<SystemConstraint>()
1200 2 : .toSet() ??
1201 : {},
1202 2 : bgTaskType: map['bgTaskType'] != null
1203 : ? BGTaskType.values
1204 1 : .where(
1205 4 : (e) => e.name == map['bgTaskType'],
1206 : )
1207 1 : .firstOrNull
1208 : : null,
1209 2 : foregroundServiceType: map['foregroundServiceType'] != null
1210 : ? ForegroundServiceType.values
1211 1 : .where(
1212 4 : (e) => e.name == map['foregroundServiceType'],
1213 : )
1214 1 : .firstOrNull
1215 : : null,
1216 : );
1217 :
1218 : /// Create a copy with updated values.
1219 3 : Constraints copyWith({
1220 : bool? requiresNetwork,
1221 : bool? requiresUnmeteredNetwork,
1222 : bool? requiresCharging,
1223 : bool? requiresDeviceIdle,
1224 : bool? requiresBatteryNotLow,
1225 : bool? requiresStorageNotLow,
1226 : bool? allowWhileIdle,
1227 : bool? isHeavyTask,
1228 : QoS? qos,
1229 : ExactAlarmIOSBehavior? exactAlarmIOSBehavior,
1230 : BackoffPolicy? backoffPolicy,
1231 : int? backoffDelayMs,
1232 : int? maxRetries,
1233 : Set<SystemConstraint>? systemConstraints,
1234 : BGTaskType? bgTaskType,
1235 : ForegroundServiceType? foregroundServiceType,
1236 : }) =>
1237 3 : Constraints(
1238 3 : requiresNetwork: requiresNetwork ?? this.requiresNetwork,
1239 : requiresUnmeteredNetwork:
1240 3 : requiresUnmeteredNetwork ?? this.requiresUnmeteredNetwork,
1241 3 : requiresCharging: requiresCharging ?? this.requiresCharging,
1242 3 : requiresDeviceIdle: requiresDeviceIdle ?? this.requiresDeviceIdle,
1243 : requiresBatteryNotLow:
1244 3 : requiresBatteryNotLow ?? this.requiresBatteryNotLow,
1245 : requiresStorageNotLow:
1246 3 : requiresStorageNotLow ?? this.requiresStorageNotLow,
1247 3 : allowWhileIdle: allowWhileIdle ?? this.allowWhileIdle,
1248 1 : isHeavyTask: isHeavyTask ?? this.isHeavyTask,
1249 3 : qos: qos ?? this.qos,
1250 : exactAlarmIOSBehavior:
1251 3 : exactAlarmIOSBehavior ?? this.exactAlarmIOSBehavior,
1252 3 : backoffPolicy: backoffPolicy ?? this.backoffPolicy,
1253 3 : backoffDelayMs: backoffDelayMs ?? this.backoffDelayMs,
1254 3 : maxRetries: maxRetries ?? this.maxRetries,
1255 3 : systemConstraints: systemConstraints ?? this.systemConstraints,
1256 3 : bgTaskType: bgTaskType ?? this.bgTaskType,
1257 : foregroundServiceType:
1258 3 : foregroundServiceType ?? this.foregroundServiceType,
1259 : );
1260 :
1261 2 : @override
1262 : bool operator ==(Object other) =>
1263 : identical(this, other) ||
1264 1 : other is Constraints &&
1265 3 : requiresNetwork == other.requiresNetwork &&
1266 3 : requiresUnmeteredNetwork == other.requiresUnmeteredNetwork &&
1267 3 : requiresCharging == other.requiresCharging &&
1268 3 : requiresDeviceIdle == other.requiresDeviceIdle &&
1269 3 : requiresBatteryNotLow == other.requiresBatteryNotLow &&
1270 3 : requiresStorageNotLow == other.requiresStorageNotLow &&
1271 3 : allowWhileIdle == other.allowWhileIdle &&
1272 3 : isHeavyTask == other.isHeavyTask &&
1273 3 : qos == other.qos &&
1274 3 : exactAlarmIOSBehavior == other.exactAlarmIOSBehavior &&
1275 3 : backoffPolicy == other.backoffPolicy &&
1276 3 : backoffDelayMs == other.backoffDelayMs &&
1277 3 : maxRetries == other.maxRetries &&
1278 3 : setEquals(systemConstraints, other.systemConstraints) &&
1279 3 : bgTaskType == other.bgTaskType &&
1280 3 : foregroundServiceType == other.foregroundServiceType;
1281 :
1282 1 : @override
1283 1 : int get hashCode => Object.hash(
1284 1 : requiresNetwork,
1285 1 : requiresUnmeteredNetwork,
1286 1 : requiresCharging,
1287 1 : requiresDeviceIdle,
1288 1 : requiresBatteryNotLow,
1289 1 : requiresStorageNotLow,
1290 1 : allowWhileIdle,
1291 1 : isHeavyTask,
1292 1 : qos,
1293 1 : exactAlarmIOSBehavior,
1294 1 : backoffPolicy,
1295 1 : backoffDelayMs,
1296 1 : maxRetries,
1297 2 : Object.hashAll(systemConstraints),
1298 1 : bgTaskType,
1299 1 : foregroundServiceType,
1300 : );
1301 :
1302 0 : @override
1303 0 : String toString() => 'Constraints('
1304 0 : 'network: $requiresNetwork, '
1305 0 : 'unmetered: $requiresUnmeteredNetwork, '
1306 0 : 'charging: $requiresCharging, '
1307 0 : 'idle: $requiresDeviceIdle, '
1308 0 : 'batteryOk: $requiresBatteryNotLow, '
1309 0 : 'storageOk: $requiresStorageNotLow, '
1310 0 : 'allowIdle: $allowWhileIdle, '
1311 0 : 'heavy: $isHeavyTask, '
1312 0 : 'qos: ${qos.name}, '
1313 0 : 'iosAlarm: ${exactAlarmIOSBehavior.name}, '
1314 0 : 'backoff: ${backoffPolicy.name}, '
1315 0 : 'backoffDelay: ${backoffDelayMs}ms)';
1316 : }
|