Commit 787f62ee authored by chriadam's avatar chriadam

[qtcontacts-sqlite] Automatically extend known display label groups at...

[qtcontacts-sqlite] Automatically extend known display label groups at runtime. Contributes to JB#44742
parent 905f6b7b
...@@ -1898,7 +1898,7 @@ static bool executeDisplayLabelGroupLocalizationStatements(QSqlDatabase &databas ...@@ -1898,7 +1898,7 @@ static bool executeDisplayLabelGroupLocalizationStatements(QSqlDatabase &databas
const QString dlg = cdb->determineDisplayLabelGroup(c); const QString dlg = cdb->determineDisplayLabelGroup(c);
displayLabelGroups.append(dlg); displayLabelGroups.append(dlg);
displayLabelGroupSortOrders.append(cdb->possibleDisplayLabelGroups().indexOf(dlg) + 1); displayLabelGroupSortOrders.append(cdb->displayLabelGroupSortValue(dlg));
} }
selectQuery.finish(); selectQuery.finish();
} }
...@@ -2660,6 +2660,60 @@ static QVector<QtContactsSqliteExtensions::DisplayLabelGroupGenerator*> initiali ...@@ -2660,6 +2660,60 @@ static QVector<QtContactsSqliteExtensions::DisplayLabelGroupGenerator*> initiali
} }
static QVector<QtContactsSqliteExtensions::DisplayLabelGroupGenerator*> s_dlgGenerators = initializeDisplayLabelGroupGenerators(); static QVector<QtContactsSqliteExtensions::DisplayLabelGroupGenerator*> s_dlgGenerators = initializeDisplayLabelGroupGenerators();
static qint32 displayLabelGroupSortValue(const QString &group, const QMap<QString, int> &knownDisplayLabelGroups)
{
static const int maxUnicodeCodePointValue = 1114111; // 0x10FFFF
static const int numberGroupSortValue = maxUnicodeCodePointValue + 1;
qint32 retn = -1;
if (!group.isEmpty()) {
retn = group == QStringLiteral("#") ? numberGroupSortValue : knownDisplayLabelGroups.value(group, -1);
if (retn < 0) {
// the group is not a previously-known display label group.
// convert the group to a utf32 code point value.
const QChar first = group.at(0);
if (first.isSurrogate()) {
if (group.size() >= 2) {
const QChar second = group.at(1);
retn = ((first.isHighSurrogate() ? first.unicode() : second.unicode() - 0xD800) * 0x400)
+ (second.isLowSurrogate() ? second.unicode() : first.unicode() - 0xDC00) + 0x10000;
} else {
// cannot calculate the true codepoint without the second character in the surrogate pair.
// assume that it's the very last possible codepoint.
retn = maxUnicodeCodePointValue;
}
} else {
// use the unicode code point value as the sort value.
retn = first.unicode();
// resolve overlap issue by compressing overlapping groups
// into a single subsequent group.
// e.g. in Chinese locale, there may be more than
// 65 default display label groups, and thus the
// letter 'A' (whose unicode value is 65) would overlap.
int lastContiguousSortValue = -1;
for (int sortValue : knownDisplayLabelGroups) {
if (sortValue != (lastContiguousSortValue + 1)) {
break;
}
lastContiguousSortValue = sortValue;
}
// instead of placing into LCSV+1, we place into LCSV+2
// to ensure that ALL overlapping groups are compressed
// into the same group, in order to avoid "first seen
// will sort first" issues (e.g. B < A).
const int compressedSortValue = lastContiguousSortValue + 2;
if (retn < compressedSortValue) {
retn = compressedSortValue;
}
}
}
}
return retn;
}
// Adapted from the inter-process mutex in QMF // Adapted from the inter-process mutex in QMF
// The first user creates the semaphore that all subsequent instances // The first user creates the semaphore that all subsequent instances
// attach to. We rely on undo semantics to release locked semaphores // attach to. We rely on undo semantics to release locked semaphores
...@@ -2721,8 +2775,9 @@ void ContactsDatabase::Query::reportError(const char *text) const ...@@ -2721,8 +2775,9 @@ void ContactsDatabase::Query::reportError(const char *text) const
reportError(QString::fromLatin1(text)); reportError(QString::fromLatin1(text));
} }
ContactsDatabase::ContactsDatabase() ContactsDatabase::ContactsDatabase(ContactsEngine *engine)
: m_mutex(QMutex::Recursive) : m_engine(engine)
, m_mutex(QMutex::Recursive)
, m_nonprivileged(false) , m_nonprivileged(false)
, m_localeName(QLocale().name()) , m_localeName(QLocale().name())
, m_defaultGenerator(new DefaultDlgGenerator) , m_defaultGenerator(new DefaultDlgGenerator)
...@@ -2768,21 +2823,38 @@ bool ContactsDatabase::open(const QString &connectionName, bool nonprivileged, b ...@@ -2768,21 +2823,38 @@ bool ContactsDatabase::open(const QString &connectionName, bool nonprivileged, b
} }
m_dlgGenerators.append(m_defaultGenerator.data()); m_dlgGenerators.append(m_defaultGenerator.data());
// and build a "superlist" of possible display label groups // and build a "superlist" of known display label groups.
// which defines a total sort ordering for display label groups.
const QLocale locale; const QLocale locale;
QStringList knownDisplayLabelGroups;
for (auto generator : m_dlgGenerators) { for (auto generator : m_dlgGenerators) {
if (generator->validForLocale(locale)) { if (generator->validForLocale(locale)) {
const QStringList groups = generator->displayLabelGroups(); const QStringList groups = generator->displayLabelGroups();
for (const QString &group : groups) { for (const QString &group : groups) {
if (!m_possibleDisplayLabelGroups.contains(group)) { if (!knownDisplayLabelGroups.contains(group)) {
m_possibleDisplayLabelGroups.append(group); knownDisplayLabelGroups.append(group);
} }
} }
} }
} }
m_possibleDisplayLabelGroups.removeAll(QStringLiteral("#")); knownDisplayLabelGroups.removeAll(QStringLiteral("#"));
m_possibleDisplayLabelGroups.append(QStringLiteral("#")); knownDisplayLabelGroups.append(QStringLiteral("#"));
// from that list, build a mapping from group to sort priority value,
// based upon the position of each group in the list,
// which defines a total sort ordering for known display label groups.
for (int i = 0; i < knownDisplayLabelGroups.size(); ++i) {
const QString &group(knownDisplayLabelGroups.at(i));
m_knownDisplayLabelGroupsSortValues.insert(
group,
group == QStringLiteral("#")
? ::displayLabelGroupSortValue(group, m_knownDisplayLabelGroupsSortValues)
: i);
}
// XXX TODO: do we need to add groups which currently exist in the database,
// but which aren't currently included in the m_knownDisplayLabelGroupsSortValues?
// I don't think we do, since it only matters on write, and we will update
// the m_knownDisplayLabelGroupsSortValues in determineDisplayLabelGroup() during write...
} }
if (m_database.isOpen()) { if (m_database.isOpen()) {
...@@ -3263,11 +3335,12 @@ QDateTime ContactsDatabase::fromDateTimeString(const QString &s) ...@@ -3263,11 +3335,12 @@ QDateTime ContactsDatabase::fromDateTimeString(const QString &s)
return QDateTime(datepart, timepart, Qt::UTC); return QDateTime(datepart, timepart, Qt::UTC);
} }
QString ContactsDatabase::determineDisplayLabelGroup(const QContact &c) const QString ContactsDatabase::determineDisplayLabelGroup(const QContact &c)
{ {
// TODO: read the preferred detail and field from system settings! // XXXXXXXX TODO: read the preferred detail and field from system settings!
const int preferredDetail = QContactName::Type; const int preferredDetail = QContactName::Type;
const int preferredField = QContactName::FieldLastName; const int preferredField = QContactName::FieldLastName;
QString data; QString data;
if (preferredDetail == QContactName::Type) { if (preferredDetail == QContactName::Type) {
// try to use the preferred field data. // try to use the preferred field data.
...@@ -3315,6 +3388,21 @@ QString ContactsDatabase::determineDisplayLabelGroup(const QContact &c) const ...@@ -3315,6 +3388,21 @@ QString ContactsDatabase::determineDisplayLabelGroup(const QContact &c) const
} }
} }
if (!group.isEmpty() && !m_knownDisplayLabelGroupsSortValues.contains(group)) {
// We are about to write a contact to the database which has a
// display label group which previously was not known / observed.
// Calculate the sort value for the display label group,
// and add it to our map of displayLabelGroup->sortValue.
// Note: this should be thread-safe since we only call this method within writes.
m_knownDisplayLabelGroupsSortValues.insert(
group, ::displayLabelGroupSortValue(
group,
m_knownDisplayLabelGroupsSortValues));
// and invoke engine->_q_displayLabelGroupsChanged() asynchronously.
QMetaObject::invokeMethod(m_engine, "_q_displayLabelGroupsChanged", Qt::QueuedConnection);
}
return group; return group;
} }
...@@ -3337,7 +3425,7 @@ QStringList ContactsDatabase::displayLabelGroups() const ...@@ -3337,7 +3425,7 @@ QStringList ContactsDatabase::displayLabelGroups() const
QMutexLocker locker(accessMutex()); QMutexLocker locker(accessMutex());
QSqlQuery selectQuery(m_database); QSqlQuery selectQuery(m_database);
selectQuery.setForwardOnly(true); selectQuery.setForwardOnly(true);
const QString statement = QStringLiteral("SELECT DISTINCT DisplayLabelGroup FROM Contacts"); const QString statement = QStringLiteral("SELECT DISTINCT DisplayLabelGroup FROM Contacts ORDER BY DisplayLabelGroupSortOrder ASC");
if (!selectQuery.prepare(statement)) { if (!selectQuery.prepare(statement)) {
QTCONTACTS_SQLITE_WARNING(QString::fromLatin1("Failed to prepare distinct display label group selection query: %1\n%2") QTCONTACTS_SQLITE_WARNING(QString::fromLatin1("Failed to prepare distinct display label group selection query: %1\n%2")
.arg(selectQuery.lastError().text()) .arg(selectQuery.lastError().text())
...@@ -3363,9 +3451,11 @@ QStringList ContactsDatabase::displayLabelGroups() const ...@@ -3363,9 +3451,11 @@ QStringList ContactsDatabase::displayLabelGroups() const
return groups; return groups;
} }
QStringList ContactsDatabase::possibleDisplayLabelGroups() const int ContactsDatabase::displayLabelGroupSortValue(const QString &group) const
{ {
return m_possibleDisplayLabelGroups; static const int maxUnicodeCodePointValue = 1114111; // 0x10FFFF
static const int nullGroupSortValue = maxUnicodeCodePointValue + 1;
return m_knownDisplayLabelGroupsSortValues.value(group, nullGroupSortValue);
} }
#include "../extensions/qcontactdeactivated_impl.h" #include "../extensions/qcontactdeactivated_impl.h"
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include <QContact> #include <QContact>
class ContactsEngine;
class ContactsDatabase class ContactsDatabase
{ {
public: public:
...@@ -105,7 +106,7 @@ public: ...@@ -105,7 +106,7 @@ public:
void reportError(const char *text) const; void reportError(const char *text) const;
}; };
ContactsDatabase(); ContactsDatabase(ContactsEngine *engine);
~ContactsDatabase(); ~ContactsDatabase();
QMutex *accessMutex() const; QMutex *accessMutex() const;
...@@ -152,9 +153,9 @@ public: ...@@ -152,9 +153,9 @@ public:
bool removeTransientDetails(quint32 contactId); bool removeTransientDetails(quint32 contactId);
bool removeTransientDetails(const QList<quint32> &contactIds); bool removeTransientDetails(const QList<quint32> &contactIds);
QString determineDisplayLabelGroup(const QContact &c) const; QString determineDisplayLabelGroup(const QContact &c);
QStringList displayLabelGroups() const; QStringList displayLabelGroups() const;
QStringList possibleDisplayLabelGroups() const; int displayLabelGroupSortValue(const QString &group) const;
static bool execute(QSqlQuery &query); static bool execute(QSqlQuery &query);
static bool executeBatch(QSqlQuery &query, QSqlQuery::BatchExecutionMode mode = QSqlQuery::ValuesAsRows); static bool executeBatch(QSqlQuery &query, QSqlQuery::BatchExecutionMode mode = QSqlQuery::ValuesAsRows);
...@@ -171,6 +172,7 @@ public: ...@@ -171,6 +172,7 @@ public:
static QDateTime fromDateTimeString(const QString &s); static QDateTime fromDateTimeString(const QString &s);
private: private:
ContactsEngine *m_engine;
QSqlDatabase m_database; QSqlDatabase m_database;
ContactsTransientStore m_transientStore; ContactsTransientStore m_transientStore;
QMutex m_mutex; QMutex m_mutex;
...@@ -180,7 +182,7 @@ private: ...@@ -180,7 +182,7 @@ private:
QHash<QString, QSqlQuery> m_preparedQueries; QHash<QString, QSqlQuery> m_preparedQueries;
QVector<QtContactsSqliteExtensions::DisplayLabelGroupGenerator*> m_dlgGenerators; QVector<QtContactsSqliteExtensions::DisplayLabelGroupGenerator*> m_dlgGenerators;
QScopedPointer<QtContactsSqliteExtensions::DisplayLabelGroupGenerator> m_defaultGenerator; QScopedPointer<QtContactsSqliteExtensions::DisplayLabelGroupGenerator> m_defaultGenerator;
QStringList m_possibleDisplayLabelGroups; QMap<QString, int> m_knownDisplayLabelGroupsSortValues;
}; };
#endif #endif
...@@ -69,13 +69,13 @@ class Job ...@@ -69,13 +69,13 @@ class Job
{ {
public: public:
struct WriterProxy { struct WriterProxy {
const ContactsEngine &engine; ContactsEngine &engine;
ContactsDatabase &database; ContactsDatabase &database;
ContactNotifier &notifier; ContactNotifier &notifier;
ContactReader &reader; ContactReader &reader;
mutable ContactWriter *writer; mutable ContactWriter *writer;
WriterProxy(const ContactsEngine &e, ContactsDatabase &db, ContactNotifier &n, ContactReader &r) WriterProxy(ContactsEngine &e, ContactsDatabase &db, ContactNotifier &n, ContactReader &r)
: engine(e), database(db), notifier(n), reader(r), writer(0) : engine(e), database(db), notifier(n), reader(r), writer(0)
{ {
} }
...@@ -518,6 +518,7 @@ public: ...@@ -518,6 +518,7 @@ public:
JobThread(ContactsEngine *engine, const QString &databaseUuid, bool nonprivileged, bool autoTest) JobThread(ContactsEngine *engine, const QString &databaseUuid, bool nonprivileged, bool autoTest)
: m_currentJob(0) : m_currentJob(0)
, m_engine(engine) , m_engine(engine)
, m_database(engine)
, m_databaseUuid(databaseUuid) , m_databaseUuid(databaseUuid)
, m_updatePending(false) , m_updatePending(false)
, m_running(false) , m_running(false)
...@@ -1174,7 +1175,7 @@ QList<QContactType::TypeValues> ContactsEngine::supportedContactTypes() const ...@@ -1174,7 +1175,7 @@ QList<QContactType::TypeValues> ContactsEngine::supportedContactTypes() const
return QList<QContactType::TypeValues>() << QContactType::TypeContact; return QList<QContactType::TypeValues>() << QContactType::TypeContact;
} }
void ContactsEngine::regenerateDisplayLabel(QContact &contact) const void ContactsEngine::regenerateDisplayLabel(QContact &contact)
{ {
QContactManager::Error displayLabelError = QContactManager::NoError; QContactManager::Error displayLabelError = QContactManager::NoError;
const QString label = synthesizedDisplayLabel(contact, &displayLabelError); const QString label = synthesizedDisplayLabel(contact, &displayLabelError);
...@@ -1412,9 +1413,9 @@ void ContactsEngine::_q_syncContactsChanged(const QStringList &syncTargets) ...@@ -1412,9 +1413,9 @@ void ContactsEngine::_q_syncContactsChanged(const QStringList &syncTargets)
emit syncContactsChanged(syncTargets); emit syncContactsChanged(syncTargets);
} }
void ContactsEngine::_q_displayLabelGroupsChanged(const QStringList &groups) void ContactsEngine::_q_displayLabelGroupsChanged()
{ {
emit displayLabelGroupsChanged(groups); emit displayLabelGroupsChanged(displayLabelGroups());
} }
void ContactsEngine::_q_contactsRemoved(const QVector<quint32> &contactIds) void ContactsEngine::_q_contactsRemoved(const QVector<quint32> &contactIds)
...@@ -1443,7 +1444,7 @@ ContactsDatabase &ContactsEngine::database() ...@@ -1443,7 +1444,7 @@ ContactsDatabase &ContactsEngine::database()
QString dbId(QStringLiteral("qtcontacts-sqlite%1-%2")); QString dbId(QStringLiteral("qtcontacts-sqlite%1-%2"));
dbId = dbId.arg(m_autoTest ? QStringLiteral("-test") : QString()).arg(databaseUuid()); dbId = dbId.arg(m_autoTest ? QStringLiteral("-test") : QString()).arg(databaseUuid());
m_database.reset(new ContactsDatabase); m_database.reset(new ContactsDatabase(this));
if (!m_database->open(dbId, m_nonprivileged, m_autoTest, true)) { if (!m_database->open(dbId, m_nonprivileged, m_autoTest, true)) {
QTCONTACTS_SQLITE_WARNING(QString::fromLatin1("Unable to open synchronous engine database connection")); QTCONTACTS_SQLITE_WARNING(QString::fromLatin1("Unable to open synchronous engine database connection"));
} }
......
...@@ -132,7 +132,7 @@ public: ...@@ -132,7 +132,7 @@ public:
bool isRelationshipTypeSupported(const QString &relationshipType, QContactType::TypeValues contactType) const override; bool isRelationshipTypeSupported(const QString &relationshipType, QContactType::TypeValues contactType) const override;
QList<QContactType::TypeValues> supportedContactTypes() const override; QList<QContactType::TypeValues> supportedContactTypes() const override;
void regenerateDisplayLabel(QContact &contact) const; void regenerateDisplayLabel(QContact &contact);
bool fetchSyncContacts(const QString &syncTarget, const QDateTime &lastSync, const QList<QContactId> &exportedIds, bool fetchSyncContacts(const QString &syncTarget, const QDateTime &lastSync, const QList<QContactId> &exportedIds,
QList<QContact> *syncContacts, QList<QContact> *addedContacts, QList<QContactId> *deletedContactIds, QList<QContact> *syncContacts, QList<QContact> *addedContacts, QList<QContactId> *deletedContactIds,
...@@ -176,7 +176,7 @@ private slots: ...@@ -176,7 +176,7 @@ private slots:
void _q_selfContactIdChanged(quint32,quint32); void _q_selfContactIdChanged(quint32,quint32);
void _q_relationshipsAdded(const QVector<quint32> &contactIds); void _q_relationshipsAdded(const QVector<quint32> &contactIds);
void _q_relationshipsRemoved(const QVector<quint32> &contactIds); void _q_relationshipsRemoved(const QVector<quint32> &contactIds);
void _q_displayLabelGroupsChanged(const QStringList &groups); void _q_displayLabelGroupsChanged();
private: private:
QString databaseUuid(); QString databaseUuid();
......
...@@ -139,7 +139,7 @@ static const QString matchEmailAddressesTable(QString::fromLatin1("matchEmailAdd ...@@ -139,7 +139,7 @@ static const QString matchEmailAddressesTable(QString::fromLatin1("matchEmailAdd
static const QString matchPhoneNumbersTable(QString::fromLatin1("matchPhoneNumbers")); static const QString matchPhoneNumbersTable(QString::fromLatin1("matchPhoneNumbers"));
static const QString matchOnlineAccountsTable(QString::fromLatin1("matchOnlineAccounts")); static const QString matchOnlineAccountsTable(QString::fromLatin1("matchOnlineAccounts"));
ContactWriter::ContactWriter(const ContactsEngine &engine, ContactsDatabase &database, ContactNotifier *notifier, ContactReader *reader) ContactWriter::ContactWriter(ContactsEngine &engine, ContactsDatabase &database, ContactNotifier *notifier, ContactReader *reader)
: m_engine(engine) : m_engine(engine)
, m_database(database) , m_database(database)
, m_notifier(notifier) , m_notifier(notifier)
...@@ -5625,7 +5625,7 @@ ContactsDatabase::Query ContactWriter::bindContactDetails(const QContact &contac ...@@ -5625,7 +5625,7 @@ ContactsDatabase::Query ContactWriter::bindContactDetails(const QContact &contac
query.bindValue(0, displayLabel); query.bindValue(0, displayLabel);
const QString displayLabelGroup = m_database.determineDisplayLabelGroup(contact); const QString displayLabelGroup = m_database.determineDisplayLabelGroup(contact);
query.bindValue(1, displayLabelGroup); query.bindValue(1, displayLabelGroup);
const int displayLabelGroupSortOrder = m_database.possibleDisplayLabelGroups().indexOf(displayLabelGroup) + 1; const int displayLabelGroupSortOrder = m_database.displayLabelGroupSortValue(displayLabelGroup);
query.bindValue(2, displayLabelGroupSortOrder); query.bindValue(2, displayLabelGroupSortOrder);
query.bindValue(3, firstName); query.bindValue(3, firstName);
......
...@@ -72,7 +72,7 @@ class ContactWriter ...@@ -72,7 +72,7 @@ class ContactWriter
public: public:
typedef QList<QContactDetail::DetailType> DetailList; typedef QList<QContactDetail::DetailType> DetailList;
ContactWriter(const ContactsEngine &engine, ContactsDatabase &database, ContactNotifier *notifier, ContactReader *reader); ContactWriter(ContactsEngine &engine, ContactsDatabase &database, ContactNotifier *notifier, ContactReader *reader);
~ContactWriter(); ~ContactWriter();
QContactManager::Error save( QContactManager::Error save(
...@@ -167,7 +167,7 @@ private: ...@@ -167,7 +167,7 @@ private:
template <typename T> bool removeCommonDetails(quint32 contactId, QContactManager::Error *error); template <typename T> bool removeCommonDetails(quint32 contactId, QContactManager::Error *error);
const ContactsEngine &m_engine; ContactsEngine &m_engine;
ContactsDatabase &m_database; ContactsDatabase &m_database;
ContactNotifier *m_notifier; ContactNotifier *m_notifier;
ContactReader *m_reader; ContactReader *m_reader;
......
TARGET = tst_displaylabelgroups TARGET = tst_displaylabelgroups
include (../../../common.pri) include (../../../common.pri)
# We need access to QtContacts private headers
QT += contacts-private
# And we need access to the ContactManagerEngine header and moc output
INCLUDEPATH += ../../../../src/extensions/
HEADERS += ../../../../src/extensions/contactmanagerengine.h
SOURCES += tst_displaylabelgroups.cpp SOURCES += tst_displaylabelgroups.cpp
...@@ -40,6 +40,9 @@ ...@@ -40,6 +40,9 @@
#include <QContactPhoneNumber> #include <QContactPhoneNumber>
#include <QContactHobby> #include <QContactHobby>
#include <private/qcontactmanager_p.h>
#include "contactmanagerengine.h"
#include "qtcontacts-extensions.h" #include "qtcontacts-extensions.h"
QTCONTACTS_USE_NAMESPACE QTCONTACTS_USE_NAMESPACE
...@@ -121,6 +124,19 @@ void tst_DisplayLabelGroups::cleanup() ...@@ -121,6 +124,19 @@ void tst_DisplayLabelGroups::cleanup()
} }
} }
#define DETERMINE_ACTUAL_ORDER_AND_GROUPS \
do { \
actualOrder.clear(); \
actualGroups.clear(); \
for (const QContact &c : sorted) { \
actualOrder += c.detail<QContactPhoneNumber>().number(); \
if (c.detail<QContactPhoneNumber>().number().isEmpty()) { \
actualOrder += c.detail<QContactHobby>().hobby(); \
} \
actualGroups += c.detail<QContactDisplayLabel>().value(QContactDisplayLabel__FieldLabelGroup).toString(); \
} \
} while (0)
void tst_DisplayLabelGroups::testDisplayLabelGroups() void tst_DisplayLabelGroups::testDisplayLabelGroups()
{ {
// this test relies on the display label grouping // this test relies on the display label grouping
...@@ -228,13 +244,7 @@ void tst_DisplayLabelGroups::testDisplayLabelGroups() ...@@ -228,13 +244,7 @@ void tst_DisplayLabelGroups::testDisplayLabelGroups()
displayLabelGroupSort.setDetailType(QContactDisplayLabel::Type, QContactDisplayLabel__FieldLabelGroup); displayLabelGroupSort.setDetailType(QContactDisplayLabel::Type, QContactDisplayLabel__FieldLabelGroup);
QList<QContact> sorted = m_cm->contacts(displayLabelGroupSort); QList<QContact> sorted = m_cm->contacts(displayLabelGroupSort);
QString actualOrder, actualGroups; QString actualOrder, actualGroups;
for (const QContact &c : sorted) { DETERMINE_ACTUAL_ORDER_AND_GROUPS;
actualOrder += c.detail<QContactPhoneNumber>().number();
if (c.detail<QContactPhoneNumber>().number().isEmpty()) {
actualOrder += c.detail<QContactHobby>().hobby();
}
actualGroups += c.detail<QContactDisplayLabel>().value(QContactDisplayLabel__FieldLabelGroup).toString();
}
// fixup for potential ambiguity in sort order. 3, 7 and 9 all sort equally. // fixup for potential ambiguity in sort order. 3, 7 and 9 all sort equally.
actualOrder.replace(QChar('7'), QChar('3')); actualOrder.replace(QChar('7'), QChar('3'));
actualOrder.replace(QChar('9'), QChar('3')); actualOrder.replace(QChar('9'), QChar('3'));
...@@ -248,15 +258,7 @@ void tst_DisplayLabelGroups::testDisplayLabelGroups() ...@@ -248,15 +258,7 @@ void tst_DisplayLabelGroups::testDisplayLabelGroups()
QContactSortOrder lastNameSort; QContactSortOrder lastNameSort;
lastNameSort.setDetailType(QContactName::Type, QContactName::FieldLastName); lastNameSort.setDetailType(QContactName::Type, QContactName::FieldLastName);
sorted = m_cm->contacts(QList<QContactSortOrder>() << displayLabelGroupSort << lastNameSort); sorted = m_cm->contacts(QList<QContactSortOrder>() << displayLabelGroupSort << lastNameSort);
actualOrder.clear(); DETERMINE_ACTUAL_ORDER_AND_GROUPS;
actualGroups.clear();
for (const QContact &c : sorted) {
actualOrder += c.detail<QContactPhoneNumber>().number();
if (c.detail<QContactPhoneNumber>().number().isEmpty()) {
actualOrder += c.detail<QContactHobby>().hobby();
}
actualGroups += c.detail<QContactDisplayLabel>().value(QContactDisplayLabel__FieldLabelGroup).toString();
}
// fixup for potential ambiguity in sort order. 3 and 9 sort equally. // fixup for potential ambiguity in sort order. 3 and 9 sort equally.
actualOrder.replace(QChar('9'), QChar('3')); actualOrder.replace(QChar('9'), QChar('3'));
QCOMPARE(actualOrder, QStringLiteral("615824337")); QCOMPARE(actualOrder, QStringLiteral("615824337"));
...@@ -269,15 +271,7 @@ void tst_DisplayLabelGroups::testDisplayLabelGroups() ...@@ -269,15 +271,7 @@ void tst_DisplayLabelGroups::testDisplayLabelGroups()
QContactSortOrder firstNameSort; QContactSortOrder firstNameSort;
firstNameSort.setDetailType(QContactName::Type, QContactName::FieldFirstName); firstNameSort.setDetailType(QContactName::Type, QContactName::FieldFirstName);
sorted = m_cm->contacts(QList<QContactSortOrder>() << displayLabelGroupSort << firstNameSort); sorted = m_cm->contacts(QList<QContactSortOrder>() << displayLabelGroupSort << firstNameSort);
actualOrder.clear(); DETERMINE_ACTUAL_ORDER_AND_GROUPS;
actualGroups.clear();
for (const QContact &c : sorted) {
actualOrder += c.detail<QContactPhoneNumber>().number();
if (c.detail<QContactPhoneNumber>().number().isEmpty()) {
actualOrder += c.detail<QContactHobby>().hobby();
}
actualGroups += c.detail<QContactDisplayLabel>().value(QContactDisplayLabel__FieldLabelGroup).toString();
}
QCOMPARE(actualOrder, QStringLiteral("615824793")); QCOMPARE(actualOrder, QStringLiteral("615824793"));
QCOMPARE(actualGroups, QStringLiteral("Z1345OEEE")); QCOMPARE(actualGroups, QStringLiteral("Z1345OEEE"));
...@@ -286,17 +280,56 @@ void tst_DisplayLabelGroups::testDisplayLabelGroups() ...@@ -286,17 +280,56 @@ void tst_DisplayLabelGroups::testDisplayLabelGroups()
// except that contact 9's last name causes it to be sorted before contact 7, // except that contact 9's last name causes it to be sorted before contact 7,
// and contact 9 should sort before contact 3 due to the first name. // and contact 9 should sort before contact 3 due to the first name.
sorted = m_cm->contacts(QList<QContactSortOrder>() << displayLabelGroupSort << lastNameSort << firstNameSort); sorted = m_cm->contacts(QList<QContactSortOrder>() << displayLabelGroupSort << lastNameSort << firstNameSort);
actualOrder.clear(); DETERMINE_ACTUAL_ORDER_AND_GROUPS;
actualGroups.clear();
for (const QContact &c : sorted) {
actualOrder += c.detail<QContactPhoneNumber>().number();
if (c.detail<QContactPhoneNumber>().number().isEmpty()) {
actualOrder += c.detail<QContactHobby>().hobby();
}
actualGroups += c.detail<QContactDisplayLabel>().value(QContactDisplayLabel__FieldLabelGroup).toString();
}
QCOMPARE(actualOrder, QStringLiteral("615824937")); QCOMPARE(actualOrder, QStringLiteral("615824937"));
QCOMPARE(actualGroups, QStringLiteral("Z1345OEEE")); QCOMPARE(actualGroups, QStringLiteral("Z1345OEEE"));
// Now add a contact which has a special name such that the test
// display label group generator plugin will generate a group
// for it which was previously "unknown".
// We expect that group to be added before '#' but after other groups.
typedef QtContactsSqliteExtensions::ContactManagerEngine EngineType;
EngineType *cme = dynamic_cast<EngineType *>(QContactManagerData::managerData(m_cm)->m_engine);
const QStringList oldContactDisplayLabelGroups = cme->displayLabelGroups();
QSignalSpy dlgcSpy(cme, SIGNAL(displayLabelGroupsChanged(QStringList)));
QContact c10, c11;
QContactName n10, n11;
QContactDisplayLabel d10, d11;
QContactPhoneNumber p10, p11;
n10.setLastName("10ten"); // first letter is digit, should be in #.
n10.setFirstName("Ten");
d10.setLabel("Test J Contact");
p10.setNumber("J");
c10.saveDetail(&n10);
c10.saveDetail(&d10);
c10.saveDetail(&p10);
n11.setLastName("tst_displaylabelgroups_unknown_dlg"); // special case, group &.
n11.setFirstName("Eleven");
d11.setLabel("Test K Contact");
p11.setNumber("K");
c11.saveDetail(&n11);
c11.saveDetail(&d11);
c11.saveDetail(&p11);
QVERIFY(m_cm->saveContact(&c10));
QVERIFY(m_cm->saveContact(&c11));
// ensure that the resultant sort order is expected
sorted = m_cm->contacts(QList<QContactSortOrder>() << displayLabelGroupSort << lastNameSort << firstNameSort);
DETERMINE_ACTUAL_ORDER_AND_GROUPS;
QCOMPARE(actualOrder, QStringLiteral("615824937KJ"));
QCOMPARE(actualGroups, QStringLiteral("Z1345OEEE&#"));
// should have received signal that display label groups have changed.
QTest::qWait(250);
QCOMPARE(dlgcSpy.count(), 1);
QStringList expected(oldContactDisplayLabelGroups);
expected.insert(expected.size() - 1, QStringLiteral("&")); // & group should have been inserted before #.
QList<QVariant> data = dlgcSpy.takeFirst();
QCOMPARE(data.first().value<QStringList>(), expected);
} }
QTEST_MAIN(tst_DisplayLabelGroups) QTEST_MAIN(tst_DisplayLabelGroups)
......
...@@ -79,12 +79,23 @@ QStringList TestDlgg::displayLabelGroups() const ...@@ -79,12 +79,23 @@ QStringList TestDlgg::displayLabelGroups() const
QStringLiteral("5"), QStringLiteral("5"),
QStringLiteral("O"), // sort O before E to test DisplayLabelGroupSortOrder semantics QStringLiteral("O"), // sort O before E to test DisplayLabelGroupSortOrder semantics
QStringLiteral("E"), QStringLiteral("E"),
QStringLiteral("#"),
}; };
return allGroups; return allGroups;
} }
QString TestDlgg::displayLabelGroup(const QString &data) const QString TestDlgg::displayLabelGroup(const QString &data) const
{ {
if (data.size() && data.at(0).isDigit()) {
return QStringLiteral("#"); // default # group for numeric names
}
if (data == QStringLiteral("tst_displaylabelgroups_unknown_dlg")) {
// special case: return a group which is NOT included in the "all groups" above.
// this allows us to test that dynamic group adding works as expected.
return QStringLiteral("&"); // should be sorted before '#' but after every other group.
}
if (data.size() > 5) { if (data.size() > 5) {
return (data.size() % 2 == 0) ? QStringLiteral("E") : QStringLiteral("O"); return (data.size() % 2 == 0) ? QStringLiteral("E") : QStringLiteral("O");
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment