~aleteoryx/muditaos

49f432cc622ba6b24d369384a3f700269477cf99 — Maciej Gibowicz 1 year, 6 months ago 395849f
[BH-1945] Add update instructions at the end of onboarding

User will see instructions on updating Harmony's OS at the end of
onboarding.
23 files changed, 589 insertions(+), 14 deletions(-)

M harmony_changelog.md
M image/system_a/data/lang/Deutsch.json
M image/system_a/data/lang/English.json
M image/system_a/data/lang/Espanol.json
M image/system_a/data/lang/Francais.json
M image/system_a/data/lang/Polski.json
M module-gui/gui/widgets/ThreeBox.cpp
M products/BellHybrid/apps/application-bell-onboarding/ApplicationBellOnBoarding.cpp
M products/BellHybrid/apps/application-bell-onboarding/CMakeLists.txt
M products/BellHybrid/apps/application-bell-onboarding/include/application-bell-onboarding/BellOnBoardingNames.hpp
A products/BellHybrid/apps/application-bell-onboarding/presenter/OnBoardingUpdateInstructionWindowPresenter.cpp
A products/BellHybrid/apps/application-bell-onboarding/presenter/OnBoardingUpdateInstructionWindowPresenter.hpp
M products/BellHybrid/apps/application-bell-onboarding/windows/OnBoardingSettingsWindow.cpp
M products/BellHybrid/apps/common/CMakeLists.txt
M products/BellHybrid/apps/common/include/common/BellCommonNames.hpp
A products/BellHybrid/apps/common/include/common/layouts/UpdateInstructionLayoutClassic.hpp
A products/BellHybrid/apps/common/include/common/layouts/UpdateInstructionLayoutProvider.hpp
A products/BellHybrid/apps/common/include/common/layouts/UpdateInstructionLayouts.hpp
A products/BellHybrid/apps/common/include/common/windows/UpdateInstructionWindow.hpp
A products/BellHybrid/apps/common/include/common/windows/UpdateInstructionWindowContract.hpp
A products/BellHybrid/apps/common/src/layouts/UpdateInstructionLayoutClassic.cpp
A products/BellHybrid/apps/common/src/layouts/UpdateInstructionLayouts.cpp
A products/BellHybrid/apps/common/src/windows/UpdateInstructionWindow.cpp
M harmony_changelog.md => harmony_changelog.md +1 -0
@@ 7,6 7,7 @@

### Added
* Added custom alarms functionality
* Added update instruction screen at end of onboarding

### Changed / Improved
* Changed the refresh rate of the progress bar screen

M image/system_a/data/lang/Deutsch.json => image/system_a/data/lang/Deutsch.json +5 -0
@@ 130,6 130,11 @@
    "app_bell_settings_time_units_time_message": "Zeit",
    "app_bell_settings_turn_off": "Ausschalten",
    "app_bell_turn_off_question": "Schalten Sie das Ger\u00e4t aus?",
    "app_bell_update_instruction_title": "Update-Leitfaden",
    "app_bell_update_instruction_1st_point": "<text>Lade Mudita Center über<br/><text weight='regular'>mudita.com/center</text></text>",
    "app_bell_update_instruction_2nd_point": "Verbinde Harmony per USB-C mit dem PC",
    "app_bell_update_instruction_3rd_point": "Öffne Mudita Center",
    "app_bell_update_instruction_4th_point": "<text>Klicke in Overview auf <text weight='regular'>Update</text></text>",
    "app_bell_welcome_charge_message": "<text>Charge Harmony<br/>und leicht dr\u00fccken</text>",
    "app_bell_welcome_message": "<text>Mudita Harmony<br/>ist ausgeschaltet</text>",
    "app_bellmain_alarm": "Alarm",

M image/system_a/data/lang/English.json => image/system_a/data/lang/English.json +5 -0
@@ 164,6 164,11 @@
    "app_bell_settings_time_units_time_message": "Time",
    "app_bell_settings_turn_off": "Turn off",
    "app_bell_turn_off_question": "Turn off Mudita Harmony?",
    "app_bell_update_instruction_title": "Update Guide",
    "app_bell_update_instruction_1st_point": "<text>Download Mudita Center from<br/><text weight='regular'>mudita.com/center</text></text>",
    "app_bell_update_instruction_2nd_point": "Connect Harmony to computer via USB-C",
    "app_bell_update_instruction_3rd_point": "Open Mudita Center",
    "app_bell_update_instruction_4th_point": "<text>On Overview click<br/><text weight='regular'>Update</text></text>",
    "app_bell_welcome_charge_message": "<text>Charge Harmony<br/>and press light click</text>",
    "app_bell_welcome_message": "<text>Mudita Harmony<br/>is switched OFF</text>",
    "app_bellmain_alarm": "Alarm",

