~aleteoryx/muditaos

85cd9dd6902b49221fa94f91b062f64c3ef9df8e — Lukasz Mastalerz 2 years ago f94d7f6
[CP-1852] Too many user files removes default relaxation audio from the list

Changed order in which files in Relaxation are displayed
Added error message when files limit is exceeded
M harmony_changelog.md => harmony_changelog.md +2 -0
@@ 17,10 17,12 @@
### Added

* Added error handling for incorrect audio formats and corrupted files inside Relaxation app
* Added error message when files limit is exceeded in Relaxation app

### Changed

* Added new field to deviceInfo endpoint
* Changed order in which files are displayed in Relaxation

## [1.9.0 2023-04-03]


M image/system_a/data/lang/Deutsch.json => image/system_a/data/lang/Deutsch.json +1 -0
@@ 667,6 667,7 @@
  "app_bell_relaxation_looped": "geschlungen",
  "app_bell_relaxation_loop_description": "der Titel wird abgespielt, bis Sie ihn ausschalten",
  "app_bell_relaxation_error_message": "Nicht unterstütztes Dateiformat",
  "app_bell_relaxation_limit_error_message": "<text>Datenlimit überschritten.<br />Es könnte zu Fehlern kommen.</text>",
  "app_bell_onboarding_info_rotate": "Drehen um auszuwählen",
  "app_bell_onboarding_info_light_click": "Leichter Klick um fortzufahren",
  "app_bell_onboarding_info_deep_click_warning": "Sie haben tief gedrückt",

M image/system_a/data/lang/English.json => image/system_a/data/lang/English.json +1 -0
@@ 716,6 716,7 @@
  "app_bell_relaxation_looped": "looped",
  "app_bell_relaxation_loop_description": "the song will play until you turn it off",
  "app_bell_relaxation_error_message": "Unsupported media type",
  "app_bell_relaxation_limit_error_message": "<text>File limit exceeded.<br />Not all files may be displayed<br />correctly</text>",
  "app_bell_turn_off_question": "Turn off Mudita Harmony?",
  "app_bell_goodbye": "Goodbye",
  "app_bell_reset_message": "<text>Resetting Mudita<br />Harmony</text>",

M image/system_a/data/lang/Espanol.json => image/system_a/data/lang/Espanol.json +1 -0
@@ 683,6 683,7 @@
  "app_bell_relaxation_looped": "en bucle",
  "app_bell_relaxation_loop_description": "la canción se reproducirá hasta que la apagues",
  "app_bell_relaxation_error_message": "Formato de archivo no admitido",
  "app_bell_relaxation_limit_error_message": "<text>Límite de archivos alcanzado,<br />pueden producirse errores.</text>",
  "app_bell_settings_home_view": "Vista de inicio",
  "app_bell_settings_alarm_settings": "Ajustes de alarma",
  "app_bell_settings_alarm_settings_title": "Ajustes de alarma",

M image/system_a/data/lang/Francais.json => image/system_a/data/lang/Francais.json +1 -0
@@ 654,6 654,7 @@
  "app_bell_relaxation_looped": "en boucle",
  "app_bell_relaxation_loop_description": "le morceau sera lu jusqu'à ce que vous l'éteigniez",
  "app_bell_relaxation_error_message": "<text>Format de fichier non pris en<br></br>charge</text>",
  "app_bell_relaxation_limit_error_message": "<text>Limite de fichiers excédée.<br />Risque de problèmes<br />d'affichage</text>",
  "app_bell_settings_home_view": "Écran d'accueil",
  "app_bell_settings_alarm_settings": "Alarme",
  "app_bell_settings_alarm_settings_title": "Alarme",

