JUN
19
2010

Akonadi porting for application developers

After all the blogging about our (as in KDE PIM developers) Akonadi porting, I thought I'll address a couple of things other application developer might consider for the next release cycle.

The most prevalent use of PIM API seems to be KABC::StddAddressBook.

Find By Uid

A common use case is to look for a contact object by its identifier, acquired by user selection somewhen in the past.
The code to do that usually looks like this:

KABC::AddressBook *addressBook = KABC::StdAddressBook::self();

KABC::Addressee contact = addressBook->findByUid( uidString );

This is quite easy but it always has a couple of issues, mainly the call to

KABC::StdAddressBook::self()

because its first invocation will load the whole address book and it will do so synchronously.
Potentially blocking the application for the whole duration or, sometimes even worse, introducing unexpected reentrancy in single threaded applications due to use of nested event loops.

Note: while the same sequence is possible with Akonadi, due to Akonadi jobs being based on KJob and it having the capability of "blocking" by use of a nested event loop, I really recommend that you use the porting effort as an opportunity to get rid of such emulated synchronousity whenever possible.

The equivalent using Akonadi API looks like this

Akonadi::ContactSearchJob *job = new Akonadi::ContactSearchJob( this );
job->setQuery( Akonadi::ContactSearchJob::ContactUid, uidString );

connect( job, SIGNAL( result( KJob* ) ), SLOT( contactSearchResult( KJob* ) ) );

and

void contactSearchResult( KJob *job )
{
  if ( job->error() != 0 ) {
    // error handling, see job->errorString()
    return;
  }

  Akonadi::ContactSearchJob *searchJob = qobject_cast( job );

  const KABC::Addressee::List contacts = searchJob->contacts();
}

Also see API docs for ContactSearchJob

Find by Email

Another use case I came across is searching by Email. The KABC code usually looks pretty identical:

KABC::AddressBook *addressBook = KABC::StdAddressBook::self();

KABC::Addressee contact = addressBook->findByEmail( emailString );

Unsurprisingly, the Akonadi code looks also quite similar to the previous use case:

Akonadi::ContactSearchJob *job = new Akonadi::ContactSearchJob( this );
job->setQuery( Akonadi::ContactSearchJob::Email, emailString );

connect( job, SIGNAL( result( KJob* ) ), SLOT( contactSearchResult( KJob* ) ) );

The code for the result slot is the same.

Who Am I

Another use case for StdAddressBook is not directly available in Akonadi:

KABC::AddressBook *addressBook = KABC::StdAddressBook::self();

KABC::Addressee contact = addressBook->whoAmI();

Even this had its shortcomings, because this depends on the users adding themselves to the address book and marking the respective entry as "this is me".

The concrete use cases I've seen this deployed for are getting the user's full name and/or their email address.

It might be better to use KPIMIdentities::IdentityManager instead.
It contains the identity users can configure in the Systemsettings module for personal information, so if there is data yet, you could embed the respective KCM in a dialog and let the user configure this centrally for all of KDE.

Modifying a contact

Using KABC::StdAddressBook modifying a contact (for example one retrieved by findByUid()) worked like this:

addressBook->insertAddressee( contact );

KABC::Ticket *ticket = addressBook->requestSaveTicket();
if ( !ticket ) {
  // error
} else if ( !addressBook->save( ticket ) ) {
    // error
    addressBook->releaseSaveTicket( ticket );
  }
}

With Akonadi, you will need the Akonadi::Item or at least the item's identifier.
Assuming you retrieved the contact using ContactSearchJob, consider keeping the item or Item::id() around, see

ContactSearchJob::items()

(inherited).

Lets say you have just the item's identifier:

Akonadi::Item item( itemId );
item.setPayload( contact );
item.setMimeType( KABC::Addressee::mimeType() );

Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob( item );

connect( job, SIGNAL( result( KJob* ) ), SLOT( contactModifyResult( KJob* ) ) );

Slot code similar to the one for search.

Buildsystem adjustments
Since you are already linking against KDE PIM Libs classes, you already have the necessary

find_package( KdepimLibs 4.5 REQUIRED )

However, your target_link_libraries need to be extended to contain

${KDEPIMLIBS_AKONADI_LIBS}

for base things like Akonadi::Item and

${KDEPIMLIBS_AKONADI_CONTACT_LIBS}

for things like

Akonadi::ContactSearchJob

.

Includes for Akonadi core classes are like

#include <Akonadi/Item>

for contact related specializations they are like

#include <Akonadi/Contact/ContactSearchJob>

In both cases also available in lowercase variants with ".h" suffix.

Other useful classes

Our Akonadi porting efforts have resulted in a couple of useful classes for various aspects of dealing with PIM data.
For contacts such classes would be: ContactEditor (also available as a dialog), ContactViewer (again also available as a dialog), ContactsTreeModel and others.

As usual: in case of questions, our IRC channel is #akonadi, freenode network, or send an email to our mailinglist (kde-pim@kde.org).

Also available on TechBase

Comments

Thank you, that might indeed come in handy when porting Konversation's KAddressBook integration to Akonadi. Those deprecation warnings are getting on my nerves :-).


By Eike Hein at Sat, 06/19/2010 - 20:42

I saw that some of your use cases are lookup by nick. ContactSearchJob can also use "NickName" as the search critiera.

The usages of iterating over all contacts and basically filtering application side is a bit more problematic.
I'll probably add the "get all contacts" to the TechBase page I've just written, but for some of these using Akonadi::ItemSearchJob with a custom query is probably the most efficient way.


By krake at Sun, 06/20/2010 - 13:30