~aleteoryx/muditaos

bf3cc2f23c293b686170a8d36b9894cfd03d378c — Wojtek Rzepecki 4 years ago 82bb7fb
[EGD-6955] Add calls notifications in DND

Add notifications for incoming calls in DND
phone mode according to design.
30 files changed, 567 insertions(+), 90 deletions(-)

M module-apps/application-call/ApplicationCall.cpp
M module-apps/application-call/ApplicationCall.hpp
M module-apps/application-call/windows/CallWindow.cpp
M module-apps/apps-common/CMakeLists.txt
M module-apps/apps-common/notifications/NotificationProvider.cpp
M module-apps/apps-common/notifications/NotificationProvider.hpp
A module-apps/apps-common/notifications/NotificationsConfiguration.cpp
A module-apps/apps-common/notifications/NotificationsConfiguration.hpp
A module-apps/apps-common/notifications/NotificationsHandler.cpp
A module-apps/apps-common/notifications/NotificationsHandler.hpp
M module-apps/apps-common/notifications/NotificationsModel.cpp
M module-apps/apps-common/notifications/NotificationsModel.hpp
A module-apps/apps-common/notifications/policies/CallNotificationPolicy.cpp
A module-apps/apps-common/notifications/policies/CallNotificationPolicy.hpp
A module-apps/apps-common/notifications/policies/NotificationsListPolicy.cpp
A module-apps/apps-common/notifications/policies/NotificationsListPolicy.hpp
M module-apps/apps-common/popups/lock-popups/PhoneLockedWindow.cpp
M module-apps/tests/CMakeLists.txt
A module-apps/tests/test-PhoneModesPolicies.cpp
M module-services/service-appmgr/data/NotificationsChangedActionsParams.cpp
M module-services/service-appmgr/model/ApplicationManager.cpp
M module-services/service-appmgr/service-appmgr/data/NotificationsChangedActionsParams.hpp
M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp
M module-services/service-cellular/CellularServiceAPI.cpp
M module-services/service-cellular/ServiceCellular.cpp
M module-services/service-cellular/service-cellular/CellularMessage.hpp
M module-services/service-cellular/service-cellular/CellularServiceAPI.hpp
M module-services/service-cellular/service-cellular/ServiceCellular.hpp
M module-services/service-db/DBServiceAPI.cpp
M module-services/service-db/service-db/DBServiceAPI.hpp
M module-apps/application-call/ApplicationCall.cpp => module-apps/application-call/ApplicationCall.cpp +0 -6
@@ 250,12 250,6 @@ namespace app
        AudioServiceAPI::StopAll(this);
    }

    void ApplicationCall::startAudioRinging()
    {
        const auto filePath = AudioServiceAPI::GetSound(this, audio::PlaybackType::CallRingtone);
        AudioServiceAPI::PlaybackStart(this, audio::PlaybackType::CallRingtone, filePath);
    }

    void ApplicationCall::startAudioRouting()
    {
        AudioServiceAPI::RoutingStart(this);

M module-apps/application-call/ApplicationCall.hpp => module-apps/application-call/ApplicationCall.hpp +0 -2
@@ 45,7 45,6 @@ namespace app
        };

        virtual void stopAudio()                           = 0;
        virtual void startAudioRinging()                   = 0;
        virtual void startAudioRouting()                   = 0;
        virtual void sendAudioEvent(AudioEvent audioEvent) = 0;



@@ 117,7 116,6 @@ namespace app
        }

        void stopAudio() override;
        void startAudioRinging() override;
        void startAudioRouting() override;
        void sendAudioEvent(AudioEvent audioEvent) override;


M module-apps/application-call/windows/CallWindow.cpp => module-apps/application-call/windows/CallWindow.cpp +0 -1
@@ 174,7 174,6 @@ namespace gui

        switch (state) {
        case State::INCOMING_CALL: {
            interface->startAudioRinging();
            bottomBar->setText(gui::BottomBar::Side::LEFT, utils::translate(strings::answer), true);
            bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::translate(strings::reject), true);
            durationLabel->setText(utils::translate(strings::iscalling));

M module-apps/apps-common/CMakeLists.txt => module-apps/apps-common/CMakeLists.txt +4 -0
@@ 16,6 16,10 @@ target_sources(apps-common
        notifications/NotificationListItem.cpp
        notifications/NotificationProvider.cpp
        notifications/NotificationsModel.cpp
        notifications/NotificationsHandler.cpp
        notifications/NotificationsConfiguration.cpp
        notifications/policies/CallNotificationPolicy.cpp
        notifications/policies/NotificationsListPolicy.cpp
        options/OptionsModel.cpp
        options/type/OptionCall.cpp
        options/type/OptionContact.cpp

M module-apps/apps-common/notifications/NotificationProvider.cpp => module-apps/apps-common/notifications/NotificationProvider.cpp +11 -6
@@ 11,7 11,8 @@

using namespace notifications;

NotificationProvider::NotificationProvider(sys::Service *ownerService) : ownerService{ownerService}
NotificationProvider::NotificationProvider(sys::Service *ownerService, NotificationsConfiguration &notifcationConfig)
    : ownerService{ownerService}, notifcationConfig{notifcationConfig}
{}

template <NotificationType type, typename T>


@@ 56,7 57,9 @@ void NotificationProvider::handle(db::query::notifications::GetAllResult *msg)
        }
    }

    if (notificationsChanged) {
    notifcationConfig.updateCurrentList(listPolicy);

    if (notificationsChanged && listPolicy.updateListAllowed()) {
        send();
    }
}


@@ 108,6 111,8 @@ namespace

