[folks] ofono: Added new Ofono backend to read contacts from SIM cards.



commit e82b632f96ea49bd55f28228de4729bc64fb57b9
Author: Jeremy Whiting <jpwhiting kde org>
Date:   Fri Sep 28 15:13:39 2012 -0600

    ofono: Added new Ofono backend to read contacts from SIM cards.

 backends/Makefile.am                      |    4 +
 backends/ofono/Makefile.am                |   73 ++++++
 backends/ofono/ofono-backend-factory.vala |   71 ++++++
 backends/ofono/ofono-backend.vala         |  338 +++++++++++++++++++++++++++++
 backends/ofono/ofono-persona-store.vala   |  291 +++++++++++++++++++++++++
 backends/ofono/ofono-persona.vala         |  230 ++++++++++++++++++++
 backends/ofono/org-ofono.vala             |   59 +++++
 configure.ac                              |   33 +++
 8 files changed, 1099 insertions(+), 0 deletions(-)
---
diff --git a/backends/Makefile.am b/backends/Makefile.am
index a81ed00..ec9659a 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -18,6 +18,10 @@ if ENABLE_EDS
 SUBDIRS += eds
 endif
 
+if ENABLE_OFONO
+SUBDIRS += ofono
+endif
+
 DIST_SUBDIRS = \
 	telepathy \
 	key-file \
