Skip to content

Commit 5f177cb

Browse files
BurninTurtlestgc-dkNeroBurnerp1gp1g
authored
InfiniTimeOrg#1359: Introduce a restricted clock-only-mode when not woken with the button (#24)
* SystemTask: implement screen locked until button is pressed When the device is woken through pressing the button everything is the same. But when woken through other means like single-tap or raise-to-wake, then the screen is locked (and showing a lock screen on touch input) until the button is pressed. Only exception is when the alarm wakes the screen, then touch input is still valid for the user to be able to press the red "stop alarm" button. Co-authored-by: NeroBurner <pyro4hell@gmail.com> * PopupMessage: little cleanup, more const * SystemTask: clang-format * Always hide lock popup when changing appview It fixes a potential double-free when removing lock popup * SystemTask: unlock at double button press as well, fixes segfault * SystemTask: hide lock before going to sleep * PopupMessage: update popup pointer when object is deleted Add a lvlg event handler when the `popup` object is deleted. This helps to prevent double free memory errors due to `PopupMessage` class not recognizing that the `popup` object was deleted by someone else (for example when switching away from a notification) --------- Co-authored-by: Tomas Groth <second@tgc.dk> Co-authored-by: NeroBurner <pyro4hell@gmail.com> Co-authored-by: sim <git@sgougeon.fr>
1 parent 8a9ccf2 commit 5f177cb

8 files changed

Lines changed: 138 additions & 2 deletions

File tree

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ list(APPEND SOURCE_FILES
403403
displayapp/widgets/PageIndicator.cpp
404404
displayapp/widgets/DotIndicator.cpp
405405
displayapp/widgets/StatusIcons.cpp
406+
displayapp/widgets/PopupMessage.cpp
406407

407408
## Settings
408409
displayapp/screens/settings/QuickSettings.cpp

src/displayapp/DisplayApp.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,12 @@ void DisplayApp::Refresh() {
485485
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
486486
motorController.RunForDuration(35);
487487
break;
488+
case Messages::ShowIgnoreTouchPopup:
489+
popupMessage.SetHidden(false);
490+
break;
491+
case Messages::HideIgnoreTouchPopup:
492+
popupMessage.SetHidden(true);
493+
break;
488494
}
489495
}
490496

