~aleteoryx/muditaos

691c2926b9879a9f88ca2df15303641cfd99c78b — Krzysztof Mozdzynski 5 years ago dbc5da5
[EGD-3360] Input and display language separation, input language setting implementation (#920)

M image/user/settings.db => image/user/settings.db +0 -0
M module-apps/CMakeLists.txt => module-apps/CMakeLists.txt +15 -15
@@ 11,21 11,21 @@ else()
    message(FATAL_ERROR "Invalid target!")
endif()

set( SOURCES 
    "Application.cpp"
    "GuiTimer.cpp"
    "UiCommonActions.cpp"
    "WindowsFactory.cpp"
    "windows/AppWindow.cpp" 
    "windows/OptionWindow.cpp"
    "windows/Options.cpp" 
    "windows/OptionsWindowOption.cpp"
    "windows/Dialog.cpp"
    "windows/NoEvents.cpp"
    "windows/OptionSetting.cpp"
    "widgets/ButtonOnOff.cpp"
    "widgets/InputBox.cpp"
    )
set(SOURCES
        "Application.cpp"
        "GuiTimer.cpp"
        "UiCommonActions.cpp"
        "WindowsFactory.cpp"
        "windows/AppWindow.cpp"
        "windows/OptionWindow.cpp"
        "windows/Options.cpp"
        "windows/OptionsWindowOption.cpp"
        "windows/Dialog.cpp"
        "windows/NoEvents.cpp"
        "windows/OptionSetting.cpp"
        "widgets/ButtonOnOff.cpp"
        "widgets/InputBox.cpp"
        )

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


M module-apps/application-calculator/tests/CalculatorUtility_tests.cpp => module-apps/application-calculator/tests/CalculatorUtility_tests.cpp +2 -2
@@ 20,7 20,7 @@ struct vfs_initializer
TEST_CASE("Calculator utilities")
{
    auto calculator = Calculator();
    utils::localize.Switch(utils::Lang::En);
    utils::localize.SetDisplayLanguage(utils::Lang::En);

    SECTION("Addition")
    {


@@ 72,7 72,7 @@ TEST_CASE("Calculator utilities")

    SECTION("Fraction with comma")
    {
        utils::localize.Switch(utils::Lang::Pl);
        utils::localize.SetDisplayLanguage(utils::Lang::Pl);
        auto result = calculator.calculate("15,5+12,056");
        REQUIRE(result.value == "27,556");
        REQUIRE(result.equation == "15.5+12.056");

M module-apps/application-settings-new/widgets/SettingsStyle.hpp => module-apps/application-settings-new/widgets/SettingsStyle.hpp +6 -0
@@ 26,6 26,12 @@ namespace style
                constexpr uint32_t w = 24;
                constexpr uint32_t h = 24;
            } // namespace crossImage
            namespace languageChange
            {
                constexpr uint32_t options_posX             = 17;
                constexpr uint32_t options_posY             = 100;
                constexpr uint32_t options_distance_between = 60;
            } // namespace languageChange
        }     // namespace window
    };        // namespace settings
} // namespace style

M module-apps/application-settings-new/windows/InputLanguageWindow.cpp => module-apps/application-settings-new/windows/InputLanguageWindow.cpp +12 -5
@@ 7,6 7,7 @@
#include "windows/OptionSetting.hpp"

#include <i18/i18.hpp>
#include <module-services/service-appmgr/service-appmgr/Controller.hpp>

namespace gui
{


@@ 19,13 20,19 @@ namespace gui
    auto InputLanguageWindow::buildOptionsList() -> std::list<gui::Option>
    {
        std::list<gui::Option> optionsList;
        std::vector<std::string> availableLanguages = {"English", "Detush", "Polski", "Español", "Français"};
        std::vector<std::string> languageListJson = {"app_settings_language_english",
                                                     "app_settings_language_polish",
                                                     "app_settings_language_german",
                                                     "app_settings_language_spanish"};
        std::vector<utils::Lang> languageListEnum = {
            utils::Lang::En, utils::Lang::Pl, utils::Lang::De, utils::Lang::Sp};

        for (auto lang : availableLanguages) {
        for (size_t i = 0; i < languageListJson.size(); i++) {
            optionsList.emplace_back(std::make_unique<gui::OptionSettings>(
                lang,
                utils::localize.get(languageListJson[i]),
                [=](gui::Item &item) {
                    selectedLang = lang;
                    selectedLang = utils::localize.get(languageListJson[i]);
                    app::manager::Controller::changeInputLanguage(application, languageListEnum[i]);
                    rebuildOptionList();
                    return true;
                },


@@ 37,7 44,7 @@ namespace gui
                    return true;
                },
                this,
                selectedLang == lang ? RightItem::Checked : RightItem::Disabled));
                selectedLang == utils::localize.get(languageListJson[i]) ? RightItem::Checked : RightItem::Disabled));
        }

        return optionsList;

