~aleteoryx/muditaos

5a2a02a1cdd2d1525e7c22224fd1712aabe6cee7 — Maciej Gibowicz 1 year, 7 months ago c77a104
[BH-1989] Add mechanism for downloading What's New entries

Created a parser that downloads all the necessary
entries from the database to be displayed in
What's New application.
M products/BellHybrid/apps/application-bell-whats-new/ApplicationWhatsNew.cpp => products/BellHybrid/apps/application-bell-whats-new/ApplicationWhatsNew.cpp +1 -1
@@ 33,7 33,7 @@ namespace app
            return ret;
        }

        whatsNewModel = std::make_unique<whatsNew::models::WhatsNewModel>();
        whatsNewModel = std::make_unique<whatsNew::models::WhatsNewModel>(this);

        batteryModel                 = std::make_unique<app::BatteryModel>(this);
        lowBatteryInfoModel          = std::make_unique<app::LowBatteryInfoModel>();

M products/BellHybrid/apps/application-bell-whats-new/models/WhatsNewModel.cpp => products/BellHybrid/apps/application-bell-whats-new/models/WhatsNewModel.cpp +55 -2
@@ 2,10 2,63 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "WhatsNewModel.hpp"
#include <ApplicationCommon.hpp>
#include <db/ServiceDB.hpp>
#include <db/WhatsNewMessages.hpp>
#include <product/version.hpp>
#include <Utils.hpp>

namespace
{
    using namespace service::db::whatsNew;
    constexpr auto versionSize{3U};

    std::optional<VersionNumber> getVersionNumber(std::string version)
    {
        std::vector<std::string> strVector{utils::split(version, '.')};
        if (strVector.size() != versionSize) {
            return std::nullopt;
        }

        std::vector<std::uint16_t> uintVector{};
        uintVector.reserve(versionSize);

        for (auto &str : strVector) {
            if (!utils::is_number(str)) {
                return std::nullopt;
            }
            uintVector.push_back(utils::getNumericValue<std::uint16_t>(str));
        }
        return VersionNumber{.major{uintVector[0]}, .minor{uintVector[1]}, .patch{uintVector[2]}};
    }

    std::optional<messages::Response> sendDBRequest(sys::Service *serv, std::shared_ptr<sys::Message> &&msg)
    {
        const auto ret = serv->bus.sendUnicastSync(std::move(msg), service::name::db, sys::BusProxy::defaultTimeout);
        if (ret.first == sys::ReturnCodes::Success) {
            if (auto resp = std::dynamic_pointer_cast<messages::Response>(ret.second)) {
                return *resp;
            }
        }
        return std::nullopt;
    }
} // namespace

namespace app::whatsNew::models
{
    WhatsNewModel::WhatsNewModel()
    {}
    WhatsNewModel::WhatsNewModel(app::ApplicationCommon *app) : app{app}
    {
        const auto version = getVersionNumber(VERSION);
        if (!version.has_value()) {
            return;
        }
        const auto result = sendDBRequest(app, std::make_shared<messages::GetByVersion>(version.value()));
        if (result.has_value()) {
            for (auto &record : result->records) {
                LOG_ERROR("*** changes: %s iconName: %s ***", record.description.c_str(), record.iconName.c_str());
                features.push_back({.description = record.description, .iconName = record.iconName});
            }
        }
    }

} // namespace app::whatsNew::models

M products/BellHybrid/apps/application-bell-whats-new/models/WhatsNewModel.hpp => products/BellHybrid/apps/application-bell-whats-new/models/WhatsNewModel.hpp +19 -1
@@ 3,11 3,29 @@

#pragma once

#include <string>
#include <vector>

namespace app
{
    class ApplicationCommon;
}

namespace app::whatsNew::models
{
    struct Feature
    {
        const std::string description;
        const std::string iconName;
    };

    class WhatsNewModel
    {
      public:
        WhatsNewModel();
        explicit WhatsNewModel(app::ApplicationCommon *app);

      private:
        app::ApplicationCommon *app{nullptr};
        std::vector<Feature> features;
    };
} // namespace app::whatsNew::models

