#include "Clock.h" #include #include #include #include "BatteryIcon.h" #include "BleIcon.h" #include "NotificationIcon.h" #include "Symbols.h" #include "components/battery/BatteryController.h" #include "components/ble/BleController.h" #include "components/ble/NotificationManager.h" #include "components/heartrate/HeartRateController.h" #include "../DisplayApp.h" using namespace Pinetime::Applications::Screens; static void event_handler(lv_obj_t * obj, lv_event_t event) { Clock* screen = static_cast(obj->user_data); screen->OnObjectEvent(obj, event); } Clock::Clock(DisplayApp* app, Controllers::DateTime& dateTimeController, Controllers::Battery& batteryController, Controllers::Ble& bleController, Controllers::NotificationManager& notificatioManager, Controllers::HeartRateController& heartRateController): Screen(app), currentDateTime{{}}, dateTimeController{dateTimeController}, batteryController{batteryController}, bleController{bleController}, notificatioManager{notificatioManager}, heartRateController{heartRateController} { displayedChar[0] = 0; displayedChar[1] = 0; displayedChar[2] = 0; displayedChar[3] = 0; displayedChar[4] = 0; batteryIcon = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(batteryIcon, Symbols::batteryFull); lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 2); batteryPlug = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(batteryPlug, Symbols::plug); lv_obj_align(batteryPlug, batteryIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0); bleIcon = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(bleIcon, Symbols::bluetooth); lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0); notificationIcon = lv_label_create(lv_scr_act(), NULL); lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false)); lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 10, 0); label_date = lv_label_create(lv_scr_act(), nullptr); lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 10, 60); label_time_shadow = lv_label_create(lv_scr_act(), nullptr); lv_label_set_recolor(label_time_shadow, true); lv_obj_align(label_time_shadow, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 7, -23); label_time = lv_label_create(lv_scr_act(), nullptr); lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 5, -25); backgroundLabel = lv_label_create(lv_scr_act(), nullptr); backgroundLabel->user_data = this; lv_obj_set_click(backgroundLabel, true); lv_obj_set_event_cb(backgroundLabel, event_handler); lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP); lv_obj_set_size(backgroundLabel, 240, 240); lv_obj_set_pos(backgroundLabel, 0, 0); lv_label_set_text(backgroundLabel, ""); heartbeatIcon = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(heartbeatIcon, Symbols::heartBeat); lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2); heartbeatValue = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(heartbeatValue, "0"); lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0); heartbeatBpm = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(heartbeatBpm, "BPM"); lv_obj_align(heartbeatBpm, heartbeatValue, LV_ALIGN_OUT_RIGHT_MID, 5, 0); stepValue = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(stepValue, "0"); lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2); stepIcon = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(stepIcon, Symbols::shoe); lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); } Clock::~Clock() { lv_obj_clean(lv_scr_act()); } bool Clock::Refresh() { batteryPercentRemaining = batteryController.PercentRemaining(); if (batteryPercentRemaining.IsUpdated()) { auto batteryPercent = batteryPercentRemaining.Get(); lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent)); auto isCharging = batteryController.IsCharging() || batteryController.IsPowerPresent(); lv_label_set_text(batteryPlug, BatteryIcon::GetPlugIcon(isCharging)); } bleState = bleController.IsConnected(); if (bleState.IsUpdated()) { if(bleState.Get() == true) { lv_label_set_text(bleIcon, BleIcon::GetIcon(true)); } else { lv_label_set_text(bleIcon, BleIcon::GetIcon(false)); } } lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 5); lv_obj_align(batteryPlug, batteryIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0); lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0); notificationState = notificatioManager.AreNewNotificationsAvailable(); if(notificationState.IsUpdated()) { if(notificationState.Get() == true) lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(true)); else lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false)); } currentDateTime = dateTimeController.CurrentDateTime(); if(currentDateTime.IsUpdated()) { auto newDateTime = currentDateTime.Get(); auto dp = date::floor(newDateTime); auto time = date::make_time(newDateTime-dp); auto yearMonthDay = date::year_month_day(dp); auto year = (int)yearMonthDay.year(); auto month = static_cast((unsigned)yearMonthDay.month()); auto day = (unsigned)yearMonthDay.day(); auto dayOfWeek = static_cast(date::weekday(yearMonthDay).iso_encoding()); auto hour = time.hours().count(); auto minute = time.minutes().count(); char minutesChar[3]; sprintf(minutesChar, "%02d", static_cast(minute)); char hoursChar[3]; sprintf(hoursChar, "%02d", static_cast(hour)); printwords(static_cast(hour), static_cast(minute)); if(hoursChar[0] != displayedChar[0] || hoursChar[1] != displayedChar[1] || minutesChar[0] != displayedChar[2] || minutesChar[1] != displayedChar[3]) { displayedChar[0] = hoursChar[0]; displayedChar[1] = hoursChar[1]; displayedChar[2] = minutesChar[0]; displayedChar[3] = minutesChar[1]; lv_label_set_text(label_time_shadow, timeStrShadow); lv_label_set_text(label_time, timeStr); } if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) { char dateStr[22]; sprintf(dateStr, "%s %d %s %d", DayOfWeekToString(dayOfWeek), day, MonthToString(month), year); lv_label_set_text(label_date, dateStr); currentYear = year; currentMonth = month; currentDayOfWeek = dayOfWeek; currentDay = day; } } heartbeat = heartRateController.HeartRate(); heartbeatRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped; if(heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) { char heartbeatBuffer[4]; if(heartbeatRunning.Get()) sprintf(heartbeatBuffer, "%d", heartbeat.Get()); else sprintf(heartbeatBuffer, "---"); lv_label_set_text(heartbeatValue, heartbeatBuffer); lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2); lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0); lv_obj_align(heartbeatBpm, heartbeatValue, LV_ALIGN_OUT_RIGHT_MID, 5, 0); } // TODO stepCount = stepController.GetValue(); if(stepCount.IsUpdated()) { char stepBuffer[5]; sprintf(stepBuffer, "%lu", stepCount.Get()); lv_label_set_text(stepValue, stepBuffer); lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2); lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); } return running; } const char *Clock::MonthToString(Pinetime::Controllers::DateTime::Months month) { return Clock::MonthsString[static_cast(month)]; } const char *Clock::DayOfWeekToString(Pinetime::Controllers::DateTime::Days dayOfWeek) { return Clock::DaysString[static_cast(dayOfWeek)]; } char const *Clock::DaysString[] = { "", "mon.", "tue.", "wed.", "thu.", "fri.", "sat.", "sun." }; char const *Clock::MonthsString[] = { "", "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; char const *Clock::nums[] = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty", "twenty one", "twenty two", "twenty three", "twenty four", "twenty five", "twenty six", "twenty seven", "twenty eight", "twenty nine", }; void Clock::OnObjectEvent(lv_obj_t *obj, lv_event_t event) { if(obj == backgroundLabel) { if (event == LV_EVENT_CLICKED) { running = false; } } } void Clock::printwords(int h, int m) { if (m == 0) { sprintf(timeStrShadow, "#404040 %s#\n#404040 o' clock#\n", nums[h]); sprintf(timeStr, "%s\no' clock\n", nums[h]); } else if (m == 1){ sprintf(timeStrShadow, "#404040 one minute#\n#404040 past# #404040 %s#\n", nums[h]); sprintf(timeStr, "one minute\npast %s\n", nums[h]); } else if (m == 59) { sprintf(timeStrShadow, "#404040 one minute#\n#404040 to# #404040 %s#\n", nums[(h % 12) + 1]); sprintf(timeStr, "one minute\nto %s\n", nums[(h % 12) + 1]); } else if (m == 15) { sprintf(timeStrShadow, "#404040 quarter past#\n#404040 %s#\n", nums[h]); sprintf(timeStr, "quarter past\n%s\n", nums[h]); } else if (m == 30) { sprintf(timeStrShadow, "#404040 half past#\n#404040 %s#\n", nums[h]); sprintf(timeStr, "half past\n%s\n", nums[h]); } else if (m == 45) { sprintf(timeStr, "quarter to\n%s\n", nums[(h % 12) + 1]); sprintf(timeStrShadow, "#404040 quarter to#\n#404040 %s#\n", nums[(h % 12) + 1]); } else if (m <= 30) { sprintf(timeStrShadow, "#404040 %s# #404040 minutes#\n#404040 past# #404040 %s#\n", nums[m], nums[h]); sprintf(timeStr, "%s minutes\npast %s\n", nums[m], nums[h]); } else if (m > 30){ sprintf(timeStrShadow, "#404040 %s# #404040 minutes#\n#404040 to# #404040 %s#\n", nums[60 - m], nums[(h % 12) + 1]); sprintf(timeStr, "%s minutes\nto %s\n", nums[60 - m], nums[(h % 12) + 1]); } } bool Clock::OnButtonPushed() { running = false; return false; }