import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:flutter/services.dart'; import 'runtime_models.dart'; abstract class DesktopPlatformService { DesktopIntegrationState get state; bool get isSupported => state.isSupported; Future initialize(LinuxDesktopConfig config); Future syncConfig(LinuxDesktopConfig config); Future refresh(); Future setMode(VpnMode mode); Future connectTunnel(); Future disconnectTunnel(); Future setLaunchAtLogin(bool enabled); void dispose() {} } DesktopPlatformService createDesktopPlatformService() { if (Platform.isLinux) { return MethodChannelDesktopPlatformService(); } return UnsupportedDesktopPlatformService(); } class UnsupportedDesktopPlatformService implements DesktopPlatformService { DesktopIntegrationState _state = DesktopIntegrationState.unsupported(); @override DesktopIntegrationState get state => _state; @override bool get isSupported => state.isSupported; @override Future initialize(LinuxDesktopConfig config) async { _state = DesktopIntegrationState.unsupported(); } @override Future syncConfig(LinuxDesktopConfig config) async {} @override Future refresh() async {} @override Future setMode(VpnMode mode) async {} @override Future connectTunnel() async {} @override Future disconnectTunnel() async {} @override Future setLaunchAtLogin(bool enabled) async {} @override void dispose() {} } class MethodChannelDesktopPlatformService implements DesktopPlatformService { static const MethodChannel _channel = MethodChannel( 'plus.svc.xworkmate/desktop_platform', ); DesktopIntegrationState _state = DesktopIntegrationState.loading(); LinuxDesktopConfig _config = LinuxDesktopConfig.defaults(); @override DesktopIntegrationState get state => _state; @override bool get isSupported => state.isSupported; @override Future initialize(LinuxDesktopConfig config) async { _config = config; await _invokeVoid('configure', _encodeConfig(config)); await refresh(); } @override Future syncConfig(LinuxDesktopConfig config) async { _config = config; await _invokeVoid('configure', _encodeConfig(config)); await refresh(); } @override Future refresh() async { final payload = await _channel.invokeMethod('getState'); _state = DesktopIntegrationState.fromJson( _decodeJsonMap(payload), fallbackConfig: _config, ); } @override Future setMode(VpnMode mode) async { await _invokeVoid('setMode', mode.name); await refresh(); } @override Future connectTunnel() async { await _invokeVoid('connectTunnel'); await refresh(); } @override Future disconnectTunnel() async { await _invokeVoid('disconnectTunnel'); await refresh(); } @override Future setLaunchAtLogin(bool enabled) async { await _invokeVoid('setAutostart', enabled); await refresh(); } @override void dispose() {} Future _invokeVoid(String method, [Object? arguments]) async { try { await _channel.invokeMethod(method, arguments); } on MissingPluginException { _state = DesktopIntegrationState.unsupported( config: _config, message: 'Desktop integration channel unavailable', ); } on PlatformException catch (error) { _state = _state.copyWith(statusMessage: error.message ?? error.code); rethrow; } } String _encodeConfig(LinuxDesktopConfig config) { return jsonEncode(config.toJson()); } Map _decodeJsonMap(String? payload) { if (payload == null || payload.trim().isEmpty) { return const {}; } final decoded = jsonDecode(payload); if (decoded is Map) { return decoded; } if (decoded is Map) { return decoded.cast(); } return const {}; } }