feat(spoof): тумблер меняет данные на лету + выбор Android/iOS

This commit is contained in:
klockky
2026-06-20 15:50:47 +00:00
parent 08d5cabae5
commit d53f974ddb
6 changed files with 42 additions and 20 deletions
+35 -13
View File
@@ -101,11 +101,11 @@ class _SpoofScreenState extends State<SpoofScreen> {
if (profile != null && profile.enabled) {
_spoofingEnabled = true;
_applyProfileToControllers(profile);
if (mounted) setState(() => _isLoading = false);
} else {
_spoofingEnabled = false;
await _loadDeviceData();
}
if (mounted) setState(() => _isLoading = false);
}
void _applyProfileToControllers(SpoofProfile profile) {
@@ -163,7 +163,6 @@ class _SpoofScreenState extends State<SpoofScreen> {
}
Future<void> _loadDeviceData() async {
setState(() => _isLoading = true);
_userAgent = '';
_spoofingEnabled = false;
@@ -204,6 +203,7 @@ class _SpoofScreenState extends State<SpoofScreen> {
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
_selectedDeviceType = 'ANDROID';
_deviceNameController.text =
'${androidInfo.manufacturer} ${androidInfo.model}';
_osVersionController.text = 'Android ${androidInfo.version.release}';
@@ -213,28 +213,32 @@ class _SpoofScreenState extends State<SpoofScreen> {
} else if (Platform.isIOS) {
final iosInfo = await deviceInfo.iosInfo;
_selectedDeviceType = 'IOS';
_selectedArch = 'arm64';
_deviceNameController.text = iosInfo.utsname.machine;
_osVersionController.text = iosInfo.systemVersion;
} else if (Platform.isLinux) {
final linuxInfo = await deviceInfo.linuxInfo;
_selectedDeviceType = 'ANDROID';
_deviceNameController.text = linuxInfo.prettyName;
_osVersionController.text = linuxInfo.name;
} else if (Platform.isWindows) {
final windowsInfo = await deviceInfo.windowsInfo;
_selectedDeviceType = 'ANDROID';
_deviceNameController.text = windowsInfo.productName;
_osVersionController.text = windowsInfo.productName;
} else if (Platform.isMacOS) {
final macInfo = await deviceInfo.macOsInfo;
_selectedDeviceType = 'ANDROID';
_deviceNameController.text = macInfo.model;
_osVersionController.text = 'macOS ${macInfo.osRelease}';
}
if (mounted) setState(() => _isLoading = false);
if (mounted) setState(() {});
}
Future<void> _applyGeneratedData() async {
final filteredPresets = devicePresets
.where((p) => p.deviceType == 'ANDROID' || p.deviceType == 'IOS')
.where((p) => p.deviceType == _selectedDeviceType)
.toList();
if (filteredPresets.isEmpty) return;
@@ -243,6 +247,12 @@ class _SpoofScreenState extends State<SpoofScreen> {
await _applyPreset(preset);
}
void _onDeviceTypeChanged(String type) {
if (type == _selectedDeviceType) return;
setState(() => _selectedDeviceType = type);
if (_spoofingEnabled) _applyGeneratedData();
}
Future<void> _applyPreset(DevicePreset preset) async {
setState(() {
_deviceNameController.text = preset.deviceName;
@@ -470,7 +480,13 @@ class _SpoofScreenState extends State<SpoofScreen> {
: l10n.spoofEnableSubtitleOff,
),
value: _spoofingEnabled,
onChanged: (value) => setState(() => _spoofingEnabled = value),
onChanged: (value) async {
if (value) {
await _applyGeneratedData();
} else {
await _loadDeviceData();
}
},
),
);
}
@@ -586,12 +602,14 @@ class _SpoofScreenState extends State<SpoofScreen> {
text: l10n.spoofDeviceTypeDescription,
),
const SizedBox(height: 12),
Row(
children: [
_buildDisabledChip('ANDROID', Icons.android_outlined, theme),
const SizedBox(width: 8),
_buildDisabledChip('iOS', Icons.phone_iphone_outlined, theme),
const SizedBox(width: 8),
_buildChipSelector<String>(
options: const [
_ChipOption('ANDROID', 'Android', Icons.android_outlined),
_ChipOption('IOS', 'iOS', Icons.phone_iphone_outlined),
],
selected: _selectedDeviceType,
onSelected: _onDeviceTypeChanged,
trailing: [
_buildDisabledChip(
'Desktop',
Icons.desktop_windows_outlined,
@@ -845,12 +863,14 @@ class _SpoofScreenState extends State<SpoofScreen> {
required List<_ChipOption<T>> options,
required T selected,
required ValueChanged<T> onSelected,
List<Widget> trailing = const [],
}) {
final cs = Theme.of(context).colorScheme;
return Wrap(
spacing: 8,
runSpacing: 8,
children: options.map((opt) {
children: [
...options.map((opt) {
final isSelected = opt.value == selected;
return ChoiceChip(
label: Text(opt.label),
@@ -878,7 +898,9 @@ class _SpoofScreenState extends State<SpoofScreen> {
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
);
}).toList(),
}),
...trailing,
],
);
}
+1 -1
View File
@@ -68,7 +68,7 @@
"spoofMethodPartialDescription": "Recommended method. Random data is used, but your real timezone and locale are kept for plausibility.",
"spoofMethodFullDescription": "All data including timezone and locale is generated randomly. Use this method at your own risk!",
"spoofDeviceTypeTitle": "Device type",
"spoofDeviceTypeDescription": "This field is not changeable, to avoid token association issues",
"spoofDeviceTypeDescription": "Controls which devices are generated: Android or iOS",
"spoofDeviceTypeLabel": "Device type",
"spoofMainSectionTitle": "Main data",
"spoofFieldDeviceName": "Device name",
+1 -1
View File
@@ -455,7 +455,7 @@ abstract class AppLocalizations {
/// No description provided for @spoofDeviceTypeDescription.
///
/// In en, this message translates to:
/// **'This field is not changeable, to avoid token association issues'**
/// **'Controls which devices are generated: Android or iOS'**
String get spoofDeviceTypeDescription;
/// No description provided for @spoofDeviceTypeLabel.
+1 -1
View File
@@ -196,7 +196,7 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get spoofDeviceTypeDescription =>
'This field is not changeable, to avoid token association issues';
'Controls which devices are generated: Android or iOS';
@override
String get spoofDeviceTypeLabel => 'Device type';
+1 -1
View File
@@ -199,7 +199,7 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get spoofDeviceTypeDescription =>
'Данное поле не изменяемое, во избежании проблем с ассоциацией токена';
'Определяет, какие устройства генерируются: Android или iOS';
@override
String get spoofDeviceTypeLabel => 'Тип устройства';
+1 -1
View File
@@ -68,7 +68,7 @@
"spoofMethodPartialDescription": "Рекомендуемый метод. Используются случайные данные, но ваш реальный часовой пояс и локаль для большей правдоподобности.",
"spoofMethodFullDescription": "Все данные, включая часовой пояс и локаль, генерируются случайно. Использование этого метода на ваш страх и риск!",
"spoofDeviceTypeTitle": "Тип устройства",
"spoofDeviceTypeDescription": "Данное поле не изменяемое, во избежании проблем с ассоциацией токена",
"spoofDeviceTypeDescription": "Определяет, какие устройства генерируются: Android или iOS",
"spoofDeviceTypeLabel": "Тип устройства",
"spoofMainSectionTitle": "Основные данные",
"spoofFieldDeviceName": "Имя устройства",