From 2406897d4fbb67d272aa78a5a199c5ec71ce4716 Mon Sep 17 00:00:00 2001 From: Adam Dobrowolski Date: Thu, 30 Jan 2020 11:37:14 +0100 Subject: [PATCH] [EGD-2394] SMS Thread gui implementation * added handle for DB update in application * thread - ready to decorate SMS on different sms types * WORKAROUND - Show thread of messages work - Text cant be set to empty, so add space instead of empty text * AppSMS made stack big enough for sms app lifetime --- .../ApplicationMessages.cpp | 28 +- .../windows/ThreadViewWindow.cpp | 259 +++++++++++++----- .../windows/ThreadViewWindow.hpp | 28 +- 3 files changed, 243 insertions(+), 72 deletions(-) diff --git a/module-apps/application-messages/ApplicationMessages.cpp b/module-apps/application-messages/ApplicationMessages.cpp index 79cd455d3..eb0f23181 100644 --- a/module-apps/application-messages/ApplicationMessages.cpp +++ b/module-apps/application-messages/ApplicationMessages.cpp @@ -18,12 +18,14 @@ #include "windows/ThreadViewWindow.hpp" #include +#include <../module-services/service-db/messages/DBNotificationMessage.hpp> #include namespace app { - ApplicationMessages::ApplicationMessages(std::string name, std::string parent, bool startBackgound) : Application(name, parent, startBackgound, 4096 + 2048) + ApplicationMessages::ApplicationMessages(std::string name, std::string parent, bool startBackgound) : Application(name, parent, startBackgound, 4096 * 2) { + busChannels.push_back(sys::BusChannels::ServiceDBNotifications); } ApplicationMessages::~ApplicationMessages() { @@ -39,10 +41,28 @@ sys::Message_t ApplicationMessages::DataReceivedHandler(sys::DataMessage *msgl, return retMsg; } - // this variable defines whether message was processed. - bool handled = false; + if (msgl->messageType == (uint32_t)MessageType::DBServiceNotification) + { + DBNotificationMessage *msg = dynamic_cast(msgl); + LOG_DEBUG("Received multicast"); + if ((msg->baseType == DB::BaseType::SmsDB) && + ((msg->notificationType == DB::NotificatonType::Updated) || (msg->notificationType == DB::NotificatonType::Added))) + { + if (this->getCurrentWindow() == this->windows[gui::name::window::thread_view]) + { + LOG_DEBUG("TODO"); + this->getCurrentWindow()->rebuild(); + // TODO rebuild fixme + // TODO refresh if it's for interesting thread + } + return std::make_shared(); + } + } - //handle database response + // this variable defines whether message was processed. + bool handled = false; + + //handle database response if (resp != nullptr) { handled = true; uint32_t msgType = resp->responseTo; diff --git a/module-apps/application-messages/windows/ThreadViewWindow.cpp b/module-apps/application-messages/windows/ThreadViewWindow.cpp index 5dae4ce65..2ba2ae121 100644 --- a/module-apps/application-messages/windows/ThreadViewWindow.cpp +++ b/module-apps/application-messages/windows/ThreadViewWindow.cpp @@ -11,7 +11,6 @@ #include "Margins.hpp" #include "service-db/messages/DBMessage.hpp" -#include "service-db/api/DBServiceAPI.hpp" #include @@ -30,6 +29,8 @@ #include #include +#include "service-db/api/DBServiceAPI.hpp" + namespace style { namespace window @@ -44,6 +45,7 @@ namespace style namespace gui { + ThreadViewWindow::ThreadViewWindow(app::Application *app) : AppWindow(app, name::window::thread_view) { AppWindow::buildInterface(); @@ -55,22 +57,57 @@ namespace gui bottomBar->setText(BottomBar::Side::LEFT, utils::localize.get("common_options")); bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get("common_send")); bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get("common_back")); + auto elements_width = this->getWidth() - style::window::default_left_margin * 2; body = new gui::VBox(this, style::window::default_left_margin, title->offset_h(), elements_width, bottomBar->getY() - title->offset_h()); body->setPenWidth(style::window::default_border_no_focus_w); body->setPenFocusWidth(style::window::default_border_no_focus_w); + body->borderCallback = [this](const InputEvent &inputEvent) -> bool { + if (inputEvent.state != InputEvent::State::keyReleasedShort) + { + return false; + } + if (inputEvent.keyCode == KeyCode::KEY_UP) + { + return this->showMessages(Action::Next); + } + else if (inputEvent.keyCode == KeyCode::KEY_DOWN) + { + return this->showMessages(Action::Previous); + } + else + { + return false; + } + }; - // new text, TODO - send it and reload sms - auto text = new gui::Text(nullptr, 0, 0, elements_width, style::window::sms_height, "", gui::Text::ExpandMode::EXPAND_UP); + rebuildText(); + /// setup + body->setReverseOrder(true); + body->setVisible(true); + setFocusItem(body); + } + + // TODO fix text and dont rebuild it... + void ThreadViewWindow::rebuildText() + { + if (text) + { + body->removeWidget(text); + } + text = new gui::Text(nullptr, 0, 0, body->getWidth(), style::window::sms_height, "", gui::Text::ExpandMode::EXPAND_UP); text->setInputMode(new InputMode( {InputMode::ABC, InputMode::abc}, [=](const UTF8 &text) { textModeShowCB(text); }, [=]() { textSelectSpecialCB(); })); text->setPenFocusWidth(style::window::default_border_focucs_w); text->setPenWidth(style::window::default_border_focucs_w); text->setEdges(gui::RectangleEdgeFlags::GUI_RECT_EDGE_BOTTOM); // TODO IFS - text->activatedCallback = [=](gui::Item &item) { - LOG_INFO("Send SMS callback"); - + text->activatedCallback = [&](gui::Item &item) { + if (text->getText().length() == 0) + { + LOG_DEBUG("No text to send in SMS"); + return true; + } SMSRecord record; record.number = title->getText(); record.body = text->getText(); @@ -81,82 +118,171 @@ namespace gui return true; }; - - body->tryAddWidget(text); - /// setup - body->setReverseOrder(true); - body->setVisible(true); - setFocusItem(body); } - void ThreadViewWindow::cleanMessages() + void ThreadViewWindow::cleanView() { - // remove items from list and and set text to no navigation (text is all the time same widget) - auto text = body->children.front(); - body->setFocusItem(text); - text->setNavigationItem(NavigationDirection::UP, nullptr); - text->setNavigationItem(NavigationDirection::DOWN, nullptr); - if (body->children.size() > 1) + body->removeWidget(text); + body->setFocusItem(nullptr); + std::for_each(body->children.begin(), body->children.end(), [&](auto &el) { delete el; }); + body->children.erase(body->children.begin(), body->children.end()); + } + + bool ThreadViewWindow::showMessages(ThreadViewWindow::Action what) + { + if (SMS.thread <= 0) { - std::for_each(std::next(body->children.begin(), 1), body->children.end(), [&](auto &el) { delete el; }); - body->children.erase(std::next(body->children.begin()), body->children.end()); + LOG_ERROR("threadID not set!"); + return false; } + addSMS(what); + return true; } - void ThreadViewWindow::addMessages(uint32_t thread_id) + // TODO TODO TODO + // ez - najpierw wylicz ile sie zmieƛci - potem wstaw + void ThreadViewWindow::addSMS(ThreadViewWindow::Action what) { - auto elements_width = this->getWidth() - style::window::default_left_margin * 2; - LOG_INFO("============================="); - std::unique_ptr> sms = DBServiceAPI::SMSGetLimitOffsetByThreadID(application, 0, 4, thread_id); - LOG_INFO("============================="); - /// dummy sms thread - TODO TODO load from db - on switchData - /// this is not 'static' do it on window focus (on swtich window) - auto labelmeta = gui::meta::Label({0, 0, elements_width, style::window::sms_height}); - labelmeta.edges = RectangleEdgeFlags::GUI_RECT_ALL_EDGES; - labelmeta.radius = style::window::sms_radius; - labelmeta.align = gui::Alignment::ALIGN_VERTICAL_TOP; - labelmeta.focus = style::window::default_border_focucs_w; - labelmeta.no_focus = style::window::sms_border_no_focus; - labelmeta.margins = Margins(0, 2, 0, 2); - // hack - tmp to fix fitting - body->setReverseOrder(false); - for (auto &el : *sms) + LOG_DEBUG("--- %d ---", what); + // if there was text - then remove it temp + gui::Alignment align; + // 1. load elements to tmp vector + SMS.dbsize = DBServiceAPI::SMSGetCount(this->application); + LOG_DEBUG("start: %d end: %d db: %d", SMS.start, SMS.end, SMS.dbsize); + if (what == Action::Start) { - auto label = new gui::Label(nullptr, labelmeta); - label->setText(el.body); - label->activatedCallback = [=](Item &) { - LOG_INFO("Message activated!"); - auto app = dynamic_cast(application); - if (app == nullptr) - { - LOG_ERROR("Something went horribly wrong"); - return false; - } - if (app->windowOptions != nullptr) - { - app->windowOptions->clearOptions(); - /// TODO get record properly... - app->windowOptions->addOptions(smsWindowOptions(app, el)); - app->switchWindow(app->windowOptions->getName(), nullptr); - } - return true; - }; - LOG_INFO("Add sms: %s %s", el.body.c_str(), el.number.c_str()); - if (body->tryAddWidget(label)) {} + SMS.start = 0; + SMS.end = maxsmsinwindow; + rebuildText(); + } + + // TODO 2. check how many of these will fit in box + // update begin / end in `SMS` + if (what == Action::Next) + { + if (SMS.end != SMS.dbsize) + { + SMS.start = SMS.end; + } else { - delete label; - break; + LOG_INFO("All sms shown"); + return; } } - body->setReverseOrder(true); + else if (what == Action::Previous) + { + if (SMS.start == 0) + { + return; + } + else if (SMS.start - maxsmsinwindow < 0) + { + SMS.start = 0; + } + else + { + SMS.start -= maxsmsinwindow; + } + // TODO show text field here! + LOG_DEBUG("---> In progress %d", SMS.start); + } + SMS.sms = DBServiceAPI::SMSGetLimitOffsetByThreadID(this->application, SMS.start, maxsmsinwindow, SMS.thread); + LOG_DEBUG("=> SMS %d < %d < %d", SMS.start, SMS.sms->size(), maxsmsinwindow); + // 3. add them to box + this->cleanView(); + // if we are going from 0 then we want to show text prompt + if (SMS.start == 0) + { + body->addWidget(text); + } + + SMS.end = SMS.start; + for (auto &el : *SMS.sms) + { + LOG_DEBUG("..."); + if (!smsBuild(el, false)) + { + break; + } + ++SMS.end; + } body->setNavigation(); + setFocusItem(body); + if (Action::Previous == what) + { + body->setVisible(true, true); + } + LOG_DEBUG("-------------"); + } + + bool ThreadViewWindow::smsBuild(const SMSRecord &el, bool bottom) + { + /// dummy sms thread - TODO TODO load from db - on switchData + /// this is not 'static' do it on window focus (on swtich window) + // auto label = new gui::Label(nullptr, labelmeta); + auto label = new Text(nullptr, 0, 1, 300, 5); + label->setTextType(Text::TextType::MULTI_LINE); + label->setEditMode(Text::EditMode::SCROLL); + label->setEdges(RectangleEdgeFlags::GUI_RECT_ALL_EDGES); + label->setRadius(5); + label->setFont(style::window::font::medium); + label->setPenFocusWidth(style::window::default_border_focucs_w); + label->setPenWidth(style::window::sms_border_no_focus); + label->expandMode = Text::ExpandMode::EXPAND_DOWN; + label->buildDrawList(); + label->setText(el.body.length() ? el.body : " "); // text doesn't really like being empty + // TODO move to other decorating function depending on what happens (bind left, right, add dashdot etc) + LOG_DEBUG("ADD SMS TYPE: %d", el.type); + switch (el.type) + { + case SMSType::OUTBOX: + default: + label->setMargins(gui::Margins(10, 10, 10, 10)); + } + label->activatedCallback = [=](Item &) { + LOG_INFO("Message activated!"); + auto app = dynamic_cast(application); + if (app == nullptr) + { + LOG_ERROR("Something went horribly wrong"); + return false; + } + if (app->windowOptions != nullptr) + { + app->windowOptions->clearOptions(); + app->windowOptions->addOptions(smsWindowOptions(app, el)); + app->switchWindow(app->windowOptions->getName(), nullptr); + } + return true; + }; + auto rect = new gui::Rect(nullptr, 0, 0, 10, 10); + rect->activeItem = false; + rect->setPenWidth(0); + if (!body->addWidget(rect)) + { + delete rect; + delete label; + return false; + } + + LOG_INFO("Add sms: %s %s", el.body.c_str(), el.number.c_str()); + if (body->addWidget(label)) + { + return true; + } + else + { + delete label; + return false; + } } void ThreadViewWindow::rebuild() { - destroyInterface(); - buildInterface(); + addSMS(ThreadViewWindow::Action::Start); + // destroyInterface(); + // buildInterface(); } void ThreadViewWindow::buildInterface() @@ -181,8 +307,9 @@ namespace gui if (pdata) { LOG_INFO("We have it! %d", pdata->thread->dbID); - cleanMessages(); - addMessages(pdata->thread->dbID); + cleanView(); + SMS.thread = pdata->thread->dbID; + showMessages(Action::Start); auto ret = DBServiceAPI::ContactGetByID(application, pdata->thread->contactID); // should be name number for now - easier to handle setTitle(ret->front().number); diff --git a/module-apps/application-messages/windows/ThreadViewWindow.hpp b/module-apps/application-messages/windows/ThreadViewWindow.hpp index 62fc6ea64..373439017 100644 --- a/module-apps/application-messages/windows/ThreadViewWindow.hpp +++ b/module-apps/application-messages/windows/ThreadViewWindow.hpp @@ -8,6 +8,8 @@ #include "gui/widgets/Image.hpp" #include "gui/widgets/Label.hpp" #include "gui/widgets/Window.hpp" +#include "service-db/api/DBServiceAPI.hpp" +#include #include namespace gui @@ -25,8 +27,27 @@ namespace gui { private: gui::VBox *body = nullptr; - void cleanMessages(); - void addMessages(uint32_t thread_id); + void cleanView(); + enum class Action + { + Start, /// first load of sms + Next, /// load previous sms + Previous /// load next sms + }; + /// return if request was handled + bool showMessages(Action what); + void addSMS(Action what); + bool smsBuild(const SMSRecord &el, bool top); + const ssize_t maxsmsinwindow = 7; + + struct + { + int start = 0; // actual shown position start + int end = 7; // actual shown position end + int thread = 0; // thread we are showing + int dbsize = 0; // size of elements in db + std::unique_ptr> sms = nullptr; // loaded sms from db + } SMS; public: ThreadViewWindow(app::Application *app); @@ -41,6 +62,9 @@ namespace gui void rebuild() override; void buildInterface() override; void destroyInterface() override; + + gui::Text *text = nullptr; + void rebuildText(); }; } /* namespace gui */