diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4a354df64..cb5e87b8b9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -403,6 +403,7 @@ list(APPEND SOURCE_FILES displayapp/widgets/PageIndicator.cpp displayapp/widgets/DotIndicator.cpp displayapp/widgets/StatusIcons.cpp + displayapp/widgets/PopupMessage.cpp ## Settings displayapp/screens/settings/QuickSettings.cpp diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 45a41032dd..3872655bdc 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -482,6 +482,12 @@ void DisplayApp::Refresh() { LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None); motorController.RunForDuration(35); break; + case Messages::ShowIgnoreTouchPopup: + popupMessage.SetHidden(false); + break; + case Messages::HideIgnoreTouchPopup: + popupMessage.SetHidden(true); + break; } } @@ -505,6 +511,11 @@ void DisplayApp::LoadNewScreen(Apps app, DisplayApp::FullRefreshDirections direc // This is mainly to fix an issue with receiving two notifications at the same time // and shouldn't happen otherwise. if (app != currentApp) { + // We need to remove the popup + // 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. + // Not removing the popup will also prevent the popup to be raised on top of + // the new app + popupMessage.SetHidden(true); returnAppStack.Push(currentApp); appStackDirections.Push(direction); } diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h index 016f91d3b6..dd1995d8b0 100644 --- a/src/displayapp/DisplayApp.h +++ b/src/displayapp/DisplayApp.h @@ -16,6 +16,7 @@ #include "components/stopwatch/StopWatchController.h" #include "components/alarm/AlarmController.h" #include "touchhandler/TouchHandler.h" +#include "displayapp/widgets/PopupMessage.h" #include "displayapp/Messages.h" #include "BootErrors.h" @@ -105,6 +106,7 @@ namespace Pinetime { Pinetime::Controllers::FirmwareValidator validator; Pinetime::Components::LittleVgl lvgl; Pinetime::Controllers::Timer timer; + Pinetime::Applications::Widgets::PopupMessage popupMessage; AppControllers controllers; TaskHandle_t taskHandle; diff --git a/src/displayapp/Messages.h b/src/displayapp/Messages.h index 1fcd72d278..76750a96e8 100644 --- a/src/displayapp/Messages.h +++ b/src/displayapp/Messages.h @@ -24,6 +24,8 @@ namespace Pinetime { AlarmTriggered, Chime, BleRadioEnableToggle, + ShowIgnoreTouchPopup, + HideIgnoreTouchPopup }; } } diff --git a/src/displayapp/widgets/PopupMessage.cpp b/src/displayapp/widgets/PopupMessage.cpp new file mode 100644 index 0000000000..56545f5143 --- /dev/null +++ b/src/displayapp/widgets/PopupMessage.cpp @@ -0,0 +1,53 @@ +#include "displayapp/widgets/PopupMessage.h" +#include "displayapp/InfiniTimeTheme.h" +#include + +using namespace Pinetime::Applications::Widgets; + +void PopupMessage::Create() { + popup = lv_obj_create(lv_scr_act(), nullptr); + lv_obj_set_size(popup, 90, 90); + lv_obj_align(popup, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_local_bg_color(popup, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, Colors::bg); + lv_obj_set_style_local_bg_opa(popup, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_60); + lv_obj_t* lockBody = lv_obj_create(popup, nullptr); + lv_obj_set_size(lockBody, 55, 50); + lv_obj_align(lockBody, popup, LV_ALIGN_CENTER, 0, 10); + + lv_obj_set_style_local_bg_color(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_bg_opa(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0); + lv_obj_set_style_local_border_color(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_border_width(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 22); + lv_obj_set_style_local_border_side(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL); + lv_obj_set_style_local_border_opa(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_100); + + lv_obj_t* lockTop = lv_obj_create(popup, nullptr); + lv_obj_set_size(lockTop, 30, 35); + lv_obj_align(lockTop, popup, LV_ALIGN_CENTER, 0, -20); + lv_obj_set_style_local_bg_color(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_bg_opa(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0); + lv_obj_set_style_local_border_color(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_border_width(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 6); + lv_obj_set_style_local_border_side(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL); + lv_obj_set_style_local_border_opa(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_100); + + lv_obj_set_hidden(popup, isHidden); +} + +void PopupMessage::SetHidden(const bool hidden) { + if (isHidden == hidden) { + return; + } + isHidden = hidden; + // create/delete on demand + if (popup == nullptr && !isHidden) { + Create(); + } else if (popup != nullptr) { + lv_obj_del(popup); + popup = nullptr; + } +} + +bool PopupMessage::IsHidden() const { + return isHidden; +} diff --git a/src/displayapp/widgets/PopupMessage.h b/src/displayapp/widgets/PopupMessage.h new file mode 100644 index 0000000000..6a43d5525b --- /dev/null +++ b/src/displayapp/widgets/PopupMessage.h @@ -0,0 +1,16 @@ +#pragma once +#include + +namespace Pinetime::Applications::Widgets { + class PopupMessage { + public: + PopupMessage() = default; + void Create(); + void SetHidden(bool hidden); + bool IsHidden() const; + + private: + lv_obj_t* popup = nullptr; + bool isHidden = true; + }; +} diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 56bf9273e1..fe848bad46 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -232,6 +232,7 @@ void SystemTask::Work() { } break; case Messages::SetOffAlarm: + unlockedByButton = true; // unlock so it is possible to press red stop button GoToRunning(); displayApp.PushMessage(Pinetime::Applications::Display::Messages::AlarmTriggered); break; @@ -241,6 +242,7 @@ void SystemTask::Work() { bleDiscoveryTimer = 5; break; case Messages::BleFirmwareUpdateStarted: + unlockedByButton = true; // prevent no screen-locked popup on firmware update GoToRunning(); wakeLocksHeld++; displayApp.PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted); @@ -268,7 +270,14 @@ void SystemTask::Work() { break; } if (state == SystemTaskState::Running) { - displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent); + if (unlockedByButton) { + displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent); + } else { + auto gesture = touchHandler.GestureGet(); + if (gesture != Pinetime::Applications::TouchEvents::None) { + displayApp.PushMessage(Pinetime::Applications::Display::Messages::ShowIgnoreTouchPopup); + } + } } else { // If asleep, check for touch panel wake triggers auto gesture = touchHandler.GestureGet(); @@ -290,6 +299,7 @@ void SystemTask::Work() { action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Press); // This is for faster wakeup, sacrificing special longpress and doubleclick handling while sleeping if (IsSleeping()) { + unlockedByButton = true; fastWakeUpDone = true; GoToRunning(); break; @@ -331,6 +341,8 @@ void SystemTask::Work() { } else { state = SystemTaskState::AODSleeping; } + // lock when going to sleep + unlockedByButton = false; break; case Messages::OnNewDay: motionSensor.ResetStepCounter(); @@ -437,12 +449,14 @@ void SystemTask::GoToSleep() { return; } NRF_LOG_INFO("[systemtask] Going to sleep"); + displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup); if (settingsController.GetAlwaysOnDisplay()) { displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToAOD); } else { displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToSleep); } heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::GoToSleep); + unlockedByButton = false; state = SystemTaskState::GoingToSleep; }; @@ -482,10 +496,21 @@ void SystemTask::HandleButtonAction(Controllers::ButtonActions action) { case Actions::Click: // If the first action after fast wakeup is a click, it should be ignored. if (!fastWakeUpDone) { - displayApp.PushMessage(Applications::Display::Messages::ButtonPushed); + if (!unlockedByButton) { + // the first button event unlocks the touch input + unlockedByButton = true; + displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup); + } else { + displayApp.PushMessage(Applications::Display::Messages::ButtonPushed); + } } break; case Actions::DoubleClick: + if (!unlockedByButton) { + // the first button event unlocks the touch input + unlockedByButton = true; + displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup); + } displayApp.PushMessage(Applications::Display::Messages::ButtonDoubleClicked); break; case Actions::LongPress: diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 606ddd3492..abc5f80491 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -144,6 +144,8 @@ namespace Pinetime { void UpdateMotion(); static constexpr TickType_t batteryMeasurementPeriod = pdMS_TO_TICKS(10 * 60 * 1000); + bool unlockedByButton = true; + SystemMonitor monitor; }; }