| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- // lib/utils/device_manager.dart
- import 'dart:io';
- import 'dart:convert';
- import 'package:crypto/crypto.dart';
- import 'package:device_info_plus/device_info_plus.dart';
- import 'package:get/get.dart';
- import 'package:path/path.dart' as path;
- import 'package:path_provider/path_provider.dart';
- import 'package:uuid/uuid.dart';
- import 'package:flutter_secure_storage/flutter_secure_storage.dart';
- import '../app/data/sp/ix_sp.dart';
- import 'log/logger.dart';
- import 'permission_manager.dart';
- class DeviceManager {
- static const String TAG = 'DeviceManager';
- static final DeviceInfoPlugin _deviceInfo = DeviceInfoPlugin();
- static const String _folderPrefix = '.omon_cache_';
- static const String _iosDeviceIdKey = 'omon_device_id';
- static const String _iosDeviceFolderKey = 'omon_device_folder';
- /// 获取设备唯一标识并确保文件夹存在
- static Future<String> getDeviceId() async {
- try {
- // 1. 先从 SharedPreferences 获取
- String? deviceId = IXSP.getDeviceId();
- // 2. 如果SP中没有,尝试从存储位置获取
- if (deviceId == null || deviceId.isEmpty) {
- try {
- deviceId = await _getDeviceIdFromStorage();
- } catch (e) {
- log(TAG, 'Error reading from storage: $e');
- }
- }
- // 3. 如果还是没有,生成新的
- if (deviceId == null || deviceId.isEmpty) {
- try {
- deviceId = await _generateNewDeviceId();
- } catch (e) {
- log(TAG, 'Error generating device ID: $e');
- deviceId = _generateFallbackId();
- }
- }
- // 4. 确保保存到存储位置
- try {
- await _saveDeviceId(deviceId);
- } catch (e) {
- log(TAG, 'Error saving device ID: $e');
- }
- // 5. 尝试创建文件夹(仅Android),但不影响返回结果
- try {
- await _ensureDeviceFolder(deviceId);
- } catch (e) {
- log(TAG, 'Error creating folder: $e');
- // 文件夹创建失败不影响 ID 的使用
- }
- return deviceId;
- } catch (e) {
- log(TAG, 'Critical error in getDeviceId: $e');
- // 最后的后备方案:生成随机ID
- final fallbackId = _generateFallbackId();
- try {
- await _saveDeviceId(fallbackId);
- } catch (e) {
- log(TAG, 'Error saving fallback ID: $e');
- }
- return fallbackId;
- }
- }
- /// 获取缓存
- static String getCacheDeviceId() {
- return IXSP.getDeviceId() ?? '';
- }
- /// 从存储位置获取设备ID
- static Future<String?> _getDeviceIdFromStorage() async {
- if (Platform.isIOS) {
- // iOS 从钥匙串获取
- try {
- const storage = FlutterSecureStorage();
- final deviceId = await storage.read(key: _iosDeviceIdKey);
- if (deviceId != null && deviceId.isNotEmpty) {
- await IXSP.setDeviceId(deviceId);
- return deviceId;
- }
- } catch (e) {
- log(TAG, 'Error reading from iOS keychain: $e');
- }
- } else {
- // Android 从文件夹获取
- try {
- final downloadsDir = await _getDownloadsDirectory();
- if (downloadsDir == null || !await downloadsDir.exists()) {
- return null;
- }
- final entities = await downloadsDir.list().toList();
- for (var entity in entities) {
- if (entity is Directory) {
- final folderName = path.basename(entity.path);
- if (folderName.startsWith(_folderPrefix)) {
- final uuid = folderName.substring(_folderPrefix.length);
- // 验证UUID格式
- if (_isValidUuid(uuid)) {
- // 找到有效的UUID,保存到SP
- await IXSP.setDeviceId(uuid);
- return uuid;
- }
- }
- }
- }
- } catch (e) {
- log(TAG, 'Error getting device ID from Android folder: $e');
- }
- }
- return null;
- }
- /// 保存设备ID到存储位置
- static Future<void> _saveDeviceId(String deviceId) async {
- // 保存到 SharedPreferences(跨平台)
- await IXSP.setDeviceId(deviceId);
- if (Platform.isIOS) {
- // iOS 保存到钥匙串
- try {
- const storage = FlutterSecureStorage();
- await storage.write(key: _iosDeviceIdKey, value: deviceId);
- } catch (e) {
- log(TAG, 'Error saving to iOS keychain: $e');
- }
- }
- }
- /// 生成新的设备ID
- static Future<String> _generateNewDeviceId() async {
- if (GetPlatform.isAndroid) {
- final AndroidDeviceInfo androidInfo = await _deviceInfo.androidInfo;
- final deviceInfo = {
- 'androidId': androidInfo.id,
- 'brand': androidInfo.brand,
- 'model': androidInfo.model,
- 'product': androidInfo.product,
- 'hardware': androidInfo.hardware,
- 'manufacturer': androidInfo.manufacturer,
- 'uuid': const Uuid().v4(),
- };
- final deviceId = _generateHash(jsonEncode(deviceInfo));
- await _saveDeviceId(deviceId);
- return deviceId;
- } else if (GetPlatform.isIOS) {
- final IosDeviceInfo iosInfo = await _deviceInfo.iosInfo;
- final deviceInfo = {
- 'identifierForVendor': iosInfo.identifierForVendor,
- 'model': iosInfo.model,
- 'systemVersion': iosInfo.systemVersion,
- 'uuid': const Uuid().v4(),
- };
- final deviceId = _generateHash(jsonEncode(deviceInfo));
- await _saveDeviceId(deviceId);
- return deviceId;
- }
- return _generateFallbackId();
- }
- /// 确保设备文件夹存在
- static Future<void> _ensureDeviceFolder(String deviceId) async {
- // iOS 不需要创建文件夹,数据存储在钥匙串中
- if (Platform.isIOS) {
- return;
- }
- try {
- final downloadsDir = await _getDownloadsDirectory();
- if (downloadsDir == null || !await downloadsDir.exists()) {
- throw Exception('Downloads directory not found');
- }
- final folderName = '$_folderPrefix$deviceId';
- final deviceFolder = Directory('${downloadsDir.path}/$folderName');
- if (!await deviceFolder.exists()) {
- await deviceFolder.create();
- // 创建.nomedia文件
- final nomedia = File('${deviceFolder.path}/.nomedia');
- await nomedia.create();
- }
- } catch (e) {
- log(TAG, 'Error ensuring device folder: $e');
- rethrow;
- }
- }
- /// 获取下载目录
- static Future<Directory?> _getDownloadsDirectory() async {
- if (Platform.isAndroid) {
- try {
- // 首先尝试获取应用的外部存储目录
- // 判断是不是Android10以下
- final hasPermission =
- await PermissionManager.requestStoragePermission();
- if (!hasPermission) {
- throw Exception('Storage permission denied');
- }
- final downloadsDir = Directory('/storage/emulated/0/Download');
- return downloadsDir;
- } catch (e) {
- log(TAG, 'Error getting downloads directory: $e');
- final downloadsDir = getExternalStorageDirectory();
- return downloadsDir;
- }
- }
- return null;
- }
- /// 生成哈希值
- static String _generateHash(String input) {
- final bytes = utf8.encode(input);
- final digest = md5.convert(bytes);
- return digest.toString();
- }
- /// 生成后备ID
- static String _generateFallbackId() {
- final random = DateTime.now().millisecondsSinceEpoch.toString();
- return _generateHash(random);
- }
- /// 验证UUID格式
- static bool _isValidUuid(String uuid) {
- // 验证是否为32位十六进制字符
- return RegExp(r'^[a-f0-9]{32}$').hasMatch(uuid);
- }
- /// 获取设备文件夹路径
- static Future<String> getDeviceFolderPath() async {
- if (Platform.isIOS) {
- // iOS 返回钥匙串标识符
- return 'iOS Keychain: $_iosDeviceIdKey';
- }
- final deviceId = await getDeviceId();
- return '/storage/emulated/0/Download/$_folderPrefix$deviceId';
- }
- /// 清除所有数据
- static Future<void> clearAll() async {
- try {
- if (Platform.isIOS) {
- // iOS 清除钥匙串数据
- try {
- const storage = FlutterSecureStorage();
- await storage.delete(key: _iosDeviceIdKey);
- await storage.delete(key: _iosDeviceFolderKey);
- } catch (e) {
- log(TAG, 'Error clearing iOS keychain: $e');
- }
- } else {
- // Android 删除文件夹
- try {
- final downloadsDir = await _getDownloadsDirectory();
- if (downloadsDir != null && await downloadsDir.exists()) {
- final entities = await downloadsDir.list().toList();
- for (var entity in entities) {
- if (entity is Directory) {
- final folderName = path.basename(entity.path);
- if (folderName.startsWith(_folderPrefix)) {
- await entity.delete(recursive: true);
- }
- }
- }
- }
- } catch (e) {
- log(TAG, 'Error clearing Android folders: $e');
- }
- }
- // 清除 SP 中的 UUID
- await IXSP.setDeviceId('');
- } catch (e) {
- log(TAG, 'Error clearing device data: $e');
- }
- }
- }
|