Skip to content

Commit a91ca2a

Browse files
authored
DesktopMainWindow notifications update (#886)
1 parent 1a31caf commit a91ca2a

File tree

9 files changed

+362
-153
lines changed

9 files changed

+362
-153
lines changed

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
* `[appmgr]` Application manager refactored.
1212

1313
### Fixed
14+
15+
* `[desktop][messages]` Fixed notifications display and navigation
1416
* `[cellular]` Fixed 32 bit UCS2 codes handling.
1517

1618
### Other

image/assets/lang/lang_en.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,8 @@
170170
"app_desktop_sim_blocked_info1": "Sorry, SIM card permanently blocked.",
171171
"app_desktop_sim_blocked_info2": "Please contact your service provider",
172172

173-
"app_desktop_unread_messages": "Unread messages",
174-
"app_desktop_missed_calls": "Missed calls",
173+
"app_desktop_unread_messages": "<text>Unread <b>messages</b></text>",
174+
"app_desktop_missed_calls": "<text>Missed <b>calls</b></text>",
175175
"app_desktop_menu_phone": "PHONE",
176176
"app_desktop_menu_contacts": "CONTACTS",
177177
"app_desktop_menu_messages": "MESSAGES",
@@ -194,6 +194,7 @@
194194
"app_desktop_show": "SHOW",
195195
"app_desktop_calls": "CALLS",
196196
"app_desktop_clear": "CLEAR",
197+
"app_desktop_clear_all": "CLEAR ALL",
197198

198199
"app_call_call": "CALL",
199200
"app_call_clear": "CLEAR",

module-apps/application-desktop/ApplicationDesktop.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,18 @@ namespace app
9797
{
9898
assert(msg);
9999
auto records = *msg->getResult();
100+
101+
bool rebuildMainWindow = false;
102+
100103
for (auto record : records) {
101104
switch (record.key) {
102105
case NotificationsRecord::Key::Calls:
106+
rebuildMainWindow |= record.value != notifications.notSeen.Calls;
103107
notifications.notSeen.Calls = record.value;
104108
break;
105109

106110
case NotificationsRecord::Key::Sms:
111+
rebuildMainWindow |= record.value != notifications.notSeen.SMS;
107112
notifications.notSeen.SMS = record.value;
108113
break;
109114

@@ -114,9 +119,11 @@ namespace app
114119
}
115120
}
116121

117-
/// TODO if current window is this one or was on stack -> requild it
118-
/// windowsFactory.build(this, app::window::name::desktop_menu);
119-
/// windowsFactory.build(this, app::window::name::desktop_main_window);
122+
auto ptr = getCurrentWindow();
123+
if (rebuildMainWindow && ptr->getName() == app::window::name::desktop_main_window) {
124+
ptr->rebuild();
125+
}
126+
120127
return true;
121128
}
122129

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

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

module-apps/application-desktop/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ target_sources( ${PROJECT_NAME}
1717
"${CMAKE_CURRENT_LIST_DIR}/ApplicationDesktop.cpp"
1818
"${CMAKE_CURRENT_LIST_DIR}/widgets/PinLock.cpp"
1919
"${CMAKE_CURRENT_LIST_DIR}/widgets/PinLockHandler.cpp"
20+
"${CMAKE_CURRENT_LIST_DIR}/widgets/NotificationsBox.cpp"
2021
"${CMAKE_CURRENT_LIST_DIR}/windows/DesktopMainWindow.cpp"
2122
"${CMAKE_CURRENT_LIST_DIR}/windows/PinLockBaseWindow.cpp"
2223
"${CMAKE_CURRENT_LIST_DIR}/windows/ScreenLockBox.cpp"
@@ -31,9 +32,11 @@ target_sources( ${PROJECT_NAME}
3132
PUBLIC
3233
"${CMAKE_CURRENT_LIST_DIR}/ApplicationDesktop.hpp"
3334
"${CMAKE_CURRENT_LIST_DIR}/data/LockPhoneData.hpp"
35+
"${CMAKE_CURRENT_LIST_DIR}/data/Style.hpp"
3436
"${CMAKE_CURRENT_LIST_DIR}/widgets/PinHash.hpp"
3537
"${CMAKE_CURRENT_LIST_DIR}/widgets/PinLock.hpp"
3638
"${CMAKE_CURRENT_LIST_DIR}/widgets/PinLockHandler.hpp"
39+
"${CMAKE_CURRENT_LIST_DIR}/widgets/NotificationsBox.hpp"
3740
"${CMAKE_CURRENT_LIST_DIR}/windows/DesktopMainWindow.hpp"
3841
"${CMAKE_CURRENT_LIST_DIR}/windows/PinLockBaseWindow.hpp"
3942
"${CMAKE_CURRENT_LIST_DIR}/windows/PinLockBox.hpp"
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
2+
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
3+
4+
#pragma once
5+
6+
namespace style::desktop
7+
{
8+
9+
namespace notifications
10+
{
11+
constexpr auto SpanSize = 8;
12+
constexpr auto DigitSize = 14;
13+
constexpr auto IconWidth = 35;
14+
constexpr auto TextMaxWidth = 250;
15+
16+
constexpr auto X = 0;
17+
constexpr auto Y = 284;
18+
constexpr auto Width = style::window_width;
19+
20+
}; // namespace notifications
21+
22+
namespace timeLabel
23+
{
24+
constexpr auto X = 0;
25+
constexpr auto Y = 106;
26+
constexpr auto Width = style::window_width;
27+
constexpr auto Hight = 96;
28+
29+
} // namespace timeLabel
30+
31+
namespace dayLabel
32+
{
33+
constexpr auto X = 0;
34+
constexpr auto Y = 204;
35+
constexpr auto Width = style::window_width;
36+
constexpr auto Hight = 51;
37+
38+
} // namespace dayLabel
39+
40+
} // namespace style::desktop
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
2+
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
3+
4+
#include "NotificationsBox.hpp"
5+
6+
#include "application-desktop/data/Style.hpp"
7+
8+
#include "TextFixedSize.hpp"
9+
#include "RichTextParser.hpp"
10+
#include "FontManager.hpp"
11+
12+
using namespace gui;
13+
using namespace style::desktop;
14+
15+
NotificationsBox::NotificationsBox(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
16+
: VBox(parent, x, y, w, h)
17+
{
18+
this->setAlignment(Alignment(gui::Alignment::Horizontal::Center));
19+
this->setPenWidth(style::window::default_border_no_focus_w);
20+
this->setPenFocusWidth(style::window::default_border_no_focus_w);
21+
22+
auto getNextNotification = [this]() -> Item * {
23+
auto focusedItem = getFocusItem();
24+
if (focusedItem == nullptr) {
25+
return nullptr;
26+
}
27+
auto nextItem = focusedItem->getNavigationItem(NavigationDirection::UP);
28+
29+
if (nextItem == nullptr) {
30+
nextItem = focusedItem->getNavigationItem(NavigationDirection::DOWN);
31+
}
32+
return nextItem;
33+
};
34+
35+
auto setNextFocusedItemAfterErase = [this](Item *item) -> bool {
36+
if (item == nullptr) {
37+
setFocus(false);
38+
}
39+
else {
40+
item->clearNavigationItem(NavigationDirection::DOWN);
41+
item->clearNavigationItem(NavigationDirection::UP);
42+
setNavigation();
43+
setFocusItem(item);
44+
}
45+
return true;
46+
};
47+
48+
inputCallback = [this, setNextFocusedItemAfterErase, getNextNotification](Item &, const InputEvent &event) -> bool {
49+
if (event.isShortPress() && event.is(KeyCode::KEY_RF)) {
50+
LOG_DEBUG("Removing single notification");
51+
auto ptr = getFocusItem();
52+
if (ptr == nullptr || focusItem == this) {
53+
return false;
54+
}
55+
auto nextItem = getNextNotification();
56+
ptr->inputCallback(*this, event);
57+
erase(ptr);
58+
return setNextFocusedItemAfterErase(nextItem);
59+
}
60+
return false;
61+
};
62+
}
63+
64+
namespace
65+
{
66+
auto buildImageInactive(const UTF8 &img) -> gui::Image *
67+
{
68+
auto thumbnail = new gui::Image(img);
69+
thumbnail->activeItem = false;
70+
return thumbnail;
71+
}
72+
73+
auto buildNotificationIcon(UTF8 icon) -> gui::Image *
74+
{
75+
auto thumbnail = buildImageInactive(icon);
76+
thumbnail->setMinimumWidth(notifications::IconWidth);
77+
thumbnail->setAlignment(Alignment(gui::Alignment::Horizontal::Right, gui::Alignment::Vertical::Center));
78+
thumbnail->setMargins(
79+
gui::Margins(style::window::default_left_margin, 0, style::window::default_right_margin, 0));
80+
return thumbnail;
81+
}
82+
83+
auto buildNotificationNameLabel(const UTF8 &name, uint32_t width) -> gui::TextFixedSize *
84+
{
85+
auto text = new gui::TextFixedSize(
86+
nullptr, 0, 0, style::desktop::notifications::TextMaxWidth, style::window::label::default_h);
87+
text->setMaximumSize(width, Axis::X);
88+
89+
TextFormat format(FontManager::getInstance().getFont(style::window::font::medium));
90+
text::RichTextParser rtParser;
91+
auto parsedText = rtParser.parse(name, &format);
92+
93+
text->setText(std::move(parsedText));
94+
text->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
95+
text->setPenWidth(style::window::default_border_no_focus_w);
96+
text->setUnderline(false);
97+
text->activeItem = false;
98+
return text;
99+
}
100+
101+
constexpr auto maxNotificationValue = "99+";
102+
103+
auto buildNotificationCountText(const UTF8 &indicator) -> gui::Text *
104+
{
105+
auto number = new gui::Text();
106+
if (indicator.length() > 2) {
107+
number->setText(maxNotificationValue);
108+
number->setMinimumWidth(strlen(maxNotificationValue) * notifications::DigitSize);
109+
}
110+
else {
111+
number->setText(indicator);
112+
number->setMinimumWidth(indicator.length() * notifications::DigitSize);
113+
}
114+
number->setMinimumWidth(indicator.length() * notifications::DigitSize);
115+
number->setFont(style::window::font::mediumbold);
116+
number->setPenWidth(style::window::default_border_no_focus_w);
117+
number->setMargins(gui::Margins(0, 0, style::window::default_right_margin, 0));
118+
number->setAlignment(Alignment(gui::Alignment::Horizontal::Right, gui::Alignment::Vertical::Center));
119+
number->activeItem = false;
120+
return number;
121+
}
122+
123+
} // namespace
124+
125+
auto NotificationsBox::addNotification(UTF8 icon,
126+
UTF8 name,
127+
UTF8 indicator,
128+
std::function<bool()> showCallback,
129+
std::function<bool()> clearCallback,
130+
std::function<void(bool)> onFocus) -> bool
131+
{
132+
// 1. create hbox for all elements
133+
auto el = new gui::HBox(nullptr, 0, 0, style::window::default_body_width, style::window::label::default_h);
134+
135+
// 2. Add all elements to hbox layout
136+
el->addWidget(buildNotificationIcon(icon));
137+
138+
el->addWidget(buildNotificationNameLabel(name, el->area().w));
139+
el->addWidget(buildImageInactive("dot_12px_hard_alpha_W_G"));
140+
el->addWidget(buildNotificationCountText(indicator));
141+
142+
// 3. Set hbox layout properties
143+
el->setAlignment(Alignment(gui::Alignment::Vertical::Center));
144+
el->setMargins(gui::Margins(0, 0, 0, 10));
145+
el->setPenWidth(style::window::default_border_no_focus_w);
146+
el->setPenFocusWidth(style::window::default_border_focus_w);
147+
el->setEdges(RectangleEdge::Bottom | RectangleEdge::Top);
148+
149+
el->focusChangedCallback = [el, onFocus](Item &) -> bool {
150+
onFocus(el->focus);
151+
return true;
152+
};
153+
el->activatedCallback = [showCallback](Item &) { return showCallback(); };
154+
el->inputCallback = [showCallback, clearCallback, this](Item &, const InputEvent &event) -> bool {
155+
if (event.isShortPress() && event.is(KeyCode::KEY_RF) && clearCallback) {
156+
return clearCallback();
157+
}
158+
return false;
159+
};
160+
161+
this->addWidget(el);
162+
return el->visible;
163+
}
164+
165+
bool NotificationsBox::onInput(const InputEvent &inputEvent)
166+
{
167+
168+
if (inputEvent.isShortPress() && (inputEvent.is(KeyCode::KEY_UP) || inputEvent.is(KeyCode::KEY_DOWN))) {
169+
auto handled = handleNavigation(inputEvent);
170+
if (!handled) {
171+
setFocus(false);
172+
}
173+
return true;
174+
}
175+
return VBox::onInput(inputEvent);
176+
}
177+
178+
bool NotificationsBox::clearAll(const InputEvent &event)
179+
{
180+
while (!children.empty()) {
181+
inputCallback(*this, event);
182+
}
183+
return true;
184+
}
185+
186+
void NotificationsBox::navigateToBottom()
187+
{
188+
setFocusItem(children.back());
189+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
2+
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
3+
4+
#pragma once
5+
6+
#include "BoxLayout.hpp"
7+
#include "Icon.hpp"
8+
9+
namespace gui
10+
{
11+
class NotificationsBox : public VBox
12+
{
13+
public:
14+
NotificationsBox(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
15+
16+
auto addNotification(UTF8 icon,
17+
UTF8 name,
18+
UTF8 indicator,
19+
std::function<bool()> showCallback,
20+
std::function<bool()> clearCallback,
21+
std::function<void(bool)> onFocus) -> bool;
22+
23+
bool onInput(const InputEvent &inputEvent) override;
24+
bool clearAll(const InputEvent &event);
25+
26+
void navigateToBottom();
27+
};
28+
} // namespace gui

0 commit comments

Comments
 (0)