M image/system_a/data/lang/Polski.json => image/system_a/data/lang/Polski.json +1 -0
@@ 706,6 706,7 @@
  "app_bell_relaxation_looped": "zapętlony",
  "app_bell_relaxation_loop_description": "utwór będzie odtwarzany do momentu wyłączenia go",
  "app_bell_relaxation_error_message": "Nieobsługiwany format pliku",
  "app_bell_relaxation_limit_error_message": "<text>Przekroczono limit plików.<br />Nie wszystkie pliki mogą być<br />wyświetlone poprawnie</text>",
  "app_bell_settings_alarm_settings_prewake_up": "Wstępne budzenie",
  "app_bell_settings_alarm_settings_prewake_up_chime_top_description": "Wstępne budzenie",
  "app_bell_settings_alarm_settings_prewake_up_chime_bottom_description": "przed alarmem",

M module-apps/application-music-player/tests/MockSongsRepository.hpp => module-apps/application-music-player/tests/MockSongsRepository.hpp +5 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 21,6 21,10 @@ namespace testing::app::music
                    getMusicFilesList,
                    (std::uint32_t offset, std::uint32_t limit, const OnGetMusicFilesListCallback &callback),
                    (override));
        MOCK_METHOD(void,
                    getMusicFilesListByPaths,
                    (std::uint32_t offset, std::uint32_t limit, const OnGetMusicFilesListCallback &callback),
                    (override));
        MOCK_METHOD(void, initCache, (), (override));
        MOCK_METHOD(std::size_t, getFileIndex, (const std::string &filePath), (const override));
        MOCK_METHOD(std::string, getNextFilePath, (const std::string &filePath), (const override));

M module-apps/apps-common/models/SongsRepository.cpp => module-apps/apps-common/models/SongsRepository.cpp +42 -24
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "SongsRepository.hpp"


@@ 56,33 56,23 @@ namespace app::music
        task->execute(application, this);
    }

    void SongsRepository::getMusicFilesList(std::uint32_t offset,
                                            std::uint32_t limit,
    void SongsRepository::getMusicFilesList(const std::uint32_t offset,
                                            const std::uint32_t limit,
                                            const OnGetMusicFilesListCallback &callback)
    {
        auto query = std::make_unique<db::multimedia_files::query::GetLimitedByPaths>(pathPrefixes, offset, limit);
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::MultimediaFiles);

        task->setCallback([this, callback, offset](auto response) {
            auto result = dynamic_cast<db::multimedia_files::query::GetLimitedResult *>(response);
            musicFilesViewCache.records.clear();

            if (result == nullptr) {
                return false;
            }
        musicFilesViewCache.records.clear();
        getMusicFilesList(pathPrefixes, offset, limit, callback);
    }

            for (auto &record : result->getResult()) {
                musicFilesViewCache.records.push_back(record);
            }
            musicFilesViewCache.recordsOffset = offset;
            musicFilesViewCache.recordsCount  = result->getCount();
    void SongsRepository::getMusicFilesListByPaths(const std::uint32_t offset,
                                                   const std::uint32_t limit,
                                                   const OnGetMusicFilesListCallback &callback)
    {

            if (callback) {
                callback(musicFilesViewCache.records, musicFilesViewCache.recordsCount);
            }
            return true;
        });
        task->execute(application, this);
        musicFilesViewCache.records.clear();
        for (const auto &pathPrefix : pathPrefixes) {
            getMusicFilesList({pathPrefix}, offset, limit, callback);
        }
    }

    std::size_t SongsRepository::getCachedFileIndex(const std::string &filePath) const


@@ 270,4 260,32 @@ namespace app::music
        return getCachedViewEntryByFilePath(filePath);
    }

    void SongsRepository::getMusicFilesList(const std::vector<std::string> &paths,
                                            const std::uint32_t offset,
                                            const std::uint32_t limit,
                                            const OnGetMusicFilesListCallback &callback)
    {

        auto taskCallback = [this, callback, offset](auto response) {
            auto result = dynamic_cast<db::multimedia_files::query::GetLimitedResult *>(response);
            if (result == nullptr) {
                return false;
            }
            for (auto &record : result->getResult()) {
                musicFilesViewCache.records.push_back(record);
            }
            musicFilesViewCache.recordsOffset = offset;
            musicFilesViewCache.recordsCount  = result->getCount();

            if (callback) {
                callback(musicFilesViewCache.records, musicFilesViewCache.recordsCount);
            }
            return true;
        };
        auto query = std::make_unique<db::multimedia_files::query::GetLimitedByPaths>(
            std::vector<std::string>{paths}, offset, limit);
        auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::MultimediaFiles);
        task->setCallback(taskCallback);
        task->execute(application, this);
    }
} // namespace app::music

