TF-1861 Config and abstraction work manager
(cherry picked from commit ee2dca9b34a907c8356ceccf0b23efba2893a01c)
This commit is contained in:
@@ -41,6 +41,7 @@ export 'utils/config/app_config_loader.dart';
|
|||||||
export 'utils/config/app_config_parser.dart';
|
export 'utils/config/app_config_parser.dart';
|
||||||
export 'utils/config/errors.dart';
|
export 'utils/config/errors.dart';
|
||||||
export 'data/utils/compress_file_utils.dart';
|
export 'data/utils/compress_file_utils.dart';
|
||||||
|
export 'utils/platform_info.dart';
|
||||||
|
|
||||||
// Views
|
// Views
|
||||||
export 'presentation/views/text/slogan_builder.dart';
|
export 'presentation/views/text/slogan_builder.dart';
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
abstract class PlatformInfo {
|
||||||
|
static bool get isWeb => kIsWeb;
|
||||||
|
static bool get isLinux => !kIsWeb && Platform.isLinux;
|
||||||
|
static bool get isWindows => !kIsWeb && Platform.isWindows;
|
||||||
|
static bool get isMacOS => !kIsWeb && Platform.isMacOS;
|
||||||
|
static bool get isIOS => !kIsWeb && Platform.isIOS;
|
||||||
|
static bool get isAndroid => !kIsWeb && Platform.isAndroid;
|
||||||
|
static bool get isMobile => isAndroid || isIOS;
|
||||||
|
static bool get isDesktop => isLinux || isWindows || isMacOS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
import 'package:core/core.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/manager/work_dispatcher.dart';
|
||||||
|
import 'package:workmanager/workmanager.dart';
|
||||||
|
|
||||||
|
class WorkManagerConfig {
|
||||||
|
static WorkManagerConfig? _instance;
|
||||||
|
|
||||||
|
WorkManagerConfig._();
|
||||||
|
|
||||||
|
factory WorkManagerConfig() => _instance ??= WorkManagerConfig._();
|
||||||
|
|
||||||
|
Future<void> initialize() {
|
||||||
|
return Workmanager().initialize(
|
||||||
|
callbackDispatcher,
|
||||||
|
isInDebugMode: BuildUtils.isDebugMode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
class WorkManagerConstants {
|
||||||
|
static const String sendingEmailUniqueId = 'com.linagora.ios.teammail.sendingQueue';
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:core/utils/app_logger.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/scheduler/one_time_work_request.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/scheduler/periodic_work_request.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/scheduler/work_request.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/scheduler/worker_type.dart';
|
||||||
|
import 'package:workmanager/workmanager.dart';
|
||||||
|
|
||||||
|
class WorkSchedulerController {
|
||||||
|
|
||||||
|
static WorkSchedulerController? _instance;
|
||||||
|
|
||||||
|
WorkSchedulerController._();
|
||||||
|
|
||||||
|
factory WorkSchedulerController() => _instance ??= WorkSchedulerController._();
|
||||||
|
|
||||||
|
Future<void> enqueue(WorkRequest workRequest) {
|
||||||
|
if (workRequest is OneTimeWorkRequest) {
|
||||||
|
return Workmanager().registerOneOffTask(
|
||||||
|
workRequest.worker.uniqueId,
|
||||||
|
workRequest.worker.id,
|
||||||
|
tag: workRequest.worker.type.name,
|
||||||
|
initialDelay: workRequest.initialDelay,
|
||||||
|
constraints: workRequest.constraints,
|
||||||
|
backoffPolicy: workRequest.backoffPolicy,
|
||||||
|
backoffPolicyDelay: workRequest.backoffPolicyDelay,
|
||||||
|
outOfQuotaPolicy: workRequest.outOfQuotaPolicy,
|
||||||
|
inputData: workRequest.worker.inputData
|
||||||
|
);
|
||||||
|
} if (workRequest is PeriodicWorkRequest) {
|
||||||
|
return Workmanager().registerPeriodicTask(
|
||||||
|
workRequest.worker.uniqueId,
|
||||||
|
workRequest.worker.id,
|
||||||
|
frequency: workRequest.frequency,
|
||||||
|
tag: workRequest.worker.type.name,
|
||||||
|
initialDelay: workRequest.initialDelay,
|
||||||
|
constraints: workRequest.constraints,
|
||||||
|
backoffPolicy: workRequest.backoffPolicy,
|
||||||
|
backoffPolicyDelay: workRequest.backoffPolicyDelay,
|
||||||
|
outOfQuotaPolicy: workRequest.outOfQuotaPolicy,
|
||||||
|
inputData: workRequest.worker.inputData
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Future.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> handleBackgroundTask(String taskName, Map<String, dynamic>? inputData) {
|
||||||
|
if (inputData != null && inputData.isNotEmpty) {
|
||||||
|
final typeTask = inputData['workerType'];
|
||||||
|
final dataObject = inputData['data'];
|
||||||
|
log('WorkSchedulerController::handleBackgroundTask():typeTask: $typeTask | dataObject: $dataObject');
|
||||||
|
final workType = WorkerType.values.firstWhereOrNull((type) => type.name == typeTask);
|
||||||
|
log('WorkSchedulerController::handleBackgroundTask():workType: $workType');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Future.value(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> cancelByWorkType(WorkerType type) => Workmanager().cancelByTag(type.name);
|
||||||
|
|
||||||
|
Future<void> cancelByUniqueId(String uniqueId) => Workmanager().cancelByUniqueName(uniqueId);
|
||||||
|
|
||||||
|
Future<void> cancelAll() => Workmanager().cancelAll();
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/controller/work_scheduler_controller.dart';
|
||||||
|
import 'package:workmanager/workmanager.dart';
|
||||||
|
|
||||||
|
@pragma('vm:entry-point')
|
||||||
|
void callbackDispatcher() {
|
||||||
|
Workmanager().executeTask(WorkSchedulerController().handleBackgroundTask);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/observer/work_observer.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/scheduler/work_status.dart';
|
||||||
|
|
||||||
|
class SendingEmailObserver extends WorkObserver<Email> {
|
||||||
|
|
||||||
|
static SendingEmailObserver? _instance;
|
||||||
|
|
||||||
|
SendingEmailObserver._();
|
||||||
|
|
||||||
|
factory SendingEmailObserver() => _instance ??= SendingEmailObserver._();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void handleFailureViewState(Failure failure) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void handleSuccessViewState(Success success) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
WorkStatus observe(Email value) {
|
||||||
|
return WorkStatus.failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:core/utils/app_logger.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/scheduler/work_status.dart';
|
||||||
|
|
||||||
|
abstract class WorkObserver<T> {
|
||||||
|
WorkStatus observe(T value);
|
||||||
|
|
||||||
|
void consumeState(Stream<Either<Failure, Success>> newStateStream) {
|
||||||
|
newStateStream.listen(
|
||||||
|
_handleStateStream,
|
||||||
|
onError: (error, stackTrace) {
|
||||||
|
logError('WorkObserver::consumeState():onError:error: $error');
|
||||||
|
logError('WorkObserver::consumeState():onError:stackTrace: $stackTrace');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleStateStream(Either<Failure, Success> newState) {
|
||||||
|
newState.fold(handleFailureViewState, handleSuccessViewState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleFailureViewState(Failure failure);
|
||||||
|
|
||||||
|
void handleSuccessViewState(Success success);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/scheduler/work_request.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/scheduler/worker.dart';
|
||||||
|
import 'package:workmanager/workmanager.dart';
|
||||||
|
|
||||||
|
/// A WorkRequest for non-repeating work.
|
||||||
|
class OneTimeWorkRequest extends WorkRequest {
|
||||||
|
OneTimeWorkRequest(
|
||||||
|
Worker worker, {
|
||||||
|
Duration initialDelay = Duration.zero,
|
||||||
|
Duration backoffPolicyDelay = Duration.zero,
|
||||||
|
ExistingWorkPolicy? existingWorkPolicy,
|
||||||
|
BackoffPolicy? backoffPolicy,
|
||||||
|
OutOfQuotaPolicy? outOfQuotaPolicy,
|
||||||
|
Constraints? constraints
|
||||||
|
}) : super(
|
||||||
|
worker,
|
||||||
|
initialDelay: initialDelay,
|
||||||
|
backoffPolicyDelay: backoffPolicyDelay,
|
||||||
|
existingWorkPolicy: existingWorkPolicy,
|
||||||
|
backoffPolicy: backoffPolicy,
|
||||||
|
outOfQuotaPolicy: outOfQuotaPolicy,
|
||||||
|
constraints: constraints
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/scheduler/work_request.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/scheduler/worker.dart';
|
||||||
|
import 'package:workmanager/workmanager.dart';
|
||||||
|
|
||||||
|
/// A WorkRequest for repeating work.
|
||||||
|
class PeriodicWorkRequest extends WorkRequest {
|
||||||
|
final Duration? frequency;
|
||||||
|
|
||||||
|
PeriodicWorkRequest(
|
||||||
|
Worker worker, {
|
||||||
|
Duration initialDelay = Duration.zero,
|
||||||
|
Duration backoffPolicyDelay = Duration.zero,
|
||||||
|
ExistingWorkPolicy? existingWorkPolicy,
|
||||||
|
BackoffPolicy? backoffPolicy,
|
||||||
|
OutOfQuotaPolicy? outOfQuotaPolicy,
|
||||||
|
Constraints? constraints,
|
||||||
|
this.frequency
|
||||||
|
}) : super(
|
||||||
|
worker,
|
||||||
|
initialDelay: initialDelay,
|
||||||
|
backoffPolicyDelay: backoffPolicyDelay,
|
||||||
|
existingWorkPolicy: existingWorkPolicy,
|
||||||
|
backoffPolicy: backoffPolicy,
|
||||||
|
outOfQuotaPolicy: outOfQuotaPolicy,
|
||||||
|
constraints: constraints
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [super.props, frequency];
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/scheduler/worker.dart';
|
||||||
|
import 'package:workmanager/workmanager.dart';
|
||||||
|
|
||||||
|
/// Represents the scheduling of requests
|
||||||
|
abstract class WorkRequest with EquatableMixin {
|
||||||
|
final Worker worker;
|
||||||
|
final Duration initialDelay;
|
||||||
|
final Duration backoffPolicyDelay;
|
||||||
|
final ExistingWorkPolicy? existingWorkPolicy;
|
||||||
|
final BackoffPolicy? backoffPolicy;
|
||||||
|
final OutOfQuotaPolicy? outOfQuotaPolicy;
|
||||||
|
final Constraints? constraints;
|
||||||
|
|
||||||
|
WorkRequest(
|
||||||
|
this.worker,
|
||||||
|
{
|
||||||
|
this.initialDelay = Duration.zero,
|
||||||
|
this.backoffPolicyDelay = Duration.zero,
|
||||||
|
this.existingWorkPolicy,
|
||||||
|
this.backoffPolicy,
|
||||||
|
this.outOfQuotaPolicy,
|
||||||
|
this.constraints
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [
|
||||||
|
worker,
|
||||||
|
initialDelay,
|
||||||
|
backoffPolicyDelay,
|
||||||
|
existingWorkPolicy,
|
||||||
|
backoffPolicy,
|
||||||
|
outOfQuotaPolicy,
|
||||||
|
constraints
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
enum WorkStatus {
|
||||||
|
success,
|
||||||
|
retry,
|
||||||
|
failure;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
import 'package:core/utils/platform_info.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/scheduler/worker_type.dart';
|
||||||
|
|
||||||
|
/// Equivalent to the task or work that needs to be done in the background
|
||||||
|
class Worker with EquatableMixin {
|
||||||
|
|
||||||
|
final String id;
|
||||||
|
final WorkerType type;
|
||||||
|
final Map<String, dynamic> data;
|
||||||
|
|
||||||
|
Worker(this.id, this.type, this.data);
|
||||||
|
|
||||||
|
String get uniqueId {
|
||||||
|
if (PlatformInfo.isIOS) {
|
||||||
|
return type.iOSUniqueId;
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> get inputData {
|
||||||
|
data['workerType'] = type;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [id, type, data];
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/config/work_manager_constants.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/observer/sending_email_observer.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/observer/work_observer.dart';
|
||||||
|
|
||||||
|
enum WorkerType {
|
||||||
|
sendingEmail;
|
||||||
|
|
||||||
|
WorkObserver usingObserver() {
|
||||||
|
switch(this) {
|
||||||
|
case WorkerType.sendingEmail:
|
||||||
|
return SendingEmailObserver();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String get iOSUniqueId {
|
||||||
|
switch(this) {
|
||||||
|
case WorkerType.sendingEmail:
|
||||||
|
return WorkManagerConstants.sendingEmailUniqueId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-4
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart';
|
import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart';
|
||||||
|
import 'package:tmail_ui_user/features/offline_mode/config/work_manager_config.dart';
|
||||||
import 'package:tmail_ui_user/main/bindings/main_bindings.dart';
|
import 'package:tmail_ui_user/main/bindings/main_bindings.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations_delegate.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations_delegate.dart';
|
||||||
@@ -17,11 +18,16 @@ void main() async {
|
|||||||
initLogger(() async {
|
initLogger(() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
ThemeUtils.setSystemLightUIStyle();
|
ThemeUtils.setSystemLightUIStyle();
|
||||||
await MainBindings().dependencies();
|
|
||||||
await HiveCacheConfig().setUp();
|
await Future.wait([
|
||||||
|
MainBindings().dependencies(),
|
||||||
|
HiveCacheConfig().setUp(),
|
||||||
|
Executor().warmUp(),
|
||||||
|
WorkManagerConfig().initialize(),
|
||||||
|
AppUtils.loadEnvFile()
|
||||||
|
]);
|
||||||
await HiveCacheConfig.initializeEncryptionKey();
|
await HiveCacheConfig.initializeEncryptionKey();
|
||||||
await Executor().warmUp();
|
|
||||||
await AppUtils.loadEnvFile();
|
|
||||||
runApp(const TMailApp());
|
runApp(const TMailApp());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user