diff --git a/configure.ac b/configure.ac index 826070eb6..f722c1392 100644 --- a/configure.ac +++ b/configure.ac @@ -73,7 +73,7 @@ GIO_MIN_VERSION=2.25.9 LIBECAL_MIN_VERSION=2.32.0 LIBEDATASERVER_MIN_VERSION=1.2.0 LIBEDATASERVERUI_MIN_VERSION=2.91.6 -TELEPATHY_GLIB_MIN_VERSION=0.13.12 +TELEPATHY_GLIB_MIN_VERSION=0.15.0 TELEPATHY_LOGGER_MIN_VERSION=0.2.4 POLKIT_MIN_VERSION=0.100 STARTUP_NOTIFICATION_MIN_VERSION=0.11 diff --git a/js/ui/telepathyClient.js b/js/ui/telepathyClient.js index b1cef1d3a..a87ab2ef3 100644 --- a/js/ui/telepathyClient.js +++ b/js/ui/telepathyClient.js @@ -77,14 +77,20 @@ Client.prototype = { // The second argument, recover, means _observeChannels will be run // for any existing channel as well. let dbus = Tp.DBusDaemon.dup(); - this._observer = Shell.TpClient.new(dbus); - this._observer.set_observe_channels_func( + this._tp_client = new Shell.TpClient({ 'dbus_daemon': dbus, + 'name': 'GnomeShell', + 'uniquify-name': true }) + this._tp_client.set_observe_channels_func( Lang.bind(this, this._observeChannels)); + this._tp_client.set_approve_channels_func( + Lang.bind(this, this._approveChannels)); + this._tp_client.set_handle_channels_func( + Lang.bind(this, this._handleChannels)); try { - this._observer.register(); + this._tp_client.register(); } catch (e) { - throw new Error('Couldn\'t register SimpleObserver. Error: \n' + e); + throw new Error('Couldn\'t register Telepathy client. Error: \n' + e); } }, @@ -135,30 +141,78 @@ Client.prototype = { if (this._sources[channel.get_object_path()]) return; - let source = new Source(account, conn, channel, contact); + let source = new Source(account, conn, channel, contact, this._tp_client); this._sources[channel.get_object_path()] = source; source.connect('destroy', Lang.bind(this, function() { + if (this._tp_client.is_handling_channel(channel)) { + // The chat box has been destroyed so it can't + // handle the channel any more. + channel.close_async(null); + } + delete this._sources[channel.get_object_path()]; })); + }, + + _handlingChannels: function(account, conn, channels) { + let len = channels.length; + for (let i = 0; i < len; i++) { + let channel = channels[i]; + + // We can only handle text channel, so close any other channel + if (!(channel instanceof Tp.TextChannel)) { + channel.close_async(null); + continue; + } + + if (this._tp_client.is_handling_channel(channel)) { + // We are already handling the channel, display the source + let source = this._sources[channel.get_object_path()]; + if (source) + source.notify(); + } + } + }, + + _approveChannels: function(approver, account, conn, channels, + dispatchOp, context) { + // Approve the channels right away as we are going to handle it + dispatchOp.claim_with_async(this._tp_client, + Lang.bind (this, function(dispatchOp, result) { + try { + dispatchOp.claim_with_finish(result); + this._handlingChannels(account, conn, channels); + } catch (err) { + global.logError('Failed to Claim channel: ' + err); + }})); + + context.accept(); + }, + + _handleChannels: function(handler, account, conn, channels, + requests, user_action_time, context) { + this._handlingChannels(account, conn, channels); + context.accept(); } }; -function Source(account, conn, channel, contact) { - this._init(account, conn, channel, contact); +function Source(account, conn, channel, contact, client) { + this._init(account, conn, channel, contact, client); } Source.prototype = { __proto__: MessageTray.Source.prototype, - _init: function(account, conn, channel, contact) { + _init: function(account, conn, channel, contact, client) { MessageTray.Source.prototype._init.call(this, contact.get_alias()); this.isChat = true; this._account = account; this._contact = contact; + this._client = client; this._conn = conn; this._channel = channel; @@ -216,13 +270,17 @@ Source.prototype = { }, open: function(notification) { - let props = {}; - props[Tp.PROP_CHANNEL_CHANNEL_TYPE] = Tp.IFACE_CHANNEL_TYPE_TEXT; - [props[Tp.PROP_CHANNEL_TARGET_HANDLE], props[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE]] = this._channel.get_handle(); + if (this._client.is_handling_channel(this._channel)) { + // We are handling the channel, try to pass it to Empathy + this._client.delegate_channels_async([this._channel], global.get_current_time(), "", null); + } + else { + // We are not the handler, just ask to present the channel + let dbus = Tp.DBusDaemon.dup(); + let cd = Tp.ChannelDispatcher.new(dbus); - let req = Tp.AccountChannelRequest.new(this._account, props, global.get_current_time()); - - req.ensure_channel_async('', null, null); + cd.present_channel_async(this._channel, global.get_current_time(), null); + } }, _getLogMessages: function() { diff --git a/src/shell-tp-client.c b/src/shell-tp-client.c index f34000daa..af507bc88 100644 --- a/src/shell-tp-client.c +++ b/src/shell-tp-client.c @@ -11,6 +11,14 @@ struct _ShellTpClientPrivate ShellTpClientObserveChannelsImpl observe_impl; gpointer user_data_obs; GDestroyNotify destroy_obs; + + ShellTpClientApproveChannelsImpl approve_channels_impl; + gpointer user_data_approve_channels; + GDestroyNotify destroy_approve_channels; + + ShellTpClientHandleChannelsImpl handle_channels_impl; + gpointer user_data_handle_channels; + GDestroyNotify destroy_handle_channels; }; /** @@ -32,23 +40,71 @@ struct _ShellTpClientPrivate * Signature of the implementation of the ObserveChannels method. */ +/** + * ShellTpClientApproveChannelsImpl: + * @client: a #ShellTpClient instance + * @account: a #TpAccount having %TP_ACCOUNT_FEATURE_CORE prepared if possible + * @connection: a #TpConnection having %TP_CONNECTION_FEATURE_CORE prepared + * if possible + * @channels: (element-type TelepathyGLib.Channel): a #GList of #TpChannel, + * all having %TP_CHANNEL_FEATURE_CORE prepared if possible + * @dispatch_operation: (allow-none): a #TpChannelDispatchOperation or %NULL; + * the dispatch_operation is not guaranteed to be prepared + * @context: a #TpAddDispatchOperationContext representing the context of this + * D-Bus call + * + * Signature of the implementation of the AddDispatchOperation method. + */ + +/** + * ShellTpClientHandleChannelsImpl: + * @client: a #ShellTpClient instance + * @account: a #TpAccount having %TP_ACCOUNT_FEATURE_CORE prepared if possible + * @connection: a #TpConnection having %TP_CONNECTION_FEATURE_CORE prepared + * if possible + * @channels: (element-type TelepathyGLib.Channel): a #GList of #TpChannel, + * all having %TP_CHANNEL_FEATURE_CORE prepared if possible + * @requests_satisfied: (element-type TelepathyGLib.ChannelRequest): a #GList of + * #TpChannelRequest having their object-path defined but are not guaranteed + * to be prepared. + * @user_action_time: the time at which user action occurred, or one of the + * special values %TP_USER_ACTION_TIME_NOT_USER_ACTION or + * %TP_USER_ACTION_TIME_CURRENT_TIME + * (see #TpAccountChannelRequest:user-action-time for details) + * @context: a #TpHandleChannelsContext representing the context of this + * D-Bus call + * + * Signature of the implementation of the HandleChannels method. + */ + static void shell_tp_client_init (ShellTpClient *self) { + GHashTable *filter; + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, SHELL_TYPE_TP_CLIENT, ShellTpClientPrivate); + /* We only care about single-user text-based chats */ + filter = tp_asv_new ( + TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, + TP_IFACE_CHANNEL_TYPE_TEXT, + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, + TP_HANDLE_TYPE_CONTACT, + NULL); + /* Observer */ tp_base_client_set_observer_recover (TP_BASE_CLIENT (self), TRUE); - /* We only care about single-user text-based chats */ - tp_base_client_take_observer_filter (TP_BASE_CLIENT (self), - tp_asv_new ( - TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, - TP_IFACE_CHANNEL_TYPE_TEXT, - TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, - TP_HANDLE_TYPE_CONTACT, - NULL)); + tp_base_client_add_observer_filter (TP_BASE_CLIENT (self), filter); + + /* Approver */ + tp_base_client_add_approver_filter (TP_BASE_CLIENT (self), filter); + + /* Handler */ + tp_base_client_add_handler_filter (TP_BASE_CLIENT (self), filter); + + g_hash_table_unref (filter); } static void @@ -68,6 +124,40 @@ observe_channels (TpBaseClient *client, dispatch_operation, requests, context, self->priv->user_data_obs); } +static void +add_dispatch_operation (TpBaseClient *client, + TpAccount *account, + TpConnection *connection, + GList *channels, + TpChannelDispatchOperation *dispatch_operation, + TpAddDispatchOperationContext *context) +{ + ShellTpClient *self = (ShellTpClient *) client; + + g_assert (self->priv->approve_channels_impl != NULL); + + self->priv->approve_channels_impl (self, account, connection, channels, + dispatch_operation, context, self->priv->user_data_approve_channels); +} + +static void +handle_channels (TpBaseClient *client, + TpAccount *account, + TpConnection *connection, + GList *channels, + GList *requests_satisfied, + gint64 user_action_time, + TpHandleChannelsContext *context) +{ + ShellTpClient *self = (ShellTpClient *) client; + + g_assert (self->priv->handle_channels_impl != NULL); + + self->priv->handle_channels_impl (self, account, connection, channels, + requests_satisfied, user_action_time, context, + self->priv->user_data_handle_channels); +} + static void shell_tp_client_dispose (GObject *object) { @@ -79,6 +169,21 @@ shell_tp_client_dispose (GObject *object) { self->priv->destroy_obs (self->priv->user_data_obs); self->priv->destroy_obs = NULL; + self->priv->user_data_obs = NULL; + } + + if (self->priv->destroy_approve_channels != NULL) + { + self->priv->destroy_approve_channels (self->priv->user_data_approve_channels); + self->priv->destroy_approve_channels = NULL; + self->priv->user_data_approve_channels = NULL; + } + + if (self->priv->destroy_handle_channels != NULL) + { + self->priv->destroy_handle_channels (self->priv->user_data_handle_channels); + self->priv->destroy_handle_channels = NULL; + self->priv->user_data_handle_channels = NULL; } if (dispose != NULL) @@ -96,22 +201,8 @@ shell_tp_client_class_init (ShellTpClientClass *cls) object_class->dispose = shell_tp_client_dispose; base_clt_cls->observe_channels = observe_channels; -} - -/** - * shell_tp_client_new: - * @dbus: a #TpDBusDaemon object, may not be %NULL - * - * Returns: a new #ShellTpClient - */ -ShellTpClient * -shell_tp_client_new (TpDBusDaemon *dbus) -{ - return g_object_new (SHELL_TYPE_TP_CLIENT, - "dbus-daemon", dbus, - "name", "GnomeShell", - "uniquify-name", TRUE, - NULL); + base_clt_cls->add_dispatch_operation = add_dispatch_operation; + base_clt_cls->handle_channels = handle_channels; } void @@ -127,6 +218,32 @@ shell_tp_client_set_observe_channels_func (ShellTpClient *self, self->priv->destroy_obs = destroy; } +void +shell_tp_client_set_approve_channels_func (ShellTpClient *self, + ShellTpClientApproveChannelsImpl approve_channels_impl, + gpointer user_data, + GDestroyNotify destroy) +{ + g_assert (self->priv->approve_channels_impl == NULL); + + self->priv->approve_channels_impl = approve_channels_impl; + self->priv->user_data_approve_channels = user_data; + self->priv->destroy_approve_channels = destroy; +} + +void +shell_tp_client_set_handle_channels_func (ShellTpClient *self, + ShellTpClientHandleChannelsImpl handle_channels_impl, + gpointer user_data, + GDestroyNotify destroy) +{ + g_assert (self->priv->handle_channels_impl == NULL); + + self->priv->handle_channels_impl = handle_channels_impl; + self->priv->user_data_handle_channels = user_data; + self->priv->destroy_handle_channels = destroy; +} + /* Telepathy utility functions */ /** diff --git a/src/shell-tp-client.h b/src/shell-tp-client.h index cec24ccbc..064c6abe5 100644 --- a/src/shell-tp-client.h +++ b/src/shell-tp-client.h @@ -51,13 +51,40 @@ typedef void (*ShellTpClientObserveChannelsImpl) (ShellTpClient *client, TpObserveChannelsContext *context, gpointer user_data); -ShellTpClient * shell_tp_client_new (TpDBusDaemon *dbus); - void shell_tp_client_set_observe_channels_func (ShellTpClient *self, ShellTpClientObserveChannelsImpl observe_impl, gpointer user_data, GDestroyNotify destroy); +typedef void (*ShellTpClientApproveChannelsImpl) ( + ShellTpClient *client, + TpAccount *account, + TpConnection *connection, + GList *channels, + TpChannelDispatchOperation *dispatch_operation, + TpAddDispatchOperationContext *context, + gpointer user_data); + +void shell_tp_client_set_approve_channels_func (ShellTpClient *self, + ShellTpClientApproveChannelsImpl approve_impl, + gpointer user_data, + GDestroyNotify destroy); + +typedef void (*ShellTpClientHandleChannelsImpl) ( + ShellTpClient *client, + TpAccount *account, + TpConnection *connection, + GList *channels, + GList *requests_satisfied, + gint64 user_action_time, + TpHandleChannelsContext *context, + gpointer user_data); + +void shell_tp_client_set_handle_channels_func (ShellTpClient *self, + ShellTpClientHandleChannelsImpl handle_channels_impl, + gpointer user_data, + GDestroyNotify destroy); + /* Telepathy utility functions */ typedef void (*ShellGetTpContactCb) (TpConnection *connection, GList *contacts,