~aleteoryx/muditaos

a53bd826c693062af27c5de0f62e06963a6ce842 — Lucjan Bryndza 5 years ago be66c10 + c8a6c4b
[EGD-5737] Merge master into experimental

Keep experimental up to date with changes from master
101 files changed, 1775 insertions(+), 559 deletions(-)

M .gitmodules
M CMakeLists.txt
M image/assets/lang/English.json
M module-apps/Application.cpp
M module-apps/application-calendar/ApplicationCalendar.cpp
M module-apps/application-calendar/ApplicationCalendar.hpp
M module-apps/application-calendar/data/CalendarData.hpp
M module-apps/application-calendar/data/dateCommon.hpp
M module-apps/application-calendar/models/DayEventsModel.cpp
M module-apps/application-calendar/models/DayEventsModel.hpp
M module-apps/application-calendar/models/MonthModel.cpp
M module-apps/application-calendar/models/MonthModel.hpp
M module-apps/application-calendar/widgets/EventTimeItem.cpp
M module-apps/application-calendar/widgets/EventTimeItem.hpp
M module-apps/application-calendar/widgets/MonthBox.cpp
M module-apps/application-calendar/widgets/MonthBox.hpp
M module-apps/application-calendar/windows/AllEventsWindow.hpp
M module-apps/application-calendar/windows/CalendarMainWindow.cpp
M module-apps/application-calendar/windows/CalendarMainWindow.hpp
M module-apps/application-calendar/windows/DayEventsWindow.hpp
M module-apps/application-call/ApplicationCall.cpp
M module-apps/application-call/ApplicationCall.hpp
M module-apps/application-call/CMakeLists.txt
M module-apps/application-call/data/CallAppStyle.hpp
M module-apps/application-call/windows/EmergencyCallWindow.cpp
M module-apps/application-call/windows/EmergencyCallWindow.hpp
M module-apps/application-call/windows/EnterNumberWindow.cpp
M module-apps/application-call/windows/EnterNumberWindow.hpp
A module-apps/application-call/windows/NumberWindow.cpp
A module-apps/application-call/windows/NumberWindow.hpp
M module-apps/application-desktop/windows/PinLockWindow.cpp
M module-apps/application-settings-new/ApplicationSettings.cpp
M module-apps/application-settings-new/ApplicationSettings.hpp
M module-apps/application-settings-new/CMakeLists.txt
A module-apps/application-settings-new/data/LanguagesData.hpp
A module-apps/application-settings-new/models/LanguagesModel.cpp
A module-apps/application-settings-new/models/LanguagesModel.hpp
A module-apps/application-settings-new/windows/LanguagesWindow.cpp
A module-apps/application-settings-new/windows/LanguagesWindow.hpp
M module-apps/application-settings-new/windows/SystemMainWindow.cpp
M module-apps/windows/OptionWindow.hpp
M module-bsp/board/linux/eink/ED028TC1.c
M module-bsp/board/linux/eink/ED028TC1.h
M module-bsp/board/rt1051/bsp/eink/ED028TC1.cpp
M module-bsp/board/rt1051/bsp/eink/ED028TC1.h
M module-bsp/board/rt1051/bsp/eink/bsp_eink.h
M module-cellular/at/ErrorCode.hpp
M module-cellular/at/response.cpp
M module-cellular/at/response.hpp
M module-cellular/test/unittest_response.cpp
M module-db/CMakeLists.txt
M module-db/Interface/AlarmsRecord.hpp
M module-db/Interface/EventsRecord.cpp
M module-db/Interface/EventsRecord.hpp
M module-db/Tables/AlarmsTable.cpp
M module-db/Tables/AlarmsTable.hpp
M module-db/Tables/EventsTable.cpp
M module-db/Tables/EventsTable.hpp
M module-db/queries/calendar/QueryEventsGetFiltered.cpp
M module-db/queries/calendar/QueryEventsGetFiltered.hpp
A module-db/queries/calendar/QueryEventsGetFilteredByDay.cpp
A module-db/queries/calendar/QueryEventsGetFilteredByDay.hpp
M module-db/queries/calendar/QueryEventsSelectFirstUpcoming.cpp
M module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp
M module-db/tests/AlarmsRecord_tests.cpp
M module-db/tests/AlarmsTable_tests.cpp
M module-db/tests/EventsRecord_tests.cpp
M module-gui/gui/widgets/ListView.cpp
M module-gui/gui/widgets/ListView.hpp
M module-services/service-appmgr/data/MmiActionsParams.cpp
M module-services/service-appmgr/model/ApplicationManager.cpp
M module-services/service-appmgr/service-appmgr/Actions.hpp
M module-services/service-appmgr/service-appmgr/data/MmiActionsParams.hpp
A module-services/service-appmgr/service-appmgr/messages/GetCurrentDisplayLanguageRequest.hpp
A module-services/service-appmgr/service-appmgr/messages/GetCurrentDisplayLanguageResponse.hpp
M module-services/service-appmgr/service-appmgr/messages/Message.hpp
M module-services/service-cellular/CellularRequestHandler.cpp
M module-services/service-cellular/ServiceCellular.cpp
M module-services/service-cellular/service-cellular/CellularRequestHandler.hpp
M module-services/service-cellular/service-cellular/RequestHandler.hpp
M module-services/service-db/DBServiceAPI.cpp
M module-services/service-db/ServiceDB.cpp
M module-services/service-eink/CMakeLists.txt
M module-services/service-eink/EinkDisplay.cpp
M module-services/service-eink/EinkDisplay.hpp
M module-services/service-eink/ServiceEink.cpp
M module-services/service-eink/ServiceEink.hpp
R module-services/service-eink/messages/{PrepareDisplayRequest => PrepareDisplayEarlyRequest}.cpp
R module-services/service-eink/messages/{PrepareDisplayRequest => PrepareDisplayEarlyRequest}.hpp
M module-services/service-gui/ServiceGUI.cpp
M module-services/service-gui/ServiceGUI.hpp
R module-services/service-gui/messages/{EinkReady => EinkInitialized}.hpp
M module-services/service-time/service-time/CalendarTimeEvents.hpp
M module-services/service-time/timeEvents/CalendarTimeEvents.cpp
M module-utils/ical/ParserICS.cpp
M module-utils/ical/ParserICS.hpp
M module-utils/time/TimeRangeParser.cpp
M module-utils/time/TimeRangeParser.hpp
M module-utils/time/time_conversion.hpp
M test/pytest/service-desktop/test_calendar.py
M tools/download_asset.py
M .gitmodules => .gitmodules +1 -0
@@ 59,6 59,7 @@
[submodule "module-bsp/board/rt1051/bsp/usb"]
	path = module-bsp/board/rt1051/bsp/usb
	url = ../usb_stack.git
	branch = stable
[submodule "module-vfs/thirdparty/fatfs"]
	path = module-vfs/thirdparty/fatfs
	url = ../fatfs.git

M CMakeLists.txt => CMakeLists.txt +9 -5
@@ 281,7 281,7 @@ if (${PROJECT_TARGET} STREQUAL "TARGET_RT1051")
            -w ${CMAKE_BINARY_DIR} ecoboot download
            > ${ECOBOOT_DOWNLOAD_LOG}
            COMMENT "Downloading ecoboot.bin"
            BYPRODUCTS ${ECOBOOT_FILE} ${ECOBOOT_DOWNLOAD_LOG}
            BYPRODUCTS ${ECOBOOT_DOWNLOAD_LOG}
            )
    else()
        message("ecoboot download with git")


@@ 290,11 290,13 @@ if (${PROJECT_TARGET} STREQUAL "TARGET_RT1051")
            -w ${CMAKE_BINARY_DIR} ecoboot download
            > ${ECOBOOT_DOWNLOAD_LOG}
            COMMENT "Downloading ecoboot.bin"
            BYPRODUCTS ${ECOBOOT_FILE} ${ECOBOOT_DOWNLOAD_LOG}
            BYPRODUCTS ${ECOBOOT_DOWNLOAD_LOG}
            )
    endif()

    add_custom_target(ecoboot.bin DEPENDS ${ECOBOOT_FILE})
    if(NOT ${CMAKE_GENERATOR} STREQUAL "Ninja")
        add_custom_target(ecoboot.bin DEPENDS ${ECOBOOT_FILE})
    endif()

    # generate version.json file (runs CMake script at build time)
    set(VERSION_JSON ${CMAKE_BINARY_DIR}/version.json)


@@ 305,9 307,11 @@ if (${PROJECT_TARGET} STREQUAL "TARGET_RT1051")
        -B ${CMAKE_BINARY_DIR}
        -P ${CMAKE_SOURCE_DIR}/config/GenUpdateVersionJson.cmake
        DEPENDS ecoboot.bin ${ECOBOOT_DOWNLOAD_LOG}
        BYPRODUCTS ${VERSION_JSON}
        )
    add_custom_target(version.json DEPENDS ${VERSION_JSON})

    if(NOT ${CMAKE_GENERATOR} STREQUAL "Ninja")
        add_custom_target(version.json DEPENDS ${VERSION_JSON})
    endif()

    multicomp_install(PROGRAMS ${ECOBOOT_FILE} DESTINATION "./" COMPONENTS Standalone Update)
    multicomp_install(FILES ${VERSION_JSON} DESTINATION "./" COMPONENTS Standalone Update)

M image/assets/lang/English.json => image/assets/lang/English.json +2 -0
@@ 158,6 158,8 @@
  "app_notes_no_notes": "<text align='center' color='9'>No notes yet.<p>Press <b>left arrow</b> to add new.</p></text>",
  "app_notes_search_no_results": "No notes found.",

  "app_call_emergency_text": "Emergency call",

  "app_calllog_title_main": "Calls",
  "app_calllog_new_note": "New Note",
  "app_calllog_save": "SAVE",

M module-apps/Application.cpp => module-apps/Application.cpp +3 -1
@@ 727,7 727,9 @@ namespace app
    void Application::setLockScreenPasscodeOn(bool screenPasscodeOn) noexcept
    {
        lockScreenPasscodeIsOn = screenPasscodeOn;
        settings->setValue(settings::SystemProperties::lockScreenPasscodeIsOn, std::to_string(lockScreenPasscodeIsOn));
        settings->setValue(settings::SystemProperties::lockScreenPasscodeIsOn,
                           std::to_string(lockScreenPasscodeIsOn),
                           settings::SettingsScope::Global);
    }

    bool Application::isLockScreenPasscodeOn() const noexcept

