void CntFavoritesView::activate( const CntViewParameters aArgs )
{
    if (mView->navigationAction() != mSoftkey)
        mView->setNavigationAction(mSoftkey);
    
    HbMainWindow* window = mView->mainWindow();
    connect(window, SIGNAL(orientationChanged(Qt::Orientation)), this, SLOT(setOrientation(Qt::Orientation)));
    setOrientation(window->orientation());
    
    mContact = new QContact(aArgs.value(ESelectedGroupContact).value<QContact>());
    mViewManager = &mEngine->viewManager();

    HbPushButton *addButton = static_cast<HbPushButton*>(mDocumentLoader.findWidget(QString("cnt_button_add")));
    connect(addButton, SIGNAL(clicked()), this, SLOT(openSelectionPopup()));
    connect(addButton, SIGNAL(longPress(QPointF)), this, SLOT(openSelectionPopup()));
    
    // If no contacts are present, then disable the button 
    QContactDetailFilter filter;
    filter.setDetailDefinitionName(QContactType::DefinitionName, QContactType::FieldType);
    filter.setValue(QLatin1String(QContactType::TypeContact));
   
    QList<QContactLocalId> contactIds = getContactManager()->contactIds(filter);   
    if (contactIds.isEmpty())
    {
        addButton->setEnabled(false); 
    }
    
}
//Factory class
MobExampleActionFactory::MobExampleActionFactory() 
{
    // Next two actions are different, but for same service. These actions can be used
    // also for other services, by changing "Test service". In real plugin all these actions
    // would have been created once for each service. In this example third action is created for
    // different service for testing purposes.
    actionList.append(new MobExampleAction("staticaction1", "Test service"));
    actionList.append(new MobExample2Action("staticaction2", "Test service"));
    // This action is for second service.
    actionList.append(new MobExampleNoDetailAction("nodetailaction", "Test service 2"));
    
    // create several variants of one action and change it behaviour in run time
    MobExampleDynamicAction* nonUiItem = new MobExampleDynamicAction("dynamicaction", "Test service 2");
    nonUiItem->setDefinitionName(QContactOnlineAccount::DefinitionName);
    QContactDetailFilter filter;
    filter.setDetailDefinitionName(QContactOnlineAccount::DefinitionName);
    nonUiItem->setFilter(filter);
    nonUiItem->setIcon(QIcon());
    nonUiItem->setTitleField("");
    nonUiItem->setTitleFieldDetail("");
    actionList.append(nonUiItem);
    
    MobExampleDynamicAction* emptyValueField = new MobExampleDynamicAction("dynamicaction2", "Test service 2");
    emptyValueField->setDefinitionName(QContactOnlineAccount::DefinitionName);
    QContactDetailFilter filter2;
    filter2.setDetailDefinitionName(QContactOnlineAccount::DefinitionName);
    emptyValueField->setFilter(filter2);
    emptyValueField->setIcon(QIcon());
    emptyValueField->setTitleFieldDetail(QContactOnlineAccount::FieldServiceProvider);
    emptyValueField->setValueField("");
    actionList.append(emptyValueField);
}
Ejemplo n.º 3
0
void CntFilterDetail::getTableNameWhereClause(const QContactDetailFilter& detailfilter,
                                              QString& tableName,
                                              QString& sqlWhereClause ,
                                              QContactManager::Error* error) const
{
    //Get the table name and the column name
    QString columnName;
    bool isSubType;

    m_dbInfo.getDbTableAndColumnName(detailfilter.detailDefinitionName(), detailfilter.detailFieldName(), tableName, columnName, isSubType);

    // return if tableName is empty
    if (tableName.isEmpty()) {
        *error = QContactManager::NotSupportedError;
        return;
    }

    //check columnName
    if (columnName.isEmpty()) {
        *error = QContactManager::NotSupportedError;
        return;
    }
    else if (isSubType) {
        sqlWhereClause += columnName;
        sqlWhereClause += " NOT NULL ";
    }
    else {
        sqlWhereClause += ' ' + columnName + ' ';
        QString fieldToUpdate;
        //Update the value depending on the match flag
        updateForMatchFlag(detailfilter,fieldToUpdate,error);
        sqlWhereClause +=  fieldToUpdate;
    }
}
void TestCntDisplayTextFormatter::testFormatter()
{
    QColor color = HbColorScheme::color("qtc_lineedit_selected");
    QColor bg = HbColorScheme::color("qtc_lineedit_marker_normal");
    QString foo = QString("<span style=\"background-color:%1;color:%2\">f</span>oo").arg(bg.name().toUpper()).arg(color.name().toUpper());
    
    QContactDetailFilter filter;
    filter.setDetailDefinitionName( QContactDisplayLabel::DefinitionName );
    filter.setMatchFlags( QContactFilter::MatchStartsWith );
    filter.setValue( "f" );
    
    CntDisplayTextFormatter* format = new CntHTMLDisplayTextFormatter;
    QString result = format->formattedText("foo", filter );
    QVERIFY( foo == result );
    QVERIFY( "" == format->formattedText("", filter) );
    // invalid filter
    QContactRelationshipFilter invalidFilter;
    QVERIFY( "foo" == format->formattedText("foo", invalidFilter) );
    QVERIFY( "" == format->formattedText("", invalidFilter) );
    
    // dummy returns always the given text, dispite of the filter
    CntDisplayTextFormatter* dummy = new CntDummyDisplayTextFormatter;
    QVERIFY( "foo" == dummy->formattedText("foo", filter) );
    QVERIFY( "foo" == dummy->formattedText("foo", invalidFilter) );
    QVERIFY( "" == dummy->formattedText("", filter) );
}
QContactDetailFilter
ut_qtcontacts_trackerplugin_common::testNicknameFilter(const QString &id) const
{
    QContactDetailFilter filter;
    filter.setDetailDefinitionName(QContactNickname::DefinitionName, QContactNickname::FieldNickname);
    filter.setMatchFlags(QContactDetailFilter::MatchStartsWith);
    filter.setValue(uniqueTestId(id));
    return filter;
}
QContactFilter MobExampleAction::contactFilter(const QVariant& value) const
{
    Q_UNUSED(value);
    
    QContactDetailFilter onlineFilter;
    onlineFilter.setDetailDefinitionName(QContactOnlineAccount::DefinitionName, QContactOnlineAccount::FieldSubTypes);
    onlineFilter.setValue(QLatin1String(QContactOnlineAccount::SubTypeImpp));

    return onlineFilter;
}
Ejemplo n.º 7
0
QContactFilter QContactMultiActionFactory::contactFilter(const QContactActionDescriptor& which) const
{
    if (which == m_actionOneDescriptor || which == m_actionTwoDescriptor) {
        QContactDetailFilter retn;
        retn.setDetailDefinitionName(QContactPhoneNumber::DefinitionName, QContactPhoneNumber::FieldNumber);
        return retn;
    }

    return QContactFilter();
}
QContactFilter MobExample2Action::contactFilter(const QVariant& value) const
{
    Q_UNUSED(value);
    
    QContactDetailFilter mobileFilter;
    mobileFilter.setDetailDefinitionName(QContactPhoneNumber::DefinitionName, QContactPhoneNumber::FieldSubTypes); 
    mobileFilter.setValue(QLatin1String(QContactPhoneNumber::SubTypeMobile));

    return mobileFilter;
}
Ejemplo n.º 9
0
bool CntFilterDetail::filterSupported(const QContactFilter& filter) 
{
    bool result = false;
    if (QContactFilter::ContactDetailFilter == filter.type()) {
        QContactDetailFilter detailFilter = static_cast<QContactFilter>(filter);
        if (m_dbInfo.SupportsDetail(detailFilter.detailDefinitionName(),
                detailFilter.detailFieldName())) {
            result = true;
        }
        else if (detailFilter.detailDefinitionName() == QContactPhoneNumber::DefinitionName &&
            detailFilter.detailFieldName() == QContactPhoneNumber::FieldNumber) {
            //special case - phone number matching 
            result = true;
        }
        else if (detailFilter.detailDefinitionName() == QContactType::DefinitionName &&
            detailFilter.detailFieldName() == QContactType::FieldType) {
            //filtering by contact type is supported
            result = true;
        }
        else if (detailFilter.detailDefinitionName() == QContactGuid::DefinitionName &&
            detailFilter.detailFieldName() == QContactGuid::FieldGuid) {
            //filtering by global Uid is supported
            result = true;
        }
    }
    return result;
}
Ejemplo n.º 10
0
void
CDBirthdayController::updateAllBirthdays()
{
    // Fetch any contact with a birthday.
    QContactDetailFilter fetchFilter;
#ifdef USING_QTPIM
    fetchFilter.setDetailType(QContactBirthday::Type);
#else
    fetchFilter.setDetailDefinitionName(QContactBirthday::DefinitionName);
#endif

    fetchContacts(fetchFilter, SLOT(onFullSyncRequestStateChanged(QContactAbstractRequest::State)));
}
Ejemplo n.º 11
0
/*! Adds contacts in  \a newContacts into \a manager, converting categories specified
  with tags into group membership relationships.  Note that this implementation uses the
  synchronous API of QContactManager for clarity.  It is recommended that the asynchronous
  API is used in practice.

  Relationships are added so that if a contact, A, has a tag "b", then a HasMember relationship is
  created between a group contact in the manager, B with display label "b", and contact A.  If there
  does not exist a group contact with display label "b", one is created.
  */