M module-apps/application-settings/windows/LanguageWindow.cpp => module-apps/application-settings/windows/LanguageWindow.cpp +15 -18
@@ 1,7 1,6 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <memory>
#include <functional>

#include "service-appmgr/Controller.hpp"


@@ 11,9 10,9 @@
#include "i18/i18.hpp"

#include "Label.hpp"
#include "Margins.hpp"
#include "LanguageWindow.hpp"
#include <Style.hpp>
#include <module-apps/application-settings-new/widgets/SettingsStyle.hpp>

namespace gui
{


@@ 21,6 20,7 @@ namespace gui
    LanguageWindow::LanguageWindow(app::Application *app) : AppWindow(app, "Languages")
    {
        buildInterface();
        setFocusItem(options[0]);
    }

    void LanguageWindow::rebuild()


@@ 50,39 50,36 @@ namespace gui

        setTitle(utils::localize.get("app_settings_title_languages"));

        // add option connectivity option
        options.push_back(addOptionLabel(utils::localize.get("app_settings_language_english"), [=](gui::Item &item) {
            LOG_INFO("selected language: english");
            app::manager::Controller::changeLanguage(application, utils::Lang::En);
            LOG_INFO("selected display language: english");
            app::manager::Controller::changeDisplayLanguage(application, utils::Lang::En);
            return true;
        }));

        // add option date and time option
        options.push_back(addOptionLabel(utils::localize.get("app_settings_language_polish"), [=](gui::Item &) {
            LOG_INFO("selected language: polish");
            app::manager::Controller::changeLanguage(application, utils::Lang::Pl);
            LOG_INFO("selected display language: polish");
            app::manager::Controller::changeDisplayLanguage(application, utils::Lang::Pl);
            return true;
        }));

        // add option display option
        options.push_back(addOptionLabel(utils::localize.get("app_settings_language_german"), [=](gui::Item &) {
            LOG_INFO("selected language: german");
            app::manager::Controller::changeLanguage(application, utils::Lang::De);
            LOG_INFO("selected display language: german");
            app::manager::Controller::changeDisplayLanguage(application, utils::Lang::De);
            return true;
        }));

        options.push_back(addOptionLabel(utils::localize.get("app_settings_language_spanish"), [=](gui::Item &) {
            LOG_INFO("selected language: spanish");
            app::manager::Controller::changeLanguage(application, utils::Lang::Sp);
            LOG_INFO("selected display language: spanish");
            app::manager::Controller::changeDisplayLanguage(application, utils::Lang::Sp);
            return true;
        }));

        // set possition and navigation for labels
        uint32_t posY = 100;
        // set position and navigation for labels
        uint32_t size = options.size();
        for (uint32_t i = 0; i < options.size(); i++) {
            options[i]->setPosition(17, posY);
            posY += 60;
        for (uint32_t i = 0; i < size; i++) {
            options[i]->setPosition(style::settings::window::languageChange::options_posX,
                                    style::settings::window::languageChange::options_posY +
                                        (i * style::settings::window::languageChange::options_distance_between));
            options[i]->setNavigationItem(NavigationDirection::DOWN, options[(i + 1) % size]);
            options[i]->setNavigationItem(NavigationDirection::UP, options[(size + i - 1) % size]);
        }

M module-db/Common/Common.hpp => module-db/Common/Common.hpp +8 -1
@@ 48,9 48,16 @@ enum class SettingsLanguage
    POLISH  = 0x02,
    GERMAN  = 0x04,
    SPANISH = 0x08,

};

namespace Language
{
    constexpr static bool ValidateLanguage(SettingsLanguage lang) noexcept
    {
        return lang >= SettingsLanguage::ENGLISH && lang <= SettingsLanguage::SPANISH;
    }
}; // namespace Language

enum class SettingsPinMode
{
    ALWAYS = 0,

M module-db/Interface/SettingsRecord.cpp => module-db/Interface/SettingsRecord.cpp +4 -2
@@ 43,7 43,8 @@ SettingsRecord SettingsRecordInterface::GetByID(uint32_t id)
                          .networkOperator = rec.networkOperator,
                          .lockPassHash    = rec.lockPassHash,
                          .lockTime        = rec.lockTime,
                          .language        = rec.language};
                          .displayLanguage = rec.displayLanguage,
                          .inputLanguage   = rec.inputLanguage};
}

