Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
81e3dd8
include prime watchface
tituscmd May 30, 2025
f4aa76c
correctly include new font
tituscmd May 30, 2025
93c848b
watchface: add implementation and infrastructure fixes
tituscmd May 30, 2025
2d207db
add font file
tituscmd May 30, 2025
6550db1
renamed to PrimeTime and moved font to external storage
tituscmd May 30, 2025
6608cf9
include prime watchface
tituscmd May 30, 2025
2de0f4f
Dont know if Im doing this correctly
tituscmd May 30, 2025
f532e0b
watchface: add implementation and infrastructure fixes
tituscmd May 30, 2025
57e896e
Rename to PrimeTime and move font to the external storage
tituscmd May 30, 2025
1028d49
remove unneccesary line
tituscmd May 30, 2025
f468ef4
remove unneccesary lines
tituscmd May 30, 2025
21671d4
Revert "include prime watchface"
tituscmd May 30, 2025
35ad0d5
hopefully fixed my mistake
tituscmd May 30, 2025
abcaf22
hopefully fixed my mistake part 2
tituscmd May 30, 2025
09ce06b
hopefully fixed my mistake part 3
tituscmd May 30, 2025
f85f7c3
Merge branch 'main' into watchface_prime
tituscmd May 30, 2025
3c7c7f8
format to camelCase
tituscmd May 30, 2025
8ceadf8
Merge branch 'watchface_prime' of github.com:tituscmd/InfiniTime into…
tituscmd May 30, 2025
6f95830
change one last variable to camelCase
tituscmd Jun 6, 2025
4438e10
remove color computation and two unused variables
tituscmd Jun 6, 2025
e89d1e6
remove unused variable
tituscmd Jun 6, 2025
d2ffb87
remove initializers
tituscmd Jun 6, 2025
408b307
Merge branch 'main' into watchface_prime
tituscmd Jun 26, 2025
fbdb54f
Update CMakeLists.txt
tituscmd Jun 26, 2025
bcded26
Merge branch 'main' into watchface_prime
tituscmd Aug 10, 2025
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
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/WatchFaceTerminal.cpp
displayapp/screens/WatchFacePineTimeStyle.cpp
displayapp/screens/WatchFaceCasioStyleG7710.cpp
displayapp/screens/WatchFacePrimeTime.cpp
displayapp/screens/WatchFacePrideFlag.cpp

##
Expand Down
1 change: 1 addition & 0 deletions src/displayapp/UserApps.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "displayapp/screens/Tile.h"
#include "displayapp/screens/ApplicationList.h"
#include "displayapp/screens/WatchFaceDigital.h"
#include "displayapp/screens/WatchFacePrimeTime.h"
#include "displayapp/screens/WatchFaceAnalog.h"
#include "displayapp/screens/WatchFaceCasioStyleG7710.h"
#include "displayapp/screens/WatchFaceInfineat.h"
Expand Down
3 changes: 2 additions & 1 deletion src/displayapp/apps/Apps.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ namespace Pinetime {
Terminal,
Infineat,
CasioStyleG7710,
PrideFlag,
PrimeTime,
PrideFlag
};

template <Apps>
Expand Down
1 change: 1 addition & 0 deletions src/displayapp/apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ else()
set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Terminal")
set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Infineat")
set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::CasioStyleG7710")
set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::PrimeTime")
set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::PrideFlag")
set(WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}" CACHE STRING "List of watch faces to build into the firmware")
endif()
Expand Down
233 changes: 233 additions & 0 deletions src/displayapp/screens/WatchFacePrimeTime.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
#include "displayapp/screens/WatchFacePrimeTime.h"

#include <lvgl/lvgl.h>
#include <cstdio>

#include "displayapp/screens/NotificationIcon.h"
#include "displayapp/screens/Symbols.h"
#include "displayapp/screens/WeatherSymbols.h"
#include "components/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/heartrate/HeartRateController.h"
#include "components/motion/MotionController.h"
#include "components/ble/SimpleWeatherService.h"
#include "components/settings/Settings.h"
#include "displayapp/InfiniTimeTheme.h"
#include "components/ble/MusicService.h"

using namespace Pinetime::Applications::Screens;

WatchFacePrimeTime::WatchFacePrimeTime(Controllers::DateTime& dateTimeController,
const Controllers::Battery& batteryController,
const Controllers::Ble& bleController,
const Controllers::AlarmController& alarmController,
Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController,
Controllers::SimpleWeatherService& weatherService,
Controllers::MusicService& music,
Controllers::FS& filesystem)
: currentDateTime {{}},
dateTimeController {dateTimeController},
notificationManager {notificationManager},
settingsController {settingsController},
heartRateController {heartRateController},
motionController {motionController},
weatherService {weatherService},
musicService(music),
statusIcons(batteryController, bleController, alarmController) {

lfs_file f = {};
if (filesystem.FileOpen(&f, "/fonts/primetime.bin", LFS_O_RDONLY) >= 0) {
filesystem.FileClose(&f);
fontPrimeTime = lv_font_load("F:/fonts/primetime.bin");
}

statusIcons.Create();

notificationIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false));
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);

weatherIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(weatherIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_obj_set_style_local_text_font(weatherIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &fontawesome_weathericons);
lv_label_set_text_static(weatherIcon, Symbols::ban);
lv_obj_align(weatherIcon, nullptr, LV_ALIGN_IN_TOP_MID, 0, 30);
lv_obj_set_auto_realign(weatherIcon, true);

temperature = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
lv_label_set_text_static(temperature, "--°C");
lv_obj_align(temperature, weatherIcon, LV_ALIGN_CENTER, 0, 25);

labelDate = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(labelDate, lv_scr_act(), LV_ALIGN_CENTER, 0, 50);
lv_obj_set_style_local_text_color(labelDate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);

labelMusic = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_fmt(labelMusic, "%s Not Playing", Symbols::music);
lv_obj_set_style_local_text_color(labelMusic, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt);
lv_label_set_long_mode(labelMusic, LV_LABEL_LONG_SROLL_CIRC);
lv_obj_set_width(labelMusic, LV_HOR_RES - 12);
lv_label_set_align(labelMusic, LV_LABEL_ALIGN_CENTER);
lv_obj_align(labelMusic, lv_scr_act(), LV_ALIGN_CENTER, 0, 78);

labelTime = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(labelTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, fontPrimeTime);
lv_obj_align(labelTime, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);

labelTimeAmPm = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(labelTimeAmPm, "");
lv_obj_align(labelTimeAmPm, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -30, -55);

heartbeatIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat);
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 1);

heartbeatValue = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(heartbeatValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xff4539));
lv_label_set_text_static(heartbeatValue, "");
lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0);

stepValue = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x0a84ff));
lv_label_set_text_static(stepValue, "0");
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);

stepIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x0a84ff));
lv_label_set_text_static(stepIcon, Symbols::shoe);
lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0);

taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
Refresh();
}

WatchFacePrimeTime::~WatchFacePrimeTime() {
lv_task_del(taskRefresh);

if (fontPrimeTime != nullptr) {
lv_font_free(fontPrimeTime);
}

lv_obj_clean(lv_scr_act());
}

void WatchFacePrimeTime::Refresh() {
statusIcons.Update();

notificationState = notificationManager.AreNewNotificationsAvailable();
if (notificationState.IsUpdated()) {
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
}

currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime());

if (currentDateTime.IsUpdated()) {
uint8_t hour = dateTimeController.Hours();
uint8_t minute = dateTimeController.Minutes();

if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
char ampmChar[3] = "AM";
if (hour == 0) {
hour = 12;
} else if (hour == 12) {
ampmChar[0] = 'P';
} else if (hour > 12) {
hour = hour - 12;
ampmChar[0] = 'P';
}
lv_label_set_text(labelTimeAmPm, ampmChar);
lv_label_set_text_fmt(labelTime, "%2d:%02d", hour, minute);
lv_obj_align(labelTime, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);
} else {
lv_label_set_text_fmt(labelTime, "%02d:%02d", hour, minute);
lv_obj_align(labelTime, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
}

currentDate = std::chrono::time_point_cast<std::chrono::days>(currentDateTime.Get());
if (currentDate.IsUpdated()) {
uint16_t year = dateTimeController.Year();
uint8_t day = dateTimeController.Day();
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) {
lv_label_set_text_fmt(labelDate,
"%s, %02d.%02d.%d",
dateTimeController.DayOfWeekShortToStringLow(dateTimeController.DayOfWeek()),
day,
dateTimeController.Month(),
year);
} else {
lv_label_set_text_fmt(labelDate,
"%s %s %d %d",
dateTimeController.DayOfWeekShortToString(),
dateTimeController.MonthShortToString(),
day,
year);
}
lv_obj_realign(labelDate);
}
}

heartbeat = heartRateController.HeartRate();
heartbeatRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
if (heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) {
if (heartbeatRunning.Get()) {
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xff4539));
lv_label_set_text_fmt(heartbeatValue, "%d", heartbeat.Get());
} else {
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B));
lv_label_set_text_static(heartbeatValue, "");
}

lv_obj_realign(heartbeatIcon);
lv_obj_realign(heartbeatValue);
}

stepCount = motionController.NbSteps();
if (stepCount.IsUpdated()) {
lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
lv_obj_realign(stepValue);
lv_obj_realign(stepIcon);
}

