Support Birthdays for the Telepathy backend.
authorTravis Reitter <travis.reitter@collabora.co.uk>
Mon, 10 Oct 2011 02:11:07 +0000 (19:11 -0700)
committerTravis Reitter <travis.reitter@collabora.co.uk>
Tue, 11 Oct 2011 01:13:17 +0000 (18:13 -0700)
NEWS
backends/telepathy/lib/tpf-persona-store.vala
backends/telepathy/lib/tpf-persona.vala
tests/lib/telepathy/contactlist/conn.c
tests/lib/telepathy/contactlist/contact-list-manager.c
tests/telepathy/individual-properties.vala

diff --git a/NEWS b/NEWS
index e4a79cf..c0d5756 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,7 @@ API changes:
 * Implement NameDetails on Tpf.Persona
 * Implement EmailDetails on Tpf.Persona
 * Implement UrlDetails on Tpf.Persona
+* Implement BirthdayDetails on Tpf.Persona
 
 Overview of changes from libfolks 0.6.3.1 to libfolks 0.6.3.2
 =============================================================
index ec033b9..9dec990 100644 (file)
@@ -2175,6 +2175,26 @@ public class Tpf.PersonaStore : Folks.PersonaStore
           (Handle) persona.contact.handle, alias);
     }
 
+  internal async void change_user_birthday (Tpf.Persona persona,
+      DateTime? birthday) throws PersonaStoreError
+    {
+      string birthday_str;
+
+      if (birthday == null)
+        birthday_str = "";
+      else
+        birthday_str = birthday.to_string ();
+
+      var info_set = new HashSet<ContactInfoField> ();
+      string[] values = { birthday_str };
+      string[] parameters = { null };
+
+      var field = new ContactInfoField ("bday", parameters, values);
+      info_set.add (field);
+
+      yield this._change_user_contact_info (persona, info_set);
+    }
+
   internal async void change_user_full_name (Tpf.Persona persona,
       string full_name) throws PersonaStoreError
     {
index d822793..ca099cf 100644 (file)
@@ -30,6 +30,7 @@ using Folks;
 public class Tpf.Persona : Folks.Persona,
     AliasDetails,
     AvatarDetails,
+    BirthdayDetails,
     EmailDetails,
     FavouriteDetails,
     GroupDetails,
@@ -161,6 +162,72 @@ public class Tpf.Persona : Folks.Persona,
     }
 
   /**
+   * {@inheritDoc}
+   *
+   * ContactInfo has no equivalent field, so this is unsupported.
+   *
+   * @since UNRELEASED
+   */
+  [CCode (notify = false)]
+  public string? calendar_event_id
+    {
+      get { return null; } /* unsupported */
+      set { this.change_calendar_event_id.begin (value); } /* not writeable */
+    }
+
+  private DateTime? _birthday = null;
+  /**
+   * {@inheritDoc}
+   *
+   * @since UNRELEASED
+   */
+  [CCode (notify = false)]
+  public DateTime? birthday
+    {
+      get { return this._birthday; }
+      set { this.change_birthday.begin (value); }
+    }
+
+  /**
+   * {@inheritDoc}
+   *
+   * @since UNRELEASED
+   */
+  public async void change_birthday (DateTime? birthday) throws PropertyError
+    {
+      var tpf_store = this.store as Tpf.PersonaStore;
+
+      if (birthday != null && this._birthday != null &&
+          birthday.equal (this._birthday))
+        {
+          return;
+        }
+
+      if (this._is_constructed)
+        {
+          try
+            {
+              yield tpf_store.change_user_birthday (this, birthday);
+            }
+          catch (PersonaStoreError.INVALID_ARGUMENT e1)
+            {
+              throw new PropertyError.NOT_WRITEABLE (e1.message);
+            }
+          catch (PersonaStoreError.STORE_OFFLINE e2)
+            {
+              throw new PropertyError.UNKNOWN_ERROR (e2.message);
+            }
+          catch (PersonaStoreError e3)
+            {
+              throw new PropertyError.UNKNOWN_ERROR (e3.message);
+            }
+        }
+
+      /* the change will be notified when we receive changes to
+       * contact.contact_info */
+    }
+
+  /**
    * The Persona's presence type.
    *
    * See {@link Folks.PresenceDetails.presence_type}.
@@ -639,6 +706,8 @@ public class Tpf.Persona : Folks.Persona,
       var tpf_store = this.store as Tpf.PersonaStore;
       this._writeable_properties = this._always_writeable_properties;
 
+      if ("bday" in tpf_store.supported_fields)
+        this._writeable_properties += "birthday";
       if ("email" in tpf_store.supported_fields)
         this._writeable_properties += "email-addresses";
       if ("fn" in tpf_store.supported_fields)
@@ -651,6 +720,7 @@ public class Tpf.Persona : Folks.Persona,
 
   private void _contact_notify_contact_info ()
     {
+      var new_birthday_str = "";
       var new_full_name = "";
       var new_email_addresses = new HashSet<EmailFieldDetails> (
           (GLib.HashFunc) EmailFieldDetails.hash,
@@ -666,6 +736,10 @@ public class Tpf.Persona : Folks.Persona,
       foreach (var info in contact_info)
         {
           if (info.field_name == "") {}
+          else if (info.field_name == "bday")
+            {
+              new_birthday_str = info.field_value[0];
+            }
           else if (info.field_name == "email")
             {
               foreach (var email_addr in info.field_value)
@@ -699,6 +773,29 @@ public class Tpf.Persona : Folks.Persona,
             }
         }
 
+      if (new_birthday_str != "")
+        {
+          var timeval = TimeVal ();
+          timeval.from_iso8601 (new_birthday_str);
+          /* work around bgo#661397 by forcing our microseconds to zero */
+          timeval.tv_usec = 0;
+          var d = new DateTime.from_timeval_utc (timeval);
+          if (this._birthday == null ||
+              (this._birthday != null && !this._birthday.equal (d.to_utc ())))
+            {
+              this._birthday = d.to_utc ();
+              this.notify_property ("birthday");
+            }
+        }
+      else
+        {
+          if (this._birthday != null)
+            {
+              this._birthday = null;
+              this.notify_property ("birthday");
+            }
+        }
+
       if (!Folks.Internal.equal_sets<EmailFieldDetails> (new_email_addresses,
               this._email_addresses))
         {
index 55b4d4f..2cb60cf 100644 (file)
@@ -411,6 +411,13 @@ conn_contact_info_properties_getter (GObject *object,
           supported_fields = g_ptr_array_new ();
 
           g_ptr_array_add (supported_fields, tp_value_array_build (4,
+              G_TYPE_STRING, "bday",
+              G_TYPE_STRV, NULL,
+              G_TYPE_UINT, 0,
+              G_TYPE_UINT, 1,
+              G_TYPE_INVALID));
+
+          g_ptr_array_add (supported_fields, tp_value_array_build (4,
               G_TYPE_STRING, "email",
               G_TYPE_STRV, NULL,
               G_TYPE_UINT, 0,
index 65e6d18..2cd6ef0 100644 (file)
@@ -518,6 +518,11 @@ receive_contact_lists (gpointer p)
         (const gchar * const *) values);
   }
   {
+    const gchar * values[] = { "1982-01-02T13:57Z", NULL };
+    _insert_contact_field (d->contact_info, "tel", NULL,
+        (const gchar * const *) values);
+  }
+  {
     const gchar * values[] = { "Olivier Crete", NULL };
     _insert_contact_field (d->contact_info, "fn", NULL,
         (const gchar * const *) values);
index 74091dc..d55924d 100644 (file)
@@ -108,6 +108,7 @@ public class IndividualPropertiesTests : Folks.TestCase
                   assert ("groups" in tpf_persona.writeable_properties);
                   /* These are only writeable for the user contact */
                   assert (tpf_persona.is_user);
+                  assert ("birthday" in tpf_persona.writeable_properties);
                   assert (
                       "email-addresses" in tpf_persona.writeable_properties);
                   assert (("full-name" in tpf_persona.writeable_properties));
@@ -152,6 +153,7 @@ public class IndividualPropertiesTests : Folks.TestCase
                   assert ("groups" in tpf_persona.writeable_properties);
                   /* These are only writeable for the user contact */
                   assert (!tpf_persona.is_user);
+                  assert (!("birthday" in tpf_persona.writeable_properties));
                   assert (
                       !("email-addresses" in tpf_persona.writeable_properties));
                   assert (!("full-name" in tpf_persona.writeable_properties));
@@ -355,6 +357,7 @@ public class IndividualPropertiesTests : Folks.TestCase
   public void test_individual_properties_change_contact_info ()
     {
       var main_loop = new GLib.MainLoop (null, false);
+      this._changes_pending.add ("birthday");
       this._changes_pending.add ("email-addresses");
       this._changes_pending.add ("phone-numbers");
       this._changes_pending.add ("full-name");
@@ -392,6 +395,11 @@ public class IndividualPropertiesTests : Folks.TestCase
       var added = changes.get_values ();
       var removed = changes.get_keys ();
 
+      var timeval = TimeVal ();
+      timeval.from_iso8601 ("1929-01-11T00:00Z");
+      /* work around bgo#661397 by forcing our microseconds to zero */
+      timeval.tv_usec = 0;
+      var new_birthday = new DateTime.from_timeval_utc (timeval);
       var new_email_fd = new EmailFieldDetails ("cave@aperturescience.com");
       new_email_fd.set_parameter (AbstractFieldDetails.PARAM_TYPE,
           AbstractFieldDetails.PARAM_TYPE_WORK);
@@ -408,11 +416,24 @@ public class IndividualPropertiesTests : Folks.TestCase
           assert (i != null);
 
           /* Check properties */
+          assert (i.birthday == null || !new_birthday.equal (i.birthday));
           assert (!(new_email_fd in i.email_addresses));
           assert (new_full_name != i.full_name);
           assert (!(new_phone_fd in i.phone_numbers));
           assert (!(new_url_fd in i.urls));
 
+          i.notify["birthday"].connect ((s, p) =>
+              {
+                /* we can't re-use i here due to Vala's implementation */
+                var ind = (Individual) s;
+
+                if (ind.birthday != null && new_birthday != null &&
+                  ind.birthday.equal (new_birthday))
+                  {
+                    this._changes_pending.remove ("birthday");
+                  }
+              });
+
           i.notify["email-addresses"].connect ((s, p) =>
               {
                 /* we can't re-use i here due to Vala's implementation */
@@ -490,6 +511,18 @@ public class IndividualPropertiesTests : Folks.TestCase
             uncaught_errors++;
           try
             {
+              yield ((Tpf.Persona) persona).change_birthday (new_birthday);
+            }
+          catch (PropertyError e_birthday)
+            {
+              if (!i.is_user)
+                uncaught_errors--;
+            }
+
+          if (!i.is_user)
+            uncaught_errors++;
+          try
+            {
               yield ((Tpf.Persona) persona).change_email_addresses (emails);
             }
           catch (PropertyError e0)