diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d903629b17..68dd5c43f8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -423,6 +423,7 @@ list(APPEND SOURCE_FILES displayapp/screens/settings/SettingSetDate.cpp displayapp/screens/settings/SettingSetTime.cpp displayapp/screens/settings/SettingChimes.cpp + displayapp/screens/settings/SettingQuietHour.cpp displayapp/screens/settings/SettingShakeThreshold.cpp displayapp/screens/settings/SettingBluetooth.cpp diff --git a/src/components/datetime/DateTimeController.cpp b/src/components/datetime/DateTimeController.cpp index 9e9fb6e4fe..7d2a8378ae 100644 --- a/src/components/datetime/DateTimeController.cpp +++ b/src/components/datetime/DateTimeController.cpp @@ -89,6 +89,16 @@ void DateTime::UpdateTime(uint32_t systickCounter) { if (systemTask != nullptr) { systemTask->PushMessage(System::Messages::OnNewHalfHour); } + Controllers::Settings::QuietHour* quietHour = settingsController.GetQuietHour(); + uint8_t now = hour * 2 + minute / 30; + // Quiet Hour Start + if (quietHour[0].auto_toggle && quietHour[0].time == now) { + settingsController.SetNotificationStatus(Controllers::Settings::Notification::Sleep); + } + // Quiet Hour Stop + if (quietHour[1].auto_toggle && quietHour[1].time == now) { + settingsController.SetNotificationStatus(settingsController.GetPrevNotificationStatus()); + } } else if (minute != 0 && minute != 30) { isHalfHourAlreadyNotified = false; } diff --git a/src/components/settings/Settings.h b/src/components/settings/Settings.h index efa44fdee4..d9870ce986 100644 --- a/src/components/settings/Settings.h +++ b/src/components/settings/Settings.h @@ -41,6 +41,12 @@ namespace Pinetime { enum class PTSGaugeStyle : uint8_t { Full, Half, Numeric }; enum class PTSWeather : uint8_t { On, Off }; + struct QuietHour { + bool auto_toggle = false; + // 30 minutes increment + uint8_t time = 0; + }; + struct PineTimeStyle { Colors ColorTime = Colors::Teal; Colors ColorBar = Colors::Teal; @@ -64,6 +70,19 @@ namespace Pinetime { void Init(); void SaveSettings(); + void SetQuietHour(QuietHour quietHour[2]) { + for (uint8_t i = 0; i < 2; i++) { + if (quietHour[i].auto_toggle != settings.quietHour[i].auto_toggle || quietHour[i].time != settings.quietHour[i].time) { + settingsChanged = true; + } + settings.quietHour[i] = quietHour[i]; + } + }; + + QuietHour* GetQuietHour() { + return settings.quietHour; + }; + void SetWatchFace(Pinetime::Applications::WatchFace face) { if (face != settings.watchFace) { settingsChanged = true; @@ -189,6 +208,9 @@ namespace Pinetime { if (status != settings.notificationStatus) { settingsChanged = true; } + if (settings.notificationStatus != Notification::Sleep) { + prevNotificationStatus = settings.notificationStatus; + } settings.notificationStatus = status; }; @@ -196,6 +218,10 @@ namespace Pinetime { return settings.notificationStatus; }; + Notification GetPrevNotificationStatus() const { + return prevNotificationStatus; + }; + void SetScreenTimeOut(uint32_t timeout) { if (timeout != settings.screenTimeOut) { settingsChanged = true; @@ -279,7 +305,7 @@ namespace Pinetime { private: Pinetime::Controllers::FS& fs; - static constexpr uint32_t settingsVersion = 0x0005; + static constexpr uint32_t settingsVersion = 0x0006; struct SettingsData { uint32_t version = settingsVersion; @@ -293,6 +319,7 @@ namespace Pinetime { ChimesOption chimesOption = ChimesOption::None; PineTimeStyle PTS; + QuietHour quietHour[2]; WatchFaceInfineat watchFaceInfineat; @@ -303,6 +330,7 @@ namespace Pinetime { SettingsData settings; bool settingsChanged = false; + Notification prevNotificationStatus = Notification::On; uint8_t appMenu = 0; uint8_t settingsMenu = 0; diff --git a/src/displayapp/Apps.h b/src/displayapp/Apps.h index f253bc0387..0b1f480c81 100644 --- a/src/displayapp/Apps.h +++ b/src/displayapp/Apps.h @@ -35,6 +35,7 @@ namespace Pinetime { SettingSteps, SettingSetDateTime, SettingChimes, + SettingQuietHour, SettingShakeThreshold, SettingBluetooth, Error diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index a930fe961c..78338f6aaf 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -46,6 +46,7 @@ #include "displayapp/screens/settings/SettingSteps.h" #include "displayapp/screens/settings/SettingSetDateTime.h" #include "displayapp/screens/settings/SettingChimes.h" +#include "displayapp/screens/settings/SettingQuietHour.h" #include "displayapp/screens/settings/SettingShakeThreshold.h" #include "displayapp/screens/settings/SettingBluetooth.h" @@ -494,6 +495,9 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio case Apps::SettingChimes: currentScreen = std::make_unique(settingsController); break; + case Apps::SettingQuietHour: + currentScreen = std::make_unique(settingsController); + break; case Apps::SettingShakeThreshold: currentScreen = std::make_unique(settingsController, motionController, *systemTask); break; diff --git a/src/displayapp/screens/settings/SettingQuietHour.cpp b/src/displayapp/screens/settings/SettingQuietHour.cpp new file mode 100644 index 0000000000..3a514afe73 --- /dev/null +++ b/src/displayapp/screens/settings/SettingQuietHour.cpp @@ -0,0 +1,135 @@ +#include "displayapp/screens/settings/SettingQuietHour.h" +#include +#include "displayapp/screens/Symbols.h" +#include "components/settings/Settings.h" + +using namespace Pinetime::Applications::Screens; +using QuietHour = Pinetime::Controllers::Settings::QuietHour; + +namespace { + void event_handler(lv_obj_t* obj, lv_event_t event) { + auto* screen = static_cast(obj->user_data); + screen->UpdateSelected(obj, event); + } +} + +SettingQuietHour::SettingQuietHour(Pinetime::Controllers::Settings& settingsController) : settingsController {settingsController} { + + lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text_static(title, "Quiet Hour"); + lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); + lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 15, 15); + + lv_obj_t* icon = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); + lv_label_set_text_static(icon, Symbols::clock); + lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER); + lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0); + + lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_bg_opa(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); + lv_obj_set_style_local_pad_all(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10); + 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, 40); + lv_obj_set_width(container1, LV_HOR_RES - 10); + lv_obj_set_height(container1, LV_VER_RES - 30); + lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); + + char optionName[2][6] = { + "Start", + "End", + }; + + // start time and end time options + for (uint8_t i = 0; i < 2; i++) { + // check box + cbOption[i] = lv_checkbox_create(container1, nullptr); + lv_checkbox_set_text(cbOption[i], optionName[i]); + cbOption[i]->user_data = this; + lv_obj_set_event_cb(cbOption[i], event_handler); + + time_container[i] = lv_cont_create(container1, nullptr); + lv_obj_set_style_local_bg_opa(time_container[i], LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); + lv_obj_set_size(time_container[i], LV_HOR_RES - 60, 50); + + // hour and minute button + for (uint8_t j = 0; j < 2; j++) { + btnTime[i][j] = lv_btn_create(time_container[i], nullptr); + btnTime[i][j]->user_data = this; + lv_obj_set_event_cb(btnTime[i][j], event_handler); + lv_obj_set_size(btnTime[i][j], LV_HOR_RES / 3, 50); + if (j == 0) { + lv_obj_align(btnTime[i][j], NULL, LV_ALIGN_IN_LEFT_MID, 0, 0); + colon[i] = lv_label_create(time_container[i], nullptr); + lv_label_set_text_static(colon[i], ":"); + lv_obj_align(colon[i], NULL, LV_ALIGN_CENTER, 0, 0); + } else { + lv_obj_align(btnTime[i][j], NULL, LV_ALIGN_IN_RIGHT_MID, 0, 0); + } + txtTime[i][j] = lv_label_create(btnTime[i][j], nullptr); + lv_label_set_align(txtTime[i][j], LV_LABEL_ALIGN_CENTER); + } + } + UpdateButton(); +} + +SettingQuietHour::~SettingQuietHour() { + lv_obj_clean(lv_scr_act()); + settingsController.SaveSettings(); +} + +void SettingQuietHour::UpdateButton() { + QuietHour* saved_options = settingsController.GetQuietHour(); + + for (uint8_t i = 0; i < 2; i++) { + lv_checkbox_set_checked(cbOption[i], saved_options[i].auto_toggle); + if (saved_options[i].auto_toggle) { + lv_btn_set_state(btnTime[i][0], LV_BTN_STATE_DISABLED); + lv_btn_set_state(btnTime[i][1], LV_BTN_STATE_DISABLED); + } else { + lv_btn_set_state(btnTime[i][0], LV_BTN_STATE_RELEASED); + lv_btn_set_state(btnTime[i][1], LV_BTN_STATE_RELEASED); + } + lv_label_set_text_fmt(txtTime[i][0], "%02d", saved_options[i].time / 2); // hour + lv_label_set_text_fmt(txtTime[i][1], "%02d", (saved_options[i].time % 2) * 30); // minute + } +} + +void SettingQuietHour::UpdateSelected(lv_obj_t* obj, lv_event_t event) { + if (event == LV_EVENT_VALUE_CHANGED) { + for (uint8_t i = 0; i < 2; i++) { + if (cbOption[i] == obj) { + if (lv_checkbox_is_inactive(obj)) { + return; + } + QuietHour* saved_options = settingsController.GetQuietHour(); + if (lv_checkbox_is_checked(obj) != saved_options[i].auto_toggle) { + saved_options[i].auto_toggle = lv_checkbox_is_checked(obj); + // if start time == stop time, uncheck the other option + if (saved_options[i].auto_toggle == true && saved_options[i].auto_toggle == true && + saved_options[0].time == saved_options[1].time) { + lv_checkbox_set_checked(cbOption[!i], false); + saved_options[!i].auto_toggle = false; + } + settingsController.SetQuietHour(saved_options); + } + UpdateButton(); + return; + } + } + } else if (event == LV_EVENT_CLICKED) { + for (uint8_t i = 0; i < 2; i++) { + for (uint8_t j = 0; j < 2; j++) { + if (btnTime[i][j] == obj) { + QuietHour* saved_options = settingsController.GetQuietHour(); + saved_options[i].time = (saved_options[i].time + 2 - j) % 48; + settingsController.SetQuietHour(saved_options); + UpdateButton(); + return; + } + } + } + } +} diff --git a/src/displayapp/screens/settings/SettingQuietHour.h b/src/displayapp/screens/settings/SettingQuietHour.h new file mode 100644 index 0000000000..d4faaafd5c --- /dev/null +++ b/src/displayapp/screens/settings/SettingQuietHour.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include "components/settings/Settings.h" +#include "displayapp/screens/Screen.h" + +namespace Pinetime { + + namespace Applications { + namespace Screens { + + class SettingQuietHour : public Screen { + public: + SettingQuietHour(Pinetime::Controllers::Settings& settingsController); + ~SettingQuietHour() override; + + void UpdateSelected(lv_obj_t* object, lv_event_t event); + void UpdateButton(); + + private: + lv_obj_t *cbOption[2], *colon[2], *btnTime[2][2], *txtTime[2][2], *time_container[2]; + + Controllers::Settings& settingsController; + }; + } + } +} diff --git a/src/displayapp/screens/settings/Settings.h b/src/displayapp/screens/settings/Settings.h index 3f8097538e..894c043d16 100644 --- a/src/displayapp/screens/settings/Settings.h +++ b/src/displayapp/screens/settings/Settings.h @@ -29,7 +29,7 @@ namespace Pinetime { static constexpr int entriesPerScreen = 4; // Increment this when more space is needed - static constexpr int nScreens = 3; + static constexpr int nScreens = 4; static constexpr std::array entries {{ {Symbols::sun, "Display", Apps::SettingDisplay}, @@ -41,6 +41,7 @@ namespace Pinetime { {Symbols::clock, "Date&Time", Apps::SettingSetDateTime}, {Symbols::batteryHalf, "Battery", Apps::BatteryInfo}, {Symbols::clock, "Chimes", Apps::SettingChimes}, + {Symbols::clock, "Quiet Hour", Apps::SettingQuietHour}, {Symbols::tachometer, "Shake Calib.", Apps::SettingShakeThreshold}, {Symbols::check, "Firmware", Apps::FirmwareValidation},