~aleteoryx/muditaos

4f0c3c82afe3811fa66c9110038b946c6d903d68 — Piotr Tański 4 years ago 48d31b8
[EGD-6209] Tethering confirmation popup added

Provided a tethering confirmation popup.
The popup shows up once a USB cable is connected to a PC.
41 files changed, 387 insertions(+), 66 deletions(-)

M enabled_unittests
M image/assets/lang/English.json
M module-apps/Application.cpp
M module-apps/Application.hpp
M module-apps/CMakeLists.txt
M module-apps/application-alarm-clock/ApplicationAlarmClock.cpp
M module-apps/application-antenna/ApplicationAntenna.cpp
M module-apps/application-calculator/ApplicationCalculator.cpp
M module-apps/application-calendar/ApplicationCalendar.cpp
M module-apps/application-call/ApplicationCall.cpp
M module-apps/application-call/ApplicationCall.hpp
M module-apps/application-calllog/ApplicationCallLog.cpp
M module-apps/application-clock/ApplicationClock.cpp
M module-apps/application-desktop/ApplicationDesktop.cpp
M module-apps/application-meditation/ApplicationMeditation.cpp
M module-apps/application-meditation/windows/MeditationTimerWindow.cpp
M module-apps/application-messages/ApplicationMessages.cpp
M module-apps/application-music-player/ApplicationMusicPlayer.cpp
M module-apps/application-notes/ApplicationNotes.cpp
M module-apps/application-onboarding/ApplicationOnBoarding.cpp
M module-apps/application-phonebook/ApplicationPhonebook.cpp
M module-apps/application-settings-new/ApplicationSettings.cpp
M module-apps/application-settings/ApplicationSettings.cpp
A module-apps/popups/Popups.cpp
M module-apps/popups/Popups.hpp
A module-apps/popups/TetheringConfirmationPopup.cpp
A module-apps/popups/TetheringConfirmationPopup.hpp
M module-apps/popups/VolumeWindow.cpp
M module-apps/popups/VolumeWindow.hpp
M module-services/service-appmgr/model/ActionsRegistry.cpp
M module-services/service-appmgr/model/ApplicationManager.cpp
M module-services/service-appmgr/service-appmgr/Actions.hpp
M module-services/service-appmgr/service-appmgr/model/ActionsRegistry.hpp
M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp
M module-services/service-appmgr/tests/test-ActionsRegistry.cpp
M module-services/service-desktop/ServiceDesktop.cpp
M module-sys/PhoneModes/Subject.cpp
M module-sys/PhoneModes/Subject.hpp
M module-sys/SystemManager/SystemManager.cpp
M module-sys/SystemManager/SystemManager.hpp
A module-sys/SystemManager/messages/TetheringQuestionRequest.hpp
M enabled_unittests => enabled_unittests +1 -0
@@ 25,6 25,7 @@ TESTS_LIST["catch2-actions-registry-tests"]="
    Given actions registry when enqueue multiple actions at once then count handled actions;
    Given actions registry when enqueue multiple actions at once then wait for them to expire.;
    Given actions registry when enqueue multiple actions at once then process the specific ones only.;
    Process the specific actions only and skip others.;
"
#---------
TESTS_LIST["catch2-audio-test"]="

M image/assets/lang/English.json => image/assets/lang/English.json +3 -1
@@ 578,5 578,7 @@
  "app_desktop_update_in_progress": "Update in progress...",
  "app_desktop_update_ready_for_reset": "Ready for reset...",
  "app_desktop_update_success": "MuditaOS has been updated to ver. $VERSION succesfully.",
  "app_call_private_number": "Private number"
  "app_call_private_number": "Private number",
  "tethering": "Tethering",
  "tethering_enable_question": "<text>You're connected to the computer.<br />Turn tethering on?<br /><text color='9'>(some functions may be disabled)</text></text>"
}

M module-apps/Application.cpp => module-apps/Application.cpp +35 -1
@@ 8,6 8,7 @@
#include "MessageType.hpp"   // for MessageType
#include "module-sys/Timers/TimerFactory.hpp" // for Timer
#include "TopBar.hpp"
#include "popups/TetheringConfirmationPopup.hpp"
#include "Translator.hpp"                // for KeyInputSim...
#include "common_data/EventStore.hpp"    // for Battery
#include "common_data/RawKey.hpp"        // for RawKey, key...


@@ 115,6 116,20 @@ namespace app
            [&](sys::phone_modes::PhoneMode phoneMode, sys::phone_modes::Tethering tetheringMode) {
                handlePhoneModeChanged(phoneMode, tetheringMode);
            });

        addActionReceiver(app::manager::actions::ShowPopup, [this](auto &&params) {
            auto popupParams = static_cast<app::PopupRequestParams *>(params.get());
            if (const auto popupId = popupParams->getPopupId(); isPopupPermitted(popupId)) {
                switchWindow(gui::popup::resolveWindowName(popupId));
            }
            return actionHandled();
        });
        addActionReceiver(app::manager::actions::AbortPopup, [this](auto &&params) {
            auto popupParams   = static_cast<app::PopupRequestParams *>(params.get());
            const auto popupId = popupParams->getPopupId();
            abortPopup(popupId);
            return actionHandled();
        });
    }

    Application::~Application() noexcept


@@ 647,7 662,6 @@ namespace app

    void Application::messageCloseApplication(sys::Service *sender, std::string application)
    {

        auto msg = std::make_shared<AppMessage>(MessageType::AppClose);
        sender->bus.sendUnicast(msg, application);
    }


