* - PersonaStore.detail_key (PersonaDetail.ROLES)
* - PersonaStore.detail_key (PersonaDetail.STRUCTURED_NAME)
* - PersonaStore.detail_key (PersonaDetail.LOCAL_IDS)
+ * - PersonaStore.detail_key (PersonaDetail.LOCATION)
* - PersonaStore.detail_key (PersonaDetail.WEB_SERVICE_ADDRESSES)
* - PersonaStore.detail_key (PersonaDetail.NOTES)
* - PersonaStore.detail_key (PersonaDetail.URLS)
Set<string> local_ids = (Set<string>) v.get_object ();
this._set_contact_local_ids (contact, local_ids);
}
+ else if (k == Folks.PersonaStore.detail_key (PersonaDetail.LOCATION))
+ {
+ var location = (Location?) v.get_object ();
+ this._set_contact_location (contact, location);
+ }
else if (k == Folks.PersonaStore.detail_key
(PersonaDetail.WEB_SERVICE_ADDRESSES))
{
case ContactField.FAMILY_NAME:
case ContactField.NAME:
return PersonaDetail.STRUCTURED_NAME;
+ case ContactField.GEO:
+ return PersonaDetail.LOCATION;
case ContactField.NICKNAME:
return PersonaDetail.NICKNAME;
case ContactField.EMAIL_1:
case ContactField.LIST_SHOW_ADDRESSES:
case ContactField.ANNIVERSARY:
case ContactField.X509_CERT:
- case ContactField.GEO:
default:
debug ("Unsupported/Unknown EDS field name '%s'.", eds_field_name);
return PersonaDetail.INVALID;
}
}
+ internal async void _set_location (Edsf.Persona persona,
+ Location? location) throws PropertyError
+ {
+ if (!("location" in this._always_writeable_properties))
+ {
+ throw new PropertyError.NOT_WRITEABLE (
+ _("Location is not writeable on this contact."));
+ }
+
+ this._set_contact_location (persona.contact, location);
+ yield this._commit_modified_property (persona, "location");
+ }
+
+ private void _set_contact_location (E.Contact contact, Location? location)
+ {
+ if (location == null)
+ {
+ this._remove_attribute (contact, "GEO");
+ }
+ else
+ {
+ E.ContactGeo geo = new E.ContactGeo ();
+ geo.latitude = location.latitude;
+ geo.longitude = location.longitude;
+ contact.set (ContactField.GEO, geo);
+ }
+ }
+
private void _contacts_added_cb (GLib.List<E.Contact> contacts)
{
HashSet<Persona> added_personas;
GroupDetails,
ImDetails,
LocalIdDetails,
+ LocationDetails,
NameDetails,
NoteDetails,
PhoneDetails,
yield ((Edsf.PersonaStore) this.store)._set_local_ids (this, local_ids);
}
+ private Location? _location = null;
+ /**
+ * {@inheritDoc}
+ *
+ * @since UNRELEASED
+ */
+ [CCode (notify = false)]
+ public Location? location
+ {
+ get { return this._location; }
+ set { this.change_location.begin (value); }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since UNRELEASED
+ */
+ public async void change_location (Location? location) throws PropertyError
+ {
+ yield ((Edsf.PersonaStore) this.store)._set_location (this, location);
+ }
+
private HashSet<PostalAddressFieldDetails>? _postal_addresses = null;
private Set<PostalAddressFieldDetails>? _postal_addresses_ro = null;
this._postal_addresses_ro = this._postal_addresses.read_only_view;
this._local_ids = new HashSet<string> ();
this._local_ids_ro = this._local_ids.read_only_view;
+ this._location = null;
this._web_service_addresses =
new HashMultiMap<string, WebServiceFieldDetails> (
null, null, AbstractFieldDetails<string>.hash_static,
this._update_groups (false);
this._update_notes (false);
this._update_local_ids ();
+ this._update_location ();
this._update_web_services_addresses ();
this._update_gender ();
this._update_birthday ();
}
}
+ private void _update_location ()
+ {
+ var _geo = this._get_property<E.ContactGeo> ("geo");
+
+ if (_geo != null)
+ {
+ if (this._location == null ||
+ // Report all changes to the location because someone must
+ // have changed the values intentionally.
+ !this._location.equal_coordinates (_geo.latitude, _geo.longitude))
+ {
+ if (this._location != null)
+ {
+ // Update existing instance instead of destroying it
+ // and creating a new one. Minimizes memory thrashing.
+ this._location.latitude = _geo.latitude;
+ this._location.longitude = _geo.longitude;
+ }
+ else
+ {
+ this._location = new Location (_geo.latitude, _geo.longitude);
+ }
+ this.notify_property ("location");
+ }
+ }
+ else
+ {
+ if (this._location != null)
+ {
+ this._location = null;
+ this.notify_property ("location");
+ }
+ }
+ }
+
private void _update_favourite ()
{
bool is_fav = false;
im-details.vala \
interaction-details.vala \
local-id-details.vala \
+ location-details.vala \
name-details.vala \
note-details.vala \
phone-details.vala \
ImDetails,
InteractionDetails,
LocalIdDetails,
+ LocationDetails,
NameDetails,
NoteDetails,
PresenceDetails,
set { this.change_local_ids.begin (value); } /* not writeable */
}
+ private Location? _location = null;
+ /**
+ * {@inheritDoc}
+ */
+ [CCode (notify = false)]
+ public Location? location
+ {
+ get { return this._location; }
+ set { this.change_location.begin (value); } /* not writeable */
+ }
+
private DateTime? _birthday = null;
/**
this._update_local_ids (false);
}
+ private void _notify_location_cb ()
+ {
+ this._update_location ();
+ }
+
/**
* Add or remove the Individual from the specified group.
*
this._update_notes (false);
this._update_postal_addresses (false);
this._update_local_ids (false);
+ this._update_location ();
}
/* Delegate to update the value of a property on this individual from the
(this._notify_postal_addresses_cb);
persona.notify["local-ids"].connect
(this._notify_local_ids_cb);
-
+ persona.notify["location"].connect (this._notify_location_cb);
if (persona is GroupDetails)
{
persona.notify["postal-addresses"].disconnect
(this._notify_postal_addresses_cb);
persona.notify["local-ids"].disconnect (this._notify_local_ids_cb);
-
+ persona.notify["location"].disconnect (this._notify_location_cb);
if (persona is GroupDetails)
{
}, emit_notification, force_update);
}
+ private void _update_location ()
+ {
+ this._update_single_valued_property (typeof (LocationDetails), (p) =>
+ {
+ return ((LocationDetails) p).location != null;
+ }, (a, b) =>
+ {
+ // TODO (https://bugzilla.gnome.org/show_bug.cgi?id=627400): pick the "better" location information. For now, pick more or less randomly.
+ return 0;
+ }, "location", (p) =>
+ {
+ unowned Location? new_location = null;
+
+ if (p != null)
+ {
+ new_location = ((LocationDetails) p).location;
+ }
+
+ if ((new_location == null) != (this.location == null) /* adding or removing a location? */ ||
+ new_location != null && !new_location.equal (this.location) /* different value? */)
+ {
+ this._location = new_location;
+ this.notify_property ("location");
+ }
+ });
+ }
+
private void _update_postal_addresses (bool create_if_not_exist, bool emit_notification = true, bool force_update = true)
{
/* FIXME: Detect duplicates somehow? */
--- /dev/null
+/*
+ * Copyright (C) 2013 Intel Corp
+ *
+ * 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:
+ * Patrick Ohly <patrick.ohly@intel.com>
+ */
+
+using GLib;
+
+/**
+ * A location. Typically latitude and longitude will
+ * be based on WGS84. However, folks often has no
+ * way of verifying that and just has to assume
+ * that.
+ *
+ * @since UNRELEASED
+ */
+public class Folks.Location : Object
+{
+ /**
+ * The latitude.
+ *
+ * @since UNRELEASED
+ */
+ public double latitude;
+ /**
+ * The longitude.
+ *
+ * @since UNRELEASED
+ */
+ public double longitude;
+
+ /**
+ * Constructs a new instance with the given coordinates.
+ * @param latitude latitude of the new instance
+ * @param longitude longitude of the new instance
+ * @since UNRELEASED
+ */
+ public Location (double latitude, double longitude)
+ {
+ this.latitude = latitude;
+ this.longitude = longitude;
+ }
+
+ /**
+ * Compare this location to another by geographical position.
+ *
+ * @param other the instance to compare against
+ * @returns true iff the coordinates are exactly the same
+ * @since UNRELEASED
+ */
+ public bool equal (Location other)
+ {
+ return this.latitude == other.latitude &&
+ this.longitude == other.longitude;
+ }
+
+ /**
+ * Compare the geographical position of this location against
+ * another position.
+ *
+ * @param latitude latitude of the other position
+ * @param longitude longitude of the other position
+ * @returns true iff the coordinates are exactly the same
+ * @since UNRELEASED
+ */
+ public bool equal_coordinates (double latitude, double longitude)
+ {
+ return this.latitude == latitude &&
+ this.longitude == longitude;
+ }
+}
+
+/**
+ * Location of a contact. folks tries to keep track of
+ * the current location and thus favors live data (say,
+ * as advertised by a chat service) over static data (from
+ * an address book).
+ *
+ * Backends are expected to report only relevant changes
+ * in a persona's location. For storage backends like EDS,
+ * all changes must have been triggered by a person and thus
+ * all are relevant.
+ *
+ * A backend pulling in live data, for example from a GPS,
+ * is expected to filter the data to minimize noise.
+ *
+ * The folks itself then will apply all changes coming
+ * from backends, without further filtering.
+ *
+ * @since UNRELEASED
+ */
+public interface Folks.LocationDetails : Object
+{
+ /**
+ * The current location of the contact. Null if the contact’s
+ * current location isn’t known, or they’re keeping it private.
+ */
+ public abstract Location? location { get; set; }
+
+ /**
+ * Set or remove the contact's currently advertised location.
+ *
+ * It's preferred to call this rather than setting
+ * {@link LocationDetails.location} directly, as this method gives error
+ * notification and will only return once the location has been written to the
+ * relevant backing store (or the operation's failed).
+ *
+ * @param location the contact's location, null to remove the information
+ * @throws PropertyError if setting the location failed
+ */
+ public virtual async void change_location (Location? location) throws PropertyError
+ {
+ /* Default implementation. */
+ throw new PropertyError.NOT_WRITEABLE (
+ _("Location is not writeable on this contact."));
+ }
+}
LOCAL_IDS,
/**
+ * Field for {@link LocationDetails.location}.
+ *
+ * @since UNRELEASED
+ */
+ LOCATION,
+
+ /**
* Field for {@link NameDetails.nickname}.
*
* @since 0.5.0
"im-addresses",
"is-favourite",
"local-ids",
+ "location",
"nickname",
"notes",
"phone-numbers",
folks/individual-aggregator.vala
folks/individual.vala
folks/local-id-details.vala
+folks/location-details.vala
folks/name-details.vala
folks/note-details.vala
[type: gettext/gsettings]folks/org.freedesktop.folks.gschema.xml.in