~aleteoryx/muditaos

b7f4ba33e3f5a35eeb431b67d7fdd3a304a32543 — Mateusz Piesta 4 years ago e8fd36c
[BH-765] Turn off the device

From now on, Bell can be switched off from:
- settings
- by back long press(5s) from any screen
54 files changed, 1145 insertions(+), 116 deletions(-)

M image/assets/lang/English.json
M module-apps/apps-common/popups/Popups.cpp
M module-apps/apps-common/popups/Popups.hpp
M module-apps/apps-common/popups/presenter/PowerOffPresenter.hpp
M module-bsp/hal/include/hal/battery_charger/AbstractBatteryCharger.hpp
M products/BellHybrid/apps/Application.cpp
M products/BellHybrid/apps/application-bell-alarm/ApplicationBellAlarm.cpp
M products/BellHybrid/apps/application-bell-alarm/windows/BellAlarmWindow.cpp
M products/BellHybrid/apps/application-bell-background-sounds/ApplicationBellBackgroundSounds.cpp
M products/BellHybrid/apps/application-bell-main/ApplicationBellMain.cpp
M products/BellHybrid/apps/application-bell-onboarding/ApplicationBellOnBoarding.cpp
M products/BellHybrid/apps/application-bell-onboarding/CMakeLists.txt
M products/BellHybrid/apps/application-bell-onboarding/windows/OnBoardingFinalizeWindow.hpp
M products/BellHybrid/apps/application-bell-powernap/ApplicationBellPowerNap.cpp
M products/BellHybrid/apps/application-bell-settings/ApplicationBellSettings.cpp
M products/BellHybrid/apps/application-bell-settings/CMakeLists.txt
M products/BellHybrid/apps/application-bell-settings/include/application-bell-settings/ApplicationBellSettings.hpp
M products/BellHybrid/apps/application-bell-settings/windows/BellSettingsFrontlight.cpp
D products/BellHybrid/apps/application-bell-settings/windows/BellSettingsTurnOffWindow.cpp
M products/BellHybrid/apps/application-bell-settings/windows/BellSettingsWindow.cpp
M products/BellHybrid/apps/application-bell-settings/windows/advanced/BellSettingsTimeUnitsWindow.cpp
M products/BellHybrid/apps/application-bell-settings/windows/alarm_settings/BellSettingsAlarmSettingsSnoozeWindow.cpp
M products/BellHybrid/apps/application-bell-settings/windows/alarm_settings/BellSettingsAlarmSettingsWindow.cpp
M products/BellHybrid/apps/application-bell-settings/windows/alarm_settings/BellSettingsPrewakeUpWindow.cpp
M products/BellHybrid/apps/common/CMakeLists.txt
A products/BellHybrid/apps/common/include/common/BellPowerOffPresenter.hpp
M products/BellHybrid/apps/common/include/common/data/StyleCommon.hpp
R products/BellHybrid/apps/{application-bell-settings/windows/BellSettingsTurnOffWindow => common/include/common/popups/BellTurnOffOptionWindow}.hpp
R products/BellHybrid/apps/common/include/common/{ => windows}/BellFinishedWindow.hpp
A products/BellHybrid/apps/common/include/common/windows/BellTurnOffWindow.hpp
R products/BellHybrid/apps/{application-bell-onboarding/windows/OnBoardingWelcomeWindow => common/include/common/windows/BellWelcomeWindow}.hpp
A products/BellHybrid/apps/common/src/BellPowerOffPresenter.cpp
A products/BellHybrid/apps/common/src/popups/BellTurnOffOptionWindow.cpp
R products/BellHybrid/apps/common/src/{ => windows}/BellFinishedWindow.cpp
A products/BellHybrid/apps/common/src/windows/BellTurnOffWindow.cpp
R products/BellHybrid/apps/{application-bell-onboarding/windows/OnBoardingWelcomeWindow => common/src/windows/BellWelcomeWindow}.cpp
M products/BellHybrid/keymap/include/keymap/KeyMap.hpp
M products/BellHybrid/services/appmgr/CMakeLists.txt
A products/BellHybrid/services/appmgr/include/appmgr/messages/PowerOffPopupRequestParams.hpp
M products/BellHybrid/services/evtmgr/CMakeLists.txt
M products/BellHybrid/services/evtmgr/EventManager.cpp
M products/BellHybrid/services/evtmgr/include/evtmgr/EventManager.hpp
A products/BellHybrid/services/evtmgr/internal/key_sequences/AbstractKeySequence.hpp
A products/BellHybrid/services/evtmgr/internal/key_sequences/AlarmActivateSequence.hpp
A products/BellHybrid/services/evtmgr/internal/key_sequences/AlarmDeactivateSequence.hpp
A products/BellHybrid/services/evtmgr/internal/key_sequences/KeySequenceMgr.cpp
A products/BellHybrid/services/evtmgr/internal/key_sequences/KeySequenceMgr.hpp
A products/BellHybrid/services/evtmgr/internal/key_sequences/LongPressSequence.cpp
A products/BellHybrid/services/evtmgr/internal/key_sequences/LongPressSequence.hpp
A products/BellHybrid/services/evtmgr/internal/key_sequences/PowerOffSequence.hpp
A products/BellHybrid/services/evtmgr/internal/key_sequences/ReleaseSequence.hpp
A products/BellHybrid/services/evtmgr/tests/CMakeLists.txt
A products/BellHybrid/services/evtmgr/tests/unittest_KeySequenceMgr.cpp
M products/BellHybrid/sys/CMakeLists.txt
M image/assets/lang/English.json => image/assets/lang/English.json +2 -0
@@ 624,6 624,8 @@
  "app_bell_powernap_session_ended_message": "<text>Hello!<br />Rise & shine</text>",
  "app_bell_background_sounds_timer_off": "OFF",
  "app_bell_background_sounds_timer_title": "Timer",
  "app_bell_turn_off_question": "Turn off the device?",
  "app_bell_goodbye": "Goodbye",
  "app_bell_greeting_msg": [
    "<text>Good Morning!<br />It's a Beautiful Day!</text>",
    "<text>Happy Day!<br />It's a brand new morning!</text>",

M module-apps/apps-common/popups/Popups.cpp => module-apps/apps-common/popups/Popups.cpp +2 -0
@@ 38,6 38,8 @@ namespace gui::popup
            return gui::popup::window::alarm_deactivated_window;
        case ID::Alarm:
            return gui::popup::window::alarm_window;
        case ID::PowerOff:
            return gui::popup::window::power_off_window;
        }

        return {};

M module-apps/apps-common/popups/Popups.hpp => module-apps/apps-common/popups/Popups.hpp +2 -1
@@ 25,7 25,8 @@ namespace gui
            SimNotReady,
            AlarmActivated,
            AlarmDeactivated,
            Alarm
            Alarm,
            PowerOff,
        };

        namespace window

M module-apps/apps-common/popups/presenter/PowerOffPresenter.hpp => module-apps/apps-common/popups/presenter/PowerOffPresenter.hpp +9 -2
@@ 10,11 10,18 @@ namespace app

namespace gui
{
    class PowerOffPresenter
    class AbstractPowerOffPresenter
    {
      public:
        virtual ~AbstractPowerOffPresenter() = default;
        virtual void powerOff()              = 0;
    };

    class PowerOffPresenter : public AbstractPowerOffPresenter
    {
      public:
        PowerOffPresenter(app::ApplicationCommon *app);
        void powerOff();
        void powerOff() override;

      private:
        app::ApplicationCommon *application;

M module-bsp/hal/include/hal/battery_charger/AbstractBatteryCharger.hpp => module-bsp/hal/include/hal/battery_charger/AbstractBatteryCharger.hpp +1 -0
@@ 7,6 7,7 @@
#include <queue.h>

#include <memory>
#include <cstdint>

namespace hal::battery
{

M products/BellHybrid/apps/Application.cpp => products/BellHybrid/apps/Application.cpp +18 -1
@@ 5,16 5,19 @@

#include <common/models/AlarmModel.hpp>

#include <common/BellPowerOffPresenter.hpp>
#include <common/popups/presenter/AlarmActivatedPresenter.hpp>
#include <common/popups/AlarmActivatedWindow.hpp>
#include <common/popups/AlarmDeactivatedWindow.hpp>
#include <common/popups/BellTurnOffOptionWindow.hpp>
#include <common/windows/BellTurnOffWindow.hpp>
#include <common/windows/BellWelcomeWindow.hpp>

namespace app
{
    void Application::attachPopups(const std::vector<gui::popup::ID> &popupsList)
    {
        using namespace gui::popup;
        app::ApplicationCommon::attachPopups(popupsList);
        for (auto popup : popupsList) {
            switch (popup) {
            case ID::AlarmActivated:


@@ 33,6 36,20 @@ namespace app
                        return std::make_unique<gui::AlarmDeactivatedWindow>(app, std::move(presenter));
                    });
                break;
            case ID::PowerOff:
                windowsFactory.attach(window::power_off_window, [](ApplicationCommon *app, const std::string &name) {
                    return std::make_unique<gui::BellTurnOffOptionWindow>(app, window::power_off_window);
                });
                windowsFactory.attach(gui::BellTurnOffWindow::name,
                                      [](ApplicationCommon *app, const std::string &name) {
                                          return std::make_unique<gui::BellTurnOffWindow>(
                                              app, std::make_unique<gui::BellPowerOffPresenter>(app));
                                      });
                windowsFactory.attach(gui::BellWelcomeWindow::defaultName,
                                      [](ApplicationCommon *app, const std::string &name) {
                                          return std::make_unique<gui::BellWelcomeWindow>(app);
                                      });
                break;
            default:
                break;
            }

M products/BellHybrid/apps/application-bell-alarm/ApplicationBellAlarm.cpp => products/BellHybrid/apps/application-bell-alarm/ApplicationBellAlarm.cpp +1 -1
@@ 62,7 62,7 @@ namespace app
            return std::make_unique<gui::BellAlarmSetWindow>(app, priv->alarmSetPresenter);
        });