@@ 698,6 712,13 @@ namespace app
                    return std::make_unique<gui::VolumeWindow>(app, window::volume_window);
                });
                break;
            case ID::Tethering:
                windowsFactory.attach(window::tethering_confirmation_window,
                                      [](Application *app, const std::string &name) {
                                          return std::make_unique<gui::TetheringConfirmationPopup>(
                                              app, window::tethering_confirmation_window);
                                      });
                break;
            case ID::PhoneModes:
                windowsFactory.attach(window::phone_modes_window, [](Application *app, const std::string &name) {
                    return std::make_unique<gui::HomeModesWindow>(app, window::phone_modes_window);


@@ 709,6 730,19 @@ namespace app
        }
    }

    void Application::abortPopup(gui::popup::ID id)
    {
        const auto popupName = gui::popup::resolveWindowName(id);
        if (getCurrentWindow()->getName() == popupName) {
            returnToPreviousWindow();
        }
    }

    bool Application::isPopupPermitted([[maybe_unused]] gui::popup::ID popupId) const
    {
        return true;
    }

    bool Application::popToWindow(const std::string &window)
    {
        if (window == gui::name::window::no_window) {

M module-apps/Application.hpp => module-apps/Application.hpp +21 -1
@@ 31,6 31,7 @@
#include "popups/Popups.hpp"
#include "WindowsFactory.hpp"
#include "WindowsStack.hpp"
#include "Popups.hpp"

namespace app
{


@@ 176,6 177,8 @@ namespace app
        sys::MessagePointer handleSIMMessage(sys::Message *msgl);
        sys::MessagePointer handleAudioKeyMessage(sys::Message *msgl);

        virtual bool isPopupPermitted(gui::popup::ID popupId) const;

        std::list<std::unique_ptr<app::GuiTimer>> gui_timers;
        std::unordered_map<manager::actions::ActionId, OnActionReceived> receivers;



@@ 331,6 334,7 @@ namespace app

        /// Method used to attach popups windows to application
        void attachPopups(const std::vector<gui::popup::ID> &popupsList);
        void abortPopup(gui::popup::ID id);

      public:
        /// @ingrup AppWindowStack


@@ 399,7 403,7 @@ namespace app
    class ApplicationLaunchData : public manager::actions::ActionParams
    {
      public:
        ApplicationLaunchData(const app::ApplicationName &appName)
        explicit ApplicationLaunchData(const app::ApplicationName &appName)
            : manager::actions::ActionParams{"Application launch parameters"}, targetAppName{appName}
        {}



@@ 411,4 415,20 @@ namespace app
      private:
        ApplicationName targetAppName;
    };

    class PopupRequestParams : public manager::actions::ActionParams
    {
      public:
        explicit PopupRequestParams(gui::popup::ID popupId)
            : manager::actions::ActionParams{"Popup request parameters"}, popupId{popupId}
        {}

        [[nodiscard]] auto getPopupId() const noexcept
        {
            return popupId;
        }

      private:
        gui::popup::ID popupId;
    };
} /* namespace app */

M module-apps/CMakeLists.txt => module-apps/CMakeLists.txt +2 -0
@@ 28,9 28,11 @@ set( SOURCES
    "windows/NoEvents.cpp"
    "widgets/ButtonOnOff.cpp"
    "widgets/InputBox.cpp"
    "popups/Popups.cpp"
    "popups/VolumeWindow.cpp"
    "popups/WindowWithTimer.cpp"
    "popups/HomeModesWindow.cpp"
    "popups/TetheringConfirmationPopup.cpp"
    "windows/BrightnessWindow.cpp"
    "widgets/BrightnessBox.cpp"
    "widgets/ModesBox.cpp"

M module-apps/application-alarm-clock/ApplicationAlarmClock.cpp => module-apps/application-alarm-clock/ApplicationAlarmClock.cpp +1 -1
@@ 108,7 108,7 @@ namespace app
            style::alarmClock::window::name::dialogYesNo,
            [](Application *app, const std::string &name) { return std::make_unique<gui::DialogYesNo>(app, name); });

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationAlarmClock::destroyUserInterface()

M module-apps/application-antenna/ApplicationAntenna.cpp => module-apps/application-antenna/ApplicationAntenna.cpp +1 -1
@@ 178,7 178,7 @@ namespace app
            return std::make_unique<gui::AlgoParamsWindow>(app);
        });

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationAntenna::destroyUserInterface()

M module-apps/application-calculator/ApplicationCalculator.cpp => module-apps/application-calculator/ApplicationCalculator.cpp +1 -1
@@ 43,7 43,7 @@ namespace app
            return std::make_unique<gui::CalculatorMainWindow>(app, name);
        });

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationCalculator::destroyUserInterface()

M module-apps/application-calendar/ApplicationCalendar.cpp => module-apps/application-calendar/ApplicationCalendar.cpp +1 -1
@@ 143,7 143,7 @@ namespace app
            return std::make_unique<gui::EventReminderWindow>(app, event_reminder_window);
        });

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationCalendar::destroyUserInterface()

M module-apps/application-call/ApplicationCall.cpp => module-apps/application-call/ApplicationCall.cpp +6 -1
@@ 84,6 84,11 @@ namespace app
        });
    }

    bool ApplicationCall::isPopupPermitted(gui::popup::ID popupId) const
    {
        return getState() == call::State::IDLE;
    }

    //  number of seconds after end call to switch back to previous application
    const inline utils::time::Duration delayToSwitchToPreviousApp = 3;



