~aleteoryx/muditaos

6c900d7b38114cd939c12bd35d7666916af8b5b4 — Piotr Tański 5 years ago 34eb92e
[EGD-5166] Add query-callback synchronization mechanism

Receiver object cancels the callbacks at the end of its lifecycle.
56 files changed, 594 insertions(+), 247 deletions(-)

M changelog.md
M module-apps/Application.cpp
M module-apps/Application.hpp
A module-apps/AsyncTask.cpp
A module-apps/AsyncTask.hpp
M module-apps/CMakeLists.txt
A module-apps/CallbackStorage.cpp
A module-apps/CallbackStorage.hpp
M module-apps/application-alarm-clock/ApplicationAlarmClock.cpp
M module-apps/application-alarm-clock/models/AlarmsRepository.cpp
M module-apps/application-alarm-clock/models/AlarmsRepository.hpp
M module-apps/application-calendar/ApplicationCalendar.cpp
M module-apps/application-calendar/models/AllEventsModel.cpp
M module-apps/application-calendar/models/AllEventsModel.hpp
M module-apps/application-calendar/models/DayEventsModel.cpp
M module-apps/application-calendar/models/DayEventsModel.hpp
M module-apps/application-calendar/windows/CalendarMainWindow.cpp
M module-apps/application-calendar/windows/CalendarMainWindow.hpp
M module-apps/application-calllog/ApplicationCallLog.cpp
M module-apps/application-desktop/ApplicationDesktop.cpp
M module-apps/application-messages/ApplicationMessages.cpp
M module-apps/application-messages/ApplicationMessages.hpp
M module-apps/application-messages/models/BaseThreadsRecordModel.cpp
M module-apps/application-messages/models/BaseThreadsRecordModel.hpp
M module-apps/application-messages/models/SMSTemplateModel.cpp
M module-apps/application-messages/models/SMSTemplateModel.hpp
M module-apps/application-messages/models/SMSThreadModel.cpp
M module-apps/application-messages/models/SMSThreadModel.hpp
M module-apps/application-messages/models/ThreadsModel.cpp
M module-apps/application-messages/models/ThreadsModel.hpp
M module-apps/application-messages/models/ThreadsSearchResultsModel.cpp
M module-apps/application-messages/models/ThreadsSearchResultsModel.hpp
M module-apps/application-messages/windows/MessagesMainWindow.cpp
M module-apps/application-messages/windows/MessagesMainWindow.hpp
M module-apps/application-messages/windows/NewMessage.cpp
M module-apps/application-messages/windows/NewMessage.hpp
M module-apps/application-messages/windows/SMSThreadViewWindow.cpp
M module-apps/application-messages/windows/SMSThreadViewWindow.hpp
M module-apps/application-notes/ApplicationNotes.cpp
M module-apps/application-notes/model/NotesRepository.cpp
M module-apps/application-notes/model/NotesRepository.hpp
M module-apps/application-phonebook/ApplicationPhonebook.cpp
M module-apps/application-phonebook/models/PhonebookModel.cpp
M module-apps/application-phonebook/models/PhonebookModel.hpp
M module-apps/application-settings-new/ApplicationSettings.cpp
M module-apps/application-settings-new/windows/BluetoothWindow.cpp
A module-apps/tests/CMakeLists.txt
A module-apps/tests/test-CallbackStorage.cpp
A module-apps/tests/tests-main.cpp
M module-services/service-cellular/ServiceCellular.cpp
M module-services/service-db/DBServiceAPI.cpp
M module-services/service-db/DBServiceAPI_GetByQuery.cpp
M module-services/service-db/ServiceDB.cpp
M module-services/service-db/service-db/DBServiceAPI.hpp
M module-services/service-desktop/endpoints/calendarEvents/CalendarEventsHelper.cpp
M module-services/service-time/timeEvents/CalendarTimeEvents.cpp
M changelog.md => changelog.md +1 -0
@@ 6,6 6,7 @@
* Battery Brownout detection
* VoLTE ON/OFF switch in Settings Network window
* Add PLL2 clock switching
* Support for asynchronous callbacks on application side.

### Changed


M module-apps/Application.cpp => module-apps/Application.cpp +11 -3
@@ 69,8 69,9 @@ namespace app
                             StartInBackground startInBackground,
                             uint32_t stackDepth,
                             sys::ServicePriority priority)
        : Service(name, parent, stackDepth, priority), default_window(gui::name::window::main_window),
          windowsStack(this), startInBackground{startInBackground}, settings(std::make_unique<settings::Settings>(this))
        : Service(std::move(name), std::move(parent), stackDepth, priority),
          default_window(gui::name::window::main_window), windowsStack(this), startInBackground{startInBackground},
          callbackStorage{std::make_unique<CallbackStorage>()}, settings(std::make_unique<settings::Settings>(this))
    {
        keyTranslator = std::make_unique<gui::KeyInputSimpleTranslation>();
        busChannels.push_back(sys::BusChannels::ServiceCellularNotifications);


@@ 85,7 86,10 @@ namespace app
                                      [this](std::string value) { timeFormatChanged(value); });
    }

    Application::~Application() = default;
    Application::~Application() noexcept
    {
        windowsStack.windows.clear();
    }

    Application::State Application::getState()
    {


@@ 701,4 705,8 @@ namespace app
        return timeFormat12;
    }

    void Application::cancelCallbacks(AsyncCallbackReceiver::Ptr receiver)
    {
        callbackStorage->removeAll(receiver);
    }
} /* namespace app */

M module-apps/Application.hpp => module-apps/Application.hpp +10 -2
@@ 3,8 3,10 @@

#pragma once

#include "AsyncTask.hpp"
#include "Audio/AudioCommon.hpp"                        // for Volume, Play...
#include "Audio/Profiles/Profile.hpp"                   // for Profile, Pro...
#include "CallbackStorage.hpp"
#include "Service/Bus.hpp"                              // for Bus
#include "Service/Common.hpp"                           // for ReturnCodes
#include "Service/Message.hpp"                          // for MessagePointer


@@ 117,7 119,7 @@ namespace app
    /// This is template for creating new applications. Main difference between Application and service is that:
    /// 1. Application has access to GUI and Input
    /// 2. Application lifetime is managed with app::manager::ApplicationManager
    class Application : public sys::Service
    class Application : public sys::Service, public AsyncCallbacksDeleter
    {
      public:
        /// state in which application is right now


@@ 187,7 189,7 @@ namespace app
                    uint32_t stackDepth                 = 4096,
                    sys::ServicePriority priority       = sys::ServicePriority::Idle);

        virtual ~Application();
        virtual ~Application() noexcept;

        Application::State getState();
        void setState(State st);


@@ 282,6 284,8 @@ namespace app

        void toggleTorch(bsp::torch::ColourTemperature temperature);

        void cancelCallbacks(AsyncCallbackReceiver::Ptr receiver) override;

        /// @defgroup Application Application static functions
        /// All this functions are meant to be used in ApplicationManager only
        /// @note consider moving these as private elements of ApplicationManager i.e. under names


@@ 351,6 355,10 @@ namespace app
        /// set of rendering commands will carry information to GUI service that system needs to be closed. After
        /// displaying the screen GUI will notify application manager to request system shutdown.
        bool shutdownInProgress = false;
        /// Storage for asynchronous tasks callbacks.
        std::unique_ptr<CallbackStorage> callbackStorage;
        friend class AsyncTask; // Async tasks need access to application internals, e.g. callback storage, to make
                                // their API simple.

        /// informs self that there was key press
        /// used to provide a way for long press/multipress handling in application

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

#include "AsyncTask.hpp"
#include "Application.hpp"

namespace app
{
    AsyncCallbackReceiver::AsyncCallbackReceiver(AsyncCallbacksDeleter *deleter) noexcept : deleter{deleter}
    {}

    AsyncCallbackReceiver::~AsyncCallbackReceiver()
    {
        if (deleter != nullptr) {
            deleter->cancelCallbacks(this);
        }
    }

    void AsyncTask::execute(Application *application, AsyncCallbackReceiver::Ptr receiverObject)
    {
        const auto requestId = onExecute(application);
        application->callbackStorage->registerCallback(requestId, receiverObject);
    }

    std::unique_ptr<AsyncQuery> AsyncQuery::createFromQuery(std::unique_ptr<db::Query> &&query,
                                                            db::Interface::Name target)
    {
        return std::make_unique<AsyncQuery>(std::move(query), target);
    }

    AsyncQuery::AsyncQuery(std::unique_ptr<db::Query> &&query, db::Interface::Name target) noexcept
        : query{std::move(query)}, target{target}
    {}

    void AsyncQuery::setCallback(std::unique_ptr<db::QueryListener> &&listener) noexcept
    {
        query->setQueryListener(std::move(listener));
    }

