~aleteoryx/muditaos

244dbb02ce2f781f9daaefb1c8ee81d92e3d61bc — Michał Kamoń 5 years ago cd07a0a
DesktopMainWindow notifications update (#886)

M changelog.md => changelog.md +2 -0
@@ 11,6 11,8 @@
* `[appmgr]` Application manager refactored.

### Fixed

* `[desktop][messages]` Fixed notifications display and navigation
* `[cellular]` Fixed 32 bit UCS2 codes handling.

### Other

M image/assets/lang/lang_en.json => image/assets/lang/lang_en.json +3 -2
@@ 170,8 170,8 @@
    "app_desktop_sim_blocked_info1": "Sorry, SIM card permanently blocked.",
    "app_desktop_sim_blocked_info2": "Please contact your service provider",

    "app_desktop_unread_messages": "Unread messages",
    "app_desktop_missed_calls": "Missed calls",
    "app_desktop_unread_messages": "<text>Unread <b>messages</b></text>",
    "app_desktop_missed_calls": "<text>Missed <b>calls</b></text>",
    "app_desktop_menu_phone": "PHONE",
    "app_desktop_menu_contacts": "CONTACTS",
    "app_desktop_menu_messages": "MESSAGES",


@@ 194,6 194,7 @@
    "app_desktop_show": "SHOW",
    "app_desktop_calls": "CALLS",
    "app_desktop_clear": "CLEAR",
    "app_desktop_clear_all": "CLEAR ALL",
    
    "app_call_call": "CALL",
    "app_call_clear": "CLEAR",

M module-apps/application-desktop/ApplicationDesktop.cpp => module-apps/application-desktop/ApplicationDesktop.cpp +10 -5
@@ 97,13 97,18 @@ namespace app
    {
        assert(msg);
        auto records = *msg->getResult();

        bool rebuildMainWindow = false;

        for (auto record : records) {
            switch (record.key) {
            case NotificationsRecord::Key::Calls:
                rebuildMainWindow |= record.value != notifications.notSeen.Calls;
                notifications.notSeen.Calls = record.value;
                break;

            case NotificationsRecord::Key::Sms:
                rebuildMainWindow |= record.value != notifications.notSeen.SMS;
                notifications.notSeen.SMS = record.value;
                break;



@@ 114,9 119,11 @@ namespace app
            }
        }

        /// TODO if current window is this one or was on stack -> requild it
        /// windowsFactory.build(this, app::window::name::desktop_menu);
        /// windowsFactory.build(this, app::window::name::desktop_main_window);
        auto ptr = getCurrentWindow();
        if (rebuildMainWindow && ptr->getName() == app::window::name::desktop_main_window) {
            ptr->rebuild();
        }

        return true;
    }



@@ 175,7 182,6 @@ namespace app
                               db::Interface::Name::Notifications,
                               std::make_unique<db::query::notifications::Clear>(NotificationsRecord::Key::Calls));
        notifications.notSeen.Calls = 0;
        getCurrentWindow()->rebuild(); // triggers rebuild - shouldn't be needed, but is
        return true;
    }



@@ 186,7 192,6 @@ namespace app
                               db::Interface::Name::Notifications,
                               std::make_unique<db::query::notifications::Clear>(NotificationsRecord::Key::Sms));
        notifications.notSeen.SMS = 0;
        getCurrentWindow()->rebuild(); // triggers rebuild - shouldn't be needed, but is
        return true;
    }