M image/system_a/data/lang/Espanol.json => image/system_a/data/lang/Espanol.json +5 -0
@@ 129,6 129,11 @@
    "app_bell_settings_time_units_time_message": "Hora",
    "app_bell_settings_turn_off": "Apagar",
    "app_bell_turn_off_question": "\u00bfApagar Mudita Harmony?",
    "app_bell_update_instruction_title": "Cómo actualizar",
    "app_bell_update_instruction_1st_point": "<text>Descarga Mudita Center en<br/><text weight='regular'>mudita.com/center</text></text>",
    "app_bell_update_instruction_2nd_point": "Conecta Harmony a un PC con un USB-C",
    "app_bell_update_instruction_3rd_point": "Abre Mudita Center",
    "app_bell_update_instruction_4th_point": "<text>Haz clic en <text weight='regular'>Update</text> en<br/>Overview</text>",
    "app_bell_welcome_charge_message": "<text>Carga tu Harmony<br/>y pulsa levemente</text>",
    "app_bell_welcome_message": "<text>Mudita Harmony<br/>est\u00e1 apagado</text>",
    "app_bellmain_alarm": "Alarma",

M image/system_a/data/lang/Francais.json => image/system_a/data/lang/Francais.json +5 -0
@@ 131,6 131,11 @@
    "app_bell_settings_time_units_time_message": "Heure",
    "app_bell_settings_turn_off": "\u00c9teindre",
    "app_bell_turn_off_question": "\u00c9teindre l'appareil ?",
    "app_bell_update_instruction_title": "Guide des MaJ",
    "app_bell_update_instruction_1st_point": "<text>Téléchargez Mudita Center sur<br/><text weight='regular'>mudita.com/center</text></text>",
    "app_bell_update_instruction_2nd_point": "Branchez Harmony à votre PC via USB-C",
    "app_bell_update_instruction_3rd_point": "Ouvrez Mudita Center",
    "app_bell_update_instruction_4th_point": "<text>Dans Overview ><br/><text weight='regular'>Update</text></text>",
    "app_bell_welcome_charge_message": "<text>Rechargez Harmony<br/>et appuyez l\u00e9g\u00e8rement</text>",
    "app_bell_welcome_message": "<text>Mudita Harmony<br/>est d\u00e9sactiv\u00e9</text>",
    "app_bellmain_alarm": "Alarme",

M image/system_a/data/lang/Polski.json => image/system_a/data/lang/Polski.json +5 -0
@@ 130,6 130,11 @@
    "app_bell_settings_time_units_time_message": "Czas",
    "app_bell_settings_turn_off": "Wy\u0142\u0105cz",
    "app_bell_turn_off_question": "Wy\u0142\u0105czy\u0107 Mudita Harmony?",
    "app_bell_update_instruction_title": "Jak aktualizować",
    "app_bell_update_instruction_1st_point": "<text>Pobierz Mudita Center<br/>z <text weight='regular'>mudita.com/center</text></text>",
    "app_bell_update_instruction_2nd_point": "Podłącz Harmony do PC za pomocą USB-C",
    "app_bell_update_instruction_3rd_point": "Otwórz Mudita Center",
    "app_bell_update_instruction_4th_point": "<text>Kliknij <text weight='regular'>Update</text><br/>w Overview</text>",
    "app_bell_welcome_charge_message": "<text>Na\u0142aduj Harmony<br/>i kliknij lekko</text>",
    "app_bell_welcome_message": "<text>Mudita Harmony<br/>jest wy\u0142\u0105czony</text>",
    "app_bellmain_alarm": "Alarm",

M module-gui/gui/widgets/ThreeBox.cpp => module-gui/gui/widgets/ThreeBox.cpp +2 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ThreeBox.hpp"


@@ 21,6 21,7 @@ namespace gui
    {}

    template class HThreeBox<HBox, HBox, HBox>;
    template class HThreeBox<HBox, VBox, HBox>;
    template class VThreeBox<VBox, VBox, VBox>;

} // namespace gui

M products/BellHybrid/apps/application-bell-onboarding/ApplicationBellOnBoarding.cpp => products/BellHybrid/apps/application-bell-onboarding/ApplicationBellOnBoarding.cpp +20 -1
@@ 5,6 5,7 @@

#include <presenter/OnBoardingLanguageWindowPresenter.hpp>
#include <presenter/OnBoardingShortcutsWindowPresenter.hpp>
#include <presenter/OnBoardingUpdateInstructionWindowPresenter.hpp>
#include <windows/OnBoardingLanguageWindow.hpp>
#include <windows/OnBoardingFinalizeWindow.hpp>
#include <windows/OnBoardingOnOffWindow.hpp>


