Making Sink(ed) contacts accessible to Plasma-Phonebook App

Plasma-phonebook is a contacts managing application for Plasma-Mobile. The app gets the data, i.e., contacts from the kpeople library. It acts as backend for the plasma-phonebook app.
The task is to reveal sink contacts, i.e., contacts synced using KDE sink API to kpeople. In other terms, we need to make sink’s contacts database as kpeople’s data-source. Sink is an offline-caching, synchronization and indexing system for calendars, contacts, mails, etc.

** “How contacts are synced using sink?” will be discussed in another blog. **

Here’s what happens. When Plasma-phonebook app is started, an instance of kpeople (the backend) is created, and following which all the datasources of kpeople are called upon to serve their master 😌.
In this case, KPeopleSinkDataSource.

class KPeopleSinkDataSource : public KPeople::BasePersonsDataSource
{
public:
    KPeopleSinkDataSource(QObject *parent, const QVariantList &data);
    virtual ~KPeopleSinkDataSource();
    QString sourcePluginId() const override;
    KPeople::AllContactsMonitor* createAllContactsMonitor() override;
};
QString KPeopleSinkDataSource::sourcePluginId() const
{
    return QStringLiteral("sink");
}
AllContactsMonitor* KPeopleSinkDataSource::createAllContactsMonitor()
{
    return new KPeopleSink();
}

All the logic of program the KPeople-Sink plugin lies in class KPeopleSink, whose temporary instance is returned in function createAllContactsMonitor [line number 14]. This class is inherited from KPeople::AllContactsMonitor. AllContactsMonitor needs to be subclassed by each datasource of kpeople.

All that is to be done is :
1. Fetch the list of addressbooks synced by sink.
2. Get resource-id of these address-books.
3. Fetch the list of contacts for that addressbook
4. Assign a unique URI to every contact
5. Create an object of a class inherited from AbstractContact. AbstractContact is the class to provide the data from a given contact by the backends. It’s virtual function customProperty, a generic method to access a random contact property needs to be defined in the subclass.
6. Emit contactAdded signal.
Contacts are now aadded to kpeople and hence, accessible to plasma-phonebook! Simple \o/

This is what code looks like:

void KPeopleSink::initialSinkContactstoKpeople(){
    //fetch all the addressbooks synced by sink
    const QList<Addressbook> sinkAdressbooks = Sink::Store::read<Addressbook>(Sink::Query());
    Q_FOREACH(const Addressbook sinkAddressbook, sinkAdressbooks){
        //to get resourceId
        QByteArray resourceId = sinkAddressbook.resourceInstanceIdentifier();

        //fetch all the contacts synced by sink
        const QList<Contact> sinkContacts = Sink::Store::read<Contact>(Sink::Query().resourceFilter(resourceId));
        Q_FOREACH (const Contact sinkContact, sinkContacts){
            //get uri
            const QString uri = getUri(sinkContact, resourceId);

            //add uri of contact to set
            m_contactUriHash.insert(uri, sinkContact);

            KPeople::AbstractContact::Ptr contact(new SinkContact(sinkContact));
            Q_EMIT contactAdded(uri,contact);
        }
    }
}

Now, suppose plasma-phonebook app is running and in the meanwhile sink syncs the updated addressbook from server. In that case, next task becomes to make the changes visible in phonebook. We set up a notifier for each resource-id that notifies everytime a contact/addressbook with that resource-id is updated.

m_notifier = new Notifier(resourceId);
m_notifier->registerHandler([=] (const Sink::Notification &notification) {
    if (notification.type == Notification::Info && notification.code == SyncStatus::SyncSuccess) {
        //Add program logic for updated addressbook/contact
    }
});

A hash-table can be maintained to keep track of new contacts being added. Fetch the list of contacts synced by sink. Each time a contact in the list is not present in hash-table, it means a new contact has been added on the server and hence, emit contactAdded signal. New contact will be added to kpeople and hence accessible in plasma-phonebook.

const QList<Contact> sinkContacts = Sink::Store::read<Contact>(Sink::Query().resourceFilter(resourceId));
Q_FOREACH (const Contact sinkContact, sinkContacts){
    const QString uri = getUri(sinkContact, resourceId);
    if(!m_contactUriHash.contains(uri)){
        m_contactUriHash.insert(uri, sinkContact);
        KPeople::AbstractContact::Ptr contact(new SinkContact(sinkContact));
        Q_EMIT contactAdded(uri,contact);
    }
}

Similarly, we can code for cases where a contact is updated or deleted.

And with this, Tada!
Our system for Nextcloud CardDav Integration on Plasma Mobile is here \o/

You can follow the similar procedure if you want to create your own contact datasource for KPeople 🙂

KPeople-Sink plugin’s code is available at: https://invent.kde.org/rpatwal/kpeople-sink
Contributions to the project are welcomed 😀

One thought on “Making Sink(ed) contacts accessible to Plasma-Phonebook App

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s