M module-apps/apps-common/models/SongsRepository.hpp => module-apps/apps-common/models/SongsRepository.hpp +20 -9
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 50,12 50,15 @@ namespace app::music

        virtual ~AbstractSongsRepository() noexcept = default;

        virtual void initCache()                                                    = 0;
        virtual void getMusicFilesList(std::uint32_t offset,
                                       std::uint32_t limit,
                                       const OnGetMusicFilesListCallback &callback) = 0;
        virtual std::string getNextFilePath(const std::string &filePath) const      = 0;
        virtual std::string getPreviousFilePath(const std::string &filePath) const  = 0;
        virtual void initCache()                                                           = 0;
        virtual void getMusicFilesList(uint32_t offset,
                                       uint32_t limit,
                                       const OnGetMusicFilesListCallback &callback)        = 0;
        virtual void getMusicFilesListByPaths(std::uint32_t offset,
                                              std::uint32_t limit,
                                              const OnGetMusicFilesListCallback &callback) = 0;
        virtual std::string getNextFilePath(const std::string &filePath) const             = 0;
        virtual std::string getPreviousFilePath(const std::string &filePath) const         = 0;
        virtual std::optional<db::multimedia_files::MultimediaFilesRecord> getRecord(
            const std::string &filePath) const                     = 0;
        virtual void updateRepository(const std::string &filePath) = 0;


@@ 68,8 71,11 @@ namespace app::music
                        std::unique_ptr<AbstractTagsFetcher> tagsFetcher,
                        const std::vector<std::string> &pathPrefixes);

        void initCache();
        void getMusicFilesList(std::uint32_t offset, std::uint32_t limit, const OnGetMusicFilesListCallback &callback);
        void initCache() override;
        void getMusicFilesList(uint32_t offset, uint32_t limit, const OnGetMusicFilesListCallback &callback) override;
        void getMusicFilesListByPaths(std::uint32_t offset,
                                      std::uint32_t limit,
                                      const OnGetMusicFilesListCallback &callback) override;
        std::string getNextFilePath(const std::string &filePath) const override;
        std::string getPreviousFilePath(const std::string &filePath) const override;
        std::optional<db::multimedia_files::MultimediaFilesRecord> getRecord(


@@ 124,5 130,10 @@ namespace app::music
        bool newFrontDataCallback(const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                                  unsigned int repoRecordsCount,
                                  std::uint32_t offset);

        void getMusicFilesList(const std::vector<std::string> &paths,
                               std::uint32_t offset,
                               std::uint32_t limit,
                               const OnGetMusicFilesListCallback &callback);
    };
} // namespace app::music