void insertWithGroups(const QList<QContact>& newContacts, QContactManager* manager)
{
    // Cache map from group names to QContactIds
    QMap<QString, QContactId> groupMap;

    foreach (QContact contact, newContacts) {
        if (!manager->saveContact(&contact))
            continue; // In practice, better error handling may be required
        foreach (const QContactTag& tag, contact.details<QContactTag>()) {
            QString groupName = tag.tag();
            QContactId groupId;
            if (groupMap.contains(groupName)) {
                // We've already seen a group with the right name
                groupId = groupMap.value(groupName);
            } else {
                QContactDetailFilter groupFilter;
                groupFilter.setDetailDefinitionName(QContactType::DefinitionName);
                groupFilter.setValue(QLatin1String(QContactType::TypeGroup));
                groupFilter.setMatchFlags(QContactFilter::MatchExactly);
                // In practice, some detail other than the display label could be used
                QContactDetailFilter nameFilter = QContactDisplayLabel::match(groupName);
                QList<QContactLocalId> matchingGroups = manager->contactIds(groupFilter & nameFilter);
                if (!matchingGroups.isEmpty()) {
                    // Found an existing group in the manager
                    QContactId groupId;
                    groupId.setManagerUri(manager->managerUri());
                    groupId.setLocalId(matchingGroups.first());
                    groupMap.insert(groupName, groupId);
                }
                else {
                    // Make a new group
                    QContact groupContact;
                    QContactName name;
                    name.setCustomLabel(groupName);
                    // Beware that not all managers support custom label
                    groupContact.saveDetail(&name);
                    if (!manager->saveContact(&groupContact))
                        continue; // In practice, better error handling may be required
                    groupId = groupContact.id();
                    groupMap.insert(groupName, groupId);
                }
            }
            // Add the relationship
            QContactRelationship rel;
            rel.setFirst(groupId);
            rel.setRelationshipType(QContactRelationship::HasMember);
            rel.setSecond(contact.id());
            manager->saveRelationship(&rel);
        }
    }
}
Ejemplo n.º 12
0
void CntFilterDetail::getTableNameWhereClause(const QContactDetailFilter& detailfilter,
                                              QString& tableName,
                                              QString& sqlWhereClause ,
                                              QContactManager::Error* error) const
{
    //Get the table name and the column name
    QString columnName;
    bool isSubType;

    m_dbInfo.getDbTableAndColumnName(detailfilter.detailDefinitionName(), detailfilter.detailFieldName(), tableName, columnName, isSubType);

    // return if tableName is empty
    if (tableName.isEmpty()) {
        *error = QContactManager::NotSupportedError;
        return;
    }

    //check columnName
    if (columnName.isEmpty()) {
        *error = QContactManager::NotSupportedError;
        return;
    }
    else if (isSubType) {
        sqlWhereClause += columnName;
        sqlWhereClause += " NOT NULL ";
    }
    else if (detailfilter.detailDefinitionName() == QContactFavorite::DefinitionName) {
        bool favoritesSearch = true;
        if (detailfilter.value().canConvert(QVariant::Bool)) {
            if (!detailfilter.value().toBool()) {
                //filter to fetch non-favorite contacts
                favoritesSearch = false;    
            }
        }
        sqlWhereClause += columnName;
        if (favoritesSearch) {
            sqlWhereClause += " NOT NULL ";   
        }
        else {
            sqlWhereClause += " IS NULL ";
        }
    }
    else {
        sqlWhereClause += ' ' + columnName + ' ';
        QString fieldToUpdate;
        //Update the value depending on the match flag
        updateForMatchFlag(detailfilter,fieldToUpdate,error);
        sqlWhereClause +=  fieldToUpdate;
    }
}
Ejemplo n.º 13
0
/*!
 * Updates match flags for columns.
 */