M module-apps/application-calendar/ApplicationCalendar.cpp => module-apps/application-calendar/ApplicationCalendar.cpp +1 -1
@@ 147,7 147,7 @@ namespace app
    void ApplicationCalendar::destroyUserInterface()
    {}

    void ApplicationCalendar::switchToNoEventsWindow(const std::string &title, const calendar::TimePoint &dateFilter)
    void ApplicationCalendar::switchToNoEventsWindow(const std::string &title, const TimePoint &dateFilter)
    {
        if (equivalentWindow == EquivalentWindow::DayEventsWindow) {
            popToWindow(gui::name::window::main_window);

M module-apps/application-calendar/ApplicationCalendar.hpp => module-apps/application-calendar/ApplicationCalendar.hpp +1 -2
@@ 55,8 55,7 @@ namespace app
        }
        void createUserInterface() override;
        void destroyUserInterface() override;
        void switchToNoEventsWindow(const std::string &title              = "",
                                    const calendar::TimePoint &dateFilter = calendar::TimePoint());
        void switchToNoEventsWindow(const std::string &title = "", const TimePoint &dateFilter = TimePoint());

        static const std::map<Reminder, const char *> reminderOptions;
        static const std::map<Repeat, const char *> repeatOptions;

M module-apps/application-calendar/data/CalendarData.hpp => module-apps/application-calendar/data/CalendarData.hpp +3 -3
@@ 42,7 42,7 @@ class DayMonthData : public gui::SwitchData
{
  protected:
    std::string dayMonth;
    calendar::TimePoint dateFilter;
    TimePoint dateFilter;

  public:
    DayMonthData()          = default;


@@ 52,12 52,12 @@ class DayMonthData : public gui::SwitchData
        return dayMonth;
    };

    calendar::TimePoint getDateFilter()
    TimePoint getDateFilter()
    {
        return dateFilter;
    };

    virtual void setData(std::string dayMonthText, const calendar::TimePoint &dateNumb)
    virtual void setData(std::string dayMonthText, const TimePoint &dateNumb)
    {
        dayMonth   = dayMonthText;
        dateFilter = dateNumb;

M module-apps/application-calendar/data/dateCommon.hpp => module-apps/application-calendar/data/dateCommon.hpp +27 -26
@@ 9,15 9,16 @@
#include <Utils.hpp>
#include <random>

using Clock     = std::chrono::system_clock;
using TimePoint = std::chrono::time_point<Clock>;

namespace calendar
{
    using Clock            = std::chrono::system_clock;
    using TimePoint        = std::chrono::time_point<Clock>;
    using YearMonthDay     = date::year_month_day;
    using YearMonthDayLast = date::year_month_day_last;
} // namespace calendar

inline constexpr auto max_month_day = 48;
inline constexpr auto max_month_day = 31;

enum class Reminder
{


@@ 43,7 44,7 @@ enum class Repeat
    yearly
};

inline constexpr calendar::TimePoint TIME_POINT_INVALID = date::sys_days{date::January / 1 / 1970};
inline constexpr TimePoint TIME_POINT_INVALID = date::sys_days{date::January / 1 / 1970};
inline constexpr uint32_t yearDigitsNumb = 4, monthDigitsNumb = 2, dayDigitsNumb = 2, HourDigitsNumb = 2,
                          MinDigitsNumb = 2, SecDigitsNumb = 2;



@@ 88,34 89,34 @@ inline time_t GetAsUTCTime(int year, int month, int day, int hour = 0, int minut
    return basetime + GetDiffLocalWithUTCTime();
}

inline calendar::TimePoint TimePointFromTimeT(const time_t &time)
inline TimePoint TimePointFromTimeT(const time_t &time)
{
    return std::chrono::system_clock::from_time_t(time);
}

inline time_t TimePointToTimeT(const calendar::TimePoint &tp)
inline time_t TimePointToTimeT(const TimePoint &tp)
{
    return std::chrono::system_clock::to_time_t(tp);
}

inline calendar::TimePoint TimePointNow()
inline TimePoint TimePointNow()
{
    utils::time::Timestamp timestamp;
    return TimePointFromTimeT(timestamp.getTime());
}

inline std::string TimePointToString(const calendar::TimePoint &tp)
inline std::string TimePointToString(const TimePoint &tp)
{
    return date::format("%F %T", std::chrono::time_point_cast<std::chrono::seconds>(tp));
}

inline auto TimePointToHourMinSec(const calendar::TimePoint &tp)
inline auto TimePointToHourMinSec(const TimePoint &tp)
{
    auto dp = date::floor<date::days>(tp);
    return date::make_time(tp - dp);
}

inline uint32_t TimePointToHour24H(const calendar::TimePoint &tp)
inline uint32_t TimePointToHour24H(const TimePoint &tp)
{
    auto time = TimePointToTimeT(tp);
    utils::time::Timestamp timestamp(time);


@@ 123,7 124,7 @@ inline uint32_t TimePointToHour24H(const calendar::TimePoint &tp)
    return hour;
}

inline uint32_t TimePointToMinutes(const calendar::TimePoint &tp)
inline uint32_t TimePointToMinutes(const TimePoint &tp)
{
    auto time = TimePointToTimeT(tp);
    utils::time::Timestamp timestamp(time);


@@ 131,7 132,7 @@ inline uint32_t TimePointToMinutes(const calendar::TimePoint &tp)
    return minute;
}

inline calendar::TimePoint getFirstWeekDay(const calendar::TimePoint &tp)
inline TimePoint getFirstWeekDay(const TimePoint &tp)
{
    auto time_of_day  = TimePointToHourMinSec(tp);
    auto yearMonthDay = date::year_month_day{date::floor<date::days>(tp)};


@@ 145,12 146,12 @@ inline calendar::TimePoint getFirstWeekDay(const calendar::TimePoint &tp)
    return finalDateTime;
}

inline std::string TimePointToString(const calendar::TimePoint &tp, date::months months)
inline std::string TimePointToString(const TimePoint &tp, date::months months)
{
    date::year_month_day yearMonthDay     = date::year_month_day{date::floor<date::days>(tp)};
    date::year_month_day yearMonthDayLast = yearMonthDay.year() / yearMonthDay.month() / date::last;

    calendar::TimePoint timePoint;
    TimePoint timePoint;

    if ((static_cast<unsigned>(yearMonthDay.month()) + months.count()) <= 12) {
        if (yearMonthDayLast.day() == yearMonthDay.day()) {


@@ 194,38 195,38 @@ inline std::string TimePointToString(const calendar::TimePoint &tp, date::years 
        std::chrono::time_point_cast<std::chrono::seconds>(timePoint + time_of_day.hours() + time_of_day.minutes()));
}

inline std::string TimePointToLocalizedDateString(const calendar::TimePoint &tp, const std::string format = "")
inline std::string TimePointToLocalizedDateString(const TimePoint &tp, const std::string format = "")
{
    auto time = TimePointToTimeT(tp);
    utils::time::Date timestamp(time);
    return timestamp.str(format);
}

inline std::string TimePointToLocalizedTimeString(const calendar::TimePoint &tp, const std::string format = "")
inline std::string TimePointToLocalizedTimeString(const TimePoint &tp, const std::string format = "")
{
    auto time = TimePointToTimeT(tp);
    utils::time::Time timestamp(time);
    return timestamp.str(format);
}

inline calendar::TimePoint TimePointFromString(const char *s1)
inline TimePoint TimePointFromString(const char *s1)
{
    calendar::TimePoint tp;
    TimePoint tp;
    std::istringstream(s1) >> date::parse("%F %T", tp);
    return tp;
}

inline calendar::YearMonthDay TimePointToYearMonthDay(const calendar::TimePoint &tp)
inline calendar::YearMonthDay TimePointToYearMonthDay(const TimePoint &tp)
{
    return date::year_month_day{date::floor<date::days>(tp)};
}

inline calendar::TimePoint TimePointFromYearMonthDay(const calendar::YearMonthDay &ymd)
inline TimePoint TimePointFromYearMonthDay(const calendar::YearMonthDay &ymd)
{
    return date::sys_days{ymd.year() / ymd.month() / ymd.day()};
}

inline time_t TimePointToMin(const calendar::TimePoint &tp)
inline time_t TimePointToMin(const TimePoint &tp)
{
    auto time     = TimePointToTimeT(tp);
    auto duration = new utils::time::Duration(time);


@@ 233,7 234,7 @@ inline time_t TimePointToMin(const calendar::TimePoint &tp)
    return minutes;
}

inline uint32_t TimePointToHour12H(const calendar::TimePoint &tp)
inline uint32_t TimePointToHour12H(const TimePoint &tp)
{
    auto time = TimePointToTimeT(tp);
    utils::time::Timestamp timestamp(time);


@@ 244,7 245,7 @@ inline uint32_t TimePointToHour12H(const calendar::TimePoint &tp)
    return hour;
}

inline std::string TimePointToHourString12H(const calendar::TimePoint &tp)
inline std::string TimePointToHourString12H(const TimePoint &tp)
{
    auto hour =
        utils::time::Timestamp(TimePointToTimeT(tp)).get_UTC_date_time_sub_value(utils::time::GetParameters::Hour);


@@ 252,14 253,14 @@ inline std::string TimePointToHourString12H(const calendar::TimePoint &tp)
    return utils::to_string(hour12h);
}

inline std::string TimePointToHourString24H(const calendar::TimePoint &tp)
inline std::string TimePointToHourString24H(const TimePoint &tp)
{
    auto hour =
        utils::time::Timestamp(TimePointToTimeT(tp)).get_UTC_date_time_sub_value(utils::time::GetParameters::Hour);
    return utils::to_string(hour);
}

inline std::string TimePointToMinutesString(const calendar::TimePoint &tp)
inline std::string TimePointToMinutesString(const TimePoint &tp)
{
    auto minute       = TimePointToMinutes(tp);
    auto minuteString = std::to_string(minute);


@@ 270,7 271,7 @@ inline std::string TimePointToMinutesString(const calendar::TimePoint &tp)
}

// 0: Monday, 1: Tuesday ... 6: Sunday
inline unsigned int WeekdayIndexFromTimePoint(const calendar::TimePoint &tp)
inline unsigned int WeekdayIndexFromTimePoint(const TimePoint &tp)
{
    auto ymw = date::year_month_weekday{std::chrono::floor<date::days>(tp)};
    return ymw.weekday().iso_encoding() - 1;

M module-apps/application-calendar/models/DayEventsModel.cpp => module-apps/application-calendar/models/DayEventsModel.cpp +5 -4
@@ 6,7 6,7 @@
#include "application-calendar/data/CalendarData.hpp"
#include "application-calendar/ApplicationCalendar.hpp"
#include <ListView.hpp>
#include <queries/calendar/QueryEventsGetFiltered.hpp>
#include <queries/calendar/QueryEventsGetFilteredByDay.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <service-db/QueryMessage.hpp>
#include <module-db/queries/RecordQuery.hpp>


@@ 27,7 27,7 @@ unsigned int DayEventsModel::getMinimalItemHeight() const

void DayEventsModel::requestRecords(const uint32_t offset, const uint32_t limit)
{
    auto query = std::make_unique<db::query::events::GetFiltered>(filterFrom, filterTill, offset, limit);
    auto query = std::make_unique<db::query::events::GetFilteredByDay>(filterFrom, offset, limit);
    auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Events);
    task->setCallback([this](auto response) { return handleQueryResponse(response); });
    task->execute(application, this);


@@ 63,7 63,7 @@ bool DayEventsModel::updateRecords(std::vector<EventsRecord> records)

auto DayEventsModel::handleQueryResponse(db::QueryResult *queryResult) -> bool
{
    if (auto response = dynamic_cast<db::query::events::GetFilteredResult *>(queryResult); response != nullptr) {
    if (auto response = dynamic_cast<db::query::events::GetFilteredByDayResult *>(queryResult); response != nullptr) {
        if (recordsCount != (response->getCountResult())) {
            recordsCount = response->getCountResult();
            list->rebuildList(style::listview::RebuildType::Full, 0, true);


@@ 95,8 95,9 @@ auto DayEventsModel::handleQueryResponse(db::QueryResult *queryResult) -> bool
    return false;
}

void DayEventsModel::setFilters(calendar::TimePoint from, calendar::TimePoint till, const std::string &dayMonth)
void DayEventsModel::setFilters(TimePoint from, TimePoint till, const std::string &dayMonth)
{
    /// TODO: change to one filter
    this->filterFrom    = from;
    this->filterTill    = till;
    this->dayMonthTitle = dayMonth;

M module-apps/application-calendar/models/DayEventsModel.hpp => module-apps/application-calendar/models/DayEventsModel.hpp +3 -3
@@ 15,13 15,13 @@ class DayEventsModel : public app::DatabaseModel<EventsRecord>,
{
    app::Application *application = nullptr;
    std::string dayMonthTitle;
    calendar::TimePoint filterFrom = TIME_POINT_INVALID;
    calendar::TimePoint filterTill = TIME_POINT_INVALID;
    TimePoint filterFrom = TIME_POINT_INVALID;
    TimePoint filterTill = TIME_POINT_INVALID;

  public:
    DayEventsModel(app::Application *app);

    void setFilters(calendar::TimePoint from, calendar::TimePoint till, const std::string &dayMonth);
    void setFilters(TimePoint from, TimePoint till, const std::string &dayMonth);

    bool updateRecords(std::vector<EventsRecord> records) override;
    auto handleQueryResponse(db::QueryResult *) -> bool;

M module-apps/application-calendar/models/MonthModel.cpp => module-apps/application-calendar/models/MonthModel.cpp +60 -7
@@ 3,9 3,34 @@

#include "MonthModel.hpp"
#include <time/time_locale.hpp>
#include <time/time_conversion.hpp>

date::year_month_day MonthModel::getYearMonthDayFromTimePoint(TimePoint timePoint) const
{
    return date::year_month_day{date::floor<date::days>(timePoint)};
}

uint32_t MonthModel::getEventDurationInDays(const EventsRecord &record) const
{
    auto eventStartDuration = utils::time::Duration(TimePointToTimeT(record.date_from));
    auto eventEndDuration   = utils::time::Duration(TimePointToTimeT(record.date_till));
    auto startEventDurationSinceEpochInDaysRoundedDown =
        (eventStartDuration.getHours() - eventStartDuration.getHours() % utils::time::hoursInday) /
        utils::time::hoursInday;
    auto endEventDurationSinceEpochInDaysRoundedDown =
        (eventEndDuration.getHours() - eventEndDuration.getHours() % utils::time::hoursInday) / utils::time::hoursInday;
    return endEventDurationSinceEpochInDaysRoundedDown - startEventDurationSinceEpochInDaysRoundedDown;
}

uint32_t MonthModel::getDayIndex(TimePoint date) const
{
    date::year_month_day recordDate = TimePointToYearMonthDay(date);
    return (static_cast<unsigned>(recordDate.day()) - 1);
}

MonthModel::MonthModel(date::year_month_day yearMonthDay)
{
    this->yearMonth                            = YearMonth(yearMonthDay);
    this->month                                = yearMonthDay.month();
    this->year                                 = yearMonthDay.year();
    date::year_month_day_last yearMonthDayLast = this->year / this->month / date::last;


@@ 14,6 39,35 @@ MonthModel::MonthModel(date::year_month_day yearMonthDay)
    this->firstWeekDayNumb                     = date::weekday{yearMonthDayFirst}.c_encoding();
}

void MonthModel::markEventsInDays(const std::vector<EventsRecord> &records, std::array<bool, 31> &isDayEmpty)
{
    for (auto &rec : records) {
        auto eventYearMonthFrom = YearMonth(getYearMonthDayFromTimePoint(rec.date_from));
        auto eventYearMonthTill = YearMonth(getYearMonthDayFromTimePoint(rec.date_till));

        if (eventYearMonthFrom < this->yearMonth && this->yearMonth < eventYearMonthTill) {
            for (uint32_t i = 0; i < max_month_day; i++) {
                isDayEmpty[i] = false;
            }
            return;
        }

        int durationInDays = getEventDurationInDays(rec);

        if (this->yearMonth == eventYearMonthFrom) {
            for (int i = getDayIndex(rec.date_from); i < max_month_day && durationInDays >= 0; i++, durationInDays--) {
                isDayEmpty[i] = false;
            }
        }

        if (this->yearMonth == eventYearMonthTill) {
            for (int i = getDayIndex(rec.date_till); i >= 0 && durationInDays >= 0; i--, durationInDays--) {
                isDayEmpty[i] = false;
            }
        }
    }
}

date::year MonthModel::getYear()
{
    return year;


@@ 52,18 106,17 @@ std::vector<std::string> MonthModel::split(const std::string &s, char delim)

std::string MonthModel::getMonthText()
{
    unsigned int monthUInt = static_cast<unsigned>(month);
    std::string monthStr   = utils::time::Locale::get_month(utils::time::Locale::Month(monthUInt - 1));
    auto monthUInt       = static_cast<unsigned>(month);
    std::string monthStr = utils::time::Locale::get_month(utils::time::Locale::Month(monthUInt - 1));
    return monthStr;
}

std::string MonthModel::getMonthYearText()
{
    int yearUInt        = static_cast<decltype(yearUInt)>(year);
    std::string yearStr = std::to_string(yearUInt);
    std::string monthStr;
    unsigned int monthUInt = static_cast<unsigned>(month);
    monthStr               = utils::time::Locale::get_month(utils::time::Locale::Month(monthUInt - 1));
    auto yearInt         = static_cast<int>(year);
    std::string yearStr  = std::to_string(yearInt);
    auto monthUInt       = static_cast<unsigned>(month);
    std::string monthStr = utils::time::Locale::get_month(utils::time::Locale::Month(monthUInt - 1));

    return monthStr + " " + yearStr;
}

M module-apps/application-calendar/models/MonthModel.hpp => module-apps/application-calendar/models/MonthModel.hpp +51 -7
@@ 13,21 13,59 @@
#include <string>
#include <vector>
#include <module-apps/application-calendar/data/dateCommon.hpp>
#include <module-db/Interface/EventsRecord.hpp>

class MonthModel
class YearMonth
{
  public:
    date::month month;
    unsigned int lastDay;
    // first week offset
    uint32_t firstWeekDayNumb;
    date::year year;
    date::month month;

  public:
    MonthModel(date::year_month_day ymd);
    YearMonth() = default;
    explicit YearMonth(date::year_month_day ymd) : year{ymd.year()}, month{ymd.month()} {};

    bool operator>(const YearMonth &other) const noexcept
    {
        if (this->year != other.year) {
            return this->year > other.year;
        }
        else {
            return this->month > other.month;
        }
    };

    bool operator==(const YearMonth &other) const noexcept
    {
        return this->month == other.month && this->year == other.year;
    };

    bool operator!=(const YearMonth &other)
    {
        return !(*this == other);
    };

    bool operator<(const YearMonth &other)
    {
        return !(*this > other) && (*this != other);
    };
};

class MonthModel
{
  private:
    YearMonth yearMonth;

    [[nodiscard]] date::year_month_day getYearMonthDayFromTimePoint(TimePoint timePoint) const;
    [[nodiscard]] uint32_t getEventDurationInDays(const EventsRecord &records) const;
    [[nodiscard]] uint32_t getDayIndex(TimePoint date) const;

  public:
    explicit MonthModel(date::year_month_day yearMonthDay);
    MonthModel()          = default;
    virtual ~MonthModel() = default;

    void markEventsInDays(const std::vector<EventsRecord> &records, std::array<bool, 31> &isDayEmpty);

    date::year getYear();
    date::month getMonth();
    uint32_t getLastDay();


@@ 35,4 73,10 @@ class MonthModel
    std::string getMonthYearText();
    std::string getMonthText();
    std::vector<std::string> split(const std::string &s, char delim);

    date::month month;
    uint32_t lastDay;
    // first week offset
    uint32_t firstWeekDayNumb;
    date::year year;
};

M module-apps/application-calendar/widgets/EventTimeItem.cpp => module-apps/application-calendar/widgets/EventTimeItem.cpp +3 -3
@@ 433,9 433,9 @@ namespace gui
        }
    }

    calendar::TimePoint EventTimeItem::calculateEventTime(calendar::YearMonthDay date,
                                                          std::chrono::hours hours,
                                                          std::chrono::minutes minutes)
    TimePoint EventTimeItem::calculateEventTime(calendar::YearMonthDay date,
                                                std::chrono::hours hours,
                                                std::chrono::minutes minutes)
    {
        return TimePointFromYearMonthDay(date) + hours + minutes;
    }

M module-apps/application-calendar/widgets/EventTimeItem.hpp => module-apps/application-calendar/widgets/EventTimeItem.hpp +3 -3
@@ 43,9 43,9 @@ namespace gui
                                    std::chrono::minutes end_hour,
                                    uint32_t start_minutes,
                                    uint32_t end_minutes);
        calendar::TimePoint calculateEventTime(calendar::YearMonthDay date,
                                               std::chrono::hours hours,
                                               std::chrono::minutes minutes);
        TimePoint calculateEventTime(calendar::YearMonthDay date,
                                     std::chrono::hours hours,
                                     std::chrono::minutes minutes);

      public:
        EventTimeItem(const std::string &description,

M module-apps/application-calendar/widgets/MonthBox.cpp => module-apps/application-calendar/widgets/MonthBox.cpp +1 -1
@@ 16,7 16,7 @@ namespace gui
                       const uint32_t &dayWidth,
                       const uint32_t &dayHeight,
                       const std::unique_ptr<MonthModel> &model,
                       bool *isDayEmpty)
                       std::array<bool, 31> &isDayEmpty)
        : GridLayout(parent, style::window::default_left_margin, offsetTop, width, height, {dayWidth, dayHeight})
    {
        LOG_DEBUG("Call MonthBox constructor");

M module-apps/application-calendar/widgets/MonthBox.hpp => module-apps/application-calendar/widgets/MonthBox.hpp +1 -1
@@ 19,7 19,7 @@ namespace gui
                 const uint32_t &dayWidth,
                 const uint32_t &dayHeight,
                 const std::unique_ptr<MonthModel> &model,
                 bool *isDayEmpty);
                 std::array<bool, 31> &isDayEmpty);

        ~MonthBox() override = default;
        std::string month;

M module-apps/application-calendar/windows/AllEventsWindow.hpp => module-apps/application-calendar/windows/AllEventsWindow.hpp +1 -1
@@ 16,7 16,7 @@ namespace gui
        gui::Image *leftArrowImage   = nullptr;
        gui::Image *newDayEventImage = nullptr;

        calendar::TimePoint dateFilter                 = TimePointNow();
        TimePoint dateFilter                           = TimePointNow();
        gui::ListView *allEventsList                   = nullptr;
        std::shared_ptr<AllEventsModel> allEventsModel = nullptr;


M module-apps/application-calendar/windows/CalendarMainWindow.cpp => module-apps/application-calendar/windows/CalendarMainWindow.cpp +3 -8
@@ 29,12 29,12 @@ namespace gui
        buildInterface();
    }

    void CalendarMainWindow::refresh()
    void CalendarMainWindow::refresh(const std::vector<EventsRecord> &records)
    {
        erase(dateLabel);
        monthBox->erase();

        monthModel           = std::make_unique<MonthModel>(actualDate);
        monthModel->markEventsInDays(records, isDayEmpty);
        std::string dateText = monthModel->getMonthYearText();
        this->buildMonth(monthModel);
        this->buildDateLabel(dateText);


@@ 213,12 213,7 @@ namespace gui
        std::fill(std::begin(isDayEmpty), std::end(isDayEmpty), true);
        if (auto response = dynamic_cast<db::query::events::GetFilteredResult *>(queryResult)) {
            const auto records = response->getResult();
            for (auto &rec : records) {
                date::year_month_day recordDate = TimePointToYearMonthDay(rec.date_from);
                uint32_t dayNumb                = static_cast<unsigned>(recordDate.day());
                isDayEmpty[dayNumb - 1]         = false;
            }
            refresh();
            refresh(records);
            return true;
        }
        if (auto response = dynamic_cast<db::query::events::GetAllResult *>(queryResult)) {

M module-apps/application-calendar/windows/CalendarMainWindow.hpp => module-apps/application-calendar/windows/CalendarMainWindow.hpp +2 -2
@@ 24,7 24,7 @@ namespace gui

    class CalendarMainWindow : public gui::AppWindow, public app::AsyncCallbackReceiver
    {
        bool isDayEmpty[31];
        std::array<bool, 31> isDayEmpty;
        uint32_t offsetFromTop = 0;
        uint32_t monthWidth    = 0;
        uint32_t monthHeight   = 0;


@@ 41,7 41,7 @@ namespace gui
        CalendarMainWindow(app::Application *app, const std::string &name);

        void rebuild() override;
        void refresh();
        void refresh(const std::vector<EventsRecord> &records);
        void filterRequest();
        void buildMonth(std::unique_ptr<MonthModel> &model);
        void buildDateLabel(std::string actualDateTime);

M module-apps/application-calendar/windows/DayEventsWindow.hpp => module-apps/application-calendar/windows/DayEventsWindow.hpp +1 -1
@@ 19,7 19,7 @@ namespace gui
    class DayEventsWindow : public gui::AppWindow
    {
        std::string dayMonthTitle;
        calendar::TimePoint filterFrom;
        TimePoint filterFrom;
        gui::Image *leftArrowImage                               = nullptr;
        gui::Image *newDayEventImage                             = nullptr;
        gui::ListView *dayEventsList                             = nullptr;

M module-apps/application-call/ApplicationCall.cpp => module-apps/application-call/ApplicationCall.cpp +16 -19
@@ 47,6 47,10 @@ namespace app
            switchWindow(window::name_enterNumber, std::forward<decltype(data)>(data));
            return msgHandled();
        });
        addActionReceiver(manager::actions::EmergencyDial, [this](auto &&data) {
            switchWindow(app::window::name_emergencyCall, std::forward<decltype(data)>(data));
            return msgHandled();
        });
    }

    //  number of seconds after end call to switch back to previous application


@@ 211,30 215,23 @@ namespace app
    {
        LOG_INFO("add contact information: %s", number.c_str());

        auto searchResults = DBServiceAPI::ContactSearch(this, UTF8{}, UTF8{}, number);
        if (const auto resultsSize = searchResults->size(); resultsSize > 1) {
            LOG_FATAL("Found more than one contact for number %s", number.c_str());
            for (auto i : *searchResults) {
                LOG_FATAL("ContactID = %" PRIu32, i.ID);
            }
        }
        else if (resultsSize == 1) {
            const auto &contactRecord = searchResults->front();
        auto numberView    = utils::PhoneNumber(number).getView();
        auto searchResults = DBServiceAPI::MatchContactByPhoneNumber(this, numberView);
        if (searchResults != nullptr) {
            LOG_INFO("Found contact matching search num %s : contact ID %" PRIu32 " - %s %s",
                     number.c_str(),
                     contactRecord.ID,
                     contactRecord.primaryName.c_str(),
                     contactRecord.alternativeName.c_str());
            app::manager::Controller::sendAction(
                this,
                app::manager::actions::AddContact,
                std::make_unique<PhonebookItemData>(std::make_shared<ContactRecord>(contactRecord)));
                     searchResults->ID,
                     searchResults->primaryName.c_str(),
                     searchResults->alternativeName.c_str());
            app::manager::Controller::sendAction(this,
                                                 app::manager::actions::EditContact,
                                                 std::make_unique<PhonebookItemData>(std::move(searchResults)));
        }
        else {
            ContactRecord contactRecord;
            contactRecord.numbers.emplace_back(ContactRecord::Number(utils::PhoneNumber(number).getView()));
            auto contactRecord = std::make_shared<ContactRecord>();
            contactRecord->numbers.emplace_back(std::move(numberView));

            auto data = std::make_unique<PhonebookItemData>(std::make_shared<ContactRecord>(contactRecord));
            auto data                        = std::make_unique<PhonebookItemData>(std::move(contactRecord));
            data->ignoreCurrentWindowOnStack = true;
            app::manager::Controller::sendAction(
                this, manager::actions::AddContact, std::move(data), manager::OnSwitchBehaviour::RunInBackground);

M module-apps/application-call/ApplicationCall.hpp => module-apps/application-call/ApplicationCall.hpp +5 -1
@@ 24,6 24,7 @@ namespace app
        inline constexpr auto name_emergencyCall     = "EmergencyCallWindow";
        inline constexpr auto name_duplicatedContact = "DuplicatedContactWindow";
        inline constexpr auto name_dialogConfirm     = "DialogConfirm";
        inline constexpr auto name_number            = "NumberWindow";
    } // namespace window

    inline constexpr auto ringtone_path = "assets/audio/Ringtone-drum2.mp3"; // Should bo moved to database


@@ 100,7 101,10 @@ namespace app
    {
        static auto GetManifest() -> manager::ApplicationManifest
        {
            return {{manager::actions::Launch, manager::actions::Call, manager::actions::Dial}};
            return {{manager::actions::Launch,
                     manager::actions::Call,
                     manager::actions::Dial,
                     manager::actions::EmergencyDial}};
        }
    };
} /* namespace app */

M module-apps/application-call/CMakeLists.txt => module-apps/application-call/CMakeLists.txt +4 -2
@@ 22,13 22,15 @@ target_sources( ${PROJECT_NAME}
		"${CMAKE_CURRENT_LIST_DIR}/windows/EmergencyCallWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/CallWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/StateIcons.cpp"
	PUBLIC
		"${CMAKE_CURRENT_LIST_DIR}/windows/NumberWindow.cpp"
		PUBLIC
		"${CMAKE_CURRENT_LIST_DIR}/ApplicationCall.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/EnterNumberWindow.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/EmergencyCallWindow.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/CallWindow.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/data/CallSwitchData.hpp"
)
		"${CMAKE_CURRENT_LIST_DIR}/windows/NumberWindow.hpp"
		)

target_link_libraries(${PROJECT_NAME}
    PRIVATE

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

#pragma once


@@ 36,6 36,14 @@ namespace callAppStyle
        inline constexpr auto borderW = 1U;
    } // namespace numberLabel

    namespace numberDescriptionLabel
    {
        inline constexpr auto x = 60U;
        inline constexpr auto y = 254U;
        inline constexpr auto w = style::window_width - 2 * x;
        inline constexpr auto h = 20U;
    } // namespace numberDescriptionLabel

    namespace icon
    {
        inline constexpr auto x_margin = 20U;

M module-apps/application-call/windows/EmergencyCallWindow.cpp => module-apps/application-call/windows/EmergencyCallWindow.cpp +17 -29
@@ 1,52 1,40 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "../data/CallSwitchData.hpp"
#include "../ApplicationCall.hpp"
#include <service-appmgr/model/ApplicationManager.hpp>

#include <i18n/i18n.hpp>
#include "EmergencyCallWindow.hpp"

#include <cassert>
#include "application-call/data/CallAppStyle.hpp"

#include <service-appmgr/Controller.hpp>

namespace gui
{

    EmergencyCallWindow::EmergencyCallWindow(app::Application *app, app::EnterNumberWindowInterface *interface)
        : EnterNumberWindow(app, interface, app::window::name_emergencyCall)
        : NumberWindow(app, interface, app::window::name_emergencyCall)
    {
        assert(app != nullptr);
        assert(interface != nullptr);
        numberLabel->setText(utils::localize.get("app_call_emergency"));
        buildInterface();
    }

    void EmergencyCallWindow::rebuild()
    {}
    void EmergencyCallWindow::buildInterface()
    {
        AppWindow::buildInterface();
    }
    void EmergencyCallWindow::destroyInterface()
    {
        AppWindow::destroyInterface();
    }

    bool EmergencyCallWindow::onInput(const InputEvent &inputEvent)
    {
        bool ret = EnterNumberWindow::onInput(inputEvent);
        using namespace callAppStyle;
        NumberWindow::buildInterface();

        //	if( number.empty() ) {
        //		numberLabel->setText( utils::localize.get("app_call_emergency") );
        //	}
        bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get("app_phonebook_ice_contacts_title"));
        bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get(style::strings::common::back));

        return ret;
        numberDescriptionLabel->setText(utils::localize.get("app_call_emergency_text"));
    }
    bool EmergencyCallWindow::handleSwitchData(SwitchData *data)

    bool EmergencyCallWindow::onInput(const InputEvent &inputEvent)
    {
        bool ret = EnterNumberWindow::handleSwitchData(data);
        if (inputEvent.is(gui::KeyCode::KEY_ENTER)) {
            app::manager::Controller::sendAction(application, app::manager::actions::ShowEmergencyContacts);
            return true;
        }

        return ret;
        return NumberWindow::onInput(inputEvent);
    }

} /* namespace gui */

M module-apps/application-call/windows/EmergencyCallWindow.hpp => module-apps/application-call/windows/EmergencyCallWindow.hpp +3 -5
@@ 3,22 3,20 @@

#pragma once

#include "EnterNumberWindow.hpp"
#include "NumberWindow.hpp"

namespace gui
{

    class EmergencyCallWindow : public EnterNumberWindow
    class EmergencyCallWindow : public NumberWindow
    {
      public:
        EmergencyCallWindow(app::Application *app, app::EnterNumberWindowInterface *interface);
        ~EmergencyCallWindow() override = default;

        bool onInput(const InputEvent &inputEvent) override;
        bool handleSwitchData(SwitchData *data) override;

        void rebuild() override;
        void buildInterface() override;
        void destroyInterface() override;
    };

} /* namespace gui */

M module-apps/application-call/windows/EnterNumberWindow.cpp => module-apps/application-call/windows/EnterNumberWindow.cpp +6 -135
@@ 3,9 3,8 @@

#include "EnterNumberWindow.hpp"

#include "../ApplicationCall.hpp"
#include "../data/CallAppStyle.hpp"
#include "../data/CallSwitchData.hpp"
#include "application-call/data/CallAppStyle.hpp"
#include "application-call/data/CallSwitchData.hpp"

#include <ContactRecord.hpp>
#include <country.hpp>


@@ 19,52 18,25 @@

#include <cassert>

using namespace utils;

namespace gui
{
    using namespace callAppStyle;
    using namespace callAppStyle::enterNumberWindow;

    EnterNumberWindow::EnterNumberWindow(app::Application *app,
                                         app::EnterNumberWindowInterface *interface,
                                         std::string windowName)
        : AppWindow(app, windowName), interface(interface), currentCountry(defaultCountry),
          numberUtil(*PhoneNumberUtil::GetInstance())
        : NumberWindow(app, interface, std::move(windowName))
    {
        assert(interface != nullptr);
        assert(app != nullptr);
        switchFormatter(country::getAlpha2Code(currentCountry));
        buildInterface();
    }

    void EnterNumberWindow::setNumberLabel(const std::string num)
    {
        numberLabel->setText(num);

        if (numberLabel->getText().length() == 0) {
            bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get(style::strings::common::back));
            return;
        }
        bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get("app_call_clear"));
    }

    void EnterNumberWindow::buildInterface()
    {
        using namespace callAppStyle;
        using namespace callAppStyle::enterNumberWindow;
        NumberWindow::buildInterface();

        AppWindow::buildInterface();

        bottomBar->setText(BottomBar::Side::LEFT, utils::localize.get("app_call_call"));
        bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get("common_add"));
        bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get("app_call_clear"));

        numberLabel = new gui::Label(this, numberLabel::x, numberLabel::y, numberLabel::w, numberLabel::h);
        numberLabel->setPenWidth(numberLabel::borderW);
        numberLabel->setFont(style::window::font::largelight);
        numberLabel->setEdges(RectangleEdge::Bottom);
        numberLabel->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));
        numberLabel->setEllipsis(Ellipsis::Left);

        newContactIcon                    = new gui::AddContactIcon(this, newContactIcon::x, newContactIcon::y);
        newContactIcon->activatedCallback = [=](gui::Item &item) { return addNewContact(); };
        setFocusItem(newContactIcon);


