From 10f27328e82c84f3e7aaaae6075112ead7225f11 Mon Sep 17 00:00:00 2001 From: Przemyslaw Brudny Date: Mon, 26 Jul 2021 16:07:22 +0200 Subject: [PATCH] [EGD-7215] Connected Sim contact import with backends Connected Sim contact import with cellular contact read and DB contacts write. --- image/assets/lang/English.json | 9 + .../ApplicationSettings.cpp | 11 +- .../application-settings/CMakeLists.txt | 1 + .../models/network/SimContactsImportModel.cpp | 66 ++++++- .../models/network/SimContactsImportModel.hpp | 20 ++- .../models/network/SimContactsRepository.cpp | 155 +++++++++++++++++ .../models/network/SimContactsRepository.hpp | 49 ++++++ .../SimContactsImportWindowPresenter.cpp | 65 ++++++- .../SimContactsImportWindowPresenter.hpp | 27 ++- .../network/SimContactImportSelectWidget.cpp | 7 +- .../network/SimContactImportSelectWidget.hpp | 3 +- .../windows/network/SimCardsWindow.cpp | 2 +- .../network/SimContactsImportWindow.cpp | 163 ++++++++++++++++-- .../network/SimContactsImportWindow.hpp | 14 +- module-db/Interface/ContactRecord.cpp | 8 +- module-db/Interface/ContactRecord.hpp | 5 +- .../QueryCheckContactsListDuplicates.cpp | 10 +- .../QueryCheckContactsListDuplicates.hpp | 5 +- module-db/tests/ContactsRecord_tests.cpp | 13 +- module-gui/gui/widgets/CheckBox.cpp | 3 +- module-gui/gui/widgets/CheckBox.hpp | 1 + module-gui/gui/widgets/ListItem.hpp | 10 +- module-gui/gui/widgets/ListView.cpp | 8 + module-gui/gui/widgets/ListView.hpp | 1 + module-gui/gui/widgets/ListViewEngine.hpp | 2 +- module-gui/gui/widgets/Style.hpp | 3 + module-utils/log/debug.hpp | 1 + 27 files changed, 605 insertions(+), 57 deletions(-) create mode 100644 module-apps/application-settings/models/network/SimContactsRepository.cpp create mode 100644 module-apps/application-settings/models/network/SimContactsRepository.hpp diff --git a/image/assets/lang/English.json b/image/assets/lang/English.json index 50ade8b09550711b72ca4044d8066afc2275e577..f782d9c6914df1bb223e3ff7a23498ddd7b71081 100644 --- a/image/assets/lang/English.json +++ b/image/assets/lang/English.json @@ -3,6 +3,7 @@ "common_open": "OPEN", "common_call": "CALL", "common_save": "SAVE", + "common_import": "IMPORT", "common_send": "SEND", "common_confirm": "CONFIRM", "common_select": "SELECT", @@ -11,6 +12,7 @@ "common_back": "BACK", "common_skip": "SKIP", "common_set": "SET", + "common_show": "SHOW", "common_yes": "Yes", "common_no": "No", "common_switch": "SWITCH", @@ -25,6 +27,7 @@ "common_resume": "RESUME", "common_pause": "PAUSE", "common_retry": "TRY AGAIN", + "common_replace": "REPLACE", "common_abort": "ABORT", "common_connect": "CONNECT", "common_disconnect": "DISCONNECT", @@ -399,7 +402,13 @@ "app_settings_network_pin_settings": "PIN settings", "app_settings_network_pin": "PIN", "app_settings_network_pin_change_code": "Change PIN code", + "app_settings_network_import_contacts": "Import contacts", + "app_settings_network_import_contacts_duplicates": "Duplicates from SIM", "app_settings_network_import_contacts_from_sim_card": "Import contacts from SIM card", + "app_settings_network_import_contacts_from_sim_card_reading": "Importing in progress...

Please wait a moment.
", + "app_settings_network_import_contacts_from_sim_card_no_contacts": "There are no contacts

on this SIM card.
", + "app_settings_network_import_contacts_from_sim_card_duplicates": "We found $DUPLICATES duplicates. Do you want

to import duplicated contacts and