void NotificationProvider::send()
{
    notifcationConfig.updateCurrentList(listPolicy);

    std::list<std::shared_ptr<const Notification>> toSendNotifications;
    transform(notifications.begin(), notifications.end(), back_inserter(toSendNotifications), get_second());



@@ 116,8 121,8 @@ void NotificationProvider::send()
            return (lhs->getPriority() > rhs->getPriority());
        });

    app::manager::Controller::sendAction(
        ownerService,
        app::manager::actions::NotificationsChanged,
        std::make_unique<app::manager::actions::NotificationsChangedParams>(std::move(toSendNotifications)));
    app::manager::Controller::sendAction(ownerService,
                                         app::manager::actions::NotificationsChanged,
                                         std::make_unique<app::manager::actions::NotificationsChangedParams>(
                                             std::move(toSendNotifications), listPolicy.showListWhenLocked()));
}

M module-apps/apps-common/notifications/NotificationProvider.hpp => module-apps/apps-common/notifications/NotificationProvider.hpp +5 -1
@@ 4,6 4,8 @@
#pragma once

#include "NotificationData.hpp"
#include "NotificationsConfiguration.hpp"
#include "policies/NotificationsListPolicy.hpp"
#include <PhoneModes/Common.hpp>

namespace sys


@@ 30,7 32,7 @@ namespace notifications
        template <NotificationType type, typename T> bool handleNotSeenWithCounter(NotificationsRecord &&record);

      public:
        explicit NotificationProvider(sys::Service *ownerService);
        explicit NotificationProvider(sys::Service *ownerService, NotificationsConfiguration &notifcationConfig);

        void handle(db::query::notifications::GetAllResult *msg);
        void handle(db::NotificationMessage *msg);


@@ 42,6 44,8 @@ namespace notifications

      private:
        sys::Service *ownerService;
        NotificationsConfiguration &notifcationConfig;
        NotificationsListPolicy listPolicy;
        Notifications notifications;
    };


A module-apps/apps-common/notifications/NotificationsConfiguration.cpp => module-apps/apps-common/notifications/NotificationsConfiguration.cpp +43 -0
@@ 0,0 1,43 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NotificationsConfiguration.hpp"
#include <Utils.hpp>
#include <agents/settings/SystemSettings.hpp>

using namespace notifications;

NotificationsConfiguration::NotificationsConfiguration(std::shared_ptr<sys::phone_modes::Observer> phoneModeObserver,
                                                       std::shared_ptr<settings::Settings> settings,
                                                       const locks::PhoneLockHandler &phoneLockHandler)
    : phoneModeObserver{phoneModeObserver}, settings{settings}, phoneLockHandler{phoneLockHandler}
{}

void NotificationsConfiguration::updateCurrentCall(CallNotificationPolicy &policy)
{
    policy.updateCurrentCall(phoneModeObserver->getCurrentPhoneMode());
}

void NotificationsConfiguration::callNumberCheck(CallNotificationPolicy &policy, bool contactInFavourites)
{
    policy.numberCheck(getDNDCallsFromFavourites(), contactInFavourites);
}

void NotificationsConfiguration::updateCurrentList(NotificationsListPolicy &policy)
{
    policy.updateCurrentList(phoneModeObserver->getCurrentPhoneMode(),
                             phoneLockHandler.isPhoneLocked(),
                             getDNDNotificationsOnLockedScreen());
}

auto NotificationsConfiguration::getDNDNotificationsOnLockedScreen() const noexcept -> bool
{
    return utils::getNumericValue<bool>(
        settings->getValue(settings::Offline::notificationsWhenLocked, settings::SettingsScope::Global));
}

auto NotificationsConfiguration::getDNDCallsFromFavourites() const noexcept -> bool
{
    return utils::getNumericValue<bool>(
        settings->getValue(settings::Offline::callsFromFavorites, settings::SettingsScope::Global));
}

A module-apps/apps-common/notifications/NotificationsConfiguration.hpp => module-apps/apps-common/notifications/NotificationsConfiguration.hpp +31 -0
@@ 0,0 1,31 @@
// 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 "policies/CallNotificationPolicy.hpp"
#include "policies/NotificationsListPolicy.hpp"
#include <service-db/Settings.hpp>
#include <PhoneModes/Observer.hpp>
#include <apps-common/locks/handlers/PhoneLockHandler.hpp>

namespace notifications
{
    class NotificationsConfiguration
    {
      public:
        NotificationsConfiguration(std::shared_ptr<sys::phone_modes::Observer> phoneModeObserver,
                                   std::shared_ptr<settings::Settings> settings,
                                   const locks::PhoneLockHandler &phoneLockHandler);
        void updateCurrentCall(CallNotificationPolicy &policy);
        void callNumberCheck(CallNotificationPolicy &policy, bool contactInFavourites);
        void updateCurrentList(NotificationsListPolicy &policy);

      private:
        auto getDNDNotificationsOnLockedScreen() const noexcept -> bool;
        auto getDNDCallsFromFavourites() const noexcept -> bool;
        std::shared_ptr<sys::phone_modes::Observer> phoneModeObserver;
        std::shared_ptr<settings::Settings> settings;
        const locks::PhoneLockHandler &phoneLockHandler;
    };
} // namespace notifications

A module-apps/apps-common/notifications/NotificationsHandler.cpp => module-apps/apps-common/notifications/NotificationsHandler.cpp +75 -0
@@ 0,0 1,75 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NotificationsHandler.hpp"
#include <service-db/DBServiceAPI.hpp>
#include <log.hpp>
#include <service-cellular/service-cellular/CellularMessage.hpp>
#include <service-appmgr/service-appmgr/Controller.hpp>
#include <service-audio/AudioServiceAPI.hpp>
#include <service-cellular/CellularServiceAPI.hpp>

using namespace notifications;

NotificationsHandler::NotificationsHandler(sys::Service *parentService, NotificationsConfiguration &notifcationConfig)
    : parentService{parentService}, notifcationConfig{notifcationConfig}
{}

