mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-10-18 11:32:11 +00:00
feat: Add password input for screen unlock
This commit is contained in:
@@ -63,3 +63,15 @@ Fully autonomous, independent of Auto-lock and of any key combo:
|
|||||||
|
|
||||||
- **Magnet near (cover closed)** — locks and blanks the display immediately, no wake grace.
|
- **Magnet near (cover closed)** — locks and blanks the display immediately, no wake grace.
|
||||||
- **Magnet away (cover opened)** — unlocks and wakes the display right away.
|
- **Magnet away (cover opened)** — unlocks and wakes the display right away.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Lock-screen password
|
||||||
|
|
||||||
|
Enable **LockPass** in **Settings › Display** to require a password when unlocking the device.
|
||||||
|
|
||||||
|
Upon enabling the setting the keyboard will show, requiring you to submit a new password.
|
||||||
|
Attempting to unlock the device will then require the same password to be typed and submitted to reach the home screen.
|
||||||
|
Submitting an incorrect password clears the keyboard input.
|
||||||
|
|
||||||
|
Disabling the setting clears the stored password and allows an instant unlock again.
|
||||||
|
|||||||
@@ -2212,6 +2212,73 @@ void UITask::syncLockToHome() {
|
|||||||
if (home) static_cast<HomeScreen*>(home)->setLocked(_locked);
|
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) {
|
bool UITask::savePrefsIfDirty(bool& dirty) {
|
||||||
if (!dirty) return false;
|
if (!dirty) return false;
|
||||||
the_mesh.savePrefs();
|
the_mesh.savePrefs();
|
||||||
@@ -2561,15 +2628,7 @@ void UITask::pollCardKB() {
|
|||||||
// two-key combo, so it doesn't need the physical combo's extra 3x
|
// two-key combo, so it doesn't need the physical combo's extra 3x
|
||||||
// repetition to guard against accidental triggering.
|
// repetition to guard against accidental triggering.
|
||||||
if (_display && !_display->isOn()) _display->turnOn();
|
if (_display && !_display->isOn()) _display->turnOn();
|
||||||
_locked = !_locked;
|
toggleLock();
|
||||||
if (_locked) {
|
|
||||||
_lock_wake_until = millis() + 2000;
|
|
||||||
} else {
|
|
||||||
uint32_t aoff = autoOffMillis();
|
|
||||||
if (aoff > 0) _auto_off = millis() + aoff;
|
|
||||||
}
|
|
||||||
syncLockToHome();
|
|
||||||
_next_refresh = 0;
|
|
||||||
return;
|
return;
|
||||||
} else if (raw >= 0x80 && raw <= 0xAF) { // Fn+<letter> -- open its accent popup
|
} else if (raw >= 0x80 && raw <= 0xAF) { // Fn+<letter> -- open its accent popup
|
||||||
char base = CARDKB_FN_BASE[raw - 0x80];
|
char base = CARDKB_FN_BASE[raw - 0x80];
|
||||||
@@ -2623,6 +2682,7 @@ void UITask::pollHallSensor() {
|
|||||||
_hall_magnet_present = present;
|
_hall_magnet_present = present;
|
||||||
|
|
||||||
if (present) { // cover closed
|
if (present) { // cover closed
|
||||||
|
cancelUnlockPrompt(); // a cover can't be over the password prompt
|
||||||
_locked = true;
|
_locked = true;
|
||||||
syncLockToHome();
|
syncLockToHome();
|
||||||
_lock_wake_until = 0;
|
_lock_wake_until = 0;
|
||||||
@@ -2663,15 +2723,7 @@ void UITask::loop() {
|
|||||||
if (_lock_seq_count >= 3) {
|
if (_lock_seq_count >= 3) {
|
||||||
_lock_seq_count = 0;
|
_lock_seq_count = 0;
|
||||||
_lock_seq_used = true; // suppress Back release click
|
_lock_seq_used = true; // suppress Back release click
|
||||||
_locked = !_locked;
|
toggleLock();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
// eat the Enter — don't pass to curr
|
// eat the Enter — don't pass to curr
|
||||||
} else {
|
} else {
|
||||||
@@ -2864,7 +2916,12 @@ void UITask::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_kq_head != _kq_tail) {
|
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
|
// 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.
|
// a blocking refresh become N navigation steps at the cost of one refresh.
|
||||||
char k;
|
char k;
|
||||||
@@ -2901,8 +2958,22 @@ void UITask::loop() {
|
|||||||
tickClockTools();
|
tickClockTools();
|
||||||
|
|
||||||
if (_display != NULL && _display->isOn()) {
|
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();
|
_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) {
|
} else if (_locked && millis() >= _next_refresh && home) {
|
||||||
_display->startFrame();
|
_display->startFrame();
|
||||||
home->render(*_display);
|
home->render(*_display);
|
||||||
@@ -2950,6 +3021,7 @@ void UITask::loop() {
|
|||||||
digitalWrite(PIN_LED, LOW); // turn off status LED with display to save power
|
digitalWrite(PIN_LED, LOW); // turn off status LED with display to save power
|
||||||
#endif
|
#endif
|
||||||
if (_node_prefs && _node_prefs->auto_lock) {
|
if (_node_prefs && _node_prefs->auto_lock) {
|
||||||
|
cancelUnlockPrompt(); // idle-lock isn't a password prompt
|
||||||
_locked = true;
|
_locked = true;
|
||||||
_lock_wake_until = 0;
|
_lock_wake_until = 0;
|
||||||
syncLockToHome();
|
syncLockToHome();
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ class UITask : public AbstractUITask {
|
|||||||
int _lock_seq_count; // Enter presses while Back held (lock/unlock sequence)
|
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)
|
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)
|
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 _alert[80];
|
||||||
char _notif_mel_buf[220]; // persistent RTTTL buffer for custom notification melodies
|
char _notif_mel_buf[220]; // persistent RTTTL buffer for custom notification melodies
|
||||||
// Persistent RTTTL buffer for the bot !buzz command (see botBuzz()) -- sized
|
// Persistent RTTTL buffer for the bot !buzz command (see botBuzz()) -- sized
|
||||||
@@ -288,6 +290,15 @@ private:
|
|||||||
// dedicated lock-screen code path in loop().
|
// dedicated lock-screen code path in loop().
|
||||||
void syncLockToHome();
|
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
|
// Centred alert overlay (the showAlert() box). Wraps long text to up to
|
||||||
// three lines inside the box instead of letting it overflow the border.
|
// 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
|
// Shared by the normal render path and the lock screen (so a ringing
|
||||||
@@ -566,7 +577,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isBuzzerQuiet() {
|
bool isBuzzerQuiet() {
|
||||||
#ifdef PIN_BUZZER
|
#ifdef PIN_BUZZER
|
||||||
return buzzer.isQuiet();
|
return buzzer.isQuiet();
|
||||||
#else
|
#else
|
||||||
|
|||||||
Reference in New Issue
Block a user