@@ 232,7 237,7 @@ namespace app
        windowsFactory.attach(app::window::name_dialogConfirm, [](Application *app, const std::string &name) {
            return std::make_unique<gui::DialogConfirm>(app, name);
        });
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    bool ApplicationCall::showNotification(std::function<bool()> action,

M module-apps/application-call/ApplicationCall.hpp => module-apps/application-call/ApplicationCall.hpp +1 -0
@@ 86,6 86,7 @@ namespace app
        sys::MessagePointer DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) override;
        sys::ReturnCodes InitHandler() override;
        sys::ReturnCodes DeinitHandler() override;
        bool isPopupPermitted(gui::popup::ID popupId) const override;

        sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override final
        {

M module-apps/application-calllog/ApplicationCallLog.cpp => module-apps/application-calllog/ApplicationCallLog.cpp +1 -1
@@ 98,7 98,7 @@ namespace app
            return std::make_unique<gui::DialogYesNo>(app, name);
        });

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationCallLog::destroyUserInterface()

M module-apps/application-clock/ApplicationClock.cpp => module-apps/application-clock/ApplicationClock.cpp +1 -1
@@ 86,7 86,7 @@ namespace app
            return std::make_unique<gui::ClockMainWindow>(app, name);
        });

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationClock::destroyUserInterface()

M module-apps/application-desktop/ApplicationDesktop.cpp => module-apps/application-desktop/ApplicationDesktop.cpp +1 -1
@@ 493,7 493,7 @@ namespace app
            return std::make_unique<gui::MmiInternalMsgWindow>(app, desktop_mmi_internal);
        });

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationDesktop::destroyUserInterface()

M module-apps/application-meditation/ApplicationMeditation.cpp => module-apps/application-meditation/ApplicationMeditation.cpp +1 -1
@@ 53,7 53,7 @@ namespace app
            return std::make_unique<gui::PreparationTimeWindow>(app);
        });

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationMeditation::destroyUserInterface()

M module-apps/application-meditation/windows/MeditationTimerWindow.cpp => module-apps/application-meditation/windows/MeditationTimerWindow.cpp +0 -1
@@ 75,7 75,6 @@ void MeditationTimerWindow::onBeforeShow(ShowMode mode, SwitchData *data)
            timer->start();
            application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
        };

        timer->registerTimeoutCallback(onPreparation);
        timer->setCounterVisible(timerData->isCounterEnabled());
        timer->reset(timerData->getPreparationTime());

M module-apps/application-messages/ApplicationMessages.cpp => module-apps/application-messages/ApplicationMessages.cpp +1 -1
@@ 160,7 160,7 @@ namespace app
            return std::make_unique<gui::SearchResults>(app);
        });

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationMessages::destroyUserInterface()

M module-apps/application-music-player/ApplicationMusicPlayer.cpp => module-apps/application-music-player/ApplicationMusicPlayer.cpp +1 -1
@@ 69,7 69,7 @@ namespace app
            return std::make_unique<gui::MusicPlayerEmptyWindow>(app);
        });

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationMusicPlayer::destroyUserInterface()

M module-apps/application-notes/ApplicationNotes.cpp => module-apps/application-notes/ApplicationNotes.cpp +1 -1
@@ 122,7 122,7 @@ namespace app
            utils::localize.get("app_phonebook_options_title"),
            [](Application *app, const std::string &name) { return std::make_unique<gui::OptionWindow>(app, name); });

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationNotes::destroyUserInterface()

M module-apps/application-onboarding/ApplicationOnBoarding.cpp => module-apps/application-onboarding/ApplicationOnBoarding.cpp +1 -1
@@ 139,7 139,7 @@ namespace app
            return std::make_unique<gui::ChangeTimeZone>(app);
        });

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationOnBoarding::destroyUserInterface()

M module-apps/application-phonebook/ApplicationPhonebook.cpp => module-apps/application-phonebook/ApplicationPhonebook.cpp +1 -1
@@ 147,7 147,7 @@ namespace app
            return std::make_unique<gui::PhonebookNewContact>(app);
        });

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationPhonebook::destroyUserInterface()

M module-apps/application-settings-new/ApplicationSettings.cpp => module-apps/application-settings-new/ApplicationSettings.cpp +1 -2
@@ 524,8 524,6 @@ namespace app
        windowsFactory.attach(gui::window::name::quote_categories, [](Application *app, const std::string &name) {
            return std::make_unique<gui::QuoteCategoriesWindow>(app);
        });

        attachPopups({gui::popup::ID::Volume});
        windowsFactory.attach(gui::window::name::phone_modes, [](Application *app, const std::string &name) {
            return std::make_unique<gui::PhoneModesWindow>(
                app, static_cast<ApplicationSettingsNew *>(app), static_cast<ApplicationSettingsNew *>(app));


@@ 539,6 537,7 @@ namespace app
        windowsFactory.attach(gui::window::name::connection_frequency, [](Application *app, const std::string &name) {
            return std::make_unique<gui::ConnectionFrequencyWindow>(app, static_cast<ApplicationSettingsNew *>(app));
        });
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering});
    }

    void ApplicationSettingsNew::destroyUserInterface()