M products/BellHybrid/apps/application-bell-relaxation/CMakeLists.txt => products/BellHybrid/apps/application-bell-relaxation/CMakeLists.txt +1 -0
@@ 37,6 37,7 @@ target_sources(application-bell-relaxation
        data/RelaxationStyle.hpp
        data/RelaxationAudioData.hpp
        data/RelaxationSwitchData.hpp
        data/RelaxationErrorData.hpp
        widgets/RelaxationPlayer.hpp
        windows/RelaxationMainWindow.hpp
        presenter/RelaxationMainWindowPresenter.hpp

A products/BellHybrid/apps/application-bell-relaxation/data/RelaxationErrorData.hpp => products/BellHybrid/apps/application-bell-relaxation/data/RelaxationErrorData.hpp +30 -0
@@ 0,0 1,30 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include <SwitchData.hpp>

namespace gui
{

    enum class RelaxationErrorType
    {
        UnsupportedMediaType,
        FilesLimitExceeded

    };

    class RelaxationErrorData : public SwitchData
    {
        RelaxationErrorType errorType;

      public:
        explicit RelaxationErrorData(const RelaxationErrorType &relaxationErrorType) : errorType{relaxationErrorType}
        {}

        [[nodiscard]] RelaxationErrorType getErrorType()
        {
            return errorType;
        }
    };
} // namespace gui

M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.cpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.cpp +9 -6
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RelaxationMainWindowPresenter.hpp"


@@ 6,8 6,8 @@

namespace
{
    constexpr auto soundsRepoOffset = 0;
    constexpr auto soundsRepoLimit  = 100;
    constexpr auto offset            = 0;
    constexpr auto filesLimitPerPath = 100;
} // namespace

namespace app::relaxation


@@ 19,12 19,15 @@ namespace app::relaxation

    void RelaxationMainWindowPresenter::loadAudioRecords()
    {
        soundsRepository->getMusicFilesList(
            soundsRepoOffset,
            soundsRepoLimit,
        soundsRepository->getMusicFilesListByPaths(
            offset,
            filesLimitPerPath,
            [this](const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                   unsigned int repoRecordsCount) {
                getView()->setSoundsList(records);
                if (repoRecordsCount > filesLimitPerPath) {
                    getView()->handleError();
                }
                return true;
            });
    }

M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.hpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.hpp +2 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 22,6 22,7 @@ namespace app::relaxation
            virtual ~View() = default;

            virtual void setSoundsList(std::vector<db::multimedia_files::MultimediaFilesRecord> songs) = 0;
            virtual void handleError()                                                                 = 0;
        };

        class Presenter : public BasePresenter<RelaxationMainWindowContract::View>

M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationErrorWindow.cpp => products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationErrorWindow.cpp +28 -11
@@ 2,13 2,18 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RelaxationErrorWindow.hpp"
#include <data/RelaxationSwitchData.hpp>
#include <data/RelaxationErrorData.hpp>
#include <data/RelaxationStyle.hpp>
#include <ApplicationBellRelaxation.hpp>
#include <gui/widgets/Icon.hpp>

#include <apps-common/widgets/BellBaseLayout.hpp>

namespace
{
    constexpr auto unsupportedMediaMessage   = "app_bell_relaxation_error_message";
    constexpr auto exceededFilesLimitMessage = "app_bell_relaxation_limit_error_message";
} // namespace

namespace gui
{
    RelaxationErrorWindow::RelaxationErrorWindow(


@@ 30,14 35,8 @@ namespace gui
    {
        statusBar->setVisible(false);

        auto icon = new Icon(this,
                             0,
                             0,
                             style::window_width,
                             style::window_height,
                             "big_information",
                             utils::translate("app_bell_relaxation_error_message"),
                             ImageTypeSpecifier::W_G);
        icon = new Icon(
            this, 0, 0, style::window_width, style::window_height, "big_information", "", ImageTypeSpecifier::W_G);
        icon->image->setMargins({0, gui::relaxationStyle::error::imageMarginTop, 0, 0});
        icon->text->setFont(style::window::font::verybiglight);
        const auto textPadding = icon->text->getPadding();


@@ 45,7 44,6 @@ namespace gui
            {textPadding.left, gui::relaxationStyle::error::textPaddingTop, textPadding.right, textPadding.bottom});
        icon->text->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        icon->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Top));

        icon->resizeItems();
    }