@@ 21,6 22,7 @@
#include <common/windows/BellFinishedWindow.hpp>
#include <common/windows/BellWelcomeWindow.hpp>
#include <common/windows/ShortcutsWindow.hpp>
#include <common/windows/UpdateInstructionWindow.hpp>
#include <common/models/LayoutModel.hpp>

#include <application-bell-settings/models/TemperatureUnitModel.hpp>


@@ 119,6 121,13 @@ namespace app
            });

        windowsFactory.attach(
            gui::window::name::onBoardingUpdateInstructionWindow,
            [this](ApplicationCommon *app, const std::string &name) {
                auto presenter = std::make_unique<OnBoarding::OnBoardingUpdateInstructionWindowPresenter>(this);
                return std::make_unique<gui::UpdateInstructionWindow>(app, std::move(presenter), name);
            });

        windowsFactory.attach(
            gui::window::name::onBoardingSettingsWindow, [this](ApplicationCommon *app, const std::string &name) {
                auto layoutModel          = std::make_unique<bell_settings::LayoutModel>(this);
                auto temperatureUnitModel = std::make_unique<bell_settings::TemperatureUnitModel>(app);


@@ 290,11 299,21 @@ namespace app
                    else
                        informationState = OnBoarding::InformationStates::RotateInfo;
                }
                else if (window->getName() == gui::window::name::onBoardingUpdateInstructionWindow) {
                    const auto updateInstructionWindow = dynamic_cast<gui::UpdateInstructionWindow *>(window);
                    if (updateInstructionWindow != nullptr && updateInstructionWindow->isLastLayout()) {
                        informationState = OnBoarding::InformationStates::RotateInfo;
                    }
                    else {
                        informationState = OnBoarding::InformationStates::LightClickInfo;
                    }
                }
                else {
                    informationState = OnBoarding::InformationStates::LightClickInfo;
                }
            }
            else if (getCurrentWindow()->getName() != gui::window::name::onBoardingShortcutsWindow) {
            else if (getCurrentWindow()->getName() != gui::window::name::onBoardingShortcutsWindow &&
                     getCurrentWindow()->getName() != gui::window::name::onBoardingUpdateInstructionWindow) {
                if (inputEvent.isKeyRelease(gui::KeyCode::KEY_RIGHT) ||
                    inputEvent.isKeyRelease(gui::KeyCode::KEY_LEFT)) {
                    informationState = OnBoarding::InformationStates::DeepClickWarningInfo;

M products/BellHybrid/apps/application-bell-onboarding/CMakeLists.txt => products/BellHybrid/apps/application-bell-onboarding/CMakeLists.txt +2 -0
@@ 23,10 23,12 @@ target_sources(application-bell-onboarding
        presenter/OnBoardingLanguageWindowPresenter.cpp
        presenter/OnBoardingFinalizeWindowPresenter.cpp
        presenter/OnBoardingShortcutsWindowPresenter.cpp
        presenter/OnBoardingUpdateInstructionWindowPresenter.cpp

        presenter/OnBoardingLanguageWindowPresenter.hpp
        presenter/OnBoardingFinalizeWindowPresenter.hpp
        presenter/OnBoardingShortcutsWindowPresenter.hpp
        presenter/OnBoardingUpdateInstructionWindowPresenter.hpp
        windows/OnBoardingLanguageWindow.hpp
        windows/OnBoardingOnOffWindow.hpp
        windows/OnBoardingSettingsWindow.hpp

M products/BellHybrid/apps/application-bell-onboarding/include/application-bell-onboarding/BellOnBoardingNames.hpp => products/BellHybrid/apps/application-bell-onboarding/include/application-bell-onboarding/BellOnBoardingNames.hpp +9 -8
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 12,12 12,13 @@ namespace app

namespace gui::window::name
{
    inline constexpr auto onBoardingOnOffWindow           = "BellOnBoardingOnOff";
    inline constexpr auto onBoardingLanguageWindow        = "BellOnBoardingLanguage";
    inline constexpr auto onBoardingShortcutsOptionWindow = "BellOnBoardingShortcutsOption";
    inline constexpr auto onBoardingShortcutsWindow       = gui::window::name::shortcutsWindow;
    inline constexpr auto onBoardingSettingsWindow        = "BellOnBoardingSettings";
    inline constexpr auto finalizeOnBoardingWindow        = "BellOnBoardingFinalize";
    inline constexpr auto informationOnBoardingWindow     = "BellOnBoardingInformation";
    inline constexpr auto onBoardingOnOffWindow             = "BellOnBoardingOnOff";
    inline constexpr auto onBoardingLanguageWindow          = "BellOnBoardingLanguage";
    inline constexpr auto onBoardingShortcutsOptionWindow   = "BellOnBoardingShortcutsOption";
    inline constexpr auto onBoardingShortcutsWindow         = gui::window::name::shortcutsWindow;
    inline constexpr auto onBoardingUpdateInstructionWindow = gui::window::name::updateInstructionWindow;
    inline constexpr auto onBoardingSettingsWindow          = "BellOnBoardingSettings";
    inline constexpr auto finalizeOnBoardingWindow          = "BellOnBoardingFinalize";
    inline constexpr auto informationOnBoardingWindow       = "BellOnBoardingInformation";

} // namespace gui::window::name

A products/BellHybrid/apps/application-bell-onboarding/presenter/OnBoardingUpdateInstructionWindowPresenter.cpp => products/BellHybrid/apps/application-bell-onboarding/presenter/OnBoardingUpdateInstructionWindowPresenter.cpp +57 -0
@@ 0,0 1,57 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "OnBoardingUpdateInstructionWindowPresenter.hpp"
#include <BellOnBoardingNames.hpp>
#include <common/layouts/UpdateInstructionLayoutProvider.hpp>
#include <common/layouts/UpdateInstructionLayouts.hpp>
#include <InputEvent.hpp>
#include <keymap/KeyMap.hpp>

namespace app::OnBoarding
{
    OnBoardingUpdateInstructionWindowPresenter::OnBoardingUpdateInstructionWindowPresenter(app::ApplicationCommon *app)
        : app(app)
    {
        initLayoutOptions();
    }

    std::vector<gui::Item *> OnBoardingUpdateInstructionWindowPresenter::getLayouts() const
    {
        return layoutOptions;
    }

    gui::Item *OnBoardingUpdateInstructionWindowPresenter::getFirstLayout() const
    {
        return layoutOptions.empty() ? nullptr : layoutOptions.front();
    }

    bool OnBoardingUpdateInstructionWindowPresenter::isLastLayout(const gui::Item *layout) const
    {
        return !layoutOptions.empty() && layoutOptions.back() == layout;
    }

    void OnBoardingUpdateInstructionWindowPresenter::initLayoutOptions()
    {
        const auto layoutsList = gui::factory::getUpdateInstructionLayouts();

        for (auto &layoutEntry : layoutsList) {
            layoutOptions.push_back(layoutEntry()->getLayout());
        }
    }

    bool OnBoardingUpdateInstructionWindowPresenter::onInput(const gui::InputEvent &inputEvent,
                                                             const gui::Item *currentLayout)
    {
        if (inputEvent.isShortRelease()) {
            const auto key = mapKey(inputEvent.getKeyCode());
            if (key == KeyMap::LightPress) {
                if (isLastLayout(currentLayout)) {
                    app->switchWindow(gui::window::name::finalizeOnBoardingWindow);
                }
                return true;
            }
        }
        return false;
    }
} // namespace app::OnBoarding

A products/BellHybrid/apps/application-bell-onboarding/presenter/OnBoardingUpdateInstructionWindowPresenter.hpp => products/BellHybrid/apps/application-bell-onboarding/presenter/OnBoardingUpdateInstructionWindowPresenter.hpp +29 -0
@@ 0,0 1,29 @@
// 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 <common/windows/UpdateInstructionWindowContract.hpp>
#include <apps-common/ApplicationCommon.hpp>

#include <vector>
#include <Item.hpp>

namespace app::OnBoarding
{
    class OnBoardingUpdateInstructionWindowPresenter : public gui::UpdateInstructionWindowContract::Presenter
    {
      private:
        app::ApplicationCommon *app;
        std::vector<gui::Item *> layoutOptions;
        void initLayoutOptions();

      public:
        explicit OnBoardingUpdateInstructionWindowPresenter(app::ApplicationCommon *app);

        std::vector<gui::Item *> getLayouts() const override;
        bool isLastLayout(const gui::Item *layout) const override;
        gui::Item *getFirstLayout() const override;
        bool onInput(const gui::InputEvent &inputEvent, const gui::Item *currentLayout) override;
    };
} // namespace app::OnBoarding

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

#include "OnBoardingSettingsWindow.hpp"


@@ 12,7 12,7 @@ namespace gui
        std::string name)
        : BellSettingsTimeUnitsWindow(app, std::move(windowPresenter), name)
    {
        finishedCallback = [this]() { application->switchWindow(window::name::finalizeOnBoardingWindow); };
        finishedCallback = [this]() { application->switchWindow(window::name::onBoardingUpdateInstructionWindow); };
        returnCallback   = [this]() { application->switchWindow(window::name::onBoardingShortcutsOptionWindow); };
    }
} // namespace gui

M products/BellHybrid/apps/common/CMakeLists.txt => products/BellHybrid/apps/common/CMakeLists.txt +8 -0
@@ 37,6 37,7 @@ target_sources(application-bell-common
        src/windows/BellBatteryStatusWindow.cpp
        src/windows/AppsBatteryStatusWindow.cpp
        src/windows/AudioErrorWindow.cpp
        src/windows/UpdateInstructionWindow.cpp

        src/models/SettingsModel.cpp
        src/models/BedtimeModel.cpp


@@ 87,6 88,8 @@ target_sources(application-bell-common
        src/layouts/HomeScreenLayoutVerticalWithDate.cpp
        src/layouts/ShortcutsLayouts.cpp
        src/layouts/ShortcutsLayoutClassic.cpp
        src/layouts/UpdateInstructionLayouts.cpp
        src/layouts/UpdateInstructionLayoutClassic.cpp

    PUBLIC
        include/common/BellListItemProvider.hpp


@@ 104,6 107,8 @@ target_sources(application-bell-common
        include/common/windows/BellBatteryStatusWindow.hpp
        include/common/windows/AppsBatteryStatusWindow.hpp
        include/common/windows/AudioErrorWindow.hpp
        include/common/windows/UpdateInstructionWindowContract.hpp
        include/common/windows/UpdateInstructionWindow.hpp
        include/common/TimeUtils.hpp

        include/common/data/BatteryUtils.hpp


@@ 173,6 178,9 @@ target_sources(application-bell-common
        include/common/layouts/ShortcutsLayouts.hpp
        include/common/layouts/ShortcutsLayoutProvider.hpp
        include/common/layouts/ShortcutsLayoutClassic.hpp
        include/common/layouts/UpdateInstructionLayouts.hpp
        include/common/layouts/UpdateInstructionLayoutProvider.hpp
        include/common/layouts/UpdateInstructionLayoutClassic.hpp
)

target_link_libraries(application-bell-common

M products/BellHybrid/apps/common/include/common/BellCommonNames.hpp => products/BellHybrid/apps/common/include/common/BellCommonNames.hpp +3 -2
@@ 5,6 5,7 @@

namespace gui::window::name
{
    inline constexpr auto shortcutsWindow = "BellShortcuts";
    inline constexpr auto audioErrorWindow = "AudioError";
    inline constexpr auto shortcutsWindow         = "BellShortcuts";
    inline constexpr auto updateInstructionWindow = "BellUpdateInstruction";
    inline constexpr auto audioErrorWindow        = "AudioError";
} // namespace gui::window::name

A products/BellHybrid/apps/common/include/common/layouts/UpdateInstructionLayoutClassic.hpp => products/BellHybrid/apps/common/include/common/layouts/UpdateInstructionLayoutClassic.hpp +45 -0
@@ 0,0 1,45 @@
// 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 "UpdateInstructionLayoutProvider.hpp"

#include <BoxLayout.hpp>
#include <ImageBox.hpp>

namespace gui
{
    class BellBaseLayout;

    using Instruction = std::vector<std::pair<std::string, UTF8>>;

    class UpdateInstructionLayoutClassic : public UpdateInstructionLayoutProvider, VBox
    {
      public:
        UpdateInstructionLayoutClassic(const UTF8 &image,
                                       const UTF8 &title,
                                       const Instruction &instruction,
                                       bool leftArrowVisible  = true,
                                       bool rightArrowVisible = true);

        auto getLayout() -> Item * override;

      private:
        struct InstructionPoint
        {
            HBox *container;
            Text *textBox;
            Text *numberBox;
        };
        std::vector<InstructionPoint> instructionPoints;

        ImageBox *leftArrowBox;
        ImageBox *rightArrowBox;

        Text *titleBox;
        ImageBox *imageBox;

        void buildInterface(const Instruction &instruction);
    };
}; // namespace gui

A products/BellHybrid/apps/common/include/common/layouts/UpdateInstructionLayoutProvider.hpp => products/BellHybrid/apps/common/include/common/layouts/UpdateInstructionLayoutProvider.hpp +30 -0
@@ 0,0 1,30 @@
// 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 <utf8/UTF8.hpp>

namespace gui
{
    class Item;

    class UpdateInstructionLayoutProvider
    {
      protected:
        const UTF8 image;
        const UTF8 title;
        const bool leftArrowVisible;
        const bool rightArrowVisible;

      public:
        UpdateInstructionLayoutProvider(const UTF8 &image,
                                        const UTF8 &title,
                                        bool leftArrow  = true,
                                        bool rightArrow = true)
            : image{image}, title{title}, leftArrowVisible{leftArrow}, rightArrowVisible{rightArrow} {};
        virtual ~UpdateInstructionLayoutProvider() noexcept = default;

        virtual Item *getLayout() = 0;
    };
}; // namespace gui

A products/BellHybrid/apps/common/include/common/layouts/UpdateInstructionLayouts.hpp => products/BellHybrid/apps/common/include/common/layouts/UpdateInstructionLayouts.hpp +19 -0
@@ 0,0 1,19 @@
// 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 <functional>
#include <map>
#include <string>

namespace gui
{
    class UpdateInstructionLayoutProvider;
    using LayoutGenerator = std::function<UpdateInstructionLayoutProvider *()>;

    namespace factory
    {
        std::vector<LayoutGenerator> getUpdateInstructionLayouts();
    } // namespace factory
};    // namespace gui

A products/BellHybrid/apps/common/include/common/windows/UpdateInstructionWindow.hpp => products/BellHybrid/apps/common/include/common/windows/UpdateInstructionWindow.hpp +32 -0
@@ 0,0 1,32 @@
// 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 "UpdateInstructionWindowContract.hpp"
#include <ApplicationCommon.hpp>
#include <AppWindow.hpp>
#include <apps-common/widgets/spinners/Spinners.hpp>

namespace gui
{
    class SideListView;
    class UpdateInstructionWindow : public AppWindow, public UpdateInstructionWindowContract::View
    {
        std::unique_ptr<UpdateInstructionWindowContract::Presenter> presenter;
        SideListView *sideListView = nullptr;
        WidgetSpinner *spinner     = nullptr;

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

        void onValueChanged(const std::uint32_t currentValue);

      public:
        UpdateInstructionWindow(app::ApplicationCommon *app,
                                std::unique_ptr<UpdateInstructionWindowContract::Presenter> &&presenter,
                                const std::string &name);

        bool isLastLayout() const;
    };
} // namespace gui

A products/BellHybrid/apps/common/include/common/windows/UpdateInstructionWindowContract.hpp => products/BellHybrid/apps/common/include/common/windows/UpdateInstructionWindowContract.hpp +30 -0
@@ 0,0 1,30 @@
// 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 <apps-common/BasePresenter.hpp>
#include <vector>
#include <Item.hpp>

namespace gui
{
    class UpdateInstructionWindowContract
    {
      public:
        class View
        {
          public:
            virtual ~View() = default;
        };

        class Presenter : public app::BasePresenter<UpdateInstructionWindowContract::View>
        {
          public:
            virtual std::vector<gui::Item *> getLayouts() const                                     = 0;
            virtual bool isLastLayout(const gui::Item *layout) const                                = 0;
            virtual gui::Item *getFirstLayout() const                                               = 0;
            virtual bool onInput(const gui::InputEvent &inputEvent, const gui::Item *currentLayout) = 0;
        };
    };
} // namespace gui

A products/BellHybrid/apps/common/src/layouts/UpdateInstructionLayoutClassic.cpp => products/BellHybrid/apps/common/src/layouts/UpdateInstructionLayoutClassic.cpp +187 -0
@@ 0,0 1,187 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "layouts/UpdateInstructionLayoutClassic.hpp"
#include <apps-common/widgets/BellBaseLayout.hpp>
#include <i18n/i18n.hpp>
#include <Style.hpp>
#include <TextFixedSize.hpp>

namespace
{
    namespace container
    {
        constexpr auto width      = 544U;
        constexpr auto height     = 436U;
        constexpr auto top_margin = 20U;

        namespace centerBox
        {
            constexpr auto width  = 448U;
            constexpr auto height = container::height;
        } // namespace centerBox

        namespace imageBox
        {
            constexpr auto width  = centerBox::width;
            constexpr auto height = 120U;
        } // namespace imageBox

        namespace title
        {
            constexpr auto height        = 56U;
            constexpr auto width         = centerBox::width;
            constexpr auto font          = style::window::font::large;
            constexpr auto bottom_margin = 32U;
        } // namespace title

        namespace instruction
        {
            constexpr auto maxNumberOfLines = 3;
            constexpr auto width            = centerBox::width - 40U;
            constexpr auto minHeight        = 43U;
            constexpr auto maxHeight        = minHeight * maxNumberOfLines;
            constexpr auto bottom_margin    = 14U;

            namespace number
            {
                constexpr auto width = 36U;
                constexpr auto font  = style::window::font::verybiglight;
            } // namespace number

            namespace text
            {
                constexpr auto width = instruction::width - number::width;
                constexpr auto font  = style::window::font::verybiglight;
            } // namespace text
        }     // namespace instruction

    } // namespace container
} // namespace

namespace gui
{
    UpdateInstructionLayoutClassic::UpdateInstructionLayoutClassic(const UTF8 &image,
                                                                   const UTF8 &title,
                                                                   const Instruction &instruction,
                                                                   bool leftArrowVisible,
                                                                   bool rightArrowVisible)
        : UpdateInstructionLayoutProvider(image, title, leftArrowVisible, rightArrowVisible),
          VBox(nullptr, 0, 0, style::bell_base_layout::w, style::bell_base_layout::h)
    {
        buildInterface(instruction);
    }

    void UpdateInstructionLayoutClassic::buildInterface(const Instruction &instruction)
    {
        setAlignment(Alignment::Horizontal::Center);

        auto containerThreeBox = new HThreeBox<HBox, VBox, HBox>(this);
        containerThreeBox->setMinimumSize(container::width, container::height);
        containerThreeBox->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        containerThreeBox->setMargins(Margins(0, container::top_margin, 0, 0));
        containerThreeBox->setEdges(RectangleEdge::None);

        // -------------------------- left box -------------------------------------------
        containerThreeBox->firstBox = new HBox(containerThreeBox);
        containerThreeBox->firstBox->setAlignment(Alignment(Alignment::Vertical::Center));
        containerThreeBox->firstBox->setEdges(RectangleEdge::None);
        containerThreeBox->firstBox->activeItem = false;

        leftArrowBox = new ImageBox(nullptr, new Image("bell_arrow_left_W_M"));
        leftArrowBox->setAlignment(Alignment(Alignment::Horizontal::Right, Alignment::Vertical::Center));
        leftArrowBox->setMinimumSizeToFitImage();
        leftArrowBox->setVisible(leftArrowVisible);
        leftArrowBox->setEdges(RectangleEdge::None);
        containerThreeBox->firstBox->setMinimumSize(leftArrowBox->widgetMinimumArea.w,
                                                    leftArrowBox->widgetMinimumArea.h);

        containerThreeBox->firstBox->addWidget(leftArrowBox);

        // -------------------------- center box -------------------------------------------
        containerThreeBox->centerBox = new VBox(containerThreeBox);
        containerThreeBox->centerBox->setEdges(RectangleEdge::None);
        containerThreeBox->centerBox->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Top));
        containerThreeBox->centerBox->setMinimumSize(container::centerBox::width, container::centerBox::height);
        containerThreeBox->centerBox->setMaximumSize(container::centerBox::width, container::centerBox::height);

        imageBox = new ImageBox(nullptr, new Image(image, ImageTypeSpecifier::W_G));
        imageBox->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Top));
        imageBox->setMinimumSizeToFitImage();
        imageBox->setMaximumSize(container::imageBox::width, container::imageBox::height);
        imageBox->setVisible(true);

        titleBox = new Text(nullptr, 0, 0, 0, 0);
        titleBox->setMinimumSize(container::title::width, container::title::height);
        titleBox->setMaximumSize(container::title::width, container::title::height);
        titleBox->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Top));
        titleBox->setMargins(Margins(0, 0, 0, container::title::bottom_margin));
        titleBox->setTextType(TextType::SingleLine);
        titleBox->setEditMode(EditMode::Browse);
        titleBox->setFont(container::title::font);
        titleBox->setRichText(utils::translate(title));

        containerThreeBox->centerBox->addWidget(imageBox);
        containerThreeBox->centerBox->addWidget(titleBox);

        for (const auto &point : instruction) {
            auto instructionContainer = new HBox(nullptr);
            instructionContainer->setEdges(RectangleEdge::None);
            instructionContainer->setAlignment(Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Top));
            instructionContainer->setMargins(Margins(0, 0, 0, container::instruction::bottom_margin));
            instructionContainer->setMinimumSize(container::instruction::width, container::instruction::minHeight);
            instructionContainer->setMaximumSize(container::instruction::width, container::instruction::maxHeight);

            auto numberBox = new Text(nullptr, 0, 0, 0, 0);
            numberBox->setMinimumSize(container::instruction::number::width, container::instruction::minHeight);
            numberBox->setAlignment(Alignment::Horizontal::Left);
            numberBox->setTextType(TextType::MultiLine);
            numberBox->setEditMode(EditMode::Browse);
            numberBox->setFont(container::instruction::number::font);
            numberBox->setRichText(point.first);

            auto textBox = new Text(nullptr, 0, 0, 0, 0);
            textBox->setMinimumSize(container::instruction::text::width, container::instruction::minHeight);
            textBox->setMaximumSize(container::instruction::text::width, container::instruction::maxHeight);
            textBox->setAlignment(Alignment::Horizontal::Left);
            textBox->setTextType(TextType::MultiLine);
            textBox->setEditMode(EditMode::Browse);
            textBox->setFont(container::instruction::text::font);
            textBox->setRichText(utils::translate(point.second));

            InstructionPoint instructionPoint{.container = std::move(instructionContainer),
                                              .textBox   = std::move(textBox),
                                              .numberBox = std::move(numberBox)};
            instructionPoints.push_back(std::move(instructionPoint));
        }

        for (const auto &point : instructionPoints) {
            point.container->addWidget(point.numberBox);
            point.container->addWidget(point.textBox);
            containerThreeBox->centerBox->addWidget(point.container);
        }

        // -------------------------- right box -------------------------------------------
        containerThreeBox->lastBox = new HBox(containerThreeBox);
        containerThreeBox->lastBox->setAlignment(Alignment(Alignment::Vertical::Center));
        containerThreeBox->lastBox->setEdges(RectangleEdge::None);
        containerThreeBox->lastBox->activeItem = false;

        rightArrowBox = new ImageBox(nullptr, new Image("bell_arrow_right_W_M"));
        rightArrowBox->setAlignment(Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Center));
        rightArrowBox->setMinimumSizeToFitImage();
        rightArrowBox->setVisible(rightArrowVisible);
        rightArrowBox->setEdges(RectangleEdge::None);
        containerThreeBox->lastBox->setMinimumSize(rightArrowBox->widgetMinimumArea.w,
                                                   rightArrowBox->widgetMinimumArea.h);

        containerThreeBox->lastBox->addWidget(rightArrowBox);

        this->resizeItems();
    }

    auto UpdateInstructionLayoutClassic::getLayout() -> Item *
    {
        return this;
    }
}; // namespace gui