void NotificationsHandler::registerMessageHandlers()
{
    parentService->connect(typeid(CellularIncominCallMessage), [&](sys::Message *request) -> sys::MessagePointer {
        incomingCallHandler(request);
        return sys::msgHandled();
    });

    parentService->connect(typeid(CellularCallerIdMessage), [&](sys::Message *request) -> sys::MessagePointer {
        callerIdHandler(request);
        return sys::msgHandled();
    });
}

void NotificationsHandler::incomingCallHandler(sys::Message *request)
{
    notifcationConfig.updateCurrentCall(currentCallPolicy);

    if (currentCallPolicy.isPopupAllowed()) {
        auto msg = static_cast<CellularIncominCallMessage *>(request);
        app::manager::Controller::sendAction(parentService,
                                             app::manager::actions::HandleIncomingCall,
                                             std::make_unique<app::manager::actions::CallParams>(msg->number));

        playbackCallRingtone();
    }
}

void NotificationsHandler::callerIdHandler(sys::Message *request)
{
    auto msg = static_cast<CellularIncominCallMessage *>(request);

    if (currentCallPolicy.isNumberCheckRequired()) {
        policyNumberCheck(msg->number);
    }
    if (currentCallPolicy.isPopupAllowed()) {
        app::manager::Controller::sendAction(parentService,
                                             app::manager::actions::HandleCallerId,
                                             std::make_unique<app::manager::actions::CallParams>(msg->number));
        playbackCallRingtone();
    }
    else {
        CellularServiceAPI::DismissCall(parentService);
    }
}

void NotificationsHandler::policyNumberCheck(const utils::PhoneNumber::View &number)
{
    auto isContactInFavourites = DBServiceAPI::IsContactInFavourites(parentService, number);
    notifcationConfig.callNumberCheck(currentCallPolicy, isContactInFavourites);
}

void NotificationsHandler::playbackCallRingtone()
{
    if (currentCallPolicy.isRingtoneAllowed()) {
        const auto filePath = AudioServiceAPI::GetSound(parentService, audio::PlaybackType::CallRingtone);
        AudioServiceAPI::PlaybackStart(parentService, audio::PlaybackType::CallRingtone, filePath);
    }
}

A module-apps/apps-common/notifications/NotificationsHandler.hpp => module-apps/apps-common/notifications/NotificationsHandler.hpp +30 -0
@@ 0,0 1,30 @@
// 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 "NotificationsConfiguration.hpp"
#include "policies/CallNotificationPolicy.hpp"
#include <PhoneModes/Observer.hpp>
#include <Service/Service.hpp>
#include <PhoneNumber.hpp>

namespace notifications
{
    class NotificationsHandler
    {
      public:
        NotificationsHandler(sys::Service *parentService, NotificationsConfiguration &notifcationConfig);
        void registerMessageHandlers();

      private:
        void policyNumberCheck(const utils::PhoneNumber::View &number);
        void playbackCallRingtone();
        void incomingCallHandler(sys::Message *request);
        void callerIdHandler(sys::Message *request);

        sys::Service *parentService = nullptr;
        NotificationsConfiguration &notifcationConfig;
        CallNotificationPolicy currentCallPolicy;
    };
} // namespace notifications

M module-apps/apps-common/notifications/NotificationsModel.cpp => module-apps/apps-common/notifications/NotificationsModel.cpp +23 -13
@@ 31,6 31,10 @@ namespace
    }
} // namespace

NotificationsModel::NotificationsModel(NotificationsListPlacement listPlacement)
    : app::InternalModel<gui::NotificationListItem *>{}, gui::ListItemProvider{}, listPlacement{listPlacement}
{}