M module-apps/application-settings/ApplicationSettings.cpp => module-apps/application-settings/ApplicationSettings.cpp +1 -1
@@ 163,7 163,7 @@ namespace app
                                  });
        }

        attachPopups({gui::popup::ID::Volume, gui::popup::ID::PhoneModes});
        attachPopups({gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes});
    }

    void ApplicationSettings::destroyUserInterface()

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

#include "Popups.hpp"

namespace gui::popup
{
    std::string resolveWindowName(ID id)
    {
        switch (id) {
        case ID::Volume:
            return gui::popup::window::volume_window;
        case ID::PhoneModes:
            return gui::popup::window::phone_modes_window;
        case ID::Brightness:
            return gui::popup::window::brightness_window;
        case ID::Tethering:
            return gui::popup::window::tethering_confirmation_window;
        }
        return {};
    }
} // namespace gui::popup

M module-apps/popups/Popups.hpp => module-apps/popups/Popups.hpp +4 -0
@@ 17,6 17,7 @@ namespace gui
            Volume,
            PhoneModes,
            Brightness,
            Tethering
        };

        namespace window


@@ 24,6 25,9 @@ namespace gui
            inline constexpr auto volume_window      = "VolumePopup";
            inline constexpr auto phone_modes_window = "PhoneModesPopup";
            inline constexpr auto brightness_window  = "BrightnessPopup";
            inline constexpr auto tethering_confirmation_window = "TetheringConfirmationPopup";
        } // namespace window

        std::string resolveWindowName(ID id);
    }     // namespace popup
} // namespace gui

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

#include "TetheringConfirmationPopup.hpp"
#include "DialogMetadataMessage.hpp"
#include "Application.hpp"

#include <module-sys/SystemManager/messages/TetheringQuestionRequest.hpp>
#include <service-appmgr/Controller.hpp>

namespace gui
{
    TetheringConfirmationPopup::TetheringConfirmationPopup(app::Application *app, const std::string &name)
        : DialogYesNo{app, name}
    {}

    void TetheringConfirmationPopup::onBeforeShow(ShowMode mode, [[maybe_unused]] SwitchData *data)
    {
        DialogMetadata metadata;
        metadata.title  = utils::translateI18("tethering");
        metadata.text   = utils::translateI18("tethering_enable_question");
        metadata.icon   = "info_big_circle_W_G";
        metadata.action = [this]() {
            application->bus.sendUnicast(std::make_shared<sys::TetheringEnabledResponse>(),
                                         service::name::system_manager);
            app::manager::Controller::sendAction(application, app::manager::actions::Home);
            return true;
        };
        auto msg = std::make_unique<DialogMetadataMessage>(std::move(metadata));
        DialogYesNo::onBeforeShow(mode, msg.get());
    }
} // namespace gui

A module-apps/popups/TetheringConfirmationPopup.hpp => module-apps/popups/TetheringConfirmationPopup.hpp +19 -0
@@ 0,0 1,19 @@
// 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 <string>

#include "Dialog.hpp"

namespace gui
{
    class TetheringConfirmationPopup : public DialogYesNo
    {
      public:
        TetheringConfirmationPopup(app::Application *app, const std::string &name);

        void onBeforeShow(ShowMode mode, SwitchData *data) override;
    };
}; // namespace gui

M module-apps/popups/VolumeWindow.cpp => module-apps/popups/VolumeWindow.cpp +1 -0
@@ 3,6 3,7 @@

#include <module-gui/gui/input/InputEvent.hpp>
#include <i18n/i18n.hpp>
#include "Application.hpp"
#include "VolumeWindow.hpp"
#include "popups/data/PopupData.hpp"


M module-apps/popups/VolumeWindow.hpp => module-apps/popups/VolumeWindow.hpp +11 -2
@@ 3,9 3,12 @@

#pragma once

#include "Application.hpp"
#include "widgets/BarGraph.hpp"
#include "module-apps/widgets/BarGraph.hpp"
#include "WindowWithTimer.hpp"

#include <module-audio/Audio/AudioCommon.hpp>
#include <module-audio/Audio/Profiles/Profile.hpp>

#include <functional>

namespace style::window::volume


@@ 23,6 26,12 @@ namespace style::window::volume
    } // namespace bar

} // namespace style::window::volume

namespace app
{
    class Application;
} // namespace app

namespace gui
{
    class VolumeWindow : public WindowWithTimer

M module-services/service-appmgr/model/ActionsRegistry.cpp => module-services/service-appmgr/model/ActionsRegistry.cpp +15 -3
@@ 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-appmgr/model/ActionsRegistry.hpp>


@@ 70,13 70,25 @@ namespace app::manager

