mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-09-14 15:16:40 +00:00
Merge pull request #32 from 3urobeat/feat/lock-screen-page-type
feat: Make lock screen a page type and display title bar on it Moves lock-screen rendering out of a dedicated branch in UITask::loop() into a proper HomePage::LOCK page in HomeScreen (appended after Count, so no existing HomePage/home_pages_mask bit shifts), adds a title bar to the lock screen (node name omitted, intentionally, for at-a-glance anonymity), and makes drawClockTime()'s center-alignment opt-in so the lock screen can left-align its clock. Reviewed: the two existing drawClockTime() call sites are both updated to keep their prior alignment, and this doesn't interact with our home_pages_mask=0 default, msg_wake_screen_off, or the shutdown()-flush fix landed this cycle. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -227,7 +227,7 @@ static int battMvToPercent(int mv, int low_mv) {
|
||||
// digits fill the narrow width. On wide panels (OLED, landscape e-ink) the
|
||||
// classic single-line "HH:MM" at size 2 is kept.
|
||||
static int drawClockTime(DisplayDriver& d, int top_y, const struct tm* ti,
|
||||
bool h12, bool show_sec) {
|
||||
bool h12, bool show_sec, bool alignCenter = false) {
|
||||
const bool tall = d.height() > d.width(); // true only on portrait e-ink
|
||||
|
||||
if (tall) {
|
||||
@@ -248,13 +248,27 @@ static int drawClockTime(DisplayDriver& d, int top_y, const struct tm* ti,
|
||||
// Centre on the visible width (minus that trailing column) instead.
|
||||
const int trail = d.getCharWidth() / 6; // one built-in column at this size
|
||||
auto drawBig = [&](const char* s, int yy) {
|
||||
int w = (int)d.getTextWidth(s) - trail;
|
||||
d.setCursor(cx - w / 2, yy);
|
||||
d.print(s);
|
||||
if (alignCenter) {
|
||||
int w = (int)d.getTextWidth(s) - trail;
|
||||
d.setCursor(cx - w / 2, yy);
|
||||
d.print(s);
|
||||
} else {
|
||||
d.setCursor(0, yy);
|
||||
d.print(s);
|
||||
}
|
||||
};
|
||||
drawBig(hbuf, y); y += lhb + 2;
|
||||
drawBig(mbuf, y); y += lhb + 2;
|
||||
if (ap) { d.setTextSize(2); d.drawTextCentered(cx, y, ap); y += d.getLineHeight() + 1; }
|
||||
if (ap) {
|
||||
d.setTextSize(2);
|
||||
if (alignCenter) {
|
||||
d.drawTextCentered(cx, y, ap);
|
||||
} else {
|
||||
d.setCursor(0, y);
|
||||
d.print(ap);
|
||||
}
|
||||
y += d.getLineHeight() + 1;
|
||||
}
|
||||
d.setTextSize(1);
|
||||
return y;
|
||||
}
|
||||
@@ -272,12 +286,21 @@ static int drawClockTime(DisplayDriver& d, int top_y, const struct tm* ti,
|
||||
if (show_sec) snprintf(buf, sizeof(buf), "%02d:%02d:%02d", ti->tm_hour, ti->tm_min, ti->tm_sec);
|
||||
else snprintf(buf, sizeof(buf), "%02d:%02d", ti->tm_hour, ti->tm_min);
|
||||
}
|
||||
d.drawTextCentered(d.width() / 2, top_y, buf);
|
||||
if (alignCenter) {
|
||||
d.drawTextCentered(d.width() / 2, top_y, buf);
|
||||
} else {
|
||||
d.setCursor(0, top_y);
|
||||
d.print(buf);
|
||||
}
|
||||
d.setTextSize(1);
|
||||
return top_y + lh2 + 2;
|
||||
}
|
||||
|
||||
// ── HomeScreen ────────────────────────────────────────────────────────────────
|
||||
// Forward declaration to be able to call formatDashVal from HomeScreen::render()
|
||||
static void formatDashVal(uint8_t field, char* val, int val_len, uint16_t batt_mv,
|
||||
uint16_t low_batt_mv, int unread, CayenneLPP* lpp = nullptr);
|
||||
|
||||
class HomeScreen : public UIScreen {
|
||||
enum HomePage {
|
||||
CLOCK,
|
||||
@@ -297,7 +320,8 @@ class HomeScreen : public UIScreen {
|
||||
TOOLS,
|
||||
QUICK_MSG,
|
||||
SHUTDOWN,
|
||||
Count // keep as last
|
||||
Count, // keep before LOCK — navigable-page count
|
||||
LOCK // lock screen: full clock + dashboard view, not a navigable page
|
||||
};
|
||||
|
||||
// Selected slot on the Favourites page (0..FAVOURITES_COUNT - 1).
|
||||
@@ -322,6 +346,7 @@ class HomeScreen : public UIScreen {
|
||||
SensorManager* _sensors;
|
||||
NodePrefs* _node_prefs;
|
||||
uint8_t _page;
|
||||
uint8_t _prev_page; // home page restored when the device unlocks
|
||||
bool _shutdown_init;
|
||||
|
||||
int pageBit(int page) const {
|
||||
@@ -530,7 +555,17 @@ class HomeScreen : public UIScreen {
|
||||
public:
|
||||
HomeScreen(UITask* task, mesh::RTCClock* rtc, SensorManager* sensors, NodePrefs* node_prefs)
|
||||
: _task(task), _rtc(rtc), _sensors(sensors), _node_prefs(node_prefs), _page(0),
|
||||
_shutdown_init(false), sensors_lpp(200) { }
|
||||
_prev_page(0), _shutdown_init(false), sensors_lpp(200) { }
|
||||
|
||||
// Flips home screen between LOCK page and the page that was showing before locking
|
||||
void setLocked(bool locked) {
|
||||
if (locked) {
|
||||
if (_page != LOCK) _prev_page = _page;
|
||||
_page = LOCK;
|
||||
} else {
|
||||
_page = _prev_page;
|
||||
}
|
||||
}
|
||||
|
||||
void poll() override {
|
||||
if (_shutdown_init && !_task->isButtonPressed()) { // must wait for USR button to be released
|
||||
@@ -709,25 +744,30 @@ public:
|
||||
const int dots_y = lh + pg_half + 1; // icon-row centre, below the header
|
||||
const int content_y = dots_y + pg_half + 3; // first content row, below the icons
|
||||
|
||||
// node name + battery — hidden on CLOCK page (full screen used for dashboard)
|
||||
// Title bar displaying node name (except on lock screen), status icons and battery.
|
||||
// Hidden on fullscreen pages (CLOCK).
|
||||
if (_page != CLOCK) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
char filtered_name[sizeof(_node_prefs->node_name)];
|
||||
display.translateUTF8ToBlocks(filtered_name, _node_prefs->node_name, sizeof(filtered_name));
|
||||
int rightEdge = renderBatteryIndicator(display, _task->getBattMilliVolts());
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
// Only show the live-power readout when APC is actually controlling power —
|
||||
// not merely when the pref is set. While repeating APC is suppressed and
|
||||
// power is pinned to the ceiling, so apcActive() is false and the name bar
|
||||
// drops the readout (matching the "--" lock in Settings).
|
||||
if (the_mesh.apcActive()) {
|
||||
char pwr_buf[8];
|
||||
snprintf(pwr_buf, sizeof(pwr_buf), "%ddB", (int)radio_driver.getTxPower());
|
||||
int pwr_w = display.getTextWidth(pwr_buf);
|
||||
display.drawTextEllipsized(0, 0, rightEdge - 2 - pwr_w - 2, filtered_name);
|
||||
display.drawTextRightAlign(rightEdge - 2, 0, pwr_buf);
|
||||
} else {
|
||||
display.drawTextEllipsized(0, 0, rightEdge - 2, filtered_name);
|
||||
|
||||
if (_page != LOCK) {
|
||||
char filtered_name[sizeof(_node_prefs->node_name)];
|
||||
display.translateUTF8ToBlocks(filtered_name, _node_prefs->node_name, sizeof(filtered_name));
|
||||
|
||||
// Only show the live-power readout when APC is actually controlling power —
|
||||
// not merely when the pref is set. While repeating APC is suppressed and
|
||||
// power is pinned to the ceiling, so apcActive() is false and the name bar
|
||||
// drops the readout (matching the "--" lock in Settings).
|
||||
if (the_mesh.apcActive()) {
|
||||
char pwr_buf[8];
|
||||
snprintf(pwr_buf, sizeof(pwr_buf), "%ddB", (int)radio_driver.getTxPower());
|
||||
int pwr_w = display.getTextWidth(pwr_buf);
|
||||
display.drawTextEllipsized(0, 0, rightEdge - 2 - pwr_w - 2, filtered_name);
|
||||
display.drawTextRightAlign(rightEdge - 2, 0, pwr_buf);
|
||||
} else {
|
||||
display.drawTextEllipsized(0, 0, rightEdge - 2, filtered_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -735,8 +775,9 @@ public:
|
||||
if (!isPageVisible(_page)) _page = navPage(_page, +1);
|
||||
|
||||
// curr page indicator — a row of small page icons, one per visible page, with
|
||||
// the current page underlined. Hidden on CLOCK (full screen used for dashboard).
|
||||
if (_page != CLOCK) {
|
||||
// the current page underlined. Hidden on CLOCK and LOCK (full screen used for
|
||||
// the clock/dashboard).
|
||||
if (_page != CLOCK && _page != LOCK) {
|
||||
int order[(int)Count]; int n = buildVisibleOrder(order);
|
||||
int curr_vis = 0;
|
||||
for (int i = 0; i < n; i++) if (order[i] == _page) { curr_vis = i; break; }
|
||||
@@ -776,7 +817,7 @@ public:
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
bool show_sec = !Features::IS_EINK && (!_node_prefs || !_node_prefs->clock_hide_seconds);
|
||||
bool h12 = _node_prefs && _node_prefs->clock_12h;
|
||||
int date_y = drawClockTime(display, 0, ti, h12, show_sec);
|
||||
int date_y = drawClockTime(display, 0, ti, h12, show_sec, true);
|
||||
|
||||
display.setTextSize(1);
|
||||
static const char* wd[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
|
||||
@@ -883,6 +924,79 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (_page == HomePage::LOCK) {
|
||||
// Lock screen: clock + two dashboard spots + unlock-hint popup
|
||||
uint32_t unix_ts = _rtc->getCurrentTime();
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.setTextSize(1);
|
||||
if (unix_ts < 1000000000UL) {
|
||||
display.drawTextCentered(display.width() / 2, display.height() / 2 - step, "No time sync");
|
||||
} else {
|
||||
int8_t tz = _node_prefs ? _node_prefs->tz_offset_hours : 0;
|
||||
unix_ts += (int32_t)tz * 3600;
|
||||
time_t t = (time_t)unix_ts;
|
||||
struct tm* ti = gmtime(&t);
|
||||
char buf[12];
|
||||
bool h12 = _node_prefs && _node_prefs->clock_12h;
|
||||
int date_y = drawClockTime(display, 0, ti, h12, /*show_sec*/false);
|
||||
display.setTextSize(1);
|
||||
static const char* wd[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
|
||||
static const char* mo[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
|
||||
snprintf(buf, sizeof(buf),"%s %d %s", wd[ti->tm_wday], ti->tm_mday, mo[ti->tm_mon]);
|
||||
display.setCursor(0, date_y);
|
||||
display.print(buf);
|
||||
|
||||
// Two sensor values side by side (dashboard_fields[0] and [1])
|
||||
if (_node_prefs) {
|
||||
char v0[20] = "", v1[20] = "";
|
||||
CayenneLPP* lpp_ptr = nullptr;
|
||||
uint8_t f0 = _node_prefs->dashboard_fields[0], f1 = _node_prefs->dashboard_fields[1];
|
||||
auto isLPP = [](uint8_t f) {
|
||||
return f==DASH_TEMP||f==DASH_HUM||f==DASH_PRES||f==DASH_ALT||f==DASH_LUX||f==DASH_CO2;
|
||||
};
|
||||
if (isLPP(f0) || isLPP(f1)) {
|
||||
sensors_lpp.reset(); sensors.querySensors(0xFF, sensors_lpp); lpp_ptr = &sensors_lpp;
|
||||
}
|
||||
int unread = (f0 == DASH_MSGS || f1 == DASH_MSGS)
|
||||
? _task->getDMUnreadTotal() + _task->getChannelUnreadCount() + _task->getRoomUnreadCount() : 0;
|
||||
uint16_t batt_mv = _task->getBattMilliVolts();
|
||||
formatDashVal(f0, v0, sizeof(v0), batt_mv, _node_prefs->low_batt_mv, unread, lpp_ptr);
|
||||
formatDashVal(f1, v1, sizeof(v1), batt_mv, _node_prefs->low_batt_mv, unread, lpp_ptr);
|
||||
if (v0[0] || v1[0]) {
|
||||
int sv_y = date_y + step;
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
if (v0[0] && v1[0]) {
|
||||
display.setCursor(0, sv_y);
|
||||
display.print(v0);
|
||||
int vw = display.getTextWidth(v1);
|
||||
display.setCursor(display.width() - vw, sv_y);
|
||||
display.print(v1);
|
||||
} else {
|
||||
const char* sv = v0[0] ? v0 : v1;
|
||||
display.drawTextCentered(display.width() / 2, sv_y, sv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Unlock-hint popup at the bottom (like alert style)
|
||||
display.setTextSize(1);
|
||||
const int lk_lh = display.getLineHeight();
|
||||
#if defined(CARDKB_I2C)
|
||||
const char* hint = _task->lockSeqCount() == 0 ? (_task->hasCardKB() ? "Back+3xEnter/Fn+Esc" : "Hold Back + 3xEnter") :
|
||||
_task->lockSeqCount() == 1 ? "Enter x2 more..." : "Enter x1 more...";
|
||||
#else
|
||||
const char* hint = _task->lockSeqCount() == 0 ? "Hold Back + 3xEnter" :
|
||||
_task->lockSeqCount() == 1 ? "Enter x2 more..." : "Enter x1 more...";
|
||||
#endif
|
||||
const int p = 3;
|
||||
const int hy = display.height() - lk_lh - p * 2;
|
||||
const int hw = display.getTextWidth(hint);
|
||||
const int hx = (display.width() - hw) / 2;
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillRect(hx - p, hy - p, hw + p*2, lk_lh + p*2);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
display.setCursor(hx, hy);
|
||||
display.print(hint);
|
||||
} else if (_page == HomePage::RADIO) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
// freq / sf
|
||||
@@ -1416,7 +1530,12 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
||||
pinMode(PIN_HALL_SENSOR, HALL_ACTIVE_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
|
||||
_hall_magnet_present = HALL_ACTIVE_HIGH ? (digitalRead(PIN_HALL_SENSOR) == HIGH)
|
||||
: (digitalRead(PIN_HALL_SENSOR) == LOW);
|
||||
if (_hall_magnet_present) _locked = true; // booting with the cover already closed
|
||||
|
||||
// Handle booting with the cover already closed
|
||||
if (_hall_magnet_present) {
|
||||
_locked = true;
|
||||
syncLockToHome();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PIN_USER_BTN)
|
||||
@@ -1472,7 +1591,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
||||
if (f) { _waypoints.readFrom(f); f.close(); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Initialize ping state
|
||||
_ping_active = false;
|
||||
_ping_tag = 0;
|
||||
@@ -1483,6 +1602,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
||||
|
||||
splash = new SplashScreen(this);
|
||||
home = new HomeScreen(this, &rtc_clock, sensors, node_prefs);
|
||||
syncLockToHome(); // booted locked (e.g. cover closed) → home starts on the LOCK page
|
||||
settings = new SettingsScreen(this, &_kb);
|
||||
messages_screen = new MessagesScreen(this, &_kb);
|
||||
tools_screen = new ToolsScreen(this);
|
||||
@@ -2001,6 +2121,11 @@ void UITask::setCurrScreen(UIScreen* c) {
|
||||
_next_refresh = 100;
|
||||
}
|
||||
|
||||
void UITask::syncLockToHome() {
|
||||
// Lock/unlock switches the home screen's page to LOCK (or back)
|
||||
if (home) static_cast<HomeScreen*>(home)->setLocked(_locked);
|
||||
}
|
||||
|
||||
bool UITask::savePrefsIfDirty(bool& dirty) {
|
||||
if (!dirty) return false;
|
||||
the_mesh.savePrefs();
|
||||
@@ -2088,7 +2213,7 @@ bool UITask::isButtonPressed() const {
|
||||
}
|
||||
|
||||
static void formatDashVal(uint8_t field, char* val, int val_len, uint16_t batt_mv,
|
||||
uint16_t low_batt_mv, int unread, CayenneLPP* lpp = nullptr) {
|
||||
uint16_t low_batt_mv, int unread, CayenneLPP* lpp) {
|
||||
val[0] = '\0';
|
||||
switch (field) {
|
||||
case DASH_NONE: return;
|
||||
@@ -2338,6 +2463,7 @@ void UITask::pollCardKB() {
|
||||
uint32_t aoff = autoOffMillis();
|
||||
if (aoff > 0) _auto_off = millis() + aoff;
|
||||
}
|
||||
syncLockToHome();
|
||||
_next_refresh = 0;
|
||||
return;
|
||||
} else if (raw >= 0x80 && raw <= 0xAF) { // Fn+<letter> -- open its accent popup
|
||||
@@ -2393,6 +2519,7 @@ void UITask::pollHallSensor() {
|
||||
|
||||
if (present) { // cover closed
|
||||
_locked = true;
|
||||
syncLockToHome();
|
||||
_lock_wake_until = 0;
|
||||
if (_display) _display->turnOff();
|
||||
#ifdef PIN_LED
|
||||
@@ -2400,6 +2527,7 @@ void UITask::pollHallSensor() {
|
||||
#endif
|
||||
} else { // cover opened
|
||||
_locked = false;
|
||||
syncLockToHome();
|
||||
if (_display && !_display->isOn()) _display->turnOn();
|
||||
uint32_t aoff = autoOffMillis();
|
||||
if (aoff > 0) _auto_off = millis() + aoff;
|
||||
@@ -2438,6 +2566,7 @@ void UITask::loop() {
|
||||
uint32_t aoff = autoOffMillis();
|
||||
if (aoff > 0) _auto_off = millis() + aoff;
|
||||
}
|
||||
syncLockToHome();
|
||||
}
|
||||
// eat the Enter — don't pass to curr
|
||||
} else {
|
||||
@@ -2669,80 +2798,9 @@ void UITask::loop() {
|
||||
if (_display != NULL && _display->isOn()) {
|
||||
if (_locked && (int32_t)(millis() - _lock_wake_until) >= 0) {
|
||||
_display->turnOff();
|
||||
} else if (_locked && millis() >= _next_refresh) {
|
||||
} else if (_locked && millis() >= _next_refresh && home) {
|
||||
_display->startFrame();
|
||||
// Lock screen: clock + unlock hint popup
|
||||
uint32_t unix_ts = rtc_clock.getCurrentTime();
|
||||
_display->setColor(DisplayDriver::LIGHT);
|
||||
_display->setTextSize(1);
|
||||
const int lk_lh = _display->getLineHeight();
|
||||
const int lk_step = _display->lineStep();
|
||||
if (unix_ts < 1000000000UL) {
|
||||
_display->drawTextCentered(_display->width() / 2, _display->height() / 2 - lk_step, "No time sync");
|
||||
} else {
|
||||
int8_t tz = _node_prefs ? _node_prefs->tz_offset_hours : 0;
|
||||
unix_ts += (int32_t)tz * 3600;
|
||||
time_t t = (time_t)unix_ts;
|
||||
struct tm* ti = gmtime(&t);
|
||||
char buf[12];
|
||||
const int clk_y = 2;
|
||||
bool h12 = _node_prefs && _node_prefs->clock_12h;
|
||||
int date_y = drawClockTime(*_display, clk_y, ti, h12, /*show_sec*/false);
|
||||
_display->setTextSize(1);
|
||||
static const char* wd[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
|
||||
static const char* mo[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
|
||||
snprintf(buf, sizeof(buf),"%s %d %s", wd[ti->tm_wday], ti->tm_mday, mo[ti->tm_mon]);
|
||||
_display->drawTextCentered(_display->width() / 2, date_y, buf);
|
||||
|
||||
// Two sensor values side by side (dashboard_fields[0] and [1])
|
||||
if (_node_prefs) {
|
||||
char v0[20] = "", v1[20] = "";
|
||||
CayenneLPP* lpp_ptr = nullptr;
|
||||
uint8_t f0 = _node_prefs->dashboard_fields[0], f1 = _node_prefs->dashboard_fields[1];
|
||||
auto isLPP = [](uint8_t f) {
|
||||
return f==DASH_TEMP||f==DASH_HUM||f==DASH_PRES||f==DASH_ALT||f==DASH_LUX||f==DASH_CO2;
|
||||
};
|
||||
if (isLPP(f0) || isLPP(f1)) {
|
||||
_dash_lpp.reset(); sensors.querySensors(0xFF, _dash_lpp); lpp_ptr = &_dash_lpp;
|
||||
}
|
||||
int unread = (f0 == DASH_MSGS || f1 == DASH_MSGS)
|
||||
? getDMUnreadTotal() + getChannelUnreadCount() + getRoomUnreadCount() : 0;
|
||||
formatDashVal(f0, v0, sizeof(v0), _batt_mv, _node_prefs->low_batt_mv, unread, lpp_ptr);
|
||||
formatDashVal(f1, v1, sizeof(v1), _batt_mv, _node_prefs->low_batt_mv, unread, lpp_ptr);
|
||||
if (v0[0] || v1[0]) {
|
||||
int sv_y = date_y + lk_step;
|
||||
_display->setColor(DisplayDriver::LIGHT);
|
||||
if (v0[0] && v1[0]) {
|
||||
_display->setCursor(0, sv_y);
|
||||
_display->print(v0);
|
||||
int vw = _display->getTextWidth(v1);
|
||||
_display->setCursor(_display->width() - vw, sv_y);
|
||||
_display->print(v1);
|
||||
} else {
|
||||
const char* sv = v0[0] ? v0 : v1;
|
||||
_display->drawTextCentered(_display->width() / 2, sv_y, sv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Hint popup at bottom (like alert style)
|
||||
_display->setTextSize(1);
|
||||
#if defined(CARDKB_I2C)
|
||||
const char* hint = _lock_seq_count == 0 ? (_has_cardkb ? "Back+3xEnter/Fn+Esc" : "Hold Back + 3xEnter") :
|
||||
_lock_seq_count == 1 ? "Enter x2 more..." : "Enter x1 more...";
|
||||
#else
|
||||
const char* hint = _lock_seq_count == 0 ? "Hold Back + 3xEnter" :
|
||||
_lock_seq_count == 1 ? "Enter x2 more..." : "Enter x1 more...";
|
||||
#endif
|
||||
int p = 3;
|
||||
int hy = _display->height() - lk_lh - p * 2;
|
||||
int hw = _display->getTextWidth(hint);
|
||||
int hx = (_display->width() - hw) / 2;
|
||||
_display->setColor(DisplayDriver::LIGHT);
|
||||
_display->fillRect(hx - p, hy - p, hw + p*2, lk_lh + p*2);
|
||||
_display->setColor(DisplayDriver::DARK);
|
||||
_display->setCursor(hx, hy);
|
||||
_display->print(hint);
|
||||
home->render(*_display);
|
||||
// Alert overlay on top — without this a ringing alarm on a locked device
|
||||
// played its melody against a screen that never said what was ringing.
|
||||
if (millis() < _alert_expiry) renderAlertOverlay();
|
||||
@@ -2789,6 +2847,7 @@ void UITask::loop() {
|
||||
if (_node_prefs && _node_prefs->auto_lock) {
|
||||
_locked = true;
|
||||
_lock_wake_until = 0;
|
||||
syncLockToHome();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -114,7 +114,6 @@ class UITask : public AbstractUITask {
|
||||
UIScreen* gpio_screen = nullptr;
|
||||
#endif
|
||||
UIScreen* curr = nullptr;
|
||||
CayenneLPP _dash_lpp;
|
||||
TrailStore _trail;
|
||||
WaypointStore _waypoints;
|
||||
LiveTrackStore _livetrack;
|
||||
@@ -280,6 +279,11 @@ private:
|
||||
|
||||
void setCurrScreen(UIScreen* c);
|
||||
|
||||
// Mirror _locked into HomeScreen's LOCK page so the lock screen renders via
|
||||
// the normal home render() path (top-right status bar included) instead of a
|
||||
// dedicated lock-screen code path in loop().
|
||||
void syncLockToHome();
|
||||
|
||||
// Centred alert overlay (the showAlert() box). Wraps long text to up to
|
||||
// three lines inside the box instead of letting it overflow the border.
|
||||
// Shared by the normal render path and the lock screen (so a ringing
|
||||
@@ -288,7 +292,7 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
UITask(mesh::MainBoard* board, BaseSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL), _node_prefs(NULL), _dash_lpp(200) {
|
||||
UITask(mesh::MainBoard* board, BaseSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL), _node_prefs(NULL) {
|
||||
next_batt_chck = _next_refresh = 0;
|
||||
ui_started_at = 0;
|
||||
_batt_mv = 0;
|
||||
@@ -524,6 +528,17 @@ public:
|
||||
}
|
||||
bool isButtonPressed() const;
|
||||
|
||||
// Lock-screen support: the HomeScreen LOCK page draws the unlock hint from
|
||||
// these (the platform combo is either "Back+3xEnter" or the CardKB nybble).
|
||||
int lockSeqCount() const { return _lock_seq_count; }
|
||||
bool hasCardKB() const {
|
||||
#if defined(CARDKB_I2C)
|
||||
return _has_cardkb;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isBuzzerQuiet() {
|
||||
#ifdef PIN_BUZZER
|
||||
return buzzer.isQuiet();
|
||||
|
||||
Reference in New Issue
Block a user