A products/BellHybrid/apps/common/src/layouts/UpdateInstructionLayouts.cpp => products/BellHybrid/apps/common/src/layouts/UpdateInstructionLayouts.cpp +24 -0
@@ 0,0 1,24 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "layouts/UpdateInstructionLayouts.hpp"
#include "layouts/UpdateInstructionLayoutClassic.hpp"

namespace gui::factory
{
    std::vector<LayoutGenerator> getUpdateInstructionLayouts()
    {
        return {{[]() {
                    const Instruction instructions{{"1.", "app_bell_update_instruction_1st_point"},
                                                   {"2.", "app_bell_update_instruction_2nd_point"}};
                    return new UpdateInstructionLayoutClassic(
                        "bell_mudita_logo", "app_bell_update_instruction_title", instructions, false, true);
                }},
                {[]() {
                    const Instruction instructions{{"3.", "app_bell_update_instruction_3rd_point"},
                                                   {"4.", "app_bell_update_instruction_4th_point"}};
                    return new UpdateInstructionLayoutClassic(
                        "bell_mudita_logo", "app_bell_update_instruction_title", instructions, true, false);
                }}};
    }
} // namespace gui::factory

A products/BellHybrid/apps/common/src/windows/UpdateInstructionWindow.cpp => products/BellHybrid/apps/common/src/windows/UpdateInstructionWindow.cpp +64 -0
@@ 0,0 1,64 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "windows/UpdateInstructionWindow.hpp"

