diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a6e7ad5625..d833135f2d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -409,6 +409,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/components/settings/Settings.h b/src/components/settings/Settings.h index 0a55c08ad4..95ab2fa2af 100644 --- a/src/components/settings/Settings.h +++ b/src/components/settings/Settings.h @@ -17,6 +17,7 @@ namespace Pinetime { DoubleTap = 1, RaiseWrist = 2, Shake = 3, + ButtonUnlocks = 4, }; enum class Colors : uint8_t { White, @@ -226,7 +227,7 @@ namespace Pinetime { } }; - std::bitset<4> getWakeUpModes() const { + std::bitset<5> getWakeUpModes() const { return settings.wakeUpMode; } @@ -267,7 +268,7 @@ namespace Pinetime { private: Pinetime::Controllers::FS& fs; - static constexpr uint32_t settingsVersion = 0x0004; + static constexpr uint32_t settingsVersion = 0x0005; struct SettingsData { uint32_t version = settingsVersion; @@ -284,7 +285,7 @@ namespace Pinetime { WatchFaceInfineat watchFaceInfineat; - std::bitset<4> wakeUpMode {0}; + std::bitset<5> wakeUpMode {0}; uint16_t shakeWakeThreshold = 150; Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium; }; diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index fe2ee21375..9bf00b650e 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -95,7 +95,8 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd, touchHandler {touchHandler}, filesystem {filesystem}, lvgl {lcd, filesystem}, - timer(this, TimerCallback) { + timer(this, TimerCallback), + popupMessage {"Touch input\nis ignored,\npush button\nto unlock."} { } void DisplayApp::Start(System::BootErrors error) { @@ -363,6 +364,12 @@ void DisplayApp::Refresh() { RestoreBrightness(); motorController.RunForDuration(15); break; + case Messages::ShowIgnoreTouchPopup: + popupMessage.SetHidden(false); + break; + case Messages::HideIgnoreTouchPopup: + popupMessage.SetHidden(true); + break; } } diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h index f537651dbf..3a058aa953 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" @@ -96,6 +97,8 @@ namespace Pinetime { Pinetime::Components::LittleVgl lvgl; Pinetime::Controllers::Timer timer; + Pinetime::Applications::Widgets::PopupMessage popupMessage; + TaskHandle_t taskHandle; States state = States::Running; diff --git a/src/displayapp/Messages.h b/src/displayapp/Messages.h index dada30888d..ddede142b5 100644 --- a/src/displayapp/Messages.h +++ b/src/displayapp/Messages.h @@ -24,6 +24,8 @@ namespace Pinetime { Chime, BleRadioEnableToggle, OnChargingEvent, + ShowIgnoreTouchPopup, + HideIgnoreTouchPopup }; } } diff --git a/src/displayapp/screens/settings/SettingWakeUp.cpp b/src/displayapp/screens/settings/SettingWakeUp.cpp index 8df34c2084..3828106417 100644 --- a/src/displayapp/screens/settings/SettingWakeUp.cpp +++ b/src/displayapp/screens/settings/SettingWakeUp.cpp @@ -8,7 +8,7 @@ using namespace Pinetime::Applications::Screens; -constexpr std::array SettingWakeUp::options; +constexpr std::array SettingWakeUp::options; namespace { void event_handler(lv_obj_t* obj, lv_event_t event) { @@ -27,9 +27,9 @@ SettingWakeUp::SettingWakeUp(Pinetime::Controllers::Settings& settingsController lv_obj_set_style_local_pad_inner(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5); lv_obj_set_style_local_border_width(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0); - lv_obj_set_pos(container1, 10, 60); + lv_obj_set_pos(container1, 10, 30); lv_obj_set_width(container1, LV_HOR_RES - 20); - lv_obj_set_height(container1, LV_VER_RES - 50); + lv_obj_set_height(container1, LV_VER_RES - 40); lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); diff --git a/src/displayapp/screens/settings/SettingWakeUp.h b/src/displayapp/screens/settings/SettingWakeUp.h index 28219ca1c4..a281460345 100644 --- a/src/displayapp/screens/settings/SettingWakeUp.h +++ b/src/displayapp/screens/settings/SettingWakeUp.h @@ -25,11 +25,12 @@ namespace Pinetime { }; Controllers::Settings& settingsController; - static constexpr std::array options = {{ + static constexpr std::array options = {{ {Controllers::Settings::WakeUpMode::SingleTap, "Single Tap"}, {Controllers::Settings::WakeUpMode::DoubleTap, "Double Tap"}, {Controllers::Settings::WakeUpMode::RaiseWrist, "Raise Wrist"}, {Controllers::Settings::WakeUpMode::Shake, "Shake Wake"}, + {Controllers::Settings::WakeUpMode::ButtonUnlocks, "Button Unlock"}, }}; lv_obj_t* cbOption[options.size()]; diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index b199d53f02..0309fcc6a9 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -214,7 +214,9 @@ void SystemTask::Work() { state = SystemTaskState::Running; break; case Messages::TouchWakeUp: { - if (touchHandler.ProcessTouchInfo(touchPanel.GetTouchInfo())) { + Pinetime::Controllers::TouchHandler::TouchProcessReply reply; + reply = touchHandler.ProcessTouchInfo(touchPanel.GetTouchInfo(), settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::ButtonUnlocks)); + if (reply == Pinetime::Controllers::TouchHandler::TouchProcessReply::TouchEvent) { auto gesture = touchHandler.GestureGet(); if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep && gesture != Pinetime::Applications::TouchEvents::None && @@ -222,12 +224,16 @@ void SystemTask::Work() { settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) || (gesture == Pinetime::Applications::TouchEvents::Tap && settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::SingleTap)))) { + touchHandler.SetIfButtonUnlocksIgnoreTouch(true); + touchHandler.SetWokenBy(Pinetime::Controllers::TouchHandler::WokenBy::WakeUpAction); GoToRunning(); } } break; } case Messages::GoToSleep: + touchHandler.SetIgnoreTouchPopupHidden(true); + displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup); if (doNotGoToSleep) { break; } @@ -291,24 +297,42 @@ void SystemTask::Work() { // TODO add intent of fs access icon or something break; case Messages::OnTouchEvent: - if (touchHandler.ProcessTouchInfo(touchPanel.GetTouchInfo())) { + Pinetime::Controllers::TouchHandler::TouchProcessReply reply; + reply = touchHandler.ProcessTouchInfo(touchPanel.GetTouchInfo(), settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::ButtonUnlocks)); + NRF_LOG_INFO("[systemtask] OnTouchEvent, reply %d", reply); + + if (reply == Pinetime::Controllers::TouchHandler::TouchProcessReply::TouchEvent) { displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent); + } else if (reply == Pinetime::Controllers::TouchHandler::TouchProcessReply::IgnoreTouchPopup) { + displayApp.PushMessage(Pinetime::Applications::Display::Messages::ShowIgnoreTouchPopup); + touchHandler.SetIgnoreTouchPopupHidden(false); } break; case Messages::HandleButtonEvent: { - Controllers::ButtonActions action = Controllers::ButtonActions::None; - if (nrf_gpio_pin_read(Pinetime::PinMap::Button) == 0) { - action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Release); - } else { - action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Press); - // This is for faster wakeup, sacrificing special longpress and doubleclick handling while sleeping - if (IsSleeping()) { - fastWakeUpDone = true; - GoToRunning(); - break; + // if the IgnoreTouchPopup is active the first button event unlocks the device + if (!touchHandler.IsIgnoreTouchPopupHidden()) { + touchHandler.SetWokenBy(Pinetime::Controllers::TouchHandler::WokenBy::Button); + touchHandler.SetIfButtonUnlocksIgnoreTouch(false); + touchHandler.SetIgnoreTouchPopupHidden(true); + displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup); + } + else { + Controllers::ButtonActions action = Controllers::ButtonActions::None; + if (nrf_gpio_pin_read(Pinetime::PinMap::Button) == 0) { + action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Release); + } else { + action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Press); + touchHandler.SetWokenBy(Pinetime::Controllers::TouchHandler::WokenBy::Button); + touchHandler.SetIfButtonUnlocksIgnoreTouch(false); + // This is for faster wakeup, sacrificing special longpress and doubleclick handling while sleeping + if (IsSleeping()) { + fastWakeUpDone = true; + GoToRunning(); + break; + } } + HandleButtonAction(action); } - HandleButtonAction(action); } break; case Messages::HandleButtonTimerEvent: { auto action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Timer); @@ -328,6 +352,8 @@ void SystemTask::Work() { } state = SystemTaskState::Sleeping; + touchHandler.SetWokenBy(Pinetime::Controllers::TouchHandler::WokenBy::Other); + touchHandler.SetIfButtonUnlocksIgnoreTouch(false); break; case Messages::OnNewDay: // We might be sleeping (with TWI device disabled. @@ -433,6 +459,8 @@ void SystemTask::UpdateMotion() { motionController.ShouldRaiseWake(state == SystemTaskState::Sleeping)) || (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake) && motionController.ShouldShakeWake(settingsController.GetShakeThreshold()))) { + touchHandler.SetWokenBy(Pinetime::Controllers::TouchHandler::WokenBy::WakeUpAction); + touchHandler.SetIfButtonUnlocksIgnoreTouch(true); GoToRunning(); } } diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 79f1cf444f..20b8ebe7c1 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -53,6 +53,11 @@ namespace Pinetime { class SystemTask { public: enum class SystemTaskState { Sleeping, Running, GoingToSleep, WakingUp }; + // Enum describes how the watch was woken: + // * WakeUpAction: The actions selected in the wakeup settings, single/double tap, raise, shake + // * Button: The hardware button + // * Other: Other things that can wake the watch up, eg. apps and notifications. + enum class WokenBy { WakeUpAction, Button, Other }; SystemTask(Drivers::SpiMaster& spi, Pinetime::Drivers::SpiNorFlash& spiNorFlash, Drivers::TwiMaster& twiMaster, diff --git a/src/touchhandler/TouchHandler.cpp b/src/touchhandler/TouchHandler.cpp index b29f951fcb..8403b56ab8 100644 --- a/src/touchhandler/TouchHandler.cpp +++ b/src/touchhandler/TouchHandler.cpp @@ -33,34 +33,51 @@ Pinetime::Applications::TouchEvents TouchHandler::GestureGet() { return returnGesture; } -bool TouchHandler::ProcessTouchInfo(Drivers::Cst816S::TouchInfos info) { +TouchHandler::TouchProcessReply TouchHandler::ProcessTouchInfo(Drivers::Cst816S::TouchInfos info, bool buttonUnlocksOn) { if (!info.isValid) { - return false; + return TouchHandler::TouchProcessReply::NoAction; } - // Only a single gesture per touch - if (info.gesture != Pinetime::Drivers::Cst816S::Gestures::None) { - if (gestureReleased) { - if (info.gesture == Pinetime::Drivers::Cst816S::Gestures::SlideDown || - info.gesture == Pinetime::Drivers::Cst816S::Gestures::SlideLeft || - info.gesture == Pinetime::Drivers::Cst816S::Gestures::SlideUp || - info.gesture == Pinetime::Drivers::Cst816S::Gestures::SlideRight || - info.gesture == Pinetime::Drivers::Cst816S::Gestures::LongPress) { - if (info.touching) { + // if the watch was just woken by touch and button must be used to unlock, ignore the first touch event which + // is the touch event that woke the watch. Otherwise the lock-popup will be displayed + if (buttonUnlocksOn && ignoreNextTouchEvent) { + ignoreNextTouchEvent = false; + return TouchHandler::TouchProcessReply::NoAction; + + } else if (!buttonUnlocksOn || wokenBy != WokenBy::WakeUpAction) + { + + // if we get to here TouchEvents is allowed and the "ButtonUnlocks" requirement can be overridden + wokenBy = WokenBy::Other; + + // Only a single gesture per touch + if (info.gesture != Pinetime::Drivers::Cst816S::Gestures::None) { + if (gestureReleased) { + if (info.gesture == Pinetime::Drivers::Cst816S::Gestures::SlideDown || + info.gesture == Pinetime::Drivers::Cst816S::Gestures::SlideLeft || + info.gesture == Pinetime::Drivers::Cst816S::Gestures::SlideUp || + info.gesture == Pinetime::Drivers::Cst816S::Gestures::SlideRight || + info.gesture == Pinetime::Drivers::Cst816S::Gestures::LongPress) { + if (info.touching) { + gesture = ConvertGesture(info.gesture); + gestureReleased = false; + } + } else { gesture = ConvertGesture(info.gesture); - gestureReleased = false; } - } else { - gesture = ConvertGesture(info.gesture); } } - } - if (!info.touching) { - gestureReleased = true; - } + if (!info.touching) { + gestureReleased = true; + } - currentTouchPoint = {info.x, info.y, info.touching}; + currentTouchPoint = {info.x, info.y, info.touching}; - return true; + return TouchHandler::TouchProcessReply::TouchEvent; + } + else + { + return TouchHandler::TouchProcessReply::IgnoreTouchPopup; + } } diff --git a/src/touchhandler/TouchHandler.h b/src/touchhandler/TouchHandler.h index a44822552d..2db5380114 100644 --- a/src/touchhandler/TouchHandler.h +++ b/src/touchhandler/TouchHandler.h @@ -6,13 +6,25 @@ namespace Pinetime { namespace Controllers { class TouchHandler { public: + // Enum describes how the watch was woken: + // * WakeUpAction: The actions selected in the wakeup settings, single/double tap, raise, shake + // * Button: The hardware button + // * Other: Other things that can wake the watch up, eg. apps and notifications. + enum class WokenBy { WakeUpAction, Button, Other }; + + // Enum describes how the reply from ProcessTouchInfo should be interpreted: + // * NoAction: Do nothing, ignore input. + // * TouchEvent: The input should be treated as a normal touch event. + // * IgnoreTouchPopup: Show the popup for when ignoring touvh input. + enum class TouchProcessReply { NoAction, TouchEvent, IgnoreTouchPopup }; + struct TouchPoint { int x; int y; bool touching; }; - bool ProcessTouchInfo(Drivers::Cst816S::TouchInfos info); + TouchProcessReply ProcessTouchInfo(Drivers::Cst816S::TouchInfos info, bool buttonUnlocksOn); bool IsTouching() const { return currentTouchPoint.touching; @@ -26,12 +38,35 @@ namespace Pinetime { return currentTouchPoint.y; } + void SetIfButtonUnlocksIgnoreTouch(bool ignore) + { + ignoreNextTouchEvent = ignore; + } + + void SetIgnoreTouchPopupHidden(bool hidden) + { + ignoreTouchPopupHidden = hidden; + } + bool IsIgnoreTouchPopupHidden() + { + return ignoreTouchPopupHidden; + } + + void SetWokenBy(WokenBy woken) + { + wokenBy = woken; + } + Pinetime::Applications::TouchEvents GestureGet(); private: Pinetime::Applications::TouchEvents gesture; TouchPoint currentTouchPoint = {}; bool gestureReleased = true; + + WokenBy wokenBy; + bool ignoreNextTouchEvent = false; + bool ignoreTouchPopupHidden = true; }; } }