@@ 76,69 48,6 @@ namespace gui
        return true;
    }

    void EnterNumberWindow::destroyInterface()
    {
        erase();
    }

    bool EnterNumberWindow::onInput(const InputEvent &inputEvent)
    {
        auto code = translator.handle(inputEvent.key, InputMode({InputMode::phone}).get());
        if (inputEvent.state == InputEvent::State::keyReleasedShort) {
            // Call function
            if (inputEvent.keyCode == KeyCode::KEY_LF) {
                interface->handleCallEvent(enteredNumber);
                return true;
            }
            // Clear/back function
            else if (inputEvent.keyCode == KeyCode::KEY_RF) {
                // if there isn't any char in phone number field return to previous application
                if (enteredNumber.empty()) {
                    formatter->Clear();
                    app::manager::Controller::switchBack(application);
                }
                // if there is the last char just clear input
                else if (enteredNumber.size() == 1) {
                    clearInput();
                }
                else {
                    // remove last digit and reformat entered number otherwise
                    enteredNumber = enteredNumber.substr(0, enteredNumber.size() - 1);
                    initFormatterInput(enteredNumber);
                    setNumberLabel(formattedNumber);

                    application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
                }
                return true;
            }
            else if (code != 0) {
                addDigit(code);
                return true;
            }
        }
        else if (inputEvent.state == InputEvent::State::keyReleasedLong) {
            // erase all characters from phone number
            if (inputEvent.keyCode == KeyCode::KEY_RF) {
                // if there isn't any char in phone number field return to previous application
                if (enteredNumber.empty()) {
                    app::manager::Controller::switchBack(application);
                    return true;
                }
                clearInput();

                return true;
            }
            // long press of '0' key is translated to '+'
            else if (inputEvent.keyCode == KeyCode::KEY_0) {
                addDigit('+');
                return true;
            }
        }

        // check if any of the lower inheritance onInput methods catch the event
        return AppWindow::onInput(inputEvent);
    }

    bool EnterNumberWindow::handleSwitchData(SwitchData *data)
    {
        if (data == nullptr) {


@@ 175,42 84,4 @@ namespace gui

        return true;
    }

    void EnterNumberWindow::switchFormatter(const std::string &regionCode)
    {
        auto newFormatter = std::unique_ptr<Formatter>(numberUtil.GetAsYouTypeFormatter(regionCode));
        formatter.swap(newFormatter);
        LOG_INFO("Switched formatter to region: %s", regionCode.c_str());
    }

    void EnterNumberWindow::initFormatterInput(const std::string &number)
    {
        enteredNumber = number;
        formatter->Clear();
        for (auto c : number) {
            formatter->InputDigit(c, &formattedNumber);
        }
    }

    void EnterNumberWindow::addDigit(const std::string::value_type &digit)
    {
        enteredNumber += digit;
        formatter->InputDigit(digit, &formattedNumber);
        setNumberLabel(formattedNumber);
        application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
    }

    const std::string &EnterNumberWindow::getEnteredNumber() const noexcept
    {
        return enteredNumber;
    }

    void EnterNumberWindow::clearInput()
    {
        enteredNumber.clear();
        formattedNumber.clear();
        formatter->Clear();
        setNumberLabel("");
        application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
    }
} /* namespace gui */

M module-apps/application-call/windows/EnterNumberWindow.hpp => module-apps/application-call/windows/EnterNumberWindow.hpp +3 -48
@@ 3,43 3,14 @@

#pragma once

#include "application-call/ApplicationCall.hpp"
#include "application-call/widgets/StateIcons.hpp"

#include <Application.hpp>
#include <AppWindow.hpp>
#include <ContactRecord.hpp>
#include <country.hpp>
#include <gui/input/Translator.hpp>
#include <gui/widgets/BottomBar.hpp>
#include <gui/widgets/Image.hpp>
#include <gui/widgets/Label.hpp>
#include <gui/widgets/TopBar.hpp>
#include <gui/widgets/Window.hpp>

#include <phonenumbers/phonenumberutil.h>
#include <phonenumbers/asyoutypeformatter.h>

#include <memory>
#include <string>
#include "NumberWindow.hpp"

namespace gui
{
    class EnterNumberWindow : public AppWindow
    class EnterNumberWindow : public NumberWindow
    {
      public:
        using CountryCode = utils::country::Id;

      private:
        using PhoneNumberUtil = i18n::phonenumbers::PhoneNumberUtil;
        using Formatter       = i18n::phonenumbers::AsYouTypeFormatter;

      protected:
        app::EnterNumberWindowInterface *interface = nullptr;
        gui::Label *numberLabel                    = nullptr;
        gui::AddContactIcon *newContactIcon        = nullptr;

        void setNumberLabel(const std::string num);
        gui::AddContactIcon *newContactIcon = nullptr;

      public:
        EnterNumberWindow(app::Application *app,


@@ 47,27 18,11 @@ namespace gui
                          std::string windowName = app::window::name_enterNumber);
        ~EnterNumberWindow() override = default;

        auto onInput(const InputEvent &inputEvent) -> bool override;
        auto handleSwitchData(SwitchData *data) -> bool override;
        [[nodiscard]] auto getEnteredNumber() const noexcept -> const std::string &;

        void buildInterface() override;
        void destroyInterface() override;

      private:
        CountryCode defaultCountry = utils::country::defaultCountry;
        CountryCode currentCountry;
        gui::KeyInputMappedTranslation translator;
        PhoneNumberUtil &numberUtil;
        std::unique_ptr<Formatter> formatter;
        std::string formattedNumber;
        std::string enteredNumber;

        auto addNewContact() -> bool;
        void switchFormatter(const std::string &countryCode);
        void initFormatterInput(const std::string &number);
        void addDigit(const std::string::value_type &digit);
        void clearInput();
    };

} /* namespace gui */

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

#include "NumberWindow.hpp"

#include "application-call/ApplicationCall.hpp"
#include "application-call/data/CallAppStyle.hpp"
#include "application-call/data/CallSwitchData.hpp"

#include <ContactRecord.hpp>
#include <gui/widgets/BottomBar.hpp>
#include <gui/widgets/Image.hpp>
#include <gui/widgets/Label.hpp>
#include <gui/widgets/TopBar.hpp>
#include <gui/widgets/Window.hpp>
#include <i18n/i18n.hpp>
#include <InputMode.hpp>
#include <service-appmgr/Controller.hpp>
#include <service-cellular/CellularServiceAPI.hpp>

#include <cassert>

namespace gui
{
    NumberWindow::NumberWindow(app::Application *app,
                               app::EnterNumberWindowInterface *interface,
                               std::string windowName)
        : AppWindow(app, std::move(windowName)), interface(interface), currentCountry(defaultCountry),
          numberUtil(*PhoneNumberUtil::GetInstance())
    {
        assert(interface != nullptr);
        assert(app != nullptr);
        switchFormatter(utils::country::getAlpha2Code(currentCountry));
    }

    void NumberWindow::setNumberLabel(const std::string &num)
    {
        numberLabel->setText(num);

        if (num.empty()) {
            bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get(style::strings::common::back));
            return;
        }
        bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get("app_call_clear"));
    }

    void NumberWindow::buildInterface()
    {
        using namespace callAppStyle;
        using namespace callAppStyle::enterNumberWindow;
        AppWindow::buildInterface();

        bottomBar->setActive(BottomBar::Side::LEFT, true);
        bottomBar->setActive(BottomBar::Side::CENTER, true);
        bottomBar->setActive(BottomBar::Side::RIGHT, true);

        bottomBar->setText(BottomBar::Side::LEFT, utils::localize.get(callAppStyle::strings::call));

        numberLabel = new gui::Label(this, numberLabel::x, numberLabel::y, numberLabel::w, numberLabel::h);
        numberLabel->setPenWidth(numberLabel::borderW);
        numberLabel->setFont(style::window::font::largelight);
        numberLabel->setEdges(RectangleEdge::Bottom);
        numberLabel->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));
        numberLabel->setEllipsis(Ellipsis::Left);

        numberDescriptionLabel = new gui::Label(this,
                                                numberDescriptionLabel::x,
                                                numberDescriptionLabel::y,
                                                numberDescriptionLabel::w,
                                                numberDescriptionLabel::h);
        numberDescriptionLabel->setFont(style::window::font::mediumlight);
        numberDescriptionLabel->setAlignment(
            gui::Alignment{gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center});
        numberDescriptionLabel->setEdges(RectangleEdge::None);
    }

    void NumberWindow::destroyInterface()
    {
        erase();
    }

    bool NumberWindow::onInput(const InputEvent &inputEvent)
    {
        auto code = translator.handle(inputEvent.key, InputMode({InputMode::phone}).get());
        if (inputEvent.isShortPress()) {
            // Call function
            if (inputEvent.is(KeyCode::KEY_LF)) {
                interface->handleCallEvent(enteredNumber);
                return true;
            }
            // Clear/back function
            else if (inputEvent.is(KeyCode::KEY_RF)) {
                // if there isn't any char in phone number field return to previous application
                if (enteredNumber.empty()) {
                    formatter->Clear();
                    app::manager::Controller::switchBack(application);
                }
                // if there is the last char just clear input
                else if (enteredNumber.size() == 1) {
                    clearInput();
                }
                else {
                    // remove last digit and reformat entered number otherwise
                    enteredNumber = enteredNumber.substr(0, enteredNumber.size() - 1);
                    initFormatterInput(enteredNumber);
                    setNumberLabel(formattedNumber);

                    application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
                }
                return true;
            }
            else if (code != 0) {
                addDigit(code);
                return true;
            }
        }
        else if (inputEvent.isLongPress()) {
            // erase all characters from phone number
            if (inputEvent.is(KeyCode::KEY_RF)) {
                // if there isn't any char in phone number field return to previous application
                if (enteredNumber.empty()) {
                    app::manager::Controller::switchBack(application);
                    return true;
                }
                clearInput();

                return true;
            }
            // long press of '0' key is translated to '+'
            else if (inputEvent.is(KeyCode::KEY_0)) {
                addDigit('+');
                return true;
            }
        }

        // check if any of the lower inheritance onInput methods catch the event
        return AppWindow::onInput(inputEvent);
    }

    void NumberWindow::switchFormatter(const std::string &regionCode)
    {
        auto newFormatter = std::unique_ptr<Formatter>(numberUtil.GetAsYouTypeFormatter(regionCode));
        formatter.swap(newFormatter);
        LOG_INFO("Switched formatter to region: %s", regionCode.c_str());
    }

    void NumberWindow::initFormatterInput(const std::string &number)
    {
        enteredNumber = number;
        formatter->Clear();
        for (auto c : number) {
            formatter->InputDigit(c, &formattedNumber);
        }
    }

    void NumberWindow::addDigit(const std::string::value_type &digit)
    {
        enteredNumber += digit;
        formatter->InputDigit(digit, &formattedNumber);
        setNumberLabel(formattedNumber);
        application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
    }

    const std::string &NumberWindow::getEnteredNumber() const noexcept
    {
        return enteredNumber;
    }

    void NumberWindow::clearInput()
    {
        enteredNumber.clear();
        formattedNumber.clear();
        formatter->Clear();
        setNumberLabel("");
        application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
    }
} /* namespace gui */

A module-apps/application-call/windows/NumberWindow.hpp => module-apps/application-call/windows/NumberWindow.hpp +62 -0
@@ 0,0 1,62 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "application-call/ApplicationCall.hpp"
#include "application-call/widgets/StateIcons.hpp"

#include <AppWindow.hpp>
#include <ContactRecord.hpp>
#include <country.hpp>
#include <gui/input/Translator.hpp>

#include <phonenumbers/phonenumberutil.h>
#include <phonenumbers/asyoutypeformatter.h>

#include <memory>
#include <string>

namespace gui
{
    class Label;
    class NumberWindow : public AppWindow
    {
      public:
        using CountryCode = utils::country::Id;

        NumberWindow(app::Application *app,
                     app::EnterNumberWindowInterface *interface,
                     std::string windowName = app::window::name_number);

        auto onInput(const InputEvent &inputEvent) -> bool override;
        [[nodiscard]] auto getEnteredNumber() const noexcept -> const std::string &;

        void buildInterface() override;
        void destroyInterface() override;
        void initFormatterInput(const std::string &number);

      protected:
        app::EnterNumberWindowInterface *interface = nullptr;
        gui::Label *numberLabel                    = nullptr;
        gui::Label *numberDescriptionLabel         = nullptr;
        std::string formattedNumber;
        std::string enteredNumber;

        void setNumberLabel(const std::string &num);

        void switchFormatter(const std::string &countryCode);
        void addDigit(const std::string::value_type &digit);
        void clearInput();

      private:
        using PhoneNumberUtil = i18n::phonenumbers::PhoneNumberUtil;
        using Formatter       = i18n::phonenumbers::AsYouTypeFormatter;

        CountryCode defaultCountry = utils::country::defaultCountry;
        CountryCode currentCountry;
        gui::KeyInputMappedTranslation translator;
        PhoneNumberUtil &numberUtil;
        std::unique_ptr<Formatter> formatter;
    };
} /* namespace gui */

M module-apps/application-desktop/windows/PinLockWindow.cpp => module-apps/application-desktop/windows/PinLockWindow.cpp +2 -2
@@ 112,11 112,11 @@ namespace gui
        }
        // accept only LF, enter, RF, #, and numeric values;
        if (inputEvent.is(KeyCode::KEY_LEFT) && iceBox->visible) {
            app::manager::Controller::sendAction(application, app::manager::actions::ShowEmergencyContacts);
            app::manager::Controller::sendAction(application, app::manager::actions::EmergencyDial);
            return true;
        }
        else if (inputEvent.is(KeyCode::KEY_LF) && bottomBar->isActive(BottomBar::Side::LEFT)) {
            app::manager::Controller::sendAction(application, app::manager::actions::ShowEmergencyContacts);
            app::manager::Controller::sendAction(application, app::manager::actions::EmergencyDial);
            return true;
        }
        else if (inputEvent.is(KeyCode::KEY_RF) && bottomBar->isActive(BottomBar::Side::RIGHT)) {

M module-apps/application-settings-new/ApplicationSettings.cpp => module-apps/application-settings-new/ApplicationSettings.cpp +22 -2
@@ 30,6 30,7 @@
#include "windows/ChangePasscodeWindow.hpp"
#include "windows/SystemMainWindow.hpp"
#include "windows/NewApnWindow.hpp"
#include "windows/LanguagesWindow.hpp"

#include "Dialog.hpp"



@@ 42,6 43,7 @@
#include <service-db/agents/settings/SystemSettings.hpp>
#include <application-settings-new/data/ApnListData.hpp>
#include <application-settings-new/data/BondedDevicesData.hpp>
#include <application-settings-new/data/LanguagesData.hpp>
#include <application-settings-new/data/PhoneNameData.hpp>
#include <module-services/service-db/agents/settings/SystemSettings.hpp>
#include <service-db/Settings.hpp>


@@ 49,6 51,8 @@
#include <i18n/i18n.hpp>
#include <module-services/service-evtmgr/service-evtmgr/ScreenLightControlMessage.hpp>
#include <module-services/service-evtmgr/service-evtmgr/Constants.hpp>
#include <module-services/service-appmgr/service-appmgr/messages/Message.hpp>
#include <module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp>

namespace app
{


@@ 158,14 162,27 @@ namespace app
            return sys::MessageNone{};
        });

        connect(typeid(manager::GetCurrentDisplayLanguageResponse), [&](sys::Message *msg) {
            if (gui::window::name::languages == getCurrentWindow()->getName()) {
                auto response = dynamic_cast<manager::GetCurrentDisplayLanguageResponse *>(msg);
                if (response != nullptr) {
                    auto languagesData = std::make_unique<LanguagesData>(response->getLanguage());
                    switchWindow(gui::window::name::languages, std::move(languagesData));
                }
            }
            return sys::MessageNone{};
        });

        createUserInterface();

        setActiveWindow(gui::name::window::main_window);

        settings->registerValueChange(settings::operators_on,
                                      [this](const std::string &value) { operatorOnChanged(value); });
        settings->registerValueChange(::settings::Cellular::volte_on,
                                      [this](const std::string &value) { volteChanged(value); });
        settings->registerValueChange(
            ::settings::Cellular::volte_on,
            [this](const std::string &value) { volteChanged(value); },
            ::settings::SettingsScope::Global);
        settings->registerValueChange(
            ::settings::SystemProperties::lockPassHash,
            [this](std::string value) { lockPassHash = utils::getNumericValue<unsigned int>(value); },


@@ 259,6 276,9 @@ namespace app
        windowsFactory.attach(gui::window::name::new_apn, [](Application *app, const std::string &name) {
            return std::make_unique<gui::NewApnWindow>(app);
        });
        windowsFactory.attach(gui::window::name::languages, [](Application *app, const std::string &name) {
            return std::make_unique<gui::LanguagesWindow>(app);
        });
    }

    void ApplicationSettingsNew::destroyUserInterface()

M module-apps/application-settings-new/ApplicationSettings.hpp => module-apps/application-settings-new/ApplicationSettings.hpp +1 -1
@@ 50,7 50,7 @@ namespace gui::window::name
    inline constexpr auto dialog_settings    = "DialogSettings";
    inline constexpr auto change_passcode    = "ChangePasscode";

    inline constexpr auto language        = "Language";
    inline constexpr auto languages       = "Languages";
    inline constexpr auto date_and_time   = "DateAndTime";
    inline constexpr auto factory_reset   = "FactoryReset";
    inline constexpr auto about_your_pure = "AboutYourPure";