bool SettingsRecordInterface::Update(const SettingsRecord &rec)


@@ 65,5 66,6 @@ bool SettingsRecordInterface::Update(const SettingsRecord &rec)
                                                        .networkOperator = rec.networkOperator,
                                                        .lockPassHash    = rec.lockPassHash,
                                                        .lockTime        = rec.lockTime,
                                                        .language        = rec.language});
                                                        .displayLanguage = rec.displayLanguage,
                                                        .inputLanguage   = rec.inputLanguage});
}

M module-db/Interface/SettingsRecord.hpp => module-db/Interface/SettingsRecord.hpp +2 -1
@@ 31,7 31,8 @@ struct SettingsRecord
    std::string networkOperator;
    uint32_t lockPassHash;
    uint32_t lockTime;
    SettingsLanguage language;
    SettingsLanguage displayLanguage;
    SettingsLanguage inputLanguage;

    static ActiveSim to(const uint32_t sim);
    static uint32_t from(const ActiveSim sim);

M module-db/Tables/SettingsTable.cpp => module-db/Tables/SettingsTable.cpp +30 -3
@@ 2,6 2,7 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "SettingsTable.hpp"
#include "magic_enum.hpp"

SettingsTable::SettingsTable(Database *db) : Table(db)
{}


@@ 30,6 31,29 @@ SettingsTableRow SettingsTable::getById(uint32_t id)
        return SettingsTableRow();
    }

    if (!Language::ValidateLanguage(static_cast<SettingsLanguage>((*retQuery)[16].getUInt32())) ||
        !Language::ValidateLanguage(static_cast<SettingsLanguage>((*retQuery)[17].getUInt32()))) {
        return SettingsTableRow{
            1,
            false,
            true,
            true,
            true,
            0,
            0,
            SettingsPinMode::DAYS,
            0,
            0,
            "",
            "",
            1,
            "",
            0,
            30000, // time of inactivity of the user after which phone will be automatically blocked.
            SettingsLanguage::ENGLISH,
            SettingsLanguage::ENGLISH};
    }

    return SettingsTableRow{
        (*retQuery)[0].getUInt32(),                                 // ID
        (*retQuery)[1].getBool(),                                   // timeFormat12


@@ 47,7 71,8 @@ SettingsTableRow SettingsTable::getById(uint32_t id)
        (*retQuery)[13].getString(),                                // networkOperator
        (*retQuery)[14].getUInt32(),                                // lockPassHash
        (*retQuery)[15].getUInt32(),                                // lockTime
        static_cast<SettingsLanguage>((*retQuery)[16].getUInt32()), // language
        static_cast<SettingsLanguage>((*retQuery)[16].getUInt32()), // displayLanguage
        static_cast<SettingsLanguage>((*retQuery)[17].getUInt32()), // inputLanguage

    };
}


@@ 58,7 83,8 @@ bool SettingsTable::update(SettingsTableRow entry)
                       ",brightness_auto = %lu, brightness_level = %lu, "
                       "bigger_font = %lu, pin_mode =%lu, pin_days = %lu ,pin_days_left = %lu, pin1_string = '%q', "
                       "pin2_string = '%q', active_sim = %lu, "
                       "network_operator = '%q', lock_pass_hash = %lu, lock_time = %lu, language = %lu WHERE _id=1;",
                       "network_operator = '%q', lock_pass_hash = %lu, lock_time = %lu, display_language = %lu, "
                       "input_language = %lu WHERE _id=1;",
                       entry.timeFormat12,
                       entry.timeAuto,
                       entry.timeDateFormat,


@@ 74,7 100,8 @@ bool SettingsTable::update(SettingsTableRow entry)
                       entry.networkOperator.c_str(),
                       entry.lockPassHash,
                       entry.lockTime,
                       entry.language);
                       entry.displayLanguage,
                       entry.inputLanguage);
}

