2025-08-21 10:50:38 +08:00
|
|
|
|
import 'dart:convert';
|
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
|
|
import 'package:device_info_plus/device_info_plus.dart';
|
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
|
import 'package:logger/logger.dart';
|
|
|
|
|
import 'package:loopin/IM/im_service.dart';
|
|
|
|
|
import 'package:loopin/models/conversation_type.dart';
|
|
|
|
|
import 'package:loopin/utils/storage.dart';
|
|
|
|
|
import 'package:tencent_cloud_chat_push/common/tim_push_listener.dart';
|
|
|
|
|
import 'package:tencent_cloud_chat_push/common/tim_push_message.dart';
|
|
|
|
|
import 'package:tencent_cloud_chat_push/tencent_cloud_chat_push.dart';
|
|
|
|
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
|
|
|
|
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
|
|
|
|
|
|
|
|
|
|
final logger = Logger();
|
|
|
|
|
|
|
|
|
|
class PushService {
|
|
|
|
|
static late TIMPushListener _timPushListener;
|
|
|
|
|
|
|
|
|
|
Future<void> _registerPushInIsolate(Map<String, dynamic> args) async {
|
|
|
|
|
final sdkAppId = args['sdkAppId'] as int;
|
|
|
|
|
final appKey = args['appKey'] as String;
|
|
|
|
|
final apnsCertificateID = args['apnsCertificateID'] as int;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await TencentCloudChatPush().registerPush(
|
|
|
|
|
sdkAppId: sdkAppId,
|
|
|
|
|
appKey: appKey,
|
|
|
|
|
apnsCertificateID: apnsCertificateID,
|
|
|
|
|
onNotificationClicked: _onNotificationClicked,
|
|
|
|
|
);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
logger.e('注册推送失败: $e');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 初始化推送服务
|
|
|
|
|
Future<void> initPush({
|
|
|
|
|
required int sdkAppId,
|
|
|
|
|
required String appKey, // 客户端密钥
|
|
|
|
|
}) async {
|
|
|
|
|
int apnsCertificateID;
|
|
|
|
|
final devices = await _getDeviceBrand();
|
|
|
|
|
apnsCertificateID = _getApnsCertificateIDForBrand(devices);
|
|
|
|
|
if (apnsCertificateID == 0) {
|
|
|
|
|
logger.w('手机厂商:$devices, 未配置证书');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 注册推送(初始化)
|
|
|
|
|
// if (Platform.isAndroid) {
|
|
|
|
|
// await compute(_registerPushInIsolate, {
|
|
|
|
|
// 'sdkAppId': sdkAppId,
|
|
|
|
|
// 'appKey': appKey,
|
|
|
|
|
// 'apnsCertificateID': apnsCertificateID,
|
|
|
|
|
// });
|
|
|
|
|
// } else {
|
|
|
|
|
await TencentCloudChatPush().registerPush(
|
|
|
|
|
onNotificationClicked: _onNotificationClicked,
|
|
|
|
|
sdkAppId: sdkAppId,
|
|
|
|
|
appKey: appKey,
|
|
|
|
|
apnsCertificateID: apnsCertificateID,
|
|
|
|
|
);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// 关闭 App 在前台时弹出通知栏
|
|
|
|
|
await TencentCloudChatPush().disablePostNotificationInForeground(disable: true);
|
|
|
|
|
|
|
|
|
|
///处理安卓端异常问题;
|
|
|
|
|
if (Platform.isAndroid) {
|
|
|
|
|
await TencentImSDKPlugin.v2TIMManager.login(userID: Storage.read('userId'), userSig: Storage.read('userSig'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.i('推送服务已注册,手机:$devices,证书ID:$apnsCertificateID');
|
|
|
|
|
|
|
|
|
|
// 添加在线时监听器
|
|
|
|
|
_addPushListener();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 注销推送(退出登录时调用)
|
|
|
|
|
static Future<void> unInitPush() async {
|
|
|
|
|
try {
|
|
|
|
|
await TencentCloudChatPush().unRegisterPush();
|
|
|
|
|
_removePushListener();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
logger.i("注销推送失败: $e");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 添加监听器
|
|
|
|
|
static void _addPushListener() {
|
|
|
|
|
_timPushListener = TIMPushListener(
|
|
|
|
|
onRecvPushMessage: (TimPushMessage message) {
|
|
|
|
|
logger.i("[推送] 收到消息: ${message.toLogString()}");
|
|
|
|
|
},
|
|
|
|
|
onRevokePushMessage: (String messageId) {
|
|
|
|
|
logger.i("[推送] 消息被撤回: $messageId");
|
|
|
|
|
},
|
|
|
|
|
onNotificationClicked: (String ext) {
|
|
|
|
|
logger.i("[推送] 点击横幅 ext: $ext");
|
|
|
|
|
_handleNotificationClick(ext);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
TencentCloudChatPush().addPushListener(listener: _timPushListener);
|
|
|
|
|
logger.i('推送服务在线监听器已注册');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 移除监听器
|
|
|
|
|
static void _removePushListener() {
|
|
|
|
|
TencentCloudChatPush().removePushListener(listener: _timPushListener);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 横幅点击事件处理
|
|
|
|
|
static void _onNotificationClicked({
|
|
|
|
|
required String ext,
|
|
|
|
|
String? userID,
|
|
|
|
|
String? groupID,
|
|
|
|
|
}) {
|
|
|
|
|
logger.i("[点击通知回调] ext: $ext, userID: $userID, groupID: $groupID");
|
|
|
|
|
_handleNotificationClick(ext, userID: userID, groupID: groupID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 统一处理跳转逻辑
|
|
|
|
|
static void _handleNotificationClick(String ext, {String? userID, String? groupID}) async {
|
|
|
|
|
try {
|
|
|
|
|
// ext={id:对应业务ID,type:'newFoucs',userID:发送人的id,groupID:群ID}
|
|
|
|
|
// final ext = jsonEncode({
|
|
|
|
|
// "userID": "123456",
|
|
|
|
|
// "groupID": "654321",
|
|
|
|
|
// });
|
|
|
|
|
final data = jsonDecode(ext);
|
|
|
|
|
logger.i(data);
|
|
|
|
|
final type = data['type'];
|
|
|
|
|
final router = conversationTypeFromString(type);
|
|
|
|
|
logger.w(router);
|
|
|
|
|
if (router == null || router != '') {
|
|
|
|
|
// 聊天
|
2025-08-26 17:38:59 +08:00
|
|
|
|
if (data['userID'] != null || data['userID'] != '') {
|
2025-08-21 10:50:38 +08:00
|
|
|
|
logger.w('有userID');
|
|
|
|
|
// 单聊,获取会话
|
|
|
|
|
final covRes = await ImService.instance.getConversation(conversationID: 'c2c_${data['userID']}');
|
|
|
|
|
final V2TimConversation conversation = covRes.data;
|
|
|
|
|
logger.w(conversation.toJson());
|
|
|
|
|
if (conversation.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false) {
|
|
|
|
|
// nofriend会话,是否第一次聊天
|
|
|
|
|
conversation.showName = conversation.showName ?? data['title'];
|
|
|
|
|
Get.toNamed('/chatNoFriend', arguments: conversation);
|
|
|
|
|
} else {
|
|
|
|
|
// 去正常的会话
|
|
|
|
|
Get.toNamed('/chat', arguments: conversation);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
logger.w('没有userID');
|
|
|
|
|
|
|
|
|
|
// 群聊消息
|
|
|
|
|
final groupRes = await ImService.instance.getConversation(conversationID: 'group_${data['groupID']}');
|
|
|
|
|
Get.toNamed('/chatGroup', arguments: groupRes.data);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 通知类相关
|
|
|
|
|
Get.toNamed('/$router', arguments: data['id'] ?? '');
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
logger.i("[推送点击] ext 解析失败: $e");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 获取手机品牌
|
|
|
|
|
static Future<String> _getDeviceBrand() async {
|
|
|
|
|
final deviceInfo = DeviceInfoPlugin();
|
|
|
|
|
try {
|
|
|
|
|
if (Platform.isAndroid) {
|
|
|
|
|
final androidInfo = await deviceInfo.androidInfo;
|
|
|
|
|
return androidInfo.brand.toLowerCase();
|
|
|
|
|
} else if (Platform.isIOS) {
|
|
|
|
|
return 'apple';
|
|
|
|
|
} else {
|
|
|
|
|
return 'unknown';
|
|
|
|
|
}
|
|
|
|
|
} catch (e, stack) {
|
|
|
|
|
logger.w("获取设备品牌失败: $e\n$stack");
|
|
|
|
|
return 'unknown';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 获取对应厂商的证书ID
|
|
|
|
|
static int _getApnsCertificateIDForBrand(String brand) {
|
|
|
|
|
switch (brand) {
|
|
|
|
|
case 'xiaomi':
|
|
|
|
|
case 'redmi':
|
|
|
|
|
return 41169;
|
|
|
|
|
case 'oppo':
|
|
|
|
|
return 41170;
|
|
|
|
|
case 'vivo':
|
|
|
|
|
return 41177;
|
|
|
|
|
case 'meizu':
|
|
|
|
|
return 41176;
|
|
|
|
|
case 'apple':
|
|
|
|
|
return 45356;
|
|
|
|
|
case 'huawei':
|
|
|
|
|
return 41171;
|
|
|
|
|
case 'honor':
|
|
|
|
|
return 41178;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|