    void AsyncQuery::setCallback(db::QueryCallbackFunction &&callback) noexcept
    {
        query->setQueryListener(db::QueryCallback::fromFunction(std::move(callback)));
    }

    RequestId AsyncQuery::onExecute(Application *application)
    {
        const auto [_, id] = DBServiceAPI::GetQuery(application, target, std::move(query));
        return id;
    }

    auto NullCallback::execute() -> bool
    {
        // Nothing to do.
        return false;
    }

    QueryCallback::QueryCallback(db::QueryResponse *response) : response{response}
    {}

    auto QueryCallback::execute() -> bool
    {
        const auto result = response->getResult();
        if (result != nullptr && result->hasListener()) {
            return result->handle();
        }
        return false;
    }
} // namespace app

A module-apps/AsyncTask.hpp => module-apps/AsyncTask.hpp +108 -0
@@ 0,0 1,108 @@
// 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-db/DBServiceAPI.hpp>
#include <service-db/QueryMessage.hpp>

#include <cstdint>

namespace app
{
    class Application; // Forward declaration

    using RequestId = std::uint64_t;
    class AsyncCallbackReceiver;

    class AsyncCallbacksDeleter
    {
      public:
        virtual ~AsyncCallbacksDeleter() noexcept = default;

        virtual void cancelCallbacks(AsyncCallbackReceiver *receiver) = 0;
    };

    class AsyncCallbackReceiver
    {
      public:
        using Ptr = AsyncCallbackReceiver *;

        explicit AsyncCallbackReceiver(AsyncCallbacksDeleter *deleter) noexcept;
        virtual ~AsyncCallbackReceiver() = 0;

      private:
        AsyncCallbacksDeleter *deleter;
    };

    /**
     * Executes an operation on the sender's thread and saves the info about the receiver context for callback use.
     * Intended to use in order to avoid callbacks called on invalid receiver contexts.
     */
    class AsyncTask
    {
      public:
        virtual ~AsyncTask() noexcept = default;

        /**
         * Executes a task in application's thread and saves the receiver object.
         * @param application       Application
         * @param receiverObject    The context of receiver
         */
        void execute(Application *application, AsyncCallbackReceiver::Ptr receiverObject);

      private:
        /**
         * The specific operation that is to be executed by a task.
         * @param application   Application
         * @return Request identifier, to be matched with a response.
         */
        [[nodiscard]] virtual auto onExecute(Application *application) -> RequestId = 0;
    };

    /**
     * A database query that is using the mechanism of AsyncTask.
     */
    class AsyncQuery : public AsyncTask
    {
      public:
        [[nodiscard]] static auto createFromQuery(std::unique_ptr<db::Query> &&query, db::Interface::Name target)
            -> std::unique_ptr<AsyncQuery>;

        AsyncQuery(std::unique_ptr<db::Query> &&query, db::Interface::Name target) noexcept;

        void setCallback(db::QueryCallbackFunction &&callback) noexcept;
        void setCallback(std::unique_ptr<db::QueryListener> &&listener) noexcept;

      private:
        [[nodiscard]] auto onExecute(Application *application) -> RequestId override;

        std::unique_ptr<db::Query> query;
        db::Interface::Name target;
    };

    class AsyncCallback
    {
      public:
        virtual ~AsyncCallback() noexcept = default;

        [[nodiscard]] virtual auto execute() -> bool = 0;
    };

    class NullCallback : public AsyncCallback
    {
      public:
        [[nodiscard]] auto execute() -> bool override;
    };

    class QueryCallback : public AsyncCallback
    {
      public:
        explicit QueryCallback(db::QueryResponse *response);

        [[nodiscard]] auto execute() -> bool override;

      private:
        db::QueryResponse *response;
    };
} // namespace app

