void TestSimPlugin::testEmpty() { QContactManager &m(m_controller->contactManager()); // Add the Forrest Gump contact manually QContact forrest; QContactNickname n; n.setNickname("Forrest Gump"); forrest.saveDetail(&n); QContactSyncTarget st; st.setSyncTarget(QStringLiteral("sim-test")); forrest.saveDetail(&st); QVERIFY(m.saveContact(&forrest)); QCOMPARE(getAllSimContacts(m).count(), 1); QCOMPARE(m_controller->busy(), false); // Process an empty VCard m_controller->simPresenceChanged(true); m_controller->vcardDataAvailable(QString()); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); // No-longer-present contact should be removed QList<QContact> simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 0); }
void TestSimPlugin::testAddAndRemove() { QContactManager &m(m_controller->contactManager()); // Add the Forrest Gump contact manually QContact forrest; QContactNickname n; n.setNickname("Forrest Gump"); forrest.saveDetail(&n); QContactSyncTarget st; st.setSyncTarget(QStringLiteral("sim-test")); forrest.saveDetail(&st); QVERIFY(m.saveContact(&forrest)); QCOMPARE(getAllSimContacts(m).count(), 1); QCOMPARE(m_controller->busy(), false); // Process a VCard set not containing Forrest Gump but another contact m_controller->simPresenceChanged(true); m_controller->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Whittaker\n" "TEL;TYPE=HOME,VOICE:(404) 555-1234\n" "END:VCARD\n")); QTRY_VERIFY(m_controller->busy() == false); // Forrest Gump should be removed, Forrest Whittaker added QList<QContact> simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).detail<QContactNickname>().nickname(), QStringLiteral("Forrest Whittaker")); }
void SeasidePerson::setNickname(const QString &name) { QContactNickname nameDetail = mContact.detail<QContactNickname>(); nameDetail.setNickname(name); mContact.saveDetail(&nameDetail); emit nicknameChanged(); recalculateDisplayLabel(); }
void ut_qtcontacts_trackerplugin_common::setTestNicknameToContact(QContact &contact, const QString &id) const { QContactNickname nicknameDetail; const QString nickname = makeUniqueName(id); nicknameDetail.setNickname(nickname); QVERIFY(contact.saveDetail(&nicknameDetail)); }
void TestSimPlugin::testClear() { QContactManager &m(m_controller->contactManager()); // Add two contacts manually QContact gump; QContactNickname n; n.setNickname("Forrest Gump"); gump.saveDetail(&n); QContactSyncTarget st; st.setSyncTarget(QStringLiteral("sim-test")); gump.saveDetail(&st); QContact whittaker; n.setNickname("Forrest Whittaker"); whittaker.saveDetail(&n); st.setSyncTarget(QStringLiteral("sim-test")); whittaker.saveDetail(&st); QVERIFY(m.saveContact(&gump)); QVERIFY(m.saveContact(&whittaker)); QCOMPARE(getAllSimContacts(m).count(), 2); QCOMPARE(m_controller->busy(), false); m_controller->simPresenceChanged(true); // Report the SIM card removed m_controller->simPresenceChanged(false); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); // All sim contacts should be removed QList<QContact> simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 0); }
QContactDetail *CntTransformNickname::transformItemField(const CContactItemField& field, const QContact &contact) { QContactNickname *name = new QContactNickname(contact.detail<QContactNickname>()); CContactTextField* storage = field.TextStorage(); QString nameValue = QString::fromUtf16(storage->Text().Ptr(), storage->Text().Length()); for (int i = 0; i < field.ContentType().FieldTypeCount(); i++) { //Prefix if (field.ContentType().FieldType(i) == KUidContactFieldSecondName) { name->setNickname(nameValue); } } return name; }
/*! Parses SIM contacts in TLV format. * * \param rawData SIM contacts in TLV format. * \return List of contacts. */ QList<QContact> CntSimStorePrivate::decodeSimContactsL(TDes8& rawData) const { PbkPrintToLog(_L("CntSymbianSimEngine::decodeSimContactsL() - IN")); QList<QContact> fetchedContacts; QContact currentContact; TBuf16<KDataClientBuf> buffer; TPtrC16 bufPtr(buffer); TUint8 tagValue(0); CPhoneBookBuffer::TPhBkTagType dataType; bool isAdditionalNumber = false; CPhoneBookBuffer* pbBuffer = new(ELeave) CPhoneBookBuffer(); CleanupStack::PushL(pbBuffer); pbBuffer->Set(&rawData); pbBuffer->StartRead(); while (pbBuffer->GetTagAndType(tagValue, dataType) == KErrNone) { switch (tagValue) { case RMobilePhoneBookStore::ETagPBAdnIndex: { //save contact's id (SIM card index) and manager's name TUint16 index; if (pbBuffer->GetValue(index) == KErrNone) { QScopedPointer<QContactId> contactId(new QContactId()); contactId->setLocalId(index); contactId->setManagerUri(m_managerUri); currentContact.setId(*contactId); } isAdditionalNumber = false; break; } case RMobilePhoneBookStore::ETagPBTonNpi: { // Note, that TON info can be incorporated into the phone number by Etel // implementation (TSY). E.g. this is the case with Nokia TSY. // Here general case is implemented. // Check number type, we are only interested if it's international or not. // We assume here that ETagPBTonNpi always comes after ETagPBNumber, not before. TUint8 tonNpi; if (pbBuffer->GetValue(tonNpi) == KErrNone) { TUint8 intFlag = (tonNpi & KEtsiTonPosition) >> 4; if (intFlag == 1) { //international number format, append "+" to the last //saved number QList<QContactDetail> phoneNumbers = currentContact.details( QContactPhoneNumber::DefinitionName); if (phoneNumbers.count() > 0) { QContactPhoneNumber lastNumber = static_cast<QContactPhoneNumber>( phoneNumbers.at(phoneNumbers.count() - 1)); QString number = lastNumber.number(); number.insert(0, "+"); lastNumber.setNumber(number); if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&lastNumber); currentContact.saveDetail(&lastNumber); } } } // We have rearched to the end of the number, // invalidate additional number flag. isAdditionalNumber = false; break; } case RMobilePhoneBookStore::ETagPBText: { if (pbBuffer->GetValue(bufPtr) == KErrNone) { if (isAdditionalNumber) { // For additional number bufPtr contains number alpha string, // this is ignored currently } else { // Contact name otherwise QContactName name; QString nameString = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length()); name.setCustomLabel(nameString); if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&name); currentContact.saveDetail(&name); QContactManager::Error error(QContactManager::NoError); m_engine.setContactDisplayLabel(¤tContact, m_engine.synthesizedDisplayLabel(currentContact, &error)); } } break; } case RMobilePhoneBookStore::ETagPBSecondName: { if (pbBuffer->GetValue(bufPtr) == KErrNone) { QContactNickname nickName; QString name = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length()); nickName.setNickname(name); if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&nickName); currentContact.saveDetail(&nickName); } break; } case RMobilePhoneBookStore::ETagPBNumber: { if (pbBuffer->GetValue(bufPtr) == KErrNone) { QContactPhoneNumber phoneNumber; QString number = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length()); phoneNumber.setNumber(number); if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&phoneNumber); currentContact.saveDetail(&phoneNumber); } break; } case RMobilePhoneBookStore::ETagPBAnrStart: { // This tag should precede every additional number entry isAdditionalNumber = true; break; } case RMobilePhoneBookStore::ETagPBEmailAddress: { if (pbBuffer->GetValue(bufPtr) == KErrNone) { QContactEmailAddress email; QString emailAddress = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length()); email.setEmailAddress(emailAddress); if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&email); currentContact.saveDetail(&email); } break; } case RMobilePhoneBookStore::ETagPBNewEntry: { // This signals the end of the entry and is a special case // which will be handled below. break; } default: { // An unsupported field type - just skip this value pbBuffer->SkipValue(dataType); break; } } //switch // save contact to the array of contact to be returned if the whole entry was extracted if ((tagValue == RMobilePhoneBookStore::ETagPBNewEntry && currentContact.localId() > 0) || (pbBuffer->RemainingReadLength() == 0 && currentContact.localId() > 0)) { fetchedContacts.append(currentContact); //clear current contact currentContact.clearDetails(); QScopedPointer<QContactId> contactId(new QContactId()); contactId->setLocalId(0); contactId->setManagerUri(QString()); currentContact.setId(*contactId); } } //while
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; }
void AsyncRequestExample::performRequests() { //! [Creating a new contact in a manager] QContact exampleContact; QContactName nameDetail; nameDetail.setFirstName("Adam"); nameDetail.setLastName("Unlikely"); QContactPhoneNumber phoneNumberDetail; phoneNumberDetail.setNumber("+123 4567"); exampleContact.saveDetail(&nameDetail); exampleContact.saveDetail(&phoneNumberDetail); // save the newly created contact in the manager connect(&m_contactSaveRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(contactSaveRequestStateChanged(QContactAbstractRequest::State))); m_contactSaveRequest.setManager(m_manager); m_contactSaveRequest.setContacts(QList<QContact>() << exampleContact); m_contactSaveRequest.start(); //! [Creating a new contact in a manager] m_contactSaveRequest.waitForFinished(); //! [Creating a new contact in a manager waiting until finished] m_contactSaveRequest.setManager(m_manager); m_contactSaveRequest.setContacts(QList<QContact>() << exampleContact); m_contactSaveRequest.start(); m_contactSaveRequest.waitForFinished(); QList<QContact> savedContacts = m_contactSaveRequest.contacts(); //! [Creating a new contact in a manager waiting until finished] //! [Filtering contacts from a manager] connect(&m_contactFetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(contactFetchRequestStateChanged(QContactAbstractRequest::State))); m_contactFetchRequest.setManager(m_manager); m_contactFetchRequest.setFilter(QContactPhoneNumber::match("+123 4567")); m_contactFetchRequest.start(); //! [Filtering contacts from a manager] m_contactFetchRequest.waitForFinished(); //! [Retrieving an existing contact from a manager] QContactLocalIdFilter idListFilter; idListFilter.setIds(QList<QContactLocalId>() << exampleContact.localId()); m_contactFetchRequest.setManager(m_manager); m_contactFetchRequest.setFilter(idListFilter); m_contactFetchRequest.start(); //! [Retrieving an existing contact from a manager] m_contactFetchRequest.waitForFinished(); //! [Updating an existing contact in a manager] phoneNumberDetail.setNumber("+123 9876"); exampleContact.saveDetail(&phoneNumberDetail); m_contactSaveRequest.setManager(m_manager); m_contactSaveRequest.setContacts(QList<QContact>() << exampleContact); m_contactSaveRequest.start(); //! [Updating an existing contact in a manager] m_contactFetchRequest.waitForFinished(); //! [Removing a contact from a manager] connect(&m_contactRemoveRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(contactRemoveRequestStateChanged(QContactAbstractRequest::State))); m_contactRemoveRequest.setManager(m_manager); m_contactRemoveRequest.setContactIds(QList<QContactLocalId>() << exampleContact.localId()); m_contactRemoveRequest.start(); //! [Removing a contact from a manager] m_contactFetchRequest.waitForFinished(); //! [Creating a new relationship between two contacts] // first, create the group and the group member QContact exampleGroup; exampleGroup.setType(QContactType::TypeGroup); QContactNickname groupName; groupName.setNickname("Example Group"); exampleGroup.saveDetail(&groupName); QContact exampleGroupMember; QContactName groupMemberName; groupMemberName.setFirstName("Member"); exampleGroupMember.saveDetail(&groupMemberName); // second, save those contacts in the manager QList<QContact> saveList; saveList << exampleGroup << exampleGroupMember; m_contactSaveRequest.setContacts(saveList); m_contactSaveRequest.start(); m_contactSaveRequest.waitForFinished(); // third, create the relationship between those contacts QContactRelationship groupRelationship; groupRelationship.setFirst(exampleGroup.id()); groupRelationship.setRelationshipType(QContactRelationship::HasMember); groupRelationship.setSecond(exampleGroupMember.id()); // finally, save the relationship in the manager connect(&m_relationshipSaveRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(relationshipSaveRequestStateChanged(QContactAbstractRequest::State))); m_relationshipSaveRequest.setManager(m_manager); m_relationshipSaveRequest.setRelationships(QList<QContactRelationship>() << groupRelationship); m_relationshipSaveRequest.start(); //! [Creating a new relationship between two contacts] m_contactFetchRequest.waitForFinished(); //! [Retrieving relationships between contacts] connect(&m_relationshipFetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(relationshipFetchRequestStateChanged(QContactAbstractRequest::State))); m_relationshipFetchRequest.setManager(m_manager); // retrieve the list of relationships between the example group contact and the example member contact // where the group contact is the first contact in the relationship, and the member contact is the // second contact in the relationship. In order to fetch all relationships between them, another // relationship fetch must be performed with their roles reversed, and the results added together. m_relationshipFetchRequest.setFirst(exampleGroup.id()); m_relationshipFetchRequest.setSecond(exampleGroupMember.id()); m_relationshipFetchRequest.start(); //! [Retrieving relationships between contacts] m_contactFetchRequest.waitForFinished(); //! [Providing a fetch hint] QContactFetchHint hasMemberRelationshipsOnly; hasMemberRelationshipsOnly.setRelationshipTypesHint(QStringList(QContactRelationship::HasMember)); m_contactFetchRequest.setManager(m_manager); m_contactFetchRequest.setFilter(QContactFilter()); // all contacts m_contactFetchRequest.setFetchHint(hasMemberRelationshipsOnly); m_contactFetchRequest.start(); //! [Providing a fetch hint] //! [Removing a relationship] connect(&m_relationshipRemoveRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(relationshipRemoveRequestStateChanged(QContactAbstractRequest::State))); m_relationshipRemoveRequest.setManager(m_manager); m_relationshipRemoveRequest.setRelationships(QList<QContactRelationship>() << groupRelationship); m_relationshipRemoveRequest.start(); //! [Removing a relationship] connect(&m_definitionFetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(definitionFetchRequestStateChanged(QContactAbstractRequest::State))); //! [Querying the schema supported by a manager] m_definitionFetchRequest.setManager(m_manager); m_definitionFetchRequest.setDefinitionNames(QStringList(QContactName::DefinitionName)); m_definitionFetchRequest.start(); m_definitionFetchRequest.waitForFinished(); QMap<QString, QContactDetailDefinition> definitions = m_definitionFetchRequest.definitions(); qDebug() << "This manager" << (definitions.value(QContactName::DefinitionName).fields().contains(QContactName::FieldCustomLabel) ? "supports" : "does not support") << "the custom label field of QContactName"; //! [Querying the schema supported by a manager] connect(&m_definitionSaveRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(definitionSaveRequestStateChanged(QContactAbstractRequest::State))); connect(&m_definitionRemoveRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(definitionRemoveRequestStateChanged(QContactAbstractRequest::State))); //! [Modifying the schema supported by a manager] // modify the name definition, adding a patronym field QContactDetailDefinition nameDefinition = definitions.value(QContactName::DefinitionName); QContactDetailFieldDefinition fieldPatronym; fieldPatronym.setDataType(QVariant::String); nameDefinition.insertField("Patronym", fieldPatronym); // save the updated definition in the manager if supported... if (m_manager->hasFeature(QContactManager::MutableDefinitions)) { m_definitionSaveRequest.setManager(m_manager); m_definitionSaveRequest.setContactType(QContactType::TypeContact); m_definitionSaveRequest.setDefinitions(QList<QContactDetailDefinition>() << nameDefinition); m_definitionSaveRequest.start(); } //! [Modifying the schema supported by a manager] QCoreApplication::exit(0); }