controller.dart 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:clean/base/base_controller.dart';
  4. import 'package:clean/dialog/loading_dialog.dart';
  5. import 'package:clean/utils/expand.dart';
  6. import 'package:clean/utils/toast_util.dart';
  7. import 'package:flutter/cupertino.dart';
  8. import 'package:flutter_contacts/flutter_contacts.dart';
  9. import 'package:flutter_screenutil/flutter_screenutil.dart';
  10. import 'package:get/get.dart';
  11. import 'package:get/get_core/src/get_main.dart';
  12. import 'package:get/get_rx/src/rx_types/rx_types.dart';
  13. import 'package:path_provider/path_provider.dart';
  14. import 'package:wechat_camera_picker/wechat_camera_picker.dart';
  15. class ContactBackUpController extends BaseController {
  16. // 是否为编辑状态
  17. RxBool isEdit = false.obs;
  18. // 是否全选
  19. RxBool isAllSelected = false.obs;
  20. RxList<FileSystemEntity> backupFiles = <FileSystemEntity>[].obs;
  21. // 存储选中的
  22. final RxSet<String> selectedFiles = <String>{}.obs;
  23. RxMap<String, List<File>> backupFilesMap = <String, List<File>>{}.obs;
  24. @override
  25. void onInit() {
  26. // TODO: implement onInit
  27. super.onInit();
  28. loadBackupFiles();
  29. }
  30. void init() {
  31. }
  32. // 获取备份文件列表
  33. Future<void> loadBackupFiles() async {
  34. final directory = await getApplicationDocumentsDirectory();
  35. final files = Directory('${directory.path}/contacts').listSync();
  36. final List<FileSystemEntity> entities = await Directory(
  37. '${directory.path}/contacts').list().toList();
  38. // 使用sort方法对文件列表进行排序
  39. entities.sort((a, b) {
  40. FileStat fileStatA = a.statSync();
  41. FileStat fileStatB = b.statSync();
  42. return fileStatB.changed.compareTo(fileStatA.changed);
  43. });
  44. backupFiles.clear();
  45. Map<String, List<File>> filesMap = {};
  46. for (var entity in entities) {
  47. if (entity is File && entity.path.endsWith('.json')) {
  48. backupFiles.add(entity);
  49. final fileName = entity.path
  50. .split('/')
  51. .last;
  52. final timestamp = fileName
  53. .split('_')
  54. .last
  55. .split('.')
  56. .first;
  57. final dateTime = DateTime.fromMillisecondsSinceEpoch(
  58. int.parse(timestamp));
  59. final formattedDate = _formatDate(dateTime);
  60. if (!filesMap.containsKey(formattedDate)) {
  61. filesMap[formattedDate] = [];
  62. }
  63. filesMap[formattedDate]!.add(entity);
  64. }
  65. }
  66. backupFilesMap.value = filesMap;
  67. }
  68. // 格式化日期为 "MMM dd, yyyy"
  69. String _formatDate(DateTime dateTime) {
  70. return '${_getMonthAbbreviation(dateTime.month)} ${dateTime.day}, ${dateTime
  71. .year}';
  72. }
  73. // 获取月份的缩写(如 Jan, Feb)
  74. String _getMonthAbbreviation(int month) {
  75. switch (month) {
  76. case 1:
  77. return 'Jan';
  78. case 2:
  79. return 'Feb';
  80. case 3:
  81. return 'Mar';
  82. case 4:
  83. return 'Apr';
  84. case 5:
  85. return 'May';
  86. case 6:
  87. return 'Jun';
  88. case 7:
  89. return 'Jul';
  90. case 8:
  91. return 'Aug';
  92. case 9:
  93. return 'Sep';
  94. case 10:
  95. return 'Oct';
  96. case 11:
  97. return 'Nov';
  98. case 12:
  99. return 'Dec';
  100. default:
  101. return 'Unknown';
  102. }
  103. }
  104. // 备份通讯录
  105. Future<void> backupContacts() async {
  106. LoadingDialog.show(displayTime: 100);
  107. try {
  108. if (!await FlutterContacts.requestPermission()) {
  109. throw Exception('通讯录权限被拒绝');
  110. }
  111. List<Contact> contacts = await FlutterContacts.getContacts(
  112. withProperties: true,
  113. withThumbnail: false,
  114. );
  115. // 生成文件名
  116. final timestamp = DateTime
  117. .now()
  118. .millisecondsSinceEpoch;
  119. final fileName = '${contacts.length} contacts_$timestamp.json';
  120. // 保存到文件
  121. final directory = await getApplicationDocumentsDirectory();
  122. final dir = Directory('${directory.path}/contacts');
  123. if (!dir.existsSync()) {
  124. dir.createSync(recursive: true);
  125. }
  126. final file = File('${directory.path}/contacts/$fileName');
  127. await file.writeAsString(jsonEncode(contacts));
  128. // 刷新备份文件列表
  129. loadBackupFiles();
  130. // LoadingDialog.hide();
  131. ToastUtil.show("Successful");
  132. print('备份成功: ${file.path}');
  133. } catch (e) {
  134. LoadingDialog.hide();
  135. ToastUtil.show("Failed");
  136. print('备份失败: $e');
  137. }
  138. }
  139. // 恢复通讯录
  140. Future<void> restoreContacts(File file) async {
  141. if (!isEdit.value) {
  142. showCupertinoModalPopup(
  143. context: Get.context!,
  144. builder: (context) {
  145. return CupertinoActionSheet(
  146. title: Text(
  147. "Replace the current contacts in the phonebook with the selected backup file.",
  148. style: TextStyle(color: "#6E6E6E".color, fontSize: 12.sp,),),
  149. actions: <Widget>[
  150. //操作按钮集合
  151. CupertinoActionSheetAction(
  152. onPressed: () async {
  153. Navigator.pop(context);
  154. try {
  155. // 获取所有联系人
  156. List<Contact> contacts = await FlutterContacts.getContacts(
  157. withProperties: true,
  158. withPhoto: true,
  159. );
  160. for (var contact in contacts) {
  161. await contact.delete();
  162. }
  163. // 读取文件内容
  164. String contactsJsonString = await file.readAsString();
  165. List<dynamic> contactsJson = jsonDecode(contactsJsonString);
  166. // 恢复联系人
  167. for (var contactJson in contactsJson) {
  168. Contact contact = Contact.fromJson(contactJson);
  169. contact.id = "";
  170. await FlutterContacts.insertContact(contact);
  171. }
  172. ToastUtil.show("Successful");
  173. print('恢复成功');
  174. } catch (e) {
  175. print('恢复失败: $e');
  176. ToastUtil.show("Failed");
  177. }
  178. },
  179. child: Text(
  180. 'Restore now',
  181. style: TextStyle(
  182. color: "#007AFF".color,
  183. fontWeight: FontWeight.w500,
  184. fontSize: 16.sp,
  185. ),
  186. ),
  187. ),
  188. ],
  189. cancelButton: CupertinoActionSheetAction(
  190. //取消按钮
  191. onPressed: () {
  192. Navigator.pop(context);
  193. },
  194. child: Text(
  195. 'Cancel',
  196. style: TextStyle(
  197. color: "#007AFF".color,
  198. fontWeight: FontWeight.w500,
  199. fontSize: 16.sp,
  200. ),
  201. ),
  202. ),
  203. );
  204. },
  205. );
  206. }
  207. }
  208. // 选择/取消选择联系人
  209. void toggleSelectFile(File selectFile) {
  210. final file = backupFiles.firstWhere((file) => file.path == selectFile.path);
  211. if (selectedFiles.contains(selectFile.path)) {
  212. selectedFiles.remove(selectFile.path);
  213. } else {
  214. selectedFiles.add(selectFile.path);
  215. }
  216. // 更新全选状态
  217. isAllSelected.value = selectedFiles.length == backupFiles.length;
  218. }
  219. // 全选/取消全选
  220. void toggleSelectAll() {
  221. if (isAllSelected.value) {
  222. selectedFiles.clear();
  223. } else {
  224. selectedFiles.addAll(backupFiles.map((file) => file.path));
  225. }
  226. isAllSelected.value = !isAllSelected.value;
  227. }
  228. // 退出编辑模式时清空选择
  229. void exitEditMode() {
  230. isEdit.value = false;
  231. selectedFiles.clear();
  232. isAllSelected.value = false;
  233. }
  234. Future<void> deleteBtnClick() async {
  235. showCupertinoModalPopup(
  236. context: Get.context!,
  237. builder: (context) {
  238. return CupertinoActionSheet(
  239. actions: <Widget>[
  240. //操作按钮集合
  241. CupertinoActionSheetAction(
  242. onPressed: () async {
  243. Navigator.pop(context);
  244. // 获取要删除的资产
  245. final fileToDelete = backupFiles.where((file) => selectedFiles.contains(file.path)).toList();
  246. //
  247. for (var file in fileToDelete) {
  248. await file.delete();
  249. }
  250. ToastUtil.show("Successful");
  251. exitEditMode();
  252. loadBackupFiles();
  253. },
  254. child: Text(
  255. 'Delete',
  256. style: TextStyle(
  257. color: "#FC4C4F".color,
  258. fontWeight: FontWeight.w500,
  259. fontSize: 16.sp,
  260. ),
  261. ),
  262. ),
  263. ],
  264. cancelButton: CupertinoActionSheetAction(
  265. //取消按钮
  266. onPressed: () {
  267. Navigator.pop(context);
  268. },
  269. child: Text(
  270. 'Cancel',
  271. style: TextStyle(
  272. color: "#007AFF".color,
  273. fontWeight: FontWeight.w500,
  274. fontSize: 16.sp,
  275. ),
  276. ),
  277. ),
  278. );
  279. },
  280. );
  281. }
  282. }