        attachPopups({gui::popup::ID::AlarmActivated, gui::popup::ID::AlarmDeactivated});
        attachPopups({gui::popup::ID::AlarmActivated, gui::popup::ID::AlarmDeactivated, gui::popup::ID::PowerOff});
    }

    sys::MessagePointer ApplicationBellAlarm::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)

M products/BellHybrid/apps/application-bell-alarm/windows/BellAlarmWindow.cpp => products/BellHybrid/apps/application-bell-alarm/windows/BellAlarmWindow.cpp +1 -2
@@ 11,9 11,8 @@
#include <module-gui/gui/input/InputEvent.hpp>
#include <module-gui/gui/widgets/TextFixedSize.hpp>
#include <module-gui/gui/widgets/ThreeBox.hpp>
#include <module-gui/gui/widgets/Image.hpp>

#include <common/BellFinishedWindow.hpp>
#include <common/windows/BellFinishedWindow.hpp>

namespace gui
{

M products/BellHybrid/apps/application-bell-background-sounds/ApplicationBellBackgroundSounds.cpp => products/BellHybrid/apps/application-bell-background-sounds/ApplicationBellBackgroundSounds.cpp +2 -0
@@ 55,6 55,8 @@ namespace app
            auto presenter = std::make_unique<bgSounds::BGSoundsVolumePresenter>();
            return std::make_unique<gui::BGSoundsVolumeWindow>(app, std::move(presenter));
        });

        attachPopups({gui::popup::ID::AlarmActivated, gui::popup::ID::AlarmDeactivated, gui::popup::ID::PowerOff});
    }

    sys::MessagePointer ApplicationBellBackgroundSounds::DataReceivedHandler(sys::DataMessage *msgl,

M products/BellHybrid/apps/application-bell-main/ApplicationBellMain.cpp => products/BellHybrid/apps/application-bell-main/ApplicationBellMain.cpp +1 -1
@@ 61,7 61,7 @@ namespace app
            gui::window::name::bell_main_menu_dialog,
            [](ApplicationCommon *app, const std::string &name) { return std::make_unique<gui::Dialog>(app, name); });

        attachPopups({gui::popup::ID::AlarmActivated, gui::popup::ID::AlarmDeactivated});
        attachPopups({gui::popup::ID::AlarmActivated, gui::popup::ID::AlarmDeactivated, gui::popup::ID::PowerOff});
    }

    sys::MessagePointer ApplicationBellMain::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)

M products/BellHybrid/apps/application-bell-onboarding/ApplicationBellOnBoarding.cpp => products/BellHybrid/apps/application-bell-onboarding/ApplicationBellOnBoarding.cpp +4 -3
@@ 7,13 7,13 @@
#include <windows/OnBoardingLanguageWindow.hpp>
#include <windows/OnBoardingFinalizeWindow.hpp>
#include <windows/OnBoardingSettingsWindow.hpp>
#include <windows/OnBoardingWelcomeWindow.hpp>

#include <service-appmgr/Constants.hpp>
#include <service-appmgr/messages/GetCurrentDisplayLanguageResponse.hpp>

#include <apps-common/messages/OnBoardingMessages.hpp>
#include <common/BellFinishedWindow.hpp>
#include <common/windows/BellFinishedWindow.hpp>
#include <common/windows/BellWelcomeWindow.hpp>

#include <application-bell-settings/models/TemperatureUnitModel.hpp>
#include <application-bell-settings/models/TimeUnitsModel.hpp>


@@ 50,7 50,8 @@ namespace app
    void ApplicationBellOnBoarding::createUserInterface()
    {
        windowsFactory.attach(gui::name::window::main_window, [this](ApplicationCommon *app, const std::string &name) {
            return std::make_unique<gui::OnBoardingWelcomeWindow>(app, name);
            return std::make_unique<gui::BellWelcomeWindow>(
                app, name, [app]() { app->switchWindow(gui::window::name::onBoardingLanguageWindow); });
        });

        windowsFactory.attach(

M products/BellHybrid/apps/application-bell-onboarding/CMakeLists.txt => products/BellHybrid/apps/application-bell-onboarding/CMakeLists.txt +0 -2
@@ 16,7 16,6 @@ target_sources(application-bell-onboarding
        windows/OnBoardingLanguageWindow.cpp
        windows/OnBoardingSettingsWindow.cpp
        windows/OnBoardingFinalizeWindow.cpp
        windows/OnBoardingWelcomeWindow.cpp
        presenter/OnBoardingLanguageWindowPresenter.cpp
        presenter/OnBoardingFinalizeWindowPresenter.cpp



@@ 24,7 23,6 @@ target_sources(application-bell-onboarding
        presenter/OnBoardingFinalizeWindowPresenter.hpp
        windows/OnBoardingLanguageWindow.hpp
        windows/OnBoardingSettingsWindow.hpp
        windows/OnBoardingWelcomeWindow.hpp
        data/OnBoardingStyle.hpp

    PUBLIC

M products/BellHybrid/apps/application-bell-onboarding/windows/OnBoardingFinalizeWindow.hpp => products/BellHybrid/apps/application-bell-onboarding/windows/OnBoardingFinalizeWindow.hpp +1 -1
@@ 4,7 4,7 @@
#pragma once

#include <presenter/OnBoardingFinalizeWindowPresenter.hpp>
#include <common/BellFinishedWindow.hpp>
#include <common/windows/BellFinishedWindow.hpp>

namespace gui
{

M products/BellHybrid/apps/application-bell-powernap/ApplicationBellPowerNap.cpp => products/BellHybrid/apps/application-bell-powernap/ApplicationBellPowerNap.cpp +1 -1
@@ 50,7 50,7 @@ namespace app
                                  return std::make_unique<gui::PowerNapSessionEndedWindow>(app, std::move(presenter));
                              });

        attachPopups({gui::popup::ID::AlarmActivated, gui::popup::ID::AlarmDeactivated});
        attachPopups({gui::popup::ID::AlarmActivated, gui::popup::ID::AlarmDeactivated, gui::popup::ID::PowerOff});
    }

    sys::MessagePointer ApplicationBellPowerNap::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)

M products/BellHybrid/apps/application-bell-settings/ApplicationBellSettings.cpp => products/BellHybrid/apps/application-bell-settings/ApplicationBellSettings.cpp +10 -5
@@ 26,11 26,12 @@
#include "windows/BellSettingsBedtimeToneWindow.hpp"
#include "windows/BellSettingsFrontlight.hpp"
#include "windows/BellSettingsHomeViewWindow.hpp"
#include "windows/BellSettingsTurnOffWindow.hpp"
#include "windows/BellSettingsWindow.hpp"

#include <apps-common/windows/Dialog.hpp>
#include <common/BellFinishedWindow.hpp>
#include <common/windows/BellFinishedWindow.hpp>
#include <common/windows/BellTurnOffWindow.hpp>
#include <common/popups/BellTurnOffOptionWindow.hpp>
#include <service-evtmgr/Constants.hpp>
#include <service-evtmgr/EventManagerServiceAPI.hpp>
#include <service-evtmgr/ScreenLightControlMessage.hpp>


@@ 120,11 121,15 @@ namespace app
                                  return std::make_unique<gui::BellSettingsBedtimeToneWindow>(app);
                              });

        windowsFactory.attach(gui::window::name::bellSettingsTurnOff,
        windowsFactory.attach(gui::BellTurnOffOptionWindow::defaultName,
                              [](ApplicationCommon *app, const std::string &name) {
                                  return std::make_unique<gui::BellSettingsTurnOffWindow>(app);
                                  return std::make_unique<gui::BellTurnOffOptionWindow>(app);
                              });

        windowsFactory.attach(gui::BellTurnOffWindow::name, [](ApplicationCommon *app, const std::string &name) {
            return std::make_unique<gui::BellTurnOffWindow>(app, std::make_unique<gui::PowerOffPresenter>(app));
        });

        windowsFactory.attach(
            gui::BellSettingsPrewakeUpWindow::name, [this](ApplicationCommon *app, const std::string &name) {
                auto chimeDurationModel = std::make_unique<bell_settings::PrewakeUpChimeDurationModel>(this);


@@ 186,7 191,7 @@ namespace app
                return std::make_unique<gui::AboutYourBellWindow>(app, std::move(aboutYourBellPresenter));
            });

        attachPopups({gui::popup::ID::AlarmActivated, gui::popup::ID::AlarmDeactivated});
        attachPopups({gui::popup::ID::AlarmActivated, gui::popup::ID::AlarmDeactivated, gui::popup::ID::PowerOff});
    }

    sys::MessagePointer ApplicationBellSettings::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)