M module-apps/application-desktop/CMakeLists.txt => module-apps/application-desktop/CMakeLists.txt +3 -0
@@ 17,6 17,7 @@ target_sources( ${PROJECT_NAME}
		"${CMAKE_CURRENT_LIST_DIR}/ApplicationDesktop.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/PinLock.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/PinLockHandler.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/NotificationsBox.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/DesktopMainWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/PinLockBaseWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/ScreenLockBox.cpp"


@@ 31,9 32,11 @@ target_sources( ${PROJECT_NAME}
	PUBLIC
		"${CMAKE_CURRENT_LIST_DIR}/ApplicationDesktop.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/data/LockPhoneData.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/data/Style.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/PinHash.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/PinLock.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/PinLockHandler.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/NotificationsBox.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/DesktopMainWindow.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/PinLockBaseWindow.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/PinLockBox.hpp"

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

#pragma once

namespace style::desktop
{

    namespace notifications
    {
        constexpr auto SpanSize     = 8;
        constexpr auto DigitSize    = 14;
        constexpr auto IconWidth    = 35;
        constexpr auto TextMaxWidth = 250;

        constexpr auto X     = 0;
        constexpr auto Y     = 284;
        constexpr auto Width = style::window_width;

    }; // namespace notifications

    namespace timeLabel
    {
        constexpr auto X     = 0;
        constexpr auto Y     = 106;
        constexpr auto Width = style::window_width;
        constexpr auto Hight = 96;

    } // namespace timeLabel

    namespace dayLabel
    {
        constexpr auto X     = 0;
        constexpr auto Y     = 204;
        constexpr auto Width = style::window_width;
        constexpr auto Hight = 51;

    } // namespace dayLabel

} // namespace style::desktop

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

#include "NotificationsBox.hpp"

#include "application-desktop/data/Style.hpp"

#include "TextFixedSize.hpp"
#include "RichTextParser.hpp"
#include "FontManager.hpp"

using namespace gui;
using namespace style::desktop;

NotificationsBox::NotificationsBox(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
    : VBox(parent, x, y, w, h)
{
    this->setAlignment(Alignment(gui::Alignment::Horizontal::Center));
    this->setPenWidth(style::window::default_border_no_focus_w);
    this->setPenFocusWidth(style::window::default_border_no_focus_w);

    auto getNextNotification = [this]() -> Item * {
        auto focusedItem = getFocusItem();
        if (focusedItem == nullptr) {
            return nullptr;
        }
        auto nextItem = focusedItem->getNavigationItem(NavigationDirection::UP);

        if (nextItem == nullptr) {
            nextItem = focusedItem->getNavigationItem(NavigationDirection::DOWN);
        }
        return nextItem;
    };

    auto setNextFocusedItemAfterErase = [this](Item *item) -> bool {
        if (item == nullptr) {
            setFocus(false);
        }
        else {
            item->clearNavigationItem(NavigationDirection::DOWN);
            item->clearNavigationItem(NavigationDirection::UP);
            setNavigation();
            setFocusItem(item);
        }
        return true;
    };

    inputCallback = [this, setNextFocusedItemAfterErase, getNextNotification](Item &, const InputEvent &event) -> bool {
        if (event.isShortPress() && event.is(KeyCode::KEY_RF)) {
            LOG_DEBUG("Removing single notification");
            auto ptr = getFocusItem();
            if (ptr == nullptr || focusItem == this) {
                return false;
            }
            auto nextItem = getNextNotification();
            ptr->inputCallback(*this, event);
            erase(ptr);
            return setNextFocusedItemAfterErase(nextItem);
        }
        return false;
    };
}

namespace
{
    auto buildImageInactive(const UTF8 &img) -> gui::Image *
    {
        auto thumbnail        = new gui::Image(img);
        thumbnail->activeItem = false;
        return thumbnail;
    }

    auto buildNotificationIcon(UTF8 icon) -> gui::Image *
    {
        auto thumbnail = buildImageInactive(icon);
        thumbnail->setMinimumWidth(notifications::IconWidth);
        thumbnail->setAlignment(Alignment(gui::Alignment::Horizontal::Right, gui::Alignment::Vertical::Center));
        thumbnail->setMargins(
            gui::Margins(style::window::default_left_margin, 0, style::window::default_right_margin, 0));
        return thumbnail;
    }

    auto buildNotificationNameLabel(const UTF8 &name, uint32_t width) -> gui::TextFixedSize *
    {
        auto text = new gui::TextFixedSize(
            nullptr, 0, 0, style::desktop::notifications::TextMaxWidth, style::window::label::default_h);
        text->setMaximumSize(width, Axis::X);

        TextFormat format(FontManager::getInstance().getFont(style::window::font::medium));
        text::RichTextParser rtParser;
        auto parsedText = rtParser.parse(name, &format);

        text->setText(std::move(parsedText));
        text->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        text->setPenWidth(style::window::default_border_no_focus_w);
        text->setUnderline(false);
        text->activeItem = false;
        return text;
    }

    constexpr auto maxNotificationValue = "99+";

    auto buildNotificationCountText(const UTF8 &indicator) -> gui::Text *
    {
        auto number = new gui::Text();
        if (indicator.length() > 2) {
            number->setText(maxNotificationValue);
            number->setMinimumWidth(strlen(maxNotificationValue) * notifications::DigitSize);
        }
        else {
            number->setText(indicator);
            number->setMinimumWidth(indicator.length() * notifications::DigitSize);
        }
        number->setMinimumWidth(indicator.length() * notifications::DigitSize);
        number->setFont(style::window::font::mediumbold);
        number->setPenWidth(style::window::default_border_no_focus_w);
        number->setMargins(gui::Margins(0, 0, style::window::default_right_margin, 0));
        number->setAlignment(Alignment(gui::Alignment::Horizontal::Right, gui::Alignment::Vertical::Center));
        number->activeItem = false;
        return number;
    }

} // namespace

auto NotificationsBox::addNotification(UTF8 icon,
                                       UTF8 name,
                                       UTF8 indicator,
                                       std::function<bool()> showCallback,
                                       std::function<bool()> clearCallback,
                                       std::function<void(bool)> onFocus) -> bool
{
    // 1. create hbox for all elements
    auto el = new gui::HBox(nullptr, 0, 0, style::window::default_body_width, style::window::label::default_h);

    // 2. Add all elements to hbox layout
    el->addWidget(buildNotificationIcon(icon));

    el->addWidget(buildNotificationNameLabel(name, el->area().w));
    el->addWidget(buildImageInactive("dot_12px_hard_alpha_W_G"));
    el->addWidget(buildNotificationCountText(indicator));

    // 3. Set hbox layout properties
    el->setAlignment(Alignment(gui::Alignment::Vertical::Center));
    el->setMargins(gui::Margins(0, 0, 0, 10));
    el->setPenWidth(style::window::default_border_no_focus_w);
    el->setPenFocusWidth(style::window::default_border_focus_w);
    el->setEdges(RectangleEdge::Bottom | RectangleEdge::Top);

    el->focusChangedCallback = [el, onFocus](Item &) -> bool {
        onFocus(el->focus);
        return true;
    };
    el->activatedCallback = [showCallback](Item &) { return showCallback(); };
    el->inputCallback     = [showCallback, clearCallback, this](Item &, const InputEvent &event) -> bool {
        if (event.isShortPress() && event.is(KeyCode::KEY_RF) && clearCallback) {
            return clearCallback();
        }
        return false;
    };

    this->addWidget(el);
    return el->visible;
}

bool NotificationsBox::onInput(const InputEvent &inputEvent)
{

    if (inputEvent.isShortPress() && (inputEvent.is(KeyCode::KEY_UP) || inputEvent.is(KeyCode::KEY_DOWN))) {
        auto handled = handleNavigation(inputEvent);
        if (!handled) {
            setFocus(false);
        }
        return true;
    }
    return VBox::onInput(inputEvent);
}

bool NotificationsBox::clearAll(const InputEvent &event)
{
    while (!children.empty()) {
        inputCallback(*this, event);
    }
    return true;
}

void NotificationsBox::navigateToBottom()
{
    setFocusItem(children.back());
}

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

#pragma once

#include "BoxLayout.hpp"
#include "Icon.hpp"

namespace gui
{
    class NotificationsBox : public VBox
    {
      public:
        NotificationsBox(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h);

        auto addNotification(UTF8 icon,
                             UTF8 name,
                             UTF8 indicator,
                             std::function<bool()> showCallback,
                             std::function<bool()> clearCallback,
                             std::function<void(bool)> onFocus) -> bool;

        bool onInput(const InputEvent &inputEvent) override;
        bool clearAll(const InputEvent &event);

        void navigateToBottom();
    };
} // namespace gui

M module-apps/application-desktop/windows/DesktopMainWindow.cpp => module-apps/application-desktop/windows/DesktopMainWindow.cpp +80 -139
@@ 10,6 10,8 @@
#include "GuiTimer.hpp"
#include "application-desktop/ApplicationDesktop.hpp"
#include "application-desktop/data/LockPhoneData.hpp"
#include "application-desktop/data/Style.hpp"
#include "application-desktop/widgets/NotificationsBox.hpp"
#include "application-messages/ApplicationMessages.hpp"
#include "gui/widgets/Image.hpp"
#include "service-appmgr/Controller.hpp"


@@ 19,26 21,12 @@

#include "i18/i18.hpp"
#include "log/log.hpp"
#include <Span.hpp>
#include <Style.hpp>

#include <application-settings-new/ApplicationSettings.hpp>
#include <application-settings/ApplicationSettings.hpp>
#include <cassert>
#include <time/time_conversion.hpp>

namespace style
{
    const auto design_time_offset          = 106;
    const auto design_time_h               = 96;
    const auto design_day_offset           = 204;
    const auto design_day_h                = 51;
    const auto design_border_offset        = 20;
    const auto design_option_span          = 8;
    const auto design_notifications_offset = 284;
    const auto digit_normal_size              = 13;
    const auto notification_icon_unified_size = 35;
}; // namespace style

namespace gui
{
    void DesktopMainWindow::buildInterface()


@@ 50,20 38,27 @@ namespace gui
        topBar->setActive(
            {{TopBar::Elements::SIGNAL, true}, {TopBar::Elements::LOCK, true}, {TopBar::Elements::BATTERY, true}});

        time = new gui::Label(this, 0, style::design_time_offset, style::window_width, style::design_time_h);
        using namespace style::desktop;

        time = new gui::Label(this, timeLabel::X, timeLabel::Y, timeLabel::Width, timeLabel::Hight);
        time->setFilled(false);
        time->setBorderColor(gui::ColorNoColor);
        time->setFont(style::window::font::supersizemelight);
        time->setText(ttime);
        time->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));

        dayText = new gui::Label(this, 0, style::design_day_offset, style::window_width, style::design_day_h);
        dayText = new gui::Label(this, dayLabel::X, dayLabel::Y, dayLabel::Width, dayLabel::Hight);
        dayText->setFilled(false);
        dayText->setBorderColor(gui::ColorNoColor);
        dayText->setFont(style::window::font::biglight);
        dayText->setText(ttime.day() + ", " + ttime.str("%d %b"));
        dayText->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));

        activatedCallback = [this](Item &) {
            application->switchWindow(app::window::name::desktop_menu);
            return true;
        };

        setVisibleState();
    }