M module-apps/application-settings-new/CMakeLists.txt => module-apps/application-settings-new/CMakeLists.txt +3 -0
@@ 16,6 16,7 @@ target_sources( ${PROJECT_NAME}
    PRIVATE
        ApplicationSettings.cpp
        models/ApnSettingsModel.cpp
        models/LanguagesModel.cpp
        models/NewApnModel.cpp
        widgets/timeWidget.cpp
        widgets/ChangePasscodeLockHandler.cpp


@@ 50,6 51,7 @@ target_sources( ${PROJECT_NAME}
        widgets/SpinBox.cpp
        widgets/SpinBoxOptionSetting.cpp
        windows/SystemMainWindow.cpp
        windows/LanguagesWindow.cpp

    PUBLIC
        ApplicationSettings.hpp


@@ 71,6 73,7 @@ target_sources( ${PROJECT_NAME}
        windows/WallpaperWindow.hpp
        windows/ChangePasscodeWindow.hpp
        windows/SystemMainWindow.hpp
        windows/LanguagesWindow.hpp
)

add_dependencies(${PROJECT_NAME} version)

A module-apps/application-settings-new/data/LanguagesData.hpp => module-apps/application-settings-new/data/LanguagesData.hpp +20 -0
@@ 0,0 1,20 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <SwitchData.hpp>

class LanguagesData : public gui::SwitchData
{
  public:
    explicit LanguagesData(Language currentDisplayLanguage) : currentDisplayLanguage(std::move(currentDisplayLanguage))
    {}
    [[nodiscard]] auto getCurrentDisplayLanguage() const noexcept -> Language
    {
        return currentDisplayLanguage;
    }

  private:
    Language currentDisplayLanguage;
};

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

#include <service-appmgr/service-appmgr/messages/GetCurrentDisplayLanguageRequest.hpp>
#include <service-appmgr/service-appmgr/model/ApplicationManager.hpp>
#include "LanguagesModel.hpp"

void LanguagesModel::requestCurrentDisplayLanguage()
{
    sys::Bus::SendUnicast(std::make_shared<app::manager::GetCurrentDisplayLanguageRequest>(),
                          app::manager::ApplicationManager::ServiceName,
                          application);
}

A module-apps/application-settings-new/models/LanguagesModel.hpp => module-apps/application-settings-new/models/LanguagesModel.hpp +18 -0
@@ 0,0 1,18 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Application.hpp>

class LanguagesModel
{
  public:
    explicit LanguagesModel(app::Application *application) : application(application)
    {}

    void requestCurrentDisplayLanguage();

  private:
    app::Application *application = nullptr;
};

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

#include "application-settings-new/ApplicationSettings.hpp"
#include "application-settings-new/data/LanguagesData.hpp"
#include "LanguagesWindow.hpp"
#include "OptionSetting.hpp"
#include "service-appmgr/Controller.hpp"

namespace gui
{
    LanguagesWindow::LanguagesWindow(app::Application *app)
        : BaseSettingsWindow(app, window::name::languages), langList(loader.getAvailableDisplayLanguages()),
          languagesModel(app)
    {
        setTitle(utils::localize.get("app_settings_title_languages"));
        languagesModel.requestCurrentDisplayLanguage();
    }

    void LanguagesWindow::addOptions(std::list<Option> &optionList)
    {
        OptionWindow::addOptions(optionList);
        optionsList->setFocusOnElement(selectedLanguageIndex);
    }

    auto LanguagesWindow::buildOptionsList() -> std::list<gui::Option>
    {
        std::list<gui::Option> options;

        for (const auto &lang : langList) {
            options.emplace_back(std::make_unique<gui::option::OptionSettings>(
                lang,
                [=](const gui::Item &item) {
                    app::manager::Controller::changeDisplayLanguage(application, lang);
                    return true;
                },
                nullptr,
                this,
                selectedLanguage == lang ? gui::option::SettingRightItem::Checked
                                         : gui::option::SettingRightItem::Disabled));
        }

        return options;
    }

    auto LanguagesWindow::handleSwitchData(SwitchData *data) -> bool
    {
        auto *languagesData = dynamic_cast<LanguagesData *>(data);
        if (languagesData == nullptr) {
            return false;
        }
        selectedLanguage = languagesData->getCurrentDisplayLanguage();
        setLanguageIndex();

        return true;
    }

    void LanguagesWindow::setLanguageIndex()
    {
        for (unsigned int langIndex = 0; langIndex < langList.size(); ++langIndex) {
            if (selectedLanguage == langList[langIndex]) {
                selectedLanguageIndex = langIndex;
                break;
            }
        }
    }
} /* namespace gui */

A module-apps/application-settings-new/windows/LanguagesWindow.hpp => module-apps/application-settings-new/windows/LanguagesWindow.hpp +29 -0
@@ 0,0 1,29 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "application-settings-new/models/LanguagesModel.hpp"
#include "BaseSettingsWindow.hpp"
#include <i18n/i18n.hpp>

namespace gui
{
    class LanguagesWindow : public BaseSettingsWindow
    {
      public:
        explicit LanguagesWindow(app::Application *app);

      private:
        void addOptions(std::list<Option> &optionList) override;
        auto buildOptionsList() -> std::list<Option> override;
        auto handleSwitchData(SwitchData *data) -> bool override;
        void setLanguageIndex();

        utils::LangLoader loader;
        const std::vector<Language> langList;
        LanguagesModel languagesModel;
        Language selectedLanguage;
        unsigned int selectedLanguageIndex{0};
    };
} /* namespace gui */

M module-apps/application-settings-new/windows/SystemMainWindow.cpp => module-apps/application-settings-new/windows/SystemMainWindow.cpp +1 -1
@@ 26,7 26,7 @@ namespace gui
                option::SettingRightItem::ArrowWhite));
        };

        addOption("app_settings_language", gui::window::name::language);
        addOption("app_settings_language", gui::window::name::languages);
        addOption("app_settings_date_and_time", gui::window::name::date_and_time);
        addOption("app_settings_factory_reset", gui::window::name::factory_reset);
        addOption("app_settings_about_your_pure", gui::window::name::about_your_pure);

M module-apps/windows/OptionWindow.hpp => module-apps/windows/OptionWindow.hpp +1 -1
@@ 23,7 23,7 @@ namespace gui
        std::shared_ptr<OptionsModel> optionsModel = nullptr;
        ListView *optionsList                      = nullptr;
        std::list<Option> options;
        void addOptions(std::list<Option> &optionList);
        virtual void addOptions(std::list<Option> &optionList);
        void addOptions(std::list<Option> &&optionList);
        void resetOptions(std::list<Option> &&optionList);


M module-bsp/board/linux/eink/ED028TC1.c => module-bsp/board/linux/eink/ED028TC1.c +1 -1
@@ 129,7 129,7 @@ EinkStatus_e EinkResetAndInitialize()
    return EinkOK;
}

EinkStatus_e EinkUpdateWaveform(const EinkWaveFormSettings_t *settings)
EinkStatus_e EinkUpdateWaveform(const EinkWaveformSettings_t *settings)
{
    return EinkOK;
}

M module-bsp/board/linux/eink/ED028TC1.h => module-bsp/board/linux/eink/ED028TC1.h +4 -2
@@ 297,6 297,8 @@ extern "C"
        EinkWaveforms_e mode;
        // temperature of surrounding
        int32_t temperature;
        // use counter to keep track if need to change
        uint32_t useCounter;
        // pointer to lookup table for lut c
        uint8_t *LUTCData;
        // sizeo of lutc data


@@ 305,7 307,7 @@ extern "C"
        uint8_t *LUTDData;
        // size of lutd data
        uint32_t LUTDSize;
    } EinkWaveFormSettings_t;
    } EinkWaveformSettings_t;

    /* Exported constants --------------------------------------------------------*/



@@ 419,7 421,7 @@ extern "C"
     * @param LUTCData [in] - Data
     * @return
     */
    EinkStatus_e EinkUpdateWaveform(const EinkWaveFormSettings_t *settings);
    EinkStatus_e EinkUpdateWaveform(const EinkWaveformSettings_t *settings);

    /**
     * This function converts the ARGB image to the L4 format

M module-bsp/board/rt1051/bsp/eink/ED028TC1.cpp => module-bsp/board/rt1051/bsp/eink/ED028TC1.cpp +8 -17
@@ 65,8 65,6 @@

#define _delay_ms(ms) vTaskDelay(pdMS_TO_TICKS(ms))

#define EINK_SUSPEND_TASK_TILL_EPD_BUSY() BSP_EinkWaitUntilDisplayBusy(pdMS_TO_TICKS(1000))

//#define EINK_DEBUG_LOG  1
//
//#if EINK_DEBUG_LOG == 1


@@ 81,14 79,11 @@ using namespace magic_enum;
static std::shared_ptr<drivers::DriverDMA> dma;
static std::shared_ptr<drivers::DriverDMAMux> dmamux;

#define EINK_LUTS_FILE_PATH "/Luts.bin"

/* Internal variable definitions */
static uint8_t s_einkIsPoweredOn = false; //  Variable which contains the state of the power of the EPD display

static EinkWaveforms_e s_einkConfiguredWaveform =
    EinkWaveformGC16;                          //  This variable contains the current waveform set in the display
static int8_t s_einkPreviousTemperature = 127; //  This variable contains the last measured temperature of the ambient

static CACHEABLE_SECTION_SDRAM(uint8_t s_einkServiceRotatedBuf[BOARD_EINK_DISPLAY_RES_X * BOARD_EINK_DISPLAY_RES_Y / 2 +
                                                               2]); // Plus 2 for the EPD command and BPP config


@@ 548,7 543,7 @@ void EinkPowerOn()
            return;
        }

        EINK_SUSPEND_TASK_TILL_EPD_BUSY();
        BSP_EinkWaitUntilDisplayBusy(pdMS_TO_TICKS(BSP_EinkBusyTimeout));
        s_einkIsPoweredOn = true;
    }
}


@@ 563,7 558,7 @@ void EinkPowerOff()
            return;
        }

        EINK_SUSPEND_TASK_TILL_EPD_BUSY();
        BSP_EinkWaitUntilDisplayBusy(pdMS_TO_TICKS(BSP_EinkBusyTimeout));
        s_einkIsPoweredOn = false;
    }
}


@@ 590,7 585,7 @@ int16_t EinkGetTemperatureInternal()
        return -1;
    }

    EINK_SUSPEND_TASK_TILL_EPD_BUSY();
    BSP_EinkWaitUntilDisplayBusy(pdMS_TO_TICKS(BSP_EinkBusyTimeout));

    if (BSP_EinkReadData(temp, sizeof(temp), SPI_MANUAL_CS) != 0) {
        //        LOG_ERROR("Requesting the temperature FAILED");


@@ 641,8 636,8 @@ static void s_EinkSetInitialConfig()
    }

    tmpbuf[0] = EinkPanelSetting;      // 0x00
    tmpbuf[1] = LUT_SEL | SHL | RST_N; // 0x25 -> _XON _RES0 LUT_SEL _DM - SHL _SPIWM RST_N // If 0x35 (DM - 1 is used
                                       // (2bpp)) the SPI speed can be 25MHz
    tmpbuf[1] = LUT_SEL | SHL | RST_N; // 0x25 -> _XON _RES0 LUT_SEL _DM - SHL _SPIWM RST_N
                                       // If 0x35 (DM - 1 is used (2bpp)) the SPI speed can be 25MHz
    tmpbuf[2] = 0x00;
    if (BSP_EinkWriteData(tmpbuf, 3, SPI_AUTOMATIC_CS) != 0) {
        //         LOG_ERROR("Setting the initial configuration for the Eink failed");


@@ 747,13 742,9 @@ EinkStatus_e EinkResetAndInitialize()
    return EinkOK;
}

EinkStatus_e EinkUpdateWaveform(const EinkWaveFormSettings_t *settings)
EinkStatus_e EinkUpdateWaveform(const EinkWaveformSettings_t *settings)
{
    // If neither the temperature nor the waveform has changed - do nothing
    if ((settings->temperature == s_einkPreviousTemperature) && (settings->mode == s_einkConfiguredWaveform)) {
        return EinkOK;
    }

    /// LUTD
    if (BSP_EinkWriteData(settings->LUTDData, settings->LUTDSize, SPI_AUTOMATIC_CS) != 0) {
        EinkResetAndInitialize();
        return EinkSPIErr;


@@ 1015,7 1006,7 @@ EinkStatus_e EinkRefreshImage(
        return EinkSPIErr;
    }

    EINK_SUSPEND_TASK_TILL_EPD_BUSY();
    BSP_EinkWaitUntilDisplayBusy(pdMS_TO_TICKS(BSP_EinkBusyTimeout));

    return EinkOK;
}

M module-bsp/board/rt1051/bsp/eink/ED028TC1.h => module-bsp/board/rt1051/bsp/eink/ED028TC1.h +4 -2
@@ 297,6 297,8 @@ extern "C"
        EinkWaveforms_e mode;
        // temperature of surrounding
        int32_t temperature;
        // counts usage of this waveform (display refreshes)
        uint32_t useCounter;
        // pointer to lookup table for lut c
        uint8_t *LUTCData;
        // sizeo of lutc data


@@ 305,7 307,7 @@ extern "C"
        uint8_t *LUTDData;
        // size of lutd data
        uint32_t LUTDSize;
    } EinkWaveFormSettings_t;
    } EinkWaveformSettings_t;

    /* Exported constants --------------------------------------------------------*/



@@ 419,7 421,7 @@ extern "C"
     * @param LUTCData [in] - Data
     * @return
     */
    EinkStatus_e EinkUpdateWaveform(const EinkWaveFormSettings_t *settings);
    EinkStatus_e EinkUpdateWaveform(const EinkWaveformSettings_t *settings);

    /**
     * This function converts the ARGB image to the L4 format

M module-bsp/board/rt1051/bsp/eink/bsp_eink.h => module-bsp/board/rt1051/bsp/eink/bsp_eink.h +1 -1
@@ 33,7 33,7 @@ extern "C"

    typedef void (*bsp_eink_BusyEvent)(void);

#define BSP_EINK_TRANSFER_TIMEOUT_MS 1000
    inline constexpr auto BSP_EinkBusyTimeout = 3000U;

    status_t BSP_EinkInit(bsp_eink_BusyEvent event);
    void BSP_EinkDeinit(void);

M module-cellular/at/ErrorCode.hpp => module-cellular/at/ErrorCode.hpp +1 -1
@@ 54,6 54,7 @@ namespace at
        ServiceProvPersPUKRequired    = 45,
        CorporatePersPINRequired      = 46,
        CorporatePersPUKRequired      = 47,
        PhoneBusy                     = 258,
        AudioUnknownError             = 901,
        AudioInvalidParameters        = 902,
        AudioOperationNotSupported    = 903,


@@ 69,7 70,6 @@ namespace at
     */
    enum class NetworkErrorCode : uint32_t
    {

        MEFailure                   = 300,
        SMS_MEReserved              = 301,
        OperationNotAllowed         = 302,

M module-cellular/at/response.cpp => module-cellular/at/response.cpp +204 -0
@@ 424,5 424,209 @@ namespace at
                return message;
            }
        } // namespace clir

        namespace ccfc
        {

            auto parse(std::vector<std::string> response, std::vector<ParsedCcfc> &parsed) -> bool
            {

                auto constexpr toRemove    = "+CCFC: ";
                auto constexpr emptyString = "";
                auto constexpr quote       = "\"";

                parsed.clear();

                for (auto el : response) {

                    if (el.find("OK") != std::string::npos) {
                        return true;
                    }

                    utils::findAndReplaceAll(el, toRemove, emptyString);
                    auto tokens = utils::split(el, ",");

                    if (tokens.size() == serviceDisabledTokenCount) {
                        parsed.push_back(ParsedCcfc(ConnectionClass::None, ForwardingStatus::NotActive, ""));
                    }
                    else if (tokens.size() > serviceDisabledTokenCount) {
                        int statusToken          = 0;
                        int connectionClassToken = 0;

                        if (!utils::toNumeric(tokens[Tokens::Status], statusToken) ||
                            !utils::toNumeric(tokens[Tokens::Class], connectionClassToken)) {
                            return false;
                        }
                        auto status          = static_cast<ForwardingStatus>(statusToken);
                        auto connectionClass = static_cast<ConnectionClass>(connectionClassToken);

                        if (magic_enum::enum_contains<ForwardingStatus>(status) &&
                            magic_enum::enum_contains<ConnectionClass>(connectionClass)) {
                            auto number = tokens[Tokens::Number];
                            utils::findAndReplaceAll(number, quote, emptyString);
                            utils::trim(number);
                            parsed.push_back(ParsedCcfc(connectionClass, status, number));
                        }
                        else {
                            return false;
                        }
                    }
                }
                return true;
            }

            auto getNumbers(std::vector<ParsedCcfc> &parsed) -> CcfcNumbers
            {
                CcfcNumbers numbers;

                for (auto el : parsed) {
                    std::string number = "";
                    if (el.status == ForwardingStatus::Active) {
                        number = el.number;
                    }
                    switch (el.connectionClass) {
                    case ConnectionClass::None:
                        break;
                    case ConnectionClass::Voice:
                        numbers.voice = number;
                        break;
                    case ConnectionClass::Data:
                        break;
                    case ConnectionClass::Fax:
                        numbers.fax = number;
                        break;
                    case ConnectionClass::AllTelephonyExceptSMS:
                        break;
                    case ConnectionClass::ShortMessgeService:
                        break;
                    case ConnectionClass::DataAsync:
                        numbers.async = number;
                        break;
                    case ConnectionClass::DataSync:
                        numbers.sync = number;
                        break;
                    }
                }
                return numbers;
            }

            auto isAnyActive(std::vector<ParsedCcfc> &parsed) -> bool
            {
                for (auto el : parsed) {
                    if (el.status == ForwardingStatus::Active) {
                        return true;
                    }
                }
                return false;
            }
        } // namespace ccfc

        namespace mmi
        {
            auto getClass(const ServiceClass &serviceClass) noexcept
                -> app::manager::actions::IMMICustomResultParams::MMIResultMessage
            {
                using namespace app::manager::actions;

                auto message = IMMICustomResultParams::MMIResultMessage::CommonNoMessage;
                switch (serviceClass) {
                case ServiceClass::Voice:
                    message = IMMICustomResultParams::MMIResultMessage::CommonVoice;
                    break;
                case ServiceClass::Data:
                    message = IMMICustomResultParams::MMIResultMessage::CommonData;
                    break;
                case ServiceClass::Fax:
                    message = IMMICustomResultParams::MMIResultMessage::CommonFax;
                    break;
                case ServiceClass::DataSync:
                    message = IMMICustomResultParams::MMIResultMessage::CommonSync;
                    break;
                case ServiceClass::DataAsync:
                    message = IMMICustomResultParams::MMIResultMessage::CommonAsync;
                    break;
                case ServiceClass::AllDisabled:
                    message = IMMICustomResultParams::MMIResultMessage::CommonAllDisabled;
                    break;
                }
                return message;
            }
        } // namespace mmi

        namespace clck
        {
            auto parseQueryResponse(const std::vector<std::string> &data, std::vector<ClckParsed> &parsed) -> bool
            {
                using namespace mmi;

                auto constexpr responseToken = "+CLCK: ";
                auto constexpr emptyString   = "";
                auto constexpr minTokens     = 2;
                parsed.clear();

                for (auto el : data) {
                    if (el.find("OK") != std::string::npos) {
                        return true;
                    }
                    if (el.find(responseToken) == std::string::npos) {
                        parsed.clear();
                        return false;
                    }
                    utils::findAndReplaceAll(el, responseToken, emptyString);
                    auto tokens = utils::split(el, ",");
                    if (tokens.size() < minTokens) {
                        parsed.clear();
                        return false;
                    }
                    for (auto &t : tokens) {
                        t = utils::trim(t);
                    }

                    int statusToken       = 0;
                    int serviceClassToken = 0;

                    if (!utils::toNumeric(tokens[0], statusToken) || !utils::toNumeric(tokens[1], serviceClassToken)) {
                        return false;
                    }
                    auto status       = static_cast<Status>(statusToken);
                    auto serviceClass = static_cast<ServiceClass>(serviceClassToken);

                    if (magic_enum::enum_contains<Status>(status) &&
                        magic_enum::enum_contains<ServiceClass>(serviceClass)) {
                        parsed.push_back(ClckParsed(status, serviceClass));
                    }
                    else {
                        parsed.clear();
                        return false;
                    }
                }
                return true;
            }

            auto iterLessIter(ClckParsed a, ClckParsed b) -> bool
            {
                return a.serviceClass < b.serviceClass;
            }

            auto getStatus(const Status &status) noexcept
                -> app::manager::actions::IMMICustomResultParams::MMIResultMessage
            {
                using namespace app::manager::actions;

                auto message = IMMICustomResultParams::MMIResultMessage::CommonNoMessage;
                switch (status) {
                case Status::Disable:
                    message = IMMICustomResultParams::MMIResultMessage::CommonDeactivated;
                    break;
                case Status::Enable:
                    message = IMMICustomResultParams::MMIResultMessage::CommonActivated;
                    break;
                case Status::Query:
                    message = IMMICustomResultParams::MMIResultMessage::CommonQuery;
                    break;
                }
                return message;
            }
        } // namespace clck
    }     // namespace response
} // namespace at

