TizenRefApp-7473 Update the sorting algorithm in the Contacts List View 27/94227/2
authorEugene Kurzberg <i.kurtsberg@samsung.com>
Fri, 28 Oct 2016 12:06:47 +0000 (15:06 +0300)
committerEugene Kurzberg <i.kurtsberg@samsung.com>
Fri, 28 Oct 2016 12:06:47 +0000 (15:06 +0300)
Change-Id: I3ec9d1812ada2618acecd395d61720c25e078427
Signed-off-by: Eugene Kurzberg <i.kurtsberg@samsung.com>
lib-apps-common/inc/I18n/Collator.h [new file with mode: 0644]
lib-apps-common/src/I18n/Collator.cpp [new file with mode: 0644]
lib-contacts/inc/Contacts/List/ListView.h
lib-contacts/inc/Contacts/List/Model/Person.h
lib-contacts/src/Contacts/List/ListView.cpp
lib-contacts/src/Contacts/List/Model/Person.cpp

diff --git a/lib-apps-common/inc/I18n/Collator.h b/lib-apps-common/inc/I18n/Collator.h
new file mode 100644 (file)
index 0000000..c4d9172
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015-2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef I18N_COLLATOR_H
+#define I18N_COLLATOR_H
+
+#include "I18n/UniString.h"
+#include <utils_i18n_ucollator.h>
+
+namespace I18n
+{
+       /**
+        * @brief Provides locale-dependent string comparison.
+        */
+       class EXPORT_API Collator
+       {
+       public:
+               /**
+                * @brief Create collator.
+                * @param[in]   strength    Collation strength
+                * @see i18n_ucollator_strength_e
+                */
+               explicit Collator(i18n_ucollator_strength_e strength = I18N_UCOLLATOR_PRIMARY);
+               Collator(const Collator &that) = delete;
+               Collator(Collator &&that);
+               ~Collator();
+
+               Collator &operator=(const Collator &that) = delete;
+               Collator &operator=(Collator &&that);
+
+               /**
+                * @brief Initialize the collator with current locale.
+                * @remark Can be called several times.
+                */
+               void initialize();
+
+               /**
+                * @brief Compare two strings.
+                * @pre Should be initialized by calling initialize().
+                * @param[in]   str1    First string
+                * @param[in]   str2    Second string
+                * @return 0 if equal, -1 if str1 < str2, otherwise 1.
+                */
+               int compare(const UniString &str1, const UniString &str2);
+
+       private:
+               i18n_ucollator_strength_e m_Strength;
+               i18n_ucollator_h m_Handle;
+       };
+}
+
+#endif /* I18N_COLLATOR_H */
diff --git a/lib-apps-common/src/I18n/Collator.cpp b/lib-apps-common/src/I18n/Collator.cpp
new file mode 100644 (file)
index 0000000..e1c958b
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015-2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "I18n/Collator.h"
+#include <system_settings.h>
+
+using namespace I18n;
+
+Collator::Collator(i18n_ucollator_strength_e strength)
+       : m_Strength(strength), m_Handle(nullptr)
+{
+}
+
+Collator::Collator(Collator &&that)
+       : m_Strength(that.m_Strength), m_Handle(that.m_Handle)
+{
+       that.m_Handle = nullptr;
+}
+
+Collator::~Collator()
+{
+       i18n_ucollator_destroy(m_Handle);
+}
+
+Collator &Collator::operator=(Collator &&that)
+{
+       i18n_ucollator_destroy(m_Handle);
+       m_Strength = that.m_Strength;
+       m_Handle = that.m_Handle;
+       that.m_Handle = nullptr;
+       return *this;
+}
+
+void Collator::initialize()
+{
+       if (m_Handle) {
+               i18n_ucollator_destroy(m_Handle);
+               m_Handle = nullptr;
+       }
+
+       char *lang = nullptr;
+       system_settings_get_value_string(SYSTEM_SETTINGS_KEY_LOCALE_LANGUAGE, &lang);
+       i18n_ucollator_create(lang, &m_Handle);
+       i18n_ucollator_set_strength(m_Handle, m_Strength);
+       free(lang);
+}
+
+int Collator::compare(const UniString& str1, const UniString &str2)
+{
+       int result = 0;
+       i18n_ucollator_str_collator(m_Handle,
+                       str1.getI18nStr().c_str(), -1,
+                       str2.getI18nStr().c_str(), -1,
+                       (i18n_ucollator_result_e *) &result);
+       return result;
+}
index 9c301a5..99ca999 100644 (file)
 #include "Common/DataTypes.h"
 #include "Model2/SearchProvider.h"
 #include "Ux/SelectView.h"
+#include "I18n/Collator.h"
 #include "I18n/UniString.h"
 
