Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timer: Remember last timer setting #2013

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/FreeRTOSConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
#define configTICK_RATE_HZ 1024
#define configMAX_PRIORITIES (3)
#define configMINIMAL_STACK_SIZE (120)
#define configTOTAL_HEAP_SIZE (1024 * 40)
#define configTOTAL_HEAP_SIZE (1024 * 39)
#define configMAX_TASK_NAME_LEN (4)
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
Expand Down
47 changes: 39 additions & 8 deletions src/components/timer/Timer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,57 @@
using namespace Pinetime::Controllers;

Timer::Timer(void* const timerData, TimerCallbackFunction_t timerCallbackFunction) {
pausedAtTimer = std::chrono::seconds(0);
state = Stopped;
timer = xTimerCreate("Timer", 1, pdFALSE, timerData, timerCallbackFunction);
}

void Timer::StartTimer(std::chrono::milliseconds duration) {
xTimerChangePeriod(timer, pdMS_TO_TICKS(duration.count()), 0);
xTimerStart(timer, 0);
}

std::chrono::milliseconds Timer::GetTimeRemaining() {
if (IsRunning()) {
TickType_t remainingTime = xTimerGetExpiryTime(timer) - xTaskGetTickCount();
const TickType_t remainingTime = xTimerGetExpiryTime(timer) - xTaskGetTickCount();
return std::chrono::milliseconds(remainingTime * 1000 / configTICK_RATE_HZ);
}

if (state == Paused)
return pausedAtTimer;

return std::chrono::milliseconds(0);
}

void Timer::StopTimer() {
xTimerStop(timer, 0);
Timer::TimerState Timer::GetState() {
return state;
}

void Timer::Start(const std::chrono::milliseconds duration) {
TimerStart(duration);
state = Running;
}

void Timer::Stop() {
TimerStop();
state = Stopped;
}

void Timer::Pause() {
pausedAtTimer = GetTimeRemaining();
TimerStop();
state = Paused;
}

void Timer::Resume() {
TimerStart(pausedAtTimer);
state = Running;
}

bool Timer::IsRunning() {
return (xTimerIsTimerActive(timer) == pdTRUE);
};

void Timer::TimerStart(const std::chrono::milliseconds duration) {
xTimerChangePeriod(timer, pdMS_TO_TICKS(duration.count()), 0);
xTimerStart(timer, 0);
}

void Timer::TimerStop() {
xTimerStop(timer, 0);
}
20 changes: 17 additions & 3 deletions src/components/timer/Timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,30 @@ namespace Pinetime {
public:
Timer(void* timerData, TimerCallbackFunction_t timerCallbackFunction);

void StartTimer(std::chrono::milliseconds duration);
void Start(const std::chrono::milliseconds duration);

void StopTimer();
void Stop();

void Pause();

void Resume();

std::chrono::milliseconds GetTimeRemaining();

bool IsRunning();
typedef enum TimerState { Running, Stopped, Paused } TimerState;

TimerState GetState();

private:
TimerHandle_t timer;

std::chrono::milliseconds pausedAtTimer;

TimerState state;

bool IsRunning();
void TimerStart(const std::chrono::milliseconds duration);
void TimerStop();
};
}
}
5 changes: 3 additions & 2 deletions src/displayapp/DisplayApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,12 @@ void DisplayApp::Refresh() {
}
if (currentApp == Apps::Timer) {
lv_disp_trig_activity(nullptr);
auto* timer = static_cast<Screens::Timer*>(currentScreen.get());
timer->Reset();
} else {
LoadNewScreen(Apps::Timer, DisplayApp::FullRefreshDirections::Up);
}
Screens::Timer* timer;
timer = static_cast<Screens::Timer*>(currentScreen.get());
timer->Stop();
motorController.RunForDuration(35);
break;
case Messages::AlarmTriggered:
Expand Down
148 changes: 112 additions & 36 deletions src/displayapp/screens/Timer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

using namespace Pinetime::Applications::Screens;

static std::chrono::milliseconds lastTimerSetting;

