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