-#include <map>
-
 namespace Ui
 {
        class Genlist;
@@ -199,6 +198,7 @@ namespace Contacts
                        PersonItem *getNextPersonItem(PersonGroupItem *group, const Model::Person &person);
                        void linkPersonItems(PersonItem *sectionItem);
 
+                       void onLanguageChanged(Evas_Object *obj, void *eventInfo);
                        void onIndexUpdated();
                        void onPersonInserted(::Model2::DataItem &data);
                        void onSectionUpdated(ContactItem *item, ::Model2::ChangeType change, SectionId sectionId);
@@ -219,10 +219,11 @@ namespace Contacts
                        std::vector<State> m_StateHistory;
 
                        Section m_Sections[SectionMax];
-                       std::map<I18n::UniString, PersonGroupItem *> m_PersonGroups;
+                       std::vector<PersonGroupItem *> m_PersonGroups;
                        Model2::DataProvider *m_PersonProvider;
                        Model2::SearchProvider *m_SearchProvider;
 
+                       I18n::Collator m_Collator;
                        FillCallback m_OnFilled;
                };
        }
index 0c5d975..0e39788 100644 (file)
@@ -138,10 +138,9 @@ namespace Contacts
                                const I18n::UniString &getIndexLetter() const;
 
                                /**
-                                * @brief Compares person's "Sort by" (first name/last name) values
-                                * @return True if sort value less than in @a that, otherwise false
+                                * @return "Sort by" (first name/last name) value.
                                 */
