~aleteoryx/muditaos

c7b7cc4c2705824b6497f9bcc1ea6b5c91018a41 — Wiktor S. Ovalle Correa 5 years ago 3b14104 + bda18b5
[EGD-5737] Merge master into experimental

Keep experimental up to date with changes from master
224 files changed, 3706 insertions(+), 1868 deletions(-)

M CMakeLists.txt
M README.md
M doc/i18n.md
M enabled_unittests
M image/user/db/settings_v2_002.sql
M module-apps/Application.cpp
M module-apps/Application.hpp
M module-apps/CMakeLists.txt
M module-apps/application-alarm-clock/widgets/AlarmInternalListItem.hpp
M module-apps/application-alarm-clock/widgets/AlarmItem.cpp
M module-apps/application-alarm-clock/widgets/AlarmItem.hpp
M module-apps/application-alarm-clock/widgets/AlarmOptionsItem.cpp
M module-apps/application-alarm-clock/widgets/AlarmOptionsItem.hpp
M module-apps/application-alarm-clock/widgets/AlarmTimeItem.cpp
M module-apps/application-alarm-clock/widgets/AlarmTimeItem.hpp
M module-apps/application-alarm-clock/widgets/CustomCheckBoxWithLabel.cpp
M module-apps/application-alarm-clock/widgets/CustomCheckBoxWithLabel.hpp
M module-apps/application-calendar/models/CustomRepeatModel.cpp
M module-apps/application-calendar/widgets/AllEventsItem.cpp
M module-apps/application-calendar/widgets/AllEventsItem.hpp
M module-apps/application-calendar/widgets/CalendarListItem.hpp
M module-apps/application-calendar/widgets/CheckBoxWithLabelItem.cpp
M module-apps/application-calendar/widgets/CheckBoxWithLabelItem.hpp
M module-apps/application-calendar/widgets/DayEventsItem.cpp
M module-apps/application-calendar/widgets/DayEventsItem.hpp
M module-apps/application-calendar/widgets/DayLabel.cpp
M module-apps/application-calendar/widgets/DayLabel.hpp
M module-apps/application-calendar/widgets/EventDateItem.cpp
M module-apps/application-calendar/widgets/EventDateItem.hpp
M module-apps/application-calendar/widgets/EventDetailDescriptionItem.cpp
M module-apps/application-calendar/widgets/EventDetailDescriptionItem.hpp
M module-apps/application-calendar/widgets/EventTimeItem.cpp
M module-apps/application-calendar/widgets/EventTimeItem.hpp
M module-apps/application-calendar/widgets/RepeatAndReminderItem.cpp
M module-apps/application-calendar/widgets/RepeatAndReminderItem.hpp
M module-apps/application-calendar/widgets/SeveralOptionsItem.cpp
M module-apps/application-calendar/widgets/SeveralOptionsItem.hpp
M module-apps/application-calendar/widgets/TextWithLabelItem.cpp
M module-apps/application-calendar/widgets/TextWithLabelItem.hpp
M module-apps/application-calllog/widgets/CalllogItem.cpp
M module-apps/application-calllog/widgets/CalllogItem.hpp
M module-apps/application-desktop/widgets/DesktopInputWidget.cpp
M module-apps/application-desktop/widgets/DesktopInputWidget.hpp
M module-apps/application-messages/widgets/SMSInputWidget.cpp
M module-apps/application-messages/widgets/SMSInputWidget.hpp
M module-apps/application-messages/widgets/SMSOutputWidget.cpp
M module-apps/application-messages/widgets/SMSOutputWidget.hpp
M module-apps/application-messages/widgets/SMSTemplateItem.cpp
M module-apps/application-messages/widgets/SMSTemplateItem.hpp
M module-apps/application-music-player/ApplicationMusicPlayer.cpp
M module-apps/application-music-player/ApplicationMusicPlayer.hpp
M module-apps/application-music-player/widgets/SongItem.cpp
M module-apps/application-music-player/widgets/SongItem.hpp
M module-apps/application-phonebook/widgets/ContactListItem.hpp
M module-apps/application-phonebook/widgets/InformationWidget.cpp
M module-apps/application-phonebook/widgets/InformationWidget.hpp
M module-apps/application-phonebook/widgets/InputBoxWithLabelAndIconWidget.cpp
M module-apps/application-phonebook/widgets/InputBoxWithLabelAndIconWidget.hpp
M module-apps/application-phonebook/widgets/InputLinesWithLabelIWidget.cpp
M module-apps/application-phonebook/widgets/InputLinesWithLabelIWidget.hpp
M module-apps/application-phonebook/widgets/OutputLinesTextWithLabelWidget.cpp
M module-apps/application-phonebook/widgets/OutputLinesTextWithLabelWidget.hpp
M module-apps/application-phonebook/widgets/PhonebookItem.cpp
M module-apps/application-phonebook/widgets/PhonebookItem.hpp
M module-apps/application-settings-new/widgets/ApnInputWidget.cpp
M module-apps/application-settings-new/widgets/ApnInputWidget.hpp
M module-apps/application-settings-new/widgets/ApnListItem.hpp
M module-apps/application-settings-new/widgets/QuoteWidget.cpp
M module-apps/application-settings-new/widgets/QuoteWidget.hpp
M module-apps/application-settings/widgets/ColorTestListItem.cpp
M module-apps/application-settings/widgets/ColorTestListItem.hpp
M module-apps/application-special-input/widgets/SpecialInputTableWidget.cpp
M module-apps/application-special-input/widgets/SpecialInputTableWidget.hpp
M module-apps/windows/AppWindow.cpp
M module-apps/windows/AppWindow.hpp
M module-audio/Audio/AudioCommon.cpp
M module-audio/Audio/AudioCommon.hpp
R {module-bsp/bsp/audio/bsp_audio => module-audio/Audio/AudioDevice}.hpp -rwxr-xr-x => -rw-r--r--
A module-audio/Audio/AudioDeviceFactory.cpp
A module-audio/Audio/AudioDeviceFactory.hpp
A module-audio/Audio/AudioPlatform.hpp
D module-audio/Audio/BluetoothProxyAudio.cpp
D module-audio/Audio/BluetoothProxyAudio.hpp
M module-audio/Audio/Operation/IdleOperation.cpp
M module-audio/Audio/Operation/Operation.cpp
M module-audio/Audio/Operation/Operation.hpp
M module-audio/Audio/Operation/PlaybackOperation.cpp
M module-audio/Audio/Operation/RecorderOperation.cpp
M module-audio/Audio/Operation/RecorderOperation.hpp
M module-audio/Audio/Operation/RouterOperation.cpp
M module-audio/Audio/Operation/RouterOperation.hpp
M module-audio/Audio/Profiles/Profile.cpp
M module-audio/Audio/Profiles/Profile.hpp
M module-audio/Audio/Profiles/ProfileIdle.hpp
M module-audio/Audio/Profiles/ProfilePlaybackBluetoothA2DP.hpp
M module-audio/Audio/Profiles/ProfilePlaybackHeadphones.hpp
M module-audio/Audio/Profiles/ProfilePlaybackLoudspeaker.hpp
M module-audio/Audio/Profiles/ProfileRecordingBluetoothHSP.hpp
M module-audio/Audio/Profiles/ProfileRecordingHeadphones.hpp
M module-audio/Audio/Profiles/ProfileRecordingOnBoardMic.hpp
M module-audio/Audio/Profiles/ProfileRoutingBluetoothHSP.hpp
M module-audio/Audio/Profiles/ProfileRoutingEarspeaker.hpp
M module-audio/Audio/Profiles/ProfileRoutingHeadphones.hpp
M module-audio/Audio/Profiles/ProfileRoutingLoudspeaker.hpp
M module-audio/CMakeLists.txt
A module-audio/board/linux/CMakeLists.txt
A module-audio/board/linux/LinuxAudioPlatform.cpp
A module-audio/board/rt1051/CMakeLists.txt
R {module-bsp/board/rt1051/bsp/audio/RT1051Audiocodec => module-audio/board/rt1051/RT1051AudioCodec}.cpp
R {module-bsp/board/rt1051/bsp/audio/RT1051Audiocodec => module-audio/board/rt1051/RT1051AudioCodec}.hpp -rwxr-xr-x => -rw-r--r--
R {module-bsp/board/rt1051/bsp/audio => module-audio/board/rt1051}/RT1051CellularAudio.cpp
R {module-bsp/board/rt1051/bsp/audio => module-audio/board/rt1051}/RT1051CellularAudio.hpp -rwxr-xr-x => -rw-r--r--
A module-audio/board/rt1051/RT1051DeviceFactory.cpp
A module-audio/board/rt1051/RT1051DeviceFactory.hpp
A module-audio/board/rt1051/RT1051Platform.cpp
R {module-bsp/board/rt1051/bsp/audio => module-audio/board/rt1051}/SAIAudioDevice.cpp
R {module-bsp/board/rt1051/bsp/audio => module-audio/board/rt1051}/SAIAudioDevice.hpp
D module-audio/targets/Target_Cross.cmake
D module-audio/targets/Target_Linux.cmake
M module-bluetooth/Bluetooth/interface/profiles/PAN.cpp
M module-bluetooth/Bluetooth/interface/profiles/Profile.hpp
M module-bluetooth/README.md
A module-bluetooth/doc/bt_on_off.md
R module-bluetooth/{ => doc}/bt_on_off_state_diagram.puml
R module-bluetooth/{ => doc}/bt_on_off_state_diagram.svg
A module-bluetooth/doc/bt_scan.md
A module-bluetooth/doc/bt_scan.puml
A module-bluetooth/doc/bt_scan.svg
M module-bsp/CMakeLists.txt
M module-bsp/board/rt1051/bsp/audio/CodecMAX98090.cpp
M module-bsp/board/rt1051/bsp/audio/CodecMAX98090.hpp
A module-bsp/board/rt1051/common/audio.cpp
A module-bsp/board/rt1051/common/audio.hpp
M module-bsp/board/rt1051/common/board.cpp
D module-bsp/bsp/audio/bsp_audio.cpp
M module-bsp/targets/Target_RT1051.cmake
M module-db/Common/Query.cpp
M module-db/Common/Query.hpp
M module-db/Interface/ContactRecord.cpp
M module-db/Interface/ContactRecord.hpp
M module-db/Interface/ThreadRecord.cpp
M module-db/queries/Filter.hpp
M module-db/queries/RecordQuery.cpp
M module-db/queries/phonebook/QueryContactGet.cpp
M module-db/queries/phonebook/QueryContactGet.hpp
M module-gui/gui/widgets/BoxLayout.cpp
M module-gui/gui/widgets/CMakeLists.txt
M module-gui/gui/widgets/ListItem.hpp
M module-gui/gui/widgets/TextBlockCursor.cpp
M module-gui/gui/widgets/TopBar.cpp
M module-gui/gui/widgets/TopBar.hpp
A module-gui/gui/widgets/TopBar/BatteryWidgetBar.cpp
A module-gui/gui/widgets/TopBar/BatteryWidgetBar.hpp
A module-gui/gui/widgets/TopBar/BatteryWidgetBase.cpp
A module-gui/gui/widgets/TopBar/BatteryWidgetBase.hpp
A module-gui/gui/widgets/TopBar/BatteryWidgetText.cpp
A module-gui/gui/widgets/TopBar/BatteryWidgetText.hpp
M module-gui/test/test-catch-text/test-gui-Text.cpp
M module-services/service-appmgr/model/ApplicationManager.cpp
M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp
M module-services/service-audio/ServiceAudio.cpp
M module-services/service-bluetooth/ServiceBluetooth.cpp
M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp
M module-services/service-cellular/ServiceCellular.cpp
M module-services/service-db/agents/settings/SystemSettings.hpp
M module-services/service-desktop/endpoints/Context.hpp
M module-services/service-desktop/endpoints/Endpoint.hpp
M module-services/service-desktop/endpoints/contacts/ContactHelper.cpp
M module-services/service-desktop/endpoints/contacts/ContactHelper.hpp
M module-services/service-desktop/endpoints/contacts/ContactsEndpoint.cpp
M module-services/service-desktop/endpoints/contacts/ContactsEndpoint.hpp
M module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp
M module-services/service-desktop/endpoints/messages/MessageHelper.cpp
M module-services/service-desktop/endpoints/messages/MessageHelper.hpp
M module-services/service-desktop/endpoints/messages/MessagesEndpoint.cpp
M module-services/service-desktop/endpoints/messages/MessagesEndpoint.hpp
M module-services/service-desktop/parser/MessageHandler.cpp
M module-services/service-desktop/parser/ParserUtils.cpp
M module-services/service-desktop/parser/ParserUtils.hpp
M module-services/service-desktop/tests/CMakeLists.txt
A module-services/service-desktop/tests/test-contacts.cpp
M module-services/service-desktop/tests/unittest.cpp
M module-services/service-evtmgr/EventManager.cpp
M module-services/service-evtmgr/WorkerEvent.cpp
M module-services/service-evtmgr/battery-level-check/BatteryLevelCheck.cpp
M module-services/service-evtmgr/battery-level-check/BatteryLevelCheck.hpp
A module-services/service-evtmgr/doc/battery_status_notification.md
A module-services/service-evtmgr/doc/battery_status_notification.puml
A module-services/service-evtmgr/doc/battery_status_notification.svg
M module-services/service-evtmgr/screen-light-control/ScreenLightControl.cpp
M module-services/service-evtmgr/screen-light-control/ScreenLightControl.hpp
M module-services/service-evtmgr/service-evtmgr/BatteryMessages.hpp
M module-services/service-evtmgr/service-evtmgr/EventManager.hpp
M module-services/service-evtmgr/service-evtmgr/WorkerEvent.hpp
M module-sys/CMakeLists.txt
A module-sys/PhoneModes/Common.hpp
A module-sys/PhoneModes/Observer.cpp
A module-sys/PhoneModes/Observer.hpp
A module-sys/PhoneModes/Subject.cpp
A module-sys/PhoneModes/Subject.hpp
M module-sys/Service/Common.hpp
M module-sys/SystemManager/SystemManager.cpp
M module-sys/SystemManager/SystemManager.hpp
A module-sys/SystemManager/doc/PowerManagement.md
A module-sys/SystemManager/doc/data/CpuFreqChangeAlgorithm.puml
A module-sys/SystemManager/doc/data/CpuFreqChangeAlgorithm.svg
A module-sys/SystemManager/doc/data/CpuFrequencyUpdate.puml
A module-sys/SystemManager/doc/data/CpuFrequencyUpdate.svg
A module-sys/SystemManager/doc/data/ResourceRequest.puml
A module-sys/SystemManager/doc/data/cellularResourceRequest.svg
A module-sys/SystemManager/doc/data/eInkResourceRequest.svg
A module-sys/SystemManager/messages/PhoneModeRequest.hpp
M module-utils/test/CMakeLists.txt
A module-utils/test/test-Utility-ConditionalInvoke.cpp
A module-utils/utility/Utility.hpp
M source/MessageType.hpp
R test/pytest/service-desktop/{test_calllog => disabled_test_calllog}.py
R test/pytest/service-desktop/{test_messages => disabled_test_messages}.py
M test/pytest/service-desktop/test_contacts.py
M test/pytest/service-desktop/test_templates.py
A test/pytest/service-desktop/test_threads.py
M test/pytest/test_search_sms.py
M test/pytest/test_send_message.py
M test/search_sms.py
M CMakeLists.txt => CMakeLists.txt +4 -0
@@ 22,6 22,10 @@ message("TARGET_COMPILE_DEFINITIONS: ${TARGET_COMPILE_OPTIONS}")
message("TARGET_LIBRARIES: ${TARGET_LIBRARIES}")
message("TARGET_LINKER_FLAGS: ${TARGET_LINKER_FLAGS}")

string(REPLACE "TARGET_" "" PROJECT_TARGET_NAME ${PROJECT_TARGET})
string(TOLOWER "${PROJECT_TARGET_NAME}" PROJECT_TARGET_NAME)
message("Project target name: ${PROJECT_TARGET_NAME}")

add_executable(${PROJECT_NAME} "" )

if (NOT ${PROJECT_TARGET} STREQUAL "TARGET_RT1051")

M README.md => README.md +5 -0
@@ 37,6 37,7 @@ MuditaOS is a mobile operating system optimized for E Ink displays. Built on Fre
* [Contributing](#Contributing)
   * [Discussions](#Discussions)
   * [Reporting issues and feature requests](#Reporting-bugs-and-feature-requests)
   * [Internationalization](#Internationalization)
   * [Development workflow](#Development-workflow)
* [Documentation](#documentation)
   * [How to generate documentation using Doxygen](#How-to-generate-documentation-using-Doxygen)    


@@ 67,6 68,10 @@ For general questions and ideas regarding MuditaOS please post in the [“Mudita

You can report bugs and feature requests on [GitHub](https://github.com/mudita/MuditaOS/issues). This is also a good place to discuss architecture decisions and things that aren’t yet covered by the documentation. Please refer to the ["Contributing to MuditaOS"](./CONTRIBUTING.md) article for more details.

### Internationalization

If you want to start localizing MuditaOS interface please start from [the "Internationalization" article](./doc/i18n.md).

### Development workflow

When contributing code or documentation changes please follow the guidleines inside the ["Development workflow"](./doc/development_workflow.md) article.

M doc/i18n.md => doc/i18n.md +44 -1
@@ 66,7 66,50 @@ Definition for every key code used in the phone is in [the key_codes.hpp file (K

### Date and time

MuditaOS follows [Linux `date`](https://man7.org/linux/man-pages/man1/date.1.html) to format date and time. 
MuditaOS follows [Linux `date`](https://man7.org/linux/man-pages/man1/date.1.html) to format date and time.

## Font conversion

MuditaOS doesn't have built-in support for ttf/otf fonts, so we generate bitmap files for fonts. The official release uses GT Pressura Typeface which is a licensed font ([more info](../LICENSE.md))

MuditaOS supports the following languages (and characters) out-of-the-box:

- English
- German 
- Spanish 
- Polish
- French

For other languages there might be a need to add language-specific glyphs, build them using [fontbuilder](https://github.com/mudita/fontbuilder) and add them to the repository.

[Here's a list of glyphs that are supported by GT Pressura](https://www.grillitype.com/api/storage/app/uploads/public/5b6/c52/16b/5b6c5216b40a8675629257.pdf) (our current license covers only the Latin alphapbet characters).

[Here's a list of characters currently added to the build](https://github.com/mudita/fontbuilder/blob/master/charset.txt).

If your characters aren't currently supported by the main font or the defualt fallback font (DeJavu Sans Bold) please know that we're working on adding a feature inside MuditaOS that would enable to render the UI in more languages with a font provided by the user.

### Adding glyphs

We use [fontbuilder](https://github.com/mudita/fontbuilder). It has font sets saved in files. This is not the best solution to store system presets (we should rather have it in release process). In order to do this:

- feed FontBuilder headless run with preset file and build MPF (Mudita Pure Font)
- put these fonts in build folder

On Mudita Pure phone we use:

- default font (`gt_pressura` font family)
- default fallback font (DeJavu Sans Bold size 27 **Emojis only**)

The `▯` glyph indicates that there is no glyph for a unicode character in the current font.

#### Naming convention

It’s a good practice not to rename font names - these are stored in font metadata too and metadata is used in code (**not the font file name**). Please just keep names as they are.

#### Where are the files?

- Source font files used for the build: `PurePhone/image/assets/fonts/`
- Font files used at runtime (copied upon build): `PurePhone/<build_folder>/assets/fonts/`

## How to start localizing the interface


M enabled_unittests => enabled_unittests +14 -0
@@ 161,6 161,7 @@ TESTS_LIST["catch2-gui-text"]="
    Text addition bounds - text widget size restricted;
    Text addition bounds - text widget line size restricted;
    Text addition bounds - multiple limits tests;
    Text newline navigation and deletion tests;
    RichText newline and empty lines tests;
    TextBlock Ctor/Dtor ;
    Text block - set/update/get text;


@@ 214,6 215,10 @@ TESTS_LIST["catch2-service-desktop"]="
    Endpoint Factory test;
"
#---------
TESTS_LIST["catch2-service-desktop-endpoint-contacts"]="
    Endpoint Contacts Test;
"
#---------
TESTS_LIST["catch2-service-evtmgr"]="
    ScreenLightControlFunctions;
"


@@ 254,6 259,15 @@ TESTS_LIST["catch2-utils-clipboard"]="
    Clipboard;
"
#---------
TESTS_LIST["catch2-utils-conditional-invoke-tests"]="
    Successful global function call;
    Failed global function call;
    Successful class static function call;
    Failed class static function call;
    Successful class member function call;
    Failed class member function call;
"
#---------
TESTS_LIST["catch2-utils-duration"]="
    Duration - creation;
    Duration - arithemtics;

M image/user/db/settings_v2_002.sql => image/user/db/settings_v2_002.sql +2 -1
@@ 21,5 21,6 @@ INSERT OR IGNORE INTO settings_tab (path, value) VALUES
    ('bt_state', '0'),
    ('bt_device_visibility', '0'),
    ('bt_device_name', 'PurePhone'),
    ('bt_bonded_devices', '');
    ('bt_bonded_devices', ''),
    ('battery_critical_level', '10');


M module-apps/Application.cpp => module-apps/Application.cpp +5 -31
@@ 84,6 84,8 @@ namespace app

        connect(typeid(AppRefreshMessage),
                [this](sys::Message *msg) -> sys::MessagePointer { return handleAppRefresh(msg); });

        connect(sevm::BatteryStatusChangeMessage(), [&](sys::Message *) { return handleBatteryStatusChange(); });
    }

    Application::~Application() noexcept


@@ 129,12 131,7 @@ namespace app
        // send drawing commands only when if application is in active and visible.
        if (state == State::ACTIVE_FORGROUND) {
            auto window = getCurrentWindow();
            if (Store::Battery::get().state == Store::Battery::State::Charging) {
                window->updateBatteryCharger(true);
            }
            else {
                window->updateBatteryLevel(Store::Battery::get().level);
            }
            window->updateBatteryStatus();
            window->setSIM();
            window->updateSignalStrength();
            window->updateNetworkAccessTechnology();


@@ 216,12 213,6 @@ namespace app
        else if (msgl->messageType == MessageType::KBDKeyEvent) {
            return handleKBDKeyEvent(msgl);
        }
        else if (msgl->messageType == MessageType::EVMBatteryLevel) {
            return handleBatteryLevel(msgl);
        }
        else if (msgl->messageType == MessageType::EVMChargerPlugged) {
            return handleChargerPlugged(msgl);
        }
        else if (msgl->messageType == MessageType::EVMMinuteUpdated) {
            return handleMinuteUpdated(msgl);
        }


@@ 299,31 290,14 @@ namespace app
        return msgHandled();
    }

    sys::MessagePointer Application::handleBatteryLevel(sys::Message *msgl)
    sys::MessagePointer Application::handleBatteryStatusChange()
    {
        auto msg = static_cast<sevm::BatteryLevelMessage *>(msgl);
        LOG_INFO("Battery level: %d", msg->levelPercents);

        if (getCurrentWindow()->updateBatteryLevel(msg->levelPercents)) {
        if (getCurrentWindow()->updateBatteryStatus()) {
            refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
        }
        return msgHandled();
    }

    sys::MessagePointer Application::handleChargerPlugged(sys::Message *msgl)
    {
        auto *msg = static_cast<sevm::BatteryPlugMessage *>(msgl);
        if (msg->plugged == true) {
            LOG_INFO("Charger connected");
        }
        else {
            LOG_INFO("Charger disconnected");
        }
        getCurrentWindow()->updateBatteryCharger(msg->plugged);
        refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
        return msgHandled();
    }

    sys::MessagePointer Application::handleMinuteUpdated(sys::Message *msgl)
    {
        auto *msg = static_cast<sevm::RtcMinuteAlarmMessage *>(msgl);

M module-apps/Application.hpp => module-apps/Application.hpp +1 -2
@@ 167,8 167,7 @@ namespace app
        sys::MessagePointer handleNetworkAccessTechnologyUpdate(sys::Message *msgl);
        sys::MessagePointer handleInputEvent(sys::Message *msgl);
        sys::MessagePointer handleKBDKeyEvent(sys::Message *msgl);
        sys::MessagePointer handleBatteryLevel(sys::Message *msgl);
        sys::MessagePointer handleChargerPlugged(sys::Message *msgl);
        sys::MessagePointer handleBatteryStatusChange();
        sys::MessagePointer handleMinuteUpdated(sys::Message *msgl);
        sys::MessagePointer handleAction(sys::Message *msgl);
        sys::MessagePointer handleApplicationSwitch(sys::Message *msgl);

M module-apps/CMakeLists.txt => module-apps/CMakeLists.txt +5 -1
@@ 1,4 1,7 @@
cmake_minimum_required(VERSION 3.14)
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

cmake_minimum_required(VERSION 3.14)

project(module-apps VERSION 1.0
        DESCRIPTION "Library with all applications.")


@@ 112,6 115,7 @@ target_link_libraries(${PROJECT_NAME}
        service-db
        service-evtmgr
    PUBLIC
        module-audio
        module-bsp
        module-os
        module-sys

M module-apps/application-alarm-clock/widgets/AlarmInternalListItem.hpp => module-apps/application-alarm-clock/widgets/AlarmInternalListItem.hpp +1 -8
@@ 7,12 7,5 @@

namespace gui
{
    class AlarmInternalListItem : public ListItem
    {
      public:
        std::function<void(std::shared_ptr<AlarmsRecord> event)> onSaveCallback = nullptr;
        std::function<void(std::shared_ptr<AlarmsRecord> event)> onLoadCallback = nullptr;
        std::function<bool()> onContentChangedCallback                          = nullptr;
    };

    using AlarmInternalListItem = ListItemWithCallbacks<AlarmsRecord>;
} /* namespace gui */

M module-apps/application-alarm-clock/widgets/AlarmItem.cpp => module-apps/application-alarm-clock/widgets/AlarmItem.cpp +4 -6
@@ 43,13 43,11 @@ namespace gui
        onOffImage->setMargins(gui::Margins(style::alarmClock::window::item::imageMargin, 0, 0, 0));

        setAlarm();
    }

    bool AlarmItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        hBox->setPosition(0, 0);
        hBox->setSize(newDim.w, newDim.h);
        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            hBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void AlarmItem::setAlarm()

M module-apps/application-alarm-clock/widgets/AlarmItem.hpp => module-apps/application-alarm-clock/widgets/AlarmItem.hpp +0 -2
@@ 24,7 24,5 @@ namespace gui

      public:
        explicit AlarmItem(std::shared_ptr<AlarmsRecord> record);

        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };
} // namespace gui

M module-apps/application-alarm-clock/widgets/AlarmOptionsItem.cpp => module-apps/application-alarm-clock/widgets/AlarmOptionsItem.cpp +4 -6
@@ 284,13 284,11 @@ namespace gui
            }
            optionLabel->setText(optionsNames[actualVectorIndex]);
        };
    }

    bool AlarmOptionsItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);
        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    std::vector<audio::Tags> AlarmOptionsItem::getMusicFilesList()

M module-apps/application-alarm-clock/widgets/AlarmOptionsItem.hpp => module-apps/application-alarm-clock/widgets/AlarmOptionsItem.hpp +0 -2
@@ 47,8 47,6 @@ namespace gui
                         AlarmOptionItemName itemName,
                         std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr,
                         std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr);

        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-alarm-clock/widgets/AlarmTimeItem.cpp => module-apps/application-alarm-clock/widgets/AlarmTimeItem.cpp +5 -7
@@ 123,6 123,11 @@ namespace gui

        onInputCallback(*hourInput);
        onInputCallback(*minuteInput);

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            hBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void AlarmTimeItem::onInputCallback(gui::Text &textItem)


@@ 203,13 208,6 @@ namespace gui
        }
    }

    bool AlarmTimeItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        hBox->setPosition(0, 0);
        hBox->setSize(newDim.w, newDim.h);
        return true;
    }

    bool AlarmTimeItem::isPm(const std::string &text) const
    {
        return !(text == utils::localize.get(utils::time::Locale::getAM()));

M module-apps/application-alarm-clock/widgets/AlarmTimeItem.hpp => module-apps/application-alarm-clock/widgets/AlarmTimeItem.hpp +0 -3
@@ 31,9 31,6 @@ namespace gui
        AlarmTimeItem(bool mode24H,
                      std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr,
                      std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr);

        // virtual methods from Item
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-alarm-clock/widgets/CustomCheckBoxWithLabel.cpp => module-apps/application-alarm-clock/widgets/CustomCheckBoxWithLabel.cpp +5 -6
@@ 80,6 80,11 @@ namespace gui
            return false;
        };
        onContentChangedCallback = [&]() { return checkBox->isChecked(); };

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            hBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void CustomCheckBoxWithLabel::setCheckBoxes()


@@ 91,10 96,4 @@ namespace gui
        }
    }

    bool CustomCheckBoxWithLabel::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        hBox->setPosition(0, 0);
        hBox->setSize(newDim.w, newDim.h);
        return true;
    }
} // namespace gui

M module-apps/application-alarm-clock/widgets/CustomCheckBoxWithLabel.hpp => module-apps/application-alarm-clock/widgets/CustomCheckBoxWithLabel.hpp +0 -2
@@ 26,8 26,6 @@ namespace gui
      public:
        CustomCheckBoxWithLabel(app::Application *app, const std::string &description, const WeekDaysRepeatData &data);

        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;

        static const std::map<WeekDayIso, std::string> weekDays;
    };
} // namespace gui

M module-apps/application-calendar/models/CustomRepeatModel.cpp => module-apps/application-calendar/models/CustomRepeatModel.cpp +1 -1
@@ 66,7 66,7 @@ std::vector<bool> CustomRepeatModel::getIsCheckedData()
{
    std::vector<bool> isCheckedData;
    for (auto item : internalData) {
        if (item->onContentChangeCallback && item->onContentChangeCallback()) {
        if (item->onContentChangedCallback && item->onContentChangedCallback()) {
            isCheckedData.push_back(true);
        }
        else {

M module-apps/application-calendar/widgets/AllEventsItem.cpp => module-apps/application-calendar/widgets/AllEventsItem.cpp +4 -6
@@ 34,13 34,11 @@ namespace gui
        description->setEdges(gui::RectangleEdge::None);
        description->setFont(style::window::font::bigbold);
        description->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
    }

    bool AllEventsItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        hBox->setPosition(0, 0);
        hBox->setSize(newDim.w, newDim.h);
        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            hBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void AllEventsItem::setMarkerItem(UTF8 text)

M module-apps/application-calendar/widgets/AllEventsItem.hpp => module-apps/application-calendar/widgets/AllEventsItem.hpp +0 -3
@@ 24,9 24,6 @@ namespace gui
        void setMarkerItem(UTF8 text);
        [[nodiscard]] UTF8 getLabelMarker() const;
        void setEvent(std::shared_ptr<EventsRecord> record);

        // virtual methods from Item
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/CalendarListItem.hpp => module-apps/application-calendar/widgets/CalendarListItem.hpp +1 -8
@@ 7,12 7,5 @@

namespace gui
{
    class CalendarListItem : public ListItem
    {
      public:
        std::function<void(std::shared_ptr<EventsRecord> event)> onSaveCallback = nullptr;
        std::function<void(std::shared_ptr<EventsRecord> event)> onLoadCallback = nullptr;
        std::function<bool()> onContentChangeCallback                           = nullptr;
    };

    using CalendarListItem = ListItemWithCallbacks<EventsRecord>;
} /* namespace gui */

M module-apps/application-calendar/widgets/CheckBoxWithLabelItem.cpp => module-apps/application-calendar/widgets/CheckBoxWithLabelItem.cpp +7 -9
@@ 57,7 57,7 @@ namespace gui
        if (checkBoxData != nullptr) {
            setCheckBoxes();
        }
        onContentChangeCallback = [&]() { return checkBox->isChecked(); };
        onContentChangedCallback = [&]() { return checkBox->isChecked(); };
    }

    void CheckBoxWithLabelItem::applyCallbacks()


@@ 76,11 76,16 @@ namespace gui
        inputCallback = [&](gui::Item &item, const gui::InputEvent &event) {
            if (checkBox->onInput(event)) {
                checkBox->resizeItems();
                onContentChangeCallback = [&]() { return checkBox->isChecked(); };
                onContentChangedCallback = [&]() { return checkBox->isChecked(); };
                return true;
            }
            return false;
        };

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            hBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void CheckBoxWithLabelItem::setCheckBoxes()


@@ 108,11 113,4 @@ namespace gui
        }
    }

    bool CheckBoxWithLabelItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        hBox->setPosition(0, 0);
        hBox->setSize(newDim.w, newDim.h);
        return true;
    }

} /* namespace gui */

M module-apps/application-calendar/widgets/CheckBoxWithLabelItem.hpp => module-apps/application-calendar/widgets/CheckBoxWithLabelItem.hpp +0 -3
@@ 30,9 30,6 @@ namespace gui

        gui::Label *descriptionLabel = nullptr;
        gui::CheckBox *checkBox      = nullptr;

        // virtual methods from Item
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/DayEventsItem.cpp => module-apps/application-calendar/widgets/DayEventsItem.cpp +5 -8
@@ 41,6 41,11 @@ namespace gui
        description->setEdges(gui::RectangleEdge::None);
        description->setFont(style::window::font::medium);
        description->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void DayEventsItem::setEvent(std::shared_ptr<EventsRecord> rec)


@@ 55,12 60,4 @@ namespace gui
            }
        }
    }

    bool DayEventsItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);
        return true;
    }

} /* namespace gui */

M module-apps/application-calendar/widgets/DayEventsItem.hpp => module-apps/application-calendar/widgets/DayEventsItem.hpp +0 -1
@@ 27,7 27,6 @@ namespace gui

        // virtual methods from Item
        void setEvent(std::shared_ptr<EventsRecord> record);
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/DayLabel.cpp => module-apps/application-calendar/widgets/DayLabel.cpp +5 -6
@@ 86,6 86,11 @@ namespace gui
            this->setPenFocusWidth(style::window::default_border_focus_w);
            this->setEdges(RectangleEdge::Top | RectangleEdge::Bottom);
        }

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    uint32_t DayLabel::getDayNumber()


@@ 105,10 110,4 @@ namespace gui
        }
    }

    bool DayLabel::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        this->vBox->setPosition(0, 0);
        this->vBox->setSize(newDim.w, newDim.h);
        return true;
    }
} /* namespace gui */

M module-apps/application-calendar/widgets/DayLabel.hpp => module-apps/application-calendar/widgets/DayLabel.hpp +0 -1
@@ 25,7 25,6 @@ namespace gui
                 bool isDayEmpty);
        ~DayLabel() override = default;

        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
        uint32_t getDayNumber();
    };


M module-apps/application-calendar/widgets/EventDateItem.cpp => module-apps/application-calendar/widgets/EventDateItem.cpp +4 -6
@@ 132,13 132,11 @@ namespace gui
        setOnInputCallback(*dayInput);
        setOnInputCallback(*monthInput);
        setOnInputCallback(*yearInput);
    }

    bool EventDateItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);
        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    calendar::YearMonthDay EventDateItem::validateDate()

M module-apps/application-calendar/widgets/EventDateItem.hpp => module-apps/application-calendar/widgets/EventDateItem.hpp +0 -2
@@ 34,8 34,6 @@ namespace gui
        EventDateItem();

        const calendar::YearMonthDay getChosenDate();
        // virtual methods from Item
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/EventDetailDescriptionItem.cpp => module-apps/application-calendar/widgets/EventDetailDescriptionItem.cpp +5 -7
@@ 64,14 64,12 @@ namespace gui
            return true;
        };

        descriptionHandler();
    }
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };

    bool EventDetailDescriptionItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);
        return true;
        descriptionHandler();
    }

    void EventDetailDescriptionItem::descriptionHandler()

M module-apps/application-calendar/widgets/EventDetailDescriptionItem.hpp => module-apps/application-calendar/widgets/EventDetailDescriptionItem.hpp +0 -2
@@ 23,8 23,6 @@ namespace gui
        virtual ~EventDetailDescriptionItem() override = default;

        void descriptionHandler();
        // virtual methods from Item
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/EventTimeItem.cpp => module-apps/application-calendar/widgets/EventTimeItem.cpp +5 -7
@@ 73,6 73,11 @@ namespace gui
            return true;
        };

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };

        applyInputCallbacks();
        prepareForTimeMode();
    }


@@ 253,13 258,6 @@ namespace gui
        }
    }

    bool EventTimeItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);
        return true;
    }

    void EventTimeItem::setConnectionToSecondItem(gui::EventTimeItem *item)
    {
        this->secondItem = item;

M module-apps/application-calendar/widgets/EventTimeItem.hpp => module-apps/application-calendar/widgets/EventTimeItem.hpp +0 -2
@@ 59,8 59,6 @@ namespace gui

        void setConnectionToSecondItem(gui::EventTimeItem *item);
        void setConnectionToDateItem(gui::EventDateItem *item);
        // virtual methods from Item
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/RepeatAndReminderItem.cpp => module-apps/application-calendar/widgets/RepeatAndReminderItem.cpp +5 -7
@@ 89,14 89,12 @@ namespace gui
        reminder->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        reminder->activeItem = false;

        descriptionHandler();
    }
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            hBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };

    bool RepeatAndReminderItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        hBox->setPosition(0, 0);
        hBox->setSize(newDim.w, newDim.h);
        return true;
        descriptionHandler();
    }

    void RepeatAndReminderItem::descriptionHandler()

M module-apps/application-calendar/widgets/RepeatAndReminderItem.hpp => module-apps/application-calendar/widgets/RepeatAndReminderItem.hpp +0 -1
@@ 30,7 30,6 @@ namespace gui
        virtual ~RepeatAndReminderItem() = default;

        void descriptionHandler();
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/SeveralOptionsItem.cpp => module-apps/application-calendar/widgets/SeveralOptionsItem.cpp +4 -7
@@ 218,13 218,10 @@ namespace gui
                optionLabel->setText(optionsNames[actualVectorIndex]);
            }
        };
    }

    bool SeveralOptionsItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);
        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

} /* namespace gui */

M module-apps/application-calendar/widgets/SeveralOptionsItem.hpp => module-apps/application-calendar/widgets/SeveralOptionsItem.hpp +0 -3
@@ 49,9 49,6 @@ namespace gui
        void prepareOptionsNames();
        void applyCallbacks();
        uint32_t repeatOptionValue = 0;

        // virtual methods from Item
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/TextWithLabelItem.cpp => module-apps/application-calendar/widgets/TextWithLabelItem.cpp +4 -7
@@ 63,13 63,10 @@ namespace gui
        };

        onLoadCallback = [&](std::shared_ptr<EventsRecord> event) { textInput->setText(event->title); };
    }

    bool TextWithLabelItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);
        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

} /* namespace gui */

M module-apps/application-calendar/widgets/TextWithLabelItem.hpp => module-apps/application-calendar/widgets/TextWithLabelItem.hpp +0 -3
@@ 21,9 21,6 @@ namespace gui
                          std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr,
                          std::function<void()> selectSpecialCharacter                 = nullptr);
        virtual ~TextWithLabelItem() override = default;

        // virtual methods from Item
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-calllog/widgets/CalllogItem.cpp => module-apps/application-calllog/widgets/CalllogItem.cpp +4 -7
@@ 50,14 50,11 @@ namespace gui
        timestamp->setEdges(gui::RectangleEdge::None);
        timestamp->setFont(style::window::font::small);
        timestamp->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Right, gui::Alignment::Vertical::Center});
    }

    bool CalllogItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        hBox->setPosition(0, 0);
        hBox->setSize(newDim.w, newDim.h);

        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            hBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void CalllogItem::setCall(std::shared_ptr<CalllogRecord> &call)

M module-apps/application-calllog/widgets/CalllogItem.hpp => module-apps/application-calllog/widgets/CalllogItem.hpp +0 -3
@@ 50,9 50,6 @@ namespace gui
        {
            return call != nullptr ? *call : CalllogRecord();
        };

        // virtual methods from Item
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-desktop/widgets/DesktopInputWidget.cpp => module-apps/application-desktop/widgets/DesktopInputWidget.cpp +4 -7
@@ 75,14 75,11 @@ namespace gui
        replyImage->setAlignment(Alignment(gui::Alignment::Vertical::Center));
        replyImage->setMargins(Margins(0, 0, 0, 0));
        replyImage->activeItem = false;
    }

    auto DesktopInputWidget::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool
    {
        body->setPosition(0, 0);
        body->setSize(newDim.w, newDim.h);

        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            body->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    auto DesktopInputWidget::handleRequestResize([[maybe_unused]] const Item *child,

M module-apps/application-desktop/widgets/DesktopInputWidget.hpp => module-apps/application-desktop/widgets/DesktopInputWidget.hpp +0 -1
@@ 27,7 27,6 @@ namespace gui
        DesktopInputWidget(app::Application *application, Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
        ~DesktopInputWidget() override = default;

        auto onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool override;
        auto handleRequestResize(const Item *, unsigned short request_w, unsigned short request_h) -> Size override;
    };


M module-apps/application-messages/widgets/SMSInputWidget.cpp => module-apps/application-messages/widgets/SMSInputWidget.cpp +5 -8
@@ 97,6 97,11 @@ namespace gui

            return true;
        };

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            body->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void SMSInputWidget::handleDraftMessage()


@@ 151,14 156,6 @@ namespace gui
        }
    }

    auto SMSInputWidget::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool
    {
        body->setPosition(0, 0);
        body->setSize(newDim.w, newDim.h);

        return true;
    }

    auto SMSInputWidget::handleRequestResize([[maybe_unused]] const Item *child,
                                             unsigned short request_w,
                                             unsigned short request_h) -> Size

M module-apps/application-messages/widgets/SMSInputWidget.hpp => module-apps/application-messages/widgets/SMSInputWidget.hpp +0 -1
@@ 33,7 33,6 @@ namespace gui
        void updateDraftMessage(const UTF8 &inputText);
        void displayDraftMessage() const;

        auto onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool override;
        auto handleRequestResize(const Item *, unsigned short request_w, unsigned short request_h) -> Size override;
    };


M module-apps/application-messages/widgets/SMSOutputWidget.cpp => module-apps/application-messages/widgets/SMSOutputWidget.cpp +9 -11
@@ 93,6 93,15 @@ namespace gui
            }
            return false;
        };

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            body->setArea({0, 0, newDim.w, newDim.h});

            // We need to calculate margin between sms and timeLabel and we can do it only after sizes are set.
            positionTimeLabel();

            return true;
        };
    }

    void SMSOutputWidget::positionTimeLabel() const


@@ 136,17 145,6 @@ namespace gui
        body->addWidget(errorIcon);
    }

    auto SMSOutputWidget::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool
    {
        body->setPosition(0, 0);
        body->setSize(newDim.w, newDim.h);

        // We need to calculate margin between sms and timeLabel and we can do it only after sizes are set.
        positionTimeLabel();

        return true;
    }

    auto SMSOutputWidget::handleRequestResize([[maybe_unused]] const Item *child,
                                              unsigned short request_w,
                                              unsigned short request_h) -> Size

M module-apps/application-messages/widgets/SMSOutputWidget.hpp => module-apps/application-messages/widgets/SMSOutputWidget.hpp +0 -1
@@ 31,7 31,6 @@ namespace gui
        SMSOutputWidget(app::Application *application, const std::shared_ptr<SMSRecord> &record);
        virtual ~SMSOutputWidget() = default;

        auto onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool override;
        auto handleRequestResize(const Item *, unsigned short request_w, unsigned short request_h) -> Size override;
    };


M module-apps/application-messages/widgets/SMSTemplateItem.cpp => module-apps/application-messages/widgets/SMSTemplateItem.cpp +4 -7
@@ 17,14 17,11 @@ namespace gui
        text = new gui::Label(this, 0, 0, 0, 0);
        style::window::decorateOption(text);
        text->setEllipsis(gui::Ellipsis::Right);
    }

    bool SMSTemplateItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        text->setPosition(0, 0);
        text->setSize(newDim.w, newDim.h);

        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            text->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void SMSTemplateItem::setTemplate(std::shared_ptr<SMSTemplateRecord> templ)

M module-apps/application-messages/widgets/SMSTemplateItem.hpp => module-apps/application-messages/widgets/SMSTemplateItem.hpp +0 -3
@@ 25,9 25,6 @@ namespace gui
        SMSTemplateItem();
        virtual ~SMSTemplateItem() = default;
        void setTemplate(std::shared_ptr<SMSTemplateRecord>);

        // virtual methods from Item
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-music-player/ApplicationMusicPlayer.cpp => module-apps/application-music-player/ApplicationMusicPlayer.cpp +27 -0
@@ 20,6 20,10 @@ namespace app
        : Application(name, parent, startInBackground, 4096)
    {
        LOG_INFO("ApplicationMusicPlayer::create");
        connect(typeid(AudioStartPlaybackResponse), [&](sys::Message *msg) {
            handlePlayResponse(msg);
            return sys::MessageNone{};
        });
    }

    ApplicationMusicPlayer::~ApplicationMusicPlayer()


@@ 27,6 31,12 @@ namespace app
        LOG_INFO("ApplicationMusicPlayer::destroy");
    }

    void ApplicationMusicPlayer::handlePlayResponse(sys::Message *msg)
    {
        auto startResponse = static_cast<AudioStartPlaybackResponse *>(msg);
        currentFileToken   = startResponse->token;
    }

    sys::MessagePointer ApplicationMusicPlayer::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)
    {
        return Application::DataReceivedHandler(msgl);


@@ 95,6 105,23 @@ namespace app
        return true;
    }

    bool ApplicationMusicPlayer::pause()
    {
        if (currentFileToken) {
            return AudioServiceAPI::Pause(this, currentFileToken.value());
        }
        return false;
    }

    bool ApplicationMusicPlayer::resume()
    {
        if (currentFileToken) {

            return AudioServiceAPI::Resume(this, currentFileToken.value());
        }
        return false;
    }

    std::optional<audio::Tags> ApplicationMusicPlayer::getFileTags(const std::string &filePath)
    {
        return AudioServiceAPI::GetFileTags(this, filePath);

M module-apps/application-music-player/ApplicationMusicPlayer.hpp => module-apps/application-music-player/ApplicationMusicPlayer.hpp +3 -0
@@ 26,6 26,7 @@ namespace app

    class ApplicationMusicPlayer : public Application
    {
        std::optional<audio::Token> currentFileToken;

      public:
        ApplicationMusicPlayer(std::string name                    = name_music_player,


@@ 51,6 52,8 @@ namespace app
        bool resume();
        bool stop();
        std::optional<audio::Tags> getFileTags(const std::string &filePath);

        void handlePlayResponse(sys::Message *msg);
    };

    template <> struct ManifestTraits<ApplicationMusicPlayer>

M module-apps/application-music-player/widgets/SongItem.cpp => module-apps/application-music-player/widgets/SongItem.cpp +4 -8
@@ 63,14 63,10 @@ namespace gui
        authorText->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        authorText->setEditMode(EditMode::Browse);
        authorText->setText(authorName);
    }

    auto SongItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);

        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

} /* namespace gui */

M module-apps/application-music-player/widgets/SongItem.hpp => module-apps/application-music-player/widgets/SongItem.hpp +0 -1
@@ 19,7 19,6 @@ namespace gui
        SongItem(std::string authorName, std::string songName, std::string duration);

        ~SongItem() override = default;
        auto onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool override;
        VBox *vBox                  = nullptr;
        HBox *firstHBox             = nullptr;
        HBox *secondHBox            = nullptr;

M module-apps/application-phonebook/widgets/ContactListItem.hpp => module-apps/application-phonebook/widgets/ContactListItem.hpp +1 -8
@@ 7,12 7,5 @@

namespace gui
{
    class ContactListItem : public ListItem
    {
      public:
        std::function<void(std::shared_ptr<ContactRecord> contact)> onSaveCallback = nullptr;
        std::function<void(std::shared_ptr<ContactRecord> contact)> onLoadCallback = nullptr;
        std::function<bool()> onEmptyCallback                                      = nullptr;
    };

    using ContactListItem = ListItemWithCallbacks<ContactRecord>;
} /* namespace gui */

M module-apps/application-phonebook/widgets/InformationWidget.cpp => module-apps/application-phonebook/widgets/InformationWidget.cpp +5 -7
@@ 106,13 106,11 @@ namespace gui
            return vBox->onInput(event);
        };

        setEdges(RectangleEdge::None);
    }
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };

    auto InformationWidget::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);
        return true;
        setEdges(RectangleEdge::None);
    }
} /* namespace gui */

M module-apps/application-phonebook/widgets/InformationWidget.hpp => module-apps/application-phonebook/widgets/InformationWidget.hpp +0 -1
@@ 19,7 19,6 @@ namespace gui
      public:
        InformationWidget(app::Application *app);
        ~InformationWidget() override = default;
        auto onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool override;
        VBox *vBox                               = nullptr;
        Label *titleLabel                        = nullptr;
        TextWithIconsWidget *primaryNumberHBox   = nullptr;

M module-apps/application-phonebook/widgets/InputBoxWithLabelAndIconWidget.cpp => module-apps/application-phonebook/widgets/InputBoxWithLabelAndIconWidget.cpp +4 -8
@@ 66,15 66,11 @@ namespace gui
        applyItemNameSpecificSettings();

        setEdges(gui::RectangleEdge::None);
    }

    auto InputBoxWithLabelAndIconWidget::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
        -> bool
    {
        hBox->setPosition(0, 0);
        hBox->setSize(newDim.w, newDim.h);

        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            hBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void InputBoxWithLabelAndIconWidget::applyItemNameSpecificSettings()

M module-apps/application-phonebook/widgets/InputBoxWithLabelAndIconWidget.hpp => module-apps/application-phonebook/widgets/InputBoxWithLabelAndIconWidget.hpp +0 -1
@@ 24,7 24,6 @@ namespace gui
                                       std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr,
                                       std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr);
        ~InputBoxWithLabelAndIconWidget() override = default;
        auto onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool override;
        gui::HBox *hBox              = nullptr;
        gui::Label *inputBoxLabel    = nullptr;
        gui::Label *descriptionLabel = nullptr;

M module-apps/application-phonebook/widgets/InputLinesWithLabelIWidget.cpp => module-apps/application-phonebook/widgets/InputLinesWithLabelIWidget.cpp +5 -7
@@ 80,15 80,13 @@ namespace gui
            }
            return result;
        };
        setEdges(RectangleEdge::None);
    }

    auto InputLinesWithLabelIWidget::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };

        return true;
        setEdges(RectangleEdge::None);
    }

    void InputLinesWithLabelIWidget::applyItemNameSpecificSettings()

M module-apps/application-phonebook/widgets/InputLinesWithLabelIWidget.hpp => module-apps/application-phonebook/widgets/InputLinesWithLabelIWidget.hpp +0 -1
@@ 26,7 26,6 @@ namespace gui
                                   unsigned int lines                                           = 1);

        ~InputLinesWithLabelIWidget() override = default;
        auto onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool override;
        VBox *vBox               = nullptr;
        Label *titleLabel        = nullptr;
        TextFixedSize *inputText = nullptr;

M module-apps/application-phonebook/widgets/OutputLinesTextWithLabelWidget.cpp => module-apps/application-phonebook/widgets/OutputLinesTextWithLabelWidget.cpp +5 -9
@@ 46,19 46,15 @@ namespace gui
            return true;
        };

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };

        this->activeItem = false;
        setEdges(RectangleEdge::All);
    }

    auto OutputLinesTextWithLabelWidget::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
        -> bool
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);

        return true;
    }

    void OutputLinesTextWithLabelWidget::applyItemNameSpecificSettings()
    {
        switch (listItemName) {

M module-apps/application-phonebook/widgets/OutputLinesTextWithLabelWidget.hpp => module-apps/application-phonebook/widgets/OutputLinesTextWithLabelWidget.hpp +0 -1
@@ 20,7 20,6 @@ namespace gui
        OutputLinesTextWithLabelWidget(phonebookInternals::ListItemName listItemName);

        ~OutputLinesTextWithLabelWidget() override = default;
        auto onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool override;
        VBox *vBox          = nullptr;
        Label *titleLabel   = nullptr;
        Text *multilineText = nullptr;

M module-apps/application-phonebook/widgets/PhonebookItem.cpp => module-apps/application-phonebook/widgets/PhonebookItem.cpp +4 -6
@@ 29,13 29,11 @@ namespace gui
        contactName->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        contactName->setMinimumHeight(phonebookStyle::contactItem::h);
        contactName->setMaximumWidth(phonebookStyle::contactItem::w);
    }

    bool PhonebookItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        hBox->setPosition(0, 0);
        hBox->setSize(newDim.w, newDim.h);
        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            hBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    // sets copy of alarm's

M module-apps/application-phonebook/widgets/PhonebookItem.hpp => module-apps/application-phonebook/widgets/PhonebookItem.hpp +0 -1
@@ 34,7 34,6 @@ namespace gui
        void setMarkerItem(UTF8 text);
        UTF8 getLabelMarker();

        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
        void setLabelMarkerDisplayMode(LabelMarkerDisplayMode mode);
    };


M module-apps/application-settings-new/widgets/ApnInputWidget.cpp => module-apps/application-settings-new/widgets/ApnInputWidget.cpp +5 -7
@@ 79,15 79,13 @@ namespace gui
            }
            return result;
        };
        setEdges(RectangleEdge::None);
    }

    auto ApnInputWidget::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };

        return true;
        setEdges(RectangleEdge::None);
    }

    void ApnInputWidget::applyItemNameSpecificSettings()

M module-apps/application-settings-new/widgets/ApnInputWidget.hpp => module-apps/application-settings-new/widgets/ApnInputWidget.hpp +0 -1
@@ 31,7 31,6 @@ namespace gui
        std::function<void()> checkTextContent = nullptr;

        void applyItemNameSpecificSettings();
        auto onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool override;
        void nameHandler();
        void apnHandler();
        void usernameHandler();

M module-apps/application-settings-new/widgets/ApnListItem.hpp => module-apps/application-settings-new/widgets/ApnListItem.hpp +1 -8
@@ 7,12 7,5 @@

namespace gui
{
    class ApnListItem : public ListItem
    {
      public:
        std::function<void(std::shared_ptr<packet_data::APN::Config> apnRecord)> onSaveCallback = nullptr;
        std::function<void(std::shared_ptr<packet_data::APN::Config> apnRecord)> onLoadCallback = nullptr;
        std::function<bool()> onEmptyCallback                                                   = nullptr;
    };

    using ApnListItem = ListItemWithCallbacks<packet_data::APN::Config>;
} /* namespace gui */

M module-apps/application-settings-new/widgets/QuoteWidget.cpp => module-apps/application-settings-new/widgets/QuoteWidget.cpp +5 -9
@@ 103,15 103,11 @@ namespace gui
            return true;
        };

        setEdges(gui::RectangleEdge::None);
    }

    auto QuoteWidget::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool
    {
        hBox->setPosition(0, 0);
        hBox->setSize(newDim.w, newDim.h);
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            hBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };

        return true;
        setEdges(gui::RectangleEdge::None);
    }

} /* namespace gui */

M module-apps/application-settings-new/widgets/QuoteWidget.hpp => module-apps/application-settings-new/widgets/QuoteWidget.hpp +0 -2
@@ 20,8 20,6 @@ namespace gui
                    std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr,
                    std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr);

        auto onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool override;

        [[nodiscard]] auto getQuoteData() const -> app::QuoteRecord
        {
            return quote;

M module-apps/application-settings/widgets/ColorTestListItem.cpp => module-apps/application-settings/widgets/ColorTestListItem.cpp +4 -6
@@ 31,13 31,11 @@ namespace gui
        else {
            colorLabel->setTextColor(ColorFullWhite);
        }
    }

    bool ColorTestListItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);
        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    bool ColorTestListItem::onInput(const InputEvent &inputEvent)

M module-apps/application-settings/widgets/ColorTestListItem.hpp => module-apps/application-settings/widgets/ColorTestListItem.hpp +0 -1
@@ 25,7 25,6 @@ namespace gui
        explicit ColorTestListItem(app::Application *app, const Color color);

        bool onInput(const InputEvent &inputEvent) override;
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
        [[nodiscard]] uint8_t getColorIntensity();
    };
} /* namespace gui */

M module-apps/application-special-input/widgets/SpecialInputTableWidget.cpp => module-apps/application-special-input/widgets/SpecialInputTableWidget.cpp +5 -6
@@ 61,13 61,12 @@ namespace gui
            }
            return true;
        };
    }

    auto SpecialInputTableWidget::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool
    {
        box->setPosition(0, 0);
        box->setSize(specialCharacterTableWidget::window_grid_w, specialCharacterTableWidget::window_grid_h);
        return true;
        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            box->setArea(
                {0, 0, specialCharacterTableWidget::window_grid_w, specialCharacterTableWidget::window_grid_h});
            return true;
        };
    }

    void SpecialInputTableWidget::decorateActionActivated(Item *it, const std::string &str)

M module-apps/application-special-input/widgets/SpecialInputTableWidget.hpp => module-apps/application-special-input/widgets/SpecialInputTableWidget.hpp +0 -1
@@ 33,7 33,6 @@ namespace gui

      public:
        SpecialInputTableWidget(app::Application *app, std::list<Carrier> &&carier);
        auto onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool override;
    };

} /* namespace gui */

M module-apps/windows/AppWindow.cpp => module-apps/windows/AppWindow.cpp +2 -11
@@ 73,19 73,10 @@ namespace gui
        return true;
    }

    bool AppWindow::updateBatteryCharger(bool charging)
    bool AppWindow::updateBatteryStatus()
    {
        topBar->updateBattery(charging);
        return true;
    }

    // updates battery level in the window
    bool AppWindow::updateBatteryLevel(uint32_t percentage)
    {
        // get old value of battery level, calcualte new level and comapre both
        // if they are different make a change and return true, otherwise return false;
        if (topBar != nullptr) {
            return topBar->updateBattery(percentage);
            return topBar->updateBattery();
        }
        return false;
    }

M module-apps/windows/AppWindow.hpp => module-apps/windows/AppWindow.hpp +1 -3
@@ 62,11 62,9 @@ namespace gui

        virtual bool onDatabaseMessage(sys::Message *msg);

        bool updateBatteryCharger(bool charging);
        bool setSIM();
        // updates battery level in the window
        bool updateBatteryLevel(uint32_t percentage);
        // updates battery level in the window
        bool updateBatteryStatus();
        bool updateSignalStrength();
        bool updateNetworkAccessTechnology();
        virtual bool updateTime(const UTF8 &timeStr);

M module-audio/Audio/AudioCommon.cpp => module-audio/Audio/AudioCommon.cpp +3 -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 "AudioCommon.hpp"


@@ 8,9 8,9 @@

namespace audio
{
    audio::RetCode GetDeviceError(bsp::AudioDevice::RetCode retCode)
    audio::RetCode GetDeviceError(AudioDevice::RetCode retCode)
    {
        if (retCode == bsp::AudioDevice::RetCode::Success) {
        if (retCode == AudioDevice::RetCode::Success) {
            return RetCode::Success;
        }


M module-audio/Audio/AudioCommon.hpp => module-audio/Audio/AudioCommon.hpp +8 -7
@@ 1,16 1,17 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "AudioDevice.hpp"
#include "Profiles/Profile.hpp"

#include <Service/Message.hpp>
#include <Utils.hpp>

#include <map>
#include <bitset>
#include <bsp/audio/bsp_audio.hpp>
#include <Utils.hpp>
#include <utility>
#include <Service/Message.hpp>

#include "Profiles/Profile.hpp"

namespace audio
{


@@ 243,7 244,7 @@ namespace audio
        friend class ::audio::AudioMux;
    };

    RetCode GetDeviceError(bsp::AudioDevice::RetCode retCode);
    RetCode GetDeviceError(AudioDevice::RetCode retCode);
    const std::string str(RetCode retcode);
    [[nodiscard]] auto GetVolumeText(const audio::Volume &volume) -> std::string;
} // namespace audio

R module-bsp/bsp/audio/bsp_audio.hpp => module-audio/Audio/AudioDevice.hpp +14 -18
@@ 1,3 1,6 @@
// 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 <Audio/Endpoint.hpp>


@@ 6,7 9,7 @@
#include <memory>
#include <functional>

namespace bsp
namespace audio
{

    class AudioDevice : public audio::IOProxy


@@ 27,35 30,30 @@ namespace bsp
            Bluetooth
        };

        enum class Flags
        {
            OutputMono   = 1 << 0,
            OutputStereo = 1 << 1,
            InputLeft    = 1 << 2,
            InputRight   = 1 << 3,
            InputStereo  = 1 << 4
        };

        enum class InputPath
        {
            Headphones,
            Microphone,
            BluetoothHSP,
            None
        };

        enum class OutputPath
        {
            Headphones,
            HeadphonesMono,
            Earspeaker,
            Loudspeaker,
            LoudspeakerMono,
            BluetoothA2DP,
            BluetoothHSP,
            None
        };

        enum class Flags
        {
            OutputMono   = 1 << 0,
            OutputStereo = 1 << 1,
            InputLeft    = 1 << 2,
            InputRight   = 1 << 3,
            InputStereo  = 1 << 4
        };

        using Format = struct
        {
            uint32_t sampleRate_Hz = 0; /*!< Sample rate of audio data */


@@ 75,8 73,6 @@ namespace bsp

        virtual ~AudioDevice() = default;

        static std::optional<std::unique_ptr<AudioDevice>> Create(Type type);

        virtual RetCode Start(const Format &format) = 0;
        virtual RetCode Stop()                      = 0;



@@ 116,4 112,4 @@ namespace bsp

        bool isInitialized = false;
    };
} // namespace bsp
} // namespace audio

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

#include "AudioDeviceFactory.hpp"

using namespace audio;

AudioDeviceFactory::AudioDeviceFactory(Observer *observer) : _observer(observer)
{}

std::shared_ptr<AudioDevice> AudioDeviceFactory::CreateDevice(AudioDevice::Type deviceType)
{
    std::shared_ptr<AudioDevice> device = getDeviceFromType(deviceType);

    if (_observer != nullptr && device) {
        _observer->onDeviceCreated(device);
    }

    return device;
}

void AudioDeviceFactory::setObserver(Observer *observer) noexcept
{
    _observer = observer;
}

A module-audio/Audio/AudioDeviceFactory.hpp => module-audio/Audio/AudioDeviceFactory.hpp +34 -0
@@ 0,0 1,34 @@
// Copyright (c) 2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "AudioDevice.hpp"

#include <memory>

namespace audio
{

    class AudioDeviceFactory
    {
        class Observer
        {
          public:
            virtual void onDeviceCreated(std::shared_ptr<AudioDevice> device) = 0;
        };

      public:
        explicit AudioDeviceFactory(Observer *observer = nullptr);

        void setObserver(Observer *observer) noexcept;
        std::shared_ptr<AudioDevice> CreateDevice(AudioDevice::Type);

      protected:
        virtual std::shared_ptr<AudioDevice> getDeviceFromType(AudioDevice::Type) = 0;

      private:
        Observer *_observer = nullptr;
    };

}; // namespace audio

A module-audio/Audio/AudioPlatform.hpp => module-audio/Audio/AudioPlatform.hpp +18 -0
@@ 0,0 1,18 @@
// Copyright (c) 2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "AudioDeviceFactory.hpp"

#include <memory>

namespace audio
{
    class AudioPlatform
    {
      public:
        static std::unique_ptr<AudioDeviceFactory> GetDeviceFactory();
    };

}; // namespace audio

D module-audio/Audio/BluetoothProxyAudio.cpp => module-audio/Audio/BluetoothProxyAudio.cpp +0 -93
@@ 1,93 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 "BluetoothProxyAudio.hpp"
#include <service-bluetooth/BluetoothMessage.hpp>

namespace bsp
{
    BluetoothProxyAudio::BluetoothProxyAudio(AudioServiceMessage::Callback callback,
                                             audio::Stream &dataStreamOut,
                                             audio::Stream &dataStreamIn,
                                             AudioDevice::Format &format)
        : dataStreamOut(dataStreamOut), dataStreamIn(dataStreamIn), serviceCallback(std::move(callback)),
          audioFormat(format)
    {
        LOG_DEBUG("BluetoothProxyAudio created.");
    }

    AudioDevice::RetCode BluetoothProxyAudio::Start(const AudioDevice::Format &format)
    {
        auto msg = BluetoothProxyStartMessage(dataStreamOut, dataStreamIn, format);
        serviceCallback(&msg);
        return AudioDevice::RetCode::Success;
    }

    AudioDevice::RetCode BluetoothProxyAudio::Stop()
    {
        auto msg = BluetoothProxyStopMessage(audioFormat);
        serviceCallback(&msg);
        return AudioDevice::RetCode::Success;
    }

    AudioDevice::RetCode BluetoothProxyAudio::OutputVolumeCtrl(float vol)
    {
        audioFormat.outputVolume = vol;
        auto msg                 = BluetoothProxySetVolumeMessage(audioFormat);
        serviceCallback(&msg);
        return AudioDevice::RetCode::Success;
    }

    AudioDevice::RetCode BluetoothProxyAudio::InputGainCtrl(float gain)
    {
        audioFormat.inputGain = gain;
        auto msg              = BluetoothProxySetGainMessage(audioFormat);
        serviceCallback(&msg);
        return AudioDevice::RetCode::Success;
    }

    AudioDevice::RetCode BluetoothProxyAudio::OutputPathCtrl(AudioDevice::OutputPath outputPath)
    {
        audioFormat.outputPath = outputPath;
        auto msg               = BluetoothProxySetOutputPathMessage(audioFormat);
        serviceCallback(&msg);
        return AudioDevice::RetCode::Success;
    }

    AudioDevice::RetCode BluetoothProxyAudio::InputPathCtrl(AudioDevice::InputPath inputPath)
    {
        audioFormat.inputPath = inputPath;
        auto msg              = BluetoothProxySetInputPathMessage(audioFormat);
        serviceCallback(&msg);
        return AudioDevice::RetCode::Success;
    }

    bool BluetoothProxyAudio::IsFormatSupported(const AudioDevice::Format &format)
    {
        LOG_DEBUG("Format assumed to be supported");
        return true;
    }

    BluetoothProxyAudio::~BluetoothProxyAudio()
    {
        Stop();
    }

    void BluetoothProxyAudio::onDataReceive()
    {}

    void BluetoothProxyAudio::onDataSend()
    {}

    void BluetoothProxyAudio::enableInput()
    {}

    void BluetoothProxyAudio::enableOutput()
    {}

    void BluetoothProxyAudio::disableInput()
    {}

    void BluetoothProxyAudio::disableOutput()
    {}
} // namespace bsp

D module-audio/Audio/BluetoothProxyAudio.hpp => module-audio/Audio/BluetoothProxyAudio.hpp +0 -45
@@ 1,45 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "AudioCommon.hpp"
#include "Stream.hpp"

#include "bsp/audio/bsp_audio.hpp"

namespace bsp
{
    class BluetoothProxyAudio final : public AudioDevice
    {
      public:
        BluetoothProxyAudio(AudioServiceMessage::Callback callback,
                            audio::Stream &dataStreamOut,
                            audio::Stream &dataStreamIn,
                            AudioDevice::Format &format);

        ~BluetoothProxyAudio() final;

        AudioDevice::RetCode Start(const Format &format) final;
        AudioDevice::RetCode Stop() final;
        AudioDevice::RetCode OutputVolumeCtrl(float vol) final;
        AudioDevice::RetCode InputGainCtrl(float gain) final;
        AudioDevice::RetCode OutputPathCtrl(OutputPath outputPath) final;
        AudioDevice::RetCode InputPathCtrl(InputPath inputPath) final;
        bool IsFormatSupported(const Format &format) final;

        void onDataReceive() final;
        void onDataSend() final;
        void enableInput() final;
        void enableOutput() final;
        void disableInput() final;
        void disableOutput() final;

      private:
        audio::Stream &dataStreamOut;
        audio::Stream &dataStreamIn;
        AudioServiceMessage::Callback serviceCallback;
        AudioDevice::Format audioFormat;
    };

} // namespace bsp

M module-audio/Audio/Operation/IdleOperation.cpp => module-audio/Audio/Operation/IdleOperation.cpp +1 -1
@@ 10,7 10,7 @@ namespace audio

    IdleOperation::IdleOperation([[maybe_unused]] const char *file) : Operation(nullptr)
    {
        supportedProfiles.emplace_back(Profile::Create(Profile::Type::Idle, nullptr), true);
        supportedProfiles.emplace_back(Profile::Create(Profile::Type::Idle), true);
        currentProfile = supportedProfiles[0].profile;
    }


M module-audio/Audio/Operation/Operation.cpp => module-audio/Audio/Operation/Operation.cpp +9 -7
@@ 5,15 5,15 @@

#include <algorithm>

#include "Audio/AudioDevice.hpp"
#include "Audio/AudioDeviceFactory.hpp"
#include "Audio/AudioPlatform.hpp"

#include "IdleOperation.hpp"
#include "PlaybackOperation.hpp"
#include "RecorderOperation.hpp"
#include "RouterOperation.hpp"

#include "Audio/BluetoothProxyAudio.hpp"

#include <bsp/audio/bsp_audio.hpp>

namespace audio
{
    std::unique_ptr<Operation> Operation::Create(Operation::Type t,


@@ 91,11 91,13 @@ namespace audio
            gain = utils::getNumericValue<audio::Gain>(val.value());
        }

        supportedProfiles.emplace_back(Profile::Create(profile, nullptr, volume, gain), isAvailable);
        supportedProfiles.emplace_back(Profile::Create(profile, volume, gain), isAvailable);
    }

    std::optional<std::unique_ptr<bsp::AudioDevice>> Operation::CreateDevice(bsp::AudioDevice::Type type)
    std::shared_ptr<AudioDevice> Operation::CreateDevice(AudioDevice::Type type)
    {
        return bsp::AudioDevice::Create(type).value_or(nullptr);
        auto factory = AudioPlatform::GetDeviceFactory();

        return factory->CreateDevice(type);
    }
} // namespace audio

M module-audio/Audio/Operation/Operation.hpp => module-audio/Audio/Operation/Operation.hpp +2 -3
@@ 4,7 4,6 @@
#pragma once

#include <memory>
#include <optional>
#include <functional>

#include <Audio/AudioCommon.hpp>


@@ 123,7 122,7 @@ namespace audio
        };

        std::shared_ptr<Profile> currentProfile;
        std::unique_ptr<bsp::AudioDevice> audioDevice;
        std::shared_ptr<AudioDevice> audioDevice;
        std::vector<SupportedProfile> supportedProfiles;

        State state = State::Idle;


@@ 142,7 141,7 @@ namespace audio
        virtual audio::RetCode SwitchProfile(const Profile::Type type) = 0;
        std::shared_ptr<Profile> GetProfile(const Profile::Type type);

        std::optional<std::unique_ptr<bsp::AudioDevice>> CreateDevice(bsp::AudioDevice::Type type);
        std::shared_ptr<AudioDevice> CreateDevice(AudioDevice::Type type);
    };

} // namespace audio

M module-audio/Audio/Operation/PlaybackOperation.cpp => module-audio/Audio/Operation/PlaybackOperation.cpp +3 -6
@@ 163,7 163,7 @@ namespace audio
        dec->stopDecodingWorker();
        audioDevice.reset();
        dataStreamOut.reset();
        audioDevice = CreateDevice(newProfile->GetAudioDeviceType()).value_or(nullptr);
        audioDevice = CreateDevice(newProfile->GetAudioDeviceType());
        if (audioDevice == nullptr) {
            LOG_ERROR("Error creating AudioDevice");
            return RetCode::Failed;


@@ 172,13 172,10 @@ namespace audio
        // adjust new profile with information from file's tags
        newProfile->SetSampleRate(tags->sample_rate);
        if (tags->num_channel == channel::stereoSound) {
            newProfile->SetInOutFlags(static_cast<uint32_t>(bsp::AudioDevice::Flags::OutputStereo));
            newProfile->SetInOutFlags(static_cast<uint32_t>(AudioDevice::Flags::OutputStereo));
        }
        else {
            newProfile->SetInOutFlags(static_cast<uint32_t>(bsp::AudioDevice::Flags::OutputMono));
            if (newProfile->GetOutputPath() == bsp::AudioDevice::OutputPath::Headphones) {
                newProfile->SetOutputPath(bsp::AudioDevice::OutputPath::HeadphonesMono);
            }
            newProfile->SetInOutFlags(static_cast<uint32_t>(AudioDevice::Flags::OutputMono));
        }

        // store profile

M module-audio/Audio/Operation/RecorderOperation.cpp => module-audio/Audio/Operation/RecorderOperation.cpp +3 -2
@@ 2,8 2,9 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RecorderOperation.hpp"

#include "Audio/AudioDevice.hpp"
#include "Audio/encoder/Encoder.hpp"
#include "bsp/audio/bsp_audio.hpp"
#include "Audio/Profiles/Profile.hpp"
#include "Audio/Profiles/ProfileRecordingHeadphones.hpp"
#include "Audio/Profiles/ProfileRecordingOnBoardMic.hpp"


@@ 157,7 158,7 @@ namespace audio
            return RetCode::UnsupportedProfile;
        }

        audioDevice = CreateDevice(currentProfile->GetAudioDeviceType()).value_or(nullptr);
        audioDevice = CreateDevice(currentProfile->GetAudioDeviceType());
        if (audioDevice == nullptr) {
            LOG_ERROR("Error creating AudioDevice");
            return RetCode::Failed;

M module-audio/Audio/Operation/RecorderOperation.hpp => module-audio/Audio/Operation/RecorderOperation.hpp +2 -2
@@ 1,11 1,11 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "Operation.hpp"
#include <Audio/encoder/Encoder.hpp>
#include <bsp/audio/bsp_audio.hpp>
#include <Audio/AudioDevice.hpp>

namespace audio
{

M module-audio/Audio/Operation/RouterOperation.cpp => module-audio/Audio/Operation/RouterOperation.cpp +5 -6
@@ 3,11 3,11 @@

#include "RouterOperation.hpp"

#include <Audio/AudioDevice.hpp>
#include <Audio/AudioCommon.hpp>
#include <Audio/Profiles/Profile.hpp>
#include <Audio/StreamFactory.hpp>

#include <bsp_audio.hpp>
#include <log/log.hpp>
#include <mutex.hpp>



@@ 60,13 60,12 @@ namespace audio
        }

        // try to run devices with the format
        if (auto ret = audioDevice->Start(currentProfile->GetAudioFormat());
            ret != bsp::AudioDevice::RetCode::Success) {
        if (auto ret = audioDevice->Start(currentProfile->GetAudioFormat()); ret != AudioDevice::RetCode::Success) {
            return GetDeviceError(ret);
        }

        if (auto ret = audioDeviceCellular->Start(currentProfile->GetAudioFormat());
            ret != bsp::AudioDevice::RetCode::Success) {
            ret != AudioDevice::RetCode::Success) {
            return GetDeviceError(ret);
        }



@@ 180,13 179,13 @@ namespace audio
            Stop();
        }

        audioDevice = CreateDevice(newProfile->GetAudioDeviceType()).value_or(nullptr);
        audioDevice = CreateDevice(newProfile->GetAudioDeviceType());
        if (audioDevice == nullptr) {
            LOG_ERROR("Error creating AudioDevice");
            return RetCode::Failed;
        }

        audioDeviceCellular = CreateDevice(bsp::AudioDevice::Type::Cellular).value_or(nullptr);
        audioDeviceCellular = CreateDevice(AudioDevice::Type::Cellular);
        if (audioDeviceCellular == nullptr) {
            LOG_ERROR("Error creating AudioDeviceCellular");
            return RetCode::Failed;

M module-audio/Audio/Operation/RouterOperation.hpp => module-audio/Audio/Operation/RouterOperation.hpp +3 -2
@@ 5,11 5,12 @@

#include "Operation.hpp"

#include <Audio/AudioDevice.hpp>
#include <Audio/encoder/Encoder.hpp>
#include <Audio/AudioCommon.hpp>
#include <Audio/Profiles/Profile.hpp>
#include <Audio/Endpoint.hpp>
#include <bsp/audio/bsp_audio.hpp>

#include <mutex.hpp>

#include <memory>


@@ 53,7 54,7 @@ namespace audio
        std::unique_ptr<Stream> dataStreamOut;
        std::unique_ptr<Stream> dataStreamIn;
        std::unique_ptr<Encoder> enc;
        std::unique_ptr<bsp::AudioDevice> audioDeviceCellular;
        std::shared_ptr<AudioDevice> audioDeviceCellular;
        std::unique_ptr<StreamConnection> outputConnection;
        std::unique_ptr<StreamConnection> inputConnection;
    };

M module-audio/Audio/Profiles/Profile.cpp => module-audio/Audio/Profiles/Profile.cpp +17 -33
@@ 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 "Profile.hpp"


@@ 24,53 24,50 @@
namespace audio
{

    std::unique_ptr<Profile> Profile::Create(const Type t,
                                             std::function<int32_t()> callback,
                                             std::optional<Volume> vol,
                                             std::optional<Gain> gain)
    std::unique_ptr<Profile> Profile::Create(const Type t, std::optional<Volume> vol, std::optional<Gain> gain)
    {
        std::unique_ptr<Profile> inst;

        switch (t) {
        case Type::PlaybackHeadphones:
            assert(vol);
            inst = std::make_unique<ProfilePlaybackHeadphones>(callback, vol.value());
            inst = std::make_unique<ProfilePlaybackHeadphones>(vol.value());
            break;
        case Type::PlaybackLoudspeaker:
            assert(vol);
            inst = std::make_unique<ProfilePlaybackLoudspeaker>(callback, vol.value());
            inst = std::make_unique<ProfilePlaybackLoudspeaker>(vol.value());
            break;
        case Type::PlaybackBluetoothA2DP:
            assert(vol);
            inst = std::make_unique<ProfilePlaybackBluetoothA2DP>(callback, vol.value());
            inst = std::make_unique<ProfilePlaybackBluetoothA2DP>(vol.value());
            break;
        case Type::RecordingBuiltInMic:
            assert(gain);
            inst = std::make_unique<ProfileRecordingOnBoardMic>(callback, gain.value());
            inst = std::make_unique<ProfileRecordingOnBoardMic>(gain.value());
            break;
        case Type::RecordingHeadphones:
            assert(gain);
            inst = std::make_unique<ProfileRecordingHeadphones>(callback, gain.value());
            inst = std::make_unique<ProfileRecordingHeadphones>(gain.value());
            break;
        case Type::RecordingBluetoothHSP:
            assert(gain);
            inst = std::make_unique<ProfileRecordingBluetoothHSP>(callback, gain.value());
            inst = std::make_unique<ProfileRecordingBluetoothHSP>(gain.value());
            break;
        case Type::RoutingHeadphones:
            assert(gain && vol);
            inst = std::make_unique<ProfileRoutingHeadphones>(callback, vol.value(), gain.value());
            inst = std::make_unique<ProfileRoutingHeadphones>(vol.value(), gain.value());
            break;
        case Type::RoutingLoudspeaker:
            assert(gain && vol);
            inst = std::make_unique<ProfileRoutingLoudspeaker>(callback, vol.value(), gain.value());
            inst = std::make_unique<ProfileRoutingLoudspeaker>(vol.value(), gain.value());
            break;
        case Type::RoutingEarspeaker:
            assert(gain && vol);
            inst = std::make_unique<ProfileRoutingEarspeaker>(callback, vol.value(), gain.value());
            inst = std::make_unique<ProfileRoutingEarspeaker>(vol.value(), gain.value());
            break;
        case Type::RoutingBluetoothHSP:
            assert(gain && vol);
            inst = std::make_unique<ProfileRoutingBluetoothHSP>(callback, vol.value(), gain.value());
            inst = std::make_unique<ProfileRoutingBluetoothHSP>(vol.value(), gain.value());
            break;
        case Type::Idle:
            inst = std::make_unique<ProfileIdle>();


@@ 82,42 79,29 @@ namespace audio

    Profile::Profile(const std::string &name,
                     const Type type,
                     const bsp::AudioDevice::Format &fmt,
                     bsp::AudioDevice::Type devType,
                     std::function<int32_t()> callback)
        : audioFormat(fmt), audioDeviceType(devType), name(name), type(type), dbAccessCallback(callback)
                     const AudioDevice::Format &fmt,
                     AudioDevice::Type devType)
        : audioFormat(fmt), audioDeviceType(devType), name(name), type(type)
    {}

    void Profile::SetInputGain(Gain gain)
    {
        audioFormat.inputGain = gain;
        if (dbAccessCallback) {
            dbAccessCallback();
        }
    }

    void Profile::SetOutputVolume(Volume vol)
    {
        audioFormat.outputVolume = vol;
        if (dbAccessCallback) {
            dbAccessCallback();
        }
    }

    void Profile::SetInputPath(bsp::AudioDevice::InputPath path)
    void Profile::SetInputPath(AudioDevice::InputPath path)
    {
        audioFormat.inputPath = path;
        if (dbAccessCallback) {
            dbAccessCallback();
        }
    }

    void Profile::SetOutputPath(bsp::AudioDevice::OutputPath path)
    void Profile::SetOutputPath(AudioDevice::OutputPath path)
    {
        audioFormat.outputPath = path;
        if (dbAccessCallback) {
            dbAccessCallback();
        }
    }

    void Profile::SetInOutFlags(uint32_t flags)

M module-audio/Audio/Profiles/Profile.hpp => module-audio/Audio/Profiles/Profile.hpp +14 -21
@@ 1,14 1,14 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "Audio/AudioDevice.hpp"

#include <memory>
#include <functional>
#include <string>

#include <bsp/audio/bsp_audio.hpp>

namespace audio
{
    using Volume      = uint32_t;


@@ 43,9 43,8 @@ namespace audio
        };

        static std::unique_ptr<Profile> Create(const Type t,
                                               std::function<int32_t()> callback = nullptr,
                                               std::optional<Volume> vol         = 0,
                                               std::optional<Gain> gain          = 0);
                                               std::optional<Volume> vol = 0,
                                               std::optional<Gain> gain  = 0);

        void SetOutputVolume(Volume vol);



@@ 55,9 54,9 @@ namespace audio

        void SetSampleRate(uint32_t samplerate);

        void SetOutputPath(bsp::AudioDevice::OutputPath path);
        void SetOutputPath(AudioDevice::OutputPath path);

        void SetInputPath(bsp::AudioDevice::InputPath path);
        void SetInputPath(AudioDevice::InputPath path);

        Volume GetOutputVolume() const
        {


@@ 79,22 78,22 @@ namespace audio
            return audioFormat.flags;
        }

        bsp::AudioDevice::OutputPath GetOutputPath() const
        AudioDevice::OutputPath GetOutputPath() const
        {
            return audioFormat.outputPath;
        }

        bsp::AudioDevice::InputPath GetInputPath() const
        AudioDevice::InputPath GetInputPath() const
        {
            return audioFormat.inputPath;
        }

        bsp::AudioDevice::Type GetAudioDeviceType() const
        AudioDevice::Type GetAudioDeviceType() const
        {
            return audioDeviceType;
        }

        bsp::AudioDevice::Format GetAudioFormat()
        AudioDevice::Format GetAudioFormat()
        {
            return audioFormat;
        }


@@ 110,19 109,13 @@ namespace audio
        }

      protected:
        Profile(const std::string &name,
                const Type type,
                const bsp::AudioDevice::Format &fmt,
                bsp::AudioDevice::Type devType,
                std::function<int32_t()> callback);
        Profile(const std::string &name, const Type type, const AudioDevice::Format &fmt, AudioDevice::Type devType);

        bsp::AudioDevice::Format audioFormat{};
        bsp::AudioDevice::Type audioDeviceType = bsp::AudioDevice::Type::Audiocodec;
        AudioDevice::Format audioFormat{};
        AudioDevice::Type audioDeviceType = AudioDevice::Type::Audiocodec;

        std::string name;
        Type type = Type::Idle;

        std::function<int32_t()> dbAccessCallback = nullptr;
    };

    [[nodiscard]] const std::string str(const Profile::Type &profileType);

M module-audio/Audio/Profiles/ProfileIdle.hpp => module-audio/Audio/Profiles/ProfileIdle.hpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 11,7 11,7 @@ namespace audio
    class ProfileIdle : public Profile
    {
      public:
        ProfileIdle() : Profile("Idle", Type::Idle, bsp::AudioDevice::Format{}, bsp::AudioDevice::Type::None, nullptr)
        ProfileIdle() : Profile("Idle", Type::Idle, AudioDevice::Format{}, AudioDevice::Type::None)
        {}
    };


M module-audio/Audio/Profiles/ProfilePlaybackBluetoothA2DP.hpp => module-audio/Audio/Profiles/ProfilePlaybackBluetoothA2DP.hpp +10 -11
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 11,18 11,17 @@ namespace audio
    class ProfilePlaybackBluetoothA2DP : public Profile
    {
      public:
        ProfilePlaybackBluetoothA2DP(std::function<int32_t()> callback, Volume volume)
        ProfilePlaybackBluetoothA2DP(Volume volume)
            : Profile("Playback Bluetooth A2DP",
                      Type::PlaybackBluetoothA2DP,
                      bsp::AudioDevice::Format{.sampleRate_Hz = 44100,
                                               .bitWidth      = 16,
                                               .flags         = 0,
                                               .outputVolume  = static_cast<float>(volume),
                                               .inputGain     = 0,
                                               .inputPath     = bsp::AudioDevice::InputPath::None,
                                               .outputPath    = bsp::AudioDevice::OutputPath::BluetoothA2DP},
                      bsp::AudioDevice::Type::Bluetooth,
                      callback)
                      AudioDevice::Format{.sampleRate_Hz = 44100,
                                          .bitWidth      = 16,
                                          .flags         = 0,
                                          .outputVolume  = static_cast<float>(volume),
                                          .inputGain     = 0,
                                          .inputPath     = AudioDevice::InputPath::None,
                                          .outputPath    = AudioDevice::OutputPath::None},
                      AudioDevice::Type::Bluetooth)
        {}
    };


M module-audio/Audio/Profiles/ProfilePlaybackHeadphones.hpp => module-audio/Audio/Profiles/ProfilePlaybackHeadphones.hpp +10 -12
@@ 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



@@ 10,20 10,18 @@ namespace audio
    class ProfilePlaybackHeadphones : public Profile
    {
      public:
        ProfilePlaybackHeadphones(std::function<int32_t()> callback, Volume volume)
        ProfilePlaybackHeadphones(Volume volume)
            : Profile("Playback Headphones",
                      Type::PlaybackHeadphones,
                      bsp::AudioDevice::Format{.sampleRate_Hz = 0,
                                               .bitWidth      = 16,
                                               .flags         = 0,
                                               .outputVolume  = static_cast<float>(volume),
                                               .inputGain     = 0,
                                               .inputPath     = bsp::AudioDevice::InputPath::None,
                                               .outputPath    = bsp::AudioDevice::OutputPath::Headphones},
                      bsp::AudioDevice::Type::Audiocodec,
                      callback)
                      AudioDevice::Format{.sampleRate_Hz = 0,
                                          .bitWidth      = 16,
                                          .flags         = 0,
                                          .outputVolume  = static_cast<float>(volume),
                                          .inputGain     = 0,
                                          .inputPath     = AudioDevice::InputPath::None,
                                          .outputPath    = AudioDevice::OutputPath::Headphones},
                      AudioDevice::Type::Audiocodec)
        {}
    };

} // namespace audio


M module-audio/Audio/Profiles/ProfilePlaybackLoudspeaker.hpp => module-audio/Audio/Profiles/ProfilePlaybackLoudspeaker.hpp +10 -11
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 11,18 11,17 @@ namespace audio
    class ProfilePlaybackLoudspeaker : public Profile
    {
      public:
        ProfilePlaybackLoudspeaker(std::function<int32_t()> callback, Volume volume)
        ProfilePlaybackLoudspeaker(Volume volume)
            : Profile("Playback Loudspeaker",
                      Type::PlaybackLoudspeaker,
                      bsp::AudioDevice::Format{.sampleRate_Hz = 0,
                                               .bitWidth      = 16,
                                               .flags         = 0,
                                               .outputVolume  = static_cast<float>(volume),
                                               .inputGain     = 0,
                                               .inputPath     = bsp::AudioDevice::InputPath::None,
                                               .outputPath    = bsp::AudioDevice::OutputPath::Loudspeaker},
                      bsp::AudioDevice::Type::Audiocodec,
                      callback)
                      AudioDevice::Format{.sampleRate_Hz = 0,
                                          .bitWidth      = 16,
                                          .flags         = 0,
                                          .outputVolume  = static_cast<float>(volume),
                                          .inputGain     = 0,
                                          .inputPath     = AudioDevice::InputPath::None,
                                          .outputPath    = AudioDevice::OutputPath::Loudspeaker},
                      AudioDevice::Type::Audiocodec)
        {}
    };


M module-audio/Audio/Profiles/ProfileRecordingBluetoothHSP.hpp => module-audio/Audio/Profiles/ProfileRecordingBluetoothHSP.hpp +13 -15
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 11,20 11,18 @@ namespace audio
    class ProfileRecordingBluetoothHSP : public Profile
    {
      public:
        ProfileRecordingBluetoothHSP(std::function<int32_t()> callback, Gain gain)
            : Profile(
                  "Recording Bluetooth HSP",
                  Type::RecordingHeadphones,
                  bsp::AudioDevice::Format{.sampleRate_Hz = 8000,
                                           .bitWidth      = 16,
                                           .flags         = static_cast<uint32_t>(
                                               bsp::AudioDevice::Flags::InputLeft), // microphone use left audio channel
                                           .outputVolume = 0,
                                           .inputGain    = static_cast<float>(gain),
                                           .inputPath    = bsp::AudioDevice::InputPath::BluetoothHSP,
                                           .outputPath   = bsp::AudioDevice::OutputPath::None},
                  bsp::AudioDevice::Type::Bluetooth,
                  callback)
        ProfileRecordingBluetoothHSP(Gain gain)
            : Profile("Recording Bluetooth HSP",
                      Type::RecordingHeadphones,
                      AudioDevice::Format{.sampleRate_Hz = 8000,
                                          .bitWidth      = 16,
                                          .flags         = static_cast<uint32_t>(
                                              AudioDevice::Flags::InputLeft), // microphone use left audio channel
                                          .outputVolume = 0,
                                          .inputGain    = static_cast<float>(gain),
                                          .inputPath    = AudioDevice::InputPath::None,
                                          .outputPath   = AudioDevice::OutputPath::None},
                      AudioDevice::Type::Bluetooth)
        {}
    };


M module-audio/Audio/Profiles/ProfileRecordingHeadphones.hpp => module-audio/Audio/Profiles/ProfileRecordingHeadphones.hpp +13 -15
@@ 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



@@ 10,20 10,18 @@ namespace audio
    class ProfileRecordingHeadphones : public Profile
    {
      public:
        ProfileRecordingHeadphones(std::function<int32_t()> callback, Gain gain)
            : Profile(
                  "Recording Headset",
                  Type::RecordingHeadphones,
                  bsp::AudioDevice::Format{.sampleRate_Hz = 44100,
                                           .bitWidth      = 16,
                                           .flags         = static_cast<uint32_t>(
                                               bsp::AudioDevice::Flags::InputLeft), // microphone use left audio channel
                                           .outputVolume = 0,
                                           .inputGain    = static_cast<float>(gain),
                                           .inputPath    = bsp::AudioDevice::InputPath::Headphones,
                                           .outputPath   = bsp::AudioDevice::OutputPath::None},
                  bsp::AudioDevice::Type::Audiocodec,
                  callback)
        ProfileRecordingHeadphones(Gain gain)
            : Profile("Recording Headset",
                      Type::RecordingHeadphones,
                      AudioDevice::Format{.sampleRate_Hz = 44100,
                                          .bitWidth      = 16,
                                          .flags         = static_cast<uint32_t>(
                                              AudioDevice::Flags::InputLeft), // microphone use left audio channel
                                          .outputVolume = 0,
                                          .inputGain    = static_cast<float>(gain),
                                          .inputPath    = AudioDevice::InputPath::Headphones,
                                          .outputPath   = AudioDevice::OutputPath::None},
                      AudioDevice::Type::Audiocodec)
        {}
    };


M module-audio/Audio/Profiles/ProfileRecordingOnBoardMic.hpp => module-audio/Audio/Profiles/ProfileRecordingOnBoardMic.hpp +13 -15
@@ 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



@@ 10,20 10,18 @@ namespace audio
    class ProfileRecordingOnBoardMic : public Profile
    {
      public:
        ProfileRecordingOnBoardMic(std::function<int32_t()> callback, Gain gain)
            : Profile(
                  "Recording On Board Microphone",
                  Type::RecordingBuiltInMic,
                  bsp::AudioDevice::Format{.sampleRate_Hz = 44100,
                                           .bitWidth      = 16,
                                           .flags         = static_cast<uint32_t>(
                                               bsp::AudioDevice::Flags::InputLeft), // microphone use left audio channel
                                           .outputVolume = 0,
                                           .inputGain    = static_cast<float>(gain),
                                           .inputPath    = bsp::AudioDevice::InputPath::Microphone,
                                           .outputPath   = bsp::AudioDevice::OutputPath::None},
                  bsp::AudioDevice::Type::Audiocodec,
                  callback)
        ProfileRecordingOnBoardMic(Gain gain)
            : Profile("Recording On Board Microphone",
                      Type::RecordingBuiltInMic,
                      AudioDevice::Format{.sampleRate_Hz = 44100,
                                          .bitWidth      = 16,
                                          .flags         = static_cast<uint32_t>(
                                              AudioDevice::Flags::InputLeft), // microphone use left audio channel
                                          .outputVolume = 0,
                                          .inputGain    = static_cast<float>(gain),
                                          .inputPath    = AudioDevice::InputPath::Microphone,
                                          .outputPath   = AudioDevice::OutputPath::None},
                      AudioDevice::Type::Audiocodec)
        {}
    };


M module-audio/Audio/Profiles/ProfileRoutingBluetoothHSP.hpp => module-audio/Audio/Profiles/ProfileRoutingBluetoothHSP.hpp +15 -16
@@ 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


@@ 10,21 10,20 @@ namespace audio
    class ProfileRoutingBluetoothHSP : public Profile
    {
      public:
        ProfileRoutingBluetoothHSP(std::function<int32_t()> callback, Volume volume, Gain gain)
            : Profile("Routing Bluetooth HSP",
                      Type::RoutingBluetoothHSP,
                      bsp::AudioDevice::Format{
                          .sampleRate_Hz = 8000,
                          .bitWidth      = 16,
                          .flags         = static_cast<uint32_t>(
                                       bsp::AudioDevice::Flags::InputLeft) | // microphone use left audio channel
                                   static_cast<uint32_t>(bsp::AudioDevice::Flags::OutputMono),
                          .outputVolume = static_cast<float>(volume),
                          .inputGain    = static_cast<float>(gain),
                          .inputPath    = bsp::AudioDevice::InputPath::BluetoothHSP,
                          .outputPath   = bsp::AudioDevice::OutputPath::BluetoothHSP},
                      bsp::AudioDevice::Type::Bluetooth,
                      callback)
        ProfileRoutingBluetoothHSP(Volume volume, Gain gain)
            : Profile(
                  "Routing Bluetooth HSP",
                  Type::RoutingBluetoothHSP,
                  AudioDevice::Format{.sampleRate_Hz = 8000,
                                      .bitWidth      = 16,
                                      .flags         = static_cast<uint32_t>(
                                                   AudioDevice::Flags::InputLeft) | // microphone use left audio channel
                                               static_cast<uint32_t>(AudioDevice::Flags::OutputMono),
                                      .outputVolume = static_cast<float>(volume),
                                      .inputGain    = static_cast<float>(gain),
                                      .inputPath    = AudioDevice::InputPath::None,
                                      .outputPath   = AudioDevice::OutputPath::None},
                  AudioDevice::Type::Bluetooth)
        {}
    };


M module-audio/Audio/Profiles/ProfileRoutingEarspeaker.hpp => module-audio/Audio/Profiles/ProfileRoutingEarspeaker.hpp +15 -17
@@ 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



@@ 10,23 10,21 @@ namespace audio
    class ProfileRoutingEarspeaker : public Profile
    {
      public:
        ProfileRoutingEarspeaker(std::function<int32_t()> callback, Volume volume, Gain gain)
            : Profile("Routing Earspeaker",
                      Type::RoutingEarspeaker,
                      bsp::AudioDevice::Format{
                          .sampleRate_Hz = 16000,
                          .bitWidth      = 16,
                          .flags         = static_cast<uint32_t>(
                                       bsp::AudioDevice::Flags::InputLeft) | // microphone use left audio channel
                                   static_cast<uint32_t>(bsp::AudioDevice::Flags::OutputMono),
                          .outputVolume = static_cast<float>(volume),
                          .inputGain    = static_cast<float>(gain),
                          .inputPath    = bsp::AudioDevice::InputPath::Microphone,
                          .outputPath   = bsp::AudioDevice::OutputPath::Earspeaker},
                      bsp::AudioDevice::Type::Audiocodec,
                      callback)
        ProfileRoutingEarspeaker(Volume volume, Gain gain)
            : Profile(
                  "Routing Earspeaker",
                  Type::RoutingEarspeaker,
                  AudioDevice::Format{.sampleRate_Hz = 16000,
                                      .bitWidth      = 16,
                                      .flags         = static_cast<uint32_t>(
                                                   AudioDevice::Flags::InputLeft) | // microphone use left audio channel
                                               static_cast<uint32_t>(AudioDevice::Flags::OutputMono),
                                      .outputVolume = static_cast<float>(volume),
                                      .inputGain    = static_cast<float>(gain),
                                      .inputPath    = AudioDevice::InputPath::Microphone,
                                      .outputPath   = AudioDevice::OutputPath::Earspeaker},
                  AudioDevice::Type::Audiocodec)
        {}
    };

} // namespace audio


M module-audio/Audio/Profiles/ProfileRoutingHeadphones.hpp => module-audio/Audio/Profiles/ProfileRoutingHeadphones.hpp +15 -16
@@ 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



@@ 10,21 10,20 @@ namespace audio
    class ProfileRoutingHeadphones : public Profile
    {
      public:
        ProfileRoutingHeadphones(std::function<int32_t()> callback, Volume volume, Gain gain)
            : Profile("Routing Headset",
                      Type::RoutingHeadphones,
                      bsp::AudioDevice::Format{
                          .sampleRate_Hz = 16000,
                          .bitWidth      = 16,
                          .flags         = static_cast<uint32_t>(
                                       bsp::AudioDevice::Flags::InputLeft) | // microphone use left audio channel
                                   static_cast<uint32_t>(bsp::AudioDevice::Flags::OutputMono),
                          .outputVolume = static_cast<float>(volume),
                          .inputGain    = static_cast<float>(gain),
                          .inputPath    = bsp::AudioDevice::InputPath::Headphones,
                          .outputPath   = bsp::AudioDevice::OutputPath::HeadphonesMono},
                      bsp::AudioDevice::Type::Audiocodec,
                      callback)
        ProfileRoutingHeadphones(Volume volume, Gain gain)
            : Profile(
                  "Routing Headset",
                  Type::RoutingHeadphones,
                  AudioDevice::Format{.sampleRate_Hz = 16000,
                                      .bitWidth      = 16,
                                      .flags         = static_cast<uint32_t>(
                                                   AudioDevice::Flags::InputLeft) | // microphone use left audio channel
                                               static_cast<uint32_t>(AudioDevice::Flags::OutputMono),
                                      .outputVolume = static_cast<float>(volume),
                                      .inputGain    = static_cast<float>(gain),
                                      .inputPath    = AudioDevice::InputPath::Headphones,
                                      .outputPath   = AudioDevice::OutputPath::Headphones},
                  AudioDevice::Type::Audiocodec)
        {}
    };


M module-audio/Audio/Profiles/ProfileRoutingLoudspeaker.hpp => module-audio/Audio/Profiles/ProfileRoutingLoudspeaker.hpp +15 -16
@@ 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



@@ 10,21 10,20 @@ namespace audio
    class ProfileRoutingLoudspeaker : public Profile
    {
      public:
        ProfileRoutingLoudspeaker(std::function<int32_t()> callback, Volume volume, Gain gain)
            : Profile("Routing Speakerphone",
                      Type::RoutingLoudspeaker,
                      bsp::AudioDevice::Format{
                          .sampleRate_Hz = 16000,
                          .bitWidth      = 16,
                          .flags         = static_cast<uint32_t>(
                                       bsp::AudioDevice::Flags::InputLeft) | // microphone use left audio channel
                                   static_cast<uint32_t>(bsp::AudioDevice::Flags::OutputMono),
                          .outputVolume = static_cast<float>(volume),
                          .inputGain    = static_cast<float>(gain),
                          .inputPath    = bsp::AudioDevice::InputPath::Microphone,
                          .outputPath   = bsp::AudioDevice::OutputPath::LoudspeakerMono},
                      bsp::AudioDevice::Type::Audiocodec,
                      callback)
        ProfileRoutingLoudspeaker(Volume volume, Gain gain)
            : Profile(
                  "Routing Speakerphone",
                  Type::RoutingLoudspeaker,
                  AudioDevice::Format{.sampleRate_Hz = 16000,
                                      .bitWidth      = 16,
                                      .flags         = static_cast<uint32_t>(
                                                   AudioDevice::Flags::InputLeft) | // microphone use left audio channel
                                               static_cast<uint32_t>(AudioDevice::Flags::OutputMono),
                                      .outputVolume = static_cast<float>(volume),
                                      .inputGain    = static_cast<float>(gain),
                                      .inputPath    = AudioDevice::InputPath::Microphone,
                                      .outputPath   = AudioDevice::OutputPath::Loudspeaker},
                  AudioDevice::Type::Audiocodec)
        {}
    };


M module-audio/CMakeLists.txt => module-audio/CMakeLists.txt +14 -34
@@ 1,9 1,11 @@
cmake_minimum_required(VERSION 3.12)
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

cmake_minimum_required(VERSION 3.12)

project(module-audio VERSION 1.0
        DESCRIPTION "Audio module library")


set(SOURCES
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/Decoder.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/DecoderWorker.cpp"


@@ 16,13 18,13 @@ set(SOURCES
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/encoder/EncoderWAV.cpp"

        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Audio.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioDeviceFactory.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioMux.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioCommon.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Endpoint.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Stream.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/StreamFactory.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/StreamQueuedEventsListener.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/BluetoothProxyAudio.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/Operation.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/PlaybackOperation.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/RecorderOperation.cpp"


@@ 31,51 33,30 @@ set(SOURCES
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Profiles/Profile.cpp"
)

if(NOT ${PROJECT_TARGET} STREQUAL "TARGET_Linux")
    include(targets/Target_Cross.cmake)
else()
    include(targets/Target_Linux.cmake)
endif()


add_library(${PROJECT_NAME} STATIC ${SOURCES} ${BOARD_SOURCES})

target_include_directories( ${PROJECT_NAME} PRIVATE ${TAGLIB_INCLUDE_DIRS} )
# include target specific rules
set(AUDIO_BOARD_LIBRARY audio-${PROJECT_TARGET_NAME})
add_subdirectory(board/${PROJECT_TARGET_NAME})
target_include_directories(${AUDIO_BOARD_LIBRARY} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})


# Board specific compilation definitions,options,include directories and features
add_library(${PROJECT_NAME} STATIC ${SOURCES})
target_include_directories(${PROJECT_NAME} PRIVATE ${TAGLIB_INCLUDE_DIRS})
target_compile_definitions(${PROJECT_NAME} PUBLIC ${PROJECT_CONFIG_DEFINITIONS})
target_compile_definitions(${PROJECT_NAME} PUBLIC ${PROJECT_TARGET})
target_compile_definitions(${PROJECT_NAME} PUBLIC ${TARGET_COMPILE_DEFINITIONS})
target_include_directories(${PROJECT_NAME} PUBLIC ${BOARD_DIR_INCLUDES})
target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_INCLUDES})
target_compile_features(${PROJECT_NAME} PUBLIC ${TARGET_COMPILE_FEATURES})
target_link_options(${PROJECT_NAME} PUBLIC ${TARGET_LINK_OPTIONS})

target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_LIST_DIR}/../module-bsp/bsp/audio")
target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

# supress warning for flac decoder
set_source_files_properties(
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderFLAC.cpp"
        PROPERTIES COMPILE_FLAGS
	    "-Wno-implicit-fallthrough -Wno-error=maybe-uninitialized"
)

target_compile_definitions(${PROJECT_NAME}

        PUBLIC

)

target_include_directories(${PROJECT_NAME}

        PUBLIC

        ${CMAKE_CURRENT_SOURCE_DIR}

	"-Wno-implicit-fallthrough -Wno-error=maybe-uninitialized"
)

target_link_libraries(${PROJECT_NAME}
    ${AUDIO_BOARD_LIBRARY}
    module-bsp
    module-os
    module-utils


@@ 86,4 67,3 @@ target_link_libraries(${PROJECT_NAME}
if (${ENABLE_TESTS})
    add_subdirectory(Audio/test)
endif ()


A module-audio/board/linux/CMakeLists.txt => module-audio/board/linux/CMakeLists.txt +12 -0
@@ 0,0 1,12 @@
# Copyright (c) 2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

set(AUDIO_LINUX_SOURCES
	LinuxAudioPlatform.cpp
)

add_library(${AUDIO_BOARD_LIBRARY} STATIC ${AUDIO_LINUX_SOURCES})
target_include_directories(${AUDIO_BOARD_LIBRARY} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${AUDIO_BOARD_LIBRARY}
    module-os
)

A module-audio/board/linux/LinuxAudioPlatform.cpp => module-audio/board/linux/LinuxAudioPlatform.cpp +25 -0
@@ 0,0 1,25 @@
// Copyright (c) 2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <Audio/AudioPlatform.hpp>

#include <memory>
#include <utility>

using audio::AudioDevice;
using audio::AudioDeviceFactory;
using audio::AudioPlatform;

class DummyAudioFactory : public AudioDeviceFactory
{
  protected:
    std::shared_ptr<AudioDevice> getDeviceFromType([[maybe_unused]] AudioDevice::Type deviceType)
    {
        return nullptr;
    }
};

std::unique_ptr<AudioDeviceFactory> AudioPlatform::GetDeviceFactory()
{
    return std::make_unique<DummyAudioFactory>();
}

A module-audio/board/rt1051/CMakeLists.txt => module-audio/board/rt1051/CMakeLists.txt +17 -0
@@ 0,0 1,17 @@
# Copyright (c) 2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

set(AUDIO_RT1051_SOURCES
    "SAIAudioDevice.cpp"
    "RT1051AudioCodec.cpp"
    "RT1051CellularAudio.cpp"
    "RT1051DeviceFactory.cpp"
    "RT1051Platform.cpp"
)

add_library(${AUDIO_BOARD_LIBRARY} STATIC ${AUDIO_RT1051_SOURCES})
target_include_directories(${AUDIO_BOARD_LIBRARY} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${AUDIO_BOARD_LIBRARY}
    module-bsp
    module-os
)

R module-bsp/board/rt1051/bsp/audio/RT1051Audiocodec.cpp => module-audio/board/rt1051/RT1051AudioCodec.cpp +71 -94
@@ 1,43 1,68 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RT1051Audiocodec.hpp"
#include "RT1051AudioCodec.hpp"
#include "board.h"
#include "dma_config.h"
#include "log/log.hpp"

#include "bsp/BoardDefinitions.hpp"
#include "board/rt1051/common/audio.hpp"

#include <mutex.hpp>

namespace bsp
namespace audio
{
    sai_edma_handle_t RT1051AudioCodec::txHandle = {};
    sai_edma_handle_t RT1051AudioCodec::rxHandle = {};

    using namespace drivers;

    std::shared_ptr<drivers::DriverPLL> RT1051Audiocodec::pllAudio;
    std::shared_ptr<drivers::DriverDMAMux> RT1051Audiocodec::dmamux;
    std::shared_ptr<drivers::DriverDMA> RT1051Audiocodec::dma;
    std::unique_ptr<drivers::DriverDMAHandle> RT1051Audiocodec::rxDMAHandle;
    std::unique_ptr<drivers::DriverDMAHandle> RT1051Audiocodec::txDMAHandle;
    sai_config_t RT1051Audiocodec::config             = {};
    std::uint32_t RT1051Audiocodec::mclkSourceClockHz = 0;
    sai_edma_handle_t RT1051Audiocodec::txHandle      = {};
    sai_edma_handle_t RT1051Audiocodec::rxHandle      = {};

    RT1051Audiocodec::RT1051Audiocodec()
    RT1051AudioCodec::RT1051AudioCodec()
        : SAIAudioDevice(BOARD_AUDIOCODEC_SAIx, &rxHandle, &txHandle), saiInFormat{}, saiOutFormat{},
          codecParams{}, codec{}
    {
        isInitialized = true;
    }

    RT1051Audiocodec::~RT1051Audiocodec()
    RT1051AudioCodec::~RT1051AudioCodec()
    {
        Stop();
    }

    AudioDevice::RetCode RT1051Audiocodec::Start(const bsp::AudioDevice::Format &format)
    CodecParamsMAX98090::InputPath RT1051AudioCodec::getCodecInputPath(const AudioDevice::Format &format)
    {
        switch (format.inputPath) {
        case AudioDevice::InputPath::Headphones:
            return CodecParamsMAX98090::InputPath::Headphones;

        case AudioDevice::InputPath::Microphone:
            return CodecParamsMAX98090::InputPath::Microphone;

        default:
            return CodecParamsMAX98090::InputPath::None;
        };
    }

    CodecParamsMAX98090::OutputPath RT1051AudioCodec::getCodecOutputPath(const AudioDevice::Format &format)
    {
        auto mono = (format.flags & static_cast<std::uint32_t>(AudioDevice::Flags::OutputMono)) != 0;

        switch (format.outputPath) {
        case AudioDevice::OutputPath::Headphones:
            return mono ? CodecParamsMAX98090::OutputPath::HeadphonesMono : CodecParamsMAX98090::OutputPath::Headphones;

        case AudioDevice::OutputPath::Earspeaker:
            return CodecParamsMAX98090::OutputPath::Earspeaker;

        case AudioDevice::OutputPath::Loudspeaker:
            return mono ? CodecParamsMAX98090::OutputPath::LoudspeakerMono
                        : CodecParamsMAX98090::OutputPath::Loudspeaker;

        default:
            return CodecParamsMAX98090::OutputPath::None;
        }
    }

    AudioDevice::RetCode RT1051AudioCodec::Start(const AudioDevice::Format &format)
    {
        cpp_freertos::LockGuard lock(mutex);



@@ 78,8 103,8 @@ namespace bsp
            LOG_ERROR("Unsupported sample rate");
        }

        codecParams.inputPath  = format.inputPath;
        codecParams.outputPath = format.outputPath;
        codecParams.inputPath  = getCodecInputPath(format);
        codecParams.outputPath = getCodecOutputPath(format);
        codecParams.outVolume  = format.outputVolume;
        codecParams.inGain     = format.inputGain;
        codec.Start(codecParams);


@@ 92,7 117,7 @@ namespace bsp
        return AudioDevice::RetCode::Success;
    }

    AudioDevice::RetCode RT1051Audiocodec::Stop()
    AudioDevice::RetCode RT1051AudioCodec::Stop()
    {
        cpp_freertos::LockGuard lock(mutex);



@@ 111,7 136,7 @@ namespace bsp
        return AudioDevice::RetCode::Success;
    }

    AudioDevice::RetCode RT1051Audiocodec::OutputVolumeCtrl(float vol)
    AudioDevice::RetCode RT1051AudioCodec::OutputVolumeCtrl(float vol)
    {
        currentFormat.outputVolume = vol;
        CodecParamsMAX98090 params;


@@ 121,7 146,7 @@ namespace bsp
        return AudioDevice::RetCode::Success;
    }

    AudioDevice::RetCode RT1051Audiocodec::InputGainCtrl(float gain)
    AudioDevice::RetCode RT1051AudioCodec::InputGainCtrl(float gain)
    {
        currentFormat.inputGain = gain;
        CodecParamsMAX98090 params;


@@ 131,27 156,27 @@ namespace bsp
        return AudioDevice::RetCode::Success;
    }

    AudioDevice::RetCode RT1051Audiocodec::InputPathCtrl(InputPath inputPath)
    AudioDevice::RetCode RT1051AudioCodec::InputPathCtrl(InputPath inputPath)
    {
        currentFormat.inputPath = inputPath;
        CodecParamsMAX98090 params;
        params.inputPath = inputPath;
        params.inputPath = getCodecInputPath(currentFormat);
        params.opCmd     = CodecParamsMAX98090::Cmd::SetInput;
        codec.Ioctrl(params);
        return AudioDevice::RetCode::Success;
    }

    AudioDevice::RetCode RT1051Audiocodec::OutputPathCtrl(OutputPath outputPath)
    AudioDevice::RetCode RT1051AudioCodec::OutputPathCtrl(OutputPath outputPath)
    {
        currentFormat.outputPath = outputPath;
        CodecParamsMAX98090 params;
        params.outputPath = outputPath;
        params.outputPath = getCodecOutputPath(currentFormat);
        params.opCmd      = CodecParamsMAX98090::Cmd::SetOutput;
        codec.Ioctrl(params);
        return AudioDevice::RetCode::Success;
    }

    bool RT1051Audiocodec::IsFormatSupported(const bsp::AudioDevice::Format &format)
    bool RT1051AudioCodec::IsFormatSupported(const AudioDevice::Format &format)
    {

        if (CodecParamsMAX98090::ValToSampleRate(format.sampleRate_Hz) == CodecParamsMAX98090::SampleRate::Invalid) {


@@ 159,68 184,19 @@ namespace bsp
        }
        return true;
    }
    // INTERNALS

    void RT1051Audiocodec::Init()
    {

        pllAudio = DriverPLL::Create(static_cast<PLLInstances>(BoardDefinitions ::AUDIO_PLL), DriverPLLParams{});
        dmamux   = DriverDMAMux::Create(static_cast<DMAMuxInstances>(BoardDefinitions ::AUDIOCODEC_DMAMUX),
                                      DriverDMAMuxParams{});
        dma      = DriverDMA::Create(static_cast<DMAInstances>(BoardDefinitions ::AUDIOCODEC_DMA), DriverDMAParams{});

        // Enable MCLK clock
        IOMUXC_GPR->GPR1 |= BOARD_AUDIOCODEC_SAIx_MCLK_MASK;

        txDMAHandle = dma->CreateHandle(static_cast<uint32_t>(BoardDefinitions ::AUDIOCODEC_TX_DMA_CHANNEL));
        rxDMAHandle = dma->CreateHandle(static_cast<uint32_t>(BoardDefinitions ::AUDIOCODEC_RX_DMA_CHANNEL));
        dmamux->Enable(static_cast<uint32_t>(BoardDefinitions ::AUDIOCODEC_TX_DMA_CHANNEL),
                       BSP_AUDIOCODEC_SAIx_DMA_TX_SOURCE);
        dmamux->Enable(static_cast<uint32_t>(BoardDefinitions ::AUDIOCODEC_RX_DMA_CHANNEL),
                       BSP_AUDIOCODEC_SAIx_DMA_RX_SOURCE);

        mclkSourceClockHz = GetPerphSourceClock(PerphClock_SAI2);

        // Initialize SAI Tx module
        SAI_TxGetDefaultConfig(&config);
        config.masterSlave = kSAI_Slave;
        SAI_TxInit(BOARD_AUDIOCODEC_SAIx, &config);

        // Initialize SAI Rx module
        SAI_RxGetDefaultConfig(&config);

        config.masterSlave = kSAI_Slave;
        SAI_RxInit(BOARD_AUDIOCODEC_SAIx, &config);
    }

    void RT1051Audiocodec::Deinit()
    {
        memset(&config, 0, sizeof config);
        SAI_Deinit(BOARD_AUDIOCODEC_SAIx);
        if (dmamux) {
            dmamux->Disable(static_cast<uint32_t>(BoardDefinitions ::AUDIOCODEC_TX_DMA_CHANNEL));
            dmamux->Disable(static_cast<uint32_t>(BoardDefinitions ::AUDIOCODEC_RX_DMA_CHANNEL));
        }

        // force order of destruction
        txDMAHandle.reset();
        rxDMAHandle.reset();
        dma.reset();
        dmamux.reset();
        pllAudio.reset();
    }

    void RT1051Audiocodec::InStart()
    void RT1051AudioCodec::InStart()
    {
        sai_transfer_format_t sai_format = {};
        sai_transfer_format_t sai_format;
        auto audioCfg = bsp::AudioConfig::get();

        /* Configure the audio format */
        sai_format.bitWidth           = saiInFormat.bitWidth;
        sai_format.channel            = 0U;
        sai_format.sampleRate_Hz      = saiInFormat.sampleRate_Hz;
        sai_format.masterClockHz      = mclkSourceClockHz;
        sai_format.masterClockHz      = audioCfg->mclkSourceClockHz;
        sai_format.isFrameSyncCompact = false;
        sai_format.protocol           = config.protocol;
        sai_format.protocol           = audioCfg->config.protocol;
        sai_format.stereo             = saiInFormat.stereo;
#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
        sai_format.watermark = FSL_FEATURE_SAI_FIFO_COUNT / 2U;


@@ 230,10 206,10 @@ namespace bsp
                                       &rxHandle,
                                       rxAudioCodecCallback,
                                       this,
                                       reinterpret_cast<edma_handle_t *>(rxDMAHandle->GetHandle()));
                                       reinterpret_cast<edma_handle_t *>(audioCfg->rxDMAHandle->GetHandle()));

        SAI_TransferRxSetFormatEDMA(
            BOARD_AUDIOCODEC_SAIx, &rxHandle, &sai_format, mclkSourceClockHz, mclkSourceClockHz);
            BOARD_AUDIOCODEC_SAIx, &rxHandle, &sai_format, audioCfg->mclkSourceClockHz, audioCfg->mclkSourceClockHz);

        DisableIRQ(BOARD_AUDIOCODEC_SAIx_RX_IRQ);



@@ 241,17 217,18 @@ namespace bsp
        SAI_RxSoftwareReset(BOARD_AUDIOCODEC_SAIx, kSAI_ResetTypeSoftware);
    }

    void RT1051Audiocodec::OutStart()
    void RT1051AudioCodec::OutStart()
    {
        sai_transfer_format_t sai_format = {};
        sai_transfer_format_t sai_format;
        auto audioCfg = bsp::AudioConfig::get();

        /* Configure the audio format */
        sai_format.bitWidth           = saiOutFormat.bitWidth;
        sai_format.channel            = 0U;
        sai_format.sampleRate_Hz      = saiOutFormat.sampleRate_Hz;
        sai_format.masterClockHz      = mclkSourceClockHz;
        sai_format.masterClockHz      = audioCfg->mclkSourceClockHz;
        sai_format.isFrameSyncCompact = false;
        sai_format.protocol           = config.protocol;
        sai_format.protocol           = audioCfg->config.protocol;
        sai_format.stereo             = saiOutFormat.stereo;
#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
        sai_format.watermark = FSL_FEATURE_SAI_FIFO_COUNT / 2U;


@@ 261,9 238,9 @@ namespace bsp
                                       &txHandle,
                                       txAudioCodecCallback,
                                       this,
                                       reinterpret_cast<edma_handle_t *>(txDMAHandle->GetHandle()));
                                       reinterpret_cast<edma_handle_t *>(audioCfg->txDMAHandle->GetHandle()));
        SAI_TransferTxSetFormatEDMA(
            BOARD_AUDIOCODEC_SAIx, &txHandle, &sai_format, mclkSourceClockHz, mclkSourceClockHz);
            BOARD_AUDIOCODEC_SAIx, &txHandle, &sai_format, audioCfg->mclkSourceClockHz, audioCfg->mclkSourceClockHz);

        DisableIRQ(BOARD_AUDIOCODEC_SAIx_TX_IRQ);



@@ 271,7 248,7 @@ namespace bsp
        SAI_TxSoftwareReset(BOARD_AUDIOCODEC_SAIx, kSAI_ResetTypeSoftware);
    }

    void RT1051Audiocodec::OutStop()
    void RT1051AudioCodec::OutStop()
    {
        SAI_TxDisableInterrupts(BOARD_AUDIOCODEC_SAIx, kSAI_FIFOErrorInterruptEnable);
        if (txHandle.dmaHandle) {


@@ 280,7 257,7 @@ namespace bsp
        memset(&txHandle, 0, sizeof(txHandle));
    }

    void RT1051Audiocodec::InStop()
    void RT1051AudioCodec::InStop()
    {
        SAI_RxDisableInterrupts(BOARD_AUDIOCODEC_SAIx, kSAI_FIFOErrorInterruptEnable);
        if (rxHandle.dmaHandle) {


@@ 291,14 268,14 @@ namespace bsp

    void rxAudioCodecCallback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData)
    {
        auto self = static_cast<RT1051Audiocodec *>(userData);
        auto self = static_cast<RT1051AudioCodec *>(userData);
        self->onDataReceive();
    }

    void txAudioCodecCallback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData)
    {
        auto self = static_cast<RT1051Audiocodec *>(userData);
        auto self = static_cast<RT1051AudioCodec *>(userData);
        self->onDataSend();
    }

} // namespace bsp
} // namespace audio

R module-bsp/board/rt1051/bsp/audio/RT1051Audiocodec.hpp => module-audio/board/rt1051/RT1051AudioCodec.hpp +9 -19
@@ 10,7 10,7 @@
#include "task.h"
#include "macros.h"

#include "CodecMAX98090.hpp"
#include "board/rt1051/bsp/audio/CodecMAX98090.hpp"

#include "drivers/pll/DriverPLL.hpp"
#include "drivers/dmamux/DriverDMAMux.hpp"


@@ 18,24 18,21 @@

#include <mutex.hpp>

namespace bsp
namespace audio
{

    void txAudioCodecCallback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData);
    void rxAudioCodecCallback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData);

    class RT1051Audiocodec : public SAIAudioDevice
    class RT1051AudioCodec : public SAIAudioDevice
    {

      public:
        static void Init();
        static void Deinit();

        friend void txAudioCodecCallback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData);
        friend void rxAudioCodecCallback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData);

        RT1051Audiocodec();
        virtual ~RT1051Audiocodec();
        RT1051AudioCodec();
        virtual ~RT1051AudioCodec();

        AudioDevice::RetCode Start(const Format &format) override final;
        AudioDevice::RetCode Stop() override final;


@@ 63,22 60,12 @@ namespace bsp
            sai_mono_stereo_t stereo; /*!< Mono or stereo */
        };

        static sai_config_t config;
        static std::uint32_t mclkSourceClockHz;

        State state = State::Stopped;
        SAIFormat saiInFormat;
        SAIFormat saiOutFormat;
        CodecParamsMAX98090 codecParams;
        CodecMAX98090 codec;

        // M.P: It is important to destroy these drivers in specific order
        static std::shared_ptr<drivers::DriverPLL> pllAudio;
        static std::shared_ptr<drivers::DriverDMAMux> dmamux;
        static std::shared_ptr<drivers::DriverDMA> dma;
        static std::unique_ptr<drivers::DriverDMAHandle> rxDMAHandle;
        static std::unique_ptr<drivers::DriverDMAHandle> txDMAHandle;

        static AT_NONCACHEABLE_SECTION_INIT(sai_edma_handle_t txHandle);
        static AT_NONCACHEABLE_SECTION_INIT(sai_edma_handle_t rxHandle);



@@ 86,5 73,8 @@ namespace bsp
        void InStart();
        void OutStop();
        void InStop();

        CodecParamsMAX98090::InputPath getCodecInputPath(const AudioDevice::Format &format);
        CodecParamsMAX98090::OutputPath getCodecOutputPath(const AudioDevice::Format &format);
    };
} // namespace bsp
} // namespace audio

R module-bsp/board/rt1051/bsp/audio/RT1051CellularAudio.cpp => module-audio/board/rt1051/RT1051CellularAudio.cpp +5 -5
@@ 10,7 10,7 @@

#include <mutex.hpp>

namespace bsp
namespace audio
{

    using namespace drivers;


@@ 30,7 30,7 @@ namespace bsp
        Deinit();
    }

    AudioDevice::RetCode RT1051CellularAudio::Start(const bsp::AudioDevice::Format &format)
    AudioDevice::RetCode RT1051CellularAudio::Start(const AudioDevice::Format &format)
    {
        cpp_freertos::LockGuard lock(mutex);



@@ 118,7 118,7 @@ namespace bsp
        return AudioDevice::RetCode::Success;
    }

    bool RT1051CellularAudio::IsFormatSupported(const bsp::AudioDevice::Format &format)
    bool RT1051CellularAudio::IsFormatSupported(const AudioDevice::Format &format)
    {
        return true;
    }


@@ 203,7 203,7 @@ namespace bsp

    void RT1051CellularAudio::OutStart()
    {
        sai_transfer_format_t sai_format = {};
        sai_transfer_format_t sai_format;

        /* Configure the audio format */
        sai_format.bitWidth           = saiOutFormat.bitWidth;


@@ 263,4 263,4 @@ namespace bsp
        self->onDataSend();
    }

} // namespace bsp
} // namespace audio

R module-bsp/board/rt1051/bsp/audio/RT1051CellularAudio.hpp => module-audio/board/rt1051/RT1051CellularAudio.hpp +2 -2
@@ 17,7 17,7 @@

#include <mutex.hpp>

namespace bsp
namespace audio
{

    void txCellularCallback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData);


@@ 80,6 80,6 @@ namespace bsp
        void OutStop();
        void InStop();
    };
} // namespace bsp
} // namespace audio

#endif // PUREPHONE_RT1051CELLULARAUDIO_HPP

A module-audio/board/rt1051/RT1051DeviceFactory.cpp => module-audio/board/rt1051/RT1051DeviceFactory.cpp +35 -0
@@ 0,0 1,35 @@
// Copyright (c) 2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RT1051DeviceFactory.hpp"
#include "board/rt1051/RT1051AudioCodec.hpp"
#include "board/rt1051/RT1051CellularAudio.hpp"

using audio::AudioDevice;
using audio::RT1051AudioCodec;
using audio::RT1051CellularAudio;
using audio::RT1051DeviceFactory;

std::shared_ptr<AudioDevice> RT1051DeviceFactory::getDeviceFromType(AudioDevice::Type deviceType)
{
    std::shared_ptr<AudioDevice> device;
    switch (deviceType) {
    case AudioDevice::Type::Audiocodec: {
        device = std::make_unique<RT1051AudioCodec>();
    } break;

    case AudioDevice::Type::Bluetooth: {
        LOG_FATAL("Bluetooth audio is not yet supported");
        device = nullptr;
    } break;

    case AudioDevice::Type::Cellular: {
        device = std::make_unique<RT1051CellularAudio>();
    } break;

    default:
        break;
    };

    return device;
}

A module-audio/board/rt1051/RT1051DeviceFactory.hpp => module-audio/board/rt1051/RT1051DeviceFactory.hpp +17 -0
@@ 0,0 1,17 @@
// Copyright (c) 2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Audio/AudioDeviceFactory.hpp>

namespace audio
{

    class RT1051DeviceFactory : public AudioDeviceFactory
    {
      protected:
        std::shared_ptr<AudioDevice> getDeviceFromType(AudioDevice::Type deviceType) override;
    };

}; // namespace audio

A module-audio/board/rt1051/RT1051Platform.cpp => module-audio/board/rt1051/RT1051Platform.cpp +16 -0
@@ 0,0 1,16 @@
// Copyright (c) 2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <Audio/AudioPlatform.hpp>

#include "RT1051DeviceFactory.hpp"

#include <utility>

using audio::AudioDeviceFactory;
using audio::AudioPlatform;

std::unique_ptr<AudioDeviceFactory> AudioPlatform::GetDeviceFactory()
{
    return std::make_unique<RT1051DeviceFactory>();
}

R module-bsp/board/rt1051/bsp/audio/SAIAudioDevice.cpp => module-audio/board/rt1051/SAIAudioDevice.cpp +1 -1
@@ 3,7 3,7 @@

#include "SAIAudioDevice.hpp"

using namespace bsp;
using namespace audio;

SAIAudioDevice::SAIAudioDevice(I2S_Type *base, sai_edma_handle_t *rxHandle, sai_edma_handle_t *txHandle)
    : AudioDevice(saiCapabilities, saiCapabilities), _base(base), rx(rxHandle), tx(txHandle)

R module-bsp/board/rt1051/bsp/audio/SAIAudioDevice.hpp => module-audio/board/rt1051/SAIAudioDevice.hpp +4 -4
@@ 3,14 3,14 @@

#pragma once

#include "bsp/audio/bsp_audio.hpp"
#include <Audio/AudioDevice.hpp>

#include "fsl_sai_edma.h"

namespace bsp
namespace audio
{

    class SAIAudioDevice : public bsp::AudioDevice
    class SAIAudioDevice : public AudioDevice
    {
      public:
        SAIAudioDevice(I2S_Type *base, sai_edma_handle_t *rxHandle, sai_edma_handle_t *txHandle);


@@ 34,4 34,4 @@ namespace bsp
        static constexpr Capabilities saiCapabilities = {.usesDMA = true};
    };

} // namespace bsp
} // namespace audio

D module-audio/targets/Target_Cross.cmake => module-audio/targets/Target_Cross.cmake +0 -9
@@ 1,9 0,0 @@
set(BOARD_SOURCES

        CACHE INTERNAL ""
        )

set(BOARD_DIR_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/board/cross CACHE INTERNAL "")




D module-audio/targets/Target_Linux.cmake => module-audio/targets/Target_Linux.cmake +0 -3
@@ 1,3 0,0 @@
set(BOARD_SOURCES  CACHE INTERNAL "")

set(BOARD_DIR_INCLUDES  ${CMAKE_CURRENT_SOURCE_DIR}/board/linux CACHE INTERNAL "")

M module-bluetooth/Bluetooth/interface/profiles/PAN.cpp => module-bluetooth/Bluetooth/interface/profiles/PAN.cpp +4 -4
@@ 115,10 115,10 @@ namespace bluetooth
    {
        // name has to have storage
        constexpr uint32_t size = 64;
        static char lname[size] = {0};
        snprintf(lname, size, "%s %s", name.c_str(), "00:00:00:00:00:00");
        LOG_INFO("Setting local name: %s", lname);
        gap_set_local_name(lname);
        static std::array<char, size> lname;
        snprintf(lname.data(), size, "%s", name.c_str());
        LOG_INFO("Setting local name: %s", lname.data());
        gap_set_local_name(lname.data());
        return Error();
    }
    namespace PAN

M module-bluetooth/Bluetooth/interface/profiles/Profile.hpp => module-bluetooth/Bluetooth/interface/profiles/Profile.hpp +4 -4
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 12,13 12,13 @@ namespace bluetooth
    class Profile
    {
      public:
        virtual ~Profile()                           = default;
        virtual ~Profile()                                                   = default;
        virtual auto init() -> Error::Code                                   = 0;
        virtual void setDeviceAddress(uint8_t *addr) = 0;
        virtual void setDeviceAddress(uint8_t *addr)                         = 0;
        virtual void setOwnerService(const sys::Service *service)            = 0;
        virtual auto getStreamData() -> std::shared_ptr<BluetoothStreamData> = 0;
        virtual void connect()                                               = 0;
        virtual void disconnect()                                            = 0;
    };

} // namespace Bt
} // namespace bluetooth

M module-bluetooth/README.md => module-bluetooth/README.md +4 -3
@@ 18,6 18,10 @@ It's meant to provide interface layer for service-bluetooth like that:
![Flowchart](./flowchart.svg)
<img src="./flowchart.svg">

## Table of content
- [Bluetooth on/off operation](./doc/bt_on_off.md)
- [Bluetooth scan operation](./doc/bt_scan.md)

# Proposal of Bluetooth settings data flow

The Bluetooth settings related data flow is directed by three main entities:


@@ 46,6 50,3 @@ Flowchart below is an example of scenario, when user opens *BluetoothWindow*, an

![change_bt_status_flowchart](./change_bt_status_flowchart.svg)

# Bluetooth on/off state machine diagram
To be able to handle all events during start up and shutting down of the Bluetooth module the following state machine has been implemented:
![bt_on_off_state_diagram](./bt_on_off_state_diagram.svg)

A module-bluetooth/doc/bt_on_off.md => module-bluetooth/doc/bt_on_off.md +37 -0
@@ 0,0 1,37 @@
# Bluetooth on-off operation
This document aims to describe the basics of the Bluetooth module power management.

## Turning on the Bluetooth module
The Bluetooth module (hereinafter referred to as BT module) is connected to the processor via UART. 
Additionally, a reset pin is available to put the BT module into a reset state, which is used in the "power on" procedure.
UART data transfers have been made using DMA, which speeds up the data rate. To be able to properly init the BT module, 
the following steps have to be taken:

- Enable and configure UART interface
- Enable and configure DMA channels
- Properly init the BT module

Enabling and configuring UART and related DMA channels is done in the BSP (board support package) module, 
therefore there is no need to describe it here. Those BSP functions are encapsulated and used by the Bluetooth stack library.

### Power on procedure
- send `message::bluetooth::SetStatus` message with `BluetoothStatus::State::On` value
- wait for settings change: `bluetooth::Settings::State` set to `BluetoothStatus::State::On`

#### A low-level flow of what's inside
After the `BluetoothWorker` receives `bluetooth::Command::PowerOn` (sent by `ServiceBluetooth` after receiving the state change message),
the state machine executes driver's `init` method. After the init is done, the Bluetooth stack is prepared to be run by the `run` method of the `Driver` class - 
it's done as the next step of the init procedure. When the Bluetooth stack has been initiated, the settings holder updates the BT state in the settings.

## Turning off the Bluetooth module
Turning off the BT module is basically reversed turning on - after sending the proper message, the Bluetooth driver 
turns off the Bluetooth stack. When the Bluetooth stack is turned off, the UART with DMA channels is deinitialized and BT module is put
to the reset state.

### Power off procedure
- send `message::bluetooth::SetStatus` message with `BluetoothStatus::State::Off` value
- wait for settings change: `bluetooth::Settings::State` set to `BluetoothStatus::State::Off`

## Bluetooth on/off state machine diagram
To be able to handle all events during start up and shutting down of the Bluetooth module the following state machine has been implemented:
![bt_on_off_state_diagram](./bt_on_off_state_diagram.svg)

R module-bluetooth/bt_on_off_state_diagram.puml => module-bluetooth/doc/bt_on_off_state_diagram.puml +0 -0
R module-bluetooth/bt_on_off_state_diagram.svg => module-bluetooth/doc/bt_on_off_state_diagram.svg +0 -0
A module-bluetooth/doc/bt_scan.md => module-bluetooth/doc/bt_scan.md +17 -0
@@ 0,0 1,17 @@
# Bluetooth scan
To be able to send data between devices it is necessary to pair the devices, and to know the physical address
of the BT device, there is a need to perform devices scan.

## Principle of operation
Bluetooth scan works underneath the `ServiceBluetooth` - it is done on the middle layer, in the GAP profile interface.
When scan request is executed, GAP performs the scan and sends scanned devices to the `ApplicationSettings`. In case when the BT
module has discovered a device without a name, it will try to fetch the device name in another request. After the name-fetching
is complete, the `ServiceBluetooth` sends back the device to the application.

### Scan flow (user point of view)
- send `BluetoothMessage(BluetoothMessage::Request::Scan)` to the `ServiceBluetooth`
- for each scanned device receive `BluetoothScanResultMessage` 
- process the device
- send `BluetoothMessage(BluetoothMessage::Request::StopScan)` to the `ServiceBluetooth` to stop the scan.

![bt_scan](./bt_scan.svg)

A module-bluetooth/doc/bt_scan.puml => module-bluetooth/doc/bt_scan.puml +13 -0
@@ 0,0 1,13 @@
@startuml

actor User
participant ServiceBluetooth
==Start scan==
activate ServiceBluetooth
User -> ServiceBluetooth : BluetoothMessage(BluetoothMessage::Request::Scan)
ServiceBluetooth -> User : for each device: BluetoothScanResultMessage
deactivate ServiceBluetooth
==Stop scan==
User -> ServiceBluetooth : BluetoothMessage(BluetoothMessage::Request::StopScan)

@enduml

A module-bluetooth/doc/bt_scan.svg => module-bluetooth/doc/bt_scan.svg +23 -0
@@ 0,0 1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="366px" preserveAspectRatio="none" style="width:506px;height:366px;" version="1.1" viewBox="0 0 506 366" width="506px" zoomAndPan="magnify"><defs><filter height="300%" id="f17abn9lsdmeoc" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><rect fill="#FFFFFF" filter="url(#f17abn9lsdmeoc)" height="93.3984" style="stroke:#A80036;stroke-width:1.0;" width="10" x="421" y="98.2969"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="24" x2="24" y1="88.2969" y2="281.9609"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="426" x2="426" y1="88.2969" y2="281.9609"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="32" x="5" y="84.9951">User</text><ellipse cx="24" cy="15" fill="#FEFECE" filter="url(#f17abn9lsdmeoc)" rx="8" ry="8" style="stroke:#A80036;stroke-width:2.0;"/><path d="M24,23 L24,50 M11,31 L37,31 M24,50 L11,65 M24,50 L37,65 " fill="none" filter="url(#f17abn9lsdmeoc)" style="stroke:#A80036;stroke-width:2.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="32" x="5" y="293.9561">User</text><ellipse cx="24" cy="307.2578" fill="#FEFECE" filter="url(#f17abn9lsdmeoc)" rx="8" ry="8" style="stroke:#A80036;stroke-width:2.0;"/><path d="M24,315.2578 L24,342.2578 M11,323.2578 L37,323.2578 M24,342.2578 L11,357.2578 M24,342.2578 L37,357.2578 " fill="none" filter="url(#f17abn9lsdmeoc)" style="stroke:#A80036;stroke-width:2.0;"/><rect fill="#FEFECE" filter="url(#f17abn9lsdmeoc)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="132" x="358" y="53"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="118" x="365" y="72.9951">ServiceBluetooth</text><rect fill="#FEFECE" filter="url(#f17abn9lsdmeoc)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="132" x="358" y="280.9609"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="118" x="365" y="300.9561">ServiceBluetooth</text><rect fill="#FFFFFF" filter="url(#f17abn9lsdmeoc)" height="93.3984" style="stroke:#A80036;stroke-width:1.0;" width="10" x="421" y="98.2969"/><rect fill="#EEEEEE" filter="url(#f17abn9lsdmeoc)" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="499" x="0" y="118.8633"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="499" y1="118.8633" y2="118.8633"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="499" y1="121.8633" y2="121.8633"/><rect fill="#EEEEEE" filter="url(#f17abn9lsdmeoc)" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="89" x="205" y="108.2969"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="75" x="211" y="124.3638">Start scan</text><polygon fill="#A80036" points="409,158.5625,419,162.5625,409,166.5625,413,162.5625" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="24" x2="415" y1="162.5625" y2="162.5625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="348" x="31" y="157.4966">BluetoothMessage(BluetoothMessage::Request::Scan)</text><polygon fill="#A80036" points="35,187.6953,25,191.6953,35,195.6953,31,191.6953" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="29" x2="425" y1="191.6953" y2="191.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="295" x="41" y="186.6294">for each device: BluetoothScanResultMessage</text><rect fill="#EEEEEE" filter="url(#f17abn9lsdmeoc)" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="499" x="0" y="220.2617"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="499" y1="220.2617" y2="220.2617"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="499" y1="223.2617" y2="223.2617"/><rect fill="#EEEEEE" filter="url(#f17abn9lsdmeoc)" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="87" x="206" y="209.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="73" x="212" y="225.7622">Stop scan</text><polygon fill="#A80036" points="414,259.9609,424,263.9609,414,267.9609,418,263.9609" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="24" x2="420" y1="263.9609" y2="263.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="378" x="31" y="258.895">BluetoothMessage(BluetoothMessage::Request::StopScan)</text><!--MD5=[3e00c5d120b5619ee5a1f6367740c9a8]
@startuml

actor User
participant ServiceBluetooth
==Start scan==
activate ServiceBluetooth
User -> ServiceBluetooth : BluetoothMessage(BluetoothMessage::Request::Scan)
ServiceBluetooth -> User : for each device: BluetoothScanResultMessage
deactivate ServiceBluetooth
==Stop scan==
User -> ServiceBluetooth : BluetoothMessage(BluetoothMessage::Request::StopScan)

@enduml

PlantUML version 1.2021.01(Tue Feb 02 08:55:08 CET 2021)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Default Encoding: UTF-8
Language: en
Country: US
--></g></svg>
\ No newline at end of file

M module-bsp/CMakeLists.txt => module-bsp/CMakeLists.txt +4 -2
@@ 1,11 1,13 @@
cmake_minimum_required(VERSION 3.12)
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

cmake_minimum_required(VERSION 3.12)

project(module-bsp VERSION 1.0
        DESCRIPTION "VFS module library")

set(SOURCES
        "${CMAKE_CURRENT_SOURCE_DIR}/bsp/cellular/bsp_cellular.cpp"
		"${CMAKE_CURRENT_SOURCE_DIR}/bsp/audio/bsp_audio.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/bsp/lpm/bsp_lpm.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/drivers/pll/DriverPLL.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/drivers/dmamux/DriverDMAMux.cpp"

M module-bsp/board/rt1051/bsp/audio/CodecMAX98090.cpp => module-bsp/board/rt1051/bsp/audio/CodecMAX98090.cpp +30 -30
@@ 79,7 79,7 @@ CodecRetCode CodecMAX98090::Start(const CodecParams &param)
    i2c->Write(i2cAddr, (uint8_t *)&q_dai_setup, 1);

    // OUT configuration
    if (params.outputPath != bsp::AudioDevice::OutputPath::None) {
    if (params.outputPath != CodecParamsMAX98090::OutputPath::None) {

        // Control HP performance
        max98090_reg_dachp_perfmode_t dacperf = {};


@@ 90,7 90,7 @@ CodecRetCode CodecMAX98090::Start(const CodecParams &param)

        switch (params.outputPath) {

        case bsp::AudioDevice::OutputPath::HeadphonesMono: {
        case CodecParamsMAX98090::OutputPath::HeadphonesMono: {
            max98090_reg_playback_quick_setup_t q_playback_setup = {};
            q_playback_setup.dig2hp                              = 1;
            i2cAddr.subAddress                                   = MAX98090_REG_PLAYBACK_QUICK_SETUP;


@@ 99,12 99,12 @@ CodecRetCode CodecMAX98090::Start(const CodecParams &param)
            // Mix left DAC channel to left&right HP output
            max98090_reg_lhp_mixer_t lmixconf = {};
            lmixconf.mixhpl                   = 1;
            i2cAddr.subAddress = MAX98090_REG_LHP_MIXER_CONF;
            i2cAddr.subAddress                = MAX98090_REG_LHP_MIXER_CONF;
            i2c->Write(i2cAddr, (uint8_t *)&lmixconf, 1);

            max98090_reg_rhp_mixer_t rmixconf = {};
            rmixconf.mixhpr                   = 1;
            i2cAddr.subAddress = MAX98090_REG_RHP_MIXER_CONF;
            i2cAddr.subAddress                = MAX98090_REG_RHP_MIXER_CONF;
            i2c->Write(i2cAddr, (uint8_t *)&rmixconf, 1);

            // Use mixer outputs instead of direct DAC outputs


@@ 116,25 116,25 @@ CodecRetCode CodecMAX98090::Start(const CodecParams &param)

        } break;

        case bsp::AudioDevice::OutputPath::Headphones: {
        case CodecParamsMAX98090::OutputPath::Headphones: {
            max98090_reg_playback_quick_setup_t q_playback_setup = {};
            q_playback_setup.dig2hp                              = 1;
            i2cAddr.subAddress = MAX98090_REG_PLAYBACK_QUICK_SETUP;
            i2cAddr.subAddress                                   = MAX98090_REG_PLAYBACK_QUICK_SETUP;
            i2c->Write(i2cAddr, (uint8_t *)&q_playback_setup, 1);

        } break;

        case bsp::AudioDevice::OutputPath::Earspeaker: {
        case CodecParamsMAX98090::OutputPath::Earspeaker: {
            max98090_reg_playback_quick_setup_t q_playback_setup = {};
            q_playback_setup.dig2ear = 1;
            i2cAddr.subAddress       = MAX98090_REG_PLAYBACK_QUICK_SETUP;
            q_playback_setup.dig2ear                             = 1;
            i2cAddr.subAddress                                   = MAX98090_REG_PLAYBACK_QUICK_SETUP;
            i2c->Write(i2cAddr, (uint8_t *)&q_playback_setup, 1);

        } break;

        case bsp::AudioDevice::OutputPath::Loudspeaker: {
        case CodecParamsMAX98090::OutputPath::Loudspeaker: {
            max98090_reg_playback_quick_setup_t q_playback_setup = {};
            q_playback_setup.dig2spk = 1;
            q_playback_setup.dig2spk                             = 1;
            i2cAddr.subAddress                                   = MAX98090_REG_PLAYBACK_QUICK_SETUP;
            i2c->Write(i2cAddr, (uint8_t *)&q_playback_setup, 1);



@@ 151,10 151,10 @@ CodecRetCode CodecMAX98090::Start(const CodecParams &param)

        } break;

        case bsp::AudioDevice::OutputPath::LoudspeakerMono: {
        case CodecParamsMAX98090::OutputPath::LoudspeakerMono: {
            max98090_reg_playback_quick_setup_t q_playback_setup = {};
            q_playback_setup.dig2spk                             = 1;
            i2cAddr.subAddress = MAX98090_REG_PLAYBACK_QUICK_SETUP;
            i2cAddr.subAddress                                   = MAX98090_REG_PLAYBACK_QUICK_SETUP;
            i2c->Write(i2cAddr, (uint8_t *)&q_playback_setup, 1);

            // Turn off right speaker path


@@ 172,18 172,18 @@ CodecRetCode CodecMAX98090::Start(const CodecParams &param)
    }

    // IN configuration
    if (params.inputPath != bsp::AudioDevice::InputPath::None) {
    if (params.inputPath != CodecParamsMAX98090::InputPath::None) {
        // Set input path
        switch (params.inputPath) {

        case bsp::AudioDevice::InputPath::Headphones: {
        case CodecParamsMAX98090::InputPath::Headphones: {
            max98090_reg_input_to_record_quick_t q_input_setup = {};
            q_input_setup.in34dan                              = 1;
            i2cAddr.subAddress                                 = MAX98090_REG_LINE_INPUT_TO_RECORD_QUICK;
            i2c->Write(i2cAddr, (uint8_t *)&q_input_setup, 1);
        } break;

        case bsp::AudioDevice::InputPath::Microphone: {
        case CodecParamsMAX98090::InputPath::Microphone: {
            max98090_reg_input_to_record_quick_t q_input_setup = {};
            max98090_reg_digmic_enable_t digena                = {};
            max98090_reg_digmic_conf_t digconf                 = {};


@@ 334,8 334,8 @@ CodecRetCode CodecMAX98090::SetOutputVolume(const float vol)
    }

    switch (currentParams.outputPath) {
    case bsp::AudioDevice::OutputPath::Headphones:
    case bsp::AudioDevice::OutputPath::HeadphonesMono: {
    case CodecParamsMAX98090::OutputPath::Headphones:
    case CodecParamsMAX98090::OutputPath::HeadphonesMono: {
        // Scale input volume(range 0 - 100) to MAX98090 range(decibels hardcoded as specific hex values)
        constexpr float scale_factor     = .31f * 10.f;
        uint8_t volume                   = static_cast<float>(vol * scale_factor);


@@ 354,7 354,7 @@ CodecRetCode CodecMAX98090::SetOutputVolume(const float vol)

    } break;

    case bsp::AudioDevice::OutputPath::Earspeaker: {
    case CodecParamsMAX98090::OutputPath::Earspeaker: {
        // Scale input volume(range 0 - 100) to MAX98090 range(decibels hardcoded as specific hex values)
        constexpr float scale_factor     = .31f * 10.f;
        uint8_t volume                   = static_cast<float>(vol * scale_factor);


@@ 367,8 367,8 @@ CodecRetCode CodecMAX98090::SetOutputVolume(const float vol)
        i2c->Write(i2cAddr, (uint8_t *)&vol, 1);
    } break;

    case bsp::AudioDevice::OutputPath::Loudspeaker:
    case bsp::AudioDevice::OutputPath::LoudspeakerMono: {
    case CodecParamsMAX98090::OutputPath::Loudspeaker:
    case CodecParamsMAX98090::OutputPath::LoudspeakerMono: {
        // Scale input volume(range 0 - 100) to MAX98090 range(decibels hardcoded as specific hex values)
        constexpr float scale_factor = .39f * 10.f;
        uint8_t volume               = static_cast<float>(vol * scale_factor) + 0x18;


@@ 406,16 406,16 @@ CodecRetCode CodecMAX98090::SetInputGain(const float gain)
    }

    max98090_reg_lrec_dig_gain_t lgain = {};
    lgain.avl = static_cast<uint8_t>(CodecParamsMAX98090::RecordPathDigitalFineGain::Gain_p3dB); // fine gain
    lgain.avlg                         = gainToSet * 0.7; // coarse gain (0.7 used as scaling factor)
    lgain.avl  = static_cast<uint8_t>(CodecParamsMAX98090::RecordPathDigitalFineGain::Gain_p3dB); // fine gain
    lgain.avlg = gainToSet * 0.7; // coarse gain (0.7 used as scaling factor)

    i2cAddr.subAddress = MAX98090_REG_LREC_DIG_GAIN;
    i2c->Write(i2cAddr, (uint8_t *)&lgain, 1);

    // coarse gain - 18dB, fine gain - 0dB
    max98090_reg_rrec_dig_gain_t rgain = {};
    rgain.avr = static_cast<uint8_t>(CodecParamsMAX98090::RecordPathDigitalFineGain::Gain_p3dB); // fine gain
    rgain.avrg                         = gainToSet * 0.7; // coarse gain (0.7 used as scaling factor)
    rgain.avr  = static_cast<uint8_t>(CodecParamsMAX98090::RecordPathDigitalFineGain::Gain_p3dB); // fine gain
    rgain.avrg = gainToSet * 0.7; // coarse gain (0.7 used as scaling factor)

    i2cAddr.subAddress = MAX98090_REG_RREC_DIG_GAIN;
    i2c->Write(i2cAddr, (uint8_t *)&rgain, 1);


@@ 566,7 566,7 @@ CodecRetCode CodecMAX98090::Reset()
    return CodecRetCode::Success;
}

CodecRetCode CodecMAX98090::SetOutputPath(const bsp::AudioDevice::OutputPath path)
CodecRetCode CodecMAX98090::SetOutputPath(const CodecParamsMAX98090::OutputPath path)
{
    Reset();
    currentParams.outputPath = path;


@@ 575,7 575,7 @@ CodecRetCode CodecMAX98090::SetOutputPath(const bsp::AudioDevice::OutputPath pat
    return CodecRetCode::Success;
}

CodecRetCode CodecMAX98090::SetInputPath(const bsp::AudioDevice::InputPath path)
CodecRetCode CodecMAX98090::SetInputPath(const CodecParamsMAX98090::InputPath path)
{
    Reset();
    currentParams.inputPath = path;


@@ 591,17 591,17 @@ CodecRetCode CodecMAX98090::SetMute(const bool enable)
    uint8_t regl, regr = 0;

    switch (currentParams.outputPath) {
    case bsp::AudioDevice::OutputPath::Headphones: {
    case CodecParamsMAX98090::OutputPath::Headphones: {
        regl = MAX98090_REG_LHP_VOL_CTRL;
        regr = MAX98090_REG_RHP_VOL_CTRL;
    } break;

    case bsp::AudioDevice::OutputPath::Earspeaker: {
    case CodecParamsMAX98090::OutputPath::Earspeaker: {
        regl = MAX98090_REG_RECV_VOL_CTRL;
        regr = 0;
    } break;

    case bsp::AudioDevice::OutputPath::Loudspeaker: {
    case CodecParamsMAX98090::OutputPath::Loudspeaker: {
        regl = MAX98090_REG_LSPK_VOL_CTRL;
        regr = MAX98090_REG_RSPK_VOL_CTRL;
    } break;

M module-bsp/board/rt1051/bsp/audio/CodecMAX98090.hpp => module-bsp/board/rt1051/bsp/audio/CodecMAX98090.hpp +21 -5
@@ 6,7 6,6 @@

#include "Codec.hpp"
#include "drivers/i2c/DriverI2C.hpp"
#include "bsp/audio/bsp_audio.hpp"

class CodecParamsMAX98090 : public CodecParams
{


@@ 67,6 66,23 @@ class CodecParamsMAX98090 : public CodecParams
        Gain_n12dB
    };

    enum class InputPath
    {
        Headphones,
        Microphone,
        None
    };

    enum class OutputPath
    {
        Headphones,
        HeadphonesMono,
        Earspeaker,
        Loudspeaker,
        LoudspeakerMono,
        None
    };

    static SampleRate ValToSampleRate(uint32_t rate)
    {
        switch (rate) {


@@ 113,8 129,8 @@ class CodecParamsMAX98090 : public CodecParams
    bool muteEnable       = false;
    bool resetEnable      = false;
    bool micBiasEnable    = false;
    bsp::AudioDevice::InputPath inputPath   = bsp::AudioDevice::InputPath::None;
    bsp::AudioDevice::OutputPath outputPath = bsp::AudioDevice::OutputPath::None;
    InputPath inputPath   = InputPath::None;
    OutputPath outputPath = OutputPath::None;
    SampleRate sampleRate = SampleRate ::Rate44K1Hz;
};



@@ 144,8 160,8 @@ class CodecMAX98090 : public Codec
    CodecRetCode SetOutputVolume(const float vol);
    CodecRetCode SetInputGain(const float gain);
    CodecRetCode SetMute(const bool enable);
    CodecRetCode SetInputPath(const bsp::AudioDevice::InputPath path);
    CodecRetCode SetOutputPath(const bsp::AudioDevice::OutputPath path);
    CodecRetCode SetInputPath(const CodecParamsMAX98090::InputPath path);
    CodecRetCode SetOutputPath(const CodecParamsMAX98090::OutputPath path);
    CodecRetCode MicBias(const bool enable);
    CodecRetCode SetupEarspeakerEqualizer();
    CodecRetCode SetupLoudspeakerEqualizer();

A module-bsp/board/rt1051/common/audio.cpp => module-bsp/board/rt1051/common/audio.cpp +74 -0
@@ 0,0 1,74 @@
// Copyright (c) 2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "audio.hpp"

#include "BoardDefinitions.hpp"

extern "C"
{
#include "board.h"
#include "dma_config.h"
}

using namespace bsp;
using namespace drivers;

bsp::AudioConfig audioConfig;

void bsp::audioInit()
{
    audioConfig.pllAudio =
        DriverPLL::Create(static_cast<PLLInstances>(BoardDefinitions ::AUDIO_PLL), DriverPLLParams{});
    audioConfig.dmamux =
        DriverDMAMux::Create(static_cast<DMAMuxInstances>(BoardDefinitions ::AUDIOCODEC_DMAMUX), DriverDMAMuxParams{});
    audioConfig.dma =
        DriverDMA::Create(static_cast<DMAInstances>(BoardDefinitions ::AUDIOCODEC_DMA), DriverDMAParams{});

    // Enable MCLK clock
    IOMUXC_GPR->GPR1 |= BOARD_AUDIOCODEC_SAIx_MCLK_MASK;

    audioConfig.txDMAHandle =
        audioConfig.dma->CreateHandle(static_cast<uint32_t>(BoardDefinitions ::AUDIOCODEC_TX_DMA_CHANNEL));
    audioConfig.rxDMAHandle =
        audioConfig.dma->CreateHandle(static_cast<uint32_t>(BoardDefinitions ::AUDIOCODEC_RX_DMA_CHANNEL));
    audioConfig.dmamux->Enable(static_cast<uint32_t>(BoardDefinitions ::AUDIOCODEC_TX_DMA_CHANNEL),
                               BSP_AUDIOCODEC_SAIx_DMA_TX_SOURCE);
    audioConfig.dmamux->Enable(static_cast<uint32_t>(BoardDefinitions ::AUDIOCODEC_RX_DMA_CHANNEL),
                               BSP_AUDIOCODEC_SAIx_DMA_RX_SOURCE);

    audioConfig.mclkSourceClockHz = GetPerphSourceClock(PerphClock_SAI2);

    // Initialize SAI Tx module
    SAI_TxGetDefaultConfig(&audioConfig.config);
    audioConfig.config.masterSlave = kSAI_Slave;
    SAI_TxInit(BOARD_AUDIOCODEC_SAIx, &audioConfig.config);

    // Initialize SAI Rx module
    SAI_RxGetDefaultConfig(&audioConfig.config);

    audioConfig.config.masterSlave = kSAI_Slave;
    SAI_RxInit(BOARD_AUDIOCODEC_SAIx, &audioConfig.config);
}

void bsp::audioDeinit()
{
    memset(&audioConfig.config, 0, sizeof(audioConfig.config));
    SAI_Deinit(BOARD_AUDIOCODEC_SAIx);
    if (audioConfig.dmamux) {
        audioConfig.dmamux->Disable(static_cast<uint32_t>(BoardDefinitions ::AUDIOCODEC_TX_DMA_CHANNEL));
        audioConfig.dmamux->Disable(static_cast<uint32_t>(BoardDefinitions ::AUDIOCODEC_RX_DMA_CHANNEL));
    }

    // force order of destruction
    audioConfig.txDMAHandle.reset();
    audioConfig.rxDMAHandle.reset();
    audioConfig.dma.reset();
    audioConfig.dmamux.reset();
    audioConfig.pllAudio.reset();
}

AudioConfig *bsp::AudioConfig::get()
{
    return &audioConfig;
}

A module-bsp/board/rt1051/common/audio.hpp => module-bsp/board/rt1051/common/audio.hpp +32 -0
@@ 0,0 1,32 @@
// Copyright (c) 2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "drivers/pll/DriverPLL.hpp"
#include "drivers/dmamux/DriverDMAMux.hpp"
#include "drivers/dma/DriverDMA.hpp"

#include "fsl_sai_edma.h"

#include <memory>

namespace bsp
{
    void audioInit();
    void audioDeinit();

    struct AudioConfig
    {
        sai_config_t config;
        std::uint32_t mclkSourceClockHz;

        std::shared_ptr<drivers::DriverPLL> pllAudio;
        std::shared_ptr<drivers::DriverDMAMux> dmamux;
        std::shared_ptr<drivers::DriverDMA> dma;
        std::unique_ptr<drivers::DriverDMAHandle> rxDMAHandle;
        std::unique_ptr<drivers::DriverDMAHandle> txDMAHandle;

        static AudioConfig *get();
    };
}; // namespace bsp

M module-bsp/board/rt1051/common/board.cpp => module-bsp/board/rt1051/common/board.cpp +2 -2
@@ 10,9 10,9 @@ extern "C"
#include "fsl_lpuart.h"
#endif
}
#include "audio.hpp"
#include "chip.hpp"
#include "irq/irq_gpio.hpp"
#include "audio/RT1051Audiocodec.hpp"

#include <cstdint>



@@ 193,7 193,7 @@ namespace bsp
        DCDC_BootIntoDCM(DCDC);

        // init audio
        RT1051Audiocodec::Init();
        audioInit();

        PrintSystemClocks();
        clearAndPrintBootReason();

D module-bsp/bsp/audio/bsp_audio.cpp => module-bsp/bsp/audio/bsp_audio.cpp +0 -52
@@ 1,52 0,0 @@
#include "bsp_audio.hpp"

#if defined(TARGET_RT1051)

#include "board/rt1051/bsp/audio/RT1051Audiocodec.hpp"
#include "board/rt1051/bsp/audio/RT1051CellularAudio.hpp"

#endif

#include <Audio/Stream.hpp>

#include <cassert>

namespace bsp
{

    std::optional<std::unique_ptr<AudioDevice>> AudioDevice::Create(bsp::AudioDevice::Type type)
    {
        std::unique_ptr<AudioDevice> inst;

        switch (type) {

        case Type ::Audiocodec: {
#if defined(TARGET_RT1051)
            inst = std::make_unique<bsp::RT1051Audiocodec>();
#else
            inst = nullptr;
#endif

        } break;

        case Type ::Bluetooth: {
            LOG_FATAL("Bluetooth audio is not yet supported");
            inst = nullptr;
        } break;

        case Type::Cellular: {
#if defined(TARGET_RT1051)
            inst = std::make_unique<bsp::RT1051CellularAudio>();
#else
            inst = nullptr;
#endif
        } break;
        }

        if (inst->isInitialized) {
            return inst;
        }

        return {};
    }
} // namespace bsp

M module-bsp/targets/Target_RT1051.cmake => module-bsp/targets/Target_RT1051.cmake +4 -3
@@ 1,3 1,6 @@
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

include(board/rt1051/bsp/usb/usb.cmake)

set(BOARD_SOURCES ${BOARD_SOURCES}


@@ 31,6 34,7 @@ set(BOARD_SOURCES ${BOARD_SOURCES}
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/common/fsl_drivers/fsl_wdog.c"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/common/fsl_drivers/fsl_pwm.c"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/common/irq/irq_gpio.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/common/audio.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/common/board.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/common/chip.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/common/clock_config.cpp"


@@ 57,9 61,6 @@ set(BOARD_SOURCES ${BOARD_SOURCES}
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/bluetooth/BlueKitchen.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/bsp/rtc/rtc.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/bsp/battery-charger/battery_charger.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/bsp/audio/RT1051Audiocodec.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/bsp/audio/RT1051CellularAudio.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/bsp/audio/SAIAudioDevice.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/bsp/audio/CodecMAX98090.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/bsp/audio/qfilter.c"
	"${USB_SRC}"

M module-db/Common/Query.cpp => module-db/Common/Query.cpp +20 -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

#include "Query.hpp"


@@ 22,7 22,12 @@ bool QueryCallback::handleQueryResponse(QueryResult *response)
}

EndpointListener::EndpointListener(EndpointQueryCallbackFunction &&_callback, parserFSM::Context &_context)
    : callback{std::move(_callback)}, context{_context}
    : callback{std::move(_callback)}, context(_context)
{}

EndpointListenerWithPages::EndpointListenerWithPages(EndpointQueryCallbackFunctionWithPages &&_callback,
                                                     const parserFSM::PagedContext &_context)
    : callback{std::move(_callback)}, context(_context)
{}

bool EndpointListener::handleQueryResponse(db::QueryResult *response)


@@ 38,6 43,19 @@ bool EndpointListener::handleQueryResponse(db::QueryResult *response)
    return false;
}

bool EndpointListenerWithPages::handleQueryResponse(db::QueryResult *response)
{
    if (callback) {
        LOG_DEBUG("Executing callback...");
        const auto ret = callback(response, context);
        LOG_DEBUG("Callback finished");
        return ret;
    }

    LOG_ERROR("callback is nullptr!");
    return false;
}

Query::Query(Type type) : type(type)
{}


M module-db/Common/Query.hpp => module-db/Common/Query.hpp +16 -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


@@ 14,7 14,7 @@ namespace db
    class QueryResult; // Forward declaration
    using QueryCallbackFunction         = std::function<bool(db::QueryResult *)>;
    using EndpointQueryCallbackFunction = std::function<bool(db::QueryResult *, parserFSM::Context &)>;

    using EndpointQueryCallbackFunctionWithPages = std::function<bool(db::QueryResult *, parserFSM::PagedContext &)>;
    class QueryListener
    {
      public:


@@ 49,6 49,20 @@ namespace db
        parserFSM::Context context;
    };

    class EndpointListenerWithPages : public EndpointListener
    {
      public:
        EndpointListenerWithPages() = default;
        EndpointListenerWithPages(EndpointQueryCallbackFunctionWithPages &&_callback,
                                  const parserFSM::PagedContext &_context);

        bool handleQueryResponse(db::QueryResult *result) override;

      private:
        EndpointQueryCallbackFunctionWithPages callback;
        parserFSM::PagedContext context;
    };

    /// virtual query input interface
    class Query
    {

M module-db/Interface/ContactRecord.cpp => module-db/Interface/ContactRecord.cpp +32 -4
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ContactRecord.hpp"


@@ 135,6 135,9 @@ auto ContactRecordInterface::runQuery(std::shared_ptr<db::Query> query) -> std::
    if (typeid(*query) == typeid(db::query::ContactGet)) {
        return getQuery(query);
    }
    else if (typeid(*query) == typeid(db::query::ContactGetWithTotalCount)) {
        return getQueryWithTotalCount(query);
    }
    else if (typeid(*query) == typeid(db::query::ContactGetLetterMap)) {
        return getLetterMapQuery(query);
    }


@@ 162,7 165,7 @@ auto ContactRecordInterface::runQuery(std::shared_ptr<db::Query> query) -> std::
    }
}

auto ContactRecordInterface::getQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
auto ContactRecordInterface::getQueryRecords(std::shared_ptr<db::Query> query) -> std::vector<ContactRecord>
{
    auto textFilter = dynamic_cast<const db::query::TextFilter *>(query.get());
    assert(query != nullptr);


@@ 203,11 206,31 @@ auto ContactRecordInterface::getQuery(std::shared_ptr<db::Query> query) -> std::
    for (uint32_t idx = 0; idx < static_cast<uint32_t>(ids.size()); idx++) {
        result[idx].contactPosOnList = offset + idx;
    }
    auto response = std::make_unique<db::query::ContactGetResult>(result);
    return result;
}

auto ContactRecordInterface::getQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
{
    auto response = std::make_unique<db::query::ContactGetResult>(getQueryRecords(query));
    response->setRequestQuery(query);
    return response;
}

auto ContactRecordInterface::getQueryWithTotalCount(std::shared_ptr<db::Query> query)
    -> std::unique_ptr<db::QueryResult>
{
    if (auto queryContacts = dynamic_cast<db::query::ContactGet *>(query.get())) {
        auto querySize = std::make_shared<db::query::ContactGetSize>(queryContacts->getFilterData(),
                                                                     queryContacts->getGroupFilterData(),
                                                                     queryContacts->getContactDisplayMode());
        auto response  = std::make_unique<db::query::ContactGetResultWithTotalCount>(getQueryRecords(query),
                                                                                    getContactsSize(querySize));
        response->setRequestQuery(query);
        return response;
    }
    return nullptr;
}

auto ContactRecordInterface::getLetterMapQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
{
    ContactsMapData result = contactDB->contacts.GetPosOfFirstLetters();


@@ 233,7 256,7 @@ auto ContactRecordInterface::getByIDQuery(std::shared_ptr<db::Query> query) -> s
    return response;
}

auto ContactRecordInterface::getSizeQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
auto ContactRecordInterface::getContactsSize(std::shared_ptr<db::Query> query) -> std::size_t
{
    auto textFilter = dynamic_cast<const db::query::TextFilter *>(query.get());
    assert(query != nullptr);


@@ 280,7 303,12 @@ auto ContactRecordInterface::getSizeQuery(std::shared_ptr<db::Query> query) -> s
    else {
        count = contactDB->name.GetCountByName(countQuery->getFilterData());
    }
    return count;
}

auto ContactRecordInterface::getSizeQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
{
    auto count = getContactsSize(query);
    debug_db_data("Contact count query result: %lu", static_cast<unsigned long>(count));

    auto response = std::make_unique<db::query::RecordsSizeQueryResult>(count);

M module-db/Interface/ContactRecord.hpp => module-db/Interface/ContactRecord.hpp +4 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 236,10 236,13 @@ class ContactRecordInterface : public RecordInterface<ContactRecord, ContactReco

    const std::uint32_t favouritesGroupId;
    auto getQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
    auto getQueryRecords(std::shared_ptr<db::Query> query) -> std::vector<ContactRecord>;
    auto getQueryWithTotalCount(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
    auto getForListQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
    auto getLetterMapQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;

    auto getByIDQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
    auto getContactsSize(std::shared_ptr<db::Query> query) -> std::size_t;
    auto getSizeQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
    auto addQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
    auto updateQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;

M module-db/Interface/ThreadRecord.cpp => module-db/Interface/ThreadRecord.cpp +3 -1
@@ 226,7 226,9 @@ std::unique_ptr<db::QueryResult> ThreadRecordInterface::markAsReadQuery(const st
        ret                   = Update(record);
    }

    return std::make_unique<db::query::MarkAsReadResult>(ret);
    auto response = std::make_unique<db::query::MarkAsReadResult>(ret);
    response->setRequestQuery(query);
    return response;
}

std::unique_ptr<db::QueryResult> ThreadRecordInterface::threadsGetQuery(const std::shared_ptr<db::Query> &query)

M module-db/queries/Filter.hpp => module-db/queries/Filter.hpp +3 -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

#pragma once


@@ 95,7 95,7 @@ namespace db::query
         *
         * @param groupText group filter text
         */
        ContactGroupFilter(const std::uint32_t &groupId) : filterData(std::move(groupId))
        ContactGroupFilter(std::uint32_t groupId) : filterData(groupId)
        {}

        [[nodiscard]] const std::uint32_t &getGroupFilterData() const noexcept


@@ 121,7 121,7 @@ namespace db::query
         *
         * @param mode contact display mode
         */
        ContactDisplayMode(const std::uint32_t &mode) : displayMode(std::move(mode))
        ContactDisplayMode(std::uint32_t mode) : displayMode(mode)
        {}

        [[nodiscard]] const std::uint32_t &getContactDisplayMode() const noexcept

M module-db/queries/RecordQuery.cpp => module-db/queries/RecordQuery.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RecordQuery.hpp"


@@ 13,7 13,7 @@ using namespace db::query;
RecordQuery::RecordQuery() noexcept : Query(Query::Type::Read)
{}

RecordQuery::RecordQuery(std::size_t offset, std::size_t limit) noexcept
RecordQuery::RecordQuery(std::size_t limit, std::size_t offset) noexcept
    : Query(Query::Type::Read), limit(limit), offset(offset)
{}


M module-db/queries/phonebook/QueryContactGet.cpp => module-db/queries/phonebook/QueryContactGet.cpp +31 -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 "QueryContactGet.hpp"


@@ 14,14 14,32 @@ ContactGet::ContactGet(const std::string &filter, const std::uint32_t &groupFilt
ContactGet::ContactGet(std::size_t limit,
                       std::size_t offset,
                       const std::string &filter,
                       const std::uint32_t &groupFilter,
                       const std::uint32_t &displayMode)
                       std::uint32_t groupFilter,
                       std::uint32_t displayMode)
    : RecordQuery(limit, offset), TextFilter(filter), ContactGroupFilter(groupFilter), ContactDisplayMode(displayMode)
{}

ContactGetWithTotalCount::ContactGetWithTotalCount(std::size_t limit,
                                                   std::size_t offset,
                                                   const std::string &filter,
                                                   std::uint32_t groupFilter,
                                                   std::uint32_t displayMode)
    : ContactGet(limit, offset, filter, groupFilter, displayMode)
{}

ContactGetResult::ContactGetResult(const std::vector<ContactRecord> &records) : RecordQueryResult(records)
{}

ContactGetResultWithTotalCount::ContactGetResultWithTotalCount(const std::vector<ContactRecord> &records,
                                                               std::size_t allLength)
    : ContactGetResult(records), allLength(allLength)
{}

auto ContactGetResultWithTotalCount::getAllLength() const -> std::size_t
{
    return allLength;
}

ContactGetSize::ContactGetSize(const std::string &filter,
                               const std::uint32_t &groupFilter,
                               const std::uint32_t &displayMode)


@@ 50,11 68,21 @@ ContactGetLetterMapResult ::ContactGetLetterMapResult(ContactsMapData &LetterMap
    return "ContactGet";
}

[[nodiscard]] auto ContactGetWithTotalCount::debugInfo() const -> std::string
{
    return "ContactGetWithTotalCount";
}

[[nodiscard]] auto ContactGetResult::debugInfo() const -> std::string
{
    return "ContactGetResult";
}

[[nodiscard]] auto ContactGetResultWithTotalCount::debugInfo() const -> std::string
{
    return "ContactGetResultWithTotalCount";
}

[[nodiscard]] auto ContactGetSize::debugInfo() const -> std::string
{
    return "ContactGetSize";

M module-db/queries/phonebook/QueryContactGet.hpp => module-db/queries/phonebook/QueryContactGet.hpp +26 -4
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 37,9 37,9 @@ namespace db::query
         */
        ContactGet(std::size_t limit,
                   std::size_t offset,
                   const std::string &filter        = "",
                   const std::uint32_t &groupFilter = 0,
                   const std::uint32_t &displayMode = 0);
                   const std::string &filter = "",
                   std::uint32_t groupFilter = 0,
                   std::uint32_t displayMode = 0);

        /**
         * @brief debug info


@@ 72,6 72,17 @@ namespace db::query
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class ContactGetResultWithTotalCount : public ContactGetResult
    {
      public:
        ContactGetResultWithTotalCount(const std::vector<ContactRecord> &records, std::size_t allLength);
        [[nodiscard]] auto debugInfo() const -> std::string override;
        auto getAllLength() const -> std::size_t;

      private:
        std::size_t allLength;
    };

    /**
     * @brief A query to get a number of contacts filtered with a text
     *


@@ 99,6 110,17 @@ namespace db::query
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class ContactGetWithTotalCount : public ContactGet
    {
      public:
        ContactGetWithTotalCount(std::size_t limit,
                                 std::size_t offset,
                                 const std::string &filter = "",
                                 std::uint32_t groupFilter = 0,
                                 std::uint32_t displayMode = 0);
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class ContactGetLetterMap : public RecordQuery,
                                public TextFilter,
                                public ContactGroupFilter,

M module-gui/gui/widgets/BoxLayout.cpp => module-gui/gui/widgets/BoxLayout.cpp +1 -0
@@ 551,6 551,7 @@ namespace gui
    {
        type = ItemType::HBOX;
    }

    HBox::HBox(Item *parent, const uint32_t &x, const uint32_t &y, const uint32_t &w, const uint32_t &h)
        : BoxLayout(parent, x, y, w, h)
    {

M module-gui/gui/widgets/CMakeLists.txt => module-gui/gui/widgets/CMakeLists.txt +3 -1
@@ 22,6 22,9 @@ target_sources( ${PROJECT_NAME}
		"${CMAKE_CURRENT_LIST_DIR}/BoxLayoutSizeStore.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/TopBar.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/TopBar/SIM.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/TopBar/BatteryWidgetBase.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/TopBar/BatteryWidgetBar.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/TopBar/BatteryWidgetText.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/Text.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/TextBlock.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/TextDocument.cpp"


@@ 71,4 74,3 @@ target_sources( ${PROJECT_NAME}
		"${CMAKE_CURRENT_LIST_DIR}/visitor/ItemTree.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/visitor/DepthFirstItemTree.hpp"
)


M module-gui/gui/widgets/ListItem.hpp => module-gui/gui/widgets/ListItem.hpp +8 -0
@@ 17,4 17,12 @@ namespace gui
        ListItem();
    };

    template <class T> class ListItemWithCallbacks : public ListItem
    {
      public:
        std::function<bool()> onEmptyCallback                          = nullptr;
        std::function<bool()> onContentChangedCallback                 = nullptr;
        std::function<void(std::shared_ptr<T> contact)> onSaveCallback = nullptr;
        std::function<void(std::shared_ptr<T> contact)> onLoadCallback = nullptr;
    };
} /* namespace gui */

M module-gui/gui/widgets/TextBlockCursor.cpp => module-gui/gui/widgets/TextBlockCursor.cpp +2 -3
@@ 267,7 267,7 @@ namespace gui
            return false;
        }

        if (nextBlock != blocksEnd() && nextBlock->isEmpty() && nextBlock != (--blocksEnd())) {
        if (nextBlock != blocksEnd() && nextBlock->isEmpty()) {
            debug_cursor("Next empty block removed");
            document->removeBlock(nextBlock);
        }


@@ 280,8 280,7 @@ namespace gui

        block->removeChar(pos);

        if (block->isEmpty() && block != blocksEnd() &&
            !(prevBlock->getEnd() == TextBlock::End::Newline && nextBlock->isEmpty())) {
        if (block->isEmpty() && block == blocksBegin()) {
            debug_cursor("Current empty block removed");
            document->removeBlock(block);
        }

M module-gui/gui/widgets/TopBar.cpp => module-gui/gui/widgets/TopBar.cpp +14 -96
@@ 8,11 8,15 @@
#include "TopBar.hpp"
#include <time/time_conversion.hpp>
#include "Style.hpp"

#include "TopBar/BatteryWidgetBar.hpp"
#include "TopBar/BatteryWidgetText.hpp"
#include "common_data/EventStore.hpp"

namespace gui::top_bar
{
    constexpr auto batteryWidgetAsText = false;
    using BatteryWidgetType = std::conditional<batteryWidgetAsText, BatteryWidgetText, BatteryWidgetBar>::type;

    namespace networkTechnology
    {
        constexpr uint32_t x = 80;


@@ 24,8 28,8 @@ namespace gui::top_bar
    static constexpr uint32_t signalOffset  = 35;
    static constexpr uint32_t batteryOffset = 413;

    TopBar::TimeMode TopBar::timeMode      = TimeMode::TIME_24H;
    uint32_t TopBar::time                  = 0;
    TopBar::TimeMode TopBar::timeMode = TimeMode::TIME_24H;
    uint32_t TopBar::time             = 0;

    void Configuration::enable(Indicator indicator)
    {


@@ 74,22 78,6 @@ namespace gui::top_bar
        };
    }

    void TopBar::batteryShowBars(uint32_t val)
    {
        if (val > batteryBars.size()) {
            LOG_ERROR("Trying to set battery level out of scope");
            val = batteryBars.size();
        }
        for (unsigned int i = 0; i < batteryBars.size(); ++i) {
            if (configuration.isEnabled(Indicator::Battery)) {
                batteryBars[i]->setVisible(i == val);
            }
            else {
                batteryBars[i]->setVisible(false);
            }
        }
    }

    void TopBar::prepareWidget()
    {
        signal[0] = new gui::Image(this, signalOffset, 17, 0, 0, "signal0");


@@ 100,24 88,7 @@ namespace gui::top_bar
        signal[5] = new gui::Image(this, signalOffset, 17, 0, 0, "signal5");
        updateSignalStrength();

        // icons for battery
        batteryBars = {
            new gui::Image(this, batteryOffset, 15, 0, 0, "battery_low_W_M"),
            new gui::Image(this, batteryOffset, 15, 0, 0, "battery1_W_M"),
            new gui::Image(this, batteryOffset, 15, 0, 0, "battery2_W_M"),
            new gui::Image(this, batteryOffset, 15, 0, 0, "battery3_W_M"),
            new gui::Image(this, batteryOffset, 15, 0, 0, "battery4_W_M"),
            new gui::Image(this, batteryOffset, 15, 0, 0, "battery5_W_M"),
        };
        batteryShowBars(0);

        batteryChargings[Store::Battery::State::Charging] =
            new gui::Image(this, batteryOffset, 15, 0, 0, "battery_charging_W_M");
        batteryChargings[Store::Battery::State::PluggedNotCharging] =
            new gui::Image(this, batteryOffset, 15, 0, 0, "battery_charging_ready_W_M");
        for (auto &el : batteryChargings) {
            el.second->setVisible(false);
        }
        batteryWidget = new BatteryWidgetType(this, batteryOffset, 15, 60, 24);

        const auto design_sim_offset = 376; // this offset is not final, but it is pixel Purefect
        sim                          = new SIM(this, design_sim_offset, 12);


@@ 181,7 152,7 @@ namespace gui::top_bar
            }
            break;
        case Indicator::Battery:
            showBattery(enabled);
            batteryWidget->show(Store::Battery::get(), enabled);
            break;
        case Indicator::SimCard:
            showSim(enabled);


@@ 192,65 163,12 @@ namespace gui::top_bar
        }
    }

    uint32_t TopBar::calculateBatteryBars(uint32_t percentage)
    {
        uint32_t level = 0;
        if (percentage <= 5) // level critical
            level = 0;
        else if (percentage <= 27)
            level = 1;
        else if (percentage <= 50)
            level = 2;
        else if (percentage <= 73)
            level = 3;
        else if (percentage <= 95)
            level = 4;
        else
            level = 5;

        if (level >= batteryBarsCount) {
            LOG_ERROR("Battery level calculations are done wrong!");
            return batteryBarsCount - 1;
        }
        return level;
    }

    bool TopBar::updateBattery(uint32_t percent)
    bool TopBar::updateBattery()
    {
        showBattery(configuration.isEnabled(Indicator::Battery));
        batteryWidget->show(Store::Battery::get(), configuration.isEnabled(Indicator::Battery));
        return true;
    }

    bool TopBar::updateBattery(bool plugged)
    {
        showBattery(configuration.isEnabled(Indicator::Battery));
        return true;
    }

    void TopBar::showBattery(bool shown)
    {
        // hide battery bars icons
        for (const auto &bars : batteryBars) {
            bars->setVisible(false);
        }
        // hide battery charging icons
        for (const auto &charging : batteryChargings) {
            charging.second->setVisible(false);
        }

        if (shown) {
            switch (Store::Battery::get().state) {
            case Store::Battery::State::Discharging:
                batteryShowBars(calculateBatteryBars(Store::Battery::get().level));
                break;
            case Store::Battery::State::Charging:
            case Store::Battery::State::PluggedNotCharging:
                batteryChargings[Store::Battery::get().state]->setVisible(true);
                break;
            }
        }
    }

    void TopBar::showSim(bool enabled)
    {
        if (!enabled) {


@@ 315,8 233,8 @@ namespace gui::top_bar
    void TopBar::setTime(uint32_t value, bool mode24H)
    {
        setTime(utils::time::Time());
        timeMode   = (mode24H ? TimeMode::TIME_24H : TimeMode::TIME_12H);
        time       = value;
        timeMode = (mode24H ? TimeMode::TIME_24H : TimeMode::TIME_12H);
        time     = value;
    }

    UTF8 TopBar::getTimeString()


@@ 342,4 260,4 @@ namespace gui::top_bar
    {
        visitor.visit(*this);
    }
} /* namespace gui */
} // namespace gui::top_bar

M module-gui/gui/widgets/TopBar.hpp => module-gui/gui/widgets/TopBar.hpp +4 -8
@@ 7,6 7,7 @@
#include "Label.hpp"
#include "Rect.hpp"
#include "TopBar/SIM.hpp"
#include "TopBar/BatteryWidgetBase.hpp"
#include <common_data/EventStore.hpp>

#include <vector>


@@ 82,18 83,14 @@ namespace gui::top_bar
        std::map<const Store::Battery::State, Image *> batteryChargings = {
            {Store::Battery::State::Charging, nullptr}, {Store::Battery::State::PluggedNotCharging, nullptr}};
        gui::SIM *sim = nullptr;
        gui::BatteryWidgetBase *batteryWidget = nullptr;
        Configuration configuration;
        static TimeMode timeMode;

        void prepareWidget();

        /// show bars in number - 0 bars, 1 bar, 2 bars...
        void batteryShowBars(uint32_t val);
        void showBattery(bool shown);
        void showSim(bool enabled);

        static uint32_t calculateBatteryBars(uint32_t percentage);

        /**
         * Sets the status of the top bar indicator.
         * @param indicator     Indicator


@@ 111,12 108,11 @@ namespace gui::top_bar
        [[nodiscard]] auto getConfiguration() const noexcept -> const Configuration &;

        /**
         * @brief Sets charge level of the battery based on percent value. This will cause appropriate image to be
         * @brief Sets charge level of the battery. This will cause appropriate image to be
         * displayed.
         * @return if display should be refreshed or not
         */
        bool updateBattery(uint32_t percent);
        bool updateBattery(bool plugged);
        bool updateBattery();

        /**
         * @brief updates signal strength. This will cause appropriate image to be displayed.

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

#include "BatteryWidgetBar.hpp"
#include "Style.hpp"
#include "Utils.hpp"
#include "visitor/GuiVisitor.hpp"

namespace gui
{
    namespace
    {
        constexpr auto batteryLow           = "battery_low_W_M";
        constexpr auto batteryCharging      = "battery_charging_W_M";
        constexpr auto batteryChargingReady = "battery_charging_ready_W_M";
        constexpr auto battery1             = "battery1_W_M";
        constexpr auto battery2             = "battery2_W_M";
        constexpr auto battery3             = "battery3_W_M";
        constexpr auto battery4             = "battery4_W_M";
        constexpr auto battery5             = "battery5_W_M";

        constexpr auto level1Threshold = 5;
        constexpr auto level2Threshold = 27;
        constexpr auto level3Threshold = 50;
        constexpr auto level4Threshold = 73;
        constexpr auto level5Threshold = 95;
    } // namespace

    BatteryWidgetBar::BatteryWidgetBar(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
        : BatteryWidgetBase(parent, x, y, w, h)
    {
        img = new Image(this, battery1);
    }

    void BatteryWidgetBar::showBatteryLevel(std::uint32_t percentage)
    {
        if (percentage <= level1Threshold) {
            img->set(batteryLow);
        }
        else if (percentage <= level2Threshold) {
            img->set(battery1);
        }
        else if (percentage <= level3Threshold) {
            img->set(battery2);
        }
        else if (percentage <= level4Threshold) {
            img->set(battery3);
        }
        else if (percentage <= level5Threshold) {
            img->set(battery4);
        }
        else {
            img->set(battery5);
        }
    }

    void BatteryWidgetBar::showBatteryCharging()
    {
        img->set(batteryCharging);
    }

    void BatteryWidgetBar::showBatteryPluggedNotCharging()
    {
        img->set(batteryChargingReady);
    }

} // namespace gui

A module-gui/gui/widgets/TopBar/BatteryWidgetBar.hpp => module-gui/gui/widgets/TopBar/BatteryWidgetBar.hpp +23 -0
@@ 0,0 1,23 @@
// 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 "BatteryWidgetBase.hpp"
#include "Image.hpp"

namespace gui
{
    class BatteryWidgetBar : public BatteryWidgetBase
    {
      public:
        BatteryWidgetBar(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h);

      private:
        void showBatteryLevel(std::uint32_t percentage) override;
        void showBatteryPluggedNotCharging() override;
        void showBatteryCharging() override;

        Image *img = nullptr;
    };
} // namespace gui

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

#include "BatteryWidgetBase.hpp"
#include "BoxLayout.hpp"

namespace gui
{
    BatteryWidgetBase::BatteryWidgetBase(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
        : HBox(parent, x, y, w, h)
    {
        setBorderColor(gui::ColorNoColor);
    }

    void BatteryWidgetBase::show(const Store::Battery batteryContext, bool shown)
    {
        if (shown) {
            setVisible(true);
            switch (batteryContext.state) {
            case Store::Battery::State::Discharging:
                showBatteryLevel(batteryContext.level);
                break;
            case Store::Battery::State::Charging:
                showBatteryCharging();
                break;
            case Store::Battery::State::PluggedNotCharging:
                showBatteryPluggedNotCharging();
                break;
            }
        }
        else {
            setVisible(false);
        }
    }
} // namespace gui

A module-gui/gui/widgets/TopBar/BatteryWidgetBase.hpp => module-gui/gui/widgets/TopBar/BatteryWidgetBase.hpp +23 -0
@@ 0,0 1,23 @@
// 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 "BoxLayout.hpp"
#include <common_data/EventStore.hpp>

namespace gui
{
    class HBox;

    class BatteryWidgetBase : public HBox
    {
        virtual void showBatteryLevel(std::uint32_t percentage) = 0;
        virtual void showBatteryPluggedNotCharging()            = 0;
        virtual void showBatteryCharging()                      = 0;

      public:
        BatteryWidgetBase(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
        void show(const Store::Battery batteryContext, bool shown);
    };
} // namespace gui

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

#include "BatteryWidgetText.hpp"
#include "Label.hpp"
#include <Utils.hpp>
#include <Style.hpp>

namespace gui
{
    BatteryWidgetText::BatteryWidgetText(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
        : BatteryWidgetBase(parent, x, y, w, h)
    {
        label = new Label(this, 0, 0, 0, 0);
        label->setFilled(false);
        label->setBorderColor(gui::ColorNoColor);
        label->setFont(style::header::font::modes);
        label->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        label->setMaximumSize(this->getWidth(), this->getHeight());
    }

    void BatteryWidgetText::showBatteryLevel(std::uint32_t percentage)
    {
        label->setText(utils::to_string(percentage) + " %");
    }

    void BatteryWidgetText::showBatteryCharging()
    {
        label->setText(utils::localize.get("topbar_battery_charging"));
    }

    void BatteryWidgetText::showBatteryPluggedNotCharging()
    {
        label->setText(utils::localize.get("topbar_battery_plugged"));
    }

} // namespace gui

A module-gui/gui/widgets/TopBar/BatteryWidgetText.hpp => module-gui/gui/widgets/TopBar/BatteryWidgetText.hpp +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

#pragma once

#include "BatteryWidgetBase.hpp"

namespace gui
{
    class Label;
    class BatteryWidgetText : public BatteryWidgetBase
    {
      public:
        BatteryWidgetText(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h);

      private:
        void showBatteryLevel(std::uint32_t percentage) override;
        void showBatteryPluggedNotCharging() override;
        void showBatteryCharging() override;
        Label *label = nullptr;
    };
} // namespace gui

M module-gui/test/test-catch-text/test-gui-Text.cpp => module-gui/test/test-catch-text/test-gui-Text.cpp +103 -0
@@ 82,6 82,13 @@ namespace gui
            return mode;
        }

        auto removeNCharacters(unsigned int n)
        {
            for (unsigned int i = 0; i < n; i++) {
                removeChar();
            }
        }

        auto moveCursor(NavigationDirection direction, unsigned int n)
        {
            cursor->moveCursor(direction, n);


@@ 1100,6 1107,102 @@ TEST_CASE("Text addition bounds - multiple limits tests")
    }
}

TEST_CASE("Text newline navigation and deletion tests")
{
    std::string testStringBlock1 = "Test String 1";
    std::string testStringBlock2 = "Test String 2";
    std::string emptyParagraph   = "<p></p>";

    SECTION("Empty new block at start and delete from text end")
    {
        mockup::fontManager();
        using namespace gui;
        auto text = std::make_unique<gui::TestText>();
        text->setMaximumSize(600, 200);

        text->addRichText("<text>" + emptyParagraph + testStringBlock1 + "</text>");

        REQUIRE(text->linesSize() == 2);
        REQUIRE((*text->lineGet(0)).getText(0) == "\n");
        REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock1);

        text->removeNCharacters(text->getText().length());

        REQUIRE(text->linesSize() == 0);
        REQUIRE(text->linesGet().empty());
    }

    SECTION("Empty new block at start and delete from text center")
    {
        mockup::fontManager();
        using namespace gui;
        auto text = std::make_unique<gui::TestText>();
        text->setMaximumSize(600, 200);

        text->addRichText("<text>" + emptyParagraph + testStringBlock1 + emptyParagraph + testStringBlock2 + "</text>");

        REQUIRE(text->linesSize() == 3);
        REQUIRE((*text->lineGet(0)).getText(0) == "\n");
        REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock1 + "\n");
        REQUIRE((*text->lineGet(2)).getText(0) == testStringBlock2);

        text->moveCursor(gui::NavigationDirection::LEFT, testStringBlock2.length());
        text->removeNCharacters(1);

        REQUIRE(text->linesSize() == 2);
        REQUIRE((*text->lineGet(0)).getText(0) == "\n");
        REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock1 + testStringBlock2);

        text->removeNCharacters(testStringBlock1.length());

        REQUIRE(text->linesSize() == 2);
        REQUIRE((*text->lineGet(0)).getText(0) == "\n");
        REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock2);

        text->removeNCharacters(1);

        REQUIRE(text->linesSize() == 1);
        REQUIRE((*text->lineGet(0)).getText(0) == testStringBlock2);

        text->removeNCharacters(testStringBlock2.length());

        REQUIRE(text->linesSize() == 0);
        REQUIRE(text->linesGet().empty());
    }

    SECTION("Empty new block at start and delete from text beginning")
    {
        mockup::fontManager();
        using namespace gui;
        auto text = std::make_unique<gui::TestText>();
        text->setCursorStartPosition(gui::CursorStartPosition::DocumentBegin);
        text->setMaximumSize(600, 200);

        text->addRichText("<text>" + emptyParagraph + testStringBlock1 + emptyParagraph + testStringBlock2 + "</text>");

        REQUIRE(text->linesSize() == 3);
        REQUIRE((*text->lineGet(0)).getText(0) == "\n");
        REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock1 + "\n");
        REQUIRE((*text->lineGet(2)).getText(0) == testStringBlock2);

        text->removeNCharacters(1);

        REQUIRE(text->linesSize() == 2);
        REQUIRE((*text->lineGet(0)).getText(0) == testStringBlock1 + "\n");
        REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock2);

        text->removeNCharacters(testStringBlock1.length() + 1);

        REQUIRE(text->linesSize() == 1);
        REQUIRE((*text->lineGet(0)).getText(0) == testStringBlock2);

        text->removeNCharacters(testStringBlock2.length());

        REQUIRE(text->linesSize() == 0);
        REQUIRE(text->linesGet().empty());
    }
}

TEST_CASE("RichText newline and empty lines tests")
{
    std::string testStringBlock1 = "Test String 1";

M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +9 -1
@@ 107,8 107,10 @@ namespace app::manager
          blockingTimer{std::make_unique<sys::Timer>(
              timerBlock, this, std::numeric_limits<sys::ms>::max(), sys::Timer::Type::SingleShot)},
          shutdownDelay{std::make_unique<sys::Timer>(timerShutdownDelay, this, shutdown_delay_ms)},
          settings(std::make_unique<settings::Settings>(this))
          settings(std::make_unique<settings::Settings>(this)),
          phoneModeObserver(std::make_unique<sys::phone_modes::Observer>())
    {
        bus.channels.push_back(sys::BusChannel::PhoneModeChanges);
        registerMessageHandlers();
        blockingTimer->connect([this](sys::Timer &) { onPhoneLocked(); });
    }


@@ 195,6 197,12 @@ namespace app::manager

    void ApplicationManager::registerMessageHandlers()
    {
        phoneModeObserver->connect(this);
        phoneModeObserver->subscribe(
            [](sys::phone_modes::PhoneMode phoneMode, sys::phone_modes::Tethering tetheringMode) {
                LOG_INFO("Phone mode changed.");
            });

        connect(typeid(ApplicationStatusRequest), [this](sys::Message *request) {
            auto msg = static_cast<ApplicationStatusRequest *>(request);
            return std::make_shared<ApplicationStatusResponse>(msg->checkAppName,

M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp => module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp +2 -0
@@ 14,6 14,7 @@
#include <Service/Message.hpp>
#include <Service/Service.hpp>
#include <Service/Timer.hpp>
#include <PhoneModes/Observer.hpp>
#include <SwitchData.hpp>

#include <deque>


@@ 154,6 155,7 @@ namespace app::manager
        std::tuple<ApplicationName, actions::ActionId, actions::ActionParamsPtr> pendingAction;

        std::unique_ptr<settings::Settings> settings;
        std::unique_ptr<sys::phone_modes::Observer> phoneModeObserver;
        void displayLanguageChanged(std::string value);
        void lockTimeChanged(std::string value);
        void inputLanguageChanged(std::string value);

M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +1 -24
@@ 121,29 121,6 @@ std::optional<std::string> ServiceAudio::AudioServicesCallback(const sys::Messag
        }
        return settings_it->second;
    }
    else if (const auto *btReq = dynamic_cast<const BluetoothProxyMessage *>(msg); btReq) {
        std::shared_ptr<BluetoothProxyMessage> request;
        if (const auto *btStart = dynamic_cast<const BluetoothProxyStartMessage *>(msg); btStart) {
            request = std::make_shared<BluetoothProxyStartMessage>(*btStart);
        }
        else if (const auto *btVolume = dynamic_cast<const BluetoothProxySetVolumeMessage *>(msg); btVolume) {
            request = std::make_shared<BluetoothProxySetVolumeMessage>(*btVolume);
        }
        else if (const auto *btGain = dynamic_cast<const BluetoothProxySetGainMessage *>(msg); btGain) {
            request = std::make_shared<BluetoothProxySetGainMessage>(*btGain);
        }
        else if (const auto *btOutPath = dynamic_cast<const BluetoothProxySetOutputPathMessage *>(msg); btOutPath) {
            request = std::make_shared<BluetoothProxySetOutputPathMessage>(*btOutPath);
        }
        else if (const auto *btInPath = dynamic_cast<const BluetoothProxySetInputPathMessage *>(msg); btInPath) {
            request = std::make_shared<BluetoothProxySetInputPathMessage>(*btInPath);
        }
        else {
            LOG_DEBUG("BluetoothProxyMessage not supported.");
            return std::nullopt;
        }
        bus.sendUnicast(request, service::name::bluetooth);
    }
    else {
        LOG_DEBUG("Message received but not handled - no effect.");
    }


@@ 383,7 360,7 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleStop(const std::vector
        for (auto &input : audioMux.GetAllInputs()) {
            const auto &currentOperation = input.audio->GetCurrentOperation();
            if (std::find(stopTypes.begin(), stopTypes.end(), currentOperation.GetPlaybackType()) != stopTypes.end()) {
                muted = true;
                muted  = true;
                auto t = input.token;
                retCodes.emplace_back(t, stopInput(&input));
            }

M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +10 -0
@@ 21,6 21,8 @@
#include <utility>
#include <service-desktop/service-desktop/DesktopMessages.hpp>
#include <service-desktop/service-desktop/Constants.hpp>
#include <service-bluetooth/messages/SetDeviceName.hpp>
#include <BtCommand.hpp>

ServiceBluetooth::ServiceBluetooth() : sys::Service(service::name::bluetooth)
{


@@ 85,6 87,14 @@ sys::ReturnCodes ServiceBluetooth::InitHandler()
        return sys::MessageNone{};
    });

    connect(typeid(message::bluetooth::SetDeviceName), [&](sys::Message *msg) {
        auto setNameMsg = static_cast<message::bluetooth::SetDeviceName *>(msg);
        auto newName    = setNameMsg->getName();
        bluetooth::set_name(newName);
        settingsHolder->setValue(bluetooth::Settings::DeviceName, newName);
        return sys::MessageNone{};
    });

    connect(sdesktop::developerMode::DeveloperModeRequest(), [&](sys::Message *msg) {
        using namespace sdesktop::developerMode;
        auto req = static_cast<DeveloperModeRequest *>(msg);

M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp => module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp +0 -69
@@ 9,7 9,6 @@
#include <Service/Message.hpp>
#include <Audio/Stream.hpp>
#include <MessageType.hpp>
#include <bsp_audio.hpp>

#include <utility>
#include <vector>


@@ 121,71 120,3 @@ class BluetoothRequestStreamResultMessage : public sys::DataMessage
  private:
    std::shared_ptr<BluetoothStreamData> data;
};

class BluetoothProxyMessage : public sys::DataMessage
{
  public:
    BluetoothProxyMessage(MessageType messageType, bsp::AudioDevice::Format format)
        : DataMessage(messageType), format(format){};

    ~BluetoothProxyMessage() override = default;

    bsp::AudioDevice::Format format;
};

/// Bluetooth proxy messages

class BluetoothProxyStartMessage : public BluetoothProxyMessage
{
  public:
    BluetoothProxyStartMessage(audio::Stream &streamOut, audio::Stream &streamIn, bsp::AudioDevice::Format format)
        : BluetoothProxyMessage(MessageType::BluetoothProxyStart, format), audioStreamOut(streamOut),
          audioStreamIn(streamIn){};

    ~BluetoothProxyStartMessage() override = default;

    audio::Stream &audioStreamOut;
    audio::Stream &audioStreamIn;
};

class BluetoothProxyStopMessage : public BluetoothProxyMessage
{
  public:
    BluetoothProxyStopMessage(bsp::AudioDevice::Format format)
        : BluetoothProxyMessage(MessageType::BluetoothProxyStop, format){};
    ~BluetoothProxyStopMessage() override = default;
};

class BluetoothProxySetVolumeMessage : public BluetoothProxyMessage
{
  public:
    BluetoothProxySetVolumeMessage(bsp::AudioDevice::Format format)
        : BluetoothProxyMessage(MessageType::BluetoothProxyOutputVolumeCtrl, format){};
    ~BluetoothProxySetVolumeMessage() override = default;
};

class BluetoothProxySetGainMessage : public BluetoothProxyMessage
{
  public:
    BluetoothProxySetGainMessage(bsp::AudioDevice::Format format)
        : BluetoothProxyMessage(MessageType::BluetoothProxyInputGainCtrl, format){};
    ~BluetoothProxySetGainMessage() override = default;

    float value;
};

class BluetoothProxySetOutputPathMessage : public BluetoothProxyMessage
{
  public:
    BluetoothProxySetOutputPathMessage(bsp::AudioDevice::Format format)
        : BluetoothProxyMessage(MessageType::BluetoothProxyOutputPathCtrl, format){};
    ~BluetoothProxySetOutputPathMessage() override = default;
};

class BluetoothProxySetInputPathMessage : public BluetoothProxyMessage
{
  public:
    BluetoothProxySetInputPathMessage(bsp::AudioDevice::Format format)
        : BluetoothProxyMessage(MessageType::BluetoothProxyInputPathCtrl, format){};
    ~BluetoothProxySetInputPathMessage() override = default;
};

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, 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 "CellularUrcHandler.hpp"


@@ 90,7 90,7 @@

const char *ServiceCellular::serviceName = "ServiceCellular";

inline constexpr auto cellularStack = 24000UL;
inline constexpr auto cellularStack = 25000UL;

using namespace cellular;


M module-services/service-db/agents/settings/SystemSettings.hpp => module-services/service-db/agents/settings/SystemSettings.hpp +5 -0
@@ 40,4 40,9 @@ namespace settings
        constexpr inline auto apn_list = "cl_apn_list";
    } // namespace Cellular

    namespace Battery
    {
        constexpr inline auto batteryCriticalLevel = "battery_critical_level";
    } // namespace Battery

}; // namespace settings

M module-services/service-desktop/endpoints/Context.hpp => module-services/service-desktop/endpoints/Context.hpp +84 -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

#pragma once


@@ 15,7 15,12 @@ namespace parserFSM
        inline constexpr auto endpoint = "endpoint";
        inline constexpr auto uuid     = "uuid";
        inline constexpr auto status   = "status";
        inline constexpr auto totalCount = "totalCount";
        inline constexpr auto body     = "body";
        inline constexpr auto offset     = "offset";
        inline constexpr auto limit      = "limit";
        inline constexpr auto nextPage   = "nextPage";
        inline constexpr auto entries    = "entries";
    } // namespace json

    struct endpointResponseContext


@@ 28,7 33,7 @@ namespace parserFSM

    class Context
    {
      private:
      protected:
        json11::Json body;
        EndpointType endpoint;
        uint32_t uuid;


@@ 84,8 89,9 @@ namespace parserFSM
            uuid     = invalidUuid;
            method   = http::Method::get;
        }
        virtual ~Context() noexcept = default;

        auto createSimpleResponse() -> std::string
        virtual auto createSimpleResponse() -> std::string
        {
            json11::Json responseJson = json11::Json::object{{json::endpoint, static_cast<int>(getEndpoint())},
                                                             {json::status, static_cast<int>(responseContext.status)},


@@ 123,4 129,79 @@ namespace parserFSM
        }
    };

    class PagedContext : public Context
    {
      private:
        // from request
        std::size_t requestedLimit, requestedOffset;
        // set by query (during helper run)
        std::size_t totalCount;
        // set it before calling handle on helper
        std::size_t pageSize;

      public:
        explicit PagedContext(json11::Json &js, size_t pageSize) : Context(js), pageSize(pageSize)
        {}
        PagedContext() = default;
        void setRequestedLimit(std::size_t limit)
        {
            requestedLimit = limit;
        }
        void setRequestedOffset(std::size_t offset)
        {
            requestedOffset = offset;
        }
        void setTotalCount(std::size_t count)
        {
            totalCount = count;
        }
        std::size_t getPageSize() const
        {
            return pageSize;
        }

        auto createSimpleResponse() -> std::string override
        {
            auto elemsCount = responseContext.body.array_items().size();
            auto newBody    = json11::Json::object{{json::entries, responseContext.body},
                                                {json::totalCount, static_cast<int>(totalCount)}};
            if (requestedLimit > elemsCount) {
                std::size_t offset = requestedOffset + elemsCount;
                if (offset < totalCount) {
                    auto lastTableIndex = std::min(totalCount, offset + requestedLimit - elemsCount);
                    std::size_t limit   = std::min(pageSize, lastTableIndex - offset);
                    auto nextPageParams = json11::Json::object{{json::offset, static_cast<int>(offset)},
                                                               {json::limit, static_cast<int>(limit)}};
                    newBody.insert({json::nextPage, nextPageParams});
                }
            }

            setResponseBody(newBody);
            return Context::createSimpleResponse();
        }
    };

    namespace endpoint_pageing
    {
        inline constexpr std::size_t contactsPageSize = 10;
    }

    class ContextFactory
    {
      public:
        static auto create(json11::Json &js) -> std::unique_ptr<parserFSM::Context>
        {
            switch (static_cast<EndpointType>(js[json::endpoint].int_value())) {
            // enable for pagination in other endpoints
            // case EndpointType::calendarEvents:
            // case EndpointType::calllog:
            case EndpointType::contacts:
                // case EndpointType::messages:
                return std::make_unique<PagedContext>(js, endpoint_pageing::contactsPageSize);
            default:
                return std::make_unique<Context>(js);
            }
        }
    };

} // namespace parserFSM

M module-services/service-desktop/endpoints/Endpoint.hpp => module-services/service-desktop/endpoints/Endpoint.hpp +3 -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


@@ 10,6 10,7 @@
#include <Common/Query.hpp>
#include <Service/Service.hpp>
#include <string>
#include <memory>

namespace parserFSM
{


@@ 20,7 21,7 @@ namespace parserFSM
      public:
        Endpoint(sys::Service *_ownerServicePtr) : ownerServicePtr(_ownerServicePtr){};
        virtual ~Endpoint()                           = default;
        virtual auto handle(Context &context) -> void = 0;
        virtual auto handle(parserFSM::Context &context) -> void = 0;
        auto c_str() -> const char *
        {
            return debugName.c_str();

M module-services/service-desktop/endpoints/contacts/ContactHelper.cpp => module-services/service-desktop/endpoints/contacts/ContactHelper.cpp +39 -30
@@ 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 "ContactHelper.hpp"


@@ 27,6 27,7 @@
#include <memory>
#include <utility>
#include <vector>
#include <algorithm>

using namespace parserFSM;



@@ 72,38 73,46 @@ auto ContactHelper::requestDataFromDB(Context &context) -> sys::ReturnCodes
    if (context.getBody()[json::contacts::id].int_value() != 0) {
        return requestContactByID(context);
    }
    else if (context.getBody()[json::contacts::count].bool_value()) {
    else if (context.getBody()[json::contacts::limit].bool_value()) {
        return requestCount(context);
    }

    auto limit = context.getBody()[json::contacts::count].int_value();
    auto offset = context.getBody()[json::contacts::offset].int_value();
    auto query  = std::make_unique<db::query::ContactGet>(offset, limit, "");

    auto listener = std::make_unique<db::EndpointListener>(
        [](db::QueryResult *result, Context context) {
            if (auto contactResult = dynamic_cast<db::query::ContactGetResult *>(result)) {

                auto recordsPtr = std::make_unique<std::vector<ContactRecord>>(contactResult->getRecords());
                json11::Json::array contactsArray;

                for (auto record : *recordsPtr.get()) {
                    contactsArray.emplace_back(ContactHelper::to_json(record));
    try {
        auto &ctx          = dynamic_cast<PagedContext &>(context);
        std::size_t limit  = ctx.getBody()[json::contacts::limit].int_value();
        std::size_t offset = ctx.getBody()[json::contacts::offset].int_value();
        ctx.setRequestedLimit(limit);
        ctx.setRequestedOffset(offset);
        auto query = std::make_unique<db::query::ContactGetWithTotalCount>(std::min(ctx.getPageSize(), limit), offset);
        auto listener = std::make_unique<db::EndpointListenerWithPages>(
            [](db::QueryResult *result, PagedContext &context) {
                if (auto contactResult = dynamic_cast<db::query::ContactGetResultWithTotalCount *>(result)) {

                    auto recordsPtr = std::make_unique<std::vector<ContactRecord>>(contactResult->getRecords());
                    context.setTotalCount(contactResult->getAllLength());
                    json11::Json::array contactsArray;

                    for (const auto &record : *recordsPtr.get()) {
                        contactsArray.emplace_back(ContactHelper::to_json(record));
                    }

                    context.setResponseBody(contactsArray);

                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }

                context.setResponseBody(contactsArray);
                MessageHandler::putToSendQueue(context.createSimpleResponse());
                return true;
            }
            else {
                return false;
            }
        },
        context);

    query->setQueryListener(std::move(listener));

    DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Contact, std::move(query));
                else {
                    return false;
                }
            },
            ctx);
        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Contact, std::move(query));
    }
    catch (const std::exception &e) {
        LOG_ERROR("%s", e.what());
        return sys::ReturnCodes::Failure;
    }

    return sys::ReturnCodes::Success;
}


@@ 118,7 127,7 @@ sys::ReturnCodes ContactHelper::requestCount(Context &context)

                auto count = contactResult->getSize();

                context.setResponseBody(json11::Json::object({{json::contacts::count, static_cast<int>(count)}}));
                context.setResponseBody(json11::Json::object({{json::contacts::limit, static_cast<int>(count)}}));
                MessageHandler::putToSendQueue(context.createSimpleResponse());
                return true;
            }

M module-services/service-desktop/endpoints/contacts/ContactHelper.hpp => module-services/service-desktop/endpoints/contacts/ContactHelper.hpp +5 -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

#pragma once


@@ 26,8 26,10 @@ namespace parserFSM

    class ContactHelper : public DBHelper
    {

      public:
        ContactHelper(sys::Service *_ownerServicePtr) : DBHelper(_ownerServicePtr){};
        ContactHelper(sys::Service *_ownerServicePtr) : DBHelper(_ownerServicePtr)
        {}

        auto createDBEntry(Context &context) -> sys::ReturnCodes override;
        auto requestDataFromDB(Context &context) -> sys::ReturnCodes override;


@@ 42,7 44,7 @@ namespace parserFSM

    namespace json::contacts
    {
        inline constexpr auto count           = "count";
        inline constexpr auto limit           = "limit";
        inline constexpr auto offset          = "offset";
        inline constexpr auto primaryName     = "priName";
        inline constexpr auto alternativeName = "altName";

M module-services/service-desktop/endpoints/contacts/ContactsEndpoint.cpp => module-services/service-desktop/endpoints/contacts/ContactsEndpoint.cpp +1 -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

#include "ContactHelper.hpp"


@@ 9,7 9,6 @@
#include <memory>

using namespace parserFSM;

auto ContactsEndpoint::handle(Context &context) -> void
{
    switch (context.getMethod()) {

M module-services/service-desktop/endpoints/contacts/ContactsEndpoint.hpp => module-services/service-desktop/endpoints/contacts/ContactsEndpoint.hpp +1 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

M module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp => module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp +3 -5
@@ 65,7 65,7 @@ auto DeveloperModeHelper::processPutRequest(Context &context) -> sys::ReturnCode
    }
    else if (body[json::developerMode::smsCommand].is_string()) {
        if (body[json::developerMode::smsCommand].string_value() == json::developerMode::smsAdd) {
            SMSType smsType = static_cast<SMSType>(context.getBody()[json::messages::type].int_value());
            const auto smsType = static_cast<SMSType>(context.getBody()[json::messages::messageType].int_value());
            if (smsType == SMSType::DRAFT || smsType == SMSType::QUEUED || smsType == SMSType::FAILED) {
                return prepareSMS(context);
            }


@@ 212,11 212,9 @@ auto DeveloperModeHelper::smsRecordFromJson(json11::Json msgJson) -> SMSRecord
{
    auto record = SMSRecord();

    record.type = static_cast<SMSType>(msgJson[json::messages::type].int_value());
    record.type = static_cast<SMSType>(msgJson[json::messages::messageType].int_value());
    record.date = utils::time::getCurrentTimestamp().getTime();
    utils::PhoneNumber phoneNumber(msgJson[json::messages::phoneNumber].string_value());
    record.number = phoneNumber.getView();
    record.body   = UTF8(msgJson[json::messages::messageBody].string_value());
    record.body = UTF8(msgJson[json::messages::messageBody].string_value());
    return record;
}


M module-services/service-desktop/endpoints/messages/MessageHelper.cpp => module-services/service-desktop/endpoints/messages/MessageHelper.cpp +456 -337
@@ 9,16 9,13 @@

#include <BaseInterface.hpp>
#include <Common/Query.hpp>
#include <PhoneNumber.hpp>
#include <SMSRecord.hpp>
#include <SMSTemplateRecord.hpp>
#include <Service/Common.hpp>
#include <ThreadRecord.hpp>
#include <json/json11.hpp>
#include <queries/messages/sms/QuerySMSGet.hpp>
#include <queries/messages/sms/QuerySMSGetByContactID.hpp>
#include <queries/messages/sms/QuerySMSGetByID.hpp>
#include <queries/messages/sms/QuerySMSGetByText.hpp>
#include <queries/messages/sms/QuerySMSGetByThreadID.hpp>
#include <queries/messages/sms/QuerySMSGetCount.hpp>
#include <queries/messages/sms/QuerySMSRemove.hpp>


@@ 28,160 25,305 @@
#include <queries/messages/templates/QuerySMSTemplateGetCount.hpp>
#include <queries/messages/templates/QuerySMSTemplateRemove.hpp>
#include <queries/messages/templates/QuerySMSTemplateUpdate.hpp>
#include <queries/messages/threads/QueryThreadsGet.hpp>
#include <queries/messages/threads/QueryThreadMarkAsRead.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <utf8/UTF8.hpp>

#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <module-db/queries/messages/sms/QuerySMSGetByText.hpp>

using namespace parserFSM;

auto MessageHelper::to_json(SMSRecord record) -> json11::Json
namespace parserFSM
{

    auto recordEntry = json11::Json::object{{json::messages::contactID, static_cast<int>(record.contactID)},
                                            {json::messages::date, static_cast<int>(record.date)},
                                            {json::messages::dateSent, static_cast<int>(record.dateSent)},
                                            {json::messages::id, static_cast<int>(record.ID)},
                                            {json::messages::messageBody, record.body.c_str()},
                                            {json::messages::type, static_cast<int>(record.type)},
                                            {json::messages::threadID, static_cast<int>(record.threadID)}};
    return recordEntry;
}

auto MessageHelper::to_json(SMSTemplateRecord record) -> json11::Json
{
    auto MessageHelper::toJson(const SMSRecord &record) -> json11::Json
    {

    auto recordEntry = json11::Json::object{{json::messages::id, static_cast<int>(record.ID)},
                                            {json::messages::templateText, record.text.c_str()}};
    return recordEntry;
}
        auto recordEntry = json11::Json::object{{json::messages::contactID, static_cast<int>(record.contactID)},
                                                {json::messages::receivedAt, static_cast<int>(record.date)},
                                                {json::messages::sentAt, static_cast<int>(record.dateSent)},
                                                {json::messages::messageID, static_cast<int>(record.ID)},
                                                {json::messages::messageBody, record.body.c_str()},
                                                {json::messages::messageType, static_cast<int>(record.type)},
                                                {json::messages::threadID, static_cast<int>(record.threadID)}};
        return recordEntry;
    }

auto MessageHelper::to_json(ThreadRecord record) -> json11::Json
{
    auto MessageHelper::toJson(const SMSTemplateRecord &record) -> json11::Json
    {

    auto recordEntry =
        json11::Json::object{{json::messages::contactID, static_cast<int>(record.contactID)},
                             {json::messages::date, static_cast<int>(record.date)},
                             {json::messages::thread::msgCount, static_cast<int>(record.msgCount)},
                             {json::messages::id, static_cast<int>(record.ID)},
                             {json::messages::thread::snippet, record.snippet.c_str()},
                             {json::messages::isUnread, record.isUnread()},
                             {json::messages::type, static_cast<int>(record.type)},
                             {json::messages::thread::unreadMsgCount, static_cast<int>(record.unreadMsgCount)}};
    return recordEntry;
}

auto MessageHelper::from_json(json11::Json msgJson) -> SMSTemplateRecord
{
    SMSTemplateRecord record;
        auto recordEntry =
            json11::Json::object{{json::messages::templateID, static_cast<int>(record.ID)},
                                 {json::messages::templateBody, record.text.c_str()},
                                 {json::messages::lastUsedAt, static_cast<int>(record.lastUsageTimestamp)}};
        return recordEntry;
    }

    record.text = UTF8(msgJson[json::messages::templateText].string_value());
    auto MessageHelper::toJson(const ThreadRecord &record) -> json11::Json
    {

    return record;
}
        auto recordEntry = json11::Json::object{{json::messages::contactID, static_cast<int>(record.contactID)},
                                                {json::messages::numberID, static_cast<int>(record.numberID)},
                                                {json::messages::lastUpdatedAt, static_cast<int>(record.date)},
                                                {json::messages::messageCount, static_cast<int>(record.msgCount)},
                                                {json::messages::threadID, static_cast<int>(record.ID)},
                                                {json::messages::messageSnippet, record.snippet.c_str()},
                                                {json::messages::isUnread, record.isUnread()},
                                                {json::messages::messageType, static_cast<int>(record.type)}};
        return recordEntry;
    }

auto MessageHelper::requestDataFromDB(Context &context) -> sys::ReturnCodes
{
    auto MessageHelper::fromJson(const json11::Json &msgJson) -> SMSTemplateRecord
    {
        SMSTemplateRecord record;

    if (context.getBody()[json::messages::msgTemplate].bool_value() == true) {
        return requestTemplate(context);
    }
    else {
        return requestSMS(context);
        record.text = UTF8(msgJson[json::messages::templateBody].string_value());

        return record;
    }
}

auto MessageHelper::requestSMS(Context &context) -> sys::ReturnCodes
{
    if (context.getBody()[json::messages::count].bool_value() == true) // get messages count
    auto MessageHelper::requestDataFromDB(Context &context) -> sys::ReturnCodes
    {
        auto query = std::make_unique<db::query::SMSGetCount>();
        if (context.getBody()[json::messages::category].string_value() == json::messages::categoryMessage) {
            return requestSMS(context);
        }
        else if (context.getBody()[json::messages::category].string_value() == json::messages::categoryTemplate) {
            return requestTemplate(context);
        }
        else if (context.getBody()[json::messages::category].string_value() == json::messages::categoryThread) {
            return requestThread(context);
        }
        LOG_ERROR("Category of request is missing or incorrect!");
        context.setResponseStatus(http::Code::BadRequest);
        MessageHandler::putToSendQueue(context.createSimpleResponse());
        return sys::ReturnCodes::Unresolved;
    }

        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto SMSResult = dynamic_cast<db::query::SMSGetCountResult *>(result)) {
                    auto id = SMSResult->getResults();
    auto MessageHelper::createDBEntry(Context &context) -> sys::ReturnCodes
    {
        if (context.getBody()[json::messages::category].string_value() == json::messages::categoryMessage) {
            return createSMS(context);
        }
        else if (context.getBody()[json::messages::category].string_value() == json::messages::categoryTemplate) {
            return createTemplate(context);
        }
        LOG_ERROR("Category of request is missing or incorrect!");
        context.setResponseStatus(http::Code::BadRequest);
        MessageHandler::putToSendQueue(context.createSimpleResponse());
        return sys::ReturnCodes::Unresolved;
    }

                    context.setResponseBody(json11::Json::object{{json::messages::count, static_cast<int>(id)}});
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }
                else {
                    return false;
                }
            },
            context);
    auto MessageHelper::updateDBEntry(Context &context) -> sys::ReturnCodes
    {
        if (context.getBody()[json::messages::category].string_value() == json::messages::categoryTemplate) {
            return updateTemplate(context);
        }
        else if (context.getBody()[json::messages::category].string_value() == json::messages::categoryThread) {
            return updateThread(context);
        }
        LOG_ERROR("Category of request is missing or incorrect!");
        context.setResponseStatus(http::Code::BadRequest);
        MessageHandler::putToSendQueue(context.createSimpleResponse());
        return sys::ReturnCodes::Unresolved;
    }

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
    auto MessageHelper::deleteDBEntry(Context &context) -> sys::ReturnCodes
    {
        if (context.getBody()[json::messages::category].string_value() == json::messages::categoryMessage) {
            return deleteSMS(context);
        }
        else if (context.getBody()[json::messages::category].string_value() == json::messages::categoryTemplate) {
            return deleteTemplate(context);
        }
        else if (context.getBody()[json::messages::category].string_value() == json::messages::categoryThread) {
            return deleteThread(context);
        }
        LOG_ERROR("Category of request is missing or incorrect!");
        context.setResponseStatus(http::Code::BadRequest);
        MessageHandler::putToSendQueue(context.createSimpleResponse());
        return sys::ReturnCodes::Unresolved;
    }
    else if (context.getBody()[json::messages::id].int_value() != 0) { // messages search

        auto query = std::make_unique<db::query::SMSGetByID>(context.getBody()[json::messages::id].int_value());
    auto MessageHelper::requestSMS(Context &context) -> sys::ReturnCodes
    {
        if (context.getBody()[json::messages::count].bool_value()) // get messages count
        {
            auto query = std::make_unique<db::query::SMSGetCount>();

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSGetCountResult *>(result)) {
                        auto id = smsResult->getResults();

                        context.setResponseBody(json11::Json::object{{json::messages::count, static_cast<int>(id)}});
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto SMSResult = dynamic_cast<db::query::SMSGetByIDResult *>(result)) {
            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
        }
        else if (context.getBody()[json::messages::messageID].int_value() != 0) { // get message by ID

                    context.setResponseBody(MessageHelper::to_json(SMSResult->getResults()));
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }
                else {
                    return false;
                }
            },
            context);
            auto query =
                std::make_unique<db::query::SMSGetByID>(context.getBody()[json::messages::messageID].int_value());

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
    }
    else if (context.getBody()[json::messages::contactID].int_value() != 0) {
            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSGetByIDResult *>(result)) {

        auto query =
            std::make_unique<db::query::SMSGetByContactID>(context.getBody()[json::messages::contactID].int_value());
                        context.setResponseBody(MessageHelper::toJson(smsResult->getResults()));
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto SMSResult = dynamic_cast<db::query::SMSGetByContactIDResult *>(result)) {
            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
        }
        else if (context.getBody()[json::messages::threadID].int_value() != 0) { // get messages by thread ID

            auto query =
                std::make_unique<db::query::SMSGetByThreadID>(context.getBody()[json::messages::threadID].int_value(),
                                                              context.getBody()[json::messages::offset].int_value(),
                                                              context.getBody()[json::messages::limit].is_number()
                                                                  ? context.getBody()[json::messages::limit].int_value()
                                                                  : defaultLimit);

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSGetByThreadIDResult *>(result)) {

                        json11::Json::array smsArray;
                        for (const auto &record : smsResult->getResults()) {
                            smsArray.emplace_back(MessageHelper::toJson(record));
                        }

                        auto responseBody = json11::Json::object{
                            {json::messages::totalCount, 0},
                            {json::messages::nextPage,
                             json11::Json::object{{json::messages::offset, 0}, {json::messages::limit, 0}}},
                            {json::messages::entries, smsArray},
                        };

                        context.setResponseBody(responseBody);
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
        }
        else if (!context.getBody()[json::messages::messageBody].string_value().empty()) // get by message body
        {
            // not adding pagination for this request, since it is just for development and testing purposes, and it's
            // not going to be used by Mudita Center
            auto query = std::make_unique<db::query::SMSGetByText>(
                context.getBody()[json::messages::messageBody].string_value());
            if (const auto filterByNumber = !context.getBody()[json::messages::phoneNumber].string_value().empty();
                filterByNumber) {
                utils::PhoneNumber number{context.getBody()[json::messages::phoneNumber].string_value()};
                query->filterByPhoneNumber(number.getView());
            }

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSGetByTextResult *>(result)) {

                    json11::Json::array SMSarray;
                    for (auto record : SMSResult->getResults()) {
                        SMSarray.emplace_back(MessageHelper::to_json(record));
                        json11::Json::array smsArray;
                        for (const auto &record : smsResult->getResults()) {
                            smsArray.emplace_back(MessageHelper::toJson(record));
                        }

                        context.setResponseBody(smsArray);
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

                    context.setResponseBody(SMSarray);
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }
                else {
                    return false;
                }
            },
            context);
            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
        }
        else // get messages
        {
            auto query = std::make_unique<db::query::SMSGet>(context.getBody()[json::messages::limit].is_number()
                                                                 ? context.getBody()[json::messages::limit].int_value()
                                                                 : defaultLimit,
                                                             context.getBody()[json::messages::offset].int_value());

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSGetResult *>(result)) {

                        json11::Json::array smsArray;
                        for (const auto &record : smsResult->getRecords()) {
                            smsArray.emplace_back(MessageHelper::toJson(record));
                            LOG_DEBUG("Record found!: %" PRIu32 "\n", record.ID);
                        }

                        auto responseBody = json11::Json::object{
                            {json::messages::totalCount, 0},
                            {json::messages::nextPage,
                             json11::Json::object{{json::messages::offset, 0}, {json::messages::limit, 0}}},
                            {json::messages::entries, smsArray},
                        };

                        context.setResponseBody(responseBody);
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
        }
        return sys::ReturnCodes::Success;
    }
    else if (context.getBody()[json::messages::threadID].int_value() != 0) {

        auto query =
            std::make_unique<db::query::SMSGetByThreadID>(context.getBody()[json::messages::threadID].int_value());
    auto MessageHelper::createSMS(Context &context) -> sys::ReturnCodes
    {
        context.setResponseStatus(http::Code::InternalServerError);
        MessageHandler::putToSendQueue(context.createSimpleResponse());
        return sys::ReturnCodes::Success;
    }

    auto MessageHelper::deleteSMS(Context &context) -> sys::ReturnCodes
    {
        if (!context.getBody()[json::messages::messageID].is_number()) {
            LOG_ERROR("Bad request! messageID is incorrect or missing!");
            return sys::ReturnCodes::Unresolved;
        }
        auto id       = context.getBody()[json::messages::messageID].int_value();
        auto query    = std::make_unique<db::query::SMSRemove>(id);
        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto SMSResult = dynamic_cast<db::query::SMSGetByThreadIDResult *>(result)) {
                if (auto smsTemplateResult = dynamic_cast<db::query::SMSRemoveResult *>(result)) {

                    json11::Json::array SMSarray;
                    for (auto record : SMSResult->getResults()) {
                        SMSarray.emplace_back(MessageHelper::to_json(record));
                    }

                    context.setResponseBody(SMSarray);
                    context.setResponseStatus(smsTemplateResult->getResults() ? http::Code::OK
                                                                              : http::Code::InternalServerError);
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }


@@ 193,53 335,121 @@ auto MessageHelper::requestSMS(Context &context) -> sys::ReturnCodes

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));

        return sys::ReturnCodes::Success;
    }
    else if (context.getBody()[json::messages::messageBody].string_value().empty() == false) {
        auto query =
            std::make_unique<db::query::SMSGetByText>(context.getBody()[json::messages::messageBody].string_value());
        if (const auto filterByNumber = !context.getBody()[json::messages::phoneNumber].string_value().empty();
            filterByNumber) {
            utils::PhoneNumber number{context.getBody()[json::messages::phoneNumber].string_value()};
            query->filterByPhoneNumber(number.getView());

    auto MessageHelper::requestTemplate(Context &context) -> sys::ReturnCodes
    {
        if (context.getBody()[json::messages::count].bool_value()) // get templates count
        {
            auto query = std::make_unique<db::query::SMSTemplateGetCount>();

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSTemplateGetCountResult *>(result)) {
                        auto id = smsResult->getResults();

                        context.setResponseBody(json11::Json::object{{json::messages::count, static_cast<int>(id)}});
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));
        }
        else if (context.getBody()[json::messages::templateID].int_value() != 0) { // get template by ID
            auto query = std::make_unique<db::query::SMSTemplateGetByID>(
                context.getBody()[json::messages::templateID].int_value());

        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto SMSResult = dynamic_cast<db::query::SMSGetByTextResult *>(result)) {
            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsTemplateResult = dynamic_cast<db::query::SMSTemplateGetByIDResult *>(result)) {

                    json11::Json::array SMSarray;
                    for (auto record : SMSResult->getResults()) {
                        SMSarray.emplace_back(MessageHelper::to_json(record));
                        context.setResponseBody(MessageHelper::toJson(smsTemplateResult->getResults()));
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

                    context.setResponseBody(SMSarray);
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }
                else {
                    return false;
                }
            },
            context);
            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));
        }
        else // get messages templates
        {
            auto query =
                std::make_unique<db::query::SMSTemplateGet>(context.getBody()[json::messages::offset].int_value(),
                                                            context.getBody()[json::messages::limit].is_number()
                                                                ? context.getBody()[json::messages::limit].int_value()
                                                                : defaultLimit);
            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsTemplateResult = dynamic_cast<db::query::SMSTemplateGetResult *>(result)) {

                        json11::Json::array smsTemplateArray;
                        for (const auto &record : smsTemplateResult->getResults()) {
                            smsTemplateArray.emplace_back(toJson(record));
                        }

                        auto responseBody = json11::Json::object{
                            {json::messages::totalCount, 0},
                            {json::messages::nextPage,
                             json11::Json::object{{json::messages::offset, 0}, {json::messages::limit, 0}}},
                            {json::messages::entries, smsTemplateArray},
                        };

                        context.setResponseBody(responseBody);
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));
        }

        return sys::ReturnCodes::Success;
    }
    else // get messages
    auto MessageHelper::updateTemplate(Context &context) -> sys::ReturnCodes
    {
        auto query = std::make_unique<db::query::SMSGet>(context.getBody()[json::messages::count].int_value(),
                                                         context.getBody()[json::messages::offset].int_value());
        if (!context.getBody()[json::messages::templateID].is_number()) {
            LOG_ERROR("Bad request! templateID is incorrect or missing!");
            context.setResponseStatus(http::Code::BadRequest);
            MessageHandler::putToSendQueue(context.createSimpleResponse());
            return sys::ReturnCodes::Unresolved;
        }

        if (!context.getBody()[json::messages::templateBody].is_string()) {
            LOG_ERROR("Bad request! templateBody is incorrect or missing!");
            context.setResponseStatus(http::Code::BadRequest);
            MessageHandler::putToSendQueue(context.createSimpleResponse());
            return sys::ReturnCodes::Unresolved;
        }

        SMSTemplateRecord record;
        record.ID   = context.getBody()[json::messages::templateID].int_value();
        record.text = context.getBody()[json::messages::templateBody].string_value();

        auto query    = std::make_unique<db::query::SMSTemplateUpdate>(record);
        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto SMSResult = dynamic_cast<db::query::SMSGetResult *>(result)) {
                if (auto smsTemplateResult = dynamic_cast<db::query::SMSTemplateUpdateResult *>(result)) {

                    json11::Json::array SMSarray;
                    for (auto record : SMSResult->getRecords()) {
                        SMSarray.emplace_back(MessageHelper::to_json(record));
                    }

                    context.setResponseBody(SMSarray);
                    context.setResponseStatus(smsTemplateResult->getResult() ? http::Code::OK
                                                                             : http::Code::InternalServerError);
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }


@@ 250,24 460,29 @@ auto MessageHelper::requestSMS(Context &context) -> sys::ReturnCodes
            context);

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
    }
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));

    return sys::ReturnCodes::Success;
}
        return sys::ReturnCodes::Success;
    }

auto MessageHelper::requestTemplate(Context &context) -> sys::ReturnCodes
{
    if (context.getBody()[json::messages::count].bool_value() == true) // get templates count
    auto MessageHelper::createTemplate(Context &context) -> sys::ReturnCodes
    {
        auto query = std::make_unique<db::query::SMSTemplateGetCount>();
        if (!context.getBody()[json::messages::templateBody].is_string()) {
            LOG_ERROR("Bad request! templateBody is incorrect or missing!");
            context.setResponseStatus(http::Code::BadRequest);
            MessageHandler::putToSendQueue(context.createSimpleResponse());
            return sys::ReturnCodes::Unresolved;
        }

        SMSTemplateRecord record = fromJson(context.getBody());

        auto query    = std::make_unique<db::query::SMSTemplateAdd>(record);
        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto SMSResult = dynamic_cast<db::query::SMSTemplateGetCountResult *>(result)) {
                    auto id = SMSResult->getResults();
                if (auto smsTemplateResult = dynamic_cast<db::query::SMSTemplateAddResult *>(result)) {

                    context.setResponseBody(json11::Json::object{{json::messages::count, static_cast<int>(id)}});
                    context.setResponseStatus(smsTemplateResult->getResult() ? http::Code::OK
                                                                             : http::Code::InternalServerError);
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }


@@ 279,15 494,27 @@ auto MessageHelper::requestTemplate(Context &context) -> sys::ReturnCodes

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));

        return sys::ReturnCodes::Success;
    }
    else if (context.getBody()[json::messages::id].int_value() != 0) { // templates search
        auto query = std::make_unique<db::query::SMSTemplateGetByID>(context.getBody()[json::messages::id].int_value());

    auto MessageHelper::deleteTemplate(Context &context) -> sys::ReturnCodes
    {
        if (!context.getBody()[json::messages::templateID].is_number()) {
            LOG_ERROR("Bad request! templateID is incorrect or missing!");
            context.setResponseStatus(http::Code::BadRequest);
            MessageHandler::putToSendQueue(context.createSimpleResponse());
            return sys::ReturnCodes::Unresolved;
        }

        auto query =
            std::make_unique<db::query::SMSTemplateRemove>(context.getBody()[json::messages::templateID].int_value());
        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto SMSTemplateResult = dynamic_cast<db::query::SMSTemplateGetByIDResult *>(result)) {
                if (auto smsTemplateResult = dynamic_cast<db::query::SMSTemplateRemoveResult *>(result)) {

                    context.setResponseBody(MessageHelper::to_json(SMSTemplateResult->getResults()));
                    context.setResponseStatus(smsTemplateResult->getResults() ? http::Code::OK
                                                                              : http::Code::InternalServerError);
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }


@@ 299,21 526,33 @@ auto MessageHelper::requestTemplate(Context &context) -> sys::ReturnCodes

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));
        return sys::ReturnCodes::Success;
    }
    else // get messages templates

    auto MessageHelper::requestThread(Context &context) -> sys::ReturnCodes
    {
        auto query = std::make_unique<db::query::SMSTemplateGet>(context.getBody()[json::messages::offset].int_value(),
                                                                 context.getBody()[json::messages::count].int_value());
        auto query = std::make_unique<db::query::ThreadsGet>(context.getBody()[json::messages::offset].int_value(),
                                                             context.getBody()[json::messages::limit].is_number()
                                                                 ? context.getBody()[json::messages::limit].int_value()
                                                                 : defaultLimit);

        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto SMSTemplateResult = dynamic_cast<db::query::SMSTemplateGetResult *>(result)) {
                if (auto threadsResults = dynamic_cast<db::query::ThreadsGetResults *>(result)) {

                    json11::Json::array SMSTemplateArray;
                    for (auto record : SMSTemplateResult->getResults()) {
                        SMSTemplateArray.emplace_back(to_json(record));
                    json11::Json::array threadsArray;
                    for (const auto &record : threadsResults->getResults()) {
                        threadsArray.emplace_back(MessageHelper::toJson(record));
                    }

                    context.setResponseBody(SMSTemplateArray);
                    auto responseBody = json11::Json::object{
                        {json::messages::totalCount, 0},
                        {json::messages::nextPage,
                         json11::Json::object{{json::messages::offset, 0}, {json::messages::limit, 0}}},
                        {json::messages::entries, threadsArray},
                    };

                    context.setResponseBody(responseBody);
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }


@@ 324,178 563,58 @@ auto MessageHelper::requestTemplate(Context &context) -> sys::ReturnCodes
            context);

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));
    }
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSThread, std::move(query));

    return sys::ReturnCodes::Success;
}

auto MessageHelper::createDBEntry(Context &context) -> sys::ReturnCodes
{

    if (context.getBody()[json::messages::msgTemplate].bool_value() == true) {
        return createTemplate(context);
        return sys::ReturnCodes::Success;
    }
    else {
        return createSMS(context);
    }
}

auto MessageHelper::createTemplate(Context &context) -> sys::ReturnCodes
{
    SMSTemplateRecord record = from_json(context.getBody());
    auto MessageHelper::updateThread(Context &context) -> sys::ReturnCodes // Set thread as read/unread
    {
        if (!context.getBody()[json::messages::threadID].is_number()) {
            LOG_ERROR("Bad request! threadID is incorrect or missing!");
            context.setResponseStatus(http::Code::BadRequest);
            MessageHandler::putToSendQueue(context.createSimpleResponse());
            return sys::ReturnCodes::Unresolved;
        }

    auto query    = std::make_unique<db::query::SMSTemplateAdd>(record);
    auto listener = std::make_unique<db::EndpointListener>(
        [=](db::QueryResult *result, Context context) {
            if (auto SMSTemplateResult = dynamic_cast<db::query::SMSTemplateAddResult *>(result)) {
        if (!context.getBody()[json::messages::isUnread].is_bool()) {
            LOG_ERROR("Bad request! isUnread is incorrect or missing!");
            context.setResponseStatus(http::Code::BadRequest);
            MessageHandler::putToSendQueue(context.createSimpleResponse());
            return sys::ReturnCodes::Unresolved;
        }

                context.setResponseStatus(SMSTemplateResult->getResult() ? http::Code::OK
                                                                         : http::Code::InternalServerError);
                MessageHandler::putToSendQueue(context.createSimpleResponse());
                return true;
            }
            else {
                return false;
            }
        },
        context);
        auto query = std::make_unique<db::query::MarkAsRead>(context.getBody()[json::messages::threadID].int_value(),
                                                             (context.getBody()[json::messages::isUnread].bool_value()
                                                                  ? db::query::MarkAsRead::Read::False
                                                                  : db::query::MarkAsRead::Read::True));

    query->setQueryListener(std::move(listener));
    DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));
        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto threadResult = dynamic_cast<db::query::MarkAsReadResult *>(result)) {

    return sys::ReturnCodes::Success;
}
                    context.setResponseStatus(threadResult->getResult() ? http::Code::OK
                                                                        : http::Code::InternalServerError);
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }
                else {
                    return false;
                }
            },
            context);

auto MessageHelper::createSMS(Context &context) -> sys::ReturnCodes
{
    return sys::ReturnCodes::Success;
}
        query->setQueryListener(std::move(listener));

auto MessageHelper::deleteDBEntry(Context &context) -> sys::ReturnCodes
{
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSThread, std::move(query));

    if (context.getBody()[json::messages::msgTemplate].bool_value() == true) {
        return deleteTemplate(context);
    }
    else {
        return deleteSMS(context);
        return sys::ReturnCodes::Success;
    }
}

auto MessageHelper::deleteSMS(Context &context) -> sys::ReturnCodes
{
    auto id       = context.getBody()[json::messages::id].int_value();
    auto query    = std::make_unique<db::query::SMSRemove>(id);
    auto listener = std::make_unique<db::EndpointListener>(
        [=](db::QueryResult *result, Context context) {
            if (auto SMSTemplateResult = dynamic_cast<db::query::SMSRemoveResult *>(result)) {

                context.setResponseStatus(SMSTemplateResult->getResults() ? http::Code::OK
                                                                          : http::Code::InternalServerError);
                MessageHandler::putToSendQueue(context.createSimpleResponse());
                return true;
            }
            else {
                return false;
            }
        },
        context);

    query->setQueryListener(std::move(listener));
    DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));

    return sys::ReturnCodes::Success;
}

auto MessageHelper::deleteTemplate(Context &context) -> sys::ReturnCodes
{
    auto id       = context.getBody()[json::messages::id].int_value();
    auto query    = std::make_unique<db::query::SMSTemplateRemove>(id);
    auto listener = std::make_unique<db::EndpointListener>(
        [=](db::QueryResult *result, Context context) {
            if (auto SMSTemplateResult = dynamic_cast<db::query::SMSTemplateRemoveResult *>(result)) {

                context.setResponseStatus(SMSTemplateResult->getResults() ? http::Code::OK
                                                                          : http::Code::InternalServerError);
                MessageHandler::putToSendQueue(context.createSimpleResponse());
                return true;
            }
            else {
                return false;
            }
        },
        context);

    query->setQueryListener(std::move(listener));
    DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));
    return sys::ReturnCodes::Success;
}

auto MessageHelper::updateDBEntry(Context &context) -> sys::ReturnCodes
{

    if (context.getBody()[json::messages::msgTemplate].bool_value() == true) {
        return updateTemplate(context);
    }
    else {
        return updateSMS(context);
    auto MessageHelper::deleteThread(Context &context) -> sys::ReturnCodes
    {
        context.setResponseStatus(http::Code::InternalServerError);
        MessageHandler::putToSendQueue(context.createSimpleResponse());
        return sys::ReturnCodes::Success;
    }
}

auto MessageHelper::updateSMS(Context &context) -> sys::ReturnCodes
{
    using namespace db::query;

    auto query = std::make_unique<db::query::MarkAsRead>(
        context.getBody()[json::messages::threadID].int_value(),
        (context.getBody()[json::messages::isUnread].bool_value() ? MarkAsRead::Read::False : MarkAsRead::Read::True));

    auto listener = std::make_unique<db::EndpointListener>(
        [=](db::QueryResult *result, Context context) {
            if (auto SMSResult = dynamic_cast<db::query::MarkAsReadResult *>(result)) {

                context.setResponseStatus(SMSResult->getResult() ? http::Code::OK : http::Code::InternalServerError);
                MessageHandler::putToSendQueue(context.createSimpleResponse());

                return true;
            }
            else {
                return false;
            }
        },
        context);

    query->setQueryListener(std::move(listener));

    DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSThread, std::move(query));
    return sys::ReturnCodes::Success;
}

auto MessageHelper::updateTemplate(Context &context) -> sys::ReturnCodes
{
    SMSTemplateRecord record;
    record.ID   = context.getBody()[json::messages::id].int_value();
    record.text = context.getBody()[json::messages::templateText].string_value();

    auto query    = std::make_unique<db::query::SMSTemplateUpdate>(record);
    auto listener = std::make_unique<db::EndpointListener>(
        [=](db::QueryResult *result, Context context) {
            if (auto SMSTemplateResult = dynamic_cast<db::query::SMSTemplateUpdateResult *>(result)) {

                context.setResponseStatus(SMSTemplateResult->getResult() ? http::Code::OK
                                                                         : http::Code::InternalServerError);
                MessageHandler::putToSendQueue(context.createSimpleResponse());
                return true;
            }
            else {
                return false;
            }
        },
        context);

    query->setQueryListener(std::move(listener));
    DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));

    return sys::ReturnCodes::Success;
}
} // namespace parserFSM

M module-services/service-desktop/endpoints/messages/MessageHelper.hpp => module-services/service-desktop/endpoints/messages/MessageHelper.hpp +17 -41
@@ 14,9 14,6 @@
#include <ThreadRecord.hpp>
#include <json/json11.hpp>

#include <memory>
#include <string>

namespace sys
{
    class Service;


@@ 29,55 26,34 @@ namespace parserFSM
    class MessageHelper final : public DBHelper
    {
      public:
        MessageHelper(sys::Service *_ownerServicePtr) : DBHelper(_ownerServicePtr){};
        explicit MessageHelper(sys::Service *_ownerServicePtr) : DBHelper(_ownerServicePtr){};

        auto createDBEntry(Context &context) -> sys::ReturnCodes override;
        auto requestDataFromDB(Context &context) -> sys::ReturnCodes override;
        auto updateDBEntry(Context &context) -> sys::ReturnCodes override;
        auto deleteDBEntry(Context &context) -> sys::ReturnCodes override;

        auto createTemplate(Context &context) -> sys::ReturnCodes;
        auto requestTemplate(Context &context) -> sys::ReturnCodes;
        auto updateTemplate(Context &context) -> sys::ReturnCodes;
        auto deleteTemplate(Context &context) -> sys::ReturnCodes;
        static auto toJson(const SMSRecord &record) -> json11::Json;
        static auto toJson(const ThreadRecord &record) -> json11::Json;
        static auto toJson(const SMSTemplateRecord &record) -> json11::Json;
        static auto fromJson(const json11::Json &msgJson) -> SMSTemplateRecord;

      private:
        auto requestThread(Context &context) -> sys::ReturnCodes;
        auto updateThread(Context &context) -> sys::ReturnCodes;
        auto deleteThread(Context &context) -> sys::ReturnCodes;

        auto createSMS(Context &context) -> sys::ReturnCodes;
        auto requestSMS(Context &context) -> sys::ReturnCodes;
        auto updateSMS(Context &context) -> sys::ReturnCodes;
        auto createSMS(Context &context) -> sys::ReturnCodes;
        auto deleteSMS(Context &context) -> sys::ReturnCodes;

        // auto createSimpleResponse(Context &context) -> std::string override;

        static auto to_json(SMSRecord record) -> json11::Json;
        static auto to_json(ThreadRecord record) -> json11::Json;
        static auto to_json(SMSTemplateRecord record) -> json11::Json;
        static auto from_json(json11::Json msgJson) -> SMSTemplateRecord;
        auto requestTemplate(Context &context) -> sys::ReturnCodes;
        auto createTemplate(Context &context) -> sys::ReturnCodes;
        auto updateTemplate(Context &context) -> sys::ReturnCodes;
        auto deleteTemplate(Context &context) -> sys::ReturnCodes;

      private:
        json11::Json receivedJson;
    };

    namespace messages
    {
        inline constexpr auto id           = "id";
        inline constexpr auto count        = "count";
        inline constexpr auto offset       = "offset";
        inline constexpr auto phoneNumber  = "phoneNumber";
        inline constexpr auto isUnread     = "unread";
        inline constexpr auto contactID    = "contactID";
        inline constexpr auto date         = "date";
        inline constexpr auto dateSent     = "dateSent";
        inline constexpr auto type         = "type";
        inline constexpr auto threadID     = "threadID";
        inline constexpr auto msgTemplate  = "template";
        inline constexpr auto templateText = "text";
        namespace thread
        {
            inline constexpr auto msgCount       = "msgCount";
            inline constexpr auto snippet        = "snippet";
            inline constexpr auto unreadMsgCount = "unreadMsgCount";

        } // namespace thread

    } // namespace messages
        const int defaultLimit = 100; // will be removed after introducing pagination
    };
} // namespace parserFSM

M module-services/service-desktop/endpoints/messages/MessagesEndpoint.cpp => module-services/service-desktop/endpoints/messages/MessagesEndpoint.cpp +3 -3
@@ 14,11 14,11 @@ auto MessagesEndpoint::handle(Context &context) -> void
    case http::Method::get:
        helper->requestDataFromDB(context);
        break;
    case http::Method::post: // update entry
        helper->updateDBEntry(context);
    case http::Method::post:
        helper->createDBEntry(context);
        break;
    case http::Method::put:
        helper->createDBEntry(context);
        helper->updateDBEntry(context);
        break;
    case http::Method::del:
        helper->deleteDBEntry(context);

M module-services/service-desktop/endpoints/messages/MessagesEndpoint.hpp => module-services/service-desktop/endpoints/messages/MessagesEndpoint.hpp +1 -1
@@ 27,7 27,7 @@ class MessagesEndpoint : public parserFSM::Endpoint
    std::shared_ptr<parserFSM::MessageHelper> helper;

  public:
    MessagesEndpoint(sys::Service *_ownerServicePtr) : Endpoint(_ownerServicePtr)
    explicit MessagesEndpoint(sys::Service *_ownerServicePtr) : Endpoint(_ownerServicePtr)
    {
        debugName = "MessagesEndpoint";
        helper    = std::make_shared<parserFSM::MessageHelper>(ownerServicePtr);

M module-services/service-desktop/parser/MessageHandler.cpp => module-services/service-desktop/parser/MessageHandler.cpp +8 -8
@@ 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 "MessageHandler.hpp"


@@ 35,18 35,18 @@ MessageHandler::MessageHandler(const std::string &message, sys::Service *OwnerSe

void MessageHandler::processMessage()
{
    Context context(messageJson);
    auto context = ContextFactory::create(messageJson);

    LOG_DEBUG("[MsgHandler]\nmethod: %d\nendpoint: %d\nuuid: %" PRIu32 "\nbody: %s\n",
              static_cast<int>(context.getMethod()),
              static_cast<int>(context.getEndpoint()),
              context.getUuid(),
              context.getBody().dump().c_str());
              static_cast<int>(context->getMethod()),
              static_cast<int>(context->getEndpoint()),
              context->getUuid(),
              context->getBody().dump().c_str());

    auto handler = EndpointFactory::create(context, OwnerServicePtr);
    auto handler = EndpointFactory::create(*context, OwnerServicePtr);

    if (handler != nullptr) {
        handler->handle(context);
        handler->handle(*context);
    }
    else {
        LOG_ERROR("No way to handle!");

M module-services/service-desktop/parser/ParserUtils.cpp => module-services/service-desktop/parser/ParserUtils.cpp +13 -11
@@ 5,16 5,18 @@

#include <log/log.hpp>

using namespace parserFSM;

bool http::isMethodValid(uint8_t method)
namespace parserFSM
{
    if (method == static_cast<uint8_t>(http::Method::get) || method == static_cast<uint8_t>(http::Method::post) ||
        method == static_cast<uint8_t>(http::Method::put) || method == static_cast<uint8_t>(http::Method::del)) {
        return true;
    }
    else {
        LOG_ERROR("Invalid method!");
        return false;

    bool http::isMethodValid(uint8_t method)
    {
        if (method == static_cast<uint8_t>(http::Method::get) || method == static_cast<uint8_t>(http::Method::post) ||
            method == static_cast<uint8_t>(http::Method::put) || method == static_cast<uint8_t>(http::Method::del)) {
            return true;
        }
        else {
            LOG_ERROR("Invalid method!");
            return false;
        }
    }
}
} // namespace parserFSM

M module-services/service-desktop/parser/ParserUtils.hpp => module-services/service-desktop/parser/ParserUtils.hpp +37 -27
@@ 45,7 45,7 @@ namespace parserFSM
            msg.erase(msg.begin(), msg.begin() + size_header);
        }

        inline unsigned long calcPayloadLength(const std::string header)
        inline unsigned long calcPayloadLength(const std::string &header)
        {
            try {
                return std::stol(header.substr(1, std::string::npos));


@@ 67,10 67,12 @@ namespace parserFSM
        }
        inline std::string extractPayload(std::string &msg, size_t payloadLength)
        {
            if (msg.size() > payloadLength)
            if (msg.size() > payloadLength) {
                return msg.substr(0, payloadLength);
            else
            }
            else {
                return msg;
            }
        }
    } // namespace message



@@ 140,9 142,9 @@ namespace parserFSM
            inline constexpr auto command = "command";
            namespace commands
            {
                inline constexpr auto upload   = "upload";
                inline constexpr auto rm       = "rm";
                inline constexpr auto download = "download";
                inline constexpr auto upload    = "upload";
                inline constexpr auto rm        = "rm";
                inline constexpr auto download  = "download";
                inline constexpr auto checkFile = "checkFile";
            } // namespace commands
        }     // namespace filesystem


@@ 159,27 161,35 @@ namespace parserFSM

        namespace messages
        {
            inline constexpr auto id           = "id";
            inline constexpr auto count        = "count";
            inline constexpr auto offset       = "offset";
            inline constexpr auto phoneNumber  = "phoneNumber";
            inline constexpr auto messageBody  = "messageBody";
            inline constexpr auto isUnread     = "unread";
            inline constexpr auto contactID    = "contactID";
            inline constexpr auto date         = "date";
            inline constexpr auto dateSent     = "dateSent";
            inline constexpr auto type         = "type";
            inline constexpr auto threadID     = "threadID";
            inline constexpr auto msgTemplate  = "template";
            inline constexpr auto templateText = "text";
            namespace thread
            {
                inline constexpr auto msgCount       = "msgCount";
                inline constexpr auto snippet        = "snippet";
                inline constexpr auto unreadMsgCount = "unreadMsgCount";

            } // namespace thread

            inline constexpr auto count            = "count";
            inline constexpr auto category         = "category";
            inline constexpr auto categoryMessage  = "message";
            inline constexpr auto categoryThread   = "thread";
            inline constexpr auto categoryTemplate = "template";

            inline constexpr auto limit              = "limit";
            inline constexpr auto offset             = "offset";
            inline constexpr auto totalCount         = "totalCount";
            inline constexpr auto nextPage           = "nextPage";
            inline constexpr auto entries            = "entries";
            inline constexpr auto messageBody        = "messageBody";
            inline constexpr auto messageCount       = "messageCount";
            inline constexpr auto messageID          = "messageID";
            inline constexpr auto messageType        = "messageType";
            inline constexpr auto phoneNumber        = "phoneNumber";
            inline constexpr auto receivedAt         = "receivedAt";
            inline constexpr auto sentAt             = "sentAt";
            inline constexpr auto lastUsedAt         = "lastUsedAt";
            inline constexpr auto lastUpdatedAt      = "lastUpdatedAt";
            inline constexpr auto isUnread           = "isUnread";
            inline constexpr auto contactID          = "contactID";
            inline constexpr auto numberID           = "numberID";
            inline constexpr auto threadID           = "threadID";
            inline constexpr auto messageSnippet     = "messageSnippet";
            inline constexpr auto unreadMessageCount = "unreadMessageCount";
            inline constexpr auto messageTemplate    = "messageTemplate";
            inline constexpr auto templateBody       = "templateBody";
            inline constexpr auto templateID         = "templateID";
        } // namespace messages

    } // namespace json

M module-services/service-desktop/tests/CMakeLists.txt => module-services/service-desktop/tests/CMakeLists.txt +14 -0
@@ 15,3 15,17 @@ add_catch2_executable(
    DEPS
        PurePhone.img-target
)

add_catch2_executable(
    NAME
        service-desktop-endpoint-contacts
    SRCS
        tests-main.cpp
        test-contacts.cpp
    LIBS
        service-desktop
        module-utils
        module-apps
        module-vfs
        iosyscalls
)

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

#include <endpoints/Endpoint.hpp>
#include <endpoints/EndpointFactory.hpp>

#include <catch2/catch.hpp>
#include <json/json11.hpp>
#include <purefs/filesystem_paths.hpp>
#include <utf8/UTF8.hpp>
#include <vfs.hpp>

#include <memory>
#include <filesystem>
#include <string>
#include <vector>

#include <module-db/Interface/ContactRecord.hpp>
#include <module-db/Databases/ContactsDB.hpp>

class vfs vfs;
std::unique_ptr<ContactsDB> contactsDb = nullptr;
std::vector<std::string> messageStrings;

// stubs
std::pair<bool, std::uint64_t> DBServiceAPI::GetQuery(sys::Service *serv,
                                                      db::Interface::Name database,
                                                      std::unique_ptr<db::Query> query)
{
    auto queryType = query->type;
    LOG_DEBUG("query is: %d", static_cast<uint32_t>(queryType));

    ContactRecordInterface cri(contactsDb.get());
    auto result        = cri.runQuery(std::move(query));
    auto queryListener = result->getRequestQuery()->getQueryListener();
    queryListener->handleQueryResponse(result.get());

    return std::make_pair(true, 0);
}
auto DBServiceAPI::ThreadGetByNumber(sys::Service *serv,
                                     const utils::PhoneNumber::View &phoneNumber,
                                     std::uint32_t timeout) -> std::unique_ptr<ThreadRecord>
{
    return nullptr;
}
auto DBServiceAPI::ThreadGetCount(sys::Service *serv, EntryState state) -> uint32_t
{
    return 0;
}

auto DBServiceAPI::GetQueryWithReply(sys::Service *serv,
                                     db::Interface::Name database,
                                     std::unique_ptr<db::Query> query,
                                     std::uint32_t timeout) -> sys::SendResult
{
    return {};
}

auto DBServiceAPI::verifyContact(sys::Service *serv, const ContactRecord &rec) -> ContactVerificationResult
{
    return {};
}
auto DBServiceAPI::ContactGetByID(sys::Service *serv, uint32_t contactID) -> std::unique_ptr<std::vector<ContactRecord>>
{
    return nullptr;
}
auto DBServiceAPI::ContactGetByIDWithTemporary(sys::Service *serv, uint32_t contactID)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    return nullptr;
}
auto DBServiceAPI::ContactGetByIDCommon(sys::Service *serv, std::shared_ptr<DBContactMessage> contactMsg)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    return nullptr;
}
auto DBServiceAPI::ContactGetBySpeeddial(sys::Service *serv, UTF8 speeddial)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    return nullptr;
}
auto DBServiceAPI::ContactGetByPhoneNumber(sys::Service *serv, UTF8 phoneNumber)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    return nullptr;
}
auto DBServiceAPI::MatchContactByPhoneNumber(sys::Service *serv, const utils::PhoneNumber::View &numberView)
    -> std::unique_ptr<ContactRecord>
{
    return nullptr;
}
auto DBServiceAPI::ContactAdd(sys::Service *serv, const ContactRecord &rec) -> bool
{
    return false;
}
auto DBServiceAPI::ContactRemove(sys::Service *serv, uint32_t id) -> bool
{
    return false;
}
auto DBServiceAPI::ContactUpdate(sys::Service *serv, const ContactRecord &rec) -> bool
{
    return false;
}
auto DBServiceAPI::ContactSearch(sys::Service *serv, UTF8 primaryName, UTF8 alternativeName, UTF8 number)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    return nullptr;
}
auto DBServiceAPI::CalllogAdd(sys::Service *serv, const CalllogRecord &rec) -> CalllogRecord
{
    return {};
}
auto DBServiceAPI::CalllogRemove(sys::Service *serv, uint32_t id) -> bool
{
    return false;
}
auto DBServiceAPI::CalllogUpdate(sys::Service *serv, const CalllogRecord &rec) -> bool
{
    return false;
}
auto DBServiceAPI::CalllogGetCount(sys::Service *serv, EntryState state) -> uint32_t
{
    return 0;
}
auto DBServiceAPI::CalllogGetLimitOffset(sys::Service *serv, uint32_t offset, uint32_t limit) -> bool
{
    return false;
}
auto DBServiceAPI::GetCountryCodeByMCC(sys::Service *serv, uint32_t mcc) -> uint32_t
{
    return 0;
}
auto DBServiceAPI::DBBackup(sys::Service *serv, std::string backupPath) -> bool
{
    return false;
}
bool DBServiceAPI::AddSMS(sys::Service *serv, const SMSRecord &record, std::unique_ptr<db::QueryListener> &&listener)
{
    return false;
}

xQueueHandle parserFSM::MessageHandler::sendQueue;
parserFSM::MessageHandler::MessageHandler(const std::string &message, sys::Service *OwnerService)
    : OwnerServicePtr(OwnerService)
{}
void parserFSM::MessageHandler::processMessage()
{}
void parserFSM::MessageHandler::putToSendQueue(const std::string &msg)
{
    messageStrings.push_back(msg);
    LOG_DEBUG("response is: %s", msg.c_str());
}
//~stubs

TEST_CASE("Endpoint Contacts Test")
{
    vfs.Init();
    Database::initialize();

    const auto contactsPath = purefs::dir::getUserDiskPath() / "contacts.db";
    std::filesystem::remove(contactsPath);

    contactsDb = std::make_unique<ContactsDB>(contactsPath.c_str());
    REQUIRE(contactsDb->isInitialized());
    messageStrings.clear();

    SECTION("Request with pages")
    {
        auto count       = 29; // requested number of record to return
        auto endpoint    = 7;
        auto uuid        = "1103";
        auto totalCount  = contactsDb->contacts.count();
        auto testMessage = "{\"endpoint\":" + std::to_string(endpoint) + ", \"method\":1, \"uuid\":" + uuid +
                           ", \"body\":{\"limit\":" + std::to_string(count) + ", \"offset\":20}}";
        std::string err;
        auto msgJson = json11::Json::parse(testMessage, err);
        REQUIRE(err.empty());

        parserFSM::PagedContext context(msgJson, 10);
        auto handler = EndpointFactory::create(context, nullptr);
        handler->handle(context);
        auto pageSize = context.getPageSize();
        REQUIRE(10 == pageSize);
        REQUIRE(1 == messageStrings.size());
        auto msg = messageStrings[0];
        REQUIRE(msg.size() > 10);
        auto retJson = json11::Json::parse(msg.substr(10), err); // string length and go to real data

        REQUIRE(err.empty());
        REQUIRE(uuid == retJson[parserFSM::json::uuid].string_value());
        REQUIRE(endpoint == retJson[parserFSM::json::endpoint].int_value());

        auto body = retJson[parserFSM::json::body];
        REQUIRE(totalCount == static_cast<uint32_t>(body[parserFSM::json::totalCount].int_value()));

        auto nextPage = body[parserFSM::json::nextPage];
        REQUIRE(static_cast<int>(pageSize) == nextPage[parserFSM::json::limit].int_value());
        // base offset + pageSize cause asked for 29
        REQUIRE(30 == nextPage[parserFSM::json::offset].int_value());

        auto entries = body[parserFSM::json::entries];
        REQUIRE(pageSize == entries.array_items().size());
    }
}

M module-services/service-desktop/tests/unittest.cpp => module-services/service-desktop/tests/unittest.cpp +7 -7
@@ 209,24 209,24 @@ TEST_CASE("DB Helpers test - json encoding (messages)")
    message->ID        = 10;
    message->type      = SMSType::DRAFT;

    auto messageJson = helper->to_json(*message);
    auto messageJson = helper->toJson(*message);

    REQUIRE(messageJson[json::messages::messageBody] == "test message");
    REQUIRE(messageJson[json::messages::contactID] == 1);
    REQUIRE(messageJson[json::messages::date] == 12345);
    REQUIRE(messageJson[json::messages::dateSent] == 54321);
    REQUIRE(messageJson[json::messages::receivedAt] == 12345);
    REQUIRE(messageJson[json::messages::sentAt] == 54321);
    REQUIRE(messageJson[json::messages::threadID] == 1);
    REQUIRE(messageJson[json::messages::id] == 10);
    REQUIRE(messageJson[json::messages::messageID] == 10);

    auto messageTemplate = std::make_unique<SMSTemplateRecord>();

    messageTemplate->text = "test template";
    messageTemplate->ID   = 1;

    auto messageTemplateJson = helper->to_json(*messageTemplate);
    auto messageTemplateJson = helper->toJson(*messageTemplate);

    REQUIRE(messageTemplateJson[json::messages::templateText] == "test template");
    REQUIRE(messageTemplateJson[json::messages::id] == 1);
    REQUIRE(messageTemplateJson[json::messages::templateBody] == "test template");
    REQUIRE(messageTemplateJson[json::messages::templateID] == 1);
}

TEST_CASE("Context class test")

M module-services/service-evtmgr/EventManager.cpp => module-services/service-evtmgr/EventManager.cpp +32 -31
@@ 39,9 39,12 @@
#include <vector>
#include <module-apps/messages/AppMessage.hpp>
#include <SystemManager/messages/CpuFrequencyMessage.hpp>
#include <common_data/EventStore.hpp>
#include <SystemManager/messages/PhoneModeRequest.hpp>

EventManager::EventManager(const std::string &name)
    : sys::Service(name), screenLightControl(std::make_unique<screen_light_control::ScreenLightControl>(this))
    : sys::Service(name), settings(std::make_shared<settings::Settings>(this)),
      screenLightControl(std::make_unique<screen_light_control::ScreenLightControl>(settings, this))
{
    LOG_INFO("[%s] Initializing", name.c_str());
    alarmTimestamp = 0;


@@ 101,9 104,16 @@ sys::MessagePointer EventManager::DataReceivedHandler(sys::DataMessage *msgl, sy
        auto message = std::make_shared<sevm::KbdMessage>();
        message->key = msg->key;

        if (message->key.state == RawKey::State::Pressed && message->key.key_code == bsp::KeyCodes::FnRight) {
            // and state == ShutDown
            bus.sendUnicast(message, service::name::system_manager);
        if (message->key.state == RawKey::State::Pressed) {
            const auto code = message->key.key_code;
            if (code == bsp::KeyCodes::FnRight) {
                bus.sendUnicast(message, service::name::system_manager);
            }
            else if (code == bsp::KeyCodes::SSwitchUp || code == bsp::KeyCodes::SSwitchMid ||
                     code == bsp::KeyCodes::SSwitchDown) {
                const auto mode = sys::SystemManager::translateSliderState(message->key);
                bus.sendUnicast(std::make_shared<sys::PhoneModeRequest>(mode), service::name::system_manager);
            }
        }

        // send key to focused application


@@ 122,32 132,6 @@ sys::MessagePointer EventManager::DataReceivedHandler(sys::DataMessage *msgl, sy
            LOG_INFO("Switching focus to %s", targetApplication.c_str());
        }
    }
    else if (msgl->messageType == MessageType::EVMBatteryLevel && msgl->sender == this->GetName()) {
        auto *msg = static_cast<sevm::BatteryLevelMessage *>(msgl);

        auto message = std::make_shared<sevm::BatteryLevelMessage>(msg->levelPercents, msg->fullyCharged);

        if (!targetApplication.empty()) {
            bus.sendUnicast(message, targetApplication);
        }

        handled = true;
    }
    else if (msgl->messageType == MessageType::EVMChargerPlugged && msgl->sender == this->GetName()) {
        auto *msg = static_cast<sevm::BatteryPlugMessage *>(msgl);

        auto message     = std::make_shared<sevm::BatteryPlugMessage>();
        message->plugged = msg->plugged;

        if (!message->plugged) {
            bus.sendUnicast(message, service::name::system_manager);
        }

        if (!targetApplication.empty()) {
            bus.sendUnicast(message, targetApplication);
        }
        handled = true;
    }
    else if (msgl->messageType == MessageType::EVMMinuteUpdated && msgl->sender == this->GetName()) {

        HandleAlarmTrigger(msgl);


@@ 287,6 271,23 @@ sys::ReturnCodes EventManager::InitHandler()
        return msg;
    });

    connect(sevm::BatteryStatusChangeMessage(), [&](sys::Message *msgl) {
        if (msgl->sender == this->GetName()) {
            LOG_INFO("Battery level: %d , charging: %d",
                     Store::Battery::get().level,
                     Store::Battery::get().state == Store::Battery::State::Charging);

            if (Store::Battery::get().state == Store::Battery::State::Discharging) {
                bus.sendUnicast(std::make_shared<sevm::BatteryStatusChangeMessage>(), service::name::system_manager);
            }

            if (!targetApplication.empty()) {
                bus.sendUnicast(std::make_shared<sevm::BatteryStatusChangeMessage>(), targetApplication);
            }
        }
        return std::make_shared<sys::ResponseMessage>();
    });

    // initialize keyboard worker
    EventWorker = std::make_unique<WorkerEvent>(this);



@@ 322,7 323,7 @@ sys::ReturnCodes EventManager::InitHandler()
    list.push_back(qLightSensor);
    list.push_back(qChargerDetect);

    EventWorker->init(list);
    EventWorker->init(list, settings);
    EventWorker->run();

    return sys::ReturnCodes::Success;

M module-services/service-evtmgr/WorkerEvent.cpp => module-services/service-evtmgr/WorkerEvent.cpp +14 -8
@@ 93,8 93,8 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
        if (notification == static_cast<std::uint8_t>(bsp::battery_charger::batteryIRQSource::INTB)) {
            auto topINT = bsp::battery_charger::getTopControllerINTSource();
            if (topINT & static_cast<std::uint8_t>(bsp::battery_charger::topControllerIRQsource::CHGR_INT)) {
                auto message     = std::make_shared<sevm::BatteryPlugMessage>();
                message->plugged = bsp::battery_charger::getChargeStatus();
                bsp::battery_charger::getChargeStatus();
                auto message = std::make_shared<sevm::BatteryStatusChangeMessage>();
                service->bus.sendUnicast(std::move(message), service::name::evt_manager);
                bsp::battery_charger::clearAllChargerIRQs();
            }


@@ 107,8 107,8 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
                if (status & static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::SOCOnePercentChange)) {
                    bsp::battery_charger::clearFuelGuageIRQ(
                        static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::SOCOnePercentChange));
                    bsp::battery_charger::StateOfCharge battLevel = bsp::battery_charger::getBatteryLevel();
                    auto message = std::make_shared<sevm::BatteryLevelMessage>(battLevel, false);
                    bsp::battery_charger::getBatteryLevel();
                    auto message = std::make_shared<sevm::BatteryStatusChangeMessage>();
                    service->bus.sendUnicast(std::move(message), service::name::evt_manager);
                    battery_level_check::checkBatteryLevelCritical();
                }


@@ 118,8 118,8 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
                        static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::maxTemp) |
                        static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::minTemp));
                    bsp::battery_charger::checkTemperatureRange();
                    auto message     = std::make_shared<sevm::BatteryPlugMessage>();
                    message->plugged = bsp::battery_charger::getChargeStatus();
                    bsp::battery_charger::getChargeStatus();
                    auto message = std::make_shared<sevm::BatteryStatusChangeMessage>();
                    service->bus.sendUnicast(std::move(message), service::name::evt_manager);
                }
            }


@@ 225,11 225,15 @@ bool WorkerEvent::init(std::list<sys::WorkerQueueInfo> queuesList)
    bsp::rtc_GetCurrentTimestamp(&timestamp);
    bsp::rtc_SetMinuteAlarm(timestamp);

    battery_level_check::init(service);

    return true;
}

void WorkerEvent::init(std::list<sys::WorkerQueueInfo> queuesList, std::shared_ptr<settings::Settings> settings)
{
    init(queuesList);
    battery_level_check::init(service, settings);
}

bool WorkerEvent::deinit(void)
{
    Worker::deinit();


@@ 241,6 245,8 @@ bool WorkerEvent::deinit(void)
    bsp::eink_frontlight::deinit();
    bsp::light_sensor::deinit();

    battery_level_check::deinit();

    return true;
}


M module-services/service-evtmgr/battery-level-check/BatteryLevelCheck.cpp => module-services/service-evtmgr/battery-level-check/BatteryLevelCheck.cpp +19 -5
@@ 4,8 4,9 @@
#include "BatteryLevelCheck.hpp"
#include "service-evtmgr/BatteryMessages.hpp"
#include "SystemManager/Constants.hpp"

#include <agents/settings/SystemSettings.hpp>
#include <common_data/EventStore.hpp>
#include <Utils.hpp>

namespace battery_level_check
{


@@ 20,12 21,12 @@ namespace battery_level_check

        CheckState state = CheckState::InitialCheck;

        constexpr inline auto DEFAULT_LEVEL = 10;

        unsigned int batteryLevelCritical = DEFAULT_LEVEL;
        unsigned int batteryLevelCritical = 0;

        sys::Service *parentService = nullptr;

        std::shared_ptr<settings::Settings> settings = nullptr;

        bool isBatteryLevelCritical(unsigned int level)
        {
            return level < batteryLevelCritical;


@@ 44,9 45,20 @@ namespace battery_level_check
        }
    } // namespace

    void init(sys::Service *service)
    void init(sys::Service *service, std::shared_ptr<settings::Settings> &setts)
    {
        parentService = service;
        settings      = setts;
        settings->registerValueChange(
            settings::Battery::batteryCriticalLevel,
            [&](const std::string &value) { batteryLevelCritical = utils::getNumericValue<unsigned int>(value); },
            settings::SettingsScope::Global);
    }

    void deinit()
    {
        settings->unregisterValueChange(settings::Battery::batteryCriticalLevel, settings::SettingsScope::Global);
        settings.reset();
    }

    void checkBatteryLevelCritical()


@@ 86,6 98,8 @@ namespace battery_level_check
    void setBatteryCriticalLevel(unsigned int level)
    {
        batteryLevelCritical = level;
        settings->setValue(
            settings::Battery::batteryCriticalLevel, utils::to_string(level), settings::SettingsScope::Global);
        checkBatteryLevelCritical();
    }


M module-services/service-evtmgr/battery-level-check/BatteryLevelCheck.hpp => module-services/service-evtmgr/battery-level-check/BatteryLevelCheck.hpp +4 -1
@@ 4,6 4,7 @@
#pragma once

#include <Service/Service.hpp>
#include <service-db/service-db/Settings.hpp>

namespace sys
{


@@ 12,7 13,9 @@ namespace sys

namespace battery_level_check
{
    void init(sys::Service *service);
    void init(sys::Service *service, std::shared_ptr<settings::Settings> &setts);

    void deinit();

    void checkBatteryLevelCritical();


A module-services/service-evtmgr/doc/battery_status_notification.md => module-services/service-evtmgr/doc/battery_status_notification.md +4 -0
@@ 0,0 1,4 @@
# Battery Status propagation in system

Battery status information is propagated according to following diagram:
![](battery_status_notification.svg)
\ No newline at end of file

A module-services/service-evtmgr/doc/battery_status_notification.puml => module-services/service-evtmgr/doc/battery_status_notification.puml +24 -0
@@ 0,0 1,24 @@
@startuml
participant "Battery Charger" as bc
participant "Worker Event" as we
participant "Event Manager" as evm
participant "System Manager" as sm
participant "Current application" as ca
participant "TopBar" as tb
participant "Battery Widget" as bw
participant "Event Store\nBattery" as es

bc -> we : Interrupt
we -> bc : Store status to EventStore
bc -> es : modify()
we -> evm : BatteryStatusChangeMessage
group If Discharging
    evm -> sm : BatteryStatusChangeMessage
    sm -> sm : If State::ShutdownReady:\n state = State:Shutdown
end
evm -> ca : BatteryStatusChangeMessage
ca -> tb : updateBattery()
tb <-> es : get() 
tb -> bw : show(batteryContext)

@enduml

A module-services/service-evtmgr/doc/battery_status_notification.svg => module-services/service-evtmgr/doc/battery_status_notification.svg +34 -0
@@ 0,0 1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="486px" preserveAspectRatio="none" style="width:1340px;height:486px;" version="1.1" viewBox="0 0 1340 486" width="1340px" zoomAndPan="magnify"><defs><filter height="300%" id="f1uj3fz6xy4wx1" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><rect fill="#FFFFFF" filter="url(#f1uj3fz6xy4wx1)" height="103.5313" style="stroke:#000000;stroke-width:2.0;" width="468.5" x="411.5" y="190.125"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="68" x2="68" y1="56.5938" y2="427.1875"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="263" x2="263" y1="56.5938" y2="427.1875"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="481.5" x2="481.5" y1="56.5938" y2="427.1875"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="700.5" x2="700.5" y1="56.5938" y2="427.1875"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="875" x2="875" y1="56.5938" y2="427.1875"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="1001" x2="1001" y1="56.5938" y2="427.1875"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="1162.5" x2="1162.5" y1="56.5938" y2="427.1875"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="1281.5" x2="1281.5" y1="56.5938" y2="427.1875"/><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="122" x="5" y="21.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="108" x="12" y="41.292">Battery Charger</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="122" x="5" y="426.1875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="108" x="12" y="446.1826">Battery Charger</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="106" x="208" y="21.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="92" x="215" y="41.292">Worker Event</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="106" x="208" y="426.1875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="92" x="215" y="446.1826">Worker Event</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="117" x="421.5" y="21.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="103" x="428.5" y="41.292">Event Manager</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="117" x="421.5" y="426.1875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="103" x="428.5" y="446.1826">Event Manager</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="129" x="634.5" y="21.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="115" x="641.5" y="41.292">System Manager</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="129" x="634.5" y="426.1875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="115" x="641.5" y="446.1826">System Manager</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="144" x="801" y="21.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="130" x="808" y="41.292">Current application</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="144" x="801" y="426.1875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="130" x="808" y="446.1826">Current application</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="64" x="967" y="21.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="50" x="974" y="41.292">TopBar</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="64" x="967" y="426.1875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="50" x="974" y="446.1826">TopBar</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="115" x="1103.5" y="21.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="101" x="1110.5" y="41.292">Battery Widget</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="115" x="1103.5" y="426.1875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="101" x="1110.5" y="446.1826">Battery Widget</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="46.5938" style="stroke:#A80036;stroke-width:1.5;" width="94" x="1232.5" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="80" x="1239.5" y="24.9951">Event Store</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="49" x="1255" y="41.292">Battery</text><rect fill="#FEFECE" filter="url(#f1uj3fz6xy4wx1)" height="46.5938" style="stroke:#A80036;stroke-width:1.5;" width="94" x="1232.5" y="426.1875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="80" x="1239.5" y="446.1826">Event Store</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="49" x="1255" y="462.4795">Battery</text><polygon fill="#A80036" points="251,83.7266,261,87.7266,251,91.7266,255,87.7266" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="68" x2="257" y1="87.7266" y2="87.7266"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="55" x="75" y="82.6606">Interrupt</text><polygon fill="#A80036" points="79,112.8594,69,116.8594,79,120.8594,75,116.8594" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="73" x2="262" y1="116.8594" y2="116.8594"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="171" x="85" y="111.7935">Store status to EventStore</text><polygon fill="#A80036" points="1269.5,141.9922,1279.5,145.9922,1269.5,149.9922,1273.5,145.9922" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="68" x2="1275.5" y1="145.9922" y2="145.9922"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="53" x="75" y="140.9263">modify()</text><polygon fill="#A80036" points="470,171.125,480,175.125,470,179.125,474,175.125" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="263" x2="476" y1="175.125" y2="175.125"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="195" x="270" y="170.0591">BatteryStatusChangeMessage</text><path d="M411.5,190.125 L559.5,190.125 L559.5,197.125 L549.5,207.125 L411.5,207.125 L411.5,190.125 " fill="#EEEEEE" style="stroke:#000000;stroke-width:1.0;"/><rect fill="none" height="103.5313" style="stroke:#000000;stroke-width:2.0;" width="468.5" x="411.5" y="190.125"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="103" x="426.5" y="203.1919">If Discharging</text><polygon fill="#A80036" points="689,224.3906,699,228.3906,689,232.3906,693,228.3906" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="482" x2="695" y1="228.3906" y2="228.3906"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="195" x="489" y="223.3247">BatteryStatusChangeMessage</text><line style="stroke:#A80036;stroke-width:1.0;" x1="701" x2="743" y1="272.6563" y2="272.6563"/><line style="stroke:#A80036;stroke-width:1.0;" x1="743" x2="743" y1="272.6563" y2="285.6563"/><line style="stroke:#A80036;stroke-width:1.0;" x1="702" x2="743" y1="285.6563" y2="285.6563"/><polygon fill="#A80036" points="712,281.6563,702,285.6563,712,289.6563,708,285.6563" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="160" x="708" y="252.4575">If State::ShutdownReady:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="154" x="712" y="267.5903">state = State:Shutdown</text><polygon fill="#A80036" points="863,317.7891,873,321.7891,863,325.7891,867,321.7891" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="482" x2="869" y1="321.7891" y2="321.7891"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="195" x="489" y="316.7231">BatteryStatusChangeMessage</text><polygon fill="#A80036" points="989,346.9219,999,350.9219,989,354.9219,993,350.9219" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="875" x2="995" y1="350.9219" y2="350.9219"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="102" x="882" y="345.856">updateBattery()</text><polygon fill="#A80036" points="1012,376.0547,1002,380.0547,1012,384.0547,1008,380.0547" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1269.5,376.0547,1279.5,380.0547,1269.5,384.0547,1273.5,380.0547" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="1006" x2="1275.5" y1="380.0547" y2="380.0547"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="31" x="1018" y="374.9888">get()</text><polygon fill="#A80036" points="1151,405.1875,1161,409.1875,1151,413.1875,1155,409.1875" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="1001" x2="1157" y1="409.1875" y2="409.1875"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="138" x="1008" y="404.1216">show(batteryContext)</text><!--MD5=[c6c243b94a88456e8572e27fdc18c06d]
@startuml
participant "Battery Charger" as bc
participant "Worker Event" as we
participant "Event Manager" as evm
participant "System Manager" as sm
participant "Current application" as ca
participant "TopBar" as tb
participant "Battery Widget" as bw
participant "Event Store\nBattery" as es

bc -> we : Interrupt
we -> bc : Store status to EventStore
bc -> es : modify()
we -> evm : BatteryStatusChangeMessage
group If Discharging
    evm -> sm : BatteryStatusChangeMessage
    sm -> sm : If State::ShutdownReady:\n state = State:Shutdown
end
evm -> ca : BatteryStatusChangeMessage
ca -> tb : updateBattery()
tb <-> es : get() 
tb -> bw : show(batteryContext)

@enduml

PlantUML version 1.2021.00(Sun Jan 10 11:25:05 CET 2021)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Default Encoding: UTF-8
Language: pl
Country: PL
--></g></svg>
\ No newline at end of file

M module-services/service-evtmgr/screen-light-control/ScreenLightControl.cpp => module-services/service-evtmgr/screen-light-control/ScreenLightControl.cpp +2 -2
@@ 33,8 33,8 @@ namespace screen_light_control
        }
    } // namespace

    ScreenLightControl::ScreenLightControl(sys::Service *parent)
        : settings(std::make_unique<settings::Settings>(parent))
    ScreenLightControl::ScreenLightControl(std::shared_ptr<settings::Settings> settings, sys::Service *parent)
        : settings(settings)
    {
        controlTimer = std::make_unique<sys::Timer>("LightControlTimer", parent, CONTROL_TIMER_MS);
        readoutTimer = std::make_unique<sys::Timer>("LightSensorReadoutTimer", parent, READOUT_TIMER_MS);

M module-services/service-evtmgr/screen-light-control/ScreenLightControl.hpp => module-services/service-evtmgr/screen-light-control/ScreenLightControl.hpp +2 -2
@@ 56,7 56,7 @@ namespace screen_light_control
    class ScreenLightControl
    {
      public:
        explicit ScreenLightControl(sys::Service *parent);
        explicit ScreenLightControl(std::shared_ptr<settings::Settings> settings, sys::Service *parent);
        ~ScreenLightControl();

        void processRequest(Action action, const Parameters &params);


@@ 98,7 98,7 @@ namespace screen_light_control
        screen_light_control::ScreenLightMode automaticMode        = ScreenLightMode::Manual;
        bsp::eink_frontlight::BrightnessPercentage brightnessValue = 0.0;

        std::unique_ptr<settings::Settings> settings;
        std::shared_ptr<settings::Settings> settings;
    };

} // namespace screen_light_control

M module-services/service-evtmgr/service-evtmgr/BatteryMessages.hpp => module-services/service-evtmgr/service-evtmgr/BatteryMessages.hpp +2 -20
@@ 9,27 9,9 @@

namespace sevm
{
    class BatteryLevelMessage : public Message
    {
      public:
        BatteryLevelMessage(uint8_t levelPercents, bool fullyCharged)
            : Message(MessageType::EVMBatteryLevel), levelPercents(levelPercents), fullyCharged(fullyCharged)
        {
            type = Type::Data;
        }
        uint8_t levelPercents = 0;
        bool fullyCharged     = false;
    };
    class BatteryStatusChangeMessage : public sys::Message
    {};

    class BatteryPlugMessage : public Message
    {
      public:
        BatteryPlugMessage() : Message(MessageType::EVMChargerPlugged)
        {
            type = Type::Data;
        }
        bool plugged = false;
    };
    class BatterySetCriticalLevel : public sys::Message
    {
      public:

M module-services/service-evtmgr/service-evtmgr/EventManager.hpp => module-services/service-evtmgr/service-evtmgr/EventManager.hpp +2 -0
@@ 31,6 31,8 @@ class EventManager : public sys::Service
    void GetNextAlarmTimestamp(time_t timestamp);
    bool processKeypadBacklightRequest(bsp::keypad_backlight::Action act);

    std::shared_ptr<settings::Settings> settings;

  protected:
    std::unique_ptr<WorkerEvent> EventWorker;
    // application where key events are sent. This is also only application that is allowed to change keyboard long

M module-services/service-evtmgr/service-evtmgr/WorkerEvent.hpp => module-services/service-evtmgr/service-evtmgr/WorkerEvent.hpp +2 -0
@@ 70,6 70,8 @@ class WorkerEvent : public sys::Worker
    virtual bool init(std::list<sys::WorkerQueueInfo> queuesList) override;
    virtual bool deinit() override;

    void init(std::list<sys::WorkerQueueInfo> queuesList, std::shared_ptr<settings::Settings> settings);

    /**
     * This method is called from thread when new message arrives in queue.
     * @param queueID Index of the queue in the queues vector.

M module-sys/CMakeLists.txt => module-sys/CMakeLists.txt +2 -0
@@ 14,6 14,8 @@ set(SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/Service/Service.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Service/Timer.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Service/CpuSentinel.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/PhoneModes/Subject.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/PhoneModes/Observer.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/SystemManager.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/DependencyGraph.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/PowerManager.cpp

A module-sys/PhoneModes/Common.hpp => module-sys/PhoneModes/Common.hpp +49 -0
@@ 0,0 1,49 @@
// 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>

namespace sys::phone_modes
{
    enum class PhoneMode
    {
        Connected,
        DoNotDisturb,
        Offline
    };

    enum class Tethering
    {
        Off,
        On
    };

    class PhoneModeChanged : public DataMessage
    {
      public:
        PhoneModeChanged(PhoneMode phoneMode, Tethering tetheringMode)
            : DataMessage{MessageType::MessageTypeUninitialized}, phoneMode{phoneMode}, tethering{tetheringMode}
        {}

        [[nodiscard]] auto getPhoneMode() const noexcept
        {
            return phoneMode;
        }
        [[nodiscard]] auto getTetheringMode() const noexcept
        {
            return tethering;
        }

      private:
        PhoneMode phoneMode;
        Tethering tethering;
    };

    class PhoneModeChangedSuccessfully : public ResponseMessage
    {};

    class PhoneModeChangeFailed : public ResponseMessage
    {};
} // namespace sys::phone_modes

A module-sys/PhoneModes/Observer.cpp => module-sys/PhoneModes/Observer.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 "Observer.hpp"

#include <Service/Service.hpp>

namespace sys::phone_modes
{
    void Observer::connect(Service *owner)
    {
        owner->connect(typeid(PhoneModeChanged), [this](sys::Message *request) -> sys::MessagePointer {
            return handlePhoneModeChange(static_cast<PhoneModeChanged *>(request));
        });
    }

    void Observer::subscribe(OnChangeCallback &&onChange,
                             OnCompleteCallback &&onComplete,
                             OnErrorCallback &&onError) noexcept
    {
        onChangeCallback   = std::move(onChange);
        onCompleteCallback = std::move(onComplete);
        onErrorCallback    = std::move(onError);
    }

    bool Observer::isInMode(PhoneMode mode) const noexcept
    {
        return phoneMode == mode;
    }

    bool Observer::isTetheringOn() const noexcept
    {
        return tetheringMode == Tethering::On;
    }

    sys::MessagePointer Observer::handlePhoneModeChange(PhoneModeChanged *message)
    {
        phoneMode     = message->getPhoneMode();
        tetheringMode = message->getTetheringMode();

        try {
            onPhoneModeChanged();
        }
        catch (const std::exception &) {
            return std::make_shared<PhoneModeChangeFailed>();
        }
        return std::make_shared<PhoneModeChangedSuccessfully>();
    }

    void Observer::onPhoneModeChanged()
    {
        if (!onChangeCallback) {
            LOG_WARN("No subscriber on phone mode change.");
            return;
        }

        try {
            onChangeCallback(phoneMode, tetheringMode);
            if (onCompleteCallback) {
                onCompleteCallback();
            }
        }
        catch (const std::exception &error) {
            if (onErrorCallback) {
                onErrorCallback(error);
            }
            throw;
        }
    }
} // namespace sys::phone_modes

A module-sys/PhoneModes/Observer.hpp => module-sys/PhoneModes/Observer.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 <functional>
#include <stdexcept>

#include "Common.hpp"

namespace sys
{
    class Service; // Forward declaration
} // namespace sys

namespace sys::phone_modes
{
    class Observer
    {
      public:
        using OnChangeCallback   = std::function<void(PhoneMode, Tethering)>;
        using OnCompleteCallback = std::function<void()>;
        using OnErrorCallback    = std::function<void(const std::exception &)>;

        void connect(Service *owner);
        void subscribe(OnChangeCallback &&onChange,
                       OnCompleteCallback &&onComplete = {},
                       OnErrorCallback &&onError       = {}) noexcept;

        bool isInMode(PhoneMode mode) const noexcept;
        bool isTetheringOn() const noexcept;

      private:
        sys::MessagePointer handlePhoneModeChange(PhoneModeChanged *message);
        void onPhoneModeChanged();

        OnChangeCallback onChangeCallback;
        OnCompleteCallback onCompleteCallback;
        OnErrorCallback onErrorCallback;
        PhoneMode phoneMode     = PhoneMode::Connected;
        Tethering tetheringMode = Tethering::Off;
    };
} // namespace sys::phone_modes

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

#include "Subject.hpp"

#include <Service/Service.hpp>

#include <module-utils/magic_enum/include/magic_enum.hpp>

#include <stdexcept>
#include <utility>

namespace sys::phone_modes
{
    Subject::Subject(Service *owner) : owner{owner}
    {
        if (owner == nullptr) {
            throw std::invalid_argument{"Subject's owner is invalid"};
        }
    }

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

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

    bool Subject::changePhoneMode(PhoneMode mode) noexcept
    {
        return std::exchange(phoneMode, mode) != mode;
    }

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

    bool Subject::changeTetheringMode(Tethering mode) noexcept
    {
        return std::exchange(tetheringMode, mode) != mode;
    }

    void Subject::notifyChange()
    {
        LOG_INFO("Phone modes changed: Phone mode: [%s]; Tethering: [%s]. Notifying phone modes observers.",
                 magic_enum::enum_name(phoneMode).data(),
                 magic_enum::enum_name(tetheringMode).data());
        auto message = std::make_shared<PhoneModeChanged>(phoneMode, tetheringMode);
        owner->bus.sendMulticast(std::move(message), BusChannel::PhoneModeChanges);
    }
} // namespace sys::phone_modes

A module-sys/PhoneModes/Subject.hpp => module-sys/PhoneModes/Subject.hpp +33 -0
@@ 0,0 1,33 @@
// 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 "Common.hpp"

namespace sys
{
    class Service; // Forward declaration
} // namespace sys

namespace sys::phone_modes
{
    class Subject
    {
      public:
        explicit Subject(Service *owner);

        void setMode(PhoneMode _phoneMode, Tethering _tetheringMode);
        void setPhoneMode(PhoneMode mode);
        void setTetheringMode(Tethering mode);

      private:
        void notifyChange();
        bool changePhoneMode(PhoneMode mode) noexcept;
        bool changeTetheringMode(Tethering mode) noexcept;

        Service *owner;
        PhoneMode phoneMode     = PhoneMode::Connected;
        Tethering tetheringMode = Tethering::Off;
    };
} // namespace sys::phone_modes

M module-sys/Service/Common.hpp => module-sys/Service/Common.hpp +4 -1
@@ 21,7 21,8 @@ namespace sys
        ServiceFotaNotifications,
        AntennaNotifications,
        ServiceEvtmgrNotifications,
        CalendarNotifications
        CalendarNotifications,
        PhoneModeChanges,
    };

    enum class ServicePriority


@@ 111,6 112,8 @@ inline const char *c_str(sys::BusChannel channel)
        return "ServiceEvtmgrNotifications";
    case sys::BusChannel::CalendarNotifications:
        return "CalendarNotifications";
    case sys::BusChannel::PhoneModeChanges:
        return "PhoneModeChanges";
    }
    return "";
}

M module-sys/SystemManager/SystemManager.cpp => module-sys/SystemManager/SystemManager.cpp +37 -6
@@ 25,12 25,20 @@
#include "messages/DeviceRegistrationMessage.hpp"
#include "messages/SentinelRegistrationMessage.hpp"
#include "messages/RequestCpuFrequencyMessage.hpp"
#include "messages/PhoneModeRequest.hpp"
#include <time/ScopedTime.hpp>

const inline size_t systemManagerStack = 4096 * 2;

namespace sys
{
    namespace
    {
        const std::map<bsp::KeyCodes, phone_modes::PhoneMode> SliderStateToPhoneModeMapping = {
            {bsp::KeyCodes::SSwitchUp, phone_modes::PhoneMode::Connected},
            {bsp::KeyCodes::SSwitchMid, phone_modes::PhoneMode::DoNotDisturb},
            {bsp::KeyCodes::SSwitchDown, phone_modes::PhoneMode::Offline}};
    } // namespace

    using namespace cpp_freertos;
    using namespace std;


@@ 146,6 154,7 @@ namespace sys
        powerManager  = std::make_unique<PowerManager>();
        cpuStatistics = std::make_unique<CpuStatistics>();
        deviceManager = std::make_unique<DeviceManager>();
        phoneModeSubject = std::make_unique<phone_modes::Subject>(this);

        systemInit = std::move(sysInit);
        userInit   = std::move(appSpaceInit);


@@ 286,17 295,17 @@ namespace sys
            return MessageNone{};
        });

        connect(sevm::KbdMessage(), [&](Message *) {
            // we are in shutdown mode - we received that there was red key pressed -> we need to reboot
            if (state == State::Shutdown) {
                set(State::Reboot);
        connect(sevm::BatteryStatusChangeMessage(), [&](Message *) {
            if ((state == State::Shutdown) && (Store::Battery::get().state == Store::Battery::State::Discharging)) {
                set(State::ShutdownReady);
            }
            return MessageNone{};
        });

        connect(sevm::BatteryPlugMessage(), [&](Message *) {
        connect(sevm::KbdMessage(), [&](Message *) {
            // we are in shutdown mode - we received that there was red key pressed -> we need to reboot
            if (state == State::Shutdown) {
                set(State::ShutdownReady);
                set(State::Reboot);
            }
            return MessageNone{};
        });


@@ 370,6 379,11 @@ namespace sys
            return sys::MessageNone{};
        });

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

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

        return ReturnCodes::Success;


@@ 476,6 490,23 @@ namespace sys
        powerManager->UpdateCpuFrequency(cpuStatistics->GetPercentageCpuLoad());
    }

    phone_modes::PhoneMode SystemManager::translateSliderState(const RawKey &key)
    {
        const auto code = key.key_code;
        if (code != bsp::KeyCodes::SSwitchUp && code != bsp::KeyCodes::SSwitchMid &&
            code != bsp::KeyCodes::SSwitchDown) {
            throw std::invalid_argument{"Invalid key code passed."};
        }
        return SliderStateToPhoneModeMapping.at(code);
    }

    MessagePointer SystemManager::handlePhoneModeRequest(PhoneModeRequest *request)
    {
        LOG_INFO("Phone mode change requested.");
        phoneModeSubject->setPhoneMode(request->getPhoneMode());
        return MessageNone{};
    }

    std::vector<std::shared_ptr<Service>> SystemManager::servicesList;
    cpp_freertos::MutexStandard SystemManager::destroyMutex;
    std::unique_ptr<PowerManager> SystemManager::powerManager;

M module-sys/SystemManager/SystemManager.hpp => module-sys/SystemManager/SystemManager.hpp +12 -0
@@ 19,6 19,8 @@
#include "Service/Service.hpp"
#include "Service/ServiceCreator.hpp"
#include "PowerManager.hpp"
#include "PhoneModes/Subject.hpp"
#include <common_data/RawKey.hpp>
#include "Constants.hpp"
#include "CpuStatistics.hpp"
#include "DeviceManager.hpp"


@@ 34,6 36,8 @@ namespace sys
        inline constexpr std::chrono::milliseconds timerPeriodInterval{100ms};
    } // namespace constants

    class PhoneModeRequest; // Forward declaration

    enum class Code
    {
        CloseSystem,


@@ 92,6 96,11 @@ namespace sys
        /// @note there is no fallback
        static bool DestroyService(const std::string &name, Service *caller, TickType_t timeout = 5000);

        /// Translates a slider state into a phone mode.
        /// \param key  Slider button state
        /// \return Phone mode.
        static phone_modes::PhoneMode translateSliderState(const RawKey &key);

        /// Kill service
        /// @note - this is final, it straight takes service, calls it's close callback and it's gone
        /// please mind that services & apps not registered in SystemManager cant be killed - these should be handled by


@@ 143,10 152,13 @@ namespace sys
        /// periodic update of cpu statistics
        void CpuStatisticsTimerHandler();

        MessagePointer handlePhoneModeRequest(PhoneModeRequest *request);

        bool cpuStatisticsTimerInit{false};

        std::vector<std::unique_ptr<BaseServiceCreator>> systemServiceCreators;
        std::unique_ptr<sys::Timer> cpuStatisticsTimer;
        std::unique_ptr<phone_modes::Subject> phoneModeSubject;
        InitFunction userInit;
        InitFunction systemInit;


A module-sys/SystemManager/doc/PowerManagement.md => module-sys/SystemManager/doc/PowerManagement.md +65 -0
@@ 0,0 1,65 @@
# Power Management

Power management is based on the frequency control of the CPU. 

## CPU frequency switching

The CPU frequency control is dependent on the CPU load measured from the time spent in the task Idle. This control is also influenced by requests from interested services via sentinels (`minFrequencyRequested`). By increasing the CPU frequency, we always jump to the maximum possible frequency value. On the other hand, the reduction of the CPU frequency occurs gradually one level down.

![](./data/CpuFreqChangeAlgorithm.svg)

## Low Power synchronization

Synchronization in Low Power mode covers 3 issues:

* immediate informing services about changing the CPU frequency so that they can update their resources (e.g. PWM filling)

* immediate switching on of peripherals that have been turned off by Low Power control (e.g. PLL2)

* request from the service of the minimum CPU frequency in order to perform a task (e.g. screen redraw, telephone conversation)

### Implementation 

To cover these issues, several classes have been implemented.

* Sentinel class

Each sentinel manages the requests, i.e. when it is needed it sends messages to `CpuGovernor` with the required minimum CPU frequency to perform the task (e.g. screen redraw). Furthermore, every sentinel is informed immediately after changing the frequency. This allows it to invoke a callback to the service to update its resources (e.g. PWM filling). Every sentinel must register itself on startup to `CpuGovernor` by sending `SentinelRegistrationMessage`.

* CpuGovernor class

`CpuGovernor` manages all sentinels in the system and has CPU frequency requests from them (e.g. `eInkSentinel`).

* Driver class

Dedicated drivers control the clocks. For example, `driverLPSPI` enables/disables the clock from LPSPI.

* PLL2 class

DriverPLL2 controls the PLL2 bus clock and if all dependent clocks are turned off, it turns off the PLL2 bus clock.

* Device class

Each driver (eg LPSPI) inherits from the Device class. Therefore, we can manage all drivers. Every device must register itself on startup to `DeviceManager` by sending `DeviceRegistrationMessage`.

* DeviceManager class

`DeviceManager` manages all devices on the system. It can turn off all devices and thus dedicated clocks.

### CPU frequency update

`PowerManager` periodically checks the CPU load and queries `CpuGovernor` to determine the conditions for changing the CPU frequency.
After changing CPU frequency, all sentinels must be synchronously updated (e.g. `eInkSentinel`, `cellularSentinel`) so that they can update their resources (e.g. PWM fill)

![](./data/CpuFrequencyUpdate.svg)

### Resource request

Below is an example of requesting resources from `service_eInk` to redraw the screen:

![](./data/eInkResourceRequest.svg)

and requesting resources from `service_cellular` to make a phone call:

![](./data/cellularResourceRequest.svg)


A module-sys/SystemManager/doc/data/CpuFreqChangeAlgorithm.puml => module-sys/SystemManager/doc/data/CpuFreqChangeAlgorithm.puml +40 -0
@@ 0,0 1,40 @@
@startuml
start

if (cpuLoad > frequencyShiftUpperThreshold) then (yes) 
  : aboveThresholdCounter++
  belowThresholdCounter = 0;
else (no)
  if (cpuLoad < frequencyShiftLowerThreshold) then (yes)
    : belowThresholdCounter++
    aboveThresholdCounter = 0;
  else (no)
  	: belowThresholdCounter = 0
    aboveThresholdCounter = 0;  
  endif
endif

if (
   aboveThresholdCounter >= maxAboveThresholdCount
   or
   minFrequencyRequested > currentCpuFrequency
   ) then (yes)
  : IncreaseCpuFrequency()
  
  belowThresholdCounter = 0
  aboveThresholdCounter = 0;
else (no) 
   if (
   belowThresholdCounter >= maxBelowThresholdCount
   and
   currentCpuFreq > minFrequencyRequested
   ) then (yes)
  : DecreaseCpuFrequency()
  
  belowThresholdCounter = 0
  aboveThresholdCounter = 0;
  endif
endif

stop
@enduml
\ No newline at end of file

A module-sys/SystemManager/doc/data/CpuFreqChangeAlgorithm.svg => module-sys/SystemManager/doc/data/CpuFreqChangeAlgorithm.svg +52 -0
@@ 0,0 1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="611px" preserveAspectRatio="none" style="width:723px;height:611px;" version="1.1" viewBox="0 0 723 611" width="723px" zoomAndPan="magnify"><defs><filter height="300%" id="f4w1gyh31egl9" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><ellipse cx="289.75" cy="20" fill="#000000" filter="url(#f4w1gyh31egl9)" rx="10" ry="10" style="stroke: none; stroke-width: 1.0;"/><polygon fill="#FEFECE" filter="url(#f4w1gyh31egl9)" points="171.25,50,408.25,50,420.25,62,408.25,74,171.25,74,159.25,62,171.25,50" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="237" x="171.25" y="65.8081">cpuLoad &gt; frequencyShiftUpperThreshold</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="20" x="139.25" y="59.4058">yes</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="14" x="420.25" y="59.4058">no</text><rect fill="#FEFECE" filter="url(#f4w1gyh31egl9)" height="47.9375" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="194" x="10" y="84"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="170" x="24" y="105.1387">aboveThresholdCounter++</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="174" x="20" y="119.1074">belowThresholdCounter = 0</text><polygon fill="#FEFECE" filter="url(#f4w1gyh31egl9)" points="354,84,591,84,603,96,591,108,354,108,342,96,354,84" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="237" x="354" y="99.8081">cpuLoad &lt; frequencyShiftLowerThreshold</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="20" x="322" y="93.4058">yes</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="14" x="603" y="93.4058">no</text><rect fill="#FEFECE" filter="url(#f4w1gyh31egl9)" height="47.9375" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="196" x="234" y="118"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="168" x="248" y="139.1387">belowThresholdCounter++</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="176" x="244" y="153.1074">aboveThresholdCounter = 0</text><rect fill="#FEFECE" filter="url(#f4w1gyh31egl9)" height="47.9375" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="198" x="514" y="118"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="174" x="528" y="139.1387">belowThresholdCounter = 0</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="176" x="524" y="153.1074">aboveThresholdCounter = 0</text><polygon fill="#FEFECE" filter="url(#f4w1gyh31egl9)" points="472.5,171.9375,484.5,183.9375,472.5,195.9375,460.5,183.9375,472.5,171.9375" style="stroke: #A80036; stroke-width: 1.5;"/><polygon fill="#FEFECE" filter="url(#f4w1gyh31egl9)" points="289.75,201.9375,301.75,213.9375,289.75,225.9375,277.75,213.9375,289.75,201.9375" style="stroke: #A80036; stroke-width: 1.5;"/><polygon fill="#FEFECE" filter="url(#f4w1gyh31egl9)" points="134.75,245.9375,444.75,245.9375,456.75,277.9492,444.75,309.9609,134.75,309.9609,122.75,277.9492,134.75,245.9375" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="0" x="138.75" y="256.1479"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="310" x="134.75" y="268.9526">aboveThresholdCounter &gt;= maxAboveThresholdCount</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="12" x="134.75" y="281.7573">or</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="277" x="134.75" y="294.562">minFrequencyRequested &gt; currentCpuFrequency</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="0" x="138.75" y="307.3667"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="20" x="102.75" y="275.355">yes</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="14" x="456.75" y="275.355">no</text><rect fill="#FEFECE" filter="url(#f4w1gyh31egl9)" height="75.875" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="196" x="14.75" y="319.9609"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="153" x="28.75" y="341.0996">IncreaseCpuFrequency()</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="0" x="28.75" y="355.0684"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="174" x="24.75" y="369.0371">belowThresholdCounter = 0</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="176" x="24.75" y="383.0059">aboveThresholdCounter = 0</text><rect fill="#FEFECE" filter="url(#f4w1gyh31egl9)" height="75.875" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="196" x="368.75" y="408.3867"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="159" x="382.75" y="429.5254">DecreaseCpuFrequency()</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="0" x="382.75" y="443.4941"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="174" x="378.75" y="457.4629">belowThresholdCounter = 0</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="176" x="378.75" y="471.4316">aboveThresholdCounter = 0</text><polygon fill="#FEFECE" filter="url(#f4w1gyh31egl9)" points="312.25,319.9609,621.25,319.9609,633.25,351.9727,621.25,383.9844,312.25,383.9844,300.25,351.9727,312.25,319.9609" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="20" x="470.75" y="394.1948">yes</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="0" x="316.25" y="330.1714"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="309" x="312.25" y="342.9761">belowThresholdCounter &gt;= maxBelowThresholdCount</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="21" x="312.25" y="355.7808">and</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="244" x="312.25" y="368.5854">currentCpuFreq &gt; minFrequencyRequested</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="0" x="316.25" y="381.3901"/><polygon fill="#FEFECE" filter="url(#f4w1gyh31egl9)" points="466.75,504.2617,478.75,516.2617,466.75,528.2617,454.75,516.2617,466.75,504.2617" style="stroke: #A80036; stroke-width: 1.5;"/><polygon fill="#FEFECE" filter="url(#f4w1gyh31egl9)" points="289.75,534.2617,301.75,546.2617,289.75,558.2617,277.75,546.2617,289.75,534.2617" style="stroke: #A80036; stroke-width: 1.5;"/><ellipse cx="289.75" cy="589.2617" fill="#FFFFFF" filter="url(#f4w1gyh31egl9)" rx="11" ry="11" style="stroke: #000000; stroke-width: 1.0;"/><ellipse cx="289.75" cy="589.2617" fill="#000000" rx="6" ry="6" style="stroke: #7F7F7F; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="342" x2="332" y1="96" y2="96"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="332" x2="332" y1="96" y2="118"/><polygon fill="#A80036" points="328,108,332,118,336,108,332,112" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="603" x2="613" y1="96" y2="96"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="613" x2="613" y1="96" y2="118"/><polygon fill="#A80036" points="609,108,613,118,617,108,613,112" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="332" x2="332" y1="165.9375" y2="183.9375"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="332" x2="460.5" y1="183.9375" y2="183.9375"/><polygon fill="#A80036" points="450.5,179.9375,460.5,183.9375,450.5,187.9375,454.5,183.9375" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="613" x2="613" y1="165.9375" y2="183.9375"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="613" x2="484.5" y1="183.9375" y2="183.9375"/><polygon fill="#A80036" points="494.5,179.9375,484.5,183.9375,494.5,187.9375,490.5,183.9375" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="159.25" x2="107" y1="62" y2="62"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="107" x2="107" y1="62" y2="84"/><polygon fill="#A80036" points="103,74,107,84,111,74,107,78" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="420.25" x2="472.5" y1="62" y2="62"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="472.5" x2="472.5" y1="62" y2="84"/><polygon fill="#A80036" points="468.5,74,472.5,84,476.5,74,472.5,78" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="107" x2="107" y1="131.9375" y2="213.9375"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="107" x2="277.75" y1="213.9375" y2="213.9375"/><polygon fill="#A80036" points="267.75,209.9375,277.75,213.9375,267.75,217.9375,271.75,213.9375" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="472.5" x2="472.5" y1="195.9375" y2="213.9375"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="472.5" x2="301.75" y1="213.9375" y2="213.9375"/><polygon fill="#A80036" points="311.75,209.9375,301.75,213.9375,311.75,217.9375,307.75,213.9375" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="289.75" x2="289.75" y1="30" y2="50"/><polygon fill="#A80036" points="285.75,40,289.75,50,293.75,40,289.75,44" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="466.75" x2="466.75" y1="383.9844" y2="408.3867"/><polygon fill="#A80036" points="462.75,398.3867,466.75,408.3867,470.75,398.3867,466.75,402.3867" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="633.25" x2="645.25" y1="351.9727" y2="351.9727"/><polygon fill="#A80036" points="641.25,426.3184,645.25,436.3184,649.25,426.3184,645.25,430.3184" style="stroke: #A80036; stroke-width: 1.5;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="645.25" x2="645.25" y1="351.9727" y2="516.2617"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="645.25" x2="478.75" y1="516.2617" y2="516.2617"/><polygon fill="#A80036" points="488.75,512.2617,478.75,516.2617,488.75,520.2617,484.75,516.2617" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="466.75" x2="466.75" y1="484.2617" y2="504.2617"/><polygon fill="#A80036" points="462.75,494.2617,466.75,504.2617,470.75,494.2617,466.75,498.2617" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="122.75" x2="112.75" y1="277.9492" y2="277.9492"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="112.75" x2="112.75" y1="277.9492" y2="319.9609"/><polygon fill="#A80036" points="108.75,309.9609,112.75,319.9609,116.75,309.9609,112.75,313.9609" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="456.75" x2="466.75" y1="277.9492" y2="277.9492"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="466.75" x2="466.75" y1="277.9492" y2="319.9609"/><polygon fill="#A80036" points="462.75,309.9609,466.75,319.9609,470.75,309.9609,466.75,313.9609" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="112.75" x2="112.75" y1="395.8359" y2="546.2617"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="112.75" x2="277.75" y1="546.2617" y2="546.2617"/><polygon fill="#A80036" points="267.75,542.2617,277.75,546.2617,267.75,550.2617,271.75,546.2617" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="466.75" x2="466.75" y1="528.2617" y2="546.2617"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="466.75" x2="301.75" y1="546.2617" y2="546.2617"/><polygon fill="#A80036" points="311.75,542.2617,301.75,546.2617,311.75,550.2617,307.75,546.2617" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="289.75" x2="289.75" y1="225.9375" y2="245.9375"/><polygon fill="#A80036" points="285.75,235.9375,289.75,245.9375,293.75,235.9375,289.75,239.9375" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="289.75" x2="289.75" y1="558.2617" y2="578.2617"/><polygon fill="#A80036" points="285.75,568.2617,289.75,578.2617,293.75,568.2617,289.75,572.2617" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[ee2983799085d65f96fa6af7dcfc3842]
@startuml
start

if (cpuLoad > frequencyShiftUpperThreshold) then (yes)
: aboveThresholdCounter++
belowThresholdCounter = 0;
else (no)
if (cpuLoad < frequencyShiftLowerThreshold) then (yes)
: belowThresholdCounter++
aboveThresholdCounter = 0;
else (no)
: belowThresholdCounter = 0
aboveThresholdCounter = 0;
endif
endif

if (
aboveThresholdCounter >= maxAboveThresholdCount
or
minFrequencyRequested > currentCpuFrequency
) then (yes)
: IncreaseCpuFrequency()

belowThresholdCounter = 0
aboveThresholdCounter = 0;
else (no)
if (
belowThresholdCounter >= maxBelowThresholdCount
and
currentCpuFreq > minFrequencyRequested
) then (yes)
: DecreaseCpuFrequency()

belowThresholdCounter = 0
aboveThresholdCounter = 0;
endif
endif

stop
@enduml

PlantUML version 1.2019.11(Sun Sep 22 12:02:15 CEST 2019)
(EPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Java Version: 14.0.2+12-46
Operating System: Linux
Default Encoding: UTF-8
Language: pl
Country: PL
--></g></svg>
\ No newline at end of file

A module-sys/SystemManager/doc/data/CpuFrequencyUpdate.puml => module-sys/SystemManager/doc/data/CpuFrequencyUpdate.puml +19 -0
@@ 0,0 1,19 @@
@startuml

PowerManager -> CpuGovernor : GetMinimumFrequencyRequested
CpuGovernor --> PowerManager : minimumFrequency

...CPU frequency change...

PowerManager -> CpuGovernor : CpuFrequencyHasChanged

CpuGovernor -> cellularSentinel : CpuFrequencyHasChanged

CpuGovernor -> eInkSentinel : CpuFrequencyHasChanged
eInkSentinel -> DriverPWM : updatePWM
note right
Critical section!
mutex necessary
end note

@enduml

A module-sys/SystemManager/doc/data/CpuFrequencyUpdate.svg => module-sys/SystemManager/doc/data/CpuFrequencyUpdate.svg +31 -0
@@ 0,0 1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="339px" preserveAspectRatio="none" style="width:883px;height:339px;" version="1.1" viewBox="0 0 883 339" width="883px" zoomAndPan="magnify"><defs><filter height="300%" id="f4w29l22zrjdz" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><line style="stroke: #A80036; stroke-width: 1.0;" x1="68" x2="68" y1="38.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="68" x2="68" y1="106.5625" y2="147.3672"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="68" x2="68" y1="147.3672" y2="299.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="308.5" x2="308.5" y1="38.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="308.5" x2="308.5" y1="106.5625" y2="147.3672"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="308.5" x2="308.5" y1="147.3672" y2="299.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="505.5" x2="505.5" y1="38.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="505.5" x2="505.5" y1="106.5625" y2="147.3672"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="505.5" x2="505.5" y1="147.3672" y2="299.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="627.5" x2="627.5" y1="38.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="627.5" x2="627.5" y1="106.5625" y2="147.3672"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="627.5" x2="627.5" y1="147.3672" y2="299.0313"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="735.5" x2="735.5" y1="38.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="735.5" x2="735.5" y1="106.5625" y2="147.3672"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="735.5" x2="735.5" y1="147.3672" y2="299.0313"/><rect fill="#FEFECE" filter="url(#f4w29l22zrjdz)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="8" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="103" x="15" y="22.9951">PowerManager</text><rect fill="#FEFECE" filter="url(#f4w29l22zrjdz)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="8" y="298.0313"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="103" x="15" y="318.0264">PowerManager</text><rect fill="#FEFECE" filter="url(#f4w29l22zrjdz)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="106" x="253.5" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="92" x="260.5" y="22.9951">CpuGovernor</text><rect fill="#FEFECE" filter="url(#f4w29l22zrjdz)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="106" x="253.5" y="298.0313"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="92" x="260.5" y="318.0264">CpuGovernor</text><rect fill="#FEFECE" filter="url(#f4w29l22zrjdz)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="118" x="444.5" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="104" x="451.5" y="22.9951">cellularSentinel</text><rect fill="#FEFECE" filter="url(#f4w29l22zrjdz)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="118" x="444.5" y="298.0313"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="104" x="451.5" y="318.0264">cellularSentinel</text><rect fill="#FEFECE" filter="url(#f4w29l22zrjdz)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="99" x="576.5" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="583.5" y="22.9951">eInkSentinel</text><rect fill="#FEFECE" filter="url(#f4w29l22zrjdz)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="99" x="576.5" y="298.0313"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="583.5" y="318.0264">eInkSentinel</text><rect fill="#FEFECE" filter="url(#f4w29l22zrjdz)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="88" x="689.5" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="74" x="696.5" y="22.9951">DriverPWM</text><rect fill="#FEFECE" filter="url(#f4w29l22zrjdz)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="88" x="689.5" y="298.0313"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="74" x="696.5" y="318.0264">DriverPWM</text><polygon fill="#A80036" points="296.5,65.4297,306.5,69.4297,296.5,73.4297,300.5,69.4297" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="68.5" x2="302.5" y1="69.4297" y2="69.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="216" x="75.5" y="64.3638">GetMinimumFrequencyRequested</text><polygon fill="#A80036" points="79.5,94.5625,69.5,98.5625,79.5,102.5625,75.5,98.5625" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 2.0,2.0;" x1="73.5" x2="307.5" y1="98.5625" y2="98.5625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="127" x="85.5" y="93.4966">minimumFrequency</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="128" x="338" y="130.7729">CPU frequency change</text><polygon fill="#A80036" points="296.5,164.5,306.5,168.5,296.5,172.5,300.5,168.5" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="68.5" x2="302.5" y1="168.5" y2="168.5"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="173" x="75.5" y="163.4341">CpuFrequencyHasChanged</text><polygon fill="#A80036" points="493.5,193.6328,503.5,197.6328,493.5,201.6328,497.5,197.6328" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="308.5" x2="499.5" y1="197.6328" y2="197.6328"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="173" x="315.5" y="192.5669">CpuFrequencyHasChanged</text><polygon fill="#A80036" points="616,222.7656,626,226.7656,616,230.7656,620,226.7656" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="308.5" x2="622" y1="226.7656" y2="226.7656"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="173" x="315.5" y="221.6997">CpuFrequencyHasChanged</text><polygon fill="#A80036" points="723.5,264.4648,733.5,268.4648,723.5,272.4648,727.5,268.4648" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="628" x2="729.5" y1="268.4648" y2="268.4648"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="75" x="635" y="263.3989">updatePWM</text><path d="M740,239.7656 L740,279.7656 L871,279.7656 L871,249.7656 L861,239.7656 L740,239.7656 " fill="#FBFB77" filter="url(#f4w29l22zrjdz)" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M861,239.7656 L861,249.7656 L871,249.7656 L861,239.7656 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="98" x="746" y="256.8325">Critical section!</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="110" x="746" y="271.9653">mutex necessary</text><!--MD5=[6c4788b841e800cf870b69c0ce829c52]
@startuml

PowerManager -> CpuGovernor : GetMinimumFrequencyRequested
CpuGovernor - -> PowerManager : minimumFrequency

...CPU frequency change...

PowerManager -> CpuGovernor : CpuFrequencyHasChanged

CpuGovernor -> cellularSentinel : CpuFrequencyHasChanged

CpuGovernor -> eInkSentinel : CpuFrequencyHasChanged
eInkSentinel -> DriverPWM : updatePWM
note right
Critical section!
mutex necessary
end note

@enduml

PlantUML version 1.2019.11(Sun Sep 22 12:02:15 CEST 2019)
(EPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Java Version: 14.0.2+12-46
Operating System: Linux
Default Encoding: UTF-8
Language: pl
Country: PL
--></g></svg>
\ No newline at end of file

A module-sys/SystemManager/doc/data/ResourceRequest.puml => module-sys/SystemManager/doc/data/ResourceRequest.puml +70 -0
@@ 0,0 1,70 @@
@startuml

service_eInk -> eInkSentinel : SetFrequencyValueRequest
eInkSentinel -> PowerManager : RequestCpuFrequencyMessage

PowerManager -> CpuGovernor : SetCpuFrequencyRequest

service_eInk -> EinkDisplay : powerOn
EinkDisplay -> driverLPSPI : Enable

driverLPSPI -> driverPLL2 : Pll2ClockEnable
note right
Critical section!
mutex necessary
end note

driverLPSPI -> clock_config : LPSPIClockEnable

...screen redraw...
 
EinkDisplay -> driverLPSPI : Disable
 
driverLPSPI -> clock_config : LPSPIClockDisable

driverLPSPI -> driverPLL2 : Pll2ClockDisable
note right
Critical section!
mutex necessary
end note

service_eInk -> eInkSentinel : ResetCpuFrequencyRequest
eInkSentinel -> PowerManager : RequestCpuFrequencyMessage

PowerManager -> CpuGovernor : ResetCpuFrequencyRequest

@enduml
 



@startuml

service_cellular -> cellularSentinel : SetFrequencyValueRequest
cellularSentinel -> PowerManager : RequestCpuFrequencyMessage

PowerManager -> CpuGovernor : SetCpuFrequencyRequest

service_cellular -> bsp_cellular : wakeUp
bsp_cellular -> driverUART : Enable

driverUART -> clock_config : ExternalOscillatorEnable
note right
Critical section!
mutex necessary
end note

driverUART -> clock_config : UARTClockEnable

...call ended...
 
bsp_cellular -> driverUART : Disable
 
driverUART -> clock_config : UARTClockDisable

service_cellular -> cellularSentinel : ResetCpuFrequencyRequest
cellularSentinel -> PowerManager : RequestCpuFrequencyMessage

PowerManager -> CpuGovernor : ResetCpuFrequencyRequest

@enduml

A module-sys/SystemManager/doc/data/cellularResourceRequest.svg => module-sys/SystemManager/doc/data/cellularResourceRequest.svg +42 -0
@@ 0,0 1,42 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="514px" preserveAspectRatio="none" style="width:1246px;height:514px;" version="1.1" viewBox="0 0 1246 514" width="1246px" zoomAndPan="magnify"><defs><filter height="300%" id="f1tv0e0kd571ez" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><line style="stroke: #A80036; stroke-width: 1.0;" x1="69" x2="69" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="69" x2="69" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="69" x2="69" y1="318.1641" y2="473.8281"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="272" x2="272" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="272" x2="272" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="272" x2="272" y1="318.1641" y2="473.8281"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="495.5" x2="495.5" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="495.5" x2="495.5" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="495.5" x2="495.5" y1="318.1641" y2="473.8281"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="699" x2="699" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="699" x2="699" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="699" x2="699" y1="318.1641" y2="473.8281"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="813" x2="813" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="813" x2="813" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="813" x2="813" y1="318.1641" y2="473.8281"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="920" x2="920" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="920" x2="920" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="920" x2="920" y1="318.1641" y2="473.8281"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="1097.5" x2="1097.5" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="1097.5" x2="1097.5" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="1097.5" x2="1097.5" y1="318.1641" y2="473.8281"/><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="118" x="8" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="104" x="15" y="22.9951">service_cellular</text><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="118" x="8" y="472.8281"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="104" x="15" y="492.8232">service_cellular</text><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="118" x="211" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="104" x="218" y="22.9951">cellularSentinel</text><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="118" x="211" y="472.8281"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="104" x="218" y="492.8232">cellularSentinel</text><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="435.5" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="103" x="442.5" y="22.9951">PowerManager</text><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="435.5" y="472.8281"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="103" x="442.5" y="492.8232">PowerManager</text><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="106" x="644" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="92" x="651" y="22.9951">CpuGovernor</text><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="106" x="644" y="472.8281"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="92" x="651" y="492.8232">CpuGovernor</text><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="95" x="764" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="81" x="771" y="22.9951">bsp_cellular</text><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="95" x="764" y="472.8281"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="81" x="771" y="492.8232">bsp_cellular</text><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="90" x="873" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="76" x="880" y="22.9951">driverUART</text><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="90" x="873" y="472.8281"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="76" x="880" y="492.8232">driverUART</text><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="99" x="1046.5" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="1053.5" y="22.9951">clock_config</text><rect fill="#FEFECE" filter="url(#f1tv0e0kd571ez)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="99" x="1046.5" y="472.8281"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="1053.5" y="492.8232">clock_config</text><polygon fill="#A80036" points="260,65.4297,270,69.4297,260,73.4297,264,69.4297" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="69" x2="266" y1="69.4297" y2="69.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="176" x="76" y="64.3638">SetFrequencyValueRequest</text><polygon fill="#A80036" points="484,94.5625,494,98.5625,484,102.5625,488,98.5625" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="272" x2="490" y1="98.5625" y2="98.5625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="200" x="279" y="93.4966">RequestCpuFrequencyMessage</text><polygon fill="#A80036" points="687,123.6953,697,127.6953,687,131.6953,691,127.6953" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="496" x2="693" y1="127.6953" y2="127.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="165" x="503" y="122.6294">SetCpuFrequencyRequest</text><polygon fill="#A80036" points="801.5,152.8281,811.5,156.8281,801.5,160.8281,805.5,156.8281" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="69" x2="807.5" y1="156.8281" y2="156.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="50" x="76" y="151.7622">wakeUp</text><polygon fill="#A80036" points="908,181.9609,918,185.9609,908,189.9609,912,185.9609" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="813.5" x2="914" y1="185.9609" y2="185.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="43" x="820.5" y="180.895">Enable</text><polygon fill="#A80036" points="1086,223.6602,1096,227.6602,1086,231.6602,1090,227.6602" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="920" x2="1092" y1="227.6602" y2="227.6602"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="154" x="927" y="222.5942">ExternalOscillatorEnable</text><path d="M1103,198.9609 L1103,238.9609 L1234,238.9609 L1234,208.9609 L1224,198.9609 L1103,198.9609 " fill="#FBFB77" filter="url(#f1tv0e0kd571ez)" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M1224,198.9609 L1224,208.9609 L1234,208.9609 L1224,198.9609 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="98" x="1109" y="216.0278">Critical section!</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="110" x="1109" y="231.1606">mutex necessary</text><polygon fill="#A80036" points="1086,265.3594,1096,269.3594,1086,273.3594,1090,269.3594" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="920" x2="1092" y1="269.3594" y2="269.3594"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="111" x="927" y="264.2935">UARTClockEnable</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="58" x="554.5" y="301.5698">call ended</text><polygon fill="#A80036" points="908,335.2969,918,339.2969,908,343.2969,912,339.2969" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="813.5" x2="914" y1="339.2969" y2="339.2969"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="47" x="820.5" y="334.231">Disable</text><polygon fill="#A80036" points="1086,364.4297,1096,368.4297,1086,372.4297,1090,368.4297" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="920" x2="1092" y1="368.4297" y2="368.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="115" x="927" y="363.3638">UARTClockDisable</text><polygon fill="#A80036" points="260,393.5625,270,397.5625,260,401.5625,264,397.5625" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="69" x2="266" y1="397.5625" y2="397.5625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="179" x="76" y="392.4966">ResetCpuFrequencyRequest</text><polygon fill="#A80036" points="484,422.6953,494,426.6953,484,430.6953,488,426.6953" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="272" x2="490" y1="426.6953" y2="426.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="200" x="279" y="421.6294">RequestCpuFrequencyMessage</text><polygon fill="#A80036" points="687,451.8281,697,455.8281,687,459.8281,691,455.8281" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="496" x2="693" y1="455.8281" y2="455.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="179" x="503" y="450.7622">ResetCpuFrequencyRequest</text><!--MD5=[6fcf4d81c1fbb048db22def46a2b620c]
@startuml

service_cellular -> cellularSentinel : SetFrequencyValueRequest
cellularSentinel -> PowerManager : RequestCpuFrequencyMessage

PowerManager -> CpuGovernor : SetCpuFrequencyRequest

service_cellular -> bsp_cellular : wakeUp
bsp_cellular -> driverUART : Enable

driverUART -> clock_config : ExternalOscillatorEnable
note right
Critical section!
mutex necessary
end note

driverUART -> clock_config : UARTClockEnable

...call ended...

bsp_cellular -> driverUART : Disable

driverUART -> clock_config : UARTClockDisable

service_cellular -> cellularSentinel : ResetCpuFrequencyRequest
cellularSentinel -> PowerManager : RequestCpuFrequencyMessage

PowerManager -> CpuGovernor : ResetCpuFrequencyRequest

@enduml

PlantUML version 1.2019.11(Sun Sep 22 12:02:15 CEST 2019)
(EPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Java Version: 14.0.2+12-46
Operating System: Linux
Default Encoding: UTF-8
Language: pl
Country: PL
--></g></svg>
\ No newline at end of file

A module-sys/SystemManager/doc/data/eInkResourceRequest.svg => module-sys/SystemManager/doc/data/eInkResourceRequest.svg +48 -0
@@ 0,0 1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="568px" preserveAspectRatio="none" style="width:1198px;height:568px;" version="1.1" viewBox="0 0 1198 568" width="1198px" zoomAndPan="magnify"><defs><filter height="300%" id="fcbnkjoqvuo08" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><line style="stroke: #A80036; stroke-width: 1.0;" x1="59" x2="59" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="59" x2="59" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="59" x2="59" y1="318.1641" y2="528.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="262" x2="262" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="262" x2="262" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="262" x2="262" y1="318.1641" y2="528.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="486" x2="486" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="486" x2="486" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="486" x2="486" y1="318.1641" y2="528.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="689.5" x2="689.5" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="689.5" x2="689.5" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="689.5" x2="689.5" y1="318.1641" y2="528.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="802.5" x2="802.5" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="802.5" x2="802.5" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="802.5" x2="802.5" y1="318.1641" y2="528.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="906.5" x2="906.5" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="906.5" x2="906.5" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="906.5" x2="906.5" y1="318.1641" y2="528.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="1034" x2="1034" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="1034" x2="1034" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="1034" x2="1034" y1="318.1641" y2="528.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="1139" x2="1139" y1="38.2969" y2="277.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="1139" x2="1139" y1="277.3594" y2="318.1641"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="1139" x2="1139" y1="318.1641" y2="528.0938"/><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="99" x="8" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="15" y="22.9951">service_eInk</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="99" x="8" y="527.0938"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="15" y="547.0889">service_eInk</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="99" x="211" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="218" y="22.9951">eInkSentinel</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="99" x="211" y="527.0938"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="218" y="547.0889">eInkSentinel</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="426" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="103" x="433" y="22.9951">PowerManager</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="426" y="527.0938"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="103" x="433" y="547.0889">PowerManager</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="106" x="634.5" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="92" x="641.5" y="22.9951">CpuGovernor</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="106" x="634.5" y="527.0938"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="92" x="641.5" y="547.0889">CpuGovernor</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="92" x="754.5" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="78" x="761.5" y="22.9951">EinkDisplay</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="92" x="754.5" y="527.0938"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="78" x="761.5" y="547.0889">EinkDisplay</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="89" x="860.5" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="75" x="867.5" y="22.9951">driverLPSPI</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="89" x="860.5" y="527.0938"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="75" x="867.5" y="547.0889">driverLPSPI</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="84" x="990" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="70" x="997" y="22.9951">driverPLL2</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="84" x="990" y="527.0938"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="70" x="997" y="547.0889">driverPLL2</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="99" x="1088" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="1095" y="22.9951">clock_config</text><rect fill="#FEFECE" filter="url(#fcbnkjoqvuo08)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="99" x="1088" y="527.0938"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="1095" y="547.0889">clock_config</text><polygon fill="#A80036" points="250.5,65.4297,260.5,69.4297,250.5,73.4297,254.5,69.4297" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="59.5" x2="256.5" y1="69.4297" y2="69.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="176" x="66.5" y="64.3638">SetFrequencyValueRequest</text><polygon fill="#A80036" points="474.5,94.5625,484.5,98.5625,474.5,102.5625,478.5,98.5625" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="262.5" x2="480.5" y1="98.5625" y2="98.5625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="200" x="269.5" y="93.4966">RequestCpuFrequencyMessage</text><polygon fill="#A80036" points="677.5,123.6953,687.5,127.6953,677.5,131.6953,681.5,127.6953" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="486.5" x2="683.5" y1="127.6953" y2="127.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="165" x="493.5" y="122.6294">SetCpuFrequencyRequest</text><polygon fill="#A80036" points="790.5,152.8281,800.5,156.8281,790.5,160.8281,794.5,156.8281" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="59.5" x2="796.5" y1="156.8281" y2="156.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="56" x="66.5" y="151.7622">powerOn</text><polygon fill="#A80036" points="895,181.9609,905,185.9609,895,189.9609,899,185.9609" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="802.5" x2="901" y1="185.9609" y2="185.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="43" x="809.5" y="180.895">Enable</text><polygon fill="#A80036" points="1022,223.6602,1032,227.6602,1022,231.6602,1026,227.6602" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="907" x2="1028" y1="227.6602" y2="227.6602"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="99" x="914" y="222.5942">Pll2ClockEnable</text><path d="M1039,198.9609 L1039,238.9609 L1170,238.9609 L1170,208.9609 L1160,198.9609 L1039,198.9609 " fill="#FBFB77" filter="url(#fcbnkjoqvuo08)" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M1160,198.9609 L1160,208.9609 L1170,208.9609 L1160,198.9609 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="98" x="1045" y="216.0278">Critical section!</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="110" x="1045" y="231.1606">mutex necessary</text><polygon fill="#A80036" points="1127.5,265.3594,1137.5,269.3594,1127.5,273.3594,1131.5,269.3594" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="907" x2="1133.5" y1="269.3594" y2="269.3594"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="112" x="914" y="264.2935">LPSPIClockEnable</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="83" x="558" y="301.5698">screen redraw</text><polygon fill="#A80036" points="895,335.2969,905,339.2969,895,343.2969,899,339.2969" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="802.5" x2="901" y1="339.2969" y2="339.2969"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="47" x="809.5" y="334.231">Disable</text><polygon fill="#A80036" points="1127.5,364.4297,1137.5,368.4297,1127.5,372.4297,1131.5,368.4297" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="907" x2="1133.5" y1="368.4297" y2="368.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="116" x="914" y="363.3638">LPSPIClockDisable</text><polygon fill="#A80036" points="1022,406.1289,1032,410.1289,1022,414.1289,1026,410.1289" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="907" x2="1028" y1="410.1289" y2="410.1289"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="103" x="914" y="405.063">Pll2ClockDisable</text><path d="M1039,381.4297 L1039,421.4297 L1170,421.4297 L1170,391.4297 L1160,381.4297 L1039,381.4297 " fill="#FBFB77" filter="url(#fcbnkjoqvuo08)" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M1160,381.4297 L1160,391.4297 L1170,391.4297 L1160,381.4297 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="98" x="1045" y="398.4966">Critical section!</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="110" x="1045" y="413.6294">mutex necessary</text><polygon fill="#A80036" points="250.5,447.8281,260.5,451.8281,250.5,455.8281,254.5,451.8281" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="59.5" x2="256.5" y1="451.8281" y2="451.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="179" x="66.5" y="446.7622">ResetCpuFrequencyRequest</text><polygon fill="#A80036" points="474.5,476.9609,484.5,480.9609,474.5,484.9609,478.5,480.9609" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="262.5" x2="480.5" y1="480.9609" y2="480.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="200" x="269.5" y="475.895">RequestCpuFrequencyMessage</text><polygon fill="#A80036" points="677.5,506.0938,687.5,510.0938,677.5,514.0938,681.5,510.0938" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="486.5" x2="683.5" y1="510.0938" y2="510.0938"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="179" x="493.5" y="505.0278">ResetCpuFrequencyRequest</text><!--MD5=[b2ae224b2d638e53bd7e0f72551ef878]
@startuml

service_eInk -> eInkSentinel : SetFrequencyValueRequest
eInkSentinel -> PowerManager : RequestCpuFrequencyMessage

PowerManager -> CpuGovernor : SetCpuFrequencyRequest

service_eInk -> EinkDisplay : powerOn
EinkDisplay -> driverLPSPI : Enable

driverLPSPI -> driverPLL2 : Pll2ClockEnable
note right
Critical section!
mutex necessary
end note

driverLPSPI -> clock_config : LPSPIClockEnable

...screen redraw...

EinkDisplay -> driverLPSPI : Disable

driverLPSPI -> clock_config : LPSPIClockDisable

driverLPSPI -> driverPLL2 : Pll2ClockDisable
note right
Critical section!
mutex necessary
end note

service_eInk -> eInkSentinel : ResetCpuFrequencyRequest
eInkSentinel -> PowerManager : RequestCpuFrequencyMessage

PowerManager -> CpuGovernor : ResetCpuFrequencyRequest

@enduml

PlantUML version 1.2019.11(Sun Sep 22 12:02:15 CEST 2019)
(EPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Java Version: 14.0.2+12-46
Operating System: Linux
Default Encoding: UTF-8
Language: pl
Country: PL
--></g></svg>
\ No newline at end of file

A module-sys/SystemManager/messages/PhoneModeRequest.hpp => module-sys/SystemManager/messages/PhoneModeRequest.hpp +25 -0
@@ 0,0 1,25 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <module-sys/PhoneModes/Common.hpp>

namespace sys
{
    class PhoneModeRequest : public sys::DataMessage
    {
      public:
        explicit PhoneModeRequest(phone_modes::PhoneMode mode)
            : sys::DataMessage(MessageType::MessageTypeUninitialized), mode{mode}
        {}

        [[nodiscard]] auto getPhoneMode() const noexcept -> phone_modes::PhoneMode
        {
            return mode;
        }

      private:
        phone_modes::PhoneMode mode;
    };
} // namespace sys

M module-utils/test/CMakeLists.txt => module-utils/test/CMakeLists.txt +9 -0
@@ 1,5 1,14 @@
cmake_minimum_required(VERSION 3.12)

add_catch2_executable(
    NAME
        utils-conditional-invoke-tests
    SRCS
        test-Utility-ConditionalInvoke.cpp
    LIBS
        module-utils
)

# Phone number tests
add_catch2_executable(
    NAME

A module-utils/test/test-Utility-ConditionalInvoke.cpp => module-utils/test/test-Utility-ConditionalInvoke.cpp +203 -0
@@ 0,0 1,203 @@
// 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 <module-utils/utility/Utility.hpp>

struct SomeStruct
{
    int value;
};

class ExampleClass
{
  public:
    int function1(int x, const SomeStruct &s)
    {
        return x + s.value;
    }

    void function2()
    {}

    int function3(int x, SomeStruct &s)
    {
        return x + s.value;
    }

    static int static_function1(int x, const SomeStruct &s)
    {
        return x + s.value;
    }

    static void static_function2()
    {}

    static int static_function3(int x, SomeStruct &s)
    {
        return x + s.value;
    }
};

int global_function1(int x, const SomeStruct &s)
{
    return x + s.value;
}

void global_function2()
{}

int global_function3(int x, SomeStruct &s)
{
    return x + s.value;
}

TEST_CASE("Successful global function call")
{
    const auto guard = []() { return true; };

    const auto [called1, ret1] = utility::conditionally_invoke(guard, &global_function1, 10, SomeStruct{5});
    REQUIRE(called1 == true);
    REQUIRE(ret1 == 15);

    int x = 10;
    SomeStruct s1{10};
    const auto [called2, ret2] = utility::conditionally_invoke(guard, &global_function1, x, s1);
    REQUIRE(called2 == true);
    REQUIRE(ret2 == 20);

    const auto called3 = utility::conditionally_invoke(guard, &global_function2);
    REQUIRE(called3 == true);

    SomeStruct s2{15};
    const auto [called4, ret4] = utility::conditionally_invoke(guard, &global_function3, 10, std::ref(s2));
    REQUIRE(called4 == true);
    REQUIRE(ret4 == 25);
}

TEST_CASE("Failed global function call")
{
    const auto guard = []() { return false; };

    const auto [called1, ret1] = utility::conditionally_invoke(guard, &global_function1, 10, SomeStruct{5});
    REQUIRE(called1 == false);
    REQUIRE(ret1 == 0);

    int x = 10;
    SomeStruct s1{10};
    const auto [called2, ret2] = utility::conditionally_invoke(guard, &global_function1, x, s1);
    REQUIRE(called2 == false);
    REQUIRE(ret2 == 0);

    const auto called3 = utility::conditionally_invoke(guard, &global_function2);
    REQUIRE(called3 == false);

    SomeStruct s2{15};
    const auto [called4, ret4] = utility::conditionally_invoke(guard, &global_function3, 10, std::ref(s2));
    REQUIRE(called4 == false);
    REQUIRE(ret4 == 0);
}

TEST_CASE("Successful class static function call")
{
    const auto guard = []() { return true; };

    const auto [called1, ret1] =
        utility::conditionally_invoke(guard, &ExampleClass::static_function1, 10, SomeStruct{5});
    REQUIRE(called1 == true);
    REQUIRE(ret1 == 15);

    int x = 10;
    SomeStruct s1{10};
    const auto [called2, ret2] = utility::conditionally_invoke(guard, &ExampleClass::static_function1, x, s1);
    REQUIRE(called2 == true);
    REQUIRE(ret2 == 20);

    const auto called3 = utility::conditionally_invoke(guard, &ExampleClass::static_function2);
    REQUIRE(called3 == true);

    SomeStruct s2{15};
    const auto [called4, ret4] =
        utility::conditionally_invoke(guard, &ExampleClass::static_function3, 10, std::ref(s2));
    REQUIRE(called4 == true);
    REQUIRE(ret4 == 25);
}

TEST_CASE("Failed class static function call")
{
    const auto guard = []() { return false; };

    const auto [called1, ret1] =
        utility::conditionally_invoke(guard, &ExampleClass::static_function1, 10, SomeStruct{5});
    REQUIRE(called1 == false);
    REQUIRE(ret1 == 0);

    int x = 10;
    SomeStruct s1{10};
    const auto [called2, ret2] = utility::conditionally_invoke(guard, &ExampleClass::static_function1, x, s1);
    REQUIRE(called2 == false);
    REQUIRE(ret2 == 0);

    const auto called3 = utility::conditionally_invoke(guard, &ExampleClass::static_function2);
    REQUIRE(called3 == false);

    SomeStruct s2{15};
    const auto [called4, ret4] =
        utility::conditionally_invoke(guard, &ExampleClass::static_function3, 10, std::ref(s2));
    REQUIRE(called4 == false);
    REQUIRE(ret4 == 0);
}

TEST_CASE("Successful class member function call")
{
    ExampleClass instance;
    const auto guard = []() { return true; };

    const auto [called1, ret1] =
        utility::conditionally_invoke(guard, instance, &ExampleClass::function1, 10, SomeStruct{5});
    REQUIRE(called1 == true);
    REQUIRE(ret1 == 15);

    int x = 10;
    SomeStruct s1{10};
    const auto [called2, ret2] = utility::conditionally_invoke(guard, instance, &ExampleClass::function1, x, s1);
    REQUIRE(called2 == true);
    REQUIRE(ret2 == 20);

    const auto called3 = utility::conditionally_invoke(guard, instance, &ExampleClass::function2);
    REQUIRE(called3 == true);

    SomeStruct s2{15};
    const auto [called4, ret4] =
        utility::conditionally_invoke(guard, instance, &ExampleClass::function3, 10, std::ref(s2));
    REQUIRE(called4 == true);
    REQUIRE(ret4 == 25);
}

TEST_CASE("Failed class member function call")
{
    ExampleClass instance;
    const auto guard = []() { return false; };

    const auto [called1, ret1] =
        utility::conditionally_invoke(guard, instance, &ExampleClass::function1, 10, SomeStruct{5});
    REQUIRE(called1 == false);
    REQUIRE(ret1 == 0);

    int x = 10;
    SomeStruct s1{10};
    const auto [called2, ret2] = utility::conditionally_invoke(guard, instance, &ExampleClass::function1, x, s1);
    REQUIRE(called2 == false);
    REQUIRE(ret2 == 0);

    const auto called3 = utility::conditionally_invoke(guard, instance, &ExampleClass::function2);
    REQUIRE(called3 == false);

    SomeStruct s2{15};
    const auto [called4, ret4] =
        utility::conditionally_invoke(guard, instance, &ExampleClass::function3, 10, std::ref(s2));
    REQUIRE(called4 == false);
    REQUIRE(ret4 == 0);
}

A module-utils/utility/Utility.hpp => module-utils/utility/Utility.hpp +87 -0
@@ 0,0 1,87 @@
// 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 <functional>
#include <utility>
#include <type_traits>

namespace utility
{
    using Guard = std::function<bool()>;

    /// Invokes a function if specified conditions are met.
    /// \tparam F       Function signature
    /// \tparam Args    Function arguments
    /// \param guard    Conditions to be met
    /// \param func     Callable object
    /// \param args     Function arguments
    /// \return If the function returns void - a flag indicating whether the conditions were met and the function has
    /// been called. If the function returns a type - a pair of: a flag indicating whether the conditions were met, and
    /// a function's return value.
    template <typename F, typename... Args>
    auto conditionally_invoke(const Guard &guard,
                              const F &func,
                              Args... args) noexcept(std::is_nothrow_invocable_v<F, Args...>)
    {
        using ResultType              = typename std::invoke_result_t<F, Args...>;
        constexpr auto isVoidFunction = std::is_void_v<ResultType>;
        if (!guard()) {
            if constexpr (isVoidFunction) {
                return false;
            }
            else {
                return std::pair(false, ResultType{});
            }
        }

        if constexpr (isVoidFunction) {
            std::invoke(func, std::forward<Args>(args)...);
            return true;
        }
        else {
            return std::pair(true, std::invoke(func, std::forward<Args>(args)...));
        }
    }

    /// Invokes a member function if specified conditions are met.
    /// \tparam T       Class which contains the member function F
    /// \tparam F       Function signature
    /// \tparam Args    Function arguments
    /// \param guard    Conditions to be met
    /// \param self     Pointer to the class T instance
    /// \param func     Callable object
    /// \param args     Function arguments
    /// \return If the function returns void - a flag indicating whether the conditions were met and the function has
    /// been called. If the function returns a type - a pair of: a flag indicating whether the conditions were met, and
    /// a function's return value.
    template <class T,
              typename F,
              typename... Args,
              typename std::enable_if_t<std::is_invocable_v<F, T, Args...>, bool> = false>
    auto conditionally_invoke(const Guard &guard,
                              T &&self,
                              const F &func,
                              Args... args) noexcept(std::is_nothrow_invocable_v<F, T, Args...>)
    {
        using ResultType              = typename std::invoke_result_t<F, T, Args...>;
        constexpr auto isVoidFunction = std::is_void_v<ResultType>;
        if (!guard()) {
            if constexpr (isVoidFunction) {
                return false;
            }
            else {
                return std::pair(false, ResultType{});
            }
        }

        if constexpr (isVoidFunction) {
            std::invoke(func, std::forward<T>(self), std::forward<Args>(args)...);
            return true;
        }
        else {
            return std::pair(true, std::invoke(func, std::forward<T>(self), std::forward<Args>(args)...));
        }
    }
} // namespace utility

M source/MessageType.hpp => source/MessageType.hpp +0 -3
@@ 173,9 173,6 @@ enum class MessageType
    SystemManagerCpuFrequency,
    SystemManagerRegistration,

    // battery charger messages
    EVMBatteryLevel,
    EVMChargerPlugged,
    // rtc messages
    EVMMinuteUpdated, ///< This message is send to current focused application on every minute time change.
    EVMTimeUpdated,   ///< This message is send on every time update.

R test/pytest/service-desktop/test_calllog.py => test/pytest/service-desktop/disabled_test_calllog.py +0 -0
R test/pytest/service-desktop/test_messages.py => test/pytest/service-desktop/disabled_test_messages.py +25 -24
@@ 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

import pytest


@@ 8,50 8,51 @@ from harness.interface.defs import status
@pytest.mark.service_desktop_test
def test_messages(harness):
    # getting the messages count
    body = {"count": True}
    body = {"category": "message", "count": True}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    count = ret["body"]["count"]
    if count == 0:
        pytest.skip("No contacts entries, skipping")
        pytest.skip("No messages entries, skipping")

    # getting all the messages
    body = {"count": count}
    body = {"category": "message", "limit": count, "offset": 0}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    messages = ret["body"]
    messages = ret["body"]["entries"]  # getting entries
    messages_count = len(messages)
    assert messages_count == count

    # remove message
    sms_to_remove = messages[0]
    body = {"id": sms_to_remove["id"]}
    ret = harness.endpoint_request("messages", "del", body)
    assert ret["status"] == status["OK"]

    # getting the messages count again
    body = {"count": True}
    # getting a number of messages
    number_of_requested_messages = 3
    body = {"category": "message", "limit": number_of_requested_messages, "offset": 0}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    assert ret["body"]["count"] == count - 1
    messages = ret["body"]["entries"]  # getting entries
    messages_count = len(messages)
    assert messages_count == number_of_requested_messages

    # getting messages binded to contactID
    contact_id = 2  # in test dataset this one has some messages
    body = {"contactID": contact_id}
    # getting messages binded to threadID
    thread_id = 1
    body = {"category": "message", "threadID": thread_id, "limit": 10, "offset": 0}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    for message in ret["body"]:
        assert message["contactID"] == contact_id
    for message in ret["body"]["entries"]:
        assert message["threadID"] == thread_id

    # getting messages binded to threadID
    thread_id = 1
    body = {"threadID": thread_id, "count": 10}
    # remove message
    sms_to_remove = messages[0]
    body = {"category": "message", "messageID": sms_to_remove["messageID"]}
    ret = harness.endpoint_request("messages", "del", body)
    assert ret["status"] == status["OK"]

    # getting the messages count again
    body = {"category": "message", "count": True}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    for message in ret["body"]:
        assert message["threadID"] == thread_id
    assert ret["body"]["count"] == count - 1

M test/pytest/service-desktop/test_contacts.py => test/pytest/service-desktop/test_contacts.py +16 -16
@@ 7,49 7,49 @@ from harness.interface.defs import status
@pytest.mark.service_desktop_test
def test_contacts(harness):
    # getting the contacts count
    body = {"count": True}
    body = {"limit": True}
    ret = harness.endpoint_request("contacts", "get", body)
    assert ret["status"] == status["OK"]

    count = ret["body"]["count"]
    count = ret["body"]["limit"]
    if count == 0:
        pytest.skip("No contacts entries, skipping")

    # getting all contacts
    batch_size = 30
    batch_size = 10
    divider = int(count / batch_size)
    reminder = count % batch_size
    contacts = []
    for i in range(divider):
        body = {"count": batch_size, "offset": batch_size*i}
        body = {"limit": batch_size, "offset": batch_size*i}
        ret = harness.endpoint_request("contacts", "get", body)
        assert ret["status"] == status["OK"]
        contacts = contacts + ret["body"]
        contacts = contacts + ret["body"]["entries"]

    body = {"count": reminder, "offset": count-reminder}
    body = {"limit": reminder, "offset": count-reminder}
    ret = harness.endpoint_request("contacts", "get", body)
    assert ret["status"] == status["OK"]
    contacts = contacts + ret["body"]
    contacts = contacts + ret["body"]["entries"]

    contacts_length = len(contacts)
    assert contacts_length
    assert contacts_length == count

    # try to get more than available
    batch_size = 30
    batch_size = 10
    divider = int((count+10) / batch_size)
    reminder = (count+10) % batch_size
    contacts = []
    for i in range(divider):
        body = {"count": batch_size, "offset": batch_size * i}
        body = {"limit": batch_size, "offset": batch_size * i}
        ret = harness.endpoint_request("contacts", "get", body)
        assert ret["status"] == status["OK"]
        contacts = contacts + ret["body"]
        contacts = contacts + ret["body"]["entries"]

    body = {"count": reminder, "offset": (count+10)-reminder}
    body = {"limit": reminder, "offset": (count+10)-reminder}
    ret = harness.endpoint_request("contacts", "get", body)
    assert ret["status"] == status["OK"]
    contacts = contacts + ret["body"]
    contacts = contacts + ret["body"]["entries"]

    contacts_length = len(contacts)
    assert contacts_length


@@ 78,10 78,10 @@ def test_contacts(harness):
    assert ret["status"] == status["NotAcceptable"]

    # checking count after adding
    body = {"count": True}
    body = {"limit": True}
    ret = harness.endpoint_request("contacts", "get", body)
    assert ret["status"] == status["OK"]
    assert ret["body"]["count"] == count + 1
    assert ret["body"]["limit"] == count + 1

    # updating existing contact
    body = {"address": "6 Czeczota St.\n02600 Warsaw",


@@ 112,8 112,8 @@ def test_contacts(harness):
    assert ret["status"] == status["OK"]

    # verifying count
    body = {"count": True}
    body = {"limit": True}
    ret = harness.endpoint_request("contacts", "get", body)
    assert ret["status"] == status["OK"]

    assert ret["body"]["count"] == count
    assert ret["body"]["limit"] == count

M test/pytest/service-desktop/test_templates.py => test/pytest/service-desktop/test_templates.py +33 -17
@@ 1,62 1,78 @@
# 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
import pytest
from harness.interface.defs import status


@pytest.mark.service_desktop_test
def test_messages(harness):
def test_templates(harness):
    # getting the templates count
    body = {"template": True, "count": True}
    body = {"category": "template", "count": True}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]
    count = ret["body"]["count"]

    if count == 0:
        body = {"template": True, "text": "first template"}
        ret = harness.endpoint_request("messages", "put", body)
        body = {"category": "template", "templateBody": "first template"}
        ret = harness.endpoint_request("messages", "post", body)
        assert ret["status"] == status["OK"]

        body = {"template": True, "count": True}
        body = {"category": "template", "count": True}
        ret = harness.endpoint_request("messages", "get", body)
        assert ret["status"] == status["OK"]
        count = ret["body"]["count"]
        assert count

    # getting all templates
    body = {"template": True, "count": count}
    body = {"category": "template", "limit": count}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]
    assert len(ret["body"]["entries"]) == count

    # getting a number of templates
    number_of_requested_templates = 3
    body = {"category": "template", "limit": number_of_requested_templates, "offset": 0}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    templates = ret["body"]["entries"]  # getting entries
    templates_count = len(templates)
    assert templates_count == number_of_requested_templates

    # getting template by ID
    body = {"category": "template", "templateID": 1}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]
    assert len(ret["body"]) == count

    # adding new template
    body = {"template": True, "text": "test template"}
    ret = harness.endpoint_request("messages", "put", body)
    body = {"category": "template", "templateBody": "test template"}
    ret = harness.endpoint_request("messages", "post", body)
    assert ret["status"] == status["OK"]

    # getting the templates count again
    body = {"template": True, "count": True}
    body = {"category": "template", "count": True}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]
    assert ret["body"]["count"] == count + 1

    # getting template to remove
    body = {"template": True, "count": count + 1}
    body = {"category": "template", "limit": count + 1}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    for template in ret["body"]:
        if template["text"] == "test template":
            id = template["id"]
    templateid = 0
    for template in ret["body"]["entries"]:
        if template["templateBody"] == "test template":
            templateid = template["templateID"]
            break

    # removing template
    body = {"template": True, "id": id}
    body = {"category": "template", "templateID": templateid}
    ret = harness.endpoint_request("messages", "del", body)
    assert ret["status"] == status["OK"]

    # getting the templates count again
    body = {"template": True, "count": True}
    body = {"category": "template", "count": True}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]
    assert ret["body"]["count"] == count

A test/pytest/service-desktop/test_threads.py => test/pytest/service-desktop/test_threads.py +27 -0
@@ 0,0 1,27 @@
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
import pytest
from harness.interface.defs import status


@pytest.mark.service_desktop_test
def test_threads(harness):
    # getting all threads
    body = {"category": "thread"}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    # getting a number of threads
    number_of_requested_threads = 3
    body = {"category": "thread", "limit": number_of_requested_threads, "offset": 0}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    threads = ret["body"]["entries"]  # getting entries
    threads_count = len(threads)
    assert threads_count == number_of_requested_threads

    # set thread as read
    body = {"category": "thread", "threadID": 1, "isUnread": False}
    ret = harness.endpoint_request("messages", "put", body)
    assert ret["status"] == status["OK"]

M test/pytest/test_search_sms.py => test/pytest/test_search_sms.py +2 -1
@@ 5,9 5,10 @@ import pytest

from harness.interface.defs import key_codes


@pytest.mark.rt1051
@pytest.mark.usefixtures("phone_unlocked")
def test_search_sms(harness, sms_text, phone_number):
    body = {"messageBody": sms_text, "phoneNumber": str(phone_number)}
    body = {"category": "message", "messageBody": sms_text, "phoneNumber": str(phone_number)}
    messages = harness.endpoint_request("messages", "get", body)["body"]
    assert len(messages) != 0

M test/pytest/test_send_message.py => test/pytest/test_send_message.py +10 -10
@@ 9,27 9,27 @@ from harness.interface.CDCSerial import Keytype

def erase_all_templates(harness):
    # getting the templates count
    body = {"template": True, "count": True}
    body = {"category": "template", "count": True}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]
    count = ret["body"]["count"]

    # getting all templates
    body = {"template": True, "count": count}
    body = {"category": "template", "limit": count}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]
    assert len(ret["body"]) == count
    assert len(ret["body"][1][1]) == count

    for template in ret["body"]:
        body = {"template": True, "id": template["id"]}
    for template in ret["body"][1][1]:
        body = {"category": "template", "templateID": template["templateID"]}
        del_res = harness.endpoint_request("messages", "del", body)
        assert del_res["status"] == status["OK"]


def add_new_template(harness, template_text: str):
    # adding new template
    body = {"template": True, "text": template_text}
    ret = harness.endpoint_request("messages", "put", body)
    body = {"category": "template", "templateBody": template_text}
    ret = harness.endpoint_request("messages", "post", body)
    assert ret["status"] == status["OK"]




@@ 72,19 72,19 @@ def erase_contacts_by_name(harness, name):


def get_message_by_text(harness, message: str, phone_number: str):
    body = {"messageBody": message, "phoneNumber": phone_number}
    body = {"category": "message", "messageBody": message, "phoneNumber": phone_number}
    return harness.endpoint_request("messages", "get", body)["body"]


# default sms type is draft
def prepare_sms(harness, message: str, phone_number: str, sms_type: int = 1):
    body = {"smsCommand": "smsAdd", "messageBody": message, "phoneNumber": phone_number, "type": sms_type}
    return harness.endpoint_request("developerMode", "put", body)
    return harness.endpoint_request("developerMode", "post", body)


def prepare_sms_template(harness, message: str, phone_number: str):
    body = {"template": True, "messageBody": message, "phoneNumber": phone_number}
    return harness.endpoint_request("developerMode", "put", body)
    return harness.endpoint_request("developerMode", "post", body)


def compare_messages(old_messages, new_messages, sms_type: SMSType = SMSType.OUTBOX):

M test/search_sms.py => test/search_sms.py +1 -2
@@ 14,7 14,7 @@ from harness.interface.error import TestError, Error
def search_sms(harness, message: str, phone_number: str):
    @harness.with_phone_unlocked
    def do_it(connection):
        body = {"messageBody": message, "phoneNumber": phone_number}
        body = {"category": "message", "messageBody": message, "phoneNumber": phone_number}
        messages = harness.endpoint_request("messages", "get", body)["body"]
        print(f'Found {len(messages)} messages')



@@ 49,4 49,3 @@ if __name__ == "__main__":
    except TestError as err:
        log.error(err)
        exit(err.get_error_code())