2022-01-04 18:42:28 +00:00
|
|
|
#include "configuration.h"
|
2022-07-31 12:11:47 +00:00
|
|
|
#if HAS_SCREEN
|
2022-02-27 08:18:35 +00:00
|
|
|
#include "CannedMessageModule.h"
|
2022-02-21 05:29:15 +00:00
|
|
|
#include "FSCommon.h"
|
2022-05-07 10:31:21 +00:00
|
|
|
#include "MeshService.h"
|
|
|
|
#include "PowerFSM.h" // neede for button bypass
|
2022-02-21 05:29:15 +00:00
|
|
|
#include "mesh/generated/cannedmessages.pb.h"
|
2022-01-04 18:42:28 +00:00
|
|
|
|
2022-09-20 14:30:21 +00:00
|
|
|
#ifdef OLED_RU
|
2022-09-20 14:36:50 +00:00
|
|
|
#include "graphics/fonts/OLEDDisplayFontsRU.h"
|
2022-09-20 14:30:21 +00:00
|
|
|
#endif
|
|
|
|
|
2022-09-20 14:21:32 +00:00
|
|
|
#if defined(USE_EINK) || defined(ILI9341_DRIVER)
|
|
|
|
// The screen is bigger so use bigger fonts
|
|
|
|
#define FONT_SMALL ArialMT_Plain_16
|
|
|
|
#define FONT_MEDIUM ArialMT_Plain_24
|
|
|
|
#define FONT_LARGE ArialMT_Plain_24
|
|
|
|
#else
|
|
|
|
#ifdef OLED_RU
|
|
|
|
#define FONT_SMALL ArialMT_Plain_10_RU
|
|
|
|
#else
|
2022-01-13 07:08:16 +00:00
|
|
|
#define FONT_SMALL ArialMT_Plain_10
|
2022-09-20 14:21:32 +00:00
|
|
|
#endif
|
2022-01-13 07:08:16 +00:00
|
|
|
#define FONT_MEDIUM ArialMT_Plain_16
|
|
|
|
#define FONT_LARGE ArialMT_Plain_24
|
2022-09-20 14:21:32 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#define fontHeight(font) ((font)[1] + 1) // height is position 1
|
|
|
|
|
|
|
|
#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
|
|
|
|
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
|
|
|
|
#define FONT_HEIGHT_LARGE fontHeight(FONT_LARGE)
|
2022-01-13 07:08:16 +00:00
|
|
|
|
2022-01-13 11:51:36 +00:00
|
|
|
// Remove Canned message screen if no action is taken for some milliseconds
|
|
|
|
#define INACTIVATE_AFTER_MS 20000
|
2022-01-13 07:08:16 +00:00
|
|
|
|
2022-09-21 08:42:10 +00:00
|
|
|
extern uint8_t cardkb_found;
|
|
|
|
|
2022-02-21 05:29:15 +00:00
|
|
|
static const char *cannedMessagesConfigFile = "/prefs/cannedConf.proto";
|
|
|
|
|
2022-02-27 09:09:36 +00:00
|
|
|
CannedMessageModuleConfig cannedMessageModuleConfig;
|
2022-02-21 05:29:15 +00:00
|
|
|
|
2022-02-27 09:20:23 +00:00
|
|
|
CannedMessageModule *cannedMessageModule;
|
2022-01-04 18:42:28 +00:00
|
|
|
|
2022-02-21 05:29:15 +00:00
|
|
|
// TODO: move it into NodeDB.h!
|
|
|
|
extern bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct);
|
2022-05-07 10:31:21 +00:00
|
|
|
extern bool saveProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields,
|
|
|
|
const void *dest_struct);
|
2022-02-21 05:29:15 +00:00
|
|
|
|
2022-02-27 09:20:23 +00:00
|
|
|
CannedMessageModule::CannedMessageModule()
|
2022-05-07 10:31:21 +00:00
|
|
|
: SinglePortModule("canned", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("CannedMessageModule")
|
2022-01-04 18:42:28 +00:00
|
|
|
{
|
2022-05-22 11:27:56 +00:00
|
|
|
if (moduleConfig.canned_message.enabled) {
|
2022-02-27 10:21:02 +00:00
|
|
|
this->loadProtoForModule();
|
2022-09-21 08:42:10 +00:00
|
|
|
if ((this->splitConfiguredMessages() <= 0) && (cardkb_found != CARDKB_ADDR)) {
|
2022-02-27 10:21:02 +00:00
|
|
|
DEBUG_MSG("CannedMessageModule: No messages are configured. Module is disabled\n");
|
2022-02-21 05:29:15 +00:00
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_DISABLED;
|
2022-05-07 10:31:21 +00:00
|
|
|
} else {
|
2022-02-27 09:20:23 +00:00
|
|
|
DEBUG_MSG("CannedMessageModule is enabled\n");
|
2022-02-21 05:29:15 +00:00
|
|
|
this->inputObserver.observe(inputBroker);
|
2022-01-12 08:26:42 +00:00
|
|
|
}
|
2022-01-11 15:02:55 +00:00
|
|
|
}
|
2022-01-04 18:42:28 +00:00
|
|
|
}
|
|
|
|
|
2022-01-12 08:26:42 +00:00
|
|
|
/**
|
|
|
|
* @brief Items in array this->messages will be set to be pointing on the right
|
2022-01-18 22:15:54 +00:00
|
|
|
* starting points of the string this->messageStore
|
2022-02-21 05:29:15 +00:00
|
|
|
*
|
2022-01-12 08:26:42 +00:00
|
|
|
* @return int Returns the number of messages found.
|
|
|
|
*/
|
2022-08-16 02:06:55 +00:00
|
|
|
// FIXME: This is just one set of messages now
|
2022-02-27 09:20:23 +00:00
|
|
|
int CannedMessageModule::splitConfiguredMessages()
|
2022-01-12 08:26:42 +00:00
|
|
|
{
|
|
|
|
int messageIndex = 0;
|
|
|
|
int i = 0;
|
2022-01-18 22:15:54 +00:00
|
|
|
|
2022-02-21 05:29:15 +00:00
|
|
|
// collect all the message parts
|
2022-08-16 02:06:55 +00:00
|
|
|
strcpy(this->messageStore, cannedMessageModuleConfig.messages);
|
2022-01-18 22:15:54 +00:00
|
|
|
|
2022-02-21 05:29:15 +00:00
|
|
|
// The first message points to the beginning of the store.
|
2022-05-07 10:31:21 +00:00
|
|
|
this->messages[messageIndex++] = this->messageStore;
|
|
|
|
int upTo = strlen(this->messageStore) - 1;
|
|
|
|
|
|
|
|
while (i < upTo) {
|
|
|
|
if (this->messageStore[i] == '|') {
|
2022-01-12 08:26:42 +00:00
|
|
|
// Message ending found, replace it with string-end character.
|
2022-01-18 22:15:54 +00:00
|
|
|
this->messageStore[i] = '\0';
|
2022-05-07 10:31:21 +00:00
|
|
|
DEBUG_MSG("CannedMessage %d is: '%s'\n", messageIndex - 1, this->messages[messageIndex - 1]);
|
2022-01-12 08:26:42 +00:00
|
|
|
|
2022-02-21 05:29:15 +00:00
|
|
|
// hit our max messages, bail
|
2022-05-07 10:31:21 +00:00
|
|
|
if (messageIndex >= CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT) {
|
2022-01-12 08:26:42 +00:00
|
|
|
this->messagesCount = messageIndex;
|
|
|
|
return this->messagesCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next message starts after pipe (|) just found.
|
2022-05-07 10:31:21 +00:00
|
|
|
this->messages[messageIndex++] = (this->messageStore + i + 1);
|
2022-01-12 08:26:42 +00:00
|
|
|
}
|
|
|
|
i += 1;
|
|
|
|
}
|
2022-05-07 10:31:21 +00:00
|
|
|
if (strlen(this->messages[messageIndex - 1]) > 0) {
|
2022-02-21 05:29:15 +00:00
|
|
|
// We have a last message.
|
2022-05-07 10:31:21 +00:00
|
|
|
DEBUG_MSG("CannedMessage %d is: '%s'\n", messageIndex - 1, this->messages[messageIndex - 1]);
|
2022-01-12 08:26:42 +00:00
|
|
|
this->messagesCount = messageIndex;
|
2022-05-07 10:31:21 +00:00
|
|
|
} else {
|
|
|
|
this->messagesCount = messageIndex - 1;
|
2022-01-12 08:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return this->messagesCount;
|
|
|
|
}
|
|
|
|
|
2022-02-27 09:20:23 +00:00
|
|
|
int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
2022-01-04 18:42:28 +00:00
|
|
|
{
|
2022-05-22 11:27:56 +00:00
|
|
|
if ((strlen(moduleConfig.canned_message.allow_input_source) > 0) &&
|
|
|
|
(strcmp(moduleConfig.canned_message.allow_input_source, event->source) != 0) &&
|
|
|
|
(strcmp(moduleConfig.canned_message.allow_input_source, "_any") != 0)) {
|
2022-01-13 13:06:10 +00:00
|
|
|
// Event source is not accepted.
|
2022-02-21 05:29:15 +00:00
|
|
|
// Event only accepted if source matches the configured one, or
|
|
|
|
// the configured one is "_any" (or if there is no configured
|
|
|
|
// source at all)
|
2022-01-11 15:02:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2022-01-12 08:26:42 +00:00
|
|
|
|
2022-01-09 09:08:31 +00:00
|
|
|
bool validEvent = false;
|
2022-09-09 10:51:41 +00:00
|
|
|
if (event->inputEvent == static_cast<char>(ModuleConfig_CannedMessageConfig_InputEventChar_UP)) {
|
2022-01-09 20:14:23 +00:00
|
|
|
DEBUG_MSG("Canned message event UP\n");
|
2022-01-19 08:55:06 +00:00
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_UP;
|
2022-01-09 09:08:31 +00:00
|
|
|
validEvent = true;
|
|
|
|
}
|
2022-09-09 10:51:41 +00:00
|
|
|
if (event->inputEvent == static_cast<char>(ModuleConfig_CannedMessageConfig_InputEventChar_DOWN)) {
|
2022-01-09 20:14:23 +00:00
|
|
|
DEBUG_MSG("Canned message event DOWN\n");
|
2022-01-19 08:55:06 +00:00
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_DOWN;
|
2022-01-09 09:08:31 +00:00
|
|
|
validEvent = true;
|
|
|
|
}
|
2022-09-09 10:51:41 +00:00
|
|
|
if (event->inputEvent == static_cast<char>(ModuleConfig_CannedMessageConfig_InputEventChar_SELECT)) {
|
2022-01-09 20:14:23 +00:00
|
|
|
DEBUG_MSG("Canned message event Select\n");
|
2022-03-25 11:27:14 +00:00
|
|
|
// when inactive, call the onebutton shortpress instead. Activate Module only on up/down
|
|
|
|
if ((this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED)) {
|
|
|
|
powerFSM.trigger(EVENT_PRESS);
|
2022-05-07 10:31:21 +00:00
|
|
|
} else {
|
2022-09-20 11:50:18 +00:00
|
|
|
this->payload = this->runState;
|
2022-03-25 11:27:14 +00:00
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_SELECT;
|
|
|
|
validEvent = true;
|
|
|
|
}
|
2022-01-09 09:08:31 +00:00
|
|
|
}
|
2022-09-20 11:50:18 +00:00
|
|
|
if (event->inputEvent == static_cast<char>(ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) {
|
|
|
|
DEBUG_MSG("Canned message event Cancel\n");
|
|
|
|
// emulate a timeout. Same result
|
|
|
|
this->lastTouchMillis = 0;
|
|
|
|
validEvent = true;
|
|
|
|
}
|
|
|
|
if ((event->inputEvent == static_cast<char>(ModuleConfig_CannedMessageConfig_InputEventChar_BACK)) ||
|
|
|
|
(event->inputEvent == static_cast<char>(ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) ||
|
|
|
|
(event->inputEvent == static_cast<char>(ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT))) {
|
2022-09-20 14:21:32 +00:00
|
|
|
DEBUG_MSG("Canned message event (%x)\n",event->kbchar);
|
2022-09-20 11:50:18 +00:00
|
|
|
if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
|
|
|
|
// pass the pressed key
|
|
|
|
this->payload = event->kbchar;
|
|
|
|
this->lastTouchMillis = millis();
|
|
|
|
validEvent = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (event->inputEvent == static_cast<char>(ANYKEY)) {
|
|
|
|
DEBUG_MSG("Canned message event any key pressed\n");
|
|
|
|
// when inactive, this will switch to the freetext mode
|
2022-09-20 12:28:42 +00:00
|
|
|
if ((this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED)) {
|
2022-09-20 11:50:18 +00:00
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
|
|
|
|
}
|
|
|
|
// pass the pressed key
|
|
|
|
this->payload = event->kbchar;
|
|
|
|
this->lastTouchMillis = millis();
|
|
|
|
validEvent = true;
|
|
|
|
}
|
|
|
|
|
2022-01-04 18:42:28 +00:00
|
|
|
|
2022-05-07 10:31:21 +00:00
|
|
|
if (validEvent) {
|
2022-01-09 09:08:31 +00:00
|
|
|
// Let runOnce to be called immediately.
|
2022-01-13 11:51:36 +00:00
|
|
|
setIntervalFromNow(0);
|
2022-01-09 09:08:31 +00:00
|
|
|
}
|
2022-01-04 18:42:28 +00:00
|
|
|
|
2022-01-09 09:08:31 +00:00
|
|
|
return 0;
|
2022-01-04 18:42:28 +00:00
|
|
|
}
|
|
|
|
|
2022-05-07 10:31:21 +00:00
|
|
|
void CannedMessageModule::sendText(NodeNum dest, const char *message, bool wantReplies)
|
2022-01-04 18:42:28 +00:00
|
|
|
{
|
|
|
|
MeshPacket *p = allocDataPacket();
|
|
|
|
p->to = dest;
|
2022-01-13 11:51:36 +00:00
|
|
|
p->want_ack = true;
|
2022-01-04 21:02:16 +00:00
|
|
|
p->decoded.payload.size = strlen(message);
|
|
|
|
memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size);
|
2022-05-22 11:27:56 +00:00
|
|
|
if (moduleConfig.canned_message.send_bell) {
|
2022-05-07 10:31:21 +00:00
|
|
|
p->decoded.payload.bytes[p->decoded.payload.size - 1] = 7; // Bell character
|
|
|
|
p->decoded.payload.bytes[p->decoded.payload.size] = '\0'; // Bell character
|
2022-01-12 08:26:42 +00:00
|
|
|
p->decoded.payload.size++;
|
|
|
|
}
|
|
|
|
|
2022-05-07 10:31:21 +00:00
|
|
|
DEBUG_MSG("Sending message id=%d, msg=%.*s\n", p->id, p->decoded.payload.size, p->decoded.payload.bytes);
|
2022-01-04 18:42:28 +00:00
|
|
|
|
|
|
|
service.sendToMesh(p);
|
|
|
|
}
|
|
|
|
|
2022-02-27 09:20:23 +00:00
|
|
|
int32_t CannedMessageModule::runOnce()
|
2022-01-04 18:42:28 +00:00
|
|
|
{
|
2022-05-22 11:27:56 +00:00
|
|
|
if ((!moduleConfig.canned_message.enabled) || (this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED) ||
|
2022-05-07 10:31:21 +00:00
|
|
|
(this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE)) {
|
2022-01-12 08:26:42 +00:00
|
|
|
return 30000; // TODO: should return MAX_VAL
|
|
|
|
}
|
2022-01-09 20:14:23 +00:00
|
|
|
DEBUG_MSG("Check status\n");
|
2022-01-13 08:19:36 +00:00
|
|
|
UIFrameEvent e = {false, true};
|
2022-05-07 10:31:21 +00:00
|
|
|
if (this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) {
|
2022-01-04 21:02:16 +00:00
|
|
|
// TODO: might have some feedback of sendig state
|
2022-01-19 08:55:06 +00:00
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
|
|
|
e.frameChanged = true;
|
|
|
|
this->currentMessageIndex = -1;
|
2022-09-20 11:50:18 +00:00
|
|
|
this->freetext = ""; // clear freetext
|
2022-09-20 14:21:32 +00:00
|
|
|
this->cursor = 0;
|
2022-01-19 08:55:06 +00:00
|
|
|
this->notifyObservers(&e);
|
2022-09-20 11:50:18 +00:00
|
|
|
} else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) && (millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS) {
|
2022-02-27 08:29:05 +00:00
|
|
|
// Reset module
|
2022-09-21 08:42:10 +00:00
|
|
|
DEBUG_MSG("Reset due to lack of activity.\n");
|
2022-01-13 11:51:36 +00:00
|
|
|
e.frameChanged = true;
|
2022-01-19 08:55:06 +00:00
|
|
|
this->currentMessageIndex = -1;
|
2022-09-20 11:50:18 +00:00
|
|
|
this->freetext = ""; // clear freetext
|
2022-09-20 14:21:32 +00:00
|
|
|
this->cursor = 0;
|
2022-01-19 08:55:06 +00:00
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
2022-01-13 08:19:36 +00:00
|
|
|
this->notifyObservers(&e);
|
2022-05-07 10:31:21 +00:00
|
|
|
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) {
|
2022-09-20 11:50:18 +00:00
|
|
|
if (this->payload == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
|
2022-09-21 08:42:10 +00:00
|
|
|
if (this->freetext.length() > 0) {
|
|
|
|
sendText(NODENUM_BROADCAST, this->freetext.c_str(), true);
|
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE;
|
|
|
|
} else {
|
|
|
|
DEBUG_MSG("Reset message is empty.\n");
|
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
|
|
|
e.frameChanged = true;
|
|
|
|
}
|
2022-09-20 11:50:18 +00:00
|
|
|
} else {
|
2022-09-21 08:42:10 +00:00
|
|
|
if (strlen(this->messages[this->currentMessageIndex]) > 0) {
|
|
|
|
sendText(NODENUM_BROADCAST, this->messages[this->currentMessageIndex], true);
|
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE;
|
|
|
|
} else {
|
|
|
|
DEBUG_MSG("Reset message is empty.\n");
|
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
|
|
|
e.frameChanged = true;
|
|
|
|
}
|
2022-09-20 11:50:18 +00:00
|
|
|
}
|
2022-01-04 21:02:16 +00:00
|
|
|
this->currentMessageIndex = -1;
|
2022-09-20 11:50:18 +00:00
|
|
|
this->freetext = ""; // clear freetext
|
2022-09-20 14:21:32 +00:00
|
|
|
this->cursor = 0;
|
2022-01-13 08:19:36 +00:00
|
|
|
this->notifyObservers(&e);
|
2022-01-04 21:02:16 +00:00
|
|
|
return 2000;
|
2022-09-20 11:50:18 +00:00
|
|
|
} else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) {
|
|
|
|
this->currentMessageIndex = 0;
|
|
|
|
DEBUG_MSG("First touch (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
|
|
|
|
e.frameChanged = true;
|
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
|
2022-05-07 10:31:21 +00:00
|
|
|
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) {
|
2022-01-04 19:41:45 +00:00
|
|
|
this->currentMessageIndex = getPrevIndex();
|
2022-09-20 12:28:42 +00:00
|
|
|
this->freetext = ""; // clear freetext
|
2022-09-20 14:21:32 +00:00
|
|
|
this->cursor = 0;
|
2022-01-19 08:55:06 +00:00
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
|
2022-02-21 05:29:15 +00:00
|
|
|
DEBUG_MSG("MOVE UP (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
|
2022-05-07 10:31:21 +00:00
|
|
|
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_DOWN) {
|
2022-01-04 19:41:45 +00:00
|
|
|
this->currentMessageIndex = this->getNextIndex();
|
2022-09-20 12:28:42 +00:00
|
|
|
this->freetext = ""; // clear freetext
|
2022-09-20 14:21:32 +00:00
|
|
|
this->cursor = 0;
|
2022-01-19 08:55:06 +00:00
|
|
|
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
|
2022-02-21 05:29:15 +00:00
|
|
|
DEBUG_MSG("MOVE DOWN (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
|
2022-09-20 11:50:18 +00:00
|
|
|
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
|
|
|
|
e.frameChanged = true;
|
|
|
|
switch (this->payload) {
|
2022-09-20 14:21:32 +00:00
|
|
|
case 0xb4: // left
|
|
|
|
if (this->cursor > 0) {
|
|
|
|
this->cursor--;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0xb7: // right
|
|
|
|
if (this->cursor < this->freetext.length()) {
|
|
|
|
this->cursor++;
|
|
|
|
}
|
|
|
|
break;
|
2022-09-20 11:50:18 +00:00
|
|
|
case 8: // backspace
|
|
|
|
if (this->freetext.length() > 0) {
|
2022-09-21 08:42:10 +00:00
|
|
|
if(this->cursor == this->freetext.length()) {
|
|
|
|
this->freetext = this->freetext.substring(0, this->freetext.length() - 1);
|
|
|
|
} else {
|
|
|
|
this->freetext = this->freetext.substring(0, this->cursor - 1) + this->freetext.substring(this->cursor, this->freetext.length());
|
|
|
|
}
|
2022-09-20 14:21:32 +00:00
|
|
|
this->cursor--;
|
2022-09-20 11:50:18 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2022-09-20 14:21:32 +00:00
|
|
|
if(this->cursor == this->freetext.length()) {
|
|
|
|
this->freetext += this->payload;
|
|
|
|
} else {
|
|
|
|
this->freetext = this->freetext.substring(0, this->cursor)
|
|
|
|
+ this->payload
|
|
|
|
+ this->freetext.substring(this->cursor);
|
|
|
|
}
|
|
|
|
this->cursor += 1;
|
|
|
|
if (this->freetext.length() > Constants_DATA_PAYLOAD_LEN) {
|
|
|
|
this->cursor = Constants_DATA_PAYLOAD_LEN;
|
|
|
|
this->freetext = this->freetext.substring(0, Constants_DATA_PAYLOAD_LEN);
|
|
|
|
}
|
2022-09-20 11:50:18 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->lastTouchMillis = millis();
|
|
|
|
this->notifyObservers(&e);
|
|
|
|
return INACTIVATE_AFTER_MS;
|
2022-01-04 18:42:28 +00:00
|
|
|
}
|
2022-01-19 08:55:06 +00:00
|
|
|
|
2022-05-07 10:31:21 +00:00
|
|
|
if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) {
|
2022-01-13 11:51:36 +00:00
|
|
|
this->lastTouchMillis = millis();
|
2022-01-13 08:19:36 +00:00
|
|
|
this->notifyObservers(&e);
|
2022-01-13 11:51:36 +00:00
|
|
|
return INACTIVATE_AFTER_MS;
|
|
|
|
}
|
2022-01-04 18:43:06 +00:00
|
|
|
|
2022-01-12 08:26:42 +00:00
|
|
|
return 30000; // TODO: should return MAX_VAL
|
2022-01-04 18:42:28 +00:00
|
|
|
}
|
|
|
|
|
2022-05-07 10:31:21 +00:00
|
|
|
const char *CannedMessageModule::getCurrentMessage()
|
2022-01-04 18:42:28 +00:00
|
|
|
{
|
2022-01-12 08:26:42 +00:00
|
|
|
return this->messages[this->currentMessageIndex];
|
2022-01-09 09:08:31 +00:00
|
|
|
}
|
2022-05-07 10:31:21 +00:00
|
|
|
const char *CannedMessageModule::getPrevMessage()
|
2022-01-09 09:08:31 +00:00
|
|
|
{
|
2022-01-12 08:26:42 +00:00
|
|
|
return this->messages[this->getPrevIndex()];
|
2022-01-09 09:08:31 +00:00
|
|
|
}
|
2022-05-07 10:31:21 +00:00
|
|
|
const char *CannedMessageModule::getNextMessage()
|
2022-01-09 09:08:31 +00:00
|
|
|
{
|
2022-01-12 08:26:42 +00:00
|
|
|
return this->messages[this->getNextIndex()];
|
2022-01-09 09:08:31 +00:00
|
|
|
}
|
2022-02-27 09:20:23 +00:00
|
|
|
bool CannedMessageModule::shouldDraw()
|
2022-01-09 09:08:31 +00:00
|
|
|
{
|
2022-05-22 11:27:56 +00:00
|
|
|
if (!moduleConfig.canned_message.enabled) {
|
2022-01-13 07:08:16 +00:00
|
|
|
return false;
|
|
|
|
}
|
2022-01-19 08:55:06 +00:00
|
|
|
return (currentMessageIndex != -1) || (this->runState != CANNED_MESSAGE_RUN_STATE_INACTIVE);
|
2022-01-04 18:42:28 +00:00
|
|
|
}
|
|
|
|
|
2022-02-27 09:20:23 +00:00
|
|
|
int CannedMessageModule::getNextIndex()
|
2022-01-04 18:42:28 +00:00
|
|
|
{
|
2022-05-07 10:31:21 +00:00
|
|
|
if (this->currentMessageIndex >= (this->messagesCount - 1)) {
|
2022-01-09 09:08:31 +00:00
|
|
|
return 0;
|
2022-05-07 10:31:21 +00:00
|
|
|
} else {
|
2022-01-09 09:08:31 +00:00
|
|
|
return this->currentMessageIndex + 1;
|
2022-01-04 18:42:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-27 09:20:23 +00:00
|
|
|
int CannedMessageModule::getPrevIndex()
|
2022-01-04 18:42:28 +00:00
|
|
|
{
|
2022-05-07 10:31:21 +00:00
|
|
|
if (this->currentMessageIndex <= 0) {
|
2022-01-12 08:26:42 +00:00
|
|
|
return this->messagesCount - 1;
|
2022-05-07 10:31:21 +00:00
|
|
|
} else {
|
2022-01-09 09:08:31 +00:00
|
|
|
return this->currentMessageIndex - 1;
|
2022-01-04 18:42:28 +00:00
|
|
|
}
|
2022-01-13 07:08:16 +00:00
|
|
|
}
|
|
|
|
|
2022-05-07 10:31:21 +00:00
|
|
|
void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
2022-01-13 07:08:16 +00:00
|
|
|
{
|
|
|
|
displayedNodeNum = 0; // Not currently showing a node pane
|
|
|
|
|
2022-05-07 10:31:21 +00:00
|
|
|
if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) {
|
2022-01-19 08:55:06 +00:00
|
|
|
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
|
|
|
display->setFont(FONT_MEDIUM);
|
2022-05-07 10:31:21 +00:00
|
|
|
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, "Sending...");
|
|
|
|
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_DISABLED) {
|
2022-03-31 12:59:42 +00:00
|
|
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
|
|
display->setFont(FONT_SMALL);
|
2022-09-20 14:21:32 +00:00
|
|
|
display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled.");
|
2022-09-20 11:50:18 +00:00
|
|
|
}else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
|
|
|
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
|
|
display->setFont(FONT_MEDIUM);
|
|
|
|
display->drawString(0 + x, 0 + y, "To: Broadcast");
|
2022-09-20 14:21:32 +00:00
|
|
|
// used chars right aligned
|
|
|
|
char buffer[9];
|
|
|
|
sprintf(buffer, "%d left", Constants_DATA_PAYLOAD_LEN - this->freetext.length());
|
|
|
|
display->drawString(x + display->getWidth() - display->getStringWidth(buffer), y + 0, buffer);
|
|
|
|
display->drawString(0 + x, 0 + y + FONT_HEIGHT_MEDIUM, cannedMessageModule->drawWithCursor(cannedMessageModule->freetext, cannedMessageModule->cursor));
|
2022-05-07 10:31:21 +00:00
|
|
|
} else {
|
2022-01-13 07:08:16 +00:00
|
|
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
|
|
display->setFont(FONT_SMALL);
|
2022-02-27 09:20:23 +00:00
|
|
|
display->drawString(0 + x, 0 + y, cannedMessageModule->getPrevMessage());
|
2022-01-13 07:08:16 +00:00
|
|
|
display->setFont(FONT_MEDIUM);
|
2022-09-20 14:21:32 +00:00
|
|
|
display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL, cannedMessageModule->getCurrentMessage());
|
2022-01-13 07:08:16 +00:00
|
|
|
display->setFont(FONT_SMALL);
|
2022-09-20 14:21:32 +00:00
|
|
|
display->drawString(0 + x, 0 + y + FONT_HEIGHT_MEDIUM, cannedMessageModule->getNextMessage());
|
2022-01-13 07:08:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-27 10:21:02 +00:00
|
|
|
void CannedMessageModule::loadProtoForModule()
|
2022-02-21 05:29:15 +00:00
|
|
|
{
|
2022-05-07 10:31:21 +00:00
|
|
|
if (!loadProto(cannedMessagesConfigFile, CannedMessageModuleConfig_size, sizeof(cannedMessagesConfigFile),
|
|
|
|
CannedMessageModuleConfig_fields, &cannedMessageModuleConfig)) {
|
2022-02-27 09:20:23 +00:00
|
|
|
installDefaultCannedMessageModuleConfig();
|
2022-02-21 05:29:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-02-27 08:29:05 +00:00
|
|
|
* @brief Save the module config to file.
|
2022-02-21 05:29:15 +00:00
|
|
|
*
|
|
|
|
* @return true On success.
|
|
|
|
* @return false On error.
|
|
|
|
*/
|
2022-02-27 10:21:02 +00:00
|
|
|
bool CannedMessageModule::saveProtoForModule()
|
2022-02-21 05:29:15 +00:00
|
|
|
{
|
|
|
|
bool okay = true;
|
|
|
|
|
|
|
|
#ifdef FS
|
|
|
|
FS.mkdir("/prefs");
|
|
|
|
#endif
|
|
|
|
|
2022-05-07 10:31:21 +00:00
|
|
|
okay &= saveProto(cannedMessagesConfigFile, CannedMessageModuleConfig_size, sizeof(CannedMessageModuleConfig),
|
|
|
|
CannedMessageModuleConfig_fields, &cannedMessageModuleConfig);
|
2022-02-21 05:29:15 +00:00
|
|
|
|
|
|
|
return okay;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Fill configuration with default values.
|
|
|
|
*/
|
2022-02-27 09:20:23 +00:00
|
|
|
void CannedMessageModule::installDefaultCannedMessageModuleConfig()
|
2022-02-21 05:29:15 +00:00
|
|
|
{
|
2022-08-16 02:06:55 +00:00
|
|
|
memset(cannedMessageModuleConfig.messages, 0, sizeof(cannedMessageModuleConfig.messages));
|
2022-02-21 05:29:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-02-27 10:21:02 +00:00
|
|
|
* @brief An admin message arrived to AdminModule. We are asked whether we want to handle that.
|
2022-02-21 05:29:15 +00:00
|
|
|
*
|
|
|
|
* @param mp The mesh packet arrived.
|
|
|
|
* @param request The AdminMessage request extracted from the packet.
|
|
|
|
* @param response The prepared response
|
|
|
|
* @return AdminMessageHandleResult HANDLED if message was handled
|
|
|
|
* HANDLED_WITH_RESULT if a result is also prepared.
|
|
|
|
*/
|
2022-05-07 10:31:21 +00:00
|
|
|
AdminMessageHandleResult CannedMessageModule::handleAdminMessageForModule(const MeshPacket &mp, AdminMessage *request,
|
|
|
|
AdminMessage *response)
|
2022-02-21 05:29:15 +00:00
|
|
|
{
|
|
|
|
AdminMessageHandleResult result;
|
|
|
|
|
2022-09-09 10:51:41 +00:00
|
|
|
switch (request->which_payload_variant) {
|
2022-08-16 02:06:55 +00:00
|
|
|
case AdminMessage_get_canned_message_module_messages_request_tag:
|
|
|
|
DEBUG_MSG("Client is getting radio canned messages\n");
|
|
|
|
this->handleGetCannedMessageModuleMessages(mp, response);
|
2022-02-21 05:29:15 +00:00
|
|
|
result = AdminMessageHandleResult::HANDLED_WITH_RESPONSE;
|
|
|
|
break;
|
|
|
|
|
2022-08-16 02:06:55 +00:00
|
|
|
case AdminMessage_set_canned_message_module_messages_tag:
|
|
|
|
DEBUG_MSG("Client is setting radio canned messages\n");
|
|
|
|
this->handleSetCannedMessageModuleMessages(request->set_canned_message_module_messages);
|
2022-02-21 05:29:15 +00:00
|
|
|
result = AdminMessageHandleResult::HANDLED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
result = AdminMessageHandleResult::NOT_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-08-16 02:06:55 +00:00
|
|
|
void CannedMessageModule::handleGetCannedMessageModuleMessages(const MeshPacket &req, AdminMessage *response)
|
2022-02-21 05:29:15 +00:00
|
|
|
{
|
2022-08-16 02:06:55 +00:00
|
|
|
DEBUG_MSG("*** handleGetCannedMessageModuleMessages\n");
|
2022-02-21 05:29:15 +00:00
|
|
|
assert(req.decoded.want_response);
|
|
|
|
|
2022-09-09 10:51:41 +00:00
|
|
|
response->which_payload_variant = AdminMessage_get_canned_message_module_messages_response_tag;
|
2022-08-16 02:06:55 +00:00
|
|
|
strcpy(response->get_canned_message_module_messages_response, cannedMessageModuleConfig.messages);
|
2022-02-21 05:29:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-16 02:06:55 +00:00
|
|
|
void CannedMessageModule::handleSetCannedMessageModuleMessages(const char *from_msg)
|
2022-02-21 05:29:15 +00:00
|
|
|
{
|
|
|
|
int changed = 0;
|
|
|
|
|
2022-05-07 10:31:21 +00:00
|
|
|
if (*from_msg) {
|
2022-08-16 02:06:55 +00:00
|
|
|
changed |= strcmp(cannedMessageModuleConfig.messages, from_msg);
|
|
|
|
strcpy(cannedMessageModuleConfig.messages, from_msg);
|
2022-02-21 05:29:15 +00:00
|
|
|
DEBUG_MSG("*** from_msg.text:%s\n", from_msg);
|
|
|
|
}
|
|
|
|
|
2022-05-07 10:31:21 +00:00
|
|
|
if (changed) {
|
2022-02-27 10:21:02 +00:00
|
|
|
this->saveProtoForModule();
|
2022-02-21 05:29:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-20 14:21:32 +00:00
|
|
|
String CannedMessageModule::drawWithCursor(String text, int cursor)
|
|
|
|
{
|
|
|
|
String result = text.substring(0, cursor)
|
|
|
|
+ "_"
|
|
|
|
+ text.substring(cursor);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-05-06 13:41:37 +00:00
|
|
|
#endif
|