Files
MuditaOS/module-apps/application-messages/ApplicationMessages.cpp
Adam Dobrowolski 0ec9428917 [EGD-7773] Popups stack poc
Stack plus minimum tests added
Updated to master
Removed useless or adressed TODOS
Constants name applied
Renamed searchModel to searchRequestModel
Review applied
We should remove legacy window names
Bell names fixes
2022-01-04 13:01:23 +01:00

419 lines
18 KiB
C++

// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "ApplicationMessages.hpp"
#include "MessagesMainWindow.hpp"
#include "NewMessage.hpp"
#include "OptionsMessages.hpp"
#include "SearchResults.hpp"
#include "SearchStart.hpp"
#include "SMSTemplatesWindow.hpp"
#include "SMSTextToSearch.hpp"
#include "SMSThreadViewWindow.hpp"
#include "ThreadWindowOptions.hpp"
#include <Dialog.hpp>
#include <DialogMetadata.hpp>
#include <DialogMetadataMessage.hpp>
#include <i18n/i18n.hpp>
#include <memory>
#include <messages/OptionsWindow.hpp>
#include <MessageType.hpp>
#include <module-db/queries/messages/sms/QuerySMSAdd.hpp>
#include <module-db/queries/messages/sms/QuerySMSRemove.hpp>
#include <module-db/queries/messages/sms/QuerySMSUpdate.hpp>
#include <module-db/queries/messages/threads/QueryThreadGetByID.hpp>
#include <module-db/queries/messages/threads/QueryThreadRemove.hpp>
#include <module-db/queries/phonebook/QueryContactGetByNumberID.hpp>
#include <OptionsWindow.hpp>
#include <OptionWindow.hpp>
#include <service-cellular/CellularMessage.hpp>
#include <service-db/DBNotificationMessage.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <service-db/QueryMessage.hpp>
#include <cassert>
#include <ctime>
#include <utility>
namespace app
{
static constexpr auto messagesStackDepth = 1024 * 6; // 6Kb stack size
ApplicationMessages::ApplicationMessages(std::string name,
std::string parent,
StatusIndicators statusIndicators,
StartInBackground startInBackground)
: Application(std::move(name), std::move(parent), statusIndicators, startInBackground, messagesStackDepth),
AsyncCallbackReceiver{this}
{
bus.channels.push_back(sys::BusChannel::ServiceDBNotifications);
addActionReceiver(manager::actions::CreateSms, [this](auto &&data) {
switchWindow(gui::name::window::new_sms, std::move(data));
return actionHandled();
});
addActionReceiver(manager::actions::ShowSmsTemplates, [this](auto &&data) {
switchWindow(gui::name::window::sms_templates, std::move(data));
return actionHandled();
});
addActionReceiver(manager::actions::SmsRejectNoSim, [this](auto &&data) {
auto action = [this]() {
returnToPreviousWindow();
return true;
};
const auto notification = utils::translate("app_messages_no_sim");
showNotification(action, notification);
return actionHandled();
});
addActionReceiver(manager::actions::SMSRejectedByOfflineNotification, [this](auto &&data) {
auto action = [=]() -> bool {
returnToPreviousWindow();
return true;
};
const auto notification = utils::translate("app_sms_offline");
showNotification(action, notification);
return actionHandled();
});
}
// Invoked upon receiving data message
sys::MessagePointer ApplicationMessages::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)
{
auto retMsg = Application::DataReceivedHandler(msgl);
// if message was handled by application's template there is no need to process further.
if (reinterpret_cast<sys::ResponseMessage *>(retMsg.get())->retCode == sys::ReturnCodes::Success) {
return retMsg;
}
if (msgl->messageType == MessageType::DBServiceNotification) {
auto msg = dynamic_cast<db::NotificationMessage *>(msgl);
if (msg != nullptr) {
userInterfaceDBNotification(msgl,
[&]([[maybe_unused]] sys::Message *, [[maybe_unused]] const std::string &) {
return msg->interface == db::Interface::Name::SMSThread ||
msg->interface == db::Interface::Name::SMS;
});
return std::make_shared<sys::ResponseMessage>();
}
}
return handleAsyncResponse(resp);
}
// Invoked during initialization
sys::ReturnCodes ApplicationMessages::InitHandler()
{
auto ret = Application::InitHandler();
if (ret != sys::ReturnCodes::Success) {
return ret;
}
createUserInterface();
return ret;
}
void ApplicationMessages::createUserInterface()
{
windowsFactory.attach(gui::name::window::main_window, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::MessagesMainWindow>(app);
});
windowsFactory.attach(gui::name::window::thread_view, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::SMSThreadViewWindow>(app);
});
windowsFactory.attach(gui::name::window::new_sms, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::NewMessageWindow>(app);
});
windowsFactory.attach(window::name::option_window, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::OptionWindow>(app, name);
});
windowsFactory.attach(gui::name::window::thread_options, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::ThreadWindowOptions>(app, name);
});
windowsFactory.attach(gui::name::window::sms_options, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::SmsWindowOptions>(app, name);
});
windowsFactory.attach(gui::name::window::dialog, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::Dialog>(app, name);
});
windowsFactory.attach(gui::name::window::dialog_confirm, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::DialogConfirm>(app, name);
});
windowsFactory.attach(gui::name::window::dialog_yes_no, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::DialogYesNo>(app, name);
});
windowsFactory.attach(
gui::name::window::thread_sms_search,
[](ApplicationCommon *app, const std::string &name) { return std::make_unique<gui::SMSSearch>(app); });
windowsFactory.attach(gui::name::window::sms_templates, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::SMSTemplatesWindow>(app);
});
windowsFactory.attach(gui::name::window::search_results, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::SearchResults>(app);
});
attachPopups({gui::popup::ID::Volume,
gui::popup::ID::Tethering,
gui::popup::ID::PhoneModes,
gui::popup::ID::PhoneLock,
gui::popup::ID::Alarm});
}
void ApplicationMessages::destroyUserInterface()
{}
bool ApplicationMessages::markSmsThreadAsRead(const uint32_t id)
{
using namespace db::query;
LOG_DEBUG("markSmsThreadAsRead");
DBServiceAPI::GetQuery(
this, db::Interface::Name::SMSThread, std::make_unique<MarkAsRead>(id, MarkAsRead::Read::True));
return true;
}
bool ApplicationMessages::markSmsThreadAsUnread(const uint32_t id)
{
using namespace db::query;
LOG_DEBUG("markSmsThreadAsUnRead");
DBServiceAPI::GetQuery(
this, db::Interface::Name::SMSThread, std::make_unique<MarkAsRead>(id, MarkAsRead::Read::False));
return true;
}
bool ApplicationMessages::removeSmsThread(const ThreadRecord *record)
{
using db::query::ContactGetByNumberID;
using db::query::ContactGetByNumberIDResult;
if (record == nullptr) {
LOG_ERROR("Trying to remove a null SMS thread!");
return false;
}
LOG_DEBUG("Removing thread: %" PRIu32, record->ID);
auto query = std::make_unique<ContactGetByNumberID>(record->numberID);
auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Contact);
task->setCallback([this, record](auto response) {
auto result = dynamic_cast<ContactGetByNumberIDResult *>(response);
if (result != nullptr) {
const auto &contact = result->getResult();
auto metaData = std::make_unique<gui::DialogMetadataMessage>(
gui::DialogMetadata{contact.getFormattedName(),
"delete_128px_W_G",
utils::translate("app_messages_thread_delete_confirmation"),
"",
[this, record]() { return onRemoveSmsThreadConfirmed(*record); }});
switchWindow(gui::name::window::dialog_yes_no, gui::ShowMode::GUI_SHOW_INIT, std::move(metaData));
return true;
}
return false;
});
task->execute(this, this);
return true;
}
bool ApplicationMessages::onRemoveSmsThreadConfirmed(const ThreadRecord &record)
{
using db::query::ThreadRemove;
using db::query::ThreadRemoveResult;
auto query = std::make_unique<ThreadRemove>(record.ID);
auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMSThread);
task->setCallback([this, threadId = record.ID](auto response) {
const auto result = dynamic_cast<ThreadRemoveResult *>(response);
if ((result != nullptr) && result->success()) {
switchWindow(gui::name::window::main_window);
return true;
}
LOG_ERROR("ThreadRemove id=%" PRIu32 " failed", threadId);
return false;
});
task->execute(this, this);
return true;
}
bool ApplicationMessages::removeSms(const SMSRecord &record)
{
LOG_DEBUG("Removing sms: %" PRIu32, record.ID);
auto metaData = std::make_unique<gui::DialogMetadataMessage>(
gui::DialogMetadata{record.body,
"delete_128px_W_G",
utils::translate("app_messages_message_delete_confirmation"),
"",
[this, record] { return onRemoveSmsConfirmed(record); }});
switchWindow(gui::name::window::dialog_yes_no, gui::ShowMode::GUI_SHOW_INIT, std::move(metaData));
return true;
}
bool ApplicationMessages::onRemoveSmsConfirmed(const SMSRecord &record)
{
using db::query::SMSRemove;
using db::query::SMSRemoveResult;
using db::query::ThreadGetByID;
using db::query::ThreadGetByIDResult;
auto query = std::make_unique<SMSRemove>(record.ID);
auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMS);
task->setCallback([this, record](auto response) {
auto result = dynamic_cast<SMSRemoveResult *>(response);
if (result != nullptr && result->getResults()) {
auto query = std::make_unique<ThreadGetByID>(record.threadID);
auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMSThread);
task->setCallback([this](auto response) {
const auto result = dynamic_cast<ThreadGetByIDResult *>(response);
if (result != nullptr) {
const auto thread = result->getRecord();
if (thread.has_value()) {
this->switchWindow(gui::name::window::thread_view);
}
else {
this->switchWindow(gui::name::window::main_window);
}
return true;
}
return false;
});
task->execute(this, this);
return true;
}
LOG_ERROR("sSMSRemove id=%" PRIu32 " failed", record.ID);
return false;
});
task->execute(this, this);
return true;
}
bool ApplicationMessages::searchEmpty(const std::string &query)
{
gui::DialogMetadata meta;
meta.icon = "search_128px_W_G";
meta.text = utils::translate("app_messages_thread_no_result");
meta.title = utils::translate("common_results_prefix") + query;
auto data = std::make_unique<gui::DialogMetadataMessage>(meta);
data->ignoreCurrentWindowOnStack = true;
switchWindow(gui::name::window::dialog, std::make_unique<gui::DialogMetadataMessage>(meta));
return true;
}
bool ApplicationMessages::showSearchResults(const UTF8 &title, const UTF8 &search_text)
{
switchWindow(gui::name::window::search_results, std::make_unique<SMSTextToSearch>(search_text, title));
return true;
}
bool ApplicationMessages::showNotification(std::function<bool()> action,
const std::string &notification,
bool ignoreCurrentWindowOnStack)
{
gui::DialogMetadata meta;
meta.icon = "info_128px_W_G";
meta.text = notification;
meta.action = std::move(action);
auto switchData = std::make_unique<gui::DialogMetadataMessage>(meta);
switchData->ignoreCurrentWindowOnStack = ignoreCurrentWindowOnStack;
switchWindow(gui::name::window::dialog_confirm, std::move(switchData));
return true;
}
bool ApplicationMessages::updateDraft(SMSRecord &record, const UTF8 &body)
{
assert(!body.empty()); // precondition check.
record.body = body;
record.date = std::time(nullptr);
using db::query::SMSUpdate;
const auto [succeed, _] =
DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSUpdate>(record));
return succeed;
}
void ApplicationMessages::createDraft(const utils::PhoneNumber::View &number,
const UTF8 &body,
std::function<void(const SMSRecord &)> onSuccess)
{
assert(!body.empty()); // precondition check.
SMSRecord record;
record.number = number;
record.body = body;
record.type = SMSType::DRAFT;
record.date = std::time(nullptr);
using db::query::SMSAdd;
auto query = std::make_unique<db::query::SMSAdd>(record);
auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMS);
task->setCallback([onSuccess](auto response) {
using db::query::SMSAddResult;
if (auto result = static_cast<SMSAddResult *>(response); result->succeed()) {
if (onSuccess) {
onSuccess(result->record);
}
return true;
}
LOG_ERROR("Failed to add an SMS record to the DB.");
return false;
});
task->execute(this, this);
}
bool ApplicationMessages::removeDraft(const SMSRecord &record)
{
using db::query::SMSRemove;
if (!record.isValid()) {
LOG_ERROR("Draft SMS is invalid.");
return false;
}
const auto [succeed, _] =
DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSRemove>(record.ID));
return succeed;
}
bool ApplicationMessages::sendSms(const utils::PhoneNumber::View &number, const UTF8 &body)
{
if (number.getEntered().empty() || body.length() == 0) {
LOG_WARN("Number or sms body is empty");
return false;
}
SMSRecord record;
record.number = number;
record.body = body;
record.type = SMSType::QUEUED;
record.date = std::time(nullptr);
using db::query::SMSAdd;
const auto [succeed, _] =
DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSAdd>(record));
return succeed;
}
bool ApplicationMessages::resendSms(const SMSRecord &record)
{
auto resendRecord = record;
resendRecord.type = SMSType::QUEUED;
resendRecord.date = std::time(nullptr); // update date sent - it will display an old, failed sms at
// the the bottom, but this is correct
using db::query::SMSUpdate;
const auto [succeed, _] =
DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSUpdate>(resendRecord));
return succeed;
}
bool ApplicationMessages::handleSendSmsFromThread(const utils::PhoneNumber::View &number, const UTF8 &body)
{
return sendSms(number, body);
}
bool ApplicationMessages::newMessageOptions(const std::string &requestingWindow, gui::Text *text)
{
LOG_INFO("New message options for %s", requestingWindow.c_str());
auto opts = std::make_unique<gui::OptionsWindowOptions>(newMessageWindowOptions(this, requestingWindow, text));
switchWindow(window::name::option_window, std::move(opts));
return true;
}
} /* namespace app */