Qt API | Qtopia API Qtopia Documentation

Qtopia Desktop and Syncing Framework

Warning: The Qtopia Desktop synchronization framework is still experimental. Note that the API may be changed for the next Qtopia release.

Introduction

Qtopia Desktop is the Qtopia application that runs on a end-users desktop. It provides the end-user with the following functionality:

Qtopia Desktop can easily be extended by the developer to be able to extend the above functionality for their application's data. In fact, most existing functionality provided by Qtopia Desktop is provided by Trolltech using this architecture. Every icon seen on the left hand column of Qtopia Desktop is in fact using the same architecture and APIs that is exposed to any developer.

Architecture and Plugins

Qtopia Desktop has modular and flexible design allowing end-users to easily install plugins to integrate Qtopia into their daily work environment.

Developers can access this architecture by writing a plugin for Qtopia Desktop. A plugin is written by implementing interfaces defined by Qtopia Desktop.

The available interfaces allow the developer to:

Qtopia Desktop provides each plugin with access to the CenterInterface. The CenterInterface class allows each plugin to use the functionality provided by Qtopia Desktop.

When Qtopia Desktop starts up it automatically scans for plugins and registers any plugins that support the interfaces. Qtopia Desktop will look in the subdirectory under the installed directory called lib. Plugin installations should place their shared object libraries in the qtopiadesktop/lib

Synchronization Architecture and Algorithms

Qtopia Desktop contains the algorithms for synchronization for any plugin.

Qtopia Desktop handles synchronizing multiple plugins working on the same data set. A data set is the data associated with an application domain, such as Address Book, To do List, and Calendar. So there can be multiple plugins that synchronize Address Book data such as Qtopia's Address Book and foobar's Address Book application.

The sync algorithm remembers the last state of each plugin when it was last synced. It then uses that information to determine what changes have occured in each plugin. Those changes are then merged together with all other plugins of the same data set type such as address book on the desktop and with the embedded device. A master document is created by applying those changes. A diff is then made between the new master document and each plugin to determine the changes for that plugin. Each plugin must then apply the changes sent to it through the interface. The new master document is also sent to the embedded device. After synchronization, all plugins and the embedded device should have the same document. Qtopia Desktop remembers that state as the base information needed for the next synchronization.

The diffs are performed on field level, so a conflict is created if the user modifies the same field of the same record in multiple plugins or interfaces. The algorithm used for conflict resolution of two records depends on what the user has selected in the Settings->Sync dialog (currently, the user can choose from duplicating records, or letting the pda or the desktop win).

This algorirthm requires two main methods for each plugin:

Data Conversion during Synchronization

Qtopia Desktop requires each plugin to convert it's data between the plugin's internal representation of its data and a generic way to define any data. Qtopia Desktop uses MergeML::Record as a generic storage class for anyone's data. MergeML::Record stores its data in an int-QString QMap, where the int refers to the type of field in the map.

For the core pims (Address Book, Todo List and Calendar), the integer values used in the QMapQString> have been defined in recordfields.h. There are individual record classes for these data sets, which are Contact, Task and Event, respectively. These PIM classes abstract for the developer away from the QMap to more readable methods (such as Contact::setLastName) and allow for the class to perform manipulations on these sets and accessors (i.e. a QString may be used to store an integer value, so this class returns the integer value not the QString).

There are static templated methods provided in mergeml.h that help the developer convert between a lists of QMap<int,QString> and a list of these PIM classes. They are MergeML::convertToML() and MergeML::convertFromML(). These templated methods will be helpfull for the developer when implementing DataSetInterface::data() and DataSetInterface::applyChanges().

Implementing a Plugin

Each plugin implementation needs to define some pure virtual methods defined in the plugin hierarchy. Each plugin must define the following methods:

    QRESULT queryInterface( const QUuid&, QUnknownInterface** );
    Q_REFCOUNT

    QString name() const;
    QString description() const;
    QString version() const;
    QString author() const;

The name() method and queryInterface() are the most important. The description(), version() and author() methods are all purely informational and not currently displayed by Qtopia Desktop but maybe in the future.

The PluginInterface::name() method defines the display name shown in the plugin selection area on the left hand side of the screen. The name() method is purely descriptive for all other interfaces.

The implementation of queryInterface() allows Qtopia Desktop determine what type of interface your plugin implements when it registers your plugin at startup.

In the the interfaces .cpp file, the following code must be present

Q_EXPORT_INTERFACE()
{
    Q_CREATE_INSTANCE( AddressBook )
}

  QRESULT AddressBook::queryInterface( const QUuid &uuid,
    QUnknownInterface** iface )
{
    *iface = 0;
    if ( uuid == IID_QUnknown )
        *iface = (QUnknownInterface*) (PluginInterface*) this;
    else if ( uuid == IID_QComponentInformation )
        *iface = (QComponentInformationInterface*)(PluginInterface *) this;
    else if ( uuid == IID_PalmtopCenterPlugin )
        *iface = (PluginInterface*)this;
    else if ( uuid == IID_DataSetInterface )
        *iface = (DataSetInterface*)this;
    else if ( uuid == IID_MergeInterface )
        *iface = (MergeInterface*)this;
    else
        return QE_NOINTERFACE;

    (*iface)->addRef();
    return QS_OK;
}

The exact implementation of the queryInterface() method depends on which interfaces your plugin is implementing. Each plugin should only have one implementation of queryInterface. Each plugin can implement more than one interface as shown above. (And multiple interfaces can be implemented by the same class as shown above). Every interface other than DataSetInterface implements both QUnknownInterface and QComponentInformationInterface and should return as such as shown above.


Copyright © 2001-2002 TrolltechTrademarks
Qtopia version 1.6.2