M module-apps/CMakeLists.txt => module-apps/CMakeLists.txt +6 -0
@@ 15,6 15,8 @@ set( SOURCES
    "Application.cpp"
    "GuiTimer.cpp"
    "WindowsFactory.cpp"
    "AsyncTask.cpp"
    "CallbackStorage.cpp"
    "windows/AppWindow.cpp" 
    "windows/OptionWindow.cpp"
    "windows/Options.cpp" 


@@ 156,3 158,7 @@ target_compile_features(${PROJECT_NAME} PUBLIC

        cxx_std_14
)

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

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

#include "CallbackStorage.hpp"

#include "MessageType.hpp"

#include <algorithm>

namespace app
{
    CallbackStorage::CallbackEntry::CallbackEntry(RequestId id, AsyncCallbackReceiver::Ptr receiver) noexcept
        : id{id}, receiver{receiver}
    {}

    auto CallbackStorage::getCallback(sys::ResponseMessage *response) -> std::unique_ptr<AsyncCallback>
    {
        if (containsCallbackFor(response)) {
            remove(response);
            return toCallback(response);
        }
        return std::make_unique<NullCallback>();
    }

    auto CallbackStorage::containsCallbackFor(sys::ResponseMessage *response) const noexcept -> bool
    {
        return std::any_of(
            entries.begin(), entries.end(), [response](const auto &entry) { return entry->id == response->uniID; });
    }

    void CallbackStorage::remove(sys::ResponseMessage *response)
    {
        const auto it = std::remove_if(
            entries.begin(), entries.end(), [response](auto &&entry) { return entry->id == response->uniID; });
        entries.erase(it, entries.end());
    }

    auto CallbackStorage::toCallback(sys::ResponseMessage *response) -> std::unique_ptr<AsyncCallback>
    {
        if (response->responseTo == MessageType::DBQuery) {
            if (auto queryResponse = dynamic_cast<db::QueryResponse *>(response); queryResponse != nullptr) {
                return std::make_unique<QueryCallback>(queryResponse);
            }
        }
        return std::make_unique<NullCallback>();
    }

    void CallbackStorage::registerCallback(RequestId id, AsyncCallbackReceiver::Ptr receiver)
    {
        entries.push_back(std::make_unique<CallbackEntry>(id, receiver));
    }

    void CallbackStorage::removeAll(AsyncCallbackReceiver::Ptr receiver)
    {
        const auto it = std::remove_if(
            entries.begin(), entries.end(), [receiver](const auto &entry) { return entry->receiver == receiver; });
        entries.erase(it, entries.end());
    }
} // namespace app

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

#pragma once

#include "AsyncTask.hpp"

#include "module-sys/Service/Message.hpp"

#include <memory>
#include <list>

namespace app
{
    class CallbackStorage
    {
      public:
        [[nodiscard]] auto containsCallbackFor(sys::ResponseMessage *response) const noexcept -> bool;
        [[nodiscard]] auto getCallback(sys::ResponseMessage *response) -> std::unique_ptr<AsyncCallback>;

        void registerCallback(RequestId id, AsyncCallbackReceiver::Ptr receiver);
        void removeAll(AsyncCallbackReceiver::Ptr receiver);

      private:
        [[nodiscard]] static auto toCallback(sys::ResponseMessage *response) -> std::unique_ptr<AsyncCallback>;

        void remove(sys::ResponseMessage *response);

        struct CallbackEntry
        {
          public:
            CallbackEntry(RequestId id, AsyncCallbackReceiver::Ptr receiver) noexcept;

            RequestId id;
            AsyncCallbackReceiver::Ptr receiver;
        };
        std::list<std::unique_ptr<CallbackEntry>> entries;
    };
} // namespace app

M module-apps/application-alarm-clock/ApplicationAlarmClock.cpp => module-apps/application-alarm-clock/ApplicationAlarmClock.cpp +2 -11
@@ 51,17 51,8 @@ namespace app
        // handle database response
        if (resp != nullptr) {
            handled = true;
            switch (resp->responseTo) {
            case MessageType::DBQuery: {
                if (auto queryResponse = dynamic_cast<db::QueryResponse *>(resp)) {
                    auto result = queryResponse->getResult();
                    if (result->hasListener() && result->handle()) {
                        refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
                    }
                }
            } break;
            default:
                break;
            if (auto command = callbackStorage->getCallback(resp); command->execute()) {
                refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
            }
        }
        if (handled) {

M module-apps/application-alarm-clock/models/AlarmsRepository.cpp => module-apps/application-alarm-clock/models/AlarmsRepository.cpp +12 -7
@@ 9,15 9,19 @@
#include <module-db/queries/alarms/QueryAlarmsTurnOffAll.hpp>
#include <service-db/DBServiceAPI.hpp>

#include "AsyncTask.hpp"

namespace app::alarmClock
{
    AlarmsDBRepository::AlarmsDBRepository(Application *application) : application{application}
    AlarmsDBRepository::AlarmsDBRepository(Application *application)
        : AsyncCallbackReceiver{application}, application{application}
    {}

    void AlarmsDBRepository::getLimited(std::uint32_t offset, std::uint32_t limit, const OnGetCallback &callback)
    {
        auto query = std::make_unique<db::query::alarms::GetLimited>(offset, limit);
        query->setQueryListener(db::QueryCallback::fromFunction([callback](auto response) {
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Alarms);
        task->setCallback([callback](auto response) {
            auto result = dynamic_cast<db::query::alarms::GetLimitedResult *>(response);
            if (result == nullptr) {
                return false;


@@ 26,15 30,16 @@ namespace app::alarmClock
                callback(result->getResult(), result->getCountResult());
            }
            return true;
        }));
        DBServiceAPI::GetQuery(application, db::Interface::Name::Alarms, std::move(query));
        });
        task->execute(application, this);
    }

    template <class QueryType, class ResultType>
    void AlarmsDBRepository::GetQuery(std::unique_ptr<QueryType> query,
                                      const AbstractAlarmsRepository::OnResultCallback &callback)
    {
        query->setQueryListener(db::QueryCallback::fromFunction([callback](auto response) {
        auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Alarms);
        task->setCallback([callback](auto response) {
            auto result = dynamic_cast<ResultType *>(response);
            if (result == nullptr) {
                return false;


@@ 43,8 48,8 @@ namespace app::alarmClock
                callback(result->succeed());
            }
            return true;
        }));
        DBServiceAPI::GetQuery(application, db::Interface::Name::Alarms, std::move(query));
        });
        task->execute(application, this);
    }

    void AlarmsDBRepository::add(const AlarmsRecord &alarm, const AbstractAlarmsRepository::OnResultCallback &callback)

M module-apps/application-alarm-clock/models/AlarmsRepository.hpp => module-apps/application-alarm-clock/models/AlarmsRepository.hpp +1 -1
@@ 23,7 23,7 @@ namespace app::alarmClock
        virtual void turnOffAll(const OnResultCallback &callback)                                         = 0;
    };

    class AlarmsDBRepository : public AbstractAlarmsRepository
    class AlarmsDBRepository : public AbstractAlarmsRepository, public AsyncCallbackReceiver
    {
      public:
        explicit AlarmsDBRepository(Application *app);

M module-apps/application-calendar/ApplicationCalendar.cpp => module-apps/application-calendar/ApplicationCalendar.cpp +2 -15
@@ 81,21 81,8 @@ namespace app
        // handle database response
        if (resp != nullptr) {
            handled = true;
            switch (resp->responseTo) {
            case MessageType::DBQuery: {
                if (auto queryResponse = dynamic_cast<db::QueryResponse *>(resp)) {
                    auto result = queryResponse->getResult();
                    LOG_DEBUG("queryResponse != nullptr");
                    if (result->hasListener()) {
                        LOG_DEBUG("Has listener");
                        if (result->handle()) {
                            refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
                        }
                    }
                }
            } break;
            default:
                break;
            if (auto command = callbackStorage->getCallback(resp); command->execute()) {
                refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
            }
        }
        if (handled) {

M module-apps/application-calendar/models/AllEventsModel.cpp => module-apps/application-calendar/models/AllEventsModel.cpp +4 -4
@@ 10,7 10,7 @@
#include <service-db/DBServiceAPI.hpp>
#include <queries/calendar/QueryEventsGetAllLimited.hpp>

AllEventsModel::AllEventsModel(app::Application *app) : DatabaseModel(app)
AllEventsModel::AllEventsModel(app::Application *app) : DatabaseModel(app), app::AsyncCallbackReceiver{app}
{
    application = app;
    assert(app != nullptr);


@@ 24,9 24,9 @@ unsigned int AllEventsModel::requestRecordsCount()
void AllEventsModel::requestRecords(const uint32_t offset, const uint32_t limit)
{
    auto query = std::make_unique<db::query::events::GetAllLimited>(offset, limit);
    query->setQueryListener(
        db::QueryCallback::fromFunction([this](auto response) { return handleQueryResponse(response); }));
    DBServiceAPI::GetQuery(application, db::Interface::Name::Events, std::move(query));
    auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Events);
    task->setCallback([this](auto response) { return handleQueryResponse(response); });
    task->execute(application, this);
}

unsigned int AllEventsModel::getMinimalItemHeight() const

M module-apps/application-calendar/models/AllEventsModel.hpp => module-apps/application-calendar/models/AllEventsModel.hpp +3 -2
@@ 8,13 8,14 @@
#include <module-db/Interface/EventsRecord.hpp>
#include <module-db/Common/Query.hpp>

class AllEventsModel : public app::DatabaseModel<EventsRecord>, public gui::ListItemProvider
class AllEventsModel : public app::DatabaseModel<EventsRecord>,
                       public gui::ListItemProvider,
                       public app::AsyncCallbackReceiver
{
    app::Application *application  = nullptr;

  public:
    AllEventsModel(app::Application *app);
    virtual ~AllEventsModel() override = default;

    void requestRecords(const uint32_t offset, const uint32_t limit) override;
    bool updateRecords(std::vector<EventsRecord> records) override;

M module-apps/application-calendar/models/DayEventsModel.cpp => module-apps/application-calendar/models/DayEventsModel.cpp +5 -4
@@ 11,7 11,8 @@
#include <service-db/QueryMessage.hpp>
#include <module-db/queries/RecordQuery.hpp>

DayEventsModel::DayEventsModel(app::Application *app) : DatabaseModel(app), application(app)
DayEventsModel::DayEventsModel(app::Application *app)
    : DatabaseModel(app), app::AsyncCallbackReceiver{app}, application(app)
{}

unsigned int DayEventsModel::requestRecordsCount()


@@ 27,9 28,9 @@ 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);
    query->setQueryListener(
        db::QueryCallback::fromFunction([this](auto response) { return handleQueryResponse(response); }));
    DBServiceAPI::GetQuery(application, db::Interface::Name::Events, std::move(query));
    auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Events);
    task->setCallback([this](auto response) { return handleQueryResponse(response); });
    task->execute(application, this);
}

gui::ListItem *DayEventsModel::getItem(gui::Order order)

M module-apps/application-calendar/models/DayEventsModel.hpp => module-apps/application-calendar/models/DayEventsModel.hpp +3 -1
@@ 9,7 9,9 @@
#include <ListItemProvider.hpp>
#include <module-db/Interface/EventsRecord.hpp>

class DayEventsModel : public app::DatabaseModel<EventsRecord>, public gui::ListItemProvider
class DayEventsModel : public app::DatabaseModel<EventsRecord>,
                       public gui::ListItemProvider,
                       public app::AsyncCallbackReceiver
{
    app::Application *application = nullptr;
    std::string dayMonthTitle;

M module-apps/application-calendar/windows/CalendarMainWindow.cpp => module-apps/application-calendar/windows/CalendarMainWindow.cpp +8 -7
@@ 17,7 17,8 @@
namespace gui
{

    CalendarMainWindow::CalendarMainWindow(app::Application *app, const std::string &name) : AppWindow(app, name)
    CalendarMainWindow::CalendarMainWindow(app::Application *app, const std::string &name)
        : AppWindow(app, name), app::AsyncCallbackReceiver{app}
    {
        auto appCalendar = dynamic_cast<app::ApplicationCalendar *>(application);
        assert(appCalendar != nullptr);


@@ 185,9 186,9 @@ namespace gui
            assert(application != nullptr);
            app->setEquivalentToEmptyWindow(EquivalentWindow::AllEventsWindow);
            auto query = std::make_unique<db::query::events::GetAll>();
            query->setQueryListener(
                db::QueryCallback::fromFunction([this](auto response) { return handleQueryResponse(response); }));
            DBServiceAPI::GetQuery(application, db::Interface::Name::Events, std::move(query));
            auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Events);
            task->setCallback([this](auto response) { return handleQueryResponse(response); });
            task->execute(application, this);
            return true;
        }



@@ 202,9 203,9 @@ namespace gui
        auto filter_till       = TimePointFromYearMonthDay(date_till);
        LOG_DEBUG("filter:  %s", TimePointToString(filter_till).c_str());
        auto query = std::make_unique<db::query::events::GetFiltered>(filter_from, filter_till);
        query->setQueryListener(
            db::QueryCallback::fromFunction([this](auto response) { return handleQueryResponse(response); }));
        DBServiceAPI::GetQuery(application, db::Interface::Name::Events, std::move(query));
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Events);
        task->setCallback([this](auto response) { return handleQueryResponse(response); });
        task->execute(application, this);
    }

    auto CalendarMainWindow::handleQueryResponse(db::QueryResult *queryResult) -> bool

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

    class CalendarMainWindow : public gui::AppWindow
    class CalendarMainWindow : public gui::AppWindow, public app::AsyncCallbackReceiver
    {
        bool isDayEmpty[31];
        uint32_t offsetFromTop = 0;


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

        ~CalendarMainWindow() override = default;
        void rebuild() override;
        void refresh();
        void filterRequest();

M module-apps/application-calllog/ApplicationCallLog.cpp => module-apps/application-calllog/ApplicationCallLog.cpp +2 -2
@@ 135,9 135,9 @@ namespace app
        DBServiceAPI::GetQuery(this,
                               db::Interface::Name::Notifications,
                               std::make_unique<db::query::notifications::Clear>(NotificationsRecord::Key::Calls));

        return DBServiceAPI::GetQuery(
        const auto [succeed, _] = DBServiceAPI::GetQuery(
            this, db::Interface::Name::Calllog, std::make_unique<db::query::calllog::SetAllRead>());
        return succeed;
    }

} /* namespace app */

M module-apps/application-desktop/ApplicationDesktop.cpp => module-apps/application-desktop/ApplicationDesktop.cpp +2 -1
@@ 257,8 257,9 @@ namespace app

    bool ApplicationDesktop::requestNotSeenNotifications()
    {
        return DBServiceAPI::GetQuery(
        const auto [succeed, _] = DBServiceAPI::GetQuery(
            this, db::Interface::Name::Notifications, std::make_unique<db::query::notifications::GetAll>());
        return succeed;
    }

    bool ApplicationDesktop::requestNotReadNotifications()

M module-apps/application-messages/ApplicationMessages.cpp => module-apps/application-messages/ApplicationMessages.cpp +36 -46
@@ 38,7 38,7 @@
namespace app
{
    ApplicationMessages::ApplicationMessages(std::string name, std::string parent, StartInBackground startInBackground)
        : Application(name, parent, startInBackground, 4096 * 2)
        : Application(name, parent, startInBackground, 4096 * 2), AsyncCallbackReceiver{this}
    {
        busChannels.push_back(sys::BusChannels::ServiceDBNotifications);
        addActionReceiver(manager::actions::CreateSms, [this](auto &&data) {


@@ 51,9 51,6 @@ namespace app
        });
    }

    ApplicationMessages::~ApplicationMessages()
    {}

    // Invoked upon receiving data message
    sys::MessagePointer ApplicationMessages::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)
    {


@@ 84,26 81,8 @@ namespace app
        // handle database response
        if (resp != nullptr) {
            handled = true;
            switch (resp->responseTo) {
            case MessageType::DBThreadGetLimitOffset:
                [[fallthrough]];
            case MessageType::DBSMSTemplateGetLimitOffset:
                if (getCurrentWindow()->onDatabaseMessage(resp)) {
                    refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
                }
                break;
            case MessageType::DBQuery:
                if (auto queryResponse = dynamic_cast<db::QueryResponse *>(resp)) {
                    auto result = queryResponse->getResult();
                    if (result && result->hasListener()) {
                        if (result->handle()) {
                            refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
                        }
                    }
                }
                break;
            default:
                break;
            if (auto command = callbackStorage->getCallback(resp); command->execute()) {
                refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
            }
        }



@@ 193,7 172,8 @@ namespace app
        LOG_DEBUG("Removing thread: %" PRIu32, record->ID);

        auto query = std::make_unique<ContactGetByID>(record->contactID, true);
        query->setQueryListener(db::QueryCallback::fromFunction([this, record](auto response) {
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Contact);
        task->setCallback([this, record](auto response) {
            auto result = dynamic_cast<ContactGetByIDResult *>(response);
            if (result != nullptr) {
                const auto &contact = result->getResult();


@@ 206,8 186,9 @@ namespace app
                return true;
            }
            return false;
        }));
        return DBServiceAPI::GetQuery(this, db::Interface::Name::Contact, std::move(query));
        });
        task->execute(this, this);
        return true;
    }

    bool ApplicationMessages::onRemoveSmsThreadConfirmed(const ThreadRecord &record)


@@ 216,7 197,8 @@ namespace app
        using db::query::ThreadRemoveResult;

        auto query = std::make_unique<ThreadRemove>(record.ID);
        query->setQueryListener(db::QueryCallback::fromFunction([this, threadId = record.ID](auto response) {
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMSThread);
        task->setCallback([this, threadId = record.ID](auto response) {
            const auto result = dynamic_cast<ThreadRemoveResult *>(response);
            if ((result != nullptr) && result->success()) {
                switchWindow(gui::name::window::main_window);


@@ 224,12 206,8 @@ namespace app
            }
            LOG_ERROR("ThreadRemove id=%" PRIu32 " failed", threadId);
            return false;
        }));

        if (const auto ok = DBServiceAPI::GetQuery(this, db::Interface::Name::SMSThread, std::move(query)); !ok) {
            LOG_ERROR("Unable to query DBServiceAPI");
            return false;
        }
        });
        task->execute(this, this);
        return true;
    }



