2 * Copyright (C) 2011 Collabora Ltd.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 * Authors: Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
22 #include "empathy-call-observer.h"
24 #include <glib/gi18n-lib.h>
25 #include <tp-account-widgets/tpaw-images.h>
26 #include <telepathy-glib/telepathy-glib-dbus.h>
28 #include "empathy-notify-manager.h"
30 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
31 #include "empathy-debug.h"
33 struct _EmpathyCallObserverPriv
{
34 EmpathyNotifyManager
*notify_mgr
;
36 TpBaseClient
*observer
;
38 /* Ongoing calls, as reffed TpChannels */
42 /* The Call Observer looks at incoming and outgoing calls, and
43 * autorejects incoming ones if there are ongoing ones, since
44 * we don't cope with simultaneous calls quite well yet.
45 * At some point, we should ask the user if he wants to put the
46 * current call on hold and answer the incoming one instead,
47 * see https://bugzilla.gnome.org/show_bug.cgi?id=623348
49 G_DEFINE_TYPE (EmpathyCallObserver
, empathy_call_observer
, G_TYPE_OBJECT
);
51 static EmpathyCallObserver
* observer_singleton
= NULL
;
54 on_channel_closed (TpProxy
*proxy
,
58 EmpathyCallObserver
*self
)
60 DEBUG ("channel %s has been invalidated; stop observing it",
61 tp_proxy_get_object_path (proxy
));
63 self
->priv
->channels
= g_list_remove (self
->priv
->channels
, proxy
);
64 g_object_unref (proxy
);
69 EmpathyCallObserver
*self
;
70 TpObserveChannelsContext
*context
;
71 TpChannel
*main_channel
;
74 static AutoRejectCtx
*
75 auto_reject_ctx_new (EmpathyCallObserver
*self
,
76 TpObserveChannelsContext
*context
,
77 TpChannel
*main_channel
)
79 AutoRejectCtx
*ctx
= g_slice_new (AutoRejectCtx
);
81 ctx
->self
= g_object_ref (self
);
82 ctx
->context
= g_object_ref (context
);
83 ctx
->main_channel
= g_object_ref (main_channel
);
88 auto_reject_ctx_free (AutoRejectCtx
*ctx
)
90 g_object_unref (ctx
->self
);
91 g_object_unref (ctx
->context
);
92 g_object_unref (ctx
->main_channel
);
93 g_slice_free (AutoRejectCtx
, ctx
);
97 display_reject_notification (EmpathyCallObserver
*self
,
101 NotifyNotification
*notification
;
102 gchar
*summary
, *body
;
103 EmpathyContact
*emp_contact
;
106 contact
= tp_channel_get_target_contact (channel
);
108 summary
= g_strdup_printf (_("Missed call from %s"),
109 tp_contact_get_alias (contact
));
110 body
= g_strdup_printf (
111 _("%s just tried to call you, but you were in another call."),
112 tp_contact_get_alias (contact
));
114 notification
= empathy_notify_manager_create_notification (summary
, body
,
117 emp_contact
= empathy_contact_dup_from_tp_contact (contact
);
118 pixbuf
= empathy_notify_manager_get_pixbuf_for_notification (
119 self
->priv
->notify_mgr
, emp_contact
, TPAW_IMAGE_AVATAR_DEFAULT
);
123 notify_notification_set_icon_from_pixbuf (notification
, pixbuf
);
124 g_object_unref (pixbuf
);
127 notify_notification_show (notification
, NULL
);
129 g_object_unref (notification
);
132 g_object_unref (emp_contact
);
136 find_main_channel (GList
*channels
)
140 for (l
= channels
; l
!= NULL
; l
= g_list_next (l
))
142 TpChannel
*channel
= l
->data
;
145 if (tp_proxy_get_invalidated (channel
) != NULL
)
148 channel_type
= tp_channel_get_channel_type_id (channel
);
150 if (channel_type
== TP_IFACE_QUARK_CHANNEL_TYPE_CALL
)
158 has_ongoing_calls (EmpathyCallObserver
*self
)
162 for (l
= self
->priv
->channels
; l
!= NULL
; l
= l
->next
)
164 TpChannel
*channel
= TP_CHANNEL (l
->data
);
165 GQuark type
= tp_channel_get_channel_type_id (channel
);
167 /* Check that Call channels are not ended */
168 if (type
== TP_IFACE_QUARK_CHANNEL_TYPE_CALL
&&
169 tp_call_channel_get_state (TP_CALL_CHANNEL (channel
),
170 NULL
, NULL
, NULL
) == TP_CALL_STATE_ENDED
)
180 claim_and_leave_cb (GObject
*source
,
181 GAsyncResult
*result
,
184 AutoRejectCtx
*ctx
= user_data
;
185 GError
*error
= NULL
;
187 if (!tp_channel_dispatch_operation_leave_channels_finish (
188 TP_CHANNEL_DISPATCH_OPERATION (source
), result
, &error
))
190 DEBUG ("Failed to reject call: %s", error
->message
);
192 g_error_free (error
);
196 display_reject_notification (ctx
->self
, ctx
->main_channel
);
199 auto_reject_ctx_free (ctx
);
203 observe_channels (TpSimpleObserver
*observer
,
205 TpConnection
*connection
,
207 TpChannelDispatchOperation
*dispatch_operation
,
209 TpObserveChannelsContext
*context
,
212 EmpathyCallObserver
*self
= EMPATHY_CALL_OBSERVER (user_data
);
216 channel
= find_main_channel (channels
);
219 GError err
= { TP_ERROR
, TP_ERROR_INVALID_ARGUMENT
,
220 "Unknown channel type" };
222 DEBUG ("Didn't find any Call channel; ignoring");
224 tp_observe_channels_context_fail (context
, &err
);
228 /* Autoreject if there are other ongoing calls */
229 if (has_ongoing_calls (self
))
231 AutoRejectCtx
*ctx
= auto_reject_ctx_new (self
, context
, channel
);
233 DEBUG ("Autorejecting incoming call since there are others in "
234 "progress: %s", tp_proxy_get_object_path (channel
));
236 tp_channel_dispatch_operation_leave_channels_async (dispatch_operation
,
237 TP_CHANNEL_GROUP_CHANGE_REASON_BUSY
, "Already in a call",
238 claim_and_leave_cb
, ctx
);
240 tp_observe_channels_context_accept (context
);
244 if ((error
= tp_proxy_get_invalidated (channel
)) != NULL
)
246 DEBUG ("The channel has already been invalidated: %s",
249 tp_observe_channels_context_fail (context
, error
);
253 DEBUG ("Observing channel %s", tp_proxy_get_object_path (channel
));
255 tp_g_signal_connect_object (channel
, "invalidated",
256 G_CALLBACK (on_channel_closed
), self
, 0);
257 self
->priv
->channels
= g_list_prepend (self
->priv
->channels
,
258 g_object_ref (channel
));
260 tp_observe_channels_context_accept (context
);
264 observer_constructor (GType type
,
266 GObjectConstructParam
*props
)
270 if (observer_singleton
)
272 retval
= g_object_ref (observer_singleton
);
276 retval
= G_OBJECT_CLASS (empathy_call_observer_parent_class
)->constructor
277 (type
, n_props
, props
);
279 observer_singleton
= EMPATHY_CALL_OBSERVER (retval
);
280 g_object_add_weak_pointer (retval
, (gpointer
) &observer_singleton
);
287 observer_dispose (GObject
*object
)
289 EmpathyCallObserver
*self
= EMPATHY_CALL_OBSERVER (object
);
291 tp_clear_object (&self
->priv
->notify_mgr
);
292 tp_clear_object (&self
->priv
->observer
);
293 g_list_free_full (self
->priv
->channels
, g_object_unref
);
294 self
->priv
->channels
= NULL
;
298 empathy_call_observer_class_init (EmpathyCallObserverClass
*klass
)
300 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
302 object_class
->dispose
= observer_dispose
;
303 object_class
->constructor
= observer_constructor
;
305 g_type_class_add_private (object_class
, sizeof (EmpathyCallObserverPriv
));
309 empathy_call_observer_init (EmpathyCallObserver
*self
)
311 EmpathyCallObserverPriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
312 EMPATHY_TYPE_CALL_OBSERVER
, EmpathyCallObserverPriv
);
313 TpAccountManager
*am
;
314 GError
*error
= NULL
;
318 self
->priv
->notify_mgr
= empathy_notify_manager_dup_singleton ();
320 am
= tp_account_manager_dup ();
322 self
->priv
->observer
= tp_simple_observer_new_with_am (am
, TRUE
,
323 "Empathy.CallObserver", FALSE
,
324 observe_channels
, self
, NULL
);
326 /* Observe Call channels */
327 tp_base_client_take_observer_filter (self
->priv
->observer
,
329 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
,
330 TP_IFACE_CHANNEL_TYPE_CALL
,
331 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, G_TYPE_UINT
,
332 TP_HANDLE_TYPE_CONTACT
,
335 tp_base_client_set_observer_delay_approvers (self
->priv
->observer
, TRUE
);
337 if (!tp_base_client_register (self
->priv
->observer
, &error
))
339 DEBUG ("Failed to register observer: %s", error
->message
);
340 g_error_free (error
);
346 EmpathyCallObserver
*
347 empathy_call_observer_dup_singleton (void)
349 return g_object_new (EMPATHY_TYPE_CALL_OBSERVER
, NULL
);