M module-apps/application-call/ApplicationCall.cpp => module-apps/application-call/ApplicationCall.cpp +52 -83
@@ 12,7 12,7 @@
#include <apps-common/windows/DialogMetadata.hpp>
#include <log/log.hpp>
#include <module-apps/application-phonebook/data/PhonebookItemData.hpp>
-#include <Timers/TimerFactory.hpp>
+
#include <PhoneNumber.hpp>
#include <service-appmgr/Controller.hpp>
#include <service-appmgr/data/MmiActionsParams.hpp>
@@ 37,7 37,7 @@ namespace app
if (popupParams.getPopupId() == gui::popup::ID::Volume) {
return true;
}
- return this->getCallState() == call::State::IDLE;
+ return true;
});
statusBarManager->enableIndicators(
{Indicator::Signal, Indicator::Time, Indicator::Battery, Indicator::SimCard});
@@ 82,34 82,45 @@ namespace app
return actionHandled();
});
addActionReceiver(manager::actions::AbortCall, [this](auto &&data) {
- callerIdTimer.reset();
if (const auto state = getState(); state == Application::State::ACTIVE_FORGROUND) {
- switchWindow(window::name_call, std::make_unique<app::CallAbortData>());
+ switchWindow(window::name_call);
}
else if (state == Application::State::ACTIVE_BACKGROUND) {
- setCallState(call::State::IDLE);
+ callModel->setState(app::call::CallState::None);
manager::Controller::finish(this);
}
return actionHandled();
});
addActionReceiver(manager::actions::ActivateCall, [this](auto &&data) {
- switchWindow(window::name_call, std::make_unique<app::CallActiveData>());
+ switchWindow(window::name_call);
return actionHandled();
});
addActionReceiver(manager::actions::HandleOutgoingCall, [this](auto &&data) {
- auto callParams = static_cast<app::manager::actions::CallParams *>(data.get());
- switchWindow(window::name_call, std::make_unique<app::ExecuteCallData>(callParams->getNumber()));
+ switchWindow(window::name_call);
return actionHandled();
});
addActionReceiver(manager::actions::HandleIncomingCall, [this](auto &&data) {
- handleIncomingCall();
+ 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());
- handleCallerId(callParams);
+ 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()
@@ 145,6 156,29 @@ namespace app
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;
@@ 155,8 189,9 @@ namespace app
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, [](ApplicationCommon *app, const std::string &name) {
- return std::make_unique<gui::CallWindow>(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));
@@ 181,47 216,17 @@ namespace app
{
auto buttonAction = [=]() -> bool { return conditionalReturnToPreviousView(); };
auto icon = type == NotificationType::Info ? "info_128px_W_G" : "fail_128px_W_G";
- setCallState(call::State::IDLE);
+ callModel->clear();
return showNotification(buttonAction, icon, text);
}
void ApplicationCall::destroyUserInterface()
{}
- void ApplicationCall::handleIncomingCall()
- {
- if (getCallState() != call::State::IDLE) {
- return;
- }
-
- constexpr auto callerIdTimeout = std::chrono::seconds{1};
- callerIdTimer = sys::TimerFactory::createSingleShotTimer(
- this, "CallerIdTimer", callerIdTimeout, [this]([[maybe_unused]] sys::Timer &timer) {
- switchWindow(window::name_call,
- std::make_unique<app::IncomingCallData>(utils::PhoneNumber().getView()));
- });
- callerIdTimer.start();
- }
-
- void ApplicationCall::handleCallerId(const app::manager::actions::CallParams *params)
- {
- /// there is race condition in this code, we require `INCOMMING_CALL` state check due to it.
- /// alternatively we would require rewritting this application state handling
- if (getCallState() != call::State::IDLE && getCallState() != app::call::State::INCOMING_CALL) {
- LOG_ERROR("call id not handled");
- return;
- }
- if (callerIdTimer.isValid()) {
- callerIdTimer.stop();
- callerIdTimer.reset();
- }
- switchWindow(window::name_call, std::make_unique<app::IncomingCallData>(params->getNumber()));
- }
-
void ApplicationCall::handleEmergencyCallEvent(const std::string &number)
{
- auto state = getCallState();
- if (state != call::State::IDLE) {
+ auto state = callModel->getState();
+ if (state != call::CallState::None) {
LOG_WARN("Cannot call in %s state", c_str(state));
return;
}
@@ 230,13 235,12 @@ namespace app
void ApplicationCall::handleCallEvent(const std::string &number, ExternalRequest isExternalRequest)
{
- auto state = getCallState();
- if (state != call::State::IDLE) {
+ 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));
- setCallState(call::State::OUTGOING_CALL);
externalRequest = isExternalRequest;
}
@@ 262,39 266,4 @@ namespace app
this, manager::actions::AddContact, std::move(data), manager::OnSwitchBehaviour::RunInBackground);
}
}
-
- void ApplicationCall::sendAudioEvent(AudioEvent audioEvent)
- {
- switch (audioEvent) {
- case AudioEvent::Mute:
- CellularServiceAPI::CallAudioMuteEvent(this);
- break;
- case AudioEvent::Unmute:
- CellularServiceAPI::CallAudioUnmuteEvent(this);
- break;
- case AudioEvent::LoudspeakerOn:
- CellularServiceAPI::CallAudioLoudspeakerOnEvent(this);
- break;
- case AudioEvent::LoudspeakerOff:
- CellularServiceAPI::CallAudioLoudspeakerOffEvent(this);
- break;
- }
- }
-
- void ApplicationCall::hangupCall()
- {
- CellularServiceAPI::HangupCall(this);
- }
-
- void ApplicationCall::answerIncomingCall()
- {
- CellularServiceAPI::AnswerIncomingCall(this);
- }
-
- void ApplicationCall::transmitDtmfTone(uint32_t digit)
- {
- if (!CellularServiceAPI::TransmitDtmfTones(this, digit)) {
- LOG_ERROR("transmitDtmfTone failed");
- }
- }
} // namespace app
M module-apps/application-call/CMakeLists.txt => module-apps/application-call/CMakeLists.txt +6 -1
@@ 20,6 20,8 @@ target_sources(application-call
ApplicationCall.cpp
data/CallAppStyle.hpp
data/CallSwitchData.hpp
+ model/CallModel.cpp
+ presenter/CallPresenter.cpp
widgets/StateIcon.hpp
widgets/StateIcons.cpp
widgets/StateIcons.hpp
@@ 33,7 35,6 @@ target_sources(application-call
windows/NumberWindow.hpp
PUBLIC
include/application-call/ApplicationCall.hpp
- include/application-call/CallState.hpp
)
target_link_libraries(application-call
@@ 55,3 56,7 @@ target_link_libraries(application-call
service-cellular
service-evtmgr
)
+
+if (${ENABLE_TESTS})
+ add_subdirectory( test )
+endif()
M module-apps/application-call/data/CallSwitchData.hpp => module-apps/application-call/data/CallSwitchData.hpp +0 -19
@@ 57,29 57,10 @@ namespace app
}
};
- class IncomingCallData : public CallSwitchData
- {
- public:
- IncomingCallData(const utils::PhoneNumber::View &phoneNumber)
- : CallSwitchData(phoneNumber, CallSwitchData::Type::INCOMING_CALL){};
- };
-
class ExecuteCallData : public CallSwitchData
{
public:
ExecuteCallData(const utils::PhoneNumber::View &phoneNumber)
: CallSwitchData(phoneNumber, app::CallSwitchData::Type::EXECUTE_CALL){};
};
-
- class CallAbortData : public gui::SwitchData
- {
- public:
- CallAbortData() = default;
- };
-
- class CallActiveData : public gui::SwitchData
- {
- public:
- CallActiveData() = default;
- };
} /* namespace app */
M module-apps/application-call/include/application-call/ApplicationCall.hpp => module-apps/application-call/include/application-call/ApplicationCall.hpp +4 -55
@@ 3,8 3,7 @@
#pragma once
-#include "Application.hpp"
-#include "CallState.hpp"
+#include <application-call/model/CallModel.hpp>
#include <Timers/TimerHandle.hpp>
#include <service-cellular/CellularMessage.hpp>
@@ 13,6 12,7 @@
#include <Service/Message.hpp>
#include <SystemManager/SystemManagerCommon.hpp>
#include <AppWindowConstants.hpp>
+#include <products/PurePhone/apps/include/Application.hpp>
namespace app
{
@@ 28,29 28,6 @@ namespace app
inline constexpr auto name_dialogConfirm = "DialogConfirm";
inline constexpr auto name_number = "NumberWindow";
} // namespace window
- class CallWindowInterface
- {
- public:
- using NumberChangeCallback = std::function<void(utils::PhoneNumber::View)>;
- virtual ~CallWindowInterface() noexcept = default;
-
- virtual void setCallState(app::call::State state) noexcept = 0;
- [[nodiscard]] virtual auto getCallState() const noexcept -> call::State = 0;
-
- enum class AudioEvent
- {
- Mute,
- Unmute,
- LoudspeakerOn,
- LoudspeakerOff
- };
-
- virtual void sendAudioEvent(AudioEvent audioEvent) = 0;
-
- virtual void transmitDtmfTone(uint32_t digit) = 0;
- virtual void hangupCall() = 0;
- virtual void answerIncomingCall() = 0;
- };
class EnterNumberWindowInterface
{
@@ 67,7 44,7 @@ namespace app
virtual void handleAddContactEvent(const std::string &number) = 0;
};
- class ApplicationCall : public Application, public CallWindowInterface, public EnterNumberWindowInterface
+ class ApplicationCall : public Application, public EnterNumberWindowInterface
{
public:
explicit ApplicationCall(std::string name = name_call,
@@ 85,8 62,6 @@ namespace app
void createUserInterface() override;
void destroyUserInterface() override;
- void handleIncomingCall();
- void handleCallerId(const app::manager::actions::CallParams *params);
void handleEmergencyCallEvent(const std::string &number) override;
void handleCallEvent(const std::string &number, ExternalRequest isExternalRequest) override;
void handleAddContactEvent(const std::string &number) override;
@@ 99,36 74,10 @@ namespace app
};
auto showNotificationAndRestartCallFlow(NotificationType type, const std::string &text) -> bool;
- [[nodiscard]] auto getCallState() const noexcept -> call::State override
- {
- return callState;
- }
- void setCallState(call::State state) noexcept override
- {
- if (state == call::State::CALL_IN_PROGRESS) {
- bus.sendUnicast(
- std::make_shared<sevm::KeypadBacklightMessage>(bsp::keypad_backlight::Action::turnOnCallMode),
- service::name::evt_manager);
- }
- else {
- bus.sendUnicast(
- std::make_shared<sevm::KeypadBacklightMessage>(bsp::keypad_backlight::Action::turnOffCallMode),
- service::name::evt_manager);
- }
- this->callState = state;
- }
-
- void sendAudioEvent(AudioEvent audioEvent) override;
-
- void transmitDtmfTone(uint32_t digit) override;
- void hangupCall() override;
- void answerIncomingCall() override;
-
private:
- sys::TimerHandle callerIdTimer;
+ std::shared_ptr<app::call::AbstractCallModel> callModel;
protected:
- call::State callState = call::State::IDLE;
ExternalRequest externalRequest = ExternalRequest::False;
bool conditionalReturnToPreviousView();
A module-apps/application-call/model/CallModel.cpp => module-apps/application-call/model/CallModel.cpp +169 -0
@@ 0,0 1,169 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "CallModel.hpp"
+
+#include <service-cellular/service-cellular/CellularServiceAPI.hpp>
+#include <service-evtmgr/service-evtmgr/EVMessages.hpp>
+#include <service-evtmgr/Constants.hpp>
+
+namespace app::call
+{
+ time_t CallModel::getTime()
+ {
+ return callTime;
+ }
+
+ void CallModel::setTime(time_t newTime)
+ {
+ callTime = newTime;
+
+ if (notifyPresenterOnDurationChange != nullptr) {
+ notifyPresenterOnDurationChange();
+ }
+ }
+
+ CallState CallModel::getState()
+ {
+ return callState;
+ }
+
+ void CallModel::setState(CallState newState)
+ {
+ if (callState == newState) {
+ LOG_INFO("Dropping call state change");
+ }
+
+ if (callState == CallState::Incoming && newState == CallState::Ended && callWasRejected) {
+ callWasRejected = false;
+ callState = CallState::Rejected;
+ }
+ else {
+ callState = newState;
+ }
+
+ if (callState == CallState::Active) {
+ turnOnKeypadBacklight();
+ }
+ else {
+ turnOffKeypadBacklight();
+ }
+
+ if (notifyPresenterOnCallStateChange != nullptr) {
+ notifyPresenterOnCallStateChange();
+ }
+ }
+
+ void CallModel::hangUpCall()
+ {
+ CellularServiceAPI::HangupCall(application);
+ if (callState == CallState::Incoming) {
+ callWasRejected = true;
+ }
+ }
+
+ void CallModel::answerCall()
+ {
+ CellularServiceAPI::AnswerIncomingCall(application);
+ }
+
+ void CallModel::transmitDtmfTone(const uint32_t &digit)
+ {
+ CellularServiceAPI::TransmitDtmfTones(application, digit);
+ }
+
+ utils::PhoneNumber CallModel::getPhoneNumber()
+ {
+ return phoneNumber;
+ }
+
+ void CallModel::muteCall()
+ {
+ CellularServiceAPI::CallAudioMuteEvent(application);
+ }
+
+ void CallModel::unmuteCall()
+ {
+ CellularServiceAPI::CallAudioUnmuteEvent(application);
+ }
+
+ void CallModel::turnLoudspeakerOn()
+ {
+ CellularServiceAPI::CallAudioLoudspeakerOnEvent(application);
+ }
+
+ void CallModel::turnLoudspeakerOff()
+ {
+ CellularServiceAPI::CallAudioLoudspeakerOffEvent(application);
+ }
+
+ void CallModel::turnOnKeypadBacklight()
+ {
+ application->bus.sendUnicast(
+ std::make_shared<sevm::KeypadBacklightMessage>(bsp::keypad_backlight::Action::turnOnCallMode),
+ service::name::evt_manager);
+ }
+
+ void CallModel::turnOffKeypadBacklight()
+ {
+ application->bus.sendUnicast(
+ std::make_shared<sevm::KeypadBacklightMessage>(bsp::keypad_backlight::Action::turnOffCallMode),
+ service::name::evt_manager);
+ }
+
+ void CallModel::clear()
+ {
+ callState = CallState::None;
+ callTime = 0;
+ callerId.clear();
+ callWasRejected = false;
+ phoneNumber = utils::PhoneNumber();
+
+ notifyPresenterOnDurationChange = nullptr;
+ notifyPresenterOnCallStateChange = nullptr;
+ }
+ void CallModel::attachDurationChangeCallback(std::function<void()> callback)
+ {
+ notifyPresenterOnDurationChange = callback;
+ }
+
+ void CallModel::attachCallStateChangeCallback(std::function<void()> callback)
+ {
+ notifyPresenterOnCallStateChange = callback;
+ }
+
+ void CallModel::setPhoneNumber(const utils::PhoneNumber number)
+ {
+ if (phoneNumber.getFormatted() == number.getFormatted()) {
+ LOG_INFO("Dropping number duplication.");
+ return;
+ }
+
+ phoneNumber = number;
+
+ if (notifyPresenterOnCallStateChange != nullptr) {
+ notifyPresenterOnCallStateChange();
+ }
+ }
+
+ UTF8 CallModel::getCallerId()
+ {
+ if (phoneNumber.getFormatted().empty()) {
+ LOG_INFO("No number provided");
+ callerId = UTF8();
+ return callerId;
+ }
+
+ auto contact = DBServiceAPI::MatchContactByPhoneNumber(application, phoneNumber.getView());
+ if (contact && !contact->isTemporary()) {
+ LOG_INFO("number recognized as contact id = %" PRIu32, contact->ID);
+ callerId = contact->getFormattedName();
+ }
+ else {
+ LOG_INFO("number was not recognized as any valid contact");
+ callerId = UTF8(phoneNumber.getFormatted());
+ }
+
+ return callerId;
+ }
+} // namespace app::call
A module-apps/application-call/model/CallModel.hpp => module-apps/application-call/model/CallModel.hpp +113 -0
@@ 0,0 1,113 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <module-utils/time/time/time_conversion.hpp>
+#include <module-utils/phonenumber/PhoneNumber.hpp>
+
+#include <products/PurePhone/apps/include/Application.hpp>
+
+#include <cstdint>
+
+namespace app::call
+{
+ enum class CallState
+ {
+ None,
+ Incoming,
+ Outgoing,
+ Active,
+ Ended,
+ Rejected
+ };
+
+ inline auto c_str(app::call::CallState state) -> const char *
+ {
+ switch (state) {
+ case CallState::None:
+ return "None";
+ case CallState::Incoming:
+ return "Incoming";
+ case CallState::Outgoing:
+ return "Outgoing";
+ case CallState::Active:
+ return "Active";
+ case CallState::Ended:
+ return "Ended";
+ case CallState::Rejected:
+ return "Ended";
+ }
+ return "";
+ }
+
+ class AbstractCallModel
+ {
+ public:
+ virtual ~AbstractCallModel() noexcept = default;
+ virtual time_t getTime() = 0;
+ virtual void setTime(time_t newTime) = 0;
+ virtual CallState getState() = 0;
+ virtual void setState(CallState newState) = 0;
+ virtual void setPhoneNumber(const utils::PhoneNumber number) = 0;
+ virtual utils::PhoneNumber getPhoneNumber() = 0;
+ virtual UTF8 getCallerId() = 0;
+
+ virtual void hangUpCall() = 0;
+ virtual void answerCall() = 0;
+ virtual void transmitDtmfTone(const uint32_t &digit) = 0;
+ virtual void muteCall() = 0;
+ virtual void unmuteCall() = 0;
+ virtual void turnLoudspeakerOn() = 0;
+ virtual void turnLoudspeakerOff() = 0;
+
+ virtual void turnOnKeypadBacklight() = 0;
+ virtual void turnOffKeypadBacklight() = 0;
+
+ virtual void clear() = 0;
+ virtual void attachDurationChangeCallback(std::function<void()> callback) = 0;
+ virtual void attachCallStateChangeCallback(std::function<void()> callback) = 0;
+ };
+
+ class CallModel : public AbstractCallModel
+ {
+ public:
+ CallModel() = delete;
+ explicit CallModel(app::Application *app) : application(app){};
+ time_t getTime() final;
+ void setTime(time_t newTime) final;
+ CallState getState() final;
+ void setState(CallState newState) final;
+ void setPhoneNumber(const utils::PhoneNumber number) final;
+ utils::PhoneNumber getPhoneNumber() final;
+ UTF8 getCallerId() final;
+
+ void hangUpCall() final;
+ void answerCall() final;
+ void transmitDtmfTone(const uint32_t &digit) final;
+ void muteCall();
+ void unmuteCall();
+ void turnLoudspeakerOn();
+ void turnLoudspeakerOff();
+
+ void turnOnKeypadBacklight() final;
+ void turnOffKeypadBacklight() final;
+
+ void clear() final;
+ void attachDurationChangeCallback(std::function<void()> callback) final;
+ void attachCallStateChangeCallback(std::function<void()> callback) final;
+
+ private:
+ Application *application;
+ CallState callState = CallState::None;
+ time_t callTime = 0;
+
+ std::function<void()> notifyPresenterOnDurationChange;
+ std::function<void()> notifyPresenterOnCallStateChange;
+
+ utils::PhoneNumber phoneNumber;
+ UTF8 callerId;
+
+ bool callWasRejected = false;
+ };
+} // namespace app::call
A module-apps/application-call/presenter/CallPresenter.cpp => module-apps/application-call/presenter/CallPresenter.cpp +162 -0
@@ 0,0 1,162 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "CallPresenter.hpp"
+#include <application-call/data/CallAppStyle.hpp>
+
+namespace app::call
+{
+
+ CallWindowContract::Presenter::Presenter(std::shared_ptr<app::call::AbstractCallModel> callModel) : model(callModel)
+ {
+ model->attachCallStateChangeCallback([this]() { this->handleCallStateChange(); });
+ model->attachDurationChangeCallback([this]() { this->handleCallDurationChange(); });
+ }
+
+ CallWindowContract::Presenter::~Presenter() noexcept
+ {
+ model->clear();
+ }
+
+ void CallWindowContract::Presenter::handleCallDurationChange()
+ {
+ if (not isCallInProgress()) {
+ return;
+ }
+ auto view = getView();
+ view->updateDuration(utils::time::Duration(model->getTime()).str());
+ view->refreshWindow();
+ }
+
+ void CallWindowContract::Presenter::handleCallStateChange()
+ {
+ buildLayout();
+ auto view = getView();
+ view->refreshWindow();
+ }
+
+ void CallWindowContract::Presenter::buildLayout()
+ {
+ auto view = getView();
+ auto callState = model->getState();
+
+ switch (callState) {
+ case app::call::CallState::Incoming:
+ view->updateDuration(utils::translate(callAppStyle::strings::iscalling));
+ view->setIncomingCallLayout(model->getCallerId().empty() ? true : false);
+ view->updateNumber(getCallerId());
+ break;
+ case app::call::CallState::Outgoing:
+ view->updateDuration(utils::translate(callAppStyle::strings::calling));
+ view->updateNumber(getCallerId());
+ view->setActiveCallLayout();
+ break;
+ case app::call::CallState::Active:
+ view->updateDuration(utils::time::Duration(model->getTime()).str());
+ view->setActiveCallLayout();
+ break;
+ case app::call::CallState::Rejected:
+ view->updateDuration(utils::translate(callAppStyle::strings::callrejected));
+ view->setCallEndedLayout();
+ break;
+ case app::call::CallState::Ended:
+ view->updateDuration(utils::translate(callAppStyle::strings::callended));
+ view->setCallEndedLayout();
+ break;
+ case app::call::CallState::None:
+ view->clearNavBar();
+ break;
+ }
+ }
+
+ UTF8 CallWindowContract::Presenter::getCallerId()
+ {
+ auto callerId = model->getCallerId();
+ if (not callerId.empty()) {
+ return callerId;
+ }
+ return UTF8(callAppStyle::strings::privateNumber);
+ }
+
+ bool CallWindowContract::Presenter::handleLeftButton()
+ {
+ if (model->getState() == app::call::CallState::Incoming) {
+ model->answerCall();
+ return true;
+ }
+ return false;
+ }
+
+ bool CallWindowContract::Presenter::handleRightButton()
+ {
+ if (isIncomingCall() || isCallInProgress()) {
+ model->hangUpCall();
+ return true;
+ }
+ return false;
+ }
+
+ bool CallWindowContract::Presenter::handleHeadsetOk()
+ {
+ if (isIncomingCall()) {
+ model->answerCall();
+ return true;
+ }
+
+ if (isCallInProgress()) {
+ model->hangUpCall();
+ return true;
+ }
+
+ return false;
+ }
+
+ void CallWindowContract::Presenter::hangUpCall()
+ {
+ model->hangUpCall();
+ }
+
+ bool CallWindowContract::Presenter::handleDigitButton(const uint32_t &digit)
+ {
+ model->transmitDtmfTone(digit);
+ return true;
+ }
+
+ void CallWindowContract::Presenter::muteCall()
+ {
+ model->muteCall();
+ }
+
+ void CallWindowContract::Presenter::unmuteCall()
+ {
+ model->unmuteCall();
+ }
+
+ void CallWindowContract::Presenter::turnLoudspeakerOn()
+ {
+ model->turnLoudspeakerOn();
+ }
+
+ void CallWindowContract::Presenter::turnLoudspeakerOff()
+ {
+ model->turnLoudspeakerOff();
+ }
+
+ bool CallWindowContract::Presenter::isIncomingCall()
+ {
+ return model->getState() == CallState::Incoming;
+ }
+
+ bool CallWindowContract::Presenter::isCallInProgress()
+ {
+ if (auto state = model->getState(); state == CallState::Outgoing || state == CallState::Active) {
+ return true;
+ }
+ return false;
+ }
+
+ utils::PhoneNumber CallWindowContract::Presenter::getPhoneNumber()
+ {
+ return model->getPhoneNumber();
+ }
+} // namespace app::call
A module-apps/application-call/presenter/CallPresenter.hpp => module-apps/application-call/presenter/CallPresenter.hpp +60 -0
@@ 0,0 1,60 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <module-apps/application-call/model/CallModel.hpp>
+#include <module-apps/application-call/widgets/StateIcons.hpp>
+#include "BasePresenter.hpp"
+
+namespace app::call
+{
+ class CallWindowContract
+ {
+ public:
+ class View
+ {
+ public:
+ virtual void updateDuration(const UTF8 &text, bool isVisible = true) = 0;
+ virtual void refreshWindow() = 0;
+ virtual void setNavBarForActiveCall() = 0;
+ virtual void setNavBarForIncomingCall() = 0;
+ virtual void clearNavBar() = 0;
+ virtual void setIncomingCallLayout(bool isValidCallerId) = 0;
+ virtual void setActiveCallLayout() = 0;
+ virtual void setCallEndedLayout() = 0;
+ virtual void updateNumber(const UTF8 &text) = 0;
+
+ virtual ~View() noexcept = default;
+ };
+
+ class Presenter : public BasePresenter<CallWindowContract::View>
+ {
+ public:
+ Presenter(std::shared_ptr<app::call::AbstractCallModel> callModel);
+ ~Presenter() noexcept override;
+
+ void handleCallDurationChange();
+ void handleCallStateChange();
+ void buildLayout();
+
+ bool handleLeftButton();
+ bool handleRightButton();
+ bool handleHeadsetOk();
+ bool handleDigitButton(const uint32_t &digit);
+ void muteCall();
+ void unmuteCall();
+ void turnLoudspeakerOn();
+ void turnLoudspeakerOff();
+
+ void hangUpCall();
+ utils::PhoneNumber getPhoneNumber();
+
+ private:
+ std::shared_ptr<app::call::AbstractCallModel> model;
+ UTF8 getCallerId();
+ bool isIncomingCall();
+ bool isCallInProgress();
+ };
+ };
+} // namespace app::call
A module-apps/application-call/test/CMakeLists.txt => module-apps/application-call/test/CMakeLists.txt +14 -0
@@ 0,0 1,14 @@
+add_catch2_executable(
+ NAME
+ call-presenter
+ SRCS
+ unittest_call_presenter.cpp
+ LIBS
+ application-call
+ i18n
+ log
+ utils-time
+ module-utils
+ app
+ apps-common
+)
A module-apps/application-call/test/mock/CallPresenterMocks.hpp => module-apps/application-call/test/mock/CallPresenterMocks.hpp +126 -0
@@ 0,0 1,126 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <module-apps/application-call/model/CallModel.hpp>
+#include <module-apps/application-call/presenter/CallPresenter.hpp>
+
+namespace app::call
+{
+
+ class ViewMock : public CallWindowContract::View
+ {
+ public:
+ void updateDuration(const UTF8 &text, bool isVisible = true) override
+ {
+ duration = text;
+ };
+ void refreshWindow() override
+ {
+ windowRefreshed = true;
+ };
+ void setNavBarForActiveCall() override{};
+ void setNavBarForIncomingCall() override{};
+ void clearNavBar() override{};
+ void setIncomingCallLayout(bool isValidCallerId) override
+ {
+ layoutShowed = LayoutShowed::Incoming;
+ };
+ void setActiveCallLayout() override
+ {
+ layoutShowed = LayoutShowed::Active;
+ };
+ ;
+ void setCallEndedLayout() override
+ {
+ layoutShowed = LayoutShowed::Ended;
+ };
+ ;
+ void updateNumber(const UTF8 &text) override
+ {
+ number = text;
+ };
+
+ bool windowRefreshed = false;
+
+ enum class LayoutShowed
+ {
+ Incoming,
+ Active,
+ Ended,
+ None
+ };
+
+ LayoutShowed layoutShowed = LayoutShowed::None;
+ UTF8 number;
+ UTF8 duration;
+ };
+
+ class ModelMock : public AbstractCallModel
+ {
+ public:
+ time_t getTime() override
+ {
+ return callTime;
+ };
+ void setTime(time_t newTime) override
+ {
+ callTime = newTime;
+ };
+ CallState getState() override
+ {
+ return callState;
+ };
+ void setState(CallState newState) override
+ {
+ callState = newState;
+ };
+ void setPhoneNumber(const utils::PhoneNumber number) override{};
+ utils::PhoneNumber getPhoneNumber() override
+ {
+ return phoneNumber;
+ };
+ UTF8 getCallerId() override
+ {
+ return callerId;
+ };
+
+ void hangUpCall() override
+ {
+ hangupCallCalled = true;
+ };
+ void answerCall() override
+ {
+ answerCallCalled = true;
+ };
+ void transmitDtmfTone(const uint32_t &digit) override{};
+ void muteCall() override{};
+ void unmuteCall() override{};
+ void turnLoudspeakerOn() override{};
+ void turnLoudspeakerOff() override{};
+
+ void turnOnKeypadBacklight() override{};
+ void turnOffKeypadBacklight() override{};
+
+ void clear() override{};
+ void attachDurationChangeCallback(std::function<void()> callback) override{};
+ void attachCallStateChangeCallback(std::function<void()> callback) override{};
+
+ bool hangupCallCalled = false;
+ bool answerCallCalled = false;
+
+ private:
+ Application *application;
+ CallState callState = CallState::None;
+ time_t callTime = 0;
+
+ std::function<void()> notifyPresenterOnDurationChange;
+ std::function<void()> notifyPresenterOnCallStateChange;
+
+ utils::PhoneNumber phoneNumber;
+ UTF8 callerId;
+
+ bool callWasRejected = false;
+ };
+} // namespace app::call
A module-apps/application-call/test/unittest_call_presenter.cpp => module-apps/application-call/test/unittest_call_presenter.cpp +245 -0
@@ 0,0 1,245 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include <catch2/catch.hpp>
+#include <module-apps/application-call/presenter/CallPresenter.hpp>
+#include <module-apps/application-call/test/mock/CallPresenterMocks.hpp>
+#include <module-apps/application-call/data/CallAppStyle.hpp>
+
+using app::call::ViewMock;
+
+TEST_CASE("Call presenter")
+{
+ SECTION("Show incoming call layout")
+ {
+ auto model = std::make_shared<app::call::ModelMock>();
+ auto presenter = app::call::CallWindowContract::Presenter(model);
+ auto view = app::call::ViewMock();
+ presenter.attach(&view);
+
+ model->setState(app::call::CallState::Incoming);
+ presenter.handleCallStateChange();
+
+ REQUIRE(view.windowRefreshed == true);
+ REQUIRE(view.layoutShowed == ViewMock::LayoutShowed::Incoming);
+ REQUIRE(view.duration == callAppStyle::strings::iscalling);
+ }
+
+ SECTION("Show call active layout")
+ {
+ auto model = std::make_shared<app::call::ModelMock>();
+ auto presenter = app::call::CallWindowContract::Presenter(model);
+ auto view = app::call::ViewMock();
+ presenter.attach(&view);
+
+ model->setState(app::call::CallState::Active);
+ presenter.handleCallStateChange();
+
+ REQUIRE(view.windowRefreshed == true);
+ REQUIRE(view.layoutShowed == ViewMock::LayoutShowed::Active);
+ }
+
+ SECTION("Show call outgoing layout")
+ {
+ auto model = std::make_shared<app::call::ModelMock>();
+ auto presenter = app::call::CallWindowContract::Presenter(model);
+ auto view = app::call::ViewMock();
+ presenter.attach(&view);
+
+ model->setState(app::call::CallState::Outgoing);
+ presenter.handleCallStateChange();
+
+ REQUIRE(view.windowRefreshed == true);
+ REQUIRE(view.layoutShowed == ViewMock::LayoutShowed::Active);
+ REQUIRE(view.duration == callAppStyle::strings::calling);
+ }
+
+ SECTION("Show call ended layout")
+ {
+ auto model = std::make_shared<app::call::ModelMock>();
+ auto presenter = app::call::CallWindowContract::Presenter(model);
+ auto view = app::call::ViewMock();
+ presenter.attach(&view);
+
+ model->setState(app::call::CallState::Ended);
+ presenter.handleCallStateChange();
+
+ REQUIRE(view.windowRefreshed == true);
+ REQUIRE(view.duration == callAppStyle::strings::callended);
+ }
+
+ SECTION("Show call rejected layout")
+ {
+ auto model = std::make_shared<app::call::ModelMock>();
+ auto presenter = app::call::CallWindowContract::Presenter(model);
+ auto view = app::call::ViewMock();
+ presenter.attach(&view);
+
+ model->setState(app::call::CallState::Rejected);
+ presenter.handleCallStateChange();
+
+ REQUIRE(view.windowRefreshed == true);
+ REQUIRE(view.layoutShowed == ViewMock::LayoutShowed::Ended);
+ REQUIRE(view.duration == callAppStyle::strings::callrejected);
+ }
+
+ SECTION("Handle left button for incoming call")
+ {
+ auto model = std::make_shared<app::call::ModelMock>();
+ auto presenter = app::call::CallWindowContract::Presenter(model);
+
+ model->setState(app::call::CallState::Incoming);
+ auto result = presenter.handleLeftButton();
+
+ REQUIRE(result == true);
+ REQUIRE(model->answerCallCalled == true);
+ }
+
+ SECTION("Handle left button for not incoming call")
+ {
+ auto model = std::make_shared<app::call::ModelMock>();
+ auto presenter = app::call::CallWindowContract::Presenter(model);
+
+ model->setState(app::call::CallState::None);
+ auto result = presenter.handleLeftButton();
+
+ REQUIRE(result == false);
+ REQUIRE(model->answerCallCalled == false);
+
+ model->answerCallCalled = false;
+ model->setState(app::call::CallState::Outgoing);
+ result = presenter.handleLeftButton();
+
+ REQUIRE(result == false);
+
+ model->answerCallCalled = false;
+ model->setState(app::call::CallState::Active);
+ result = presenter.handleLeftButton();
+
+ REQUIRE(result == false);
+ REQUIRE(model->answerCallCalled == false);
+
+ model->answerCallCalled = false;
+ model->setState(app::call::CallState::Ended);
+ result = presenter.handleLeftButton();
+
+ REQUIRE(result == false);
+ REQUIRE(model->answerCallCalled == false);
+
+ model->answerCallCalled = false;
+ model->setState(app::call::CallState::Rejected);
+ result = presenter.handleLeftButton();
+
+ REQUIRE(result == false);
+ REQUIRE(model->answerCallCalled == false);
+ }
+
+ SECTION("Handle right button for incoming call")
+ {
+ auto model = std::make_shared<app::call::ModelMock>();
+ auto presenter = app::call::CallWindowContract::Presenter(model);
+
+ model->setState(app::call::CallState::Incoming);
+ auto result = presenter.handleRightButton();
+
+ REQUIRE(result == true);
+ REQUIRE(model->hangupCallCalled == true);
+ }
+
+ SECTION("Handle right button for active call")
+ {
+ auto model = std::make_shared<app::call::ModelMock>();
+ auto presenter = app::call::CallWindowContract::Presenter(model);
+
+ model->setState(app::call::CallState::Active);
+ auto result = presenter.handleRightButton();
+
+ REQUIRE(result == true);
+ REQUIRE(model->hangupCallCalled == true);
+
+ model->hangupCallCalled = false;
+ model->setState(app::call::CallState::Outgoing);
+ result = presenter.handleRightButton();
+
+ REQUIRE(result == true);
+ REQUIRE(model->hangupCallCalled == true);
+ }
+
+ SECTION("Handle right button for not active call")
+ {
+ auto model = std::make_shared<app::call::ModelMock>();
+ auto presenter = app::call::CallWindowContract::Presenter(model);
+
+ model->setState(app::call::CallState::None);
+ auto result = presenter.handleRightButton();
+
+ REQUIRE(result == false);
+ REQUIRE(model->hangupCallCalled == false);
+
+ model->hangupCallCalled = false;
+ model->setState(app::call::CallState::Ended);
+ result = presenter.handleRightButton();
+
+ REQUIRE(result == false);
+ REQUIRE(model->hangupCallCalled == false);
+
+ model->hangupCallCalled = false;
+ model->setState(app::call::CallState::Rejected);
+ result = presenter.handleRightButton();
+
+ REQUIRE(result == false);
+ REQUIRE(model->hangupCallCalled == false);
+ }
+
+ SECTION("Handle headset ok for incoming call")
+ {
+ auto model = std::make_shared<app::call::ModelMock>();
+ auto presenter = app::call::CallWindowContract::Presenter(model);
+
+ model->setState(app::call::CallState::Incoming);
+ auto result = presenter.handleHeadsetOk();
+
+ REQUIRE(result == true);
+ REQUIRE(model->answerCallCalled == true);
+ }
+
+ SECTION("Handle headset ok active call")
+ {
+ auto model = std::make_shared<app::call::ModelMock>();
+ auto presenter = app::call::CallWindowContract::Presenter(model);
+
+ model->setState(app::call::CallState::Outgoing);
+ auto result = presenter.handleHeadsetOk();
+
+ REQUIRE(result == true);
+ REQUIRE(model->hangupCallCalled == true);
+
+ model->hangupCallCalled = false;
+ model->setState(app::call::CallState::Active);
+ result = presenter.handleHeadsetOk();
+
+ REQUIRE(result == true);
+ REQUIRE(model->hangupCallCalled == true);
+ }
+
+ SECTION("Handle headset ok for not active call")
+ {
+ auto model = std::make_shared<app::call::ModelMock>();
+ auto presenter = app::call::CallWindowContract::Presenter(model);
+
+ model->setState(app::call::CallState::None);
+ auto result = presenter.handleHeadsetOk();
+
+ REQUIRE(result == false);
+ REQUIRE(model->hangupCallCalled == false);
+ REQUIRE(model->answerCallCalled == false);
+
+ model->hangupCallCalled = false;
+ model->setState(app::call::CallState::Ended);
+ result = presenter.handleHeadsetOk();
+
+ REQUIRE(result == false);
+ REQUIRE(model->hangupCallCalled == false);
+ REQUIRE(model->answerCallCalled == false);
+ }
+}
M module-apps/application-call/windows/CallWindow.cpp => module-apps/application-call/windows/CallWindow.cpp +93 -242
@@ 3,7 3,6 @@
#include "ApplicationCall.hpp"
#include "CallAppStyle.hpp"
-#include "CallState.hpp"
#include "CallSwitchData.hpp"
#include "CallWindow.hpp"
#include "StateIcons.hpp"
@@ 32,13 31,12 @@ namespace gui
using namespace callAppStyle;
using namespace callAppStyle::callWindow;
using namespace app::call;
- using AudioEvent = app::CallWindowInterface::AudioEvent;
- CallWindow::CallWindow(app::ApplicationCommon *app, app::CallWindowInterface *interface, std::string windowName)
- : AppWindow(app, windowName), interface(interface)
+ CallWindow::CallWindow(app::ApplicationCommon *app,
+ std::unique_ptr<app::call::CallWindowContract::Presenter> &&windowPresenter)
+ : gui::AppWindow{app, app::window::name_call}, presenter{std::move(windowPresenter)}
{
- assert(interface != nullptr);
- assert(app != nullptr);
+ presenter->attach(this);
buildInterface();
}
@@ 86,8 84,7 @@ namespace gui
microphoneIcon->setNext();
LOG_INFO("Mic activated %d", static_cast<int>(microphoneIcon->get()));
- microphoneIcon->get() == MicrophoneIconState::MUTED ? interface->sendAudioEvent(AudioEvent::Mute)
- : interface->sendAudioEvent(AudioEvent::Unmute);
+ microphoneIcon->get() == MicrophoneIconState::MUTED ? presenter->muteCall() : presenter->unmuteCall();
return true;
};
@@ 99,10 96,10 @@ namespace gui
switch (speakerIcon->get()) {
case SpeakerIconState::SPEAKER: {
- interface->sendAudioEvent(AudioEvent::LoudspeakerOff);
+ presenter->turnLoudspeakerOff();
} break;
case SpeakerIconState::SPEAKERON: {
- interface->sendAudioEvent(AudioEvent::LoudspeakerOn);
+ presenter->turnLoudspeakerOn();
} break;
// case SpeakerIconState::BLUETOOTH: {
// // TODO: need implementation
@@ 118,7 115,7 @@ namespace gui
sendSmsIcon->activatedCallback = [=](gui::Item &item) {
LOG_INFO("Send message template and reject the call");
constexpr auto preventAutoLock = true;
- auto msg = std::make_unique<SMSSendTemplateRequest>(phoneNumber, preventAutoLock);
+ auto msg = std::make_unique<SMSSendTemplateRequest>(presenter->getPhoneNumber().getView(), preventAutoLock);
msg->ignoreCurrentWindowOnStack = true;
return app::manager::Controller::sendAction(application,
app::manager::actions::ShowSmsTemplates,
@@ 132,8 129,6 @@ namespace gui
speakerIcon->setNavigationItem(NavigationDirection::LEFT, microphoneIcon);
speakerIcon->setNavigationItem(NavigationDirection::RIGHT, microphoneIcon);
-
- setState(State::IDLE);
}
void CallWindow::destroyInterface()
@@ 147,217 142,24 @@ namespace gui
return appConfiguration;
}
- void CallWindow::setState(State state)
- {
- auto prevState = getState();
- LOG_INFO("==> Call state change: %s -> %s", c_str(prevState), c_str(state));
- interface->setCallState(state);
-
- switch (state) {
- case State::INCOMING_CALL: {
- navBar->setText(gui::nav_bar::Side::Left, utils::translate(strings::answer), true);
- navBar->setText(gui::nav_bar::Side::Right, utils::translate(strings::reject), true);
- durationLabel->setText(utils::translate(strings::iscalling));
- durationLabel->setVisible(true);
- speakerIcon->setVisible(false);
- microphoneIcon->setVisible(false);
- if (phoneNumber.getFormatted().empty()) {
- navBar->setActive(gui::nav_bar::Side::Center, false);
- sendSmsIcon->setVisible(false);
- setFocusItem(nullptr);
- }
- else {
- navBar->setActive(gui::nav_bar::Side::Center, true);
- navBar->setText(gui::nav_bar::Side::Center, utils::translate(style::strings::common::send), true);
- sendSmsIcon->setVisible(true);
- setFocusItem(sendSmsIcon);
- }
- } break;
- case State::CALL_ENDED: {
- stopCallTimer();
- navBar->setActive(gui::nav_bar::Side::Left, false);
- navBar->setActive(gui::nav_bar::Side::Center, false);
- navBar->setActive(gui::nav_bar::Side::Right, false);
- durationLabel->setVisible(true);
- setCallEndMessage();
- sendSmsIcon->setVisible(false);
- speakerIcon->setVisible(false);
- microphoneIcon->setVisible(false);
- speakerIcon->set(SpeakerIconState::SPEAKER);
- microphoneIcon->set(MicrophoneIconState::MUTE);
- setFocusItem(nullptr);
- connectTimerOnExit();
- } break;
- case State::CALL_IN_PROGRESS: {
- runCallTimer();
- navBar->setActive(gui::nav_bar::Side::Left, false);
- navBar->setActive(gui::nav_bar::Side::Center, false);
- navBar->setText(gui::nav_bar::Side::Right, utils::translate(strings::endcall), true);
- navBar->setText(gui::nav_bar::Side::Center, utils::translate(style::strings::common::Switch), true);
- durationLabel->setVisible(true);
- sendSmsIcon->setVisible(false);
- speakerIcon->setVisible(true);
- microphoneIcon->setVisible(true);
- setFocusItem(microphoneIcon);
- } break;
- case State::OUTGOING_CALL: {
- navBar->setActive(gui::nav_bar::Side::Left, false);
- navBar->setActive(gui::nav_bar::Side::Center, false);
- navBar->setText(gui::nav_bar::Side::Right, utils::translate(strings::endcall), true);
- navBar->setText(gui::nav_bar::Side::Center, utils::translate(style::strings::common::Switch), true);
- durationLabel->setText(utils::translate(strings::calling));
- durationLabel->setVisible(true);
- sendSmsIcon->setVisible(false);
- speakerIcon->setVisible(true);
- microphoneIcon->setVisible(true);
- setFocusItem(microphoneIcon);
- } break;
- case State::IDLE:
- stopCallTimer();
- [[fallthrough]];
- default:
- numberLabel->clear();
- navBar->setActive(gui::nav_bar::Side::Left, false);
- navBar->setActive(gui::nav_bar::Side::Center, false);
- navBar->setActive(gui::nav_bar::Side::Right, false);
- durationLabel->setVisible(false);
- sendSmsIcon->setVisible(false);
- speakerIcon->setVisible(false);
- microphoneIcon->setVisible(false);
- setFocusItem(nullptr);
- break;
- };
- iconsBox->resizeItems();
- }
-
- auto CallWindow::getState() const noexcept -> State
- {
- return interface->getCallState();
- }
-
- void CallWindow::updateDuration(const utils::time::Duration duration)
+ void CallWindow::updateDuration(const UTF8 &text, bool isVisible)
{
if (durationLabel != nullptr) {
- durationLabel->setText(duration.str());
+ durationLabel->setText(text);
+ durationLabel->setVisible(isVisible);
}
}
void CallWindow::onBeforeShow(ShowMode mode, SwitchData *data)
{
- if (auto callData = dynamic_cast<app::CallSwitchData *>(data); callData != nullptr) {
- phoneNumber = callData->getPhoneNumber();
- if (!callData->getPhoneNumber().getFormatted().empty()) {
- auto contact = DBServiceAPI::MatchContactByPhoneNumber(this->application, phoneNumber);
- auto displayName = phoneNumber.getFormatted();
- if (contact && !contact->isTemporary()) {
- LOG_INFO("number recognized as contact id = %" PRIu32, contact->ID);
- displayName = contact->getFormattedName();
- }
- else {
- LOG_INFO("number was not recognized as any valid contact");
- }
-
- numberLabel->setText(displayName);
- }
- else {
- numberLabel->setText(utils::translate(strings::privateNumber));
- }
-
- if (dynamic_cast<app::IncomingCallData *>(data) != nullptr) {
- if (getState() == State::INCOMING_CALL) {
- LOG_DEBUG("ignoring IncomingCallData message");
- return;
- }
- callEndType = CallEndType::None;
- setState(State::INCOMING_CALL);
- return;
- }
- if (dynamic_cast<app::ExecuteCallData *>(data) != nullptr) {
- callEndType = CallEndType::None;
- setState(State::OUTGOING_CALL);
- return;
- }
- }
-
- if (dynamic_cast<app::CallAbortData *>(data) != nullptr) {
- if (callEndType == CallEndType::None) {
- callEndType = CallEndType::Ended;
- }
- setState(State::CALL_ENDED);
- return;
- }
-
- if (dynamic_cast<app::CallActiveData *>(data) != nullptr) {
- if (getState() != State::CALL_ENDED) {
- setState(State::CALL_IN_PROGRESS);
- return;
- }
- LOG_DEBUG("Ignoring CallActiveData message");
- return;
- }
+ presenter->buildLayout();
if (dynamic_cast<SMSTemplateSent *>(data) != nullptr) {
- callEndType = CallEndType::Rejected;
- interface->hangupCall();
+ presenter->hangUpCall();
return;
}
}
- bool CallWindow::handleLeftButton()
- {
- if (getState() == State::INCOMING_CALL) {
- interface->answerIncomingCall();
- return true;
- }
-
- return false;
- }
-
- bool CallWindow::handleRightButton()
- {
- switch (getState()) {
- case State::INCOMING_CALL:
- callEndType = CallEndType::Rejected;
- interface->hangupCall();
- return true;
- case State::OUTGOING_CALL:
- case State::CALL_IN_PROGRESS:
- callEndType = CallEndType::Ended;
- interface->hangupCall();
- return true;
- case State::IDLE:
- case State::CALL_ENDED:
- break;
- }
-
- return false;
- }
-
- bool CallWindow::handleHeadsetOkButton()
- {
- switch (getState()) {
- case State::INCOMING_CALL:
- interface->answerIncomingCall();
- return true;
- case State::OUTGOING_CALL:
- [[fallthrough]];
- case State::CALL_IN_PROGRESS:
- interface->hangupCall();
- return true;
- case State::IDLE:
- case State::CALL_ENDED:
- break;
- }
-
- return false;
- }
-
- bool CallWindow::handleDigit(const uint32_t digit)
- {
- interface->transmitDtmfTone(digit);
- return true;
- }
-
bool CallWindow::onInput(const InputEvent &inputEvent)
{
bool handled = false;
@@ 369,19 171,19 @@ namespace gui
auto code = translator.handle(inputEvent.getRawKey(), InputMode({InputMode::phone}).get());
switch (inputEvent.getKeyCode()) {
case KeyCode::KEY_LF:
- handled = handleLeftButton();
+ handled = presenter->handleLeftButton();
break;
case KeyCode::KEY_RF:
- handled = handleRightButton();
+ handled = presenter->handleRightButton();
break;
case KeyCode::HEADSET_OK:
- handled = handleHeadsetOkButton();
+ handled = presenter->handleHeadsetOk();
break;
default:
break;
}
if (!handled && code != 0) {
- handled = handleDigit(code);
+ handled = presenter->handleDigitButton(code);
}
}
@@ 398,7 200,6 @@ namespace gui
{
timerCallback = [this](Item &, sys::Timer &timer) {
LOG_DEBUG("Delayed exit timer callback");
- setState(State::IDLE);
app::manager::Controller::switchBack(application);
application->popCurrentWindow();
return true;
@@ 408,39 209,89 @@ namespace gui
delayedExitTimer.start();
}
- void CallWindow::runCallTimer()
+ void CallWindow::refreshWindow()
{
- constexpr auto callTimeTimeout = std::chrono::seconds{1};
- callTimer = app::GuiTimerFactory::createPeriodicTimer(application, durationLabel, "CallTime", callTimeTimeout);
- durationLabel->timerCallback = [&](Item &item, sys::Timer &timer) {
- std::chrono::time_point<std::chrono::system_clock> systemUnitDuration(callDuration);
- updateDuration(std::chrono::system_clock::to_time_t(systemUnitDuration));
- callDuration++;
- LOG_DEBUG("Update duration timer callback - %" PRIu32, static_cast<uint32_t>(callDuration.count()));
- application->refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
- return true;
- };
- callTimer.start();
+ application->refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
}
- void CallWindow::stopCallTimer()
+ void CallWindow::setNavBarForActiveCall()
{
- callDuration = std::chrono::seconds().zero();
- if (callTimer.isActive()) {
- callTimer.stop();
- }
+ navBar->setActive(gui::nav_bar::Side::Left, false);
+ navBar->setActive(gui::nav_bar::Side::Center, false);
+ navBar->setText(gui::nav_bar::Side::Right, utils::translate(strings::endcall), true);
+ navBar->setText(gui::nav_bar::Side::Center, utils::translate(style::strings::common::Switch), true);
+ }
+
+ void CallWindow::setNavBarForIncomingCall()
+ {
+ navBar->setText(gui::nav_bar::Side::Left, utils::translate(strings::answer), true);
+ navBar->setText(gui::nav_bar::Side::Right, utils::translate(strings::reject), true);
}
- void CallWindow::setCallEndMessage()
+ void CallWindow::clearNavBar()
{
- switch (callEndType) {
- case CallEndType::None:
- case CallEndType::Ended:
- durationLabel->setText(utils::translate(strings::callended));
- break;
- case CallEndType::Rejected:
- durationLabel->setText(utils::translate(strings::callrejected));
- break;
+ navBar->setActive(gui::nav_bar::Side::Left, false);
+ navBar->setActive(gui::nav_bar::Side::Center, false);
+ navBar->setActive(gui::nav_bar::Side::Right, false);
+ }
+ void CallWindow::setIncomingCallLayout(bool isValidCallerId)
+ {
+ navBar->setText(gui::nav_bar::Side::Left, utils::translate(strings::answer), true);
+ navBar->setText(gui::nav_bar::Side::Right, utils::translate(strings::reject), true);
+
+ microphoneIcon->setVisible(false);
+ speakerIcon->setVisible(false);
+
+ if (isValidCallerId) {
+ navBar->setActive(gui::nav_bar::Side::Center, false);
+ sendSmsIcon->setVisible(false);
+ setFocusItem(nullptr);
+ }
+ else {
+ navBar->setActive(gui::nav_bar::Side::Center, true);
+ navBar->setText(gui::nav_bar::Side::Center, utils::translate(style::strings::common::send), true);
+ sendSmsIcon->setVisible(true);
+ setFocusItem(sendSmsIcon);
}
+
+ iconsBox->resizeItems();
+ }
+
+ void CallWindow::setActiveCallLayout()
+ {
+ navBar->setActive(gui::nav_bar::Side::Left, false);
+ navBar->setActive(gui::nav_bar::Side::Center, false);
+ navBar->setText(gui::nav_bar::Side::Right, utils::translate(strings::endcall), true);
+ navBar->setText(gui::nav_bar::Side::Center, utils::translate(style::strings::common::Switch), true);
+
+ sendSmsIcon->setVisible(false);
+ microphoneIcon->setVisible(true);
+ speakerIcon->setVisible(true);
+
+ iconsBox->resizeItems();
+
+ setFocusItem(microphoneIcon);
}
+
+ void CallWindow::setCallEndedLayout()
+ {
+ navBar->setActive(gui::nav_bar::Side::Left, false);
+ navBar->setActive(gui::nav_bar::Side::Center, false);
+ navBar->setActive(gui::nav_bar::Side::Right, false);
+
+ sendSmsIcon->setVisible(false);
+ microphoneIcon->setVisible(false);
+ speakerIcon->setVisible(false);
+
+ iconsBox->resizeItems();
+
+ setFocusItem(nullptr);
+ connectTimerOnExit();
+ }
+
+ void CallWindow::updateNumber(const UTF8 &text)
+ {
+ numberLabel->setText(text);
+ }
+
} /* namespace gui */
M module-apps/application-call/windows/CallWindow.hpp => module-apps/application-call/windows/CallWindow.hpp +15 -25
@@ 4,8 4,8 @@
#pragma once
#include "ApplicationCall.hpp"
-#include "CallState.hpp"
#include "StateIcons.hpp"
+#include <application-call/presenter/CallPresenter.hpp>
#include <AppWindow.hpp>
#include <gui/input/Translator.hpp>
@@ 15,30 15,22 @@
namespace gui
{
- class CallWindow : public AppWindow
+ class CallWindow : public AppWindow, public app::call::CallWindowContract::View
{
private:
- enum class CallEndType
- {
- None,
- Ended,
- Rejected,
- } callEndType = CallEndType::None;
gui::KeyInputMappedTranslation translator;
- sys::TimerHandle callTimer;
sys::TimerHandle delayedExitTimer;
- std::chrono::seconds callDuration = std::chrono::seconds().zero();
static constexpr inline auto callDelayedStopTime = std::chrono::milliseconds{3000};
[[nodiscard]] auto getDelayedStopTime() const
{
return callDelayedStopTime;
}
- void setCallEndMessage();
+
+ std::unique_ptr<app::call::CallWindowContract::Presenter> presenter;
protected:
- app::CallWindowInterface *interface = nullptr;
// used to display both number and name of contact
gui::Label *numberLabel = nullptr;
// used to inform user about call state of call and display duration of call
@@ 53,18 45,9 @@ namespace gui
utils::PhoneNumber::View phoneNumber;
- bool handleLeftButton();
- bool handleRightButton();
- bool handleHeadsetOkButton();
- void setState(app::call::State state);
- [[nodiscard]] auto getState() const noexcept -> app::call::State;
-
public:
CallWindow(app::ApplicationCommon *app,
- app::CallWindowInterface *interface,
- std::string windowName = app::window::name_call);
-
- void updateDuration(const utils::time::Duration duration);
+ std::unique_ptr<app::call::CallWindowContract::Presenter> &&windowPresenter);
bool onInput(const InputEvent &inputEvent) override;
void onBeforeShow(ShowMode mode, SwitchData *data) override;
@@ 74,10 57,17 @@ namespace gui
void destroyInterface() override;
status_bar::Configuration configureStatusBar(status_bar::Configuration appConfiguration) override;
- bool handleDigit(const uint32_t digit);
void connectTimerOnExit();
- void runCallTimer();
- void stopCallTimer();
+ void refreshWindow() override;
+
+ void updateDuration(const UTF8 &text, bool isVisible = true) override;
+ void setNavBarForActiveCall() override;
+ void setNavBarForIncomingCall() override;
+ void clearNavBar() override;
+ void setIncomingCallLayout(bool isValidCallerId) override;
+ void setActiveCallLayout() override;
+ void setCallEndedLayout() override;
+ void updateNumber(const UTF8 &text) override;
};
} /* namespace gui */
M module-services/service-cellular/call/CallGUI.cpp => module-services/service-cellular/call/CallGUI.cpp +3 -2
@@ 21,9 21,9 @@ void CallGUI::notifyCLIP(const utils::PhoneNumber::View &number)
&owner, app::manager::actions::HandleCallerId, std::make_unique<app::manager::actions::CallParams>(number));
}
-void CallGUI::notifyCallStarted()
+void CallGUI::notifyCallStarted(utils::PhoneNumber phoneNumber)
{
- owner.bus.sendMulticast(std::make_shared<cellular::CallStartedNotification>(),
+ owner.bus.sendMulticast(std::make_shared<cellular::CallStartedNotification>(phoneNumber),
sys::BusChannel::ServiceCellularNotifications);
}
@@ 38,6 38,7 @@ void CallGUI::notifyCallActive()
owner.bus.sendMulticast(std::make_shared<cellular::CallActiveNotification>(),
sys::BusChannel::ServiceCellularNotifications);
}
+
void CallGUI::notifyCallDurationUpdate(const time_t &duration)
{
owner.bus.sendMulticast(std::make_shared<cellular::CallDurationNotification>(duration),
M module-services/service-cellular/call/CallGUI.hpp => module-services/service-cellular/call/CallGUI.hpp +1 -1
@@ 20,7 20,7 @@ class CallGUI
void notifyRING();
void notifyCLIP(const utils::PhoneNumber::View &number);
- void notifyCallStarted();
+ void notifyCallStarted(utils::PhoneNumber phoneNumber);
void notifyCallEnded();
void notifyCallActive();
void notifyCallDurationUpdate(const time_t &duration);
M module-services/service-cellular/call/CellularCall.cpp => module-services/service-cellular/call/CellularCall.cpp +4 -1
@@ 109,7 109,7 @@ namespace CellularCall
return false;
}
- gui.notifyCallStarted();
+ gui.notifyCallStarted(number);
LOG_INFO("call started");
return true;
}
@@ 209,6 209,9 @@ namespace CellularCall
}
void Call::handleCallDurationTimer()
{
+ if (not isActiveCall) {
+ return;
+ }
auto now = utils::time::getCurrentTimestamp();
callDurationTime = (now - startActiveTime).getSeconds();
gui.notifyCallDurationUpdate(callDurationTime);
M module-services/service-cellular/service-cellular/CellularMessage.hpp => module-services/service-cellular/service-cellular/CellularMessage.hpp +11 -8
@@ 602,17 602,11 @@ class CellularAnswerIncomingCallMessage : public CellularMessage
{}
};
-class CellularHangupCallMessage : public CellularMessage, public app::manager::actions::ConvertibleToAction
+class CellularHangupCallMessage : public CellularMessage
{
public:
CellularHangupCallMessage() : CellularMessage(Type::HangupCall)
{}
-
- [[nodiscard]] auto toAction() const -> std::unique_ptr<app::manager::ActionRequest>
- {
- return std::make_unique<app::manager::ActionRequest>(
- sender, app::manager::actions::AbortCall, std::make_unique<app::manager::actions::ActionParams>());
- }
};
class CellularDismissCallMessage : public CellularMessage
@@ 1048,7 1042,15 @@ namespace cellular
class CallStartedNotification : public sys::DataMessage
{
public:
- explicit CallStartedNotification() : sys::DataMessage(MessageType::MessageTypeUninitialized){};
+ explicit CallStartedNotification(utils::PhoneNumber phoneNumber)
+ : sys::DataMessage(MessageType::MessageTypeUninitialized), phoneNumber(phoneNumber){};
+ utils::PhoneNumber getNumber()
+ {
+ return phoneNumber;
+ };
+
+ private:
+ utils::PhoneNumber phoneNumber;
};
class CallEndedNotification : public sys::DataMessage
@@ 1070,4 1072,5 @@ namespace cellular
: sys::DataMessage(MessageType::MessageTypeUninitialized), callDuration(duration){};
time_t callDuration;
};
+
} // namespace cellular
M module-utils/time/time/time_conversion.hpp => module-utils/time/time/time_conversion.hpp +1 -1
@@ 6,7 6,7 @@
/// this calss uses strftime to convert timestamp to text, but for UTF8 elements (mon,day) it uses our locale
/// as stdlib builtin locale doesn't provide way to substitute C-LOCALE
-#include "time/time_locale.hpp"
+#include <time/time_locale.hpp>
#include <log/log.hpp>
#include <vector>