home_controller.dart 13 KB

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