diff --git a/src/components/motor/MotorController.cpp b/src/components/motor/MotorController.cpp index 4e392416ae..d3bd2cf3a9 100644 --- a/src/components/motor/MotorController.cpp +++ b/src/components/motor/MotorController.cpp @@ -34,6 +34,10 @@ void MotorController::StopRinging() { nrf_gpio_pin_set(PinMap::Motor); } +bool MotorController::IsRinging() { + return (xTimerIsTimerActive(longVib) == pdTRUE); +} + void MotorController::StopMotor(TimerHandle_t /*xTimer*/) { nrf_gpio_pin_set(PinMap::Motor); } diff --git a/src/components/motor/MotorController.h b/src/components/motor/MotorController.h index 6dea6d1f9e..ab8c956e41 100644 --- a/src/components/motor/MotorController.h +++ b/src/components/motor/MotorController.h @@ -15,6 +15,7 @@ namespace Pinetime { void RunForDuration(uint8_t motorDuration); void StartRinging(); void StopRinging(); + bool IsRinging(); private: static void Ring(TimerHandle_t xTimer); diff --git a/src/components/timer/Timer.cpp b/src/components/timer/Timer.cpp index 4784cdb2ff..3ccffc7c04 100644 --- a/src/components/timer/Timer.cpp +++ b/src/components/timer/Timer.cpp @@ -41,3 +41,7 @@ void Timer::StopTimer() { bool Timer::IsRunning() { return (xTimerIsTimerActive(timer) == pdTRUE); } + +void Timer::ResetExpiredTime() { + triggered = false; +} diff --git a/src/components/timer/Timer.h b/src/components/timer/Timer.h index a99f6c4d04..d43c7970e7 100644 --- a/src/components/timer/Timer.h +++ b/src/components/timer/Timer.h @@ -25,6 +25,8 @@ namespace Pinetime { bool IsRunning(); + void ResetExpiredTime(); + private: TimerHandle_t timer; TickType_t expiry; diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 45a41032dd..85e0ad5624 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -368,19 +368,21 @@ void DisplayApp::Refresh() { case Messages::NewNotification: LoadNewScreen(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down); break; - case Messages::TimerDone: + case Messages::TimerDone: { if (state != States::Running) { PushMessageToSystemTask(System::Messages::GoToRunning); } - if (currentApp == Apps::Timer) { - lv_disp_trig_activity(nullptr); - auto* timer = static_cast(currentScreen.get()); - timer->Reset(); - } else { + // Load timer app if not loaded + if (currentApp != Apps::Timer) { LoadNewScreen(Apps::Timer, DisplayApp::FullRefreshDirections::Up); } - motorController.RunForDuration(35); + // Set the timer to ringing mode + lv_disp_trig_activity(nullptr); + auto* timerScreen = static_cast(currentScreen.get()); + timerScreen->SetTimerRinging(); + motorController.StartRinging(); break; + } case Messages::AlarmTriggered: if (currentApp == Apps::Alarm) { auto* alarm = static_cast(currentScreen.get()); diff --git a/src/displayapp/screens/Timer.cpp b/src/displayapp/screens/Timer.cpp index 6f086e02fb..02c8416033 100644 --- a/src/displayapp/screens/Timer.cpp +++ b/src/displayapp/screens/Timer.cpp @@ -17,7 +17,8 @@ static void btnEventHandler(lv_obj_t* obj, lv_event_t event) { } } -Timer::Timer(Controllers::Timer& timerController) : timer {timerController} { +Timer::Timer(Controllers::Timer& timerController, Controllers::MotorController& motorController, System::SystemTask& systemTask) + : timer {timerController}, motorController {motorController}, wakeLock(systemTask) { lv_obj_t* colonLabel = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_font(colonLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); @@ -62,7 +63,9 @@ Timer::Timer(Controllers::Timer& timerController) : timer {timerController} { // Create the label as a child of the button so it stays centered by default txtPlayPause = lv_label_create(btnPlayPause, nullptr); - if (timer.IsRunning()) { + if (motorController.IsRinging()) { + SetTimerRinging(); + } else if (timer.IsRunning()) { SetTimerRunning(); } else { SetTimerStopped(); @@ -103,7 +106,23 @@ void Timer::UpdateMask() { } void Timer::Refresh() { - if (timer.IsRunning()) { + if (isRinging) { + DisplayTime(); + if (motorController.IsRinging()) { + if (displaySeconds.Get().count() > 10) { + // Stop buzzing after 10 seconds, but continue the counter + motorController.StopRinging(); + wakeLock.Release(); + } else { + // Keep the screen awake during the first 10 seconds + wakeLock.Lock(); + } + } + // Reset timer after 1 minute + if (displaySeconds.Get().count() > 60) { + Reset(); + } + } else if (timer.IsRunning()) { DisplayTime(); } else if (buttonPressing && xTaskGetTickCount() - pressTime > pdMS_TO_TICKS(150)) { lv_label_set_text_static(txtPlayPause, "Reset"); @@ -130,16 +149,30 @@ void Timer::SetTimerRunning() { minuteCounter.HideControls(); secondCounter.HideControls(); lv_label_set_text_static(txtPlayPause, "Pause"); + lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); } void Timer::SetTimerStopped() { + isRinging = false; minuteCounter.ShowControls(); secondCounter.ShowControls(); lv_label_set_text_static(txtPlayPause, "Start"); + lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN); +} + +void Timer::SetTimerRinging() { + isRinging = true; + minuteCounter.HideControls(); + secondCounter.HideControls(); + lv_label_set_text_static(txtPlayPause, "Reset"); + lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); } void Timer::ToggleRunning() { - if (timer.IsRunning()) { + if (isRinging) { + motorController.StopRinging(); + Reset(); + } else if (timer.IsRunning()) { DisplayTime(); timer.StopTimer(); SetTimerStopped(); @@ -152,6 +185,7 @@ void Timer::ToggleRunning() { } void Timer::Reset() { + timer.ResetExpiredTime(); DisplayTime(); SetTimerStopped(); } diff --git a/src/displayapp/screens/Timer.h b/src/displayapp/screens/Timer.h index f4ceddefe8..63ca245694 100644 --- a/src/displayapp/screens/Timer.h +++ b/src/displayapp/screens/Timer.h @@ -1,7 +1,9 @@ #pragma once #include "displayapp/screens/Screen.h" +#include "components/motor/MotorController.h" #include "systemtask/SystemTask.h" +#include "systemtask/WakeLock.h" #include "displayapp/LittleVgl.h" #include "displayapp/widgets/Counter.h" #include "utility/DirtyValue.h" @@ -14,13 +16,14 @@ namespace Pinetime::Applications { namespace Screens { class Timer : public Screen { public: - Timer(Controllers::Timer& timerController); + Timer(Controllers::Timer& timerController, Controllers::MotorController& motorController, System::SystemTask& systemTask); ~Timer() override; void Refresh() override; void Reset(); void ToggleRunning(); void ButtonPressed(); void MaskReset(); + void SetTimerRinging(); private: void SetTimerRunning(); @@ -28,6 +31,9 @@ namespace Pinetime::Applications { void UpdateMask(); void DisplayTime(); Pinetime::Controllers::Timer& timer; + Pinetime::Controllers::MotorController& motorController; + + Pinetime::System::WakeLock wakeLock; lv_obj_t* btnPlayPause; lv_obj_t* txtPlayPause; @@ -42,6 +48,7 @@ namespace Pinetime::Applications { Widgets::Counter secondCounter = Widgets::Counter(0, 59, jetbrains_mono_76); bool buttonPressing = false; + bool isRinging = false; lv_coord_t maskPosition = 0; TickType_t pressTime = 0; Utility::DirtyValue displaySeconds; @@ -54,7 +61,7 @@ namespace Pinetime::Applications { static constexpr const char* icon = Screens::Symbols::hourGlass; static Screens::Screen* Create(AppControllers& controllers) { - return new Screens::Timer(controllers.timer); + return new Screens::Timer(controllers.timer, controllers.motorController, *controllers.systemTask); }; static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {