From 6cede4a6941a527f93b617f81bd1fd5dfaf99e1f Mon Sep 17 00:00:00 2001 From: Jganenokk Date: Sun, 5 Apr 2026 00:20:16 +0700 Subject: [PATCH] =?UTF-8?q?=D0=B2=D1=81=D1=91=20=D1=8F=20=D1=81=D0=BF?= =?UTF-8?q?=D0=B0=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 +- .../screens/profile/security_screen.dart | 397 ++++++++++++++++++ .../screens/profile/settings_tab.dart | 24 +- .../screens/profile/spoof_screen.dart | 333 +++++++++++++++ 4 files changed, 755 insertions(+), 2 deletions(-) create mode 100644 lib/frontend/screens/profile/security_screen.dart create mode 100644 lib/frontend/screens/profile/spoof_screen.dart diff --git a/.vscode/settings.json b/.vscode/settings.json index 08303a5..31f08e1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "cmake.sourceDirectory": "/run/media/invisedivine/Drive/! My projects/Komet/linux/runner" + "cmake.sourceDirectory": "/run/media/invisedivine/Drive/! My projects/Komet/linux/runner", + "kiroAgent.configureMCP": "Disabled" } \ No newline at end of file diff --git a/lib/frontend/screens/profile/security_screen.dart b/lib/frontend/screens/profile/security_screen.dart new file mode 100644 index 0000000..a2c84d0 --- /dev/null +++ b/lib/frontend/screens/profile/security_screen.dart @@ -0,0 +1,397 @@ +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/symbols.dart'; +import '../../widgets/custom_notification.dart'; + +class SecurityScreen extends StatefulWidget { + const SecurityScreen({super.key}); + + @override + State createState() => _SecurityScreenState(); +} + +class _SecurityScreenState extends State { + bool _safeMode = false; + + @override + Widget build(BuildContext context) { + final cs = Theme.of(context).colorScheme; + + return Scaffold( + backgroundColor: cs.surface, + body: SafeArea( + bottom: false, + child: CustomScrollView( + physics: const BouncingScrollPhysics(), + slivers: [ + SliverToBoxAdapter(child: _buildAppBar(context, cs)), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), + child: _buildTopSection(cs), + ), + ), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), + child: _buildSafeModeSection(cs), + ), + ), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 20, 16, 0), + child: _buildInfoLabel(cs), + ), + ), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), + child: _buildOnlineSection(cs), + ), + ), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 120), + child: _buildBlacklistSection(cs), + ), + ), + ], + ), + ), + ); + } + + Widget _buildAppBar(BuildContext context, ColorScheme cs) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4), + child: Row( + children: [ + IconButton( + icon: Icon(Symbols.arrow_back, color: cs.onSurface, size: 24, weight: 400), + onPressed: () => Navigator.pop(context), + ), + const SizedBox(width: 4), + Text( + 'Безопасность', + style: TextStyle( + color: cs.onSurface, + fontSize: 20, + fontWeight: FontWeight.w700, + fontFamily: 'Outfit', + ), + ), + ], + ), + ); + } + + Widget _buildTopSection(ColorScheme cs) { + return Container( + decoration: BoxDecoration( + color: cs.surfaceContainerHigh, + borderRadius: BorderRadius.circular(20), + ), + child: Column( + children: [ + _buildNavRow( + cs, + icon: Symbols.key, + label: 'Пароль для входа', + subtitle: 'Отключён', + trailing: _buildWarningBadge(cs), + isLast: false, + ), + _buildNavRow( + cs, + icon: Symbols.shield, + label: 'Семейная защита', + subtitle: 'Отключена', + isLast: true, + ), + ], + ), + ); + } + + Widget _buildSafeModeSection(ColorScheme cs) { + return Container( + decoration: BoxDecoration( + color: cs.surfaceContainerHigh, + borderRadius: BorderRadius.circular(20), + ), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 17), + child: Row( + children: [ + Icon(Symbols.lock, color: cs.onSurfaceVariant, size: 22, weight: 400), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Безопасный режим', + style: TextStyle( + color: cs.onSurface, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 2), + Text( + 'Доступно только в мобильном приложении', + style: TextStyle( + color: cs.onSurfaceVariant, + fontSize: 13, + ), + ), + ], + ), + ), + Switch( + value: _safeMode, + onChanged: (v) => setState(() => _safeMode = v), + ), + ], + ), + ), + if (_safeMode) ...[ + Padding( + padding: const EdgeInsets.only(left: 58), + child: Divider(height: 1, thickness: 1, color: cs.outlineVariant.withValues(alpha: 0.35)), + ), + _buildSubRow(cs, label: 'Найти меня по номеру', value: 'Могут все', isLast: false), + _buildSubRow(cs, label: 'Позвонить', value: 'Могут все', isLast: false), + _buildSubRow(cs, label: 'Пригласить в чат', value: 'Могут контакты', isLast: false), + _buildSubRow(cs, label: 'Показывать контент', value: 'Весь', isLast: true), + ], + ], + ), + ); + } + + Widget _buildInfoLabel(ColorScheme cs) { + return Padding( + padding: const EdgeInsets.only(left: 4, bottom: 0), + child: Text( + 'ИНФОРМАЦИЯ', + style: TextStyle( + color: cs.onSurfaceVariant.withValues(alpha: 0.6), + fontSize: 12, + fontWeight: FontWeight.w600, + letterSpacing: 0.8, + ), + ), + ); + } + + Widget _buildOnlineSection(ColorScheme cs) { + return Container( + decoration: BoxDecoration( + color: cs.surfaceContainerHigh, + borderRadius: BorderRadius.circular(20), + ), + child: _buildNavRow( + cs, + icon: null, + label: 'Видеть статус «в сети»', + value: 'Никто', + isLast: true, + noIcon: true, + ), + ); + } + + Widget _buildBlacklistSection(ColorScheme cs) { + return Container( + decoration: BoxDecoration( + color: cs.surfaceContainerHigh, + borderRadius: BorderRadius.circular(20), + ), + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () => showCustomNotification(context, 'Чёрный список'), + borderRadius: BorderRadius.circular(20), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 17), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Чёрный список', + style: TextStyle( + color: cs.onSurface, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 2), + Text( + 'Список тех, кто не может вам писать, звонить и добавлять в чаты', + style: TextStyle( + color: cs.onSurfaceVariant, + fontSize: 13, + ), + ), + ], + ), + ), + const SizedBox(width: 8), + Icon(Symbols.chevron_right, color: cs.outline, size: 20, weight: 400), + ], + ), + ), + ), + ), + ); + } + + Widget _buildNavRow( + ColorScheme cs, { + required IconData? icon, + required String label, + String? subtitle, + String? value, + Widget? trailing, + required bool isLast, + bool noIcon = false, + }) { + return Column( + children: [ + Material( + color: Colors.transparent, + child: InkWell( + onTap: () => showCustomNotification(context, label), + borderRadius: isLast + ? const BorderRadius.vertical(bottom: Radius.circular(20)) + : BorderRadius.zero, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 17), + child: Row( + children: [ + if (!noIcon) ...[ + Icon(icon, color: cs.onSurfaceVariant, size: 22, weight: 400), + const SizedBox(width: 16), + ], + Expanded( + child: subtitle != null + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: TextStyle( + color: cs.onSurface, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 2), + Text( + subtitle, + style: TextStyle( + color: cs.onSurfaceVariant, + fontSize: 13, + ), + ), + ], + ) + : Text( + label, + style: TextStyle( + color: cs.onSurface, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), + if (value != null) + Text( + value, + style: TextStyle( + color: cs.onSurfaceVariant, + fontSize: 14, + ), + ), + if (trailing != null) trailing, + const SizedBox(width: 4), + Icon(Symbols.chevron_right, color: cs.outline, size: 20, weight: 400), + ], + ), + ), + ), + ), + if (!isLast) + Padding( + padding: const EdgeInsets.only(left: 58), + child: Divider(height: 1, thickness: 1, color: cs.outlineVariant.withValues(alpha: 0.35)), + ), + ], + ); + } + + Widget _buildSubRow( + ColorScheme cs, { + required String label, + required String value, + required bool isLast, + }) { + return Column( + children: [ + Material( + color: Colors.transparent, + child: InkWell( + onTap: () => showCustomNotification(context, label), + borderRadius: isLast + ? const BorderRadius.vertical(bottom: Radius.circular(20)) + : BorderRadius.zero, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + child: Row( + children: [ + Expanded( + child: Text( + label, + style: TextStyle(color: cs.onSurface, fontSize: 15), + ), + ), + Text( + value, + style: TextStyle(color: cs.onSurfaceVariant, fontSize: 14), + ), + const SizedBox(width: 4), + Icon(Symbols.chevron_right, color: cs.outline, size: 18, weight: 400), + ], + ), + ), + ), + ), + if (!isLast) + Divider( + height: 1, + thickness: 1, + color: cs.outlineVariant.withValues(alpha: 0.35), + indent: 20, + endIndent: 20, + ), + ], + ); + } + + Widget _buildWarningBadge(ColorScheme cs) { + return Container( + width: 22, + height: 22, + decoration: BoxDecoration( + color: cs.error, + shape: BoxShape.circle, + ), + child: Icon(Symbols.priority_high, color: cs.onError, size: 14, weight: 700), + ); + } +} diff --git a/lib/frontend/screens/profile/settings_tab.dart b/lib/frontend/screens/profile/settings_tab.dart index 6efef04..3dc116c 100644 --- a/lib/frontend/screens/profile/settings_tab.dart +++ b/lib/frontend/screens/profile/settings_tab.dart @@ -3,6 +3,8 @@ import 'package:flutter/material.dart'; import 'package:material_symbols_icons/symbols.dart'; import '../../../core/storage/app_database.dart'; import 'devices_screen.dart'; +import 'security_screen.dart'; +import 'spoof_screen.dart'; class SettingsTab extends StatefulWidget { const SettingsTab({super.key}); @@ -75,9 +77,29 @@ class _SettingsTabState extends State { icon: Symbols.notifications_active, label: 'Уведомления и звук', ), - const _SettingsItem( + _SettingsItem( + icon: Symbols.shield_lock, + label: 'Подделка данных', + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SpoofScreen(), + ), + ); + }, + ), + _SettingsItem( icon: Symbols.lock, label: 'Безопасность', + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SecurityScreen(), + ), + ); + }, ), _SettingsItem( icon: Symbols.devices, diff --git a/lib/frontend/screens/profile/spoof_screen.dart b/lib/frontend/screens/profile/spoof_screen.dart new file mode 100644 index 0000000..accabc4 --- /dev/null +++ b/lib/frontend/screens/profile/spoof_screen.dart @@ -0,0 +1,333 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/symbols.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../../../core/config/spoof_data.dart'; +import '../../widgets/custom_notification.dart'; + +class SpoofScreen extends StatefulWidget { + const SpoofScreen({super.key}); + + @override + State createState() => _SpoofScreenState(); +} + +class _SpoofScreenState extends State { + final _deviceTypeController = TextEditingController(); + final _deviceNameController = TextEditingController(); + final _osVersionController = TextEditingController(); + final _screenController = TextEditingController(); + final _timezoneController = TextEditingController(); + final _localeController = TextEditingController(); + final _deviceLocaleController = TextEditingController(); + final _deviceIdController = TextEditingController(); + final _appVersionController = TextEditingController(); + final _buildNumberController = TextEditingController(); + final _architectureController = TextEditingController(); + final _pushDeviceTypeController = TextEditingController(); + final _mtInstanceIdController = TextEditingController(); + final _clientSessionIdController = TextEditingController(); + + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _loadSettings(); + } + + Future _loadSettings() async { + final prefs = await SharedPreferences.getInstance(); + final random = Random(); + + setState(() { + _deviceTypeController.text = prefs.getString('spoof_device_type') ?? SpoofData.deviceType; + _deviceNameController.text = prefs.getString('spoof_device_name') ?? + SpoofData.deviceNames[random.nextInt(SpoofData.deviceNames.length)]; + _osVersionController.text = prefs.getString('spoof_os_version') ?? + 'Android ${SpoofData.osVersions[random.nextInt(SpoofData.osVersions.length)]}'; + _screenController.text = prefs.getString('spoof_screen') ?? + SpoofData.resolutions[random.nextInt(SpoofData.resolutions.length)]; + _timezoneController.text = prefs.getString('spoof_timezone') ?? SpoofData.timezone; + _localeController.text = prefs.getString('spoof_locale') ?? SpoofData.locale; + _deviceLocaleController.text = prefs.getString('spoof_device_locale') ?? 'ru'; + _deviceIdController.text = prefs.getString('spoof_device_id') ?? + SpoofData.deviceIds[random.nextInt(SpoofData.deviceIds.length)]; + _appVersionController.text = prefs.getString('spoof_app_version') ?? SpoofData.appVersion; + _buildNumberController.text = prefs.getString('spoof_build_number') ?? SpoofData.buildNumber; + _architectureController.text = prefs.getString('spoof_architecture') ?? + SpoofData.architectures[random.nextInt(SpoofData.architectures.length)]; + _pushDeviceTypeController.text = prefs.getString('spoof_push_device_type') ?? 'GCM'; + _mtInstanceIdController.text = prefs.getString('spoof_mt_instanceid') ?? + '550e8400-e29b-41d4-a716-446655440000'; + _clientSessionIdController.text = prefs.getString('spoof_client_session_id') ?? '42'; + _isLoading = false; + }); + } + + Future _saveSettings() async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setString('spoof_device_type', _deviceTypeController.text); + await prefs.setString('spoof_device_name', _deviceNameController.text); + await prefs.setString('spoof_os_version', _osVersionController.text); + await prefs.setString('spoof_screen', _screenController.text); + await prefs.setString('spoof_timezone', _timezoneController.text); + await prefs.setString('spoof_locale', _localeController.text); + await prefs.setString('spoof_device_locale', _deviceLocaleController.text); + await prefs.setString('spoof_device_id', _deviceIdController.text); + await prefs.setString('spoof_app_version', _appVersionController.text); + await prefs.setString('spoof_build_number', _buildNumberController.text); + await prefs.setString('spoof_architecture', _architectureController.text); + await prefs.setString('spoof_push_device_type', _pushDeviceTypeController.text); + await prefs.setString('spoof_mt_instanceid', _mtInstanceIdController.text); + await prefs.setString('spoof_client_session_id', _clientSessionIdController.text); + + if (mounted) { + showCustomNotification(context, 'Настройки сохранены'); + } + } + + Future _randomizeAll() async { + final random = Random(); + setState(() { + _deviceNameController.text = SpoofData.deviceNames[random.nextInt(SpoofData.deviceNames.length)]; + _osVersionController.text = 'Android ${SpoofData.osVersions[random.nextInt(SpoofData.osVersions.length)]}'; + _screenController.text = SpoofData.resolutions[random.nextInt(SpoofData.resolutions.length)]; + _deviceIdController.text = SpoofData.deviceIds[random.nextInt(SpoofData.deviceIds.length)]; + _architectureController.text = SpoofData.architectures[random.nextInt(SpoofData.architectures.length)]; + }); + await _saveSettings(); + if (mounted) { + showCustomNotification(context, 'Данные рандомизированы'); + } + } + + @override + void dispose() { + _deviceTypeController.dispose(); + _deviceNameController.dispose(); + _osVersionController.dispose(); + _screenController.dispose(); + _timezoneController.dispose(); + _localeController.dispose(); + _deviceLocaleController.dispose(); + _deviceIdController.dispose(); + _appVersionController.dispose(); + _buildNumberController.dispose(); + _architectureController.dispose(); + _pushDeviceTypeController.dispose(); + _mtInstanceIdController.dispose(); + _clientSessionIdController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final cs = Theme.of(context).colorScheme; + + if (_isLoading) { + return Scaffold( + backgroundColor: cs.surface, + body: const Center(child: CircularProgressIndicator()), + ); + } + + return Scaffold( + backgroundColor: cs.surface, + body: SafeArea( + bottom: false, + child: CustomScrollView( + physics: const BouncingScrollPhysics(), + slivers: [ + SliverToBoxAdapter(child: _buildAppBar(context, cs)), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), + child: _buildSection(cs, [ + _buildField(cs, 'Тип устройства', _deviceTypeController), + _buildField(cs, 'Имя устройства', _deviceNameController), + _buildField(cs, 'Версия ОС', _osVersionController), + _buildField(cs, 'Разрешение экрана', _screenController), + _buildField(cs, 'Архитектура', _architectureController), + _buildField(cs, 'ID устройства', _deviceIdController), + _buildField(cs, 'Часовой пояс', _timezoneController), + _buildField(cs, 'Локаль', _localeController), + _buildField(cs, 'Локаль устройства', _deviceLocaleController), + _buildField(cs, 'Версия приложения', _appVersionController), + _buildField(cs, 'Build Number', _buildNumberController), + _buildField(cs, 'Push Device Type', _pushDeviceTypeController), + _buildField(cs, 'MT Instance ID', _mtInstanceIdController), + _buildField(cs, 'Client Session ID', _clientSessionIdController, isLast: true), + ]), + ), + ), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 120), + child: _buildActionButtons(cs), + ), + ), + ], + ), + ), + ); + } + + Widget _buildAppBar(BuildContext context, ColorScheme cs) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4), + child: Row( + children: [ + IconButton( + icon: Icon(Symbols.arrow_back, color: cs.onSurface, size: 24, weight: 400), + onPressed: () => Navigator.pop(context), + ), + const SizedBox(width: 4), + Text( + 'Подделка данных', + style: TextStyle( + color: cs.onSurface, + fontSize: 20, + fontWeight: FontWeight.w700, + fontFamily: 'Outfit', + ), + ), + ], + ), + ); + } + + + + Widget _buildSection(ColorScheme cs, List children) { + return Container( + decoration: BoxDecoration( + color: cs.surfaceContainerHigh, + borderRadius: BorderRadius.circular(20), + ), + child: Column(children: children), + ); + } + + Widget _buildField( + ColorScheme cs, + String label, + TextEditingController controller, { + bool isLast = false, + }) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + child: Row( + children: [ + SizedBox( + width: 140, + child: Text( + label, + style: TextStyle( + color: cs.onSurfaceVariant, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: TextField( + controller: controller, + style: TextStyle( + color: cs.onSurface, + fontSize: 14, + fontWeight: FontWeight.w400, + ), + decoration: InputDecoration( + filled: true, + fillColor: cs.surfaceContainerHighest, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide.none, + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 10, + ), + isDense: true, + ), + ), + ), + ], + ), + ), + if (!isLast) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Divider( + height: 1, + thickness: 1, + color: cs.outlineVariant.withValues(alpha: 0.35), + ), + ), + ], + ); + } + + Widget _buildActionButtons(ColorScheme cs) { + return Row( + children: [ + Expanded( + child: SizedBox( + height: 48, + child: OutlinedButton( + onPressed: _randomizeAll, + style: OutlinedButton.styleFrom( + foregroundColor: cs.onSurface, + side: BorderSide(color: cs.outline, width: 1), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Symbols.shuffle, size: 18, weight: 400), + const SizedBox(width: 6), + const Text( + 'Рандомизировать', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: SizedBox( + height: 48, + child: FilledButton( + onPressed: _saveSettings, + style: FilledButton.styleFrom( + backgroundColor: cs.primary, + foregroundColor: cs.onPrimary, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14), + ), + ), + child: const Text( + 'Сохранить', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ), + ], + ); + } +}