main.dart 3.57 KB
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:intl/date_symbol_data_local.dart';

import 'app/bootstrap/app_bootstrap.dart';
import 'app/double_feel_app.dart';
import 'core/services/health_raw_data_core_service.dart';
import 'core/theme/app_theme.dart';
import 'data/local/user_preferences_storage.dart';

Future<void> main() async {
  runZonedGuarded<Future<Null>>(
    () async {
      WidgetsFlutterBinding.ensureInitialized();
      FlutterError.onError = (errorDetails) {};
      PlatformDispatcher.instance.onError = (error, stack) {
        return true;
      };

      SystemChrome.setSystemUIOverlayStyle(AppTheme.systemUiOverlayStyle);

      await AppBootstrap.init();
      await initializeDateFormatting();
      runApp(const DoubleFeelApp());
    },
    (error, stackTrace) async {
      if (kDebugMode) {}
    },
  );
}

@pragma('vm:entry-point')
Future<void> healthBackgroundMain() async {
  WidgetsFlutterBinding.ensureInitialized();
  const channel = MethodChannel('doublefeel_flutter_health_background_channel');

  await AppBootstrap.init();

  channel.setMethodCallHandler((call) async {
    switch (call.method) {
      case 'runPendingHealthTasks':
        await _runPendingHealthTasks(channel, call.arguments);
        return true;
      default:
        return null;
    }
  });

  await channel.invokeMethod('healthBackgroundTaskReady', {
    'timestamp': DateTime.now().millisecondsSinceEpoch ~/ 1000,
  });
}

Future<void> _runPendingHealthTasks(
  MethodChannel channel,
  Object? arguments,
) async {
  final tasks = _backgroundHealthTasksFromArguments(arguments);
  if (tasks.isEmpty) return;

  for (final task in tasks) {
    await channel.invokeMethod('healthBackgroundTaskReceived', {
      'taskId': task.taskId,
    });
  }

  final userPrefs = Get.find<UserPreferencesStorage>();
  final userId = userPrefs.preferences.value.meUserInfo?.id ?? 0;
  final token = userPrefs.accessToken;
  if (userId <= 0 || token.isEmpty) {
    for (final task in tasks) {
      await channel.invokeMethod('healthBackgroundTaskFinished', {
        'taskId': task.taskId,
        'success': false,
        'retryable': true,
        'error': 'missingLogin',
      });
    }
    return;
  }

  try {
    await Get.find<HealthRawDataCoreService>().startCoreCaculate();
    for (final task in tasks) {
      await channel.invokeMethod('healthBackgroundTaskFinished', {
        'taskId': task.taskId,
        'success': true,
      });
    }
  } catch (error) {
    for (final task in tasks) {
      await channel.invokeMethod('healthBackgroundTaskFinished', {
        'taskId': task.taskId,
        'success': false,
        'retryable': true,
        'error': error.toString(),
      });
    }
  }
}

List<_BackgroundHealthTask> _backgroundHealthTasksFromArguments(
  Object? arguments,
) {
  final rawTasksValue =
      arguments is Map<Object?, Object?> ? arguments['tasks'] : null;
  final rawTasks =
      rawTasksValue is List ? rawTasksValue.cast<Object?>() : const <Object?>[];
  return rawTasks
      .whereType<Map<Object?, Object?>>()
      .map(_BackgroundHealthTask.fromMap)
      .whereType<_BackgroundHealthTask>()
      .toList(growable: false);
}

class _BackgroundHealthTask {
  const _BackgroundHealthTask({required this.taskId});

  final String taskId;

  static _BackgroundHealthTask? fromMap(Map<Object?, Object?> map) {
    final taskId = map['taskId'];
    if (taskId is! String || taskId.isEmpty) return null;
    return _BackgroundHealthTask(taskId: taskId);
  }
}