M products/BellHybrid/apps/application-bell-settings/CMakeLists.txt => products/BellHybrid/apps/application-bell-settings/CMakeLists.txt +0 -3
@@ 52,7 52,6 @@ target_sources(application-bell-settings
        windows/BellSettingsBedtimeToneWindow.cpp
        windows/BellSettingsFrontlight.cpp
        windows/BellSettingsHomeViewWindow.cpp
        windows/BellSettingsTurnOffWindow.cpp
        windows/BellSettingsWindow.cpp
        windows/advanced/AboutYourBellWindow.cpp
        windows/advanced/BellSettingsAdvancedWindow.cpp


@@ 95,7 94,6 @@ target_sources(application-bell-settings
        windows/BellSettingsBedtimeToneWindow.hpp
        windows/BellSettingsFrontlight.hpp
        windows/BellSettingsHomeViewWindow.hpp
        windows/BellSettingsTurnOffWindow.hpp
        windows/BellSettingsWindow.hpp
        windows/advanced/AboutYourBellWindow.hpp
        windows/advanced/BellSettingsLanguageWindow.hpp


@@ 118,7 116,6 @@ target_link_libraries(application-bell-settings
    PRIVATE
        app
        bellgui
        bell::app-common
        bell::db
        bell::alarms
        service-appmgr

M products/BellHybrid/apps/application-bell-settings/include/application-bell-settings/ApplicationBellSettings.hpp => products/BellHybrid/apps/application-bell-settings/include/application-bell-settings/ApplicationBellSettings.hpp +0 -1
@@ 17,7 17,6 @@ namespace gui::window::name
    inline constexpr auto bellSettingsHomeView    = "BellSettingsHomeView";
    inline constexpr auto bellSettingsLanguage    = "BellSettingsLanguage";
    inline constexpr auto bellSettingsBedtimeTone = "BellSettingsBedtimeTone";
    inline constexpr auto bellSettingsTurnOff     = "BellSettingsTurnOff";
} // namespace gui::window::name

namespace app

M products/BellHybrid/apps/application-bell-settings/windows/BellSettingsFrontlight.cpp => products/BellHybrid/apps/application-bell-settings/windows/BellSettingsFrontlight.cpp +1 -1
@@ 7,7 7,7 @@

#include <gui/input/InputEvent.hpp>
#include <apps-common/options/OptionStyle.hpp>
#include <common/BellFinishedWindow.hpp>
#include <common/windows/BellFinishedWindow.hpp>
#include <widgets/SideListView.hpp>
#include <widgets/BellBaseLayout.hpp>


D products/BellHybrid/apps/application-bell-settings/windows/BellSettingsTurnOffWindow.cpp => products/BellHybrid/apps/application-bell-settings/windows/BellSettingsTurnOffWindow.cpp +0 -29
@@ 1,29 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BellSettingsTurnOffWindow.hpp"

#include <application-bell-settings/ApplicationBellSettings.hpp>

namespace gui
{
    BellSettingsTurnOffWindow::BellSettingsTurnOffWindow(app::ApplicationCommon *app, std::string name)
        : AppWindow(app, std::move(name))
    {
        buildInterface();
    }

    void BellSettingsTurnOffWindow::rebuild()
    {
        erase();
        buildInterface();
    }

    void BellSettingsTurnOffWindow::buildInterface()
    {
        AppWindow::buildInterface();
        statusBar->setVisible(false);
        header->setTitleVisibility(false);
        bottomBar->setVisible(false);
    }
} /* namespace gui */

M products/BellHybrid/apps/application-bell-settings/windows/BellSettingsWindow.cpp => products/BellHybrid/apps/application-bell-settings/windows/BellSettingsWindow.cpp +2 -1
@@ 9,6 9,7 @@

#include <apps-common/messages/DialogMetadataMessage.hpp>
#include <common/options/OptionBellMenu.hpp>
#include <common/popups/BellTurnOffOptionWindow.hpp>
#include <apps-common/windows/Dialog.hpp>

namespace gui


@@ 46,7 47,7 @@ namespace gui
        addWinSettings(utils::translate("app_bell_settings_alarm_settings"), BellSettingsAlarmSettingsMenuWindow::name);
        addWinSettings(utils::translate("app_bell_settings_bedtime_tone"), window::name::bellSettingsBedtimeTone);
        addWinSettings(utils::translate("app_bell_settings_advanced"), window::name::bellSettingsAdvanced);
        addWinSettings(utils::translate("app_bell_settings_turn_off"), window::name::bellSettingsTurnOff);
        addWinSettings(utils::translate("app_bell_settings_turn_off"), BellTurnOffOptionWindow::defaultName);

        return settingsOptionList;
    }

M products/BellHybrid/apps/application-bell-settings/windows/advanced/BellSettingsTimeUnitsWindow.cpp => products/BellHybrid/apps/application-bell-settings/windows/advanced/BellSettingsTimeUnitsWindow.cpp +1 -1
@@ 7,7 7,7 @@

#include <gui/input/InputEvent.hpp>
#include <apps-common/options/OptionStyle.hpp>
#include <common/BellFinishedWindow.hpp>
#include <common/windows/BellFinishedWindow.hpp>
#include <widgets/SideListView.hpp>

namespace gui

M products/BellHybrid/apps/application-bell-settings/windows/alarm_settings/BellSettingsAlarmSettingsSnoozeWindow.cpp => products/BellHybrid/apps/application-bell-settings/windows/alarm_settings/BellSettingsAlarmSettingsSnoozeWindow.cpp +1 -1
@@ 8,7 8,7 @@
#include "presenter/alarm_settings/SnoozePresenter.hpp"

#include <apps-common/ApplicationCommon.hpp>
#include <common/BellFinishedWindow.hpp>
#include <common/windows/BellFinishedWindow.hpp>
#include <gui/input/InputEvent.hpp>
#include <widgets/SideListView.hpp>


M products/BellHybrid/apps/application-bell-settings/windows/alarm_settings/BellSettingsAlarmSettingsWindow.cpp => products/BellHybrid/apps/application-bell-settings/windows/alarm_settings/BellSettingsAlarmSettingsWindow.cpp +1 -1
@@ 5,7 5,7 @@
#include "BellSettingsAlarmSettingsMenuWindow.hpp"
#include "BellSettingsAlarmSettingsWindow.hpp"
#include "BellSettingsStyle.hpp"
#include <common/BellFinishedWindow.hpp>
#include <common/windows/BellFinishedWindow.hpp>

#include <apps-common/options/OptionStyle.hpp>
#include <module-gui/gui/input/InputEvent.hpp>

M products/BellHybrid/apps/application-bell-settings/windows/alarm_settings/BellSettingsPrewakeUpWindow.cpp => products/BellHybrid/apps/application-bell-settings/windows/alarm_settings/BellSettingsPrewakeUpWindow.cpp +1 -1
@@ 5,7 5,7 @@
#include "BellSettingsAlarmSettingsMenuWindow.hpp"
#include "BellSettingsPrewakeUpWindow.hpp"
#include "BellSettingsStyle.hpp"
#include <common/BellFinishedWindow.hpp>
#include <common/windows/BellFinishedWindow.hpp>

#include <apps-common/options/OptionStyle.hpp>
#include <module-gui/gui/input/InputEvent.hpp>

M products/BellHybrid/apps/common/CMakeLists.txt => products/BellHybrid/apps/common/CMakeLists.txt +10 -2
@@ 10,21 10,28 @@ target_include_directories(application-bell-common

target_sources(application-bell-common
    PRIVATE
        src/BellPowerOffPresenter.cpp
        src/AlarmModel.cpp
        src/TimeModel.cpp
        src/BellFinishedWindow.cpp
        src/windows/BellFinishedWindow.cpp
        src/windows/BellTurnOffWindow.cpp
        src/windows/BellWelcomeWindow.cpp
        src/BellSideListItemWithCallbacks.cpp
        src/TimeUtils.cpp
        src/popups/presenter/AlarmActivatedPresenter.cpp
        src/popups/AlarmActivatedWindow.cpp
        src/popups/AlarmDeactivatedWindow.cpp
        src/popups/BellTurnOffOptionWindow.cpp
        src/widgets/ListItems.cpp

        src/options/BellOptionWindow.cpp
        src/options/BellShortOptionWindow.cpp
        src/options/OptionBellMenu.cpp
    PUBLIC
        include/common/BellFinishedWindow.hpp
        include/common/BellPowerOffPresenter.hpp
        include/common/windows/BellFinishedWindow.hpp
        include/common/windows/BellTurnOffWindow.hpp
        include/common/windows/BellWelcomeWindow.hpp
        include/common/TimeUtils.hpp
        include/common/models/AbstractAlarmModel.hpp
        include/common/models/AbstractSettingsModel.hpp


@@ 33,6 40,7 @@ target_sources(application-bell-common
        include/common/popups/presenter/AlarmActivatedPresenter.hpp
        include/common/popups/AlarmActivatedWindow.hpp
        include/common/popups/AlarmDeactivatedWindow.hpp
        include/common/popups/BellTurnOffOptionWindow.hpp
        include/common/widgets/BellSideListItemWithCallbacks.hpp
        include/common/widgets/ListItems.hpp
        include/common/options/BellOptionWindow.hpp

A products/BellHybrid/apps/common/include/common/BellPowerOffPresenter.hpp => products/BellHybrid/apps/common/include/common/BellPowerOffPresenter.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 <apps-common/popups/presenter/PowerOffPresenter.hpp>

namespace gui
{
    class BellPowerOffPresenter : public gui::AbstractPowerOffPresenter
    {
      public:
        explicit BellPowerOffPresenter(app::ApplicationCommon *app);
        void powerOff() override;

      private:
        app::ApplicationCommon *application;
    };
} // namespace gui

M products/BellHybrid/apps/common/include/common/data/StyleCommon.hpp => products/BellHybrid/apps/common/include/common/data/StyleCommon.hpp +1 -0
@@ 10,4 10,5 @@ namespace gui::bell_style
{
    inline constexpr auto font        = ::style::window::font::supersizemelight;
    inline constexpr auto font_center = ::style::window::font::largelight;
    inline constexpr auto font_top    = ::style::window::font::largelight;
} // namespace gui::bell_style

R products/BellHybrid/apps/application-bell-settings/windows/BellSettingsTurnOffWindow.hpp => products/BellHybrid/apps/common/include/common/popups/BellTurnOffOptionWindow.hpp +9 -8
@@ 3,19 3,20 @@

#pragma once

#include <application-bell-settings/ApplicationBellSettings.hpp>

#include <apps-common/windows/AppWindow.hpp>
#include <common/options/BellShortOptionWindow.hpp>

namespace gui
{
    class BellSettingsTurnOffWindow : public AppWindow
    class BellTurnOffOptionWindow : public BellShortOptionWindow
    {
      public:
        explicit BellSettingsTurnOffWindow(app::ApplicationCommon *app,
                                           std::string name = gui::window::name::bellSettingsTurnOff);
        static constexpr auto defaultName = "BellTurnOffOptionWindow";
        explicit BellTurnOffOptionWindow(app::ApplicationCommon *app, const char *name = defaultName);

      private:
        std::list<Option> settingsOptionsList();

        void buildInterface() override;
        void rebuild() override;
        const UTF8 yesStr;
        const UTF8 noStr;
    };
} /* namespace gui */

R products/BellHybrid/apps/common/include/common/BellFinishedWindow.hpp => products/BellHybrid/apps/common/include/common/windows/BellFinishedWindow.hpp +0 -0
A products/BellHybrid/apps/common/include/common/windows/BellTurnOffWindow.hpp => products/BellHybrid/apps/common/include/common/windows/BellTurnOffWindow.hpp +28 -0
@@ 0,0 1,28 @@
// 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 <apps-common/popups/presenter/PowerOffPresenter.hpp>
#include <apps-common/popups/WindowWithTimer.hpp>

namespace gui
{
    class Icon;

    class BellTurnOffWindow : public WindowWithTimer
    {
      public:
        static constexpr auto name = "BellTurnOffWindow";

        BellTurnOffWindow(app::ApplicationCommon *app, std::unique_ptr<AbstractPowerOffPresenter> presenter);

      private:
        void buildInterface() override;
        bool onInput(const InputEvent &inputEvent) override;

        Icon *icon{};
        std::unique_ptr<AbstractPowerOffPresenter> presenter;
    };

} // namespace gui

R products/BellHybrid/apps/application-bell-onboarding/windows/OnBoardingWelcomeWindow.hpp => products/BellHybrid/apps/common/include/common/windows/BellWelcomeWindow.hpp +16 -4
@@ 3,23 3,35 @@

#pragma once

#include <BellOnBoardingNames.hpp>
#include <apps-common/windows/AppWindow.hpp>

#include <functional>

namespace gui
{
    class BellBaseLayout;

    class OnBoardingWelcomeWindow : public AppWindow
    class BellWelcomeWindow : public AppWindow
    {
        static constexpr auto midline_correction    = 40U;
        static constexpr auto midline_w             = 480U;
        static constexpr auto midline_h             = 1U;
        static constexpr auto sun_logo_margin_top   = 10U;
        static constexpr auto sun_logo_margin_right = 25U;

      public:
        explicit OnBoardingWelcomeWindow(app::ApplicationCommon *app,
                                         const std::string &name = gui::window::name::onBoardingLanguageWindow);
        using Callback                    = std::function<void()>;
        static constexpr auto defaultName = "BellWelcomeWindow";

        explicit BellWelcomeWindow(app::ApplicationCommon *app,
                                   const std::string &name = defaultName,
                                   Callback onAction       = {});

        void buildInterface() override;
        bool onInput(const InputEvent &inputEvent) override;

      private:
        BellBaseLayout *body{nullptr};
        Callback onAction;
    };
} /* namespace gui */

A products/BellHybrid/apps/common/src/BellPowerOffPresenter.cpp => products/BellHybrid/apps/common/src/BellPowerOffPresenter.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 "BellPowerOffPresenter.hpp"

#include <common/windows/BellWelcomeWindow.hpp>
#include <service-appmgr/messages/UserPowerDownRequest.hpp>
#include <system/Constants.hpp>

namespace gui
{
    BellPowerOffPresenter::BellPowerOffPresenter(app::ApplicationCommon *app) : application(app)
    {}

    void BellPowerOffPresenter::powerOff()
    {
        application->switchWindow(BellWelcomeWindow::defaultName);
        auto msg = std::make_shared<app::UserPowerDownRequest>();
        application->bus.sendUnicast(std::move(msg), service::name::system_manager);
    }

} // namespace gui

A products/BellHybrid/apps/common/src/popups/BellTurnOffOptionWindow.cpp => products/BellHybrid/apps/common/src/popups/BellTurnOffOptionWindow.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 "popups/BellTurnOffOptionWindow.hpp"

#include <apps-common/widgets/BellBaseLayout.hpp>
#include <apps-common/ApplicationCommon.hpp>
#include <common/data/StyleCommon.hpp>
#include <common/windows/BellTurnOffWindow.hpp>
#include <i18n/i18n.hpp>
#include <options/OptionBellMenu.hpp>

namespace gui
{
    BellTurnOffOptionWindow::BellTurnOffOptionWindow(app::ApplicationCommon *app, const char *name)
        : BellShortOptionWindow(app, name), yesStr{utils::translate("common_yes")}, noStr{utils::translate("common_no")}
    {
        addOptions(settingsOptionsList());
        setListTitle(utils::translate("app_bell_turn_off_question"));
        optionsList->setBoundaries(Boundaries::Continuous);
        optionsList->setAlignment(Alignment(Alignment::Vertical::Center));
    }

    std::list<Option> BellTurnOffOptionWindow::settingsOptionsList()
    {
        std::list<gui::Option> settingsOptionList;
        auto addWinSettings = [&](const UTF8 &name, std::function<bool(Item &)> activatedCallback) {
            settingsOptionList.emplace_back(std::make_unique<gui::option::OptionBellMenu>(
                name, activatedCallback, [=](gui::Item &) { return true; }, this));
        };

        addWinSettings(noStr, [this](auto &) {
            application->returnToPreviousWindow();
            return true;
        });
        addWinSettings(yesStr, [this](auto &) {
            application->switchWindow(BellTurnOffWindow::name);
            return true;
        });

        return settingsOptionList;
    }
} /* namespace gui */

R products/BellHybrid/apps/common/src/BellFinishedWindow.cpp => products/BellHybrid/apps/common/src/windows/BellFinishedWindow.cpp +1 -1
@@ 1,7 1,7 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "common/BellFinishedWindow.hpp"
#include "windows/BellFinishedWindow.hpp"
#include <apps-common/ApplicationCommon.hpp>
#include <gui/input/InputEvent.hpp>
#include <gui/widgets/Icon.hpp>

A products/BellHybrid/apps/common/src/windows/BellTurnOffWindow.cpp => products/BellHybrid/apps/common/src/windows/BellTurnOffWindow.cpp +46 -0
@@ 0,0 1,46 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "windows/BellTurnOffWindow.hpp"
#include <gui/input/InputEvent.hpp>
#include <gui/widgets/Icon.hpp>
#include <i18n/i18n.hpp>
#include <Application.hpp>

namespace gui
{
    BellTurnOffWindow::BellTurnOffWindow(app::ApplicationCommon *app,
                                         std::unique_ptr<AbstractPowerOffPresenter> presenter)
        : WindowWithTimer(app, name), presenter(std::move(presenter))
    {
        buildInterface();

        timerCallback = [this](Item &, sys::Timer &) {
            this->presenter->powerOff();
            return true;
        };
    }

    void gui::BellTurnOffWindow::buildInterface()
    {
        WindowWithTimer::buildInterface();

        statusBar->setVisible(false);
        header->setTitleVisibility(false);
        bottomBar->setVisible(false);

        icon = new Icon(this,
                        0,
                        0,
                        style::window_width,
                        style::window_height,
                        "bell_mudita_logo_W_G",
                        utils::translate("app_bell_goodbye"));
        icon->text->setFont(style::window::font::verybiglight);
    }
    bool BellTurnOffWindow::onInput(const InputEvent &)
    {
        return true;
    }

} // namespace gui

R products/BellHybrid/apps/application-bell-onboarding/windows/OnBoardingWelcomeWindow.cpp => products/BellHybrid/apps/common/src/windows/BellWelcomeWindow.cpp +13 -13
@@ 1,24 1,23 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "OnBoardingWelcomeWindow.hpp"
#include "OnBoardingLanguageWindow.hpp"
#include "windows/BellWelcomeWindow.hpp"

#include <data/OnBoardingStyle.hpp>
#include <apps-common/widgets/BellBaseLayout.hpp>

#include <module-gui/gui/input/InputEvent.hpp>
#include <module-gui/gui/widgets/TextFixedSize.hpp>
#include <i18n/i18n.hpp>

namespace gui
{
    OnBoardingWelcomeWindow::OnBoardingWelcomeWindow(app::ApplicationCommon *app, const std::string &name)
        : AppWindow(app, name)
    BellWelcomeWindow::BellWelcomeWindow(app::ApplicationCommon *app, const std::string &name, Callback onAction)
        : AppWindow(app, name), onAction{onAction}
    {
        buildInterface();
    }

    void OnBoardingWelcomeWindow::buildInterface()
    void BellWelcomeWindow::buildInterface()
    {
        AppWindow::buildInterface();
        statusBar->setVisible(false);


@@ 32,14 31,14 @@ namespace gui

        auto welcomeText = new TextFixedSize(body->centerBox);
        welcomeText->setMinimumSize(style::bell_base_layout::w,
                                    style::bell_base_layout::center_layout_h - window::welcome::midline_correction);
                                    style::bell_base_layout::center_layout_h - midline_correction);
        welcomeText->setFont(style::window::font::largelight);
        welcomeText->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        welcomeText->drawUnderline(false);
        welcomeText->setText(utils::translate("app_bell_onboarding_welcome_message"));

        auto midLine = new Rect(body->centerBox, 0, 0, 0, 0);
        midLine->setMinimumSize(window::welcome::midline_w, window::welcome::midline_h);
        midLine->setMinimumSize(midline_w, midline_h);
        midLine->setEdges(RectangleEdge::Bottom);

        auto informationBody = new HBox(body->lastBox);


@@ 50,11 49,10 @@ namespace gui

        auto sunLogo = new ImageBox(informationBody, new Image("button_icon_sun", ImageTypeSpecifier::W_G));
        sunLogo->setMinimumSizeToFitImage();
        sunLogo->setMargins(
            Margins(0, window::welcome::sun_logo_margin_top, window::welcome::sun_logo_margin_right, 0));
        sunLogo->setMargins(Margins(0, sun_logo_margin_top, sun_logo_margin_right, 0));

        auto instructionText = new TextFixedSize(informationBody);
        instructionText->setMargins(Margins(0, window::welcome::sun_logo_margin_top, 0, 0));
        instructionText->setMargins(Margins(0, sun_logo_margin_top, 0, 0));
        instructionText->setFont(style::window::font::largelight);
        instructionText->setAlignment(Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Center));
        instructionText->drawUnderline(false);


@@ 67,10 65,12 @@ namespace gui
        body->resize();
    }

    bool OnBoardingWelcomeWindow::onInput(const InputEvent &inputEvent)
    bool BellWelcomeWindow::onInput(const InputEvent &inputEvent)
    {
        if (inputEvent.isKeyRelease(KeyCode::KEY_LF)) {
            application->switchWindow(gui::window::name::onBoardingLanguageWindow);
            if (onAction) {
                onAction();
            }
        }
        return false;
    }

M products/BellHybrid/keymap/include/keymap/KeyMap.hpp => products/BellHybrid/keymap/include/keymap/KeyMap.hpp +51 -9
@@ 4,21 4,63 @@
#pragma once

#include <gui/input/InputEvent.hpp>
#include <bitset>

/// Key mapping structure to ease translation between PureOS key definitions and nomenclature used throughout the
/// GUI design
enum class KeyMap
enum class KeyMap : std::uint8_t
{
    Frontlight    = static_cast<int>(gui::KeyCode::KEY_LF),
    Back          = static_cast<int>(gui::KeyCode::KEY_RF),
    LightPress    = static_cast<int>(gui::KeyCode::KEY_ENTER),
    RotateLeft    = static_cast<int>(gui::KeyCode::KEY_DOWN),
    RotateRight   = static_cast<int>(gui::KeyCode::KEY_UP),
    DeepPressUp   = static_cast<int>(gui::KeyCode::KEY_LEFT),
    DeepPressDown = static_cast<int>(gui::KeyCode::KEY_RIGHT),
    Frontlight    = 0,
    Back          = 1,
    LightPress    = 2,
    RotateLeft    = 3,
    RotateRight   = 4,
    DeepPressUp   = 5,
    DeepPressDown = 6
};

inline static KeyMap mapKey(gui::KeyCode key)
{
    return KeyMap{key};
    switch (key) {
    case gui::KeyCode::KEY_LF:
        return KeyMap::Frontlight;
    case gui::KeyCode::KEY_RF:
        return KeyMap::Back;
    case gui::KeyCode::KEY_ENTER:
        return KeyMap::LightPress;
    case gui::KeyCode::KEY_DOWN:
        return KeyMap::RotateLeft;
    case gui::KeyCode::KEY_UP:
        return KeyMap::RotateRight;
    case gui::KeyCode::KEY_LEFT:
        return KeyMap::DeepPressUp;
    case gui::KeyCode::KEY_RIGHT:
        return KeyMap::DeepPressDown;
    default:
        assert(("Unrecognized key code"));
        return KeyMap{};
    }
}

struct KeyStates
{
    using KeySet = std::bitset<32U>;

    void set(KeyMap key, bool value)
    {
        states.set(magic_enum::enum_integer(key), value);
    }
    bool state(KeyMap key)
    {
        return states.test(magic_enum::enum_integer(key));
    }

    template <typename... Args> bool ifOnlySet(Args... args)
    {
        const auto mask = (... | [](auto x) { return KeySet{1UL << magic_enum::enum_integer(x)}; }(args));
        return (states | mask) == mask;
    }

  private:
    KeySet states;
};

M products/BellHybrid/services/appmgr/CMakeLists.txt => products/BellHybrid/services/appmgr/CMakeLists.txt +2 -0
@@ 1,4 1,5 @@
add_library(appmgr STATIC)
add_library(bell::appmgr ALIAS appmgr)

target_sources(appmgr
    PRIVATE


@@ 6,6 7,7 @@ target_sources(appmgr
    PUBLIC
        include/appmgr/messages/AlarmMessage.hpp
        include/appmgr/ApplicationManager.hpp
        include/appmgr/messages/PowerOffPopupRequestParams.hpp
)

target_include_directories(appmgr

A products/BellHybrid/services/appmgr/include/appmgr/messages/PowerOffPopupRequestParams.hpp => products/BellHybrid/services/appmgr/include/appmgr/messages/PowerOffPopupRequestParams.hpp +16 -0
@@ 0,0 1,16 @@
// 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 <Service/Message.hpp>
#include <apps-common/popups/data/PopupRequestParams.hpp>
#include <service-appmgr/Actions.hpp>
#include <service-appmgr/messages/ActionRequest.hpp>

class PowerOffPopupRequestParams : public gui::PopupRequestParams
{
  public:
    PowerOffPopupRequestParams() : PopupRequestParams{gui::popup::ID::PowerOff}
    {}
};

M products/BellHybrid/services/evtmgr/CMakeLists.txt => products/BellHybrid/services/evtmgr/CMakeLists.txt +15 -1
@@ 8,10 8,19 @@ target_sources(evtmgr
        WorkerEvent.hpp
        internal/StaticData.cpp
        internal/TemperatureApi.cpp

        internal/key_sequences/LongPressSequence.cpp
        internal/key_sequences/KeySequenceMgr.cpp
        internal/StaticData.hpp
        screen-light-control/ScreenLightControl.cpp
        backlight-handler/BacklightHandler.cpp

        internal/key_sequences/AbstractKeySequence.hpp
        internal/key_sequences/KeySequenceMgr.hpp
        internal/key_sequences/PowerOffSequence.hpp
        internal/key_sequences/AlarmActivateSequence.hpp
        internal/key_sequences/AlarmDeactivateSequence.hpp
        internal/key_sequences/ReleaseSequence.hpp
        internal/key_sequences/LongPressSequence.hpp
    PUBLIC
        include/evtmgr/EventManager.hpp
        include/evtmgr/api/TemperatureApi.hpp


@@ 30,5 39,10 @@ target_link_libraries(evtmgr
        module-bsp
        module-utils
        service-evtmgr
        bell::appmgr
        sys
)

if (${ENABLE_TESTS})
    add_subdirectory(tests)
endif()

M products/BellHybrid/services/evtmgr/EventManager.cpp => products/BellHybrid/services/evtmgr/EventManager.cpp +40 -18
@@ 1,11 1,16 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "WorkerEvent.hpp"
#include "internal/StaticData.hpp"
#include "internal/key_sequences/KeySequenceMgr.hpp"
#include "internal/key_sequences/PowerOffSequence.hpp"
#include "internal/key_sequences/AlarmActivateSequence.hpp"
#include "internal/key_sequences/AlarmDeactivateSequence.hpp"

#include "WorkerEvent.hpp"
#include <appmgr/messages/PowerOffPopupRequestParams.hpp>
#include <evtmgr/EventManager.hpp>
#include <keymap/KeyMap.hpp>
#include <service-appmgr/Controller.hpp>
#include <hal/temperature_source/TemperatureSource.hpp>
#include <system/Constants.hpp>
#include <screen-light-control/ScreenLightControl.hpp>


@@ 31,6 36,7 @@ EventManager::EventManager(const std::string &name)
    : EventManagerCommon(name), temperatureSource{hal::temperature::AbstractTemperatureSource::Factory::create()},
      backlightHandler(settings, this)
{
    buildKeySequences();
    updateTemperature(*temperatureSource);

    onMinuteTick = [this](const time_t) { updateTemperature(*temperatureSource); };


@@ 45,24 51,11 @@ void EventManager::handleKeyEvent(sys::Message *msg)
        return;
    }

    auto key = mapKey(static_cast<gui::KeyCode>(kbdMessage->key.keyCode));

    if (kbdMessage->key.state == RawKey::State::Released) {
        if (key == KeyMap::DeepPressUp) {
            bus.sendUnicast(
                std::make_shared<sys::AlarmActivationStatusChangeRequest>(sys::AlarmActivationStatus::ACTIVATED),
                service::name::system_manager);
        }
        else if (key == KeyMap::DeepPressDown) {
            bus.sendUnicast(
                std::make_shared<sys::AlarmActivationStatusChangeRequest>(sys::AlarmActivationStatus::DEACTIVATED),
                service::name::system_manager);
        }
    }

    if (kbdMessage->key.state == RawKey::State::Pressed) {
        backlightHandler.handleKeyPressed(static_cast<int>(key));
        backlightHandler.handleKeyPressed(static_cast<int>(static_cast<gui::KeyCode>(kbdMessage->key.keyCode)));
    }

    keySequenceMgr->process(kbdMessage->key);
}

auto EventManager::createEventWorker() -> std::unique_ptr<WorkerEventCommon>


@@ 115,3 108,32 @@ sys::MessagePointer EventManager::DataReceivedHandler(sys::DataMessage *msgl, sy

    return std::make_shared<sys::ResponseMessage>(sys::ReturnCodes::Unresolved);
}
void EventManager::buildKeySequences()
{
    KeySequenceMgr::SequenceCollection collection;

    auto powerOffSeq      = std::make_unique<PowerOffSequence>(*this);
    powerOffSeq->onAction = [this]() {
        app::manager::Controller::sendAction(
            this, app::manager::actions::ShowPopup, std::make_unique<PowerOffPopupRequestParams>());
    };
    collection.emplace_back(std::move(powerOffSeq));

    auto alarmActivateSeq      = std::make_unique<AlarmActivateSequence>();
    alarmActivateSeq->onAction = [this]() {
        bus.sendUnicast(
            std::make_shared<sys::AlarmActivationStatusChangeRequest>(sys::AlarmActivationStatus::ACTIVATED),
            service::name::system_manager);
    };
    collection.emplace_back(std::move(alarmActivateSeq));

    auto alarmDeactivateSeq      = std::make_unique<AlarmDeactivateSequence>();
    alarmDeactivateSeq->onAction = [this]() {
        bus.sendUnicast(
            std::make_shared<sys::AlarmActivationStatusChangeRequest>(sys::AlarmActivationStatus::DEACTIVATED),
            service::name::system_manager);
    };
    collection.emplace_back(std::move(alarmDeactivateSeq));

    keySequenceMgr = std::make_unique<KeySequenceMgr>(std::move(collection));
}

M products/BellHybrid/services/evtmgr/include/evtmgr/EventManager.hpp => products/BellHybrid/services/evtmgr/include/evtmgr/EventManager.hpp +5 -0
@@ 7,6 7,8 @@

#include "BacklightHandler.hpp"

class KeySequenceMgr;

namespace hal::temperature
{
    class AbstractTemperatureSource;


@@ 22,8 24,11 @@ class EventManager : public EventManagerCommon
    sys::MessagePointer DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) override;
    void initProductEvents() final;
    auto createEventWorker() -> std::unique_ptr<WorkerEventCommon> final;
    void buildKeySequences();
    std::shared_ptr<hal::temperature::AbstractTemperatureSource> temperatureSource;
    backlight::Handler backlightHandler;

    std::shared_ptr<KeySequenceMgr> keySequenceMgr;
};

namespace sys

A products/BellHybrid/services/evtmgr/internal/key_sequences/AbstractKeySequence.hpp => products/BellHybrid/services/evtmgr/internal/key_sequences/AbstractKeySequence.hpp +66 -0
@@ 0,0 1,66 @@
// 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 <hal/key_input/RawKey.hpp>

#include <functional>

class AbstractKeySequence
{
    template <class Cb, class... Args> static void invoke(Cb &&callback, Args &&...args)
    {
        if (callback) {
            callback(args...);
        }
    }

  public:
    using Callback = std::function<void(const AbstractKeySequence &)>;

    virtual ~AbstractKeySequence()          = default;
    virtual void process(const RawKey &key) = 0;

    void trigger() const
    {
        invoke(onTriggered, *this);
    }

    void ready() const
    {
        invoke(onReady, *this);
    }

    void idle() const
    {
        invoke(onIdle, *this);
    }

    void abort() const
    {
        invoke(onAbort, *this);
    }

    void action() const
    {
        invoke(onAction);
    }

    void setCallbacks(Callback &&triggered, Callback &&ready, Callback &&idle, Callback &&abort)
    {
        onTriggered = triggered;
        onReady     = ready;
        onIdle      = idle;
        onAbort     = abort;
    }

    /// Used to inject custom action into key sequence
    std::function<void()> onAction;

  private:
    Callback onTriggered;
    Callback onReady;
    Callback onIdle;
    Callback onAbort;
};

A products/BellHybrid/services/evtmgr/internal/key_sequences/AlarmActivateSequence.hpp => products/BellHybrid/services/evtmgr/internal/key_sequences/AlarmActivateSequence.hpp +13 -0
@@ 0,0 1,13 @@
// 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 "ReleaseSequence.hpp"

class AlarmActivateSequence : public ReleaseSequence
{
  public:
    AlarmActivateSequence() : ReleaseSequence(KeyMap::DeepPressUp)
    {}
};

A products/BellHybrid/services/evtmgr/internal/key_sequences/AlarmDeactivateSequence.hpp => products/BellHybrid/services/evtmgr/internal/key_sequences/AlarmDeactivateSequence.hpp +13 -0
@@ 0,0 1,13 @@
// 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 "ReleaseSequence.hpp"

class AlarmDeactivateSequence : public ReleaseSequence
{
  public:
    AlarmDeactivateSequence() : ReleaseSequence(KeyMap::DeepPressDown)
    {}
};

A products/BellHybrid/services/evtmgr/internal/key_sequences/KeySequenceMgr.cpp => products/BellHybrid/services/evtmgr/internal/key_sequences/KeySequenceMgr.cpp +70 -0
@@ 0,0 1,70 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "KeySequenceMgr.hpp"

KeySequenceMgr::KeySequenceMgr(std::vector<std::unique_ptr<AbstractKeySequence>> collection)
    : sequenceCollection(std::move(collection))
{
    auto onIdle = [this](auto &seq) {
        removeFromInProgressList(seq);
        actionIfPossible(seq);
    };
    auto onTriggered = [this](auto &seq) { addToInProgressList(seq); };
    auto onReady     = [this](auto &seq) {
        moveToReadyList(seq);
        actionIfPossible(seq);
    };
    auto onAbort = [this](auto &seq) {
        removeFromInProgressList(seq);
        removeFromReadyList(seq);
    };

    for (auto &seq : sequenceCollection) {
        seq->setCallbacks(onTriggered, onReady, onIdle, onAbort);
    }
}
void KeySequenceMgr::process(const RawKey &key)
{
    for (const auto &seq : sequenceCollection) {
        seq->process(key);
    }
}
void KeySequenceMgr::moveToReadyList(const AbstractKeySequence &seq)
{
    const auto res = std::find(inProgressSequences.begin(), inProgressSequences.end(), &seq);
    if (res != inProgressSequences.end()) {
        readySequences.emplace_back(&seq);
        inProgressSequences.remove(&seq);
    }
}
void KeySequenceMgr::actionIfPossible(const AbstractKeySequence &seq)
{
    if (inProgressSequences.empty()) {
        if (const auto s = readySequences.back(); s != nullptr) {
            s->action();
            readySequences.clear();
        }
    }
}
bool KeySequenceMgr::searchInReadyList(const AbstractKeySequence &seq)
{
    return std::find(readySequences.begin(), readySequences.end(), &seq) != readySequences.end();
}
void KeySequenceMgr::removeFromInProgressList(const AbstractKeySequence &seq)
{
    const auto result = std::find(inProgressSequences.begin(), inProgressSequences.end(), &seq);
    if (result != inProgressSequences.end()) {
        inProgressSequences.remove(&seq);
    }
}
void KeySequenceMgr::removeFromReadyList(const AbstractKeySequence &seq)
{
    if (searchInReadyList(seq)) {
        readySequences.remove(&seq);
    }
}
void KeySequenceMgr::addToInProgressList(const AbstractKeySequence &seq)
{
    inProgressSequences.emplace_back(&seq);
}

A products/BellHybrid/services/evtmgr/internal/key_sequences/KeySequenceMgr.hpp => products/BellHybrid/services/evtmgr/internal/key_sequences/KeySequenceMgr.hpp +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

#pragma once

#include <memory>
#include <list>

#include "AbstractKeySequence.hpp"

class KeySequenceMgr
{
  public:
    using SequenceCollection = std::vector<std::unique_ptr<AbstractKeySequence>>;
    explicit KeySequenceMgr(SequenceCollection collection);

    void process(const RawKey &key);

  private:
    void actionIfPossible(const AbstractKeySequence &seq);

    void moveToReadyList(const AbstractKeySequence &seq);
    bool searchInReadyList(const AbstractKeySequence &seq);
    void removeFromReadyList(const AbstractKeySequence &seq);

    void addToInProgressList(const AbstractKeySequence &seq);
    void removeFromInProgressList(const AbstractKeySequence &seq);

    SequenceCollection sequenceCollection;
    std::list<const AbstractKeySequence *> inProgressSequences;
    std::list<const AbstractKeySequence *> readySequences;
};

A products/BellHybrid/services/evtmgr/internal/key_sequences/LongPressSequence.cpp => products/BellHybrid/services/evtmgr/internal/key_sequences/LongPressSequence.cpp +39 -0
@@ 0,0 1,39 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "LongPressSequence.hpp"

LongPressSequence::LongPressSequence(KeyMap keyToScan, sys::Service &service, std::chrono::milliseconds timeout)
    : keyToScan{keyToScan}
{
    timerHandle = sys::TimerFactory::createSingleShotTimer(&service, "lpressseq", timeout, [this](auto &) {
        state = State::Ready;
        ready();
    });
}
void LongPressSequence::process(const RawKey &key)
{
    keyStates.set(mapKey(static_cast<gui::KeyCode>(key.keyCode)), key.state == RawKey::State::Pressed);

    if (gate = keyStates.state(keyToScan) && not keyStates.ifOnlySet(keyToScan); gate) {
        if (state != State::Idle) {
            timerHandle.stop();
            state = State::Idle;
            abort();
        }
        return;
    }

    if (keyStates.state(keyToScan) && state == State::Idle) {
        timerHandle.stop();
        timerHandle.start();
        state = State::InProgress;
        trigger();
    }

    if (not keyStates.state(keyToScan) && state != State::Idle) {
        timerHandle.stop();
        state = State::Idle;
        idle();
    }
}

A products/BellHybrid/services/evtmgr/internal/key_sequences/LongPressSequence.hpp => products/BellHybrid/services/evtmgr/internal/key_sequences/LongPressSequence.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 "AbstractKeySequence.hpp"
#include <keymap/KeyMap.hpp>
#include <Timers/TimerFactory.hpp>

class LongPressSequence : public AbstractKeySequence
{
  public:
    enum class State
    {
        Idle,
        InProgress,
        Ready
    };
    LongPressSequence(KeyMap keyToScan, sys::Service &service, std::chrono::milliseconds timeout);

    void process(const RawKey &key) override;

  private:
    bool gate{false};
    KeyStates keyStates;
    KeyMap keyToScan;
    State state = State::Idle;
    std::chrono::milliseconds timeout;
    sys::TimerHandle timerHandle;
};

A products/BellHybrid/services/evtmgr/internal/key_sequences/PowerOffSequence.hpp => products/BellHybrid/services/evtmgr/internal/key_sequences/PowerOffSequence.hpp +15 -0
@@ 0,0 1,15 @@
// 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 "LongPressSequence.hpp"

class PowerOffSequence : public LongPressSequence
{
  public:
    explicit PowerOffSequence(sys::Service &service,
                              std::chrono::milliseconds timeout = std::chrono::milliseconds{5000})
        : LongPressSequence(KeyMap::Back, service, timeout)
    {}
};

A products/BellHybrid/services/evtmgr/internal/key_sequences/ReleaseSequence.hpp => products/BellHybrid/services/evtmgr/internal/key_sequences/ReleaseSequence.hpp +28 -0
@@ 0,0 1,28 @@
// 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 "AbstractKeySequence.hpp"
#include <keymap/KeyMap.hpp>

class ReleaseSequence : public AbstractKeySequence
{
  public:
    explicit ReleaseSequence(KeyMap key) : mappedKey{key}
    {}
    void process(const RawKey &key) override
    {
        if (mapKey(static_cast<gui::KeyCode>(key.keyCode)) != mappedKey) {
            return;
        }

        if (key.state == RawKey::State::Released) {
            trigger();
            ready();
        }
    }

  private:
    KeyMap mappedKey;
};

A products/BellHybrid/services/evtmgr/tests/CMakeLists.txt => products/BellHybrid/services/evtmgr/tests/CMakeLists.txt +10 -0
@@ 0,0 1,10 @@
add_catch2_executable(
        NAME
        bell_evtmgr
        SRCS
        unittest_KeySequenceMgr.cpp
        LIBS
        bell::evtmgr
        INCLUDE
        $<TARGET_PROPERTY:bell::evtmgr,INCLUDE_DIRECTORIES>
)

A products/BellHybrid/services/evtmgr/tests/unittest_KeySequenceMgr.cpp => products/BellHybrid/services/evtmgr/tests/unittest_KeySequenceMgr.cpp +429 -0
@@ 0,0 1,429 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#define CATCH_CONFIG_MAIN

#include <catch2/catch.hpp>
#include <internal/key_sequences/KeySequenceMgr.hpp>
#include <keymap/KeyMap.hpp>

namespace
{
    enum class State
    {
        Idle,
        InProgress,
        Ready
    };

    class TwoKeysSequence : public AbstractKeySequence
    {
      public:
        explicit TwoKeysSequence(KeyMap keyToScan1, KeyMap keyToScan2) : keyToScan1{keyToScan1}, keyToScan2{keyToScan2}
        {}
        void process(const RawKey &key) override
        {
            const auto mappedKey = mapKey(static_cast<gui::KeyCode>(key.keyCode));
            if (mappedKey != keyToScan1 && mappedKey != keyToScan2) {
                return;
            }

            if (mappedKey == keyToScan1) {
                key1Pressed = key.state == RawKey::State::Pressed;
            }
            if (mappedKey == keyToScan2) {
                key2Pressed = key.state == RawKey::State::Pressed;
            }

            if (key1Pressed && key2Pressed && state == State::Idle) {
                state = State::InProgress;
                trigger();
            }

            if (not key1Pressed || not key2Pressed) {
                state = State::Idle;
                idle();
            }
        }

        void make_ready()
        {
            state = State::Ready;
            ready();
        }

      private:
        bool key1Pressed{false};
        bool key2Pressed{false};
        KeyMap keyToScan1;
        KeyMap keyToScan2;
        State state = State::Idle;
    };

    class LongPressSequence : public AbstractKeySequence
    {
      public:
        explicit LongPressSequence(KeyMap keyToScan) : keyToScan{keyToScan}
        {}
        void process(const RawKey &key) override
        {
            keyStates.set(mapKey(static_cast<gui::KeyCode>(key.keyCode)), key.state == RawKey::State::Pressed);

            if (gate = keyStates.state(keyToScan) && not keyStates.ifOnlySet(keyToScan); gate) {
                if (state != State::Idle) {
                    state = State::Idle;
                    abort();
                }
                return;
            }

            if (keyStates.state(keyToScan) && state == State::Idle) {
                state = State::InProgress;
                trigger();
            }

            if (not keyStates.state(keyToScan) && state != State::Idle) {
                state = State::Idle;
                idle();
            }
        }

        void make_ready()
        {
            state = State::Ready;
            ready();
        }

      private:
        bool gate{false};
        KeyStates keyStates;

        KeyMap keyToScan;
        State state = State::Idle;
    };

    RawKey make_press_back()
    {
        RawKey rawKey{};
        rawKey.state   = RawKey::State::Pressed;
        rawKey.keyCode = bsp::KeyCodes::FnRight;
        return rawKey;
    }

    RawKey make_release_back()
    {
        RawKey rawKey{};
        rawKey.state   = RawKey::State::Released;
        rawKey.keyCode = bsp::KeyCodes::FnRight;
        return rawKey;
    }

    RawKey make_press_frontlight()
    {
        RawKey rawKey{};
        rawKey.state   = RawKey::State::Pressed;
        rawKey.keyCode = bsp::KeyCodes::FnLeft;
        return rawKey;
    }

    RawKey make_release_frontlight()
    {
        RawKey rawKey{};
        rawKey.state   = RawKey::State::Released;
        rawKey.keyCode = bsp::KeyCodes::FnLeft;
        return rawKey;
    }

} // namespace

TEST_CASE("Single long press")
{
    std::uint8_t actionTriggered{};
    KeySequenceMgr::SequenceCollection collection;

    auto mockLongPressBack      = std::make_unique<LongPressSequence>(KeyMap::Back);
    mockLongPressBack->onAction = [&actionTriggered]() { actionTriggered++; };
    LongPressSequence *mockRef  = mockLongPressBack.get();

    collection.emplace_back(std::move(mockLongPressBack));
    KeySequenceMgr keySequenceMgr{std::move(collection)};

    SECTION("Short press&release")
    {
        keySequenceMgr.process(make_press_back());
        CHECK(not actionTriggered);
        keySequenceMgr.process(make_release_back());
        CHECK(not actionTriggered);
    }

    SECTION("Long press without release")
    {
        keySequenceMgr.process(make_press_back());
        mockRef->make_ready();
        CHECK(actionTriggered == 1);
    }

    SECTION("Valid sequence")
    {
        keySequenceMgr.process(make_press_back());
        mockRef->make_ready();
        keySequenceMgr.process(make_release_back());
        CHECK(actionTriggered == 1);
    }
}

TEST_CASE("Many long press sequences")
{
    KeySequenceMgr::SequenceCollection collection;

    std::uint8_t actionTriggered_1{};
    auto longPressBack_1      = std::make_unique<LongPressSequence>(KeyMap::Back);
    longPressBack_1->onAction = [&actionTriggered_1]() { actionTriggered_1++; };
    LongPressSequence *ref_1  = longPressBack_1.get();
    collection.emplace_back(std::move(longPressBack_1));

    std::uint8_t actionTriggered_2{};
    auto longPressBack_2      = std::make_unique<LongPressSequence>(KeyMap::Back);
    longPressBack_2->onAction = [&actionTriggered_2]() { actionTriggered_2++; };
    LongPressSequence *ref_2  = longPressBack_2.get();
    collection.emplace_back(std::move(longPressBack_2));

    std::uint8_t actionTriggered_3{};
    auto longPressBack_3      = std::make_unique<LongPressSequence>(KeyMap::Back);
    longPressBack_3->onAction = [&actionTriggered_3]() { actionTriggered_3++; };
    LongPressSequence *ref_3  = longPressBack_3.get();
    collection.emplace_back(std::move(longPressBack_3));

    KeySequenceMgr keySequenceMgr{std::move(collection)};

    SECTION("Third sequence ready -> ought be invoked")
    {
        keySequenceMgr.process(make_press_back());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);

        ref_1->make_ready();
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);

        ref_2->make_ready();
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);

        ref_3->make_ready();
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(actionTriggered_3 == 1);

        keySequenceMgr.process(make_release_back());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(actionTriggered_3 == 1);
    }

    SECTION("Two sequences ready & release -> only second sequence ought to be invoked")
    {
        keySequenceMgr.process(make_press_back());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);

        ref_1->make_ready();
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);

        ref_2->make_ready();
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);

        keySequenceMgr.process(make_release_back());
        CHECK(not actionTriggered_1);
        CHECK(actionTriggered_2 == 1);
        CHECK(not actionTriggered_3);
    }

    SECTION("First sequence ready -> should be invoked")
    {
        keySequenceMgr.process(make_press_back());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);

        ref_1->make_ready();
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);

        keySequenceMgr.process(make_release_back());
        CHECK(actionTriggered_1 == 1);
        CHECK(not actionTriggered_2);
    }

    SECTION("Second sequence ready -> should be invoked")
    {
        keySequenceMgr.process(make_press_back());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(not actionTriggered_3);

        ref_1->make_ready();
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(not actionTriggered_3);

        ref_2->make_ready();
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(not actionTriggered_3);

        keySequenceMgr.process(make_release_back());
        CHECK(not actionTriggered_1);
        CHECK(actionTriggered_2 == 1);
        CHECK(not actionTriggered_3);
    }

    SECTION("Press&release without ready -> no action ought to be invoked")
    {
        keySequenceMgr.process(make_press_back());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);

        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);

        keySequenceMgr.process(make_release_back());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
    }
}