@@ 57,6 55,25 @@ namespace gui
        };
    }

    void RelaxationErrorWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        if (data && typeid(*data) == typeid(RelaxationErrorData)) {
            auto *errorData = static_cast<RelaxationErrorData *>(data);
            switch (errorData->getErrorType()) {
            case RelaxationErrorType::UnsupportedMediaType: {
                icon->text->setRichText(utils::translate(unsupportedMediaMessage));
                break;
            }

            case RelaxationErrorType::FilesLimitExceeded: {
                icon->text->setRichText(utils::translate(exceededFilesLimitMessage));
                break;
            }
            }
        }
        WindowWithTimer::onBeforeShow(mode, data);
    }

    bool RelaxationErrorWindow::onInput(const InputEvent &inputEvent)
    {
        if (inputEvent.isShortRelease(KeyCode::KEY_ENTER) || inputEvent.isShortRelease(KeyCode::KEY_RF)) {

M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationErrorWindow.hpp => products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationErrorWindow.hpp +4 -0
@@ 5,6 5,7 @@

#include "presenter/RelaxationErrorPresenter.hpp"
#include <apps-common/popups/WindowWithTimer.hpp>
#include <gui/widgets/Icon.hpp>
#include <memory>

namespace gui


@@ 19,10 20,13 @@ namespace gui

      private:
        std::unique_ptr<app::relaxation::RelaxationErrorContract::Presenter> presenter;
        std::string errorText;
        gui::Icon *icon = nullptr;

        void buildInterface() override;
        bool onInput(const gui::InputEvent &inputEvent) override;
        void registerCallbacks();
        void buildLayout();
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
    };
} // namespace gui

M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationMainWindow.cpp => products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationMainWindow.cpp +7 -1
@@ 3,6 3,7 @@

#include "RelaxationMainWindow.hpp"
#include <data/RelaxationAudioData.hpp>
#include <data/RelaxationErrorData.hpp>
#include <ApplicationBellRelaxation.hpp>

#include <common/options/OptionBellMenu.hpp>


@@ 41,7 42,6 @@ namespace gui
        for (const auto &sound : sounds) {
            addRecord(sound);
        }

        addOptions(std::move(menuOptionList));
    }



@@ 58,4 58,10 @@ namespace gui
        application->switchWindow(gui::window::name::relaxationTimerSelect, std::move(switchData));
    }

    void RelaxationMainWindow::handleError()
    {
        auto switchData = std::make_unique<RelaxationErrorData>(RelaxationErrorType::FilesLimitExceeded);
        application->switchWindow(gui::window::name::relaxationError, std::move(switchData));
    }

} // namespace gui

M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationMainWindow.hpp => products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationMainWindow.hpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 19,7 19,7 @@ namespace gui
        std::unique_ptr<app::relaxation::RelaxationMainWindowContract::Presenter> presenter;

        void buildInterface() override;

        void handleError() override;
        void setSoundsList(std::vector<db::multimedia_files::MultimediaFilesRecord> soundsTags);
        void onActivated(const db::multimedia_files::MultimediaFilesRecord &selectedSound);
    };

M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationRunningLoopWindow.cpp => products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationRunningLoopWindow.cpp +3 -1
@@ 4,6 4,7 @@
#include "RelaxationRunningLoopWindow.hpp"
#include <data/RelaxationStyle.hpp>
#include <data/RelaxationSwitchData.hpp>
#include <data/RelaxationErrorData.hpp>

#include <ApplicationBellRelaxation.hpp>
#include <apps-common/widgets/BellBaseLayout.hpp>


@@ 224,6 225,7 @@ namespace gui
    }
    void RelaxationRunningLoopWindow::handleError()
    {
        application->switchWindow(gui::window::name::relaxationError);
        auto switchData = std::make_unique<RelaxationErrorData>(RelaxationErrorType::UnsupportedMediaType);
        application->switchWindow(gui::window::name::relaxationError, std::move(switchData));
    }
} // namespace gui

M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationRunningProgressWindow.cpp => products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationRunningProgressWindow.cpp +3 -1
@@ 4,6 4,7 @@
#include "RelaxationRunningProgressWindow.hpp"
#include <data/RelaxationStyle.hpp>
#include <data/RelaxationSwitchData.hpp>
#include <data/RelaxationErrorData.hpp>

#include <ApplicationBellRelaxation.hpp>
#include <audio/AudioMessage.hpp>


@@ 174,7 175,8 @@ namespace gui

    void RelaxationRunningProgressWindow::handleError()
    {
        application->switchWindow(gui::window::name::relaxationError);
        auto switchData = std::make_unique<RelaxationErrorData>(RelaxationErrorType::UnsupportedMediaType);
        application->switchWindow(gui::window::name::relaxationError, std::move(switchData));
    }

} // namespace gui