home_controller.dart 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:airbridge_flutter_sdk/airbridge_flutter_sdk.dart';
  4. import 'package:classify_photo/classify_photo.dart';
  5. import 'package:clean/base/base_controller.dart';
  6. import 'package:clean/data/consts/constants.dart';
  7. import 'package:clean/data/repositories/event_repository.dart';
  8. import 'package:clean/data/repositories/user_repository.dart';
  9. import 'package:clean/module/image_picker/image_picker_util.dart';
  10. import 'package:clean/module/locations_photo/locations_photo_view.dart';
  11. import 'package:clean/module/people_photo/people_photo_view.dart';
  12. import 'package:clean/module/screenshots_blurry/screenshots_view.dart';
  13. import 'package:clean/module/similar_photo/similar_photo_view.dart';
  14. import 'package:clean/router/app_pages.dart';
  15. import 'package:clean/utils/toast_util.dart';
  16. import 'package:flutter/Material.dart';
  17. import 'package:get/get.dart';
  18. import 'package:permission_handler/permission_handler.dart';
  19. import 'package:wechat_assets_picker/wechat_assets_picker.dart';
  20. import '../../data/api/response/user_info_response.dart';
  21. import '../../data/consts/event_report_id.dart';
  22. import '../../data/repositories/config_repository.dart';
  23. import '../../handler/event_handler.dart';
  24. import '../../widget/multi_segment_circle_indicator.dart';
  25. class HomeController extends BaseController {
  26. Rx<double> totalSpace = 0.0.obs;
  27. Rx<double> usedSpace = 0.0.obs;
  28. Rx<double> photoSpace = 0.0.obs;
  29. Rx<double> freeSpace = 0.0.obs;
  30. Rx<String> totalSpaceStr = "".obs;
  31. Rx<String> usedSpaceStr = "".obs;
  32. Rx<String> photoSpaceStr = "".obs;
  33. Rx<String> freeSpaceStr = "".obs;
  34. // 计算已用存储百分比
  35. double get usedSpacePercentage => (usedSpace.value / totalSpace.value) * 100;
  36. // 计算照片占用存储百分比
  37. double get photoSpacePercentage =>
  38. (photoSpace.value / totalSpace.value) * 100;
  39. // 计算可用存储百分比
  40. double get freeSpacePercentage => (freeSpace.value / totalSpace.value) * 100;
  41. RxList<String> similarImages =
  42. List.generate(4, (index) => 'iconHomeNoPhoto').obs;
  43. RxInt imageCount = 0.obs;
  44. // 相似图片
  45. RxList<AssetEntity> similarPhotos = <AssetEntity>[].obs;
  46. // 人物图片
  47. RxList<AssetEntity> peoplePhotos = <AssetEntity>[].obs;
  48. // 地点图片
  49. Rx<AssetEntity?> locationPhoto = Rx<AssetEntity?>(null);
  50. // 截图照片
  51. Rx<AssetEntity?> screenshotPhoto = Rx<AssetEntity?>(null);
  52. // 模糊照片
  53. Rx<AssetEntity?> blurryPhoto = Rx<AssetEntity?>(null);
  54. // 是否扫描完成
  55. RxBool isSimilarScanned = false.obs;
  56. // 是否扫描完成
  57. RxBool isPeopleScanned = false.obs;
  58. // 是否扫描完成
  59. RxBool isScreenShotScanned = false.obs;
  60. // 是否扫描完成
  61. RxBool isBlurryScanned = false.obs;
  62. // 存储是否扫描完成
  63. RxBool isStorageScanned = false.obs;
  64. UserInfoResponse? get userInfo => userRepository.userInfo.value;
  65. List<PieData> get pieDataList => [
  66. PieData("PhotoSpace", photoSpacePercentage, Colors.blue),
  67. PieData("OtherUsed", usedSpacePercentage - photoSpacePercentage,
  68. Colors.red),
  69. PieData("totalSpace", totalSpace.value, Colors.grey.withOpacity(0.1)),
  70. ];
  71. @override
  72. Future<void> onInit() async {
  73. // TODO: implement onInit
  74. super.onInit();
  75. if (Platform.isAndroid) {
  76. await loadPhotosFromDirectory();
  77. // 延迟3秒
  78. Future.delayed(const Duration(seconds: 3), () {
  79. isStorageScanned.value = true;
  80. totalSpace.value = 200.0;
  81. usedSpace.value = 50.0;
  82. photoSpace.value = 30.0;
  83. freeSpace.value = 150.0;
  84. isScreenShotScanned.value = true;
  85. isBlurryScanned.value = true;
  86. isPeopleScanned.value = true;
  87. isSimilarScanned.value = true;
  88. isStorageScanned.value = true;
  89. });
  90. }
  91. // if (await Permission.photos.request().isGranted) {
  92. // PhotoManager.clearFileCache();
  93. // getStorageInfo();
  94. // handlePhotos();
  95. // } else {
  96. // ToastUtil.show("Please enable the album permission");
  97. // }
  98. configRepository.refreshConfig();
  99. await userRepository.getUserInfo();
  100. if (userRepository.userInfo.value != null) {
  101. Airbridge.setUserID(userRepository.userInfo.value!.ssid);
  102. // 接收归因结果
  103. Airbridge.setOnAttributionReceived((result) {
  104. print(result);
  105. Map<String, String> attr = <String, String>{};
  106. attr["attributedChannel"] = "Appstore";
  107. Airbridge.fetchDeviceUUID(onSuccess: (uuid) {
  108. eventRepository.attrPush(uuid, "airbridge", jsonEncode(result));
  109. });
  110. });
  111. }
  112. EventHandler.pushInstall();
  113. if (!isFirstIntoApp() && !userRepository.isVip()) {
  114. Get.toNamed(RoutePath.discount);
  115. }
  116. setFirstIntoApp(false);
  117. if (Platform.isAndroid) {
  118. loadPhotosFromDirectory();
  119. }
  120. var result= await Permission.photos.request();
  121. if (result.isGranted) {
  122. PhotoManager.clearFileCache();
  123. getStorageInfo();
  124. // handlePhotos();
  125. await handleScreenPhotos();
  126. await handleBlurryPhotos();
  127. await handlePeoplePhotos();
  128. await handleSimilarPhotos();
  129. } else if (result.isPermanentlyDenied) {
  130. ToastUtil.show("Please enable the album permission");
  131. openAppSettings();
  132. } else {
  133. ToastUtil.show("Please enable the album permission");
  134. }
  135. }
  136. @override
  137. void onReady() {
  138. super.onReady();
  139. // EventHandler.report(EventId.event_03000);
  140. }
  141. Future<void> loadPhotosFromDirectory() async {
  142. if (ImagePickerUtil.peoplePhotos.isEmpty ||
  143. ImagePickerUtil.similarPhotos.isEmpty ||
  144. ImagePickerUtil.locationPhotos.isEmpty ||
  145. ImagePickerUtil.screenshotPhotos.isEmpty) {
  146. try {
  147. final List<AssetEntity> result = await ImagePickerUtil.loadAssets();
  148. ImagePickerUtil.peoplePhotos.value = result ?? [];
  149. if (ImagePickerUtil.peoplePhotos.isNotEmpty) {
  150. for (var personPhotos in ImagePickerUtil.peoplePhotos) {
  151. peoplePhotos.add(personPhotos);
  152. if (peoplePhotos.length == 2) {
  153. break;
  154. }
  155. }
  156. }
  157. ImagePickerUtil.locationPhotos['location'] = result ?? [];
  158. if (ImagePickerUtil.locationPhotos.isNotEmpty) {
  159. // 获取第一个地点的第一张照片
  160. final firstLocationPhotos =
  161. ImagePickerUtil.locationPhotos.values.first;
  162. if (firstLocationPhotos.isNotEmpty) {
  163. var asset = firstLocationPhotos.first;
  164. locationPhoto.value = asset;
  165. }
  166. }
  167. ImagePickerUtil.screenshotPhotos.value = result ?? [];
  168. if (ImagePickerUtil.screenshotPhotos.isNotEmpty) {
  169. var asset = ImagePickerUtil.screenshotPhotos.first;
  170. screenshotPhoto.value = asset;
  171. }
  172. ImagePickerUtil.similarPhotos.add(result ?? []);
  173. if (ImagePickerUtil.similarPhotos.isNotEmpty) {
  174. for (var group in ImagePickerUtil.similarPhotos) {
  175. for (var asset in group) {
  176. similarPhotos.add(asset);
  177. if (similarPhotos.length == 4) {
  178. break;
  179. }
  180. }
  181. }
  182. }
  183. ImagePickerUtil.blurryPhotos.value = result ?? [];
  184. if (ImagePickerUtil.blurryPhotos.isNotEmpty) {
  185. var asset = ImagePickerUtil.blurryPhotos.first;
  186. blurryPhoto.value = asset;
  187. }
  188. } catch (e) {
  189. print('Error loading photos: $e');
  190. }
  191. }
  192. }
  193. Future<void> getStorageInfo() async {
  194. final classifyPhoto = ClassifyPhoto();
  195. try {
  196. final storageInfo = await classifyPhoto.getStorageInfo();
  197. // 转换为 GB
  198. final totalSpaceGB = storageInfo['totalSpace']! / (1000 * 1000 * 1000);
  199. final freeSpaceGB = storageInfo['freeSpace']! / (1024 * 1024 * 1024);
  200. final usedSpaceGB = storageInfo['usedSpace']! / (1024 * 1024 * 1024);
  201. final photoSpaceGB = storageInfo['photoSpace']! / (1024 * 1024 * 1024);
  202. totalSpaceStr.value = ImagePickerUtil.formatFileSize(
  203. storageInfo['totalSpace']!,
  204. decimals: 1);
  205. freeSpaceStr.value = ImagePickerUtil.formatFileSize(
  206. storageInfo['freeSpace']!,
  207. decimals: 1);
  208. usedSpaceStr.value = ImagePickerUtil.formatFileSize(
  209. storageInfo['usedSpace']!,
  210. decimals: 1);
  211. photoSpaceStr.value = ImagePickerUtil.formatFileSize(
  212. storageInfo['photoSpace']!,
  213. decimals: 1);
  214. totalSpace.value = totalSpaceGB.round().toDouble();
  215. freeSpace.value = freeSpaceGB;
  216. usedSpace.value = usedSpaceGB;
  217. photoSpace.value = photoSpaceGB;
  218. print('总容量: $totalSpaceStr');
  219. print('可用空间: $freeSpaceStr');
  220. print('已用空间: $usedSpaceStr');
  221. print('照片占用: $photoSpaceStr');
  222. isStorageScanned.value = true;
  223. } catch (e) {
  224. print('获取存储信息失败: $e');
  225. }
  226. }
  227. Future<void> handleScreenPhotos() async {
  228. final photoClassify = ClassifyPhoto();
  229. try {
  230. print('开始获取截图照片');
  231. final photos = await photoClassify.getScreenshots();
  232. print('获取截图照片完成: ${photos?.length ?? 0} 组照片');
  233. isScreenShotScanned.value = true;
  234. if (photos != null) {
  235. await ImagePickerUtil.updatePhotos(photos);
  236. if (ImagePickerUtil.screenshotPhotos.isNotEmpty) {
  237. var asset = ImagePickerUtil.screenshotPhotos.first;
  238. screenshotPhoto.value = asset;
  239. }
  240. }
  241. } catch (e, stackTrace) {
  242. print('获取照片失败: $e');
  243. print('Stack trace: $stackTrace');
  244. }
  245. }
  246. Future<void> handleBlurryPhotos() async {
  247. final photoClassify = ClassifyPhoto();
  248. try {
  249. print('开始获取模糊照片');
  250. final photos = await photoClassify.getBlurryPhotos();
  251. print('获取模糊照片完成: ${photos?.length ?? 0} 组照片');
  252. isBlurryScanned.value = true;
  253. if (photos != null) {
  254. await ImagePickerUtil.updatePhotos(photos);
  255. if (ImagePickerUtil.blurryPhotos.isNotEmpty) {
  256. var asset = ImagePickerUtil.blurryPhotos.first;
  257. blurryPhoto.value = asset;
  258. }
  259. }
  260. } catch (e, stackTrace) {
  261. print('获取照片失败: $e');
  262. print('Stack trace: $stackTrace');
  263. }
  264. }
  265. Future<void> handlePeoplePhotos() async {
  266. final photoClassify = ClassifyPhoto();
  267. try {
  268. print('开始获取人物照片');
  269. final photos = await photoClassify.getPeoplePhotos();
  270. print('获取人物照片完成: ${photos?.length ?? 0} 组照片');
  271. isPeopleScanned.value = true;
  272. if (photos != null) {
  273. await ImagePickerUtil.updatePhotos(photos);
  274. // 处理人物照片
  275. peoplePhotos.clear();
  276. if (ImagePickerUtil.peoplePhotos.isNotEmpty) {
  277. for (var personPhotos in ImagePickerUtil.peoplePhotos) {
  278. peoplePhotos.add(personPhotos);
  279. if (peoplePhotos.length == 2) {
  280. break;
  281. }
  282. }
  283. }
  284. }
  285. } catch (e, stackTrace) {
  286. print('获取照片失败: $e');
  287. print('Stack trace: $stackTrace');
  288. }
  289. }
  290. Future<void> handleSimilarPhotos() async {
  291. final photoClassify = ClassifyPhoto();
  292. try {
  293. print('开始获取相似照片');
  294. final photos = await photoClassify.getSimilarPhotos();
  295. print('获取相似照片完成: ${photos?.length ?? 0} 组照片');
  296. isSimilarScanned.value = true;
  297. if (photos != null) {
  298. await ImagePickerUtil.updatePhotos(photos);
  299. similarPhotos.clear();
  300. if (ImagePickerUtil.similarPhotos.isNotEmpty) {
  301. for (var group in ImagePickerUtil.similarPhotos) {
  302. for (var asset in group) {
  303. similarPhotos.add(asset);
  304. if (similarPhotos.length == 4) {
  305. break;
  306. }
  307. }
  308. }
  309. }
  310. }
  311. } catch (e, stackTrace) {
  312. print('获取照片失败: $e');
  313. print('Stack trace: $stackTrace');
  314. }
  315. }
  316. Future<void> handlePhotos() async {
  317. final photoClassify = ClassifyPhoto();
  318. try {
  319. print('开始获取照片');
  320. final photos = await photoClassify.getPhoto();
  321. print('获取照片完成: ${photos?.length ?? 0} 组照片');
  322. // 已完成扫描
  323. // isScanned.value = true;
  324. if (photos != null) {
  325. await ImagePickerUtil.updatePhotos(photos);
  326. similarPhotos.clear();
  327. if (ImagePickerUtil.similarPhotos.isNotEmpty) {
  328. for (var group in ImagePickerUtil.similarPhotos) {
  329. for (var asset in group) {
  330. similarPhotos.add(asset);
  331. if (similarPhotos.length == 4) {
  332. break;
  333. }
  334. }
  335. }
  336. }
  337. // 处理地点照片
  338. locationPhoto.value = null;
  339. if (ImagePickerUtil.locationPhotos.isNotEmpty) {
  340. // 获取第一个地点的第一张照片
  341. final firstLocationPhotos =
  342. ImagePickerUtil.locationPhotos.values.first;
  343. if (firstLocationPhotos.isNotEmpty) {
  344. var asset = firstLocationPhotos.first;
  345. locationPhoto.value = asset;
  346. }
  347. }
  348. // 处理人物照片
  349. peoplePhotos.clear();
  350. if (ImagePickerUtil.peoplePhotos.isNotEmpty) {
  351. for (var personPhotos in ImagePickerUtil.peoplePhotos) {
  352. peoplePhotos.add(personPhotos);
  353. if (peoplePhotos.length == 2) {
  354. break;
  355. }
  356. }
  357. }
  358. if (ImagePickerUtil.screenshotPhotos.isNotEmpty) {
  359. var asset = ImagePickerUtil.screenshotPhotos.first;
  360. screenshotPhoto.value = asset;
  361. }
  362. if (ImagePickerUtil.blurryPhotos.isNotEmpty) {
  363. var asset = ImagePickerUtil.blurryPhotos.first;
  364. blurryPhoto.value = asset;
  365. }
  366. }
  367. } catch (e, stackTrace) {
  368. print('获取照片失败: $e');
  369. print('Stack trace: $stackTrace');
  370. }
  371. }
  372. void _navigateAndStartPage(Function pageStartFunction) {
  373. // if (isFirstClickHomeClean()) {
  374. // setFirstClickHomeClean(false);
  375. // Get.toNamed(RoutePath.discount)?.then((value) {
  376. // pageStartFunction();
  377. // });
  378. // } else {
  379. pageStartFunction();
  380. // }
  381. }
  382. similarCleanClick() {
  383. print('similarCleanClick');
  384. EventHandler.report(EventId.event_03001);
  385. _navigateAndStartPage(SimilarPhotoPage.start);
  386. }
  387. peopleCleanClick() {
  388. print('peopleCleanClick');
  389. EventHandler.report(EventId.event_03002);
  390. _navigateAndStartPage(PeoplePhotoPage.start);
  391. }
  392. locationCleanClick() {
  393. print('locationCleanClick');
  394. EventHandler.report(EventId.event_03003);
  395. _navigateAndStartPage(LocationsPhotoPage.start);
  396. }
  397. screenshotCleanClick() {
  398. print('screenshotCleanClick');
  399. EventHandler.report(EventId.event_03004);
  400. _navigateAndStartPage(() => ScreenshotsPage.start("Screenshots"));
  401. }
  402. blurryCleanClick() {
  403. print('blurCleanClick');
  404. EventHandler.report(EventId.event_03005);
  405. _navigateAndStartPage(() => ScreenshotsPage.start("Blurry"));
  406. }
  407. titleVipClick() {
  408. EventHandler.report(EventId.event_02000);
  409. Get.toNamed(RoutePath.store);
  410. }
  411. }