Files
vpn/lib/quick_tile_dispatcher.dart

136 lines
3.7 KiB
Dart

import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:v2ray_box/v2ray_box.dart';
const MethodChannel _quickTileControlChannel = MethodChannel(
'vpn/quick_tile_control',
);
final class _QuickTileController {
final V2rayBox _box = V2rayBox();
bool _isInitialized = false;
StreamSubscription<VpnStatus>? _statusSubscription;
VpnStatus _lastStatus = VpnStatus.stopped;
Future<Map<String, dynamic>> handle(MethodCall call) async {
switch (call.method) {
case 'ping':
return <String, dynamic>{
'success': true,
'ready': true,
'status': _lastStatus.name,
'connected': _isConnectedStatus(_lastStatus),
};
case 'getStatus':
return _statusPayload();
case 'toggleVpn':
return _toggleVpn(call.arguments);
default:
return <String, dynamic>{
'success': false,
'connected': false,
'message': 'Unsupported method: ${call.method}',
'errorCode': 'unsupported_method',
'openApp': false,
};
}
}
Future<void> _ensureInitialized() async {
if (_isInitialized) {
return;
}
await _box.initialize(notificationStopButtonText: 'Stop');
_statusSubscription ??= _box.watchStatus().listen((status) {
_lastStatus = status;
});
_isInitialized = true;
}
bool _isConnectedStatus(VpnStatus status) {
return status == VpnStatus.started;
}
Map<String, dynamic> _statusPayload() {
return <String, dynamic>{
'success': true,
'ready': _isInitialized,
'status': _lastStatus.name,
'connected': _isConnectedStatus(_lastStatus),
};
}
Future<Map<String, dynamic>> _toggleVpn(dynamic arguments) async {
final args = arguments is Map
? Map<String, dynamic>.from(arguments)
: <String, dynamic>{};
final String link = (args['link'] as String? ?? '').trim();
final String name = (args['name'] as String? ?? '').trim();
final bool proxyOnly = args['proxyOnly'] as bool? ?? false;
final bool connected = args['connected'] as bool? ?? false;
if (link.isEmpty) {
return <String, dynamic>{
'success': false,
'connected': false,
'message': 'Config is empty',
'errorCode': 'empty_config',
'openApp': true,
};
}
try {
await _ensureInitialized();
if (connected) {
final bool stopped = await _box.disconnect();
return <String, dynamic>{
'success': stopped,
'connected': false,
'message': stopped ? null : 'Failed to disconnect',
'errorCode': stopped ? null : 'disconnect_failed',
'openApp': false,
'status': _lastStatus.name,
};
}
await _box.setServiceMode(proxyOnly ? VpnMode.proxy : VpnMode.vpn);
final bool started = await _box.connect(
link,
name: name.isEmpty ? 'VPN' : name,
);
return <String, dynamic>{
'success': started,
'connected': started,
'message': started ? null : 'Failed to connect',
'errorCode': started ? null : 'connect_failed',
'openApp': !started,
'status': _lastStatus.name,
};
} catch (error) {
return <String, dynamic>{
'success': false,
'connected': connected,
'message': 'Error: $error',
'errorCode': 'exception',
'openApp': true,
'status': _lastStatus.name,
};
}
}
}
@pragma('vm:entry-point')
Future<void> quickTileDispatcher() async {
WidgetsFlutterBinding.ensureInitialized();
final controller = _QuickTileController();
_quickTileControlChannel.setMethodCallHandler(controller.handle);
await Completer<void>().future;
}