From d159318b00172b3b2ed0c169772a1b84adf55e0a Mon Sep 17 00:00:00 2001 From: pelgraine <140762863+pelgraine@users.noreply.github.com> Date: Thu, 26 Feb 2026 02:19:10 +1100 Subject: [PATCH] Fixed in-call screen and call ended notifications; fixed dial number screen print responsiveness; fixed firmware version 4G text issue caused by - instead of . --- examples/companion_radio/main.cpp | 70 ++++++++++++--- examples/companion_radio/ui-new/SMSScreen.h | 95 +++------------------ examples/companion_radio/ui-new/UITask.h | 6 ++ variants/lilygo_tdeck_pro/platformio.ini | 4 +- 4 files changed, 77 insertions(+), 98 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index f15ffa16..cb647b27 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -33,6 +33,11 @@ static bool composeNeedsRefresh = false; #define COMPOSE_REFRESH_INTERVAL 100 // ms before starting e-ink refresh after keypress (refresh itself takes ~644ms) + // Phone dialer debounce — independent from compose/smsSuppressLoop to avoid + // interfering with call view rendering and alert display + static bool dialerNeedsRefresh = false; + static unsigned long lastDialerRefresh = 0; + // DM compose mode (direct message to a specific contact) static bool composeDM = false; static int composeDMContactIdx = -1; @@ -887,6 +892,19 @@ void loop() { } else if (callEvt.type == CallEventType::ENDED) { Serial.printf("[Call] Ended (%lus) with %s\n", (unsigned long)callEvt.duration, callEvt.phone); + // Show alert with duration (supplements the immediate alert from Q hangup; + // this catches remote hangups and network drops) + { + char alertBuf[48]; + if (callEvt.duration > 0) { + snprintf(alertBuf, sizeof(alertBuf), "Call Ended %lu:%02lu", + (unsigned long)(callEvt.duration / 60), + (unsigned long)(callEvt.duration % 60)); + } else { + snprintf(alertBuf, sizeof(alertBuf), "Call Ended"); + } + ui_task.showAlert(alertBuf, 2000); + } ui_task.forceRefresh(); } else if (callEvt.type == CallEventType::MISSED) { char alertBuf[48]; @@ -931,7 +949,7 @@ void loop() { webReaderNeedsRefresh = false; } #endif - if (!composeMode && !notesSuppressLoop && !smsSuppressLoop + if (!composeMode && !notesSuppressLoop && !smsSuppressLoop && !dialerNeedsRefresh #ifdef MECK_WEB_READER && !webReaderTextEntry #endif @@ -961,6 +979,21 @@ void loop() { lastComposeRefresh = millis(); composeNeedsRefresh = false; } + // Phone dialer debounced render (separate from compose debounce) + #ifdef HAS_4G_MODEM + if (dialerNeedsRefresh && (millis() - lastDialerRefresh) >= COMPOSE_REFRESH_INTERVAL) { + if (smsMode) { + SMSScreen* dialScr = (SMSScreen*)ui_task.getSMSScreen(); + if (dialScr && dialScr->getSubView() == SMSScreen::PHONE_DIALER) { + display.startFrame(); + dialScr->render(display); + display.endFrame(); + } + } + dialerNeedsRefresh = false; + lastDialerRefresh = millis(); + } + #endif #ifdef MECK_WEB_READER if (webReaderNeedsRefresh && (millis() - lastWebReaderRefresh) >= COMPOSE_REFRESH_INTERVAL) { WebReaderScreen* wr2 = (WebReaderScreen*)ui_task.getWebReaderScreen(); @@ -1026,7 +1059,8 @@ void loop() { touchFingerDown = true; lastTouchAccepted = now; if (smsScr->handleTouch(tx, ty)) { - ui_task.forceRefresh(); + dialerNeedsRefresh = true; + lastDialerRefresh = millis(); } } } else { @@ -1488,17 +1522,18 @@ void handleKeyboardInput() { if (smsMode) { SMSScreen* smsScr = (SMSScreen*)ui_task.getSMSScreen(); if (smsScr) { - // During active call views, route all keys directly to the screen - // and force a refresh after each keypress (no debounce needed) + // Keep display alive — SMS routes many keys via handleInput() directly, + // bypassing injectKey() which normally extends the auto-off timer. + ui_task.keepAlive(); if (smsScr->isInCallView()) { smsScr->handleInput(key); - if (smsScr->isInCallView()) { - // Still in a call view — refresh to update display - ui_task.forceRefresh(); - } else { - // Call ended, returned to a non-call view - ui_task.forceRefresh(); + if (!smsScr->isInCallView()) { + // Hangup just happened — show "Call Ended" alert immediately + ui_task.showAlert("Call Ended", 2000); } + // Force immediate render (call screen updates or return-to-dialer) + ui_task.forceRefresh(); + ui_task.loop(); return; } @@ -1509,10 +1544,21 @@ void handleKeyboardInput() { return; } - // Phone dialer: route keys directly (letter keys map to numbers) + // Phone dialer: debounced refresh for digit entry, immediate render for + // view transitions (Enter=call, Q=back). This avoids the 686ms e-ink + // block per keypress while ensuring call/back screens render instantly. if (smsScr->getSubView() == SMSScreen::PHONE_DIALER) { smsScr->handleInput(key); - ui_task.forceRefresh(); + if (smsScr->getSubView() == SMSScreen::PHONE_DIALER) { + // Still on dialer (digit/backspace) — debounced refresh + dialerNeedsRefresh = true; + lastDialerRefresh = millis(); + } else { + // View changed (startCall or Q back) — render immediately + dialerNeedsRefresh = false; + ui_task.forceRefresh(); + ui_task.loop(); + } return; } diff --git a/examples/companion_radio/ui-new/SMSScreen.h b/examples/companion_radio/ui-new/SMSScreen.h index a6a8c25b..61cf7252 100644 --- a/examples/companion_radio/ui-new/SMSScreen.h +++ b/examples/companion_radio/ui-new/SMSScreen.h @@ -47,7 +47,7 @@ class UITask; // forward declaration class SMSScreen : public UIScreen { public: enum SubView { APP_MENU, INBOX, CONVERSATION, COMPOSE, CONTACTS, EDIT_CONTACT, PHONE_DIALER, - DIALING_OUT, INCOMING_CALL, IN_CALL, CALL_ENDED }; + DIALING_OUT, INCOMING_CALL, IN_CALL }; private: UITask* _task; @@ -96,8 +96,6 @@ private: unsigned long _callConnectTime; // millis() when call connected (UI timer) uint8_t _callVolume; // Current speaker volume (0-5) uint8_t _callDotAnim; // Animation frame for dialing dots - unsigned long _callEndedTime; // millis() when call ended (for brief splash) - unsigned long _callEndedDuration; // Call duration in seconds (for ended screen) // Refresh debounce bool _needsRefresh; @@ -129,7 +127,6 @@ public: , _contactsCursor(0), _contactsScrollTop(0) , _editNamePos(0), _editIsNew(false), _editReturnView(INBOX) , _callReturnView(APP_MENU), _callConnectTime(0), _callVolume(3), _callDotAnim(0) - , _callEndedTime(0), _callEndedDuration(0) , _needsRefresh(false), _lastRefresh(0) , _sdReady(false) { @@ -154,7 +151,7 @@ public: bool isComposing() const { return _view == COMPOSE; } bool isEnteringPhone() const { return _enteringPhone || _view == PHONE_DIALER; } bool isInCallView() const { - return _view == DIALING_OUT || _view == INCOMING_CALL || _view == IN_CALL || _view == CALL_ENDED; + return _view == DIALING_OUT || _view == INCOMING_CALL || _view == IN_CALL; } // Transition to dialing screen — used by all dial callsites @@ -169,13 +166,6 @@ public: modemManager.dialCall(phone); } - // Show brief "Call Ended" splash before returning to previous view - void showCallEnded(unsigned long duration) { - _callEndedDuration = duration; - _callEndedTime = millis(); - _view = CALL_ENDED; - } - // Handle call events from modem (incoming, connected, ended, etc.) void onCallEvent(const CallEvent& evt) { switch (evt.type) { @@ -202,13 +192,12 @@ public: case CallEventType::ENDED: Serial.printf("[SMSScreen] Call ended (%lus)\n", (unsigned long)evt.duration); if (_view == IN_CALL || _view == DIALING_OUT) { - // Remote hangup or network drop — show ended splash - showCallEnded(evt.duration); - } else if (_view != CALL_ENDED) { - // Already left call view (e.g. user hung up), just clean up - _callPhone[0] = '\0'; - _callConnectTime = 0; + // Remote hangup or network drop — return to previous view + // "Call Ended" alert is shown by main.cpp via showAlert() + _view = _callReturnView; } + _callPhone[0] = '\0'; + _callConnectTime = 0; _needsRefresh = true; break; @@ -318,7 +307,6 @@ public: case DIALING_OUT: return renderDialingOut(display); case INCOMING_CALL: return renderIncomingCall(display); case IN_CALL: return renderInCall(display); - case CALL_ENDED: return renderCallEnded(display); } return 1000; } @@ -1110,54 +1098,6 @@ public: return 1000; // 1s refresh for timer } - // ---- Call ended (brief splash) ---- - int renderCallEnded(DisplayDriver& display) { - // Auto-dismiss after 2 seconds - if (_callEndedTime > 0 && (millis() - _callEndedTime) > 2000) { - _view = _callReturnView; - _callPhone[0] = '\0'; - _callConnectTime = 0; - return 0; // Immediate re-render in new view - } - - int W = display.width(); - int H = display.height(); - - // Header - display.setTextSize(1); - display.setColor(DisplayDriver::GREEN); - display.setCursor(0, 0); - display.print("Call Ended"); - - renderSignalIndicator(display, W - 2, 0); - - display.setColor(DisplayDriver::LIGHT); - display.drawRect(0, 11, W, 1); - - // Contact name - char dispName[SMS_CONTACT_NAME_LEN]; - smsContacts.displayName(_callPhone, dispName, sizeof(dispName)); - - display.setTextSize(1); - display.setColor(DisplayDriver::LIGHT); - display.setCursor(4, 20); - display.print(dispName); - - // Duration - if (_callEndedDuration > 0) { - char durBuf[16]; - snprintf(durBuf, sizeof(durBuf), "%02lu:%02lu", - _callEndedDuration / 60, _callEndedDuration % 60); - display.setTextSize(1); - display.setColor(DisplayDriver::LIGHT); - uint16_t durW = display.getTextWidth(durBuf); - display.setCursor((W - durW) / 2, H / 2 + 4); - display.print(durBuf); - } - - return 500; // Check frequently for auto-dismiss - } - // ========================================================================= // INPUT HANDLING // ========================================================================= @@ -1174,7 +1114,6 @@ public: case DIALING_OUT: return handleDialingOutInput(c); case INCOMING_CALL: return handleIncomingCallInput(c); case IN_CALL: return handleInCallInput(c); - case CALL_ENDED: return handleCallEndedInput(c); } return false; } @@ -1658,15 +1597,12 @@ public: bool handleInCallInput(char c) { switch (c) { case '\r': // Enter - hang up - case 'q': case 'Q': { - unsigned long dur = 0; - if (_callConnectTime > 0) { - dur = (millis() - _callConnectTime) / 1000; - } + case 'q': case 'Q': modemManager.hangupCall(); - showCallEnded(dur); + _view = _callReturnView; + _callPhone[0] = '\0'; + _callConnectTime = 0; return true; - } case 'w': case 'W': // Volume up if (_callVolume < 5) { @@ -1690,15 +1626,6 @@ public: return true; // Absorb all keys during call } } - - // ---- Call ended input (any key dismisses) ---- - bool handleCallEndedInput(char c) { - (void)c; - _view = _callReturnView; - _callPhone[0] = '\0'; - _callConnectTime = 0; - return true; - } }; #endif // SMS_SCREEN_H diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index ac3743be..ded69d2c 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -114,6 +114,12 @@ public: #endif void showAlert(const char* text, int duration_millis) override; void forceRefresh() override { _next_refresh = 100; } + // Wake display and extend auto-off timer. Call this when handling keys + // outside of injectKey() to prevent display auto-off during direct input. + void keepAlive() { + if (_display != NULL && !_display->isOn()) _display->turnOn(); + _auto_off = millis() + 15000; // matches AUTO_OFF_MILLIS default + } int getMsgCount() const { return _msgcount; } int getUnreadMsgCount() const; // Per-channel unread tracking (standalone) bool hasDisplay() const { return _display != NULL; } diff --git a/variants/lilygo_tdeck_pro/platformio.ini b/variants/lilygo_tdeck_pro/platformio.ini index ea15fb9b..0b50557d 100644 --- a/variants/lilygo_tdeck_pro/platformio.ini +++ b/variants/lilygo_tdeck_pro/platformio.ini @@ -154,7 +154,7 @@ build_flags = -D OFFLINE_QUEUE_SIZE=256 -D HAS_4G_MODEM=1 -D MECK_WEB_READER=1 - -D FIRMWARE_VERSION='"Meck v0.9.4-4G"' + -D FIRMWARE_VERSION='"Meck v0.9.4.4G"' build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter} + + @@ -179,7 +179,7 @@ build_flags = -D OFFLINE_QUEUE_SIZE=256 -D HAS_4G_MODEM=1 -D MECK_WEB_READER=1 - -D FIRMWARE_VERSION='"Meck v0.9.4-4G-SA"' + -D FIRMWARE_VERSION='"Meck v0.9.4.4G.SA"' build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter} + +