M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +8 -12
@@ 1903,18 1903,14 @@ SMSRecord ServiceCellular::createSMSRecord(const UTF8 &decodedMessage,
bool ServiceCellular::dbAddSMSRecord(const SMSRecord &record)
{
- auto query = std::make_unique<db::query::SMSAdd>(record);
- query->setQueryListener(db::QueryCallback::fromFunction([this](auto response) {
- auto result = dynamic_cast<db::query::SMSAddResult *>(response);
- if (result == nullptr || !result->result) {
- return false;
- }
-
- onSMSReceived();
- return true;
- }));
- const auto [succeed, _] = DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::move(query));
- return succeed;
+ return DBServiceAPI::AddSMS(this, record, db::QueryCallback::fromFunction([this](auto response) {
+ auto result = dynamic_cast<db::query::SMSAddResult *>(response);
+ if (result == nullptr || !result->result) {
+ return false;
+ }
+ onSMSReceived();
+ return true;
+ }));
}
void ServiceCellular::onSMSReceived()
M module-services/service-db/DBServiceAPI.cpp => module-services/service-db/DBServiceAPI.cpp +9 -1
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "service-db/DBServiceAPI.hpp"
@@ 363,3 363,11 @@ auto DBServiceAPI::DBBackup(sys::Service *serv, std::string backupPath) -> bool
LOG_ERROR("DBBackup error, return code: %s", c_str(ret.first));
return false;
}
+
+bool DBServiceAPI::AddSMS(sys::Service *serv, const SMSRecord &record, std::unique_ptr<db::QueryListener> &&listener)
+{
+ auto query = std::make_unique<db::query::SMSAdd>(record);
+ query->setQueryListener(std::move(listener));
+ const auto [succeed, _] = DBServiceAPI::GetQuery(serv, db::Interface::Name::SMS, std::move(query));
+ return succeed;
+}
M module-services/service-db/service-db/DBServiceAPI.hpp => module-services/service-db/service-db/DBServiceAPI.hpp +13 -1
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
@@ 14,6 14,8 @@
#include <PhoneNumber.hpp>
#include <Service/Message.hpp>
#include <utf8/UTF8.hpp>
+#include <module-db/queries/messages/sms/QuerySMSAdd.hpp>
+#include <module-db/Interface/SMSRecord.hpp>
#include <cstdint>
#include <memory>
@@ 121,4 123,14 @@ class DBServiceAPI
static auto GetCountryCodeByMCC(sys::Service *serv, uint32_t mcc) -> uint32_t;
static auto DBBackup(sys::Service *serv, std::string backupPath) -> bool;
+
+ /**
+ * @brief Add sms via DBService interface
+ *
+ * @param serv - calling service
+ * @param record - sms record data
+ * @param listener - query listener to obtain and handle query result
+ * @return true if adding sms operation succeed, otherwise false
+ */
+ static bool AddSMS(sys::Service *serv, const SMSRecord &record, std::unique_ptr<db::QueryListener> &&listener);
};
M module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp => module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp +55 -0
@@ 16,6 16,9 @@
#include <service-appmgr/Actions.hpp>
#include <messages/AppMessage.hpp>
+#include <service-db/DBServiceAPI.hpp>
+#include <time/time_conversion.hpp>
+
namespace parserFSM
{
class Context;
@@ 74,6 77,18 @@ auto DeveloperModeHelper::processPutRequest(Context &context) -> sys::ReturnCode
requestSimChange(simSelected);
MessageHandler::putToSendQueue(context.createSimpleResponse());
}
+ else if (body[json::developerMode::smsCommand].is_string()) {
+ if (body[json::developerMode::smsCommand].string_value() == json::developerMode::smsAdd) {
+ SMSType smsType = static_cast<SMSType>(context.getBody()[json::messages::type].int_value());
+ if (smsType == SMSType::DRAFT || smsType == SMSType::QUEUED || smsType == SMSType::FAILED) {
+ return prepareSMS(context);
+ }
+ else {
+ context.setResponseStatus(http::Code::NotAcceptable);
+ MessageHandler::putToSendQueue(context.createSimpleResponse());
+ }
+ }
+ }
else {
context.setResponseStatus(http::Code::BadRequest);
MessageHandler::putToSendQueue(context.createSimpleResponse());
@@ 181,3 196,43 @@ void DeveloperModeHelper::requestSimChange(const int simSelected)
}
CellularServiceAPI::SetSimCard(ownerServicePtr, sim);
}
+
+auto DeveloperModeHelper::smsRecordFromJson(json11::Json msgJson) -> SMSRecord
+{
+ auto record = SMSRecord();
+
+ record.type = static_cast<SMSType>(msgJson[json::messages::type].int_value());
+ record.date = utils::time::getCurrentTimestamp().getTime();
+ utils::PhoneNumber phoneNumber(msgJson[json::messages::phoneNumber].string_value());
+ record.number = phoneNumber.getView();
+ record.body = UTF8(msgJson[json::messages::messageBody].string_value());
+ return record;
+}
+
+auto DeveloperModeHelper::prepareSMS(Context &context) -> sys::ReturnCodes
+{
+ SMSRecord record = smsRecordFromJson(context.getBody());
+
+ LOG_INFO("Adding sms of type %d to database", static_cast<int>(record.type));
+ auto listener = std::make_unique<db::EndpointListener>(
+ [=](db::QueryResult *result, Context context) {
+ bool res = false;
+ if (auto SMSAddResult = dynamic_cast<db::query::SMSAddResult *>(result)) {
+ context.setResponseStatus(SMSAddResult->result ? http::Code::OK : http::Code::InternalServerError);
+ MessageHandler::putToSendQueue(context.createSimpleResponse());
+ LOG_INFO("Adding sms of type %d to database - %s",
+ static_cast<int>(record.type),
+ SMSAddResult->result ? "OK" : "NOK");
+ res = true;
+ }
+ else {
+ context.setResponseStatus(http::Code::InternalServerError);
+ MessageHandler::putToSendQueue(context.createSimpleResponse());
+ }
+ return res;
+ },
+ context);
+
+ DBServiceAPI::AddSMS(ownerServicePtr, record, std::move(listener));
+ return sys::ReturnCodes::Success;
+}
M module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.hpp => module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.hpp +7 -2
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
@@ 10,6 10,7 @@
#include <Service/Service.hpp>
#include <bsp/keyboard/key_codes.hpp>
#include <input/InputEvent.hpp>
+#include <module-db/Interface/SMSRecord.hpp>
namespace sys
{
@@ 26,6 27,8 @@ namespace parserFSM
void sendKeypress(bsp::KeyCodes keyCode, gui::InputEvent::State state);
void requestSimChange(const int simSelected);
+ auto smsRecordFromJson(json11::Json msgJson) -> SMSRecord;
+ auto prepareSMS(Context &context) -> sys::ReturnCodes;
public:
DeveloperModeHelper(sys::Service *_ownerServicePtr) : ownerServicePtr(_ownerServicePtr){};
@@ 46,9 49,11 @@ namespace parserFSM
inline constexpr auto btOn = "on";
inline constexpr auto btCommand = "btCommand";
inline constexpr auto changeSim = "changeSim";
+ inline constexpr auto smsCommand = "smsCommand";
inline constexpr auto getInfo = "getInfo";
-
/// values for getInfo cmd
inline constexpr auto simStateInfo = "simState";
+ /// values for smsCommand
+ inline constexpr auto smsAdd = "smsAdd";
}
} // namespace parserFSM
M test/harness/utils.py => test/harness/utils.py +139 -27
@@ 1,4 1,4 @@
-# Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
import time
@@ 99,6 99,105 @@ keymap = {
"caps": key_codes["*"],
}
+special_chars_keymap = {
+ U'.': "",
+ U',': "d",
+ U'\'': "dd",
+ U'?': "ddd",
+ U'!': "dddd",
+ U'"': "ddddd",
+ U'-': "dddddd",
+ U'(': "s",
+ U')': "sd",
+ U'@': "sdd",
+ U'/': "sddd",
+ U':': "sdddd",
+ U'_': "sddddd",
+ U';': "sdddddd",
+ U'': "ss",
+ U'+': "ssd",
+ U'&': "ssdd",
+ U'%': "ssddd",
+ U'*': "ssdddd",
+ U'<': "ssddddd",
+ U'>': "ssdddddd",
+ U'=': "sss",
+ U'£': "sssd",
+ U'€': "sssdd",
+ U'$': "sssddd",
+ U'[': "sssdddd",
+ U']': "sssddddd",
+ U'{': "sssdddddd",
+ U'}': "ssss",
+ U'\'': "ssssd",
+ U'^': "ssssdd",
+ U'~': "ssssddd",
+ U'`': "ssssdddd",
+ U'į': "ssssddddd",
+ U'§': "ssssdddddd",
+ U'…': "sssss",
+ U'#': "sssssd",
+ U'|': "sssssdd",
+ U'÷': "sssssddd",
+ U'·': "sssssdddd",
+ U'°': "sssssddddd",
+ U'¿': "sssssdddddd",
+ U'¡': "ssssss",
+ U'ą': "ssssssd",
+ U'à': "ssssssdd",
+ U'á': "ssssssddd",
+ U'ä': "ssssssdddd",
+ U'â': "ssssssddddd",
+ U'ć': "ssssssdddddd",
+ U'ç': "sssssss",
+ U'ę': "sssssssd",
+ U'é': "sssssssdd",
+ U'è': "sssssssddd",
+ U'ê': "sssssssdddd",
+ U'ë': "sssssssddddd",
+ U'î': "sssssssdddddd",
+ U'ï': "ssssssss",
+ U'í': "ssssssssd",
+ U'ł': "ssssssssdd",
+ U'ń': "ssssssssddd",
+ U'ñ': "ssssssssdddd",
+ U'ó': "ssssssssddddd",
+ U'ô': "ssssssssdddddd",
+ U'ö': "sssssssss",
+ U'ś': "sssssssssd",
+ U'û': "sssssssssdd",
+ U'ú': "sssssssssddd",
+ U'ù': "sssssssssdddd",
+ U'ü': "sssssssssddddd",
+ U'ÿ': "sssssssssdddddd",
+ U'ż': "ssssssssss",
+ U'ź': "ssssssssssd",
+ U'ß': "ssssssssssdd"
+}
+
+emojis_keymap = {
+ U'😁': "",
+ U'😂': "d",
+ U'😃': "dd",
+ U'😄': "ddd",
+ U'😅': "dddd",
+ U'😆': "ddddd",
+ U'😉': "dddddd",
+ U'😊': "s",
+ U'😋': "sd",
+ U'😌': "sdd",
+ U'😍': "sddd",
+ U'😏': "sdddd",
+ U'😒': "sddddd",
+ U'😓': "sdddddd",
+ U'😔': "ss",
+ U'😖': "ssd",
+ U'😘': "ssdd",
+ U'😚': "ssddd",
+ U'😜': "ssdddd",
+ U'😝': "ssddddd",
+ U'😼': "ssdddddd"
+}
def send_keystoke(keypath, connection):
for key in keypath:
@@ 112,34 211,47 @@ last_char = '\0'
def send_char(char: str, connection):
global last_char
key_type = Keytype.short_press
- if char.isdigit():
- key_type = Keytype.long_press
- if last_char is char:
- print("repeated key!")
+ if char.isascii():
+ if char.isdigit():
+ key_type = Keytype.long_press
+ if last_char is char:
+ print("repeated key!")
+ connection.send_key_code(key_codes["right"])
+ connection.send_key_code(int(char), key_type)
connection.send_key_code(key_codes["right"])
- connection.send_key_code(int(char), key_type)
- connection.send_key_code(key_codes["right"])
- last_char = char
- elif char.islower():
- tmp = char.upper()
- # toggle to lowercase mode
- connection.send_key_code(key_codes["*"], key_type)
- if last_char is keymap[tmp][0]:
- print("repeated key!")
- connection.send_key_code(key_codes["right"], key_type)
- for key in keymap[tmp]:
- connection.send_key_code(int(key), key_type)
- last_char = keymap[tmp][0]
- # toggle to uppercase mode
- connection.send_key_code(key_codes["*"], key_type)
- connection.send_key_code(key_codes["*"], key_type)
+ last_char = char
+ elif char.islower():
+ tmp = char.upper()
+ # toggle to lowercase mode
+ connection.send_key_code(key_codes["*"], key_type)
+ if last_char is keymap[tmp][0]:
+ print("repeated key!")
+ connection.send_key_code(key_codes["right"], key_type)
+ for key in keymap[tmp]:
+ connection.send_key_code(int(key), key_type)
+ last_char = keymap[tmp][0]
+ # toggle to uppercase mode
+ connection.send_key_code(key_codes["*"], key_type)
+ connection.send_key_code(key_codes["*"], key_type)
+ else:
+ if last_char is keymap[char][0]:
+ print("repeated key!")
+ connection.send_key_code(key_codes["right"], key_type)
+ for key in keymap[char]:
+ connection.send_key_code(int(key), key_type)
+ last_char = keymap[char][0]
else:
- if last_char is keymap[char][0]:
- print("repeated key!")
- connection.send_key_code(key_codes["right"], key_type)
- for key in keymap[char]:
- connection.send_key_code(int(key), key_type)
- last_char = keymap[char][0]
+ connection.send_key_code(key_codes["*"], Keytype.long_press)
+ if char in special_chars_keymap:
+ for key in special_chars_keymap[char]:
+ connection.send_key_code(ord(key), key_type)
+ elif char in emojis_keymap:
+ connection.send_key_code(key_codes["fnLeft"], Keytype.short_press)
+ for key in emojis_keymap[char]:
+ connection.send_key_code(ord(key), key_type)
+ else:
+ print("Not supported char {} !!!".format(char))
+ connection.send_key_code(key_codes["enter"], key_type)
def send_number(number: str, connection):
M test/pytest/test_send_message.py => test/pytest/test_send_message.py +77 -13
@@ 1,9 1,10 @@
-# Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
import time
import pytest
from harness.interface.defs import key_codes, SMSType
+from harness.interface.CDCSerial import Keytype
def get_message_by_text(harness, message: str, phone_number: str):
@@ 11,17 12,38 @@ def get_message_by_text(harness, message: str, phone_number: str):
return harness.endpoint_request("messages", "get", body)["body"]
+# default sms type is draft
+def prepare_sms(harness, message: str, phone_number: str, sms_type: int = 1):
+ body = {"smsCommand": "smsAdd", "messageBody": message, "phoneNumber": phone_number, "type": sms_type}
+ return harness.endpoint_request("developerMode", "put", body)
+
+
+def compare_messages(old_messages, new_messages):
+ diff_messages = []
+
+ for message in new_messages:
+ if message not in old_messages:
+ diff_messages.append(message)
+
+ assert len(diff_messages) == 1
+ assert SMSType(diff_messages[0]["type"]) == SMSType.OUTBOX
+
+
+def enter_messages_menu(harness):
+ harness.connection.send_key_code(key_codes["enter"])
+ harness.open_application("messages")
+ if harness.connection.get_window_name() != "ApplicationMessages":
+ time.sleep(2)
+ assert harness.connection.get_window_name() == "ApplicationMessages"
+
+
@pytest.mark.rt1051
@pytest.mark.usefixtures("phone_unlocked")
def test_send_message(harness, phone_number, sms_text):
old_messages = get_message_by_text(harness, sms_text, str(phone_number))
# enter menu
- harness.connection.send_key_code(key_codes["enter"])
- harness.open_application("messages")
- if harness.connection.get_application_name() != "ApplicationMessages":
- time.sleep(2)
- assert harness.connection.get_application_name() == "ApplicationMessages"
+ enter_messages_menu(harness)
# create new message
harness.connection.send_key_code(key_codes["left"])
@@ 35,16 57,58 @@ def test_send_message(harness, phone_number, sms_text):
harness.connection.send_key_code(key_codes["enter"])
# go back to main screen
for _ in range(3):
- time.sleep(1.2) # it take horrendous amount of time to go back to thread view
+ time.sleep(1.2) # it take horrendous amount of time to go back to thread view
harness.connection.send_key_code(key_codes["fnRight"])
+ time.sleep(3)
+
new_messages = get_message_by_text(harness, sms_text, str(phone_number))
- diff_messages = []
+ compare_messages(old_messages, new_messages)
- for message in new_messages:
- if message not in old_messages:
- diff_messages.append(message)
- assert len(diff_messages) == 1
- assert SMSType(diff_messages[0]["type"]) == SMSType.OUTBOX
+@pytest.mark.rt1051
+@pytest.mark.usefixtures("phone_unlocked")
+def test_send_prepared_message(harness, phone_number, sms_text):
+ old_messages = get_message_by_text(harness, sms_text, str(phone_number))
+
+ # prepare sms and send it (add sms to sending queue)
+ prepare_sms(harness, sms_text, str(phone_number), 0x10)
+ # time to send message
+ time.sleep(3)
+ # check whether message was sent
+ new_messages = get_message_by_text(harness, sms_text, str(phone_number))
+ compare_messages(old_messages, new_messages)
+
+
+testdata = [
+ "Ala1Ma śżżń 😚",
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA😚"
+]
+
+
+@pytest.mark.rt1051
+@pytest.mark.usefixtures("phone_unlocked")
+@pytest.mark.parametrize("sms_text", testdata, ids=["special_characters", "long_text"])
+def test_send_prepared_draft_message(harness, phone_number, sms_text):
+ old_messages = get_message_by_text(harness, sms_text, str(phone_number))
+
+ # prepare sms and send it (add sms to sending queue)
+ prepare_sms(harness, sms_text, str(phone_number), SMSType.DRAFT.value)
+
+ # enter menu
+ enter_messages_menu(harness)
+ # enter thread with draft
+ harness.connection.send_key_code(key_codes["enter"])
+ # send
+ harness.connection.send_key_code(key_codes["enter"])
+ # go back to main screen
+ harness.connection.send_key_code(key_codes["fnRight"], Keytype.long_press)
+ # wait for sms to send
+ time.sleep(3)
+
+ # check whether message was sent
+ new_messages = get_message_by_text(harness, sms_text, str(phone_number))
+ compare_messages(old_messages, new_messages)