#include <glib/gi18n-lib.h>
#include <dbus/dbus-glib.h>
+#include <telepathy-glib/account-manager.h>
#include <telepathy-glib/dbus.h>
#include <telepathy-glib/util.h>
-#include "empathy-account-manager.h"
#include "empathy-idle.h"
#include "empathy-utils.h"
#include "empathy-connectivity.h"
/* Number of seconds before entering extended autoaway. */
#define EXT_AWAY_TIME (30*60)
+/* Number of seconds to consider an account in the "just connected" state
+ * for. */
+#define ACCOUNT_IS_JUST_CONNECTED_SECONDS 10
+
#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIdle)
typedef struct {
DBusGProxy *gs_proxy;
EmpathyConnectivity *connectivity;
gulong state_change_signal_id;
+ gboolean ready;
+
TpConnectionPresenceType state;
gchar *status;
- TpConnectionPresenceType flash_state;
gboolean auto_away;
TpConnectionPresenceType away_saved_state;
gboolean is_idle;
guint ext_away_timeout;
- EmpathyAccountManager *manager;
+ TpAccountManager *manager;
+ gulong idle_presence_changed_id;
+
+ /* pointer to a TpAccount --> glong of time of connection */
+ GHashTable *connect_times;
+
+ TpConnectionPresenceType requested_presence_type;
+ gchar *requested_status_message;
+
} EmpathyIdlePriv;
typedef enum {
PROP_0,
PROP_STATE,
PROP_STATUS,
- PROP_FLASH_STATE,
PROP_AUTO_AWAY
};
static EmpathyIdle * idle_singleton = NULL;
+static const gchar *presence_type_to_status[NUM_TP_CONNECTION_PRESENCE_TYPES] = {
+ NULL,
+ "offline",
+ "available",
+ "away",
+ "xa",
+ "hidden",
+ "busy",
+ NULL,
+ NULL,
+};
+
static void
-idle_presence_changed_cb (EmpathyAccountManager *manager,
+idle_presence_changed_cb (TpAccountManager *manager,
TpConnectionPresenceType state,
gchar *status,
gchar *status_message,
new_status = priv->status;
}
- DEBUG ("Restoring state to %d, reset status to %s",
- priv->away_saved_state, new_status);
-
- empathy_idle_set_presence (idle,
- priv->away_saved_state,
- new_status);
+ /* Only try and set the presence if the away saved state is not
+ * unset. This is an odd case because it means that the session
+ * didn't notify us of the state change to idle, and as a
+ * result, we couldn't save the current state at that time.
+ */
+ if (priv->away_saved_state != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
+ DEBUG ("Restoring state to %d, reset status to %s",
+ priv->away_saved_state, new_status);
+
+ empathy_idle_set_presence (idle,
+ priv->away_saved_state,
+ new_status);
+ } else {
+ DEBUG ("Away saved state is unset. This means that we "
+ "weren't told when the session went idle. "
+ "As a result, I'm not trying to set presence");
+ }
priv->away_saved_state = TP_CONNECTION_PRESENCE_TYPE_UNSET;
}
priv = GET_PRIV (object);
g_free (priv->status);
+ g_free (priv->requested_status_message);
if (priv->gs_proxy) {
g_object_unref (priv->gs_proxy);
priv->state_change_signal_id);
priv->state_change_signal_id = 0;
+ if (priv->manager != NULL) {
+ g_signal_handler_disconnect (priv->manager,
+ priv->idle_presence_changed_id);
+ g_object_unref (priv->manager);
+ }
+
g_object_unref (priv->connectivity);
+ g_hash_table_destroy (priv->connect_times);
+ priv->connect_times = NULL;
+
idle_ext_away_stop (EMPATHY_IDLE (object));
}
return retval;
}
+static const gchar *
+empathy_idle_get_status (EmpathyIdle *idle)
+{
+ EmpathyIdlePriv *priv;
+
+ priv = GET_PRIV (idle);
+
+ if (G_UNLIKELY (!priv->ready))
+ g_critical (G_STRLOC ": %s called before AccountManager ready",
+ G_STRFUNC);
+
+ if (!priv->status) {
+ return empathy_presence_get_default_message (priv->state);
+ }
+
+ return priv->status;
+}
+
static void
idle_get_property (GObject *object,
guint param_id,
case PROP_STATUS:
g_value_set_string (value, empathy_idle_get_status (idle));
break;
- case PROP_FLASH_STATE:
- g_value_set_enum (value, empathy_idle_get_flash_state (idle));
- break;
case PROP_AUTO_AWAY:
g_value_set_boolean (value, empathy_idle_get_auto_away (idle));
break;
case PROP_STATUS:
empathy_idle_set_status (idle, g_value_get_string (value));
break;
- case PROP_FLASH_STATE:
- empathy_idle_set_flash_state (idle, g_value_get_enum (value));
- break;
case PROP_AUTO_AWAY:
empathy_idle_set_auto_away (idle, g_value_get_boolean (value));
break;
"status",
NULL,
G_PARAM_READWRITE));
- g_object_class_install_property (object_class,
- PROP_FLASH_STATE,
- g_param_spec_uint ("flash-state",
- "flash-state",
- "flash-state",
- 0, NUM_TP_CONNECTION_PRESENCE_TYPES,
- TP_CONNECTION_PRESENCE_TYPE_UNSET,
- G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_AUTO_AWAY,
g_type_class_add_private (object_class, sizeof (EmpathyIdlePriv));
}
+static void
+account_status_changed_cb (TpAccount *account,
+ guint old_status,
+ guint new_status,
+ guint reason,
+ gchar *dbus_error_name,
+ GHashTable *details,
+ gpointer user_data)
+{
+ EmpathyIdle *idle = EMPATHY_IDLE (user_data);
+ EmpathyIdlePriv *priv = GET_PRIV (idle);
+ GTimeVal val;
+
+ if (new_status == TP_CONNECTION_STATUS_CONNECTED) {
+ g_get_current_time (&val);
+ g_hash_table_insert (priv->connect_times, account,
+ GINT_TO_POINTER (val.tv_sec));
+ } else if (new_status == TP_CONNECTION_STATUS_DISCONNECTED) {
+ g_hash_table_remove (priv->connect_times, account);
+ }
+}
+
+static void
+account_manager_ready_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EmpathyIdle *idle = user_data;
+ TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
+ EmpathyIdlePriv *priv;
+ TpConnectionPresenceType state;
+ gchar *status, *status_message;
+ GList *accounts, *l;
+ GError *error = NULL;
+
+ /* In case we've been finalized before reading this callback */
+ if (idle_singleton == NULL)
+ return;
+
+ priv = GET_PRIV (idle);
+ priv->ready = TRUE;
+
+ if (!tp_account_manager_prepare_finish (account_manager, result, &error)) {
+ DEBUG ("Failed to prepare account manager: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ state = tp_account_manager_get_most_available_presence (priv->manager,
+ &status, &status_message);
+
+ idle_presence_changed_cb (account_manager, state, status,
+ status_message, idle);
+
+ accounts = tp_account_manager_get_valid_accounts (priv->manager);
+ for (l = accounts; l != NULL; l = l->next) {
+ empathy_signal_connect_weak (l->data, "status-changed",
+ G_CALLBACK (account_status_changed_cb),
+ G_OBJECT (idle));
+ }
+ g_list_free (accounts);
+
+ g_free (status);
+ g_free (status_message);
+}
+
static void
empathy_idle_init (EmpathyIdle *idle)
{
idle->priv = priv;
priv->is_idle = FALSE;
- priv->manager = empathy_account_manager_dup_singleton ();
- priv->state = empathy_account_manager_get_global_presence (priv->manager,
- NULL, &priv->status);
+ priv->manager = tp_account_manager_dup ();
+ tp_account_manager_prepare_async (priv->manager, NULL,
+ account_manager_ready_cb, idle);
- g_signal_connect (priv->manager, "global-presence-changed",
+ priv->idle_presence_changed_id = g_signal_connect (priv->manager,
+ "most-available-presence-changed",
G_CALLBACK (idle_presence_changed_cb), idle);
priv->gs_proxy = dbus_g_proxy_new_for_name (tp_get_bus (),
priv->connectivity = empathy_connectivity_dup_singleton ();
priv->state_change_signal_id = g_signal_connect (priv->connectivity,
"state-change", G_CALLBACK (idle_state_change_cb), idle);
+
+ priv->connect_times = g_hash_table_new (g_direct_hash, g_direct_equal);
}
EmpathyIdle *
priv = GET_PRIV (idle);
+ if (G_UNLIKELY (!priv->ready))
+ g_critical (G_STRLOC ": %s called before AccountManager ready",
+ G_STRFUNC);
+
return priv->state;
}
empathy_idle_set_presence (idle, state, priv->status);
}
-const gchar *
-empathy_idle_get_status (EmpathyIdle *idle)
-{
- EmpathyIdlePriv *priv;
-
- priv = GET_PRIV (idle);
-
- if (!priv->status) {
- return empathy_presence_get_default_message (priv->state);
- }
-
- return priv->status;
-}
-
void
empathy_idle_set_status (EmpathyIdle *idle,
const gchar *status)
empathy_idle_set_presence (idle, priv->state, status);
}
-TpConnectionPresenceType
-empathy_idle_get_flash_state (EmpathyIdle *idle)
-{
- EmpathyIdlePriv *priv;
-
- priv = GET_PRIV (idle);
-
- return priv->flash_state;
-}
-
-void
-empathy_idle_set_flash_state (EmpathyIdle *idle,
- TpConnectionPresenceType state)
-{
- EmpathyIdlePriv *priv;
-
- priv = GET_PRIV (idle);
-
- priv->flash_state = state;
-
- if (state == TP_CONNECTION_PRESENCE_TYPE_UNSET) {
- }
-
- g_object_notify (G_OBJECT (idle), "flash-state");
-}
-
static void
empathy_idle_do_set_presence (EmpathyIdle *idle,
TpConnectionPresenceType status_type,
const gchar *status_message)
{
EmpathyIdlePriv *priv = GET_PRIV (idle);
- const gchar *statuses[NUM_TP_CONNECTION_PRESENCE_TYPES] = {
- NULL,
- "offline",
- "available",
- "away",
- "xa",
- "hidden",
- "busy",
- NULL,
- NULL,
- };
const gchar *status;
g_assert (status_type > 0 && status_type < NUM_TP_CONNECTION_PRESENCE_TYPES);
- status = statuses[status_type];
+ status = presence_type_to_status[status_type];
g_return_if_fail (status != NULL);
- empathy_account_manager_request_global_presence (priv->manager,
+ /* We possibly should be sure that the account manager is prepared, but
+ * sometimes this isn't possible, like when exiting. In other words,
+ * we need a callback to empathy_idle_set_presence to be sure the
+ * presence is set on all accounts successfully.
+ * However, in practice, this is fine as we've already prepared the
+ * account manager here in _init. */
+ tp_account_manager_set_all_requested_presences (priv->manager,
status_type, status, status_message);
}
DEBUG ("Changing presence to %s (%d)", status, state);
+ g_free (priv->requested_status_message);
+ priv->requested_presence_type = state;
+ priv->requested_status_message = g_strdup (status);
+
/* Do not set translated default messages */
default_status = empathy_presence_get_default_message (state);
if (!tp_strdiff (status, default_status)) {
status = NULL;
}
- if (!empathy_connectivity_is_online (priv->connectivity)) {
+ if (state != TP_CONNECTION_PRESENCE_TYPE_OFFLINE &&
+ !empathy_connectivity_is_online (priv->connectivity)) {
DEBUG ("Empathy is not online");
+ priv->saved_state = state;
if (tp_strdiff (priv->status, status)) {
- g_free (priv->status);
- priv->status = NULL;
+ g_free (priv->saved_status);
+ priv->saved_status = NULL;
if (!EMP_STR_EMPTY (status)) {
- priv->status = g_strdup (status);
+ priv->saved_status = g_strdup (status);
}
- g_object_notify (G_OBJECT (idle), "status");
}
+ return;
}
empathy_idle_do_set_presence (idle, state, status);
g_object_notify (G_OBJECT (idle), "auto-away");
}
+TpConnectionPresenceType
+empathy_idle_get_requested_presence (EmpathyIdle *idle,
+ gchar **status,
+ gchar **status_message)
+{
+ EmpathyIdlePriv *priv = GET_PRIV (idle);
+
+ if (status != NULL) {
+ *status = g_strdup (presence_type_to_status[priv->requested_presence_type]);
+ }
+
+ if (status_message != NULL) {
+ *status_message = g_strdup (priv->requested_status_message);
+ }
+
+ return priv->requested_presence_type;
+}
+
+/* This function returns %TRUE if EmpathyIdle considers the account
+ * @account as having just connected recently. Otherwise, it returns
+ * %FALSE. In doubt, %FALSE is returned. */
+gboolean
+empathy_idle_account_is_just_connected (EmpathyIdle *idle,
+ TpAccount *account)
+{
+ EmpathyIdlePriv *priv = GET_PRIV (idle);
+ GTimeVal val;
+ gpointer ptr;
+ glong t;
+
+ if (tp_account_get_connection_status (account, NULL)
+ != TP_CONNECTION_STATUS_CONNECTED) {
+ return FALSE;
+ }
+
+ ptr = g_hash_table_lookup (priv->connect_times, account);
+
+ if (ptr == NULL) {
+ return FALSE;
+ }
+
+ t = GPOINTER_TO_INT (ptr);
+
+ g_get_current_time (&val);
+
+ return (val.tv_sec - t) < ACCOUNT_IS_JUST_CONNECTED_SECONDS;
+}