M module-cellular/at/response.hpp => module-cellular/at/response.hpp +96 -0
@@ 278,5 278,101 @@ namespace at
            auto getStatus(const ServiceStatus &status)
                -> app::manager::actions::IMMICustomResultParams::MMIResultMessage;
        } // namespace clir

        namespace ccfc
        {

            enum class ConnectionClass
            {
                None                  = 0,
                Voice                 = 1,
                Data                  = 2,
                Fax                   = 4,
                AllTelephonyExceptSMS = 7,
                ShortMessgeService    = 8,
                DataAsync             = 16,
                DataSync              = 32
            };

            enum class ForwardingStatus
            {
                NotActive,
                Active
            };

            struct ParsedCcfc
            {
                ConnectionClass connectionClass;
                ForwardingStatus status;
                std::string number;

                explicit ParsedCcfc(ConnectionClass connectionClass, ForwardingStatus status, const std::string &number)
                    : connectionClass(connectionClass), status(status), number(number)
                {}
            };

            enum Tokens
            {
                Status,
                Class,
                Number,
                Type
            };

            struct CcfcNumbers
            {
                std::string voice;
                std::string fax;
                std::string sync;
                std::string async;
            };

            auto constexpr serviceDisabledTokenCount = 2;
            auto constexpr serviceEnabledTokenCount  = 4;

            auto parse(std::vector<std::string> response, std::vector<ParsedCcfc> &parsed) -> bool;
            auto getNumbers(std::vector<ParsedCcfc> &parsed) -> CcfcNumbers;
            auto isAnyActive(std::vector<ParsedCcfc> &parsed) -> bool;
        } // namespace ccfc

        namespace mmi
        {

            enum class ServiceClass
            {
                Voice       = 1,
                Data        = 2,
                Fax         = 4,
                DataSync    = 16,
                DataAsync   = 32,
                AllDisabled = 255
            };

            auto getClass(const ServiceClass &serviceClass) noexcept
                -> app::manager::actions::IMMICustomResultParams::MMIResultMessage;
        } // namespace mmi

        namespace clck
        {
            enum class Status
            {
                Disable,
                Enable,
                Query
            };

            struct ClckParsed
            {
                Status status;
                mmi::ServiceClass serviceClass;
                explicit ClckParsed(Status status, mmi::ServiceClass serviceClass)
                    : status(status), serviceClass(serviceClass){};
            };

            auto parseQueryResponse(const std::vector<std::string> &data, std::vector<ClckParsed> &parsed) -> bool;
            auto iterLessIter(ClckParsed a, ClckParsed b) -> bool;
            auto getStatus(const Status &status) noexcept
                -> app::manager::actions::IMMICustomResultParams::MMIResultMessage;
        } // namespace clck
    }     // namespace response
} // namespace at

M module-cellular/test/unittest_response.cpp => module-cellular/test/unittest_response.cpp +116 -0
@@ 152,3 152,119 @@ TEST_CASE("Response COPS")
        REQUIRE(at::response::parseCOPS(resp, ret) == false);
    }
}

TEST_CASE("Response CLCK")
{
    SECTION("OK CLCK QUERY - all disabled")
    {

        at::Result resp;
        resp.code = at::Result::Code::OK;
        std::vector<at::response::clck::ClckParsed> ret;
        resp.response.push_back("+CLCK: 0,255");
        resp.response.push_back("OK");

        REQUIRE(resp.code == at::Result::Code::OK);
        REQUIRE(at::response::clck::parseQueryResponse(resp.response, ret) == true);
        REQUIRE(ret.size() == 1);
        REQUIRE(ret[0].status == at::response::clck::Status::Disable);
        REQUIRE(ret[0].serviceClass == at::response::mmi::ServiceClass::AllDisabled);
    }

    SECTION("OK CLCK QUERY - voice enabled")
    {

        at::Result resp;
        resp.code = at::Result::Code::OK;
        std::vector<at::response::clck::ClckParsed> ret;
        resp.response.push_back("+CLCK: 1,1");
        resp.response.push_back("OK");

        REQUIRE(resp.code == at::Result::Code::OK);
        REQUIRE(at::response::clck::parseQueryResponse(resp.response, ret) == true);
        REQUIRE(ret.size() == 1);
        REQUIRE(ret[0].status == at::response::clck::Status::Enable);
        REQUIRE(ret[0].serviceClass == at::response::mmi::ServiceClass::Voice);
    }

    SECTION("OK CLCK QUERY - voice, fax enabled")
    {

        at::Result resp;
        resp.code = at::Result::Code::OK;
        std::vector<at::response::clck::ClckParsed> ret;
        resp.response.push_back("+CLCK: 1,1");
        resp.response.push_back("+CLCK: 1,4");
        resp.response.push_back("OK");

        REQUIRE(resp.code == at::Result::Code::OK);
        REQUIRE(at::response::clck::parseQueryResponse(resp.response, ret) == true);
        REQUIRE(ret.size() == 2);

        REQUIRE(ret[0].status == at::response::clck::Status::Enable);
        REQUIRE(ret[0].serviceClass == at::response::mmi::ServiceClass::Voice);

        REQUIRE(ret[1].status == at::response::clck::Status::Enable);
        REQUIRE(ret[1].serviceClass == at::response::mmi::ServiceClass::Fax);
    }

    SECTION("WRONG CLCK QUERY - invalid status")
    {

        at::Result resp;
        resp.code = at::Result::Code::OK;
        std::vector<at::response::clck::ClckParsed> ret;
        resp.response.push_back("+CLCK: 8,1");
        resp.response.push_back("+CLCK: 1,4");
        resp.response.push_back("OK");

        REQUIRE(resp.code == at::Result::Code::OK);
        REQUIRE(at::response::clck::parseQueryResponse(resp.response, ret) == false);
        REQUIRE(ret.size() == 0);
    }

    SECTION("WRONG CLCK QUERY - invalid class")
    {

        at::Result resp;
        resp.code = at::Result::Code::OK;
        std::vector<at::response::clck::ClckParsed> ret;
        resp.response.push_back("+CLCK: 1,1");
        resp.response.push_back("+CLCK: 1,99");
        resp.response.push_back("OK");

        REQUIRE(resp.code == at::Result::Code::OK);
        REQUIRE(at::response::clck::parseQueryResponse(resp.response, ret) == false);
        REQUIRE(ret.size() == 0);
    }

    SECTION("WRONG CLCK QUERY - too short")
    {

        at::Result resp;
        resp.code = at::Result::Code::OK;
        std::vector<at::response::clck::ClckParsed> ret;
        resp.response.push_back("+CLCK: 1");
        resp.response.push_back("+CLCK: 1,4");
        resp.response.push_back("OK");

        REQUIRE(resp.code == at::Result::Code::OK);
        REQUIRE(at::response::clck::parseQueryResponse(resp.response, ret) == false);
        REQUIRE(ret.size() == 0);
    }

    SECTION("WRONG CLCK QUERY - invalid response token")
    {

        at::Result resp;
        resp.code = at::Result::Code::OK;
        std::vector<at::response::clck::ClckParsed> ret;
        resp.response.push_back("+CLC: 1,1");
        resp.response.push_back("+CLCK: 1,4");
        resp.response.push_back("OK");

        REQUIRE(resp.code == at::Result::Code::OK);
        REQUIRE(at::response::clck::parseQueryResponse(resp.response, ret) == false);
        REQUIRE(ret.size() == 0);
    }
}