    void ActionsRegistry::notifyAboutNextAction()
    {
        for (auto &action : actions) {
            if (const auto handled = nextActionReady(action); handled) {
        for (auto it = actions.begin(); it != actions.end();) {
            auto &action = *it;
            switch (const auto status = nextActionReady(action); status) {
            case ActionProcessStatus::Accepted: {
                LOG_INFO(
                    "Pending action %s to %s", magic_enum::enum_name(action.actionId).data(), action.target.c_str());
                actionInProgress = &action;
                return;
            }
            case ActionProcessStatus::Dropped: {
                // Drop the action, so it'll not be processed again.
                it = actions.erase(it);
                break;
            }
            case ActionProcessStatus::Skipped:
                // Skip the action and check the next one.
                ++it;
                break;
            }
        }
    }


M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +38 -15
@@ 10,6 10,7 @@
#include <module-sys/Timers/TimerFactory.hpp>
#include <SystemManager/SystemManager.hpp>
#include <SystemManager/messages/SystemManagerMessage.hpp>
#include <SystemManager/messages/TetheringQuestionRequest.hpp>
#include <application-call/ApplicationCall.hpp>
#include <application-special-input/ApplicationSpecialInput.hpp>
#include <application-desktop/ApplicationDesktop.hpp>


@@ 331,7 332,7 @@ namespace app::manager
        });
        connect(typeid(ActionHandledResponse), [this](sys::Message *response) {
            actionsRegistry.finished();
            return nullptr;
            return sys::MessageNone{};
        });
        connect(typeid(GetCurrentDisplayLanguageRequest), [&](sys::Message *request) {
            return std::make_shared<GetCurrentDisplayLanguageResponse>(utils::localize.getDisplayLanguage());


@@ 364,6 365,8 @@ namespace app::manager
        connect(typeid(sdesktop::passcode::ScreenPasscodeRequest), convertibleToActionHandler);
        connect(typeid(CellularSMSRejectedByOfflineNotification), convertibleToActionHandler);
        connect(typeid(CellularCallRejectedByOfflineNotification), convertibleToActionHandler);
        connect(typeid(sys::TetheringQuestionRequest), convertibleToActionHandler);
        connect(typeid(sys::TetheringQuestionAbort), convertibleToActionHandler);
    }

    sys::ReturnCodes ApplicationManager::SwitchPowerModeHandler(const sys::ServicePowerMode mode)


@@ 467,15 470,17 @@ namespace app::manager
            LOG_INFO("No focused application at the moment. Starting new application...");
            onApplicationSwitch(*app, std::move(msg->getData()), msg->getWindow());
            startApplication(*app);
            return true;
            return false;
        }

        onApplicationSwitch(*app, std::move(msg->getData()), msg->getWindow());
        if (app->name() == currentlyFocusedApp->name()) {
            LOG_WARN("Failed to return to currently focused application.");
            // Switch window only.
            app::Application::messageSwitchApplication(
                this, app->name(), app->switchWindow, std::move(app->switchData));
            return false;
        }

        onApplicationSwitch(*app, std::move(msg->getData()), msg->getWindow());
        const bool isFocusedAppCloseable =
            closeCurrentlyFocusedApp && currentlyFocusedApp->closeable() && !currentlyFocusedApp->blockClosing;
        requestApplicationClose(*currentlyFocusedApp, isFocusedAppCloseable);


@@ 514,48 519,65 @@ namespace app::manager
        actionsRegistry.enqueue(std::move(entry));
    }

    bool ApplicationManager::handleAction(ActionEntry &action)
    ActionProcessStatus ApplicationManager::handleAction(ActionEntry &action)
    {
        switch (action.actionId) {
        case actions::Home:
            return handleHomeAction(action);
        case actions::Launch:
            return handleLaunchAction(action);
        case actions::ShowPopup:
            [[fallthrough]];
        case actions::AbortPopup:
            return handlePopupAction(action);
        default:
            return handleCustomAction(action);
        }
    }

    auto ApplicationManager::handleHomeAction(ActionEntry &action) -> bool
    auto ApplicationManager::handleHomeAction(ActionEntry &action) -> ActionProcessStatus
    {
        action.setTargetApplication(rootApplicationName);
        SwitchRequest switchRequest(ServiceName, rootApplicationName, gui::name::window::main_window, nullptr);
        return handleSwitchApplication(&switchRequest);
        return handleSwitchApplication(&switchRequest) ? ActionProcessStatus::Accepted : ActionProcessStatus::Dropped;
    }

    auto ApplicationManager::handleLaunchAction(ActionEntry &action) -> bool
    auto ApplicationManager::handleLaunchAction(ActionEntry &action) -> ActionProcessStatus
    {
        auto launchParams = static_cast<ApplicationLaunchData *>(action.params.get());
        auto targetApp    = getApplication(launchParams->getTargetApplicationName());
        if (targetApp == nullptr || !targetApp->handles(actions::Launch)) {
            return false;
            return ActionProcessStatus::Dropped;
        }

        action.setTargetApplication(targetApp->name());
        SwitchRequest switchRequest(ServiceName, targetApp->name(), gui::name::window::main_window, nullptr);
        return handleSwitchApplication(&switchRequest);
        return handleSwitchApplication(&switchRequest) ? ActionProcessStatus::Accepted : ActionProcessStatus::Dropped;
    }

    auto ApplicationManager::handleCustomAction(ActionEntry &action) -> bool
    auto ApplicationManager::handlePopupAction(ActionEntry &action) -> ActionProcessStatus
    {
        auto targetApp = getFocusedApplication();
        if (targetApp == nullptr) {
            return ActionProcessStatus::Skipped;
        }
        action.setTargetApplication(targetApp->name());

        auto &params = action.params;
        app::Application::requestAction(this, targetApp->name(), action.actionId, std::move(params));
        return ActionProcessStatus::Accepted;
    }

    auto ApplicationManager::handleCustomAction(ActionEntry &action) -> ActionProcessStatus
    {
        const auto actionHandlers = applications.findByAction(action.actionId);
        if (actionHandlers.empty()) {
            LOG_ERROR("No applications handling action #%d.", action.actionId);
            return false;
            return ActionProcessStatus::Dropped;
        }
        if (actionHandlers.size() > 1) {
            LOG_FATAL("Choosing amongst multiple action handler applications is not yet implemented.");
            return false;
            return ActionProcessStatus::Dropped;
        }

        const auto targetApp = actionHandlers.front();


@@ 565,11 587,12 @@ namespace app::manager
            const auto focusedAppClose = !(actionParams && actionParams->disableAppClose);
            SwitchRequest switchRequest(
                ServiceName, targetApp->name(), targetApp->switchWindow, std::move(targetApp->switchData));
            return handleSwitchApplication(&switchRequest, focusedAppClose);
            return handleSwitchApplication(&switchRequest, focusedAppClose) ? ActionProcessStatus::Accepted
                                                                            : ActionProcessStatus::Skipped;
        }

        app::Application::requestAction(this, targetApp->name(), action.actionId, std::move(actionParams));
        return true;
        return ActionProcessStatus::Accepted;
    }

    auto ApplicationManager::handleSwitchBack(SwitchBackRequest *msg) -> bool

