[gnome-shell] Implement a Telepathy Approver and Handler (#643594)



commit 60c88612f7c4f8d302c05bd3ee6101904f6d8b34
Author: Guillaume Desmottes <guillaume desmottes collabora co uk>
Date:   Tue Apr 5 13:28:11 2011 +0200

    Implement a Telepathy Approver and Handler (#643594)
    
    As the Shell does more than observing channels (users can interact with them),
    it has to be an Handler as well. We have to make sure that all the new
    incoming text channels are handled by the Shell by default, so we make it an
    Approver as well.
    
    From an user point of view, the only difference is that Empathy's tray icon
    will stop blicking when receiving new channels.
    
    We rely on ChannelDispatcher.DelegateChannels() and PresentChannel() to
    interact with Empathy. Those methods have been implemented in
    telepathy-mission-control 5.9.0 and telepathy-glib 0.15.0.

 configure.ac             |    2 +-
 js/ui/telepathyClient.js |   88 ++++++++++++++++++++----
 src/shell-tp-client.c    |  165 +++++++++++++++++++++++++++++++++++++++-------
 src/shell-tp-client.h    |   31 ++++++++-
 4 files changed, 244 insertions(+), 42 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 826070e..f722c13 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 b1cef1d..a87ab2e 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();
-
-        let req = Tp.AccountChannelRequest.new(this._account, props, global.get_current_time());
-
-        req.ensure_channel_async('', null, null);
+          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);
+
+              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 f34000d..af507bc 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
@@ -69,6 +125,40 @@ observe_channels (TpBaseClient *client,
 }
 
 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)
 {
   ShellTpClient *self = SHELL_TP_CLIENT (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 cec24cc..064c6ab 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,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]