M image/assets/lang/English.json => image/assets/lang/English.json +9 -0
@@ 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": "<text>Importing in progress...<br></br>Please wait a moment.</text>",
+ "app_settings_network_import_contacts_from_sim_card_no_contacts": "<text>There are no contacts<br></br>on this SIM card.</text>",
+ "app_settings_network_import_contacts_from_sim_card_duplicates": "<text>We found <token>$DUPLICATES</token> duplicates. Do you want<br></br>to import duplicated contacts and<br></br>replace existing ones.</text>",
+ "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",
M module-apps/application-settings/ApplicationSettings.cpp => module-apps/application-settings/ApplicationSettings.cpp +8 -3
@@ 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<gui::SimPINSettingsWindow>(app);
});
windowsFactory.attach(gui::window::name::import_contacts, [&](Application *app, const std::string &name) {
- auto model = std::make_unique<SimContactsImportModel>(this);
- auto presenter = std::make_unique<SimContactsImportWindowPresenter>(std::move(model));
+ auto repository = std::make_unique<SimContactsRepository>(this);
+ auto model = std::make_unique<SimContactsImportModel>(this, std::move(repository));
+ auto presenter = std::make_unique<SimContactsImportWindowPresenter>(this, std::move(model));
return std::make_unique<gui::SimContactsImportWindow>(app, std::move(presenter));
});
M module-apps/application-settings/CMakeLists.txt => module-apps/application-settings/CMakeLists.txt +1 -0
@@ 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
M module-apps/application-settings/models/network/SimContactsImportModel.cpp => module-apps/application-settings/models/network/SimContactsImportModel.cpp +60 -6
@@ 5,12 5,11 @@
#include <application-settings/widgets/network/SimContactImportSelectWidget.hpp>
#include <ListView.hpp>
-#include <i18n/i18n.hpp>
-SimContactsImportModel::SimContactsImportModel(app::Application *app) : application(app)
-{
- createData();
-}
+SimContactsImportModel::SimContactsImportModel(app::Application *app,
+ std::unique_ptr<AbstractSimContactsRepository> 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<ContactRecord> &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<bool> SimContactsImportModel::getSelectedContacts()
+{
+ std::vector<bool> selectedContacts;
+ for (const auto &item : internalData) {
+ selectedContacts.push_back(item->isChecked());
+ }
+ return selectedContacts;
+}
+
+void SimContactsImportModel::findDuplicates(std::function<void(bool)> onDuplicatesCheckCallback)
+{
+ contactsRepository->findDuplicates(getSelectedContacts(), std::move(onDuplicatesCheckCallback));
+}
+
+void SimContactsImportModel::saveData(std::function<void()> onSaveCallback)
+{
+ auto duplicatesFound = getDuplicatesCount() != 0;
+ contactsRepository->save(getSelectedContacts(), duplicatesFound, std::move(onSaveCallback));
+}
+
+void SimContactsImportModel::requestSimContacts(std::function<void()> onSimContactsReadCallback)
+{
+ contactsRepository->read(std::move(onSimContactsReadCallback));
+}
M module-apps/application-settings/models/network/SimContactsImportModel.hpp => module-apps/application-settings/models/network/SimContactsImportModel.hpp +16 -4
@@ 3,22 3,34 @@
#pragma once
+#include "SimContactsRepository.hpp"
+#include <application-settings/widgets/network/SimContactImportSelectWidget.hpp>
+
#include <InternalModel.hpp>
#include <ListItemProvider.hpp>
#include <ContactRecord.hpp>
#include <Application.hpp>
-class SimContactsImportModel : public app::InternalModel<gui::ListItem *>, public gui::ListItemProvider
+class SimContactsImportModel : public app::InternalModel<gui::SimContactImportSelectWidget *>,
+ public gui::ListItemProvider
{
private:
app::Application *application = nullptr;
- std::vector<ContactRecord> importedRecords;
+ std::shared_ptr<AbstractSimContactsRepository> contactsRepository;
+ std::vector<bool> getSelectedContacts();
public:
- explicit SimContactsImportModel(app::Application *app);
+ SimContactsImportModel(app::Application *app, std::unique_ptr<AbstractSimContactsRepository> contactsRepository);
- void createData();
+ void createSimImported();
+ void createDuplicates();
+ unsigned int getDuplicatesCount();
+ void createData(const std::vector<ContactRecord> &importedRecords);
void clearData();
+ void eraseData();
+ void requestSimContacts(std::function<void()> onSimContactsReadCallback);
+ void saveData(std::function<void()> onSaveCallback);
+ void findDuplicates(std::function<void(bool)> onDuplicatesCheckCallback);
[[nodiscard]] auto requestRecordsCount() -> unsigned int override;
[[nodiscard]] auto getMinimalItemSpaceRequired() const -> unsigned int override;
A module-apps/application-settings/models/network/SimContactsRepository.cpp => module-apps/application-settings/models/network/SimContactsRepository.cpp +155 -0
@@ 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 <queries/phonebook/QueryMergeContactsList.hpp>
+#include <queries/phonebook/QueryCheckContactsListDuplicates.hpp>
+#include <service-cellular/ServiceCellular.hpp>
+
+#include <application-settings/ApplicationSettings.hpp>
+
+SimContactsRepository::SimContactsRepository(app::Application *application)
+ : app::AsyncCallbackReceiver{application}, application{application}
+{}
+
+const std::vector<ContactRecord> &SimContactsRepository::getImportedRecords()
+{
+ return importedRecords;
+}
+
+const std::vector<ContactRecord> &SimContactsRepository::getDuplicatedRecords()
+{
+ return duplicatedRecords;
+}
+
+void SimContactsRepository::clear()
+{
+ importedRecords.clear();
+ uniqueRecords.clear();
+ duplicatedRecords.clear();
+}
+
+void SimContactsRepository::findDuplicates(const std::vector<bool> &selectedContacts,
+ AbstractSimContactsRepository::OnDupplicatesCheckCallback callback)
+{
+ std::vector<ContactRecord> 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<db::query::CheckContactsListDuplicates>(recordsToSave);
+ auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Contact);
+ task->setCallback([&, callback](auto response) {
+ auto result = dynamic_cast<db::query::CheckContactsListDuplicatesResult *>(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<bool> &selectedContacts,
+ bool duplicatesFound,
+ OnSaveCallback callback)
+{
+ std::vector<ContactRecord> 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<db::query::MergeContactsList>(recordsToSave);
+ auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Contact);
+ task->setCallback([&, callback](auto response) {
+ auto result = dynamic_cast<db::query::MergeContactsListResult *>(response);
+ if (result == nullptr) {
+ return false;
+ }
+ if (callback) {
+ callback();
+ }
+ return true;
+ });
+ task->execute(application, this);
+}
+
+void SimContactsRepository::read(AbstractSimContactsRepository::OnReadCallback readDoneCallback)
+{
+ std::function<void(const std::vector<cellular::SimContact> &simData)> callback =
+ [&](const std::vector<cellular::SimContact> &simData) { updateImportedRecords(simData); };
+
+ auto msg = std::make_unique<cellular::GetSimContactsRequest>();
+ auto task = app::AsyncRequest::createFromMessage(std::move(msg), cellular::service::name);
+ auto cb = [callback, readDoneCallback](auto response) {
+ auto result = dynamic_cast<cellular::GetSimContactsResponse *>(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<cellular::SimContact> &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<ContactRecord> &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
A module-apps/application-settings/models/network/SimContactsRepository.hpp => module-apps/application-settings/models/network/SimContactsRepository.hpp +49 -0
@@ 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 <apps-common/Application.hpp>
+#include <module-db/Interface/ContactRecord.hpp>
+#include <service-cellular/CellularMessage.hpp>
+
+class AbstractSimContactsRepository
+{
+ public:
+ using OnReadCallback = std::function<void()>;
+ using OnDupplicatesCheckCallback = std::function<void(bool duplicatesFound)>;
+ using OnSaveCallback = std::function<void()>;
+
+ virtual ~AbstractSimContactsRepository() noexcept = default;
+
+ virtual const std::vector<ContactRecord> &getImportedRecords() = 0;
+ virtual const std::vector<ContactRecord> &getDuplicatedRecords() = 0;
+ virtual void read(OnReadCallback callback) = 0;
+ virtual void clear() = 0;
+ virtual void save(const std::vector<bool> &selectedContacts, bool duplicatesFound, OnSaveCallback callback) = 0;
+ virtual void findDuplicates(const std::vector<bool> &selectedContacts, OnDupplicatesCheckCallback callback) = 0;
+};
+
+class SimContactsRepository : public AbstractSimContactsRepository, public app::AsyncCallbackReceiver
+{
+ public:
+ explicit SimContactsRepository(app::Application *application);
+
+ const std::vector<ContactRecord> &getImportedRecords() override;
+ const std::vector<ContactRecord> &getDuplicatedRecords() override;
+ void read(OnReadCallback callback) override;
+ void clear() override;
+ void save(const std::vector<bool> &selectedContacts, bool duplicatesFound, OnSaveCallback callback) override;
+ void findDuplicates(const std::vector<bool> &selectedContacts, OnDupplicatesCheckCallback callback) override;
+ void updateImportedRecords(const std::vector<cellular::SimContact> &simData);
+
+ private:
+ std::vector<ContactRecord> importedRecords;
+ std::vector<ContactRecord> uniqueRecords;
+ std::vector<ContactRecord> duplicatedRecords;
+ app::Application *application;
+
+#if DEBUG_SIM_IMPORT_DATA == 1
+ void printRecordsData(const std::string &name, const std::vector<ContactRecord> &data);
+#endif
+};
M module-apps/application-settings/presenter/network/SimContactsImportWindowPresenter.cpp => module-apps/application-settings/presenter/network/SimContactsImportWindowPresenter.cpp +60 -5
@@ 4,16 4,71 @@
#include "SimContactsImportWindowPresenter.hpp"
SimContactsImportWindowPresenter::SimContactsImportWindowPresenter(
- std::shared_ptr<SimContactsImportModel> simContactsProvider)
- : simContactsProvider{std::move(simContactsProvider)}
-{}
+ app::Application *application, std::shared_ptr<SimContactsImportModel> 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<gui::ListItemProvider> 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);
}
M module-apps/application-settings/presenter/network/SimContactsImportWindowPresenter.hpp => module-apps/application-settings/presenter/network/SimContactsImportWindowPresenter.hpp +23 -4
@@ 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<SimContactsImportWindowContract::View>
{
@@ 20,18 24,33 @@ class SimContactsImportWindowContract
virtual ~Presenter() noexcept = default;
virtual std::shared_ptr<gui::ListItemProvider> 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<SimContactsImportModel> simContactsProvider);
+ explicit SimContactsImportWindowPresenter(app::Application *application,
+ std::shared_ptr<SimContactsImportModel> simContactsProvider);
std::shared_ptr<gui::ListItemProvider> 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<void()> onSave = nullptr;
+ std::function<void(bool)> onDuplicatesCheck = nullptr;
+ std::function<void()> onSimContactsReady = nullptr;
std::shared_ptr<SimContactsImportModel> simContactsProvider;
};
M module-apps/application-settings/widgets/network/SimContactImportSelectWidget.cpp => module-apps/application-settings/widgets/network/SimContactImportSelectWidget.cpp +6 -1
@@ 6,7 6,7 @@
namespace gui
{
SimContactImportSelectWidget::SimContactImportSelectWidget(
- std::string contactName,
+ const std::string &contactName,
const std::function<void(const UTF8 &text)> &bottomBarTemporaryMode,
const std::function<void()> &bottomBarRestoreFromTemporaryMode)
{
@@ 38,4 38,9 @@ namespace gui
return true;
};
}
+
+ bool SimContactImportSelectWidget::isChecked()
+ {
+ return checkBoxWithLabel->isChecked();
+ }
} /* namespace gui */
M module-apps/application-settings/widgets/network/SimContactImportSelectWidget.hpp => module-apps/application-settings/widgets/network/SimContactImportSelectWidget.hpp +2 -1
@@ 14,9 14,10 @@ namespace gui
gui::CheckBoxWithLabel *checkBoxWithLabel = nullptr;
public:
- SimContactImportSelectWidget(std::string contactName,
+ SimContactImportSelectWidget(const std::string &contactName,
const std::function<void(const UTF8 &text)> &bottomBarTemporaryMode = nullptr,
const std::function<void()> &bottomBarRestoreFromTemporaryMode = nullptr);
+ bool isChecked();
};
} /* namespace gui */
M module-apps/application-settings/windows/network/SimCardsWindow.cpp => module-apps/application-settings/windows/network/SimCardsWindow.cpp +1 -1
@@ 85,7 85,7 @@ namespace gui
optList.emplace_back(std::make_unique<gui::option::OptionSettings>(
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,
M module-apps/application-settings/windows/network/SimContactsImportWindow.cpp => module-apps/application-settings/windows/network/SimContactsImportWindow.cpp +149 -14
@@ 4,6 4,8 @@
#include "SimContactsImportWindow.hpp"
#include <application-settings/windows/WindowNames.hpp>
+#include <InputEvent.hpp>
+#include <service-appmgr/Controller.hpp>
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
M module-apps/application-settings/windows/network/SimContactsImportWindow.hpp => module-apps/application-settings/windows/network/SimContactsImportWindow.hpp +12 -2
@@ 6,22 6,32 @@
#include <application-settings/windows/BaseSettingsWindow.hpp>
#include <application-settings/presenter/network/SimContactsImportWindowPresenter.hpp>
#include <application-settings/models/network/SimContactsImportModel.hpp>
+#include <Icon.hpp>
namespace gui
{
class SimContactsImportWindow : public AppWindow, public SimContactsImportWindowContract::View
{
-
public:
SimContactsImportWindow(app::Application *app,
std::unique_ptr<SimContactsImportWindowContract::Presenter> presenter);
private:
- ListView *list = nullptr;
+ ListView *list = nullptr;
+ Icon *emptyListIcon = nullptr;
+ std::function<void()> onEnterInputCallback = nullptr;
+ std::function<void()> 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<SimContactsImportWindowPresenter::Presenter> presenter;
};
M module-db/Interface/ContactRecord.cpp => module-db/Interface/ContactRecord.cpp +6 -2
@@ 1231,8 1231,9 @@ auto ContactRecordInterface::MergeContactsList(std::vector<ContactRecord> &conta
}
auto ContactRecordInterface::CheckContactsListDuplicates(std::vector<ContactRecord> &contacts)
- -> std::vector<ContactRecord>
+ -> std::pair<std::vector<ContactRecord>, std::vector<ContactRecord>>
{
+ std::vector<ContactRecord> unique;
std::vector<ContactRecord> duplicates;
std::vector<ContactNumberHolder> contactNumberHolders;
auto numberMatcher = buildNumberMatcher(contactNumberHolders);
@@ 1246,6 1247,9 @@ auto ContactRecordInterface::CheckContactsListDuplicates(std::vector<ContactReco
if (matchedNumber != numberMatcher.END) {
duplicates.push_back(contact);
}
+ else {
+ unique.push_back(contact);
+ }
}
- return duplicates;
+ return {unique, duplicates};
}
M module-db/Interface/ContactRecord.hpp => module-db/Interface/ContactRecord.hpp +3 -2
@@ 232,9 232,10 @@ class ContactRecordInterface : public RecordInterface<ContactRecord, ContactReco
* @brief Check which contacts in vector are duplicating contacts in DB
*
* @param contacts vector of contacts with single number
- * @return vector of contacts with numbers appearing in contacts DB
+ * @return first vector of contacts with unique numbers and second with duplicates appearing in contacts DB
*/
- auto CheckContactsListDuplicates(std::vector<ContactRecord> &contacts) -> std::vector<ContactRecord>;
+ auto CheckContactsListDuplicates(std::vector<ContactRecord> &contacts)
+ -> std::pair<std::vector<ContactRecord>, std::vector<ContactRecord>>;
private:
ContactsDB *contactDB;
M module-db/queries/phonebook/QueryCheckContactsListDuplicates.cpp => module-db/queries/phonebook/QueryCheckContactsListDuplicates.cpp +8 -2
@@ 20,10 20,16 @@ std::vector<ContactRecord> &CheckContactsListDuplicates::getContactsList()
return "CheckContactsListDuplicates";
}
-CheckContactsListDuplicatesResult::CheckContactsListDuplicatesResult(std::vector<ContactRecord> duplicates)
- : duplicates(std::move(duplicates))
+CheckContactsListDuplicatesResult::CheckContactsListDuplicatesResult(
+ std::pair<std::vector<ContactRecord>, std::vector<ContactRecord>> result)
+ : unique(std::move(result.first)), duplicates(std::move(result.second))
{}
+std::vector<ContactRecord> &CheckContactsListDuplicatesResult::getUnique()
+{
+ return unique;
+}
+
std::vector<ContactRecord> &CheckContactsListDuplicatesResult::getDuplicates()
{
return duplicates;
M module-db/queries/phonebook/QueryCheckContactsListDuplicates.hpp => module-db/queries/phonebook/QueryCheckContactsListDuplicates.hpp +4 -1
@@ 28,12 28,15 @@ namespace db::query
class CheckContactsListDuplicatesResult : public QueryResult
{
public:
- CheckContactsListDuplicatesResult(std::vector<ContactRecord> duplicates);
+ explicit CheckContactsListDuplicatesResult(
+ std::pair<std::vector<ContactRecord>, std::vector<ContactRecord>> result);
+ std::vector<ContactRecord> &getUnique();
std::vector<ContactRecord> &getDuplicates();
[[nodiscard]] auto debugInfo() const -> std::string override;
private:
+ std::vector<ContactRecord> unique;
std::vector<ContactRecord> duplicates;
};
M module-db/tests/ContactsRecord_tests.cpp => module-db/tests/ContactsRecord_tests.cpp +11 -2
@@ 559,8 559,10 @@ TEST_CASE("Contacts list duplicates search")
}
// Prepare contacts list to compare with DB
+ std::pair<std::string, std::string> uniqueContact = {"600100500", "test5"};
std::array<std::pair<std::string, std::string>, 3> rawContactsToCheck = {
- {rawContactsInitial[2], {"600100500", "test5"}, rawContactsInitial[0]}};
+ {rawContactsInitial[2], uniqueContact, rawContactsInitial[0]}};
+ constexpr auto numOfUniqueContacts = 1;
constexpr auto numOfDuplicatedContacts = 2;
std::vector<ContactRecord> contacts;
@@ 570,7 572,14 @@ TEST_CASE("Contacts list duplicates search")
record.numbers = std::vector<ContactRecord::Number>({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);
M module-gui/gui/widgets/CheckBox.cpp => module-gui/gui/widgets/CheckBox.cpp +2 -1
@@ 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 */
M module-gui/gui/widgets/CheckBox.hpp => module-gui/gui/widgets/CheckBox.hpp +1 -0
@@ 11,6 11,7 @@ namespace gui
{
class CheckBox : public HBox
{
+ bool checkState = false;
Image *image = nullptr;
std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr;
std::function<void()> bottomBarRestoreFromTemporaryMode = nullptr;
M module-gui/gui/widgets/ListItem.hpp => module-gui/gui/widgets/ListItem.hpp +5 -5
@@ 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 T> class ListItemWithCallbacks : public ListItem
{
public:
- std::function<bool()> onEmptyCallback = nullptr;
- std::function<bool()> onContentChangedCallback = nullptr;
- std::function<void(std::shared_ptr<T> record)> onSaveCallback = nullptr;
- std::function<void(std::shared_ptr<T> record)> onLoadCallback = nullptr;
+ std::function<bool()> onEmptyCallback = nullptr;
+ std::function<bool()> onContentChangedCallback = nullptr;
+ std::function<void(std::shared_ptr<T> record)> onSaveCallback = nullptr;
+ std::function<void(std::shared_ptr<T> record)> onLoadCallback = nullptr;
};
} /* namespace gui */
M module-gui/gui/widgets/ListView.cpp => module-gui/gui/widgets/ListView.cpp +8 -0
@@ 207,6 207,14 @@ namespace gui
};
}
+ void ListView::clear()
+ {
+ if (scroll) {
+ scroll->setVisible(false);
+ }
+ ListViewEngine::clear();
+ }
+
void ListView::setFocus()
{
if (!focus) {
M module-gui/gui/widgets/ListView.hpp => module-gui/gui/widgets/ListView.hpp +1 -0
@@ 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
M module-gui/gui/widgets/ListViewEngine.hpp => module-gui/gui/widgets/ListViewEngine.hpp +1 -1
@@ 177,7 177,7 @@ namespace gui
std::function<void()> prepareRebuildCallback;
void reset();
- void clear();
+ virtual void clear();
void onClose();
std::shared_ptr<ListItemProvider> getProvider();
M module-gui/gui/widgets/Style.hpp => module-gui/gui/widgets/Style.hpp +3 -0
@@ 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
M module-utils/log/debug.hpp => module-utils/log/debug.hpp +1 -0
@@ 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