@@ 255,11 233,13 @@ namespace app
        using db::query::ThreadGetByIDResult;

        auto query = std::make_unique<SMSRemove>(record.ID);
        query->setQueryListener(db::QueryCallback::fromFunction([this, record](auto response) {
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMS);
        task->setCallback([this, record](auto response) {
            auto result = dynamic_cast<SMSRemoveResult *>(response);
            if (result != nullptr && result->getResults()) {
                auto query = std::make_unique<ThreadGetByID>(record.threadID);
                query->setQueryListener(db::QueryCallback::fromFunction([this](auto response) {
                auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMSThread);
                task->setCallback([this](auto response) {
                    const auto result = dynamic_cast<ThreadGetByIDResult *>(response);
                    if (result != nullptr) {
                        const auto thread = result->getRecord();


@@ 272,14 252,15 @@ namespace app
                        return true;
                    }
                    return false;
                }));
                return DBServiceAPI::GetQuery(this, db::Interface::Name::SMSThread, std::move(query));
                });
                task->execute(this, this);
                return true;
            }
            LOG_ERROR("sSMSRemove id=%" PRIu32 " failed", record.ID);
            return false;
        }));

        return DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::move(query));
        });
        task->execute(this, this);
        return true;
    }

    bool ApplicationMessages::searchEmpty(const std::string &query)


@@ 320,7 301,9 @@ namespace app
        record.date = utils::time::getCurrentTimestamp().getTime();

        using db::query::SMSUpdate;
        return DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSUpdate>(record));
        const auto [succeed, _] =
            DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSUpdate>(record));
        return succeed;
    }

    std::pair<SMSRecord, bool> ApplicationMessages::createDraft(const utils::PhoneNumber::View &number,


@@ 335,14 318,17 @@ namespace app
        record.date   = utils::time::getCurrentTimestamp().getTime();

        using db::query::SMSAdd;
        const auto success = DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSAdd>(record));
        const auto [success, _] =
            DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSAdd>(record));
        return std::make_pair(record, success);
    }

    bool ApplicationMessages::removeDraft(const SMSRecord &record)
    {
        using db::query::SMSRemove;
        return DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSRemove>(record.ID));
        const auto [succeed, _] =
            DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSRemove>(record.ID));
        return succeed;
    }

    bool ApplicationMessages::sendSms(const utils::PhoneNumber::View &number, const UTF8 &body)


@@ 358,7 344,9 @@ namespace app
        record.date   = utils::time::getCurrentTimestamp().getTime();

        using db::query::SMSAdd;
        return DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSAdd>(record));
        const auto [succeed, _] =
            DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSAdd>(record));
        return succeed;
    }

    bool ApplicationMessages::resendSms(const SMSRecord &record)


@@ 370,7 358,9 @@ namespace app
                                                          // the the bottom, but this is correct

        using db::query::SMSUpdate;
        return DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSUpdate>(resendRecord));
        const auto [succeed, _] =
            DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::make_unique<SMSUpdate>(resendRecord));
        return succeed;
    }

    bool ApplicationMessages::handleSendSmsFromThread(const utils::PhoneNumber::View &number, const UTF8 &body)

M module-apps/application-messages/ApplicationMessages.hpp => module-apps/application-messages/ApplicationMessages.hpp +1 -2
@@ 35,13 35,12 @@ namespace app

    inline constexpr auto name_messages = "ApplicationMessages";

    class ApplicationMessages : public app::Application
    class ApplicationMessages : public app::Application, public app::AsyncCallbackReceiver
    {
      public:
        ApplicationMessages(std::string name                    = name_messages,
                            std::string parent                  = {},
                            StartInBackground startInBackground = {false});
        virtual ~ApplicationMessages();

        sys::MessagePointer DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) override;
        sys::ReturnCodes InitHandler() override;

M module-apps/application-messages/models/BaseThreadsRecordModel.cpp => module-apps/application-messages/models/BaseThreadsRecordModel.cpp +0 -5
@@ 21,8 21,3 @@ bool BaseThreadsRecordModel::updateRecords(std::vector<ThreadListStruct> records
    list->onProviderDataUpdate();
    return true;
}

void BaseThreadsRecordModel::requestRecords(uint32_t offset, uint32_t limit)
{
    DBServiceAPI::ThreadGetLimitOffset(application, offset, limit);
}

M module-apps/application-messages/models/BaseThreadsRecordModel.hpp => module-apps/application-messages/models/BaseThreadsRecordModel.hpp +0 -1
@@ 33,7 33,6 @@ class BaseThreadsRecordModel : public app::DatabaseModel<ThreadListStruct>, publ

    unsigned int requestRecordsCount() override;
    bool updateRecords(std::vector<ThreadListStruct> records) override;
    void requestRecords(const uint32_t offset, const uint32_t limit) override;

    app::Application *getApplication(void)
    {

M module-apps/application-messages/models/SMSTemplateModel.cpp => module-apps/application-messages/models/SMSTemplateModel.cpp +4 -4
@@ 8,7 8,7 @@
#include <service-db/DBServiceAPI.hpp>
#include <module-db/queries/messages/templates/QuerySMSTemplateGetForList.hpp>

SMSTemplateModel::SMSTemplateModel(app::Application *app) : DatabaseModel(app)
SMSTemplateModel::SMSTemplateModel(app::Application *app) : DatabaseModel(app), app::AsyncCallbackReceiver{app}
{}

unsigned int SMSTemplateModel::requestRecordsCount()


@@ 56,9 56,9 @@ gui::ListItem *SMSTemplateModel::getItem(gui::Order order)
void SMSTemplateModel::requestRecords(const uint32_t offset, const uint32_t limit)
{
    auto query = std::make_unique<db::query::SMSTemplateGetForList>(offset, limit);
    query->setQueryListener(
        db::QueryCallback::fromFunction([this](auto response) { return handleQueryResponse(response); }));
    DBServiceAPI::GetQuery(application, db::Interface::Name::SMSTemplate, std::move(query));
    auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMSTemplate);
    task->setCallback([this](auto response) { return handleQueryResponse(response); });
    task->execute(application, this);
}

auto SMSTemplateModel::handleQueryResponse(db::QueryResult *queryResult) -> bool

M module-apps/application-messages/models/SMSTemplateModel.hpp => module-apps/application-messages/models/SMSTemplateModel.hpp +3 -2
@@ 9,12 9,13 @@
#include <Application.hpp>
#include <ListItemProvider.hpp>

class SMSTemplateModel : public app::DatabaseModel<SMSTemplateRecord>, public gui::ListItemProvider
class SMSTemplateModel : public app::DatabaseModel<SMSTemplateRecord>,
                         public gui::ListItemProvider,
                         public app::AsyncCallbackReceiver
{
  public:
    SMSTemplateModel() = delete;
    SMSTemplateModel(app::Application *app);
    virtual ~SMSTemplateModel() = default;

    unsigned int requestRecordsCount() override;
    bool updateRecords(std::vector<SMSTemplateRecord> records) override;

M module-apps/application-messages/models/SMSThreadModel.cpp => module-apps/application-messages/models/SMSThreadModel.cpp +4 -4
@@ 12,7 12,7 @@
#include "SMSThreadModel.hpp"
#include "ListView.hpp"

SMSThreadModel::SMSThreadModel(app::Application *app) : DatabaseModel(app)
SMSThreadModel::SMSThreadModel(app::Application *app) : DatabaseModel(app), app::AsyncCallbackReceiver{app}
{
    smsInput = new gui::SMSInputWidget(application);
}


@@ 52,9 52,9 @@ unsigned int SMSThreadModel::requestRecordsCount()
void SMSThreadModel::requestRecords(uint32_t offset, uint32_t limit)
{
    auto query = std::make_unique<db::query::SMSGetForList>(smsThreadID, offset, limit, numberID);
    query->setQueryListener(
        db::QueryCallback::fromFunction([this](auto response) { return handleQueryResponse(response); }));
    DBServiceAPI::GetQuery(application, db::Interface::Name::SMS, std::move(query));
    auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMS);
    task->setCallback([this](auto response) { return handleQueryResponse(response); });
    task->execute(application, this);
}

bool SMSThreadModel::updateRecords(std::vector<SMSRecord> records)

M module-apps/application-messages/models/SMSThreadModel.hpp => module-apps/application-messages/models/SMSThreadModel.hpp +3 -1
@@ 9,7 9,9 @@
#include "Interface/SMSRecord.hpp"
#include <application-messages/widgets/SMSInputWidget.hpp>

class SMSThreadModel : public app::DatabaseModel<SMSRecord>, public gui::ListItemProvider
class SMSThreadModel : public app::DatabaseModel<SMSRecord>,
                       public gui::ListItemProvider,
                       public app::AsyncCallbackReceiver
{
  public:
    unsigned int smsThreadID      = 0;

M module-apps/application-messages/models/ThreadsModel.cpp => module-apps/application-messages/models/ThreadsModel.cpp +4 -4
@@ 15,7 15,7 @@
#include <module-db/queries/messages/threads/QueryThreadsGetForList.hpp>
#include <service-db/DBServiceAPI.hpp>

ThreadsModel::ThreadsModel(app::Application *app) : BaseThreadsRecordModel(app)
ThreadsModel::ThreadsModel(app::Application *app) : BaseThreadsRecordModel(app), app::AsyncCallbackReceiver{app}
{}

auto ThreadsModel::getMinimalItemHeight() const -> unsigned int


@@ 66,9 66,9 @@ auto ThreadsModel::getItem(gui::Order order) -> gui::ListItem *
void ThreadsModel::requestRecords(uint32_t offset, uint32_t limit)
{
    auto query = std::make_unique<db::query::ThreadsGetForList>(offset, limit);
    query->setQueryListener(
        db::QueryCallback::fromFunction([this](auto response) { return handleQueryResponse(response); }));
    DBServiceAPI::GetQuery(getApplication(), db::Interface::Name::SMSThread, std::move(query));
    auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMSThread);
    task->setCallback([this](auto response) { return handleQueryResponse(response); });
    task->execute(getApplication(), this);
}

auto ThreadsModel::handleQueryResponse(db::QueryResult *queryResult) -> bool

M module-apps/application-messages/models/ThreadsModel.hpp => module-apps/application-messages/models/ThreadsModel.hpp +1 -1
@@ 6,7 6,7 @@
#include <module-db/Interface/ContactRecord.hpp>
#include "BaseThreadsRecordModel.hpp"

class ThreadsModel : public BaseThreadsRecordModel
class ThreadsModel : public BaseThreadsRecordModel, public app::AsyncCallbackReceiver
{
  public:
    explicit ThreadsModel(app::Application *app);

M module-apps/application-messages/models/ThreadsSearchResultsModel.cpp => module-apps/application-messages/models/ThreadsSearchResultsModel.cpp +5 -4
@@ 14,7 14,8 @@
namespace gui::model
{

    ThreadsSearchResultsModel::ThreadsSearchResultsModel(app::Application *app) : BaseThreadsRecordModel(app)
    ThreadsSearchResultsModel::ThreadsSearchResultsModel(app::Application *app)
        : BaseThreadsRecordModel(app), app::AsyncCallbackReceiver{app}
    {}

    auto ThreadsSearchResultsModel::getMinimalItemHeight() const -> unsigned int


@@ 45,9 46,9 @@ namespace gui::model
    {
        if (std::string(textToSearch).compare("") != 0) {
            auto query = std::make_unique<db::query::ThreadsSearchForList>(textToSearch, offset, limit);
            query->setQueryListener(
                db::QueryCallback::fromFunction([this](auto response) { return handleQueryResponse(response); }));
            DBServiceAPI::GetQuery(getApplication(), db::Interface::Name::SMSThread, std::move(query));
            auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMSThread);
            task->setCallback([this](auto response) { return handleQueryResponse(response); });
            task->execute(application, this);
        }
    }


M module-apps/application-messages/models/ThreadsSearchResultsModel.hpp => module-apps/application-messages/models/ThreadsSearchResultsModel.hpp +1 -1
@@ 9,7 9,7 @@
namespace gui::model
{

