
* AlarmController: Add saving alarm time to file Save the set alarm time to the SPI NOR flash, so it does not reset to the default value when the watch resets, e.g. due to watchdog timeout or reflashing of a new version of InfiniTime. Just like the `Settings.h` `LoadSettingsFromFile()` the previous alarm at boot (if available) and `SaveSettingsToFile()` the current alarm when the `Alarm.h` screen is closed (only if the settings have changed). The alarm-settings file is stored in `.system/alarm.dat`. The `.system` folder is created if it doesn't yet exist. Fixes: https://github.com/InfiniTimeOrg/InfiniTime/issues/1330 * alarmController: close .system dir after usage Close the `lfs_dir` object for the `.system` dir after usage. Otherwise on the second changed alarm the system will lockup because the `.system` dir is already open and was never closed. --------- Co-authored-by: Galdor Takacs <g@ldor.de>
299 lines
9.6 KiB
C++
299 lines
9.6 KiB
C++
/* Copyright (C) 2021 mruss77, Florian
|
|
|
|
This file is part of InfiniTime.
|
|
|
|
InfiniTime is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published
|
|
by the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
InfiniTime is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "displayapp/screens/Alarm.h"
|
|
#include "displayapp/screens/Screen.h"
|
|
#include "displayapp/screens/Symbols.h"
|
|
#include "displayapp/InfiniTimeTheme.h"
|
|
#include "components/settings/Settings.h"
|
|
#include "components/alarm/AlarmController.h"
|
|
#include "components/motor/MotorController.h"
|
|
#include "systemtask/SystemTask.h"
|
|
|
|
using namespace Pinetime::Applications::Screens;
|
|
using Pinetime::Controllers::AlarmController;
|
|
|
|
namespace {
|
|
void ValueChangedHandler(void* userData) {
|
|
auto* screen = static_cast<Alarm*>(userData);
|
|
screen->OnValueChanged();
|
|
}
|
|
}
|
|
|
|
static void btnEventHandler(lv_obj_t* obj, lv_event_t event) {
|
|
auto* screen = static_cast<Alarm*>(obj->user_data);
|
|
screen->OnButtonEvent(obj, event);
|
|
}
|
|
|
|
static void StopAlarmTaskCallback(lv_task_t* task) {
|
|
auto* screen = static_cast<Alarm*>(task->user_data);
|
|
screen->StopAlerting();
|
|
}
|
|
|
|
Alarm::Alarm(Controllers::AlarmController& alarmController,
|
|
Controllers::Settings::ClockType clockType,
|
|
System::SystemTask& systemTask,
|
|
Controllers::MotorController& motorController)
|
|
: alarmController {alarmController}, wakeLock(systemTask), motorController {motorController} {
|
|
|
|
hourCounter.Create();
|
|
lv_obj_align(hourCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
|
if (clockType == Controllers::Settings::ClockType::H12) {
|
|
hourCounter.EnableTwelveHourMode();
|
|
|
|
lblampm = lv_label_create(lv_scr_act(), nullptr);
|
|
lv_obj_set_style_local_text_font(lblampm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20);
|
|
lv_label_set_text_static(lblampm, "AM");
|
|
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.SetValueChangedEventCallback(this, ValueChangedHandler);
|
|
|
|
minuteCounter.Create();
|
|
lv_obj_align(minuteCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
|
|
minuteCounter.SetValue(alarmController.Minutes());
|
|
minuteCounter.SetValueChangedEventCallback(this, ValueChangedHandler);
|
|
|
|
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);
|
|
lv_label_set_text_static(colonLabel, ":");
|
|
lv_obj_align(colonLabel, lv_scr_act(), LV_ALIGN_CENTER, 0, -29);
|
|
|
|
btnStop = lv_btn_create(lv_scr_act(), nullptr);
|
|
btnStop->user_data = this;
|
|
lv_obj_set_event_cb(btnStop, btnEventHandler);
|
|
lv_obj_set_size(btnStop, 115, 50);
|
|
lv_obj_align(btnStop, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
|
|
lv_obj_set_style_local_bg_color(btnStop, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED);
|
|
txtStop = lv_label_create(btnStop, nullptr);
|
|
lv_label_set_text_static(txtStop, Symbols::stop);
|
|
lv_obj_set_hidden(btnStop, true);
|
|
|
|
static constexpr lv_color_t bgColor = Colors::bgAlt;
|
|
|
|
btnRecur = lv_btn_create(lv_scr_act(), nullptr);
|
|
btnRecur->user_data = this;
|
|
lv_obj_set_event_cb(btnRecur, btnEventHandler);
|
|
lv_obj_set_size(btnRecur, 115, 50);
|
|
lv_obj_align(btnRecur, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
|
|
txtRecur = lv_label_create(btnRecur, nullptr);
|
|
SetRecurButtonState();
|
|
lv_obj_set_style_local_bg_color(btnRecur, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, bgColor);
|
|
|
|
btnInfo = lv_btn_create(lv_scr_act(), nullptr);
|
|
btnInfo->user_data = this;
|
|
lv_obj_set_event_cb(btnInfo, btnEventHandler);
|
|
lv_obj_set_size(btnInfo, 50, 50);
|
|
lv_obj_align(btnInfo, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, -4);
|
|
lv_obj_set_style_local_bg_color(btnInfo, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, bgColor);
|
|
lv_obj_set_style_local_border_width(btnInfo, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 4);
|
|
lv_obj_set_style_local_border_color(btnInfo, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
|
|
|
|
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);
|
|
|
|
UpdateAlarmTime();
|
|
|
|
if (alarmController.IsAlerting()) {
|
|
SetAlerting();
|
|
} else {
|
|
SetSwitchState(LV_ANIM_OFF);
|
|
}
|
|
}
|
|
|
|
Alarm::~Alarm() {
|
|
if (alarmController.IsAlerting()) {
|
|
StopAlerting();
|
|
}
|
|
lv_obj_clean(lv_scr_act());
|
|
alarmController.SaveAlarm();
|
|
}
|
|
|
|
void Alarm::DisableAlarm() {
|
|
if (alarmController.IsEnabled()) {
|
|
alarmController.DisableAlarm();
|
|
lv_switch_off(enableSwitch, LV_ANIM_ON);
|
|
}
|
|
}
|
|
|
|
void Alarm::OnButtonEvent(lv_obj_t* obj, lv_event_t event) {
|
|
if (event == LV_EVENT_CLICKED) {
|
|
if (obj == btnStop) {
|
|
StopAlerting();
|
|
return;
|
|
}
|
|
if (obj == btnInfo) {
|
|
ShowInfo();
|
|
return;
|
|
}
|
|
if (obj == btnMessage) {
|
|
HideInfo();
|
|
return;
|
|
}
|
|
if (obj == enableSwitch) {
|
|
if (lv_switch_get_state(enableSwitch)) {
|
|
alarmController.ScheduleAlarm();
|
|
} else {
|
|
alarmController.DisableAlarm();
|
|
}
|
|
return;
|
|
}
|
|
if (obj == btnRecur) {
|
|
DisableAlarm();
|
|
ToggleRecurrence();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Alarm::OnButtonPushed() {
|
|
if (txtMessage != nullptr && btnMessage != nullptr) {
|
|
HideInfo();
|
|
return true;
|
|
}
|
|
if (alarmController.IsAlerting()) {
|
|
StopAlerting();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Alarm::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
|
// Don't allow closing the screen by swiping while the alarm is alerting
|
|
return alarmController.IsAlerting() && event == TouchEvents::SwipeDown;
|
|
}
|
|
|
|
void Alarm::OnValueChanged() {
|
|
DisableAlarm();
|
|
UpdateAlarmTime();
|
|
}
|
|
|
|
void Alarm::UpdateAlarmTime() {
|
|
if (lblampm != nullptr) {
|
|
if (hourCounter.GetValue() >= 12) {
|
|
lv_label_set_text_static(lblampm, "PM");
|
|
} else {
|
|
lv_label_set_text_static(lblampm, "AM");
|
|
}
|
|
}
|
|
alarmController.SetAlarmTime(hourCounter.GetValue(), minuteCounter.GetValue());
|
|
}
|
|
|
|
void Alarm::SetAlerting() {
|
|
lv_obj_set_hidden(enableSwitch, true);
|
|
lv_obj_set_hidden(btnStop, false);
|
|
taskStopAlarm = lv_task_create(StopAlarmTaskCallback, pdMS_TO_TICKS(60 * 1000), LV_TASK_PRIO_MID, this);
|
|
motorController.StartRinging();
|
|
wakeLock.Lock();
|
|
}
|
|
|
|
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(enableSwitch, false);
|
|
lv_obj_set_hidden(btnStop, true);
|
|
}
|
|
|
|
void Alarm::SetSwitchState(lv_anim_enable_t anim) {
|
|
if (alarmController.IsEnabled()) {
|
|
lv_switch_on(enableSwitch, anim);
|
|
} else {
|
|
lv_switch_off(enableSwitch, anim);
|
|
}
|
|
}
|
|
|
|
void Alarm::ShowInfo() {
|
|
if (btnMessage != nullptr) {
|
|
return;
|
|
}
|
|
btnMessage = lv_btn_create(lv_scr_act(), nullptr);
|
|
btnMessage->user_data = this;
|
|
lv_obj_set_event_cb(btnMessage, btnEventHandler);
|
|
lv_obj_set_height(btnMessage, 200);
|
|
lv_obj_set_width(btnMessage, 150);
|
|
lv_obj_align(btnMessage, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
|
|
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()) {
|
|
auto timeToAlarm = alarmController.SecondsToAlarm();
|
|
|
|
auto daysToAlarm = timeToAlarm / 86400;
|
|
auto hrsToAlarm = (timeToAlarm % 86400) / 3600;
|
|
auto minToAlarm = (timeToAlarm % 3600) / 60;
|
|
auto secToAlarm = timeToAlarm % 60;
|
|
|
|
lv_label_set_text_fmt(txtMessage,
|
|
"Time to\nalarm:\n%2lu Days\n%2lu Hours\n%2lu Minutes\n%2lu Seconds",
|
|
daysToAlarm,
|
|
hrsToAlarm,
|
|
minToAlarm,
|
|
secToAlarm);
|
|
} else {
|
|
lv_label_set_text_static(txtMessage, "Alarm\nis not\nset.");
|
|
}
|
|
}
|
|
|
|
void Alarm::HideInfo() {
|
|
lv_obj_del(btnMessage);
|
|
txtMessage = nullptr;
|
|
btnMessage = nullptr;
|
|
}
|
|
|
|
void Alarm::SetRecurButtonState() {
|
|
using Pinetime::Controllers::AlarmController;
|
|
switch (alarmController.Recurrence()) {
|
|
case AlarmController::RecurType::None:
|
|
lv_label_set_text_static(txtRecur, "ONCE");
|
|
break;
|
|
case AlarmController::RecurType::Daily:
|
|
lv_label_set_text_static(txtRecur, "DAILY");
|
|
break;
|
|
case AlarmController::RecurType::Weekdays:
|
|
lv_label_set_text_static(txtRecur, "MON-FRI");
|
|
}
|
|
}
|
|
|
|
void Alarm::ToggleRecurrence() {
|
|
using Pinetime::Controllers::AlarmController;
|
|
switch (alarmController.Recurrence()) {
|
|
case AlarmController::RecurType::None:
|
|
alarmController.SetRecurrence(AlarmController::RecurType::Daily);
|
|
break;
|
|
case AlarmController::RecurType::Daily:
|
|
alarmController.SetRecurrence(AlarmController::RecurType::Weekdays);
|
|
break;
|
|
case AlarmController::RecurType::Weekdays:
|
|
alarmController.SetRecurrence(AlarmController::RecurType::None);
|
|
}
|
|
SetRecurButtonState();
|
|
}
|