bool SettingsTable::add(SettingsTableRow entry)

M module-db/Tables/SettingsTable.hpp => module-db/Tables/SettingsTable.hpp +7 -4
@@ 27,7 27,8 @@ struct SettingsTableRow
    uint32_t lockPassHash;
    // time of inactivity of the user after which phone will be automatically blocked.
    uint32_t lockTime;
    SettingsLanguage language;
    SettingsLanguage displayLanguage;
    SettingsLanguage inputLanguage;
};

enum class SettingsTableFields


@@ 73,12 74,14 @@ class SettingsTable : public Table<SettingsTableRow, SettingsTableFields>
                                   "network_operator TEXT DEFAULT '', "
                                   "lock_pass_hash INTEGER DEFAULT 0, "
                                   "lock_time INTEGER DEFAULT 30000, "
                                   "language INTEGER DEFAULT 1 "
                                   "display_language INTEGER DEFAULT 1, "
                                   "input_language INTEGER DEFAULT 1 "
                                   ");";

    const char *settingsInitialization = "INSERT OR IGNORE INTO settings (_id, time_format_12, time_auto, "
                                         "time_date_format, brightness_auto, brightness_level, "
                                         "bigger_font, pin_mode, pin_days, pin_days_left, pin1_string,pin2_string, "
                                         "active_sim,network_operator,lock_pass_hash,lock_time, language) "
                                         "VALUES(1,0,1,1,1,0,0,1,0,0,'','',1,'',0,30000,1)";
                                         "active_sim,network_operator,lock_pass_hash,lock_time, display_language, "
                                         "input_language) "
                                         "VALUES(1,0,1,1,1,0,0,1,0,0,'','',1,'',0,30000,1,1)";
};

M module-db/tests/SettingsRecord_tests.cpp => module-db/tests/SettingsRecord_tests.cpp +2 -1
@@ 42,7 42,8 @@ TEST_CASE("Settings Record tests")
    REQUIRE(settingsRecord.activeSIM == SettingsRecord::ActiveSim::SIM1);
    REQUIRE(settingsRecord.networkOperator == "");
    REQUIRE(settingsRecord.lockPassHash == 0);
    REQUIRE(settingsRecord.language == SettingsLanguage ::ENGLISH);
    REQUIRE(settingsRecord.displayLanguage == SettingsLanguage ::ENGLISH);
    REQUIRE(settingsRecord.inputLanguage == SettingsLanguage ::ENGLISH);

    settingsRecord.networkOperator = "MuditaConnectingPeople";
    settingsRecord.fontSize        = 10;

M module-db/tests/SettingsTable_tests.cpp => module-db/tests/SettingsTable_tests.cpp +7 -4
@@ 33,8 33,9 @@ TEST_CASE("Settings Table tests")
        settingsRow.timeFormat12   = false;
        settingsRow.timeDateFormat = false;
        settingsRow.pin1           = "4321";
        settingsRow.pin2           = "5432";
        settingsRow.language       = SettingsLanguage ::POLISH;
        settingsRow.pin2            = "5432";
        settingsRow.displayLanguage = SettingsLanguage ::POLISH;
        settingsRow.inputLanguage   = SettingsLanguage ::POLISH;
        REQUIRE(settingsDb.settings.update(settingsRow));

        settingsRow = settingsDb.settings.getById(1);


@@ 43,7 44,8 @@ TEST_CASE("Settings Table tests")
        REQUIRE(settingsRow.timeDateFormat == false);
        REQUIRE(settingsRow.pin1 == "4321");
        REQUIRE(settingsRow.pin2 == "5432");
        REQUIRE(settingsRow.language == SettingsLanguage ::POLISH);
        REQUIRE(settingsRow.displayLanguage == SettingsLanguage ::POLISH);
        REQUIRE(settingsRow.inputLanguage == SettingsLanguage ::POLISH);
    }

    {


@@ 55,7 57,8 @@ TEST_CASE("Settings Table tests")
        REQUIRE(settingsRow.timeDateFormat == false);
        REQUIRE(settingsRow.pin1 == "4321");
        REQUIRE(settingsRow.pin2 == "5432");
        REQUIRE(settingsRow.language == SettingsLanguage ::POLISH);
        REQUIRE(settingsRow.displayLanguage == SettingsLanguage ::POLISH);
        REQUIRE(settingsRow.inputLanguage == SettingsLanguage ::POLISH);
    }

    Database::deinitialize();