TEST_CASE("Two keys sequence")
{
    KeySequenceMgr::SequenceCollection collection;

    std::uint8_t actionTriggered_1{};
    auto sequence          = std::make_unique<TwoKeysSequence>(KeyMap::Back, KeyMap::Frontlight);
    sequence->onAction     = [&actionTriggered_1]() { actionTriggered_1++; };
    TwoKeysSequence *ref_1 = sequence.get();
    collection.emplace_back(std::move(sequence));

    KeySequenceMgr keySequenceMgr{std::move(collection)};

    SECTION("Valid sequence -> action ought to be invoked regardless of the type of the key release")
    {
        keySequenceMgr.process(make_press_frontlight());
        keySequenceMgr.process(make_press_back());
        CHECK(not actionTriggered_1);

        ref_1->make_ready();
        CHECK(actionTriggered_1 == 1);

        keySequenceMgr.process(make_release_frontlight());
        CHECK(actionTriggered_1 == 1);
        keySequenceMgr.process(make_release_back());
        CHECK(actionTriggered_1 == 1);
    }

    SECTION("Key release in the middle of sequence -> no action ought to be invoked")
    {
        keySequenceMgr.process(make_press_frontlight());
        keySequenceMgr.process(make_press_back());
        CHECK(not actionTriggered_1);

        keySequenceMgr.process(make_release_back());
        ref_1->make_ready();
        CHECK(not actionTriggered_1);

        keySequenceMgr.process(make_release_frontlight());
        CHECK(not actionTriggered_1);
    }
}

