feat: Add password input for screen unlock

This commit is contained in:
3urobeat
2026-09-06 16:25:03 +02:00
parent 06e1dcdbfc
commit b710910225
3 changed files with 116 additions and 21 deletions
+92 -20
View File
@@ -2212,6 +2212,73 @@ void UITask::syncLockToHome() {
if (home) static_cast<HomeScreen*>(home)->setLocked(_locked);
}
bool UITask::passwordLockEnabled() const {
// Is lock_screen_password setting not empty?
return _node_prefs && _node_prefs->lock_screen_password[0] != '\0';
}
void UITask::beginUnlockPrompt() {
_unlock_kb = true; // Track that keyboard is visible and is waiting for input
int max_len = _node_prefs ? (int)sizeof(_node_prefs->lock_screen_password) - 1 : 32;
_kb.begin("", max_len);
_kb.clearPlaceholders();
_lock_wake_until = millis() + 5000; // keep the display on while typing
_next_refresh = 0;
}
void UITask::cancelUnlockPrompt() {
_unlock_kb = false;
_next_refresh = 100;
}
void UITask::handleUnlockKey(char c) {
auto res = _kb.handleInput(c);
if (res == KeyboardWidget::DONE) { // Process input on submit
if (_node_prefs && strcmp(_kb.buf, _node_prefs->lock_screen_password) == 0) {
// Match: Unlock
_unlock_kb = false;
_locked = false;
if (_display && !_display->isOn()) _display->turnOn();
uint32_t aoff = autoOffMillis();
if (aoff > 0) _auto_off = millis() + aoff;
syncLockToHome();
} else {
// Invalid: Clear input and reset keyboard
_kb.len = 0;
_kb.buf[0] = '\0';
_kb.cursor_pos = 0;
_kb.page = _kb.row = _kb.col = 0;
_kb.caps = _kb.caps_lock = false;
_next_refresh = 0;
}
} else if (res == KeyboardWidget::CANCELLED) {
cancelUnlockPrompt();
}
_lock_wake_until = millis() + 5000; // keep the display on while typing
}
void UITask::toggleLock() {
if (_unlock_kb) {
cancelUnlockPrompt();
return;
}
if (_locked) {
if (passwordLockEnabled()) { // Prompt for password
beginUnlockPrompt();
} else { // ...or unlock instantly
_locked = false;
if (_display && !_display->isOn()) _display->turnOn();
uint32_t aoff = autoOffMillis();
if (aoff > 0) _auto_off = millis() + aoff;
}
} else { // Device is currently unlocked -> lock it
_locked = true;
_lock_wake_until = millis() + 2000; // Briefly show lockscreen before blanking
}
syncLockToHome();
_next_refresh = 0;
}
bool UITask::savePrefsIfDirty(bool& dirty) {
if (!dirty) return false;
the_mesh.savePrefs();
@@ -2561,15 +2628,7 @@ void UITask::pollCardKB() {
// two-key combo, so it doesn't need the physical combo's extra 3x
// repetition to guard against accidental triggering.
if (_display && !_display->isOn()) _display->turnOn();
_locked = !_locked;
if (_locked) {
_lock_wake_until = millis() + 2000;
} else {
uint32_t aoff = autoOffMillis();
if (aoff > 0) _auto_off = millis() + aoff;
}
syncLockToHome();
_next_refresh = 0;
toggleLock();
return;
} else if (raw >= 0x80 && raw <= 0xAF) { // Fn+<letter> -- open its accent popup
char base = CARDKB_FN_BASE[raw - 0x80];
@@ -2623,6 +2682,7 @@ void UITask::pollHallSensor() {
_hall_magnet_present = present;
if (present) { // cover closed
cancelUnlockPrompt(); // a cover can't be over the password prompt
_locked = true;
syncLockToHome();
_lock_wake_until = 0;
@@ -2663,15 +2723,7 @@ void UITask::loop() {
if (_lock_seq_count >= 3) {
_lock_seq_count = 0;
_lock_seq_used = true; // suppress Back release click
_locked = !_locked;
if (_locked) {
_lock_wake_until = millis() + 2000;
} else {
if (_display && !_display->isOn()) _display->turnOn();
uint32_t aoff = autoOffMillis();
if (aoff > 0) _auto_off = millis() + aoff;
}
syncLockToHome();
toggleLock();
}
// eat the Enter — don't pass to curr
} else {
@@ -2864,7 +2916,12 @@ void UITask::loop() {
}
if (_kq_head != _kq_tail) {
if (!_locked && curr) {
if (_unlock_kb) {
// Lock-screen password keyboard: Consume every key press
char k;
while (dequeueKey(k)) handleUnlockKey(k);
_next_refresh = 100; // redraw immediately after key press
} else if (!_locked && curr) {
// Apply the whole queued burst, then redraw once — N taps captured during
// a blocking refresh become N navigation steps at the cost of one refresh.
char k;
@@ -2901,8 +2958,22 @@ void UITask::loop() {
tickClockTools();
if (_display != NULL && _display->isOn()) {
if (_locked && (int32_t)(millis() - _lock_wake_until) >= 0) {
// Lock-screen password prompt
if (_locked && _unlock_kb && (int32_t)(millis() - _lock_wake_until) >= 0) {
cancelUnlockPrompt(); // Cancel unlock attempt on idle
_next_refresh = 0;
}
if (_locked && !_unlock_kb && (int32_t)(millis() - _lock_wake_until) >= 0) {
_display->turnOff();
} else if (_locked && _unlock_kb && millis() >= _next_refresh) {
// While the prompt is up the password keyboard replaces the lockscreen view
_display->startFrame();
_kb.beginFrame();
int delay_millis = _kb.render(*_display);
// Alert overlay on top // TODO: Is this necessary here?
// if (millis() < _alert_expiry) renderAlertOverlay();
_display->endFrame();
_next_refresh = millis() + delay_millis;
} else if (_locked && millis() >= _next_refresh && home) {
_display->startFrame();
home->render(*_display);
@@ -2950,6 +3021,7 @@ void UITask::loop() {
digitalWrite(PIN_LED, LOW); // turn off status LED with display to save power
#endif
if (_node_prefs && _node_prefs->auto_lock) {
cancelUnlockPrompt(); // idle-lock isn't a password prompt
_locked = true;
_lock_wake_until = 0;
syncLockToHome();
+12 -1
View File
@@ -58,6 +58,8 @@ class UITask : public AbstractUITask {
int _lock_seq_count; // Enter presses while Back held (lock/unlock sequence)
unsigned long _lock_seq_ms; // millis() of last lock-sequence press (for timeout)
bool _lock_seq_used; // true = suppress next back_btn CLICK (post-sequence release)
// True while the lock screen shows the on-screen keyboard and waits for submission
bool _unlock_kb = false;
char _alert[80];
char _notif_mel_buf[220]; // persistent RTTTL buffer for custom notification melodies
// Persistent RTTTL buffer for the bot !buzz command (see botBuzz()) -- sized
@@ -288,6 +290,15 @@ private:
// dedicated lock-screen code path in loop().
void syncLockToHome();
// Whether a lockscreen password is set
bool passwordLockEnabled() const;
// Handles (un)locking and requesting password input when one is set
void toggleLock();
void beginUnlockPrompt();
void cancelUnlockPrompt();
// Handles shortcuts during lockscreen password input
void handleUnlockKey(char c);
// 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
@@ -566,7 +577,7 @@ public:
#endif
}
bool isBuzzerQuiet() {
bool isBuzzerQuiet() {
#ifdef PIN_BUZZER
return buzzer.isQuiet();
#else