M module-gui/gui/input/Translator.cpp => module-gui/gui/input/Translator.cpp +0 -1
@@ 179,7 179,6 @@ namespace gui
        if (key.state == RawKey::State::Released) {
            prev_key_press = key;
        }

        return Profiles::get(keymap).get(key.key_code, times);
    }


M module-gui/gui/widgets/InputMode.cpp => module-gui/gui/widgets/InputMode.cpp +1 -1
@@ 60,7 60,7 @@ void InputMode::next()

const std::string &InputMode::get()
{
    return utils::localize.get(input_mode.at(modeNow()));
    return utils::localize.getInputLanguage(input_mode.at(modeNow()));
}

void InputMode::show_input_type()

M module-gui/test/test-catch-text/test-gui-Text.cpp => module-gui/test/test-catch-text/test-gui-Text.cpp +1 -1
@@ 125,7 125,7 @@ TEST_CASE("Text buildDrawList")

TEST_CASE("handle input mode ABC/abc/1234")
{
    utils::localize.Switch(utils::Lang::En); /// needed to load input mode
    utils::localize.SetDisplayLanguage(utils::Lang::En); /// needed to load input mode
    auto &fontmanager = mockup::fontManager();
    auto font         = fontmanager.getFont(0);
    auto text         = gui::TestText();

M module-services/service-appmgr/Controller.cpp => module-services/service-appmgr/Controller.cpp +8 -2
@@ 64,9 64,15 @@ namespace app::manager
        return sys::Bus::SendUnicast(switchMsg, ApplicationManager::ServiceName, sender);
    }

    auto Controller::changeLanguage(sys::Service *sender, utils::Lang language) -> bool
    auto Controller::changeDisplayLanguage(sys::Service *sender, utils::Lang language) -> bool
    {
        auto msg = std::make_shared<app::manager::LanguageChangeRequest>(sender->GetName(), language);
        auto msg = std::make_shared<app::manager::DisplayLanguageChangeRequest>(sender->GetName(), language);
        return sys::Bus::SendUnicast(msg, ApplicationManager::ServiceName, sender);
    }

    auto Controller::changeInputLanguage(sys::Service *sender, utils::Lang language) -> bool
    {
        auto msg = std::make_shared<app::manager::InputLanguageChangeRequest>(sender->GetName(), language);
        return sys::Bus::SendUnicast(msg, ApplicationManager::ServiceName, sender);
    }