-                               bool operator<(const Person &that) const;
+                               const I18n::UniString &getSortValue() const;
 
                                /**
                                 * @brief Add contact to person
@@ -167,7 +166,6 @@ namespace Contacts
                                contacts_record_h getContactChildRecord(unsigned propertyId, Pred predicate) const;
                                const ContactChildRecords getContactChildRecords(unsigned propertyId) const;
 
-                               const I18n::UniString &getSortValue() const;
                                virtual int onUpdate(void *data) override;
                                int updateName(contacts_record_h record, unsigned sortProperty);
                                void updateDefaultContactRecord();
index 0fe60d2..fd9cdb0 100644 (file)
@@ -45,6 +45,7 @@
 #include "Ui/Window.h"
 #include "Utils/Callback.h"
 #include "Utils/Logger.h"
+#include "Utils/Range.h"
 #include "Utils/Thread.h"
 
 #include "ListPath.h"
@@ -129,6 +130,8 @@ DataProvider *ListView::getProvider() const
 
 void ListView::onInitialized()
 {
+       m_Collator.initialize();
+
        fillPersonList();
        updateIndexFirstItem();
        updateSections();
@@ -432,7 +435,7 @@ Ui::GenItem *ListView::getNextSectionItem(SectionId sectionId)
                }
        }
 
-       return !m_PersonGroups.empty() ? m_PersonGroups.begin()->second : nullptr;
+       return !m_PersonGroups.empty() ? m_PersonGroups.front(): nullptr;
 }
 
 bool ListView::getSectionVisibility(SectionId sectionId)
@@ -464,6 +467,8 @@ bool ListView::getSectionVisibility(SectionId sectionId)
 void ListView::fillLayout()
 {
        Evas_Object *box = getEvasObject();
+       evas_object_smart_callback_add(box, "language,changed",
+                       makeCallback(&ListView::onLanguageChanged), this);
 
        m_ListLayout = createListLayout(box);
        elm_box_pack_end(box, m_ListLayout);
@@ -618,7 +623,7 @@ void ListView::updateIndex()
 void ListView::updateIndexFirstItem()
 {
        if (!m_PersonGroups.empty()) {
-               m_Index->setFirstIndexedItem(m_PersonGroups.begin()->second);
+               m_Index->setFirstIndexedItem(m_PersonGroups.front());
        } else {
                m_Index->setFirstIndexedItem(nullptr);
        }
@@ -681,21 +686,23 @@ void ListView::fillPersonList()
 
 PersonGroupItem *ListView::getPersonGroupItem(const UniString &indexLetter)
 {
-       auto insertResult = m_PersonGroups.insert({ indexLetter, nullptr });
-       auto groupIt = insertResult.first;
+       auto it = std::lower_bound(m_PersonGroups.begin(), m_PersonGroups.end(), indexLetter,
+               [this](const PersonGroupItem *groupItem, const UniString &letter) {
+                       return m_Collator.compare(groupItem->getTitle(), letter) < 0;
+               });
 
-       /* Whether insert was successful (i.e. there is no such group yet) */
-       if (insertResult.second) {
-               PersonGroupItem *nextGroupItem = nullptr;
-               auto nextGroupIt = Utils::advance(groupIt, 1);
-               if (nextGroupIt != m_PersonGroups.end()) {
-                       nextGroupItem = nextGroupIt->second;
+       PersonGroupItem *nextGroupItem = nullptr;
+       if (it != m_PersonGroups.end()) {
+               if ((*it)->getTitle() == indexLetter) {
+                       return *it;
+               } else {
+                       nextGroupItem = *it;
                }
-
-               groupIt->second = insertPersonGroupItem(indexLetter, nextGroupItem);
        }
 
-       return groupIt->second;
+       PersonGroupItem *groupItem = insertPersonGroupItem(indexLetter, nextGroupItem);
+       m_PersonGroups.insert(it, groupItem);
+       return groupItem;
 }
 
 PersonGroupItem *ListView::insertPersonGroupItem(const UniString &indexLetter,
@@ -709,7 +716,8 @@ PersonGroupItem *ListView::insertPersonGroupItem(const UniString &indexLetter,
 
 void ListView::deletePersonGroupItem(PersonGroupItem *group)
 {
-       m_PersonGroups.erase(group->getTitle());
+       auto it = std::find(m_PersonGroups.begin(), m_PersonGroups.end(), group);
+       m_PersonGroups.erase(it);
        m_Index->removeIndexedItem(group);
        updateIndexFirstItem();
        delete group;
@@ -764,7 +772,7 @@ PersonItem *ListView::getNextPersonItem(PersonGroupItem *group, const Person &pe
 {
        for (auto &&item : *group) {
                auto personItem = static_cast<PersonItem *>(item);
-               if (person < personItem->getPerson()) {
+               if (m_Collator.compare(person.getSortValue(), personItem->getPerson().getSortValue()) < 0) {
                        return personItem;
                }
        }
@@ -775,7 +783,7 @@ PersonItem *ListView::getNextPersonItem(PersonGroupItem *group, const Person &pe
 void ListView::linkPersonItems(PersonItem *sectionItem)
 {
        for (auto &&group : m_PersonGroups) {
-               for (auto &&item : *group.second) {
+               for (auto &&item : *group) {
                        auto personItem = static_cast<PersonItem *>(item);
                        if (sectionItem->getPerson().getId() == personItem->getPerson().getId()) {
                                sectionItem->setExcluded(true);
@@ -786,14 +794,30 @@ void ListView::linkPersonItems(PersonItem *sectionItem)
        }
 }
 
+void ListView::onLanguageChanged(Evas_Object *obj, void *eventInfo)
+{
+       m_Collator.initialize();
+       std::sort(m_PersonGroups.begin(), m_PersonGroups.end(),
+               [this](const PersonGroupItem *group1, const PersonGroupItem *group2) {
+                       return m_Collator.compare(group1->getTitle(), group2->getTitle()) < 0;
+               });
+
+       PersonGroupItem *nextGroupItem = nullptr;
+       for (auto &&groupItem : makeRange(m_PersonGroups.rbegin(), m_PersonGroups.rend())) {
+               if (groupItem->getNextGroupItem() != nextGroupItem) {
+                       m_Genlist->insert(groupItem, nullptr, nextGroupItem);
+               }
+               nextGroupItem = groupItem;
+       }
+}
+
 void ListView::onIndexUpdated()
 {
        Ui::GenGroupItem *sectionItem = m_Sections[SectionFavorites].m_Item;
        if (sectionItem && sectionItem->isInserted()) {
                m_Index->addIndexedItem(sectionItem, SYMBOL_STAR);
        }
-       for (auto &&pair : m_PersonGroups) {
-               PersonGroupItem *groupItem = pair.second;
+       for (auto &&groupItem : m_PersonGroups) {
                m_Index->addIndexedItem(groupItem, groupItem->getTitle().getUtf8Str().c_str());
        }
 }
index 57333cf..2db2bf9 100644 (file)
@@ -176,9 +176,15 @@ const UniString &Person::getIndexLetter() const
        return m_IndexLetter;
 }
 
-bool Person::operator<(const Person &that) const
+const UniString &Person::getSortValue() const
 {
-       return getSortValue() < that.getSortValue();
+       if (m_SortValue.getI18nStr().empty()) {
+               contacts_record_h nameRecord = getChildRecord(m_DefaultContactRecord, _contacts_contact.name);
+               const char *value = getRecordStr(nameRecord, m_SortProperty);
+               m_SortValue = (value && *value) ? value : getName();
+       }
+
+       return m_SortValue;
 }
 
 void Person::addContact(contacts_record_h record)
@@ -259,17 +265,6 @@ const Person::ContactChildRecords Person::getContactChildRecords(unsigned proper
        return records;
 }
 
-const UniString &Person::getSortValue() const
-{
-       if (m_SortValue.getI18nStr().empty()) {
-               contacts_record_h nameRecord = getChildRecord(m_DefaultContactRecord, _contacts_contact.name);
-               const char *value = getRecordStr(nameRecord, m_SortProperty);
-               m_SortValue = (value && *value) ? value : getName();
-       }
-
-       return m_SortValue;
-}
-
 int Person::onUpdate(void *data)
 {
        contacts_record_h personRecord = (contacts_record_h) data;