завтра доделаю
This commit is contained in:
@@ -9,6 +9,7 @@ import '../../../core/storage/token_storage.dart';
|
|||||||
import '../../../core/utils/haptics.dart';
|
import '../../../core/utils/haptics.dart';
|
||||||
import '../../../l10n/app_localizations.dart';
|
import '../../../l10n/app_localizations.dart';
|
||||||
import '../../../main.dart';
|
import '../../../main.dart';
|
||||||
|
import '../../widgets/info_action_sheet.dart';
|
||||||
import '../auth/login_screen.dart';
|
import '../auth/login_screen.dart';
|
||||||
import '../auth/proxy_settings_sheet.dart';
|
import '../auth/proxy_settings_sheet.dart';
|
||||||
import 'customization_screen.dart';
|
import 'customization_screen.dart';
|
||||||
@@ -98,6 +99,38 @@ class _SettingsTabState extends State<SettingsTab> {
|
|||||||
if (mounted) setState(() => _hapticsEnabled = value);
|
if (mounted) setState(() => _hapticsEnabled = value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _openCloudStorage(BuildContext context) async {
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
|
await showInfoActionSheet(
|
||||||
|
context,
|
||||||
|
headerIcon: Symbols.cloud,
|
||||||
|
title: 'Облачное хранилище',
|
||||||
|
subtitle: 'Через МАХ',
|
||||||
|
items: [
|
||||||
|
const InfoActionSheetItem(
|
||||||
|
icon: Symbols.cloud_done,
|
||||||
|
title: 'Работает при белых списках',
|
||||||
|
body: 'Вы сможете передать файл даже при ограниченном интернете.',
|
||||||
|
),
|
||||||
|
const InfoActionSheetItem(
|
||||||
|
icon: Symbols.inventory_2,
|
||||||
|
title: 'Файлы до 4ГБ, безлимитное количество.',
|
||||||
|
body: 'Можете хранить массивный обьем информации.',
|
||||||
|
),
|
||||||
|
InfoActionSheetItem(
|
||||||
|
icon: Symbols.gpp_maybe,
|
||||||
|
title: 'Не обеспечивается конфединциальность файлов',
|
||||||
|
body:
|
||||||
|
'Облачное хранилище работает через ваш аккаунт на сервере МАХ, '
|
||||||
|
'нужные люди всё равно могут его посмотреть.',
|
||||||
|
titleColor: cs.error,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
confirmLabel: 'ОК',
|
||||||
|
confirmDelay: const Duration(seconds: 3),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _confirmLogout() async {
|
Future<void> _confirmLogout() async {
|
||||||
final cs = Theme.of(context).colorScheme;
|
final cs = Theme.of(context).colorScheme;
|
||||||
final confirmed = await showModalBottomSheet<bool>(
|
final confirmed = await showModalBottomSheet<bool>(
|
||||||
@@ -277,6 +310,11 @@ child: _buildSection(
|
|||||||
toggleValue: _hapticsEnabled,
|
toggleValue: _hapticsEnabled,
|
||||||
onToggle: _setHaptics,
|
onToggle: _setHaptics,
|
||||||
),
|
),
|
||||||
|
_SettingsItem(
|
||||||
|
icon: Symbols.cloud,
|
||||||
|
label: 'Облачное хранилище [BETA]',
|
||||||
|
onTap: () => _openCloudStorage(context),
|
||||||
|
),
|
||||||
_SettingsItem(
|
_SettingsItem(
|
||||||
icon: Symbols.vpn_lock,
|
icon: Symbols.vpn_lock,
|
||||||
label: 'Прокси',
|
label: 'Прокси',
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ import 'package:flutter_timezone/flutter_timezone.dart';
|
|||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import '../../../core/config/device_presets.dart';
|
import '../../../core/config/device_presets.dart';
|
||||||
|
import '../../../core/storage/device_identity.dart';
|
||||||
import '../../../core/storage/spoofing_service.dart';
|
import '../../../core/storage/spoofing_service.dart';
|
||||||
import '../../../core/storage/token_storage.dart';
|
import '../../../core/storage/token_storage.dart';
|
||||||
import '../../../l10n/app_localizations.dart';
|
import '../../../l10n/app_localizations.dart';
|
||||||
import '../../../main.dart';
|
import '../../../main.dart';
|
||||||
|
import '../../widgets/info_action_sheet.dart';
|
||||||
import '../auth/login_screen.dart';
|
import '../auth/login_screen.dart';
|
||||||
|
|
||||||
enum SpoofingMethod { partial, full }
|
enum SpoofingMethod { partial, full }
|
||||||
@@ -36,6 +38,10 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
final _deviceIdController = TextEditingController();
|
final _deviceIdController = TextEditingController();
|
||||||
final _appVersionController = TextEditingController();
|
final _appVersionController = TextEditingController();
|
||||||
final _buildNumberController = TextEditingController();
|
final _buildNumberController = TextEditingController();
|
||||||
|
final _instanceIdController = TextEditingController();
|
||||||
|
final _clientSessionIdController = TextEditingController();
|
||||||
|
final _deviceLocaleController = TextEditingController();
|
||||||
|
final _pushDeviceTypeController = TextEditingController(text: 'GCM');
|
||||||
|
|
||||||
String _selectedDeviceType = 'ANDROID';
|
String _selectedDeviceType = 'ANDROID';
|
||||||
String _selectedArch = 'arm64-v8a';
|
String _selectedArch = 'arm64-v8a';
|
||||||
@@ -45,9 +51,34 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_localeController.addListener(_syncDeviceLocale);
|
||||||
_loadInitialData();
|
_loadInitialData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _syncDeviceLocale() {
|
||||||
|
if (_selectedMethod == SpoofingMethod.full) return;
|
||||||
|
final derived = _localeController.text.split(RegExp(r'[-_]')).first;
|
||||||
|
if (_deviceLocaleController.text != derived) {
|
||||||
|
_deviceLocaleController.text = derived;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _confirmFullSpoofing() {
|
||||||
|
return showInfoActionSheet(
|
||||||
|
context,
|
||||||
|
headerIcon: Icons.warning_amber_rounded,
|
||||||
|
title: 'Могут быть последствия.',
|
||||||
|
subtitle: 'Меняй, только если знаешь что делаешь.',
|
||||||
|
confirmLabel: 'ОК',
|
||||||
|
confirmDelay: const Duration(seconds: 3),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadSessionIdentifiers() async {
|
||||||
|
_instanceIdController.text = await DeviceIdentity.instanceId();
|
||||||
|
_clientSessionIdController.text = '${DeviceIdentity.clientSessionId}';
|
||||||
|
}
|
||||||
|
|
||||||
String _generateDeviceId() {
|
String _generateDeviceId() {
|
||||||
final bytes = List<int>.generate(8, (_) => _random.nextInt(256));
|
final bytes = List<int>.generate(8, (_) => _random.nextInt(256));
|
||||||
return bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
|
return bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
|
||||||
@@ -55,6 +86,7 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
|
|
||||||
Future<void> _loadInitialData() async {
|
Future<void> _loadInitialData() async {
|
||||||
setState(() => _isLoading = true);
|
setState(() => _isLoading = true);
|
||||||
|
await _loadSessionIdentifiers();
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final isSpoofingEnabled = prefs.getBool('spoofing_enabled') ?? false;
|
final isSpoofingEnabled = prefs.getBool('spoofing_enabled') ?? false;
|
||||||
|
|
||||||
@@ -336,6 +368,7 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_localeController.removeListener(_syncDeviceLocale);
|
||||||
_deviceNameController.dispose();
|
_deviceNameController.dispose();
|
||||||
_osVersionController.dispose();
|
_osVersionController.dispose();
|
||||||
_screenController.dispose();
|
_screenController.dispose();
|
||||||
@@ -344,6 +377,10 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
_deviceIdController.dispose();
|
_deviceIdController.dispose();
|
||||||
_appVersionController.dispose();
|
_appVersionController.dispose();
|
||||||
_buildNumberController.dispose();
|
_buildNumberController.dispose();
|
||||||
|
_instanceIdController.dispose();
|
||||||
|
_clientSessionIdController.dispose();
|
||||||
|
_deviceLocaleController.dispose();
|
||||||
|
_pushDeviceTypeController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,8 +490,16 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
selected: {_selectedMethod},
|
selected: {_selectedMethod},
|
||||||
onSelectionChanged: (s) =>
|
onSelectionChanged: (s) async {
|
||||||
setState(() => _selectedMethod = s.first),
|
final next = s.first;
|
||||||
|
if (next == _selectedMethod) return;
|
||||||
|
if (next == SpoofingMethod.full) {
|
||||||
|
final confirmed = await _confirmFullSpoofing();
|
||||||
|
if (!confirmed || !mounted) return;
|
||||||
|
}
|
||||||
|
setState(() => _selectedMethod = next);
|
||||||
|
_syncDeviceLocale();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
descriptionWidget,
|
descriptionWidget,
|
||||||
@@ -604,6 +649,15 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
Icons.language_outlined,
|
Icons.language_outlined,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextField(
|
||||||
|
controller: _deviceLocaleController,
|
||||||
|
enabled: _selectedMethod == SpoofingMethod.full,
|
||||||
|
decoration: _inputDecoration(
|
||||||
|
l10n.spoofFieldDeviceLocale,
|
||||||
|
Icons.translate_outlined,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -625,6 +679,24 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
text: l10n.spoofIdentifiersDescription,
|
text: l10n.spoofIdentifiersDescription,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
TextField(
|
||||||
|
controller: _instanceIdController,
|
||||||
|
enabled: _selectedMethod == SpoofingMethod.full,
|
||||||
|
decoration: _inputDecoration(
|
||||||
|
l10n.spoofFieldInstanceId,
|
||||||
|
Icons.fingerprint_outlined,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextField(
|
||||||
|
controller: _clientSessionIdController,
|
||||||
|
enabled: _selectedMethod == SpoofingMethod.full,
|
||||||
|
decoration: _inputDecoration(
|
||||||
|
l10n.spoofFieldClientSessionId,
|
||||||
|
Icons.vpn_key_outlined,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _deviceIdController,
|
controller: _deviceIdController,
|
||||||
decoration:
|
decoration:
|
||||||
@@ -642,7 +714,7 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _appVersionController,
|
controller: _appVersionController,
|
||||||
enabled: false,
|
enabled: _selectedMethod == SpoofingMethod.full,
|
||||||
decoration: _inputDecoration(
|
decoration: _inputDecoration(
|
||||||
l10n.spoofFieldAppVersion,
|
l10n.spoofFieldAppVersion,
|
||||||
Icons.info_outline_rounded,
|
Icons.info_outline_rounded,
|
||||||
@@ -651,7 +723,7 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _buildNumberController,
|
controller: _buildNumberController,
|
||||||
enabled: false,
|
enabled: _selectedMethod == SpoofingMethod.full,
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
decoration: _inputDecoration(
|
decoration: _inputDecoration(
|
||||||
l10n.spoofFieldBuildNumber,
|
l10n.spoofFieldBuildNumber,
|
||||||
@@ -659,6 +731,15 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
TextField(
|
||||||
|
controller: _pushDeviceTypeController,
|
||||||
|
enabled: _selectedMethod == SpoofingMethod.full,
|
||||||
|
decoration: _inputDecoration(
|
||||||
|
l10n.spoofFieldPushDeviceType,
|
||||||
|
Icons.notifications_outlined,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 4, bottom: 8),
|
padding: const EdgeInsets.only(left: 4, bottom: 8),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|||||||
@@ -0,0 +1,275 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
class InfoActionSheetItem {
|
||||||
|
final IconData icon;
|
||||||
|
final String title;
|
||||||
|
final String body;
|
||||||
|
final Color? titleColor;
|
||||||
|
final Color? iconColor;
|
||||||
|
|
||||||
|
const InfoActionSheetItem({
|
||||||
|
required this.icon,
|
||||||
|
required this.title,
|
||||||
|
required this.body,
|
||||||
|
this.titleColor,
|
||||||
|
this.iconColor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> showInfoActionSheet(
|
||||||
|
BuildContext context, {
|
||||||
|
String? headerEmoji,
|
||||||
|
IconData? headerIcon,
|
||||||
|
required String title,
|
||||||
|
String? subtitle,
|
||||||
|
List<InfoActionSheetItem> items = const [],
|
||||||
|
String confirmLabel = 'ОК',
|
||||||
|
Duration confirmDelay = Duration.zero,
|
||||||
|
String? seenKey,
|
||||||
|
}) async {
|
||||||
|
assert(
|
||||||
|
headerEmoji == null || headerIcon == null,
|
||||||
|
'Use either headerEmoji or headerIcon, not both.',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (seenKey != null) {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
if (prefs.getBool(seenKey) ?? false) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context.mounted) return false;
|
||||||
|
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
|
final confirmed = await showModalBottomSheet<bool>(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: cs.surfaceContainerHigh,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
||||||
|
),
|
||||||
|
builder: (ctx) => _InfoActionSheet(
|
||||||
|
headerEmoji: headerEmoji,
|
||||||
|
headerIcon: headerIcon,
|
||||||
|
title: title,
|
||||||
|
subtitle: subtitle,
|
||||||
|
items: items,
|
||||||
|
confirmLabel: confirmLabel,
|
||||||
|
confirmDelay: confirmDelay,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final ok = confirmed == true;
|
||||||
|
if (ok && seenKey != null) {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setBool(seenKey, true);
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InfoActionSheet extends StatefulWidget {
|
||||||
|
final String? headerEmoji;
|
||||||
|
final IconData? headerIcon;
|
||||||
|
final String title;
|
||||||
|
final String? subtitle;
|
||||||
|
final List<InfoActionSheetItem> items;
|
||||||
|
final String confirmLabel;
|
||||||
|
final Duration confirmDelay;
|
||||||
|
|
||||||
|
const _InfoActionSheet({
|
||||||
|
this.headerEmoji,
|
||||||
|
this.headerIcon,
|
||||||
|
required this.title,
|
||||||
|
this.subtitle,
|
||||||
|
this.items = const [],
|
||||||
|
required this.confirmLabel,
|
||||||
|
required this.confirmDelay,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_InfoActionSheet> createState() => _InfoActionSheetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InfoActionSheetState extends State<_InfoActionSheet> {
|
||||||
|
Timer? _ticker;
|
||||||
|
int _remaining = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_remaining = widget.confirmDelay.inSeconds;
|
||||||
|
if (_remaining > 0) {
|
||||||
|
_ticker = Timer.periodic(const Duration(seconds: 1), (_) {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_remaining--;
|
||||||
|
if (_remaining == 0) {
|
||||||
|
_ticker?.cancel();
|
||||||
|
_ticker = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_ticker?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
|
final enabled = _remaining == 0;
|
||||||
|
final buttonText = enabled
|
||||||
|
? widget.confirmLabel
|
||||||
|
: '${widget.confirmLabel}($_remaining)';
|
||||||
|
final hasHeader = widget.headerEmoji != null || widget.headerIcon != null;
|
||||||
|
|
||||||
|
return SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(24, 28, 24, 20),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
physics: const BouncingScrollPhysics(),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
if (hasHeader) ...[
|
||||||
|
_buildHeader(cs),
|
||||||
|
const SizedBox(height: 18),
|
||||||
|
],
|
||||||
|
Text(
|
||||||
|
widget.title,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: GoogleFonts.inter(
|
||||||
|
color: cs.onSurface,
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (widget.subtitle != null) ...[
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Text(
|
||||||
|
widget.subtitle!,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: GoogleFonts.inter(
|
||||||
|
color: cs.onSurfaceVariant,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (widget.items.isNotEmpty) ...[
|
||||||
|
const SizedBox(height: 22),
|
||||||
|
for (int i = 0; i < widget.items.length; i++) ...[
|
||||||
|
_buildItem(cs, widget.items[i]),
|
||||||
|
if (i < widget.items.length - 1)
|
||||||
|
const SizedBox(height: 18),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 28),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: enabled ? () => Navigator.pop(context, true) : null,
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
backgroundColor: cs.primary,
|
||||||
|
foregroundColor: cs.onPrimary,
|
||||||
|
disabledBackgroundColor: cs.primary.withValues(alpha: 0.45),
|
||||||
|
disabledForegroundColor: cs.onPrimary.withValues(alpha: 0.85),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(28),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
buttonText,
|
||||||
|
style: GoogleFonts.inter(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHeader(ColorScheme cs) {
|
||||||
|
if (widget.headerEmoji != null) {
|
||||||
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
widget.headerEmoji!,
|
||||||
|
style: const TextStyle(fontSize: 72, height: 1.0),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Center(
|
||||||
|
child: Icon(
|
||||||
|
widget.headerIcon,
|
||||||
|
size: 72,
|
||||||
|
color: cs.primary,
|
||||||
|
weight: 400,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildItem(ColorScheme cs, InfoActionSheetItem item) {
|
||||||
|
final titleColor = item.titleColor ?? cs.onSurface;
|
||||||
|
final iconColor = item.iconColor ?? cs.onSurfaceVariant;
|
||||||
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 2),
|
||||||
|
child: Icon(
|
||||||
|
item.icon,
|
||||||
|
size: 26,
|
||||||
|
color: iconColor,
|
||||||
|
weight: 400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item.title,
|
||||||
|
style: GoogleFonts.inter(
|
||||||
|
color: titleColor,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
height: 1.25,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
item.body,
|
||||||
|
style: GoogleFonts.inter(
|
||||||
|
color: cs.onSurfaceVariant,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
height: 1.35,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -73,8 +73,12 @@
|
|||||||
"spoofFieldScreen": "Screen resolution",
|
"spoofFieldScreen": "Screen resolution",
|
||||||
"spoofFieldTimezone": "Timezone",
|
"spoofFieldTimezone": "Timezone",
|
||||||
"spoofFieldLocale": "Locale",
|
"spoofFieldLocale": "Locale",
|
||||||
|
"spoofFieldDeviceLocale": "Device locale (derived)",
|
||||||
"spoofIdentifiersSectionTitle": "Identifiers",
|
"spoofIdentifiersSectionTitle": "Identifiers",
|
||||||
"spoofIdentifiersDescription": "mt_instanceid and clientSessionId are generated automatically on every app launch. Only the Device ID can be changed.",
|
"spoofIdentifiersDescription": "mt_instanceid and clientSessionId are generated automatically on every app launch. Only the Device ID can be changed.",
|
||||||
|
"spoofFieldInstanceId": "mt_instanceid",
|
||||||
|
"spoofFieldClientSessionId": "clientSessionId",
|
||||||
|
"spoofFieldPushDeviceType": "Push device type",
|
||||||
"spoofFieldDeviceId": "Device ID",
|
"spoofFieldDeviceId": "Device ID",
|
||||||
"spoofRegenerateIdTooltip": "Generate a new ID",
|
"spoofRegenerateIdTooltip": "Generate a new ID",
|
||||||
"spoofFieldAppVersion": "App version",
|
"spoofFieldAppVersion": "App version",
|
||||||
|
|||||||
@@ -482,6 +482,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'Locale'**
|
/// **'Locale'**
|
||||||
String get spoofFieldLocale;
|
String get spoofFieldLocale;
|
||||||
|
|
||||||
|
/// No description provided for @spoofFieldDeviceLocale.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Device locale (derived)'**
|
||||||
|
String get spoofFieldDeviceLocale;
|
||||||
|
|
||||||
/// No description provided for @spoofIdentifiersSectionTitle.
|
/// No description provided for @spoofIdentifiersSectionTitle.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -494,6 +500,24 @@ abstract class AppLocalizations {
|
|||||||
/// **'mt_instanceid and clientSessionId are generated automatically on every app launch. Only the Device ID can be changed.'**
|
/// **'mt_instanceid and clientSessionId are generated automatically on every app launch. Only the Device ID can be changed.'**
|
||||||
String get spoofIdentifiersDescription;
|
String get spoofIdentifiersDescription;
|
||||||
|
|
||||||
|
/// No description provided for @spoofFieldInstanceId.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'mt_instanceid'**
|
||||||
|
String get spoofFieldInstanceId;
|
||||||
|
|
||||||
|
/// No description provided for @spoofFieldClientSessionId.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'clientSessionId'**
|
||||||
|
String get spoofFieldClientSessionId;
|
||||||
|
|
||||||
|
/// No description provided for @spoofFieldPushDeviceType.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Push device type'**
|
||||||
|
String get spoofFieldPushDeviceType;
|
||||||
|
|
||||||
/// No description provided for @spoofFieldDeviceId.
|
/// No description provided for @spoofFieldDeviceId.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -209,6 +209,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get spoofFieldLocale => 'Locale';
|
String get spoofFieldLocale => 'Locale';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofFieldDeviceLocale => 'Device locale (derived)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get spoofIdentifiersSectionTitle => 'Identifiers';
|
String get spoofIdentifiersSectionTitle => 'Identifiers';
|
||||||
|
|
||||||
@@ -216,6 +219,15 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String get spoofIdentifiersDescription =>
|
String get spoofIdentifiersDescription =>
|
||||||
'mt_instanceid and clientSessionId are generated automatically on every app launch. Only the Device ID can be changed.';
|
'mt_instanceid and clientSessionId are generated automatically on every app launch. Only the Device ID can be changed.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofFieldInstanceId => 'mt_instanceid';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofFieldClientSessionId => 'clientSessionId';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofFieldPushDeviceType => 'Push device type';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get spoofFieldDeviceId => 'Device ID';
|
String get spoofFieldDeviceId => 'Device ID';
|
||||||
|
|
||||||
|
|||||||
@@ -211,6 +211,9 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get spoofFieldLocale => 'Локаль';
|
String get spoofFieldLocale => 'Локаль';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofFieldDeviceLocale => 'Системная локаль (производная)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get spoofIdentifiersSectionTitle => 'Идентификаторы';
|
String get spoofIdentifiersSectionTitle => 'Идентификаторы';
|
||||||
|
|
||||||
@@ -218,6 +221,15 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
String get spoofIdentifiersDescription =>
|
String get spoofIdentifiersDescription =>
|
||||||
'mt_instanceid и clientSessionId генерируются автоматически при каждом запуске приложения. Изменить можно только Device ID.';
|
'mt_instanceid и clientSessionId генерируются автоматически при каждом запуске приложения. Изменить можно только Device ID.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofFieldInstanceId => 'mt_instanceid';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofFieldClientSessionId => 'clientSessionId';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofFieldPushDeviceType => 'Тип push-уведомлений';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get spoofFieldDeviceId => 'ID Устройства';
|
String get spoofFieldDeviceId => 'ID Устройства';
|
||||||
|
|
||||||
|
|||||||
@@ -73,8 +73,12 @@
|
|||||||
"spoofFieldScreen": "Разрешение экрана",
|
"spoofFieldScreen": "Разрешение экрана",
|
||||||
"spoofFieldTimezone": "Часовой пояс",
|
"spoofFieldTimezone": "Часовой пояс",
|
||||||
"spoofFieldLocale": "Локаль",
|
"spoofFieldLocale": "Локаль",
|
||||||
|
"spoofFieldDeviceLocale": "Системная локаль (производная)",
|
||||||
"spoofIdentifiersSectionTitle": "Идентификаторы",
|
"spoofIdentifiersSectionTitle": "Идентификаторы",
|
||||||
"spoofIdentifiersDescription": "mt_instanceid и clientSessionId генерируются автоматически при каждом запуске приложения. Изменить можно только Device ID.",
|
"spoofIdentifiersDescription": "mt_instanceid и clientSessionId генерируются автоматически при каждом запуске приложения. Изменить можно только Device ID.",
|
||||||
|
"spoofFieldInstanceId": "mt_instanceid",
|
||||||
|
"spoofFieldClientSessionId": "clientSessionId",
|
||||||
|
"spoofFieldPushDeviceType": "Тип push-уведомлений",
|
||||||
"spoofFieldDeviceId": "ID Устройства",
|
"spoofFieldDeviceId": "ID Устройства",
|
||||||
"spoofRegenerateIdTooltip": "Сгенерировать новый ID",
|
"spoofRegenerateIdTooltip": "Сгенерировать новый ID",
|
||||||
"spoofFieldAppVersion": "Версия приложения",
|
"spoofFieldAppVersion": "Версия приложения",
|
||||||
|
|||||||
Reference in New Issue
Block a user