home_controller.dart 12 KB

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