diff --git a/backends/ofono/Makefile.am b/backends/ofono/Makefile.am
new file mode 100644
index 0000000..966e5f4
--- /dev/null
+++ b/backends/ofono/Makefile.am
@@ -0,0 +1,73 @@
+BACKEND_NAME = "ofono"
+
+backenddir = $(BACKEND_DIR)/ofono
+backend_LTLIBRARIES = ofono.la
+
+ofono_la_SOURCES = \
+	ofono-backend.vala \
+	ofono-backend-factory.vala \
+	ofono-persona.vala \
+	ofono-persona-store.vala \
+	org-ofono.vala \
+	$(NULL)
+
+ofono_la_VALAFLAGS = \
+	$(AM_VALAFLAGS) \
+	$(ERROR_VALAFLAGS) \
+	--vapidir=. \
+	--vapidir=$(top_srcdir)/folks \
+	--pkg folks \
+	--pkg folks-internal \
+	--pkg gee-1.0 \
+	--pkg gio-2.0 \
+	--pkg gobject-2.0 \
+	--pkg libebook-1.2 \
+	--pkg libedataserver-1.2 \
+	$(NULL)
+
+# FIXME: libedataserver-1.2 doesn't need to be explicitly mentioned
+# once we depend on libebook version that includes libedataserver-1.2
+# in libebook-1.2.deps.
+
+ofono_la_CPPFLAGS = \
+	-I$(top_srcdir)/folks \
+	-include $(CONFIG_HEADER) \
+	-DPACKAGE_DATADIR=\"$(pkgdatadir)\" \
+	-DBACKEND_NAME=\"$(BACKEND_NAME)\" \
+	-DG_LOG_DOMAIN=\"$(BACKEND_NAME)\" \
+	$(NULL)
+
+ofono_la_CFLAGS = \
+	$(AM_CFLAGS) \
+	$(CODE_COVERAGE_CFLAGS) \
+	$(GIO_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	$(GEE_CFLAGS) \
+	$(EBOOK_CFLAGS) \
+	$(NULL)
+
+ofono_la_LIBADD = \
+	$(AM_LIBADD) \
+	$(GIO_LIBS) \
+	$(GLIB_LIBS) \
+	$(GEE_LIBS) \
+	$(EBOOK_LIBS) \
+	$(top_builddir)/folks/libfolks.la \
+	$(top_builddir)/folks/libfolks-internal.la \
+	$(NULL)
+
+ofono_la_LDFLAGS = \
+	$(AM_LDFLAGS) \
+	$(CODE_COVERAGE_LDFLAGS) \
+	-shared \
+	-fPIC \
+	-module \
+	-avoid-version \
+	$(NULL)
+
+GITIGNOREFILES = \
+	$(ofono_la_SOURCES:.vala=.c) \
+	ofono_la_vala.stamp \
+	$(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/backends/ofono/ofono-backend-factory.vala b/backends/ofono/ofono-backend-factory.vala
new file mode 100644
index 0000000..5a8aa33
--- /dev/null
+++ b/backends/ofono/ofono-backend-factory.vala
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2009 Zeeshan Ali (Khattak) <zeeshanak gnome org>.
+ * Copyright (C) 2009 Nokia Corporation.
+ * Copyright (C) 2012 Collabora Ltd.
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *          Jeremy Whiting <jeremy whiting collabora co uk>
+ *
+ * Based on kf-backend-factory.vala by:
+ *          Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *          Travis Reitter <travis reitter collabora co uk>
+ *          Philip Withnall <philip withnall collabora co uk>
+ */
+
+using Folks;
+using Folks.Backends.Ofono;
+
+private BackendFactory? _backend_factory = null;
+
+/**
+ * The backend module entry point.
+ *
+ * @param backend_store the { link BackendStore} to use in this factory.
+ *
+ * @since UNRELEASED
+ */
+public void module_init (BackendStore backend_store)
+{
+  _backend_factory = new BackendFactory (backend_store);
+}
+
+/**
+ * The backend module exit point.
+ *
+ * @param backend_store the { link BackendStore} used in this factory.
+ *
+ * @since UNRELEASED
+ */
+public void module_finalize (BackendStore backend_store)
+{
+  _backend_factory = null;
+}
+
+/**
+ * A backend factory to create a single { link Backend}.
+ *
+ * @since UNRELEASED
+ */
+public class Folks.Backends.Ofono.BackendFactory : Object
+{
+  /**
+   * { inheritDoc}
+   */
+  public BackendFactory (BackendStore backend_store)
+    {
+      backend_store.add_backend (new Backend ());
+    }
+}
diff --git a/backends/ofono/ofono-backend.vala b/backends/ofono/ofono-backend.vala
new file mode 100644
index 0000000..404b098
--- /dev/null
+++ b/backends/ofono/ofono-backend.vala
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2012 Collabora Ltd.
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *       Jeremy Whiting <jeremy whiting collabora co uk>
+ *
+ * Based on kf-backend.vala by:
+ *       Philip Withnall <philip withnall collabora co uk>
+ */
+
+using GLib;
+using Gee;
+using Folks;
+using Folks.Backends.Ofono;
+using org.ofono;
+
+extern const string BACKEND_NAME;
+
+/**
+ * A backend which loads { link Persona}s from Modem
+ * devices using the Ofono Phonebook D-Bus API and presents them
+ * using one { link PersonaStore} per device.
+ *
+ * @since UNRELEASED
+ */
+public class Folks.Backends.Ofono.Backend : Folks.Backend
+{
+  private bool _is_prepared = false;
+  private bool _prepare_pending = false; /* used for unprepare() too */
+  private bool _is_quiescent = false;
+  private HashMap<string, PersonaStore> _persona_stores;
+  private Map<string, PersonaStore> _persona_stores_ro;
+  private ModemProperties[] _modems;
+
+  /**
+   * { inheritDoc}
+   */
+  public override bool is_prepared
+    {
+      get { return this._is_prepared; }
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override bool is_quiescent
+    {
+      get { return this._is_quiescent; }
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override string name { get { return BACKEND_NAME; } }
+
+  /**
+   * { inheritDoc}
+   */
+  public override Map<string, PersonaStore> persona_stores
+    {
+      get { return this._persona_stores_ro; }
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override void disable_persona_store (Folks.PersonaStore store)
+    {
+      if (this._persona_stores.has_key (store.id))
+        {
+          this._store_removed_cb (store);
+        }
+    }
+    
+  /**
+   * { inheritDoc}
+   */
+  public override void enable_persona_store (Folks.PersonaStore store)
+    {
+      if (this._persona_stores.has_key (store.id) == false)
+        {
+          this._add_store ((Ofono.PersonaStore) store);
+        }
+    }
+    
+  /**
+   * { inheritDoc}
+   */
+  public override void set_persona_stores (Set<string>? storeids)
+    {
+      bool added_stores = false;
+      PersonaStore[] removed_stores = {};
+      
+      /* First handle adding any missing persona stores. */
+      foreach (ModemProperties modem in this._modems)
+        {
+          if (modem.path in storeids && 
+              this._persona_stores.has_key (modem.path) == false)
+            {
+              string alias = this._modem_alias (modem.properties);
+              PersonaStore store = new Ofono.PersonaStore (modem.path, alias);
+              this._add_store (store, false);
+              added_stores = true;
+            }
+        }
+      
+      foreach (PersonaStore store in this._persona_stores.values)
+        {
+          if (!storeids.contains (store.id))
+            {
+              removed_stores += store;
+            }
+        }
+      
+      for (int i = 0; i < removed_stores.length; ++i)
+        {
+          this._remove_store ((Ofono.PersonaStore) removed_stores[i], false);
+        }
+      
+      /* Finally, if anything changed, emit the persona-stores notification. */
+      if (added_stores || removed_stores.length > 0)
+        {
+          this.notify_property ("persona-stores");
+        }
+    }
+  
+  /**
+   * { inheritDoc}
+   */
+  public Backend ()
+    {
+      Object ();
+    }
+
+  construct
+    {
+      this._persona_stores = new HashMap<string, PersonaStore> ();
+      this._persona_stores_ro = this._persona_stores.read_only_view;
+    }
+
+  private void _add_modem (ObjectPath path, string alias)
+    {
+      PersonaStore store =
+          new Ofono.PersonaStore (path, alias);
+
+      this._add_store (store);
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override async void prepare () throws GLib.Error
+    {
+      Internal.profiling_start ("preparing Ofono.Backend");
+
+      if (this._is_prepared || this._prepare_pending)
+        {
+          return;
+        }
+
+      try
+        {
+          this._prepare_pending = true;
+
+          /* New modem devices can be caught in notifications */
+          Manager manager = yield Bus.get_proxy (BusType.SYSTEM,
+                                                       "org.ofono",
+                                                       "/");
+          manager.ModemAdded.connect (this._modem_added);
+          manager.ModemRemoved.connect (this._modem_removed);
+          
+          this._modems = manager.GetModems ();
+
+          foreach (ModemProperties modem in this._modems)
+            {
+              this._modem_added (modem.path, modem.properties);
+            }
+
+          this.freeze_notify ();
+          this._is_prepared = true;
+          this.notify_property ("is-prepared");
+
+          this._is_quiescent = true;
+          this.notify_property ("is-quiescent");
+          this.thaw_notify ();
+        }
+      finally
+        {
+          this._prepare_pending = false;
+        }
+
+      Internal.profiling_end ("preparing Ofono.Backend");
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override async void unprepare () throws GLib.Error
+    {
+      if (!this._is_prepared || this._prepare_pending == true)
+        {
+          return;
+        }
+
+      try
+        {
+          this._prepare_pending = true;
+
+          foreach (var persona_store in this._persona_stores.values)
+            {
+              this.persona_store_removed (persona_store);
+            }
+
+          this.freeze_notify ();
+          this._persona_stores.clear ();
+          this.notify_property ("persona-stores");
+
+          this._is_quiescent = false;
+          this.notify_property ("is-quiescent");
+
+          this._is_prepared = false;
+          this.notify_property ("is-prepared");
+          this.thaw_notify ();
+        }
+      finally
+        {
+          this._prepare_pending = false;
+        }
+    }
+
+  /**
+   * Utility function to extract a modem's alias from its properties.
+   *
+   * @param properties, the properties of the modem.
+   * @returns the alias to use for this modem.
+   */
+  private string _modem_alias (HashTable<string, Variant> properties)
+    {
+      string alias = "";
+      
+      /* Name is more user friendly than Manufacturer, but both are optional,
+       * so use Name if it's there, otherwise Manufacturer, otherwise leave
+       * it blank. */
+      Variant? name_variant = properties.get ("Name");
+      Variant? manufacturer_variant = properties.get ("Manufacturer");
+      if (name_variant != null)
+        {
+          alias = name_variant.get_string ();
+        }
+      else if (manufacturer_variant != null)
+        {
+          alias = manufacturer_variant.get_string ();
+        }
+      return alias;
+    }
+  
+  private void _modem_added (ObjectPath path, HashTable<string, Variant> properties)
+    {
+      Variant? features_variant = properties.get ("Features");
+      if (features_variant != null)
+        {
+          string alias = this._modem_alias (properties);
+          string[] features = features_variant.get_strv ();
+          
+          foreach (string feature in features)
+            {
+              if (feature == "sim")
+                {
+                  /* This modem has a sim card, so add a persona store for it */
+                  this._add_modem (path, alias);
+                  break;
+                }
+            }
+        }
+    }
+
+  /**
+   * Utility function to add a persona store.
+   *
+   * @param store the store to add.
+   * @param notify whether or not to emit notification signals.
+   */
+  private void _add_store (PersonaStore store, bool notify = true)
+    {
+      this._persona_stores.set (store.id, store);
+      store.removed.connect (this._store_removed_cb);
+      this.persona_store_added (store);
+      if (notify)
+        {
+          this.notify_property ("persona-stores");
+        }
+    }
+    
+  /**
+   * Utility function to remove a persona store.
+   *
+   * @param store the store to remove.
+   * @param notify whether or not to emit notification signals.
+   */
+  private void _remove_store (PersonaStore store, bool notify = true)
+    {
+      store.removed.disconnect (this._store_removed_cb);
+      this._persona_stores.unset (store.id);
+      this.persona_store_removed (store);
+    
+      if (notify)
+        {
+          this.notify_property ("persona-stores");
+        }
+    }
+    
+
+  private void _modem_removed (ObjectPath path)
+    {
+      if (this._persona_stores.has_key (path))
+        {
+          this._store_removed_cb (this._persona_stores.get (path));
+        }
+    }
+    
+  private void _store_removed_cb (Folks.PersonaStore store)
+    {
+      this._remove_store ((Ofono.PersonaStore) store);
+    }
+}
diff --git a/backends/ofono/ofono-persona-store.vala b/backends/ofono/ofono-persona-store.vala
new file mode 100644
index 0000000..05a2dad
--- /dev/null
+++ b/backends/ofono/ofono-persona-store.vala
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2012 Collabora Ltd.
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *          Jeremy Whiting <jeremy whiting collabora co uk>
+ *
+ * Based on kf-persona-store.vala by:
+ *       Travis Reitter <travis reitter collabora co uk>
+ *       Philip Withnall <philip withnall collabora co uk>
+ */
+
+using GLib;
+using Gee;
+using Folks;
+using Folks.Backends.Ofono;
+
+/**
+ * A persona store which is associated with a single Ofono device. It will 
+ * create a { link Persona} for each contact on the SIM card phonebook.
+ *
+ * @since UNRELEASED
+ */
+public class Folks.Backends.Ofono.PersonaStore : Folks.PersonaStore
+{
+  private HashMap<string, Persona> _personas;
+  private Map<string, Persona> _personas_ro;
+  private bool _is_prepared = false;
+  private bool _prepare_pending = false;
+  private bool _is_quiescent = false;
+
+  private static string[] _always_writeable_properties = {};
+  private ObjectPath? _path = null;
+
+  private org.ofono.Phonebook? _ofono_phonebook = null;
+
+  /**
+   * { inheritDoc}
+   */
+  public override string type_id { get { return BACKEND_NAME; } }
+
+  /**
+   * { inheritDoc}
+   */
+  public override MaybeBool can_add_personas
+    {
+      get { return MaybeBool.FALSE; }
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override MaybeBool can_alias_personas
+    {
+      get { return MaybeBool.FALSE; }
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override MaybeBool can_group_personas
+    {
+      get { return MaybeBool.FALSE; }
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override MaybeBool can_remove_personas
+    {
+      get { return MaybeBool.FALSE; }
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override bool is_prepared
+    {
+      get { return this._is_prepared; }
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override bool is_quiescent
+    {
+      get { return this._is_quiescent; }
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override string[] always_writeable_properties
+    {
+      get { return Ofono.PersonaStore._always_writeable_properties; }
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override Map<string, Persona> personas
+    {
+      get { return this._personas_ro; }
+    }
+
+  /**
+   * Create a new PersonaStore.
+   *
+   * Create a new persona store to expose the { link Persona}s provided by the
+   * modem with the given address.
+   *
+   * @param path the D-Bus object path of this modem
+   * @param alias the name this modem should display to users
+   *
+   * @since UNRELEASED
+   */
+  public PersonaStore (ObjectPath path, string alias)
+    {
+      Object (id: path,
+              display_name: alias);
+
+      this.trust_level = PersonaStoreTrust.FULL;
+      this._personas = new HashMap<string, Persona> ();
+      this._personas_ro = this._personas.read_only_view;
+      this._path = path;
+    }
+
+  private void _property_changed (string property, Variant value)
+    {
+      if (property == "Present" && value.get_boolean () == false)
+        {
+          this._remove_self ();
+        }
+    }
+    
+  private void _remove_self ()
+    {
+      /* Marshal the personas from a Collection to a Set. */
+      var removed_personas = new HashSet<Persona> ();
+      var iter = this._personas.map_iterator ();
+      
+      while (iter.next () == true)
+      {
+        removed_personas.add (iter.get_value ());
+      }
+      
+      this._emit_personas_changed (null, removed_personas);
+      this.removed ();
+    }
+    
+  private string[] _split_all_vcards (string all_vcards)
+    {
+      /* Ofono vcards are in vcard 3.0 format and can include the following:
+       * FN, CATEGORIES, EMAIL and IMPP fields. */
+      string[] lines = all_vcards.split ("\n");
+      string[] vcards = {};
+      string vcard = "";
+
+      foreach (string line in lines)
+        {
+          vcard += line;
+          vcard += "\n";
+
+          if (line.strip () == "END:VCARD")
+            {
+              vcards += vcard;
+              vcard = "";
+            }
+        }
+
+      return vcards;
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override async void prepare () throws IOError, DBusError
+    {
+      Internal.profiling_start ("preparing Ofono.PersonaStore (ID: %s)",
+          this.id);
+
+      if (this._is_prepared || this._prepare_pending)
+        {
+          return;
+        }
+
+      try
+        {
+          this._prepare_pending = true;
+          this._ofono_phonebook = yield Bus.get_proxy (BusType.SYSTEM,
+                                                             "org.ofono",
+                                                             this._path);
+
+          org.ofono.SimManager sim_manager = yield Bus.get_proxy (BusType.SYSTEM,
+                                                                        "org.ofono",
+                                                                        this._path);
+          sim_manager.PropertyChanged.connect (this._property_changed);
+
+          string all_vcards = this._ofono_phonebook.Import ();
+
+          string[] vcards = this._split_all_vcards (all_vcards);
+
+          HashSet<Persona> added_personas = new HashSet<Persona> ();
+
+          foreach (string vcard in vcards)
+            {
+              Persona persona = new Persona (vcard, this);
+              this._personas.set (persona.iid, persona);
+              added_personas.add (persona);
+            }
+
+          if (this._personas.size > 0)
+            {
+              this._emit_personas_changed (added_personas, null);
+            }
+        }
+      catch (GLib.DBusError e)
+        {
+          warning ("DBus Error has occurred when fetching ofono phonebook, %s", e.message);
+          this._remove_self ();
+        }
+      catch (GLib.IOError e)
+        {
+          warning ("IO Error has occurred when fetching ofono phonebook, %s", e.message);
+          this._remove_self ();
+        }
+      finally
+        {
+          this._is_prepared = true;
+          this.notify_property ("is-prepared");
+
+          /* We've finished loading all the personas we know about */
+          this._is_quiescent = true;
+          this.notify_property ("is-quiescent");
+
+          this._prepare_pending = false;
+        }
+
+      Internal.profiling_end ("preparing Ofono.PersonaStore (ID: %s)", this.id);
+    }
+
+  /**
+   * Remove a { link Persona} from the PersonaStore.
+   *
+   * See { link Folks.PersonaStore.remove_persona}.
+   *
+   * @throws Folks.PersonaStoreError.READ_ONLY every time since the
+   * Ofono backend is read-only.
+   *
+   * @param persona the { link Persona} to remove.
+   *
+   * @since UNRELEASED
+   */
+  public override async void remove_persona (Folks.Persona persona)
+      throws Folks.PersonaStoreError
+    {
+      throw new PersonaStoreError.READ_ONLY (
+          "Personas cannot be removed from this store.");
+    }
+
+  /**
+   * Add a new { link Persona} to the PersonaStore.
+   *
+   * See { link Folks.PersonaStore.add_persona_from_details}.
+   *
+   * @throws Folks.PersonaStoreError.READ_ONLY every time since the
+   * Ofono backend is read-only.
+   *
+   * @param details the details of the { link Persona} to add.
+   *
+   * @since UNRELEASED
+   */
+  public override async Folks.Persona? add_persona_from_details (
+      HashTable<string, Value?> details) throws Folks.PersonaStoreError
+    {
+      throw new PersonaStoreError.READ_ONLY (
+          "Personas cannot be added to this store.");
+    }
+}
diff --git a/backends/ofono/ofono-persona.vala b/backends/ofono/ofono-persona.vala
new file mode 100644
index 0000000..79a91ed
--- /dev/null
+++ b/backends/ofono/ofono-persona.vala
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2012 Collabora Ltd.
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *          Jeremy Whiting <jeremy whiting collabora co uk>
+ *
+ * Based on kf-persona.vala by:
+ *       Philip Withnall <philip withnall collabora co uk>
+ */
+
+using GLib;
+using Gee;
+using Folks;
+using Folks.Backends.Ofono;
+
+/**
+ * A persona subclass which represents a single persona from a simple key file.
+ *
+ * @since UNRELEASED
+ */
+public class Folks.Backends.Ofono.Persona : Folks.Persona,
+    EmailDetails,
+    NameDetails,
+    PhoneDetails
+{
+  private StructuredName? _structured_name = null;
+  private string _full_name = "";
+  private string _nickname = "";
+  private HashSet<PhoneFieldDetails> _phone_numbers;
+  private Set<PhoneFieldDetails> _phone_numbers_ro;
+  private HashSet<EmailFieldDetails> _email_addresses;
+  private Set<EmailFieldDetails> _email_addresses_ro;
+  
+  private const string[] _linkable_properties =
+    {
+      "phone-numbers",
+      "email-addresses"
+    };
+  private static string[] _writeable_properties = {};
+
+  /**
+   * { inheritDoc}
+   */
+  public override string[] linkable_properties
+    {
+      get { return Ofono.Persona._linkable_properties; }
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  public override string[] writeable_properties
+    {
+      get { return Ofono.Persona._writeable_properties; }
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  [CCode (notify = false)]
+  public Set<PhoneFieldDetails> phone_numbers
+    {
+      get { return this._phone_numbers_ro; }
+      set { this.change_phone_numbers.begin (value); } /* not writeable */
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  [CCode (notify = false)]
+  public StructuredName? structured_name
+    {
+      get { return this._structured_name; }
+      set { this.change_structured_name.begin (value); } /* not writeable */
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  [CCode (notify = false)]
+  public string full_name
+    {
+      get { return this._full_name; }
+      set { this.change_full_name.begin (value); } /* not writeable */
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  [CCode (notify = false)]
+  public string nickname
+    {
+      get { return this._nickname; }
+      set { this.change_nickname.begin (value); } /* not writeable */
+    }
+
+  /**
+   * { inheritDoc}
+   */
+  [CCode (notify = false)]
+  public Set<EmailFieldDetails> email_addresses
+    {
+      get { return this._email_addresses_ro; }
+      set { this.change_email_addresses.begin (value); } /* not writeable */
+    }
+
+  /**
+   * Create a new persona.
+   *
+   * Create a new persona for the given vCard contents.
+   *
+   * @param vcard the vCard data to use for this { link Persona}.
+   * @param store the { link PersonaStore} this { link Persona} belongs to.
+   *
+   * @since UNRELEASED
+   */
+  public Persona (string vcard, Folks.PersonaStore store)
+    {
+      var iid = Checksum.compute_for_string (ChecksumType.SHA1, vcard);
+      var uid = Folks.Persona.build_uid ("ofono", store.id, iid);
+
+      Object (display_id: uid,
+              iid: iid,
+              uid: uid,
+              store: store,
+              is_user: false);
+      this._set_vcard (vcard);
+    }
+
+  construct
+    {
+      debug ("Adding Ofono Persona '%s' (IID '%s', group '%s')", this.uid,
+          this.iid, this.display_id);
+      
+      this._phone_numbers = new HashSet<PhoneFieldDetails> ();
+      this._phone_numbers_ro = this._phone_numbers.read_only_view;
+
+      this._email_addresses = new HashSet<EmailFieldDetails> ();
+      this._email_addresses_ro = this._email_addresses.read_only_view;
+    }
+
+  private void _set_vcard (string vcard)
+    {
+      E.VCard card = new E.VCard.from_string (vcard);
+      
+      E.VCardAttribute? attribute = card.get_attribute ("TEL");
+      if (attribute != null)
+        {
+          this._phone_numbers.add (new PhoneFieldDetails (attribute.get_value_decoded ().str) );
+        }
+
+      attribute = card.get_attribute ("FN");
+      if (attribute != null)
+        {
+          this._full_name = attribute.get_value_decoded ().str;
+        }
+      
+      attribute = card.get_attribute ("NICKNAME");
+      if (attribute != null)
+        {
+          this._nickname = attribute.get_value_decoded ().str;
+        }
+        
+      attribute = card.get_attribute ("N");
+      if (attribute != null)
+        {
+          unowned GLib.List<StringBuilder> values = attribute.get_values_decoded ();
+          if (values.length () >= 5)
+            {
+              this._structured_name = new StructuredName (values.nth_data (0).str,
+                  values.nth_data (1).str,
+                  values.nth_data (2).str,
+                  values.nth_data (3).str,
+                  values.nth_data (4).str);
+            }
+          else
+            {
+              warning ("Expected 5 components to N value of vcard, got %u", values.length ());
+            }
+        }
+        
+      attribute = card.get_attribute ("EMAIL");
+      if (attribute != null)
+        {
+          this._email_addresses.add (new EmailFieldDetails (attribute.get_value_decoded ().str) );
+        }
+    }
+    
+  /**
+   * { inheritDoc}
+   */
+  public override void linkable_property_to_links (string prop_name,
+      Folks.Persona.LinkablePropertyCallback callback)
+    {
+      if (prop_name == "phone-numbers")
+        {
+          foreach (var phone_number in this._phone_numbers)
+            {
+                if (phone_number.value != null)
+                    callback (phone_number.value);
+            }
+        }
+      else if (prop_name == "email-addresses")
+        {
+          foreach (var email_address in this._email_addresses)
+            {
+                if (email_address.value != null)
+                    callback (email_address.value);
+            }
+        }
+      else
+        {
+          /* Chain up */
+          base.linkable_property_to_links (prop_name, callback);
+        }
+    }
+}
diff --git a/backends/ofono/org-ofono.vala b/backends/ofono/org-ofono.vala
new file mode 100644
index 0000000..cc069bd
--- /dev/null
+++ b/backends/ofono/org-ofono.vala
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 Collabora Ltd.
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *          Jeremy Whiting <jeremy whiting collabora co uk>
+ */
+
+using GLib;
+
+namespace org
+  {
+    namespace ofono
+      {
+        public struct ModemProperties
+          {
+            ObjectPath path;
+            HashTable<string, Variant> properties;
+          }
+        
+        [DBus (name = "org.ofono.Manager")]
+        public interface Manager : Object
+          {
+            [DBus (name = "GetModems")]
+            public abstract ModemProperties[] GetModems() throws DBusError, IOError;
+            
+            public signal void ModemAdded (ObjectPath path, HashTable<string, Variant> properties);
+            public signal void ModemRemoved (ObjectPath path);
+          }
+
+        [DBus (name = "org.ofono.Phonebook")]
+        public interface Phonebook : Object
+          {
+            [DBus (name = "Import")]
+            public abstract string Import() throws DBusError, IOError;
+          }
+        
+        [DBus (name = "org.ofono.SimManager")]
+        public interface SimManager : Object
+          {
+              [DBus (name = "GetProperties")]
+              public abstract HashTable<string, Variant> GetProperties() throws DBusError, IOError;
+              
+              public signal void PropertyChanged (string property, Variant value);
+          }
+      }
+  }
diff --git a/configure.ac b/configure.ac
index 8337642..9c460c3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -100,6 +100,20 @@ AS_IF([test "x$enable_eds_backend" = "xyes"], [
 
 AM_CONDITIONAL([ENABLE_EDS], [test "x$enable_eds_backend" = "xyes"])
 
+AC_ARG_ENABLE(ofono-backend,
+	AC_HELP_STRING([--enable-ofono-backend],
+		       [ build the ofono backend]),
+	enable_ofono_backend=$enableval,
+	enable_ofono_backend=yes )
+
+AS_IF([test "x$enable_ofono_backend" = "xyes"], [
+        AC_DEFINE(HAVE_OFONO, [1], [Define as 1 if you have the ofono backend])
+], [
+        AC_DEFINE(HAVE_OFONO, [0], [Define as 1 if you have the ofono backend])
+])
+
+AM_CONDITIONAL([ENABLE_OFONO], [test "x$enable_ofono_backend" = "xyes"])
+
 AC_ARG_ENABLE(telepathy-backend,
         AC_HELP_STRING([--enable-telepathy-backend],
                        [ build the Telepathy backend]),
@@ -211,6 +225,10 @@ AS_IF([test x$enable_eds_backend = xyes], [
         PKG_CHECK_MODULES([EDATASERVER], [libedataserver-1.2 >= $EDATASERVER_REQUIRED])
 ])
 
+AS_IF([test x$enable_ofono_backend = xyes], [
+        PKG_CHECK_MODULES([EBOOK], [libebook-1.2 >= $EBOOK_REQUIRED])
+])
+
 #
 # Vala building options -- allows tarball builds without installing Vala
 #
@@ -297,6 +315,10 @@ AS_IF([test "x$enable_vala" = "xyes"], [
         AS_IF([test x$enable_eds_backend = xyes], [
           VALA_CHECK_PACKAGES([libebook-1.2 libedataserver-1.2 libxml-2.0])
         ])
+
+        AS_IF([test x$enable_ofono_backend = xyes], [
+          VALA_CHECK_PACKAGES([libebook-1.2])
+        ])
 ])
 
 # this will set HAVE_INTROSPECTION
@@ -341,6 +363,11 @@ AS_IF([test x$enable_eds_backend = xyes], [
   AC_SUBST([BACKEND_EDS])
 ])
 
+if test x$enable_ofono_backend = xyes; then
+  BACKEND_OFONO='$(top_builddir)/backends/ofono/.libs/ofono.so'
+  AC_SUBST([BACKEND_OFONO])
+fi
+
 # All of the backend libraries in our tree; to be used by the tests
 BACKEND_UNINST_PATH='$(BACKEND_KF):$(BACKEND_TP)'
 AS_IF([test x$have_libsocialweb_backend = xyes], [
@@ -355,6 +382,10 @@ AS_IF([test x$enable_eds_backend = xyes], [
   EDS_BACKEND_UNINST_PATH='$(BACKEND_EDS)'
   BACKEND_UNINST_PATH="$BACKEND_UNINST_PATH:$EDS_BACKEND_UNINST_PATH"
 ])
+AS_IF([test x$enable_ofono_backend = xyes], 
+  OFONO_BACKEND_UNINST_PATH='$(BACKEND_OFONO)'
+  BACKEND_UNINST_PATH="$BACKEND_UNINST_PATH:$OFONO_BACKEND_UNINST_PATH"
+])
 AC_SUBST([BACKEND_UNINST_PATH])
 
 # -----------------------------------------------------------
@@ -605,6 +636,7 @@ AC_CONFIG_FILES([
     backends/tracker/lib/Makefile
     backends/eds/Makefile
     backends/eds/lib/Makefile
+    backends/ofono/Makefile
     folks/Makefile
     docs/Makefile
     po/Makefile.in
@@ -649,5 +681,6 @@ Configure summary:
         Tracker backend.............:  ${enable_tracker_backend}
         Libsocialweb backend........:  ${have_libsocialweb_backend}
         E-D-S backend...............:  ${enable_eds_backend}
+        Ofono backend...............:  ${enable_ofono_backend}
         Build tests.................:  ${enable_tests}
 "



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]