M products/BellHybrid/services/db/CMakeLists.txt => products/BellHybrid/services/db/CMakeLists.txt +2 -0
@@ 10,8 10,10 @@ target_sources(databases
        agents/MeditationStatsAgent.cpp
        agents/QuotesAgent.cpp
        agents/ShuffleQuoteModel.cpp
        agents/WhatsNewAgent.cpp
        agents/QuotesAgent.hpp
        agents/ShuffleQuoteModel.hpp
        agents/WhatsNewAgent.hpp
    PUBLIC
        include/db/ServiceDB.hpp
        include/db/SystemSettings.hpp

M products/BellHybrid/services/db/ServiceDB.cpp => products/BellHybrid/services/db/ServiceDB.cpp +18 -6
@@ 1,10 1,11 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <db/ServiceDB.hpp>
#include "include/db/ServiceDB.hpp"
#include <db/BellFactorySettings.hpp>

#include "agents/MeditationStatsAgent.hpp"
#include "agents/WhatsNewAgent.hpp"

#include <module-db/databases/EventsDB.hpp>
#include <module-db/databases/MultimediaFilesDB.hpp>


@@ 21,6 22,16 @@
#include <CrashdumpMetadataStore.hpp>
#include <product/version.hpp>

namespace
{
    constexpr auto eventsDatabaseName{"events.db"};
    constexpr auto quotesDatabaseName{"quotes.db"};
    constexpr auto multimediaDatabaseName{"multimedia.db"};
    constexpr auto settingsDatabaseName{"settings_bell.db"};
    constexpr auto meditationStatsDatabaseName{"meditation_stats.db"};
    constexpr auto whatsNewDatabaseName{"whats-new.db"};
} // namespace

ServiceDB::~ServiceDB()
{
    eventsDB.reset();


@@ 52,10 63,10 @@ sys::ReturnCodes ServiceDB::InitHandler()
    }

    // Create databases
    eventsDB          = std::make_unique<EventsDB>((purefs::dir::getDatabasesPath() / "events.db").c_str());
    quotesDB          = std::make_unique<Database>((purefs::dir::getDatabasesPath() / "quotes.db").c_str());
    eventsDB          = std::make_unique<EventsDB>((purefs::dir::getDatabasesPath() / eventsDatabaseName).c_str());
    quotesDB          = std::make_unique<Database>((purefs::dir::getDatabasesPath() / quotesDatabaseName).c_str());
    multimediaFilesDB = std::make_unique<db::multimedia_files::MultimediaFilesDB>(
        (purefs::dir::getDatabasesPath() / "multimedia.db").c_str());
        (purefs::dir::getDatabasesPath() / multimediaDatabaseName).c_str());

    // Create record interfaces
    alarmEventRecordInterface = std::make_unique<AlarmEventRecordInterface>(eventsDB.get());


