diff --git a/src/components/alarm/AlarmController.cpp b/src/components/alarm/AlarmController.cpp index 4ae42c0830..c85858aa62 100644 --- a/src/components/alarm/AlarmController.cpp +++ b/src/components/alarm/AlarmController.cpp @@ -26,6 +26,14 @@ using namespace std::chrono_literals; AlarmController::AlarmController(Controllers::DateTime& dateTimeController, Controllers::FS& fs) : dateTimeController {dateTimeController}, fs {fs} { + // Initialize all alarms with defaults + for (uint8_t i = 0; i < MaxAlarms; i++) { + alarms[i].version = alarmFormatVersion; + alarms[i].hours = 6 + i; + alarms[i].minutes = 0; + alarms[i].recurrence = RecurType::Weekdays; + alarms[i].isEnabled = false; + } } namespace { @@ -39,9 +47,13 @@ void AlarmController::Init(System::SystemTask* systemTask) { this->systemTask = systemTask; alarmTimer = xTimerCreate("Alarm", 1, pdFALSE, this, SetOffAlarm); LoadSettingsFromFile(); - if (alarm.isEnabled) { - NRF_LOG_INFO("[AlarmController] Loaded alarm was enabled, scheduling"); - ScheduleAlarm(); + // Check if any alarms are enabled and schedule the next one + for (uint8_t i = 0; i < MaxAlarms; i++) { + if (alarms[i].isEnabled) { + NRF_LOG_INFO("[AlarmController] Found enabled alarm %u, scheduling next alarm", i); + ScheduleAlarm(); + break; + } } } @@ -53,38 +65,47 @@ void AlarmController::SaveAlarm() { alarmChanged = false; } -void AlarmController::SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin) { - if (alarm.hours == alarmHr && alarm.minutes == alarmMin) { +void AlarmController::SetAlarmTime(uint8_t index, uint8_t alarmHr, uint8_t alarmMin) { + if (index >= MaxAlarms) { return; } - alarm.hours = alarmHr; - alarm.minutes = alarmMin; + if (alarms[index].hours == alarmHr && alarms[index].minutes == alarmMin) { + return; + } + alarms[index].hours = alarmHr; + alarms[index].minutes = alarmMin; alarmChanged = true; } void AlarmController::ScheduleAlarm() { - // Determine the next time the alarm needs to go off and set the timer + // Determine the next alarm to schedule and set the timer xTimerStop(alarmTimer, 0); + nextAlarmIndex = CalculateNextAlarm(); + if (nextAlarmIndex >= MaxAlarms) { + // No enabled alarms found + return; + } + auto now = dateTimeController.CurrentDateTime(); alarmTime = now; time_t ttAlarmTime = std::chrono::system_clock::to_time_t(std::chrono::time_point_cast(alarmTime)); tm* tmAlarmTime = std::localtime(&ttAlarmTime); // If the time being set has already passed today,the alarm should be set for tomorrow - if (alarm.hours < dateTimeController.Hours() || - (alarm.hours == dateTimeController.Hours() && alarm.minutes <= dateTimeController.Minutes())) { + if (alarms[nextAlarmIndex].hours < dateTimeController.Hours() || + (alarms[nextAlarmIndex].hours == dateTimeController.Hours() && alarms[nextAlarmIndex].minutes <= dateTimeController.Minutes())) { tmAlarmTime->tm_mday += 1; // tm_wday doesn't update automatically tmAlarmTime->tm_wday = (tmAlarmTime->tm_wday + 1) % 7; } - tmAlarmTime->tm_hour = alarm.hours; - tmAlarmTime->tm_min = alarm.minutes; + tmAlarmTime->tm_hour = alarms[nextAlarmIndex].hours; + tmAlarmTime->tm_min = alarms[nextAlarmIndex].minutes; tmAlarmTime->tm_sec = 0; // if alarm is in weekday-only mode, make sure it shifts to the next weekday - if (alarm.recurrence == RecurType::Weekdays) { + if (alarms[nextAlarmIndex].recurrence == RecurType::Weekdays) { if (tmAlarmTime->tm_wday == 0) { // Sunday, shift 1 day tmAlarmTime->tm_mday += 1; } else if (tmAlarmTime->tm_wday == 6) { // Saturday, shift 2 days @@ -98,69 +119,104 @@ void AlarmController::ScheduleAlarm() { auto secondsToAlarm = std::chrono::duration_cast(alarmTime - now).count(); xTimerChangePeriod(alarmTimer, secondsToAlarm * configTICK_RATE_HZ, 0); xTimerStart(alarmTimer, 0); - - if (!alarm.isEnabled) { - alarm.isEnabled = true; - alarmChanged = true; - } } uint32_t AlarmController::SecondsToAlarm() const { return std::chrono::duration_cast(alarmTime - dateTimeController.CurrentDateTime()).count(); } -void AlarmController::DisableAlarm() { - xTimerStop(alarmTimer, 0); - if (alarm.isEnabled) { - alarm.isEnabled = false; +void AlarmController::DisableAlarm(uint8_t index) { + if (index >= MaxAlarms) { + return; + } + if (alarms[index].isEnabled) { + alarms[index].isEnabled = false; alarmChanged = true; + ScheduleAlarm(); + } +} + +void AlarmController::SetEnabled(uint8_t index, bool enabled) { + if (index >= MaxAlarms) { + return; + } + if (alarms[index].isEnabled != enabled) { + alarms[index].isEnabled = enabled; + alarmChanged = true; + ScheduleAlarm(); } } void AlarmController::SetOffAlarmNow() { isAlerting = true; + alertingAlarmIndex = nextAlarmIndex; systemTask->PushMessage(System::Messages::SetOffAlarm); } void AlarmController::StopAlerting() { isAlerting = false; // Disable alarm unless it is recurring - if (alarm.recurrence == RecurType::None) { - alarm.isEnabled = false; + if (alarms[alertingAlarmIndex].recurrence == RecurType::None) { + alarms[alertingAlarmIndex].isEnabled = false; alarmChanged = true; - } else { - // set next instance - ScheduleAlarm(); } + ScheduleAlarm(); } -void AlarmController::SetRecurrence(RecurType recurrence) { - if (alarm.recurrence != recurrence) { - alarm.recurrence = recurrence; +void AlarmController::SetRecurrence(uint8_t index, RecurType recurrence) { + if (index >= MaxAlarms) { + return; + } + if (alarms[index].recurrence != recurrence) { + alarms[index].recurrence = recurrence; alarmChanged = true; } } void AlarmController::LoadSettingsFromFile() { lfs_file_t alarmFile; - AlarmSettings alarmBuffer; if (fs.FileOpen(&alarmFile, "/.system/alarm.dat", LFS_O_RDONLY) != LFS_ERR_OK) { NRF_LOG_WARNING("[AlarmController] Failed to open alarm data file"); return; } - fs.FileRead(&alarmFile, reinterpret_cast(&alarmBuffer), sizeof(alarmBuffer)); - fs.FileClose(&alarmFile); - if (alarmBuffer.version != alarmFormatVersion) { - NRF_LOG_WARNING("[AlarmController] Loaded alarm settings has version %u instead of %u, discarding", - alarmBuffer.version, - alarmFormatVersion); - return; - } + // Read version byte to determine format + uint8_t version; + fs.FileRead(&alarmFile, &version, sizeof(version)); + fs.FileSeek(&alarmFile, 0); + + if (version == 1) { + // Migrate from old single-alarm format + NRF_LOG_INFO("[AlarmController] Migrating from version 1 to version 2"); + AlarmSettings oldAlarm; + fs.FileRead(&alarmFile, reinterpret_cast(&oldAlarm), sizeof(oldAlarm)); + fs.FileClose(&alarmFile); + + // Copy old alarm to first slot + alarms[0] = oldAlarm; + alarms[0].version = alarmFormatVersion; - alarm = alarmBuffer; - NRF_LOG_INFO("[AlarmController] Loaded alarm settings from file"); + // Initialize other alarms with defaults + for (uint8_t i = 1; i < MaxAlarms; i++) { + alarms[i].version = alarmFormatVersion; + alarms[i].hours = 6 + i; + alarms[i].minutes = 0; + alarms[i].recurrence = RecurType::Weekdays; + alarms[i].isEnabled = false; + } + + alarmChanged = true; + NRF_LOG_INFO("[AlarmController] Migrated alarm settings from version 1"); + } else if (version == alarmFormatVersion) { + // Read new multi-alarm format + fs.FileRead(&alarmFile, reinterpret_cast(alarms.data()), sizeof(alarms)); + fs.FileClose(&alarmFile); + NRF_LOG_INFO("[AlarmController] Loaded %u alarms from file", MaxAlarms); + } else { + NRF_LOG_WARNING("[AlarmController] Unknown alarm version %u, using defaults", version); + fs.FileClose(&alarmFile); + } } void AlarmController::SaveSettingsToFile() const { @@ -175,7 +231,56 @@ void AlarmController::SaveSettingsToFile() const { return; } - fs.FileWrite(&alarmFile, reinterpret_cast(&alarm), sizeof(alarm)); + fs.FileWrite(&alarmFile, reinterpret_cast(alarms.data()), sizeof(alarms)); fs.FileClose(&alarmFile); - NRF_LOG_INFO("[AlarmController] Saved alarm settings with format version %u to file", alarm.version); + NRF_LOG_INFO("[AlarmController] Saved %u alarms with format version %u to file", MaxAlarms, alarmFormatVersion); +} + +uint8_t AlarmController::CalculateNextAlarm() const { + auto now = dateTimeController.CurrentDateTime(); + uint32_t minSecondsToAlarm = UINT32_MAX; + uint8_t nextIndex = MaxAlarms; // Invalid index means no alarm found + + for (uint8_t i = 0; i < MaxAlarms; i++) { + if (!alarms[i].isEnabled) { + continue; + } + + // Calculate seconds to this alarm + auto alarmTimePoint = now; + time_t ttAlarmTime = + std::chrono::system_clock::to_time_t(std::chrono::time_point_cast(alarmTimePoint)); + tm* tmAlarmTime = std::localtime(&ttAlarmTime); + + // If the time has already passed today, schedule for tomorrow + if (alarms[i].hours < dateTimeController.Hours() || + (alarms[i].hours == dateTimeController.Hours() && alarms[i].minutes <= dateTimeController.Minutes())) { + tmAlarmTime->tm_mday += 1; + tmAlarmTime->tm_wday = (tmAlarmTime->tm_wday + 1) % 7; + } + + tmAlarmTime->tm_hour = alarms[i].hours; + tmAlarmTime->tm_min = alarms[i].minutes; + tmAlarmTime->tm_sec = 0; + + // Handle weekday-only mode + if (alarms[i].recurrence == RecurType::Weekdays) { + if (tmAlarmTime->tm_wday == 0) { // Sunday, shift 1 day + tmAlarmTime->tm_mday += 1; + } else if (tmAlarmTime->tm_wday == 6) { // Saturday, shift 2 days + tmAlarmTime->tm_mday += 2; + } + } + tmAlarmTime->tm_isdst = -1; + + auto thisAlarmTime = std::chrono::system_clock::from_time_t(std::mktime(tmAlarmTime)); + auto secondsToThisAlarm = std::chrono::duration_cast(thisAlarmTime - now).count(); + + if (secondsToThisAlarm > 0 && static_cast(secondsToThisAlarm) < minSecondsToAlarm) { + minSecondsToAlarm = static_cast(secondsToThisAlarm); + nextIndex = i; + } + } + + return nextIndex; } diff --git a/src/components/alarm/AlarmController.h b/src/components/alarm/AlarmController.h index 278e9cdb6a..bcbcb36ffb 100644 --- a/src/components/alarm/AlarmController.h +++ b/src/components/alarm/AlarmController.h @@ -19,6 +19,7 @@ #include #include +#include #include #include "components/datetime/DateTimeController.h" @@ -30,44 +31,73 @@ namespace Pinetime { namespace Controllers { class AlarmController { public: + static constexpr uint8_t MaxAlarms = 4; + AlarmController(Controllers::DateTime& dateTimeController, Controllers::FS& fs); void Init(System::SystemTask* systemTask); void SaveAlarm(); - void SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin); + void SetAlarmTime(uint8_t index, uint8_t alarmHr, uint8_t alarmMin); void ScheduleAlarm(); - void DisableAlarm(); + void DisableAlarm(uint8_t index); void SetOffAlarmNow(); uint32_t SecondsToAlarm() const; void StopAlerting(); enum class RecurType { None, Daily, Weekdays }; - uint8_t Hours() const { - return alarm.hours; + uint8_t Hours(uint8_t index) const { + if (index >= MaxAlarms) { + return 0; + } + return alarms[index].hours; } - uint8_t Minutes() const { - return alarm.minutes; + uint8_t Minutes(uint8_t index) const { + if (index >= MaxAlarms) { + return 0; + } + return alarms[index].minutes; } bool IsAlerting() const { return isAlerting; } - bool IsEnabled() const { - return alarm.isEnabled; + uint8_t AlertingAlarmIndex() const { + return alertingAlarmIndex; + } + + bool IsEnabled(uint8_t index) const { + if (index >= MaxAlarms) { + return false; + } + return alarms[index].isEnabled; } - RecurType Recurrence() const { - return alarm.recurrence; + bool AnyAlarmEnabled() const { + for (uint8_t i = 0; i < MaxAlarms; i++) { + if (alarms[i].isEnabled) { + return true; + } + } + return false; + } + + void SetEnabled(uint8_t index, bool enabled); + + RecurType Recurrence(uint8_t index) const { + if (index >= MaxAlarms) { + return RecurType::None; + } + return alarms[index].recurrence; } - void SetRecurrence(RecurType recurrence); + void SetRecurrence(uint8_t index, RecurType recurrence); private: // Versions 255 is reserved for now, so the version field can be made // bigger, should it ever be needed. - static constexpr uint8_t alarmFormatVersion = 1; + static constexpr uint8_t alarmFormatVersion = 2; struct AlarmSettings { uint8_t version = alarmFormatVersion; @@ -79,16 +109,19 @@ namespace Pinetime { bool isAlerting = false; bool alarmChanged = false; + uint8_t alertingAlarmIndex = 0; + uint8_t nextAlarmIndex = 0; Controllers::DateTime& dateTimeController; Controllers::FS& fs; System::SystemTask* systemTask = nullptr; TimerHandle_t alarmTimer; - AlarmSettings alarm; + std::array alarms; std::chrono::time_point alarmTime; void LoadSettingsFromFile(); void SaveSettingsToFile() const; + uint8_t CalculateNextAlarm() const; }; } } diff --git a/src/components/motion/MotionController.cpp b/src/components/motion/MotionController.cpp index ce959f0efb..b27d297e14 100644 --- a/src/components/motion/MotionController.cpp +++ b/src/components/motion/MotionController.cpp @@ -65,9 +65,13 @@ void MotionController::Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps) // Update accumulated speed // Currently polling at 10Hz, if this ever goes faster scalar and EMA might need adjusting - int32_t speed = std::abs(zHistory[0] - zHistory[histSize - 1] + ((yHistory[0] - yHistory[histSize - 1]) / 2) + - ((xHistory[0] - xHistory[histSize - 1]) / 4)) * - 100 / (time - lastTime); + int32_t timeDelta = time - lastTime; + int32_t speed = 0; + if (timeDelta > 0) { + speed = std::abs(zHistory[0] - zHistory[histSize - 1] + ((yHistory[0] - yHistory[histSize - 1]) / 2) + + ((xHistory[0] - xHistory[histSize - 1]) / 4)) * + 100 / timeDelta; + } // integer version of (.2 * speed) + ((1 - .2) * accumulatedSpeed); accumulatedSpeed = speed / 5 + accumulatedSpeed * 4 / 5; diff --git a/src/displayapp/screens/Alarm.cpp b/src/displayapp/screens/Alarm.cpp index 4cf4392157..dab00e519c 100644 --- a/src/displayapp/screens/Alarm.cpp +++ b/src/displayapp/screens/Alarm.cpp @@ -44,11 +44,110 @@ static void StopAlarmTaskCallback(lv_task_t* task) { screen->StopAlerting(); } +static void launcherBtnEventHandler(lv_obj_t* obj, lv_event_t event) { + if (event == LV_EVENT_CLICKED) { + auto* screen = static_cast(obj->user_data); + screen->OnLauncherButtonClicked(obj); + } +} + +static void switchEventHandler(lv_obj_t* obj, lv_event_t event) { + if (event == LV_EVENT_VALUE_CHANGED) { + auto* screen = static_cast(obj->user_data); + screen->OnButtonEvent(obj, event); + } +} + Alarm::Alarm(Controllers::AlarmController& alarmController, Controllers::Settings::ClockType clockType, System::SystemTask& systemTask, Controllers::MotorController& motorController) - : alarmController {alarmController}, wakeLock(systemTask), motorController {motorController} { + : alarmController {alarmController}, wakeLock(systemTask), motorController {motorController}, clockType {clockType} { + + // Decide which UI to show + if (alarmController.IsAlerting()) { + // If alarming, go directly to config mode for the alerting alarm + launcherMode = false; + selectedAlarmIndex = alarmController.AlertingAlarmIndex(); + CreateAlarmConfigUI(selectedAlarmIndex); + SetAlerting(); + } else { + // Show launcher by default + launcherMode = true; + CreateLauncherUI(); + } +} + +void Alarm::CreateLauncherUI() { + static constexpr lv_color_t bgColor = Colors::bgAlt; + static constexpr uint8_t btnHeight = 55; + static constexpr uint8_t btnSpacing = 5; + + lv_style_init(&launcherButtonStyle); + lv_style_set_radius(&launcherButtonStyle, LV_STATE_DEFAULT, 10); + lv_style_set_bg_color(&launcherButtonStyle, LV_STATE_DEFAULT, bgColor); + + for (uint8_t i = 0; i < Controllers::AlarmController::MaxAlarms; i++) { + // Create button container + alarmButtons[i] = lv_btn_create(lv_scr_act(), nullptr); + alarmButtons[i]->user_data = this; + lv_obj_set_event_cb(alarmButtons[i], launcherBtnEventHandler); + lv_obj_set_size(alarmButtons[i], 150, btnHeight); + lv_obj_add_style(alarmButtons[i], LV_BTN_PART_MAIN, &launcherButtonStyle); + lv_obj_align(alarmButtons[i], lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 5, -90 + (i * (btnHeight + btnSpacing))); + + // Create time label + alarmTimeLabels[i] = lv_label_create(alarmButtons[i], nullptr); + lv_obj_set_style_local_text_font(alarmTimeLabels[i], LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20); + uint8_t hours = alarmController.Hours(i); + if (clockType == Controllers::Settings::ClockType::H12) { + uint8_t displayHours = hours % 12; + if (displayHours == 0) { + displayHours = 12; + } + lv_label_set_text_fmt(alarmTimeLabels[i], "%2hhu:%02hhu %s", displayHours, alarmController.Minutes(i), hours < 12 ? "AM" : "PM"); + } else { + lv_label_set_text_fmt(alarmTimeLabels[i], "%02hhu:%02hhu", hours, alarmController.Minutes(i)); + } + lv_obj_align(alarmTimeLabels[i], alarmButtons[i], LV_ALIGN_CENTER, 0, -10); + + // Create recurrence label + alarmRecurLabels[i] = lv_label_create(alarmButtons[i], nullptr); + lv_obj_set_style_local_text_font(alarmRecurLabels[i], LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20); + lv_obj_set_style_local_text_opa(alarmRecurLabels[i], LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_60); + + using Pinetime::Controllers::AlarmController; + switch (alarmController.Recurrence(i)) { + case AlarmController::RecurType::None: + lv_label_set_text_static(alarmRecurLabels[i], "Once"); + break; + case AlarmController::RecurType::Daily: + lv_label_set_text_static(alarmRecurLabels[i], "Daily"); + break; + case AlarmController::RecurType::Weekdays: + lv_label_set_text_static(alarmRecurLabels[i], "M-F"); + break; + } + lv_obj_align(alarmRecurLabels[i], alarmButtons[i], LV_ALIGN_CENTER, 0, 12); + + // Create enable/disable switch + alarmSwitches[i] = lv_switch_create(lv_scr_act(), nullptr); + alarmSwitches[i]->user_data = this; + lv_obj_set_event_cb(alarmSwitches[i], switchEventHandler); + lv_obj_set_size(alarmSwitches[i], 70, 40); + lv_obj_set_style_local_bg_color(alarmSwitches[i], LV_SWITCH_PART_BG, LV_STATE_DEFAULT, bgColor); + lv_obj_align(alarmSwitches[i], lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -5, -90 + (i * (btnHeight + btnSpacing))); + + if (alarmController.IsEnabled(i)) { + lv_switch_on(alarmSwitches[i], LV_ANIM_OFF); + } else { + lv_switch_off(alarmSwitches[i], LV_ANIM_OFF); + } + } +} + +void Alarm::CreateAlarmConfigUI(uint8_t alarmIndex) { + selectedAlarmIndex = alarmIndex; hourCounter.Create(); lv_obj_align(hourCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0); @@ -61,12 +160,12 @@ Alarm::Alarm(Controllers::AlarmController& alarmController, lv_label_set_align(lblampm, LV_LABEL_ALIGN_CENTER); lv_obj_align(lblampm, lv_scr_act(), LV_ALIGN_CENTER, 0, 30); } - hourCounter.SetValue(alarmController.Hours()); + hourCounter.SetValue(alarmController.Hours(alarmIndex)); hourCounter.SetValueChangedEventCallback(this, ValueChangedHandler); minuteCounter.Create(); lv_obj_align(minuteCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0); - minuteCounter.SetValue(alarmController.Minutes()); + minuteCounter.SetValue(alarmController.Minutes(alarmIndex)); minuteCounter.SetValueChangedEventCallback(this, ValueChangedHandler); lv_obj_t* colonLabel = lv_label_create(lv_scr_act(), nullptr); @@ -107,23 +206,38 @@ Alarm::Alarm(Controllers::AlarmController& alarmController, lv_obj_t* txtInfo = lv_label_create(btnInfo, nullptr); lv_label_set_text_static(txtInfo, "i"); - enableSwitch = lv_switch_create(lv_scr_act(), nullptr); - enableSwitch->user_data = this; - lv_obj_set_event_cb(enableSwitch, btnEventHandler); - lv_obj_set_size(enableSwitch, 100, 50); - // Align to the center of 115px from edge - lv_obj_align(enableSwitch, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 7, 0); - lv_obj_set_style_local_bg_color(enableSwitch, LV_SWITCH_PART_BG, LV_STATE_DEFAULT, bgColor); + btnBack = lv_btn_create(lv_scr_act(), nullptr); + btnBack->user_data = this; + lv_obj_set_event_cb(btnBack, btnEventHandler); + lv_obj_set_size(btnBack, 115, 50); + lv_obj_align(btnBack, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); + lv_obj_set_style_local_bg_color(btnBack, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, bgColor); + lv_obj_t* txtBack = lv_label_create(btnBack, nullptr); + lv_label_set_text_static(txtBack, "Back"); UpdateAlarmTime(); +} - if (alarmController.IsAlerting()) { - SetAlerting(); - } else { - SetSwitchState(LV_ANIM_OFF); +void Alarm::OnLauncherButtonClicked(lv_obj_t* obj) { + // Find which alarm button was clicked + for (uint8_t i = 0; i < Controllers::AlarmController::MaxAlarms; i++) { + if (obj == alarmButtons[i]) { + // Switch to config mode for this alarm + lv_style_reset(&launcherButtonStyle); + lv_obj_clean(lv_scr_act()); + launcherMode = false; + CreateAlarmConfigUI(i); + return; + } } } +void Alarm::ReturnToLauncher() { + lv_obj_clean(lv_scr_act()); + launcherMode = true; + CreateLauncherUI(); +} + Alarm::~Alarm() { if (alarmController.IsAlerting()) { StopAlerting(); @@ -133,16 +247,30 @@ Alarm::~Alarm() { } void Alarm::DisableAlarm() { - if (alarmController.IsEnabled()) { - alarmController.DisableAlarm(); - lv_switch_off(enableSwitch, LV_ANIM_ON); + if (alarmController.IsEnabled(selectedAlarmIndex)) { + alarmController.DisableAlarm(selectedAlarmIndex); } } void Alarm::OnButtonEvent(lv_obj_t* obj, lv_event_t event) { + // Handle launcher mode switch events + if (launcherMode) { + if (event == LV_EVENT_VALUE_CHANGED) { + for (uint8_t i = 0; i < Controllers::AlarmController::MaxAlarms; i++) { + if (obj == alarmSwitches[i]) { + alarmController.SetEnabled(i, lv_switch_get_state(alarmSwitches[i])); + return; + } + } + } + return; + } + + // Handle config mode events if (event == LV_EVENT_CLICKED) { if (obj == btnStop) { StopAlerting(); + ReturnToLauncher(); return; } if (obj == btnInfo) { @@ -153,12 +281,8 @@ void Alarm::OnButtonEvent(lv_obj_t* obj, lv_event_t event) { HideInfo(); return; } - if (obj == enableSwitch) { - if (lv_switch_get_state(enableSwitch)) { - alarmController.ScheduleAlarm(); - } else { - alarmController.DisableAlarm(); - } + if (obj == btnBack) { + ReturnToLauncher(); return; } if (obj == btnRecur) { @@ -175,6 +299,7 @@ bool Alarm::OnButtonPushed() { } if (alarmController.IsAlerting()) { StopAlerting(); + ReturnToLauncher(); return true; } return false; @@ -198,11 +323,11 @@ void Alarm::UpdateAlarmTime() { lv_label_set_text_static(lblampm, "AM"); } } - alarmController.SetAlarmTime(hourCounter.GetValue(), minuteCounter.GetValue()); + alarmController.SetAlarmTime(selectedAlarmIndex, hourCounter.GetValue(), minuteCounter.GetValue()); } void Alarm::SetAlerting() { - lv_obj_set_hidden(enableSwitch, true); + lv_obj_set_hidden(btnBack, true); lv_obj_set_hidden(btnRecur, true); lv_obj_set_hidden(btnInfo, true); hourCounter.HideControls(); @@ -216,26 +341,12 @@ void Alarm::SetAlerting() { void Alarm::StopAlerting() { alarmController.StopAlerting(); motorController.StopRinging(); - SetSwitchState(LV_ANIM_OFF); if (taskStopAlarm != nullptr) { lv_task_del(taskStopAlarm); taskStopAlarm = nullptr; } wakeLock.Release(); - lv_obj_set_hidden(btnStop, true); - hourCounter.ShowControls(); - minuteCounter.ShowControls(); - lv_obj_set_hidden(btnInfo, false); - lv_obj_set_hidden(btnRecur, false); - lv_obj_set_hidden(enableSwitch, false); -} - -void Alarm::SetSwitchState(lv_anim_enable_t anim) { - if (alarmController.IsEnabled()) { - lv_switch_on(enableSwitch, anim); - } else { - lv_switch_off(enableSwitch, anim); - } + lv_indev_wait_release(lv_indev_get_act()); } void Alarm::ShowInfo() { @@ -251,7 +362,7 @@ void Alarm::ShowInfo() { txtMessage = lv_label_create(btnMessage, nullptr); lv_obj_set_style_local_bg_color(btnMessage, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_NAVY); - if (alarmController.IsEnabled()) { + if (alarmController.IsEnabled(selectedAlarmIndex)) { auto timeToAlarm = alarmController.SecondsToAlarm(); auto daysToAlarm = timeToAlarm / 86400; @@ -278,7 +389,7 @@ void Alarm::HideInfo() { void Alarm::SetRecurButtonState() { using Pinetime::Controllers::AlarmController; - switch (alarmController.Recurrence()) { + switch (alarmController.Recurrence(selectedAlarmIndex)) { case AlarmController::RecurType::None: lv_label_set_text_static(txtRecur, "ONCE"); break; @@ -292,15 +403,15 @@ void Alarm::SetRecurButtonState() { void Alarm::ToggleRecurrence() { using Pinetime::Controllers::AlarmController; - switch (alarmController.Recurrence()) { + switch (alarmController.Recurrence(selectedAlarmIndex)) { case AlarmController::RecurType::None: - alarmController.SetRecurrence(AlarmController::RecurType::Daily); + alarmController.SetRecurrence(selectedAlarmIndex, AlarmController::RecurType::Daily); break; case AlarmController::RecurType::Daily: - alarmController.SetRecurrence(AlarmController::RecurType::Weekdays); + alarmController.SetRecurrence(selectedAlarmIndex, AlarmController::RecurType::Weekdays); break; case AlarmController::RecurType::Weekdays: - alarmController.SetRecurrence(AlarmController::RecurType::None); + alarmController.SetRecurrence(selectedAlarmIndex, AlarmController::RecurType::None); } SetRecurButtonState(); } diff --git a/src/displayapp/screens/Alarm.h b/src/displayapp/screens/Alarm.h index 2dde6e8754..2c202e0faa 100644 --- a/src/displayapp/screens/Alarm.h +++ b/src/displayapp/screens/Alarm.h @@ -37,6 +37,7 @@ namespace Pinetime { ~Alarm() override; void SetAlerting(); void OnButtonEvent(lv_obj_t* obj, lv_event_t event); + void OnLauncherButtonClicked(lv_obj_t* obj); bool OnButtonPushed() override; bool OnTouchEvent(TouchEvents event) override; void OnValueChanged(); @@ -46,22 +47,36 @@ namespace Pinetime { Controllers::AlarmController& alarmController; System::WakeLock wakeLock; Controllers::MotorController& motorController; + Controllers::Settings::ClockType clockType; - lv_obj_t *btnStop, *txtStop, *btnRecur, *txtRecur, *btnInfo, *enableSwitch; + bool launcherMode = true; + uint8_t selectedAlarmIndex = 0; + + // Launcher mode elements + lv_obj_t* alarmButtons[Controllers::AlarmController::MaxAlarms] = {nullptr}; + lv_obj_t* alarmTimeLabels[Controllers::AlarmController::MaxAlarms] = {nullptr}; + lv_obj_t* alarmRecurLabels[Controllers::AlarmController::MaxAlarms] = {nullptr}; + lv_obj_t* alarmSwitches[Controllers::AlarmController::MaxAlarms] = {nullptr}; + lv_style_t launcherButtonStyle; + + // Config mode elements + lv_obj_t *btnStop, *txtStop, *btnRecur, *txtRecur, *btnInfo, *btnBack; lv_obj_t* lblampm = nullptr; lv_obj_t* txtMessage = nullptr; lv_obj_t* btnMessage = nullptr; lv_task_t* taskStopAlarm = nullptr; enum class EnableButtonState { On, Off, Alerting }; + void CreateLauncherUI(); + void CreateAlarmConfigUI(uint8_t alarmIndex); void DisableAlarm(); void SetRecurButtonState(); - void SetSwitchState(lv_anim_enable_t anim); void SetAlarm(); void ShowInfo(); void HideInfo(); void ToggleRecurrence(); void UpdateAlarmTime(); + void ReturnToLauncher(); Widgets::Counter hourCounter = Widgets::Counter(0, 23, jetbrains_mono_76); Widgets::Counter minuteCounter = Widgets::Counter(0, 59, jetbrains_mono_76); }; diff --git a/src/displayapp/widgets/StatusIcons.cpp b/src/displayapp/widgets/StatusIcons.cpp index 777731a59f..ce6d9cce70 100644 --- a/src/displayapp/widgets/StatusIcons.cpp +++ b/src/displayapp/widgets/StatusIcons.cpp @@ -43,7 +43,7 @@ void StatusIcons::Update() { batteryIcon.SetBatteryPercentage(batteryPercent); } - alarmEnabled = alarmController.IsEnabled(); + alarmEnabled = alarmController.AnyAlarmEnabled(); if (alarmEnabled.IsUpdated()) { lv_obj_set_hidden(alarmIcon, !alarmEnabled.Get()); } diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 56bf9273e1..9ef6858116 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -219,7 +219,7 @@ void SystemTask::Work() { GoToSleep(); break; case Messages::OnNewTime: - if (alarmController.IsEnabled()) { + if (alarmController.AnyAlarmEnabled()) { alarmController.ScheduleAlarm(); } break;