PIM Manager: autotools + libfolks + API
Initial step towards using SyncEvolution, PBAP and libfolks in the context of IVI (in-vehicle-infotainment): D-Bus API definition for the org._01.pim.contact API, --enable-dbus-service-pim, find libs, compile into syncevo-dbus-server and client-test. The only functional code at this time is the unit testing of libfolks, GValueCXX and libgee.
This commit is contained in:
parent
849c5408e5
commit
4107353d14
7 changed files with 657 additions and 3 deletions
16
configure.ac
16
configure.ac
|
@ -515,8 +515,23 @@ if test $enable_dbus_service = "yes"; then
|
|||
MLITE_LIBS=
|
||||
fi
|
||||
AC_DEFINE(DBUS_SERVICE, 1, [define if dbus service is enabled])
|
||||
|
||||
AC_ARG_ENABLE(dbus-service-pim,
|
||||
AS_HELP_STRING([--enable-dbus-service-pim],
|
||||
[enable implementation of org._01.pim D-Bus APIs (depends on libfolks)]),
|
||||
[ enable_dbus_pim="$enableval" ],
|
||||
[ enable_dbus_pim="no" ])
|
||||
case "$enable_dbus_pim" in
|
||||
no) ;;
|
||||
yes)
|
||||
PKG_CHECK_MODULES(FOLKS, [folks])
|
||||
AC_DEFINE(ENABLE_DBUS_PIM, 1, [org._01.pim D-Bus API enabled])
|
||||
;;
|
||||
*) AC_MSG_ERROR([invalid value for --enable-dbus-service-pim: '$enable_dbus_pim']);;
|
||||
esac
|
||||
fi
|
||||
AM_CONDITIONAL([NOTIFY_COMPATIBILITY], [test "$enable_notify_compat" = "yes"])
|
||||
AM_CONDITIONAL([COND_DBUS_PIM], [test "$enable_dbus_pim" = "yes"])
|
||||
|
||||
AC_SUBST(DBUS_CFLAGS)
|
||||
AC_SUBST(DBUS_LIBS)
|
||||
|
@ -1023,6 +1038,7 @@ for backend in $BACKENDS; do
|
|||
eval echo $backend: \${enable_${backend}}
|
||||
done
|
||||
echo "DBus service: $enable_dbus_service"
|
||||
echo "org._01.pim support in DBus service: $enable_dbus_pim"
|
||||
echo "Notifications: $enable_notify"
|
||||
echo "GIO GDBus: $with_gio_gdbus"
|
||||
echo "GNOME keyring: $enable_gnome_keyring"
|
||||
|
|
25
src/dbus/server/pim/README
Normal file
25
src/dbus/server/pim/README
Normal file
|
@ -0,0 +1,25 @@
|
|||
The code in this directory only gets compiled when SyncEvolution is
|
||||
configured with --enable-dbus-service-pim. It uses libfolks and the
|
||||
PBAP backend to implement a unified address book. This additional
|
||||
functionality is provided via the org._01.pim D-Bus API.
|
||||
|
||||
That API is independent of libfolks and SyncEvolution. That it is
|
||||
implemented inside syncevo-dbus-server merely simplifies the
|
||||
implementation, by reusing some code provided by SyncEvolution
|
||||
and the core Server class:
|
||||
* C++ D-Bus bindings
|
||||
* logging
|
||||
* D-Bus server life cycle control: delay shut down while
|
||||
clients make calls, inhibit shutdown while clients are
|
||||
registered, restart when files change on disk during system
|
||||
update, ...
|
||||
* direct access to SyncEvolution instead having to go through
|
||||
the http://api.syncevolution.org D-Bus API
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
194
src/dbus/server/pim/folks.cpp
Normal file
194
src/dbus/server/pim/folks.cpp
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Intel Corporation
|
||||
*
|
||||
* 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) version 3.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <folks/folks.h>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <syncevo/GLibSupport.h>
|
||||
#include <syncevo/GeeSupport.h>
|
||||
#include <syncevo/GValueSupport.h>
|
||||
#include "test.h"
|
||||
|
||||
SE_GOBJECT_TYPE(FolksIndividualAggregator)
|
||||
SE_GOBJECT_TYPE(FolksIndividual)
|
||||
SE_GOBJECT_TYPE(FolksEmailFieldDetails)
|
||||
|
||||
#include <syncevo/declarations.h>
|
||||
SE_BEGIN_CXX
|
||||
|
||||
#ifdef ENABLE_UNIT_TESTS
|
||||
|
||||
class FolksTest : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST_SUITE(FolksTest);
|
||||
CPPUNIT_TEST(open);
|
||||
CPPUNIT_TEST(asyncError);
|
||||
CPPUNIT_TEST(gvalue);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
static void asyncCB(const GError *gerror, const char *func, bool &failed, bool &done) {
|
||||
done = true;
|
||||
if (gerror) {
|
||||
failed = true;
|
||||
SE_LOG_ERROR(NULL, NULL, "%s: %s", func, gerror->message);
|
||||
}
|
||||
}
|
||||
|
||||
void open() {
|
||||
FolksIndividualAggregatorCXX aggregator(folks_individual_aggregator_new(), false);
|
||||
bool done = false, failed = false;
|
||||
SYNCEVO_GLIB_CALL_ASYNC(folks_individual_aggregator_prepare,
|
||||
boost::bind(asyncCB, _1,
|
||||
"folks_individual_aggregator_prepare",
|
||||
boost::ref(failed), boost::ref(done)),
|
||||
aggregator);
|
||||
|
||||
while (!done) {
|
||||
g_main_context_iteration(NULL, true);
|
||||
}
|
||||
CPPUNIT_ASSERT(!failed);
|
||||
|
||||
while (!folks_individual_aggregator_get_is_quiescent(aggregator)) {
|
||||
g_main_context_iteration(NULL, true);
|
||||
}
|
||||
|
||||
GeeMap *individuals = folks_individual_aggregator_get_individuals(aggregator);
|
||||
SE_LOG_DEBUG(NULL, NULL, "%d individuals", gee_map_get_size(individuals));
|
||||
|
||||
GeeMapIteratorCXX it(gee_map_map_iterator(individuals), false);
|
||||
while (gee_map_iterator_next(it)) {
|
||||
PlainGStr id(reinterpret_cast<gchar *>(gee_map_iterator_get_key(it)));
|
||||
FolksIndividualCXX individual(reinterpret_cast<FolksIndividual *>(gee_map_iterator_get_value(it)),
|
||||
false);
|
||||
GValueStringCXX fullname;
|
||||
g_object_get_property(G_OBJECT(individual.get()), "full-name", &fullname);
|
||||
SE_LOG_DEBUG(NULL, NULL, "map: id %s name %s = %s",
|
||||
id.get(),
|
||||
fullname.toString().c_str(),
|
||||
fullname.get());
|
||||
}
|
||||
|
||||
GeeIteratorCXX it2(gee_iterable_iterator(GEE_ITERABLE(individuals)), false);
|
||||
while (gee_iterator_next(it2)) {
|
||||
GeeMapEntryCXX entry(reinterpret_cast<GeeMapEntry *>(gee_iterator_get(it2)), false);
|
||||
gchar *id(reinterpret_cast<gchar *>(const_cast<gpointer>(gee_map_entry_get_key(entry))));
|
||||
FolksIndividual *individual(reinterpret_cast<FolksIndividual *>(const_cast<gpointer>(gee_map_entry_get_value(entry))));
|
||||
GValueStringCXX fullname;
|
||||
g_object_get_property(G_OBJECT(individual), "full-name", &fullname);
|
||||
SE_LOG_DEBUG(NULL, NULL, "iterable: id %s name %s = %s",
|
||||
id,
|
||||
fullname.toString().c_str(),
|
||||
fullname.get());
|
||||
}
|
||||
|
||||
typedef GeeCollCXX< GeeMapEntryWrapper<const gchar *, FolksIndividual *> > Coll;
|
||||
Coll coll(individuals);
|
||||
Coll::const_iterator curr = coll.begin();
|
||||
Coll::const_iterator end = coll.end();
|
||||
if (curr != end) {
|
||||
const gchar *id = (*curr).key();
|
||||
FolksIndividual *individual((*curr).value());
|
||||
GValueStringCXX fullname;
|
||||
g_object_get_property(G_OBJECT(individual), "full-name", &fullname);
|
||||
|
||||
SE_LOG_DEBUG(NULL, NULL, "first: id %s name %s = %s",
|
||||
id,
|
||||
fullname.toString().c_str(),
|
||||
fullname.get());
|
||||
++curr;
|
||||
}
|
||||
|
||||
BOOST_FOREACH (Coll::value_type &entry, Coll(individuals)) {
|
||||
const gchar *id = entry.key();
|
||||
FolksIndividual *individual(entry.value());
|
||||
// GValueStringCXX fullname;
|
||||
// g_object_get_property(G_OBJECT(individual), "full-name", &fullname);
|
||||
const gchar *fullname = folks_name_details_get_full_name(FOLKS_NAME_DETAILS(individual));
|
||||
|
||||
SE_LOG_DEBUG(NULL, NULL, "boost: id %s %s name %s",
|
||||
id,
|
||||
fullname ? "has" : "has no",
|
||||
fullname);
|
||||
|
||||
GeeSet *emails = folks_email_details_get_email_addresses(FOLKS_EMAIL_DETAILS(individual));
|
||||
SE_LOG_DEBUG(NULL, NULL, " %d emails", gee_collection_get_size(GEE_COLLECTION(emails)));
|
||||
typedef GeeCollCXX<FolksEmailFieldDetails *> EmailColl;
|
||||
BOOST_FOREACH (FolksEmailFieldDetails *email, EmailColl(emails)) {
|
||||
SE_LOG_DEBUG(NULL, NULL, " %s",
|
||||
reinterpret_cast<const gchar *>(folks_abstract_field_details_get_value(FOLKS_ABSTRACT_FIELD_DETAILS(email))));
|
||||
}
|
||||
}
|
||||
|
||||
aggregator.reset();
|
||||
}
|
||||
|
||||
void gvalue() {
|
||||
GValueBooleanCXX b(true);
|
||||
SE_LOG_DEBUG(NULL, NULL, "GValueBooleanCXX(true) = %s", b.toString().c_str());
|
||||
GValueBooleanCXX b2(b);
|
||||
CPPUNIT_ASSERT_EQUAL(b.get(), b2.get());
|
||||
b2.set(false);
|
||||
CPPUNIT_ASSERT_EQUAL(b.get(), (gboolean)!b2.get());
|
||||
b2 = b;
|
||||
CPPUNIT_ASSERT_EQUAL(b.get(), b2.get());
|
||||
|
||||
GValueStringCXX str("foo bar");
|
||||
SE_LOG_DEBUG(NULL, NULL, "GValueStringCXX(\"foo bar\") = %s", str.toString().c_str());
|
||||
CPPUNIT_ASSERT(!strcmp(str.get(), "foo bar"));
|
||||
GValueStringCXX str2(str);
|
||||
CPPUNIT_ASSERT(!strcmp(str.get(), str2.get()));
|
||||
CPPUNIT_ASSERT(str.get() != str2.get());
|
||||
str2.set("foo");
|
||||
CPPUNIT_ASSERT(strcmp(str.get(), str2.get()));
|
||||
CPPUNIT_ASSERT(str.get() != str2.get());
|
||||
str2 = str;
|
||||
CPPUNIT_ASSERT(!strcmp(str.get(), str2.get()));
|
||||
CPPUNIT_ASSERT(str.get() != str2.get());
|
||||
str2.take(g_strdup("bar"));
|
||||
CPPUNIT_ASSERT(strcmp(str.get(), str2.get()));
|
||||
CPPUNIT_ASSERT(str.get() != str2.get());
|
||||
const char *fixed = "fixed";
|
||||
str2.setStatic(fixed);
|
||||
CPPUNIT_ASSERT(!strcmp(str2.get(), fixed));
|
||||
CPPUNIT_ASSERT(str2.get() == fixed);
|
||||
}
|
||||
|
||||
void asyncError() {
|
||||
bool done = false, failed = false;
|
||||
SYNCEVO_GLIB_CALL_ASYNC(folks_individual_aggregator_remove_individual,
|
||||
boost::bind(asyncCB, _1,
|
||||
"folks_individual_aggregator_remove_individual",
|
||||
boost::ref(failed), boost::ref(done)),
|
||||
NULL, NULL);
|
||||
while (!done) {
|
||||
g_main_context_iteration(NULL, true);
|
||||
}
|
||||
// Invalid parameters are not reported!
|
||||
CPPUNIT_ASSERT(!failed);
|
||||
}
|
||||
};
|
||||
|
||||
SYNCEVOLUTION_TEST_SUITE_REGISTRATION(FolksTest);
|
||||
|
||||
#endif
|
||||
|
||||
SE_END_CXX
|
||||
|
28
src/dbus/server/pim/folks.h
Normal file
28
src/dbus/server/pim/folks.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Intel Corporation
|
||||
*
|
||||
* 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) version 3.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef INCL_SYNCEVO_DBUS_SERVER_IVI_FOLKS
|
||||
#define INCL_SYNCEVO_DBUS_SERVER_IVI_FOLKS
|
||||
|
||||
#include <syncevo/declarations.h>
|
||||
SE_BEGIN_CXX
|
||||
|
||||
SE_END_CXX
|
||||
|
||||
#endif // INCL_SYNCEVO_DBUS_SERVER_IVI_FOLKS
|
372
src/dbus/server/pim/pim-manager-api.txt
Normal file
372
src/dbus/server/pim/pim-manager-api.txt
Normal file
|
@ -0,0 +1,372 @@
|
|||
Preamble
|
||||
========
|
||||
|
||||
This text describes a D-Bus API. It might get copied into XML API definitions
|
||||
in future revisions.
|
||||
|
||||
The API implements in-vehicle infotainment (IVI) use cases around
|
||||
contacts:
|
||||
- cache address books from peers (primarily phones connected via Bluetooth)
|
||||
in local address books
|
||||
- provide a unified address book that combines a configurable (and changing)
|
||||
subset of the local address books
|
||||
- fast phone number lookup
|
||||
- browsing and searching in the unified address book
|
||||
|
||||
Tasks that are expected to be done by the user of this API:
|
||||
- identify peers and their capabilities
|
||||
- decide how and when peer data should be cached
|
||||
- define which data goes into the unified address book
|
||||
|
||||
In other words, the API provides the mechanisms and the user the
|
||||
policy.
|
||||
|
||||
Datatypes
|
||||
=========
|
||||
|
||||
Peers
|
||||
-----
|
||||
|
||||
A peer is an entity which has exactly one address book that is meant
|
||||
to be cached locally. Typically a peer is a phone connected via
|
||||
Bluetooth and accessed via PBAP, but it could also be a web service
|
||||
that supports CardDAV or a phone with SyncML support.
|
||||
|
||||
Peers are identified by a unique string ID. That ID needs to be
|
||||
assigned by the user of this API. The string must not be empty and may
|
||||
only contain characters a-z, 0-9, hyphen and colon. No other
|
||||
assumptions about its content are made. For example, the phone's
|
||||
Bluetooth MAC address could be used.
|
||||
|
||||
For an entity that has more than one address book, multiple peers must
|
||||
be configured.
|
||||
|
||||
For each peer, enough information must be provided to access its
|
||||
address book. That information is passed via D-Bus as a
|
||||
string-to-string dict, with the following keys:
|
||||
|
||||
- "protocol" - defines how to access the address book. Currently
|
||||
only "PBAP" is implemented. "SyncML", "CardDAV" are documented
|
||||
to illustrate how the API would work for them.
|
||||
- "transport" - defines how to establish
|
||||
the connection. Currently only "Bluetooth" is allowed
|
||||
(for protocol=PBAP or SyncML) and taken as default when
|
||||
"transport" is not set.
|
||||
- "address" - the Bluetooth MAC address in the aa:bb:cc:dd:ee:ff
|
||||
format (for transport=Bluetooth)
|
||||
- "database" - empty or unset for the internal address book
|
||||
(protocol=PBAP), the URI (protocol=SyncML)
|
||||
|
||||
Address books
|
||||
-------------
|
||||
|
||||
Address books which mirror data from a specific peer use the string
|
||||
"peer-<uid>" as ID, where <uid> is the unique ID of that peer.
|
||||
|
||||
In addition, there is a system address book which is independent of
|
||||
any particular phone. Its ID is the empty string.
|
||||
|
||||
This naming scheme can be extended later on, to support other kinds
|
||||
of address books.
|
||||
|
||||
Contact
|
||||
-------
|
||||
|
||||
A single contact is transferred via D-Bus as a string->variant dict
|
||||
where the keys are predefined property names and the values represent
|
||||
simple values (a string for "full-name") or more complex structures
|
||||
(list of phone numbers for "phone-numbers", with each list entry
|
||||
itself being a combination of type flags and the actual value).
|
||||
|
||||
[comment: this mirrors the properties of a libfolks Individual:
|
||||
http://telepathy.freedesktop.org/doc/folks/c/FolksIndividual.html]
|
||||
|
||||
Some properties of a FolksIndividual only make sense locally and are
|
||||
not transmitted, for example the personas it is derived from.
|
||||
|
||||
Some other properties provide information not found that way in
|
||||
FolksIndividual:
|
||||
- "source" = list of string pairs, where each pair is a combination
|
||||
of address book ID and local contact ID inside that address book
|
||||
(not necessarily the same as the vCard UID of a contact!)
|
||||
|
||||
Property values which are large (like photos) are not sent via
|
||||
D-Bus. Instead a link to a local file is sent.
|
||||
|
||||
TODO: document all properties and their types.
|
||||
|
||||
Search results
|
||||
--------------
|
||||
|
||||
The goal is to support a UI which:
|
||||
- displays an ordered list of the search result,
|
||||
- can show the initial results with minimal delay,
|
||||
- can load actual content for the display as needed (only
|
||||
load the parts which are visible or will be visible soon).
|
||||
|
||||
The content of the unified address book can change at any time. The
|
||||
API design takes that into account by using a model/view/controller
|
||||
model.
|
||||
|
||||
The model is the complete list of contacts, sorted according to the
|
||||
currently configured sort order. Sorting is part of the model to
|
||||
simplify generating views.
|
||||
|
||||
The view is the subset of the data that a user of the API has
|
||||
requested. In the most extreme case, all contacts are part of the
|
||||
view. Therefore contact data has to be requested explicitly. To
|
||||
support requesting data in batches, contacts are numbered 0 to n-1 in
|
||||
each view, where n is the number of contacts in the view. Sort order
|
||||
is the same as in the underlying model. Change notifications with
|
||||
these index numbers are sent as contacts are added, modified or
|
||||
removed.
|
||||
|
||||
The controller is the part of the API which allows changing contacts
|
||||
in the system address book, changing the sort order, enabling or disabling
|
||||
address books, etc.
|
||||
|
||||
Note that removing or adding a contact changes the numbers assigned to
|
||||
other contacts. Example:
|
||||
|
||||
- A view containing 10 contacts is created.
|
||||
- A notification about "contacts #0 to #9 added" is sent
|
||||
(given as pair of first index and count, not list of numbers).
|
||||
- Data for contacts #5 to #19 gets requested, five contacts #5 to #9
|
||||
are returned. It's okay to ask for more contacts than exist,
|
||||
because the caller cannot be sure anyway how many contacts
|
||||
still exist at the time when the request gets processed.
|
||||
- Contact #4 gets removed. The user needs to remember that
|
||||
the data that it has now corresponds to contacts #4 to #8.
|
||||
- Contact #5 gets added, before the contact which had that
|
||||
number before. The user now has contacts #4 and #6 to #9.
|
||||
It should request contact #5 if (or once) it is needed to
|
||||
provide a complete list to the user.
|
||||
|
||||
[comment: using a view could be simplified by including contact data
|
||||
in the change notifications. This is not planned at the moment because
|
||||
it would not work well for large views. When adding it, there should
|
||||
be an API to restrict which properties of a contact get sent.]
|
||||
|
||||
|
||||
Error handling
|
||||
==============
|
||||
|
||||
D-Bus error messages are not localized. They are meant for debugging,
|
||||
not for displaying to the user. In cases where the caller may be able
|
||||
to do something about an error, specific error codes could be defined
|
||||
and returned as part of the API. However, typically errors are generic
|
||||
and the caller simply has to assume that the PIM storage is currently
|
||||
unusable.
|
||||
|
||||
Unless noted otherwise, calls return when the requested operation is
|
||||
complete.
|
||||
|
||||
|
||||
API
|
||||
===
|
||||
|
||||
PIM Manager
|
||||
-----------
|
||||
|
||||
The PIM manager is used to hold the unified address book in memory,
|
||||
create views on it, change configuration and control data transfers
|
||||
from phones.
|
||||
|
||||
Service: org._01.pim.contacts
|
||||
Interface: org._01.pim.contacts.Manager
|
||||
Object path: /org/01/pim/contacts
|
||||
|
||||
Methods:
|
||||
|
||||
void Start()
|
||||
|
||||
The PIM manager does not start loading contact data right
|
||||
away. That allows setting the options like sort order first
|
||||
and/or delaying the loading until it is needed. After
|
||||
Start(), changing options that affect the unified address
|
||||
book will take effect immediately.
|
||||
|
||||
Calling Start() is optional, any method asking for data will
|
||||
automatically do that.
|
||||
|
||||
void Stop()
|
||||
|
||||
Explicitly tells the PIM manager to discard the unified address
|
||||
book and free up the memory if possible (= not currently in use).
|
||||
Primarily useful for testing.
|
||||
|
||||
void SetSortOrder(string mode)
|
||||
|
||||
"mode" must be one of "first/last", "last/first", "full name".
|
||||
"first/last" sorts based on the first name stored in the "name"
|
||||
property, with the last name used to break ties between equal first
|
||||
names. "last/first" reverts that comparison. "full name" sorts based on
|
||||
the full name chosen for the contact if there is such a string, otherwise
|
||||
it uses "<last name>" and "<first name>" as sort strings.
|
||||
|
||||
The sort order is stored persistently. The default is "last/first".
|
||||
|
||||
string GetSortOrder()
|
||||
|
||||
Returns the current sort order.
|
||||
|
||||
list of strings GetActiveAddressBooks()
|
||||
|
||||
Returns the IDs of the address books which currently
|
||||
contribute to the unified address book.
|
||||
|
||||
void SetActiveAddressBooks(list of strings)
|
||||
|
||||
Sets the address books which contribute to the unified
|
||||
address book.
|
||||
|
||||
void SetPeer(string uid, dict properties)
|
||||
|
||||
Adds or modifies a peer. Modifying a peer does *not* affect
|
||||
any contact data which might be cached for it.
|
||||
|
||||
void RemovePeer(string uid)
|
||||
|
||||
Removes a peer and all its cached data. If that data was
|
||||
part of the active address books, it will be removed
|
||||
automatically.
|
||||
|
||||
void SyncPeer(string uid)
|
||||
|
||||
Retrieve contacts from the peer and ensure that the local
|
||||
cache is identical to the address book of the peer. The call
|
||||
returns once the operation is complete. Only if there was no
|
||||
error can the caller assume that the cache is up-to-date.
|
||||
Otherwise it is in an undefined state.
|
||||
|
||||
void StopSync(string uid)
|
||||
|
||||
Stop any running sync for the given peer. The SyncPeer() method
|
||||
which started such a sync will return with an "aborted" error
|
||||
once the sync was stopped.
|
||||
|
||||
dict of UID to string key/value dict GetAllPeers()
|
||||
|
||||
Returns information about all currently configured peers.
|
||||
|
||||
object Search(dict filter, object agent)
|
||||
|
||||
Creates a new view which contains all contacts matching the
|
||||
filter. The call returns the object path of a view object
|
||||
after validating parameters and starting the result
|
||||
gathering, and before completing the search. The view object
|
||||
can be used to control the view via the
|
||||
org._01.pim.contacts.ViewControl interface.
|
||||
|
||||
An empty filter matches all contacts. TODO: define other
|
||||
searches.
|
||||
|
||||
Notifications for the view are sent back to the caller by
|
||||
invoking methods from the org._01.pim.contacts.ViewAgent
|
||||
interface on the object whose path is given in the "view"
|
||||
parameter. If any of these method calls fail, the view will
|
||||
automatically be destroyed.
|
||||
|
||||
In other words, the caller first needs to get ready to process
|
||||
results by registering an object on the bus before calling
|
||||
Search().
|
||||
|
||||
[comment: this allows sending results to just one recipient,
|
||||
something that cannot be done easily with the use of signals as in,
|
||||
for example, obexd. In obexd, the initiator of a transfer
|
||||
has to subscribe to org.bluez.obex.Transfer on the object path
|
||||
returned to it when starting the transfer, then check the current
|
||||
status before waiting for signals, because the "Completed" signal
|
||||
might have been sent before it could register for it.]
|
||||
|
||||
string AddContact(string addressbook, dict contact)
|
||||
|
||||
Adds a new contact to the given address book. Typically
|
||||
only the system address book is writable. Contact properties
|
||||
which are unknown or cannot be stored are silently ignored.
|
||||
Returns the local ID of the new contact in the address book.
|
||||
|
||||
Photo data that is sent inline in the dict will be split out
|
||||
into a file that gets associated with the contact. A photo
|
||||
file that gets linked will continue to be owned by the
|
||||
caller; the contact storage may or may not make a copy of it,
|
||||
depending on which storage is used.
|
||||
|
||||
void ModifyContact(string addressbook, string localid, dict contact)
|
||||
|
||||
Updates an existing contact.
|
||||
|
||||
void RemoveContact(string addressbook, string localid)
|
||||
|
||||
Remove the contact and all of its associated data (like the
|
||||
photo, if the photo file is owned by the contact storage).
|
||||
|
||||
|
||||
Service: org._01.pim.contacts
|
||||
Interface: org._01.pim.contacts.ViewControl
|
||||
Object path: [variable prefix]/{view0,view1,....}
|
||||
Methods:
|
||||
|
||||
list of contact dicts ReadContacts(int start, int count)
|
||||
|
||||
Requests at most "count" contacts in the view, starting
|
||||
with the one at "start" (numbered starting with 1). May
|
||||
return less (or no) contacts if the request range is
|
||||
beyond the end of the view at the time when the call is
|
||||
processed.
|
||||
|
||||
Note that the caller must process the call response
|
||||
after all events via the ViewAgent interface if it wants
|
||||
to keep in sync with the view. Doing this call asynchronously
|
||||
and dealing with the response as part of the main event
|
||||
loop will do the right thing automatically, because D-Bus
|
||||
guarantees ordering of messages.
|
||||
|
||||
Making this explicit by returning data via another
|
||||
org._01.pim.contacts.ViewAgent method was considered and
|
||||
rejected a) for the sake of keeping this API simple and
|
||||
b) to allow simple synchronous calls where it makes sense
|
||||
(testing, for example).
|
||||
|
||||
void Close()
|
||||
|
||||
Closes the view and all resources associated with it.
|
||||
Pending ReadContacts() calls will return without any
|
||||
data and no error.
|
||||
|
||||
void RefineSearch(dict filter)
|
||||
|
||||
Replaces the current filter of the view with a new one.
|
||||
The new filter must be stricter than the old one. Contacts
|
||||
which were already filtered out will not be added back
|
||||
to the view when setting a less restrictive filter (simplifies
|
||||
the implementation).
|
||||
|
||||
|
||||
Service: [user of the PIM Manager]
|
||||
Interface: org._01.pim.contacts.ViewAgent
|
||||
Object path: [as chosen by user of PIM Manager]
|
||||
|
||||
Methods:
|
||||
|
||||
void ContactsModified(object view, int start, int count)
|
||||
|
||||
Contacts #start till #start + count (inclusive) have
|
||||
changed. Data that the recipient of the call might have
|
||||
cached became invalid and should be reloaded. In cases
|
||||
where changing a contact changes its position in the
|
||||
sorted list, "contact removed" and "contact added"
|
||||
notifications will be triggered instead of a "contact
|
||||
changed".
|
||||
|
||||
void ContactsAdded(object view, int start, int count)
|
||||
|
||||
New contacts were added to the view.
|
||||
The contact which previously had index #start now
|
||||
has index #start + count, etc.
|
||||
|
||||
void ContactsRemoved(object view, int start, int count)
|
||||
|
||||
Some contacts were removed from the view.
|
||||
The contact which previous had index #start + count
|
||||
(if there was one) now has index #start, etc.
|
|
@ -49,6 +49,13 @@ src_dbus_server_libsyncevodbusserver_la_LIBADD = $(LIBNOTIFY_LIBS) $(MLITE_LIBS)
|
|||
src_dbus_server_libsyncevodbusserver_la_CPPFLAGS = -DHAVE_CONFIG_H -DSYNCEVOLUTION_LOCALEDIR=\"${SYNCEVOLUTION_LOCALEDIR}\" -I$(top_srcdir)/src -I$(top_srcdir)/test -I$(top_srcdir) -I$(gdbus_dir) $(BACKEND_CPPFLAGS)
|
||||
src_dbus_server_libsyncevodbusserver_la_CXXFLAGS = $(SYNCEVOLUTION_CXXFLAGS) $(CORE_CXXFLAGS) $(SYNTHESIS_CFLAGS) $(GLIB_CFLAGS) $(DBUS_CFLAGS) $(LIBNOTIFY_CFLAGS) $(MLITE_CFLAGS) $(SYNCEVO_WFLAGS)
|
||||
|
||||
if COND_DBUS_PIM
|
||||
src_dbus_server_server_cpp_files += \
|
||||
src/dbus/server/pim/folks.cpp
|
||||
src_dbus_server_libsyncevodbusserver_la_LIBADD += $(FOLKS_LIBS)
|
||||
src_dbus_server_libsyncevodbusserver_la_CXXFLAGS += $(FOLKS_CFLAGS)
|
||||
endif
|
||||
|
||||
# Session helper: syncevo-dbus-helper
|
||||
noinst_LTLIBRARIES += src/dbus/server/libsyncevodbushelper.la
|
||||
|
||||
|
|
18
src/src.am
18
src/src.am
|
@ -265,10 +265,22 @@ TEST_FILES_PATCHED = $(wildcard src/testcases/*.tem)
|
|||
# add files created via patches
|
||||
CLIENT_LIB_TEST_FILES += $(TEST_FILES_GENERATED)
|
||||
|
||||
# client-test must link against all static utility libs which might contain
|
||||
# object files with SYNCEVOLUTION_TEST_SUITE_REGISTRATION() macros.
|
||||
# To pull in those object files, LDFLAGS must contain undef statements
|
||||
# for the C symbols exported by the macro.
|
||||
src_client_test_libs = src/syncevo/libsyncevolution.la
|
||||
if COND_DBUS
|
||||
src_client_test_libs += src/dbus/server/libsyncevodbushelper.la src/dbus/server/libsyncevodbusserver.la
|
||||
endif
|
||||
|
||||
# src/syncevo/libsyncevolution.la -> src/syncevo/.libs/libsyncevolution.a -> -Wl,-u...
|
||||
src_client_test_undef = $(shell nm $(patsubst %.la,%.a,$(subst /lib,/.libs/lib,$(src_client_test_libs))) | grep funambolAutoRegisterRegistry | sed -e 's/.* /-Wl,-u/' )
|
||||
|
||||
src_client_test_CPPFLAGS = -DHAVE_CONFIG_H -DENABLE_INTEGRATION_TESTS -DENABLE_UNIT_TESTS $(src_cppflags) $(QT_CPPFLAGS)
|
||||
src_client_test_CXXFLAGS = @CPPUNIT_CXXFLAGS@ $(PCRECPP_CFLAGS) $(SYNCEVOLUTION_CXXFLAGS) $(CORE_CXXFLAGS) $(filter-out -O2 -g -W -Wall, $(QT_CXXFLAGS)) $(SYNCEVO_WFLAGS)
|
||||
src_client_test_LDFLAGS = @CPPUNIT_LDFLAGS@ `nm src/syncevo/.libs/libsyncevolution.a | grep funambolAutoRegisterRegistry | sed -e 's/.* /-Wl,-u/'` $(PCRECPP_LIBS) $(CORE_LD_FLAGS) $(QT_LDFLAGS)
|
||||
src_client_test_LDADD = $(CORE_LDADD) $(SYNTHESIS_ENGINE) $(QT_LIBS)
|
||||
src_client_test_LDFLAGS = @CPPUNIT_LDFLAGS@ $(src_client_test_undef) $(CORE_LD_FLAGS) $(QT_LDFLAGS)
|
||||
src_client_test_LDADD = $(src_client_test_libs) $(CORE_LDADD) $(PCRECPP_LIBS) $(SYNTHESIS_ENGINE) $(QT_LIBS)
|
||||
# These dependencies are intentionally a bit too broad:
|
||||
# they ensure that all files are in place to *run* client-test.
|
||||
|
||||
|
@ -342,7 +354,7 @@ $(filter-out %.tem, $(filter src/testcases/%, $(subst $(top_srcdir)/test/,src/,$
|
|||
# The binary does not really depend on the test cases, only running it does.
|
||||
# Listing the dependencies here is done to ensure that one doesn't accidentally
|
||||
# runs the binary with out-dated auxiliary files.
|
||||
src_client_test_DEPENDENCIES = $(EXTRA_LTLIBRARIES) $(CORE_DEP) $(CLIENT_LIB_TEST_FILES) testcase2patch src/synccompare src/synclog2html src/templates
|
||||
src_client_test_DEPENDENCIES = $(EXTRA_LTLIBRARIES) $(src_client_test_libs) $(CORE_DEP) $(CLIENT_LIB_TEST_FILES) testcase2patch src/synccompare src/synclog2html src/templates
|
||||
|
||||
# Copy template directory into current working directory, if not there
|
||||
# yet. -ef flag checks whether device and inode numbers of both files
|
||||
|
|
Loading…
Reference in a new issue