mirror of
https://github.com/pelgraine/Meck.git
synced 2026-08-10 18:52:44 +02:00
Fix twatch tilt raise lock screen functionality. Implement unread count on tw_picker. Fix channel missage history and full message view
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
#define FIRMWARE_VER_CODE 11
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "4 July 2026"
|
||||
#define FIRMWARE_BUILD_DATE "7 July 2026"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
|
||||
@@ -182,7 +182,58 @@ public:
|
||||
|
||||
// Sanitize emoji: replace UTF-8 emoji sequences with single-byte escape codes
|
||||
// The text already contains "Sender: message" format
|
||||
#if defined(LILYGO_TWATCH_S3_PLUS)
|
||||
// Watch build: drop emoji entirely instead of storing sprite escape bytes.
|
||||
// The watch UI has no sprite rendering and no room for emoji at 120x120
|
||||
// virtual. Non-emoji UTF-8 (accented letters etc.) passes through
|
||||
// unchanged, the same as emojiSanitize does.
|
||||
{
|
||||
const uint8_t* s = (const uint8_t*)text;
|
||||
int srcLen = (int)strlen(text);
|
||||
int si = 0, di = 0;
|
||||
while (si < srcLen && di < CHANNEL_MSG_TEXT_LEN - 1) {
|
||||
uint8_t b = s[si];
|
||||
if (b < 0x80) { msg->text[di++] = (char)b; si++; continue; }
|
||||
int consumed;
|
||||
uint32_t cp = emojiDecodeUtf8(s + si, srcLen - si, &consumed);
|
||||
if (cp == 0xFE0F) { si += consumed; continue; } // variation selector
|
||||
if (cp == 0xFFFD) { si += consumed; continue; } // invalid UTF-8 -- skip
|
||||
bool isEmoji = false;
|
||||
for (int e = 0; e < EMOJI_COUNT; e++) {
|
||||
if (EMOJI_CODEPOINTS[e].cp == cp) {
|
||||
if (EMOJI_CODEPOINTS[e].cp2 != 0) {
|
||||
// two-codepoint emoji: only a match when the pair matches
|
||||
int consumed2;
|
||||
if (si + consumed < srcLen) {
|
||||
uint32_t cp2 = emojiDecodeUtf8(s + si + consumed, srcLen - si - consumed, &consumed2);
|
||||
if (cp2 == EMOJI_CODEPOINTS[e].cp2) {
|
||||
si += consumed + consumed2;
|
||||
isEmoji = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
si += consumed;
|
||||
isEmoji = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isEmoji) {
|
||||
for (int a = 0; a < EMOJI_ALIAS_COUNT; a++) {
|
||||
if (EMOJI_ALIASES[a].cp == cp) { si += consumed; isEmoji = true; break; }
|
||||
}
|
||||
}
|
||||
if (isEmoji) continue; // dropped
|
||||
if (di + consumed >= CHANNEL_MSG_TEXT_LEN) break;
|
||||
for (int k = 0; k < consumed; k++) msg->text[di++] = (char)s[si + k];
|
||||
si += consumed;
|
||||
}
|
||||
msg->text[di] = 0;
|
||||
}
|
||||
#else
|
||||
emojiSanitize(text, msg->text, CHANNEL_MSG_TEXT_LEN);
|
||||
#endif
|
||||
|
||||
if (_msgCount < CHANNEL_MSG_HISTORY_SIZE) {
|
||||
_msgCount++;
|
||||
@@ -221,6 +272,23 @@ public:
|
||||
}
|
||||
|
||||
int getMessageCount() const { return _msgCount; }
|
||||
|
||||
// Read-only access to stored messages for a given channel, by recency.
|
||||
// n = 0 returns the newest message on that channel, n = 1 the next newest,
|
||||
// and so on. Returns nullptr when fewer than n + 1 messages exist.
|
||||
const ChannelMessage* getChannelMsgByRecency(uint8_t channel_idx, int n) const {
|
||||
if (n < 0 || _msgCount == 0) return nullptr;
|
||||
int seen = 0;
|
||||
for (int i = 0; i < _msgCount; i++) {
|
||||
int idx = _newestIdx - i;
|
||||
while (idx < 0) idx += CHANNEL_MSG_HISTORY_SIZE;
|
||||
const ChannelMessage& m = _messages[idx];
|
||||
if (!m.valid || m.channel_idx != channel_idx) continue;
|
||||
if (seen == n) return &m;
|
||||
seen++;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8_t getViewChannelIdx() const { return _viewChannelIdx; }
|
||||
void setViewChannelIdx(uint8_t idx) {
|
||||
|
||||
@@ -478,8 +478,15 @@ public:
|
||||
char timeBuf[6];
|
||||
sprintf(timeBuf, "%02d:%02d", hrs, mins);
|
||||
int clockX = (display.width() - lcd->smallTextWidth(timeBuf)) / 2;
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
lcd->printSmallFont(clockX, HOME_HDR_Y, timeBuf);
|
||||
// Long node names: keep the clock centred when there is room,
|
||||
// otherwise push it right so it starts just past the name. When even
|
||||
// that would run into the unread count, hide the clock instead.
|
||||
int nameEndX = lcd->smallTextWidth(filtered_name) + 3;
|
||||
if (clockX < nameEndX) clockX = nameEndX;
|
||||
if (clockX + lcd->smallTextWidth(timeBuf) + 3 <= numX) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
lcd->printSmallFont(clockX, HOME_HDR_Y, timeBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -1459,7 +1466,9 @@ public:
|
||||
"Jul","Aug","Sep","Oct","Nov","Dec"};
|
||||
sprintf(buf, "%s %d %s", days[tmv.tm_wday], tmv.tm_mday, mons[tmv.tm_mon]);
|
||||
display.setTextSize(2);
|
||||
display.setColor(DisplayDriver::GREEN); // light grey on the watch palette
|
||||
display.drawTextCentered(display.width() / 2, display.height() / 2 + 12, buf);
|
||||
display.setColor(DisplayDriver::LIGHT); // restore for the battery line
|
||||
}
|
||||
|
||||
// battery % and unread count
|
||||
@@ -1657,6 +1666,8 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
||||
tw_picker = new TWatchChannelPicker(_display);
|
||||
tw_channel = new TWatchChannelScreen(_display);
|
||||
tw_keyboard = new TWatchKeyboardScreen(_display);
|
||||
((TWatchChannelPicker*)tw_picker)->setStore((ChannelScreen*)channel_screen);
|
||||
((TWatchChannelScreen*)tw_channel)->setStore((ChannelScreen*)channel_screen);
|
||||
#endif
|
||||
audiobook_screen = nullptr; // Created and assigned from main.cpp if audio hardware present
|
||||
#ifdef MECK_AUDIO_VARIANT
|
||||
@@ -1774,9 +1785,6 @@ void UITask::msgRead(int msgcount) {
|
||||
void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount,
|
||||
const uint8_t* path, int8_t snr, uint8_t scope_idx) {
|
||||
_msgcount = msgcount;
|
||||
#ifdef TWATCH_COMPOSE_ENABLED
|
||||
if (tw_channel) ((TWatchChannelScreen*)tw_channel)->notifyMsg(from_name, text);
|
||||
#endif
|
||||
|
||||
// --- Dedup: suppress retry spam (same sender + text within 60s) ---
|
||||
uint32_t nameH = simpleHash(from_name);
|
||||
@@ -1930,6 +1938,10 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i
|
||||
// app is connected (they'll see it there), mark as read immediately
|
||||
if ((isOnChannelScreen() &&
|
||||
((ChannelScreen *) channel_screen)->getViewChannelIdx() == channel_idx) ||
|
||||
#ifdef TWATCH_COMPOSE_ENABLED
|
||||
(curr == tw_channel &&
|
||||
((TWatchChannelScreen*)tw_channel)->getChannelIdx() == channel_idx) ||
|
||||
#endif
|
||||
hasConnection()) {
|
||||
((ChannelScreen *) channel_screen)->markChannelRead(channel_idx);
|
||||
}
|
||||
@@ -2309,6 +2321,7 @@ if (curr) curr->poll();
|
||||
if (curr == tw_picker) {
|
||||
TWatchChannelPicker* picker = (TWatchChannelPicker*)tw_picker;
|
||||
if (picker->isConfirmed()) {
|
||||
((TWatchChannelScreen*)tw_channel)->setSelfName(the_mesh.getNodePrefs()->node_name);
|
||||
((TWatchChannelScreen*)tw_channel)->activate(picker->getSelectedChannelIdx(),
|
||||
picker->getSelectedChannelName());
|
||||
setCurrScreen(tw_channel);
|
||||
@@ -2347,7 +2360,7 @@ if (curr) curr->poll();
|
||||
the_mesh.sendGroupMessage(ts, ch.channel, the_mesh.getNodeName(),
|
||||
sendText, strlen(sendText));
|
||||
showAlert("Sent!", 800);
|
||||
((TWatchChannelScreen*)tw_channel)->addSentMsg(sendText);
|
||||
addSentChannelMessage(kb->getChannelIdx(), the_mesh.getNodePrefs()->node_name, sendText);
|
||||
}
|
||||
kb->clearOutBuf();
|
||||
setCurrScreen(tw_channel);
|
||||
@@ -2541,17 +2554,17 @@ if (curr) curr->poll();
|
||||
if (millis() > _auto_off) {
|
||||
_display->turnOff();
|
||||
}
|
||||
#endif
|
||||
#if defined(LILYGO_TWATCH_S3_PLUS)
|
||||
// Raise-to-wake: a wrist-raise (BMA423 tilt) while the display is off turns
|
||||
// it back on showing the clock screen and restarts the idle timer.
|
||||
if (!_display->isOn() && board.tiltFired()) {
|
||||
_display->turnOn();
|
||||
setCurrScreen(lock_screen);
|
||||
_auto_off = millis() + AUTO_OFF_MILLIS;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if defined(LILYGO_TWATCH_S3_PLUS)
|
||||
// Raise-to-wake: a wrist-raise (BMA423 tilt) while the display is off turns
|
||||
// it back on showing the clock screen and restarts the idle timer.
|
||||
if (_display != NULL && !_display->isOn() && board.tiltFired()) {
|
||||
_display->turnOn();
|
||||
setCurrScreen(lock_screen);
|
||||
_auto_off = millis() + AUTO_OFF_MILLIS;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Auto-lock idle timer — runs regardless of display on/off state
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro) || defined(LilyGo_TDeck_Pro)
|
||||
|
||||
Reference in New Issue
Block a user