elysia/lib/plugin/Notify.dart
2025-11-04 09:53:47 +08:00

149 lines
4.2 KiB
Dart

import 'dart:convert';
import 'dart:developer';
import 'package:awesome_notifications/awesome_notifications.dart';
import 'package:flutter/cupertino.dart';
import '../page/child/ChatPage.dart';
import '../page/child/UpdateCheckPage.dart';
import 'NotifyImageCache.dart';
import 'RouteAnimation.dart';
import 'StringTool.dart';
enum NotifyChannel { update, ad, message }
class Notify {
static BuildContext? _context;
/// 初始化(必须在 main() 中调用一次)
static Future<void> init() async {
await AwesomeNotifications().initialize(null, _channels, debug: true);
bool isAllowed = await AwesomeNotifications().isNotificationAllowed();
if (!isAllowed) {
await AwesomeNotifications().requestPermissionToSendNotifications();
}
// 直接传静态方法
AwesomeNotifications().setListeners(
onActionReceivedMethod: _onActionReceivedMethod,
);
}
/// 必须是静态或顶级方法
static Future<void> _onActionReceivedMethod(ReceivedAction action) async {
onClick(action.payload);
}
static void setBuildContext(BuildContext context) {
_context = context;
}
static List<NotificationChannel> get _channels => [
NotificationChannel(
channelKey: NotifyChannel.update.name,
channelName: '更新通知',
channelDescription: '新版本通知',
importance: NotificationImportance.Default,
),
NotificationChannel(
channelKey: NotifyChannel.ad.name,
channelName: '推广',
channelDescription: '新品推广',
importance: NotificationImportance.High,
),
NotificationChannel(
channelKey: NotifyChannel.message.name,
channelName: '新消息',
channelDescription: '新消息通知',
importance: NotificationImportance.Max,
),
];
/// 发送通知
static Future<void> showNotification({
required NotifyChannel channel,
required String title,
required String body,
String? smallIcon,
String? bigPicture,
Map<String, String>? payload,
}) async {
String? finalIcon = smallIcon;
String? finalBigPic = bigPicture;
// ✅ 如果是网络图,优先使用缓存
if (smallIcon != null && smallIcon.startsWith('http')) {
finalIcon = await NotifyImageCache.getCachedImage(smallIcon) ?? smallIcon;
}
if (bigPicture != null && bigPicture.startsWith('http')) {
finalBigPic =
await NotifyImageCache.getCachedImage(bigPicture) ?? bigPicture;
}
// 2. 转成本地文件路径格式
if (finalIcon != null && finalIcon.startsWith('/')) {
finalIcon = 'file://$finalIcon';
}
if (finalBigPic != null && finalBigPic.startsWith('/')) {
finalBigPic = 'file://$finalBigPic';
}
String text = StringTool.stripMarkdown(body);
text = text.length > 25 ? text.substring(0, 24) + "..." : text;
await AwesomeNotifications().createNotification(
content: NotificationContent(
id: DateTime.now().millisecondsSinceEpoch.remainder(100000),
channelKey: channel.name,
title: title,
body: text,
largeIcon: finalIcon,
bigPicture: finalBigPic,
notificationLayout: finalBigPic != null
? NotificationLayout.BigPicture
: NotificationLayout.Default,
payload: payload,
),
);
}
/// 清除全部消息
static Future<void> clearAll() async {
await AwesomeNotifications().cancelAll();
}
static void onClick(Map<String, String?>? payload) {
log(payload.toString());
WidgetsBinding.instance.addPostFrameCallback((_) {
switch (payload?["type"]) {
case "NEW_MESSAGE":
if (_context != null) {
Navigator.push(
_context!,
RouteAnimation(
ChatPage(
roomId: payload!["roomId"]!,
robotAvatar: payload["avatar"]!,
robotName: payload["name"]!,
),
Offset(1, 0),
),
);
break;
}
case "NEW_VERSION":
{
if (_context != null) {
Navigator.push(
_context!,
RouteAnimation(UpdateCheckPage(), Offset(1, 0)),
);
}
break;
}
}
});
}
}