void CntFilterDetail::updateForMatchFlag(const QContactDetailFilter& filter,
                                         QString& fieldToUpdate ,
                                         QContactManager::Error* error) const
{
    // Modify the filed depending on the query
    switch (filter.matchFlags()) {
        case QContactFilter::MatchExactly: {
            // Pattern for MatchExactly:
            // " ='xyz'"
            fieldToUpdate = " ='"
                           + filter.value().toString() + '\'';
            *error = QContactManager::NoError;
            break;
        }
        case QContactFilter::MatchContains: {
            // Pattern for MatchContains:
            // " LIKE '%xyz%'"
            fieldToUpdate = " LIKE '%" + filter.value().toString() + "%'" ;
            *error = QContactManager::NoError;
            break;
        }
        case QContactFilter::MatchStartsWith: {
            // Pattern for MatchStartsWith:
            // " LIKE 'xyz%'"
            fieldToUpdate = " LIKE '" +  filter.value().toString() + "%'"  ;
            *error = QContactManager::NoError;
            break;
        }
        case QContactFilter::MatchEndsWith: {
            // Pattern for MatchEndsWith:
            // " LIKE '%xyz'"
            fieldToUpdate = " LIKE '%" + filter.value().toString() + '\'' ;
            *error = QContactManager::NoError;
            break;
        }
        case QContactFilter::MatchFixedString: {
            *error = QContactManager::NotSupportedError;
            break;
        }
        case QContactFilter::MatchCaseSensitive: {
            *error = QContactManager::NotSupportedError;
            break;
        }
        default: {
            *error = QContactManager::NotSupportedError;
            break;
        }
    }
}
Ejemplo n.º 14
0
QString NotifyProvider::findContact(QString number)
{
        qDebug()<<"findContact";
        QContactDetailFilter f;
        f.setDetailDefinitionName(QContactPhoneNumber::DefinitionName,QContactPhoneNumber::FieldNumber);
        f.setValue(number);
        QList<QContact> list=contacts->contacts(f);
        if (list.length()>0)
        {
            QContactName name=list[0].detail<QContactName>();
            return name.lastName()+" "+name.firstName();
        }
        else
        {return number;}
}
Ejemplo n.º 15
0
bool CntFilterDetail::filterSupported(const QContactFilter& filter) 
{
    bool result = false;
    if (QContactFilter::ContactDetailFilter == filter.type()) {
        QContactDetailFilter detailFilter = static_cast<QContactFilter>(filter);
        if (m_dbInfo.SupportsDetail(detailFilter.detailDefinitionName(),
                detailFilter.detailFieldName())) {
            result = true;
        }
        if (detailFilter.detailDefinitionName() == QContactPhoneNumber::DefinitionName &&
            detailFilter.detailFieldName() == QContactPhoneNumber::FieldNumber) {
            //cpecial case - phone number matching 
            result = true;
        }
    }
    return result;
}
Ejemplo n.º 16
0
void FeedView::startPressed()
{
    if (!mStarted)
	{
        static_cast<HbPushButton*>(mDocumentLoader.findObject("createContactsButton"))->setEnabled(false);

        HbMainWindow* window = mView->mainWindow();
        window->setInteractive(false);
        HbLabel *label = static_cast<HbLabel*>(mDocumentLoader.findObject("textLabel"));
        label->setPlainText("Presence feeder active.");
    
        QContactDetailFilter filter;
        filter.setDetailDefinitionName(QContactType::DefinitionName, QContactType::FieldType);
        QString typeContact = QContactType::TypeContact;
        filter.setValue(typeContact);
    
        QList<QContactLocalId> contacts = mManager->contactIds(filter);
    
        foreach (QContactLocalId id, contacts)
        {
            QContact contact = mManager->contact(id);
    
            QList<QContactOnlineAccount> accounts = contact.details<QContactOnlineAccount>();
    
            foreach (QContactOnlineAccount account, accounts)
            {
                PrcPresenceBuddyInfoQt* buddy = PrcPresenceBuddyInfoQt::createInstance();
                
                QString fullAccount = account.serviceProvider() + ':' + account.accountUri();
                buddy->setIdentity(fullAccount);
                mBuddyList.append(fullAccount);
                
                if (qrand() % 2 == 0)
                {
                    buddy->setAvailability(PrcPresenceBuddyInfoQt::PrcNotAvailable, "hep");
                }
                else
                {
                    buddy->setAvailability(PrcPresenceBuddyInfoQt::PrcAvailable, "hep");
                }
                iWriter->writePresence( *buddy );
                
                delete buddy;
            }
Ejemplo n.º 17
0
bool BusinessCardHandling::findContact(const QString phoneNumber, QContact& c)
{
    // Create QContactManager
    if (!m_contactManager) {
        createContactManager();
    }

    QContact contact;
    QContactDetailFilter phoneFilter;
    phoneFilter.setDetailDefinitionName(QContactPhoneNumber::DefinitionName,
        QContactPhoneNumber::FieldNumber);

#if defined Q_WS_MAEMO_5
    // Workaround for Maemo bug http://bugreports.qt.nokia.com/browse/QTMOBILITY-437
    phoneFilter.setValue(phoneNumber.right(7));
    phoneFilter.setMatchFlags(QContactFilter::MatchContains);
#else
    phoneFilter.setValue(phoneNumber);
    phoneFilter.setMatchFlags(QContactFilter::MatchPhoneNumber);
#endif

    // Find contacts
    QList<QContact> matchingContacts = m_contactManager->contacts(phoneFilter);
    if (matchingContacts.size() > 0) {
        contact = matchingContacts.at(0);
        c = contact;
        return true;
    } else {
        return false;
    }
}
void CntDisplayLabelSqlFilter::createSqlQuery(const QContactDetailFilter& filter,
                        QString& sqlQuery,
                        QContactManager::Error* error)
{
    *error = QContactManager::NoError;
    
    //get the contact fields that should be checked
    CntDisplayLabel displayLabel;
    QList<QPair<QLatin1String, QLatin1String> > contactFields = displayLabel.contactFilterDetails();
    
    //search values
    QStringList searchStrings = filter.value().toStringList();

    //default sql query
    sqlQuery = "SELECT contact_id FROM contact WHERE (type_flags>>24)=0";
    
    //everything ok
    if(!searchStrings.isEmpty() && searchStrings.count() <= contactFields.count() )
    {
        QString subQuery;
        QStringList columns;
        
        //get the column names
        for(int i = 0; i < contactFields.count(); i++)
        {
            columns << columnName(contactFields.at(i));
        }
        
        //single search value
        if(searchStrings.count() == 1)
        {
            createQuerySingleSearchValue(subQuery, searchStrings.at(0), columns);
        }
        
        //multiple search values
        else
        {
            createQueryMultipleSearchValues(subQuery, searchStrings, columns);    
        }
        
        if(!subQuery.isEmpty()){
            sqlQuery += " AND (" + subQuery + ')';
        }
   
        *error = QContactManager::NoError;
    }
    
    //if specified more filter criterias than contact fields return error
    else if(searchStrings.count() > contactFields.count()){
        *error = QContactManager::BadArgumentError;
    }
}
Ejemplo n.º 19
0
QContact DecodedDataDocumentParser::getContact(string name, string surname) { 

	QContactFetchRequest fetchRequest; 
	QContactSortOrder s;  
	s.setDirection(Qt::DescendingOrder);  
	s.setCaseSensitivity(Qt::CaseInsensitive);  
	s.setDetailDefinitionName("Name","FirstName");  
	s.setBlankPolicy(QContactSortOrder::BlanksFirst);  
	QList<QContactSortOrder> sortList;
	sortList.push_back(s); 
 
	//Search filters  
	QContactUnionFilter nameFilter; 
	QContactDetailFilter subFilterF;  
	subFilterF.setDetailDefinitionName("Name", "FirstName");  
	subFilterF.setValue(name.c_str());  
	subFilterF.setMatchFlags(QContactFilter::MatchContains);  
	nameFilter.append(subFilterF); 
	QContactDetailFilter subFilterL;  
	subFilterL.setDetailDefinitionName("Name", "LastName");  
	subFilterL.setValue(surname.c_str());  
	subFilterL.setMatchFlags(QContactFilter::MatchContains);  
	nameFilter.append(subFilterL); 
 
	fetchRequest.setSorting(sortList);  
	fetchRequest.setFilter(nameFilter);  
	fetchRequest.setManager(new QContactManager(&fetchRequest)); 
	//connect(request, SIGNAL(stateChanged(QContactAbstractRequest::State)),  //SLOT(onStateChanged(QContactAbstractRequest::State)) 
	fetchRequest.start(); 
	
	QEventLoop loop;
	QObject::connect(&fetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), &loop, SLOT(quit()));
	loop.exec();

	QList<QContact> list=fetchRequest.contacts();  
	qWarning("Contacts readed %d",list.count()); 

	if(list.count()>0) {   
		return list.at(0);  
	} 
	else {   
		QContact c;
		return c;    
	} 

} 
Ejemplo n.º 20
0
int main(int argc, char  *argv[])
{
    QCoreApplication application(argc, argv);

    QMap<QString, QString> parameters;
    parameters.insert(QString::fromLatin1("mergePresenceChanges"), QString::fromLatin1("false"));

    QContactManager manager(QString::fromLatin1("org.nemomobile.contacts.sqlite"), parameters);

    QContactFetchRequest request;
    request.setManager(&manager);

    // Perform an initial request to ensure the database has been created before we start timing
    request.start();
    request.waitForFinished();

    qint64 elapsedTimeTotal = 0;

    QElapsedTimer asyncTotalTimer;
    asyncTotalTimer.start();

    // Fetch all, no optimization hints
    for (int i = 0; i < 3; ++i) {
        QElapsedTimer timer;
        timer.start();
        request.start();
        request.waitForFinished();

        qint64 elapsed = timer.elapsed();
        qDebug() << i << ": Fetch completed in" << elapsed << "ms";
        elapsedTimeTotal += elapsed;
    }

    // Skip relationships
    QContactFetchHint hint;
    hint.setOptimizationHints(QContactFetchHint::NoRelationships);
    request.setFetchHint(hint);

    for (int i = 0; i < 3; ++i) {
        QElapsedTimer timer;
        timer.start();
        request.start();
        request.waitForFinished();

        qint64 elapsed = timer.elapsed();
        qDebug() << i << ": No-relationships fetch completed in" << elapsed << "ms";
        elapsedTimeTotal += elapsed;
    }

    // Reduce data access
#ifdef USING_QTPIM
    hint.setDetailTypesHint(QList<QContactDetail::DetailType>() << QContactName::Type << QContactAddress::Type);
#else
    hint.setDetailDefinitionsHint(QStringList() << QContactName::DefinitionName << QContactAddress::DefinitionName);
#endif
    request.setFetchHint(hint);

    for (int i = 0; i < 3; ++i) {
        QElapsedTimer timer;
        timer.start();
        request.start();
        request.waitForFinished();

        qint64 elapsed = timer.elapsed();
        qDebug() << i << ": Reduced data fetch completed in" << elapsed << "ms";
        elapsedTimeTotal += elapsed;
    }

    // Reduce number of results
    hint.setMaxCountHint(request.contacts().count() / 8);
    request.setFetchHint(hint);

    for (int i = 0; i < 3; ++i) {
        QElapsedTimer timer;
        timer.start();
        request.start();
        request.waitForFinished();

        qint64 elapsed = timer.elapsed();
        qDebug() << i << ": Max count fetch completed in" << elapsed << "ms";
        elapsedTimeTotal += elapsed;
    }
    qint64 asyncTotalElapsed = asyncTotalTimer.elapsed();



    // Time some synchronous operations.  First, generate the test data.
    qsrand((int)asyncTotalElapsed);
    QList<int> nbrContacts;
    nbrContacts << 10 << 100 << 500 << 1000 << 2000;
    QList<QList<QContact> > testData;
    qDebug() << "\n\n\n\n\n";
    qDebug() << "Generating test data for timings...";
    for (int i = 0; i < nbrContacts.size(); ++i) {
        int howMany = nbrContacts.at(i);
        QList<QContact> newTestData;
        newTestData.reserve(howMany);

        for (int j = 0; j < howMany; ++j) {
            // Use testing sync target, so 'local' won't be modified into 'was_local' via aggregation
            newTestData.append(generateContact(QString::fromLatin1("testing")));
        }

        testData.append(newTestData);
    }


    // Perform the timings - these all create new contacts and assume an "empty" initial database
    QElapsedTimer syncTimer;
    for (int i = 0; i < testData.size(); ++i) {
        QList<QContact> td = testData.at(i);
        qint64 ste = 0;
        qDebug() << "Performing tests for" << td.size() << "contacts:";

        syncTimer.start();
        manager.saveContacts(&td);
        ste = syncTimer.elapsed();
        qDebug() << "    saving took" << ste << "milliseconds (" << ((1.0 * ste) / (1.0 * td.size())) << "msec per contact )";
        elapsedTimeTotal += ste;

        QContactDetailFilter testingFilter;
#ifdef USING_QTPIM
        testingFilter.setDetailType(QContactSyncTarget::Type, QContactSyncTarget::FieldSyncTarget);
#else
        testingFilter.setDetailDefinitionName(QContactSyncTarget::DefinitionName, QContactSyncTarget::FieldSyncTarget);
#endif
        testingFilter.setValue(QString::fromLatin1("testing"));

        QContactFetchHint fh;
        syncTimer.start();
        QList<QContact> readContacts = manager.contacts(testingFilter, QList<QContactSortOrder>(), fh);
        ste = syncTimer.elapsed();
        if (readContacts.size() != td.size()) {
            qWarning() << "Invalid retrieval count:" << readContacts.size() << "expecting:" << td.size();
        }
        qDebug() << "    reading all (" << readContacts.size() << "), all details, took" << ste << "milliseconds (" << ((1.0 * ste) / (1.0 * td.size())) << "msec per contact )";
        elapsedTimeTotal += ste;

#ifdef USING_QTPIM
        fh.setDetailTypesHint(QList<QContactDetail::DetailType>() << QContactDisplayLabel::Type
                << QContactName::Type << QContactAvatar::Type
                << QContactPhoneNumber::Type << QContactEmailAddress::Type);
#else
        fh.setDetailDefinitionsHint(QStringList() << QContactDisplayLabel::DefinitionName
                << QContactName::DefinitionName << QContactAvatar::DefinitionName
                << QContactPhoneNumber::DefinitionName << QContactEmailAddress::DefinitionName);
#endif
        syncTimer.start();
        readContacts = manager.contacts(testingFilter, QList<QContactSortOrder>(), fh);
        ste = syncTimer.elapsed();
        if (readContacts.size() != td.size()) {
            qWarning() << "Invalid retrieval count:" << readContacts.size() << "expecting:" << td.size();
        }
        qDebug() << "    reading all, common details, took" << ste << "milliseconds (" << ((1.0 * ste) / (1.0 * td.size())) << "msec per contact )";
        elapsedTimeTotal += ste;

        fh.setOptimizationHints(QContactFetchHint::NoRelationships);
#ifdef USING_QTPIM
        fh.setDetailTypesHint(QList<QContactDetail::DetailType>());
#else
        fh.setDetailDefinitionsHint(QStringList());
#endif
        syncTimer.start();
        readContacts = manager.contacts(testingFilter, QList<QContactSortOrder>(), fh);
        ste = syncTimer.elapsed();
        if (readContacts.size() != td.size()) {
            qWarning() << "Invalid retrieval count:" << readContacts.size() << "expecting:" << td.size();
        }
        qDebug() << "    reading all, no relationships, took" << ste << "milliseconds (" << ((1.0 * ste) / (1.0 * td.size())) << "msec per contact )";
        elapsedTimeTotal += ste;

#ifdef USING_QTPIM
        fh.setDetailTypesHint(QList<QContactDetail::DetailType>() << QContactDisplayLabel::Type
                << QContactName::Type << QContactAvatar::Type);
#else
        fh.setDetailDefinitionsHint(QStringList() << QContactDisplayLabel::DefinitionName
                << QContactName::DefinitionName << QContactAvatar::DefinitionName);
#endif
        syncTimer.start();
        readContacts = manager.contacts(testingFilter, QList<QContactSortOrder>(), fh);
        ste = syncTimer.elapsed();
        if (readContacts.size() != td.size()) {
            qWarning() << "Invalid retrieval count:" << readContacts.size() << "expecting:" << td.size();
        }
        qDebug() << "    reading all, display details + no rels, took" << ste << "milliseconds (" << ((1.0 * ste) / (1.0 * td.size())) << "msec per contact )";
        elapsedTimeTotal += ste;

        // Read the contacts, selected by ID
        QList<QContactId> idsToRetrieve;
        for (int j = 0; j < td.size(); ++j) {
            idsToRetrieve.append(retrievalId(td.at(j)));
        }

        syncTimer.start();
        readContacts = manager.contacts(idsToRetrieve, fh, 0);
        ste = syncTimer.elapsed();
        if (readContacts.size() != td.size()) {
            qWarning() << "Invalid retrieval count:" << readContacts.size() << "expecting:" << td.size();
        }
        qDebug() << "    reading all by IDs, display details + no rels, took" << ste << "milliseconds (" << ((1.0 * ste) / (1.0 * td.size())) << "msec per contact )";
        elapsedTimeTotal += ste;

        // Read the same set using ID filtering
#ifdef USING_QTPIM
        QContactIdFilter idFilter;
#else
        QContactLocalIdFilter idFilter;
#endif
        idFilter.setIds(idsToRetrieve);

        syncTimer.start();
        readContacts = manager.contacts(idFilter, QList<QContactSortOrder>(), fh);
        ste = syncTimer.elapsed();
        if (readContacts.size() != td.size()) {
            qWarning() << "Invalid retrieval count:" << readContacts.size() << "expecting:" << td.size();
        }
        qDebug() << "    reading all by ID filter, display details + no rels, took" << ste << "milliseconds (" << ((1.0 * ste) / (1.0 * td.size())) << "msec per contact )";
        elapsedTimeTotal += ste;

        // Read the same set, but filter everything out using syncTarget
        QContactDetailFilter aggregateFilter;
#ifdef USING_QTPIM
        aggregateFilter.setDetailType(QContactSyncTarget::Type, QContactSyncTarget::FieldSyncTarget);
#else
        aggregateFilter.setDetailDefinitionName(QContactSyncTarget::DefinitionName, QContactSyncTarget::FieldSyncTarget);
#endif
        aggregateFilter.setValue(QString::fromLatin1("aggregate"));

        syncTimer.start();
        readContacts = manager.contacts(idFilter & aggregateFilter, QList<QContactSortOrder>(), fh);
        ste = syncTimer.elapsed();
        if (readContacts.size() != 0) {
            qWarning() << "Invalid retrieval count:" << readContacts.size() << "expecting:" << 0;
        }
        qDebug() << "    reading all by ID filter & aggregate, display details + no rels, took" << ste << "milliseconds (" << ((1.0 * ste) / (1.0 * td.size())) << "msec per contact )";
        elapsedTimeTotal += ste;

        QContactDetailFilter firstNameStartsA;
#ifdef USING_QTPIM
        firstNameStartsA.setDetailType(QContactName::Type, QContactName::FieldFirstName);
#else
        firstNameStartsA.setDetailDefinitionName(QContactName::DefinitionName, QContactName::FieldFirstName);
#endif
        firstNameStartsA.setValue("A");
        firstNameStartsA.setMatchFlags(QContactDetailFilter::MatchStartsWith);
#ifdef USING_QTPIM
        fh.setDetailTypesHint(QList<QContactDetail::DetailType>());
#else
        fh.setDetailDefinitionsHint(QStringList());
#endif
        syncTimer.start();
        readContacts = manager.contacts(firstNameStartsA, QList<QContactSortOrder>(), fh);
        ste = syncTimer.elapsed();
        qDebug() << "    reading filtered (" << readContacts.size() << "), no relationships, took" << ste << "milliseconds (" << ((1.0 * ste) / (1.0 * td.size())) << "msec per contact )";
        elapsedTimeTotal += ste;

        QList<ContactIdType> idsToRemove;
        for (int j = 0; j < td.size(); ++j) {
            idsToRemove.append(retrievalId(td.at(j)));
        }

        syncTimer.start();
        manager.removeContacts(idsToRemove);
        ste = syncTimer.elapsed();
        qDebug() << "    removing test data took" << ste << "milliseconds (" << ((1.0 * ste) / (1.0 * td.size())) << "msec per contact )";
        elapsedTimeTotal += ste;
    }

    // these tests are slightly different to those above.  They operate on much smaller
    // batches, but occur after the database has already been prefilled with some data.
    QList<int> smallerNbrContacts;
    smallerNbrContacts << 1 << 2 << 5 << 10 << 20 << 50;
    QList<QList<QContact> > smallerTestData;
    qDebug() << "\n\nGenerating smaller test data for prefilled timings...";
    for (int i = 0; i < smallerNbrContacts.size(); ++i) {
        int howMany = smallerNbrContacts.at(i);
        QList<QContact> newTestData;
        newTestData.reserve(howMany);

        for (int j = 0; j < howMany; ++j) {
            newTestData.append(generateContact());
        }

        smallerTestData.append(newTestData);
    }

    // prefill the database
    QList<QContact> prefillData;
    for (int i = 0; i < testData.size() && testData.at(i).size() < 1001; ++i) {
        prefillData = testData.at(i);
    }
    qDebug() << "Prefilling database with" << prefillData.size() << "contacts... this will take a while...";
    manager.saveContacts(&prefillData);
    qDebug() << "Now performing timings (shouldn't get aggregated)...";
    for (int i = 0; i < smallerTestData.size(); ++i) {
        QList<QContact> td = smallerTestData.at(i);
        qint64 ste = 0;
        qDebug() << "Performing tests for" << td.size() << "contacts:";

        syncTimer.start();
        manager.saveContacts(&td);
        ste = syncTimer.elapsed();
        qDebug() << "    saving took" << ste << "milliseconds (" << ((1.0 * ste) / (1.0 * td.size())) << "msec per contact )";
        elapsedTimeTotal += ste;

        QContactFetchHint fh;
        syncTimer.start();
        QList<QContact> readContacts = manager.contacts(QContactFilter(), QList<QContactSortOrder>(), fh);
        ste = syncTimer.elapsed();
        qDebug() << "    reading all (" << readContacts.size() << "), all details, took" << ste << "milliseconds";
        elapsedTimeTotal += ste;

#ifdef USING_QTPIM
        fh.setDetailTypesHint(QList<QContactDetail::DetailType>() << QContactDisplayLabel::Type
                << QContactName::Type << QContactAvatar::Type
                << QContactPhoneNumber::Type << QContactEmailAddress::Type);
#else
        fh.setDetailDefinitionsHint(QStringList() << QContactDisplayLabel::DefinitionName
                << QContactName::DefinitionName << QContactAvatar::DefinitionName
                << QContactPhoneNumber::DefinitionName << QContactEmailAddress::DefinitionName);
#endif
        syncTimer.start();
        readContacts = manager.contacts(QContactFilter(), QList<QContactSortOrder>(), fh);
        ste = syncTimer.elapsed();
        qDebug() << "    reading all, common details, took" << ste << "milliseconds";
        elapsedTimeTotal += ste;

        fh.setOptimizationHints(QContactFetchHint::NoRelationships);
#ifdef USING_QTPIM
        fh.setDetailTypesHint(QList<QContactDetail::DetailType>());
#else
        fh.setDetailDefinitionsHint(QStringList());
#endif
        syncTimer.start();
        readContacts = manager.contacts(QContactFilter(), QList<QContactSortOrder>(), fh);
        ste = syncTimer.elapsed();
        qDebug() << "    reading all, no relationships, took" << ste << "milliseconds";
        elapsedTimeTotal += ste;

#ifdef USING_QTPIM
        fh.setDetailTypesHint(QList<QContactDetail::DetailType>() << QContactDisplayLabel::Type
                << QContactName::Type << QContactAvatar::Type);
#else
        fh.setDetailDefinitionsHint(QStringList() << QContactDisplayLabel::DefinitionName
                << QContactName::DefinitionName << QContactAvatar::DefinitionName);
#endif
        syncTimer.start();
        readContacts = manager.contacts(QContactFilter(), QList<QContactSortOrder>(), fh);
        ste = syncTimer.elapsed();
        qDebug() << "    reading all, display details + no rels, took" << ste << "milliseconds";
        elapsedTimeTotal += ste;

        QContactDetailFilter firstNameStartsA;
#ifdef USING_QTPIM
        firstNameStartsA.setDetailType(QContactName::Type, QContactName::FieldFirstName);
#else
        firstNameStartsA.setDetailDefinitionName(QContactName::DefinitionName, QContactName::FieldFirstName);
#endif
        firstNameStartsA.setValue("A");
        firstNameStartsA.setMatchFlags(QContactDetailFilter::MatchStartsWith);
#ifdef USING_QTPIM
        fh.setDetailTypesHint(QList<QContactDetail::DetailType>());
#else
        fh.setDetailDefinitionsHint(QStringList());
#endif
        syncTimer.start();
        readContacts = manager.contacts(firstNameStartsA, QList<QContactSortOrder>(), fh);
        ste = syncTimer.elapsed();
        qDebug() << "    reading filtered (" << readContacts.size() << "), no relationships, took" << ste << "milliseconds";
        elapsedTimeTotal += ste;

        QList<ContactIdType> idsToRemove;
        for (int j = 0; j < td.size(); ++j) {
            idsToRemove.append(retrievalId(td.at(j)));
        }

        syncTimer.start();
        manager.removeContacts(idsToRemove);
        ste = syncTimer.elapsed();
        qDebug() << "    removing test data took" << ste << "milliseconds (" << ((1.0 * ste) / (1.0 * td.size())) << "msec per contact )";
        elapsedTimeTotal += ste;
    }

    // The next test is about saving contacts which should get aggregated into others.
    // Aggregation is an expensive operation, so we expect these save operations to take longer.
    qDebug() << "\n\nPerforming aggregation tests";
    QList<QContact> contactsToAggregate;
    for (int i = 0; i < 100; ++i) {
        QContact existingContact = prefillData.at(prefillData.size() - 1 - i);
        QContact contactToAggregate;
        QContactSyncTarget newSyncTarget;
        newSyncTarget.setSyncTarget(QString(QLatin1String("fetchtimes-aggregation")));
        QContactName aggName = existingContact.detail<QContactName>(); // ensures it'll get aggregated
        QContactOnlineAccount newOnlineAcct; // new data, which should get promoted up etc.
        newOnlineAcct.setAccountUri(QString(QLatin1String("test-aggregation-%1@fetchtimes")).arg(i));
        contactToAggregate.saveDetail(&newSyncTarget);
        contactToAggregate.saveDetail(&aggName);
        contactToAggregate.saveDetail(&newOnlineAcct);
        contactsToAggregate.append(contactToAggregate);
    }

    syncTimer.start();
    manager.saveContacts(&contactsToAggregate);
    qint64 aggregationElapsed = syncTimer.elapsed();
    int totalAggregatesInDatabase = manager.contactIds().count();
    qDebug() << "Average time for aggregation of" << contactsToAggregate.size() << "contacts (with" << totalAggregatesInDatabase << "existing in database):" << aggregationElapsed
             << "milliseconds (" << ((1.0 * aggregationElapsed) / (1.0 * contactsToAggregate.size())) << " msec per aggregated contact )";
    elapsedTimeTotal += aggregationElapsed;

    // Now perform the test again, this time with more aggregates, to test nonlinearity.
    contactsToAggregate.clear();
    for (int i = 200; i < 400; ++i) {
        QContact existingContact = prefillData.at(prefillData.size() - 1 - i);
        QContact contactToAggregate;
        QContactSyncTarget newSyncTarget;
        newSyncTarget.setSyncTarget(QString(QLatin1String("fetchtimes-aggregation")));
        QContactName aggName = existingContact.detail<QContactName>(); // ensures it'll get aggregated
        QContactOnlineAccount newOnlineAcct; // new data, which should get promoted up etc.
        newOnlineAcct.setAccountUri(QString(QLatin1String("test-aggregation-%1@fetchtimes")).arg(i));
        contactToAggregate.saveDetail(&newSyncTarget);
        contactToAggregate.saveDetail(&aggName);
        contactToAggregate.saveDetail(&newOnlineAcct);
        contactsToAggregate.append(contactToAggregate);
    }

    syncTimer.start();
    manager.saveContacts(&contactsToAggregate);
    aggregationElapsed = syncTimer.elapsed();
    totalAggregatesInDatabase = manager.contactIds().count();
    qDebug() << "Average time for aggregation of" << contactsToAggregate.size() << "contacts (with" << totalAggregatesInDatabase << "existing in database):" << aggregationElapsed
             << "milliseconds (" << ((1.0 * aggregationElapsed) / (1.0 * contactsToAggregate.size())) << " msec per aggregated contact )";
    elapsedTimeTotal += aggregationElapsed;


    // The next test is about updating existing contacts, amongst a large set.
    // We're especially interested in presence updates, as these are common.
    qDebug() << "\n\nPerforming presence update tests:";

    // in the first presence update test, we update a small number of contacts.
    QStringList presenceAvatars = generateAvatarsList();
    QList<QContact> contactsToUpdate;
    for (int i = 0; i < 10; ++i) {
        contactsToUpdate.append(prefillData.at(prefillData.size() - 1 - i));
    }

    // modify the presence, nickname and avatar of the test data
    for (int j = 0; j < contactsToUpdate.size(); ++j) {
        QString genstr = QString::number(j);
        QContact curr = contactsToUpdate[j];
        QContactPresence cp = curr.detail<QContactPresence>();
        QContactNickname nn = curr.detail<QContactNickname>();
        QContactAvatar av = curr.detail<QContactAvatar>();
        cp.setNickname(genstr);
        cp.setCustomMessage(genstr);
        cp.setTimestamp(QDateTime::currentDateTime());
        cp.setPresenceState(static_cast<QContactPresence::PresenceState>(qrand() % 4));
        nn.setNickname(nn.nickname() + genstr);
        av.setImageUrl(genstr + presenceAvatars.at(qrand() % presenceAvatars.size()));
        curr.saveDetail(&cp);
        curr.saveDetail(&nn);
        curr.saveDetail(&av);
        contactsToUpdate.replace(j, curr);
    }

    // perform a batch save.
    syncTimer.start();
    manager.saveContacts(&contactsToUpdate);
    qint64 presenceElapsed = syncTimer.elapsed();
    totalAggregatesInDatabase = manager.contactIds().count();
    qDebug() << "    update ( batch of" << contactsToUpdate.size() << ") presence+nick+avatar (with" << totalAggregatesInDatabase << "existing in database, all overlap):" << presenceElapsed
             << "milliseconds (" << ((1.0 * presenceElapsed) / (1.0 * contactsToUpdate.size())) << " msec per updated contact )";
    elapsedTimeTotal += presenceElapsed;

    // in the second presence update test, we update ALL of the contacts
    // This simulates having a large number of contacts from a single source (eg, a social network)
    // where (due to changed connectivity status) presence updates for the entire set become available.
    contactsToUpdate.clear();
    QDateTime timestamp = QDateTime::currentDateTime();
    for (int j = 0; j < prefillData.size(); ++j) {
        QContact curr = prefillData.at(j);
        QString genstr = QString::number(j) + "2";
        QContactPresence cp = curr.detail<QContactPresence>();
        QContactNickname nn = curr.detail<QContactNickname>();
        QContactAvatar av = curr.detail<QContactAvatar>();
        cp.setNickname(genstr);
        cp.setCustomMessage(genstr);
        cp.setTimestamp(timestamp);
        cp.setPresenceState(static_cast<QContactPresence::PresenceState>((qrand() % 4) + 1));
        nn.setNickname(nn.nickname() + genstr);
        av.setImageUrl(genstr + presenceAvatars.at(qrand() % presenceAvatars.size()));
        curr.saveDetail(&cp);
        curr.saveDetail(&nn);
        curr.saveDetail(&av);
        contactsToUpdate.append(curr);
    }

    // perform a batch save.
    syncTimer.start();
    manager.saveContacts(&contactsToUpdate);
    presenceElapsed = syncTimer.elapsed();
    totalAggregatesInDatabase = manager.contactIds().count();
    qDebug() << "    update ( batch of" << contactsToUpdate.size() << ") presence+nick+avatar (with" << totalAggregatesInDatabase << "existing in database, all overlap):" << presenceElapsed
             << "milliseconds (" << ((1.0 * presenceElapsed) / (1.0 * contactsToUpdate.size())) << " msec per updated contact )";
    elapsedTimeTotal += presenceElapsed;

    // the third presence update test is identical to the previous, but with 2000 prefilled contacts in database.
    qDebug() << "    Adding more prefill data, please wait...";
    QList<QContact> morePrefillData;
    for (int i = 0; i < 1000; ++i) {
        morePrefillData.append(generateContact(QString::fromLatin1("testing")));
    }
    manager.saveContacts(&morePrefillData);

    // now do the updates and save.
    contactsToUpdate.clear();
    timestamp = QDateTime::currentDateTime();
    for (int j = 0; j < prefillData.size(); ++j) {
        QContact curr = prefillData.at(j);
        QString genstr = QString::number(j) + "3";
        QContactPresence cp = curr.detail<QContactPresence>();
        QContactNickname nn = curr.detail<QContactNickname>();
        QContactAvatar av = curr.detail<QContactAvatar>();
        cp.setNickname(genstr);
        cp.setCustomMessage(genstr);
        cp.setTimestamp(timestamp);
        cp.setPresenceState(static_cast<QContactPresence::PresenceState>((qrand() % 4) + 1));
        nn.setNickname(nn.nickname() + genstr);
        av.setImageUrl(genstr + presenceAvatars.at(qrand() % presenceAvatars.size()));
        curr.saveDetail(&cp);
        curr.saveDetail(&nn);
        curr.saveDetail(&av);
        contactsToUpdate.append(curr);
    }
    for (int j = 0; j < morePrefillData.size(); ++j) {
        QContact curr = morePrefillData.at(j);
        QString genstr = QString::number(j) + "3";
        QContactPresence cp = curr.detail<QContactPresence>();
        QContactNickname nn = curr.detail<QContactNickname>();
        QContactAvatar av = curr.detail<QContactAvatar>();
        cp.setNickname(genstr);
        cp.setCustomMessage(genstr);
        cp.setTimestamp(timestamp);
        cp.setPresenceState(static_cast<QContactPresence::PresenceState>((qrand() % 4) + 1));
        nn.setNickname(nn.nickname() + genstr);
        av.setImageUrl(genstr + presenceAvatars.at(qrand() % presenceAvatars.size()));
        curr.saveDetail(&cp);
        curr.saveDetail(&nn);
        curr.saveDetail(&av);
        contactsToUpdate.append(curr);
    }

    // perform a batch save.
    syncTimer.start();
    manager.saveContacts(&contactsToUpdate);
    presenceElapsed = syncTimer.elapsed();
    totalAggregatesInDatabase = manager.contactIds().count();
    qDebug() << "    update ( batch of" << contactsToUpdate.size() << ") presence+nick+avatar (with" << totalAggregatesInDatabase << "existing in database, all overlap):" << presenceElapsed
             << "milliseconds (" << ((1.0 * presenceElapsed) / (1.0 * contactsToUpdate.size())) << " msec per updated contact )";
    elapsedTimeTotal += presenceElapsed;

    // clean up the "more prefill data"
    qDebug() << "    cleaning up extra prefill data, please wait...";
    QList<ContactIdType> morePrefillIds;
    for (int j = 0; j < morePrefillData.size(); ++j) {
        morePrefillIds.append(retrievalId(morePrefillData.at(j)));
    }

    manager.removeContacts(morePrefillIds);

    // the fourth presence update test checks update time for non-overlapping sets of data.
    qDebug() << "    generating non-overlapping / aggregated prefill data, please wait...";
    morePrefillData.clear();
    for (int i = 0; i < 1000; ++i) {
        morePrefillData.append(generateContact("test-presence-4", false)); // false = don't aggregate.
    }
    manager.saveContacts(&morePrefillData);

    // now do the update
    contactsToUpdate.clear();
    timestamp = QDateTime::currentDateTime();
    for (int j = 0; j < morePrefillData.size(); ++j) {
        QContact curr = morePrefillData.at(j);
        QString genstr = QString::number(j) + "4";
        QContactPresence cp = curr.detail<QContactPresence>();
        QContactNickname nn = curr.detail<QContactNickname>();
        QContactAvatar av = curr.detail<QContactAvatar>();
        cp.setNickname(genstr);
        cp.setCustomMessage(genstr);
        cp.setTimestamp(timestamp);
        cp.setPresenceState(static_cast<QContactPresence::PresenceState>((qrand() % 4) + 1));
        nn.setNickname(nn.nickname() + genstr);
        av.setImageUrl(genstr + presenceAvatars.at(qrand() % presenceAvatars.size()));
        curr.saveDetail(&cp);
        curr.saveDetail(&nn);
        curr.saveDetail(&av);
        contactsToUpdate.append(curr);
    }

    // perform a batch save.
    syncTimer.start();
    manager.saveContacts(&contactsToUpdate);
    presenceElapsed = syncTimer.elapsed();
    totalAggregatesInDatabase = manager.contactIds().count();
    qDebug() << "    update ( batch of" << contactsToUpdate.size() << ") presence+nick+avatar (with" << totalAggregatesInDatabase << "existing in database, no overlap):" << presenceElapsed
             << "milliseconds (" << ((1.0 * presenceElapsed) / (1.0 * contactsToUpdate.size())) << " msec per updated contact )";
    elapsedTimeTotal += presenceElapsed;

    // clean up the "more prefill data"
    qDebug() << "    cleaning up extra prefill data, please wait...";
#ifdef USING_QTPIM
    morePrefillIds.clear();
    for (int j = 0; j < morePrefillData.size(); ++j) {
        morePrefillIds.append(morePrefillData.at(j).id());
    }
#else
    morePrefillIds.clear();
    for (int j = 0; j < morePrefillData.size(); ++j) {
        morePrefillIds.append(morePrefillData.at(j).localId());
    }
#endif
    manager.removeContacts(morePrefillIds);

    // the fifth presence update test is similar to the above except that half of
    // the extra contacts have a (high) chance of being aggregated into an existing contact.
    // So, database should have 2000 constituents, 1000 from "local", 1000 from "test-presence-5"
    // with 1500 aggregates (about 500 of test-presence-5 contacts will share an aggregate with
    // a local contact).  TODO: check what happens if multiple aggregates for local contacts
    // could possibly match a given test-presence-5 contact (which is possible, since the backend
    // never aggregates two contacts from the same sync source...)
    qDebug() << "    generating partially-overlapping / aggregated prefill data, please wait...";
    morePrefillData.clear();
    for (int i = 0; i < 1000; ++i) {
        if (i < 500) {
            morePrefillData.append(generateContact("test-presence-5", false)); // false = don't aggregate.
        } else {
            morePrefillData.append(generateContact("test-presence-5", true));  // true = possibly aggregate.
        }
    }
    manager.saveContacts(&morePrefillData);

    // now do the update
    contactsToUpdate.clear();
    timestamp = QDateTime::currentDateTime();
    for (int j = 0; j < morePrefillData.size(); ++j) {
        QContact curr = morePrefillData.at(j);
        QString genstr = QString::number(j) + "5";
        QContactPresence cp = curr.detail<QContactPresence>();
        QContactNickname nn = curr.detail<QContactNickname>();
        QContactAvatar av = curr.detail<QContactAvatar>();
        cp.setNickname(genstr);
        cp.setCustomMessage(genstr);
        cp.setTimestamp(timestamp);
        cp.setPresenceState(static_cast<QContactPresence::PresenceState>((qrand() % 4) + 1));
        nn.setNickname(nn.nickname() + genstr);
        av.setImageUrl(genstr + presenceAvatars.at(qrand() % presenceAvatars.size()));
        curr.saveDetail(&cp);
        curr.saveDetail(&nn);
        curr.saveDetail(&av);
        contactsToUpdate.append(curr);
    }

    // perform a batch save.
    syncTimer.start();
    manager.saveContacts(&contactsToUpdate);
    presenceElapsed = syncTimer.elapsed();
    totalAggregatesInDatabase = manager.contactIds().count();
    qDebug() << "    update ( batch of" << contactsToUpdate.size() << ") presence+nick+avatar (with" << totalAggregatesInDatabase << "existing in database, 500 overlap):" << presenceElapsed
             << "milliseconds (" << ((1.0 * presenceElapsed) / (1.0 * contactsToUpdate.size())) << " msec per updated contact )";
    elapsedTimeTotal += presenceElapsed;

    // the sixth presence update test is identical to the fifth test, except that we ONLY
    // update the presence status (not nickname or avatar).
    morePrefillData = contactsToUpdate;
    contactsToUpdate.clear();
    for (int j = 0; j < morePrefillData.size(); ++j) {
        QContact curr = morePrefillData.at(j);
        QContactPresence cp = curr.detail<QContactPresence>();
        cp.setPresenceState(static_cast<QContactPresence::PresenceState>((qrand() % 4) + 1));
        curr.saveDetail(&cp);
        contactsToUpdate.append(curr);
    }

    // perform a batch save.
    syncTimer.start();
    manager.saveContacts(&contactsToUpdate);
    presenceElapsed = syncTimer.elapsed();
    totalAggregatesInDatabase = manager.contactIds().count();
    qDebug() << "    update ( batch of" << contactsToUpdate.size() << ") presence only (with" << totalAggregatesInDatabase << "existing in database, 500 overlap):" << presenceElapsed
             << "milliseconds (" << ((1.0 * presenceElapsed) / (1.0 * contactsToUpdate.size())) << " msec per updated contact )";
    elapsedTimeTotal += presenceElapsed;

    // the seventh presence update test is identical to the 6th test, except that
    // we also pass a "detail type mask" to the update.  This allows the backend
    // to perform optimisation based upon which details are modified.
    morePrefillData = contactsToUpdate;
    contactsToUpdate.clear();
    for (int j = 0; j < morePrefillData.size(); ++j) {
        QContact curr = morePrefillData.at(j);
        QContactPresence cp = curr.detail<QContactPresence>();
        cp.setPresenceState(static_cast<QContactPresence::PresenceState>((qrand() % 4) + 1));
        curr.saveDetail(&cp);
        contactsToUpdate.append(curr);
    }

    // perform a batch save.
#ifdef USING_QTPIM
    QList<QContactDetail::DetailType> typeMask;
    typeMask << QContactDetail::TypePresence;
#else
    QStringList typeMask;
    typeMask << QString(QLatin1String(QContactPresence::DefinitionName));
#endif
    syncTimer.start();
    manager.saveContacts(&contactsToUpdate, typeMask);
    presenceElapsed = syncTimer.elapsed();
    totalAggregatesInDatabase = manager.contactIds().count();
    qDebug() << "    update ( batch of" << contactsToUpdate.size() << ") masked presence only (with" << totalAggregatesInDatabase << "existing in database, 500 overlap):" << presenceElapsed
             << "milliseconds (" << ((1.0 * presenceElapsed) / (1.0 * contactsToUpdate.size())) << " msec per updated contact )";
    elapsedTimeTotal += presenceElapsed;

    // clean up the "more prefill data"
    qDebug() << "    cleaning up extra prefill data, please wait...";
#ifdef USING_QTPIM
    morePrefillIds.clear();
    for (int j = 0; j < morePrefillData.size(); ++j) {
        morePrefillIds.append(morePrefillData.at(j).id());
    }
#else
    morePrefillIds.clear();
    for (int j = 0; j < morePrefillData.size(); ++j) {
        morePrefillIds.append(morePrefillData.at(j).localId());
    }
#endif
    manager.removeContacts(morePrefillIds);

    qDebug() << "\n\nCumulative elapsed time:" << elapsedTimeTotal << "milliseconds";
    return 0;
}