arblant
firefox-vpn-client / app / lib / main.dart
main.dart85 lines · 3.2 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
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'app_settings.dart';
import 'core/android_core.dart';
import 'core/core_api.dart';
import 'core/core_controller.dart';
import 'core/updates.dart';
import 'core/core_process.dart';
import 'core/ports.dart';
import 'core/tun_service.dart';
import 'desktop/desktop_integration.dart';
import 'ui/app.dart';

/// The desktop app starts the core as its child process (`-app`) and stops
/// it on quit. Arguments: `--data-dir=<path>` (default: see
/// [CoreLocator.dataDir]) and `--core=<path>` (default:
/// [CoreProcess.executableFrom], `core\firefox-vpn-core.exe` beside the app).
Future<void> main(List<String> args) async {
  WidgetsFlutterBinding.ensureInitialized();
  if (Platform.isAndroid) return runAndroid();
  final dataDir = CoreLocator.dataDir(args);
  final settings = await AppSettings.load(dataDir);
  final process = CoreProcess(
    executable: CoreProcess.executableFrom(args),
    dataDir: dataDir,
  );
  final core = CoreController(
    process.connect,
    moveStoppedCore: (port) async {
      final listen = await CoreLocator.dashboardListen(dataDir);
      await CoreLocator.setDashboardListen(dataDir, withPort(listen, port));
      process.retryNow();
    },
    retryStoppedCore: () async => process.retryNow(),
    // The TUN mode's service, beside the core.
    tunService: Platform.isWindows || Platform.isLinux || Platform.isMacOS
        ? TunService.besideCore(process.executable)
        : null,
  )..start();
  // Back in front: the core tries again at once, not after a wait.
  AppLifecycleListener(onResume: core.retryNow);
  final navigatorKey = GlobalKey<NavigatorState>();
  // Before the desktop's: a version installed at the start goes first.
  Updates.start(core, settings, args: args);
  if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
    DesktopIntegration(
      core: core,
      settings: settings,
      process: process,
      navigatorKey: navigatorKey,
      // --hidden: a new version that its update started in the tray.
      startHidden: args.contains('--autostart') || args.contains('--hidden'),
      autostarted: args.contains('--autostart'),
      manageAutostart: !args.any((a) => a.startsWith('--data-dir=')),
    ).start();
  }
  runApp(VpnApp(core: core, settings: settings, navigatorKey: navigatorKey));
}

/// Android: the core runs in this app's process, in a foreground service
/// that keeps the proxy up while the UI is closed.
Future<void> runAndroid() async {
  WidgetsFlutterBinding.ensureInitialized();
  // Edge to edge: the home page's hero colors the status bar.
  await SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
  SystemChrome.setSystemUIOverlayStyle(
    const SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
      systemNavigationBarColor: Colors.transparent,
    ),
  );
  final dataDir = await AndroidCore.dataDir();
  final settings = await AppSettings.load(dataDir);
  await AndroidCore.loadMode();
  final core = CoreController(
    AndroidCore(dataDir).connect,
    turnOn: AndroidCore.setOn,
  )..start();
  AppLifecycleListener(onResume: core.retryNow);
  Updates.start(core, settings);
  runApp(VpnApp(core: core, settings: settings));
}