diff --git a/lib/frontend/screens/profile/spoof_screen.dart b/lib/frontend/screens/profile/spoof_screen.dart index d2267b0..8bf482b 100644 --- a/lib/frontend/screens/profile/spoof_screen.dart +++ b/lib/frontend/screens/profile/spoof_screen.dart @@ -101,11 +101,11 @@ class _SpoofScreenState extends State { 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 { } Future _loadDeviceData() async { - setState(() => _isLoading = true); _userAgent = ''; _spoofingEnabled = false; @@ -204,6 +203,7 @@ class _SpoofScreenState extends State { 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 { } 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 _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 { await _applyPreset(preset); } + void _onDeviceTypeChanged(String type) { + if (type == _selectedDeviceType) return; + setState(() => _selectedDeviceType = type); + if (_spoofingEnabled) _applyGeneratedData(); + } + Future _applyPreset(DevicePreset preset) async { setState(() { _deviceNameController.text = preset.deviceName; @@ -470,7 +480,13 @@ class _SpoofScreenState extends State { : 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 { 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( + 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,14 +863,16 @@ class _SpoofScreenState extends State { required List<_ChipOption> options, required T selected, required ValueChanged onSelected, + List trailing = const [], }) { final cs = Theme.of(context).colorScheme; return Wrap( spacing: 8, runSpacing: 8, - children: options.map((opt) { - final isSelected = opt.value == selected; - return ChoiceChip( + children: [ + ...options.map((opt) { + final isSelected = opt.value == selected; + return ChoiceChip( label: Text(opt.label), avatar: isSelected ? Icon(Icons.check, size: 18, color: cs.onSecondaryContainer) @@ -878,7 +898,9 @@ class _SpoofScreenState extends State { padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ); - }).toList(), + }), + ...trailing, + ], ); } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 7157f69..b7323e5 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -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", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index dac7020..6f8ded8 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -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. diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 9c6dd7e..edd8f07 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -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'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index a5c4f38..b6a58ca 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -199,7 +199,7 @@ class AppLocalizationsRu extends AppLocalizations { @override String get spoofDeviceTypeDescription => - 'Данное поле не изменяемое, во избежании проблем с ассоциацией токена'; + 'Определяет, какие устройства генерируются: Android или iOS'; @override String get spoofDeviceTypeLabel => 'Тип устройства'; diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index 5b373f6..8e15e71 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -68,7 +68,7 @@ "spoofMethodPartialDescription": "Рекомендуемый метод. Используются случайные данные, но ваш реальный часовой пояс и локаль для большей правдоподобности.", "spoofMethodFullDescription": "Все данные, включая часовой пояс и локаль, генерируются случайно. Использование этого метода на ваш страх и риск!", "spoofDeviceTypeTitle": "Тип устройства", - "spoofDeviceTypeDescription": "Данное поле не изменяемое, во избежании проблем с ассоциацией токена", + "spoofDeviceTypeDescription": "Определяет, какие устройства генерируются: Android или iOS", "spoofDeviceTypeLabel": "Тип устройства", "spoofMainSectionTitle": "Основные данные", "spoofFieldDeviceName": "Имя устройства",