From a5ef0adc0c5accb2b90640b8c57fb8febdf0571c Mon Sep 17 00:00:00 2001 From: Seif Lotfy Date: Wed, 23 May 2012 14:47:25 +0200 Subject: [PATCH] Added new properties to the Telepathy backends. The Telepathy backend requires Zeitgeist to pull its info. Signed-off-by: Seif Lotfy --- backends/telepathy/lib/Makefile.am | 7 ++ backends/telepathy/lib/tpf-persona-store.vala | 130 +++++++++++++++++++++++++- backends/telepathy/lib/tpf-persona.vala | 103 ++++++++++++++++++++ configure.ac | 29 ++++++ 4 files changed, 268 insertions(+), 1 deletion(-) diff --git a/backends/telepathy/lib/Makefile.am b/backends/telepathy/lib/Makefile.am index 77c75f3..ac30174 100644 --- a/backends/telepathy/lib/Makefile.am +++ b/backends/telepathy/lib/Makefile.am @@ -146,6 +146,13 @@ libfolks_telepathy_la_LIBADD = \ $(top_builddir)/folks/libfolks-internal.la \ $(NULL) +if ENABLE_ZEITGEIST +libfolks_telepathy_la_VALAFLAGS += --pkg zeitgeist-1.0 +libfolks_telepathy_la_VALAFLAGS += --define=HAVE_ZEITGEIST +libfolks_telepathy_la_CFLAGS += $(ZEITGEIST_CFLAGS) +libfolks_telepathy_la_LIBADD += $(ZEITGEIST_LIBS) +endif + # The quoting here is unnecessary but harmless, and has the useful side-effect # that vim quickfix mode (:make) doesn't interpret the libtool --mode=link # command as an error message in a bizarrely named file diff --git a/backends/telepathy/lib/tpf-persona-store.vala b/backends/telepathy/lib/tpf-persona-store.vala index cb04c0d..61189c2 100644 --- a/backends/telepathy/lib/tpf-persona-store.vala +++ b/backends/telepathy/lib/tpf-persona-store.vala @@ -24,7 +24,9 @@ using GLib; using Gee; using TelepathyGLib; using Folks; - +#if HAVE_ZEITGEIST +using Zeitgeist; +#endif extern const string G_LOG_DOMAIN; extern const string BACKEND_NAME; @@ -85,6 +87,11 @@ public class Tpf.PersonaStore : Folks.PersonaStore private Account _account; +#if HAVE_ZEITGEIST + private Zeitgeist.Log? _log= null; + private Zeitgeist.Monitor? _monitor = null; +#endif + /** * The Telepathy account this store is based upon. */ @@ -1022,6 +1029,12 @@ public class Tpf.PersonaStore : Folks.PersonaStore new GLib.GenericArray ()); this._got_initial_members = true; +#if HAVE_ZEITGEIST + if (this._monitor == null) + { + this._populate_counters (); + } +#endif this._notify_if_is_quiescent (); } @@ -1529,4 +1542,119 @@ public class Tpf.PersonaStore : Folks.PersonaStore return store; } + +#if HAVE_ZEITGEIST + private string? _get_iid_from_event_metadata (string? uri) + { + /* Format a proper id represting a persona in the store. + * Zeitgeist uses x-telepathy-identifier as a prefix for telepathy, which + * is stored as the uri of a subject of an event. */ + if (uri == null) + { + return null; + } + var new_uri = uri.replace ("x-telepathy-identifier:", ""); + return this.account.protocol + ":" + new_uri; + } + + private void _increase_persona_counter (string? id, string? interaction_type, Event event) + { + /* Check if the persona id and interaction is valid. If so increase the + * appropriate interacton counter, to signify that an + * interaction was successfully counted. */ + if (id != null && this._personas.has_key (id) && interaction_type != null) + { + var persona = this._personas.get (id); + persona._increase_counter (id, interaction_type, event); + } + } + + private void _handle_new_interaction (TimeRange timerange, ResultSet events) + { + foreach (var e in events) + { + for (var i = 1; i < e.num_subjects (); i++) + { + var id = this._get_iid_from_event_metadata (e.get_subject (i).get_uri ()); + var interaction_type = e.get_subject (0).get_interpretation (); + this._increase_persona_counter (id, interaction_type, e); + } + } + } + + private PtrArray _get_zeitgeist_event_templates () + { + /* To fetch events from Zeitgeist about the interaction with contacts we + * create templates reflecting how the telepathy-logger stores events in + * Zeitgeist */ + var origin = this.id.replace (TelepathyGLib.ACCOUNT_OBJECT_PATH_BASE, + "x-telepathy-account-path:"); + + Event ev1 = new Event.full ("", "", "dbus://org.freedesktop.Telepathy.Logger.service"); + ev1.add_subject (new Subject.full ("", Zeitgeist.NMO_IMMESSAGE, "", "", "", "", "")); + ev1.set_origin (origin); + + Event ev2 = new Event.full ("", "", "dbus://org.freedesktop.Telepathy.Logger.service"); + ev2.add_subject (new Subject.full ("", "", Zeitgeist.NFO_MEDIA_STREAM, "", "", "", "")); + ev2.set_origin (origin); + + var templates = new PtrArray (); + templates.add (ev1.ref ()); + templates.add (ev2.ref ()); + return templates; + } + + private async void _populate_counters () + { + this._log = new Zeitgeist.Log (); + + /* Prepare a monitor for this account to populate the counters upon + * interaction changes */ + if (this._monitor == null) + { + PtrArray monitor_events = this._get_zeitgeist_event_templates (); + this._monitor = new Zeitgeist.Monitor (new Zeitgeist.TimeRange.from_now (), + (owned) monitor_events); + this._monitor.events_inserted.connect (this._handle_new_interaction); + } + + /* Get all events for this account from Zeitgeist and increase the + * the counters of the personas */ + try + { + PtrArray events = this._get_zeitgeist_event_templates (); + var results = yield this._log.find_events (new TimeRange.anytime (), + (owned) events, StorageState.ANY, 0, ResultType.MOST_RECENT_EVENTS, + null); + + foreach (var persona in this.personas.values) + { + persona.freeze_notify (); + } + foreach (var e in results) + { + var interaction_type = e.get_subject (0).get_interpretation (); + for (var i = 1; i < e.num_subjects (); i++) + { + var id = this._get_iid_from_event_metadata (e.get_subject (i).get_uri ()); + this._increase_persona_counter (id, interaction_type, e); + } + } + foreach (var persona in this.personas.values) + { + persona.thaw_notify (); + } + } + catch + { + warning ("Failed to fetch events from Zeitgeist"); + } + + /* Install the monitor for this account to be notified when a persona has + * been interacted with */ + this._log.install_monitor (this._monitor); + + this._notify_if_is_quiescent (); + } +#endif } diff --git a/backends/telepathy/lib/tpf-persona.vala b/backends/telepathy/lib/tpf-persona.vala index c5dffd8..d3c8ded 100644 --- a/backends/telepathy/lib/tpf-persona.vala +++ b/backends/telepathy/lib/tpf-persona.vala @@ -22,6 +22,9 @@ using Gee; using GLib; using TelepathyGLib; using Folks; +#if HAVE_ZEITGEIST +using Zeitgeist; +#endif /** * A persona subclass which represents a single instant messaging contact from @@ -34,6 +37,7 @@ public class Tpf.Persona : Folks.Persona, EmailDetails, FavouriteDetails, GroupDetails, + InteractionDetails, ImDetails, NameDetails, PhoneDetails, @@ -388,6 +392,64 @@ public class Tpf.Persona : Folks.Persona, set { this.change_im_addresses.begin (value); } } + private uint _im_interaction_count = 0; + + /** + * A counter for IM interactions (send/receive message) with the persona. + * + * See {@link Folks.InteractionDetails.im_interaction_count} + * + * @since UNRELEASED + */ + public uint im_interaction_count + { + get { return this._im_interaction_count; } + } + + internal DateTime? _last_im_interaction_datetime = null; + + /** + * The latest datetime for IM interactions (send/receive message) with the + * persona. + * + * See {@link Folks.InteractionDetails.last_im_interaction_datetime} + * + * @since UNRELEASED + */ + public DateTime? last_im_interaction_datetime + { + get { return this._last_im_interaction_datetime; } + } + + private uint _call_interaction_count = 0; + + /** + * A counter for call interactions (only successful calls) with the persona. + * + * See {@link Folks.InteractionDetails.call_interaction_count} + * + * @since UNRELEASED + */ + public uint call_interaction_count + { + get { return this._call_interaction_count; } + } + + internal DateTime? _last_call_interaction_datetime = null; + + /** + * The latest datetime for call interactions (only successful calls) with the + * persona. + * + * See {@link Folks.InteractionDetails.last_call_interaction_datetime} + * + * @since UNRELEASED + */ + public DateTime? last_call_interaction_datetime + { + get { return this._last_call_interaction_datetime; } + } + private HashSet _groups = new HashSet (); private Set _groups_ro; @@ -1136,4 +1198,45 @@ public class Tpf.Persona : Folks.Persona, var store = PersonaStore.dup_for_account (account); return store._ensure_persona_for_contact (contact); } + +#if HAVE_ZEITGEIST + internal void _increase_counter (string id, string interaction_type, Event event) + { + var timestamp = (uint) (event.get_timestamp () / 1000); + var converted_datetime = new DateTime.from_unix_utc (timestamp); + var interpretation = event.get_interpretation (); + + /* Only count send/receive for IM interactions */ + if (interaction_type == Zeitgeist.NMO_IMMESSAGE && + (interpretation == Zeitgeist.ZG_SEND_EVENT || + interpretation == Zeitgeist.ZG_RECEIVE_EVENT)) + { + this._im_interaction_count++; + this.notify_property ("im-interaction-count"); + if (this._last_im_interaction_datetime == null || + this._last_im_interaction_datetime.compare (converted_datetime) == -1) + { + this._last_im_interaction_datetime = converted_datetime; + this.notify_property ("last-im-interaction-datetime"); + } + debug ("Persona %s IM interaction details changed:\n - count: %u \n - timestamp: %lld\n", + id, this._im_interaction_count, this._last_im_interaction_datetime.format ("%H %M %S - %d %m %y")); + } + /* Only count successful call for call interactions */ + else if (interaction_type == Zeitgeist.NFO_AUDIO && + interpretation == Zeitgeist.ZG_LEAVE_EVENT) + { + this._call_interaction_count++; + this.notify_property ("call-interaction-count"); + if (this._last_call_interaction_datetime == null || + this._last_call_interaction_datetime.compare (converted_datetime) == -1) + { + this._last_call_interaction_datetime = converted_datetime; + this.notify_property ("last-call-interaction-datetime"); + } + debug ("Persona %s Call interaction details changed:\n - count: %u \n - timestamp: %lld\n", + id, this._call_interaction_count, this._last_call_interaction_datetime.format ("%H %M %S - %d %m %y")); + } + } +#endif } diff --git a/configure.ac b/configure.ac index 1fed8c8..15bd756 100644 --- a/configure.ac +++ b/configure.ac @@ -101,6 +101,24 @@ fi AM_CONDITIONAL([ENABLE_EDS], [test "x$enable_eds_backend" = "xyes"]) + +AC_ARG_ENABLE(zeitgeist, + AC_HELP_STRING([--enable-zeitgeist], + [ build the Zeitgeist]), + enable_zeitgeist=$enableval, + enable_zeitgeist=no ) + +AM_CONDITIONAL([ENABLE_ZEITGEIST], [test "x$enable_zeitgeist" = "xyes"]) + +if test "x$enable_zeitgeist" = "xyes"; then + AC_DEFINE(HAVE_ZEITGEIST, [1], + [Define as 1 if you have the Zeitgeist support]) +else + AC_DEFINE(HAVE_ZEITGEIST, [0], + [Define as 1 if you have the Zeitgeist support]) +fi + + # Automatically check the dependencies for the libsocialweb backend SW_CLIENT_REQUIRED=0.25.20 AC_ARG_ENABLE(libsocialweb-backend, @@ -152,6 +170,7 @@ TRACKER_SPARQL_MAJOR=0.14 TRACKER_SPARQL_REQUIRED=0.13.1 EBOOK_REQUIRED=3.1.5 EDATASERVER_REQUIRED=3.1.5 +ZEITGEIST_REQUIRED=0.3.14 AC_SUBST([TRACKER_SPARQL_MAJOR]) @@ -194,6 +213,10 @@ if test x$enable_eds_backend = xyes; then PKG_CHECK_MODULES([EDATASERVER], [libedataserver-1.2 >= $EDATASERVER_REQUIRED]) fi +if test x$enable_zeitgeist = xyes; then + PKG_CHECK_MODULES([ZEITGEIST], [zeitgeist-1.0 >= $ZEITGEIST_REQUIRED]) +fi + # # Vala building options -- allows tarball builds without installing Vala # @@ -276,6 +299,11 @@ if test "x$enable_vala" = "xyes" ; then if test x$enable_eds_backend = xyes; then VALA_CHECK_PACKAGES([libebook-1.2 libedataserver-1.2 libxml-2.0]) fi + + if test x$enable_zeitgeist = xyes; then + VALA_CHECK_PACKAGES([zeitgeist-1.0]) + fi + fi # this will set HAVE_INTROSPECTION @@ -584,5 +612,6 @@ Configure summary: Tracker backend.............: ${enable_tracker_backend} Libsocialweb backend........: ${have_libsocialweb_backend} E-D-S backend...............: ${enable_eds_backend} + Zeitgeist support...........: ${enable_zeitgeist} " -- 2.7.4