Add explicit TUN and SOCKS5 connection modes, app routing controls, authenticated per-session local SOCKS5, Android network monitoring, persistent haptics, resilient session recovery, notification hardening and client-side Hivemind cleanup. Keep the hosted flutter_vless_android 1.1.5 runtime as the base and apply the Android compatibility/security patches at build time instead of restoring the unstable vendored runtime.
93 lines
2.9 KiB
Dart
93 lines
2.9 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:revoltvpn/logic/app_colors.dart';
|
|
import 'package:revoltvpn/logic/connection_settings.dart';
|
|
import 'package:revoltvpn/logic/vpn_connection.dart';
|
|
|
|
class ResilienceModeTile extends StatefulWidget {
|
|
const ResilienceModeTile({super.key});
|
|
|
|
@override
|
|
State<ResilienceModeTile> createState() => _ResilienceModeTileState();
|
|
}
|
|
|
|
class _ResilienceModeTileState extends State<ResilienceModeTile> {
|
|
ResilienceMode _mode = ConnectionSettings.resilienceMode;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_load();
|
|
}
|
|
|
|
Future<void> _load() async {
|
|
await ConnectionSettings.initialize();
|
|
if (mounted) setState(() => _mode = ConnectionSettings.resilienceMode);
|
|
}
|
|
|
|
bool _vpnBusy() {
|
|
final status = context.read<VpnConnection>().status;
|
|
return status == VpnStatus.connected ||
|
|
status == VpnStatus.connecting ||
|
|
status == VpnStatus.disconnecting;
|
|
}
|
|
|
|
Future<void> _change(ResilienceMode? next) async {
|
|
if (next == null || next == _mode) return;
|
|
if (_vpnBusy()) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Disconnect before changing resilience mode.'),
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
|
|
await ConnectionSettings.setResilienceMode(next);
|
|
if (mounted) setState(() => _mode = next);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final extreme = _mode == ResilienceMode.extreme;
|
|
|
|
return Consumer<VpnConnection>(
|
|
builder: (context, vpn, _) {
|
|
final connected = vpn.status == VpnStatus.connected ||
|
|
vpn.status == VpnStatus.connecting;
|
|
final active = connected ? ' Active: ${vpn.activeTransportProfile}.' : '';
|
|
|
|
return ListTile(
|
|
title: const Text(
|
|
'Network resilience',
|
|
style: TextStyle(color: AppColors.textWhite, fontSize: 15),
|
|
),
|
|
subtitle: Text(
|
|
extreme
|
|
? 'Extreme: keeps the exact server transport unchanged and makes up to two controlled recovery attempts after each real Xray disconnect.$active'
|
|
: 'Standard: keeps the exact server transport unchanged and does not force health-check reconnects.$active',
|
|
style: const TextStyle(color: AppColors.textDim, fontSize: 12),
|
|
),
|
|
trailing: DropdownButtonHideUnderline(
|
|
child: DropdownButton<ResilienceMode>(
|
|
value: _mode,
|
|
dropdownColor: AppColors.bgCard,
|
|
onChanged: _change,
|
|
items: const [
|
|
DropdownMenuItem(
|
|
value: ResilienceMode.standard,
|
|
child: Text('Standard'),
|
|
),
|
|
DropdownMenuItem(
|
|
value: ResilienceMode.extreme,
|
|
child: Text('Extreme'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|