ix_sp.dart 13 KB

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