~aleteoryx/muditaos

1f7d47183133e84f05babb8626d6d032421d97e7 — alek 5 years ago 42c22d7
[EGD-4429] Fix gui issues in application call

Fix the persistent answer and reject labels in Call Window issue
Fix the wrong call duration issue in both Call and Calls apps
Fix the not working call ignoring issue
M module-apps/application-call/ApplicationCall.cpp => module-apps/application-call/ApplicationCall.cpp +15 -13
@@ 1,11 1,10 @@
// 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

#include "ApplicationCall.hpp"

#include "DialogMetadata.hpp"
#include "DialogMetadataMessage.hpp"
#include "application-call/data/CallWindowData.hpp"
#include "data/CallSwitchData.hpp"
#include "windows/CallMainWindow.hpp"
#include "windows/CallWindow.hpp"


@@ 35,11 34,11 @@ namespace app
        : Application(name, parent, startInBackground, app::call_stack_size)
    {
        addActionReceiver(manager::actions::Call, [this](auto &&data) {
            switchWindow(window::name_call, std::move(data));
            switchWindow(window::name_call, std::forward<decltype(data)>(data));
            return msgHandled();
        });
        addActionReceiver(manager::actions::Dial, [this](auto data) {
            switchWindow(window::name_enterNumber, std::move(data));
        addActionReceiver(manager::actions::Dial, [this](auto &&data) {
            switchWindow(window::name_enterNumber, std::forward<decltype(data)>(data));
            return msgHandled();
        });
    }


@@ 59,8 58,10 @@ namespace app

    void ApplicationCall::IncomingCallHandler(const CellularCallMessage *const msg)
    {
        manager::Controller::sendAction(
            this, manager::actions::Call, std::make_unique<app::IncomingCallData>(msg->number));
        if (state == call::State::IDLE) {
            manager::Controller::sendAction(
                this, manager::actions::Call, std::make_unique<app::IncomingCallData>(msg->number));
        }
    }

    void ApplicationCall::RingingHandler(const CellularCallMessage *const msg)


@@ 82,7 83,7 @@ namespace app
        }

        if (msgl->messageType == MessageType::CellularNotification) {
            CellularNotificationMessage *msg = dynamic_cast<CellularNotificationMessage *>(msgl);
            auto *msg = dynamic_cast<CellularNotificationMessage *>(msgl);
            assert(msg != nullptr);

            switch (msg->type) {


@@ 135,12 136,13 @@ namespace app
    {

        auto ret = Application::InitHandler();
        if (ret != sys::ReturnCodes::Success)
        if (ret != sys::ReturnCodes::Success) {
            return ret;
        }

        createUserInterface();

        setActiveWindow("MainWindow");
        setActiveWindow(gui::name::window::main_window);

        return ret;
    }


@@ 156,13 158,13 @@ namespace app
            return std::make_unique<gui::CallMainWindow>(app);
        });
        windowsFactory.attach(app::window::name_enterNumber, [](Application *app, const std::string newname) {
            return std::make_unique<gui::EnterNumberWindow>(app);
            return std::make_unique<gui::EnterNumberWindow>(app, static_cast<ApplicationCall *>(app));
        });
        windowsFactory.attach(app::window::name_call, [](Application *app, const std::string &name) {
            return std::make_unique<gui::CallWindow>(app);
            return std::make_unique<gui::CallWindow>(app, static_cast<ApplicationCall *>(app));
        });
        windowsFactory.attach(app::window::name_emergencyCall, [](Application *app, const std::string &name) {
            return std::make_unique<gui::EmergencyCallWindow>(app);
            return std::make_unique<gui::EmergencyCallWindow>(app, static_cast<ApplicationCall *>(app));
        });
        windowsFactory.attach(app::window::name_dialogConfirm, [](Application *app, const std::string &name) {
            return std::make_unique<gui::DialogConfirm>(app, name);

M module-apps/application-call/ApplicationCall.hpp => module-apps/application-call/ApplicationCall.hpp +45 -19
@@ 1,12 1,14 @@
// 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

#include "Application.hpp"
#include "Service/Message.hpp"
#include "Service/Timer.hpp"
#include "SystemManager/SystemManager.hpp"
#include "data/CallState.hpp"

#include <Application.hpp>
#include <Service/Message.hpp>
#include <Service/Timer.hpp>
#include <SystemManager/SystemManager.hpp>
#include <service-cellular/CellularMessage.hpp>
#include <time/time_conversion.hpp>



@@ 26,7 28,29 @@ namespace app

    inline constexpr auto ringtone_path = "assets/audio/Ringtone-drum2.mp3"; // Should bo moved to database

    class ApplicationCall : public Application
    class CallWindowInterface
    {
      public:
        virtual ~CallWindowInterface() noexcept = default;

        virtual void setState(app::call::State state) noexcept              = 0;
        [[nodiscard]] virtual auto getState() const noexcept -> call::State = 0;

        virtual void transmitDtmfTone(uint32_t digit) = 0;
        virtual void stopAudio()                      = 0;
        virtual void startRinging()                   = 0;
        virtual void startRouting()                   = 0;
    };

    class EnterNumberWindowInterface
    {
      public:
        virtual ~EnterNumberWindowInterface() noexcept                = default;
        virtual void handleCallEvent(const std::string &number)       = 0;
        virtual void handleAddContactEvent(const std::string &number) = 0;
    };

    class ApplicationCall : public Application, public CallWindowInterface, public EnterNumberWindowInterface
    {
      private:
        void CallAbortHandler();


@@ 35,7 59,7 @@ namespace app
        void RingingHandler(const CellularCallMessage *const msg);

      protected:
        sys::ms callDelayedStopTime = 3000;
        call::State state = call::State::IDLE;

      public:
        ApplicationCall(std::string name                    = name_call,


@@ 52,22 76,24 @@ namespace app

        void createUserInterface() override;
        void destroyUserInterface() override;
        void setDisplayedNumber(std::string num);
        const std::string &getDisplayedNumber();

        void handleCallEvent(const std::string &number);
        void handleAddContactEvent(const std::string &number);
        void handleCallEvent(const std::string &number) override;
        void handleAddContactEvent(const std::string &number) override;

        auto showNotification(std::function<bool()> action) -> bool;

        bool showNotification(std::function<bool()> action);
        void transmitDtmfTone(uint32_t digit);
        auto getDelayedStopTime() const
        [[nodiscard]] auto getState() const noexcept -> call::State override
        {
            return callDelayedStopTime;
            return state;
        }

        void stopAudio();
        void startRinging();
        void startRouting();
        void setState(call::State state) noexcept override
        {
            this->state = state;
        }
        void transmitDtmfTone(uint32_t digit) override;
        void stopAudio() override;
        void startRinging() override;
        void startRouting() override;
    };

    template <> struct ManifestTraits<ApplicationCall>

R module-apps/application-call/data/CallWindowState.hpp => module-apps/application-call/data/CallState.hpp +8 -8
@@ 1,9 1,9 @@
// 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

namespace gui::call
namespace app::call
{
    enum class State
    {


@@ 15,18 15,18 @@ namespace gui::call
    };
}

inline const char *c_str(gui::call::State state)
inline auto c_str(app::call::State state) -> const char *
{
    switch (state) {
    case gui::call::State::IDLE:
    case app::call::State::IDLE:
        return "IDLE";
    case gui::call::State::INCOMING_CALL:
    case app::call::State::INCOMING_CALL:
        return "INCOMING_CALL";
    case gui::call::State::OUTGOING_CALL:
    case app::call::State::OUTGOING_CALL:
        return "OUTGOING_CALL";
    case gui::call::State::CALL_IN_PROGRESS:
    case app::call::State::CALL_IN_PROGRESS:
        return "CALL_IN_PROGRESS";
    case gui::call::State::CALL_ENDED:
    case app::call::State::CALL_ENDED:
        return "CALL_ENDED";
    }
    return "";

D module-apps/application-call/data/CallWindowData.hpp => module-apps/application-call/data/CallWindowData.hpp +0 -23
@@ 1,23 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "CallWindowState.hpp"
#include <SwitchData.hpp>

namespace gui
{
    class CallWindowData : public SwitchData
    {
        call::State state;

      public:
        CallWindowData(call::State state) : state(state)
        {}
        [[nodiscard]] const auto &getState() const noexcept
        {
            return state;
        }
    };
} // namespace gui

M module-apps/application-call/windows/CallWindow.cpp => module-apps/application-call/windows/CallWindow.cpp +74 -109
@@ 1,11 1,11 @@
// 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

#include "CallWindow.hpp"
#include <application-call/data/CallWindowData.hpp>

#include "GuiTimer.hpp"
#include "InputEvent.hpp"
#include "application-call/data/CallState.hpp"
#include "application-call/widgets/StateIcons.hpp"
#include "log/log.hpp"
#include "service-appmgr/Controller.hpp"


@@ 13,6 13,7 @@
#include "application-call/ApplicationCall.hpp"
#include "application-call/data/CallSwitchData.hpp"

#include <bits/stdint-uintn.h>
#include <i18n/i18n.hpp>

#include <service-audio/AudioServiceAPI.hpp>


@@ 39,10 40,13 @@ namespace gui
{
    using namespace callAppStyle;
    using namespace callAppStyle::callWindow;
    using namespace call;
    using namespace app::call;

    CallWindow::CallWindow(app::Application *app, std::string windowName) : AppWindow(app, windowName)
    CallWindow::CallWindow(app::Application *app, app::CallWindowInterface *interface, std::string windowName)
        : AppWindow(app, windowName), interface(interface)
    {
        assert(interface != nullptr);
        assert(app != nullptr);
        buildInterface();
    }



@@ 86,7 90,8 @@ namespace gui

        speakerIcon                       = new SpeakerIcon(this, speakerIcon::x, speakerIcon::y);
        speakerIcon->focusChangedCallback = [=](gui::Item &item) {
            bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get(style::strings::common::Switch));
            LOG_DEBUG("speakerIcon get/lost focus");
            bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get(style::strings::common::Switch), false);
            return true;
        };
        speakerIcon->activatedCallback = [=](gui::Item &item) {


@@ 113,13 118,14 @@ namespace gui

        microphoneIcon                       = new MicrophoneIcon(this, microphoneIcon::x, microphoneIcon::y);
        microphoneIcon->focusChangedCallback = [=](gui::Item &item) {
            bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get(style::strings::common::Switch));
            LOG_DEBUG("microphoneIcon get/lost focus");
            bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get(style::strings::common::Switch), false);
            return true;
        };
        microphoneIcon->activatedCallback = [=](gui::Item &item) {
            microphoneIcon->setNext();
            application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
            LOG_INFO("Mic activated activated %d", static_cast<int>(microphoneIcon->get()));
            LOG_INFO("Mic activated %d", static_cast<int>(microphoneIcon->get()));

            microphoneIcon->get() == MicrophoneIconState::MUTED
                ? AudioServiceAPI::SendEvent(this->application, audio::EventType::CallMute)


@@ 130,12 136,12 @@ namespace gui

        sendSmsIcon                       = new gui::SendSmsIcon(this, sendMessageIcon::x, sendMessageIcon::y);
        sendSmsIcon->focusChangedCallback = [=](gui::Item &item) {
            LOG_INFO("Send message gets focus");
            bottomBar->setText(gui::BottomBar::Side::CENTER, utils::localize.get(strings::message));
            LOG_DEBUG("Send message get/lost focus");
            bottomBar->setText(gui::BottomBar::Side::CENTER, utils::localize.get(strings::message), false);
            return true;
        };
        sendSmsIcon->activatedCallback = [=](gui::Item &item) {
            LOG_INFO("Send messaage template and reject the call");
            LOG_INFO("Send message template and reject the call");
            return app::manager::Controller::sendAction(application,
                                                        app::manager::actions::ShowSmsTemplates,
                                                        std::make_unique<SMSSendTemplateRequest>(phoneNumber));


@@ 154,26 160,15 @@ namespace gui
        erase();
    }

    CallWindow::~CallWindow()
    {}

    void CallWindow::setState(State state)
    {
        LOG_INFO("==> Call state change: %s -> %s", c_str(this->state), c_str(state));
        this->state = state;
        setVisibleState();
    }
        auto prevState = getState();
        LOG_INFO("==> Call state change: %s -> %s", c_str(prevState), c_str(state));
        interface->setState(state);

    const State &CallWindow::getState()
    {
        return state;
    }

    void CallWindow::setVisibleState()
    {
        // show state of the window
        switch (state) {
        case State::INCOMING_CALL: {
            interface->startRinging();
            bottomBar->setActive(gui::BottomBar::Side::CENTER, true);
            bottomBar->setText(gui::BottomBar::Side::LEFT, utils::localize.get(strings::answer), true);
            bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::localize.get(strings::reject), true);


@@ 185,8 180,8 @@ namespace gui
            setFocusItem(sendSmsIcon);
        } break;
        case State::CALL_ENDED: {
            interface->stopAudio();
            stopCallTimer();

            bottomBar->setActive(gui::BottomBar::Side::LEFT, false);
            bottomBar->setActive(gui::BottomBar::Side::CENTER, false);
            bottomBar->setActive(gui::BottomBar::Side::RIGHT, false);


@@ 195,13 190,17 @@ namespace gui
            sendSmsIcon->setVisible(false);
            speakerIcon->setVisible(false);
            microphoneIcon->setVisible(false);
            setFocusItem(nullptr);
            speakerIcon->set(SpeakerIconState::SPEAKER);
            microphoneIcon->set(MicrophoneIconState::MUTE);

            setFocusItem(nullptr);
            connectTimerOnExit();
            LOG_FATAL("CALL_ENDED");
        } break;
        case State::CALL_IN_PROGRESS: {
            if (prevState == State::INCOMING_CALL) { // otherwise it is already started
                interface->startRouting();
            }
            runCallTimer();
            bottomBar->setActive(gui::BottomBar::Side::LEFT, false);
            bottomBar->setActive(gui::BottomBar::Side::CENTER, false);
            bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::localize.get(strings::endcall), true);


@@ 209,9 208,13 @@ namespace gui
            sendSmsIcon->setVisible(false);
            speakerIcon->setVisible(true);
            microphoneIcon->setVisible(true);
            setFocusItem(getFocusItem() != sendSmsIcon ? getFocusItem() : microphoneIcon);
            auto focusItem = getFocusItem();
            if (focusItem != microphoneIcon || focusItem != speakerIcon) {
                setFocusItem(microphoneIcon);
            }
        } break;
        case State::OUTGOING_CALL: {
            interface->startRouting();
            bottomBar->setActive(gui::BottomBar::Side::LEFT, false);
            bottomBar->setActive(gui::BottomBar::Side::CENTER, false);
            bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::localize.get(strings::endcall), true);


@@ 238,8 241,10 @@ namespace gui
        };
    }

    void CallWindow::setCallNumber(std::string)
    {}
    auto CallWindow::getState() const noexcept -> State
    {
        return interface->getState();
    }

    void CallWindow::updateDuration(const utils::time::Duration duration)
    {


@@ 248,96 253,58 @@ namespace gui
        }
    }

    bool CallWindow::handleSwitchData(SwitchData *data)
    void CallWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        if (data == nullptr) {
            LOG_DEBUG("Received null pointer");
            return false;
        }

        if (dynamic_cast<SMSTemplateSent *>(data) != nullptr) {
            CellularServiceAPI::HangupCall(application);
            return true;
        }

        if (data->getDescription() == app::CallSwitchData::descriptionStr) {
            auto *callData = dynamic_cast<app::CallSwitchData *>(data);
            assert(callData != nullptr);
            phoneNumber = callData->getPhoneNumber();
            switch (callData->getType()) {
            case app::CallSwitchData::Type::INCOMING_CALL: {
                state = State::INCOMING_CALL;
            } break;

            case app::CallSwitchData::Type::EXECUTE_CALL: {
                state = State::OUTGOING_CALL;
            } break;

            default:
                LOG_ERROR("Unhandled type");
        if (auto callData = dynamic_cast<app::CallSwitchData *>(data); callData != nullptr) {
            phoneNumber      = callData->getPhoneNumber();
            auto contact     = DBServiceAPI::MatchContactByPhoneNumber(this->application, phoneNumber);
            auto displayName = phoneNumber.getFormatted();
            if (contact) {
                LOG_INFO("number = %s recognized as contact id = %" PRIu32 ", name = %s",
                         phoneNumber.getEntered().c_str(),
                         contact->ID,
                         contact->getFormattedName().c_str());
                displayName = contact->getFormattedName();
            }
            else {
                LOG_INFO("number = %s was not recognized as any valid contact", phoneNumber.getEntered().c_str());
            }
        }
        else {
            return false;
        }

        auto contact            = DBServiceAPI::MatchContactByPhoneNumber(this->application, phoneNumber);
        std::string displayName = phoneNumber.getFormatted();
        if (contact) {
            LOG_INFO("number = %s recognized as contact id = %" PRIu32 ", name = %s",
                     phoneNumber.getEntered().c_str(),
                     contact->ID,
                     contact->getFormattedName().c_str());
            displayName = contact->getFormattedName();
        }
        else {
            LOG_INFO("number = %s was not recognized as any valid contact", phoneNumber.getEntered().c_str());
        }

        numberLabel->setText(displayName);

        setVisibleState();
        application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
            numberLabel->setText(displayName);

        return true;
    }

    void CallWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        auto app = static_cast<app::ApplicationCall *>(application);

        if (dynamic_cast<app::IncomingCallData *>(data) != nullptr) {
            if (getState() == call::State::INCOMING_CALL) {
                LOG_INFO("ignoring call incoming");
            if (dynamic_cast<app::IncomingCallData *>(data) != nullptr) {
                if (getState() == State::INCOMING_CALL) {
                    LOG_DEBUG("ignoring call incoming");
                    return;
                }
                setState(State::INCOMING_CALL);
                return;
            }
            if (dynamic_cast<app::ExecuteCallData *>(data) != nullptr) {
                setState(State::OUTGOING_CALL);
                return;
            }
            app->startRinging();
            setState(call::State::INCOMING_CALL);
            return;
        }

        if (dynamic_cast<app::CallAbortData *>(data) != nullptr) {
            app->stopAudio();
            setState(State::CALL_ENDED);
            return;
        }

        if (dynamic_cast<app::CallActiveData *>(data) != nullptr) {
            app->startRouting();
            runCallTimer();
            setState(State::CALL_IN_PROGRESS);
            return;
        }

        if (dynamic_cast<app::ExecuteCallData *>(data) != nullptr) {
            AudioServiceAPI::RoutingStart(application);
            runCallTimer();
            setState(State::OUTGOING_CALL);
        if (dynamic_cast<SMSTemplateSent *>(data) != nullptr) {
            CellularServiceAPI::HangupCall(application);
            return;
        }
    }

    bool CallWindow::handleLeftButton()
    {
        if (state == State::INCOMING_CALL) {
        if (getState() == State::INCOMING_CALL) {
            auto ret = CellularServiceAPI::AnswerIncomingCall(application);

            LOG_INFO("AnswerIncomingCall: %s", (ret ? "OK" : "FAIL"));


@@ 349,7 316,7 @@ namespace gui

    bool CallWindow::handleRightButton()
    {
        switch (state) {
        switch (getState()) {
        case State::INCOMING_CALL:
        case State::OUTGOING_CALL:
        case State::CALL_IN_PROGRESS:


@@ 365,9 332,7 @@ namespace gui

    bool CallWindow::handleDigit(const uint32_t digit)
    {
        auto app = dynamic_cast<app::ApplicationCall *>(application);
        assert(app != nullptr);
        app->transmitDtmfTone(digit);
        interface->transmitDtmfTone(digit);
        return true;
    }



@@ 410,18 375,17 @@ namespace gui

    void CallWindow::connectTimerOnExit()
    {
        auto app = dynamic_cast<app::ApplicationCall *>(application);
        assert(app != nullptr);

        auto timer = std::make_unique<app::GuiTimer>(app);
        timer->setInterval(app->getDelayedStopTime());
        auto timer = std::make_unique<app::GuiTimer>(application);
        timer->setInterval(getDelayedStopTime());

        timerCallback = [this](Item &, Timer &timer) {
            LOG_DEBUG("Delayed exit timer callback");
            setState(State::IDLE);
            detachTimer(timer);
            app::manager::Controller::switchBack(application);
            return true;
        };
        timer->start();
        application->connect(std::move(timer), this);
    }



@@ 438,6 402,7 @@ namespace gui
            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;
        };


@@ 448,6 413,6 @@ namespace gui
    void CallWindow::stopCallTimer()
    {
        callDuration = std::chrono::seconds().zero();
        stop_timer = true;
        stop_timer   = true;
    }
} /* namespace gui */

M module-apps/application-call/windows/CallWindow.hpp => module-apps/application-call/windows/CallWindow.hpp +22 -23
@@ 1,16 1,16 @@
// 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

#include "application-call/ApplicationCall.hpp"
#include "AppWindow.hpp"
#include "application-call/widgets/StateIcons.hpp"
#include "application-call/data/CallState.hpp"

#include <gui/input/Translator.hpp>
#include <Rect.hpp>
#include <Image.hpp>
#include <application-call/data/CallWindowState.hpp>
#include <AppWindow.hpp>

namespace gui
{


@@ 18,45 18,44 @@ namespace gui
    {
      private:
        gui::KeyInputMappedTranslation translator;
        std::chrono::seconds callDuration = std::chrono::seconds().zero();
        bool stop_timer = false;
        std::chrono::seconds callDuration                   = std::chrono::seconds().zero();
        bool stop_timer                                     = false;
        static constexpr inline sys::ms callDelayedStopTime = 3000;

        [[nodiscard]] auto getDelayedStopTime() const
        {
            return callDelayedStopTime;
        }

      protected:
        // used to display both nnumber and name of contact
        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
        gui::Label *durationLabel = nullptr;

        gui::SendSmsIcon *sendSmsIcon  = nullptr;
        gui::SendSmsIcon *sendSmsIcon       = nullptr;
        gui::MicrophoneIcon *microphoneIcon = nullptr;
        gui::SpeakerIcon *speakerIcon       = nullptr;
        gui::Image *imageCircleTop     = nullptr;
        gui::Image *imageCircleBottom  = nullptr;
        gui::Image *imageCircleTop          = nullptr;
        gui::Image *imageCircleBottom       = nullptr;

        call::State state = call::State::IDLE;
        utils::PhoneNumber::View phoneNumber;
        /**
         * Manipulates widgets to handle currently set state of the window.
         */
        void setVisibleState();

        bool handleLeftButton();
        bool handleRightButton();
        void setState(call::State state);
        void setState(app::call::State state);
        [[nodiscard]] auto getState() const noexcept -> app::call::State;

      public:
        CallWindow(app::Application *app, std::string windowName = app::window::name_call);
        virtual ~CallWindow();
        CallWindow(app::Application *app,
                   app::CallWindowInterface *interface,
                   std::string windowName = app::window::name_call);

        /**
         * Used by application to update window's state
         */
        const call::State &getState();
        void updateDuration(const utils::time::Duration duration);
        void setCallNumber(std::string);

        bool onInput(const InputEvent &inputEvent) override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        bool handleSwitchData(SwitchData *data) override;

        void rebuild() override;
        void buildInterface() override;

M module-apps/application-call/windows/EmergencyCallWindow.cpp => module-apps/application-call/windows/EmergencyCallWindow.cpp +7 -4
@@ 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

#include "../data/CallSwitchData.hpp"


@@ 8,13 8,16 @@
#include <i18n/i18n.hpp>
#include "EmergencyCallWindow.hpp"

#include <cassert>

namespace gui
{

    EmergencyCallWindow::EmergencyCallWindow(app::Application *app)
        : EnterNumberWindow(app, app::window::name_emergencyCall)
    EmergencyCallWindow::EmergencyCallWindow(app::Application *app, app::EnterNumberWindowInterface *interface)
        : EnterNumberWindow(app, interface, app::window::name_emergencyCall)
    {

        assert(app != nullptr);
        assert(interface != nullptr);
        numberLabel->setText(utils::localize.get("app_call_emergency"));
    }


M module-apps/application-call/windows/EmergencyCallWindow.hpp => module-apps/application-call/windows/EmergencyCallWindow.hpp +2 -2
@@ 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


@@ 11,7 11,7 @@ namespace gui
    class EmergencyCallWindow : public EnterNumberWindow
    {
      public:
        EmergencyCallWindow(app::Application *app);
        EmergencyCallWindow(app::Application *app, app::EnterNumberWindowInterface *interface);

        bool onInput(const InputEvent &inputEvent) override;
        bool handleSwitchData(SwitchData *data) override;

M module-apps/application-call/windows/EnterNumberWindow.cpp => module-apps/application-call/windows/EnterNumberWindow.cpp +14 -25
@@ 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

#include "EnterNumberWindow.hpp"


@@ 17,6 17,8 @@
#include <phonenumbers/phonenumberutil.h>
#include <phonenumbers/asyoutypeformatter.h>

#include <cassert>

using namespace utils;

namespace gui


@@ 24,16 26,18 @@ namespace gui
    using namespace callAppStyle;
    using namespace callAppStyle::enterNumberWindow;

    EnterNumberWindow::EnterNumberWindow(app::Application *app, std::string windowName)
        : AppWindow(app, windowName), currentCountry(defaultCountry), numberUtil(*PhoneNumberUtil::GetInstance())
    EnterNumberWindow::EnterNumberWindow(app::Application *app,
                                         app::EnterNumberWindowInterface *interface,
                                         std::string windowName)
        : AppWindow(app, windowName), interface(interface), currentCountry(defaultCountry),
          numberUtil(*PhoneNumberUtil::GetInstance())
    {
        assert(interface != nullptr);
        assert(app != nullptr);
        switchFormatter(country::getAlpha2Code(currentCountry));
        buildInterface();
    }

    void EnterNumberWindow::rebuild()
    {}

    void EnterNumberWindow::setNumberLabel(const std::string num)
    {
        numberLabel->setText(num);


@@ 72,11 76,8 @@ namespace gui

    bool EnterNumberWindow::addNewContact()
    {
        auto app = dynamic_cast<app::ApplicationCall *>(application);
        if (app != nullptr) {
            app->handleAddContactEvent(enteredNumber);
        }
        return false;
        interface->handleAddContactEvent(enteredNumber);
        return true;
    }

    void EnterNumberWindow::destroyInterface()


@@ 84,22 85,13 @@ namespace gui
        erase();
    }

    EnterNumberWindow::~EnterNumberWindow()
    {
    }

    bool EnterNumberWindow::onInput(const InputEvent &inputEvent)
    {
        auto app = dynamic_cast<app::ApplicationCall *>(application);
        if (app == nullptr) {
            LOG_ERROR("app != ApplicationCall");
            return AppWindow::onInput(inputEvent);
        }
        auto code = translator.handle(inputEvent.key, InputMode({InputMode::phone}).get());
        if (inputEvent.state == InputEvent::State::keyReleasedShort) {
            // Call function
            if (inputEvent.keyCode == KeyCode::KEY_LF) {
                app->handleCallEvent(enteredNumber);
                interface->handleCallEvent(enteredNumber);
                return true;
            }
            // Clear/back function


@@ 136,7 128,6 @@ namespace gui
                    app::manager::Controller::switchBack(application);
                    return true;
                }

                clearInput();

                return true;


@@ 154,8 145,6 @@ namespace gui

    bool EnterNumberWindow::handleSwitchData(SwitchData *data)
    {
        auto app = dynamic_cast<app::ApplicationCall *>(application);

        if (data == nullptr) {
            LOG_ERROR("Received null pointer");
            return false;


@@ 180,7 169,7 @@ namespace gui
            application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);

            if (callData->getType() == app::CallSwitchData::Type::EXECUTE_CALL) {
                app->handleCallEvent(phoneNumber.getEntered());
                interface->handleCallEvent(phoneNumber.getEntered());
            }
        }
        else {

M module-apps/application-call/windows/EnterNumberWindow.hpp => module-apps/application-call/windows/EnterNumberWindow.hpp +12 -11
@@ 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


@@ 35,21 35,22 @@ namespace gui
        using Formatter       = i18n::phonenumbers::AsYouTypeFormatter;

      protected:
        gui::Label *numberLabel             = nullptr;
        gui::AddContactIcon *newContactIcon = nullptr;
        app::EnterNumberWindowInterface *interface = nullptr;
        gui::Label *numberLabel                    = nullptr;
        gui::AddContactIcon *newContactIcon        = nullptr;

        void setNumberLabel(const std::string num);

      public:
        EnterNumberWindow() = delete;
        EnterNumberWindow(app::Application *app, std::string windowName = app::window::name_enterNumber);
        virtual ~EnterNumberWindow();
        EnterNumberWindow(app::Application *app,
                          app::EnterNumberWindowInterface *interface,
                          std::string windowName = app::window::name_enterNumber);
        ~EnterNumberWindow() override = default;

        bool onInput(const InputEvent &inputEvent) override;
        bool handleSwitchData(SwitchData *data) override;
        const std::string &getEnteredNumber() const noexcept;
        auto onInput(const InputEvent &inputEvent) -> bool override;
        auto handleSwitchData(SwitchData *data) -> bool override;
        [[nodiscard]] auto getEnteredNumber() const noexcept -> const std::string &;

        void rebuild() override;
        void buildInterface() override;
        void destroyInterface() override;



@@ 62,7 63,7 @@ namespace gui
        std::string formattedNumber;
        std::string enteredNumber;

        bool addNewContact();
        auto addNewContact() -> bool;
        void switchFormatter(const std::string &countryCode);
        void initFormatterInput(const std::string &number);
        void addDigit(const std::string::value_type &digit);

M module-services/service-cellular/CellularCall.cpp => module-services/service-cellular/CellularCall.cpp +9 -11
@@ 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

#include "service-cellular/CellularCall.hpp"


@@ 40,9 40,9 @@ namespace ModemCall
            throw std::runtime_error("Wrong number of tokens" + std::to_string(numberOfTokens));
        }

        idx              = std::stoul(tokens[0]);
        auto conv_val    = std::stoul(tokens[1]);
        auto tmp_dir     = magic_enum::enum_cast<CallDir>(conv_val);
        idx           = std::stoul(tokens[0]);
        auto conv_val = std::stoul(tokens[1]);
        auto tmp_dir  = magic_enum::enum_cast<CallDir>(conv_val);
        if (tmp_dir.has_value()) {
            dir = tmp_dir.value();
        }


@@ 71,15 71,13 @@ namespace ModemCall
        isConferenceCall = static_cast<bool>(std::stoul(tokens[4]));
        phoneNumber      = tokens[5];

        conv_val = std::stoul(tokens[6]);

        auto tmp_type = magic_enum::enum_cast<CallType>(conv_val);
        if (tmp_type.has_value()) {
            type = tmp_type.value();
        try {
            conv_val = std::stoul(tokens[6]);
        }
        else {
            throw std::runtime_error("type value out of range CallType enum - " + tokens[6]);
        catch (const std::logic_error &) {
            conv_val = 0;
        }
        type = conv_val;

        if (numberOfTokens == 8) {
            phoneBookName = tokens[7];

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +1 -0
@@ 224,6 224,7 @@ static bool isSettingsAutomaticTimeSyncEnabled()

void ServiceCellular::CallStateTimerHandler()
{
    LOG_DEBUG("CallStateTimerHandler");
    std::shared_ptr<CellularRequestMessage> msg =
        std::make_shared<CellularRequestMessage>(MessageType::CellularListCurrentCalls);
    sys::Bus::SendUnicast(msg, ServiceCellular::serviceName, this);

M module-services/service-cellular/service-cellular/CellularCall.hpp => module-services/service-cellular/service-cellular/CellularCall.hpp +6 -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


@@ 41,7 41,8 @@ namespace ModemCall
        FAX   = 2,
    };

    // TODO: alek: check specification
    /// Usually contains one of defined values
    /// More details in 3GPP TS 24.008 subclause 10.5.4.7
    enum class CallType : uint8_t
    {
        UknownType      = 129,


@@ 57,9 58,9 @@ namespace ModemCall
        CallMode mode;
        bool isConferenceCall;
        std::string phoneNumber;
        CallType type;
        std::string phoneBookName; // TODO: alek: This field is defined in the AT+CLCC command resposne but our modem is
                                   // not returning it. Need to verify in modem specification
        uint8_t type;              /// Usually contains on of values defined in CallType
        std::string phoneBookName; /// This field is defined in the AT+CLCC command response but our modem is
                                   /// not returning it.

        ModemCall()  = delete;
        ~ModemCall() = default;

M module-utils/log/debug.hpp => module-utils/log/debug.hpp +1 -1
@@ 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