]> git.0d.be Git - empathy.git/blob - src/empathy-notifications-approver.c
Move should_create_salut_account to local-xmpp-assistant-widget
[empathy.git] / src / empathy-notifications-approver.c
1 /* * Copyright (C) 2009 Collabora Ltd.
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2.1 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16  *
17  * Authors: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
18  */
19
20 #include <config.h>
21 #include <string.h>
22
23 #include <glib/gi18n.h>
24 #include <libnotify/notification.h>
25 #include <libnotify/notify.h>
26 #include <telepathy-glib/telepathy-glib.h>
27
28 #include <telepathy-yell/telepathy-yell.h>
29
30 #include <libempathy/empathy-tp-streamed-media.h>
31
32 #include <libempathy-gtk/empathy-notify-manager.h>
33
34 #include "empathy-event-manager.h"
35
36 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
37 #include <libempathy/empathy-debug.h>
38
39 #include "empathy-notifications-approver.h"
40
41 struct _EmpathyNotificationsApproverPrivate
42 {
43   EmpathyEventManager *event_mgr;
44   EmpathyNotifyManager *notify_mgr;
45
46   NotifyNotification *notification;
47   EmpathyEvent *event;
48 };
49
50 G_DEFINE_TYPE (EmpathyNotificationsApprover, empathy_notifications_approver,
51     G_TYPE_OBJECT);
52
53 static EmpathyNotificationsApprover *notifications_approver = NULL;
54
55 static GObject *
56 notifications_approver_constructor (GType type,
57     guint n_construct_params,
58     GObjectConstructParam *construct_params)
59 {
60   GObject *retval;
61
62   if (notifications_approver != NULL)
63     return g_object_ref (notifications_approver);
64
65   retval = G_OBJECT_CLASS (empathy_notifications_approver_parent_class)->
66       constructor (type, n_construct_params, construct_params);
67
68   notifications_approver = EMPATHY_NOTIFICATIONS_APPROVER (retval);
69   g_object_add_weak_pointer (retval, (gpointer) &notifications_approver);
70
71   return retval;
72 }
73
74 static void
75 notifications_approver_dispose (GObject *object)
76 {
77   EmpathyNotificationsApprover *self = (EmpathyNotificationsApprover *) object;
78
79   tp_clear_object (&self->priv->event_mgr);
80   tp_clear_object (&self->priv->notify_mgr);
81
82   if (self->priv->notification != NULL)
83     {
84       notify_notification_close (self->priv->notification, NULL);
85       tp_clear_object (&self->priv->notification);
86     }
87
88   G_OBJECT_CLASS (empathy_notifications_approver_parent_class)->dispose (
89       object);
90 }
91
92 static void
93 empathy_notifications_approver_class_init (
94     EmpathyNotificationsApproverClass *klass)
95 {
96   GObjectClass *object_class = G_OBJECT_CLASS (klass);
97
98   object_class->dispose = notifications_approver_dispose;
99   object_class->constructor = notifications_approver_constructor;
100
101   g_type_class_add_private (object_class,
102       sizeof (EmpathyNotificationsApproverPrivate));
103 }
104
105 static void
106 notification_closed_cb (NotifyNotification *notification,
107     EmpathyNotificationsApprover *self)
108 {
109   if (self->priv->notification == notification)
110     tp_clear_object (&self->priv->notification);
111 }
112
113 static void
114 notification_close_helper (EmpathyNotificationsApprover *self)
115 {
116   if (self->priv->notification != NULL)
117     {
118       notify_notification_close (self->priv->notification, NULL);
119       tp_clear_object (&self->priv->notification);
120     }
121 }
122
123 static void
124 notification_approve_no_video_cb (NotifyNotification *notification,
125     gchar *action,
126     EmpathyNotificationsApprover *self)
127 {
128   if (self->priv->event)
129     {
130       tpy_call_channel_send_video (
131           TPY_CALL_CHANNEL (self->priv->event->handler_instance),
132           FALSE);
133       empathy_event_approve (self->priv->event);
134     }
135 }
136
137 static void
138 notification_approve_cb (NotifyNotification *notification,
139     gchar *action,
140     EmpathyNotificationsApprover *self)
141 {
142   if (self->priv->event != NULL)
143     empathy_event_approve (self->priv->event);
144 }
145
146 static void
147 notification_decline_cb (NotifyNotification *notification,
148     gchar *action,
149     EmpathyNotificationsApprover  *self)
150 {
151   if (self->priv->event != NULL)
152     empathy_event_decline (self->priv->event);
153 }
154
155 static void
156 notification_decline_subscription_cb (NotifyNotification *notification,
157     gchar *action,
158     EmpathyNotificationsApprover *self)
159 {
160   if (self->priv->event == NULL)
161     return;
162
163   empathy_contact_remove_from_contact_list (self->priv->event->contact);
164
165   empathy_event_remove (self->priv->event);
166 }
167
168 static void
169 notification_accept_subscription_cb (NotifyNotification *notification,
170     gchar *action,
171     EmpathyNotificationsApprover *self)
172 {
173   if (self->priv->event == NULL)
174     return;
175
176   empathy_contact_add_to_contact_list (self->priv->event->contact, "");
177
178   empathy_event_remove (self->priv->event);
179 }
180
181 static void
182 add_notification_actions (EmpathyNotificationsApprover *self,
183     NotifyNotification *notification)
184 {
185   gboolean video;
186
187   switch (self->priv->event->type) {
188     case EMPATHY_EVENT_TYPE_CHAT:
189       notify_notification_add_action (notification,
190         "respond", _("Respond"), (NotifyActionCallback) notification_approve_cb,
191           self, NULL);
192       break;
193
194     case EMPATHY_EVENT_TYPE_VOIP:
195     case EMPATHY_EVENT_TYPE_CALL:
196       if (self->priv->event->type == EMPATHY_EVENT_TYPE_VOIP)
197         video = empathy_tp_streamed_media_has_initial_video (
198             EMPATHY_TP_STREAMED_MEDIA (self->priv->event->handler_instance));
199       else
200         video = tpy_call_channel_has_initial_video (
201             TPY_CALL_CHANNEL (self->priv->event->handler_instance));
202
203       notify_notification_add_action (notification,
204         "reject", _("Reject"), (NotifyActionCallback) notification_decline_cb,
205           self, NULL);
206
207       if (video && self->priv->event->type == EMPATHY_EVENT_TYPE_CALL)
208           notify_notification_add_action (notification,
209           "answer-no-video", _("Answer"),
210           (NotifyActionCallback) notification_approve_no_video_cb,
211           self, NULL);
212
213       notify_notification_add_action (notification,
214           "answer", video ? _("Answer with video") : _("Answer"),
215           (NotifyActionCallback) notification_approve_cb,
216           self, NULL);
217       break;
218
219     case EMPATHY_EVENT_TYPE_TRANSFER:
220     case EMPATHY_EVENT_TYPE_INVITATION:
221       notify_notification_add_action (notification,
222         "decline", _("Decline"), (NotifyActionCallback) notification_decline_cb,
223           self, NULL);
224
225       notify_notification_add_action (notification,
226         "accept", _("Accept"), (NotifyActionCallback) notification_approve_cb,
227           self, NULL);
228       break;
229
230     case EMPATHY_EVENT_TYPE_SUBSCRIPTION:
231       notify_notification_add_action (notification,
232         "decline", _("Decline"),
233           (NotifyActionCallback) notification_decline_subscription_cb,
234           self, NULL);
235
236       notify_notification_add_action (notification,
237         "accept", _("Accept"),
238           (NotifyActionCallback) notification_accept_subscription_cb,
239           self, NULL);
240       break;
241
242     case EMPATHY_EVENT_TYPE_AUTH:
243       notify_notification_add_action (notification,
244         /* translators: the 'Provide' button is displayed in a notification
245          * bubble when Empathy is asking for an account password; clicking on it
246          * brings the password popup. */
247         "provide", _("Provide"), (NotifyActionCallback) notification_approve_cb,
248           self, NULL);
249       break;
250
251     default:
252       break;
253   }
254 }
255
256 static gboolean
257 notification_is_urgent (EmpathyNotificationsApprover *self,
258     NotifyNotification *notification)
259 {
260   /* Mark as urgent all the notifications with which user should
261    * interact ASAP */
262   switch (self->priv->event->type) {
263     case EMPATHY_EVENT_TYPE_CHAT:
264     case EMPATHY_EVENT_TYPE_VOIP:
265     case EMPATHY_EVENT_TYPE_CALL:
266     case EMPATHY_EVENT_TYPE_TRANSFER:
267     case EMPATHY_EVENT_TYPE_INVITATION:
268     case EMPATHY_EVENT_TYPE_AUTH:
269       return TRUE;
270
271     case EMPATHY_EVENT_TYPE_SUBSCRIPTION:
272     case EMPATHY_EVENT_TYPE_PRESENCE_ONLINE:
273     case EMPATHY_EVENT_TYPE_PRESENCE_OFFLINE:
274       return FALSE;
275   }
276
277   return FALSE;
278 }
279
280 /* Use x-empathy as prefix for unofficial categories
281  * http://www.galago-project.org/specs/notification/0.9/x211.html */
282 static const gchar *
283 get_category_for_event_type (EmpathyEventType type)
284 {
285   switch (type) {
286     case EMPATHY_EVENT_TYPE_CHAT:
287       return "im.received";
288     case EMPATHY_EVENT_TYPE_PRESENCE_ONLINE:
289       return "presence.online";
290     case EMPATHY_EVENT_TYPE_PRESENCE_OFFLINE:
291       return "presence.offline";
292     case EMPATHY_EVENT_TYPE_VOIP:
293     case EMPATHY_EVENT_TYPE_CALL:
294       return "x-empathy.call.incoming";
295     case EMPATHY_EVENT_TYPE_TRANSFER:
296       return "x-empathy.transfer.incoming";
297     case EMPATHY_EVENT_TYPE_INVITATION:
298       return "x-empathy.im.room-invitation";
299     case EMPATHY_EVENT_TYPE_AUTH:
300       return "x-empathy.network.auth-request";
301     case EMPATHY_EVENT_TYPE_SUBSCRIPTION:
302       return "x-empathy.im.subscription-request";
303   }
304
305   return NULL;
306 }
307
308 static void
309 update_notification (EmpathyNotificationsApprover *self)
310 {
311   GdkPixbuf *pixbuf = NULL;
312   gchar *message_esc = NULL;
313   gboolean has_x_canonical_append;
314   NotifyNotification *notification;
315
316   if (!empathy_notify_manager_notification_is_enabled (self->priv->notify_mgr))
317     {
318       /* always close the notification if this happens */
319       notification_close_helper (self);
320       return;
321     }
322
323   if (self->priv->event == NULL)
324     {
325       notification_close_helper (self);
326       return;
327      }
328
329   if (self->priv->event->message != NULL)
330     message_esc = g_markup_escape_text (self->priv->event->message, -1);
331
332   has_x_canonical_append = empathy_notify_manager_has_capability (
333       self->priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
334
335   if (self->priv->notification != NULL && ! has_x_canonical_append)
336     {
337       /* if the notification server does NOT supports x-canonical-append, it is
338        * better to not use notify_notification_update to avoid
339        * overwriting the current notification message */
340       notification = g_object_ref (self->priv->notification);
341
342       notify_notification_update (notification,
343           self->priv->event->header, message_esc, NULL);
344     }
345   else
346     {
347       const gchar *category;
348
349       /* if the notification server supports x-canonical-append,
350        * the hint will be added, so that the message from the
351        * just created notification will be automatically appended
352        * to an existing notification with the same title.
353        * In this way the previous message will not be lost: the new
354        * message will appear below it, in the same notification */
355       notification = notify_notification_new (self->priv->event->header,
356            message_esc, NULL);
357
358       if (self->priv->notification == NULL)
359         {
360           self->priv->notification = g_object_ref (notification);
361
362           g_signal_connect (notification, "closed",
363               G_CALLBACK (notification_closed_cb), self);
364         }
365
366       notify_notification_set_timeout (notification,
367           NOTIFY_EXPIRES_DEFAULT);
368
369       if (has_x_canonical_append)
370         {
371           notify_notification_set_hint (notification,
372               EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND,
373               g_variant_new_boolean (TRUE));
374         }
375
376       if (empathy_notify_manager_has_capability (self->priv->notify_mgr,
377             EMPATHY_NOTIFY_MANAGER_CAP_ACTIONS))
378         add_notification_actions (self, notification);
379
380       if (notification_is_urgent (self, notification))
381         notify_notification_set_urgency (notification, NOTIFY_URGENCY_CRITICAL);
382
383       category = get_category_for_event_type (self->priv->event->type);
384       if (category != NULL)
385         {
386           notify_notification_set_hint (notification,
387               EMPATHY_NOTIFY_MANAGER_CAP_CATEGORY,
388               g_variant_new_string (category));
389         }
390     }
391
392   pixbuf = empathy_notify_manager_get_pixbuf_for_notification (
393       self->priv->notify_mgr, self->priv->event->contact,
394       self->priv->event->icon_name);
395
396   if (pixbuf != NULL)
397     {
398       notify_notification_set_image_from_pixbuf (notification, pixbuf);
399       g_object_unref (pixbuf);
400     }
401
402   notify_notification_show (notification, NULL);
403
404   g_free (message_esc);
405   g_object_unref (notification);
406 }
407
408 static void
409 event_added_cb (EmpathyEventManager *manager,
410     EmpathyEvent *event,
411     EmpathyNotificationsApprover *self)
412 {
413   if (self->priv->event != NULL)
414     return;
415
416   self->priv->event = event;
417
418   update_notification (self);
419 }
420
421 static void
422 event_removed_cb (EmpathyEventManager *manager,
423     EmpathyEvent *event,
424     EmpathyNotificationsApprover *self)
425 {
426   if (event != self->priv->event)
427     return;
428
429   self->priv->event = empathy_event_manager_get_top_event (
430       self->priv->event_mgr);
431
432   update_notification (self);
433 }
434
435 static void
436 event_updated_cb (EmpathyEventManager *manager,
437     EmpathyEvent *event,
438     EmpathyNotificationsApprover *self)
439 {
440   if (event != self->priv->event)
441     return;
442
443   if (empathy_notify_manager_notification_is_enabled (self->priv->notify_mgr))
444     update_notification (self);
445 }
446
447 static void
448 empathy_notifications_approver_init (EmpathyNotificationsApprover *self)
449 {
450   EmpathyNotificationsApproverPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
451     EMPATHY_TYPE_NOTIFICATIONS_APPROVER, EmpathyNotificationsApproverPrivate);
452
453   self->priv = priv;
454
455   self->priv->event_mgr = empathy_event_manager_dup_singleton ();
456   self->priv->notify_mgr = empathy_notify_manager_dup_singleton ();
457
458   tp_g_signal_connect_object (self->priv->event_mgr, "event-added",
459       G_CALLBACK (event_added_cb), self, 0);
460   tp_g_signal_connect_object (priv->event_mgr, "event-removed",
461       G_CALLBACK (event_removed_cb), self, 0);
462   tp_g_signal_connect_object (priv->event_mgr, "event-updated",
463       G_CALLBACK (event_updated_cb), self, 0);
464 }
465
466 EmpathyNotificationsApprover *
467 empathy_notifications_approver_dup_singleton (void)
468 {
469   return g_object_new (EMPATHY_TYPE_NOTIFICATIONS_APPROVER, NULL);
470 }