currentWeather = weatherService.Current();
if (currentWeather.IsUpdated()) {
auto optCurrentWeather = currentWeather.Get();
if (optCurrentWeather) {
int16_t temp = optCurrentWeather->temperature.Celsius();
char tempUnit = 'C';
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
temp = optCurrentWeather->temperature.Fahrenheit();
tempUnit = 'F';
}
lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
} else {
lv_label_set_text_static(temperature, "--°");
lv_label_set_text(weatherIcon, Symbols::ban);
}
lv_obj_realign(temperature);
lv_obj_realign(weatherIcon);
}
if (track != musicService.getTrack()) {
track = musicService.getTrack();
lv_label_set_text_fmt(labelMusic, "%s %s", Symbols::music, track.data());
lv_obj_realign(labelMusic);
}
}

bool WatchFacePrimeTime::IsAvailable(Pinetime::Controllers::FS& filesystem) {
lfs_file file = {};

if (filesystem.FileOpen(&file, "/fonts/primetime.bin", LFS_O_RDONLY) < 0) {
return false;
}

filesystem.FileClose(&file);
return true;
}
110 changes: 110 additions & 0 deletions src/displayapp/screens/WatchFacePrimeTime.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#pragma once

#include <lvgl/src/lv_core/lv_obj.h>
#include <chrono>
#include <cstdint>
#include "displayapp/screens/Screen.h"
#include "components/datetime/DateTimeController.h"
#include "components/ble/SimpleWeatherService.h"
#include "components/ble/BleController.h"
#include "displayapp/widgets/StatusIcons.h"
#include "utility/DirtyValue.h"
#include "displayapp/apps/Apps.h"

namespace Pinetime {
namespace Controllers {
class Settings;
class Battery;
class Ble;
class AlarmController;
class NotificationManager;
class HeartRateController;
class MotionController;
class MusicService;
}

namespace Applications {
namespace Screens {

class WatchFacePrimeTime : public Screen {
public:
WatchFacePrimeTime(Controllers::DateTime& dateTimeController,
const Controllers::Battery& batteryController,
const Controllers::Ble& bleController,
const Controllers::AlarmController& alarmController,
Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController,
Controllers::SimpleWeatherService& weather,
Controllers::MusicService& music,
Controllers::FS& filesystem);
~WatchFacePrimeTime() override;

void Refresh() override;

static bool IsAvailable(Pinetime::Controllers::FS& filesystem);

private:
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime;
Utility::DirtyValue<uint32_t> stepCount;
Utility::DirtyValue<uint8_t> heartbeat;
Utility::DirtyValue<bool> heartbeatRunning;
Utility::DirtyValue<bool> notificationState;
Utility::DirtyValue<std::optional<Pinetime::Controllers::SimpleWeatherService::CurrentWeather>> currentWeather;

Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;

lv_obj_t* labelTime;
lv_obj_t* labelTimeAmPm;
lv_obj_t* labelDate;
lv_obj_t* heartbeatIcon;
lv_obj_t* heartbeatValue;
lv_obj_t* stepIcon;
lv_obj_t* stepValue;
lv_obj_t* notificationIcon;
lv_obj_t* weatherIcon;
lv_obj_t* temperature;
lv_obj_t* labelMusic;
std::string track;

Controllers::DateTime& dateTimeController;
Controllers::NotificationManager& notificationManager;
Controllers::Settings& settingsController;
Controllers::HeartRateController& heartRateController;
Controllers::MotionController& motionController;
Controllers::SimpleWeatherService& weatherService;
Controllers::MusicService& musicService;

lv_font_t* fontPrimeTime = nullptr;

lv_task_t* taskRefresh;
Widgets::StatusIcons statusIcons;
};
}

template <>
struct WatchFaceTraits<WatchFace::PrimeTime> {
static constexpr WatchFace watchFace = WatchFace::PrimeTime;
static constexpr const char* name = "PrimeTime";

static Screens::Screen* Create(AppControllers& controllers) {
return new Screens::WatchFacePrimeTime(controllers.dateTimeController,
controllers.batteryController,
controllers.bleController,
controllers.alarmController,
controllers.notificationManager,
controllers.settingsController,
controllers.heartRateController,
controllers.motionController,
*controllers.weatherController,
*controllers.musicService,
controllers.filesystem);
};

static bool IsAvailable(Pinetime::Controllers::FS& filesystem) {
return Screens::WatchFacePrimeTime::IsAvailable(filesystem);
}
};
}
}
12 changes: 12 additions & 0 deletions src/resources/fonts.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,17 @@
"size": 115,
"format": "bin",
"target_path": "/fonts/"
},
"primetime" : {
"sources": [
{
"file": "fonts/Karnivore.ttf",
"symbols": "0123456789:"
}
],
"bpp": 1,
"size": 85,
"format": "bin",
"target_path": "/fonts/"
}
}
Binary file added src/resources/fonts/Karnivore.ttf
Binary file not shown.