M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +30 -10
@@ 141,7 141,8 @@ namespace app::manager
    {
        settings = DBServiceAPI::SettingsGet(this);
        blockingTimer->setInterval(settings.lockTime != 0 ? settings.lockTime : default_application_locktime_ms);
        utils::localize.Switch(toUtilsLanguage(settings.language));
        utils::localize.SetDisplayLanguage(toUtilsLanguage(settings.displayLanguage));
        utils::localize.setInputLanguage(toUtilsLanguage(settings.inputLanguage));

        startSystemServices();
        startBackgroundApplications();


@@ 240,9 241,14 @@ namespace app::manager
            handleInitApplication(msg);
            return std::make_shared<sys::ResponseMessage>();
        });
        connect(typeid(LanguageChangeRequest), [this](sys::DataMessage *request, sys::ResponseMessage *) {
            auto msg = static_cast<LanguageChangeRequest *>(request);
            handleLanguageChange(msg);
        connect(typeid(DisplayLanguageChangeRequest), [this](sys::DataMessage *request, sys::ResponseMessage *) {
            auto msg = static_cast<DisplayLanguageChangeRequest *>(request);
            handleDisplayLanguageChange(msg);
            return std::make_shared<sys::ResponseMessage>();
        });
        connect(typeid(InputLanguageChangeRequest), [this](sys::DataMessage *request, sys::ResponseMessage *) {
            auto msg = static_cast<InputLanguageChangeRequest *>(request);
            handleInputLanguageChange(msg);
            return std::make_shared<sys::ResponseMessage>();
        });
        connect(typeid(ShutdownRequest), [this](sys::DataMessage *, sys::ResponseMessage *) {


@@ 516,20 522,34 @@ namespace app::manager
        Controller::switchBack(this);
    }

    auto ApplicationManager::handleLanguageChange(app::manager::LanguageChangeRequest *msg) -> bool
    auto ApplicationManager::handleDisplayLanguageChange(app::manager::DisplayLanguageChangeRequest *msg) -> bool
    {
        const auto requestedLanguage = toSettingsLanguage(msg->getLanguage());
        if (requestedLanguage == settings.language) {
        settings                     = DBServiceAPI::SettingsGet(this);

        if (requestedLanguage == settings.displayLanguage) {
            LOG_WARN("The selected language is already set. Ignore.");
            return true;
        }
        settings.displayLanguage = requestedLanguage;
        utils::localize.SetDisplayLanguage(msg->getLanguage());
        rebuildActiveApplications();
        DBServiceAPI::SettingsUpdate(this, settings);
        return true;
    }

        settings          = DBServiceAPI::SettingsGet(this);
        settings.language = requestedLanguage;
    auto ApplicationManager::handleInputLanguageChange(app::manager::InputLanguageChangeRequest *msg) -> bool
    {
        const auto requestedLanguage = toSettingsLanguage(msg->getLanguage());
        settings                     = DBServiceAPI::SettingsGet(this);

        if (requestedLanguage == settings.inputLanguage) {
            LOG_WARN("The selected language is already set. Ignore.");
            return true;
        }
        settings.inputLanguage = requestedLanguage;
        utils::localize.setInputLanguage(msg->getLanguage());
        DBServiceAPI::SettingsUpdate(this, settings);
        utils::localize.Switch(msg->getLanguage());
        rebuildActiveApplications();
        return true;
    }


M module-services/service-appmgr/service-appmgr/Controller.hpp => module-services/service-appmgr/service-appmgr/Controller.hpp +2 -1
@@ 38,7 38,8 @@ namespace app::manager
                                      std::unique_ptr<gui::SwitchData> data = nullptr) -> bool;
        static auto switchBack(sys::Service *sender, std::unique_ptr<SwitchBackRequest> msg = nullptr) -> bool;
        static auto closeApplication(sys::Service *sender, const ApplicationName &name) -> bool;
        static auto changeLanguage(sys::Service *sender, utils::Lang language) -> bool;
        static auto changeDisplayLanguage(sys::Service *sender, utils::Lang language) -> bool;
        static auto changeInputLanguage(sys::Service *sender, utils::Lang language) -> bool;
        static auto changePowerSaveMode(sys::Service *sender) -> bool;
        static auto stopApplicationManager(sys::Service *sender) -> bool;
        static auto preventBlockingDevice(sys::Service *sender) -> bool;

M module-services/service-appmgr/service-appmgr/Message.hpp => module-services/service-appmgr/service-appmgr/Message.hpp +20 -3
@@ 141,11 141,28 @@ namespace app::manager
        ApplicationName application;
    };

    /// Requests to change the language.
    class LanguageChangeRequest : public Message
    /// Requests to change the display language.
    class DisplayLanguageChangeRequest : public Message
    {
      public:
        LanguageChangeRequest(const ApplicationName &senderName, utils::Lang language)
        DisplayLanguageChangeRequest(const ApplicationName &senderName, utils::Lang language)
            : Message(MessageType::APMChangeLanguage, senderName), language{language}
        {}

        [[nodiscard]] auto getLanguage() const noexcept -> utils::Lang
        {
            return language;
        }

      private:
        utils::Lang language;
    };

    /// Requests to change the input language.
    class InputLanguageChangeRequest : public Message
    {
      public:
        InputLanguageChangeRequest(const ApplicationName &senderName, utils::Lang language)
            : Message(MessageType::APMChangeLanguage, senderName), language{language}
        {}


M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp => module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp +2 -1
@@ 116,7 116,8 @@ namespace app::manager
        auto handleSwitchConfirmation(SwitchConfirmation *msg) -> bool;
        auto handleSwitchBack(SwitchBackRequest *msg) -> bool;
        auto handleInitApplication(ApplicationInitialisation *msg) -> bool;
        auto handleLanguageChange(LanguageChangeRequest *msg) -> bool;
        auto handleDisplayLanguageChange(DisplayLanguageChangeRequest *msg) -> bool;
        auto handleInputLanguageChange(InputLanguageChangeRequest *msg) -> bool;
        auto handlePowerSavingModeInit() -> bool;

        void requestApplicationClose(ApplicationHandle &app, bool isCloseable);

M module-services/service-db/agents/settings/Settings.cpp => module-services/service-db/agents/settings/Settings.cpp +0 -1
@@ 206,7 206,6 @@ namespace Settings
        else {
            sendMsg(std::make_shared<::Settings::Messages::RegisterOnModeChange>());
        }

        cbMode = cb;
    }


M module-utils/i18/i18.cpp => module-utils/i18/i18.cpp +119 -1
@@ 5,7 5,125 @@

namespace utils
{
    namespace
    {
        auto returnNonEmptyString(const std::string &str, const std::string &ret) -> const std::string &
        {
            return str.empty() ? ret : str;
        }
    } // namespace

    i18 localize;
    json11::Json LangLoader::Create(Lang lang)
    {
        const char *path = nullptr;

}
        switch (lang) {
        case Lang::Pl:
            path = langPL_path;
            break;
        case Lang::En:
            path = langEN_path;
            break;
        case Lang::De:
            path = langDE_path;
            break;
        case Lang::Sp:
            path = langSP_path;
            break;

        default:
            return json11::Json();
        }

        auto fd = vfs.fopen(path, "r");
        if (fd == nullptr) {
            LOG_FATAL("Error during opening file %s", path);
            return json11::Json();
        }

        uint32_t fsize = vfs.filelength(fd);

        auto stream = new char[fsize + 1]; // +1 for NULL terminator

        memset(stream, 0, fsize + 1);

        vfs.fread(stream, 1, fsize, fd);

        std::string err;
        json11::Json js = json11::Json::parse(stream, err);

        delete[] stream;
        vfs.fclose(fd);

        // Error
        if (err.length() != 0) {
            LOG_FATAL("%s", err.c_str());
            return json11::Json();
        }
        else {
            return js;
        }
    }

    void i18::setInputLanguage(Lang lang)
    {
        if (!input_init) {
            backupLang = loader.Create(lang_default);
            inputLang  = backupLang;
            input_init = true;
        }
        if (lang == inputCurrent) {
            return;
        }
        inputCurrent = lang;
        if (lang == lang_default) {
            inputLang = backupLang;
        }
        else {
            json11::Json pack = loader.Create(lang);
            inputLang         = pack;
        }
    }
    const std::string &i18::getInputLanguage(const std::string &str)
    {
        // if language pack returned nothing then try default language
        if (inputLang[str].string_value().empty()) {
            return returnNonEmptyString(backupLang[str].string_value(), str);
        }
        return returnNonEmptyString(inputLang[str].string_value(), str);
    }

    const std::string &i18::get(const std::string &str)
    {
        // if language pack returned nothing then try default language
        if (displayLang[str].string_value().empty()) {
            return returnNonEmptyString(backupLang[str].string_value(), str);
        }
        return returnNonEmptyString(displayLang[str].string_value(), str);
    }

    void i18::SetDisplayLanguage(Lang lang)
    {
        if (!display_init) {
            backupLang   = loader.Create(lang_default);
            displayLang  = backupLang;
            display_init = true;
        }
        if (lang == displayCurrent) {
            return;
        }
        displayCurrent = lang;
        if (lang == lang_default) {
            displayLang = backupLang;
        }
        else {
            json11::Json pack = loader.Create(lang);
            // Suspend whole system during switching lang packs
            vTaskSuspendAll();
            displayLang = pack;
            xTaskResumeAll();
        }
    }

} // namespace utils

M module-utils/i18/i18.hpp => module-utils/i18/i18.hpp +12 -102
@@ 36,73 36,21 @@ namespace utils
        virtual ~LangLoader()
        {}

        json11::Json Create(Lang lang)
        {
            std::string err;
            const char *path = nullptr;

            switch (lang) {
            case Lang::Pl:
                path = langPL_path;
                break;
            case Lang::En:
                path = langEN_path;
                break;
            case Lang::De:
                path = langDE_path;
                break;
            case Lang::Sp:
                path = langSP_path;
                break;

            default:
                return json11::Json();
            }

            auto fd = vfs.fopen(path, "r");
            if (fd == NULL) {
                LOG_FATAL("Error during opening file %s", path);
                return json11::Json();
            }

            uint32_t fsize = vfs.filelength(fd);

            char *stream = static_cast<char *>(malloc(fsize + 1)); // +1 for NULL terminator
            if (stream == NULL) {
                LOG_FATAL("Memory allocation failure");
                vfs.fclose(fd);
                return json11::Json();
            }

            memset(stream, 0, fsize + 1);

            vfs.fread(stream, 1, fsize, fd);

            json11::Json js = json11::Json::parse(stream, err);

            free(stream);
            vfs.fclose(fd);

            // Error
            if (err.length() != 0) {
                LOG_FATAL("%s", err.c_str());
                return json11::Json();
            }
            else {
                return js;
            }
        }
        json11::Json Create(Lang lang);
    };

    class i18
    {

        json11::Json langPack;
        json11::Json langBack; // backup language if item not found
        json11::Json displayLang;
        json11::Json inputLang;
        json11::Json backupLang; // backup language if item not found
        LangLoader loader;
        static const Lang lang_default = Lang::En;
        Lang current                   = lang_default;
        bool debugRequests             = true;
        Lang displayCurrent            = lang_default;
        Lang inputCurrent              = lang_default;
        bool display_init              = false;
        bool input_init                = false;

      public:
        // Default constructor, left empty on purpose


@@ 113,48 61,10 @@ namespace utils
        // which is not available at program's startup.
        virtual ~i18()
        {}

        const std::string &get(const std::string &str)
        {
            auto retwithfallback = [this](const std::string &ret, const std::string &str) -> const std::string & {
                if (debugRequests && ret == "") {
                    return str;
                }
                else {
                    return ret;
                }
            };
            auto &ret = langPack[str].string_value();
            // if language pack returnet nothing then try default language
            if (ret == "" && current != lang_default) {
                auto &ret = langBack[str].string_value();
                return retwithfallback(ret, str);
            }
            return retwithfallback(ret, str);
        }

        void Switch(Lang lang)
        {
            static bool initialized = false;
            if (!initialized) {
                langBack    = loader.Create(lang_default);
                langPack    = langBack;
                initialized = true;
            }
            if (lang == current)
                return;
            current = lang;
            if (lang == lang_default) {
                langPack = langBack;
            }
            else {
                json11::Json pack = loader.Create(lang);
                // Suspend whole system during switching lang packs
                vTaskSuspendAll();
                langPack = pack;
                xTaskResumeAll();
            }
        }
        void setInputLanguage(Lang lang);
        const std::string &getInputLanguage(const std::string &str);
        const std::string &get(const std::string &str);
        void SetDisplayLanguage(Lang lang);
    };

    // Global instance of i18 class

