feat(flutter): TTL-desync toggle in the Android profile editor

Mirrors the desktop toggle: a per-profile ttlDesync flag on OstpProfile, a
SwitchListTile in the profile editor (shown for all transports, since it is the
UDP-path counterpart to the UoT-only junk/fragmentation block), and
transport.ttl_desync in the config handed to the engine, with ttl_desync_auto
true so the shared ostp-client measures the hop distance and aims the decoys
itself. The auto-calibration engine is shared, so Android already had the
capability — this just exposes the switch.
This commit is contained in:
ospab 2026-08-19 21:08:24 +03:00
parent bfd079ff48
commit f59d70778a
3 changed files with 16 additions and 0 deletions

View File

@ -23,6 +23,7 @@ class OstpProfile {
int junkPcMax;
int junkPsMin;
int junkPsMax;
bool ttlDesync; // TTL-desync decoys (UDP), auto-calibrated in the engine
OstpProfile({
required this.id,
@ -38,6 +39,7 @@ class OstpProfile {
this.junkPcMax = 5,
this.junkPsMin = 100,
this.junkPsMax = 1000,
this.ttlDesync = false,
});
Map<String, dynamic> toJson() {
@ -55,6 +57,7 @@ class OstpProfile {
'junkPcMax': junkPcMax,
'junkPsMin': junkPsMin,
'junkPsMax': junkPsMax,
'ttlDesync': ttlDesync,
};
}
@ -73,6 +76,7 @@ class OstpProfile {
junkPcMax: json['junkPcMax'] as int? ?? 5,
junkPsMin: json['junkPsMin'] as int? ?? 100,
junkPsMax: json['junkPsMax'] as int? ?? 1000,
ttlDesync: json['ttlDesync'] as bool? ?? false,
);
}
}

View File

@ -138,6 +138,8 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
"frag_sleep": p?.fragSleep ?? 2,
"junk_pc": [p?.junkPcMin ?? 2, p?.junkPcMax ?? 5],
"junk_ps": [p?.junkPsMin ?? 100, p?.junkPsMax ?? 1000],
"ttl_desync": p?.ttlDesync ?? false,
"ttl_desync_auto": true,
},
"multiplex": {
"enabled": muxEnabled,

View File

@ -224,6 +224,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
final junkPsMaxCtrl = TextEditingController(text: (profile?.junkPsMax ?? 1000).toString());
String transportMode = profile?.transportMode ?? 'udp';
bool tcpFragmentation = profile?.tcpFragmentation ?? false;
bool ttlDesync = profile?.ttlDesync ?? false;
bool obscureKey = true;
showDialog(
@ -301,6 +302,13 @@ class _SettingsScreenState extends State<SettingsScreen> {
],
),
],
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: const Text('TTL Desync', style: TextStyle(fontSize: 14)),
subtitle: const Text('Decoy packets that die before the server (UDP, auto-tuned)', style: TextStyle(fontSize: 12, color: Colors.white54)),
value: ttlDesync,
onChanged: (v) => setDialogState(() => ttlDesync = v),
),
],
),
),
@ -348,6 +356,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
junkPcMax: int.tryParse(junkPcMaxCtrl.text) ?? 5,
junkPsMin: int.tryParse(junkPsMinCtrl.text) ?? 100,
junkPsMax: int.tryParse(junkPsMaxCtrl.text) ?? 1000,
ttlDesync: ttlDesync,
));
} else {
profile.name = nameCtrl.text.trim().isNotEmpty ? nameCtrl.text.trim() : server;
@ -361,6 +370,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
profile.junkPcMax = int.tryParse(junkPcMaxCtrl.text) ?? 5;
profile.junkPsMin = int.tryParse(junkPsMinCtrl.text) ?? 100;
profile.junkPsMax = int.tryParse(junkPsMaxCtrl.text) ?? 1000;
profile.ttlDesync = ttlDesync;
}
_saveProfiles();
});