MeeGo 1.2 Harmattan Developer Documentation Develop for the Nokia N9

Example of updating Events feed from an application

This section describes an example version of event feed implementation using Synchronization Framework. Since the SyncFW plugin and the actual UI application have separate operational areas, they are created as subprojects inside a parent project.

To create the parent project:

  1. Create a New Project in Qt Creator.
  2. Select the project type as Other Project --> Subdirs Project.
  3. Enter the name of the project.
  4. Select at least the Harmattan target.
  5. Finish.

To create the shared library project:

  1. Right-click the parent project created above, and select New Subproject....
  2. Select the project type as Other Project --> C++ Project.
  3. Verify that the type is Shared Library.
  4. Select QtDBus and QtNetwork as additional modules.
  5. Finish.

This example only describes how the SyncFW plugin is created, creating the UI part of the application is up to you.

Project file

The project file (.pro) looks roughly similar as this, but you need to add the missing fields from the below project file example. Pay careful attention that the CONFIG += meegotouchevents string and the install paths are correct.

TEMPLATE = lib
TARGET = example-client
DEPENDPATH += . 
INCLUDEPATH += .  \
                 /usr/include/libsynccommon \
                 /usr/include/libsyncprofile 

LIBS += -lsyncpluginmgr -lsyncprofile

CONFIG += debug plugin meegotouchevents  

QT += dbus network
QT -= gui

#input
HEADERS += \
           EventsExample.h 
SOURCES += \
           EventsExample.cpp 

QMAKE_CXXFLAGS = -Wall \
    -g \
    -Wno-cast-align \
    -O2 -finline-functions 

#install
target.path = /usr/lib/sync/ 

client.path = /etc/sync/profiles/client 
client.files = xml/example.xml

