mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-06-19 05:49:56 -04:00
Use specialized class to to pass phone number between apps and services instead of ambigious std::string. Introduced class (utils::PhoneNumber) wraps calls to libphonenumber providing more convienient interface. However using libphonenumber directly may be resource hungry in terms of stack, heap and cpu usage, so helper class has been introduced to pass information about phone numbers (utils::PhoneNumber::View). It is designed to maintain integrity thus can be created from an instance of utils::PhoneNumber only or moved/copied to. Add new field to the calllog db holding e164 formatted number. Both entered and e164 numbers will be needed to be able to match with contacts correctly. Add constexpr information about country codes (utils::country).
362 lines
15 KiB
C++
362 lines
15 KiB
C++
/*
|
|
* @file DesktopMainWindow.cpp
|
|
* @author Robert Borzecki (robert.borzecki@mudita.com)
|
|
* @date 19 cze 2019
|
|
* @brief
|
|
* @copyright Copyright (C) 2019 mudita.com
|
|
* @details
|
|
*/
|
|
#include <memory>
|
|
#include <functional>
|
|
|
|
#include "../ApplicationDesktop.hpp"
|
|
#include "../data/LockPhoneData.hpp"
|
|
#include "Alignment.hpp"
|
|
#include "Common.hpp"
|
|
#include "DesktopMainWindow.hpp"
|
|
#include "application-calllog/ApplicationCallLog.hpp"
|
|
#include "application-messages/ApplicationMessages.hpp"
|
|
#include "gui/widgets/Image.hpp"
|
|
#include "service-appmgr/ApplicationManager.hpp"
|
|
|
|
#include "application-call/ApplicationCall.hpp"
|
|
#include "application-call/data/CallSwitchData.hpp"
|
|
#include <UiCommonActions.hpp>
|
|
|
|
#include "Common.hpp"
|
|
#include "i18/i18.hpp"
|
|
#include <Span.hpp>
|
|
#include <Style.hpp>
|
|
#include <application-settings/ApplicationSettings.hpp>
|
|
#include <cassert>
|
|
#include <service-appmgr/ApplicationManager.hpp>
|
|
#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;
|
|
}; // namespace style
|
|
|
|
namespace gui
|
|
{
|
|
|
|
void DesktopMainWindow::buildInterface()
|
|
{
|
|
|
|
auto ttime = utils::time::Time();
|
|
AppWindow::buildInterface();
|
|
|
|
bottomBar->setActive(BottomBar::Side::CENTER, true);
|
|
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);
|
|
time->setFilled(false);
|
|
time->setBorderColor(gui::ColorNoColor);
|
|
time->setFont(style::window::font::supersizemelight);
|
|
time->setText(ttime);
|
|
time->setAlignment(Alignment::ALIGN_HORIZONTAL_CENTER);
|
|
|
|
dayText = new gui::Label(this, 0, style::design_day_offset, style::window_width, style::design_day_h);
|
|
dayText->setFilled(false);
|
|
dayText->setBorderColor(gui::ColorNoColor);
|
|
dayText->setFont(style::window::font::biglight);
|
|
dayText->setText(ttime.day() + ", " + ttime.str("%d %b"));
|
|
dayText->setAlignment(Alignment::ALIGN_HORIZONTAL_CENTER);
|
|
|
|
setVisibleState();
|
|
}
|
|
|
|
void DesktopMainWindow::destroyInterface()
|
|
{
|
|
erase();
|
|
}
|
|
|
|
DesktopMainWindow::DesktopMainWindow(app::Application *app) : AppWindow(app, app::window::name::desktop_main_window)
|
|
{
|
|
buildInterface();
|
|
}
|
|
|
|
void DesktopMainWindow::setVisibleState()
|
|
{
|
|
|
|
auto app = dynamic_cast<app::ApplicationDesktop *>(application);
|
|
if (!app) {
|
|
return;
|
|
}
|
|
|
|
if (app->getScreenLocked()) {
|
|
bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get("app_desktop_unlock"));
|
|
topBar->setActive(TopBar::Elements::LOCK, true);
|
|
}
|
|
else {
|
|
auto app = dynamic_cast<app::ApplicationDesktop *>(application);
|
|
assert(app);
|
|
bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get("app_desktop_menu"));
|
|
topBar->setActive(TopBar::Elements::LOCK, false);
|
|
if (!fillNotifications(app)) {
|
|
LOG_ERROR("Couldn't fit in all notifications");
|
|
}
|
|
if (app->need_sim_select && Store::GSM::get()->sim == Store::GSM::SIM::SIM_UNKNOWN) {
|
|
sapm::ApplicationManager::messageSwitchApplication(
|
|
this->application, app::name_settings, app::sim_select, nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DesktopMainWindow::onBeforeShow(ShowMode mode, SwitchData *data)
|
|
{
|
|
|
|
// update time
|
|
time->setText(topBar->getTimeString());
|
|
|
|
// check if there was a signal to lock the pone due to inactivity.
|
|
if ((data != nullptr) && (data->getDescription() == "LockPhoneData")) {
|
|
auto app = dynamic_cast<app::ApplicationDesktop *>(application);
|
|
if (!app) {
|
|
return;
|
|
}
|
|
app->setScreenLocked(true);
|
|
|
|
LockPhoneData *lockData = reinterpret_cast<LockPhoneData *>(data);
|
|
lockTimeoutApplilcation = lockData->getPreviousApplication();
|
|
|
|
reinterpret_cast<app::ApplicationDesktop *>(application)->setSuspendFlag(true);
|
|
}
|
|
|
|
setVisibleState();
|
|
}
|
|
|
|
bool DesktopMainWindow::onInput(const InputEvent &inputEvent)
|
|
{
|
|
|
|
// do nothing
|
|
if (inputEvent.state == InputEvent::State::keyReleasedShort && inputEvent.keyCode == KeyCode::KEY_RF)
|
|
return false;
|
|
|
|
app::ApplicationDesktop *app = dynamic_cast<app::ApplicationDesktop *>(application);
|
|
if (app == nullptr) {
|
|
LOG_ERROR("not ApplicationDesktop");
|
|
return AppWindow::onInput(inputEvent);
|
|
}
|
|
|
|
auto code = translator.handle(inputEvent.key, InputMode({InputMode::phone}).get());
|
|
|
|
// process shortpress
|
|
if (inputEvent.state == InputEvent::State::keyReleasedShort) {
|
|
|
|
if (app->getScreenLocked()) {
|
|
// if enter was pressed
|
|
if (inputEvent.keyCode == KeyCode::KEY_ENTER) {
|
|
unlockStartTime = xTaskGetTickCount();
|
|
enterPressed = true;
|
|
}
|
|
else if ((inputEvent.keyCode == KeyCode::KEY_PND) && enterPressed) {
|
|
// if interval between enter and pnd keys is less than time defined for unlocking
|
|
if (xTaskGetTickCount() - unlockStartTime < unclockTime) {
|
|
// display pin lock screen or simply refresh current window to update labels
|
|
if (app->getPinLocked())
|
|
// if there was no application on to before closing proceed normally to pin protection
|
|
// window.
|
|
if (lockTimeoutApplilcation.empty()) {
|
|
application->switchWindow("PinLockWindow");
|
|
}
|
|
else {
|
|
std::unique_ptr<LockPhoneData> data = std::make_unique<LockPhoneData>();
|
|
data->setPrevApplication(lockTimeoutApplilcation);
|
|
lockTimeoutApplilcation = "";
|
|
application->switchWindow("PinLockWindow", std::move(data));
|
|
}
|
|
|
|
else {
|
|
|
|
// if phone was locked by user show unlocked main window
|
|
if (lockTimeoutApplilcation.empty()) {
|
|
app->setScreenLocked(false);
|
|
setVisibleState();
|
|
application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
|
|
}
|
|
// if there was application on top when timeout occurred
|
|
else {
|
|
lockTimeoutApplilcation = "";
|
|
sapm::ApplicationManager::messageSwitchPreviousApplication(application);
|
|
}
|
|
}
|
|
}
|
|
enterPressed = false;
|
|
}
|
|
else {
|
|
enterPressed = false;
|
|
}
|
|
}
|
|
// screen is unlocked
|
|
else {
|
|
// pressing enter moves user to menu screen
|
|
if (inputEvent.keyCode == KeyCode::KEY_ENTER) {
|
|
application->switchWindow("MenuWindow");
|
|
}
|
|
// if numeric key was pressed record that key and send it to call application
|
|
else if (code != 0) {
|
|
return app::prepare_call(application, std::string(1, static_cast<char>(code)));
|
|
}
|
|
}
|
|
}
|
|
else if (inputEvent.state == InputEvent::State::keyReleasedLong) {
|
|
// long press of # locks screen if it was unlocked
|
|
if ((inputEvent.keyCode == KeyCode::KEY_PND) && (app->getScreenLocked() == false)) {
|
|
app->setScreenLocked(true);
|
|
setVisibleState();
|
|
app->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
|
|
app->setSuspendFlag(true);
|
|
return true;
|
|
}
|
|
// long press of right function button move user to power off window
|
|
else if (inputEvent.keyCode == KeyCode::KEY_RF) {
|
|
application->switchWindow("PowerOffWindow");
|
|
return true;
|
|
}
|
|
// long press of '0' key is translated to '+'
|
|
else if (inputEvent.keyCode == KeyCode::KEY_0) {
|
|
return app::prepare_call(application, std::to_string('+'));
|
|
}
|
|
}
|
|
|
|
// check if any of the lower inheritance onInput methods catch the event
|
|
return AppWindow::onInput(inputEvent);
|
|
}
|
|
|
|
void DesktopMainWindow::rebuild()
|
|
{
|
|
destroyInterface();
|
|
buildInterface();
|
|
}
|
|
|
|
bool DesktopMainWindow::updateTime(const UTF8 &timeStr)
|
|
{
|
|
auto ret = AppWindow::updateTime(timeStr);
|
|
time->setText(topBar->getTimeString());
|
|
return ret;
|
|
}
|
|
bool DesktopMainWindow::updateTime(const uint32_t ×tamp, bool mode24H)
|
|
{
|
|
auto ret = AppWindow::updateTime(timestamp, mode24H);
|
|
time->setText(topBar->getTimeString());
|
|
return ret;
|
|
}
|
|
|
|
std::list<DrawCommand *> DesktopMainWindow::buildDrawList()
|
|
{
|
|
time->setText(topBar->getTimeString());
|
|
return gui::AppWindow::buildDrawList();
|
|
}
|
|
|
|
auto add_box_icon(gui::BoxLayout *layout, UTF8 icon)
|
|
{
|
|
auto thumbnail = new gui::Image(icon);
|
|
center(layout, thumbnail, Axis::Y);
|
|
thumbnail->activeItem = false;
|
|
layout->addWidget(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()> callback)
|
|
-> bool
|
|
{
|
|
const auto text_normal_size = 200;
|
|
const auto size_needed_for_2digits = 30;
|
|
// 1. create hbox for all elements
|
|
auto el = new gui::HBox(nullptr, 0, 0, layout->getWidth(), style::window::label::default_h);
|
|
auto text = new gui::Label(nullptr, 0, 0, text_normal_size, style::window::label::default_h, "");
|
|
text->area(Item::Area::Max) = text->area(Item::Area::Normal); // set area from 0 to normal
|
|
text->area(Item::Area::Max).size(Axis::X) = el->area().w; // set max area for text in axis to max
|
|
text->setText(name);
|
|
text->setFont(style::window::font::medium);
|
|
text->setAlignment(Alignment::ALIGN_VERTICAL_CENTER);
|
|
text->setPenWidth(style::window::default_border_no_focus_w);
|
|
text->activeItem = false;
|
|
|
|
auto number = new gui::Label();
|
|
number->setText(indicator);
|
|
number->setFont(style::window::font::mediumbold);
|
|
number->setPenWidth(style::window::default_border_no_focus_w);
|
|
number->setSize(size_needed_for_2digits, el->area().h);
|
|
number->setAlignment(Alignment::ALIGN_VERTICAL_CENTER | Alignment::ALIGN_HORIZONTAL_RIGHT);
|
|
number->activeItem = false;
|
|
// 2. Add all elements to hbox layout
|
|
new gui::Span(el, Axis::X, style::design_border_offset);
|
|
add_box_icon(el, icon);
|
|
new gui::Span(el, Axis::X, style::design_border_offset);
|
|
el->addWidget(text);
|
|
add_box_icon(el, "dot_12px_soft");
|
|
el->addWidget(number);
|
|
// box right inner margin
|
|
new gui::Span(el, Axis::X, style::design_border_offset);
|
|
// 3. Set hbox layout properties
|
|
el->setPenWidth(style::window::default_border_no_focus_w);
|
|
el->setPenFocusWidth(style::window::default_border_focucs_w);
|
|
el->setEdges(RectangleEdgeFlags::GUI_RECT_EDGE_BOTTOM | RectangleEdgeFlags::GUI_RECT_EDGE_TOP);
|
|
el->inputCallback = [callback](Item &, const InputEvent &event) {
|
|
if (event.state == InputEvent::State::keyReleasedShort && event.keyCode == KeyCode::KEY_LF) {
|
|
return callback();
|
|
}
|
|
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
|
|
{
|
|
erase(notifications);
|
|
// 1. create notifications box
|
|
notifications = new gui::VBox(nullptr,
|
|
style::window::default_left_margin,
|
|
style::design_notifications_offset,
|
|
style::window_width - 2 * style::window::default_left_margin,
|
|
bottomBar->widgetArea.pos(Axis::Y) - style::design_notifications_offset);
|
|
notifications->setPenWidth(style::window::default_border_no_focus_w);
|
|
notifications->setPenFocusWidth(style::window::default_border_no_focus_w);
|
|
this->addWidget(notifications);
|
|
if (!notifications->visible) {
|
|
LOG_ERROR("Can't fit notifications box!");
|
|
return false;
|
|
}
|
|
|
|
// 2. actually fill it in
|
|
if (app->notifications.notSeenCalls) {
|
|
add_notification(notifications,
|
|
"phone",
|
|
utils::localize.get("app_desktop_missed_calls"),
|
|
std::to_string(app->notifications.notSeenCalls),
|
|
[this]() {
|
|
return sapm::ApplicationManager::messageSwitchApplication(
|
|
application, app::CallLogAppStr, gui::name::window::main_window, nullptr);
|
|
});
|
|
}
|
|
if (app->notifications.notSeenSMS) {
|
|
add_notification(notifications,
|
|
"mail",
|
|
utils::localize.get("app_desktop_unread_messages"),
|
|
std::to_string(app->notifications.notSeenSMS),
|
|
[this]() {
|
|
return sapm::ApplicationManager::messageSwitchApplication(
|
|
application, app::name_messages, gui::name::window::main_window, nullptr);
|
|
});
|
|
}
|
|
setFocusItem(notifications);
|
|
return true;
|
|
}
|
|
|
|
} /* namespace gui */
|