replace existing ones.
", + "app_settings_network_import_contacts_from_sim_card_success": "Contacts imported successfully.", "app_settings_network_sim1": "SIM1", "app_settings_network_sim2": "SIM2", "app_settings_network_sim_none": "No SIM", diff --git a/module-apps/application-settings/ApplicationSettings.cpp b/module-apps/application-settings/ApplicationSettings.cpp index 0a0262d16a06c313d2180afe283528897679a79a..316f7d3f234ecb43c8ffd35a39dc6113d35e9a7c 100644 --- a/module-apps/application-settings/ApplicationSettings.cpp +++ b/module-apps/application-settings/ApplicationSettings.cpp @@ -90,11 +90,14 @@ namespace app constexpr inline auto operators_on = "operators_on"; } // namespace settings + static constexpr auto settingStackDepth = 1024 * 6; // 6Kb stack size + ApplicationSettings::ApplicationSettings(std::string name, std::string parent, sys::phone_modes::PhoneMode mode, StartInBackground startInBackground) - : Application(std::move(name), std::move(parent), mode, startInBackground), AsyncCallbackReceiver{this} + : Application(std::move(name), std::move(parent), mode, startInBackground, settingStackDepth), + AsyncCallbackReceiver{this} { CellularServiceAPI::SubscribeForOwnNumber(this, [&](const std::string &number) { selectedSimNumber = number; @@ -132,6 +135,7 @@ namespace app if (auto command = callbackStorage->getCallback(resp); command->execute()) { refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST); } + return sys::msgHandled(); } return sys::MessageNone{}; @@ -381,8 +385,9 @@ namespace app return std::make_unique(app); }); windowsFactory.attach(gui::window::name::import_contacts, [&](Application *app, const std::string &name) { - auto model = std::make_unique(this); - auto presenter = std::make_unique(std::move(model)); + auto repository = std::make_unique(this); + auto model = std::make_unique(this, std::move(repository)); + auto presenter = std::make_unique(this, std::move(model)); return std::make_unique(app, std::move(presenter)); }); diff --git a/module-apps/application-settings/CMakeLists.txt b/module-apps/application-settings/CMakeLists.txt index 1b443e29fe91675b3979be9298628e7d79451e13..790f7a3ab42b8dd789bd8e6ac1225dc9ea9b616e 100644 --- a/module-apps/application-settings/CMakeLists.txt +++ b/module-apps/application-settings/CMakeLists.txt @@ -20,6 +20,7 @@ target_sources( ${PROJECT_NAME} models/network/ApnSettingsModel.cpp models/network/NewApnModel.cpp models/network/SimContactsImportModel.cpp + models/network/SimContactsRepository.cpp models/display-keypad/QuotesModel.cpp models/display-keypad/CategoriesModel.cpp models/apps/AudioSettingsModel.cpp diff --git a/module-apps/application-settings/models/network/SimContactsImportModel.cpp b/module-apps/application-settings/models/network/SimContactsImportModel.cpp index 4c2f6f5c6ff4ace7ab096c3d03d6635b1e24a44d..db8c7c821758331b7eea4193cc29d2a384776ff1 100644 --- a/module-apps/application-settings/models/network/SimContactsImportModel.cpp +++ b/module-apps/application-settings/models/network/SimContactsImportModel.cpp @@ -5,12 +5,11 @@ #include #include -#include -SimContactsImportModel::SimContactsImportModel(app::Application *app) : application(app) -{ - createData(); -} +SimContactsImportModel::SimContactsImportModel(app::Application *app, + std::unique_ptr contactsRepository) + : application(app), contactsRepository(std::move(contactsRepository)) +{} auto SimContactsImportModel::requestRecordsCount() -> unsigned int { @@ -33,8 +32,32 @@ auto SimContactsImportModel::getItem(gui::Order order) -> gui::ListItem * return getRecord(order); } -void SimContactsImportModel::createData() +void SimContactsImportModel::createSimImported() +{ + createData(contactsRepository->getImportedRecords()); +} + +void SimContactsImportModel::createDuplicates() +{ + createData(contactsRepository->getDuplicatedRecords()); +} + +unsigned int SimContactsImportModel::getDuplicatesCount() { + return contactsRepository->getDuplicatedRecords().size(); +} + +void SimContactsImportModel::createData(const std::vector &importedRecords) +{ + auto app = application; + + for (const auto &record : importedRecords) { + internalData.push_back(new gui::SimContactImportSelectWidget( + record.primaryName + " " + record.alternativeName, + [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text, false); }, + [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); })); + } + for (auto item : internalData) { item->deleteByList = false; } @@ -45,3 +68,34 @@ void SimContactsImportModel::clearData() list->reset(); eraseInternalData(); } + +void SimContactsImportModel::eraseData() +{ + clearData(); + contactsRepository->clear(); +} + +std::vector SimContactsImportModel::getSelectedContacts() +{ + std::vector selectedContacts; + for (const auto &item : internalData) { + selectedContacts.push_back(item->isChecked()); + } + return selectedContacts; +} + +void SimContactsImportModel::findDuplicates(std::function onDuplicatesCheckCallback) +{ + contactsRepository->findDuplicates(getSelectedContacts(), std::move(onDuplicatesCheckCallback)); +} + +void SimContactsImportModel::saveData(std::function onSaveCallback) +{ + auto duplicatesFound = getDuplicatesCount() != 0; + contactsRepository->save(getSelectedContacts(), duplicatesFound, std::move(onSaveCallback)); +} + +void SimContactsImportModel::requestSimContacts(std::function onSimContactsReadCallback) +{ + contactsRepository->read(std::move(onSimContactsReadCallback)); +} diff --git a/module-apps/application-settings/models/network/SimContactsImportModel.hpp b/module-apps/application-settings/models/network/SimContactsImportModel.hpp index fabe7e06b436aeb8ebfb05d2011daddb9141122e..0986c82b38a553e23b41103dfa3d790ecfd8a9a7 100644 --- a/module-apps/application-settings/models/network/SimContactsImportModel.hpp +++ b/module-apps/application-settings/models/network/SimContactsImportModel.hpp @@ -3,22 +3,34 @@ #pragma once +#include "SimContactsRepository.hpp" +#include + #include #include #include #include -class SimContactsImportModel : public app::InternalModel, public gui::ListItemProvider +class SimContactsImportModel : public app::InternalModel, + public gui::ListItemProvider { private: app::Application *application = nullptr; - std::vector importedRecords; + std::shared_ptr contactsRepository; + std::vector getSelectedContacts(); public: - explicit SimContactsImportModel(app::Application *app); + SimContactsImportModel(app::Application *app, std::unique_ptr contactsRepository); - void createData(); + void createSimImported(); + void createDuplicates(); + unsigned int getDuplicatesCount(); + void createData(const std::vector &importedRecords); void clearData(); + void eraseData(); + void requestSimContacts(std::function onSimContactsReadCallback); + void saveData(std::function onSaveCallback); + void findDuplicates(std::function onDuplicatesCheckCallback); [[nodiscard]] auto requestRecordsCount() -> unsigned int override; [[nodiscard]] auto getMinimalItemSpaceRequired() const -> unsigned int override; diff --git a/module-apps/application-settings/models/network/SimContactsRepository.cpp b/module-apps/application-settings/models/network/SimContactsRepository.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0febd19a882db0e49a952cb088c3a4524c77793c --- /dev/null +++ b/module-apps/application-settings/models/network/SimContactsRepository.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "SimContactsRepository.hpp" + +#include +#include +#include + +#include + +SimContactsRepository::SimContactsRepository(app::Application *application) + : app::AsyncCallbackReceiver{application}, application{application} +{} + +const std::vector &SimContactsRepository::getImportedRecords() +{ + return importedRecords; +} + +const std::vector &SimContactsRepository::getDuplicatedRecords() +{ + return duplicatedRecords; +} + +void SimContactsRepository::clear() +{ + importedRecords.clear(); + uniqueRecords.clear(); + duplicatedRecords.clear(); +} + +void SimContactsRepository::findDuplicates(const std::vector &selectedContacts, + AbstractSimContactsRepository::OnDupplicatesCheckCallback callback) +{ + std::vector recordsToSave; + for (unsigned int i = 0; i < selectedContacts.size(); i++) { + if (selectedContacts[i]) { + recordsToSave.push_back(importedRecords[i]); + } + } + +#if DEBUG_SIM_IMPORT_DATA == 1 + printRecordsData("Sim import selected", recordsToSave); +#endif + + auto query = std::make_unique(recordsToSave); + auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Contact); + task->setCallback([&, callback](auto response) { + auto result = dynamic_cast(response); + if (result == nullptr) { + return false; + } + + auto duplicatesFound = !result->getDuplicates().empty(); + + if (callback) { + if (duplicatesFound) { + uniqueRecords = std::move(result->getUnique()); + duplicatedRecords = std::move(result->getDuplicates()); + +#if DEBUG_SIM_IMPORT_DATA == 1 + printRecordsData("Sim import uniques", uniqueRecords); + printRecordsData("Sim import duplicates", duplicatedRecords); +#endif + } + callback(duplicatesFound); + } + return true; + }); + task->execute(application, this); +} + +void SimContactsRepository::save(const std::vector &selectedContacts, + bool duplicatesFound, + OnSaveCallback callback) +{ + std::vector recordsToSave = uniqueRecords; + for (unsigned int i = 0; i < selectedContacts.size(); i++) { + if (selectedContacts[i]) { + recordsToSave.push_back(duplicatesFound ? duplicatedRecords[i] : importedRecords[i]); + } + } + +#if DEBUG_SIM_IMPORT_DATA == 1 + printRecordsData("Sim import to save data", recordsToSave); +#endif + + auto query = std::make_unique(recordsToSave); + auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Contact); + task->setCallback([&, callback](auto response) { + auto result = dynamic_cast(response); + if (result == nullptr) { + return false; + } + if (callback) { + callback(); + } + return true; + }); + task->execute(application, this); +} + +void SimContactsRepository::read(AbstractSimContactsRepository::OnReadCallback readDoneCallback) +{ + std::function &simData)> callback = + [&](const std::vector &simData) { updateImportedRecords(simData); }; + + auto msg = std::make_unique(); + auto task = app::AsyncRequest::createFromMessage(std::move(msg), cellular::service::name); + auto cb = [callback, readDoneCallback](auto response) { + auto result = dynamic_cast(response); + if (result != nullptr && result->retCode == sys::ReturnCodes::Success) { + callback(*result->getContacts()); + } + readDoneCallback(); + return true; + }; + task->execute(this->application, this, cb); +} + +void SimContactsRepository::updateImportedRecords(const std::vector &simData) +{ + for (const auto &simRecord : simData) { + ContactRecord rec = ContactRecord(); + + auto description = simRecord.name; + auto splitPos = simRecord.name.find(' '); + + rec.primaryName = description.substr(0, splitPos); + rec.alternativeName = + splitPos != std::string::npos ? description.substr(rec.primaryName.length() + 1, description.length()) : ""; + rec.numbers.push_back( + ContactRecord::Number(utils::PhoneNumber(simRecord.number, utils::country::Id::UNKNOWN).getView())); + + importedRecords.push_back(rec); + } + +#if DEBUG_SIM_IMPORT_DATA == 1 + printRecordsData("Imported records from sim", importedRecords); +#endif +} + +#if DEBUG_SIM_IMPORT_DATA == 1 +void SimContactsRepository::printRecordsData(const std::string &name, const std::vector &data) +{ + for (auto record : data) { + LOG_SENSITIVE("%s: %s %s, Number: %s", + name.c_str(), + record.primaryName.c_str(), + record.alternativeName.c_str(), + record.numbers.front().number.getFormatted().c_str()); + } +} +#endif diff --git a/module-apps/application-settings/models/network/SimContactsRepository.hpp b/module-apps/application-settings/models/network/SimContactsRepository.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5706aa4baf1435946be5e8fb639edad3ed02514d --- /dev/null +++ b/module-apps/application-settings/models/network/SimContactsRepository.hpp @@ -0,0 +1,49 @@ +// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +#include +#include +#include + +class AbstractSimContactsRepository +{ + public: + using OnReadCallback = std::function; + using OnDupplicatesCheckCallback = std::function; + using OnSaveCallback = std::function; + + virtual ~AbstractSimContactsRepository() noexcept = default; + + virtual const std::vector &getImportedRecords() = 0; + virtual const std::vector &getDuplicatedRecords() = 0; + virtual void read(OnReadCallback callback) = 0; + virtual void clear() = 0; + virtual void save(const std::vector &selectedContacts, bool duplicatesFound, OnSaveCallback callback) = 0; + virtual void findDuplicates(const std::vector &selectedContacts, OnDupplicatesCheckCallback callback) = 0; +}; + +class SimContactsRepository : public AbstractSimContactsRepository, public app::AsyncCallbackReceiver +{ + public: + explicit SimContactsRepository(app::Application *application); + + const std::vector &getImportedRecords() override; + const std::vector &getDuplicatedRecords() override; + void read(OnReadCallback callback) override; + void clear() override; + void save(const std::vector &selectedContacts, bool duplicatesFound, OnSaveCallback callback) override; + void findDuplicates(const std::vector &selectedContacts, OnDupplicatesCheckCallback callback) override; + void updateImportedRecords(const std::vector &simData); + + private: + std::vector importedRecords; + std::vector uniqueRecords; + std::vector duplicatedRecords; + app::Application *application; + +#if DEBUG_SIM_IMPORT_DATA == 1 + void printRecordsData(const std::string &name, const std::vector &data); +#endif +}; diff --git a/module-apps/application-settings/presenter/network/SimContactsImportWindowPresenter.cpp b/module-apps/application-settings/presenter/network/SimContactsImportWindowPresenter.cpp index 9f224ef6b6b14a9736df352040e51df993a9f3e6..2343358be7708b1636db1f3ca0ac606cc02cec82 100644 --- a/module-apps/application-settings/presenter/network/SimContactsImportWindowPresenter.cpp +++ b/module-apps/application-settings/presenter/network/SimContactsImportWindowPresenter.cpp @@ -4,16 +4,71 @@ #include "SimContactsImportWindowPresenter.hpp" SimContactsImportWindowPresenter::SimContactsImportWindowPresenter( - std::shared_ptr simContactsProvider) - : simContactsProvider{std::move(simContactsProvider)} -{} + app::Application *application, std::shared_ptr simContactsProvider) + : application(application), simContactsProvider{std::move(simContactsProvider)} +{ + onSave = [&]() { + this->simContactsProvider->clearData(); + getView()->contactsImported(); + this->application->refreshWindow(gui::RefreshModes::GUI_REFRESH_DEEP); + requestCompleted = true; + }; + + onDuplicatesCheck = [&](bool duplicatesFound) { + if (duplicatesFound) { + duplicatesChecked = true; + this->simContactsProvider->clearData(); + getView()->displayDuplicatesCount(this->simContactsProvider->getDuplicatesCount()); + this->application->refreshWindow(gui::RefreshModes::GUI_REFRESH_DEEP); + } + else { + this->simContactsProvider->saveData(onSave); + } + requestCompleted = true; + }; + + onSimContactsReady = [&]() { + this->simContactsProvider->createSimImported(); + getView()->contactsReady(); + this->application->refreshWindow(gui::RefreshModes::GUI_REFRESH_DEEP); + }; +} std::shared_ptr SimContactsImportWindowPresenter::getSimContactsProvider() const { return simContactsProvider; } -void SimContactsImportWindowPresenter::clearProviderData() const +void SimContactsImportWindowPresenter::eraseProviderData() const +{ + simContactsProvider->eraseData(); +} + +void SimContactsImportWindowPresenter::saveImportedContacts() +{ + this->application->refreshWindow(gui::RefreshModes::GUI_REFRESH_DEEP); + requestCompleted = false; + if (!duplicatesChecked) { + simContactsProvider->findDuplicates(onDuplicatesCheck); + } + else { + simContactsProvider->saveData(onSave); + } +} + +bool SimContactsImportWindowPresenter::isRequestCompleted() +{ + return requestCompleted; +} + +void SimContactsImportWindowPresenter::requestSimContacts() +{ + simContactsProvider->requestSimContacts(onSimContactsReady); +} + +void SimContactsImportWindowPresenter::requestDuplicates() { - simContactsProvider->clearData(); + this->simContactsProvider->createDuplicates(); + getView()->displayDuplicates(); + this->application->refreshWindow(gui::RefreshModes::GUI_REFRESH_DEEP); } diff --git a/module-apps/application-settings/presenter/network/SimContactsImportWindowPresenter.hpp b/module-apps/application-settings/presenter/network/SimContactsImportWindowPresenter.hpp index 6600a5eed6de7eff74b3cca6655affed8ea969b3..5ab1f6484a05041061599da1e8ee7ba9146b4095 100644 --- a/module-apps/application-settings/presenter/network/SimContactsImportWindowPresenter.hpp +++ b/module-apps/application-settings/presenter/network/SimContactsImportWindowPresenter.hpp @@ -12,7 +12,11 @@ class SimContactsImportWindowContract class View { public: - virtual ~View() noexcept = default; + virtual ~View() noexcept = default; + virtual void displayDuplicatesCount(unsigned int duplicatesCount) noexcept = 0; + virtual void displayDuplicates() noexcept = 0; + virtual void contactsImported() noexcept = 0; + virtual void contactsReady() noexcept = 0; }; class Presenter : public app::BasePresenter { @@ -20,18 +24,33 @@ class SimContactsImportWindowContract virtual ~Presenter() noexcept = default; virtual std::shared_ptr getSimContactsProvider() const = 0; - virtual void clearProviderData() const = 0; + virtual void eraseProviderData() const = 0; + virtual void saveImportedContacts() = 0; + virtual void requestDuplicates() = 0; + virtual void requestSimContacts() = 0; + virtual bool isRequestCompleted() = 0; }; }; class SimContactsImportWindowPresenter : public SimContactsImportWindowContract::Presenter { public: - explicit SimContactsImportWindowPresenter(std::shared_ptr simContactsProvider); + explicit SimContactsImportWindowPresenter(app::Application *application, + std::shared_ptr simContactsProvider); std::shared_ptr getSimContactsProvider() const override; - void clearProviderData() const override; + void eraseProviderData() const override; + void saveImportedContacts() override; + void requestDuplicates() override; + void requestSimContacts() override; + bool isRequestCompleted() override; private: + app::Application *application = nullptr; + bool requestCompleted = true; + bool duplicatesChecked = false; + std::function onSave = nullptr; + std::function onDuplicatesCheck = nullptr; + std::function onSimContactsReady = nullptr; std::shared_ptr simContactsProvider; }; diff --git a/module-apps/application-settings/widgets/network/SimContactImportSelectWidget.cpp b/module-apps/application-settings/widgets/network/SimContactImportSelectWidget.cpp index 767e18ed4757ec74d02efdfd3c94bc7689768fa9..44315ed3dfbf98056cf9867c9e1fdc7cadb2ed71 100644 --- a/module-apps/application-settings/widgets/network/SimContactImportSelectWidget.cpp +++ b/module-apps/application-settings/widgets/network/SimContactImportSelectWidget.cpp @@ -6,7 +6,7 @@ namespace gui { SimContactImportSelectWidget::SimContactImportSelectWidget( - std::string contactName, + const std::string &contactName, const std::function &bottomBarTemporaryMode, const std::function &bottomBarRestoreFromTemporaryMode) { @@ -38,4 +38,9 @@ namespace gui return true; }; } + + bool SimContactImportSelectWidget::isChecked() + { + return checkBoxWithLabel->isChecked(); + } } /* namespace gui */ diff --git a/module-apps/application-settings/widgets/network/SimContactImportSelectWidget.hpp b/module-apps/application-settings/widgets/network/SimContactImportSelectWidget.hpp index f250d0d1ef84a3c8d71375c5cccbc9205cde2047..5a7e56bfb96381e2bad2e0eb9c244cfaec410160 100644 --- a/module-apps/application-settings/widgets/network/SimContactImportSelectWidget.hpp +++ b/module-apps/application-settings/widgets/network/SimContactImportSelectWidget.hpp @@ -14,9 +14,10 @@ namespace gui gui::CheckBoxWithLabel *checkBoxWithLabel = nullptr; public: - SimContactImportSelectWidget(std::string contactName, + SimContactImportSelectWidget(const std::string &contactName, const std::function &bottomBarTemporaryMode = nullptr, const std::function &bottomBarRestoreFromTemporaryMode = nullptr); + bool isChecked(); }; } /* namespace gui */ diff --git a/module-apps/application-settings/windows/network/SimCardsWindow.cpp b/module-apps/application-settings/windows/network/SimCardsWindow.cpp index 478982042ad749a9645e1b19eb88b394debcc2e0..d9e515099e2b8b419b859a785cf419c1e6ca10ed 100644 --- a/module-apps/application-settings/windows/network/SimCardsWindow.cpp +++ b/module-apps/application-settings/windows/network/SimCardsWindow.cpp @@ -85,7 +85,7 @@ namespace gui optList.emplace_back(std::make_unique( utils::translate("app_settings_network_import_contacts_from_sim_card"), [=](gui::Item &item) { - this->application->switchWindow(gui::window::name::import_contacts, nullptr); + application->switchWindow(gui::window::name::import_contacts); return true; }, nullptr, diff --git a/module-apps/application-settings/windows/network/SimContactsImportWindow.cpp b/module-apps/application-settings/windows/network/SimContactsImportWindow.cpp index 85dbc1d02faeabedeb76111002e7b18551da83b0..03b2f1ce9590b70b39dedb762fc3518f3ee1bcdd 100644 --- a/module-apps/application-settings/windows/network/SimContactsImportWindow.cpp +++ b/module-apps/application-settings/windows/network/SimContactsImportWindow.cpp @@ -4,6 +4,8 @@ #include "SimContactsImportWindow.hpp" #include +#include +#include namespace gui { @@ -12,29 +14,18 @@ namespace gui : AppWindow(app, gui::window::name::import_contacts), presenter(std::move(simImportPresenter)) { presenter->attach(this); + preventsAutoLock = true; buildInterface(); } - void SimContactsImportWindow::onBeforeShow(ShowMode mode, SwitchData *data) - { - list->rebuildList(); - } - - void SimContactsImportWindow::onClose(CloseReason reason) - { - if (reason != CloseReason::PhoneLock) { - presenter->clearProviderData(); - } - } - void SimContactsImportWindow::buildInterface() { AppWindow::buildInterface(); - setTitle(utils::translate("app_settings_network_import_contacts_from_sim_card")); + setTitle(utils::translate("app_settings_network_import_contacts")); - bottomBar->setActive(gui::BottomBar::Side::RIGHT, true); bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::translate(::style::strings::common::back)); + bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(::style::strings::common::import)); list = new ListView(this, style::window::default_left_margin, @@ -44,6 +35,150 @@ namespace gui presenter->getSimContactsProvider(), listview::ScrollBarType::Fixed); + emptyListIcon = new gui::Icon(this, + ::style::window::default_left_margin, + ::style::window::default_vertical_pos, + ::style::window::default_body_width, + ::style::window::default_body_height, + "info_icon_W_G", + utils::translate("app_settings_network_import_contacts_from_sim_card_reading")); + emptyListIcon->setAlignment(Alignment::Horizontal::Center); + + list->emptyListCallback = [this]() { emptyListIcon->setVisible(true); }; + list->notEmptyListCallback = [this]() { emptyListIcon->setVisible(false); }; + setFocusItem(list); } + + void SimContactsImportWindow::onBeforeShow(ShowMode mode, SwitchData *data) + { + emptyListIcon->text->setRichText( + utils::translate("app_settings_network_import_contacts_from_sim_card_reading")); + emptyListIcon->image->set("update_icon_W_G"); + bottomBar->setActive(BottomBar::Side::RIGHT, true); + bottomBar->setActive(BottomBar::Side::CENTER, false); + bottomBar->setActive(BottomBar::Side::LEFT, false); + + presenter->requestSimContacts(); + } + + void SimContactsImportWindow::onClose(CloseReason reason) + { + if (reason != CloseReason::PhoneLock) { + presenter->eraseProviderData(); + } + } + + bool SimContactsImportWindow::onInput(const InputEvent &inputEvent) + { + if (inputEvent.isKeyRelease(gui::KeyCode::KEY_LF) && !bottomBar->isActive(BottomBar::Side::LEFT)) { + return false; + } + + if (inputEvent.isKeyRelease(gui::KeyCode::KEY_ENTER) && !bottomBar->isActive(BottomBar::Side::CENTER)) { + return false; + } + + if (inputEvent.isKeyRelease(gui::KeyCode::KEY_RF) && !bottomBar->isActive(BottomBar::Side::RIGHT)) { + return false; + } + + if (inputEvent.isKeyRelease(gui::KeyCode::KEY_LF) && presenter->isRequestCompleted() && onLFInputCallback) { + onLFInputCallback(); + return true; + } + + if (inputEvent.isKeyRelease(gui::KeyCode::KEY_ENTER) && presenter->isRequestCompleted() && + onEnterInputCallback) { + onEnterInputCallback(); + return true; + } + + return AppWindow::onInput(inputEvent); + } + + void SimContactsImportWindow::contactsReady() noexcept + { + bottomBar->setActive(BottomBar::Side::CENTER, true); + list->rebuildList(); + + if (list->isEmpty()) { + bottomBar->setActive(BottomBar::Side::CENTER, false); + emptyListIcon->text->setRichText( + utils::translate("app_settings_network_import_contacts_from_sim_card_no_contacts")); + emptyListIcon->image->set("info_icon_W_G"); + } + else { + setTitle(utils::translate("app_settings_network_import_contacts_from_sim_card")); + onEnterInputCallback = [&]() { + displayProgressInfo(); + presenter->saveImportedContacts(); + }; + } + } + + void SimContactsImportWindow::displayDuplicatesCount(unsigned int duplicatesCount) noexcept + { + list->rebuildList(); + + setTitle(utils::translate("app_settings_network_import_contacts")); + emptyListIcon->text->setRichText( + utils::translate("app_settings_network_import_contacts_from_sim_card_duplicates"), + {{"$DUPLICATES", std::to_string(duplicatesCount)}}); + emptyListIcon->image->set("info_icon_W_G"); + bottomBar->setActive(BottomBar::Side::RIGHT, true); + bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(::style::strings::common::show)); + bottomBar->setText(gui::BottomBar::Side::LEFT, utils::translate(::style::strings::common::skip)); + + onLFInputCallback = [&]() { + displayProgressInfo(); + presenter->saveImportedContacts(); + }; + onEnterInputCallback = [&]() { presenter->requestDuplicates(); }; + } + + void SimContactsImportWindow::displayDuplicates() noexcept + { + bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(::style::strings::common::replace)); + list->rebuildList(); + + setTitle(utils::translate("app_settings_network_import_contacts_duplicates")); + bottomBar->setActive(BottomBar::Side::RIGHT, true); + + onLFInputCallback = nullptr; + onEnterInputCallback = [&]() { + displayProgressInfo(); + presenter->saveImportedContacts(); + }; + } + + void SimContactsImportWindow::contactsImported() noexcept + { + list->rebuildList(); + + setTitle(utils::translate("app_settings_network_import_contacts_from_sim_card")); + emptyListIcon->text->setRichText( + utils::translate("app_settings_network_import_contacts_from_sim_card_success")); + emptyListIcon->image->set("success_icon_W_G"); + bottomBar->setActive(BottomBar::Side::RIGHT, false); + bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(::style::strings::common::ok)); + bottomBar->setText(gui::BottomBar::Side::LEFT, utils::translate("app_desktop_menu_contacts")); + + onLFInputCallback = [&]() { + app::manager::Controller::sendAction(application, app::manager::actions::ShowContacts); + }; + onEnterInputCallback = [&]() { application->switchWindow(gui::window::name::sim_cards); }; + } + + void SimContactsImportWindow::displayProgressInfo() + { + list->clear(); + list->emptyListCallback(); + emptyListIcon->text->setRichText( + utils::translate("app_settings_network_import_contacts_from_sim_card_reading")); + emptyListIcon->image->set("update_icon_W_G"); + bottomBar->setActive(BottomBar::Side::RIGHT, false); + bottomBar->setActive(BottomBar::Side::CENTER, false); + bottomBar->setActive(BottomBar::Side::LEFT, false); + } } // namespace gui diff --git a/module-apps/application-settings/windows/network/SimContactsImportWindow.hpp b/module-apps/application-settings/windows/network/SimContactsImportWindow.hpp index c57baf701e790a54edbf50a44d95ef63da5fec7d..812873babb6731d3ac3686620fab4f0d9c2f99ac 100644 --- a/module-apps/application-settings/windows/network/SimContactsImportWindow.hpp +++ b/module-apps/application-settings/windows/network/SimContactsImportWindow.hpp @@ -6,22 +6,32 @@ #include #include #include +#include namespace gui { class SimContactsImportWindow : public AppWindow, public SimContactsImportWindowContract::View { - public: SimContactsImportWindow(app::Application *app, std::unique_ptr presenter); private: - ListView *list = nullptr; + ListView *list = nullptr; + Icon *emptyListIcon = nullptr; + std::function onEnterInputCallback = nullptr; + std::function onLFInputCallback = nullptr; void buildInterface() override; void onBeforeShow(ShowMode mode, SwitchData *data) override; void onClose(CloseReason reason) override; + bool onInput(const InputEvent &inputEvent) override; + + void displayDuplicatesCount(unsigned int duplicatesCount) noexcept override; + void displayDuplicates() noexcept override; + void contactsImported() noexcept override; + void contactsReady() noexcept override; + void displayProgressInfo(); std::shared_ptr presenter; }; diff --git a/module-db/Interface/ContactRecord.cpp b/module-db/Interface/ContactRecord.cpp index 7bdc35a5d34cb3ca6367ef6840cad563d5b3dae6..54baac81d1a12078d9cfe677e94c6e8ccae8907c 100644 --- a/module-db/Interface/ContactRecord.cpp +++ b/module-db/Interface/ContactRecord.cpp @@ -1231,8 +1231,9 @@ auto ContactRecordInterface::MergeContactsList(std::vector &conta } auto ContactRecordInterface::CheckContactsListDuplicates(std::vector &contacts) - -> std::vector + -> std::pair, std::vector> { + std::vector unique; std::vector duplicates; std::vector contactNumberHolders; auto numberMatcher = buildNumberMatcher(contactNumberHolders); @@ -1246,6 +1247,9 @@ auto ContactRecordInterface::CheckContactsListDuplicates(std::vector &contacts) -> std::vector; + auto CheckContactsListDuplicates(std::vector &contacts) + -> std::pair, std::vector>; private: ContactsDB *contactDB; diff --git a/module-db/queries/phonebook/QueryCheckContactsListDuplicates.cpp b/module-db/queries/phonebook/QueryCheckContactsListDuplicates.cpp index 24546b4a3c426a29161c8a35303c743444909558..86a4a1e1b59371793cb42d40f1cc0670af874d0b 100644 --- a/module-db/queries/phonebook/QueryCheckContactsListDuplicates.cpp +++ b/module-db/queries/phonebook/QueryCheckContactsListDuplicates.cpp @@ -20,10 +20,16 @@ std::vector &CheckContactsListDuplicates::getContactsList() return "CheckContactsListDuplicates"; } -CheckContactsListDuplicatesResult::CheckContactsListDuplicatesResult(std::vector duplicates) - : duplicates(std::move(duplicates)) +CheckContactsListDuplicatesResult::CheckContactsListDuplicatesResult( + std::pair, std::vector> result) + : unique(std::move(result.first)), duplicates(std::move(result.second)) {} +std::vector &CheckContactsListDuplicatesResult::getUnique() +{ + return unique; +} + std::vector &CheckContactsListDuplicatesResult::getDuplicates() { return duplicates; diff --git a/module-db/queries/phonebook/QueryCheckContactsListDuplicates.hpp b/module-db/queries/phonebook/QueryCheckContactsListDuplicates.hpp index f8dad372c47a2d7463aa46e7c7deae5bebbec4de..b4c9345e7a51b7e8856612a3fe8da7fe67958fc4 100644 --- a/module-db/queries/phonebook/QueryCheckContactsListDuplicates.hpp +++ b/module-db/queries/phonebook/QueryCheckContactsListDuplicates.hpp @@ -28,12 +28,15 @@ namespace db::query class CheckContactsListDuplicatesResult : public QueryResult { public: - CheckContactsListDuplicatesResult(std::vector duplicates); + explicit CheckContactsListDuplicatesResult( + std::pair, std::vector> result); + std::vector &getUnique(); std::vector &getDuplicates(); [[nodiscard]] auto debugInfo() const -> std::string override; private: + std::vector unique; std::vector duplicates; }; diff --git a/module-db/tests/ContactsRecord_tests.cpp b/module-db/tests/ContactsRecord_tests.cpp index 8f1101a4d2fc7b04918fd334fdd3f9122ef94770..4359ab63c9a590a1455f3a78962e008fb645dd2b 100644 --- a/module-db/tests/ContactsRecord_tests.cpp +++ b/module-db/tests/ContactsRecord_tests.cpp @@ -559,8 +559,10 @@ TEST_CASE("Contacts list duplicates search") } // Prepare contacts list to compare with DB + std::pair uniqueContact = {"600100500", "test5"}; std::array, 3> rawContactsToCheck = { - {rawContactsInitial[2], {"600100500", "test5"}, rawContactsInitial[0]}}; + {rawContactsInitial[2], uniqueContact, rawContactsInitial[0]}}; + constexpr auto numOfUniqueContacts = 1; constexpr auto numOfDuplicatedContacts = 2; std::vector contacts; @@ -570,7 +572,14 @@ TEST_CASE("Contacts list duplicates search") record.numbers = std::vector({ContactRecord::Number(rawContact.first, std::string(""))}); contacts.push_back(record); } - auto duplicates = records.CheckContactsListDuplicates(contacts); + auto results = records.CheckContactsListDuplicates(contacts); + auto unique = results.first; + auto duplicates = results.second; + + REQUIRE(unique.size() == numOfUniqueContacts); + + REQUIRE(unique[0].numbers[0].number.getEntered() == uniqueContact.first); + REQUIRE(unique[0].primaryName == uniqueContact.second); REQUIRE(duplicates.size() == numOfDuplicatedContacts); diff --git a/module-gui/gui/widgets/CheckBox.cpp b/module-gui/gui/widgets/CheckBox.cpp index 25913ba87e76843ba7a9055b1a9e6c7bbbe06fc9..77fbeead76d26dfa6462a5e1162f05cd1981e435 100644 --- a/module-gui/gui/widgets/CheckBox.cpp +++ b/module-gui/gui/widgets/CheckBox.cpp @@ -80,13 +80,14 @@ namespace gui void CheckBox::setCheck(bool state) { + checkState = state; image->setVisible(state); resizeItems(); } bool CheckBox::isChecked() { - return image->visible; + return checkState; } } /* namespace gui */ diff --git a/module-gui/gui/widgets/CheckBox.hpp b/module-gui/gui/widgets/CheckBox.hpp index a31281301699ce6dfb45239b8dd6674f92e796bc..465594d89f6aaf7b3dec7c5d914ca36df87b082f 100644 --- a/module-gui/gui/widgets/CheckBox.hpp +++ b/module-gui/gui/widgets/CheckBox.hpp @@ -11,6 +11,7 @@ namespace gui { class CheckBox : public HBox { + bool checkState = false; Image *image = nullptr; std::function bottomBarTemporaryMode = nullptr; std::function bottomBarRestoreFromTemporaryMode = nullptr; diff --git a/module-gui/gui/widgets/ListItem.hpp b/module-gui/gui/widgets/ListItem.hpp index 67355646ffe992ae50f2468e6ed85f4f8a563651..e8e6dfec9d0f1e2b66bcf33264189c929f643038 100644 --- a/module-gui/gui/widgets/ListItem.hpp +++ b/module-gui/gui/widgets/ListItem.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #pragma once @@ -21,9 +21,9 @@ namespace gui template class ListItemWithCallbacks : public ListItem { public: - std::function onEmptyCallback = nullptr; - std::function onContentChangedCallback = nullptr; - std::function record)> onSaveCallback = nullptr; - std::function record)> onLoadCallback = nullptr; + std::function onEmptyCallback = nullptr; + std::function onContentChangedCallback = nullptr; + std::function record)> onSaveCallback = nullptr; + std::function record)> onLoadCallback = nullptr; }; } /* namespace gui */ diff --git a/module-gui/gui/widgets/ListView.cpp b/module-gui/gui/widgets/ListView.cpp index d7a109225afe490e0d5531eae8fedcd741cf7e6e..b43248cbce4d00b0f141eb9597a332f0d89c447d 100644 --- a/module-gui/gui/widgets/ListView.cpp +++ b/module-gui/gui/widgets/ListView.cpp @@ -207,6 +207,14 @@ namespace gui }; } + void ListView::clear() + { + if (scroll) { + scroll->setVisible(false); + } + ListViewEngine::clear(); + } + void ListView::setFocus() { if (!focus) { diff --git a/module-gui/gui/widgets/ListView.hpp b/module-gui/gui/widgets/ListView.hpp index e1544c2826c96ce6f1598fbe7e466c3a88faf191..f7531e334303d5e8a0306cee3a5b01f348438ea5 100644 --- a/module-gui/gui/widgets/ListView.hpp +++ b/module-gui/gui/widgets/ListView.hpp @@ -51,6 +51,7 @@ namespace gui listview::ScrollBarType scrollType = listview::ScrollBarType::Proportional); void setScrollTopMargin(int value); + void clear() override; void setAlignment(const Alignment &value) override; // virtual methods from Item diff --git a/module-gui/gui/widgets/ListViewEngine.hpp b/module-gui/gui/widgets/ListViewEngine.hpp index 4e104498049c59d860804615a353326253a5fe21..936e013fe1ae33f7c42a21edfe9c2cc7ff9243ee 100644 --- a/module-gui/gui/widgets/ListViewEngine.hpp +++ b/module-gui/gui/widgets/ListViewEngine.hpp @@ -177,7 +177,7 @@ namespace gui std::function prepareRebuildCallback; void reset(); - void clear(); + virtual void clear(); void onClose(); std::shared_ptr getProvider(); diff --git a/module-gui/gui/widgets/Style.hpp b/module-gui/gui/widgets/Style.hpp index 69a1259fb7f4daa4ccba0293f50897f60ebe1995..115f454df21f4483a624329019f91890557d46cc 100644 --- a/module-gui/gui/widgets/Style.hpp +++ b/module-gui/gui/widgets/Style.hpp @@ -162,6 +162,7 @@ namespace style inline constexpr auto call = "common_call"; inline constexpr auto send = "common_send"; inline constexpr auto save = "common_save"; + inline constexpr auto import = "common_import"; inline constexpr auto confirm = "common_confirm"; inline constexpr auto select = "common_select"; inline constexpr auto use = "common_use"; @@ -169,6 +170,7 @@ namespace style inline constexpr auto back = "common_back"; inline constexpr auto skip = "common_skip"; inline constexpr auto set = "common_set"; + inline constexpr auto show = "common_show"; inline constexpr auto yes = "common_yes"; inline constexpr auto no = "common_no"; inline constexpr auto check = "common_check"; @@ -185,6 +187,7 @@ namespace style inline constexpr auto pause = "common_pause"; inline constexpr auto accept = "common_accept"; inline constexpr auto retry = "common_retry"; + inline constexpr auto replace = "common_replace"; inline constexpr auto abort = "common_abort"; inline constexpr auto adjust = "common_adjust"; // days diff --git a/module-utils/log/debug.hpp b/module-utils/log/debug.hpp index 3abe3250e039d3d48a2e13ee904a4e8a9fad207e..a2dfd37cc05f0160dd3ae93a47aee0b16279322b 100644 --- a/module-utils/log/debug.hpp +++ b/module-utils/log/debug.hpp @@ -10,6 +10,7 @@ #define DEBUG_BLUETOOTH_HCI_BYTES 0 /// show communication with BT module - all the HCI bytes #define DEBUG_SERVICE_MESSAGES 0 /// show messages prior to handling in service #define DEBUG_DB_MODEL_DATA 0 /// show messages prior to handling in service +#define DEBUG_SIM_IMPORT_DATA 0 /// show messages connected to sim data imports #define DEBUG_FONT 0 /// show Font debug messages #define DEBUG_GUI_TEXT 0 /// show basic debug messages for gui::Text - warning this can be hard on cpu #define DEBUG_GUI_TEXT_LINES 0 /// show extended debug messages for gui::Text - lines building