    class ThreadsSearchResultsModel : public BaseThreadsRecordModel
    class ThreadsSearchResultsModel : public BaseThreadsRecordModel, public app::AsyncCallbackReceiver
    {
        UTF8 textToSearch;


M module-apps/application-messages/windows/MessagesMainWindow.cpp => module-apps/application-messages/windows/MessagesMainWindow.cpp +6 -4
@@ 26,7 26,8 @@

namespace gui
{
    MessagesMainWindow::MessagesMainWindow(app::Application *app) : AppWindow(app, gui::name::window::main_window)
    MessagesMainWindow::MessagesMainWindow(app::Application *app)
        : AppWindow(app, gui::name::window::main_window), app::AsyncCallbackReceiver{app}
    {
        buildInterface();
    }


@@ 112,7 113,8 @@ namespace gui
            if (pdata != nullptr) {
                using db::query::ThreadGetByContactID;
                auto query = std::make_unique<ThreadGetByContactID>(pdata->result->ID);
                query->setQueryListener(db::QueryCallback::fromFunction([app = application](auto response) {
                auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Contact);
                task->setCallback([app = application](auto response) {
                    using db::query::ThreadGetByContactIDResult;
                    const auto result = dynamic_cast<ThreadGetByContactIDResult *>(response);
                    if ((result != nullptr) && result->getRecord().has_value()) {


@@ 123,8 125,8 @@ namespace gui
                    }
                    LOG_FATAL("No thread and thread not created!");
                    return false;
                }));
                DBServiceAPI::GetQuery(application, db::Interface::Name::Contact, std::move(query));
                });
                task->execute(application, this);
            }
        }


M module-apps/application-messages/windows/MessagesMainWindow.hpp => module-apps/application-messages/windows/MessagesMainWindow.hpp +1 -1
@@ 17,7 17,7 @@

namespace gui
{
    class MessagesMainWindow : public AppWindow
    class MessagesMainWindow : public AppWindow, public app::AsyncCallbackReceiver
    {
      protected:
        Image *leftArrowImage                      = nullptr;

M module-apps/application-messages/windows/NewMessage.cpp => module-apps/application-messages/windows/NewMessage.cpp +17 -13
@@ 49,7 49,8 @@ namespace gui
    std::unique_ptr<NewMessageWindow::MessageMemento> NewMessageWindow::memento =
        std::make_unique<NewMessageWindow::MessageMemento>();

    NewMessageWindow::NewMessageWindow(app::Application *app) : AppWindow(app, name::window::new_sms)
    NewMessageWindow::NewMessageWindow(app::Application *app)
        : AppWindow(app, name::window::new_sms), app::AsyncCallbackReceiver{app}
    {
        buildInterface();
    }


@@ 311,7 312,8 @@ namespace gui
    {
        auto number = contactRecord.numbers.front().number;
        auto query  = std::make_unique<db::query::ThreadGetByContactID>(contactRecord.ID);
        query->setQueryListener(db::QueryCallback::fromFunction([this, number](auto response) {
        auto task   = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMSThread);
        task->setCallback([this, number](auto response) {
            const auto result = dynamic_cast<db::query::ThreadGetByContactIDResult *>(response);
            if (result == nullptr) {
                return false;


@@ 323,15 325,16 @@ namespace gui
                return true;
            }
            return addDraftToExistingThread(thread->ID, number, message->getText());
        }));

        return DBServiceAPI::GetQuery(application, db::Interface::Name::SMSThread, std::move(query));
        });
        task->execute(application, this);
        return true;
    }

    auto NewMessageWindow::addDraft(const utils::PhoneNumber &number) -> bool
    {
        auto query = std::make_unique<db::query::ThreadGetByNumber>(number.getView());
        query->setQueryListener(db::QueryCallback::fromFunction([this, number](auto response) {
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMSThread);
        task->setCallback([this, number](auto response) {
            const auto result = dynamic_cast<db::query::ThreadGetByNumberResult *>(response);
            if (result == nullptr) {
                return false;


@@ 343,9 346,9 @@ namespace gui
                return true;
            }
            return addDraftToExistingThread(thread.ID, number.getView(), message->getText());
        }));

        return DBServiceAPI::GetQuery(application, db::Interface::Name::SMSThread, std::move(query));
        });
        task->execute(application, this);
        return true;
    }

    auto NewMessageWindow::addDraftToExistingThread(unsigned int threadId,


@@ 353,7 356,8 @@ namespace gui
                                                    const UTF8 &text) -> bool
    {
        auto query = std::make_unique<db::query::SMSGetLastByThreadID>(threadId);
        query->setQueryListener(db::QueryCallback::fromFunction([this, number](auto response) {
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMS);
        task->setCallback([this, number](auto response) {
            const auto result = dynamic_cast<db::query::SMSGetLastByThreadIDResult *>(response);
            if (result == nullptr) {
                return false;


@@ 366,9 370,9 @@ namespace gui
            }
            storeMessageDraft(number, message->getText());
            return true;
        }));

        return DBServiceAPI::GetQuery(application, db::Interface::Name::SMS, std::move(query));
        });
        task->execute(application, this);
        return true;
    }

    void NewMessageWindow::storeMessageDraft(const utils::PhoneNumber::View &number, const UTF8 &text)

M module-apps/application-messages/windows/NewMessage.hpp => module-apps/application-messages/windows/NewMessage.hpp +2 -1
@@ 6,6 6,7 @@
#include <chrono>
#include <string>

#include <AsyncTask.hpp>
#include <AppWindow.hpp>
#include <PhoneNumber.hpp>
#include <widgets/Text.hpp>


@@ 14,7 15,7 @@

namespace gui
{
    class NewMessageWindow : public AppWindow
    class NewMessageWindow : public AppWindow, public app::AsyncCallbackReceiver
    {
      public:
        explicit NewMessageWindow(app::Application *app);

M module-apps/application-messages/windows/SMSThreadViewWindow.cpp => module-apps/application-messages/windows/SMSThreadViewWindow.cpp +5 -4
@@ 24,7 24,8 @@ namespace gui
{

    SMSThreadViewWindow::SMSThreadViewWindow(app::Application *app)
        : AppWindow(app, name::window::thread_view), smsModel{std::make_shared<SMSThreadModel>(this->application)}
        : AppWindow(app, name::window::thread_view), app::AsyncCallbackReceiver{app},
          smsModel{std::make_shared<SMSThreadModel>(app)}
    {
        AppWindow::buildInterface();
        setTitle(utils::localize.get("app_messages_title_main"));


@@ 124,9 125,9 @@ namespace gui
    auto SMSThreadViewWindow::requestContactName(unsigned int contactID) -> void
    {
        auto query = std::make_unique<db::query::ContactGetByID>(contactID, true);
        query->setQueryListener(db::QueryCallback::fromFunction(
            [this](auto response) { return handleContactNameQueryResponse(response); }));
        DBServiceAPI::GetQuery(application, db::Interface::Name::Contact, std::move(query));
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Contact);
        task->setCallback([this](auto response) { return handleContactNameQueryResponse(response); });
        task->execute(application, this);
    }

    auto SMSThreadViewWindow::handleContactNameQueryResponse(db::QueryResult *queryResult) -> bool

M module-apps/application-messages/windows/SMSThreadViewWindow.hpp => module-apps/application-messages/windows/SMSThreadViewWindow.hpp +1 -1
@@ 14,7 14,7 @@

namespace gui
{
    class SMSThreadViewWindow : public AppWindow
    class SMSThreadViewWindow : public AppWindow, public app::AsyncCallbackReceiver
    {
      private:
        std::shared_ptr<SMSThreadModel> smsModel;

M module-apps/application-notes/ApplicationNotes.cpp => module-apps/application-notes/ApplicationNotes.cpp +2 -12
@@ 56,18 56,8 @@ namespace app
        }

        if (resp != nullptr) {
            switch (resp->responseTo) {
            case MessageType::DBQuery:
                if (auto queryResponse = dynamic_cast<db::QueryResponse *>(resp); queryResponse != nullptr) {
                    if (auto result = queryResponse->getResult(); result && result->hasListener()) {
                        if (result->handle()) {
                            refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
                        }
                    }
                }
                break;
            default:
                break;
            if (auto command = callbackStorage->getCallback(resp); command->execute()) {
                refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
            }
            return msgHandled();
        }

M module-apps/application-notes/model/NotesRepository.cpp => module-apps/application-notes/model/NotesRepository.cpp +18 -13
@@ 12,13 12,15 @@

namespace app::notes
{
    NotesDBRepository::NotesDBRepository(Application *application) : application{application}
    NotesDBRepository::NotesDBRepository(Application *application)
        : app::AsyncCallbackReceiver{application}, application{application}
    {}

    void NotesDBRepository::get(std::uint32_t offset, std::uint32_t limit, const OnGetCallback &callback)
    {
        auto query = std::make_unique<db::query::QueryNotesGet>(offset, limit);
        query->setQueryListener(db::QueryCallback::fromFunction([callback](auto response) {
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Notes);
        task->setCallback([callback](auto response) {
            auto result = dynamic_cast<db::query::NotesGetResult *>(response);
            if (result == nullptr) {
                return false;


@@ 27,14 29,15 @@ namespace app::notes
                callback(result->getRecords(), result->getCount());
            }
            return true;
        }));
        DBServiceAPI::GetQuery(application, db::Interface::Name::Notes, std::move(query));
        });
        task->execute(application, this);
    }

    void NotesDBRepository::getByText(const std::string &text, const OnFilteredCallback &callback)
    {
        auto query = std::make_unique<db::query::QueryNotesGetByText>(text);
        query->setQueryListener(db::QueryCallback::fromFunction([callback](auto response) {
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Notes);
        task->setCallback([callback](auto response) {
            auto result = dynamic_cast<db::query::NotesGetByTextResult *>(response);
            if (result == nullptr) {
                return false;


@@ 43,14 46,15 @@ namespace app::notes
                callback(result->getRecords());
            }
            return true;
        }));
        DBServiceAPI::GetQuery(application, db::Interface::Name::Notes, std::move(query));
        });
        task->execute(application, this);
    }

    void NotesDBRepository::save(const NotesRecord &note, const OnResultCallback &callback)
    {
        auto query = std::make_unique<db::query::QueryNoteStore>(note);
        query->setQueryListener(db::QueryCallback::fromFunction([callback](auto response) {
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Notes);
        task->setCallback([callback](auto response) {
            auto result = dynamic_cast<db::query::NoteStoreResult *>(response);
            if (result == nullptr) {
                return false;


@@ 59,14 63,15 @@ namespace app::notes
                callback(result->succeed());
            }
            return true;
        }));
        DBServiceAPI::GetQuery(application, db::Interface::Name::Notes, std::move(query));
        });
        task->execute(application, this);
    }

    void NotesDBRepository::remove(const NotesRecord &note, const OnResultCallback &callback)
    {
        auto query = std::make_unique<db::query::QueryNoteRemove>(note.ID);
        query->setQueryListener(db::QueryCallback::fromFunction([callback](auto response) {
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Notes);
        task->setCallback([callback](auto response) {
            auto result = dynamic_cast<db::query::NoteRemoveResult *>(response);
            if (result == nullptr) {
                return false;


@@ 75,7 80,7 @@ namespace app::notes
                callback(result->succeed());
            }
            return true;
        }));
        DBServiceAPI::GetQuery(application, db::Interface::Name::Notes, std::move(query));
        });
        task->execute(application, this);
    }
} // namespace app::notes

M module-apps/application-notes/model/NotesRepository.hpp => module-apps/application-notes/model/NotesRepository.hpp +1 -1
@@ 26,7 26,7 @@ namespace app::notes
        virtual void remove(const NotesRecord &note, const OnResultCallback &callback)             = 0;
    };