M module-db/CMakeLists.txt => module-db/CMakeLists.txt +1 -0
@@ 111,6 111,7 @@ set(SOURCES
        queries/calendar/QueryEventsEditICS.cpp
        queries/calendar/QueryEventsEdit.cpp
        queries/calendar/QueryEventsGetFiltered.cpp
        queries/calendar/QueryEventsGetFilteredByDay.cpp
        queries/calendar/QueryEventsGetAllLimited.cpp
        queries/calendar/QueryEventsSelectFirstUpcoming.cpp
        queries/alarms/QueryAlarmsAdd.cpp

M module-db/Interface/AlarmsRecord.hpp => module-db/Interface/AlarmsRecord.hpp +1 -1
@@ 28,7 28,7 @@ namespace db::query::alarms

struct AlarmsRecord : public Record
{
    calendar::TimePoint time = TIME_POINT_INVALID;
    TimePoint time     = TIME_POINT_INVALID;
    uint32_t snooze    = 0;
    AlarmStatus status = AlarmStatus::On;
    uint32_t repeat    = 0;

M module-db/Interface/EventsRecord.cpp => module-db/Interface/EventsRecord.cpp +44 -8
@@ 10,6 10,7 @@
#include "module-db/queries/calendar/QueryEventsEdit.hpp"
#include <module-db/queries/calendar/QueryEventsEditICS.hpp>
#include <module-db/queries/calendar/QueryEventsGetFiltered.hpp>
#include <module-db/queries/calendar/QueryEventsGetFilteredByDay.hpp>
#include <module-db/queries/calendar/QueryEventsGetAllLimited.hpp>
#include <module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp>
#include <log/log.hpp>


@@ 70,8 71,8 @@ bool EventsRecordInterface::Add(const EventsRecord &rec)
    return false;
}

std::vector<EventsRecord> EventsRecordInterface::Select(calendar::TimePoint filter_from,
                                                        calendar::TimePoint filter_till,
std::vector<EventsRecord> EventsRecordInterface::Select(TimePoint filter_from,
                                                        TimePoint filter_till,
                                                        uint32_t offset,
                                                        uint32_t limit)
{


@@ 86,6 87,19 @@ std::vector<EventsRecord> EventsRecordInterface::Select(calendar::TimePoint filt
    return records;
}

std::vector<EventsRecord> EventsRecordInterface::Select(TimePoint date, uint32_t offset, uint32_t limit)
{
    auto rows = eventsDb->events.selectByDate(date, offset, limit);

    auto records = std::vector<EventsRecord>();

    for (auto &r : rows) {
        records.push_back(r);
    }

    return records;
}

std::unique_ptr<std::vector<EventsRecord>> EventsRecordInterface::GetLimitOffsetByField(uint32_t offset,
                                                                                        uint32_t limit,
                                                                                        EventsRecordField field,


@@ 265,13 279,17 @@ uint32_t EventsRecordInterface::GetCount()
    return eventsDb->events.count();
}

uint32_t EventsRecordInterface::GetCountFiltered(calendar::TimePoint from, calendar::TimePoint till)
uint32_t EventsRecordInterface::GetCountFiltered(TimePoint from, TimePoint till)
{
    return eventsDb->events.countFromFilter(from, till);
}

std::vector<EventsRecord> EventsRecordInterface::SelectFirstUpcoming(calendar::TimePoint filter_from,
                                                                     calendar::TimePoint filter_till)
uint32_t EventsRecordInterface::GetCountFilteredByDay(TimePoint filter)
{
    return eventsDb->events.countFromDayFilter(filter);
}

std::vector<EventsRecord> EventsRecordInterface::SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till)
{
    auto rows = eventsDb->events.SelectFirstUpcoming(filter_from, filter_till);



@@ 297,6 315,9 @@ std::unique_ptr<db::QueryResult> EventsRecordInterface::runQuery(std::shared_ptr
    if (typeid(*query) == typeid(db::query::events::GetFiltered)) {
        return runQueryImplGetFilteredResult(query);
    }
    if (typeid(*query) == typeid(db::query::events::GetFilteredByDay)) {
        return runQueryImplGetFilteredByDayResult(query);
    }
    if (typeid(*query) == typeid(db::query::events::Add)) {
        return runQueryImplAdd(query);
    }


@@ 354,12 375,27 @@ std::unique_ptr<db::query::events::GetFilteredResult> EventsRecordInterface::run
    std::shared_ptr<db::Query> query)
{
    auto getFilteredQuery = static_cast<db::query::events::GetFiltered *>(query.get());
    auto records          = Select(getFilteredQuery->filter_from,

    auto records = Select(getFilteredQuery->filter_from,
                          getFilteredQuery->filter_till,
                          getFilteredQuery->offset,
                          getFilteredQuery->limit);
    auto numberOfEvents   = GetCountFiltered(getFilteredQuery->filter_from, getFilteredQuery->filter_till);
    auto response         = std::make_unique<db::query::events::GetFilteredResult>(std::move(records), numberOfEvents);

    auto numberOfEvents = GetCountFiltered(getFilteredQuery->filter_from, getFilteredQuery->filter_till);
    auto response       = std::make_unique<db::query::events::GetFilteredResult>(std::move(records), numberOfEvents);
    response->setRequestQuery(query);
    return response;
}

std::unique_ptr<db::query::events::GetFilteredByDayResult> EventsRecordInterface::runQueryImplGetFilteredByDayResult(
    std::shared_ptr<db::Query> query)
{
    auto getFilteredQuery = static_cast<db::query::events::GetFilteredByDay *>(query.get());

    auto records = Select(getFilteredQuery->filterDate, getFilteredQuery->offset, getFilteredQuery->limit);

    auto numberOfEvents = GetCountFilteredByDay(getFilteredQuery->filterDate);
    auto response = std::make_unique<db::query::events::GetFilteredByDayResult>(std::move(records), numberOfEvents);
    response->setRequestQuery(query);
    return response;
}

M module-db/Interface/EventsRecord.hpp => module-db/Interface/EventsRecord.hpp +12 -9
@@ 23,6 23,8 @@ namespace db::query::events
    class GetAllLimitedResult;
    class GetFiltered;
    class GetFilteredResult;
    class GetFilteredByDay;
    class GetFilteredByDayResult;
    class Add;
    class AddResult;
    class Remove;


@@ 41,11 43,11 @@ struct EventsRecord : public Record
{
    std::string UID;
    std::string title;
    calendar::TimePoint date_from;
    calendar::TimePoint date_till;
    TimePoint date_from;
    TimePoint date_till;
    uint32_t reminder = 0;
    uint32_t repeat   = 0;
    calendar::TimePoint reminder_fired;
    TimePoint reminder_fired;
    std::string provider_type;
    std::string provider_id;
    std::string provider_iCalUid;


@@ 77,18 79,17 @@ class EventsRecordInterface : public RecordInterface<EventsRecord, EventsRecordF
    EventsRecord GetByID(uint32_t id) override final;
    EventsRecord GetByUID(const std::string &uid);
    uint32_t GetCount() override final;
    uint32_t GetCountFiltered(calendar::TimePoint from, calendar::TimePoint till);
    std::vector<EventsRecord> Select(calendar::TimePoint filter_from,
                                     calendar::TimePoint filter_till,
                                     uint32_t offset,
                                     uint32_t limit);
    uint32_t GetCountFiltered(TimePoint from, TimePoint till);
    uint32_t GetCountFilteredByDay(TimePoint filter);
    std::vector<EventsRecord> Select(TimePoint filter_from, TimePoint filter_till, uint32_t offset, uint32_t limit);
    std::vector<EventsRecord> Select(TimePoint date, uint32_t offset, uint32_t limit);
    std::unique_ptr<std::vector<EventsRecord>> GetLimitOffset(uint32_t offset, uint32_t limit) override final;
    std::unique_ptr<std::vector<EventsRecord>> GetLimitOffsetByField(uint32_t offset,
                                                                     uint32_t limit,
                                                                     EventsRecordField field,
                                                                     const char *str) override final;
    std::vector<EventsRecord> GetLimitOffsetByDate(uint32_t offset, uint32_t limit);
    std::vector<EventsRecord> SelectFirstUpcoming(calendar::TimePoint filter_from, calendar::TimePoint filter_till);
    std::vector<EventsRecord> SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till);

    std::unique_ptr<db::QueryResult> runQuery(std::shared_ptr<db::Query> query) override;



@@ 101,6 102,8 @@ class EventsRecordInterface : public RecordInterface<EventsRecord, EventsRecordF
        std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::GetFilteredResult> runQueryImplGetFilteredResult(
        std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::GetFilteredByDayResult> runQueryImplGetFilteredByDayResult(
        std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::AddResult> runQueryImplAdd(std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::RemoveResult> runQueryImplRemove(std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::RemoveICSResult> runQueryImplRemoveICS(std::shared_ptr<db::Query> query);

M module-db/Tables/AlarmsTable.cpp => module-db/Tables/AlarmsTable.cpp +1 -1
@@ 9,7 9,7 @@ AlarmsTableRow::AlarmsTableRow(const AlarmsRecord &rec)
{}

AlarmsTableRow::AlarmsTableRow(
    uint32_t id, calendar::TimePoint time, uint32_t snooze, AlarmStatus status, uint32_t repeat, UTF8 path)
    uint32_t id, TimePoint time, uint32_t snooze, AlarmStatus status, uint32_t repeat, UTF8 path)
    : Record{id}, time{time}, snooze{snooze}, status{status}, repeat{repeat}, path{std::move(path)}
{}


M module-db/Tables/AlarmsTable.hpp => module-db/Tables/AlarmsTable.hpp +2 -3
@@ 25,15 25,14 @@ enum class AlarmStatus

struct AlarmsTableRow : public Record
{
    calendar::TimePoint time = TIME_POINT_INVALID;
    TimePoint time     = TIME_POINT_INVALID;
    uint32_t snooze    = 0;
    AlarmStatus status = AlarmStatus::On;
    uint32_t repeat    = 0;
    UTF8 path;

    AlarmsTableRow() = default;
    AlarmsTableRow(
        uint32_t id, calendar::TimePoint time, uint32_t snooze, AlarmStatus status, uint32_t repeat, UTF8 path);
    AlarmsTableRow(uint32_t id, TimePoint time, uint32_t snooze, AlarmStatus status, uint32_t repeat, UTF8 path);
    explicit AlarmsTableRow(const AlarmsRecord &rec);
    explicit AlarmsTableRow(const QueryResult &result);
};

M module-db/Tables/EventsTable.cpp => module-db/Tables/EventsTable.cpp +70 -13
@@ 643,17 643,61 @@ EventsTableRow EventsTable::getByUID(const std::string &UID)
    };
}

std::vector<EventsTableRow> EventsTable::selectByDatePeriod(calendar::TimePoint date_filter,
                                                            calendar::TimePoint filter_till,
std::vector<EventsTableRow> EventsTable::selectByDatePeriod(TimePoint filterFrom,
                                                            TimePoint filterTill,
                                                            uint32_t offset,
                                                            uint32_t limit)
{
    auto retQuery = db->query("SELECT * FROM events WHERE date_from >= date('%q') AND date_from < date('%q', 'start of "
                              "day') ORDER BY datetime(date_from) LIMIT %u OFFSET %u;",
                              TimePointToString(date_filter).c_str(),
                              TimePointToString(filter_till).c_str(),
                              limit,
                              offset);
    auto retQuery = db->query(
        "SELECT * FROM events WHERE ( date('%q') >= date_from AND date_from > date('%q', 'start of day') ) OR "
        " ( date('%q') >= date_till AND date_till > date('%q', 'start of day') ) OR "
        " ( date_till > date('%q') AND date('%q', 'start of day') > date_from ) ORDER BY datetime(date_from) LIMIT %u "
        "OFFSET %u;",
        TimePointToString(filterFrom).c_str(),
        TimePointToString(filterTill).c_str(),
        TimePointToString(filterFrom).c_str(),
        TimePointToString(filterTill).c_str(),
        TimePointToString(filterFrom).c_str(),
        TimePointToString(filterTill).c_str(),
        limit,
        offset);

    if (retQuery == nullptr || retQuery->getRowCount() == 0) {
        return std::vector<EventsTableRow>();
    }

    std::vector<EventsTableRow> ret;

    do {
        ret.push_back(EventsTableRow{
            (*retQuery)[0].getUInt32(),                              // ID
            (*retQuery)[1].getString(),                              // UID
            (*retQuery)[2].getString(),                              // title
            TimePointFromString((*retQuery)[3].getString().c_str()), // date_from
            TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
            (*retQuery)[5].getUInt32(),                              // reminder
            (*retQuery)[6].getUInt32(),                              // repeat
            TimePointFromString((*retQuery)[7].getString().c_str()), // reminder_fired
            (*retQuery)[8].getString(),                              // provider type
            (*retQuery)[9].getString(),                              // provider id
            (*retQuery)[10].getString()                              // provider iCalUid
        });

    } while (retQuery->nextRow());

    return ret;
}

std::vector<EventsTableRow> EventsTable::selectByDate(TimePoint dateFilter, uint32_t offset, uint32_t limit)
{
    auto dateFilterFrom = dateFilter + date::days{1};
    auto retQuery       = db->query(
        "SELECT * FROM events WHERE date_from < date('%q', 'start of day') AND date('%q', 'start of day') <= date_till "
        "ORDER BY datetime(date_from) LIMIT %u OFFSET %u;",
        TimePointToString(dateFilterFrom).c_str(),
        TimePointToString(dateFilter).c_str(),
        limit,
        offset);

    if (retQuery == nullptr || retQuery->getRowCount() == 0) {
        return std::vector<EventsTableRow>();


@@ 714,7 758,6 @@ std::vector<EventsTableRow> EventsTable::getLimitOffset(uint32_t offset, uint32_

std::vector<EventsTableRow> EventsTable::getLimitOffsetByDate(uint32_t offset, uint32_t limit)
{

    auto retQuery = db->query("SELECT * from events ORDER BY datetime(date_from) LIMIT %u OFFSET %u;", limit, offset);

    if (retQuery == nullptr || retQuery->getRowCount() == 0) {


@@ 770,10 813,10 @@ uint32_t EventsTable::count()
    return (*queryRet)[0].getUInt32();
}

uint32_t EventsTable::countFromFilter(calendar::TimePoint from, calendar::TimePoint till)
uint32_t EventsTable::countFromFilter(TimePoint from, TimePoint till)
{
    auto queryRet = db->query(
        "SELECT COUNT(*) FROM events WHERE date_from >= date('%q') AND date_from < date('%q', 'start of day');",
        "SELECT COUNT(*) FROM events WHERE date('%q') <= date_from AND date_from < date('%q', 'start of day');",
        TimePointToString(from).c_str(),
        TimePointToString(till).c_str());



@@ 784,6 827,21 @@ uint32_t EventsTable::countFromFilter(calendar::TimePoint from, calendar::TimePo
    return (*queryRet)[0].getUInt32();
}

uint32_t EventsTable::countFromDayFilter(TimePoint filter)
{
    auto filterFrom = filter + date::days{1};
    auto queryRet   = db->query("SELECT COUNT(*) FROM events WHERE date_from < datetime('%q', 'start of day') AND "
                              "datetime('%q', 'start of day') <= date_till;",
                              TimePointToString(filterFrom).c_str(),
                              TimePointToString(filter).c_str());

    if (queryRet == nullptr || queryRet->getRowCount() == 0) {
        return 0;
    }

    return (*queryRet)[0].getUInt32();
}

uint32_t EventsTable::countByFieldId(const char *field, uint32_t id)
{
    assert(0 && "Not implemented");


@@ 791,8 849,7 @@ uint32_t EventsTable::countByFieldId(const char *field, uint32_t id)
    return 0;
}

std::vector<EventsTableRow> EventsTable::SelectFirstUpcoming(calendar::TimePoint filter_from,
                                                             calendar::TimePoint filter_till)
std::vector<EventsTableRow> EventsTable::SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till)
{
    auto retQuery = db->query("SELECT DATETIME(date_from, '-' || reminder || ' minutes') AS calc_dt, * "
                              "FROM events "

M module-db/Tables/EventsTable.hpp => module-db/Tables/EventsTable.hpp +11 -7
@@ 14,11 14,11 @@ struct EventsTableRow : public Record
{
    std::string UID;
    std::string title;
    calendar::TimePoint date_from      = TIME_POINT_INVALID;
    calendar::TimePoint date_till      = TIME_POINT_INVALID;
    TimePoint date_from      = TIME_POINT_INVALID;
    TimePoint date_till      = TIME_POINT_INVALID;
    uint32_t reminder        = 0;
    uint32_t repeat          = 0;
    calendar::TimePoint reminder_fired = TIME_POINT_INVALID;
    TimePoint reminder_fired = TIME_POINT_INVALID;
    std::string provider_type;
    std::string provider_id;
    std::string provider_iCalUid;


@@ 53,12 53,16 @@ class EventsTable : public Table<EventsTableRow, EventsTableFields>
    bool drop();
    EventsTableRow getByUID(const std::string &UID);
    EventsTableRow getById(uint32_t id) override final;
    std::vector<EventsTableRow> selectByDatePeriod(calendar::TimePoint filter_from,
                                                   calendar::TimePoint filter_till,
    std::vector<EventsTableRow> selectByDatePeriod(TimePoint filter_from,
                                                   TimePoint filter_till,
                                                   uint32_t offset,
                                                   uint32_t limit);

    std::vector<EventsTableRow> selectByDate(TimePoint date, uint32_t offset, uint32_t limit);

    uint32_t count() override final;
    uint32_t countFromFilter(calendar::TimePoint from, calendar::TimePoint till);
    uint32_t countFromFilter(TimePoint from, TimePoint till);
    uint32_t countFromDayFilter(TimePoint filter);
    uint32_t countByFieldId(const char *field, uint32_t id) override final;
    std::vector<EventsTableRow> getLimitOffset(uint32_t offset, uint32_t limit) override final;
    std::vector<EventsTableRow> getLimitOffsetByField(uint32_t offset,


@@ 67,7 71,7 @@ class EventsTable : public Table<EventsTableRow, EventsTableFields>
                                                      const char *str) override final;

    std::vector<EventsTableRow> getLimitOffsetByDate(uint32_t offset, uint32_t limit);
    std::vector<EventsTableRow> SelectFirstUpcoming(calendar::TimePoint filter_from, calendar::TimePoint filter_till);
    std::vector<EventsTableRow> SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till);

  private:
    const char *createTableQuery = "CREATE TABLE IF NOT EXISTS events("

M module-db/queries/calendar/QueryEventsGetFiltered.cpp => module-db/queries/calendar/QueryEventsGetFiltered.cpp +1 -4
@@ 5,10 5,7 @@

namespace db::query::events
{
    GetFiltered::GetFiltered(calendar::TimePoint filter_from,
                             calendar::TimePoint filter_till,
                             uint32_t offset,
                             uint32_t limit)
    GetFiltered::GetFiltered(TimePoint filter_from, TimePoint filter_till, uint32_t offset, uint32_t limit)
        : Query(Query::Type::Read), filter_from(filter_from), filter_till(filter_till), offset(offset), limit(limit)
    {}


M module-db/queries/calendar/QueryEventsGetFiltered.hpp => module-db/queries/calendar/QueryEventsGetFiltered.hpp +3 -6
@@ 13,14 13,11 @@ namespace db::query::events
    class GetFiltered : public Query
    {
      public:
        GetFiltered(calendar::TimePoint filter_from,
                    calendar::TimePoint filter_till,
                    uint32_t offset = 0,
                    uint32_t limit  = UINT32_MAX);
        GetFiltered(TimePoint filter_from, TimePoint filter_till, uint32_t offset = 0, uint32_t limit = UINT32_MAX);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        calendar::TimePoint filter_from;
        calendar::TimePoint filter_till;
        TimePoint filter_from;
        TimePoint filter_till;
        uint32_t offset;
        uint32_t limit;
    };

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

#include "QueryEventsGetFilteredByDay.hpp"

namespace db::query::events
{
    GetFilteredByDay::GetFilteredByDay(TimePoint filterDate, uint32_t offset, uint32_t limit)
        : Query(Query::Type::Read), filterDate(filterDate), offset(offset), limit(limit)
    {}

    auto GetFilteredByDay::debugInfo() const -> std::string
    {
        return "GetFilteredByDay";
    }

    GetFilteredByDayResult::GetFilteredByDayResult(const std::vector<EventsRecord> &records, uint32_t count)
        : records{std::move(records)}, recordsCount{count}
    {}

    auto GetFilteredByDayResult::getResult() -> std::vector<EventsRecord>
    {
        return records;
    }

    auto GetFilteredByDayResult::getCountResult() -> uint32_t
    {
        return recordsCount;
    }

    auto GetFilteredByDayResult::debugInfo() const -> std::string
    {
        return "GetFilteredByDayResult";
    }
} // namespace db::query::events

A module-db/queries/calendar/QueryEventsGetFilteredByDay.hpp => module-db/queries/calendar/QueryEventsGetFilteredByDay.hpp +37 -0
@@ 0,0 1,37 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Interface/EventsRecord.hpp"
#include <Common/Query.hpp>
#include <string>
#include <module-apps/application-calendar/data/dateCommon.hpp>

namespace db::query::events
{
    class GetFilteredByDay : public Query
    {
      public:
        GetFilteredByDay(TimePoint filterDate, uint32_t offset = 0, uint32_t limit = UINT32_MAX);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        TimePoint filterDate;
        uint32_t offset;
        uint32_t limit;
    };

    class GetFilteredByDayResult : public QueryResult
    {
        std::vector<EventsRecord> records;
        uint32_t recordsCount;

      public:
        GetFilteredByDayResult(const std::vector<EventsRecord> &records, uint32_t count);
        auto getCountResult() -> uint32_t;
        [[nodiscard]] auto getResult() -> std::vector<EventsRecord>;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

} // namespace db::query::events

M module-db/queries/calendar/QueryEventsSelectFirstUpcoming.cpp => module-db/queries/calendar/QueryEventsSelectFirstUpcoming.cpp +1 -1
@@ 5,7 5,7 @@

namespace db::query::events
{
    SelectFirstUpcoming::SelectFirstUpcoming(calendar::TimePoint filter_from, calendar::TimePoint filter_till)
    SelectFirstUpcoming::SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till)
        : Query(Query::Type::Read), filter_from(filter_from), filter_till(filter_till)
    {}


M module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp => module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp +3 -3
@@ 14,11 14,11 @@ namespace db::query::events
    class SelectFirstUpcoming : public Query
    {
      public:
        SelectFirstUpcoming(calendar::TimePoint filter_from, calendar::TimePoint filter_till);
        SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        calendar::TimePoint filter_from;
        calendar::TimePoint filter_till;
        TimePoint filter_from;
        TimePoint filter_till;
    };

    /// Result of SelectFirstUpcoming query

M module-db/tests/AlarmsRecord_tests.cpp => module-db/tests/AlarmsRecord_tests.cpp +1 -1
@@ 183,7 183,7 @@ TEST_CASE("Alarms Record tests")
    }

    auto getQuery = [&](uint32_t id,
                        calendar::TimePoint alarmTime,
                        TimePoint alarmTime,
                        uint32_t snooze,
                        AlarmStatus status,
                        uint32_t repeat,

M module-db/tests/AlarmsTable_tests.cpp => module-db/tests/AlarmsTable_tests.cpp +5 -5
@@ 138,11 138,11 @@ TEST_CASE("Alarms Table tests")
        REQUIRE(alarmsTbl.add(
            AlarmsTableRow(5, TimePointFromString("2020-12-11 07:15:00"), 1, AlarmStatus::On, 1, "file2.mp3")));

        const std::array<calendar::TimePoint, 5> paramTime{TimePointFromString("2020-12-11 07:15:00"),
                                                           TimePointFromString("2020-11-11 15:10:00"),
                                                           TimePointFromString("2020-11-11 15:15:00"),
                                                           TimePointFromString("2020-11-12 17:10:00"),
                                                           TimePointFromString("2020-11-11 19:25:00")};
        const std::array<TimePoint, 5> paramTime{TimePointFromString("2020-12-11 07:15:00"),
                                                 TimePointFromString("2020-11-11 15:10:00"),
                                                 TimePointFromString("2020-11-11 15:15:00"),
                                                 TimePointFromString("2020-11-12 17:10:00"),
                                                 TimePointFromString("2020-11-11 19:25:00")};

        REQUIRE(alarmsTbl.count() == 5);
        auto entries   = alarmsTbl.getLimitOffset(0, 5);

M module-db/tests/EventsRecord_tests.cpp => module-db/tests/EventsRecord_tests.cpp +3 -6
@@ 730,7 730,7 @@ TEST_CASE("Events Record tests")

    SECTION("Select first upcoming event")
    {
        calendar::TimePoint start_date = TimePointFromString("2019-10-19 14:24:00");
        TimePoint start_date = TimePointFromString("2019-10-19 14:24:00");
        auto nextUpcoming    = eventsRecordInterface.SelectFirstUpcoming(start_date, start_date);
        REQUIRE(nextUpcoming.size() == 1);
        EventsRecord nextEventsRecord = nextUpcoming.at(0);


@@ 799,9 799,7 @@ TEST_CASE("Events Record tests")
        getAll(expectedRecords, numberOfEvents);
    }

    auto getFiltered = [&](calendar::TimePoint filter_from,
                           calendar::TimePoint filter_till,
                           std::vector<EventsRecord> expected_records) {
    auto getFiltered = [&](TimePoint filter_from, TimePoint filter_till, std::vector<EventsRecord> expected_records) {
        auto query  = std::make_shared<db::query::events::GetFiltered>(filter_from, filter_till);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::GetFilteredResult *>(ret.get());


@@ 1013,8 1011,7 @@ TEST_CASE("Events Record tests")
        EditQueryICS(testRecord6.UID, testRecord6);
    }

    [[maybe_unused]] auto selectFirstUpcomingEvent = [&](calendar::TimePoint filter_from,
                                                         calendar::TimePoint filter_till) {
    [[maybe_unused]] auto selectFirstUpcomingEvent = [&](TimePoint filter_from, TimePoint filter_till) {
        auto query  = std::make_shared<db::query::events::SelectFirstUpcoming>(filter_from, filter_till);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::SelectFirstUpcomingResult *>(ret.get());

M module-gui/gui/widgets/ListView.cpp => module-gui/gui/widgets/ListView.cpp +5 -0
@@ 141,6 141,11 @@ namespace gui
        }
    }

    void ListView::setFocusOnElement(unsigned int elementNumber)
    {
        body->setFocusOnElement(elementNumber);
    }

    void ListView::setProvider(std::shared_ptr<ListItemProvider> prov)
    {
        if (prov != nullptr) {

M module-gui/gui/widgets/ListView.hpp => module-gui/gui/widgets/ListView.hpp +1 -0
@@ 87,6 87,7 @@ namespace gui
        std::shared_ptr<ListItemProvider> getProvider();
        void setOrientation(style::listview::Orientation value);
        void setBoundaries(style::listview::Boundaries value);
        void setFocusOnElement(unsigned int elementNumber);
        void setScrollTopMargin(int value);
        void setAlignment(const Alignment &value) override;
        void onProviderDataUpdate();

M module-services/service-appmgr/data/MmiActionsParams.cpp => module-services/service-appmgr/data/MmiActionsParams.cpp +11 -1
@@ 38,7 38,7 @@ auto MMICallForwardingResult::getData() const -> std::tuple<std::string, std::st
void MMICallForwardingResult::accept(Visitor &v, std::string &displayMessage)
{
    v.visit(*this, displayMessage);
};
}

MMIParams::MMIParams(std::string mmiData) : mmiData{std::move(mmiData)}
{}


@@ 62,3 62,13 @@ auto MMIResultParams::getCustomData() const noexcept -> std::shared_ptr<MMICusto
{
    return customResult;
}

void MMICallBarringResult::addMessages(const std::pair<MMIResultMessage, MMIResultMessage> &message) noexcept
{
    data.push_back(message);
}

auto MMICallBarringResult::getMessages(void) noexcept -> std::vector<std::pair<MMIResultMessage, MMIResultMessage>>
{
    return data;
}

M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +3 -0
@@ 246,6 246,9 @@ namespace app::manager
            handleAction(actionMsg);
            return std::make_shared<sys::ResponseMessage>();
        });
        connect(typeid(GetCurrentDisplayLanguageRequest), [&](sys::Message *request) {
            return std::make_shared<GetCurrentDisplayLanguageResponse>(displayLanguage);
        });

        auto convertibleToActionHandler = [this](sys::Message *request) { return handleMessageAsAction(request); };
        connect(typeid(CellularSimRequestPinMessage), convertibleToActionHandler);

M module-services/service-appmgr/service-appmgr/Actions.hpp => module-services/service-appmgr/service-appmgr/Actions.hpp +1 -0
@@ 27,6 27,7 @@ namespace app::manager
            CloseSystem,
            Call,
            Dial,
            EmergencyDial,
            ShowCallLog,
            CreateSms,
            ShowSmsTemplates,

M module-services/service-appmgr/service-appmgr/data/MmiActionsParams.hpp => module-services/service-appmgr/service-appmgr/data/MmiActionsParams.hpp +33 -2
@@ 30,7 30,9 @@ namespace app::manager::actions
            NoneSpecified,
            CallForwardingNotification,
            CallForwardingData,
            Clir
            Clir,
            CallBarringNotification,
            CallBarringData
        };

        enum class MMIResultMessage


@@ 38,7 40,18 @@ namespace app::manager::actions
            NoneSpecifiedSuccess,
            NoneSpecifiedFailed,
            CommonFailure,
            CommonMMINotSupported,
            CommonNoMessage,
            CommonVoice,
            CommonData,
            CommonFax,
            CommonAsync,
            CommonSync,
            CommonAllDisabled,

            CommonActivated,
            CommonDeactivated,
            CommonQuery,

            ClirAccordingToSubscription,
            ClirEnabled,


@@ 49,6 62,7 @@ namespace app::manager::actions
            ClirTemporaryRestricted,
            ClirTemporaryAllowed,

            // Call forwarding
            RegistrationSuccessful,
            RegistrationFailed,
            ErasureSuccessful,


@@ 56,7 70,12 @@ namespace app::manager::actions
            DisablingSuccessful,
            DisablingFailed,
            EnablingSuccessful,
            EnablingFailed
            EnablingFailed,
            CallForwardingDisabled,

            CallBarringActivated,
            CallBarringDeactivated,

        };

        virtual auto getMessage() const -> std::vector<MMIResultMessage> = 0;


@@ 115,6 134,18 @@ namespace app::manager::actions
        {}
    };

    class MMICallBarringResult : public MMICustomResultParams
    {
      public:
        explicit MMICallBarringResult(MMIType type) : MMICustomResultParams(type)
        {}
        void addMessages(const std::pair<MMIResultMessage, MMIResultMessage> &message) noexcept;
        auto getMessages(void) noexcept -> std::vector<std::pair<MMIResultMessage, MMIResultMessage>>;

      private:
        std::vector<std::pair<MMIResultMessage, MMIResultMessage>> data;
    };

    class MMIParams : public ActionParams
    {
      public:

A module-services/service-appmgr/service-appmgr/messages/GetCurrentDisplayLanguageRequest.hpp => module-services/service-appmgr/service-appmgr/messages/GetCurrentDisplayLanguageRequest.hpp +12 -0
@@ 0,0 1,12 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "Message.hpp"

namespace app::manager
{
    class GetCurrentDisplayLanguageRequest : public sys::Message
    {};
} // namespace app::manager

A module-services/service-appmgr/service-appmgr/messages/GetCurrentDisplayLanguageResponse.hpp => module-services/service-appmgr/service-appmgr/messages/GetCurrentDisplayLanguageResponse.hpp +24 -0
@@ 0,0 1,24 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

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

namespace app::manager
{
    class GetCurrentDisplayLanguageResponse : public sys::ResponseMessage
    {
      public:
        explicit GetCurrentDisplayLanguageResponse(Language language) : language(std::move(language))
        {}
        [[nodiscard]] auto getLanguage() const -> Language
        {
            return language;
        }

      private:
        Language language;
    };
} // namespace app::manager

M module-services/service-appmgr/service-appmgr/messages/Message.hpp => module-services/service-appmgr/service-appmgr/messages/Message.hpp +2 -0
@@ 16,3 16,5 @@
#include "PowerSaveModeInitRequest.hpp"
#include "PreventBlockingRequest.hpp"
#include "ShutdownRequest.hpp"
#include "GetCurrentDisplayLanguageRequest.hpp"
#include "GetCurrentDisplayLanguageResponse.hpp"

M module-services/service-cellular/CellularRequestHandler.cpp => module-services/service-cellular/CellularRequestHandler.cpp +125 -9
@@ 15,6 15,9 @@
#include "service-cellular/requests/PinChangeRequest.hpp"
#include "service-cellular/requests/ImeiRequest.hpp"
#include "service-cellular/requests/UssdRequest.hpp"
#include "service-cellular/service-cellular/requests/ClirRequest.hpp"
#include "service-cellular/requests/CallForwardingRequest.hpp"
#include "service-cellular/requests/CallBarringRequest.hpp"

#include <service-appmgr/model/ApplicationManager.hpp>
#include "service-cellular/service-cellular/requests/ClirRequest.hpp"


@@ 22,9 25,7 @@

#include <module-cellular/at/response.hpp>

using namespace cellular;

void CellularRequestHandler::handle(ImeiRequest &request, at::Result &result)
void CellularRequestHandler::handle(cellular::ImeiRequest &request, at::Result &result)
{
    if (!request.checkModemResponse(result)) {
        request.setHandled(false);


@@ 36,7 37,7 @@ void CellularRequestHandler::handle(ImeiRequest &request, at::Result &result)
    sys::Bus::SendUnicast(msg, app::manager::ApplicationManager::ServiceName, &cellular);
}

void CellularRequestHandler::handle(UssdRequest &request, at::Result &result)
void CellularRequestHandler::handle(cellular::UssdRequest &request, at::Result &result)
{
    auto requestHandled = request.checkModemResponse(result);



@@ 50,7 51,7 @@ void CellularRequestHandler::handle(UssdRequest &request, at::Result &result)
    request.setHandled(requestHandled);
}

void CellularRequestHandler::handle(CallRequest &request, at::Result &result)
void CellularRequestHandler::handle(cellular::CallRequest &request, at::Result &result)
{
    if (!request.checkModemResponse(result)) {
        request.setHandled(false);


@@ 66,28 67,28 @@ void CellularRequestHandler::handle(CallRequest &request, at::Result &result)
    request.setHandled(true);
}

void CellularRequestHandler::handle(SupplementaryServicesRequest &request, at::Result &result)
void CellularRequestHandler::handle(cellular::SupplementaryServicesRequest &request, at::Result &result)
{
    auto requestHandled = request.checkModemResponse(result);
    request.setHandled(requestHandled);
    sendMmiResult(requestHandled);
}

void CellularRequestHandler::handle(PasswordRegistrationRequest &request, at::Result &result)
void CellularRequestHandler::handle(cellular::PasswordRegistrationRequest &request, at::Result &result)
{
    auto requestHandled = request.checkModemResponse(result);
    request.setHandled(requestHandled);
    sendMmiResult(requestHandled);
}

void CellularRequestHandler::handle(PinChangeRequest &request, at::Result &result)
void CellularRequestHandler::handle(cellular::PinChangeRequest &request, at::Result &result)
{
    auto requestHandled = request.checkModemResponse(result);
    request.setHandled(requestHandled);
    sendMmiResult(requestHandled);
}

void CellularRequestHandler::handle(ClirRequest &request, at::Result &result)
void CellularRequestHandler::handle(cellular::ClirRequest &request, at::Result &result)
{
    using namespace app::manager::actions;
    using namespace cellular;


@@ 118,6 119,121 @@ void CellularRequestHandler::handle(ClirRequest &request, at::Result &result)
    sys::Bus::SendUnicast(msg, app::manager::ApplicationManager::ServiceName, &cellular);
    request.setHandled(requestHandled);
}

void CellularRequestHandler::handle(cellular::CallForwardingRequest &request, at::Result &result)
{
    using namespace app::manager::actions;
    using namespace cellular;
    using namespace at::response;

    auto requestHandled = request.checkModemResponse(result);
    std::shared_ptr<MMICustomResultParams> response;
    if (requestHandled) {
        auto procedureType = request.getProcedureType();
        if (procedureType == SupplementaryServicesRequest::ProcedureType::Registration) {
            response =
                std::make_shared<MMICallForwardingResult>(IMMICustomResultParams::MMIType::CallForwardingNotification);
            response->addMessage(IMMICustomResultParams::MMIResultMessage::RegistrationSuccessful);
        }
        else if (procedureType == SupplementaryServicesRequest::ProcedureType::Deactivation) {
            response =
                std::make_shared<MMICallForwardingResult>(IMMICustomResultParams::MMIType::CallForwardingNotification);
            response->addMessage(IMMICustomResultParams::MMIResultMessage::DisablingSuccessful);
        }
        else if (procedureType == SupplementaryServicesRequest::ProcedureType::Erasure) {
            response =
                std::make_shared<MMICallForwardingResult>(IMMICustomResultParams::MMIType::CallForwardingNotification);
            response->addMessage(IMMICustomResultParams::MMIResultMessage::ErasureSuccessful);
        }
        else if (procedureType == SupplementaryServicesRequest::ProcedureType::Interrogation) {
            std::vector<ccfc::ParsedCcfc> parsed;
            if (at::response::ccfc::parse(result.response, parsed)) {
                if (ccfc::isAnyActive(parsed)) {
                    auto numbers = ccfc::getNumbers(parsed);
                    response =
                        std::make_shared<MMICallForwardingResult>(IMMICustomResultParams::MMIType::CallForwardingData,
                                                                  numbers.voice,
                                                                  numbers.fax,
                                                                  numbers.sync,
                                                                  numbers.async);
                }
                else {
                    response = std::make_shared<MMICallForwardingResult>(
                        IMMICustomResultParams::MMIType::CallForwardingNotification);
                    response->addMessage(IMMICustomResultParams::MMIResultMessage::CallForwardingDisabled);
                }
            }
        }
    }

    else {
        if (result.code == at::Result::Code::ERROR &&
            std::get<at::EquipmentErrorCode>(result.errorCode) == at::EquipmentErrorCode::PhoneBusy) {
            response =
                std::make_shared<MMICallForwardingResult>(IMMICustomResultParams::MMIType::CallForwardingNotification);
            response->addMessage(IMMICustomResultParams::MMIResultMessage::CommonMMINotSupported);
        }
    }

    if (response.get() == nullptr) {
        response =
            std::make_shared<MMICallForwardingResult>(IMMICustomResultParams::MMIType::CallForwardingNotification);
        response->addMessage(IMMICustomResultParams::MMIResultMessage::CommonFailure);
    }
    auto msg = std::make_shared<CellularMMIResultMessage>(MMIResultParams::MMIResult::Success, response);
    sys::Bus::SendUnicast(msg, app::manager::ApplicationManager::ServiceName, &cellular);
    request.setHandled(requestHandled);
}

void CellularRequestHandler::handle(cellular::CallBarringRequest &request, at::Result &result)
{
    using namespace app::manager::actions;
    using namespace at::response;
    using namespace cellular;
    auto requestHandled = request.checkModemResponse(result);

    std::shared_ptr<MMICustomResultParams> response;
    if (requestHandled) {
        auto procedureType = request.getProcedureType();

        if (procedureType == cellular::SupplementaryServicesRequest::ProcedureType::Activation) {
            response = std::make_shared<MMICallBarringResult>(IMMICustomResultParams::MMIType::CallBarringNotification);
            response->addMessage(IMMICustomResultParams::MMIResultMessage::CallBarringActivated);
        }
        else if (procedureType == SupplementaryServicesRequest::ProcedureType::Deactivation) {
            response = std::make_shared<MMICallBarringResult>(IMMICustomResultParams::MMIType::CallBarringNotification);
            response->addMessage(IMMICustomResultParams::MMIResultMessage::CallBarringDeactivated);
        }
        else if (procedureType == SupplementaryServicesRequest::ProcedureType::Interrogation) {
            std::vector<clck::ClckParsed> parsed;

            if (clck::parseQueryResponse(result.response, parsed)) {
                MMICallBarringResult resp = MMICallBarringResult(IMMICustomResultParams::MMIType::CallBarringData);
                std::sort(parsed.begin(), parsed.end(), clck::iterLessIter);
                for (auto el : parsed) {
                    resp.addMessages(std::make_pair<IMMICustomResultParams::MMIResultMessage,
                                                    IMMICustomResultParams::MMIResultMessage>(
                        mmi::getClass(el.serviceClass), clck::getStatus(el.status)));
                }
                response = std::make_shared<MMICallBarringResult>(resp);
            }
        }
        else {
            response = std::make_shared<MMICallBarringResult>(IMMICustomResultParams::MMIType::CallBarringNotification);
            response->addMessage(IMMICustomResultParams::MMIResultMessage::CommonMMINotSupported);
        }
    }

    if (response == nullptr) {
        response = std::make_shared<MMICallBarringResult>(IMMICustomResultParams::MMIType::CallBarringNotification);
        response->addMessage(IMMICustomResultParams::MMIResultMessage::CommonFailure);
    }

    auto msg = std::make_shared<CellularMMIResultMessage>(MMIResultParams::MMIResult::Success, response);
    sys::Bus::SendUnicast(msg, app::manager::ApplicationManager::ServiceName, &cellular);
    request.setHandled(requestHandled);
}

void CellularRequestHandler::sendMmiResult(bool result)
{
    using namespace app::manager::actions;

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +6 -4
@@ 211,7 211,7 @@ ServiceCellular::ServiceCellular() : sys::Service(serviceName, "", cellularStack
ServiceCellular::~ServiceCellular()
{
    LOG_INFO("[ServiceCellular] Cleaning resources");
    settings->unregisterValueChange(settings::Cellular::volte_on);
    settings->unregisterValueChange(settings::Cellular::volte_on, ::settings::SettingsScope::Global);
}

// this static function will be replaced by Settings API


@@ 233,8 233,10 @@ sys::ReturnCodes ServiceCellular::InitHandler()
    board = EventManagerServiceAPI::GetBoard(this);

    state.set(this, State::ST::WaitForStartPermission);
    settings->registerValueChange(settings::Cellular::volte_on,
                                  [this](const std::string &value) { volteChanged(value); });
    settings->registerValueChange(
        settings::Cellular::volte_on,
        [this](const std::string &value) { volteChanged(value); },
        ::settings::SettingsScope::Global);
    return sys::ReturnCodes::Success;
}



@@ 335,7 337,7 @@ void ServiceCellular::registerMessageHandlers()
    connect(typeid(CellularChangeVoLTEDataMessage), [&](sys::Message *request) -> sys::MessagePointer {
        auto msg = static_cast<CellularChangeVoLTEDataMessage *>(request);
        volteOn  = msg->getVoLTEon();
        settings->setValue(settings::Cellular::volte_on, std::to_string(volteOn));
        settings->setValue(settings::Cellular::volte_on, std::to_string(volteOn), settings::SettingsScope::Global);
        return std::make_shared<CellularResponseMessage>(true);
    });


M module-services/service-cellular/service-cellular/CellularRequestHandler.hpp => module-services/service-cellular/service-cellular/CellularRequestHandler.hpp +2 -0
@@ 19,6 19,8 @@ class CellularRequestHandler : public cellular::RequestHandler
    void handle(cellular::SupplementaryServicesRequest &request, at::Result &result) final;
    void handle(cellular::PinChangeRequest &request, at::Result &result) final;
    void handle(cellular::ClirRequest &request, at::Result &result) final;
    void handle(cellular::CallForwardingRequest &request, at::Result &result) final;
    void handle(cellular::CallBarringRequest &request, at::Result &result) final;

  private:
    ServiceCellular &cellular;

M module-services/service-cellular/service-cellular/RequestHandler.hpp => module-services/service-cellular/service-cellular/RequestHandler.hpp +4 -0
@@ 15,6 15,8 @@ namespace cellular
    class PinChangeRequest;
    class ClipRequest;
    class ClirRequest;
    class CallForwardingRequest;
    class CallBarringRequest;

    class RequestHandler
    {


@@ 26,5 28,7 @@ namespace cellular
        virtual void handle(PinChangeRequest &request, at::Result &result)             = 0;
        virtual void handle(SupplementaryServicesRequest &request, at::Result &result) = 0;
        virtual void handle(ClirRequest &request, at::Result &result)                  = 0;
        virtual void handle(CallForwardingRequest &request, at::Result &result)        = 0;
        virtual void handle(CallBarringRequest &request, at::Result &result)           = 0;
    };
} // namespace cellular

M module-services/service-db/DBServiceAPI.cpp => module-services/service-db/DBServiceAPI.cpp +3 -3
@@ 137,9 137,9 @@ auto DBServiceAPI::MatchContactByPhoneNumber(sys::Service *serv, const utils::Ph
{
    auto msg = std::make_shared<DBContactNumberMessage>(numberView);

    auto ret             = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto ret             = sys::Bus::SendUnicast(std::move(msg), service::name::db, serv, DefaultTimeoutInMs);
    auto contactResponse = dynamic_cast<DBContactNumberResponseMessage *>(ret.second.get());
    if (contactResponse == nullptr) {
    if (contactResponse == nullptr || contactResponse->retCode != sys::ReturnCodes::Success) {
        LOG_ERROR("DB response error, return code: %s", c_str(ret.first));
        return nullptr;
    }


@@ 241,7 241,7 @@ auto DBServiceAPI::ContactSearch(sys::Service *serv, UTF8 primaryName, UTF8 alte
                                                 (alternativeName.length() > 0) ? alternativeName.c_str() : "",
                                                 (number.length() > 0) ? number.c_str() : "");

    auto ret             = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto ret             = sys::Bus::SendUnicast(std::move(msg), service::name::db, serv, DefaultTimeoutInMs);
    auto contactResponse = dynamic_cast<DBContactResponseMessage *>(ret.second.get());
    if (contactResponse == nullptr) {
        LOG_ERROR("DB response error, return code: %s", c_str(ret.first));

M module-services/service-db/ServiceDB.cpp => module-services/service-db/ServiceDB.cpp +1 -1
@@ 334,7 334,7 @@ sys::MessagePointer ServiceDB::DataReceivedHandler(sys::DataMessage *msgl, sys::

        if (ret.has_value()) {
            responseMsg = std::make_shared<DBContactNumberResponseMessage>(
                sys::ReturnCodes::Success, std::make_unique<ContactRecord>(ret->contact));
                sys::ReturnCodes::Success, std::make_unique<ContactRecord>(std::move(ret->contact)));
        }
        else {
            responseMsg = std::make_shared<DBContactNumberResponseMessage>(sys::ReturnCodes::Failure,

M module-services/service-eink/CMakeLists.txt => module-services/service-eink/CMakeLists.txt +1 -1
@@ 16,7 16,7 @@ set(SOURCES
    ServiceEink.cpp
    EinkDisplay.cpp
    messages/ImageMessage.cpp
    messages/PrepareDisplayRequest.cpp
    messages/PrepareDisplayEarlyRequest.cpp
)

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

M module-services/service-eink/EinkDisplay.cpp => module-services/service-eink/EinkDisplay.cpp +102 -47
@@ 17,17 17,23 @@ namespace service::eink
        constexpr auto LutsFileName                  = "Luts.bin";
        constexpr auto LUTDSize                      = 16385;
        constexpr auto LUTCSize                      = 64;
        constexpr auto LUTRSize                      = 256; ///< Needed due to \ref EINK_LUTS_FILE_PATH structure
        constexpr auto LUTRSize                      = 256; ///< Needed due to \ref LutsFileName structure
        constexpr auto LUTSTotalSize                 = LUTDSize + LUTCSize + LUTRSize;
        constexpr auto LUTVersionInterval            = 3;
        constexpr auto LUTSubcritical                = 12;
        constexpr auto LUTCritical                   = 13;

        EinkWaveFormSettings_t createDefaultWaveFormSettings(EinkWaveforms_e waveformMode)
        constexpr auto LUTTemperatureMinimal     = 0;
        constexpr auto LUTTemperatureSubcritical = 38;
        constexpr auto LUTTemperatureCritical    = 43;

        constexpr auto LUTTemperatureOffsetInterval    = 3;
        constexpr auto LUTTemperatureOffsetSubcritical = 12;
        constexpr auto LUTTemperatureOffsetCritical    = 13;

        EinkWaveformSettings_t createDefaultWaveFormSettings(EinkWaveforms_e waveformMode)
        {
            EinkWaveFormSettings_t settings{};
            EinkWaveformSettings_t settings{};
            settings.mode        = waveformMode;
            settings.temperature = DefaultSurroundingTemperature;
            settings.useCounter  = 0;
            settings.LUTCData    = nullptr;
            settings.LUTCSize    = 0;
            settings.LUTDData    = nullptr;


@@ 37,14 43,14 @@ namespace service::eink
    } // namespace

    EinkDisplay::EinkDisplay(::gui::Size screenSize)
        : size{screenSize}, waveformSettings{createDefaultWaveFormSettings(EinkWaveformGC16)},
        : size{screenSize}, currentWaveform{createDefaultWaveFormSettings(EinkWaveformGC16)},
          displayMode{EinkDisplayColorMode_e::EinkDisplayColorModeStandard}
    {}

    EinkDisplay::~EinkDisplay() noexcept
    {
        delete[] waveformSettings.LUTCData;
        delete[] waveformSettings.LUTDData;
        delete[] currentWaveform.LUTCData;
        delete[] currentWaveform.LUTDData;
    }

    EinkStatus_e EinkDisplay::resetAndInit()


@@ 85,27 91,68 @@ namespace service::eink

    EinkBpp_e EinkDisplay::getCurrentBitsPerPixelFormat() const noexcept
    {
        if ((waveformSettings.mode == EinkWaveformA2) || (waveformSettings.mode == EinkWaveformDU2)) {
            return Eink4Bpp; /// this should be 1Bpp, but the OS is not ready for this
        if ((currentWaveform.mode == EinkWaveformA2) || (currentWaveform.mode == EinkWaveformDU2)) {
            return Eink4Bpp; // this should be 1Bpp, but the OS is not ready for this (in 1Bpp → halftones disappear)
        }
        return Eink4Bpp;
    }

    EinkStatus_e EinkDisplay::refresh(EinkDisplayTimingsMode_e refreshMode)
    {
        currentWaveform.useCounter += 1;
        return EinkRefreshImage(pointTopLeft.x, pointTopLeft.y, size.width, size.height, refreshMode);
    }

    bool EinkDisplay::changeWaveform(EinkWaveforms_e mode, std::int32_t temperature)
    bool EinkDisplay::isNewWaveformNeeded(EinkWaveforms_e newMode, std::int32_t newTemperature) const
    {
        constexpr auto lenientTemperatureUseCounter = 50; // arbitrary. not documented
        auto alloweLenientTemperature               = currentWaveform.useCounter < lenientTemperatureUseCounter;

        // at least: modes cannot differ
        if (alloweLenientTemperature && newMode == currentWaveform.mode) {
            bool temperatureFine = false;

            switch (currentWaveform.mode) {
            case EinkWaveformA2:
                [[fallthrough]];
            case EinkWaveformDU2:
                temperatureFine = abs(newTemperature - currentWaveform.temperature) <= 3;
                break;
            case EinkWaveformINIT:
                [[fallthrough]];
            case EinkWaveformGLD16:
                [[fallthrough]];
            case EinkWaveformGC16:
                temperatureFine = abs(newTemperature - currentWaveform.temperature) <= 2;
                break;
            }

            if (temperatureFine) {
                return false;
            }
        }
        return true;
    }

    bool EinkDisplay::setWaveform(EinkWaveforms_e mode, std::int32_t temperature)
    {
        if (temperature == waveformSettings.temperature && mode == waveformSettings.mode) {
            return EinkOK;
        if (!isNewWaveformNeeded(mode, temperature)) {
            return true;
        }
        waveformSettings.temperature = temperature;
        waveformSettings.mode        = mode;

        const auto segment = calculateWaveFormSegment(temperature);
        auto offset        = calculateWaveFormOffset(mode, segment);
        auto currentOffset =
            toWaveformOffset(currentWaveform.mode, toWaveformTemperatureOffset(currentWaveform.temperature));
        // assume it is changed
        currentWaveform.useCounter  = 0;
        currentWaveform.temperature = temperature;
        currentWaveform.mode        = mode;

        auto offset = toWaveformOffset(mode, toWaveformTemperatureOffset(temperature));

        if (offset == currentOffset) {
            // current waveform is still the best fit
            return true;
        }

        auto file = std::fopen(LutsFileName, "rb");
        if (file == nullptr) {


@@ 114,62 161,70 @@ namespace service::eink
        }
        auto fileHandlerCleanup = gsl::finally([&file]() { std::fclose(file); });

        resetWaveFormSettings();
        resetWaveformSettings();
        std::fseek(file, offset, SEEK_SET);
        std::fread(&waveformSettings.LUTDData[1], 1, LUTDSize, file);
        std::fread(&currentWaveform.LUTDData[1], 1, LUTDSize, file);

        // 0x00 - 1 frame, ... , 0x0F - 16 frames
        const uint8_t frameCount = waveformSettings.LUTDData[1] + 1;
        const uint8_t waveformFrameCount = currentWaveform.LUTDData[1] + 1;
        // (frameCount * 64) - size of actual LUT; (+1) - the byte containing frameCount; (+1) - EinkLUTD command
        waveformSettings.LUTDSize = (frameCount * 64) + 1 + 1;
        currentWaveform.LUTDSize = (waveformFrameCount * 64) + 1 + 1;

        offset += LUTDSize;
        std::fseek(file, offset, SEEK_SET);
        std::fread(&waveformSettings.LUTCData[1], 1, LUTCSize, file);
        std::fread(&currentWaveform.LUTCData[1], 1, LUTCSize, file);

        EinkUpdateWaveform(&waveformSettings);
        EinkUpdateWaveform(&currentWaveform);
        return true;
    }

    unsigned int EinkDisplay::calculateWaveFormSegment(std::int32_t temperature) const
    unsigned int EinkDisplay::toWaveformTemperatureOffset(std::int32_t temperature) noexcept
    {
        if (temperature < 38) {
            return temperature / LUTVersionInterval;
        }
        if (temperature < 43) {
            return LUTSubcritical;
        if (temperature >= LUTTemperatureCritical)
            return LUTTemperatureOffsetCritical;
        if (temperature >= LUTTemperatureSubcritical)
            return LUTTemperatureOffsetSubcritical;
        if (temperature < LUTTemperatureMinimal) {
            temperature = 0;
        }
        return LUTCritical;
        return temperature / LUTTemperatureOffsetInterval;
    }

    unsigned int EinkDisplay::toWaveformOffset(unsigned short LUTbank, unsigned int temperatureOffset) noexcept
    {
        constexpr auto singleLUTOffset = (LUTTemperatureOffsetCritical + 1);
        return LUTSTotalSize * (singleLUTOffset * LUTbank + temperatureOffset);
    }

    unsigned int EinkDisplay::calculateWaveFormOffset(EinkWaveforms_e mode, unsigned int segment) const
    unsigned int EinkDisplay::toWaveformOffset(EinkWaveforms_e mode, unsigned int temperatureOffset)
    {
        switch (mode) {
        case EinkWaveformINIT:
            return LUTSTotalSize * segment;
            return toWaveformOffset(0, temperatureOffset);
        case EinkWaveformA2:
            return LUTSTotalSize * (14 + segment);
            return toWaveformOffset(1, temperatureOffset);
        case EinkWaveformDU2:
            return LUTSTotalSize * (28 + segment);
            return toWaveformOffset(2, temperatureOffset);
        case EinkWaveformGLD16:
            return LUTSTotalSize * (42 + segment);
            return toWaveformOffset(3, temperatureOffset);
        case EinkWaveformGC16:
            return LUTSTotalSize * (56 + segment);
            return toWaveformOffset(4, temperatureOffset);
        default:
            throw std::invalid_argument{"Invalid waveform mode."};
        }
        throw std::invalid_argument{"Invalid waveform mode."};
    }

    void EinkDisplay::resetWaveFormSettings()
    void EinkDisplay::resetWaveformSettings()
    {
        delete[] waveformSettings.LUTDData;
        waveformSettings.LUTDSize    = 0;
        waveformSettings.LUTDData    = new std::uint8_t[LUTDSize + 1];
        waveformSettings.LUTDData[0] = EinkLUTD;
        delete[] currentWaveform.LUTDData;
        currentWaveform.LUTDSize    = 0;
        currentWaveform.LUTDData    = new std::uint8_t[LUTDSize + 1];
        currentWaveform.LUTDData[0] = EinkLUTD;

        delete[] waveformSettings.LUTCData;
        waveformSettings.LUTCSize    = LUTCSize;
        waveformSettings.LUTCData    = new std::uint8_t[LUTCSize + 1];
        waveformSettings.LUTCData[0] = EinkLUTC;
        delete[] currentWaveform.LUTCData;
        currentWaveform.LUTCSize    = LUTCSize;
        currentWaveform.LUTCData    = new std::uint8_t[LUTCSize + 1];
        currentWaveform.LUTCData[0] = EinkLUTC;
    }

    void EinkDisplay::setMode(EinkDisplayColorMode_e mode) noexcept

M module-services/service-eink/EinkDisplay.hpp => module-services/service-eink/EinkDisplay.hpp +8 -6
@@ 32,20 32,22 @@ namespace service::eink
        void powerOff();
        void shutdown();

        bool changeWaveform(EinkWaveforms_e mode, std::int32_t temperature);
        bool setWaveform(EinkWaveforms_e mode, std::int32_t temperature);
        void setMode(EinkDisplayColorMode_e mode) noexcept;

        ::gui::Size getSize() const noexcept;

      private:
        unsigned int calculateWaveFormSegment(std::int32_t temperature) const;
        unsigned int calculateWaveFormOffset(EinkWaveforms_e mode, unsigned int segment) const;
        void resetWaveFormSettings();
        EinkBpp_e getCurrentBitsPerPixelFormat() const noexcept;
        static unsigned int toWaveformTemperatureOffset(std::int32_t temperature) noexcept;
        static unsigned int toWaveformOffset(unsigned short LUTbank, unsigned int temperatureOffset) noexcept;
        static unsigned int toWaveformOffset(EinkWaveforms_e mode, unsigned int temperatureOffset);
        bool isNewWaveformNeeded(EinkWaveforms_e newMode, int32_t newTemperature) const;
        void resetWaveformSettings();

        EinkBpp_e getCurrentBitsPerPixelFormat() const noexcept;
        static constexpr ::gui::Point pointTopLeft{0, 0};
        const ::gui::Size size;
        EinkWaveFormSettings_t waveformSettings;
        EinkWaveformSettings_t currentWaveform;
        EinkDisplayColorMode_e displayMode;
    };
} // namespace service::eink

M module-services/service-eink/ServiceEink.cpp => module-services/service-eink/ServiceEink.cpp +11 -11
@@ 3,9 3,9 @@

#include "ServiceEink.hpp"
#include "messages/EinkModeMessage.hpp"
#include "messages/PrepareDisplayRequest.hpp"
#include "messages/PrepareDisplayEarlyRequest.hpp"
#include <service-gui/Common.hpp>
#include <service-gui/messages/EinkReady.hpp>
#include <service-gui/messages/EinkInitialized.hpp>
#include <time/ScopedTime.hpp>

#include <log/log.hpp>


@@ 32,8 32,8 @@ namespace service::eink
        connect(typeid(ImageMessage),
                [this](sys::Message *request) -> sys::MessagePointer { return handleImageMessage(request); });

        connect(typeid(PrepareDisplayRequest),
                [this](sys::Message *request) -> sys::MessagePointer { return handlePrepareRequest(request); });
        connect(typeid(PrepareDisplayEarlyRequest),
                [this](sys::Message *request) -> sys::MessagePointer { return handlePrepareEarlyRequest(request); });
    }

    sys::MessagePointer ServiceEink::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *response)


@@ 51,7 51,7 @@ namespace service::eink

        display.powerOn();

        auto msg = std::make_shared<service::gui::EinkReady>(display.getSize());
        auto msg = std::make_shared<service::gui::EinkInitialized>(display.getSize());
        sys::Bus::SendUnicast(msg, service::name::gui, this);

        return sys::ReturnCodes::Success;


@@ 139,19 139,19 @@ namespace service::eink
    {
        display.powerOn();

        const auto isDeepRefresh = refreshMode == ::gui::RefreshModes::GUI_REFRESH_DEEP;
        if (const auto temperature = EinkGetTemperatureInternal(); isDeepRefresh) {
            display.changeWaveform(EinkWaveforms_e::EinkWaveformGC16, temperature);
        const auto temperature = EinkGetTemperatureInternal();
        if (refreshMode == ::gui::RefreshModes::GUI_REFRESH_DEEP) {
            display.setWaveform(EinkWaveforms_e::EinkWaveformGC16, temperature);
            display.dither();
        }
        else {
            display.changeWaveform(EinkWaveforms_e::EinkWaveformDU2, temperature);
            display.setWaveform(EinkWaveforms_e::EinkWaveformDU2, temperature);
        }
    }

    sys::MessagePointer ServiceEink::handlePrepareRequest(sys::Message *message)
    sys::MessagePointer ServiceEink::handlePrepareEarlyRequest(sys::Message *message)
    {
        const auto waveformUpdateMsg = static_cast<service::eink::PrepareDisplayRequest *>(message);
        const auto waveformUpdateMsg = static_cast<service::eink::PrepareDisplayEarlyRequest *>(message);
        prepareDisplay(waveformUpdateMsg->getRefreshMode());
        return sys::MessageNone{};
    }

M module-services/service-eink/ServiceEink.hpp => module-services/service-eink/ServiceEink.hpp +1 -1
@@ 41,7 41,7 @@ namespace service::eink

        sys::MessagePointer handleEinkModeChangedMessage(sys::Message *message);
        sys::MessagePointer handleImageMessage(sys::Message *message);
        sys::MessagePointer handlePrepareRequest(sys::Message *message);
        sys::MessagePointer handlePrepareEarlyRequest(sys::Message *message);

        EinkDisplay display;
        State currentState;

R module-services/service-eink/messages/PrepareDisplayRequest.cpp => module-services/service-eink/messages/PrepareDisplayEarlyRequest.cpp +3 -3
@@ 1,14 1,14 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "PrepareDisplayRequest.hpp"
#include "PrepareDisplayEarlyRequest.hpp"

namespace service::eink
{
    PrepareDisplayRequest::PrepareDisplayRequest(::gui::RefreshModes refreshMode) : refreshMode{refreshMode}
    PrepareDisplayEarlyRequest::PrepareDisplayEarlyRequest(::gui::RefreshModes refreshMode) : refreshMode{refreshMode}
    {}

    auto PrepareDisplayRequest::getRefreshMode() const noexcept -> ::gui::RefreshModes
    auto PrepareDisplayEarlyRequest::getRefreshMode() const noexcept -> ::gui::RefreshModes
    {
        return refreshMode;
    }

R module-services/service-eink/messages/PrepareDisplayRequest.hpp => module-services/service-eink/messages/PrepareDisplayEarlyRequest.hpp +2 -2
@@ 9,10 9,10 @@

namespace service::eink
{
    class PrepareDisplayRequest : public EinkMessage
    class PrepareDisplayEarlyRequest : public EinkMessage
    {
      public:
        explicit PrepareDisplayRequest(::gui::RefreshModes refreshMode);
        explicit PrepareDisplayEarlyRequest(::gui::RefreshModes refreshMode);

        [[nodiscard]] auto getRefreshMode() const noexcept -> ::gui::RefreshModes;


M module-services/service-gui/ServiceGUI.cpp => module-services/service-gui/ServiceGUI.cpp +16 -12
@@ 1,11 1,11 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ServiceGUI.hpp"
#include "WorkerGUI.hpp"

#include "messages/DrawMessage.hpp"
#include "messages/EinkReady.hpp"
#include "messages/EinkInitialized.hpp"

#include <DrawCommand.hpp>
#include <FontManager.hpp>


@@ 14,7 14,7 @@
#include <service-eink/Common.hpp>
#include <service-eink/messages/ImageMessage.hpp>
#include <service-eink/messages/EinkMessage.hpp>
#include <service-eink/messages/PrepareDisplayRequest.hpp>
#include <service-eink/messages/PrepareDisplayEarlyRequest.hpp>
#include <SystemManager/SystemManager.hpp>

#include <gsl/gsl_util>


@@ 27,7 27,9 @@ namespace service::gui
        constexpr auto ServiceGuiStackDepth  = 4096U;
        constexpr auto ContextsCount         = 2;
        constexpr auto CommandsQueueCapacity = 3;
        constexpr std::chrono::milliseconds ContextReleaseTimeout{1000};
        constexpr std::chrono::milliseconds BSPEinkBusyTimeout{3000}; ///< sync with \ref BSP_EinkBusyTimeout
        constexpr std::chrono::milliseconds RTOSMessageRoundtripTimeout{1000};
        constexpr std::chrono::milliseconds ContextReleaseTimeout{BSPEinkBusyTimeout + RTOSMessageRoundtripTimeout};
    } // namespace

    ServiceGUI::ServiceGUI(const std::string &name, std::string parent)


@@ 52,8 54,8 @@ namespace service::gui

    void ServiceGUI::registerMessageHandlers()
    {
        connect(typeid(EinkReady),
                [this](sys::Message *request) -> sys::MessagePointer { return handleEinkReady(request); });
        connect(typeid(EinkInitialized),
                [this](sys::Message *request) -> sys::MessagePointer { return handleEinkInitialized(request); });

        connect(typeid(DrawMessage),
                [this](sys::Message *request) -> sys::MessagePointer { return handleDrawMessage(request); });


@@ 119,15 121,17 @@ namespace service::gui
                setState(State::Suspended);
            }

            prepareDisplay(drawMsg->mode);
            if (!contextPool->isAnyContextLocked()) {
                prepareDisplayEarly(drawMsg->mode);
            }
            notifyRenderer(std::move(drawMsg->commands), drawMsg->mode);
        }
        return sys::MessageNone{};
    }

    void ServiceGUI::prepareDisplay(::gui::RefreshModes refreshMode)
    void ServiceGUI::prepareDisplayEarly(::gui::RefreshModes refreshMode)
    {
        auto msg = std::make_shared<service::eink::PrepareDisplayRequest>(refreshMode);
        auto msg = std::make_shared<service::eink::PrepareDisplayEarlyRequest>(refreshMode);
        sys::Bus::SendUnicast(msg, service::name::eink, this);
    }



@@ 199,9 203,9 @@ namespace service::gui
        cachedRender = std::nullopt;
    }

    sys::MessagePointer ServiceGUI::handleEinkReady(sys::Message *message)
    sys::MessagePointer ServiceGUI::handleEinkInitialized(sys::Message *message)
    {
        const auto msg = static_cast<service::gui::EinkReady *>(message);
        const auto msg = static_cast<service::gui::EinkInitialized *>(message);
        contextPool    = std::make_unique<ContextPool>(msg->getDisplaySize(), ContextsCount);
        setState(State::Idle);
        return sys::MessageNone{};


@@ 246,4 250,4 @@ namespace service::gui
    {
        return currentState == state;
    }
} /* namespace sgui */
} // namespace service::gui

M module-services/service-gui/ServiceGUI.hpp => module-services/service-gui/ServiceGUI.hpp +2 -2
@@ 61,7 61,7 @@ namespace service::gui
        void cacheRender(int contextId, ::gui::RefreshModes refreshMode);
        void invalidateCache();

        void prepareDisplay(::gui::RefreshModes refreshMode);
        void prepareDisplayEarly(::gui::RefreshModes refreshMode);
        void notifyRenderer(std::list<std::unique_ptr<::gui::DrawCommand>> &&commands, ::gui::RefreshModes refreshMode);
        void enqueueDrawCommands(DrawCommandsQueue::QueueItem &&item);
        void sendOnDisplay(::gui::Context *context, int contextId, ::gui::RefreshModes refreshMode);


@@ 74,7 74,7 @@ namespace service::gui

        sys::MessagePointer handleDrawMessage(sys::Message *message);
        sys::MessagePointer handleGUIRenderingFinished(sys::Message *message);
        sys::MessagePointer handleEinkReady(sys::Message *message);
        sys::MessagePointer handleEinkInitialized(sys::Message *message);
        sys::MessagePointer handleImageDisplayedNotification(sys::Message *message);

        std::unique_ptr<ContextPool> contextPool;

R module-services/service-gui/messages/EinkReady.hpp => module-services/service-gui/messages/EinkInitialized.hpp +2 -2
@@ 9,10 9,10 @@

namespace service::gui
{
    class EinkReady : public GUIMessage
    class EinkInitialized : public GUIMessage
    {
      public:
        explicit EinkReady(::gui::Size displaySize) : einkDisplaySize{displaySize}
        explicit EinkInitialized(::gui::Size displaySize) : einkDisplaySize{displaySize}
        {}

        [[nodiscard]] auto getDisplaySize() const noexcept -> ::gui::Size

M module-services/service-time/service-time/CalendarTimeEvents.hpp => module-services/service-time/service-time/CalendarTimeEvents.hpp +1 -1
@@ 27,7 27,7 @@ namespace stm
    {
      private:
        EventsRecord eventRecord;
        calendar::TimePoint startTP = TIME_POINT_INVALID;
        TimePoint startTP = TIME_POINT_INVALID;

      protected:
        const std::string timerName() override

M module-services/service-time/timeEvents/CalendarTimeEvents.cpp => module-services/service-time/timeEvents/CalendarTimeEvents.cpp +2 -2
@@ 37,8 37,8 @@ namespace stm

    bool CalendarTimeEvents::sendNextEventQuery()
    {
        calendar::TimePoint filterFrom = TimePointNow();
        calendar::TimePoint filterTill = filterFrom;
        TimePoint filterFrom = TimePointNow();
        TimePoint filterTill = filterFrom;
        if (startTP != TIME_POINT_INVALID) {
            filterFrom = std::min(startTP, filterFrom);
            filterTill = filterFrom;

M module-utils/ical/ParserICS.cpp => module-utils/ical/ParserICS.cpp +5 -8
@@ 446,7 446,7 @@ auto Event::timeStringFrom(const std::string &icalTime) const -> std::string
           getSecondsFromIcalTime(icalTime);
}

auto Event::TimePointFromIcalDate(const std::string &icalDateTime) const -> calendar::TimePoint
auto Event::TimePointFromIcalDate(const std::string &icalDateTime) const -> TimePoint
{
    std::string icalDate(getDateFromIcalFormat(icalDateTime));
    std::string icalTime(getTimeFromIcalFormat(icalDateTime));


@@ 459,7 459,7 @@ auto Event::TimePointFromIcalDate(const std::string &icalDateTime) const -> cale
    return TimePointFromString(dateTime.c_str());
}

auto Event::TimePointToIcalDate(const calendar::TimePoint &tp) const -> std::string
auto Event::TimePointToIcalDate(const TimePoint &tp) const -> std::string
{
    constexpr uint32_t bufferLimit = 16;
    auto time                      = TimePointToTimeT(tp);


@@ 541,10 541,7 @@ auto Event::validateUID(const std::string &UID) -> bool
    return validateDT(DTimestamp);
}

Event::Event(const std::string &summary,
             const calendar::TimePoint from,
             calendar::TimePoint till,
             const std::string &uid)
Event::Event(const std::string &summary, const TimePoint from, TimePoint till, const std::string &uid)
{
    if (summary.empty()) {
        isValid = false;


@@ 598,12 595,12 @@ auto Event::getSummary() const -> std::string
    return summary;
}

auto Event::getDTStartTimePoint() const -> calendar::TimePoint
auto Event::getDTStartTimePoint() const -> TimePoint
{
    return dtstart;
}

auto Event::getDTEndTimePoint() const -> calendar::TimePoint
auto Event::getDTEndTimePoint() const -> TimePoint
{
    return dtend;
}

M module-utils/ical/ParserICS.hpp => module-utils/ical/ParserICS.hpp +7 -7
@@ 91,8 91,8 @@ class Event
{
    std::string uid;
    std::string summary;
    calendar::TimePoint dtstart;
    calendar::TimePoint dtend;
    TimePoint dtstart;
    TimePoint dtend;

    auto isDate(const std::string &dt) -> bool;
    auto isTime(const std::string &dt) -> bool;


@@ 112,13 112,13 @@ class Event
    [[nodiscard]] auto dateStringFrom(const std::string &icalDate) const -> std::string;
    [[nodiscard]] auto timeStringFrom(const std::string &icalTime) const -> std::string;

    [[nodiscard]] auto TimePointFromIcalDate(const std::string &icalDateTime) const -> calendar::TimePoint;
    [[nodiscard]] auto TimePointToIcalDate(const calendar::TimePoint &tp) const -> std::string;
    [[nodiscard]] auto TimePointFromIcalDate(const std::string &icalDateTime) const -> TimePoint;
    [[nodiscard]] auto TimePointToIcalDate(const TimePoint &tp) const -> std::string;

  public:
    bool isValid = true;
    Event()      = default;
    Event(const std::string &summary, calendar::TimePoint from, calendar::TimePoint till, const std::string &uid);
    Event(const std::string &summary, TimePoint from, TimePoint till, const std::string &uid);

    void setUID(const std::string &property);
    void setSummary(const std::string &property);


@@ 127,8 127,8 @@ class Event

    [[nodiscard]] auto getUID() const -> std::string;
    [[nodiscard]] auto getSummary() const -> std::string;
    [[nodiscard]] auto getDTStartTimePoint() const -> calendar::TimePoint;
    [[nodiscard]] auto getDTEndTimePoint() const -> calendar::TimePoint;
    [[nodiscard]] auto getDTStartTimePoint() const -> TimePoint;
    [[nodiscard]] auto getDTEndTimePoint() const -> TimePoint;

    [[nodiscard]] auto getDTStartString() const -> std::string;
    [[nodiscard]] auto getDTEndString() const -> std::string;

M module-utils/time/TimeRangeParser.cpp => module-utils/time/TimeRangeParser.cpp +2 -2
@@ 19,8 19,8 @@ namespace utils::time
        return utils::localize.get(utils::time::Locale::getPM());
    }

    std::string TimeRangeParser::getCalendarTimeString(calendar::TimePoint startDate,
                                                       calendar::TimePoint endDate,
    std::string TimeRangeParser::getCalendarTimeString(TimePoint startDate,
                                                       TimePoint endDate,
                                                       Version version,
                                                       bool isMode24H)
    {

M module-utils/time/TimeRangeParser.hpp => module-utils/time/TimeRangeParser.hpp +2 -2
@@ 20,8 20,8 @@ namespace utils::time
        std::string AMPMtoString(bool isAm);

      public:
        std::string getCalendarTimeString(calendar::TimePoint startDate,
                                          calendar::TimePoint endDate,
        std::string getCalendarTimeString(TimePoint startDate,
                                          TimePoint endDate,
                                          Version version = Version::normal,
                                          bool isMode24H  = false);
    };

M module-utils/time/time_conversion.hpp => module-utils/time/time_conversion.hpp +5 -0
@@ 226,6 226,11 @@ namespace utils
                return minutes;
            }

            time_t getHours() const
            {
                return hours;
            }

            UTF8 str(DisplayedFormat displayedFormat = DisplayedFormat::Auto0M) const;

            // uses default format

M test/pytest/service-desktop/test_calendar.py => test/pytest/service-desktop/test_calendar.py +1 -0
@@ 4,6 4,7 @@ import pytest
from harness.interface.defs import status
import copy

@pytest.mark.skip("not working on CI")
@pytest.mark.service_desktop_test
def test_calendar(harness):
    # add events

M tools/download_asset.py => tools/download_asset.py +43 -12
@@ 15,6 15,7 @@ import os
import requests
import sys
from tqdm import tqdm
import time


class Getter(object):


@@ 24,12 25,6 @@ class Getter(object):
        self.host = 'https://api.github.com/repos'
        self.organisation = 'mudita'
        self.repo = 'ecoboot'
        self.restPrefix = self.host
        self.restPrefix += '/'
        self.restPrefix += self.organisation
        self.restPrefix += '/'
        self.restPrefix += self.repo
        self.restPrefix += '/'
        self.apitoken = None
        self.ghLogin = None
        self.getGitRoot()


@@ 38,6 33,16 @@ class Getter(object):
        self.releases = []
        self.workdir = ""

    def genRestPrefix(self):
        self.restPrefix = self.host
        self.restPrefix += '/'
        self.restPrefix += self.organisation
        self.restPrefix += '/'
        self.restPrefix += self.repo
        self.restPrefix += '/'
        return self.restPrefix


    def getGitRoot(self):
        'Find git root directory'
        self.gitRepo = git.Repo(os.getcwd(), search_parent_directories=True)


@@ 78,7 83,8 @@ class Getter(object):
        self.args = args
        self.getGHLogin(args)
        self.getApiToken(args)
        request = self.restPrefix + "releases"
        request = self.genRestPrefix() + "releases"
        print ("Request:", request)
        headers = {'accept': 'application/vnd.github.v3+json'}
        page = 0
        itemsOnPage = 100


@@ 89,12 95,21 @@ class Getter(object):
            response = requests.get(request, auth=(self.ghLogin, self.apitoken), headers=headers, params=queryParams)
            if response.status_code != requests.codes.ok:
                print("download error:", response.status_code)
                print(response.content)
                print("content:", response.content)
                sys.exit(1)
            items = json.loads(response.content)
            count = len(items)
            self.releases += items
        self.releases.sort(key=lambda r: r['published_at'], reverse=True)
        drafts = []
        releases = []
        for item in self.releases:
            if item['published_at'] is None:
                drafts.append(item)
            else:
                releases.append(item)
        releases.sort(key=lambda r: r['published_at'], reverse=True)
        self.releases=drafts
        self.releases.extend(releases)

    def listReleases(self, args):
        self.getReleases(args)


@@ 106,7 121,8 @@ class Getter(object):
    def downloadRelease(self, args):
        print(sys._getframe().f_code.co_name)
        self.getReleases(args)
        print(args.tag)
        print("tag:", args.tag)
        print("asset:", args.asset)
        release = None
        if args.tag is None:
            release = self.releases[0]


@@ 119,7 135,19 @@ class Getter(object):
            print("No release with tag:", args.tag)
        print("release:", release['tag_name'])
        assets = release['assets']
        self.downloadAsset(assets[0])
        if len(assets) > 1 and args.asset is None:
            print("Available assets")
            i=0
            for asset in assets:
                print(i, asset['name'])
                i+=1
            print("Use `-a <number>` to select file")
            return
        if args.asset is not None:
            self.downloadAsset(assets[int(args.asset)])
        else:
            self.downloadAsset(assets[0])


    def downloadAsset(self, asset):
        self.createWorkdir()


@@ 131,8 159,10 @@ class Getter(object):
                                auth=(self.ghLogin, self.apitoken),
                                headers=headers,
                                stream=True)
        destination_file = self.args.workdir + "/" + asset['name']
        print("downloading to:", destination_file)
        progres_bar = tqdm(total=asset['size'], unit='iB', unit_scale=True)
        with open(self.args.workdir + "/" + asset['name'], 'wb') as fd:
        with open(destination_file, 'wb') as fd:
            for chunk in response.iter_content(chunk_size=1024):
                progres_bar.update(len(chunk))
                fd.write(chunk)


@@ 183,6 213,7 @@ def main():
                                             description="Download Release based on tag or the latest")
    getReleases_args.set_defaults(func=getter.downloadRelease)
    getReleases_args.add_argument('tag', help="Download release with selected tag", nargs='?')
    getReleases_args.add_argument('-a', '--asset', help="Asset name to download, use asset number")

    args = parser.parse_args()
    getter.repo = args.repository