Half-baked timeline peak support

This commit is contained in:
2026-06-13 23:42:21 -04:00
parent df3ec1cc72
commit 57bcad1a0e
16 changed files with 196 additions and 23 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ Want something more personal? Everything is individually configurable, including
Some optional features include the display of unlit segments behind the time, shake to see the date (configurable timeout), and time color shift on BT disconnect. Some optional features include the display of unlit segments behind the time, shake to see the date (configurable timeout), and time color shift on BT disconnect.
Warning light legend: Warning light legend:
- Lit "E" icon: low fuel (battery) - Lit "E" and "F" icons: low fuel (battery)
- Lit "C" and "H" icons: temperature fetch failure - Lit "C" and "H" icons: temperature fetch failure
- Lit time/date digits: BT connection lost (this feature is off by default) - Lit time/date digits: BT connection lost (this feature is off by default)
+78
View File
@@ -28,6 +28,7 @@
"messageKeys": [ "messageKeys": [
"PKJS_READY", "PKJS_READY",
"PKJS_TEMP_BAR_COUNT", "PKJS_TEMP_BAR_COUNT",
"CLAY_ENABLE_PEEK",
"CLAY_BT_INDICATOR", "CLAY_BT_INDICATOR",
"CLAY_SHOW_TIME_MG", "CLAY_SHOW_TIME_MG",
"CLAY_USE_CUSTOM_LED", "CLAY_USE_CUSTOM_LED",
@@ -156,6 +157,83 @@
"name": "SUPER8", "name": "SUPER8",
"spaceOptimization": "storage", "spaceOptimization": "storage",
"type": "bitmap" "type": "bitmap"
},
{
"file": "0_m.png",
"memoryFormat": "Smallest",
"name": "0_M",
"spaceOptimization": "storage",
"type": "bitmap"
},
{
"file": "1_m.png",
"memoryFormat": "Smallest",
"name": "1_M",
"spaceOptimization": "storage",
"type": "bitmap"
},
{
"file": "2_m.png",
"memoryFormat": "Smallest",
"name": "2_M",
"spaceOptimization": "storage",
"type": "bitmap"
},
{
"file": "3_m.png",
"memoryFormat": "Smallest",
"name": "3_M",
"spaceOptimization": "storage",
"type": "bitmap"
},
{
"file": "4_m.png",
"memoryFormat": "Smallest",
"name": "4_M",
"spaceOptimization": "storage",
"type": "bitmap"
},
{
"file": "5_m.png",
"memoryFormat": "Smallest",
"name": "5_M",
"spaceOptimization": "storage",
"type": "bitmap"
},
{
"file": "6_m.png",
"memoryFormat": "Smallest",
"name": "6_M",
"spaceOptimization": "storage",
"type": "bitmap"
},
{
"file": "7_m.png",
"memoryFormat": "Smallest",
"name": "7_M",
"spaceOptimization": "storage",
"type": "bitmap"
},
{
"file": "8_m.png",
"memoryFormat": "Smallest",
"name": "8_M",
"spaceOptimization": "storage",
"type": "bitmap"
},
{
"file": "9_m.png",
"memoryFormat": "Smallest",
"name": "9_M",
"spaceOptimization": "storage",
"type": "bitmap"
},
{
"file": "super8_m.png",
"memoryFormat": "Smallest",
"name": "SUPER8_M",
"spaceOptimization": "storage",
"type": "bitmap"
} }
] ]
} }
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