    class NotesDBRepository : public AbstractNotesRepository
    class NotesDBRepository : public AbstractNotesRepository, public app::AsyncCallbackReceiver
    {
      public:
        explicit NotesDBRepository(Application *application);

M module-apps/application-phonebook/ApplicationPhonebook.cpp => module-apps/application-phonebook/ApplicationPhonebook.cpp +2 -16
@@ 80,22 80,8 @@ namespace app
        // handle database response
        if (resp != nullptr) {
            handled = true;
            switch (resp->responseTo) {
            case MessageType::DBQuery: {

                if (auto queryResponse = dynamic_cast<db::QueryResponse *>(resp)) {
                    auto result = queryResponse->getResult();

                    if (result->hasListener()) {
                        if (result->handle()) {
                            refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
                        }
                    }
                }

            } break;
            default:
                break;
            if (auto command = callbackStorage->getCallback(resp); command->execute()) {
                refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
            }
        }


M module-apps/application-phonebook/models/PhonebookModel.cpp => module-apps/application-phonebook/models/PhonebookModel.cpp +7 -5
@@ 3,6 3,8 @@

#include <application-phonebook/ApplicationPhonebook.hpp>
#include <application-phonebook/windows/PhonebookContactDetails.hpp>
#include <AsyncTask.hpp>

#include "ListView.hpp"
#include "PhonebookModel.hpp"



@@ 24,8 26,8 @@ PhonebookModel::PhonebookModel(app::Application *app,
                               std::string filter,
                               std::uint32_t groupFilter,
                               std::uint32_t displayMode)
    : DatabaseModel(app), queryFilter(std::move(filter)), queryGroupFilter(std::move(groupFilter)),
      queryDisplayMode(std::move(displayMode))
    : DatabaseModel(app), app::AsyncCallbackReceiver{app}, queryFilter(std::move(filter)),
      queryGroupFilter(std::move(groupFilter)), queryDisplayMode(std::move(displayMode))
{}

auto PhonebookModel::requestRecordsCount() -> unsigned int


@@ 60,9 62,9 @@ void PhonebookModel::requestRecords(const uint32_t offset, const uint32_t limit)
{
    auto query =
        std::make_unique<db::query::ContactGet>(offset, limit, queryFilter, queryGroupFilter, queryDisplayMode);
    query->setQueryListener(
        db::QueryCallback::fromFunction([this](auto response) { return handleQueryResponse(response); }));
    DBServiceAPI::GetQuery(application, db::Interface::Name::Contact, std::move(query));
    auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Contact);
    task->setCallback([this](auto response) { return handleQueryResponse(response); });
    task->execute(application, this);
}

auto PhonebookModel::requestLetterMap() -> ContactsMapData

M module-apps/application-phonebook/models/PhonebookModel.hpp => module-apps/application-phonebook/models/PhonebookModel.hpp +3 -2
@@ 17,7 17,9 @@

#include <string>

class PhonebookModel : public app::DatabaseModel<ContactRecord>, public gui::ListItemProvider
class PhonebookModel : public app::DatabaseModel<ContactRecord>,
                       public gui::ListItemProvider,
                       public app::AsyncCallbackReceiver
{
  private:
    std::string queryFilter;


@@ 31,7 33,6 @@ class PhonebookModel : public app::DatabaseModel<ContactRecord>, public gui::Lis
                   std::string filter        = "",
                   std::uint32_t groupFilter = 0,
                   std::uint32_t displayMode = 0);
    ~PhonebookModel() override = default;

    // virtual methods from DatabaseModel
    auto updateRecords(std::vector<ContactRecord> records) -> bool override;

M module-apps/application-settings-new/ApplicationSettings.cpp => module-apps/application-settings-new/ApplicationSettings.cpp +1 -1
@@ 96,7 96,7 @@ namespace app
                currentWindow->rebuild();
            }
        }
        else if (auto responseStatusMsg = dynamic_cast<message::bluetooth::ResponseStatus *>(msgl);
        else if (auto responseStatusMsg = dynamic_cast<::message::bluetooth::ResponseStatus *>(msgl);
                 nullptr != responseStatusMsg) {
            if (gui::window::name::bluetooth == getCurrentWindow()->getName()) {
                auto btStatusData = std::make_unique<gui::BluetoothStatusData>(responseStatusMsg->getStatus());

M module-apps/application-settings-new/windows/BluetoothWindow.cpp => module-apps/application-settings-new/windows/BluetoothWindow.cpp +3 -3
@@ 18,7 18,7 @@ namespace gui
        topBar->setActive(TopBar::Elements::BATTERY, false);
        topBar->setActive(TopBar::Elements::SIM, false);
        sys::Bus::SendUnicast(
            std::make_shared<message::bluetooth::RequestStatus>(), service::name::bluetooth, application);
            std::make_shared<::message::bluetooth::RequestStatus>(), service::name::bluetooth, application);
    }

    void BluetoothWindow::onBeforeShow(ShowMode mode, SwitchData *data)


@@ 116,10 116,10 @@ namespace gui
            btStatus.state = BluetoothStatus::BluetoothState::Off;
        }
        btStatus.visibility = isPhoneVisibilitySwitchOn;
        message::bluetooth::SetStatus setStatus(btStatus);
        ::message::bluetooth::SetStatus setStatus(btStatus);

        sys::Bus::SendUnicast(
            std::make_shared<message::bluetooth::SetStatus>(setStatus), service::name::bluetooth, application);
            std::make_shared<::message::bluetooth::SetStatus>(setStatus), service::name::bluetooth, application);
    }

    void BluetoothWindow::rebuildOptionList()

A module-apps/tests/CMakeLists.txt => module-apps/tests/CMakeLists.txt +9 -0
@@ 0,0 1,9 @@
add_catch2_executable(
    NAME
        callback-storage-test
    SRCS
        tests-main.cpp
        test-CallbackStorage.cpp
    LIBS
        ${PROJECT_NAME}
)

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

#include <catch2/catch.hpp>

#include "CallbackStorage.hpp"

#include <functional>

using namespace app;

class TestCallbacksDeleter : public AsyncCallbacksDeleter
{
  public:
    explicit TestCallbacksDeleter(CallbackStorage &storage) : storage{storage}
    {}

    void cancelCallbacks(AsyncCallbackReceiver *receiver) override
    {
        storage.removeAll(receiver);
    }

  private:
    CallbackStorage &storage;
};

