moxxy/lib/service/notifications.dart

155 lines
5.6 KiB
Dart

import 'dart:math';
import 'package:awesome_notifications/awesome_notifications.dart';
import 'package:flutter/foundation.dart';
import 'package:get_it/get_it.dart';
import 'package:logging/logging.dart';
import 'package:moxxyv2/i18n/strings.g.dart';
import 'package:moxxyv2/service/contact.dart';
import 'package:moxxyv2/service/events.dart';
import 'package:moxxyv2/service/service.dart';
import 'package:moxxyv2/service/xmpp.dart';
import 'package:moxxyv2/shared/commands.dart';
import 'package:moxxyv2/shared/events.dart';
import 'package:moxxyv2/shared/helpers.dart';
import 'package:moxxyv2/shared/models/conversation.dart' as modelc;
import 'package:moxxyv2/shared/models/message.dart' as modelm;
const _maxNotificationId = 2147483647;
const _messageChannelKey = 'message_channel';
const _warningChannelKey = 'warning_channel';
const _notificationActionKeyRead = 'markAsRead';
const _notificationActionKeyReply = 'reply';
// TODO(Unknown): Add resolution dependent drawables for the notification icon
class NotificationsService {
NotificationsService() : _log = Logger('NotificationsService');
// ignore: unused_field
final Logger _log;
@pragma('vm:entry-point')
static Future<void> onReceivedAction(ReceivedAction action) async {
final logger = Logger('NotificationHandler');
if (action.buttonKeyPressed.isEmpty && action.buttonKeyInput.isEmpty) {
// The notification has been tapped
sendEvent(
MessageNotificationTappedEvent(
conversationJid: action.payload!['conversationJid']!,
title: action.payload!['title']!,
avatarUrl: action.payload!['avatarUrl']!,
),
);
} else if (action.buttonKeyPressed == _notificationActionKeyRead) {
// TODO(Unknown): Maybe refactor this call such that we don't have to use
// a command.
await performMarkMessageAsRead(
MarkMessageAsReadCommand(
conversationJid: action.payload!['conversationJid']!,
sid: action.payload!['sid']!,
newUnreadCounter: 0,
),
);
} else {
logger.warning('Received unknown notification action key ${action.buttonKeyPressed}');
}
}
Future<void> init() async {
final an = AwesomeNotifications();
await an.initialize(
'resource://drawable/ic_service_icon',
[
NotificationChannel(
channelKey: _messageChannelKey,
channelName: t.notifications.channels.messagesChannelName,
channelDescription: t.notifications.channels.messagesChannelDescription,
),
NotificationChannel(
channelKey: _warningChannelKey,
channelName: t.notifications.channels.warningChannelName,
channelDescription: t.notifications.channels.warningChannelDescription,
),
],
debug: kDebugMode,
);
await an.setListeners(
onActionReceivedMethod: onReceivedAction,
);
}
/// Returns true if a notification should be shown. false otherwise.
bool shouldShowNotification(String jid) {
return GetIt.I.get<XmppService>().getCurrentlyOpenedChatJid() != jid;
}
/// Show a notification for a message [m] grouped by its conversationJid
/// attribute. If the message is a media message, i.e. mediaUrl != null and isMedia == true,
/// then Android's BigPicture will be used.
Future<void> showNotification(modelc.Conversation c, modelm.Message m, String title, { String? body }) async {
// See https://github.com/MaikuB/flutter_local_notifications/blob/master/flutter_local_notifications/example/lib/main.dart#L1293
final body = m.isMedia ?
mimeTypeToEmoji(m.mediaType) :
m.body;
final title = await GetIt.I.get<ContactsService>().getContactDisplayName(
c.contactId,
) ?? c.title;
await AwesomeNotifications().createNotification(
content: NotificationContent(
id: m.id,
groupKey: c.jid,
channelKey: _messageChannelKey,
summary: title,
title: title,
body: body,
largeIcon: c.avatarUrl.isNotEmpty ? 'file://${c.avatarUrl}' : null,
notificationLayout: m.isThumbnailable ?
NotificationLayout.BigPicture :
NotificationLayout.Messaging,
category: NotificationCategory.Message,
bigPicture: m.isThumbnailable ? 'file://${m.mediaUrl}' : null,
payload: <String, String>{
'conversationJid': c.jid,
'sid': m.sid,
'title': title,
// TODO(PapaTutuWawa): Somehow handle this
'avatarUrl': c.avatarUrl,
},
),
actionButtons: [
NotificationActionButton(
key: _notificationActionKeyReply,
label: t.notifications.message.reply,
requireInputText: true,
autoDismissible: false,
),
NotificationActionButton(
key: _notificationActionKeyRead,
label: t.notifications.message.markAsRead,
)
],
);
}
/// Show a notification with the highest priority that uses [title] as the title
/// and [body] as the body.
// TODO(Unknown): Use the warning icon as the notification icon
Future<void> showWarningNotification(String title, String body) async {
await AwesomeNotifications().createNotification(
content: NotificationContent(
id: Random().nextInt(_maxNotificationId),
title: title,
body: body,
channelKey: _warningChannelKey,
),
);
}
/// Since all notifications are grouped by the conversation's JID, this function
/// clears all notifications for [jid].
Future<void> dismissNotificationsByJid(String jid) async {
await AwesomeNotifications().dismissNotificationsByGroupKey(jid);
}
}