TEST_CASE("Two keys sequence type mixed with long press sequences")
{
    KeySequenceMgr::SequenceCollection collection;

    std::uint8_t actionTriggered_1{};
    auto sequence1      = std::make_unique<LongPressSequence>(KeyMap::Back);
    sequence1->onAction = [&actionTriggered_1]() { actionTriggered_1++; };
    // LongPressSequence *ref_1 = sequence1.get();
    collection.emplace_back(std::move(sequence1));

    std::uint8_t actionTriggered_2{};
    auto sequence2      = std::make_unique<LongPressSequence>(KeyMap::Back);
    sequence2->onAction = [&actionTriggered_2]() { actionTriggered_2++; };
    // LongPressSequence *ref_2 = sequence2.get();
    collection.emplace_back(std::move(sequence2));

    std::uint8_t actionTriggered_3{};
    auto sequence3         = std::make_unique<TwoKeysSequence>(KeyMap::Back, KeyMap::Frontlight);
    sequence3->onAction    = [&actionTriggered_3]() { actionTriggered_3++; };
    TwoKeysSequence *ref_3 = sequence3.get();
    collection.emplace_back(std::move(sequence3));

    KeySequenceMgr keySequenceMgr{std::move(collection)};

    SECTION("Interrupt third sequence")
    {
        keySequenceMgr.process(make_press_back());
        keySequenceMgr.process(make_press_frontlight());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(not actionTriggered_3);

        keySequenceMgr.process(make_release_frontlight());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(not actionTriggered_3);

        keySequenceMgr.process(make_release_back());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(not actionTriggered_3);
    }

    SECTION("Two keys sequence ready, back pressed first")
    {
        keySequenceMgr.process(make_press_back());
        keySequenceMgr.process(make_press_frontlight());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(not actionTriggered_3);

        ref_3->make_ready();
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(actionTriggered_3 == 1);

        keySequenceMgr.process(make_release_back());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(actionTriggered_3 == 1);

        keySequenceMgr.process(make_release_frontlight());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(actionTriggered_3 == 1);
    }

    SECTION("Two keys sequence ready, frontlight pressed first")
    {
        keySequenceMgr.process(make_press_frontlight());
        keySequenceMgr.process(make_press_back());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(not actionTriggered_3);

        ref_3->make_ready();
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(actionTriggered_3 == 1);

        keySequenceMgr.process(make_release_back());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(actionTriggered_3 == 1);

        keySequenceMgr.process(make_release_frontlight());
        CHECK(not actionTriggered_1);
        CHECK(not actionTriggered_2);
        CHECK(actionTriggered_3 == 1);
    }
}

M products/BellHybrid/sys/CMakeLists.txt => products/BellHybrid/sys/CMakeLists.txt +1 -1
@@ 15,6 15,6 @@ target_include_directories(sys

target_link_libraries(sys
    PRIVATE
        appmgr
        bell::appmgr
        module-sys
)