Files
xianyan/lib/features/file_transfer/services/notification_service.dart
Developer c44457f94c style: 修复文件头部注释的多余BOM头字符
移除所有文件头部的不可见BOM前缀字符,统一文件头部注释格式,确保跨平台编译一致性
2026-05-27 08:05:47 +08:00

215 lines
6.1 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// 闲言APP — 传输通知服务
// 创建时间: 2026-05-10
// 更新时间: 2026-05-17
// 作用: 传输完成/失败/接收等本地通知
// 上次更新: 鸿蒙适配-使用桥接方法隔离OhosInitializationSettings
// ============================================================
import 'dart:io';
import 'package:xianyan/core/utils/platform/platform_utils.dart' as pu;
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:xianyan/core/utils/logger.dart';
import 'package:xianyan/core/services/notification/notification_init_stub.dart';
import '../models/transfer_enums.dart';
import '../models/transfer_task.dart';
class TransferNotificationService {
TransferNotificationService();
final FlutterLocalNotificationsPlugin _plugin =
FlutterLocalNotificationsPlugin();
bool _initialized = false;
static const String _channelId = 'file_transfer';
static const String _channelName = '文件传输';
static const String _channelDesc = '文件传输进度和完成通知';
Future<void> initialize() async {
if (_initialized) return;
if (!pu.isOhos &&
!Platform.isAndroid &&
!Platform.isIOS &&
!Platform.isMacOS) {
Log.w('Notification: Platform not supported');
_initialized = true;
return;
}
const androidSettings = AndroidInitializationSettings(
'@mipmap/ic_launcher',
);
const iosSettings = DarwinInitializationSettings();
const macOsSettings = DarwinInitializationSettings();
// 通过桥接方法构建 InitializationSettings
// 官方SDK不含ohos参数鸿蒙端动态注入ohos参数
final settings = buildNotificationInitSettings(
androidSettings: androidSettings,
iosSettings: iosSettings,
macOsSettings: macOsSettings,
);
await _plugin.initialize(
settings: settings,
onDidReceiveNotificationResponse: (response) {
Log.i('Notification: Tapped payload: ${response.payload}');
},
);
if (pu.isOhos) {
// 鸿蒙端:通过桥接方法动态请求权限
await requestOhosNotificationPermission(_plugin);
} else if (Platform.isAndroid) {
await _plugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>()
?.requestNotificationsPermission();
}
_initialized = true;
}
Future<void> showTransferComplete(TransferTask task) async {
if (!_initialized) await initialize();
final title = task.direction == TransferDirection.send
? '📤 发送完成'
: '📥 接收完成';
final body = '${task.fileName} (${_formatBytes(task.fileSize)})';
await _showNotification(
id: task.id.hashCode,
title: title,
body: body,
payload: 'transfer_complete:${task.id}',
);
}
Future<void> showTransferFailed(TransferTask task) async {
if (!_initialized) await initialize();
final title = task.direction == TransferDirection.send
? '❌ 发送失败'
: '❌ 接收失败';
final body = '${task.fileName}: ${task.errorMessage ?? "未知错误"}';
await _showNotification(
id: task.id.hashCode,
title: title,
body: body,
payload: 'transfer_failed:${task.id}',
);
}
Future<void> showIncomingTransfer(String fileName, int fileSize) async {
if (!_initialized) await initialize();
await _showNotification(
id: DateTime.now().millisecondsSinceEpoch ~/ 1000,
title: '📥 收到文件',
body: '$fileName (${_formatBytes(fileSize)})',
payload: 'transfer_incoming',
);
}
Future<void> showTransferProgress({
required String taskId,
required String fileName,
required double progress,
}) async {
if (!_initialized) await initialize();
if (!Platform.isAndroid && !pu.isOhos) return;
final percent = (progress * 100).toStringAsFixed(0);
await _showNotification(
id: taskId.hashCode,
title: '📡 传输中...',
body: '$fileName$percent%',
ongoing: true,
progress: (progress * 100).round(),
maxProgress: 100,
payload: 'transfer_progress:$taskId',
);
}
Future<void> cancelNotification(int id) async {
await _plugin.cancel(id: id);
}
Future<void> _showNotification({
required int id,
required String title,
required String body,
String? payload,
bool ongoing = false,
int? progress,
int? maxProgress,
}) async {
try {
const androidDetails = AndroidNotificationDetails(
_channelId,
_channelName,
channelDescription: _channelDesc,
importance: Importance.high,
priority: Priority.high,
);
const iosDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
);
AndroidNotificationDetails? androidSpecific;
if ((Platform.isAndroid || pu.isOhos) &&
progress != null &&
maxProgress != null) {
androidSpecific = AndroidNotificationDetails(
_channelId,
_channelName,
channelDescription: _channelDesc,
importance: Importance.low,
priority: Priority.low,
showProgress: true,
progress: progress,
maxProgress: maxProgress,
ongoing: ongoing,
onlyAlertOnce: true,
);
}
final details = NotificationDetails(
android: androidSpecific ?? androidDetails,
iOS: iosDetails,
);
await _plugin.show(
id: id,
title: title,
body: body,
notificationDetails: details,
payload: payload,
);
} catch (e) {
Log.w('Notification: Failed to show: $e');
}
}
String _formatBytes(int bytes) {
if (bytes < 1024) return '$bytes B';
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
if (bytes < 1024 * 1024 * 1024) {
return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
}
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB';
}
}