You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

315 lines
10 KiB

import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get/get.dart';
import 'package:im_flutter_sdk/im_flutter_sdk.dart';
import '../pages/message/chat_page.dart';
import '../controller/message/conversation_controller.dart';
/// 本地通知服务
class LocalNotificationService {
// 单例模式
static final LocalNotificationService _instance = LocalNotificationService._internal();
factory LocalNotificationService() => _instance;
static LocalNotificationService get instance => _instance;
final FlutterLocalNotificationsPlugin _localNotifications = FlutterLocalNotificationsPlugin();
bool _notificationsInitialized = false;
static int _notificationId = 0; // 通知ID计数器
LocalNotificationService._internal();
/// 初始化本地通知
Future<void> initialize() async {
if (_notificationsInitialized) {
return;
}
try {
// Android 初始化设置
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('@mipmap/ic_launcher');
// iOS 初始化设置
const DarwinInitializationSettings initializationSettingsIOS =
DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);
// 初始化设置
const InitializationSettings initializationSettings =
InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
);
// 初始化插件
final bool? initialized = await _localNotifications.initialize(
initializationSettings,
onDidReceiveNotificationResponse: _onNotificationTapped,
);
if (initialized == true) {
_notificationsInitialized = true;
if (Get.isLogEnable) {
Get.log('✅ [LocalNotificationService] 本地通知初始化成功');
}
} else {
if (Get.isLogEnable) {
Get.log('⚠️ [LocalNotificationService] 本地通知初始化失败');
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('❌ [LocalNotificationService] 本地通知初始化异常: $e');
}
}
}
/// 通知点击回调
void _onNotificationTapped(NotificationResponse response) {
try {
final payload = response.payload;
if (payload != null && payload.isNotEmpty) {
// payload 格式: "type|fromId"
final parts = payload.split('|');
if (parts.length >= 2) {
final fromId = parts[1];
// 跳转到聊天页面
Get.to(() => ChatPage(
userId: fromId,
userData: null,
));
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [LocalNotificationService] 处理通知点击失败: $e');
}
}
}
/// 获取消息内容文本
String _getMessageContent(EMMessage message) {
try {
if (message.body.type == MessageType.TXT) {
final body = message.body as EMTextMessageBody;
return body.content ?? '';
} else if (message.body.type == MessageType.IMAGE) {
return '[图片]';
} else if (message.body.type == MessageType.VOICE) {
return '[语音]';
} else if (message.body.type == MessageType.VIDEO) {
return '[视频]';
} else if (message.body.type == MessageType.FILE) {
return '[文件]';
} else if (message.body.type == MessageType.LOCATION) {
return '[位置]';
} else if (message.body.type == MessageType.CUSTOM) {
final body = message.body as EMCustomMessageBody;
if (body.event == 'live_room_invite') {
return '[分享房间]';
} else if (body.event == 'gift') {
return '[礼物]';
} else if (body.event == 'call') {
// 解析通话类型
try {
if (body.params != null) {
final callType = body.params!['callType'] ?? 'voice';
if (callType == 'video') {
return '[视频通话]';
} else if (callType == 'voice') {
return '[语音通话]';
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [LocalNotificationService] 解析通话消息类型失败: $e');
}
}
return '[通话消息]';
}
return '[自定义消息]';
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [LocalNotificationService] 获取消息内容失败: $e');
}
}
return '';
}
/// 显示本地通知(当 APP 在后台时)
Future<void> showNotification(EMMessage message, {Set<String>? activeChatUserIds}) async {
if (!_notificationsInitialized) {
await initialize();
}
try {
// 获取消息发送者ID
final fromId = message.from;
if (fromId == null || fromId.isEmpty) {
return;
}
// 检查发送者是否是当前正在聊天的用户
if (activeChatUserIds != null && activeChatUserIds.contains(fromId)) {
// 是当前正在聊天的用户,不显示通知
return;
}
// 获取消息内容
String messageContent = _getMessageContent(message);
if (messageContent.isEmpty) {
return;
}
// 从消息扩展字段中获取用户信息(头像、昵称)
Map<String, dynamic>? attributes;
try {
attributes = message.attributes;
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [LocalNotificationService] 无法访问消息扩展字段: $e');
}
return;
}
String? nickName;
String? avatarUrl;
if (attributes != null) {
// 从扩展字段中获取发送者信息
nickName = attributes['sender_nickName'] as String?;
avatarUrl = attributes['sender_avatarUrl'] as String?;
}
// 如果从消息扩展字段中获取不到,尝试从 ConversationController 的缓存中获取
if ((nickName == null || nickName.isEmpty) || (avatarUrl == null || avatarUrl.isEmpty)) {
try {
if (Get.isRegistered<ConversationController>()) {
final conversationController = Get.find<ConversationController>();
final cachedUserInfo = conversationController.getCachedUserInfo(fromId);
if (cachedUserInfo != null) {
nickName = nickName ?? cachedUserInfo.nickName;
avatarUrl = avatarUrl ?? cachedUserInfo.avatarUrl;
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [LocalNotificationService] 从ConversationController获取用户信息失败: $e');
}
}
}
// 使用默认值
final finalNickName = nickName ?? fromId;
// 处理视频通话消息
Map<String, dynamic>? callInfo;
String? callType;
String? callStatus;
try {
// 自定义消息
if (message.body.type == MessageType.CUSTOM) {
final customBody = message.body as EMCustomMessageBody;
if (customBody.event == 'call' && customBody.params != null) {
final params = customBody.params!;
callType = params['callType'] ?? 'voice';
callStatus = params['callStatus'] ?? 'missed';
callInfo = {
'callType': callType,
'callStatus': callStatus,
};
}
}
// 如果是视频通话且状态为 missed 或 calling
if (callInfo != null && callType != null && callStatus != null) {
if (callType == 'video' && (callStatus == 'missed' || callStatus == 'calling')) {
// 显示视频通话通知
final notificationId = _notificationId++;
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'video_call_channel',
'视频通话',
channelDescription: '视频通话通知',
importance: Importance.max,
priority: Priority.high,
showWhen: true,
);
const DarwinNotificationDetails iOSPlatformChannelSpecifics =
DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
);
const NotificationDetails platformChannelSpecifics =
NotificationDetails(
android: androidPlatformChannelSpecifics,
iOS: iOSPlatformChannelSpecifics,
);
await _localNotifications.show(
notificationId,
'视频通话',
'$finalNickName 邀请您进行视频通话',
platformChannelSpecifics,
payload: 'video_call|$fromId',
);
if (Get.isLogEnable) {
Get.log('📱 [LocalNotificationService] 显示视频通话本地通知: $fromId');
}
return;
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [LocalNotificationService] 处理视频通话通知失败: $e');
}
}
// 显示普通消息通知
final notificationId = _notificationId++;
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'message_channel',
'消息通知',
channelDescription: '新消息通知',
importance: Importance.high,
priority: Priority.high,
showWhen: true,
);
const DarwinNotificationDetails iOSPlatformChannelSpecifics =
DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
);
const NotificationDetails platformChannelSpecifics =
NotificationDetails(
android: androidPlatformChannelSpecifics,
iOS: iOSPlatformChannelSpecifics,
);
await _localNotifications.show(
notificationId,
finalNickName,
messageContent,
platformChannelSpecifics,
payload: 'message|$fromId',
);
if (Get.isLogEnable) {
Get.log('📱 [LocalNotificationService] 显示本地通知: $fromId, $messageContent');
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('❌ [LocalNotificationService] 显示本地通知失败: $e');
}
}
}
}