@@ 90,12 85,12 @@ namespace gui
        auto app = getAppDesktop();

        if (app->lockHandler.lock.isLocked() && app->lockHandler.lock.getLockType() == PinLock::LockType::Screen) {
            bottomBar->restore();

            bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get("app_desktop_unlock"));
            topBar->setActive(TopBar::Elements::LOCK, true);
            inputCallback = nullptr;
            setFocusItem(nullptr);
            erase(notifications);
            buildNotifications(app);

            sys::Bus::SendUnicast(
                std::make_shared<TimersProcessingStopMessage>(), service::name::service_time, application);


@@ 107,11 102,13 @@ namespace gui
                std::make_shared<TimersProcessingStopMessage>(), service::name::service_time, application);
        }
        else {
            bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get("app_desktop_menu"));
            topBar->setActive(TopBar::Elements::LOCK, false);
            if (!fillNotifications(app)) {

            if (!buildNotifications(app)) {
                LOG_ERROR("Couldn't fit in all notifications");
            }
            setActiveState(app);

            if (app->need_sim_select && Store::GSM::get()->sim == Store::GSM::SIM::SIM_UNKNOWN) {
                app::manager::Controller::switchApplication(
                    this->application, app::name_settings, app::sim_select, nullptr);


@@ 120,6 117,7 @@ namespace gui
            sys::Bus::SendUnicast(
                std::make_shared<TimersProcessingStartMessage>(), service::name::service_time, application);
        }
        application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
    }

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


