~aleteoryx/muditaos

d6055890b6e8010a311a4c05efb597b50fcee0be — Lefucjusz 2 years ago 5b06da4
[MOS-1036] Fix song time not updated when returning from list

* Fix of the issue that switching from songs
list to main view after playing and stopping
some track resulted in elapsed time counter
not being updated until the song is unpaused;
* cleanups.
M module-apps/application-music-player/ApplicationMusicPlayer.cpp => module-apps/application-music-player/ApplicationMusicPlayer.cpp +7 -7
@@ 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 <application-music-player/ApplicationMusicPlayer.hpp>


@@ 75,22 75,22 @@ namespace app
        lockPolicyHandler.setPreventsAutoLockByStateCallback(std::move(stateLockCallback));

        connect(typeid(AudioStopNotification), [&](sys::Message *msg) -> sys::MessagePointer {
            auto notification = static_cast<AudioStopNotification *>(msg);
            const auto notification = static_cast<AudioStopNotification *>(msg);
            music_player::AudioNotificationsHandler audioNotificationHandler{priv->songsPresenter};
            return audioNotificationHandler.handleAudioStopNotification(notification);
        });
        connect(typeid(AudioEOFNotification), [&](sys::Message *msg) -> sys::MessagePointer {
            auto notification = static_cast<AudioStopNotification *>(msg);
            const auto notification = static_cast<AudioStopNotification *>(msg);
            music_player::AudioNotificationsHandler audioNotificationHandler{priv->songsPresenter};
            return audioNotificationHandler.handleAudioEofNotification(notification);
        });
        connect(typeid(AudioPausedNotification), [&](sys::Message *msg) -> sys::MessagePointer {
            auto notification = static_cast<AudioPausedNotification *>(msg);
            const auto notification = static_cast<AudioPausedNotification *>(msg);
            music_player::AudioNotificationsHandler audioNotificationHandler{priv->songsPresenter};
            return audioNotificationHandler.handleAudioPausedNotification(notification);
        });
        connect(typeid(AudioResumedNotification), [&](sys::Message *msg) -> sys::MessagePointer {
            auto notification = static_cast<AudioResumedNotification *>(msg);
            const auto notification = static_cast<AudioResumedNotification *>(msg);
            music_player::AudioNotificationsHandler audioNotificationHandler{priv->songsPresenter};
            return audioNotificationHandler.handleAudioResumedNotification(notification);
        });