M module-services/service-appmgr/service-appmgr/Actions.hpp => module-services/service-appmgr/service-appmgr/Actions.hpp +2 -0
@@ 60,6 60,8 @@ namespace app::manager
            DisplayLogoAtExit,
            SMSRejectedByOfflineNotification,
            CallRejectedByOfflineNotification,
            ShowPopup,
            AbortPopup,
            UserAction // The last enumerator in the Action enum.
                       // All user-defined actions shall have values greater than UserAction.
                       // All system-wide actions shall have values lesser than UserAction.

M module-services/service-appmgr/service-appmgr/model/ActionsRegistry.hpp => module-services/service-appmgr/service-appmgr/model/ActionsRegistry.hpp +9 -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


@@ 38,10 38,17 @@ namespace app::manager
    bool operator==(const ActionEntry &lhs, const ActionEntry &rhs) noexcept;
    bool operator!=(const ActionEntry &lhs, const ActionEntry &rhs) noexcept;

    enum class ActionProcessStatus
    {
        Accepted,
        Skipped,
        Dropped
    };

    class ActionsRegistry
    {
      public:
        using OnNextActionReadyCallback = std::function<bool(ActionEntry &action)>;
        using OnNextActionReadyCallback = std::function<ActionProcessStatus(ActionEntry &action)>;

        explicit ActionsRegistry(OnNextActionReadyCallback &&callback);


M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp => module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp +5 -4
@@ 116,11 116,12 @@ namespace app::manager

        // Message handlers
        void registerMessageHandlers();
        bool handleAction(ActionEntry &action);
        auto handleAction(ActionEntry &action) -> ActionProcessStatus;
        void handleActionRequest(ActionRequest *actionMsg);
        auto handleHomeAction(ActionEntry &action) -> bool;
        auto handleLaunchAction(ActionEntry &action) -> bool;
        auto handleCustomAction(ActionEntry &action) -> bool;
        auto handleHomeAction(ActionEntry &action) -> ActionProcessStatus;
        auto handleLaunchAction(ActionEntry &action) -> ActionProcessStatus;
        auto handlePopupAction(ActionEntry &action) -> ActionProcessStatus;
        auto handleCustomAction(ActionEntry &action) -> ActionProcessStatus;
        auto handleSwitchApplication(SwitchRequest *msg, bool closeCurrentlyFocusedApp = true) -> bool;
        auto handleCloseConfirmation(CloseConfirmation *msg) -> bool;
        auto handleSwitchConfirmation(SwitchConfirmation *msg) -> bool;

M module-services/service-appmgr/tests/test-ActionsRegistry.cpp => module-services/service-appmgr/tests/test-ActionsRegistry.cpp +41 -9
@@ 14,7 14,7 @@ TEST_CASE("Given actions registry when initialise it incorrectly then verify")

TEST_CASE("Given empty actions registry when verify its state then no pending actions")
{
    auto nextActionCallback = [](ActionEntry & /*entry*/) { return true; };
    auto nextActionCallback = [](ActionEntry & /*entry*/) { return ActionProcessStatus::Accepted; };
    ActionsRegistry registry{std::move(nextActionCallback)};

    REQUIRE(!registry.hasPendingAction());


@@ 22,7 22,7 @@ TEST_CASE("Given empty actions registry when verify its state then no pending ac

TEST_CASE("Given actions registry when enqueue then check pending action")
{
    auto nextActionCallback = [](ActionEntry & /*entry*/) { return true; };
    auto nextActionCallback = [](ActionEntry & /*entry*/) { return ActionProcessStatus::Accepted; };
    ActionsRegistry registry{std::move(nextActionCallback)};

    registry.enqueue(ActionEntry{actions::Launch});


@@ 35,7 35,7 @@ TEST_CASE("Given actions registry when enqueue then check if action was processe
    bool handled            = false;
    auto nextActionCallback = [&handled](ActionEntry & /*entry*/) {
        handled = true;
        return true;
        return ActionProcessStatus::Accepted;
    };
    ActionsRegistry registry{std::move(nextActionCallback)};



@@ 45,7 45,7 @@ TEST_CASE("Given actions registry when enqueue then check if action was processe

TEST_CASE("Given actions registry when finished queued action then no pending actions")
{
    auto nextActionCallback = [](ActionEntry & /*entry*/) { return true; };
    auto nextActionCallback = [](ActionEntry & /*entry*/) { return ActionProcessStatus::Accepted; };
    ActionsRegistry registry{std::move(nextActionCallback)};

    registry.enqueue(ActionEntry{actions::Launch});


@@ 58,7 58,7 @@ TEST_CASE("Given actions registry when enqueue multiple actions sequentially the
    int counter             = 0;
    auto nextActionCallback = [&counter](ActionEntry & /*entry*/) {
        ++counter;
        return true;
        return ActionProcessStatus::Accepted;
    };
    ActionsRegistry registry{std::move(nextActionCallback)};



@@ 79,7 79,7 @@ TEST_CASE("Given actions registry when enqueue multiple actions at once then cou
    int counter             = 0;
    auto nextActionCallback = [&counter](ActionEntry & /*entry*/) {
        ++counter;
        return true;
        return ActionProcessStatus::Accepted;
    };
    ActionsRegistry registry{std::move(nextActionCallback)};



@@ 104,7 104,7 @@ TEST_CASE("Given actions registry when enqueue multiple actions at once then cou

TEST_CASE("Given actions registry when enqueue multiple actions at once then wait for them to expire.")
{
    auto nextActionCallback = [](ActionEntry & /*entry*/) { return true; };
    auto nextActionCallback = [](ActionEntry & /*entry*/) { return ActionProcessStatus::Accepted; };
    ActionsRegistry registry{std::move(nextActionCallback)};

    registry.enqueue(ActionEntry{actions::Launch});


@@ 126,9 126,9 @@ TEST_CASE("Given actions registry when enqueue multiple actions at once then pro
    auto nextActionCallback = [&counter](ActionEntry &entry) {
        if (entry.actionId == actions::UserAction) {
            ++counter;
            return true;
            return ActionProcessStatus::Accepted;
        }
        return false;
        return ActionProcessStatus::Skipped;
    };
    ActionsRegistry registry{std::move(nextActionCallback)};



@@ 143,3 143,35 @@ TEST_CASE("Given actions registry when enqueue multiple actions at once then pro
    REQUIRE(counter == 1);
    REQUIRE(!registry.hasPendingAction());
}

TEST_CASE("Process the specific actions only and skip others.")
{
    int acceptedCounter     = 0;
    int skippedCounter      = 0;
    int droppedCounter      = 0;
    auto nextActionCallback = [&acceptedCounter, &skippedCounter, &droppedCounter](ActionEntry &entry) {
        if (entry.actionId == actions::UserAction) {
            ++acceptedCounter;
            return ActionProcessStatus::Accepted;
        }
        if (entry.actionId == actions::Home) {
            ++skippedCounter;
            return ActionProcessStatus::Skipped;
        }
        ++droppedCounter;
        return ActionProcessStatus::Dropped;
    };
    ActionsRegistry registry{std::move(nextActionCallback)};

    registry.enqueue(ActionEntry{actions::Launch});
    REQUIRE(droppedCounter == 1);
    REQUIRE(!registry.hasPendingAction());

    registry.enqueue(ActionEntry{actions::Home});
    REQUIRE(skippedCounter == 1);
    REQUIRE(!registry.hasPendingAction());

    registry.enqueue(ActionEntry{actions::UserAction});
    registry.finished();
    REQUIRE(acceptedCounter == 1);
}

M module-services/service-desktop/ServiceDesktop.cpp => module-services/service-desktop/ServiceDesktop.cpp +3 -0
@@ 211,6 211,9 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
    });

    connect(sdesktop::usb::USBConfigured(), [&](sys::Message *msg) {
        bus.sendUnicast(std::make_shared<sys::TetheringStateRequest>(sys::phone_modes::Tethering::On),
                        service::name::system_manager);

        if (!usbSecurityModel->isSecurityEnabled()) {
            LOG_INFO("Endpoint security disabled.");
            return sys::MessageNone{};

M module-sys/PhoneModes/Subject.cpp => module-sys/PhoneModes/Subject.cpp +12 -6
@@ 19,18 19,22 @@ namespace sys::phone_modes
        }
    }

    void Subject::setMode(PhoneMode _phoneMode, Tethering _tetheringMode)
    bool Subject::setMode(PhoneMode _phoneMode, Tethering _tetheringMode)
    {
        if (const bool changed = changePhoneMode(_phoneMode) || changeTetheringMode(_tetheringMode); changed) {
        const bool changed = changePhoneMode(_phoneMode) || changeTetheringMode(_tetheringMode);
        if (changed) {
            notifyChange();
        }
        return changed;
    }

    void Subject::setPhoneMode(PhoneMode mode)
    bool Subject::setPhoneMode(PhoneMode mode)
    {
        if (const auto changed = changePhoneMode(mode); changed) {
        const auto changed = changePhoneMode(mode);
        if (changed) {
            notifyChange();
        }
        return changed;
    }

    bool Subject::changePhoneMode(PhoneMode mode) noexcept


@@ 38,11 42,13 @@ namespace sys::phone_modes
        return std::exchange(phoneMode, mode) != mode;
    }

    void Subject::setTetheringMode(Tethering mode)
    bool Subject::setTetheringMode(Tethering mode)
    {
        if (const auto changed = changeTetheringMode(mode); changed) {
        const auto changed = changeTetheringMode(mode);
        if (changed) {
            notifyChange();
        }
        return changed;
    }

    bool Subject::changeTetheringMode(Tethering mode) noexcept

M module-sys/PhoneModes/Subject.hpp => module-sys/PhoneModes/Subject.hpp +21 -3
@@ 17,9 17,27 @@ namespace sys::phone_modes
      public:
        explicit Subject(Service *owner);

        void setMode(PhoneMode _phoneMode, Tethering _tetheringMode);
        void setPhoneMode(PhoneMode mode);
        void setTetheringMode(Tethering mode);
        /**
         * Sets phone and tethering modes
         * @param _phoneMode        Phone mode
         * @param _tetheringMode    Tethering mode
         * @return true if modes changed, false otherwise
         */
        bool setMode(PhoneMode _phoneMode, Tethering _tetheringMode);

        /**
         * Sets the phone mode
         * @param mode  Phone mode
         * @return true if phone mode changed, false otherwise
         */
        bool setPhoneMode(PhoneMode mode);

        /**
         * Sets the tethering mode
         * @param mode  Tethering mode
         * @return true if tethering mode changed, false otherwise
         */
        bool setTetheringMode(Tethering mode);

      private:
        void notifyChange();

M module-sys/SystemManager/SystemManager.cpp => module-sys/SystemManager/SystemManager.cpp +23 -1
@@ 27,6 27,7 @@
#include "messages/RequestCpuFrequencyMessage.hpp"
#include "messages/PhoneModeRequest.hpp"
#include "messages/TetheringStateRequest.hpp"
#include "messages/TetheringQuestionRequest.hpp"
#include <time/ScopedTime.hpp>
#include "Timers/TimerFactory.hpp"
#include <service-appmgr/StartupType.hpp>


@@ 603,6 604,11 @@ namespace sys
            return sys::MessageNone{};
        });

        connect(typeid(TetheringEnabledResponse), [this](sys::Message *message) -> sys::MessagePointer {
            auto response = static_cast<TetheringEnabledResponse *>(message);
            return enableTethering(response);
        });

        deviceManager->RegisterNewDevice(powerManager->getExternalRamDevice());

        return ReturnCodes::Success;


@@ 768,7 774,23 @@ namespace sys
    MessagePointer SystemManager::handleTetheringStateRequest(TetheringStateRequest *request)
    {
        LOG_INFO("Tethering state change requested");
        phoneModeSubject->setTetheringMode(request->getTetheringState());
        if (const auto requestedState = request->getTetheringState(); requestedState == phone_modes::Tethering::On) {
            bus.sendUnicast(std::make_shared<TetheringQuestionRequest>(),
                            app::manager::ApplicationManager::ServiceName);
        }
        else {
            if (const auto tetheringChanged = phoneModeSubject->setTetheringMode(phone_modes::Tethering::Off);
                !tetheringChanged) {
                bus.sendUnicast(std::make_shared<TetheringQuestionAbort>(),
                                app::manager::ApplicationManager::ServiceName);
            }
        }
        return MessageNone{};
    }

    MessagePointer SystemManager::enableTethering([[maybe_unused]] TetheringEnabledResponse *response)
    {
        phoneModeSubject->setTetheringMode(phone_modes::Tethering::On);
        return MessageNone{};
    }


M module-sys/SystemManager/SystemManager.hpp => module-sys/SystemManager/SystemManager.hpp +2 -0
@@ 41,6 41,7 @@ namespace sys

    class PhoneModeRequest; // Forward declaration
    class TetheringStateRequest; // Forward declaration
    class TetheringEnabledResponse; // Forward declaration

    enum class Code
    {


@@ 191,6 192,7 @@ namespace sys

        MessagePointer handlePhoneModeRequest(PhoneModeRequest *request);
        MessagePointer handleTetheringStateRequest(TetheringStateRequest *request);
        MessagePointer enableTethering(TetheringEnabledResponse *response);

        void batteryCriticalLevelAction(bool charging);
        void batteryNormalLevelAction();

A module-sys/SystemManager/messages/TetheringQuestionRequest.hpp => module-sys/SystemManager/messages/TetheringQuestionRequest.hpp +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

#pragma once

#include <module-sys/Service/Message.hpp>

#include <service-appmgr/Actions.hpp>
#include <service-appmgr/messages/ActionRequest.hpp>

namespace sys
{
    class TetheringQuestionRequest : public sys::DataMessage, public app::manager::actions::ConvertibleToAction
    {
      public:
        TetheringQuestionRequest() : sys::DataMessage(MessageType::MessageTypeUninitialized)
        {}

        std::unique_ptr<app::manager::ActionRequest> toAction() const override
        {
            auto params = std::make_unique<app::PopupRequestParams>(gui::popup::ID::Tethering);
            return std::make_unique<app::manager::ActionRequest>(
                sender, app::manager::actions::ShowPopup, std::move(params));
        }
    };

    class TetheringQuestionAbort : public sys::DataMessage, public app::manager::actions::ConvertibleToAction
    {
      public:
        TetheringQuestionAbort() : sys::DataMessage(MessageType::MessageTypeUninitialized)
        {}

        std::unique_ptr<app::manager::ActionRequest> toAction() const override
        {
            auto params = std::make_unique<app::PopupRequestParams>(gui::popup::ID::Tethering);
            return std::make_unique<app::manager::ActionRequest>(
                sender, app::manager::actions::AbortPopup, std::move(params));
        }
    };

    class TetheringEnabledResponse : public sys::ResponseMessage
    {};
} // namespace sys