ix_sp.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';
  3. import 'package:shared_preferences/shared_preferences.dart';
  4. import 'dart:convert';
  5. import '../../../config/translations/localization_service.dart';
  6. import '../../../utils/crypto.dart';
  7. import '../../../utils/log/logger.dart';
  8. import '../../constants/keys.dart';
  9. import '../../constants/sp_keys.dart';
  10. import '../models/banner/banner_list.dart';
  11. import '../models/launch/app_config.dart';
  12. import '../models/launch/groups.dart';
  13. import '../models/launch/launch.dart';
  14. import '../models/launch/upgrade.dart';
  15. import '../models/launch/user.dart';
  16. import '../../modules/home/controllers/home_controller.dart';
  17. import '../../modules/node/controllers/node_controller.dart';
  18. class IXSP {
  19. // prevent making instance
  20. IXSP._();
  21. // get storage
  22. static late SharedPreferences _sharedPreferences;
  23. /// init get storage services
  24. static Future<void> init() async {
  25. _sharedPreferences = await SharedPreferences.getInstance();
  26. }
  27. static setStorage(SharedPreferences sharedPreferences) {
  28. _sharedPreferences = sharedPreferences;
  29. }
  30. /// set theme current type as light theme
  31. static Future<void> setThemeIsLight(bool lightTheme) =>
  32. _sharedPreferences.setBool(SPKeys.lightTheme, lightTheme);
  33. /// get if the current theme type is light
  34. static bool getThemeIsLight() =>
  35. _sharedPreferences.getBool(SPKeys.lightTheme) ??
  36. false; // todo set the default theme (true for light, false for dark)
  37. /// save current locale
  38. static Future<void> setCurrentLanguage(String languageCode) =>
  39. _sharedPreferences.setString(SPKeys.currentLocal, languageCode);
  40. /// get current locale
  41. static Locale getCurrentLocal() {
  42. String? langCode = _sharedPreferences.getString(SPKeys.currentLocal);
  43. // default language is english
  44. if (langCode == null) {
  45. return LocalizationService.defaultLanguage;
  46. }
  47. return LocalizationService.supportedLanguages[langCode]!;
  48. }
  49. /// save generated fcm token
  50. static Future<void> setFcmToken(String token) =>
  51. _sharedPreferences.setString(SPKeys.fcmToken, token);
  52. /// get authorization token
  53. static String getFcmToken() =>
  54. _sharedPreferences.getString(SPKeys.fcmToken) ?? '';
  55. /// set launch game
  56. static Future<void> setLaunchGame(bool isLaunch) =>
  57. _sharedPreferences.setBool(SPKeys.launchGame, isLaunch);
  58. /// get launch game
  59. static bool getLaunchGame() =>
  60. _sharedPreferences.getBool(SPKeys.launchGame) ?? false;
  61. /// clear all data from shared pref
  62. static Future<void> clear() async => await _sharedPreferences.clear();
  63. /// Save unique ID to shared preferences
  64. static Future<void> setDeviceId(String deviceId) =>
  65. _sharedPreferences.setString(SPKeys.deviceId, deviceId);
  66. /// Get unique ID from shared preferences
  67. static String? getDeviceId() => _sharedPreferences.getString(SPKeys.deviceId);
  68. /// Save ignore version to shared preferences
  69. static Future<void> setIgnoreVersion(int version) =>
  70. _sharedPreferences.setInt(SPKeys.ignoreVersion, version);
  71. /// Get ignore version from shared preferences
  72. static int getIgnoreVersion() =>
  73. _sharedPreferences.getInt(SPKeys.ignoreVersion) ?? 0;
  74. /// Save is new install to shared preferences
  75. static Future<void> setIsNewInstall(bool isNewInstall) =>
  76. _sharedPreferences.setBool(SPKeys.isNewInstall, isNewInstall);
  77. /// Get is new install from shared preferences
  78. static bool getIsNewInstall() =>
  79. _sharedPreferences.getBool(SPKeys.isNewInstall) ?? true;
  80. // 记录最后一次是否是用户禁用
  81. static Future<void> setLastIsUserDisabled(bool isUserDisabled) async {
  82. await _sharedPreferences.setBool(SPKeys.isUserDisabled, isUserDisabled);
  83. }
  84. // 获取最后一次是否是用户禁用
  85. static bool getLastIsUserDisabled() {
  86. return _sharedPreferences.getBool(SPKeys.isUserDisabled) ?? false;
  87. }
  88. // 记录最后一次是否是地区禁用
  89. static Future<void> setLastIsRegionDisabled(bool isRegionDisabled) async {
  90. await _sharedPreferences.setBool(SPKeys.isRegionDisabled, isRegionDisabled);
  91. }
  92. // 获取最后一次是否是地区禁用
  93. static bool getLastIsRegionDisabled() {
  94. return _sharedPreferences.getBool(SPKeys.isRegionDisabled) ?? false;
  95. }
  96. // 记录最后一次是否是设备禁用
  97. static Future<void> setLastIsDeviceDisabled(bool isDeviceDisabled) async {
  98. await _sharedPreferences.setBool(SPKeys.isDeviceDisabled, isDeviceDisabled);
  99. }
  100. // 获取最后一次是否是设备禁用
  101. static bool getLastIsDeviceDisabled() {
  102. return _sharedPreferences.getBool(SPKeys.isDeviceDisabled) ?? false;
  103. }
  104. /// Save last metrics log
  105. static Future<void> setLastMetricsLog(String log) async {
  106. await _sharedPreferences.setString(SPKeys.lastMetricsLog, log);
  107. }
  108. /// Get last metrics log
  109. static String getLastMetricsLog() =>
  110. _sharedPreferences.getString(SPKeys.lastMetricsLog) ?? '';
  111. /// Save last es log
  112. static Future<void> setLastESLog(String log) async {
  113. await _sharedPreferences.setString(SPKeys.lastESLog, log);
  114. }
  115. /// Get last es log
  116. static String getLastESLog() =>
  117. _sharedPreferences.getString(SPKeys.lastESLog) ?? '';
  118. /// Save enable debug log
  119. static Future<void> setEnableDebugLog(bool enable) async {
  120. await _sharedPreferences.setBool(SPKeys.enableDebugLog, enable);
  121. }
  122. /// Get enable debug log
  123. static bool getEnableDebugLog() =>
  124. _sharedPreferences.getBool(SPKeys.enableDebugLog) ?? false;
  125. /// Save enable ping
  126. static Future<void> setEnablePingMode(int model) async {
  127. await _sharedPreferences.setInt(SPKeys.enablePingMode, model);
  128. }
  129. /// Get enable ping
  130. static int getEnablePingMode() =>
  131. _sharedPreferences.getInt(SPKeys.enablePingMode) ?? 0;
  132. /// 保存当前选中的节点
  133. static Future<void> saveSelectedLocation(
  134. Map<String, dynamic> location,
  135. ) async {
  136. try {
  137. await _sharedPreferences.setString(
  138. SPKeys.selectedLocation,
  139. jsonEncode(location),
  140. );
  141. log('IXSP', 'Selected location saved successfully');
  142. } catch (e) {
  143. log('IXSP', 'Error saving selected location: $e');
  144. }
  145. }
  146. /// 获取当前选中的节点
  147. static Map<String, dynamic>? getSelectedLocation() {
  148. try {
  149. final jsonData = _sharedPreferences.getString(SPKeys.selectedLocation);
  150. if (jsonData == null) {
  151. return null;
  152. }
  153. return jsonDecode(jsonData) as Map<String, dynamic>;
  154. } catch (e) {
  155. log('IXSP', 'Error getting selected location: $e');
  156. return null;
  157. }
  158. }
  159. /// 保存最近选择的节点列表
  160. static Future<void> saveRecentLocations(
  161. List<Map<String, dynamic>> locations,
  162. ) async {
  163. try {
  164. await _sharedPreferences.setString(
  165. SPKeys.recentLocations,
  166. jsonEncode(locations),
  167. );
  168. log('IXSP', 'Recent locations saved successfully');
  169. } catch (e) {
  170. log('IXSP', 'Error saving recent locations: $e');
  171. }
  172. }
  173. /// 获取最近选择的节点列表
  174. static List<Map<String, dynamic>> getRecentLocations() {
  175. try {
  176. final jsonData = _sharedPreferences.getString(SPKeys.recentLocations);
  177. if (jsonData == null || jsonData.isEmpty) {
  178. return [];
  179. }
  180. final List<dynamic> list = jsonDecode(jsonData);
  181. return list.map((e) => e as Map<String, dynamic>).toList();
  182. } catch (e) {
  183. log('IXSP', 'Error getting recent locations: $e');
  184. return [];
  185. }
  186. }
  187. /// 保存 Launch 数据
  188. static Future<bool> saveLaunch(Launch launch) async {
  189. try {
  190. // 获取旧的 Launch 数据,用于比较 groups
  191. final oldLaunch = getLaunch();
  192. final oldGroups = oldLaunch?.groups;
  193. final newGroups = launch.groups;
  194. final jsonData = jsonEncode(launch.toJson());
  195. // 保存数据
  196. await _sharedPreferences.setString(
  197. SPKeys.launchData,
  198. Crypto.encrypt(jsonData, Keys.aesKey, Keys.aesIv),
  199. );
  200. log('IXSP', 'Launch data saved successfully');
  201. // 检查 groups 是否有变化,如果有则通知更新
  202. if (_isGroupsChanged(oldGroups, newGroups)) {
  203. log('IXSP', 'Groups data changed, notifying controllers');
  204. _notifyControllersOnGroupsChanged();
  205. }
  206. return true;
  207. } catch (e) {
  208. log('IXSP', 'Error saving launch data: $e');
  209. return false;
  210. }
  211. }
  212. /// 检查 Groups 数据是否有变化
  213. static bool _isGroupsChanged(Groups? oldGroups, Groups? newGroups) {
  214. // 如果两者都为 null,没有变化
  215. if (oldGroups == null && newGroups == null) return false;
  216. // 如果一个为 null 一个不为 null,有变化
  217. if (oldGroups == null || newGroups == null) return true;
  218. // 比较 JSON 序列化后的字符串
  219. try {
  220. final oldJson = jsonEncode(oldGroups.toJson());
  221. final newJson = jsonEncode(newGroups.toJson());
  222. return oldJson != newJson;
  223. } catch (e) {
  224. log('IXSP', 'Error comparing groups: $e');
  225. return true; // 出错时默认认为有变化
  226. }
  227. }
  228. /// 通知控制器 Groups 数据变化
  229. static void _notifyControllersOnGroupsChanged() {
  230. try {
  231. // 通知 HomeController
  232. if (Get.isRegistered<HomeController>()) {
  233. Get.find<HomeController>().refreshOnLaunchChanged();
  234. }
  235. // 通知 NodeController
  236. if (Get.isRegistered<NodeController>()) {
  237. Get.find<NodeController>().refreshOnLaunchChanged();
  238. }
  239. } catch (e) {
  240. log('IXSP', 'Error notifying controllers: $e');
  241. }
  242. }
  243. /// 获取 Launch 数据
  244. static Launch? getLaunch() {
  245. try {
  246. final jsonData = _sharedPreferences.getString(SPKeys.launchData);
  247. if (jsonData == null) {
  248. return null;
  249. }
  250. final Map<String, dynamic> map = jsonDecode(
  251. Crypto.decrypt(jsonData, Keys.aesKey, Keys.aesIv),
  252. );
  253. return Launch.fromJson(map);
  254. } catch (e) {
  255. log('IXSP', 'Error getting launch data: $e');
  256. return null;
  257. }
  258. }
  259. /// 获取用户信息
  260. static User? getUser() {
  261. final launch = getLaunch();
  262. return launch?.userConfig;
  263. }
  264. /// 获取app配置
  265. static AppConfig? getAppConfig() {
  266. final launch = getLaunch();
  267. return launch?.appConfig;
  268. }
  269. /// 获取升级信息
  270. static Upgrade? getUpgrade() {
  271. final launch = getLaunch();
  272. return launch?.upgradeConfig;
  273. }
  274. /// 保存用户信息
  275. static Future<void> saveUser(User user) async {
  276. final launch = getLaunch();
  277. final newLaunch = launch!.copyWith(userConfig: user);
  278. await saveLaunch(newLaunch);
  279. }
  280. /// 保存app配置
  281. static Future<void> saveAppConfig(AppConfig appConfig) async {
  282. final launch = getLaunch();
  283. final newLaunch = launch!.copyWith(appConfig: appConfig);
  284. await saveLaunch(newLaunch);
  285. }
  286. /// 保存 Groups 数据
  287. static Future<void> saveGroups(Groups groups) async {
  288. final launch = getLaunch();
  289. final newLaunch = launch!.copyWith(groups: groups);
  290. await saveLaunch(newLaunch);
  291. }
  292. /// 清除 Launch 数据
  293. static Future<bool> clearLaunchData() async {
  294. try {
  295. await _sharedPreferences.remove(SPKeys.launchData);
  296. log('IXSP', 'Launch data cleared');
  297. return true;
  298. } catch (e) {
  299. log('IXSP', 'Error clearing launch data: $e');
  300. return false;
  301. }
  302. }
  303. /// 通用获取字符串方法
  304. static String? getString(String key) {
  305. return _sharedPreferences.getString(key);
  306. }
  307. /// 通用设置字符串方法
  308. static Future<bool> setString(String key, String value) async {
  309. try {
  310. return await _sharedPreferences.setString(key, value);
  311. } catch (e) {
  312. log('IXSP', 'Error setting string for key $key: $e');
  313. return false;
  314. }
  315. }
  316. /// 保存 Banner 缓存
  317. /// [position] banner 位置类型,如 "boost"、"home" 等
  318. static Future<bool> saveBanner(String position, BannerList bannerList) async {
  319. try {
  320. final key = SPKeys.bannerCacheKey(position);
  321. final jsonData = jsonEncode(bannerList.toJson());
  322. await _sharedPreferences.setString(key, jsonData);
  323. log('IXSP', 'Banner cache saved for position: $position');
  324. return true;
  325. } catch (e) {
  326. log('IXSP', 'Error saving banner cache for position $position: $e');
  327. return false;
  328. }
  329. }
  330. /// 获取 Banner 缓存
  331. /// [position] banner 位置类型,如 "boost"、"home" 等
  332. static BannerList? getBanner(String position) {
  333. try {
  334. final key = SPKeys.bannerCacheKey(position);
  335. final jsonData = _sharedPreferences.getString(key);
  336. if (jsonData == null) {
  337. return null;
  338. }
  339. final Map<String, dynamic> map = jsonDecode(jsonData);
  340. return BannerList.fromJson(map);
  341. } catch (e) {
  342. log('IXSP', 'Error getting banner cache for position $position: $e');
  343. return null;
  344. }
  345. }
  346. /// 清除指定位置的 Banner 缓存
  347. static Future<bool> clearBanner(String position) async {
  348. try {
  349. final key = SPKeys.bannerCacheKey(position);
  350. await _sharedPreferences.remove(key);
  351. log('IXSP', 'Banner cache cleared for position: $position');
  352. return true;
  353. } catch (e) {
  354. log('IXSP', 'Error clearing banner cache for position $position: $e');
  355. return false;
  356. }
  357. }
  358. }