@@ -508,6 +514,11 @@ void DisplayApp::LoadNewScreen(Apps app, DisplayApp::FullRefreshDirections direc
508514
// This is mainly to fix an issue with receiving two notifications at the same time
509515
// and shouldn't happen otherwise.
510516
if (app != currentApp) {
517+
// We need to remove the popup
518+
// If we keep the popup linked to the previous view, and this view is deleted, a bug will occur if we try to re-remove the popup.
519+
// Not removing the popup will also prevent the popup to be raised on top of
520+
// the new app
521+
popupMessage.SetHidden(true);
511522
returnAppStack.Push(currentApp);
512523
appStackDirections.Push(direction);
513524
}

src/displayapp/DisplayApp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "components/stopwatch/StopWatchController.h"
1717
#include "components/alarm/AlarmController.h"
1818
#include "touchhandler/TouchHandler.h"
19+
#include "displayapp/widgets/PopupMessage.h"
1920

2021
#include "displayapp/Messages.h"
2122
#include "BootErrors.h"
@@ -105,6 +106,7 @@ namespace Pinetime {
105106
Pinetime::Controllers::FirmwareValidator validator;
106107
Pinetime::Components::LittleVgl lvgl;
107108
Pinetime::Controllers::Timer timer;
109+
Pinetime::Applications::Widgets::PopupMessage popupMessage;
108110

109111
AppControllers controllers;
110112
TaskHandle_t taskHandle;

src/displayapp/Messages.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ namespace Pinetime {
2424
AlarmTriggered,
2525
Chime,
2626
BleRadioEnableToggle,
27+
ShowIgnoreTouchPopup,
28+
HideIgnoreTouchPopup
2729
};
2830
}
2931
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include "displayapp/widgets/PopupMessage.h"
2+
#include "displayapp/InfiniTimeTheme.h"
3+
#include <libraries/log/nrf_log.h>
4+
5+
using namespace Pinetime::Applications::Widgets;
6+
7+
namespace {
8+
void event_handler(lv_obj_t* obj, lv_event_t event) {
9+
auto* popupMessage = static_cast<PopupMessage*>(obj->user_data);
10+
if (event == LV_EVENT_DELETE) {
11+
popupMessage->HandleDelete(obj, event);
12+
}
13+
}
14+
}
15+
16+
void PopupMessage::HandleDelete(lv_obj_t* object, lv_event_t event) {
17+
if (object == popup && event == LV_EVENT_DELETE) {
18+
// make sure to update the state of the popup pointer when someone
19+
// deletes the popup object
20+
popup = nullptr;
21+
isHidden = true;
22+
}
23+
}
24+
25+
void PopupMessage::Create() {
26+
popup = lv_obj_create(lv_scr_act(), nullptr);
27+
lv_obj_set_size(popup, 90, 90);
28+
lv_obj_align(popup, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
29+
lv_obj_set_style_local_bg_color(popup, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, Colors::bg);
30+
lv_obj_set_style_local_bg_opa(popup, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_60);
31+
lv_obj_t* lockBody = lv_obj_create(popup, nullptr);
32+
lv_obj_set_size(lockBody, 55, 50);
33+
lv_obj_align(lockBody, popup, LV_ALIGN_CENTER, 0, 10);
34+
35+
lv_obj_set_style_local_bg_color(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
36+
lv_obj_set_style_local_bg_opa(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
37+
lv_obj_set_style_local_border_color(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
38+
lv_obj_set_style_local_border_width(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 22);
39+
lv_obj_set_style_local_border_side(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL);
40+
lv_obj_set_style_local_border_opa(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_100);
41+
42+
lv_obj_t* lockTop = lv_obj_create(popup, nullptr);
43+
lv_obj_set_size(lockTop, 30, 35);
44+
lv_obj_align(lockTop, popup, LV_ALIGN_CENTER, 0, -20);
45+
lv_obj_set_style_local_bg_color(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
46+
lv_obj_set_style_local_bg_opa(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
47+
lv_obj_set_style_local_border_color(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
48+
lv_obj_set_style_local_border_width(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 6);
49+
lv_obj_set_style_local_border_side(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL);
50+
lv_obj_set_style_local_border_opa(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_100);
51+
52+
// handler to keep popup pointer in sync
53+
popup->user_data = this;
54+
lv_obj_set_event_cb(popup, event_handler);
55+
56+
lv_obj_set_hidden(popup, isHidden);
57+
}
58+
59+
void PopupMessage::SetHidden(const bool hidden) {
60+
if (isHidden == hidden) {
61+
return;
62+
}
63+
isHidden = hidden;
64+
// create/delete on demand
65+
if (popup == nullptr && !isHidden) {
66+
Create();
67+
} else if (popup != nullptr) {
68+
lv_obj_del(popup);
69+
popup = nullptr;
70+
}
71+
}
72+
73+
bool PopupMessage::IsHidden() const {
74+
return isHidden;
75+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#pragma once
2+
#include <lvgl/lvgl.h>
3+
4+
namespace Pinetime::Applications::Widgets {
5+
class PopupMessage {
6+
public:
7+
PopupMessage() = default;
8+
void Create();
9+
void SetHidden(bool hidden);
10+
bool IsHidden() const;
11+
// public function only to be used by lvgl handler
12+
void HandleDelete(lv_obj_t* object, lv_event_t event);
13+
14+
private:
15+
lv_obj_t* popup = nullptr;
16+
bool isHidden = true;
17+
};
18+
}

src/systemtask/SystemTask.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ void SystemTask::Work() {
232232
}
233233
break;
234234
case Messages::SetOffAlarm:
235+
unlockedByButton = true; // unlock so it is possible to press red stop button
235236
GoToRunning();
236237
displayApp.PushMessage(Pinetime::Applications::Display::Messages::AlarmTriggered);
237238
break;
@@ -241,6 +242,7 @@ void SystemTask::Work() {
241242
bleDiscoveryTimer = 5;
242243
break;
243244
case Messages::BleFirmwareUpdateStarted:
245+
unlockedByButton = true; // prevent no screen-locked popup on firmware update
244246
GoToRunning();
245247
wakeLocksHeld++;
246248
displayApp.PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted);
@@ -268,7 +270,14 @@ void SystemTask::Work() {
268270
break;
269271
}
270272
if (state == SystemTaskState::Running) {
271-
displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent);
273+
if (unlockedByButton) {
274+
displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent);
275+
} else {
276+
auto gesture = touchHandler.GestureGet();
277+
if (gesture != Pinetime::Applications::TouchEvents::None) {
278+
displayApp.PushMessage(Pinetime::Applications::Display::Messages::ShowIgnoreTouchPopup);
279+
}
280+
}
272281
} else {
273282
// If asleep, check for touch panel wake triggers
274283
auto gesture = touchHandler.GestureGet();
@@ -290,6 +299,7 @@ void SystemTask::Work() {
290299
action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Press);
291300
// This is for faster wakeup, sacrificing special longpress and doubleclick handling while sleeping
292301
if (IsSleeping()) {
302+
unlockedByButton = true;
293303
fastWakeUpDone = true;
294304
GoToRunning();
295305
break;
@@ -331,6 +341,8 @@ void SystemTask::Work() {
331341
} else {
332342
state = SystemTaskState::AODSleeping;
333343
}
344+
// lock when going to sleep
345+
unlockedByButton = false;
334346
break;
335347
case Messages::OnNewDay:
336348
motionSensor.ResetStepCounter();
@@ -437,12 +449,14 @@ void SystemTask::GoToSleep() {
437449
return;
438450
}
439451
NRF_LOG_INFO("[systemtask] Going to sleep");
452+
displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup);
440453
if (settingsController.GetAlwaysOnDisplay()) {
441454
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToAOD);
442455
} else {
443456
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToSleep);
444457
}
445458
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::GoToSleep);
459+
unlockedByButton = false;
446460

447461
state = SystemTaskState::GoingToSleep;
448462
};
@@ -481,10 +495,21 @@ void SystemTask::HandleButtonAction(Controllers::ButtonActions action) {
481495
case Actions::Click:
482496
// If the first action after fast wakeup is a click, it should be ignored.
483497
if (!fastWakeUpDone) {
484-
displayApp.PushMessage(Applications::Display::Messages::ButtonPushed);
498+
if (!unlockedByButton) {
499+
// the first button event unlocks the touch input
500+
unlockedByButton = true;
501+
displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup);
502+
} else {
503+
displayApp.PushMessage(Applications::Display::Messages::ButtonPushed);
504+
}
485505
}
486506
break;
487507
case Actions::DoubleClick:
508+
if (!unlockedByButton) {
509+
// the first button event unlocks the touch input
510+
unlockedByButton = true;
511+
displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup);
512+
}
488513
displayApp.PushMessage(Applications::Display::Messages::ButtonDoubleClicked);
489514
break;
490515
case Actions::LongPress:

src/systemtask/SystemTask.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ namespace Pinetime {
144144
void UpdateMotion();
145145
static constexpr TickType_t batteryMeasurementPeriod = pdMS_TO_TICKS(10 * 60 * 1000);
146146

147+
bool unlockedByButton = true;
148+
147149
SystemMonitor monitor;
148150
};
149151
}

0 commit comments

Comments
 (0)