class TestReceiver : public AsyncCallbackReceiver
{
  public:
    TestReceiver(TestCallbacksDeleter *deleter = nullptr) : AsyncCallbackReceiver(deleter)
    {}
};

TEST_CASE("CallbackStorageTests")
{
    CallbackStorage storage;

    SECTION("Get callback")
    {
        constexpr auto MessageId = 1;
        sys::ResponseMessage response{};
        response.uniID = MessageId;

        TestReceiver receiver;
        storage.registerCallback(MessageId, &receiver);
        REQUIRE(storage.containsCallbackFor(&response));

        [[maybe_unused]] auto callback = storage.getCallback(&response);
        REQUIRE(!storage.containsCallbackFor(&response));
    }

    SECTION("Remove receiver")
    {
        constexpr auto MessageId = 2;
        sys::ResponseMessage response{};
        response.uniID = MessageId;

        {
            TestCallbacksDeleter deleter{storage};
            TestReceiver receiver{&deleter};

            storage.registerCallback(MessageId, &receiver);
            REQUIRE(storage.containsCallbackFor(&response));
        }

        REQUIRE(!storage.containsCallbackFor(&response));
        [[maybe_unused]] auto callback = storage.getCallback(&response);
        REQUIRE(callback->execute() == false);
    }
}

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

#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include <catch2/catch.hpp>

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +2 -1
@@ 1820,7 1820,8 @@ bool ServiceCellular::dbAddSMSRecord(const SMSRecord &record)
        onSMSReceived();
        return true;
    }));
    return DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::move(query));
    const auto [succeed, _] = DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::move(query));
    return succeed;
}

void ServiceCellular::onSMSReceived()

M module-services/service-db/DBServiceAPI.cpp => module-services/service-db/DBServiceAPI.cpp +0 -11
@@ 54,17 54,6 @@ auto DBServiceAPI::ThreadGetByNumber(sys::Service *serv,
    return nullptr;
}

auto DBServiceAPI::ThreadGetLimitOffset(sys::Service *serv, uint32_t offset, uint32_t limit) -> bool
{
    std::shared_ptr<DBThreadMessage> msg = std::make_shared<DBThreadMessage>(MessageType::DBThreadGetLimitOffset);
    msg->offset                          = offset;
    msg->limit                           = limit;

    sys::Bus::SendUnicast(msg, service::name::db, serv);

    return true;
}

auto DBServiceAPI::ThreadGetCount(sys::Service *serv, EntryState state) -> uint32_t
{
    auto msg = std::make_shared<DBThreadGetCountMessage>(state);

M module-services/service-db/DBServiceAPI_GetByQuery.cpp => module-services/service-db/DBServiceAPI_GetByQuery.cpp +5 -2
@@ 22,10 22,13 @@ namespace sys
    class Service;
} // namespace sys

bool DBServiceAPI::GetQuery(sys::Service *serv, db::Interface::Name database, std::unique_ptr<db::Query> query)
std::pair<bool, std::uint64_t> DBServiceAPI::GetQuery(sys::Service *serv,
                                                      db::Interface::Name database,
                                                      std::unique_ptr<db::Query> query)
{
    auto msg = std::make_shared<db::QueryMessage>(database, std::move(query));
    return sys::Bus::SendUnicast(msg, service::name::db, serv);
    const auto isSuccess = sys::Bus::SendUnicast(msg, service::name::db, serv);
    return std::make_pair(isSuccess, msg->uniID);
}

sys::SendResult DBServiceAPI::GetQueryWithReply(sys::Service *serv,

M module-services/service-db/ServiceDB.cpp => module-services/service-db/ServiceDB.cpp +0 -9
@@ 209,15 209,6 @@ sys::MessagePointer ServiceDB::DataReceivedHandler(sys::DataMessage *msgl, sys::
        sendUpdateNotification(db::Interface::Name::SMSThread, db::Query::Type::Delete);
    } break;

    case MessageType::DBThreadGetLimitOffset: {
        auto time            = utils::time::Scoped("DBThreadGetLimitOffset");
        DBThreadMessage *msg = reinterpret_cast<DBThreadMessage *>(msgl);
        auto ret             = threadRecordInterface->GetLimitOffset(msg->offset, msg->limit);
        LOG_INFO("Thread get limit offset");
        responseMsg =
            std::make_shared<DBThreadResponseMessage>(std::move(ret), true, msg->limit, msg->offset, ret->size());
    } break;

    case MessageType::DBThreadGetCount: {
        auto *msg   = static_cast<DBThreadGetCountMessage *>(msgl);
        auto time   = utils::time::Scoped("DBThreadGetCountMessage");

M module-services/service-db/service-db/DBServiceAPI.hpp => module-services/service-db/service-db/DBServiceAPI.hpp +10 -2
@@ 52,10 52,18 @@ class DBServiceAPI
    static auto ThreadGetByNumber(sys::Service *serv,
                                  const utils::PhoneNumber::View &phoneNumber,
                                  std::uint32_t timeout = DefaultTimeoutInMs) -> std::unique_ptr<ThreadRecord>;
    static auto ThreadGetLimitOffset(sys::Service *serv, uint32_t offset, uint32_t limit) -> bool;
    static auto ThreadGetCount(sys::Service *serv, EntryState state = EntryState::ALL) -> uint32_t;

    static auto GetQuery(sys::Service *serv, db::Interface::Name database, std::unique_ptr<db::Query> query) -> bool;
    /**
     * Queries the database.
     * @param serv      Sender service.
     * @param database  Target database name.
     * @param query     Query.
     * @return A pair of: a flag that indicates whether query send was successful, and a message identifier that common
     * for the query and its response.
     */
    static auto GetQuery(sys::Service *serv, db::Interface::Name database, std::unique_ptr<db::Query> query)
        -> std::pair<bool, std::uint64_t>;
    static auto GetQueryWithReply(sys::Service *serv,
                                  db::Interface::Name database,
                                  std::unique_ptr<db::Query> query,

M module-services/service-desktop/endpoints/calendarEvents/CalendarEventsHelper.cpp => module-services/service-desktop/endpoints/calendarEvents/CalendarEventsHelper.cpp +8 -4
@@ 184,7 184,7 @@ auto CalendarEventsHelper::requestDataFromDB(Context &context) -> sys::ReturnCod
        context);

    query->setQueryListener(std::move(listener));
    auto ret = DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Events, std::move(query));
    auto [ret, _] = DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Events, std::move(query));

    if (ret) {
        return sys::ReturnCodes::Success;


@@ 280,7 280,9 @@ auto CalendarEventsHelper::createDBEntry(Context &context) -> sys::ReturnCodes
            context);

        query->setQueryListener(std::move(listener));
        ret = ret && DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Events, std::move(query));
        const auto [succeed, _] =
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Events, std::move(query));
        ret = ret && succeed;
    }

    if (ret) {


@@ 315,7 317,9 @@ auto CalendarEventsHelper::updateDBEntry(Context &context) -> sys::ReturnCodes
            context);

        query->setQueryListener(std::move(listener));
        ret = ret && DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Events, std::move(query));
        const auto [succeed, _] =
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Events, std::move(query));
        ret = ret && succeed;
    }
    if (ret) {
        return sys::ReturnCodes::Success;


@@ 341,7 345,7 @@ auto CalendarEventsHelper::deleteDBEntry(Context &context) -> sys::ReturnCodes
        context);

    query->setQueryListener(std::move(listener));
    auto ret = DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Events, std::move(query));
    auto [ret, _] = DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Events, std::move(query));

    if (ret) {
        return sys::ReturnCodes::Success;

M module-services/service-time/timeEvents/CalendarTimeEvents.cpp => module-services/service-time/timeEvents/CalendarTimeEvents.cpp +7 -4
@@ 43,9 43,11 @@ namespace stm
            filterTill = filterFrom;
        }

        return DBServiceAPI::GetQuery(service(),
                                      db::Interface::Name::Events,
                                      std::make_unique<db::query::events::SelectFirstUpcoming>(filterFrom, filterTill));
        const auto [succeed, _] =
            DBServiceAPI::GetQuery(service(),
                                   db::Interface::Name::Events,
                                   std::make_unique<db::query::events::SelectFirstUpcoming>(filterFrom, filterTill));
        return succeed;
    }

    uint32_t CalendarTimeEvents::calcToNextEventInterval(std::unique_ptr<db::QueryResult> nextEventQueryResult)


@@ 74,8 76,9 @@ namespace stm
    bool CalendarTimeEvents::sendEventFiredQuery()
    {
        eventRecord.reminder_fired = TimePointNow();
        return DBServiceAPI::GetQuery(
        const auto [succeed, _]    = DBServiceAPI::GetQuery(
            service(), db::Interface::Name::Events, std::make_unique<db::query::events::Edit>(eventRecord));
        return succeed;
    }

    void CalendarTimeEvents::invokeEvent()