#include <glib/gi18n-lib.h>
#include <dbus/dbus-glib.h>
-#ifdef HAVE_NM
-#include <nm-client.h>
-#endif
+#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"
#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
#include "empathy-debug.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;
-#ifdef HAVE_NM
- NMClient *nm_client;
-#endif
+ EmpathyConnectivity *connectivity;
+ gulong state_change_signal_id;
+
+ gboolean ready;
TpConnectionPresenceType state;
gchar *status;
- TpConnectionPresenceType flash_state;
gboolean auto_away;
- gboolean use_nm;
TpConnectionPresenceType away_saved_state;
- TpConnectionPresenceType nm_saved_state;
- gchar *nm_saved_status;
+ TpConnectionPresenceType saved_state;
+ gchar *saved_status;
gboolean is_idle;
- gboolean nm_connected;
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,
- PROP_USE_NM
+ PROP_AUTO_AWAY
};
G_DEFINE_TYPE (EmpathyIdle, empathy_idle, G_TYPE_OBJECT);
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,
is_idle ? "yes" : "no");
if (!priv->auto_away ||
- (priv->nm_saved_state == TP_CONNECTION_PRESENCE_TYPE_UNSET &&
+ (priv->saved_state == TP_CONNECTION_PRESENCE_TYPE_UNSET &&
(priv->state <= TP_CONNECTION_PRESENCE_TYPE_OFFLINE ||
priv->state == TP_CONNECTION_PRESENCE_TYPE_HIDDEN))) {
/* We don't want to go auto away OR we explicitely asked to be
idle_ext_away_start (idle);
- if (priv->nm_saved_state != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
+ if (priv->saved_state != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
/* We are disconnected, when coming back from away
* we want to restore the presence before the
* disconnection. */
- priv->away_saved_state = priv->nm_saved_state;
+ priv->away_saved_state = priv->saved_state;
} else {
priv->away_saved_state = priv->state;
}
priv->away_saved_state, new_state);
empathy_idle_set_state (idle, new_state);
} else if (!is_idle && priv->is_idle) {
- const gchar *new_status;
/* We are no more idle, restore state */
idle_ext_away_stop (idle);
- if (priv->away_saved_state == TP_CONNECTION_PRESENCE_TYPE_AWAY ||
- priv->away_saved_state == TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY) {
- priv->away_saved_state = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
- new_status = NULL;
+ /* 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",
+ priv->away_saved_state);
+
+ empathy_idle_set_state (idle,priv->away_saved_state);
} else {
- new_status = priv->status;
+ 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");
}
- 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);
-
priv->away_saved_state = TP_CONNECTION_PRESENCE_TYPE_UNSET;
}
priv->is_idle = is_idle;
}
-#ifdef HAVE_NM
static void
-idle_nm_state_change_cb (NMClient *client,
- const GParamSpec *pspec,
- EmpathyIdle *idle)
+idle_state_change_cb (EmpathyConnectivity *connectivity,
+ gboolean new_online,
+ EmpathyIdle *idle)
{
EmpathyIdlePriv *priv;
- gboolean old_nm_connected;
- gboolean new_nm_connected;
- NMState state;
priv = GET_PRIV (idle);
- if (!priv->use_nm) {
- return;
- }
-
- state = nm_client_get_state (priv->nm_client);
- old_nm_connected = priv->nm_connected;
- new_nm_connected = !(state == NM_STATE_CONNECTING ||
- state == NM_STATE_DISCONNECTED);
- priv->nm_connected = TRUE; /* To be sure _set_state will work */
-
- DEBUG ("New network state %d", state);
-
- if (old_nm_connected && !new_nm_connected) {
- /* We are no more connected */
+ if (!new_online) {
+ /* We are no longer connected */
DEBUG ("Disconnected: Save state %d (%s)",
priv->state, priv->status);
- priv->nm_saved_state = priv->state;
- g_free (priv->nm_saved_status);
- priv->nm_saved_status = g_strdup (priv->status);
+ priv->saved_state = priv->state;
+ g_free (priv->saved_status);
+ priv->saved_status = g_strdup (priv->status);
empathy_idle_set_state (idle, TP_CONNECTION_PRESENCE_TYPE_OFFLINE);
}
- else if (!old_nm_connected && new_nm_connected
- && priv->nm_saved_state != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
+ else if (new_online
+ && priv->saved_state != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
/* We are now connected */
DEBUG ("Reconnected: Restore state %d (%s)",
- priv->nm_saved_state, priv->nm_saved_status);
+ priv->saved_state, priv->saved_status);
empathy_idle_set_presence (idle,
- priv->nm_saved_state,
- priv->nm_saved_status);
- priv->nm_saved_state = TP_CONNECTION_PRESENCE_TYPE_UNSET;
- g_free (priv->nm_saved_status);
- priv->nm_saved_status = NULL;
+ priv->saved_state,
+ priv->saved_status);
+ priv->saved_state = TP_CONNECTION_PRESENCE_TYPE_UNSET;
+ g_free (priv->saved_status);
+ priv->saved_status = NULL;
}
-
- priv->nm_connected = new_nm_connected;
}
-#endif
static void
idle_finalize (GObject *object)
priv = GET_PRIV (object);
g_free (priv->status);
+ g_free (priv->requested_status_message);
if (priv->gs_proxy) {
g_object_unref (priv->gs_proxy);
}
- if (priv->manager != NULL)
- g_object_unref (priv->manager);
+ g_signal_handler_disconnect (priv->connectivity,
+ priv->state_change_signal_id);
+ priv->state_change_signal_id = 0;
-#ifdef HAVE_NM
- if (priv->nm_client) {
- g_object_unref (priv->nm_client);
+ if (priv->manager != NULL) {
+ g_signal_handler_disconnect (priv->manager,
+ priv->idle_presence_changed_id);
+ g_object_unref (priv->manager);
}
-#endif
+
+ 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_USE_NM:
- g_value_set_boolean (value, empathy_idle_get_use_nm (idle));
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
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;
- case PROP_USE_NM:
- empathy_idle_set_use_nm (idle, g_value_get_boolean (value));
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
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,
FALSE,
G_PARAM_READWRITE));
- g_object_class_install_property (object_class,
- PROP_USE_NM,
- g_param_spec_boolean ("use-nm",
- "Use Network Manager",
- "Set presence according to Network Manager",
- TRUE,
- G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
-
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) {
+ tp_g_signal_connect_object (l->data, "status-changed",
+ G_CALLBACK (account_status_changed_cb),
+ idle, 0);
+ }
+ g_list_free (accounts);
+
+ g_free (status);
+ g_free (status_message);
+}
+
static void
empathy_idle_init (EmpathyIdle *idle)
{
EmpathyIdlePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (idle,
EMPATHY_TYPE_IDLE, EmpathyIdlePriv);
+ TpDBusDaemon *dbus;
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 (),
+ dbus = tp_dbus_daemon_dup (NULL);
+
+ priv->gs_proxy = dbus_g_proxy_new_for_name (
+ tp_proxy_get_dbus_connection (dbus),
"org.gnome.SessionManager",
"/org/gnome/SessionManager/Presence",
"org.gnome.SessionManager.Presence");
DEBUG ("Failed to get gs proxy");
}
-#ifdef HAVE_NM
- priv->nm_client = nm_client_new ();
- if (priv->nm_client) {
- g_signal_connect (priv->nm_client, "notify::" NM_CLIENT_STATE,
- G_CALLBACK (idle_nm_state_change_cb),
- idle);
- } else {
- DEBUG ("Failed to get nm proxy");
- }
-#endif
+ g_object_unref (dbus);
+
+ 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 (!priv->nm_connected) {
- DEBUG ("NM not connected");
+ if (state != TP_CONNECTION_PRESENCE_TYPE_OFFLINE &&
+ !empathy_connectivity_is_online (priv->connectivity)) {
+ DEBUG ("Empathy is not online");
- priv->nm_saved_state = state;
+ 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;
}
g_object_notify (G_OBJECT (idle), "auto-away");
}
-gboolean
-empathy_idle_get_use_nm (EmpathyIdle *idle)
+TpConnectionPresenceType
+empathy_idle_get_requested_presence (EmpathyIdle *idle,
+ gchar **status,
+ gchar **status_message)
{
EmpathyIdlePriv *priv = GET_PRIV (idle);
- return priv->use_nm;
+ 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;
}
-void
-empathy_idle_set_use_nm (EmpathyIdle *idle,
- gboolean use_nm)
+/* 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;
-#ifdef HAVE_NM
- if (!priv->nm_client || use_nm == priv->use_nm) {
- return;
+ if (tp_account_get_connection_status (account, NULL)
+ != TP_CONNECTION_STATUS_CONNECTED) {
+ return FALSE;
}
-#endif
- priv->use_nm = use_nm;
+ ptr = g_hash_table_lookup (priv->connect_times, account);
-#ifdef HAVE_NM
- if (use_nm) {
- idle_nm_state_change_cb (priv->nm_client, NULL, idle);
-#else
- if (0) {
-#endif
- } else {
- priv->nm_connected = TRUE;
- if (priv->nm_saved_state != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
- empathy_idle_set_state (idle, priv->nm_saved_state);
- }
- priv->nm_saved_state = TP_CONNECTION_PRESENCE_TYPE_UNSET;
+ if (ptr == NULL) {
+ return FALSE;
}
- g_object_notify (G_OBJECT (idle), "use-nm");
-}
+ t = GPOINTER_TO_INT (ptr);
+
+ g_get_current_time (&val);
+ return (val.tv_sec - t) < ACCOUNT_IS_JUST_CONNECTED_SECONDS;
+}