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.
This commit is contained in:
parent
82648bc86c
commit
60c88612f7
@ -73,7 +73,7 @@ GIO_MIN_VERSION=2.25.9
|
|||||||
LIBECAL_MIN_VERSION=2.32.0
|
LIBECAL_MIN_VERSION=2.32.0
|
||||||
LIBEDATASERVER_MIN_VERSION=1.2.0
|
LIBEDATASERVER_MIN_VERSION=1.2.0
|
||||||
LIBEDATASERVERUI_MIN_VERSION=2.91.6
|
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
|
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
||||||
POLKIT_MIN_VERSION=0.100
|
POLKIT_MIN_VERSION=0.100
|
||||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||||
|
@ -77,14 +77,20 @@ Client.prototype = {
|
|||||||
// The second argument, recover, means _observeChannels will be run
|
// The second argument, recover, means _observeChannels will be run
|
||||||
// for any existing channel as well.
|
// for any existing channel as well.
|
||||||
let dbus = Tp.DBusDaemon.dup();
|
let dbus = Tp.DBusDaemon.dup();
|
||||||
this._observer = Shell.TpClient.new(dbus);
|
this._tp_client = new Shell.TpClient({ 'dbus_daemon': dbus,
|
||||||
this._observer.set_observe_channels_func(
|
'name': 'GnomeShell',
|
||||||
|
'uniquify-name': true })
|
||||||
|
this._tp_client.set_observe_channels_func(
|
||||||
Lang.bind(this, this._observeChannels));
|
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 {
|
try {
|
||||||
this._observer.register();
|
this._tp_client.register();
|
||||||
} catch (e) {
|
} 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()])
|
if (this._sources[channel.get_object_path()])
|
||||||
return;
|
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;
|
this._sources[channel.get_object_path()] = source;
|
||||||
source.connect('destroy', Lang.bind(this,
|
source.connect('destroy', Lang.bind(this,
|
||||||
function() {
|
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()];
|
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) {
|
function Source(account, conn, channel, contact, client) {
|
||||||
this._init(account, conn, channel, contact);
|
this._init(account, conn, channel, contact, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
Source.prototype = {
|
Source.prototype = {
|
||||||
__proto__: MessageTray.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());
|
MessageTray.Source.prototype._init.call(this, contact.get_alias());
|
||||||
|
|
||||||
this.isChat = true;
|
this.isChat = true;
|
||||||
|
|
||||||
this._account = account;
|
this._account = account;
|
||||||
this._contact = contact;
|
this._contact = contact;
|
||||||
|
this._client = client;
|
||||||
|
|
||||||
this._conn = conn;
|
this._conn = conn;
|
||||||
this._channel = channel;
|
this._channel = channel;
|
||||||
@ -216,13 +270,17 @@ Source.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
open: function(notification) {
|
open: function(notification) {
|
||||||
let props = {};
|
if (this._client.is_handling_channel(this._channel)) {
|
||||||
props[Tp.PROP_CHANNEL_CHANNEL_TYPE] = Tp.IFACE_CHANNEL_TYPE_TEXT;
|
// We are handling the channel, try to pass it to Empathy
|
||||||
[props[Tp.PROP_CHANNEL_TARGET_HANDLE], props[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE]] = this._channel.get_handle();
|
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());
|
cd.present_channel_async(this._channel, global.get_current_time(), null);
|
||||||
|
}
|
||||||
req.ensure_channel_async('', null, null);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_getLogMessages: function() {
|
_getLogMessages: function() {
|
||||||
|
@ -11,6 +11,14 @@ struct _ShellTpClientPrivate
|
|||||||
ShellTpClientObserveChannelsImpl observe_impl;
|
ShellTpClientObserveChannelsImpl observe_impl;
|
||||||
gpointer user_data_obs;
|
gpointer user_data_obs;
|
||||||
GDestroyNotify destroy_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.
|
* 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
|
static void
|
||||||
shell_tp_client_init (ShellTpClient *self)
|
shell_tp_client_init (ShellTpClient *self)
|
||||||
{
|
{
|
||||||
|
GHashTable *filter;
|
||||||
|
|
||||||
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, SHELL_TYPE_TP_CLIENT,
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, SHELL_TYPE_TP_CLIENT,
|
||||||
ShellTpClientPrivate);
|
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 */
|
/* Observer */
|
||||||
tp_base_client_set_observer_recover (TP_BASE_CLIENT (self), TRUE);
|
tp_base_client_set_observer_recover (TP_BASE_CLIENT (self), TRUE);
|
||||||
|
|
||||||
/* We only care about single-user text-based chats */
|
tp_base_client_add_observer_filter (TP_BASE_CLIENT (self), filter);
|
||||||
tp_base_client_take_observer_filter (TP_BASE_CLIENT (self),
|
|
||||||
tp_asv_new (
|
/* Approver */
|
||||||
TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
|
tp_base_client_add_approver_filter (TP_BASE_CLIENT (self), filter);
|
||||||
TP_IFACE_CHANNEL_TYPE_TEXT,
|
|
||||||
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
|
/* Handler */
|
||||||
TP_HANDLE_TYPE_CONTACT,
|
tp_base_client_add_handler_filter (TP_BASE_CLIENT (self), filter);
|
||||||
NULL));
|
|
||||||
|
g_hash_table_unref (filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -68,6 +124,40 @@ observe_channels (TpBaseClient *client,
|
|||||||
dispatch_operation, requests, context, self->priv->user_data_obs);
|
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
|
static void
|
||||||
shell_tp_client_dispose (GObject *object)
|
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 (self->priv->user_data_obs);
|
||||||
self->priv->destroy_obs = NULL;
|
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)
|
if (dispose != NULL)
|
||||||
@ -96,22 +201,8 @@ shell_tp_client_class_init (ShellTpClientClass *cls)
|
|||||||
object_class->dispose = shell_tp_client_dispose;
|
object_class->dispose = shell_tp_client_dispose;
|
||||||
|
|
||||||
base_clt_cls->observe_channels = observe_channels;
|
base_clt_cls->observe_channels = observe_channels;
|
||||||
}
|
base_clt_cls->add_dispatch_operation = add_dispatch_operation;
|
||||||
|
base_clt_cls->handle_channels = handle_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -127,6 +218,32 @@ shell_tp_client_set_observe_channels_func (ShellTpClient *self,
|
|||||||
self->priv->destroy_obs = destroy;
|
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 */
|
/* Telepathy utility functions */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,13 +51,40 @@ typedef void (*ShellTpClientObserveChannelsImpl) (ShellTpClient *client,
|
|||||||
TpObserveChannelsContext *context,
|
TpObserveChannelsContext *context,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
|
|
||||||
ShellTpClient * shell_tp_client_new (TpDBusDaemon *dbus);
|
|
||||||
|
|
||||||
void shell_tp_client_set_observe_channels_func (ShellTpClient *self,
|
void shell_tp_client_set_observe_channels_func (ShellTpClient *self,
|
||||||
ShellTpClientObserveChannelsImpl observe_impl,
|
ShellTpClientObserveChannelsImpl observe_impl,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
GDestroyNotify destroy);
|
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 */
|
/* Telepathy utility functions */
|
||||||
typedef void (*ShellGetTpContactCb) (TpConnection *connection,
|
typedef void (*ShellGetTpContactCb) (TpConnection *connection,
|
||||||
GList *contacts,
|
GList *contacts,
|
||||||
|
Loading…
Reference in New Issue
Block a user