主要变更: 1. 全局修复类型转换问题,将多处`as int?`改为`(num?)?.toInt()`兼容浮点/字符串类型的数字字段 2. 移除废弃的nearby_p2p配对方式和对应的依赖包 3. 优化鸿蒙端快捷方式、引导页、路由导航的稳定性 4. 合并日志输出避免鸿蒙端IDE卡顿 5. 修复安卓端蓝牙权限冗余声明
262 lines
8.3 KiB
Dart
262 lines
8.3 KiB
Dart
// ============================================================
|
||
// 闲言APP — 传输设备模型
|
||
// 创建时间: 2026-05-09
|
||
// 更新时间: 2026-05-20
|
||
// 作用: 文件传输助手远程设备数据模型 — 设备信息/配对状态/传输偏好
|
||
// 上次更新: 删除fromJsonWithDefaults重复代码(与fromJson完全重复且缺少字段),统一使用fromJson
|
||
// ============================================================
|
||
|
||
import 'transfer_enums.dart';
|
||
|
||
class TransferDevice {
|
||
const TransferDevice({
|
||
required this.id,
|
||
required this.alias,
|
||
required this.deviceType,
|
||
required this.port,
|
||
required this.pairingMethod,
|
||
required this.preferredTransport,
|
||
required this.lastSeen,
|
||
required this.isOnline,
|
||
required this.isVerified,
|
||
this.deviceModel,
|
||
this.ip,
|
||
this.publicKey,
|
||
this.fingerprint,
|
||
this.isFavorite = false,
|
||
this.userId,
|
||
this.ipCity,
|
||
this.ipRange,
|
||
this.accountAlias,
|
||
});
|
||
|
||
final String id;
|
||
final String alias;
|
||
final String? deviceModel;
|
||
final DeviceType deviceType;
|
||
final String? ip;
|
||
final int port;
|
||
final PairingMethod pairingMethod;
|
||
final TransportType preferredTransport;
|
||
final DateTime lastSeen;
|
||
final bool isOnline;
|
||
final bool isVerified;
|
||
final String? publicKey;
|
||
final String? fingerprint;
|
||
final bool isFavorite;
|
||
final String? userId;
|
||
final String? ipCity;
|
||
final String? ipRange;
|
||
final String? accountAlias;
|
||
|
||
String get displayEmoji => deviceType.emoji;
|
||
String get displayType => deviceType.label;
|
||
String get displayTransport => preferredTransport.label;
|
||
String get displayStatus {
|
||
if (isOnline) return '🟢 在线';
|
||
return '⚪ 离线';
|
||
}
|
||
|
||
String get displayAlias {
|
||
if (accountAlias != null && accountAlias!.isNotEmpty) return accountAlias!;
|
||
if (deviceType == DeviceType.web)
|
||
return alias.isNotEmpty ? alias : 'Web浏览器';
|
||
if (alias != '闲言设备' && alias != '未知设备') return alias;
|
||
final parts = <String>[];
|
||
if (deviceModel != null && deviceModel!.isNotEmpty && deviceModel != 'localhost') {
|
||
parts.add(deviceModel!);
|
||
}
|
||
if (parts.isNotEmpty) return parts.join(' · ');
|
||
return alias;
|
||
}
|
||
|
||
String get displayInfo {
|
||
final parts = <String>[];
|
||
parts.add(displayType);
|
||
if (ip != null) parts.add(ip!);
|
||
parts.add('$port');
|
||
return parts.join(' · ');
|
||
}
|
||
|
||
bool get hasIp => ip != null && ip!.isNotEmpty;
|
||
bool get hasPublicKey => publicKey != null && publicKey!.isNotEmpty;
|
||
bool get hasIpCity => ipCity != null && ipCity!.isNotEmpty;
|
||
bool get hasIpRange => ipRange != null && ipRange!.isNotEmpty;
|
||
bool get isAccountDevice => userId != null && userId!.isNotEmpty;
|
||
String get displayLocation => hasIpCity ? '📍 $ipCity' : '';
|
||
|
||
TransferDevice copyWith({
|
||
String? id,
|
||
String? alias,
|
||
String? deviceModel,
|
||
DeviceType? deviceType,
|
||
String? ip,
|
||
int? port,
|
||
PairingMethod? pairingMethod,
|
||
TransportType? preferredTransport,
|
||
DateTime? lastSeen,
|
||
bool? isOnline,
|
||
bool? isVerified,
|
||
String? publicKey,
|
||
String? fingerprint,
|
||
bool? isFavorite,
|
||
String? userId,
|
||
String? ipCity,
|
||
String? ipRange,
|
||
String? accountAlias,
|
||
}) {
|
||
return TransferDevice(
|
||
id: id ?? this.id,
|
||
alias: alias ?? this.alias,
|
||
deviceModel: deviceModel ?? this.deviceModel,
|
||
deviceType: deviceType ?? this.deviceType,
|
||
ip: ip ?? this.ip,
|
||
port: port ?? this.port,
|
||
pairingMethod: pairingMethod ?? this.pairingMethod,
|
||
preferredTransport: preferredTransport ?? this.preferredTransport,
|
||
lastSeen: lastSeen ?? this.lastSeen,
|
||
isOnline: isOnline ?? this.isOnline,
|
||
isVerified: isVerified ?? this.isVerified,
|
||
publicKey: publicKey ?? this.publicKey,
|
||
fingerprint: fingerprint ?? this.fingerprint,
|
||
isFavorite: isFavorite ?? this.isFavorite,
|
||
userId: userId ?? this.userId,
|
||
ipCity: ipCity ?? this.ipCity,
|
||
ipRange: ipRange ?? this.ipRange,
|
||
accountAlias: accountAlias ?? this.accountAlias,
|
||
);
|
||
}
|
||
|
||
Map<String, dynamic> toJson() => {
|
||
'id': id,
|
||
'alias': alias,
|
||
'deviceModel': deviceModel,
|
||
'deviceType': deviceType.id,
|
||
'ip': ip,
|
||
'port': port,
|
||
'pairingMethod': pairingMethod.id,
|
||
'preferredTransport': preferredTransport.id,
|
||
'lastSeen': lastSeen.toIso8601String(),
|
||
'isOnline': isOnline,
|
||
'isVerified': isVerified,
|
||
'isFavorite': isFavorite,
|
||
'publicKey': publicKey,
|
||
'fingerprint': fingerprint,
|
||
'userId': userId,
|
||
'ipCity': ipCity,
|
||
'ipRange': ipRange,
|
||
'accountAlias': accountAlias,
|
||
};
|
||
|
||
factory TransferDevice.fromJson(Map<String, dynamic> json) {
|
||
return TransferDevice(
|
||
id: json['id'] as String? ?? '',
|
||
alias: json['alias'] as String? ?? '未知设备',
|
||
deviceModel: json['deviceModel'] as String?,
|
||
deviceType: DeviceType.fromId(json['deviceType'] as String? ?? 'mobile'),
|
||
ip: json['ip'] as String?,
|
||
port: (json['port'] as num?)?.toInt() ?? 53317,
|
||
pairingMethod: PairingMethod.fromId(
|
||
json['pairingMethod'] as String? ?? 'lan',
|
||
),
|
||
preferredTransport: TransportType.fromId(
|
||
json['preferredTransport'] as String? ?? 'localsend_http',
|
||
),
|
||
lastSeen: json['lastSeen'] != null
|
||
? DateTime.parse(json['lastSeen'] as String)
|
||
: DateTime.now(),
|
||
isOnline: json['isOnline'] as bool? ?? false,
|
||
isVerified: json['isVerified'] as bool? ?? false,
|
||
isFavorite: json['isFavorite'] as bool? ?? false,
|
||
publicKey: json['publicKey'] as String?,
|
||
fingerprint: json['fingerprint'] as String?,
|
||
userId: json['userId'] as String?,
|
||
ipCity: json['ipCity'] as String?,
|
||
ipRange: json['ipRange'] as String?,
|
||
accountAlias: json['accountAlias'] as String?,
|
||
);
|
||
}
|
||
|
||
factory TransferDevice.fromAnnounce(
|
||
Map<String, dynamic> announce,
|
||
String sourceIp,
|
||
) {
|
||
return TransferDevice(
|
||
id: announce['fingerprint'] as String? ?? '',
|
||
alias: announce['alias'] as String? ?? '未知设备',
|
||
deviceModel: announce['deviceModel'] as String?,
|
||
deviceType: DeviceType.fromId(
|
||
announce['deviceType'] as String? ?? 'mobile',
|
||
),
|
||
ip: sourceIp,
|
||
port: announce['port'] as int? ?? 53317,
|
||
pairingMethod: PairingMethod.lan,
|
||
preferredTransport: TransportType.localsendHttp,
|
||
lastSeen: DateTime.now(),
|
||
isOnline: true,
|
||
isVerified: false,
|
||
fingerprint: announce['fingerprint'] as String?,
|
||
);
|
||
}
|
||
|
||
factory TransferDevice.fromSignaling(Map<String, dynamic> data) {
|
||
final fingerprint = data['fingerprint'] as String?;
|
||
final serverId = data['id'] as String? ?? '';
|
||
final deviceId = (fingerprint != null && fingerprint.isNotEmpty)
|
||
? fingerprint
|
||
: serverId;
|
||
final rawDeviceType =
|
||
data['platform'] as String? ??
|
||
data['deviceType'] as String? ??
|
||
'mobile';
|
||
final resolvedDeviceType = DeviceType.fromId(rawDeviceType);
|
||
final isWeb =
|
||
resolvedDeviceType == DeviceType.web || rawDeviceType == 'browser';
|
||
|
||
final accountAlias =
|
||
data['accountAlias'] as String? ??
|
||
data['nickname'] as String? ??
|
||
data['username'] as String?;
|
||
|
||
return TransferDevice(
|
||
id: deviceId,
|
||
alias:
|
||
data['alias'] as String? ??
|
||
data['deviceName'] as String? ??
|
||
(isWeb ? 'Web浏览器' : '未知设备'),
|
||
deviceModel: data['deviceModel'] as String?,
|
||
deviceType: isWeb ? DeviceType.web : resolvedDeviceType,
|
||
ip: data['ip'] as String?,
|
||
port: 53317,
|
||
pairingMethod: PairingMethod.account,
|
||
preferredTransport: isWeb
|
||
? TransportType.wsRelay
|
||
: (data['rtcSupported'] as bool? ?? false)
|
||
? TransportType.webrtcP2p
|
||
: TransportType.wsRelay,
|
||
lastSeen: DateTime.now(),
|
||
isOnline: data['isOnline'] as bool? ?? true,
|
||
isVerified: false,
|
||
fingerprint: fingerprint ?? serverId,
|
||
userId: data['userId'] as String?,
|
||
ipCity: data['ipCity'] as String?,
|
||
ipRange: data['ipRange'] as String?,
|
||
accountAlias: accountAlias,
|
||
);
|
||
}
|
||
|
||
@override
|
||
String toString() =>
|
||
'TransferDevice(id: $id, alias: $alias, ip: $ip, online: $isOnline)';
|
||
|
||
@override
|
||
bool operator ==(Object other) =>
|
||
identical(this, other) ||
|
||
other is TransferDevice &&
|
||
runtimeType == other.runtimeType &&
|
||
id == other.id;
|
||
|
||
@override
|
||
int get hashCode => id.hashCode;
|
||
}
|