controller.dart 7.7 KB

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