static void btnEventHandler(lv_obj_t* obj, lv_event_t event) {
auto* screen = static_cast<Timer*>(obj->user_data);
if (event == LV_EVENT_PRESSED) {
Expand All @@ -17,6 +19,11 @@ static void btnEventHandler(lv_obj_t* obj, lv_event_t event) {
}
}

static void counterChangeHandler(void *timerScreen) {
Timer* timer = (Timer*)timerScreen;
lastTimerSetting = timer->GetCounters();
}

Timer::Timer(Controllers::Timer& timerController) : timer {timerController} {

lv_obj_t* colonLabel = lv_label_create(lv_scr_act(), nullptr);
Expand All @@ -29,6 +36,8 @@ Timer::Timer(Controllers::Timer& timerController) : timer {timerController} {
secondCounter.Create();
lv_obj_align(minuteCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
lv_obj_align(secondCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
minuteCounter.SetValueChangedEventCallback(this, counterChangeHandler);
secondCounter.SetValueChangedEventCallback(this, counterChangeHandler);

highlightObjectMask = lv_objmask_create(lv_scr_act(), nullptr);
lv_obj_set_size(highlightObjectMask, 240, 50);
Expand Down Expand Up @@ -60,12 +69,20 @@ Timer::Timer(Controllers::Timer& timerController) : timer {timerController} {
lv_obj_set_size(btnPlayPause, LV_HOR_RES, 50);

txtPlayPause = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(txtPlayPause, btnPlayPause, LV_ALIGN_CENTER, 0, 0);

if (timer.IsRunning()) {
SetTimerRunning();
} else {
SetTimerStopped();
switch (timer.GetState()) {
case Controllers::Timer::TimerState::Stopped:
SetCounters(lastTimerSetting);
SetInterfaceStopped();
break;
case Controllers::Timer::TimerState::Running:
SetCounters(timer.GetTimeRemaining());
SetInterfaceRunning();
break;
case Controllers::Timer::TimerState::Paused:
SetCounters(timer.GetTimeRemaining());
SetInterfacePaused();
break;
}

taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
Expand All @@ -76,22 +93,52 @@ Timer::~Timer() {
lv_obj_clean(lv_scr_act());
}

void Timer::SetButtonText(const char* text) {
lv_label_set_text_static(txtPlayPause, text);
lv_obj_align(txtPlayPause, btnPlayPause, LV_ALIGN_CENTER, 0, 0);
}

void Timer::ButtonPressed() {
pressTime = xTaskGetTickCount();
buttonPressing = true;
}

void Timer::MaskReset() {
buttonPressing = false;

// A click event is processed before a release event,
// so the release event would override the "Pause" text without this check
if (!timer.IsRunning()) {
lv_label_set_text_static(txtPlayPause, "Start");
switch (timer.GetState()) {
case Controllers::Timer::TimerState::Stopped:
SetButtonText("Start");
break;
case Controllers::Timer::TimerState::Running:
SetButtonText("Pause");
break;
case Controllers::Timer::TimerState::Paused:
SetButtonText("Resume");
break;
}

maskPosition = 0;
UpdateMask();
}

void Timer::HandleHold() {
if (timer.GetState() == Controllers::Timer::TimerState::Stopped) {
SetButtonText("Reset");
} else {
SetButtonText("Stop");
}

maskPosition += 15;
if (maskPosition > 240) {
HandleLongPress();
} else {
UpdateMask();
}
}

void Timer::UpdateMask() {
lv_draw_mask_line_param_t maskLine;

Expand All @@ -102,52 +149,81 @@ void Timer::UpdateMask() {
lv_objmask_update_mask(btnObjectMask, btnMask, &maskLine);
}

void Timer::HandleLongPress() {
if (timer.GetState() == Controllers::Timer::TimerState::Stopped) {
Reset();
} else {
Stop();
}

MaskReset();
}

void Timer::Refresh() {
if (timer.IsRunning()) {
if (timer.GetState() == Controllers::Timer::TimerState::Running) {
auto secondsRemaining = std::chrono::duration_cast<std::chrono::seconds>(timer.GetTimeRemaining());
minuteCounter.SetValue(secondsRemaining.count() / 60);
secondCounter.SetValue(secondsRemaining.count() % 60);
} else if (buttonPressing && xTaskGetTickCount() > pressTime + pdMS_TO_TICKS(150)) {
lv_label_set_text_static(txtPlayPause, "Reset");
maskPosition += 15;
if (maskPosition > 240) {
MaskReset();
Reset();
} else {
UpdateMask();
SetCounters(secondsRemaining);
} else {
if (buttonPressing && xTaskGetTickCount() > pressTime + pdMS_TO_TICKS(150)) {
HandleHold();
}
}
}

void Timer::SetTimerRunning() {
void Timer::SetInterfaceRunning() {
minuteCounter.HideControls();
secondCounter.HideControls();
lv_label_set_text_static(txtPlayPause, "Pause");
SetButtonText("Pause");
}

void Timer::SetTimerStopped() {
void Timer::SetInterfacePaused() {
minuteCounter.HideControls();
secondCounter.HideControls();
SetButtonText("Resume");
}

void Timer::SetInterfaceStopped() {
minuteCounter.ShowControls();
secondCounter.ShowControls();
lv_label_set_text_static(txtPlayPause, "Start");
SetButtonText("Start");
}

void Timer::SetCounters(const std::chrono::milliseconds& duration) {
SetCounters(std::chrono::duration_cast<std::chrono::seconds>(duration));
}

void Timer::SetCounters(const std::chrono::seconds& duration) {
minuteCounter.SetValue(duration.count() / 60);
secondCounter.SetValue(duration.count() % 60);
}

std::chrono::seconds Timer::GetCounters() {
return std::chrono::minutes(minuteCounter.GetValue()) + std::chrono::seconds(secondCounter.GetValue());
}

void Timer::ToggleRunning() {
if (timer.IsRunning()) {
auto secondsRemaining = std::chrono::duration_cast<std::chrono::seconds>(timer.GetTimeRemaining());
minuteCounter.SetValue(secondsRemaining.count() / 60);
secondCounter.SetValue(secondsRemaining.count() % 60);
timer.StopTimer();
SetTimerStopped();
} else if (secondCounter.GetValue() + minuteCounter.GetValue() > 0) {
auto timerDuration = std::chrono::minutes(minuteCounter.GetValue()) + std::chrono::seconds(secondCounter.GetValue());
timer.StartTimer(timerDuration);
Refresh();
SetTimerRunning();
if (timer.GetState() == Controllers::Timer::TimerState::Stopped) {
if (secondCounter.GetValue() + minuteCounter.GetValue() > 0) {
timer.Start(GetCounters());
Refresh();
SetInterfaceRunning();
}
} else if (timer.GetState() == Controllers::Timer::TimerState::Running) {
timer.Pause();
SetInterfacePaused();
} else { // Paused
timer.Resume();
SetInterfaceRunning();
}
}

void Timer::Reset() {
minuteCounter.SetValue(0);
secondCounter.SetValue(0);
SetTimerStopped();
lastTimerSetting = std::chrono::seconds(0);
Stop();
}

void Timer::Stop() {
timer.Stop();
SetCounters(lastTimerSetting);
SetInterfaceStopped();
}
12 changes: 10 additions & 2 deletions src/displayapp/screens/Timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,23 @@ namespace Pinetime::Applications {
Timer(Controllers::Timer& timerController);
~Timer() override;
void Refresh() override;
void Stop();
void Reset();
void ToggleRunning();
void ButtonPressed();
void MaskReset();
std::chrono::seconds GetCounters();

private:
void SetTimerRunning();
void SetTimerStopped();
void SetInterfaceRunning();
void SetInterfacePaused();
void SetInterfaceStopped();
void HandleHold();
void UpdateMask();
void HandleLongPress();
void SetCounters(const std::chrono::milliseconds& duration);
void SetCounters(const std::chrono::seconds& duration);
void SetButtonText(const char* text);
Pinetime::Controllers::Timer& timer;

lv_obj_t* btnPlayPause;
Expand Down
Loading