2020-11-25 23:12:21 +00:00
|
|
|
using Dino;
|
|
|
|
using Dino.Entities;
|
2021-02-27 17:42:07 +00:00
|
|
|
using winrt.Windows.UI.Notifications;
|
2020-11-25 23:12:21 +00:00
|
|
|
using Xmpp;
|
2020-11-26 21:50:03 +00:00
|
|
|
using Gee;
|
2020-11-25 23:12:21 +00:00
|
|
|
|
|
|
|
namespace Dino.Plugins.WindowsNotification {
|
|
|
|
public class WindowsNotificationProvider : NotificationProvider, Object {
|
|
|
|
|
2021-02-27 18:53:56 +00:00
|
|
|
private delegate void DelegateToUi();
|
|
|
|
|
2021-02-27 17:42:07 +00:00
|
|
|
private static uint notification_counter = 0;
|
|
|
|
private ToastNotifier notifier;
|
2020-11-25 23:12:21 +00:00
|
|
|
private StreamInteractor stream_interactor;
|
|
|
|
private Dino.Application app;
|
2020-11-26 23:48:59 +00:00
|
|
|
|
2021-02-27 17:42:07 +00:00
|
|
|
private Gee.List<uint?> marked_for_removal;
|
|
|
|
|
|
|
|
// we must keep a reference to the notification itself or else their actions are disabled
|
|
|
|
private HashMap<uint, ToastNotification> notifications;
|
|
|
|
private Gee.List<uint?> content_notifications;
|
|
|
|
private HashMap<Conversation, Gee.List<uint?>> conversation_notifications;
|
2020-11-25 23:12:21 +00:00
|
|
|
|
2021-02-27 17:42:07 +00:00
|
|
|
public WindowsNotificationProvider(Dino.Application app, ToastNotifier notifier) {
|
|
|
|
this.notifier = notifier;
|
2020-11-25 23:12:21 +00:00
|
|
|
this.stream_interactor = app.stream_interactor;
|
|
|
|
this.app = app;
|
2021-02-27 17:42:07 +00:00
|
|
|
this.marked_for_removal = new Gee.ArrayList<uint?>();
|
|
|
|
this.content_notifications = new Gee.ArrayList<uint?>();
|
|
|
|
this.conversation_notifications = new HashMap<Conversation, Gee.List<uint?>>(Conversation.hash_func, Conversation.equals_func);
|
|
|
|
this.notifications = new HashMap<uint, ToastNotification>();
|
2020-11-25 23:12:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public double get_priority() {
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async void notify_message(Message message, Conversation conversation, string conversation_display_name, string? participant_display_name) {
|
|
|
|
yield notify_content_item(conversation, conversation_display_name, participant_display_name, message.body);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async void notify_file(FileTransfer file_transfer, Conversation conversation, bool is_image, string conversation_display_name, string? participant_display_name) {
|
|
|
|
string text = "";
|
|
|
|
if (file_transfer.direction == Message.DIRECTION_SENT) {
|
|
|
|
text = is_image ? _("Image sent") : _("File sent");
|
|
|
|
} else {
|
|
|
|
text = is_image ? _("Image received") : _("File received");
|
|
|
|
}
|
|
|
|
|
|
|
|
yield notify_content_item(conversation, conversation_display_name, participant_display_name, text);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async void notify_subscription_request(Conversation conversation) {
|
|
|
|
string summary = _("Subscription request");
|
|
|
|
string body = Markup.escape_text(conversation.counterpart.to_string());
|
|
|
|
|
2020-11-26 21:50:03 +00:00
|
|
|
var image_path = get_avatar(conversation);
|
2021-02-27 21:20:34 +00:00
|
|
|
var notification = yield new ToastNotificationBuilder()
|
2021-02-27 17:42:07 +00:00
|
|
|
.SetHeader(summary)
|
|
|
|
.SetBody(body)
|
|
|
|
.SetImage(image_path)
|
|
|
|
.AddButton(_("Accept"), "accept-subscription")
|
|
|
|
.AddButton(_("Deny"), "deny-subscription")
|
|
|
|
.Build();
|
|
|
|
|
|
|
|
var notification_id = generate_id();
|
|
|
|
notification.Activated((argument, user_input) => {
|
2021-02-27 18:53:56 +00:00
|
|
|
run_on_ui(() => {
|
|
|
|
if (argument != null) {
|
|
|
|
app.activate_action(argument, conversation.id);
|
|
|
|
} else {
|
|
|
|
app.activate_action("open-conversation", conversation.id);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-02-27 17:42:07 +00:00
|
|
|
marked_for_removal.add(notification_id);
|
|
|
|
});
|
2020-11-26 23:48:59 +00:00
|
|
|
|
2021-02-27 18:53:56 +00:00
|
|
|
notification.Dismissed((reason) => marked_for_removal.add(notification_id));
|
2020-11-26 21:50:03 +00:00
|
|
|
|
2021-02-27 17:42:07 +00:00
|
|
|
notification.Failed(() => marked_for_removal.add(notification_id));
|
2020-11-26 23:48:59 +00:00
|
|
|
|
2021-02-27 17:42:07 +00:00
|
|
|
notifications[notification_id] = notification;
|
|
|
|
|
|
|
|
if (!conversation_notifications.has_key(conversation)) {
|
|
|
|
conversation_notifications[conversation] = new ArrayList<uint?>();
|
2020-11-25 23:12:21 +00:00
|
|
|
}
|
2021-02-27 17:42:07 +00:00
|
|
|
conversation_notifications[conversation].add(notification_id);
|
|
|
|
|
|
|
|
notifier.Show(notification);
|
2020-11-25 23:12:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public async void notify_connection_error(Account account, ConnectionManager.ConnectionError error) {
|
|
|
|
string summary = _("Could not connect to %s").printf(account.bare_jid.domainpart);
|
|
|
|
string body = "";
|
|
|
|
switch (error.source) {
|
|
|
|
case ConnectionManager.ConnectionError.Source.SASL:
|
|
|
|
body = _("Wrong password");
|
|
|
|
break;
|
|
|
|
case ConnectionManager.ConnectionError.Source.TLS:
|
|
|
|
body = _("Invalid TLS certificate");
|
|
|
|
break;
|
|
|
|
case ConnectionManager.ConnectionError.Source.STREAM_ERROR:
|
|
|
|
body = "Stream Error";
|
|
|
|
break;
|
|
|
|
case ConnectionManager.ConnectionError.Source.CONNECTION:
|
|
|
|
body = "Connection";
|
|
|
|
break;
|
|
|
|
}
|
2021-02-27 17:42:07 +00:00
|
|
|
|
2021-02-27 21:20:34 +00:00
|
|
|
var notification = yield new ToastNotificationBuilder()
|
2021-02-27 17:42:07 +00:00
|
|
|
.SetHeader(summary)
|
|
|
|
.SetBody(body)
|
|
|
|
.Build();
|
|
|
|
|
|
|
|
var notification_id = generate_id();
|
|
|
|
notification.Activated((argument, user_input) => marked_for_removal.add(notification_id));
|
|
|
|
notification.Dismissed((reason) => marked_for_removal.add(notification_id));
|
|
|
|
notification.Failed(() => marked_for_removal.add(notification_id));
|
|
|
|
|
|
|
|
notifications[notification_id] = notification;
|
|
|
|
notifier.Show(notification);
|
2020-11-25 23:12:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public async void notify_muc_invite(Account account, Jid room_jid, Jid from_jid, string inviter_display_name) {
|
|
|
|
Conversation direct_conversation = new Conversation(from_jid, account, Conversation.Type.CHAT);
|
|
|
|
|
|
|
|
string display_room = room_jid.bare_jid.to_string();
|
|
|
|
string summary = _("Invitation to %s").printf(display_room);
|
|
|
|
string body = _("%s invited you to %s").printf(inviter_display_name, display_room);
|
|
|
|
|
2020-11-26 23:48:59 +00:00
|
|
|
var image_path = get_avatar(direct_conversation);
|
2021-02-27 21:20:34 +00:00
|
|
|
var notification = yield new ToastNotificationBuilder()
|
2021-02-27 17:42:07 +00:00
|
|
|
.SetHeader(summary)
|
|
|
|
.SetBody(body)
|
|
|
|
.SetImage(image_path)
|
|
|
|
.AddButton(_("Accept"), "open-muc-join")
|
|
|
|
.AddButton(_("Deny"), "deny-invite")
|
|
|
|
.Build();
|
|
|
|
|
|
|
|
var notification_id = generate_id();
|
|
|
|
var group_conversation_id = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(room_jid, account, Conversation.Type.GROUPCHAT).id;
|
|
|
|
notification.Activated((argument, user_input) => {
|
2021-02-27 18:53:56 +00:00
|
|
|
run_on_ui(() => {
|
|
|
|
if (argument != null) {
|
|
|
|
app.activate_action(argument, group_conversation_id);
|
|
|
|
} else {
|
|
|
|
app.activate_action("open-muc-join", group_conversation_id);
|
|
|
|
}
|
|
|
|
});
|
2020-11-26 21:50:03 +00:00
|
|
|
|
2021-02-27 17:42:07 +00:00
|
|
|
marked_for_removal.add(notification_id);
|
|
|
|
});
|
2020-11-26 23:48:59 +00:00
|
|
|
|
2021-02-27 18:53:56 +00:00
|
|
|
notification.Dismissed((reason) => marked_for_removal.add(notification_id));
|
2021-02-27 17:42:07 +00:00
|
|
|
|
|
|
|
notification.Failed(() => marked_for_removal.add(notification_id));
|
|
|
|
|
|
|
|
notifications[notification_id] = notification;
|
|
|
|
notifier.Show(notification);
|
2020-11-25 23:12:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public async void notify_voice_request(Conversation conversation, Jid from_jid) {
|
|
|
|
string display_name = Dino.get_participant_display_name(stream_interactor, conversation, from_jid);
|
|
|
|
string display_room = Dino.get_conversation_display_name(stream_interactor, conversation, _("%s from %s"));
|
|
|
|
string summary = _("Permission request");
|
|
|
|
string body = _("%s requests the permission to write in %s").printf(display_name, display_room);
|
|
|
|
|
2020-11-26 21:50:03 +00:00
|
|
|
var image_path = get_avatar(conversation);
|
2021-02-27 21:20:34 +00:00
|
|
|
var notification = yield new ToastNotificationBuilder()
|
2021-02-27 17:42:07 +00:00
|
|
|
.SetHeader(summary)
|
|
|
|
.SetBody(body)
|
|
|
|
.SetImage(image_path)
|
|
|
|
.AddButton(_("Accept"), "accept-voice-request")
|
|
|
|
.AddButton(_("Deny"), "deny-voice-request")
|
|
|
|
.Build();
|
|
|
|
|
|
|
|
var notification_id = generate_id();
|
|
|
|
notification.Activated((argument, user_input) => {
|
|
|
|
if (argument != null) {
|
2021-02-27 18:53:56 +00:00
|
|
|
run_on_ui(() => app.activate_action(argument, conversation.id));
|
2020-11-26 21:50:03 +00:00
|
|
|
}
|
|
|
|
|
2021-02-27 17:42:07 +00:00
|
|
|
marked_for_removal.add(notification_id);
|
|
|
|
});
|
2020-11-26 23:48:59 +00:00
|
|
|
|
2021-02-27 18:53:56 +00:00
|
|
|
notification.Dismissed((reason) => marked_for_removal.add(notification_id));
|
2021-02-27 17:42:07 +00:00
|
|
|
|
|
|
|
notification.Failed(() => marked_for_removal.add(notification_id));
|
|
|
|
|
|
|
|
notifications[notification_id] = notification;
|
|
|
|
notifier.Show(notification);
|
2020-11-25 23:12:21 +00:00
|
|
|
}
|
|
|
|
|
2020-11-26 23:48:59 +00:00
|
|
|
private async void notify_content_item(Conversation conversation, string conversation_display_name, string? participant_display_name, string body_) {
|
2020-11-28 01:25:19 +00:00
|
|
|
clear_marked();
|
2020-11-26 23:48:59 +00:00
|
|
|
|
|
|
|
string body = body_;
|
|
|
|
if (participant_display_name != null) {
|
|
|
|
body = @"$participant_display_name: $body";
|
|
|
|
}
|
|
|
|
|
|
|
|
var image_path = get_avatar(conversation);
|
2021-02-27 21:20:34 +00:00
|
|
|
var notification = yield new ToastNotificationBuilder()
|
2021-02-27 17:42:07 +00:00
|
|
|
.SetHeader(conversation_display_name)
|
|
|
|
.SetBody(body)
|
|
|
|
.SetImage(image_path)
|
|
|
|
.Build();
|
|
|
|
|
|
|
|
var notification_id = generate_id();
|
|
|
|
notification.Activated((argument, user_input) => {
|
2021-02-27 18:53:56 +00:00
|
|
|
run_on_ui(() => app.activate_action("open-conversation", conversation.id));
|
2021-02-27 17:42:07 +00:00
|
|
|
marked_for_removal.add(notification_id);
|
|
|
|
});
|
|
|
|
|
2021-02-27 19:30:36 +00:00
|
|
|
notification.Dismissed((reason) => marked_for_removal.add(notification_id));
|
2021-02-27 17:42:07 +00:00
|
|
|
|
|
|
|
notification.Failed(() => marked_for_removal.add(notification_id));
|
|
|
|
|
|
|
|
notifications[notification_id] = notification;
|
|
|
|
notifier.Show(notification);
|
2021-03-15 11:38:03 +00:00
|
|
|
|
|
|
|
content_notifications.add(notification_id);
|
2020-11-25 23:12:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private string? get_avatar(Conversation conversation) {
|
|
|
|
var avatar_manager = app.stream_interactor.get_module(AvatarManager.IDENTITY);
|
|
|
|
return avatar_manager.get_avatar_filepath(conversation.account, conversation.counterpart);
|
|
|
|
}
|
2020-11-26 23:48:59 +00:00
|
|
|
|
2021-02-27 17:42:07 +00:00
|
|
|
public async void retract_content_item_notifications() {
|
|
|
|
foreach (uint id in content_notifications) {
|
|
|
|
remove_notification(id);
|
|
|
|
}
|
|
|
|
content_notifications.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
public async void retract_conversation_notifications(Conversation conversation) {
|
|
|
|
if (conversation_notifications.has_key(conversation)) {
|
|
|
|
var conversation_items = conversation_notifications[conversation];
|
|
|
|
foreach (uint id in conversation_items) {
|
|
|
|
remove_notification(id);
|
|
|
|
}
|
|
|
|
conversation_items.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:25:19 +00:00
|
|
|
private void clear_marked() {
|
|
|
|
foreach (var id in marked_for_removal) {
|
2021-02-27 17:42:07 +00:00
|
|
|
remove_notification(id);
|
2020-11-26 23:48:59 +00:00
|
|
|
}
|
2020-11-28 01:25:19 +00:00
|
|
|
marked_for_removal.clear();
|
2020-11-26 23:48:59 +00:00
|
|
|
}
|
|
|
|
|
2021-02-27 17:42:07 +00:00
|
|
|
private void remove_notification(uint id) {
|
|
|
|
ToastNotification notification = null;
|
|
|
|
notifications.unset(id, out notification);
|
|
|
|
if (notification != null) {
|
|
|
|
notifier.Hide(notification);
|
2020-11-26 23:48:59 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-27 17:42:07 +00:00
|
|
|
|
|
|
|
private uint generate_id() {
|
|
|
|
return AtomicUint.add(ref notification_counter, 1);
|
|
|
|
}
|
2021-02-27 18:53:56 +00:00
|
|
|
|
|
|
|
private void run_on_ui(DelegateToUi func) {
|
|
|
|
Idle.add(() => { func(); return false; }, GLib.Priority.HIGH);
|
|
|
|
}
|
2020-11-25 23:12:21 +00:00
|
|
|
}
|
|
|
|
}
|