sync.path = /etc/sync/profiles/sync
sync.files = xml/sync/*

service.path = /etc/sync/profiles/service
service.files = xml/service/*

settingsdesktop.path = /usr/share/duicontrolpanel/desktops
settingsdesktop.files = settings/feedexample.desktop

settingsxml.path = /usr/share/duicontrolpanel/uidescriptions
settingsxml.files = settings/feedexample.xml

INSTALLS += target client sync service settingsdesktop settingsxml

XML files for Synchronization Framework

The example implementation has three XML files for setting up the SyncFW. Since all the files need to have an identical name, they are placed into different directories within the xml subdirectory of the main project. The name used for the files in this example is example.

The file paths in the development environment and on the device after installation are defined in the project file. See the #install section in the project file source above.

xml/example.xml

This file informs the SyncFW what library it needs to load. The name of the client library follows the convention lib[profilename]-client.so, which in this case is libexample-client.so.

<?xml version="1.0" encoding="UTF-8"?>
<profile name="example" type="client" > 
</profile>

xml/service/example.xml

This file loads the client plugin for the SyncFW service and activates it with the online key attribute.

<?xml version="1.0" encoding="UTF-8"?>
<profile  name="example" type="service">
   <key name="destinationtype" value="online"/>
   <profile name="example" type="client" >
   </profile>
</profile>

xml/sync/example.xml

This file sets the synchronization parameters and profile name. Detailed descriptions are entered as XML comments in the snippet below:

<?xml version="1.0" encoding="UTF-8"?>
<profile name="example" type="sync" >
   <!--This key is used for display name of the  profile in UI -->        
    <key name="displayname" value="Example"/>
    <!--This key is used to tell sync-fw if the profile is enabled or not -->     
    <key name="enabled" value="true" />
    <!--This key is used if accounts fw is used -->       
    <key name="use_accounts" value="false" />
    <!--This key is used if profile needs to be hidden in UI -->  
    <key name="hidden" value="true" />
    <!--This key is used to tell the sync-fw which service to use -->     
    <profile type="service" name="example" >
    </profile>
    <!--This is used by sync-fw to set customized scheduling information for the profile -->
   <schedule enabled="true" interval="15" days="1,2,3,4,5,6,7" syncconfiguredtime="" time="">
       <rush begin="00:00:00" enabled="true" interval="15" end="17:00:00" days="1,2,3,4,5,6,7"/>
   </schedule>
</profile>

Header file

The header file defines a constructor for the event feed updater.

#include <libsyncpluginmgr/ClientPlugin.h>
#include <libsyncprofile/SyncResults.h>

class EventsExample : public Buteo::ClientPlugin
{
    Q_OBJECT;
public:
    EventsExample( const QString& aPluginName,
                   const Buteo::SyncProfile& aProfile,
                   Buteo::PluginCbInterface *aCbInterface );

    virtual ~EventsExample();
    virtual bool init();
    virtual bool uninit();
    virtual bool startSync();
    virtual void abortSync(Sync::SyncStatus aStatus = Sync::SYNC_ABORTED);
    virtual Buteo::SyncResults getSyncResults() const;
    virtual bool cleanUp();

public slots:
    virtual void connectivityStateChanged( Sync::ConnectivityType aType,
                                          bool aState );

protected slots:      
    void syncSuccess();
    void syncFailed();
    void updateFeed();
private:
    void updateResults(const Buteo::SyncResults &aResults);
private:
    QMap<QString, QString>      iProperties;
    Buteo::SyncResults          iResults;
};

extern "C" EventsExample* createPlugin( const QString& aPluginName,
                                       const Buteo::SyncProfile& aProfile,
                                       Buteo::PluginCbInterface *aCbInterface );

extern "C" void destroyPlugin( EventsExample *aClient ); 

#endif  //  EVENTSEXAMPLE_H

Source file

The source file contains all the implementations of the event feed item handling and interaction with Synchronization Framework.

#include "EventsExample.h"
#include <libsyncpluginmgr/PluginCbInterface.h>
#include <LogMacros.h>
#include <QTimer>
#include <QDateTime>
#include <QMap>
#include <QUrl>
#include <meventfeed.h> 

extern "C" EventsExample* createPlugin(const QString& aPluginName, 
                                       const Buteo::SyncProfile& aProfile,
                                       Buteo::PluginCbInterface *aCbInterface) 
{
    return new EventsExample(aPluginName, aProfile, aCbInterface);
}

extern "C" void destroyPlugin(EventsExample*aClient) 
{
    delete aClient;
}

EventsExample::EventsExample(const QString& aPluginName, 
                const Buteo::SyncProfile& aProfile,
                Buteo::PluginCbInterface *aCbInterface) :
                ClientPlugin(aPluginName, aProfile, aCbInterface)
{
   FUNCTION_CALL_TRACE;
}

EventsExample::~EventsExample() 
{
        FUNCTION_CALL_TRACE;
}

bool EventsExample::init() 
{
    FUNCTION_CALL_TRACE;

   //The sync profiles can have some specific key/value pairs this info
   //can be accessed by this method.
   iProperties = iProfile.allNonStorageKeys();

   //return false - if error 
   //syncfw will call this method first if the plugin is able to initialize properly 
   //and its ready for sync it should return 'true' in case of any error return false.
   return true;
}

bool EventsExample::uninit()
{
   FUNCTION_CALL_TRACE;
   // called before unloading the plugin , the plugin should clean up 
   return true;
}

bool EventsExample::startSync()
{
   FUNCTION_CALL_TRACE;

   // This method is called after init(), the plugin is expected to return 
   // either true or false based on if the sync was started successfully or 
   // it failed for some reason
   //call appropriate slots based on the status of operation success/failed...
   //updateFeed Its just a helper function. 
   QTimer::singleShot(60000, this, SLOT(updateFeed()));
   
   return true;
}

void EventsExample::abortSync(Sync::SyncStatus aStatus)
{
   FUNCTION_CALL_TRACE;
   Q_UNUSED(aStatus);
   // This method is called if used cancels the 
   // sync in between , with the applet use case 
   // it should not ideally happen as there is no UI
   // in case of device sync and accounts sync we have 
   // a cancel button 
}

bool EventsExample::cleanUp() 
{
   FUNCTION_CALL_TRACE;

   // this method is called in case of account being deleted
   // or the profile being deleted from UI in case of applet
   // it will not be called ....need to check as if there 
   // can be any use case for this
   return true;
}

Buteo::SyncResults EventsExample::getSyncResults() const 
{
        FUNCTION_CALL_TRACE;
        return iResults;
}

void EventsExample::connectivityStateChanged(Sync::ConnectivityType aType,
                bool aState) 
{
   FUNCTION_CALL_TRACE;
   // This function notifies of the plugin of any connectivity related state changes
   LOG_DEBUG("Received connectivity change event:" << aType << " changed to " << aState);
   if ((aType == Sync::CONNECTIVITY_INTERNET) && (aState == false)) {
       // Network disconnect!!
   }
}

void EventsExample::syncSuccess()
{
   FUNCTION_CALL_TRACE;
   updateResults(Buteo::SyncResults(QDateTime::currentDateTime(), Buteo::SyncResults::SYNC_RESULT_SUCCESS, Buteo::SyncResults::NO_ERROR));
   //Notify Sync FW of result - Now sync fw will call uninit and then will unload plugin
   emit success(getProfileName(), "Success!!");
}

void EventsExample::syncFailed()
{
   FUNCTION_CALL_TRACE;
   //Notify Sync FW of result - Now sync fw will call uninit and then will unload plugin
   updateResults(Buteo::SyncResults(QDateTime::currentDateTime(),
                 Buteo::SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::ABORTED));
   emit error(getProfileName(), "Error!!", Buteo::SyncResults::SYNC_RESULT_FAILED);
}

void EventsExample::updateResults(const Buteo::SyncResults &aResults)
{
   FUNCTION_CALL_TRACE;
   iResults = aResults;
   iResults.setScheduled(true);
}

void EventsExample::updateFeed()
{
   FUNCTION_CALL_TRACE;
   QMap <QString, QVariant> parameters;
   //Test parameters

   bool success = false;
   //Ok assuming that we now have the data that needs to be updated to event feed
   qlonglong id  = MEventFeed::instance()->addItem(QString("icon-m-transfer-sync"),
                           QString("EventsExample"),
                           QString("Event Feed updated"),
                           QStringList(),
                           QDateTime::currentDateTime(),
                           QString(),
                           false,
                           QUrl(),
                           QString("SyncFW-event-example"),
                           QString("SyncFW and event feed example app"));
   if (id != -1) {
            success = true;     
   }
   if(success)
       syncSuccess();
   else
        syncFailed();    
}

Additional files

In addition to the basic application functionality, consider adding more features such as a Control Panel Applet for setting up event feed behaviour in the application and some additional installation scripts to register the application's SyncFW plugin immediately after installing the application.

Setting up Control Panel Applet

To allow the user to customize the event feed behaviour in the application, create an Applet in the device Control Panel. The Control Panel contains a separate category for setting up event feed updates in all applications that use the event feed feature.

The Control Panel Applet is created with two files, a .desktop file and an XML file. They have been included in the example project file as attributes settingsxml and settingsdesktop.

.desktop file

[Desktop Entry]
Type=ControlPanelApplet
Name=Event feed example
X-logical-id=feedexample
X-translation-catalog=feedexamplecatalog
Icon=
X-Maemo-Service=com.nokia.DuiControlPanel
X-Maemo-Method=com.nokia.DuiControlPanelIf.appletPage
X-Maemo-Object-Path=/
# this has to be identical to the value in Name
X-Maemo-Fixed-Args=Event feed Example

[DUI]
X-DUIApplet-Applet=libdeclarative.so

[DCP]
Category=Events Feed 
Order=100
Part=feedexample.xml

The important definitions in this file are in the [DCP] section. Category=Events Feed is required to place the settings applet in the correct location within the Control Panel and the Part refers to the name of the XML file that contains the settings that can be set within the Settings Applet.

XML file

The available settings that affect the application event feed's behaviour are defined in the XML file. It contains a list of elements such as sliders and boolean toggles that are then displayed in the Applet for the user to interact with. The example file is named feedexample.xml and it only contains a single boolean toggle that enables and disables the feed.

<settings>
<boolean key="/apps/ControlPanel/FeedExample/EnableFeed" title="Publish to Feed"></boolean>
</settings>

Installation scripts

The basic installation of this example creates a completely functional application, but the SyncFW plugin is loaded and the synchronization feature enabled only after the device has been restarted. You can skip restarting the device by adding two installation scripts in the packaged file, postinst and prerm. The first script is run in the post-installation phase, and the second one in the pre-uninstall phase.

postinst

#!/bin/sh

/usr/bin/aegis-exec -s -u user dbus-send --dest=com.meego.msyncd --print-reply /synchronizer com.meego.msyncd.installPlugin string:'example'

# Make sure the installation is always considered successful
exit 0

prerm

#!/bin/sh

/usr/bin/aegis-exec -s -u user dbus-send --dest=com.meego.msyncd --print-reply /synchronizer com.meego.msyncd.uninstallPlugin string:'example'

# Clean up the feed items published by this application
/usr/bin/aegis-exec -s -u user dbus-send --dest=com.nokia.home.EventFeed --print-reply /eventfeed com.nokia.home.EventFeed.removeItemsBySourceName string:'SyncFW-event-example'

# Make sure the uninstallation is always considered successful
exit 0

The scripts launch a D-Bus call for the msyncd daemon during install and uninstall that notify the Synchronization Framework immediately about the new plugin or its removal. The prerm script also handles the necessary cleanup and removes all the feed items published by the application.