@@ 101,7 101,7 @@ namespace app
    sys::MessagePointer ApplicationMusicPlayer::DataReceivedHandler(sys::DataMessage *msgl,
                                                                    [[maybe_unused]] sys::ResponseMessage *resp)
    {
        auto retMsg = Application::DataReceivedHandler(msgl);
        const auto retMsg = Application::DataReceivedHandler(msgl);
        // if message was handled by application's template there is no need to process further.
        if (static_cast<sys::ResponseMessage *>(retMsg.get())->retCode == sys::ReturnCodes::Success) {
            return retMsg;


@@ 113,7 113,7 @@ namespace app
    // Invoked during initialization
    sys::ReturnCodes ApplicationMusicPlayer::InitHandler()
    {
        auto ret = Application::InitHandler();
        const auto ret = Application::InitHandler();
        if (ret != sys::ReturnCodes::Success) {
            return ret;
        }

M module-apps/application-music-player/presenters/SongsPresenter.cpp => module-apps/application-music-player/presenters/SongsPresenter.cpp +42 -32
@@ 1,12 1,17 @@
// 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 "SongsPresenter.hpp"

#include <service-audio/AudioMessage.hpp>
#include <Timers/TimerFactory.hpp>
#include <algorithm>

namespace
{
    constexpr auto songProgressTimerInterval = std::chrono::seconds{1};
    constexpr auto songProgressTimerName     = "MusicPlayerSongProgress";
} // namespace

namespace app::music_player
{
    SongsPresenter::SongsPresenter(app::ApplicationCommon *app,


@@ 14,9 19,8 @@ namespace app::music_player
                                   std::unique_ptr<AbstractAudioOperations> &&audioOperations)
        : songsModelInterface{std::move(songsModelInterface)}, audioOperations{std::move(audioOperations)}
    {
        auto tick         = std::chrono::seconds{1};
        songProgressTimer = sys::TimerFactory::createPeriodicTimer(
            app, "MP Song Progres", tick, [this]([[maybe_unused]] sys::Timer &) { handleTrackProgressTick(); });
            app, songProgressTimerName, songProgressTimerInterval, [this](sys::Timer &) { handleTrackProgressTick(); });
    }

    std::shared_ptr<app::music::SongsModelInterface> SongsPresenter::getMusicPlayerModelInterface() const


@@ 43,7 47,7 @@ namespace app::music_player
                          str(retCode).c_str(),
                          token.IsValid());
                refreshView();
                auto nextSongToPlay = songsModelInterface->getNextFilePath(filePath);
                const auto &nextSongToPlay = songsModelInterface->getNextFilePath(filePath);
                if (nextSongToPlay.empty()) {
                    return;
                }


@@ 51,7 55,7 @@ namespace app::music_player
                return;
            }

            app::music::SongContext songContext{
            const app::music::SongContext songContext{
                app::music::SongState::Playing, token, filePath, app::music::SongContext::StartPos};
            songsModelInterface->setCurrentSongContext(songContext);



@@ 62,15 66,15 @@ namespace app::music_player
            songsModelInterface->updateRepository(filePath);
            resetTrackProgressRatio();
            songProgressTimer.start();
            updateViewProgresState();
            updateViewProgressState();
            refreshView();
        });
    }

    bool SongsPresenter::pause()
    {
        auto currentFileToken = songsModelInterface->getCurrentFileToken();
        if (currentFileToken) {
        const auto &currentFileToken = songsModelInterface->getCurrentFileToken();
        if (currentFileToken.has_value()) {
            return audioOperations->pause(currentFileToken.value(), [this](audio::RetCode retCode, audio::Token token) {
                if (retCode != audio::RetCode::Success || !token.IsValid()) {
                    LOG_ERROR("Pause audio operation failed, retcode = %s, token validity = %d",


@@ 89,7 93,7 @@ namespace app::music_player
                updateViewSongState();
                songProgressTimer.stop();
                updateTrackProgressRatio();
                updateViewProgresState();
                updateViewProgressState();
                refreshView();
            });
        }


@@ 98,8 102,8 @@ namespace app::music_player

    bool SongsPresenter::resume()
    {
        auto currentFileToken = songsModelInterface->getCurrentFileToken();
        if (currentFileToken) {
        const auto &currentFileToken = songsModelInterface->getCurrentFileToken();
        if (currentFileToken.has_value()) {
            return audioOperations->resume(
                currentFileToken.value(), [this](audio::RetCode retCode, audio::Token token) {
                    if (retCode != audio::RetCode::Success || !token.IsValid()) {


@@ 127,14 131,14 @@ namespace app::music_player

    bool SongsPresenter::stop()
    {
        auto currentFileToken = songsModelInterface->getCurrentFileToken();
        if (currentFileToken) {
        const auto &currentFileToken = songsModelInterface->getCurrentFileToken();
        if (currentFileToken.has_value()) {
            return audioOperations->stop(currentFileToken.value(), [this](audio::RetCode, audio::Token) {
                // The answer will come via multicast and will be handled in the application
                updateViewSongState();
                songProgressTimer.stop();
                resetTrackProgressRatio();
                updateViewProgresState();
                updateViewProgressState();
                refreshView();
            });
        }


@@ 143,8 147,8 @@ namespace app::music_player

    bool SongsPresenter::playNext()
    {
        auto currentSongContext = songsModelInterface->getCurrentSongContext();
        auto nextSongToPlay     = songsModelInterface->getNextFilePath(currentSongContext.filePath);
        const auto &currentSongContext = songsModelInterface->getCurrentSongContext();
        const auto &nextSongToPlay     = songsModelInterface->getNextFilePath(currentSongContext.filePath);
        if (nextSongToPlay.empty()) {
            return false;
        }


@@ 153,8 157,8 @@ namespace app::music_player

    bool SongsPresenter::playPrevious()
    {
        auto currentSongContext = songsModelInterface->getCurrentSongContext();
        auto prevSongToPlay     = songsModelInterface->getPreviousFilePath(currentSongContext.filePath);
        const auto &currentSongContext = songsModelInterface->getCurrentSongContext();
        const auto &prevSongToPlay     = songsModelInterface->getPreviousFilePath(currentSongContext.filePath);
        if (prevSongToPlay.empty()) {
            return false;
        }


@@ 167,6 171,11 @@ namespace app::music_player
        updateViewSongState();
    }

    void SongsPresenter::progressStateRequest()
    {
        updateViewProgressState();
    }

    void SongsPresenter::setPlayingStateCallback(std::function<void(app::music::SongState)> cb)
    {
        changePlayingStateCallback = std::move(cb);


@@ 190,7 199,7 @@ namespace app::music_player

    bool SongsPresenter::handleAudioEofNotification(audio::Token token)
    {
        auto currentSongContext = songsModelInterface->getCurrentSongContext();
        const auto &currentSongContext = songsModelInterface->getCurrentSongContext();

        if (token == currentSongContext.currentFileToken) {
            songsModelInterface->clearCurrentSongContext();


@@ 220,7 229,7 @@ namespace app::music_player
            updateViewSongState();
            songProgressTimer.stop();
            updateTrackProgressRatio();
            updateViewProgresState();
            updateViewProgressState();
            refreshView();
            return true;
        }


@@ 245,7 254,7 @@ namespace app::music_player

    bool SongsPresenter::handlePlayOrPauseRequest()
    {
        auto currentSongContext = songsModelInterface->getCurrentSongContext();
        const auto &currentSongContext = songsModelInterface->getCurrentSongContext();
        if (currentSongContext.isPaused()) {
            return resume();
        }


@@ 257,30 266,31 @@ namespace app::music_player

    void SongsPresenter::updateTrackProgressRatio()
    {
        auto secondsTotal = 0;
        if (songsModelInterface->getActivatedRecord()) {
        std::uint32_t secondsTotal = 0;
        if (songsModelInterface->getActivatedRecord().has_value()) {
            secondsTotal = songsModelInterface->getActivatedRecord()->audioProperties.songLength;
        }

        const std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
        auto millisecondsToAdd = std::chrono::duration_cast<std::chrono::milliseconds>(now - songProgressTimestamp);
        const auto now = std::chrono::system_clock::now();
        const auto millisecondsToAdd =
            std::chrono::duration_cast<std::chrono::milliseconds>(now - songProgressTimestamp);
        songMillisecondsElapsed += millisecondsToAdd;
        songProgressTimestamp = now;

        if (secondsTotal == 0) {
            currentProgressRatio = 0.f;
            currentProgressRatio = 0.0f;
        }
        else {
            currentProgressRatio =
                static_cast<float>(std::chrono::duration_cast<std::chrono::seconds>(songMillisecondsElapsed).count()) /
                secondsTotal;
                static_cast<float>(secondsTotal);
        }
        currentProgressRatio = std::clamp(currentProgressRatio, 0.f, 1.f);
        currentProgressRatio = std::clamp(currentProgressRatio, 0.0f, 1.0f);
    }

    void SongsPresenter::resetTrackProgressRatio()
    {
        currentProgressRatio    = 0.0;
        currentProgressRatio    = 0.0f;
        songMillisecondsElapsed = std::chrono::milliseconds::zero();
        songProgressTimestamp   = std::chrono::system_clock::now();
        updateTrackProgressRatio();


@@ 289,7 299,7 @@ namespace app::music_player
    void SongsPresenter::handleTrackProgressTick()
    {
        updateTrackProgressRatio();
        updateViewProgresState();
        updateViewProgressState();
        refreshView();
    }



@@ 329,7 339,7 @@ namespace app::music_player
        }
    }

    void SongsPresenter::updateViewProgresState()
    void SongsPresenter::updateViewProgressState()
    {
        if (auto view = getView(); view != nullptr) {
            view->updateSongProgress(currentProgressRatio);

M module-apps/application-music-player/presenters/SongsPresenter.hpp => module-apps/application-music-player/presenters/SongsPresenter.hpp +4 -3
@@ 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,6 50,7 @@ namespace app::music_player
            virtual bool playPrevious()                    = 0;

            virtual void songsStateRequest()                                      = 0;
            virtual void progressStateRequest()                                   = 0;
            virtual void setPlayingStateCallback(OnPlayingStateChangeCallback cb) = 0;
            virtual bool handleAudioStopNotifiaction(audio::Token token)          = 0;
            virtual bool handleAudioEofNotification(audio::Token token)           = 0;


@@ 78,6 79,7 @@ namespace app::music_player
        bool playPrevious() override;

        void songsStateRequest() override;
        void progressStateRequest() override;
        void setPlayingStateCallback(std::function<void(app::music::SongState)> cb) override;
        bool handleAudioStopNotifiaction(audio::Token token) override;
        bool handleAudioEofNotification(audio::Token token) override;


@@ 90,8 92,7 @@ namespace app::music_player

      private:
        void updateViewSongState();
        void updateViewProgresState();
        void updateViewSongTitleAndArtist(const std::string &title, const std::string &artist);
        void updateViewProgressState();
        void refreshView();
        void updateTrackProgressRatio();
        void resetTrackProgressRatio();

M module-apps/application-music-player/windows/MusicPlayerAllSongsWindow.cpp => module-apps/application-music-player/windows/MusicPlayerAllSongsWindow.cpp +9 -12
@@ 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 "MusicPlayerAllSongsWindow.hpp"


@@ 6,18 6,15 @@
#include <data/MusicPlayerStyle.hpp>

#include <Style.hpp>
#include <cassert>
#include <i18n/i18n.hpp>
#include <service-audio/AudioServiceAPI.hpp>
#include <gui/widgets/ListView.hpp>
#include <gui/widgets/Icon.hpp>

namespace gui
{

    MusicPlayerAllSongsWindow::MusicPlayerAllSongsWindow(
        app::ApplicationCommon *app, std::shared_ptr<app::music_player::SongsContract::Presenter> windowPresenter)
        : AppWindow(app, gui::name::window::all_songs_window), presenter{windowPresenter}
        : AppWindow(app, gui::name::window::all_songs_window), presenter{std::move(windowPresenter)}
    {
        buildInterface();
    }


@@ 64,12 61,16 @@ namespace gui
        presenter->createData();
    }

    void MusicPlayerAllSongsWindow::updateSongsState(std::optional<db::multimedia_files::MultimediaFilesRecord> record,
                                                     RecordState state)
    void MusicPlayerAllSongsWindow::updateSongsState(
        [[maybe_unused]] std::optional<db::multimedia_files::MultimediaFilesRecord> record,
        [[maybe_unused]] RecordState state)
    {
        songsList->rebuildList(gui::listview::RebuildType::InPlace);
    }

    void MusicPlayerAllSongsWindow::updateSongProgress([[maybe_unused]] float progress)
    {}

    void MusicPlayerAllSongsWindow::refreshWindow()
    {
        application->refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);


@@ 87,10 88,6 @@ namespace gui

    bool MusicPlayerAllSongsWindow::onInput(const InputEvent &inputEvent)
    {
        if (AppWindow::onInput(inputEvent)) {
            return true;
        }

        return false;
        return AppWindow::onInput(inputEvent);
    }
} /* namespace gui */

M module-apps/application-music-player/windows/MusicPlayerAllSongsWindow.hpp => module-apps/application-music-player/windows/MusicPlayerAllSongsWindow.hpp +3 -5
@@ 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


@@ 17,15 17,14 @@ namespace gui
        explicit MusicPlayerAllSongsWindow(app::ApplicationCommon *app,
                                           std::shared_ptr<app::music_player::SongsContract::Presenter> presenter);

        void onBeforeShow([[maybe_unused]] ShowMode mode, [[maybe_unused]] SwitchData *data) override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;

        void buildInterface() override;
        bool onInput(const InputEvent &inputEvent) override;

        void updateSongsState(std::optional<db::multimedia_files::MultimediaFilesRecord> record,
                              RecordState state) override;
        void updateSongProgress(float progres) override
        {}
        void updateSongProgress(float progress) override;
        void refreshWindow() override;
        void setNavBarTemporaryMode(const std::string &text) override;
        void restoreFromNavBarTemporaryMode() override;


@@ 35,5 34,4 @@ namespace gui
        ListView *songsList = nullptr;
        Icon *emptyListIcon = nullptr;
    };

} /* namespace gui */

M module-apps/application-music-player/windows/MusicPlayerMainWindow.cpp => module-apps/application-music-player/windows/MusicPlayerMainWindow.cpp +95 -80
@@ 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 "MusicPlayerMainWindow.hpp"


@@ 6,7 6,6 @@
#include <application-music-player/ApplicationMusicPlayer.hpp>
#include <apps-common/options/type/OptionSetting.hpp>

#include <service-audio/AudioServiceAPI.hpp>
#include <gui/widgets/ThreeBox.hpp>
#include <gui/widgets/ImageBox.hpp>
#include <gui/widgets/ListView.hpp>


@@ 16,13 15,39 @@
#include <Style.hpp>
#include <time/time_constants.hpp>

#include <cassert>
namespace
{
    std::string secondsToTimeString(std::uint32_t seconds)
    {
        constexpr auto maxTimeStringLength = 10;
        char timeStringBuffer[maxTimeStringLength];

        if (seconds < utils::time::secondsInHour) {
            const auto minutes          = seconds / utils::time::secondsInMinute;
            const auto secondsRemainder = seconds % utils::time::secondsInMinute;
            snprintf(timeStringBuffer, sizeof(timeStringBuffer), "%" PRIu32 ":%02" PRIu32, minutes, secondsRemainder);
        }
        else {
            const auto hours            = seconds / utils::time::secondsInHour;
            const auto minutes          = (seconds % utils::time::secondsInHour) / utils::time::secondsInMinute;
            const auto secondsRemainder = seconds % utils::time::secondsInMinute;
            snprintf(timeStringBuffer,
                     sizeof(timeStringBuffer),
                     "%" PRIu32 ":%02" PRIu32 ":%02" PRIu32,
                     hours,
                     minutes,
                     secondsRemainder);
        }

        return std::string(timeStringBuffer);
    };
} // namespace

namespace gui
{
    MusicPlayerMainWindow::MusicPlayerMainWindow(
        app::ApplicationCommon *app, std::shared_ptr<app::music_player::SongsContract::Presenter> windowPresenter)
        : OptionWindow(app, name::window::track_info_window), presenter{windowPresenter}
        : OptionWindow(app, name::window::track_info_window), presenter{std::move(windowPresenter)}
    {
        presenter->attach(this);
        buildInterface();


@@ 37,7 62,7 @@ namespace gui
    void MusicPlayerMainWindow::buildInterface()
    {
        AppWindow::buildInterface();
        updateSongProgress(currentProgress);
        presenter->progressStateRequest();

        if (myViewMode == ViewMode::START) {
            buildInterfaceStartMode();


@@ 58,12 83,12 @@ namespace gui
        auto mainBox = new VBox(this, 0, 0, style::window_width, style::window_height);
        mainBox->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Top));

        ImageBox *note = new ImageBox(mainBox, 0, 0, 0, 0, new Image("mp_note", musicPlayerStyle::common::imageType));
        auto note = new ImageBox(mainBox, 0, 0, 0, 0, new Image("mp_note", musicPlayerStyle::common::imageType));
        note->setMinimumSize(startScreen::noteSize, startScreen::noteSize);
        note->setMargins(Margins(0, startScreen::noteUpMargin, 0, startScreen::noteDownMargin));
        note->setEdges(RectangleEdge::None);

        Text *desc = new Text(mainBox, 0, 0, 0, 0);
        auto desc = new Text(mainBox, 0, 0, 0, 0);
        desc->setMinimumSize(startScreen::descriptionWidth, startScreen::descriptionHeight);
        desc->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        desc->setTextType(TextType::MultiLine);


@@ 137,40 162,41 @@ namespace gui

        buildTrackInfoInterface(mainBox);

        ImageBox *swipe =
        auto swipe =
            new ImageBox(mainBox, 0, 0, 0, 0, new Image("mp_line_arrow_up", musicPlayerStyle::common::imageType));
        swipe->setMinimumSizeToFitImage();
        swipe->setMargins(Margins(0, musicLibraryScreen::topArrowMargin, 0, musicLibraryScreen::bottomArrowMargin));
        swipe->setEdges(RectangleEdge::None);

        options.clear();
        auto addOption = [this](UTF8 name, const std::string &window = "", bool permissionToChangeViewMode = false) {
            options.emplace_back(std::make_unique<option::OptionSettings>(
                name,
                [=](Item &item) {
                    if (window.empty()) {
        auto addOption =
            [this](const UTF8 &name, const std::string &window = "", bool permissionToChangeViewMode = false) {
                options.emplace_back(std::make_unique<option::OptionSettings>(
                    name,
                    [=]([[maybe_unused]] Item &item) {
                        if (window.empty()) {
                            return true;
                        }
                        LOG_INFO("switching to window %s", window.c_str());
                        application->switchWindow(window, nullptr);
                        return true;
                    }
                    LOG_INFO("switching to window %s", window.c_str());
                    application->switchWindow(window, nullptr);
                    return true;
                },
                [=](Item &item) {
                    if (!item.focus) {
                    },
                    [=](Item &item) {
                        if (!item.focus) {
                            return true;
                        }
                        isPermissionToChangeViewMode = permissionToChangeViewMode;
                        if (window.empty()) {
                            clearNavBarText(nav_bar::Side::Center);
                        }
                        else {
                            navBar->setText(nav_bar::Side::Center, utils::translate("common_select"));
                        }
                        return true;
                    }
                    isPermissionToChangeViewMode = permissionToChangeViewMode;
                    if (window.empty()) {
                        clearNavBarText(nav_bar::Side::Center);
                    }
                    else {
                        navBar->setText(nav_bar::Side::Center, utils::translate("common_select"));
                    }
                    return true;
                },
                this,
                window.empty() ? option::SettingRightItem::Disabled : option::SettingRightItem::ArrowWhite));
        };
                    },
                    this,
                    window.empty() ? option::SettingRightItem::Disabled : option::SettingRightItem::ArrowWhite));
            };

        addOption(utils::translate("app_music_player_all_songs"), name::window::all_songs_window, true);
        addOption(utils::translate("app_music_player_artists"));


@@ 203,7 229,7 @@ namespace gui
        using namespace musicPlayerStyle::mainWindow;

        using Box3    = HThreeBox<HBox, HBox, HBox>;
        Box3 *buttons = new Box3(parent);
        auto buttons  = new Box3(parent);
        buttons->setMinimumSize(playButtons::width, playButtons::height);
        buttons->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        buttons->setEdges(RectangleEdge::None);


@@ 241,7 267,7 @@ namespace gui
    {
        using namespace musicPlayerStyle::mainWindow;

        Text *musicLib = new Text(parent, 0, 0, 0, 0);
        auto musicLib = new Text(parent, 0, 0, 0, 0);
        musicLib->setMinimumSize(lineArrow::textWidth, lineArrow::textHeight);
        musicLib->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        musicLib->setTextType(TextType::SingleLine);


@@ 250,7 276,7 @@ namespace gui
        musicLib->setRichText(utils::translate("app_music_player_music_library"));
        musicLib->setMargins(Margins(0, 0, 0, lineArrow::internalMargin));

        ImageBox *swipe =
        auto swipe =
            new ImageBox(parent, 0, 0, 0, 0, new Image("mp_line_arrow_down", musicPlayerStyle::common::imageType));
        swipe->setMinimumSizeToFitImage();
        swipe->setEdges(RectangleEdge::None);


@@ 271,7 297,7 @@ namespace gui
        topBox->setMinimumSize(trackProgress::barWidth, trackProgress::barHeight);
        topBox->setEdges(RectangleEdge::None);

        unsigned spacerWidth =
        const auto spacerWidth =
            (trackProgress::barWidth - progressBarSize * trackProgress::barThickness) / (progressBarSize - 1);
        for (auto i = 0; i < progressBarSize; ++i) {
            if (i < currentProgressBarsBlack) {


@@ 319,7 345,7 @@ namespace gui
    {
        using namespace musicPlayerStyle::mainWindow;

        HBox *songData = new HBox(parent);
        auto songData = new HBox(parent);
        songData->setMinimumSize(trackInfo::width, trackInfo::height);
        songData->setAlignment(Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Center));
        songData->setMargins(Margins(0, trackInfo::topMargin, 0, 0));


@@ 331,7 357,7 @@ namespace gui
        stateImageBox->setEdges(RectangleEdge::None);
        stateImageBox->setMargins(Margins(0, 0, trackInfo::internalMargin, 0));

        HBox *textBox = new HBox(songData);
        auto textBox = new HBox(songData);
        textBox->setMinimumSize(trackInfo::width - trackInfo::internalMargin - trackInfo::height, trackInfo::height);
        textBox->setEdges(RectangleEdge::None);
        textBox->setAlignment(Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Center));


@@ 360,7 386,7 @@ namespace gui

        stateImageBox   = nullptr;
        descriptionText = nullptr;
        memset(progressBarItems, 0, progressBarSize * sizeof(Image *));
        std::memset(progressBarItems, 0, progressBarSize * sizeof(Image *));

        optionsList = nullptr;
    }


@@ 376,12 402,14 @@ namespace gui
    {
        if (record) {
            currentTitle = record->tags.title;
            if (currentTitle.empty())
            if (currentTitle.empty()) {
                currentTitle = utils::translate("app_music_player_uknown_title");
            }

            currentArtist = record->tags.album.artist;
            if (currentArtist.empty())
            if (currentArtist.empty()) {
                currentArtist = utils::translate("app_music_player_uknown_artist");
            }

            currentTotalTime = record->audioProperties.songLength;
            if (myViewMode == ViewMode::START) {


@@ 403,7 431,7 @@ namespace gui

    void MusicPlayerMainWindow::updateSongProgress(float progress)
    {
        progress        = std::clamp(progress, 0.f, 1.f);
        progress        = std::clamp(progress, 0.0f, 1.0f);
        currentProgress = progress;

        updateVisibleProgressData();


@@ 444,25 472,31 @@ namespace gui

    void MusicPlayerMainWindow::updateVisibleTrackData(RecordState state) noexcept
    {
        auto isPlaying = state == RecordState::Playing;
        auto isPaused  = state == RecordState::Paused;
        const auto isPlaying = (state == RecordState::Playing);
        const auto isPaused  = (state == RecordState::Paused);

        if (titleText != nullptr)
        if (titleText != nullptr) {
            titleText->setText(currentTitle);
        if (artistText != nullptr)
        }
        if (artistText != nullptr) {
            artistText->setText(currentArtist);
        }

        if (totalTimeText != nullptr)
        if (totalTimeText != nullptr) {
            totalTimeText->setRichText(currentTotalTimeString);
        if (rewImageBox != nullptr)
        }
        if (rewImageBox != nullptr) {
            rewImageBox->setImage((isPlaying || isPaused) ? "mp_prev" : "mp_prev_gray",
                                  musicPlayerStyle::common::imageType);
        if (pauseImageBox != nullptr)
        }
        if (pauseImageBox != nullptr) {
            pauseImageBox->setImage(isPlaying ? "mp_pause" : ((isPaused) ? "mp_play" : "mp_pause_gray"),
                                    musicPlayerStyle::common::imageType);
        if (ffImageBox != nullptr)
        }
        if (ffImageBox != nullptr) {
            ffImageBox->setImage((isPlaying || isPaused) ? "mp_next" : "mp_next_gray",
                                 musicPlayerStyle::common::imageType);
        }

        if (stateImageBox != nullptr) {
            stateImageBox->setImage(isPlaying


@@ 489,24 523,22 @@ namespace gui
        }
    }

    void MusicPlayerMainWindow::updateVisibleProgressData(void) noexcept
    void MusicPlayerMainWindow::updateVisibleProgressData() noexcept
    {
        if (myViewMode != ViewMode::TRACK) {
            return;
        }
        constexpr auto maxTimeToDisplaySize{10};
        char timeToDisplay[maxTimeToDisplaySize];

        uint8_t passedBarsNumber = std::round(currentProgress * progressBarSize);
        const auto passedBarsNumber = static_cast<std::uint8_t>(std::round(currentProgress * progressBarSize));
        if (passedBarsNumber != currentProgressBarsBlack) {

            if (passedBarsNumber < currentProgressBarsBlack) {
                needToDeepRedrawScreen = true;
            }
            currentProgressBarsBlack = passedBarsNumber;
            for (auto i = 0; i < progressBarSize; ++i) {
                if (progressBarItems[i] == nullptr)
                if (progressBarItems[i] == nullptr) {
                    continue;
                }

                if (i < currentProgressBarsBlack) {
                    progressBarItems[i]->set("mp_bar", musicPlayerStyle::common::imageType);


@@ 517,32 549,15 @@ namespace gui
            }
        }

        auto secsToStr = [&](int secs) {
            if (secs < utils::time::secondsInHour) {
                snprintf(timeToDisplay,
                         maxTimeToDisplaySize,
                         "%d:%02d",
                         static_cast<int>(secs / utils::time::secondsInMinute),
                         static_cast<int>(secs) % utils::time::secondsInMinute);
            }
            else {
                snprintf(timeToDisplay,
                         maxTimeToDisplaySize,
                         "%d:%02d:%02d",
                         static_cast<int>(secs) / utils::time::secondsInHour,
                         static_cast<int>((secs) % utils::time::secondsInHour) / utils::time::secondsInMinute,
                         static_cast<int>(secs) % utils::time::secondsInMinute);
            }
            return timeToDisplay;
        };

        currentTotalTimeString = secsToStr(currentTotalTime);
        currentTimeString      = secsToStr(static_cast<uint32_t>(currentTotalTime * currentProgress));
        currentTotalTimeString = secondsToTimeString(currentTotalTime);
        currentTimeString      = secondsToTimeString(static_cast<std::uint32_t>(currentTotalTime * currentProgress));

        if (totalTimeText != nullptr)
        if (totalTimeText != nullptr) {
            totalTimeText->setRichText(currentTotalTimeString);
        if (currentTimeText != nullptr)
        }
        if (currentTimeText != nullptr) {
            currentTimeText->setRichText(currentTimeString);
        }
    }

    bool MusicPlayerMainWindow::onInput(const InputEvent &inputEvent)

M module-apps/application-music-player/windows/MusicPlayerMainWindow.hpp => module-apps/application-music-player/windows/MusicPlayerMainWindow.hpp +6 -7
@@ 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


@@ 23,7 23,7 @@ namespace gui
    {
      public:
        /// number of vertical items in track's progress bar
        static constexpr uint8_t progressBarSize = 27;
        static constexpr std::uint8_t progressBarSize = 27;

        /// current view mode (switching with up / down arrow)
        enum class ViewMode


@@ 36,7 36,7 @@ namespace gui
        explicit MusicPlayerMainWindow(app::ApplicationCommon *app,
                                       std::shared_ptr<app::music_player::SongsContract::Presenter> presenter);

        void onBeforeShow([[maybe_unused]] ShowMode mode, [[maybe_unused]] SwitchData *data) override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;

        void rebuild() override;
        void buildInterface() override;


@@ 81,9 81,9 @@ namespace gui
        Label *descriptionText                   = nullptr;
        Image *progressBarItems[progressBarSize] = {nullptr};

        float currentProgress            = 0.f;
        uint32_t currentTotalTime        = 0;
        uint8_t currentProgressBarsBlack = 0;
        float currentProgress                 = 0.0f;
        std::uint32_t currentTotalTime        = 0;
        std::uint8_t currentProgressBarsBlack = 0;
        std::string currentTitle;
        std::string currentArtist;
        std::string currentTimeString;


@@ 91,5 91,4 @@ namespace gui
        bool isPermissionToChangeViewMode = false;
        bool needToDeepRedrawScreen       = false;
    };

} /* namespace gui */

M pure_changelog.md => pure_changelog.md +1 -0
@@ 45,6 45,7 @@
* Fixed losing drafted message and recipient number in new message windows
* Fixed misleading "Save" button behavior in "Date and time" window
* Fixed losing unsaved user data when tethering is switching on
* Fixed invalid elapsed time in music player after playing and pausing track from songs list view

## [1.7.2 2023-07-28]