+106 -20
View File
@@ -4,6 +4,7 @@
// window statics // window statics
static Window *s_main_window; static Window *s_main_window;
static Layer *s_window_layer;
static BitmapLayer *s_time_mg_layers[4]; static BitmapLayer *s_time_mg_layers[4];
static BitmapLayer *s_time_fg_layers[4]; static BitmapLayer *s_time_fg_layers[4];
static BitmapLayer *s_c_layer; static BitmapLayer *s_c_layer;
@@ -23,13 +24,13 @@ static const uint16_t s_fuel_guage_x_pos = PBL_DISPLAY_WIDTH - 23;
// settings statics // settings statics
static ClaySettings settings; static ClaySettings settings;
static GColor s_applied_time_mg = GColorDarkGray; static GColor s_applied_time_mg = GColorDarkGray;
static GColor s_applied_f = GColorWhite; // only foreground element to not change color for warnings, so it is used as the base fg color static GColor s_applied_e_f = GColorWhite;
static GColor s_applied_e = GColorWhite;
static GColor s_applied_c_h = GColorWhite; static GColor s_applied_c_h = GColorWhite;
static GColor s_applied_time_date = GColorWhite; static GColor s_applied_time_date = GColorWhite;
// bitmap statics // bitmap statics
static GBitmap *s_font_bitmaps[10]; static GBitmap *s_font_bitmaps[10];
static GBitmap *s_font_bitmaps_mini[10];
static GBitmap *s_time_mg_super8; static GBitmap *s_time_mg_super8;
static GBitmap *s_c_icon; static GBitmap *s_c_icon;
static GBitmap *s_h_icon; static GBitmap *s_h_icon;
@@ -37,6 +38,7 @@ static GBitmap *s_e_icon;
static GBitmap *s_f_icon; static GBitmap *s_f_icon;
// tracking statics // tracking statics
static bool s_peek_active = false;
static bool s_first_settings_application = true; static bool s_first_settings_application = true;
static bool s_showing_date = false; static bool s_showing_date = false;
static AppTimer *s_date_timer = NULL; static AppTimer *s_date_timer = NULL;
@@ -56,13 +58,14 @@ static void toggle_fuel_warning_state(bool enable_warning, bool force) {
return; return;
} }
GColor dst = enable_warning ? settings.ColorWarning : settings.ColorFG; GColor dst = enable_warning ? settings.ColorWarning : settings.ColorFG;
if (s_first_settings_application && !enable_warning && dst.argb == s_applied_e.argb) { if (s_first_settings_application && !enable_warning && dst.argb == s_applied_e_f.argb) {
// at startup, s_applied_e.argb is GColorWhite // at startup, s_applied_e.argb is GColorWhite
// for those using white fg, do not pointlessly replace white at startup if that is the destination anyway // for those using white fg, do not pointlessly replace white at startup if that is the destination anyway
return; return;
} }
replace_gbitmap_color(s_applied_e, dst, s_e_icon, s_e_layer); replace_gbitmap_color(s_applied_e_f, dst, s_e_icon, s_e_layer);
s_applied_e = dst; replace_gbitmap_color(s_applied_e_f, dst, s_f_icon, s_f_layer);
s_applied_e_f = dst;
s_fuel_is_warning = enable_warning; s_fuel_is_warning = enable_warning;
} }
@@ -196,9 +199,16 @@ static void update_minute_1() {
s_time_digits[2] = minute / 10; s_time_digits[2] = minute / 10;
s_time_digits[3] = minute % 10; s_time_digits[3] = minute % 10;
if (!s_peek_active) {
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
bitmap_layer_set_bitmap(s_time_fg_layers[i], s_font_bitmaps[s_time_digits[i]]); bitmap_layer_set_bitmap(s_time_fg_layers[i], s_font_bitmaps[s_time_digits[i]]);
} }
} else {
bitmap_layer_set_bitmap(s_time_fg_layers[0], s_font_bitmaps[s_time_digits[0]]);
bitmap_layer_set_bitmap(s_time_fg_layers[1], s_font_bitmaps[s_time_digits[1]]);
bitmap_layer_set_bitmap(s_time_fg_layers[2], s_font_bitmaps_mini[s_time_digits[2]]);
bitmap_layer_set_bitmap(s_time_fg_layers[3], s_font_bitmaps_mini[s_time_digits[3]]);
}
} }
static void minute_handler(struct tm *tick_time, TimeUnits units_changed) { static void minute_handler(struct tm *tick_time, TimeUnits units_changed) {
@@ -232,6 +242,38 @@ static void accel_tap_handler(AccelAxisType axis, int32_t direction) {
s_date_timer = app_timer_register(settings.DateTimeoutSecs * 1000, date_timeout, NULL); s_date_timer = app_timer_register(settings.DateTimeoutSecs * 1000, date_timeout, NULL);
} }
static void peek_will_change(GRect final_unobstructed_screen_area, void *context) {
// if obstructed, set s_peek_active and run update_minute_1 to use small minutes
GRect full_bounds = layer_get_bounds(s_window_layer);
if (!grect_equal(&full_bounds, &final_unobstructed_screen_area)) {
s_peek_active = true;
if (!s_showing_date) {
update_minute_1();
}
} else {
s_peek_active = false;
}
}
static void peek_change(AnimationProgress progress, void *context) {
// animate the moving of the minute layers depending on s_peek_active
if (!s_peek_active) {
layer_set_frame(bitmap_layer_get_layer(s_time_fg_layers[2]), s_time_grects[2]);
layer_set_frame(bitmap_layer_get_layer(s_time_fg_layers[3]), s_time_grects[3]);
} else {
uint8_t y = s_time_grects[2].origin.y - 1;
layer_set_frame(bitmap_layer_get_layer(s_time_fg_layers[2]), GRect(s_time_grects[2].origin.x, y, 69, 54));
layer_set_frame(bitmap_layer_get_layer(s_time_fg_layers[3]), GRect(s_time_grects[3].origin.x, y, 69, 54));
}
}
static void peek_did_change(void *context) {
// if not obstructed, run update_minute_1 to use large minutes
if (!s_peek_active && !s_showing_date) {
update_minute_1();
}
}
static void apply_settings() { static void apply_settings() {
// LED&BG // LED&BG
if (settings.UseCustomLED) { if (settings.UseCustomLED) {
@@ -251,7 +293,7 @@ static void apply_settings() {
//// create and insert any mg layer that does not already exist //// create and insert any mg layer that does not already exist
Layer *sibling_layer = NULL; Layer *sibling_layer = NULL;
if (s_first_settings_application) { if (s_first_settings_application) {
sibling_layer = window_get_root_layer(s_main_window); sibling_layer = s_window_layer;
} else { } else {
sibling_layer = bitmap_layer_get_layer(s_time_fg_layers[0]); sibling_layer = bitmap_layer_get_layer(s_time_fg_layers[0]);
} }
@@ -308,10 +350,6 @@ static void apply_settings() {
// fuel (e icon) // fuel (e icon)
toggle_fuel_warning_state(s_fuel_is_warning, true); toggle_fuel_warning_state(s_fuel_is_warning, true);
// f icon (always fg color, never a warning indicator)
replace_gbitmap_color(s_applied_f, settings.ColorFG, s_f_icon, s_f_layer);
s_applied_f = settings.ColorFG;
// changes to temperature range // changes to temperature range
update_temperature(); update_temperature();
@@ -324,13 +362,50 @@ static void apply_settings() {
accel_tap_service_subscribe(accel_tap_handler); accel_tap_service_subscribe(accel_tap_handler);
} }
// re-sub timeline peek and create/destroy resources as needed
// TODO add mg support, fix broken-ness when toggling peak support off while running
unobstructed_area_service_unsubscribe();
if (settings.EnablePeek) {
if (!s_font_bitmaps_mini[0]) {
s_font_bitmaps_mini[0] = gbitmap_create_with_resource(RESOURCE_ID_0_M);
s_font_bitmaps_mini[1] = gbitmap_create_with_resource(RESOURCE_ID_1_M);
s_font_bitmaps_mini[2] = gbitmap_create_with_resource(RESOURCE_ID_2_M);
s_font_bitmaps_mini[3] = gbitmap_create_with_resource(RESOURCE_ID_3_M);
s_font_bitmaps_mini[4] = gbitmap_create_with_resource(RESOURCE_ID_4_M);
s_font_bitmaps_mini[5] = gbitmap_create_with_resource(RESOURCE_ID_5_M);
s_font_bitmaps_mini[6] = gbitmap_create_with_resource(RESOURCE_ID_6_M);
s_font_bitmaps_mini[7] = gbitmap_create_with_resource(RESOURCE_ID_7_M);
s_font_bitmaps_mini[8] = gbitmap_create_with_resource(RESOURCE_ID_8_M);
s_font_bitmaps_mini[9] = gbitmap_create_with_resource(RESOURCE_ID_9_M);
}
peek_will_change(layer_get_unobstructed_bounds(s_window_layer), NULL);
peek_change(0, NULL);
peek_did_change(NULL);
UnobstructedAreaHandlers peek_handlers = {
.will_change = peek_will_change,
.change = peek_change,
.did_change = peek_did_change,
};
unobstructed_area_service_subscribe(peek_handlers, NULL);
} else {
peek_will_change(layer_get_bounds(s_window_layer), NULL);
peek_change(0, NULL);
peek_did_change(NULL);
if (s_font_bitmaps_mini[0]) {
for (int i = 0; i < 10; ++i) {
gbitmap_destroy(s_font_bitmaps_mini[i]);
s_font_bitmaps_mini[i] = NULL;
}
}
}
if (s_first_settings_application) { if (s_first_settings_application) {
s_first_settings_application = false; s_first_settings_application = false;
} }
} }
static void main_window_load() { static void main_window_load() {
Layer *window_layer = window_get_root_layer(s_main_window); s_window_layer = window_get_root_layer(s_main_window);
s_temp_mg_layer = layer_create(GRect(s_temp_guage_x_pos - 1, 21, s_bar_width + 2, PBL_DISPLAY_HEIGHT - 43)); s_temp_mg_layer = layer_create(GRect(s_temp_guage_x_pos - 1, 21, s_bar_width + 2, PBL_DISPLAY_HEIGHT - 43));
layer_set_update_proc(s_temp_mg_layer, temp_mg_update_proc); layer_set_update_proc(s_temp_mg_layer, temp_mg_update_proc);
@@ -353,19 +428,25 @@ static void main_window_load() {
apply_settings(); apply_settings();
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
layer_add_child(window_layer, bitmap_layer_get_layer(s_time_fg_layers[i])); layer_add_child(s_window_layer, bitmap_layer_get_layer(s_time_fg_layers[i]));
} }
layer_add_child(window_layer, bitmap_layer_get_layer(s_c_layer)); layer_add_child(s_window_layer, bitmap_layer_get_layer(s_c_layer));
layer_add_child(window_layer, bitmap_layer_get_layer(s_h_layer)); layer_add_child(s_window_layer, bitmap_layer_get_layer(s_h_layer));
layer_add_child(window_layer, bitmap_layer_get_layer(s_e_layer)); layer_add_child(s_window_layer, bitmap_layer_get_layer(s_e_layer));
layer_add_child(window_layer, bitmap_layer_get_layer(s_f_layer)); layer_add_child(s_window_layer, bitmap_layer_get_layer(s_f_layer));
layer_add_child(window_layer, s_temp_mg_layer); layer_add_child(s_window_layer, s_temp_mg_layer);
layer_add_child(window_layer, s_temp_layer); layer_add_child(s_window_layer, s_temp_layer);
layer_add_child(window_layer, s_fuel_mg_layer); layer_add_child(s_window_layer, s_fuel_mg_layer);
layer_add_child(window_layer, s_fuel_layer); layer_add_child(s_window_layer, s_fuel_layer);
} }
static void main_window_unload() { static void main_window_unload() {
if (settings.EnablePeek) {
unobstructed_area_service_unsubscribe();
for (int i = 0; i < 10; ++i) {
gbitmap_destroy(s_font_bitmaps_mini[i]);
}
}
connection_service_unsubscribe(); connection_service_unsubscribe();
accel_tap_service_unsubscribe(); accel_tap_service_unsubscribe();
layer_destroy(s_fuel_mg_layer); layer_destroy(s_fuel_mg_layer);
@@ -405,6 +486,7 @@ static void inbox_received_handler(DictionaryIterator *iter, void *context) {
} }
// CLAY SETTINGS // CLAY SETTINGS
Tuple *enable_peek_tuple = dict_find(iter, MESSAGE_KEY_CLAY_ENABLE_PEEK);
Tuple *track_bt_tuple = dict_find(iter, MESSAGE_KEY_CLAY_BT_INDICATOR); Tuple *track_bt_tuple = dict_find(iter, MESSAGE_KEY_CLAY_BT_INDICATOR);
Tuple *show_time_mg_tuple = dict_find(iter, MESSAGE_KEY_CLAY_SHOW_TIME_MG); Tuple *show_time_mg_tuple = dict_find(iter, MESSAGE_KEY_CLAY_SHOW_TIME_MG);
Tuple *use_custom_led_tuple = dict_find(iter, MESSAGE_KEY_CLAY_USE_CUSTOM_LED); Tuple *use_custom_led_tuple = dict_find(iter, MESSAGE_KEY_CLAY_USE_CUSTOM_LED);
@@ -418,6 +500,9 @@ static void inbox_received_handler(DictionaryIterator *iter, void *context) {
Tuple *color_mg_time_tuple = dict_find(iter, MESSAGE_KEY_CLAY_TIME_MG_COLOR); Tuple *color_mg_time_tuple = dict_find(iter, MESSAGE_KEY_CLAY_TIME_MG_COLOR);
Tuple *color_fg_tuple = dict_find(iter, MESSAGE_KEY_CLAY_FG_COLOR); Tuple *color_fg_tuple = dict_find(iter, MESSAGE_KEY_CLAY_FG_COLOR);
Tuple *color_warning_tuple = dict_find(iter, MESSAGE_KEY_CLAY_WARNING_COLOR); Tuple *color_warning_tuple = dict_find(iter, MESSAGE_KEY_CLAY_WARNING_COLOR);
if (enable_peek_tuple) {
settings.EnablePeek = enable_peek_tuple->value->uint8;
}
if (track_bt_tuple) { if (track_bt_tuple) {
settings.TrackBTStatus = track_bt_tuple->value->uint8; settings.TrackBTStatus = track_bt_tuple->value->uint8;
} }
@@ -466,6 +551,7 @@ static void init() {
if (persist_exists(STORAGE_KEY_SETTINGS)) { if (persist_exists(STORAGE_KEY_SETTINGS)) {
persist_read_data(STORAGE_KEY_SETTINGS, &settings, sizeof(settings)); persist_read_data(STORAGE_KEY_SETTINGS, &settings, sizeof(settings));
} else { } else {
settings.EnablePeek = true;
settings.TrackBTStatus = false; settings.TrackBTStatus = false;
settings.ShowTimeMG = false; settings.ShowTimeMG = false;
settings.UseCustomLED = false; settings.UseCustomLED = false;
+1
View File
@@ -4,6 +4,7 @@
#define STORAGE_KEY_SETTINGS 69 #define STORAGE_KEY_SETTINGS 69
typedef struct ClaySettings { typedef struct ClaySettings {
bool EnablePeek;
bool TrackBTStatus; bool TrackBTStatus;
bool ShowTimeMG; bool ShowTimeMG;
bool UseCustomLED; bool UseCustomLED;
+8
View File
@@ -11,6 +11,14 @@ module.exports = [
"size": 3, "size": 3,
"defaultValue": "Options" "defaultValue": "Options"
}, },
{
"id": "peek_toggle",
"type": "toggle",
"messageKey": "CLAY_ENABLE_PEEK",
"defaultValue": true,
"label": "Timeline peek/quickview support",
"description": "Disable to reduce resource usage if NOT using timeline peek/quickview."
},
{ {
"id": "time_mg_toggle", "id": "time_mg_toggle",
"type": "toggle", "type": "toggle",