M module-utils/test/test_time_conversion.cpp => module-utils/test/test_time_conversion.cpp +1 -1
@@ 240,7 240,7 @@ int main(int argc, char *argv[])
    }

    // set locale to PL
    utils::localize.Switch(utils::Lang::Pl);
    utils::localize.SetDisplayLanguage(utils::Lang::Pl);
    std::setlocale(LC_TIME, "pl_PL.UTF-8");

    // set default output to nullstream

M module-utils/test/unittest_duration.cpp => module-utils/test/unittest_duration.cpp +4 -4
@@ 25,7 25,7 @@ struct vfs_initializer

TEST_CASE("Duration - creation")
{
    utils::localize.Switch(utils::Lang::En);
    utils::localize.SetDisplayLanguage(utils::Lang::En);

    SECTION("default constructor")
    {


@@ 68,7 68,7 @@ TEST_CASE("Duration - creation")

TEST_CASE("Duration - arithemtics")
{
    utils::localize.Switch(utils::Lang::En);
    utils::localize.SetDisplayLanguage(utils::Lang::En);

    SECTION("Addition")
    {


@@ 106,7 106,7 @@ TEST_CASE("Duration - arithemtics")

TEST_CASE("Duration - comparision")
{
    utils::localize.Switch(utils::Lang::En);
    utils::localize.SetDisplayLanguage(utils::Lang::En);

    SECTION("Duration")
    {


@@ 150,7 150,7 @@ TEST_CASE("Duration - comparision")

TEST_CASE("Duration - display")
{
    utils::localize.Switch(utils::Lang::En);
    utils::localize.SetDisplayLanguage(utils::Lang::En);

    {
        using namespace utils::time;