~aleteoryx/muditaos

47f377d54056569c784af3b24b7a46fc37a025c9 — Lefucjusz 1 year, 9 months ago b95894a
Revert "[MOS-1064] Fix no input language selected for French/Spanish"

This reverts commit 9ef454085e4cca8a1f6c3c270cf460f899c9ee23.
34 files changed, 289 insertions(+), 378 deletions(-)

M image/system_a/data/profiles/Deutsch_lower.json
M image/system_a/data/profiles/Deutsch_upper.json
M image/system_a/data/profiles/English_lower.json
M image/system_a/data/profiles/English_upper.json
M image/system_a/data/profiles/Espanol_lower.json
M image/system_a/data/profiles/Espanol_upper.json
M image/system_a/data/profiles/Francais_lower.json
M image/system_a/data/profiles/Francais_upper.json
M image/system_a/data/profiles/Polski_lower.json
M image/system_a/data/profiles/Polski_upper.json
M image/system_a/data/profiles/Svenska_lower.json
M image/system_a/data/profiles/Svenska_upper.json
M image/system_a/data/profiles/numeric.json
M image/system_a/data/profiles/phone.json
M module-apps/application-calculator/tests/CalculatorInput_tests.cpp
M module-apps/application-settings/windows/display-keypad/InputLanguageWindow.hpp
M module-bsp/hal/include/hal/key_input/RawKey.hpp
M module-gui/gui/input/InputEvent.hpp
M module-gui/gui/input/Profile.cpp
M module-gui/gui/input/Profile.hpp
M module-gui/gui/input/Translator.cpp
M module-gui/gui/input/Translator.hpp
M module-gui/test/test-catch/test-gui-callbacks.cpp
M module-gui/test/test-catch/test-key-translator.cpp
M module-gui/test/test-catch/test-language-input-parser.cpp
M module-gui/test/test-google/test-gui-boxlayout.cpp
M module-gui/test/test-google/test-gui-gridlayout.cpp
M module-gui/test/test-google/test-gui-listview.cpp
M module-services/service-cellular/ServiceCellular.cpp
M module-utils/i18n/JSONLoader.hpp
M module-utils/i18n/i18n.cpp
M module-utils/i18n/i18nImpl.hpp
M module-utils/i18n/include/i18n/i18n.hpp
M products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp
M image/system_a/data/profiles/Deutsch_lower.json => image/system_a/data/profiles/Deutsch_lower.json +0 -1
@@ 1,6 1,5 @@
{
  "filetype": "normal",
  "name": "Deutsch_lower",
  "31": ".,_:;)(?!#/*+",
  "32": "abcä",
  "33": "def",

M image/system_a/data/profiles/Deutsch_upper.json => image/system_a/data/profiles/Deutsch_upper.json +0 -1
@@ 1,6 1,5 @@
{
  "filetype": "normal",
  "name": "Deutsch_upper",
  "31": ".,_:;)(?!#/*+",
  "32": "ABCÄ",
  "33": "DEF",

M image/system_a/data/profiles/English_lower.json => image/system_a/data/profiles/English_lower.json +0 -1
@@ 1,6 1,5 @@
{
  "filetype": "normal",
  "name": "English_lower",
  "31": ".,_:;)(?!#/*+",
  "32": "abc",
  "33": "def",

M image/system_a/data/profiles/English_upper.json => image/system_a/data/profiles/English_upper.json +0 -1
@@ 1,6 1,5 @@
{
  "filetype": "normal",
  "name": "English_upper",
  "31": ".,_:;)(?!#/*+",
  "32": "ABC",
  "33": "DEF",

M image/system_a/data/profiles/Espanol_lower.json => image/system_a/data/profiles/Espanol_lower.json +0 -1
@@ 1,6 1,5 @@
{
  "filetype": "normal",
  "name": "Espa\u00f1ol_lower",
  "31": ".,_:;)(?!#/*+",
  "32": "abcá",
  "33": "defé",

M image/system_a/data/profiles/Espanol_upper.json => image/system_a/data/profiles/Espanol_upper.json +0 -1
@@ 1,6 1,5 @@
{
  "filetype": "normal",
  "name": "Espa\u00f1ol_upper",
  "31": ".,_:;)(?!#/*+",
  "32": "ABCÁ",
  "33": "DEFÉ",

M image/system_a/data/profiles/Francais_lower.json => image/system_a/data/profiles/Francais_lower.json +0 -1
@@ 1,6 1,5 @@
{
  "filetype": "normal",
  "name": "Fran\u00e7ais_lower",
  "31": ".,_:;)(?!#/*+",
  "32": "abcàâç",
  "33": "deféèêë",

M image/system_a/data/profiles/Francais_upper.json => image/system_a/data/profiles/Francais_upper.json +0 -1
@@ 1,6 1,5 @@
{
  "filetype": "normal",
  "name": "Fran\u00e7ais_upper",
  "31": ".,_:;)(?!#/*+",
  "32": "ABCÀÂÇ",
  "33": "DEFÉÈÊË",

M image/system_a/data/profiles/Polski_lower.json => image/system_a/data/profiles/Polski_lower.json +0 -1
@@ 1,6 1,5 @@
{
  "filetype": "normal",
  "name": "Polski_lower",
  "31": ".,_:;)(?!#/*+",
  "32": "abcąć",
  "33": "defę",

M image/system_a/data/profiles/Polski_upper.json => image/system_a/data/profiles/Polski_upper.json +0 -1
@@ 1,6 1,5 @@
{
  "filetype": "normal",
  "name": "Polski_upper",
  "31": ".,_:;)(?!#/*+",
  "32": "ABCĄĆ",
  "33": "DEFĘ",

M image/system_a/data/profiles/Svenska_lower.json => image/system_a/data/profiles/Svenska_lower.json +0 -1
@@ 1,6 1,5 @@
{
  "filetype": "normal",
  "name": "Svenska_lower",
  "31": ".,/+?!1:;)(#*",
  "32": "abcäå2",
  "33": "defé3",

M image/system_a/data/profiles/Svenska_upper.json => image/system_a/data/profiles/Svenska_upper.json +0 -1
@@ 1,6 1,5 @@
{
  "filetype": "normal",
  "name": "Svenska_upper",
  "31": ".,/+?!1:;)(#*",
  "32": "ABCÄÅ2",
  "33": "DEFÉ3",

M image/system_a/data/profiles/numeric.json => image/system_a/data/profiles/numeric.json +0 -1
@@ 1,6 1,5 @@
{
  "filetype": "special",
  "name": "numeric",
  "31": "1",
  "32": "2",
  "33": "3",

M image/system_a/data/profiles/phone.json => image/system_a/data/profiles/phone.json +1 -1
@@ 1,6 1,5 @@
{
  "filetype": "special",
  "name": "phone",
  "31": "1",
  "32": "2",
  "33": "3",


@@ 20,3 19,4 @@
  "12": "",
  "23": ""
}


M module-apps/application-calculator/tests/CalculatorInput_tests.cpp => module-apps/application-calculator/tests/CalculatorInput_tests.cpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <data/CalculatorInputProcessorText.hpp>


@@ 30,13 30,13 @@ SCENARIO("Input Processor tests")

        auto shortPressEvent = [](KeyCodes code) -> gui::InputEvent {
            return gui::InputEvent{RawKey{RawKey::State::Released, code, 0, 0},
                                   gui::InputEvent::State::KeyReleasedShort,
                                   gui::InputEvent::State::keyReleasedShort,
                                   static_cast<gui::KeyCode>(code)};
        };

        auto longPressEvent = [](KeyCodes code) -> gui::InputEvent {
            return gui::InputEvent{RawKey{RawKey::State::Released, code, 0, 0},
                                   gui::InputEvent::State::KeyReleasedLong,
                                   gui::InputEvent::State::keyReleasedLong,
                                   static_cast<gui::KeyCode>(code)};
        };


M module-apps/application-settings/windows/display-keypad/InputLanguageWindow.hpp => module-apps/application-settings/windows/display-keypad/InputLanguageWindow.hpp +2 -1
@@ 1,9 1,10 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <application-settings/windows/BaseSettingsWindow.hpp>

#include <Translator.hpp>

namespace gui

M module-bsp/hal/include/hal/key_input/RawKey.hpp => module-bsp/hal/include/hal/key_input/RawKey.hpp +4 -4
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, 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


@@ 7,11 7,11 @@
#include <hal/key_input/KeyEventDefinitions.hpp>

/// default application timer trigger
inline constexpr std::uint32_t keyTimerMs = 200;
const inline uint32_t keyTimerMs = 200;
/// default time key press will be counted as press again
inline constexpr std::uint32_t keyTimeCycleMs = 1200;
const inline uint32_t keyTimeCycleMs = 1200;
/// default long press time
inline constexpr std::uint32_t keyTimeLongpressMs = 1000;
const inline uint32_t keyTimeLongpressMs = 1000;

struct RawKey
{

M module-gui/gui/input/InputEvent.hpp => module-gui/gui/input/InputEvent.hpp +17 -17
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, 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


@@ 85,10 85,10 @@ namespace gui
        enum class State
        {
            Undefined        = 0x00, /// No action defined or translation error
            KeyPressed       = 0x01, /// Key pressed event
            KeyReleasedShort = 0x02, /// Key released before timeout
            KeyReleasedLong  = 0x04, /// Key released after timeout
            KeyMoved         = 0x05, /// Monostable key event
            keyPressed       = 0x01, /// Key pressed event
            keyReleasedShort = 0x02, /// Key released before timeout
            keyReleasedLong  = 0x04, /// Key released after timeout
            keyMoved         = 0x05, /// Monostable key event
        };

        InputEvent(RawKey key, State state = State::Undefined, KeyCode keyCode = KeyCode::KEY_UNDEFINED);


@@ 120,7 120,7 @@ namespace gui

        [[nodiscard]] auto isKeyPress() const -> bool
        {
            return state == State::KeyPressed;
            return state == State::keyPressed;
        }

        [[nodiscard]] auto isKeyPress(KeyCode code) const -> bool


@@ 130,7 130,7 @@ namespace gui

        [[nodiscard]] auto isShortRelease() const -> bool
        {
            return state == State::KeyReleasedShort;
            return state == State::keyReleasedShort;
        }

        [[nodiscard]] auto isShortRelease(KeyCode code) const -> bool


@@ 140,7 140,7 @@ namespace gui

        [[nodiscard]] auto isLongRelease() const -> bool
        {
            return state == State::KeyReleasedLong;
            return state == State::keyReleasedLong;
        }

        [[nodiscard]] auto isLongRelease(KeyCode code) const -> bool


@@ 174,7 174,7 @@ namespace gui

      private:
        RawKey rawKey   = {};                     /// RawKey data
        State state     = State::KeyPressed;      /// initial translated key state
        State state     = State::keyPressed;      /// initial translated key state
        KeyCode keyCode = KeyCode::KEY_UNDEFINED; /// initial translated key code
    };



@@ 187,14 187,14 @@ namespace gui
    switch (state) {
    case gui::InputEvent::State::Undefined:
        return "Undefined";
    case gui::InputEvent::State::KeyPressed:
        return "KeyPressed";
    case gui::InputEvent::State::KeyReleasedShort:
        return "KeyReleasedShort";
    case gui::InputEvent::State::KeyReleasedLong:
        return "KeyReleasedLong ";
    case gui::InputEvent::State::KeyMoved:
        return "KeyMoved";
    case gui::InputEvent::State::keyPressed:
        return "keyPressed";
    case gui::InputEvent::State::keyReleasedShort:
        return "keyReleasedShort";
    case gui::InputEvent::State::keyReleasedLong:
        return "keyReleasedLong ";
    case gui::InputEvent::State::keyMoved:
        return "keyMoved";
    }
    return "";
}

M module-gui/gui/input/Profile.cpp => module-gui/gui/input/Profile.cpp +14 -11
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <log/log.hpp>


@@ 9,31 9,33 @@

namespace gui
{

    Profile::Profile(const std::filesystem::path &filepath)
    {
        profileJson = createJson(filepath);
        name       = filepath.stem();
        inputChars = createJson(filepath);
    }

    const std::string &Profile::getName() noexcept
    {
        return profileJson[nameKey].string_value();
        return name;
    }

    const json11::Json &Profile::getJson() const noexcept
    {
        return profileJson;
        return inputChars;
    }

    const json11::Json Profile::createJson(const std::string &filepath)
    {
        const auto fd = std::fopen(filepath.c_str(), "r");
        auto fd = std::fopen(filepath.c_str(), "r");

        if (fd == nullptr) {
            LOG_FATAL("Error during opening file %s", filepath.c_str());
            return json11::Json();
        }

        const auto fsize = std::filesystem::file_size(filepath);
        uint32_t fsize = std::filesystem::file_size(filepath);

        auto stream = std::make_unique<char[]>(fsize + 1);



@@ 42,7 44,7 @@ namespace gui
        std::fread(stream.get(), 1, fsize, fd);

        std::string err;
        const auto parsedJson = json11::Json::parse(stream.get(), err);
        json11::Json parsedJson = json11::Json::parse(stream.get(), err);

        auto _ = gsl::finally([fd] { std::fclose(fd); });



@@ 53,13 55,14 @@ namespace gui
        return parsedJson;
    }

    std::uint32_t Profile::getCharKey(bsp::KeyCodes code, std::uint32_t times)
    uint32_t Profile::getCharKey(bsp::KeyCodes code, uint32_t times)
    {
        const auto ts  = profileJson[utils::to_string(static_cast<int>(code))].string_value();
        const auto utf = UTF8(ts);
        std::string ts = inputChars[utils::to_string(static_cast<int>(code))].string_value();
        UTF8 utf       = UTF8(ts);
        if (ts.size() > 0) {
            return utf[times % utf.length()];
        }
        return utf[0];
    }
} // namespace gui

} /* namespace gui */

M module-gui/gui/input/Profile.hpp => module-gui/gui/input/Profile.hpp +12 -11
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, 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


@@ 13,22 13,23 @@

namespace gui
{

    class Profile
    {
      private:
        std::string name;
        json11::Json inputChars;

        const json11::Json createJson(const std::string &filepath);

      public:
        static constexpr uint32_t none_key = 0;
        Profile()                          = default;
        explicit Profile(const std::filesystem::path &filepath);

        [[nodiscard]] const std::string &getName() noexcept;
        [[nodiscard]] std::uint32_t getCharKey(bsp::KeyCodes code, std::uint32_t times);
        [[nodiscard]] uint32_t getCharKey(bsp::KeyCodes code, uint32_t times);
        [[nodiscard]] const json11::Json &getJson() const noexcept;

        static constexpr std::uint32_t none_key = 0;

      private:
        const json11::Json createJson(const std::string &filepath);

        static constexpr auto nameKey = "name";
        json11::Json profileJson;
    };
} // namespace gui

} /* namespace gui */

M module-gui/gui/input/Translator.cpp => module-gui/gui/input/Translator.cpp +68 -37
@@ 1,10 1,9 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "Translator.hpp"
#include <log/log.hpp>
#include <algorithm>
#include <set>
#include <filesystem>
#include "i18n/i18n.hpp"



@@ 22,13 21,13 @@ namespace gui
        gui::InputEvent evt(key);
        switch (key.state) {
        case RawKey::State::Pressed:
            evt.setState(InputEvent::State::KeyPressed);
            evt.setState(InputEvent::State::keyPressed);
            break;
        case RawKey::State::Released:
            translateRelease(evt, key);
            break;
        case RawKey::State::Moved:
            evt.setState(InputEvent::State::KeyMoved);
            evt.setState(InputEvent::State::keyMoved);
            break;
        case RawKey::State::Undefined:
            evt.setState(InputEvent::State::Undefined);


@@ 52,14 51,14 @@ namespace gui
        }
        if ((previousKeyPress.keyCode == key.keyCode) &&
            (key.timeRelease - previousKeyPress.timePress >= keyTimeLongpressMs)) {
            evt.setState(InputEvent::State::KeyReleasedLong);
            evt.setState(InputEvent::State::keyReleasedLong);
        }
        else {
            evt.setState(InputEvent::State::KeyReleasedShort);
            evt.setState(InputEvent::State::keyReleasedShort);
        }
    }

    bool KeyBaseTranslation::isKeyPressTimedOut(std::uint32_t actualTimeStamp)
    bool KeyBaseTranslation::isKeyPressTimedOut(uint32_t actualTimeStamp)
    {
        if (isPreviousKeyPressed && (previousKeyPress.timePress != 0) &&
            (actualTimeStamp - previousKeyPress.timePress >= keyTimeLongpressMs)) {


@@ 85,60 84,88 @@ namespace gui
        switch (code) {
        case bsp::KeyCodes::NumericKey1:
            return gui::KeyCode::KEY_1;
            break;
        case bsp::KeyCodes::NumericKey2:
            return gui::KeyCode::KEY_2;
            break;
        case bsp::KeyCodes::NumericKey3:
            return gui::KeyCode::KEY_3;
            break;
        case bsp::KeyCodes::NumericKey4:
            return gui::KeyCode::KEY_4;
            break;
        case bsp::KeyCodes::NumericKey5:
            return gui::KeyCode::KEY_5;
            break;
        case bsp::KeyCodes::NumericKey6:
            return gui::KeyCode::KEY_6;
            break;
        case bsp::KeyCodes::NumericKey7:
            return gui::KeyCode::KEY_7;
            break;
        case bsp::KeyCodes::NumericKey8:
            return gui::KeyCode::KEY_8;
            break;
        case bsp::KeyCodes::NumericKey9:
            return gui::KeyCode::KEY_9;
            break;
        case bsp::KeyCodes::NumericKey0:
            return gui::KeyCode::KEY_0;
            break;
        case bsp::KeyCodes::NumericKeyAst:
            return gui::KeyCode::KEY_AST;
            break;
        case bsp::KeyCodes::NumericKeyPnd:
            return gui::KeyCode::KEY_PND;
            break;
        case bsp::KeyCodes::JoystickLeft:
            return gui::KeyCode::KEY_LEFT;
            break;
        case bsp::KeyCodes::JoystickRight:
            return gui::KeyCode::KEY_RIGHT;
            break;
        case bsp::KeyCodes::JoystickUp:
            return gui::KeyCode::KEY_UP;
            break;
        case bsp::KeyCodes::JoystickDown:
            return gui::KeyCode::KEY_DOWN;
            break;
        case bsp::KeyCodes::JoystickEnter:
            return gui::KeyCode::KEY_ENTER;
            break;
        case bsp::KeyCodes::FnLeft:
            return gui::KeyCode::KEY_LF;
            break;
        case bsp::KeyCodes::FnRight:
            return gui::KeyCode::KEY_RF;
            break;
        case bsp::KeyCodes::VolUp:
            return gui::KeyCode::KEY_VOLUP;
            break;
        case bsp::KeyCodes::VolDown:
            return gui::KeyCode::KEY_VOLDN;
            break;
        case bsp::KeyCodes::Torch:
            return gui::KeyCode::KEY_TORCH;
            break;
        case bsp::KeyCodes::SSwitchUp:
            return gui::KeyCode::SWITCH_UP;
            break;
        case bsp::KeyCodes::SSwitchDown:
            return gui::KeyCode::SWITCH_DN;
            break;
        case bsp::KeyCodes::SSwitchMid:
            return gui::KeyCode::SWITCH_MID;
            break;
        case bsp::KeyCodes::HeadsetOk:
            return gui::KeyCode::HEADSET_OK;

        case bsp::KeyCodes::HeadsetVolUp:
            return gui::KeyCode::HEADSET_VOLUP;

        case bsp::KeyCodes::HeadsetVolDown:
            return gui::KeyCode::HEADSET_VOLDN;

        default:
            LOG_ERROR("Unhandled bsp key!");
            return gui::KeyCode::KEY_UNDEFINED;


@@ 155,19 182,19 @@ namespace gui
        }
        else if (key.state == RawKey::State::Moved) {
            // translation of single moved event to keyReleasedShort
            evt.setState(InputEvent::State::KeyReleasedShort);
            evt.setState(InputEvent::State::keyReleasedShort);
        }
        evt.setKeyCode(getKeyCode(key.keyCode));
        return evt;
    }

    InputEvent KeyInputSimpleTranslation::translate(std::uint32_t timeout)
    InputEvent KeyInputSimpleTranslation::translate(uint32_t timeout)
    {
        RawKey key{RawKey::State::Released, previousKeyPress.keyCode, 0, timeout};
        return InputEvent{key, InputEvent::State::KeyReleasedLong, getKeyCode(key.keyCode)};
        return InputEvent{key, InputEvent::State::keyReleasedLong, getKeyCode(key.keyCode)};
    }

    std::uint32_t KeyInputMappedTranslation::handle(RawKey key, const std::string &keymap, bool incrementTimes)
    uint32_t KeyInputMappedTranslation::handle(RawKey key, const std::string &keymap, bool incrementTimes)
    {
        // get shortpress
        if (previousKeyPress.keyCode != key.keyCode) {


@@ 187,7 214,7 @@ namespace gui
        return profiles.get(keymap).getCharKey(key.keyCode, times);
    }

    std::uint32_t KeyInputMappedTranslation::getTimes() const noexcept
    uint32_t KeyInputMappedTranslation::getTimes() const noexcept
    {
        return times;
    }


@@ 195,51 222,54 @@ namespace gui
    void Profiles::loadProfile(const std::string &filepath)
    {
        LOG_INFO("Load profile: %s", filepath.c_str());
        auto profile = Profile(filepath);
        if (const auto &name = profile.getName(); !name.empty()) {
            profilesList.insert({name, std::move(profile)});
        auto p = Profile(filepath);
        if (auto name = p.getName(); !name.empty()) {
            profilesList.insert({p.getName(), std::move(p)});
        }
    }

    std::vector<std::string> Profiles::getProfilesFilenames()
    std::vector<std::string> Profiles::getProfilesNames()
    {
        std::vector<std::string> filenames;
        std::vector<std::string> profilesNames;
        LOG_INFO("Scanning %s profiles folder: %s", utils::files::jsonExtension, utils::getInputLanguagePath().c_str());

        for (const auto &entry : std::filesystem::directory_iterator(utils::getInputLanguagePath())) {
            filenames.push_back(entry.path());
            profilesNames.push_back(std::filesystem::path(entry.path().stem()));
        }

        LOG_INFO("Total number of profiles: %u", static_cast<unsigned>(filenames.size()));
        return filenames;
        LOG_INFO("Total number of profiles: %u", static_cast<unsigned int>(profilesNames.size()));
        return profilesNames;
    }

    std::vector<std::string> Profiles::getAvailableInputLanguages()
    {
        std::set<std::string> uniqueLanguages;
        const auto &profiles = get().profilesList;
        for (const auto &[name, profile] : profiles) {
        std::vector<std::string> profilesNames = getProfilesNames(), availableProfiles;

        for (auto &name : profilesNames) {
            auto profile = get().profilesList[name];
            if (profile.getJson()[filetype::jsonKey] == filetype::normal) {
                const auto breakSignPosition      = name.find_last_of(utils::files::breakSign);
                const auto &displayedLanguageName = name.substr(0, breakSignPosition);
                uniqueLanguages.insert(displayedLanguageName);
                auto breakSignPosition            = name.find_last_of(utils::files::breakSign);
                std::string displayedLanguageName = name.substr(0, breakSignPosition);

                if (std::find(availableProfiles.begin(), availableProfiles.end(), displayedLanguageName) ==
                    availableProfiles.end()) {
                    availableProfiles.push_back(displayedLanguageName);
                }
            }
        }

        std::vector<std::string> availableLanguages;
        std::copy(uniqueLanguages.begin(), uniqueLanguages.end(), std::back_inserter(availableLanguages));
        return availableLanguages;
        return availableProfiles;
    }

    void Profiles::init()
    {
        const auto &profilesFilenames = getProfilesFilenames();
        for (const auto &filename : profilesFilenames) {
            if (!filename.empty()) {
                loadProfile(utils::getInputLanguagePath() / filename);
        std::vector<std::string> profilesNames = getProfilesNames();
        for (const auto &profileName : profilesNames) {
            if (!profileName.empty()) {
                auto filePath = utils::getInputLanguagePath() / (profileName + utils::files::jsonExtension);
                loadProfile(filePath);
            }
        }
        if (profilesList.empty()) {
        if (std::size(profilesList) == 0) {
            LOG_ERROR("No keyboard profiles loaded");
        }
    }


@@ 256,7 286,7 @@ namespace gui

    Profile &Profiles::get(const std::string &name)
    {
        const auto &filepath = utils::getInputLanguagePath() / (name + utils::files::jsonExtension);
        std::filesystem::path filepath = utils::getInputLanguagePath() / (name + utils::files::jsonExtension);
        // if profile not in profile map -> load
        if (filepath.empty()) {
            LOG_ERROR("Request for nonexistent profile: %s", filepath.c_str());


@@ 267,4 297,5 @@ namespace gui
        }
        return get().profilesList[name];
    }
} // namespace gui

} /* namespace gui */

M module-gui/gui/input/Translator.hpp => module-gui/gui/input/Translator.hpp +9 -8
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 20,7 20,7 @@ namespace gui
        /// RawKey to Input Event translation
        InputEvent set(RawKey key);
        /// Check if keyPress is timed out for particular timestamp
        bool isKeyPressTimedOut(std::uint32_t actualTimeStamp);
        bool isKeyPressTimedOut(uint32_t actualTimeStamp);
        /// Reset previous key press status
        void resetPreviousKeyPress();
        /// Set previous key press timeout status


@@ 41,7 41,7 @@ namespace gui
        /// translate incomming key
        InputEvent translate(RawKey key);
        /// translate timeout - simulate key release
        InputEvent translate(std::uint32_t timeout);
        InputEvent translate(uint32_t timeout);
    };

    /// profiles cache - load once for all


@@ 51,7 51,7 @@ namespace gui
        std::map<std::string, gui::Profile> profilesList = {};

        void loadProfile(const std::string &filepath);
        std::vector<std::string> getProfilesFilenames();
        std::vector<std::string> getProfilesNames();
        void init();
        Profile empty;



@@ 65,11 65,12 @@ namespace gui
    /// translator using & switching KeyMaps for use per widget basis ,called for selected widget, per widget basis
    class KeyInputMappedTranslation : public KeyBaseTranslation
    {
        std::uint32_t times = 0;
        uint32_t times = 0;
        Profiles profiles;

      public:
        std::uint32_t handle(RawKey key, const std::string &keymap, bool incrementTimes = true);
        std::uint32_t getTimes() const noexcept;
        uint32_t handle(RawKey key, const std::string &keymap, bool incrementTimes = true);
        uint32_t getTimes() const noexcept;
    };
} // namespace gui

} /* namespace gui */

M module-gui/test/test-catch/test-gui-callbacks.cpp => module-gui/test/test-catch/test-gui-callbacks.cpp +4 -4
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>


@@ 101,14 101,14 @@ TEST_CASE("gui::Window on input flow test")

        win.setFocusItem(l1);
        focus_acquired_l2 = false;
        win.onInput(gui::InputEvent({}, gui::InputEvent::State::KeyReleasedShort, gui::KeyCode::KEY_DOWN));
        win.onInput(gui::InputEvent({}, gui::InputEvent::State::keyReleasedShort, gui::KeyCode::KEY_DOWN));
        REQUIRE(focus_acquired_l2);

        focus_acquired_l1 = false;
        win.onInput(gui::InputEvent({}, gui::InputEvent::State::KeyReleasedShort, gui::KeyCode::KEY_DOWN));
        win.onInput(gui::InputEvent({}, gui::InputEvent::State::keyReleasedShort, gui::KeyCode::KEY_DOWN));
        REQUIRE(focus_acquired_l1);

        win.onInput(gui::InputEvent({}, gui::InputEvent::State::KeyReleasedShort, gui::KeyCode::KEY_TORCH));
        win.onInput(gui::InputEvent({}, gui::InputEvent::State::keyReleasedShort, gui::KeyCode::KEY_TORCH));
        REQUIRE(l1_input_handled);
    }
}

M module-gui/test/test-catch/test-key-translator.cpp => module-gui/test/test-catch/test-key-translator.cpp +6 -6
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, 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 <catch2/catch.hpp>


@@ 11,11 11,11 @@ TEST_CASE("Regular key press and release")

    key.state  = RawKey::State::Pressed;
    auto event = translator.set(key);
    REQUIRE(event.getState() == gui::InputEvent::State::KeyPressed);
    REQUIRE(event.getState() == gui::InputEvent::State::keyPressed);

    key.state = RawKey::State::Released;
    event     = translator.set(key);
    REQUIRE(event.getState() == gui::InputEvent::State::KeyReleasedShort);
    REQUIRE(event.getState() == gui::InputEvent::State::keyReleasedShort);
}

TEST_CASE("Key release before first key press")


@@ 37,12 37,12 @@ TEST_CASE("Key long release")
    key.state     = RawKey::State::Pressed;
    key.timePress = 0;
    auto event    = translator.set(key);
    REQUIRE(event.getState() == gui::InputEvent::State::KeyPressed);
    REQUIRE(event.getState() == gui::InputEvent::State::keyPressed);

    key.state       = RawKey::State::Released;
    key.timeRelease = timeToLongRelease;
    event           = translator.set(key);
    REQUIRE(event.getState() == gui::InputEvent::State::KeyReleasedLong);
    REQUIRE(event.getState() == gui::InputEvent::State::keyReleasedLong);
}

TEST_CASE("Key Moved")


@@ 52,7 52,7 @@ TEST_CASE("Key Moved")

    key.state  = RawKey::State::Moved;
    auto event = translator.set(key);
    REQUIRE(event.getState() == gui::InputEvent::State::KeyMoved);
    REQUIRE(event.getState() == gui::InputEvent::State::keyMoved);
}

TEST_CASE("Key Undefined")

M module-gui/test/test-catch/test-language-input-parser.cpp => module-gui/test/test-catch/test-language-input-parser.cpp +67 -192
@@ 1,197 1,61 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>
#include <Translator.hpp>

namespace
TEST_CASE("Parsing English input language")
{
    auto quickClick(gui::KeyInputMappedTranslation *translator, RawKey key, const std::string &keymap) -> char32_t
    {
        key.state = RawKey::State::Pressed;
        translator->handle(key, keymap);
        key.state = RawKey::State::Released;
        return static_cast<char32_t>(translator->handle(key, keymap));
    }
} // namespace

TEST_CASE("Test language with no Unicode characters in their names")
{
    SECTION("Parsing English input language")
    {
        SECTION("Getting charKey from lower letters")
        {
            gui::KeyInputMappedTranslation translator;
            RawKey key;

            key.keyCode = bsp::KeyCodes::NumericKey1;
            REQUIRE(translator.handle(key, "English_lower") == '.');
            key.keyCode = bsp::KeyCodes::NumericKey2;
            REQUIRE(translator.handle(key, "English_lower") == 'a');
            key.keyCode = bsp::KeyCodes::NumericKey3;
            REQUIRE(translator.handle(key, "English_lower") == 'd');
            key.keyCode = bsp::KeyCodes::NumericKey4;
            REQUIRE(translator.handle(key, "English_lower") == 'g');
            key.keyCode = bsp::KeyCodes::NumericKey5;
            REQUIRE(translator.handle(key, "English_lower") == 'j');
            key.keyCode = bsp::KeyCodes::NumericKey6;
            REQUIRE(translator.handle(key, "English_lower") == 'm');
            key.keyCode = bsp::KeyCodes::NumericKey7;
            REQUIRE(translator.handle(key, "English_lower") == 'p');
            key.keyCode = bsp::KeyCodes::NumericKey8;
            REQUIRE(translator.handle(key, "English_lower") == 't');
            key.keyCode = bsp::KeyCodes::NumericKey9;
            REQUIRE(translator.handle(key, "English_lower") == 'w');
            key.keyCode = bsp::KeyCodes::NumericKey0;
            REQUIRE(translator.handle(key, "English_lower") == ' ');
        }

        SECTION("Getting charKey from upper letters")
        {
            gui::KeyInputMappedTranslation translator;
            RawKey key;

            key.keyCode = bsp::KeyCodes::NumericKey1;
            REQUIRE(translator.handle(key, "English_upper") == '.');
            key.keyCode = bsp::KeyCodes::NumericKey2;
            REQUIRE(translator.handle(key, "English_upper") == 'A');
            key.keyCode = bsp::KeyCodes::NumericKey3;
            REQUIRE(translator.handle(key, "English_upper") == 'D');
            key.keyCode = bsp::KeyCodes::NumericKey4;
            REQUIRE(translator.handle(key, "English_upper") == 'G');
            key.keyCode = bsp::KeyCodes::NumericKey5;
            REQUIRE(translator.handle(key, "English_upper") == 'J');
            key.keyCode = bsp::KeyCodes::NumericKey6;
            REQUIRE(translator.handle(key, "English_upper") == 'M');
            key.keyCode = bsp::KeyCodes::NumericKey7;
            REQUIRE(translator.handle(key, "English_upper") == 'P');
            key.keyCode = bsp::KeyCodes::NumericKey8;
            REQUIRE(translator.handle(key, "English_upper") == 'T');
            key.keyCode = bsp::KeyCodes::NumericKey9;
            REQUIRE(translator.handle(key, "English_upper") == 'W');
            key.keyCode = bsp::KeyCodes::NumericKey0;
            REQUIRE(translator.handle(key, "English_upper") == ' ');
        }

        SECTION("Getting charKey after clicking button twice")
        {
            gui::KeyInputMappedTranslation translator;
            RawKey key;

            key.keyCode = bsp::KeyCodes::NumericKey2;
            key.state   = RawKey::State::Released;
            translator.handle(key, "English_lower");
            REQUIRE(translator.handle(key, "English_lower") == 'b');
        }
    }
}
    gui::KeyInputMappedTranslation translator;
    RawKey key;

TEST_CASE("Test languages containing Unicode characters in their names")
{
    SECTION("Parsing French input language")
    SECTION("Getting charKey from lower letters")
    {
        gui::KeyInputMappedTranslation translator;
        RawKey key;

        SECTION("Getting charKey from lower letters")
        {
            SECTION("Numeric key 2")
            {
                key.keyCode = bsp::KeyCodes::NumericKey2;
                REQUIRE(quickClick(&translator, key, "Français_lower") == 'a');
                REQUIRE(quickClick(&translator, key, "Français_lower") == 'b');
                REQUIRE(quickClick(&translator, key, "Français_lower") == 'c');
                REQUIRE(quickClick(&translator, key, "Français_lower") == U'à');
                REQUIRE(quickClick(&translator, key, "Français_lower") == U'â');
                REQUIRE(quickClick(&translator, key, "Français_lower") == U'ç');
            }

            SECTION("Numeric key 3")
            {
                key.keyCode = bsp::KeyCodes::NumericKey3;
                REQUIRE(quickClick(&translator, key, "Français_lower") == 'd');
                REQUIRE(quickClick(&translator, key, "Français_lower") == 'e');
                REQUIRE(quickClick(&translator, key, "Français_lower") == 'f');
                REQUIRE(quickClick(&translator, key, "Français_lower") == U'é');
                REQUIRE(quickClick(&translator, key, "Français_lower") == U'è');
                REQUIRE(quickClick(&translator, key, "Français_lower") == U'ê');
                REQUIRE(quickClick(&translator, key, "Français_lower") == U'ë');
            }
        }

        SECTION("Getting charKey from upper letters")
        {
            SECTION("Numeric key 2")
            {
                key.keyCode = bsp::KeyCodes::NumericKey2;
                REQUIRE(quickClick(&translator, key, "Français_upper") == 'A');
                REQUIRE(quickClick(&translator, key, "Français_upper") == 'B');
                REQUIRE(quickClick(&translator, key, "Français_upper") == 'C');
                REQUIRE(quickClick(&translator, key, "Français_upper") == U'À');
                REQUIRE(quickClick(&translator, key, "Français_upper") == U'Â');
                REQUIRE(quickClick(&translator, key, "Français_upper") == U'Ç');
            }

            SECTION("Numeric key 3")
            {
                key.keyCode = bsp::KeyCodes::NumericKey3;
                REQUIRE(quickClick(&translator, key, "Français_upper") == 'D');
                REQUIRE(quickClick(&translator, key, "Français_upper") == 'E');
                REQUIRE(quickClick(&translator, key, "Français_upper") == 'F');
                REQUIRE(quickClick(&translator, key, "Français_upper") == U'É');
                REQUIRE(quickClick(&translator, key, "Français_upper") == U'È');
                REQUIRE(quickClick(&translator, key, "Français_upper") == U'Ê');
                REQUIRE(quickClick(&translator, key, "Français_upper") == U'Ë');
            }
        }
        key.keyCode = bsp::KeyCodes::NumericKey1;
        REQUIRE(translator.handle(key, "English_lower") == 46);
        key.keyCode = bsp::KeyCodes::NumericKey2;
        REQUIRE(translator.handle(key, "English_lower") == 97);
        key.keyCode = bsp::KeyCodes::NumericKey3;
        REQUIRE(translator.handle(key, "English_lower") == 100);
        key.keyCode = bsp::KeyCodes::NumericKey4;
        REQUIRE(translator.handle(key, "English_lower") == 103);
        key.keyCode = bsp::KeyCodes::NumericKey5;
        REQUIRE(translator.handle(key, "English_lower") == 106);
        key.keyCode = bsp::KeyCodes::NumericKey6;
        REQUIRE(translator.handle(key, "English_lower") == 109);
        key.keyCode = bsp::KeyCodes::NumericKey7;
        REQUIRE(translator.handle(key, "English_lower") == 112);
        key.keyCode = bsp::KeyCodes::NumericKey8;
        REQUIRE(translator.handle(key, "English_lower") == 116);
        key.keyCode = bsp::KeyCodes::NumericKey9;
        REQUIRE(translator.handle(key, "English_lower") == 119);
        key.keyCode = bsp::KeyCodes::NumericKey0;
        REQUIRE(translator.handle(key, "English_lower") == 32);
    }

    SECTION("Parsing Spanish input language")
    SECTION("Getting charKey from upper letters")
    {
        gui::KeyInputMappedTranslation translator;
        RawKey key;

        SECTION("Getting charKey from lower letters")
        {
            SECTION("Numeric key 2")
            {
                key.keyCode = bsp::KeyCodes::NumericKey2;
                REQUIRE(quickClick(&translator, key, "Español_lower") == 'a');
                REQUIRE(quickClick(&translator, key, "Español_lower") == 'b');
                REQUIRE(quickClick(&translator, key, "Español_lower") == 'c');
                REQUIRE(quickClick(&translator, key, "Español_lower") == U'á');
            }

            SECTION("Numeric key 3")
            {
                key.keyCode = bsp::KeyCodes::NumericKey3;
                REQUIRE(quickClick(&translator, key, "Español_lower") == 'd');
                REQUIRE(quickClick(&translator, key, "Español_lower") == 'e');
                REQUIRE(quickClick(&translator, key, "Español_lower") == 'f');
                REQUIRE(quickClick(&translator, key, "Español_lower") == U'é');
            }
        }

        SECTION("Getting charKey from upper letters")
        {
            SECTION("Numeric key 2")
            {
                key.keyCode = bsp::KeyCodes::NumericKey2;
                REQUIRE(quickClick(&translator, key, "Español_upper") == 'A');
                REQUIRE(quickClick(&translator, key, "Español_upper") == 'B');
                REQUIRE(quickClick(&translator, key, "Español_upper") == 'C');
                REQUIRE(quickClick(&translator, key, "Español_upper") == U'Á');
            }

            SECTION("Numeric key 3")
            {
                key.keyCode = bsp::KeyCodes::NumericKey3;
                REQUIRE(quickClick(&translator, key, "Español_upper") == 'D');
                REQUIRE(quickClick(&translator, key, "Español_upper") == 'E');
                REQUIRE(quickClick(&translator, key, "Español_upper") == 'F');
                REQUIRE(quickClick(&translator, key, "Español_upper") == U'É');
            }
        }
        key.keyCode = bsp::KeyCodes::NumericKey1;
        REQUIRE(translator.handle(key, "English_upper") == 46);
        key.keyCode = bsp::KeyCodes::NumericKey2;
        REQUIRE(translator.handle(key, "English_upper") == 65);
        key.keyCode = bsp::KeyCodes::NumericKey3;
        REQUIRE(translator.handle(key, "English_upper") == 68);
        key.keyCode = bsp::KeyCodes::NumericKey4;
        REQUIRE(translator.handle(key, "English_upper") == 71);
        key.keyCode = bsp::KeyCodes::NumericKey5;
        REQUIRE(translator.handle(key, "English_upper") == 74);
        key.keyCode = bsp::KeyCodes::NumericKey6;
        REQUIRE(translator.handle(key, "English_upper") == 77);
        key.keyCode = bsp::KeyCodes::NumericKey7;
        REQUIRE(translator.handle(key, "English_upper") == 80);
        key.keyCode = bsp::KeyCodes::NumericKey8;
        REQUIRE(translator.handle(key, "English_upper") == 84);
        key.keyCode = bsp::KeyCodes::NumericKey9;
        REQUIRE(translator.handle(key, "English_upper") == 87);
        key.keyCode = bsp::KeyCodes::NumericKey0;
        REQUIRE(translator.handle(key, "English_upper") == 32);
    }
}



@@ 201,23 65,34 @@ TEST_CASE("Parsing numeric keyboard")
    RawKey key;

    key.keyCode = bsp::KeyCodes::NumericKey1;
    REQUIRE(translator.handle(key, "numeric") == '1');
    REQUIRE(translator.handle(key, "numeric") == 49);
    key.keyCode = bsp::KeyCodes::NumericKey2;
    REQUIRE(translator.handle(key, "numeric") == '2');
    REQUIRE(translator.handle(key, "numeric") == 50);
    key.keyCode = bsp::KeyCodes::NumericKey3;
    REQUIRE(translator.handle(key, "numeric") == '3');
    REQUIRE(translator.handle(key, "numeric") == 51);
    key.keyCode = bsp::KeyCodes::NumericKey4;
    REQUIRE(translator.handle(key, "numeric") == '4');
    REQUIRE(translator.handle(key, "numeric") == 52);
    key.keyCode = bsp::KeyCodes::NumericKey5;
    REQUIRE(translator.handle(key, "numeric") == '5');
    REQUIRE(translator.handle(key, "numeric") == 53);
    key.keyCode = bsp::KeyCodes::NumericKey6;
    REQUIRE(translator.handle(key, "numeric") == '6');
    REQUIRE(translator.handle(key, "numeric") == 54);
    key.keyCode = bsp::KeyCodes::NumericKey7;
    REQUIRE(translator.handle(key, "numeric") == '7');
    REQUIRE(translator.handle(key, "numeric") == 55);
    key.keyCode = bsp::KeyCodes::NumericKey8;
    REQUIRE(translator.handle(key, "numeric") == '8');
    REQUIRE(translator.handle(key, "numeric") == 56);
    key.keyCode = bsp::KeyCodes::NumericKey9;
    REQUIRE(translator.handle(key, "numeric") == '9');
    REQUIRE(translator.handle(key, "numeric") == 57);
    key.keyCode = bsp::KeyCodes::NumericKey0;
    REQUIRE(translator.handle(key, "numeric") == '0');
    REQUIRE(translator.handle(key, "numeric") == 48);
}

TEST_CASE("Getting charKey after clicking button twice")
{
    gui::KeyInputMappedTranslation translator;
    RawKey key;

    key.keyCode = bsp::KeyCodes::NumericKey2;
    key.state   = RawKey::State::Released;
    translator.handle(key, "English_lower");
    REQUIRE(translator.handle(key, "English_lower") == 98);
}

M module-gui/test/test-google/test-gui-boxlayout.cpp => module-gui/test/test-google/test-gui-boxlayout.cpp +25 -25
@@ 1,37 1,37 @@
// Copyright (c) 2017-2024, 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 "gtest/gtest.h"

#include "TestBoxLayout.hpp"

#include <log/log.hpp>
#include <module-gui/test/mock/TestListViewProvider.hpp>
#include <gui/input/InputEvent.hpp>

namespace testStyle
{
    constexpr std::uint32_t box_x = 0;
    constexpr std::uint32_t box_y = 0;
    constexpr std::uint32_t box_w = 200;
    constexpr std::uint32_t box_h = 600;

    constexpr std::uint32_t VBox_w = 200;
    constexpr std::uint32_t VBox_h = 600;
    constexpr std::uint32_t HBox_w = 200;
    constexpr std::uint32_t HBox_h = 50;

    constexpr std::uint32_t VBox_item_w = 200;
    constexpr std::uint32_t VBox_item_h = 100;
    constexpr std::uint32_t HBox_item_w = 50;
    constexpr std::uint32_t HBox_item_h = 50;
    const inline uint32_t box_x = 0;
    const inline uint32_t box_y = 0;
    const inline uint32_t box_w = 200;
    const inline uint32_t box_h = 600;

    const inline uint32_t VBox_w = 200;
    const inline uint32_t VBox_h = 600;
    const inline uint32_t HBox_w = 200;
    const inline uint32_t HBox_h = 50;

    const inline uint32_t VBox_item_w = 200;
    const inline uint32_t VBox_item_h = 100;
    const inline uint32_t HBox_item_w = 50;
    const inline uint32_t HBox_item_h = 50;
} // namespace testStyle

class TestItem : public gui::Rect
{
  public:
    unsigned int ID = 0;
    TestItem(Item *parent, std::uint32_t x, std::uint32_t y, std::uint32_t w, std::uint32_t h)
        : Rect(parent, x, y, w, h){};
    TestItem(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h) : Rect(parent, x, y, w, h){};
    ~TestItem() = default;
};



@@ 64,14 64,14 @@ class BoxLayoutTesting : public ::testing::Test
    void moveNTimes(gui::BoxLayout *Box, unsigned int n, gui::KeyCode key)
    {
        for (unsigned int i = 0; i < n; i++) {
            Box->onInput(gui::InputEvent({}, gui::InputEvent::State::KeyReleasedShort, key));
            Box->onInput(gui::InputEvent({}, gui::InputEvent::State::keyReleasedShort, key));
        }
    }

    void addNItems(gui::BoxLayout *Box,
                   unsigned int n,
                   std::uint32_t item_w,
                   std::uint32_t item_h,
                   uint32_t item_w,
                   uint32_t item_h,
                   const gui::Margins &margins = gui::Margins())
    {
        for (unsigned int i = 1; i <= n; i++) {


@@ 97,11 97,11 @@ class BoxLayoutTesting : public ::testing::Test
    gui::VBox *testVBoxLayout    = nullptr;
    gui::HBox *testHBoxLayout    = nullptr;

    static constexpr unsigned int fillVBoxPage     = testStyle::VBox_h / testStyle::VBox_item_h;
    static constexpr unsigned int notFillVBoxPage  = fillVBoxPage - 2;
    static constexpr unsigned int fillHBoxPage     = testStyle::HBox_w / testStyle::HBox_item_w;
    static constexpr unsigned int notFillHVBoxPage = fillHBoxPage - 1;
    static constexpr unsigned int overflowHBoxPage = fillHBoxPage + 2;
    const unsigned int fillVBoxPage     = testStyle::VBox_h / testStyle::VBox_item_h;
    const unsigned int notFillVBoxPage  = fillVBoxPage - 2;
    const unsigned int fillHBoxPage     = testStyle::HBox_w / testStyle::HBox_item_w;
    const unsigned int notFillHVBoxPage = fillHBoxPage - 1;
    const unsigned int overflowHBoxPage = fillHBoxPage + 2;
};

TEST_F(BoxLayoutTesting, Constructor_Destructor_Test)

M module-gui/test/test-google/test-gui-gridlayout.cpp => module-gui/test/test-google/test-gui-gridlayout.cpp +21 -21
@@ 1,28 1,28 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "gtest/gtest.h"
#include <log/log.hpp>
#include <module-gui/gui/widgets/BoxLayout.hpp>
#include <module-gui/gui/widgets/GridLayout.hpp>
#include <gui/input/InputEvent.hpp>

namespace testStyle
{
    constexpr std::uint32_t box_x = 0;
    constexpr std::uint32_t box_y = 0;
    constexpr std::uint32_t box_w = 600;
    constexpr std::uint32_t box_h = 200;
    const inline uint32_t box_x = 0;
    const inline uint32_t box_y = 0;
    const inline uint32_t box_w = 600;
    const inline uint32_t box_h = 200;

    constexpr std::uint32_t grid_item_w = 50;
    constexpr std::uint32_t grid_item_h = 50;
    const inline uint32_t grid_item_w = 50;
    const inline uint32_t grid_item_h = 50;
} // namespace testStyle

class TestItem : public gui::Rect
{
  public:
    unsigned int ID = 0;
    TestItem(Item *parent, std::uint32_t x, std::uint32_t y, std::uint32_t w, std::uint32_t h)
        : Rect(parent, x, y, w, h){};
    TestItem(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h) : Rect(parent, x, y, w, h){};
};

class GridLayoutTesting : public ::testing::Test


@@ 47,14 47,14 @@ class GridLayoutTesting : public ::testing::Test
    void moveNTimes(gui::BoxLayout *Box, unsigned int n, gui::KeyCode key)
    {
        for (unsigned int i = 0; i < n; i++) {
            Box->onInput(gui::InputEvent({}, gui::InputEvent::State::KeyReleasedShort, key));
            Box->onInput(gui::InputEvent({}, gui::InputEvent::State::keyReleasedShort, key));
        }
    }

    void addNItems(gui::BoxLayout *Box,
                   unsigned int n,
                   std::uint32_t item_w,
                   std::uint32_t item_h,
                   uint32_t item_w,
                   uint32_t item_h,
                   const gui::Margins &margins = gui::Margins())
    {
        for (unsigned int i = 1; i <= n; i++) {


@@ 142,20 142,20 @@ TEST_F(GridLayoutTesting, Navigate_Test_ActiveItems_1)
    ///> | 25  A | 26 NA | 27  A | 28 NA | 29  A | 30 NA | 31 A  | 32 NA | 33  A | 34 NA | 35 A  | 36 NA |
    ///> | 37 NA | 38  A | 39 NA | 40  A | 41 NA | 42  A | 43 NA | 44  A | 45 NA | 46  A | 47 NA | 48  A |
    ///> | 49 NV | 50 NV | 51 NV | 52 NV |
    for (std::uint32_t i = 1; i <= 12; i++) {
    for (uint32_t i = 1; i <= 12; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, (i % 2) ? true : false);
    }
    for (std::uint32_t i = 13; i <= 24; i++) {
    for (uint32_t i = 13; i <= 24; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, ((i + 1) % 2) ? true : false);
    }
    for (std::uint32_t i = 25; i <= 36; i++) {
    for (uint32_t i = 25; i <= 36; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, (i % 2) ? true : false);
    }
    for (std::uint32_t i = 37; i <= 48; i++) {
    for (uint32_t i = 37; i <= 48; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, ((i + 1) % 2) ? true : false);
    }
    ///> Add some items to exceed grid layout area
    for (std::uint32_t i = 49; i <= 52; i++) {
    for (uint32_t i = 49; i <= 52; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, true);
    }
    gridLayout->setFocus(true);


@@ 186,16 186,16 @@ TEST_F(GridLayoutTesting, Navigate_Test_ActiveItems_2_BorderCallback)
    ///> | 13 NA | 14  A | 15 NA | 16  A | 17 NA | 18  A | 19 NA | 20  A | 21 NA | 22  A | 23 NA | 24  A |
    ///> | 25  A | 26 NA | 27  A | 28 NA | 29  A | 30 NA | 31 A  | 32 NA | 33  A | 34 NA | 35 A  | 36 NA |
    ///> | 37 NA | 38  A | 39 NA |
    for (std::uint32_t i = 1; i <= 12; i++) {
    for (uint32_t i = 1; i <= 12; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, (i % 2) ? true : false);
    }
    for (std::uint32_t i = 13; i <= 24; i++) {
    for (uint32_t i = 13; i <= 24; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, ((i + 1) % 2) ? true : false);
    }
    for (std::uint32_t i = 25; i <= 36; i++) {
    for (uint32_t i = 25; i <= 36; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, (i % 2) ? true : false);
    }
    for (std::uint32_t i = 37; i <= 39; i++) {
    for (uint32_t i = 37; i <= 39; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, ((i + 1) % 2) ? true : false);
    }


M module-gui/test/test-google/test-gui-listview.cpp => module-gui/test/test-google/test-gui-listview.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, 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 "gtest/gtest.h"


@@ 42,7 42,7 @@ class ListViewTesting : public ::testing::Test
            key = gui::KeyCode::KEY_DOWN;

        for (unsigned int i = 0; i < n; i++) {
            testListView->onInput(gui::InputEvent({}, gui::InputEvent::State::KeyReleasedShort, key));
            testListView->onInput(gui::InputEvent({}, gui::InputEvent::State::keyReleasedShort, key));
        }
    }


M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +5 -5
@@ 114,15 114,15 @@ namespace constants
{
    using namespace std::chrono_literals;

    constexpr auto serviceCellularStackSize = 1024 * 8;
    inline constexpr auto cellularStack = 1024 * 8;

    constexpr std::chrono::milliseconds sleepTimerInterval{500ms};
    constexpr std::chrono::milliseconds maxUrcHandleTime{5s};
    constexpr std::chrono::milliseconds maxTimeWithoutCommunication{1s};
    inline constexpr std::chrono::milliseconds sleepTimerInterval{500ms};
    inline constexpr std::chrono::milliseconds maxUrcHandleTime{5s};
    inline constexpr std::chrono::milliseconds maxTimeWithoutCommunication{1s};
} // namespace constants

ServiceCellular::ServiceCellular()
    : sys::Service(::service::name::cellular, "", constants::serviceCellularStackSize, sys::ServicePriority::Idle),
    : sys::Service(::service::name::cellular, "", constants::cellularStack, sys::ServicePriority::Idle),
      phoneModeObserver{std::make_unique<sys::phone_modes::Observer>()},
      priv{std::make_unique<internal::ServiceCellularPriv>(this)}
{

M module-utils/i18n/JSONLoader.hpp => module-utils/i18n/JSONLoader.hpp +2 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 31,4 31,5 @@ namespace utils
            return js;
        }
    }

} // namespace utils

M module-utils/i18n/i18n.cpp => module-utils/i18n/i18n.cpp +16 -7
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "i18nImpl.hpp"


@@ 52,12 52,13 @@ namespace utils
        }

        i18nPrivateInterface localize;

    } // namespace

    void i18n::resetAssetsPath(const std::filesystem::path &assets)
    {
        displayLanguageDirPath = assets / "lang";
        inputLanguageDirPath   = assets / "profiles";
        DisplayLanguageDirPath = assets / "lang";
        InputLanguageDirPath   = assets / "profiles";
    }

    bool i18n::setInputLanguage(const Language &lang)


@@ 177,9 178,10 @@ namespace utils
        if (result != metadata.end()) {
            return *result;
        }
        return {};
        else {
            return {};
        }
    }

    std::optional<LanguageMetadata> i18n::fetchMetadata(const std::filesystem::path &path) const
    {
        if (const auto jsonData = loader(path)) {


@@ 187,7 189,6 @@ namespace utils
        }
        return {};
    }

    std::vector<Language> i18n::getAvailableDisplayLanguages() const
    {
        std::vector<Language> languages{metadata.size()};


@@ 197,6 198,14 @@ namespace utils
        std::sort(languages.begin(), languages.end());
        return languages;
    }
    std::vector<Language> i18n::getAvailableInputLanguages() const
    {
        std::vector<Language> languageNames;
        for (const auto &entry : std::filesystem::directory_iterator(getInputLanguagePath())) {
            languageNames.push_back(std::filesystem::path(entry.path()).stem());
        }
        return languageNames;
    }

    void resetDisplayLanguages()
    {


@@ 207,9 216,9 @@ namespace utils
    {
        return localize.resetAssetsPath(p);
    }

    std::vector<Language> getAvailableDisplayLanguages()
    {
        return localize.getAvailableDisplayLanguages();
    }

} // namespace utils

M module-utils/i18n/i18nImpl.hpp => module-utils/i18n/i18nImpl.hpp +7 -5
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 27,8 27,8 @@ namespace utils
        Language inputLanguage        = fallbackLanguageName;
        Language inputLanguageFilename;
        Language currentDisplayLanguage;
        std::filesystem::path inputLanguageDirPath   = purefs::dir::getSystemDataDirPath() / "profiles";
        std::filesystem::path displayLanguageDirPath = purefs::dir::getSystemDataDirPath() / "lang";
        std::filesystem::path InputLanguageDirPath   = purefs::dir::getSystemDataDirPath() / "profiles";
        std::filesystem::path DisplayLanguageDirPath = purefs::dir::getSystemDataDirPath() / "lang";
        cpp_freertos::MutexStandard mutex;
        std::vector<LanguageMetadata> metadata;



@@ 59,16 59,18 @@ namespace utils
        bool setDisplayLanguage(const Language &lang);
        const std::filesystem::path getInputLanguagePath() const
        {
            return inputLanguageDirPath;
            return InputLanguageDirPath;
        }
        const std::filesystem::path getDisplayLanguagePath() const
        {
            return displayLanguageDirPath;
            return DisplayLanguageDirPath;
        }

        std::vector<Language> getAvailableDisplayLanguages() const;
        std::vector<Language> getAvailableInputLanguages() const;

        void resetDisplayLanguages();
        void resetAssetsPath(const std::filesystem::path &);
    };

} // namespace utils

M module-utils/i18n/include/i18n/i18n.hpp => module-utils/i18n/include/i18n/i18n.hpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 14,8 14,8 @@ namespace utils
{
    namespace files
    {
        inline constexpr auto jsonExtension = ".json";
        inline constexpr auto breakSign     = "_";
        constexpr auto jsonExtension = ".json";
        constexpr auto breakSign     = "_";
    } // namespace files

    const std::string &translate(const std::string &text);

M products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp => products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp +1 -1
@@ 476,7 476,7 @@ namespace app::home_screen
        const auto key = mapKey(inputEvent.getKeyCode());
        switch (key) {
        case KeyMap::Back:
            if (inputEvent.getState() == gui::InputEvent::State::KeyReleasedLong) {
            if (inputEvent.getState() == gui::InputEvent::State::keyReleasedLong) {
                pimpl->sm->process_event(Events::LongBackPress{});
            }
            else {