#include <Style.hpp>
#include <SideListView.hpp>
#include <gui/input/InputEvent.hpp>

namespace gui
{
    UpdateInstructionWindow::UpdateInstructionWindow(
        app::ApplicationCommon *app,
        std::unique_ptr<UpdateInstructionWindowContract::Presenter> &&presenter,
        const std::string &name)
        : AppWindow(app, name), presenter{std::move(presenter)}
    {
        this->presenter->attach(this);
        buildInterface();
    }

    void UpdateInstructionWindow::buildInterface()
    {
        AppWindow::buildInterface();

        statusBar->setVisible(false);
        header->setTitleVisibility(false);
        navBar->setVisible(false);

        auto layouts = presenter->getLayouts();
        spinner      = new WidgetSpinner(this, {layouts.begin(), layouts.end()}, Boundaries::Fixed);
        spinner->setSize(style::window_width, style::window_height);
        spinner->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        spinner->setFocusEdges(RectangleEdge::None);
        auto selectedLayout = presenter->getFirstLayout();
        spinner->setCurrentValue(selectedLayout);

        spinner->onValueChanged = [this](const auto &) {
            getApplication()->render(gui::RefreshModes::GUI_REFRESH_DEEP);
        };

        setFocusItem(spinner);
    }

    bool UpdateInstructionWindow::isLastLayout() const
    {
        auto currentLayout = spinner->getCurrentValue();
        return presenter->isLastLayout(currentLayout);
    }

    bool UpdateInstructionWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (spinner->onInput(inputEvent)) {
            return true;
        }

        if (presenter->onInput(inputEvent, spinner->getCurrentValue())) {
            return true;
        }

        return AppWindow::onInput(inputEvent);
    }

} // namespace gui