@@ 145,19 143,19 @@ namespace gui
    {
        auto *app = getAppDesktop();

        if ((inputEvent.keyCode == KeyCode::KEY_PND) && (!app->lockHandler.lock.isLocked())) {
        if (inputEvent.is(KeyCode::KEY_PND) && (!app->lockHandler.lock.isLocked())) {
            app->lockHandler.lock.lock();
            setVisibleState();
            application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
            application->setSuspendFlag(true);
            return true;
        }
        else if (inputEvent.keyCode == KeyCode::KEY_RF) {
        else if (inputEvent.is(KeyCode::KEY_RF)) {
            application->switchWindow("PowerOffWindow");
            return true;
        }
        // long press of '0' key is translated to '+'
        else if (inputEvent.keyCode == KeyCode::KEY_0) {
        else if (inputEvent.is(KeyCode::KEY_0)) {
            return app::prepareCall(application, "+");
        }
        // check if any of the lower inheritance onInput methods catch the event


@@ 167,14 165,19 @@ namespace gui
    bool DesktopMainWindow::processShortPressEventOnUnlocked(const InputEvent &inputEvent)
    {
        auto code = translator.handle(inputEvent.key, InputMode({InputMode::phone}).get());
        if (inputEvent.keyCode == KeyCode::KEY_ENTER) {
            application->switchWindow(app::window::name::desktop_menu);
            return true;
        }
        // if numeric key was pressed record that key and send it to call application
        else if (code != 0) {
        if (code != 0) {
            return app::prepareCall(application, std::string(1, static_cast<char>(code)));
        }
        else if (inputEvent.is(KeyCode::KEY_UP) && focusItem == nullptr) {
            setFocusItem(notifications);
            notifications->navigateToBottom();
            return true;
        }
        else if (inputEvent.is(KeyCode::KEY_DOWN) && focusItem == nullptr) {
            setFocusItem(notifications);
            return true;
        }
        // check if any of the lower inheritance onInput methods catch the event
        return AppWindow::onInput(inputEvent);
    }


@@ 252,123 255,38 @@ namespace gui
        return gui::AppWindow::buildDrawList();
    }

    auto add_image_inactive(UTF8 img)
    {
        auto thumbnail        = new gui::Image(img);
        thumbnail->activeItem = false;
        return thumbnail;
    }

    auto add_box_icon(UTF8 icon)
    {
        auto thumbnail = add_image_inactive(icon);
        thumbnail->setMinimumWidth(style::notification_icon_unified_size);
        thumbnail->setMargins(
            gui::Margins(style::window::default_left_margin, 0, style::window::default_right_margin, 0));
        return thumbnail;
    }

    /// for now notifications are like that: `^<span>[icon]<span>[dumb text]       [dot image] [number of
    /// notifications]<span>$`
    auto add_notification(BoxLayout *layout,
                          UTF8 icon,
                          UTF8 name,
                          UTF8 indicator,
                          std::function<bool()> showCallback,
                          std::function<bool()> clearCallback) -> bool
    {
        // 1. create hbox for all elements
        auto el = new gui::HBox(nullptr, 0, 0, style::window::default_body_width, style::window::label::default_h);
        el->setAlignment(Alignment(gui::Alignment::Vertical::Center));

        auto text = new gui::Label();
        text->setMaximumSize(el->area().w, Axis::X);
        text->setText(name);
        text->setFont(style::window::font::medium);
        text->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        text->setPenWidth(style::window::default_border_no_focus_w);
        text->activeItem = false;

        auto number = new gui::Text();
        if (indicator.length() > 2) {
            const UTF8 max_notification_value = "99+";
            number->setText(max_notification_value);
            number->setMinimumWidth(max_notification_value.length() * style::digit_normal_size);
        }
        else {
            number->setText(indicator);
            number->setMinimumWidth(indicator.length() * style::digit_normal_size);
        }
        number->setFont(style::window::font::mediumbold);
        number->setPenWidth(style::window::default_border_no_focus_w);
        number->setMargins(gui::Margins(0, 0, style::window::default_right_margin, 0));
        number->setAlignment(Alignment(gui::Alignment::Horizontal::Right, gui::Alignment::Vertical::Center));
        number->activeItem = false;

        // 2. Add all elements to hbox layout
        el->addWidget(add_box_icon(icon));
        el->addWidget(text);
        el->addWidget(add_image_inactive("dot_12px_hard_alpha_W_G"));
        el->addWidget(number);

        // 3. Set hbox layout properties
        el->setPenWidth(style::window::default_border_no_focus_w);
        el->setPenFocusWidth(style::window::default_border_focus_w);
        el->setEdges(RectangleEdge::Bottom | RectangleEdge::Top);
        el->inputCallback = [showCallback, clearCallback](Item &, const InputEvent &event) -> bool {
            if (event.state != InputEvent::State::keyReleasedShort) {
                return false;
            }
            if (event.keyCode == KeyCode::KEY_LF && showCallback) {
                return showCallback();
            }
            if (event.keyCode == KeyCode::KEY_RF && clearCallback) {
                return clearCallback();
            }
            return false;
        };

        layout->addWidget(el);
        if (el->visible) {
            // space between next notifications to show
            layout->addWidget(new gui::Span(Axis::Y, style::design_option_span));
        }

        return el->visible;
    }

    auto DesktopMainWindow::fillNotifications(app::ApplicationDesktop *app) -> bool
    auto DesktopMainWindow::buildNotifications(app::ApplicationDesktop *app) -> bool
    {
        bottomBar->restore();
        erase(notifications);
        // 1. create notifications box
        notifications = new gui::VBox(nullptr,
                                      0,
                                      style::design_notifications_offset,
                                      style::window_width,
                                      bottomBar->widgetArea.pos(Axis::Y) - style::design_notifications_offset);
        notifications->setAlignment(Alignment(gui::Alignment::Horizontal::Center));
        notifications->setPenWidth(style::window::default_border_no_focus_w);
        notifications->setPenFocusWidth(style::window::default_border_no_focus_w);
        this->addWidget(notifications);
        using namespace style::desktop;
        notifications = new NotificationsBox(this,
                                             notifications::X,
                                             notifications::Y,
                                             notifications::Width,
                                             bottomBar->widgetArea.pos(Axis::Y) - notifications::Y);

        addWidget(notifications);
        if (!notifications->visible) {
            LOG_ERROR("Can't fit notifications box!");
            return false;
        }

        // 2. actually fill it in
        auto onNotificationFocus = [this](bool isFocused) -> void {
            bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get("app_desktop_show"), isFocused);
            bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get("app_desktop_clear"), isFocused);
        };

        if (app->notifications.notSeen.Calls > 0) {
            add_notification(
                notifications,
            notifications->addNotification(
                "phone",
                utils::localize.get("app_desktop_missed_calls"),
                std::to_string(app->notifications.notSeen.Calls),
                [app]() -> bool { return app->showCalls(); },
                [app]() -> bool { return app->clearCallsNotification(); });
                [app]() -> bool { return app->clearCallsNotification(); },
                onNotificationFocus);
        }
        if (app->notifications.notSeen.SMS > 0) {
            add_notification(
                notifications,
            notifications->addNotification(
                "mail",
                utils::localize.get("app_desktop_unread_messages"),
                std::to_string(app->notifications.notSeen.SMS),


@@ 376,19 294,42 @@ namespace gui
                    return app::manager::Controller::switchApplication(
                        application, app::name_messages, gui::name::window::main_window, nullptr);
                },
                [app]() -> bool { return app->clearMessagesNotification(); });
                [app, this]() -> bool { return app->clearMessagesNotification(); },
                onNotificationFocus);
        }
        bottomBar->store();

        notifications->focusChangedCallback = [this, app](Item &) -> bool {
            if (notifications->focus == false) {
                LOG_DEBUG("All notifications removed, notification box lost focus");
                bottomBar->setActive(BottomBar::Side::RIGHT, false);
                setFocusItem(nullptr);
                setActiveState(app);
                return true;
            }
            return false;
        };

        return true;
    }
    auto DesktopMainWindow::setActiveState(app::ApplicationDesktop *app) -> bool
    {
        bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get("app_desktop_menu"));
        if (app->notifications.notSeen.areEmpty() != true) {
            setFocusItem(notifications);
            bottomBar->setText(BottomBar::Side::LEFT, utils::localize.get("app_desktop_show"));
            bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get("app_desktop_clear"));
            bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get("app_desktop_clear_all"));
            inputCallback = [this, app](Item &, const InputEvent &inputEvent) -> bool {
                if (inputEvent.isShortPress() && inputEvent.is(KeyCode::KEY_RF) && getFocusItem() == nullptr) {
                    LOG_DEBUG("KEY_RF pressed to clear all notifications");
                    setFocusItem(notifications);
                    return notifications->clearAll(inputEvent);
                }
                return false;
            };
        }
        else {
            bottomBar->setText(BottomBar::Side::LEFT, utils::localize.get("app_desktop_calls"));
            inputCallback = [app](Item &, const InputEvent &inputEvent) -> bool {
                if (inputEvent.state == InputEvent::State::keyReleasedShort &&
                    inputEvent.keyCode == gui::KeyCode::KEY_LF) {
                if (inputEvent.isShortPress() && inputEvent.is(gui::KeyCode::KEY_LF)) {
                    LOG_DEBUG("KEY_LF pressed to navigate to calls");
                    return app->showCalls();
                }
                return false;

M module-apps/application-desktop/windows/DesktopMainWindow.hpp => module-apps/application-desktop/windows/DesktopMainWindow.hpp +7 -7
@@ 4,13 4,11 @@
#pragma once

#include "AppWindow.hpp"
#include "gui/widgets/BottomBar.hpp"
#include "gui/widgets/Image.hpp"
#include "gui/widgets/Label.hpp"
#include "gui/widgets/Text.hpp"
#include "gui/widgets/TopBar.hpp"
#include "gui/widgets/Window.hpp"

#include "Translator.hpp"

namespace app
{
    class ApplicationDesktop;


@@ 18,13 16,14 @@ namespace app

namespace gui
{
    class NotificationsBox;

    class DesktopMainWindow : public AppWindow
    {
      protected:
        gui::Label *time          = nullptr;
        gui::Label *dayText       = nullptr;
        gui::VBox *notifications  = nullptr;
        gui::NotificationsBox *notifications = nullptr;

        /// Timed enter value cache, could be templated to any value really
        class EnterCache


@@ 59,13 58,14 @@ namespace gui
            }
        } enter_cache;
        /**
         * Name of the appliction that was on top when lock timeout occured
         * Name of the application that was on top when lock timeout occurred
         */
        std::string lockTimeoutApplilcation = "";

        // method hides or show widgets and sets bars according to provided state
        void setVisibleState();
        auto fillNotifications(app::ApplicationDesktop *app) -> bool;
        auto buildNotifications(app::ApplicationDesktop *app) -> bool;
        auto setActiveState(app::ApplicationDesktop *app) -> bool;
        bool processLongPressEvent(const InputEvent &inputEvent);
        bool processShortPressEventOnUnlocked(const InputEvent &inputEvent);
        bool processShortPressEventOnLocked(const InputEvent &inputEvent);