Files
MuditaOS/module-apps/application-call/ApplicationCall.cpp
Kuba 106440c78d [MOS-326] Change call logic removed from call window
Call logic is now removed from call window and call app.
There was spaghetti logic mixed in window and app, now
call logic is based on notificatins from service cellular.
2022-04-22 12:59:29 +02:00

270 lines
12 KiB
C++

// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "ApplicationCall.hpp"
#include "CallSwitchData.hpp"
#include "CallWindow.hpp"
#include "EmergencyCallWindow.hpp"
#include "EnterNumberWindow.hpp"
#include <apps-common/messages/DialogMetadataMessage.hpp>
#include <apps-common/windows/Dialog.hpp>
#include <apps-common/windows/DialogMetadata.hpp>
#include <log/log.hpp>
#include <module-apps/application-phonebook/data/PhonebookItemData.hpp>
#include <PhoneNumber.hpp>
#include <service-appmgr/Controller.hpp>
#include <service-appmgr/data/MmiActionsParams.hpp>
#include <service-cellular/CellularServiceAPI.hpp>
#include <time/time_conversion.hpp>
#include <WindowsPopupFilter.hpp>
#include <cassert>
#include <memory>
namespace app
{
ApplicationCall::ApplicationCall(std::string name,
std::string parent,
StatusIndicators statusIndicators,
StartInBackground startInBackground)
: Application(name, parent, statusIndicators, startInBackground, app::call_stack_size)
{
using namespace gui::status_bar;
getPopupFilter().addAppDependentFilter([&](const gui::PopupRequestParams &popupParams) {
if (popupParams.getPopupId() == gui::popup::ID::Volume) {
return true;
}
return true;
});
statusBarManager->enableIndicators(
{Indicator::Signal, Indicator::Time, Indicator::Battery, Indicator::SimCard});
addActionReceiver(manager::actions::Call, [this](auto &&data) {
if (auto msg = dynamic_cast<app::CallSwitchData *>(data.get()); msg != nullptr) {
handleCallEvent(msg->getPhoneNumber().getEntered(), ExternalRequest::True);
return actionHandled();
}
return actionNotHandled();
});
addActionReceiver(manager::actions::Dial, [this](auto &&data) {
switchWindow(window::name_enterNumber, std::forward<decltype(data)>(data));
return actionHandled();
});
addActionReceiver(manager::actions::EmergencyDial, [this](auto &&data) {
switchWindow(app::window::name_emergencyCall, std::forward<decltype(data)>(data));
return actionHandled();
});
addActionReceiver(manager::actions::NotAnEmergencyNotification, [this](auto &&data) {
auto textNoEmergency = utils::translate("app_call_wrong_emergency");
utils::findAndReplaceAll(textNoEmergency, "$NUMBER", data->getDescription());
showNotificationAndRestartCallFlow(NotificationType::Info, textNoEmergency);
return actionHandled();
});
addActionReceiver(manager::actions::NoSimNotification, [this](auto &&data) {
showNotificationAndRestartCallFlow(NotificationType::Info, utils::translate("app_call_no_sim"));
return actionHandled();
});
addActionReceiver(manager::actions::NoNetworkConnectionNotification, [this](auto &&data) {
showNotificationAndRestartCallFlow(NotificationType::Info,
utils::translate("app_call_no_network_connection"));
return actionHandled();
});
addActionReceiver(manager::actions::CallRequestGeneralErrorNotification, [this](auto &&data) {
showNotificationAndRestartCallFlow(NotificationType::Failure,
utils::translate("app_call_call_request_failed"));
return actionHandled();
});
addActionReceiver(manager::actions::CallRejectedByOfflineNotification, [this](auto &&data) {
showNotificationAndRestartCallFlow(NotificationType::Info, utils::translate("app_call_offline"));
return actionHandled();
});
addActionReceiver(manager::actions::AbortCall, [this](auto &&data) {
if (const auto state = getState(); state == Application::State::ACTIVE_FORGROUND) {
switchWindow(window::name_call);
}
else if (state == Application::State::ACTIVE_BACKGROUND) {
callModel->setState(app::call::CallState::None);
manager::Controller::finish(this);
}
return actionHandled();
});
addActionReceiver(manager::actions::ActivateCall, [this](auto &&data) {
switchWindow(window::name_call);
return actionHandled();
});
addActionReceiver(manager::actions::HandleOutgoingCall, [this](auto &&data) {
switchWindow(window::name_call);
return actionHandled();
});
addActionReceiver(manager::actions::HandleIncomingCall, [this](auto &&data) {
callModel->setState(call::CallState::Incoming);
auto window = getCurrentWindow();
if (window->getName() != app::window::name_call) {
LOG_INFO("Switch to call window");
switchWindow(app::window::name_call);
}
return actionHandled();
});
addActionReceiver(manager::actions::HandleCallerId, [this](auto &&data) {
auto callParams = static_cast<app::manager::actions::CallParams *>(data.get());
callModel->setPhoneNumber(callParams->getNumber());
callModel->setState(call::CallState::Incoming);
auto window = getCurrentWindow();
if (window->getName() != app::window::name_call) {
LOG_INFO("Switch to call window");
switchWindow(app::window::name_call);
}
return actionHandled();
});
callModel = std::make_shared<app::call::CallModel>(this);
}
bool ApplicationCall::conditionalReturnToPreviousView()
{
// if external request simply return to previous app
if (externalRequest == ExternalRequest::True) {
app::manager::Controller::switchBack(this);
return true;
}
returnToPreviousWindow();
return true;
}
// Invoked upon receiving data message
sys::MessagePointer ApplicationCall::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.
auto response = dynamic_cast<sys::ResponseMessage *>(retMsg.get());
assert(response);
if (response->retCode == sys::ReturnCodes::Success) {
return retMsg;
}
return handleAsyncResponse(resp);
} // namespace app
// Invoked during initialization
sys::ReturnCodes ApplicationCall::InitHandler()
{
auto ret = Application::InitHandler();
if (ret != sys::ReturnCodes::Success) {
return ret;
}
connect(typeid(cellular::CallDurationNotification), [&](sys::Message *request) {
auto message = static_cast<cellular::CallDurationNotification *>(request);
callModel->setTime(message->callDuration);
return sys::MessageNone{};
});
connect(typeid(cellular::CallActiveNotification), [&](sys::Message *request) {
callModel->setState(app::call::CallState::Active);
return sys::MessageNone{};
});
connect(typeid(cellular::CallEndedNotification), [&](sys::Message *request) {
callModel->setState(app::call::CallState::Ended);
return sys::MessageNone{};
});
connect(typeid(cellular::CallStartedNotification), [&](sys::Message *request) {
auto message = static_cast<cellular::CallStartedNotification *>(request);
callModel->setPhoneNumber(message->getNumber());
callModel->setState(app::call::CallState::Outgoing);
return sys::MessageNone{};
});
createUserInterface();
return ret;
}
void ApplicationCall::createUserInterface()
{
windowsFactory.attach(app::window::name_enterNumber, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::EnterNumberWindow>(app, static_cast<ApplicationCall *>(app));
});
windowsFactory.attach(app::window::name_call, [this](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::CallWindow>(
app, std::make_unique<app::call::CallWindowContract::Presenter>(this->callModel));
});
windowsFactory.attach(app::window::name_emergencyCall, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::EmergencyCallWindow>(app, static_cast<ApplicationCall *>(app));
});
windowsFactory.attach(app::window::name_dialogConfirm, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::DialogConfirm>(app, name);
});
attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
}
bool ApplicationCall::showNotification(std::function<bool()> action,
const std::string &icon,
const std::string &text)
{
auto metaData =
std::make_unique<gui::DialogMetadataMessage>(gui::DialogMetadata{"", icon, text, "", std::move(action)});
switchWindow(app::window::name_dialogConfirm, gui::ShowMode::GUI_SHOW_INIT, std::move(metaData));
return true;
}
auto ApplicationCall::showNotificationAndRestartCallFlow(NotificationType type, const std::string &text) -> bool
{
auto buttonAction = [=]() -> bool { return conditionalReturnToPreviousView(); };
auto icon = type == NotificationType::Info ? "info_128px_W_G" : "fail_128px_W_G";
callModel->clear();
return showNotification(buttonAction, icon, text);
}
void ApplicationCall::destroyUserInterface()
{}
void ApplicationCall::handleEmergencyCallEvent(const std::string &number)
{
auto state = callModel->getState();
if (state != call::CallState::None) {
LOG_WARN("Cannot call in %s state", c_str(state));
return;
}
CellularServiceAPI::DialEmergencyNumber(this, utils::PhoneNumber(number));
}
void ApplicationCall::handleCallEvent(const std::string &number, ExternalRequest isExternalRequest)
{
auto state = callModel->getState();
if (state != call::CallState::None) {
LOG_WARN("Cannot call in %s state", c_str(state));
return;
}
CellularServiceAPI::DialNumber(this, utils::PhoneNumber(number));
externalRequest = isExternalRequest;
}
void ApplicationCall::handleAddContactEvent(const std::string &number)
{
LOG_INFO("add contact information");
auto numberView = utils::PhoneNumber(number).getView();
auto searchResults = DBServiceAPI::MatchContactByPhoneNumber(this, numberView);
if (searchResults != nullptr) {
LOG_INFO("Found contact matching search num : contact ID %" PRIu32, searchResults->ID);
app::manager::Controller::sendAction(this,
app::manager::actions::EditContact,
std::make_unique<PhonebookItemData>(std::move(searchResults)));
}
else {
auto contactRecord = std::make_shared<ContactRecord>();
contactRecord->numbers.emplace_back(std::move(numberView));
auto data = std::make_unique<PhonebookItemData>(std::move(contactRecord));
data->ignoreCurrentWindowOnStack = true;
app::manager::Controller::sendAction(
this, manager::actions::AddContact, std::move(data), manager::OnSwitchBehaviour::RunInBackground);
}
}
} // namespace app