import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:convert'; import '../../../config/translations/localization_service.dart'; import '../../../utils/crypto.dart'; import '../../../utils/log/logger.dart'; import '../../constants/keys.dart'; import '../models/disconnect_domain.dart'; import '../models/launch/app_config.dart'; import '../models/launch/groups.dart'; import '../models/launch/launch.dart'; import '../models/launch/upgrade.dart'; import '../models/launch/user.dart'; import '../../modules/home/controllers/home_controller.dart'; import '../../modules/node/controllers/node_controller.dart'; class IXSP { // prevent making instance IXSP._(); // get storage static late SharedPreferences _sharedPreferences; // STORING KEYS /// Key for storing the device ID static const String _deviceIdKey = 'omon_device_id'; static const String _fcmTokenKey = 'fcm_token'; static const String _currentLocalKey = 'current_local'; static const String _lightThemeKey = 'is_theme_light'; static const String _launchGame = 'is_launch_game'; static const String _ignoreVersionKey = 'ignore_version'; static const String _isNewInstallKey = 'is_new_install'; static const String _launchDataKey = 'launch_data'; //记录网络报错的域名信息 static const String _disconnectDomainsKey = 'disconnect_domains'; //记录最后一次是否是地区禁用 static const String _isRegionDisabledKey = 'is_region_disabled'; //记录最后一次是否是用户禁用 static const String _isUserDisabledKey = 'is_user_disabled'; //记录最后一次是否是设备禁用 static const String _isDeviceDisabledKey = 'is_device_disabled'; //记录最后一次上传的性能日志boostSessionId static const String _lastMetricsLogKey = 'last_metrics_log'; //记录最后一次上传的es日志boostSessionId static const String _lastESLogKey = 'last_es_log'; //记录是否开启debug log static const String _enableDebugLogKey = 'enable_debug_log'; //记录是否开启ping 0默认 1开启 2关闭 static const String _enablePingModeKey = 'enable_ping_mode'; /// init get storage services static Future init() async { _sharedPreferences = await SharedPreferences.getInstance(); } static setStorage(SharedPreferences sharedPreferences) { _sharedPreferences = sharedPreferences; } /// set theme current type as light theme static Future setThemeIsLight(bool lightTheme) => _sharedPreferences.setBool(_lightThemeKey, lightTheme); /// get if the current theme type is light static bool getThemeIsLight() => _sharedPreferences.getBool(_lightThemeKey) ?? true; // todo set the default theme (true for light, false for dark) /// save current locale static Future setCurrentLanguage(String languageCode) => _sharedPreferences.setString(_currentLocalKey, languageCode); /// get current locale static Locale getCurrentLocal() { String? langCode = _sharedPreferences.getString(_currentLocalKey); // default language is english if (langCode == null) { return LocalizationService.defaultLanguage; } return LocalizationService.supportedLanguages[langCode]!; } /// save generated fcm token static Future setFcmToken(String token) => _sharedPreferences.setString(_fcmTokenKey, token); /// get authorization token static String getFcmToken() => _sharedPreferences.getString(_fcmTokenKey) ?? ''; /// set launch game static Future setLaunchGame(bool isLaunch) => _sharedPreferences.setBool(_launchGame, isLaunch); /// get launch game static bool getLaunchGame() => _sharedPreferences.getBool(_launchGame) ?? false; /// clear all data from shared pref static Future clear() async => await _sharedPreferences.clear(); /// Save unique ID to shared preferences static Future setDeviceId(String deviceId) => _sharedPreferences.setString(_deviceIdKey, deviceId); /// Get unique ID from shared preferences static String? getDeviceId() => _sharedPreferences.getString(_deviceIdKey); /// Save ignore version to shared preferences static Future setIgnoreVersion(int version) => _sharedPreferences.setInt(_ignoreVersionKey, version); /// Get ignore version from shared preferences static int getIgnoreVersion() => _sharedPreferences.getInt(_ignoreVersionKey) ?? 0; /// Save is new install to shared preferences static Future setIsNewInstall(bool isNewInstall) => _sharedPreferences.setBool(_isNewInstallKey, isNewInstall); /// Get is new install from shared preferences static bool getIsNewInstall() => _sharedPreferences.getBool(_isNewInstallKey) ?? true; /// 保存 DisconnectDomain 列表,合并 domain+status 相同的数据 static Future addDisconnectDomain(DisconnectDomain newItem) async { try { final prefs = _sharedPreferences; // 读取已存在的数据 final oldJson = prefs.getString(_disconnectDomainsKey); List allList = []; if (oldJson != null && oldJson.isNotEmpty) { final List oldList = jsonDecode(oldJson); allList = oldList.map((e) => DisconnectDomain.fromJson(e)).toList(); } // 合并新旧数据 allList.add(newItem); // 按 domain+status 分组合并 final Map merged = {}; for (var item in allList) { final key = '${item.domain}_${item.status}'; if (!merged.containsKey(key)) { merged[key] = DisconnectDomain( domain: item.domain, status: item.status, msg: item.msg, startTime: item.startTime, endTime: item.endTime, count: item.count, ); } else { final exist = merged[key]!; exist.startTime = exist.startTime < item.startTime ? exist.startTime : item.startTime; exist.endTime = exist.endTime > item.endTime ? exist.endTime : item.endTime; exist.count += item.count; // msg 以最新的为准 exist.msg = item.msg; } } // 保存合并后的数据 final mergedList = merged.values.toList(); await prefs.setString( _disconnectDomainsKey, jsonEncode(mergedList.map((e) => e.toJson()).toList()), ); } catch (e) { log('IVSP', 'addDisconnectDomain error: $e'); } } /// 读取 DisconnectDomain 列表 static List getDisconnectDomains() { final jsonStr = _sharedPreferences.getString(_disconnectDomainsKey); if (jsonStr == null || jsonStr.isEmpty) return []; final List list = jsonDecode(jsonStr); return list.map((e) => DisconnectDomain.fromJson(e)).toList(); } /// 清空 DisconnectDomain 列表 static Future clearDisconnectDomains() async { await _sharedPreferences.remove(_disconnectDomainsKey); } // 记录最后一次是否是用户禁用 static Future setLastIsUserDisabled(bool isUserDisabled) async { await _sharedPreferences.setBool(_isUserDisabledKey, isUserDisabled); } // 获取最后一次是否是用户禁用 static bool getLastIsUserDisabled() { return _sharedPreferences.getBool(_isUserDisabledKey) ?? false; } // 记录最后一次是否是地区禁用 static Future setLastIsRegionDisabled(bool isRegionDisabled) async { await _sharedPreferences.setBool(_isRegionDisabledKey, isRegionDisabled); } // 获取最后一次是否是地区禁用 static bool getLastIsRegionDisabled() { return _sharedPreferences.getBool(_isRegionDisabledKey) ?? false; } // 记录最后一次是否是设备禁用 static Future setLastIsDeviceDisabled(bool isDeviceDisabled) async { await _sharedPreferences.setBool(_isDeviceDisabledKey, isDeviceDisabled); } // 获取最后一次是否是设备禁用 static bool getLastIsDeviceDisabled() { return _sharedPreferences.getBool(_isDeviceDisabledKey) ?? false; } /// Save last metrics log static Future setLastMetricsLog(String log) async { await _sharedPreferences.setString(_lastMetricsLogKey, log); } /// Get last metrics log static String getLastMetricsLog() => _sharedPreferences.getString(_lastMetricsLogKey) ?? ''; /// Save last es log static Future setLastESLog(String log) async { await _sharedPreferences.setString(_lastESLogKey, log); } /// Get last es log static String getLastESLog() => _sharedPreferences.getString(_lastESLogKey) ?? ''; /// Save enable debug log static Future setEnableDebugLog(bool enable) async { await _sharedPreferences.setBool(_enableDebugLogKey, enable); } /// Get enable debug log static bool getEnableDebugLog() => _sharedPreferences.getBool(_enableDebugLogKey) ?? false; /// Save enable ping static Future setEnablePingMode(int model) async { await _sharedPreferences.setInt(_enablePingModeKey, model); } /// Get enable ping static int getEnablePingMode() => _sharedPreferences.getInt(_enablePingModeKey) ?? 0; //记录当前选中的节点 static const String _selectedLocationKey = 'selected_location'; //记录最近选择的节点列表(最多3个) static const String _recentLocationsKey = 'recent_locations'; /// 保存当前选中的节点 static Future saveSelectedLocation( Map location, ) async { try { await _sharedPreferences.setString( _selectedLocationKey, jsonEncode(location), ); log('IXSP', 'Selected location saved successfully'); } catch (e) { log('IXSP', 'Error saving selected location: $e'); } } /// 获取当前选中的节点 static Map? getSelectedLocation() { try { final jsonData = _sharedPreferences.getString(_selectedLocationKey); if (jsonData == null) { return null; } return jsonDecode(jsonData) as Map; } catch (e) { log('IXSP', 'Error getting selected location: $e'); return null; } } /// 保存最近选择的节点列表 static Future saveRecentLocations( List> locations, ) async { try { await _sharedPreferences.setString( _recentLocationsKey, jsonEncode(locations), ); log('IXSP', 'Recent locations saved successfully'); } catch (e) { log('IXSP', 'Error saving recent locations: $e'); } } /// 获取最近选择的节点列表 static List> getRecentLocations() { try { final jsonData = _sharedPreferences.getString(_recentLocationsKey); if (jsonData == null || jsonData.isEmpty) { return []; } final List list = jsonDecode(jsonData); return list.map((e) => e as Map).toList(); } catch (e) { log('IXSP', 'Error getting recent locations: $e'); return []; } } /// 保存 Launch 数据 static Future saveLaunch(Launch launch) async { try { // 获取旧的 Launch 数据,用于比较 groups final oldLaunch = getLaunch(); final oldGroups = oldLaunch?.groups; final newGroups = launch.groups; final jsonData = jsonEncode(launch.toJson()); // 保存数据 await _sharedPreferences.setString( _launchDataKey, Crypto.encrypt(jsonData, Keys.aesKey, Keys.aesIv), ); log('IXSP', 'Launch data saved successfully'); // 检查 groups 是否有变化,如果有则通知更新 if (_isGroupsChanged(oldGroups, newGroups)) { log('IXSP', 'Groups data changed, notifying controllers'); _notifyControllersOnGroupsChanged(); } return true; } catch (e) { log('IXSP', 'Error saving launch data: $e'); return false; } } /// 检查 Groups 数据是否有变化 static bool _isGroupsChanged(Groups? oldGroups, Groups? newGroups) { // 如果两者都为 null,没有变化 if (oldGroups == null && newGroups == null) return false; // 如果一个为 null 一个不为 null,有变化 if (oldGroups == null || newGroups == null) return true; // 比较 JSON 序列化后的字符串 try { final oldJson = jsonEncode(oldGroups.toJson()); final newJson = jsonEncode(newGroups.toJson()); return oldJson != newJson; } catch (e) { log('IXSP', 'Error comparing groups: $e'); return true; // 出错时默认认为有变化 } } /// 通知控制器 Groups 数据变化 static void _notifyControllersOnGroupsChanged() { try { // 通知 HomeController if (Get.isRegistered()) { Get.find().refreshOnLaunchChanged(); } // 通知 NodeController if (Get.isRegistered()) { Get.find().refreshOnLaunchChanged(); } } catch (e) { log('IXSP', 'Error notifying controllers: $e'); } } /// 获取 Launch 数据 static Launch? getLaunch() { try { final jsonData = _sharedPreferences.getString(_launchDataKey); if (jsonData == null) { return null; } final Map map = jsonDecode( Crypto.decrypt(jsonData, Keys.aesKey, Keys.aesIv), ); return Launch.fromJson(map); } catch (e) { log('IXSP', 'Error getting launch data: $e'); return null; } } /// 获取用户信息 static User? getUser() { final launch = getLaunch(); return launch?.userConfig; } /// 获取app配置 static AppConfig? getAppConfig() { final launch = getLaunch(); return launch?.appConfig; } /// 获取升级信息 static Upgrade? getUpgrade() { final launch = getLaunch(); return launch?.upgradeConfig; } /// 保存用户信息 static Future saveUser(User user) async { final launch = getLaunch(); final newLaunch = launch!.copyWith(userConfig: user); await saveLaunch(newLaunch); } /// 保存app配置 static Future saveAppConfig(AppConfig appConfig) async { final launch = getLaunch(); final newLaunch = launch!.copyWith(appConfig: appConfig); await saveLaunch(newLaunch); } /// 保存 Groups 数据 static Future saveGroups(Groups groups) async { final launch = getLaunch(); final newLaunch = launch!.copyWith(groups: groups); await saveLaunch(newLaunch); } /// 清除 Launch 数据 static Future clearLaunchData() async { try { await _sharedPreferences.remove(_launchDataKey); log('IXSP', 'Launch data cleared'); return true; } catch (e) { log('IXSP', 'Error clearing launch data: $e'); return false; } } /// 通用获取字符串方法 static String? getString(String key) { return _sharedPreferences.getString(key); } /// 通用设置字符串方法 static Future setString(String key, String value) async { try { return await _sharedPreferences.setString(key, value); } catch (e) { log('IXSP', 'Error setting string for key $key: $e'); return false; } } }