unsigned int NotificationsModel::requestRecordsCount()
{
    return internalData.size();


@@ 113,19 117,25 @@ void NotificationsModel::updateData(app::manager::actions::NotificationsChangedP
            delete item;
        }
    };
    tetheringOn = hasTetheringNotification(params);
    for (const auto &notification : params->getNotifications()) {
        if (not tetheringOn && typeid(*notification) == typeid(notifications::NotSeenSMSNotification)) {
            auto sms = static_cast<const notifications::NotSeenSMSNotification *>(notification.get());
            internalData.push_back(create(sms));
        }
        else if (not tetheringOn && typeid(*notification) == typeid(notifications::NotSeenCallNotification)) {
            auto call = static_cast<const notifications::NotSeenCallNotification *>(notification.get());
            internalData.push_back(create(call));
        }
        else if (typeid(*notification) == typeid(notifications::TetheringNotification)) {
            auto tethering = static_cast<const notifications::TetheringNotification *>(notification.get());
            internalData.push_back(create(tethering));

    const auto showOnLocked =
        (listPlacement == NotificationsListPlacement::LockedScreen) && params->showNotificationsWhenLocked();

    if ((listPlacement == NotificationsListPlacement::Desktop) || showOnLocked) {
        tetheringOn = hasTetheringNotification(params);
        for (const auto &notification : params->getNotifications()) {
            if (not tetheringOn && typeid(*notification) == typeid(notifications::NotSeenSMSNotification)) {
                auto sms = static_cast<const notifications::NotSeenSMSNotification *>(notification.get());
                internalData.push_back(create(sms));
            }
            else if (not tetheringOn && typeid(*notification) == typeid(notifications::NotSeenCallNotification)) {
                auto call = static_cast<const notifications::NotSeenCallNotification *>(notification.get());
                internalData.push_back(create(call));
            }
            else if (typeid(*notification) == typeid(notifications::TetheringNotification)) {
                auto tethering = static_cast<const notifications::TetheringNotification *>(notification.get());
                internalData.push_back(create(tethering));
            }
        }
    }


M module-apps/apps-common/notifications/NotificationsModel.hpp => module-apps/apps-common/notifications/NotificationsModel.hpp +7 -1
@@ 13,7 13,11 @@

namespace gui
{

    enum class NotificationsListPlacement
    {
        Desktop,
        LockedScreen
    };
    class NotificationsModel : public app::InternalModel<gui::NotificationListItem *>, public gui::ListItemProvider
    {
        [[nodiscard]] unsigned int requestRecordsCount() final;


@@ 23,6 27,7 @@ namespace gui

      protected:
        bool tetheringOn = false;
        const NotificationsListPlacement listPlacement;
        [[nodiscard]] virtual auto create(const notifications::NotSeenSMSNotification *notification)
            -> NotificationListItem *;
        [[nodiscard]] virtual auto create(const notifications::NotSeenCallNotification *notification)


@@ 31,6 36,7 @@ namespace gui
            -> NotificationListItem *;

      public:
        explicit NotificationsModel(NotificationsListPlacement listPlacement = NotificationsListPlacement::Desktop);
        [[nodiscard]] bool isEmpty() const noexcept;
        [[nodiscard]] bool hasDismissibleNotification() const noexcept;
        [[nodiscard]] bool isTetheringOn() const noexcept;

A module-apps/apps-common/notifications/policies/CallNotificationPolicy.cpp => module-apps/apps-common/notifications/policies/CallNotificationPolicy.cpp +47 -0
@@ 0,0 1,47 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CallNotificationPolicy.hpp"

using namespace notifications;

void CallNotificationPolicy::updateCurrentCall(sys::phone_modes::PhoneMode phoneMode)
{
    popupOn           = false;
    ringtoneOn        = false;
    numberCheckNeeded = false;

    switch (phoneMode) {
    case sys::phone_modes::PhoneMode::Connected:
        popupOn    = true;
        ringtoneOn = true;
        break;
    case sys::phone_modes::PhoneMode::DoNotDisturb:
        numberCheckNeeded = true;
        break;
    case sys::phone_modes::PhoneMode::Offline:
        break;
    }
}

bool CallNotificationPolicy::isPopupAllowed() const noexcept
{
    return popupOn && !numberCheckNeeded;
}

bool CallNotificationPolicy::isRingtoneAllowed() const noexcept
{
    return ringtoneOn;
}

bool CallNotificationPolicy::isNumberCheckRequired() const noexcept
{
    return numberCheckNeeded;
}

void CallNotificationPolicy::numberCheck(bool callsFromFavouritesSetting, bool isNumberinFavourites)
{
    numberCheckNeeded = false;
    popupOn           = callsFromFavouritesSetting && isNumberinFavourites;
    ringtoneOn        = callsFromFavouritesSetting && isNumberinFavourites;
}

A module-apps/apps-common/notifications/policies/CallNotificationPolicy.hpp => module-apps/apps-common/notifications/policies/CallNotificationPolicy.hpp +25 -0
@@ 0,0 1,25 @@
// 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 <PhoneNumber.hpp>
#include <PhoneModes/Common.hpp>

namespace notifications
{
    class CallNotificationPolicy
    {
      public:
        void updateCurrentCall(sys::phone_modes::PhoneMode phoneMode);
        bool isPopupAllowed() const noexcept;
        bool isRingtoneAllowed() const noexcept;
        bool isNumberCheckRequired() const noexcept;
        void numberCheck(bool callsFromFavouritesSetting, bool isNumberInFavourites);

      private:
        bool popupOn;
        bool ringtoneOn;
        bool numberCheckNeeded;
    };
} // namespace notifications

A module-apps/apps-common/notifications/policies/NotificationsListPolicy.cpp => module-apps/apps-common/notifications/policies/NotificationsListPolicy.cpp +37 -0
@@ 0,0 1,37 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NotificationsListPolicy.hpp"

using namespace notifications;

void NotificationsListPolicy::updateCurrentList(sys::phone_modes::PhoneMode phoneMode,
                                                bool isLocked,
                                                bool lockedScreenNotificationSetting)
{
    updateAllowed  = true;
    showWhenLocked = true;

    switch (phoneMode) {
    case sys::phone_modes::PhoneMode::DoNotDisturb:
        showWhenLocked = lockedScreenNotificationSetting;
        updateAllowed  = (isLocked && lockedScreenNotificationSetting) || (!isLocked);
        break;
    case sys::phone_modes::PhoneMode::Connected:
        [[fallthrough]];
    case sys::phone_modes::PhoneMode::Offline:
        updateAllowed  = true;
        showWhenLocked = true;
        break;
    }
}

bool NotificationsListPolicy::updateListAllowed() const noexcept
{
    return updateAllowed;
}

bool NotificationsListPolicy::showListWhenLocked() const noexcept
{
    return showWhenLocked;
}

A module-apps/apps-common/notifications/policies/NotificationsListPolicy.hpp => module-apps/apps-common/notifications/policies/NotificationsListPolicy.hpp +24 -0
@@ 0,0 1,24 @@
// 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 <PhoneNumber.hpp>
#include <PhoneModes/Common.hpp>

namespace notifications
{
    class NotificationsListPolicy
    {
      public:
        void updateCurrentList(sys::phone_modes::PhoneMode phoneMode,
                               bool isLocked,
                               bool lockedScreenNotificationSetting);
        bool updateListAllowed() const noexcept;
        bool showListWhenLocked() const noexcept;

      private:
        bool showWhenLocked = true;
        bool updateAllowed  = true;
    };
} // namespace notifications

M module-apps/apps-common/popups/lock-popups/PhoneLockedWindow.cpp => module-apps/apps-common/popups/lock-popups/PhoneLockedWindow.cpp +2 -1
@@ 12,7 12,8 @@
namespace gui
{
    PhoneLockedWindow::PhoneLockedWindow(app::Application *app, const std::string &name)
        : AppWindow(app, name), notificationsModel(std::make_shared<NotificationsModel>())
        : AppWindow(app, name),
          notificationsModel(std::make_shared<NotificationsModel>(NotificationsListPlacement::LockedScreen))
    {
        buildInterface();


M module-apps/tests/CMakeLists.txt => module-apps/tests/CMakeLists.txt +2 -1
@@ 1,9 1,10 @@
add_catch2_executable(
    NAME
        callback-storage-test
        apps-common
    SRCS
        tests-main.cpp
        test-CallbackStorage.cpp
        test-PhoneModesPolicies.cpp
    LIBS
        module-apps
)

A module-apps/tests/test-PhoneModesPolicies.cpp => module-apps/tests/test-PhoneModesPolicies.cpp +114 -0
@@ 0,0 1,114 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>

#include "notifications/policies/CallNotificationPolicy.hpp"
#include "notifications/policies/NotificationsListPolicy.hpp"

using namespace notifications;

TEST_CASE("Connected Mode notifications - calls policy test")
{
    CallNotificationPolicy callPolicy;

    callPolicy.updateCurrentCall(sys::phone_modes::PhoneMode::Connected);
    REQUIRE(callPolicy.isPopupAllowed());
    REQUIRE(callPolicy.isRingtoneAllowed());
    REQUIRE(!callPolicy.isNumberCheckRequired());
}

TEST_CASE("Connected Mode notifications - list policy test")
{
    NotificationsListPolicy listPolicy;

    auto phoneLocked             = true;
    auto notificationsWhenLocked = true;
    listPolicy.updateCurrentList(sys::phone_modes::PhoneMode::Connected, phoneLocked, notificationsWhenLocked);
    REQUIRE(listPolicy.updateListAllowed());
    REQUIRE(listPolicy.showListWhenLocked());

    phoneLocked             = true;
    notificationsWhenLocked = false;
    listPolicy.updateCurrentList(sys::phone_modes::PhoneMode::Connected, phoneLocked, notificationsWhenLocked);
    REQUIRE(listPolicy.updateListAllowed());
    REQUIRE(listPolicy.showListWhenLocked());

    phoneLocked             = false;
    notificationsWhenLocked = false;
    listPolicy.updateCurrentList(sys::phone_modes::PhoneMode::Connected, phoneLocked, notificationsWhenLocked);
    REQUIRE(listPolicy.updateListAllowed());
    REQUIRE(listPolicy.showListWhenLocked());

    phoneLocked             = false;
    notificationsWhenLocked = true;
    listPolicy.updateCurrentList(sys::phone_modes::PhoneMode::Connected, phoneLocked, notificationsWhenLocked);
    REQUIRE(listPolicy.updateListAllowed());
    REQUIRE(listPolicy.showListWhenLocked());
}

TEST_CASE("DoNotDisturb Mode notifications  - calls policy test")
{
    CallNotificationPolicy callPolicy;

    callPolicy.updateCurrentCall(sys::phone_modes::PhoneMode::DoNotDisturb);
    REQUIRE(!callPolicy.isPopupAllowed());
    REQUIRE(!callPolicy.isRingtoneAllowed());
    REQUIRE(callPolicy.isNumberCheckRequired());

    SECTION("Number in/not in Favourites")
    {
        auto numberInFavourites   = true;
        auto callsFromFavsAllowed = true;
        callPolicy.numberCheck(callsFromFavsAllowed, numberInFavourites);
        REQUIRE(callPolicy.isPopupAllowed());
        REQUIRE(callPolicy.isRingtoneAllowed());

        numberInFavourites   = true;
        callsFromFavsAllowed = false;
        callPolicy.numberCheck(callsFromFavsAllowed, numberInFavourites);
        REQUIRE(!callPolicy.isPopupAllowed());
        REQUIRE(!callPolicy.isRingtoneAllowed());

        numberInFavourites   = false;
        callsFromFavsAllowed = true;
        callPolicy.numberCheck(callsFromFavsAllowed, numberInFavourites);
        REQUIRE(!callPolicy.isPopupAllowed());
        REQUIRE(!callPolicy.isRingtoneAllowed());

        numberInFavourites   = false;
        callsFromFavsAllowed = false;
        callPolicy.numberCheck(callsFromFavsAllowed, numberInFavourites);
        REQUIRE(!callPolicy.isPopupAllowed());
        REQUIRE(!callPolicy.isRingtoneAllowed());
    }
}

TEST_CASE("DoNotDisturb Mode notifications  - list policy test")
{
    NotificationsListPolicy listPolicy;

    auto phoneLocked             = true;
    auto notificationsWhenLocked = true;
    listPolicy.updateCurrentList(sys::phone_modes::PhoneMode::DoNotDisturb, phoneLocked, notificationsWhenLocked);
    REQUIRE(listPolicy.updateListAllowed());
    REQUIRE(listPolicy.showListWhenLocked());

    phoneLocked             = true;
    notificationsWhenLocked = false;
    listPolicy.updateCurrentList(sys::phone_modes::PhoneMode::DoNotDisturb, phoneLocked, notificationsWhenLocked);
    REQUIRE(!listPolicy.updateListAllowed());
    REQUIRE(!listPolicy.showListWhenLocked());

    phoneLocked             = false;
    notificationsWhenLocked = false;
    listPolicy.updateCurrentList(sys::phone_modes::PhoneMode::DoNotDisturb, phoneLocked, notificationsWhenLocked);
    REQUIRE(listPolicy.updateListAllowed());
    REQUIRE(!listPolicy.showListWhenLocked());

    phoneLocked             = false;
    notificationsWhenLocked = true;
    listPolicy.updateCurrentList(sys::phone_modes::PhoneMode::DoNotDisturb, phoneLocked, notificationsWhenLocked);
    REQUIRE(listPolicy.updateListAllowed());
    REQUIRE(listPolicy.showListWhenLocked());
}

M module-services/service-appmgr/data/NotificationsChangedActionsParams.cpp => module-services/service-appmgr/data/NotificationsChangedActionsParams.cpp +7 -2
@@ 7,11 7,16 @@

using namespace app::manager::actions;

NotificationsChangedParams::NotificationsChangedParams(Notifications notifications)
    : notifications{std::move(notifications)}
NotificationsChangedParams::NotificationsChangedParams(Notifications notifications, bool showNotificationsWhenLocked)
    : notifications{std::move(notifications)}, showWhenLocked{showNotificationsWhenLocked}
{}

auto NotificationsChangedParams::getNotifications() const noexcept -> const Notifications &
{
    return notifications;
}

auto NotificationsChangedParams::showNotificationsWhenLocked() const noexcept -> bool
{
    return showWhenLocked;
}

M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +8 -6
@@ 124,10 124,12 @@ namespace app::manager
                                           const ApplicationName &_rootApplicationName)
        : Service{serviceName, {}, ApplicationManagerStackDepth},
          ApplicationManagerBase(std::move(launchers)), rootApplicationName{_rootApplicationName},
          actionsRegistry{[this](ActionEntry &action) { return handleAction(action); }}, notificationProvider(this),
          settings(std::make_unique<settings::Settings>()),
          phoneModeObserver(std::make_unique<sys::phone_modes::Observer>()),
          phoneLockHandler(locks::PhoneLockHandler(this, settings)), simLockHandler(this)
          actionsRegistry{[this](ActionEntry &action) { return handleAction(action); }},
          settings(std::make_shared<settings::Settings>()),
          phoneModeObserver(std::make_shared<sys::phone_modes::Observer>()),
          phoneLockHandler(locks::PhoneLockHandler(this, settings)),
          simLockHandler(this), notificationsConfig{phoneModeObserver, settings, phoneLockHandler},
          notificationsHandler{this, notificationsConfig}, notificationProvider{this, notificationsConfig}
    {
        autoLockTimer = sys::TimerFactory::createSingleShotTimer(
            this, autoLockTimerName, sys::timer::InfiniteTimeout, [this](sys::Timer &) { onPhoneLocked(); });


@@ 267,6 269,8 @@ namespace app::manager
        phoneModeObserver->subscribe(
            [this](sys::phone_modes::Tethering tethering) { handleTetheringChanged(tethering); });

        notificationsHandler.registerMessageHandlers();

        connect(typeid(StartAllowedMessage), [this](sys::Message *request) {
            auto msg = static_cast<StartAllowedMessage *>(request);
            handleStart(msg);


@@ 553,8 557,6 @@ namespace app::manager
        connect(typeid(VolumeChanged), convertibleToActionHandler);
        connect(typeid(CellularCallAbortedNotification), convertibleToActionHandler);
        connect(typeid(CellularRingingMessage), convertibleToActionHandler);
        connect(typeid(CellularIncominCallMessage), convertibleToActionHandler);
        connect(typeid(CellularCallerIdMessage), convertibleToActionHandler);
        connect(typeid(CellularCallActiveNotification), convertibleToActionHandler);
        connect(typeid(CellularHangupCallMessage), convertibleToActionHandler);
    }

M module-services/service-appmgr/service-appmgr/data/NotificationsChangedActionsParams.hpp => module-services/service-appmgr/service-appmgr/data/NotificationsChangedActionsParams.hpp +3 -1
@@ 17,11 17,13 @@ namespace app::manager::actions
    {
      public:
        using Notifications = std::list<std::shared_ptr<const notifications::Notification>>;
        explicit NotificationsChangedParams(Notifications notifications);
        explicit NotificationsChangedParams(Notifications notifications, bool showWhenLocked);

        [[nodiscard]] auto getNotifications() const noexcept -> const Notifications &;
        [[nodiscard]] auto showNotificationsWhenLocked() const noexcept -> bool;

      private:
        Notifications notifications;
        const bool showWhenLocked;
    };
} // namespace app::manager::actions

M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp => module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp +7 -2
@@ 35,6 35,8 @@
#include <notifications/NotificationProvider.hpp>
#include <apps-common/locks/handlers/PhoneLockHandler.hpp>
#include <apps-common/locks/handlers/SimLockHandler.hpp>
#include <apps-common/notifications/NotificationsHandler.hpp>
#include <apps-common/notifications/NotificationsConfiguration.hpp>

namespace app
{


@@ 174,18 176,21 @@ namespace app::manager
        ApplicationName rootApplicationName;
        ActionsRegistry actionsRegistry;
        OnActionPolicy actionPolicy;
        notifications::NotificationProvider notificationProvider;

        sys::TimerHandle autoLockTimer; //< auto-lock timer to count time from last user's activity.
                                        // If it reaches time defined in settings database application
                                        // manager is sending signal to Application Desktop in order to
                                        // lock screen.
        std::shared_ptr<settings::Settings> settings;
        std::unique_ptr<sys::phone_modes::Observer> phoneModeObserver;
        std::shared_ptr<sys::phone_modes::Observer> phoneModeObserver;

        locks::PhoneLockHandler phoneLockHandler;
        locks::SimLockHandler simLockHandler;

        notifications::NotificationsConfiguration notificationsConfig;
        notifications::NotificationsHandler notificationsHandler;
        notifications::NotificationProvider notificationProvider;

        void displayLanguageChanged(std::string value);
        void lockTimeChanged(std::string value);
        void inputLanguageChanged(std::string value);

M module-services/service-cellular/CellularServiceAPI.cpp => module-services/service-cellular/CellularServiceAPI.cpp +6 -0
@@ 47,6 47,12 @@ bool CellularServiceAPI::HangupCall(sys::Service *serv)
    return true;
}

bool CellularServiceAPI::DismissCall(sys::Service *serv)
{
    auto msg = std::make_shared<CellularDismissCallMessage>();
    return serv->bus.sendUnicast(msg, ServiceCellular::serviceName);
}

std::string CellularServiceAPI::GetIMSI(sys::Service *serv, bool getFullIMSINumber)
{


M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +29 -28
@@ 439,6 439,11 @@ void ServiceCellular::registerMessageHandlers()
        return sys::MessageNone{};
    });

    connect(typeid(CellularDismissCallMessage), [&](sys::Message *request) -> sys::MessagePointer {
        handleCellularDismissCallMessage(request);
        return sys::MessageNone{};
    });

    connect(typeid(db::QueryResponse), [&](sys::Message *request) -> sys::MessagePointer {
        auto msg = static_cast<db::QueryResponse *>(request);
        return handleDBQueryResponseMessage(msg);


@@ 1930,6 1935,12 @@ void ServiceCellular::handleCellularHangupCallMessage(CellularHangupCallMessage 
                      sys::BusChannel::ServiceCellularNotifications);
}

void ServiceCellular::handleCellularDismissCallMessage(sys::Message *msg)
{
    hangUpCall();
    handleCallAbortedNotification(msg);
}

auto ServiceCellular::handleDBQueryResponseMessage(db::QueryResponse *msg) -> std::shared_ptr<sys::ResponseMessage>
{
    bool responseHandled = false;


@@ 2258,37 2269,32 @@ auto ServiceCellular::handleCellularSendSMSMessage(sys::Message *msg) -> std::sh

auto ServiceCellular::handleCellularRingNotification(sys::Message *msg) -> std::shared_ptr<sys::ResponseMessage>
{
    if (isIncommingCallAllowed()) {
        if (!callManager.isIncomingCallPropagated()) {
            auto message = static_cast<CellularRingNotification *>(msg);
            bus.sendMulticast(std::make_shared<CellularIncominCallMessage>(message->getNubmer()),
                              sys::BusChannel::ServiceCellularNotifications);
            callManager.ring();
        }
        return std::make_shared<CellularResponseMessage>(true);
    if (phoneModeObserver->isTetheringOn())
        return std::make_shared<CellularResponseMessage>(hangUpCall());

    if (!callManager.isIncomingCallPropagated()) {
        auto message = static_cast<CellularRingNotification *>(msg);
        bus.sendMulticast(std::make_shared<CellularIncominCallMessage>(message->getNubmer()),
                          sys::BusChannel::ServiceCellularNotifications);
        callManager.ring();
    }
    return std::make_shared<CellularResponseMessage>(this->hangUpCall());
    return std::make_shared<CellularResponseMessage>(true);
}

auto ServiceCellular::handleCellularCallerIdNotification(sys::Message *msg) -> std::shared_ptr<sys::ResponseMessage>
{
    auto message = static_cast<CellularCallerIdNotification *>(msg);
    if (isIncommingCallAllowed()) {
        if (!callManager.isCallerInfoComplete()) {
            auto message = static_cast<CellularCallerIdNotification *>(msg);
            bus.sendMulticast(std::make_shared<CellularCallerIdMessage>(message->getNubmer()),
                              sys::BusChannel::ServiceCellularNotifications);
            callManager.completeCallerInfo();
        }
        return std::make_shared<CellularResponseMessage>(true);
    if (phoneModeObserver->isTetheringOn()) {
        tetheringCalllog.push_back(CalllogRecord{CallType::CT_MISSED, message->getNubmer()});
        return std::make_shared<CellularResponseMessage>(hangUpCallBusy());
    }
    else {
        if (phoneModeObserver->isTetheringOn()) {
            tetheringCalllog.push_back(CalllogRecord{CallType::CT_MISSED, message->getNubmer()});
            return std::make_shared<CellularResponseMessage>(hangUpCallBusy());
        }

    if (!callManager.isCallerInfoComplete()) {
        bus.sendMulticast(std::make_shared<CellularCallerIdMessage>(message->getNubmer()),
                          sys::BusChannel::ServiceCellularNotifications);
        callManager.completeCallerInfo();
    }
    return std::make_shared<CellularResponseMessage>(hangUpCall());
    return std::make_shared<CellularResponseMessage>(true);
}

auto ServiceCellular::handleCellularSetConnectionFrequencyMessage(sys::Message *msg)


@@ 2304,11 2310,6 @@ auto ServiceCellular::handleCellularSetConnectionFrequencyMessage(sys::Message *
    return std::make_shared<CellularResponseMessage>(true);
}

auto ServiceCellular::isIncommingCallAllowed() -> bool
{
    return phoneModeObserver->isInMode(sys::phone_modes::PhoneMode::Connected) && !phoneModeObserver->isTetheringOn();
}

auto ServiceCellular::hangUpCall() -> bool
{
    auto channel = cmux->get(CellularMux::Channel::Commands);

M module-services/service-cellular/service-cellular/CellularMessage.hpp => module-services/service-cellular/service-cellular/CellularMessage.hpp +11 -17
@@ 35,6 35,7 @@ class CellularMessage : public sys::DataMessage
        Notification,       ///< Async notification message
        AnswerIncomingCall, ///< Answer incoming call
        HangupCall,         ///< Hang up call
        DismissCall,        ///< Dismiss incoming call request
        Ringing,
        IncomingCall,
        CallerId,


@@ 42,7 43,7 @@ class CellularMessage : public sys::DataMessage
        PowerStateChange, ///< Change power state of the module

        ListCurrentCalls,
        SimResponse,  // Send to PIN window (show, error state, hide)
        SimResponse, // Send to PIN window (show, error state, hide)
        SetVoLTE,
        SetFlightMode,



@@ 103,7 104,7 @@ class CellularRingingMessage : public CellularMessage, public app::manager::acti
    utils::PhoneNumber::View number;
};

class CellularIncominCallMessage : public CellularMessage, public app::manager::actions::ConvertibleToAction
class CellularIncominCallMessage : public CellularMessage
{
  public:
    CellularIncominCallMessage(const utils::PhoneNumber::View &number)


@@ 116,18 117,10 @@ class CellularIncominCallMessage : public CellularMessage, public app::manager::
        : CellularMessage(Type::IncomingCall), number(utils::PhoneNumber::parse(e164number))
    {}

    [[nodiscard]] auto toAction() const -> std::unique_ptr<app::manager::ActionRequest>
    {
        return std::make_unique<app::manager::ActionRequest>(
            sender,
            app::manager::actions::HandleIncomingCall,
            std::make_unique<app::manager::actions::CallParams>(number));
    }

    utils::PhoneNumber::View number;
};

class CellularCallerIdMessage : public CellularMessage, public app::manager::actions::ConvertibleToAction
class CellularCallerIdMessage : public CellularMessage
{
  public:
    CellularCallerIdMessage(const utils::PhoneNumber::View &number) : CellularMessage(Type::CallerId), number(number)


@@ 139,12 132,6 @@ class CellularCallerIdMessage : public CellularMessage, public app::manager::act
        : CellularMessage(Type::CallerId), number(utils::PhoneNumber::parse(e164number))
    {}

    [[nodiscard]] auto toAction() const -> std::unique_ptr<app::manager::ActionRequest>
    {
        return std::make_unique<app::manager::ActionRequest>(
            sender, app::manager::actions::HandleCallerId, std::make_unique<app::manager::actions::CallParams>(number));
    }

    utils::PhoneNumber::View number;
};



@@ 591,6 578,13 @@ class CellularHangupCallMessage : public CellularMessage, public app::manager::a
    }
};

class CellularDismissCallMessage : public CellularMessage
{
  public:
    CellularDismissCallMessage() : CellularMessage(Type::DismissCall)
    {}
};

class CellularListCallsMessage : public CellularMessage
{
  public:

M module-services/service-cellular/service-cellular/CellularServiceAPI.hpp => module-services/service-cellular/service-cellular/CellularServiceAPI.hpp +1 -0
@@ 27,6 27,7 @@ namespace CellularServiceAPI

    bool AnswerIncomingCall(sys::Service *serv);
    bool HangupCall(sys::Service *serv);
    bool DismissCall(sys::Service *serv);
    /*
     * @brief Its calls sercive-cellular for selected SIM IMSI number.
     * @param serv pointer to caller service.

M module-services/service-cellular/service-cellular/ServiceCellular.hpp => module-services/service-cellular/service-cellular/ServiceCellular.hpp +1 -2
@@ 269,6 269,7 @@ class ServiceCellular : public sys::Service
    auto handleCellularAnswerIncomingCallMessage(CellularMessage *msg) -> std::shared_ptr<CellularResponseMessage>;
    auto handleCellularCallRequestMessage(CellularCallRequestMessage *msg) -> std::shared_ptr<CellularResponseMessage>;
    void handleCellularHangupCallMessage(CellularHangupCallMessage *msg);
    void handleCellularDismissCallMessage(sys::Message *msg);
    auto handleDBQueryResponseMessage(db::QueryResponse *msg) -> std::shared_ptr<sys::ResponseMessage>;
    auto handleCellularListCallsMessage(CellularMessage *msg) -> std::shared_ptr<sys::ResponseMessage>;
    auto handleDBNotificatioMessage(db::NotificationMessage *msg) -> std::shared_ptr<sys::ResponseMessage>;


@@ 310,8 311,6 @@ class ServiceCellular : public sys::Service
    auto handleCellularCallerIdNotification(sys::Message *msg) -> std::shared_ptr<sys::ResponseMessage>;
    auto handleCellularSetConnectionFrequencyMessage(sys::Message *msg) -> std::shared_ptr<sys::ResponseMessage>;

    auto isIncommingCallAllowed() -> bool;

    auto hangUpCall() -> bool;
    auto hangUpCallBusy() -> bool;


M module-services/service-db/DBServiceAPI.cpp => module-services/service-db/DBServiceAPI.cpp +13 -0
@@ 90,6 90,19 @@ auto DBServiceAPI::MatchContactByPhoneNumber(sys::Service *serv, const utils::Ph
    return std::move(contactResponse->contact);
}

auto DBServiceAPI::IsContactInFavourites(sys::Service *serv, const utils::PhoneNumber::View &numberView) -> bool
{
    auto msg = std::make_shared<DBContactNumberMessage>(numberView);

    auto ret             = serv->bus.sendUnicastSync(std::move(msg), service::name::db, DefaultTimeoutInMs);
    auto contactResponse = dynamic_cast<DBContactNumberResponseMessage *>(ret.second.get());
    if (contactResponse == nullptr || contactResponse->retCode != sys::ReturnCodes::Success) {
        LOG_ERROR("DB response error, return code: %s", c_str(ret.first));
        return false;
    }
    return contactResponse->contact->isOnFavourites();
}

auto DBServiceAPI::verifyContact(sys::Service *serv, const ContactRecord &rec)
    -> DBServiceAPI::ContactVerificationResult
{

M module-services/service-db/service-db/DBServiceAPI.hpp => module-services/service-db/service-db/DBServiceAPI.hpp +1 -0
@@ 109,6 109,7 @@ class DBServiceAPI

    static auto DBBackup(sys::Service *serv, std::string backupPath) -> bool;

    static auto IsContactInFavourites(sys::Service *serv, const utils::PhoneNumber::View &numberView) -> bool;
    /**
     * @brief Add sms via DBService interface
     *