@@ 63,8 74,9 @@ sys::ReturnCodes ServiceDB::InitHandler()
        std::make_unique<db::multimedia_files::MultimediaFilesRecordInterface>(multimediaFilesDB.get());

    const auto factorySettings = std::make_unique<settings::BellFactorySettings>();
    databaseAgents.emplace(std::make_unique<SettingsAgent>(this, "settings_bell.db", factorySettings.get()));
    databaseAgents.emplace(std::make_unique<service::db::agents::MeditationStats>(this, "meditation_stats.db"));
    databaseAgents.emplace(std::make_unique<SettingsAgent>(this, settingsDatabaseName, factorySettings.get()));
    databaseAgents.emplace(std::make_unique<service::db::agents::MeditationStats>(this, meditationStatsDatabaseName));
    databaseAgents.emplace(std::make_unique<service::db::agents::WhatsNew>(this, whatsNewDatabaseName));

    for (auto &dbAgent : databaseAgents) {
        dbAgent->registerMessages();

A products/BellHybrid/services/db/agents/WhatsNewAgent.cpp => products/BellHybrid/services/db/agents/WhatsNewAgent.cpp +74 -0
@@ 0,0 1,74 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "WhatsNewAgent.hpp"
#include "db/WhatsNewMessages.hpp"

#include <Service/Service.hpp>
#include <purefs/filesystem_paths.hpp>
#include <i18n/i18n.hpp>

namespace
{
    using namespace service::db::whatsNew;

    constexpr auto query = "SELECT %s, Icon FROM WhatsNew WHERE Major > %d OR (Major = %d AND Minor > %d) OR (Major = "
                           "%d AND Minor = %d AND Patch > %d)";

    std::vector<Record> getRecordsByVersion(Database *db, VersionNumber version)
    {
        const auto retQuery = db->query(query,
                                        utils::getDisplayLanguage().c_str(),
                                        version.major,
                                        version.major,
                                        version.minor,
                                        version.major,
                                        version.minor,
                                        version.patch);

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

        std::vector<Record> ret;
        ret.reserve(retQuery->getRowCount());

        do {
            ret.push_back({.description{(*retQuery)[0].getString()}, .iconName{(*retQuery)[1].getString()}});
        } while (retQuery->nextRow());

        return ret;
    }
} // namespace

namespace service::db::agents
{
    WhatsNew::WhatsNew(sys::Service *parentService, const std::string dbName)
        : DatabaseAgent{parentService}, dbName{dbName}, db{(purefs::dir::getDatabasesPath() / dbName).c_str()}
    {}

    void WhatsNew::registerMessages()
    {
        parentService->connect(whatsNew::messages::GetByVersion({}),
                               [this](const auto &req) { return handleGetRecordsByVersion(req); });
    }

    void WhatsNew::unRegisterMessages()
    {
        parentService->disconnect(typeid(whatsNew::messages::GetByVersion));
    }

    auto WhatsNew::getAgentName() -> const std::string
    {
        return dbName + "_agent";
    }

    sys::MessagePointer WhatsNew::handleGetRecordsByVersion(const sys::Message *req)
    {
        if (auto msg = dynamic_cast<const whatsNew::messages::GetByVersion *>(req)) {
            const auto records = getRecordsByVersion(&db, msg->version);
            return std::make_shared<whatsNew::messages::Response>(records);
        }
        return std::make_shared<sys::ResponseMessage>();
    }
} // namespace service::db::agents
\ No newline at end of file

A products/BellHybrid/services/db/agents/WhatsNewAgent.hpp => products/BellHybrid/services/db/agents/WhatsNewAgent.hpp +34 -0
@@ 0,0 1,34 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <service-db/DatabaseAgent.hpp>
#include <module-services/service-db/include/service-db/SettingsMessages.hpp>
#include <module-sys/Service/include/Service/Message.hpp>

#include <string>

namespace sys
{
    class Service;
} // namespace sys

namespace service::db::agents
{
    class WhatsNew : public DatabaseAgent
    {
      public:
        WhatsNew(sys::Service *parentService, std::string dbName);

        void registerMessages() override;
        void unRegisterMessages() override;
        auto getAgentName() -> const std::string override;

      private:
        sys::MessagePointer handleGetRecordsByVersion(const sys::Message *req);

        std::string dbName;
        Database db;
    };
} // namespace service::db::agents

A products/BellHybrid/services/db/include/db/WhatsNewMessages.hpp => products/BellHybrid/services/db/include/db/WhatsNewMessages.hpp +44 -0
@@ 0,0 1,44 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <MessageType.hpp>
#include <Service/Message.hpp>
#include <vector>

namespace service::db::whatsNew
{
    struct Record
    {
        std::string description;
        std::string iconName;
    };

    struct VersionNumber
    {
        std::uint16_t major;
        std::uint16_t minor;
        std::uint16_t patch;
    };

    namespace messages
    {
        struct GetByVersion : public sys::DataMessage
        {
            explicit GetByVersion(VersionNumber version) : version{version}
            {}
            VersionNumber version;
        };

        struct Response : public sys::ResponseMessage
        {
            Response() = default;
            explicit Response(const std::vector<Record> &records) : records{records}
            {}

            std::vector<Record> records{};
        };

    } // namespace messages
} // namespace service::db::whatsNew
\ No newline at end of file