diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5cd2e656a4..a9488c8c34 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 bfd7dbed6d..1bc5e137e7 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -477,6 +477,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; } } @@ -500,6 +506,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); } @@ -718,6 +729,10 @@ void DisplayApp::Register(Pinetime::Controllers::NavigationService* NavigationSe this->controllers.navigationService = NavigationService; } +bool DisplayApp::IsWatchFace() { + return this->currentApp == Apps::Clock; +} + void DisplayApp::ApplyBrightness() { auto brightness = settingsController.GetBrightness(); if (brightness != Controllers::BrightnessController::Levels::Low && brightness != Controllers::BrightnessController::Levels::Medium && diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h index dabed99ea7..1f4cf17b69 100644 --- a/src/displayapp/DisplayApp.h +++ b/src/displayapp/DisplayApp.h @@ -15,6 +15,7 @@ #include "components/timer/Timer.h" #include "components/alarm/AlarmController.h" #include "touchhandler/TouchHandler.h" +#include "displayapp/widgets/PopupMessage.h" #include "displayapp/Messages.h" #include "BootErrors.h" @@ -80,6 +81,8 @@ namespace Pinetime { void Register(Pinetime::Controllers::MusicService* musicService); void Register(Pinetime::Controllers::NavigationService* NavigationService); + bool IsWatchFace(); + private: Pinetime::Drivers::St7789& lcd; const Pinetime::Drivers::Cst816S& touchPanel; @@ -102,6 +105,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/DisplayAppRecovery.h b/src/displayapp/DisplayAppRecovery.h index 162ff2575e..e7ac448af0 100644 --- a/src/displayapp/DisplayAppRecovery.h +++ b/src/displayapp/DisplayAppRecovery.h @@ -74,6 +74,10 @@ namespace Pinetime { void Register(Pinetime::Controllers::MusicService* musicService); void Register(Pinetime::Controllers::NavigationService* NavigationService); + bool IsWatchFace() { + return false; + } + private: TaskHandle_t taskHandle; static void Process(void* instance); 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..cdd5744377 --- /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, 50, 45); + lv_obj_align(lockBody, popup, LV_ALIGN_CENTER, 0, 9); + lv_obj_set_style_local_bg_color(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_SILVER); + 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_SILVER); + lv_obj_set_style_local_border_width(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 23); + 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, -17); + lv_obj_set_style_local_bg_color(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_SILVER); + 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_SILVER); + 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 8e0435e372..723b5742be 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -215,6 +215,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; @@ -224,6 +225,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); @@ -251,7 +253,20 @@ 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 ( + displayApp.IsWatchFace() + && (gesture == Pinetime::Applications::TouchEvents::Tap + || gesture == Pinetime::Applications::TouchEvents::DoubleTap) + ) { + // Do not show the lock popup on the watch face with a single tap + } else 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(); @@ -273,6 +288,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; @@ -314,6 +330,8 @@ void SystemTask::Work() { } else { state = SystemTaskState::AODSleeping; } + // lock when going to sleep + unlockedByButton = false; break; case Messages::OnNewDay: // We might be sleeping (with TWI device disabled. @@ -424,6 +442,8 @@ void SystemTask::GoToSleep() { displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToSleep); } heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::GoToSleep); + unlockedByButton = false; + displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup); state = SystemTaskState::GoingToSleep; }; @@ -473,7 +493,13 @@ 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: diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 0060e36096..be0d6e4dea 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -134,6 +134,8 @@ namespace Pinetime { bool stepCounterMustBeReset = false; static constexpr TickType_t batteryMeasurementPeriod = pdMS_TO_TICKS(10 * 60 * 1000); + bool unlockedByButton = true; + SystemMonitor monitor; }; }