From 57bcad1a0e27bd7495aaffc3bc63550b71980532 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Sat, 13 Jun 2026 23:42:21 -0400 Subject: [PATCH] Half-baked timeline peak support --- README.md | 2 +- package.json | 78 +++++++++++++++++++++++++ resources/0_m.png | Bin 0 -> 230 bytes resources/1_m.png | Bin 0 -> 179 bytes resources/2_m.png | Bin 0 -> 250 bytes resources/3_m.png | Bin 0 -> 229 bytes resources/4_m.png | Bin 0 -> 238 bytes resources/5_m.png | Bin 0 -> 241 bytes resources/6_m.png | Bin 0 -> 232 bytes resources/7_m.png | Bin 0 -> 210 bytes resources/8_m.png | Bin 0 -> 229 bytes resources/9_m.png | Bin 0 -> 246 bytes resources/super8_m.png | Bin 0 -> 241 bytes src/c/main.c | 130 ++++++++++++++++++++++++++++++++++------- src/c/settings.h | 1 + src/pkjs/config.js | 8 +++ 16 files changed, 196 insertions(+), 23 deletions(-) create mode 100644 resources/0_m.png create mode 100644 resources/1_m.png create mode 100644 resources/2_m.png create mode 100644 resources/3_m.png create mode 100644 resources/4_m.png create mode 100644 resources/5_m.png create mode 100644 resources/6_m.png create mode 100644 resources/7_m.png create mode 100644 resources/8_m.png create mode 100644 resources/9_m.png create mode 100644 resources/super8_m.png diff --git a/README.md b/README.md index 733fe7a..17a0e22 100644 --- a/README.md +++ b/README.md @@ -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. 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 time/date digits: BT connection lost (this feature is off by default) diff --git a/package.json b/package.json index 7c29a38..2afd151 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "messageKeys": [ "PKJS_READY", "PKJS_TEMP_BAR_COUNT", + "CLAY_ENABLE_PEEK", "CLAY_BT_INDICATOR", "CLAY_SHOW_TIME_MG", "CLAY_USE_CUSTOM_LED", @@ -156,6 +157,83 @@ "name": "SUPER8", "spaceOptimization": "storage", "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" } ] } diff --git a/resources/0_m.png b/resources/0_m.png new file mode 100644 index 0000000000000000000000000000000000000000..bc112df46e464eb7b61369996f18813d6f510876 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^u0U+Y2qYMGI%K{EQcNX5e!&b3Ws}cZ0D1F0T^vIy z7<(@af47 zd9tOLZEv{Vsbk7j-{&}o|GJ>^a;24t>*TLyxeM0*`^OjhSa0p`t?$-|OzF84)Z`WF dsa^Y5FyG~rmQchBfma|8c)I$ztaD0e0sx~wSVaH; literal 0 HcmV?d00001 diff --git a/resources/1_m.png b/resources/1_m.png new file mode 100644 index 0000000000000000000000000000000000000000..bdb8e5c81bd72ac87f32d4107299c54c6c8d9241 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^u0U+Y2qYMGI%K{EQcNX5e!&b3Ws}cZ0C_o{E{-7; zjJ@X+`C1JanjgLmE8cx=^6rzn*@QZ0yPj)QIjEDE!?8);ku@~_(vp=slbc_9X)JEi z%Ias53KY84^2ys}n-^PTztXKh$FmDP{|c{7cGYoza&&dS(x$8b!xl>NzPKKG)B0kg c*q8I-n@l4smp0CL0JN9E)78&qol`;+0PYP!hX4Qo literal 0 HcmV?d00001 diff --git a/resources/2_m.png b/resources/2_m.png new file mode 100644 index 0000000000000000000000000000000000000000..dc7c3dd7429d1f5acf82114193e5d096d1e5ac0a GIT binary patch literal 250 zcmeAS@N?(olHy`uVBq!ia0vp^u0U+Y2qYMGI%K{EQcNX5e!&b3Ws}cZ0C}4|T^vIy z7<(@?vN1Ud9Q*iQrSS{POVg&}^`c9vC*H|sDQQq(WGR`z{er{yUD@{Bv^7(Y^8RT2 zIG^#^xqeN?Cu@$K-oSCfh1ozbGC{5AfKzDF(p7;gW_ehLil1Dmap`|bpBtyWZ>9Ic zttqj4wyhCwwbr<@-ubiU&G6RlDA5m`_cv_X$$V#CX!YGlhD93^=NwY`Kk1eAD!GYy ytF-5>V!pSEw`!uQ#6-o2K>q^E)nBbPYyD#SY90Ujl$$Lt$WxxKelF{r5}E+{s$+`) literal 0 HcmV?d00001 diff --git a/resources/3_m.png b/resources/3_m.png new file mode 100644 index 0000000000000000000000000000000000000000..9055817778dccc4aba80e0c4419115b6223908d0 GIT binary patch literal 229 zcmeAS@N?(olHy`uVBq!ia0vp^u0U+Y2qYMGI%K{EQcNX5e!&b3Ws}cZ0D1E~T^vIy z7^hxjWMfJcIQsFw7t>3Ymr{=3QZntO%Z&H2v`-nm=* zqQQ3sd)oxZ;4e*{7dbX7wY#oLo@#YrS(QrJN@<~aMk@Q}AA76*knQq}OHw+{>CL$> z@@^fTQn&WZ@hrQ&uJH>rXZ`tTt{Ska{(Hl#*sP!5cC9<&svC3lj54dgsl|$`fxf$M deGgvonD^fQ@I%?1_Papt_jL7hS?83{1OVXdUqJu> literal 0 HcmV?d00001 diff --git a/resources/4_m.png b/resources/4_m.png new file mode 100644 index 0000000000000000000000000000000000000000..33d97be5bb1c33f0642dcadec1a251ba816e4139 GIT binary patch literal 238 zcmeAS@N?(olHy`uVBq!ia0vp^u0U+Y2qYMGI%K{EQcNX5e!&b3Ws}cZ0C~$jT^vIy z7<d4;VYcM=olbfFWWIZVNqvE7@ zgD}Gk$A;`HGF#kpnI@;OwyUVGm{iklY`SQf+LkMmRxZEhGjEd5ET?EO{l8y6DQ@H4 zUX}m= literal 0 HcmV?d00001 diff --git a/resources/5_m.png b/resources/5_m.png new file mode 100644 index 0000000000000000000000000000000000000000..7bd8911cd9edf0381ed820c2dba17acfc44a8155 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^u0U+Y2qYMGI%K{EQcNX5e!&b3Ws}cZ0C}rCT^vIy z7<(=@vK>kgIr{OxO5+!nm!?f`PA=Q)Q8k5)fZ@t*MCA;?jI~^0CH{@HQ1Z^LlHUas?)78&qol`;+0JQjG`2YX_ literal 0 HcmV?d00001 diff --git a/resources/6_m.png b/resources/6_m.png new file mode 100644 index 0000000000000000000000000000000000000000..50a6c844b5faa35cfcdd79c1b23d8a53ef95679d GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^u0U+Y2qYMGI%K{EQcNX5e!&b3Ws}cZ0C@{NT^vIy z7<(@E{-7; zjJ@X-`3@O~9Q#<`>>${8z%wc_zbmt124al*cu6{1- HoD!M<4ck!l literal 0 HcmV?d00001 diff --git a/resources/8_m.png b/resources/8_m.png new file mode 100644 index 0000000000000000000000000000000000000000..6dac0392cbfb542b2d4379425175355bba94fb30 GIT binary patch literal 229 zcmeAS@N?(olHy`uVBq!ia0vp^u0U+Y2qYMGI%K{EQcNX5e!&b3Ws}cZ0D1E~T^vIy z7<(=yldH!qa?vGLhk^UnuQ z*&Fj8&@*8wKCxi+tc1YOgvh5G{CIPM(tlmiDD<#Zu}Kw@n$KXhUH+7F{M{t){2zuT zrl;(_n8a1`KaP7E?YGj~c>cauE!#3S|69i5_4U22$=dSFr`dZqM+F#XdGoFcHC_9y cl7E^K!#3mm4N`S(ks$Yby85}Sb4q9e0BhV?qyPW_ literal 0 HcmV?d00001 diff --git a/resources/9_m.png b/resources/9_m.png new file mode 100644 index 0000000000000000000000000000000000000000..c4b160d48d5bec0778d7cb3400f2d034efe1b2a1 GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^u0U+Y2qYMGI%K{EQcNX5e!&b3Ws}cZ0D0>@T^vIy z7+WtkvK>bLxIe%Eb|utDW;Mj zzu^B2Pq!O*0eLe#T^vIy7<(@+CGEsh`$r&v}%xKaDB)?_U1$e@|n4mfFOo ma+@Vbn`Nvv%l-XNFu#6l$l2LObw_}1WbkzLb6Mw<&;$T=2U+(3 literal 0 HcmV?d00001 diff --git a/src/c/main.c b/src/c/main.c index 6598143..834aa24 100644 --- a/src/c/main.c +++ b/src/c/main.c @@ -4,6 +4,7 @@ // window statics static Window *s_main_window; +static Layer *s_window_layer; static BitmapLayer *s_time_mg_layers[4]; static BitmapLayer *s_time_fg_layers[4]; static BitmapLayer *s_c_layer; @@ -23,13 +24,13 @@ static const uint16_t s_fuel_guage_x_pos = PBL_DISPLAY_WIDTH - 23; // settings statics static ClaySettings settings; 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 = GColorWhite; +static GColor s_applied_e_f = GColorWhite; static GColor s_applied_c_h = GColorWhite; static GColor s_applied_time_date = GColorWhite; // bitmap statics static GBitmap *s_font_bitmaps[10]; +static GBitmap *s_font_bitmaps_mini[10]; static GBitmap *s_time_mg_super8; static GBitmap *s_c_icon; static GBitmap *s_h_icon; @@ -37,6 +38,7 @@ static GBitmap *s_e_icon; static GBitmap *s_f_icon; // tracking statics +static bool s_peek_active = false; static bool s_first_settings_application = true; static bool s_showing_date = false; static AppTimer *s_date_timer = NULL; @@ -56,13 +58,14 @@ static void toggle_fuel_warning_state(bool enable_warning, bool force) { return; } 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 // for those using white fg, do not pointlessly replace white at startup if that is the destination anyway return; } - replace_gbitmap_color(s_applied_e, dst, s_e_icon, s_e_layer); - s_applied_e = dst; + replace_gbitmap_color(s_applied_e_f, dst, s_e_icon, s_e_layer); + 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; } @@ -196,8 +199,15 @@ static void update_minute_1() { s_time_digits[2] = minute / 10; s_time_digits[3] = minute % 10; - for (int i = 0; i < 4; ++i) { - bitmap_layer_set_bitmap(s_time_fg_layers[i], s_font_bitmaps[s_time_digits[i]]); + if (!s_peek_active) { + for (int i = 0; i < 4; ++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]]); } } @@ -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); } +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() { // LED&BG if (settings.UseCustomLED) { @@ -251,7 +293,7 @@ static void apply_settings() { //// create and insert any mg layer that does not already exist Layer *sibling_layer = NULL; if (s_first_settings_application) { - sibling_layer = window_get_root_layer(s_main_window); + sibling_layer = s_window_layer; } else { sibling_layer = bitmap_layer_get_layer(s_time_fg_layers[0]); } @@ -308,10 +350,6 @@ static void apply_settings() { // fuel (e icon) 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 update_temperature(); @@ -324,13 +362,50 @@ static void apply_settings() { 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) { s_first_settings_application = false; } } 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)); layer_set_update_proc(s_temp_mg_layer, temp_mg_update_proc); @@ -353,19 +428,25 @@ static void main_window_load() { apply_settings(); 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(window_layer, bitmap_layer_get_layer(s_h_layer)); - layer_add_child(window_layer, bitmap_layer_get_layer(s_e_layer)); - layer_add_child(window_layer, bitmap_layer_get_layer(s_f_layer)); - layer_add_child(window_layer, s_temp_mg_layer); - layer_add_child(window_layer, s_temp_layer); - layer_add_child(window_layer, s_fuel_mg_layer); - layer_add_child(window_layer, s_fuel_layer); + layer_add_child(s_window_layer, bitmap_layer_get_layer(s_c_layer)); + layer_add_child(s_window_layer, bitmap_layer_get_layer(s_h_layer)); + layer_add_child(s_window_layer, bitmap_layer_get_layer(s_e_layer)); + layer_add_child(s_window_layer, bitmap_layer_get_layer(s_f_layer)); + layer_add_child(s_window_layer, s_temp_mg_layer); + layer_add_child(s_window_layer, s_temp_layer); + layer_add_child(s_window_layer, s_fuel_mg_layer); + layer_add_child(s_window_layer, s_fuel_layer); } 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(); accel_tap_service_unsubscribe(); layer_destroy(s_fuel_mg_layer); @@ -405,6 +486,7 @@ static void inbox_received_handler(DictionaryIterator *iter, void *context) { } // 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 *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); @@ -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_fg_tuple = dict_find(iter, MESSAGE_KEY_CLAY_FG_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) { settings.TrackBTStatus = track_bt_tuple->value->uint8; } @@ -466,6 +551,7 @@ static void init() { if (persist_exists(STORAGE_KEY_SETTINGS)) { persist_read_data(STORAGE_KEY_SETTINGS, &settings, sizeof(settings)); } else { + settings.EnablePeek = true; settings.TrackBTStatus = false; settings.ShowTimeMG = false; settings.UseCustomLED = false; diff --git a/src/c/settings.h b/src/c/settings.h index 40caa2d..778d33c 100644 --- a/src/c/settings.h +++ b/src/c/settings.h @@ -4,6 +4,7 @@ #define STORAGE_KEY_SETTINGS 69 typedef struct ClaySettings { + bool EnablePeek; bool TrackBTStatus; bool ShowTimeMG; bool UseCustomLED; diff --git a/src/pkjs/config.js b/src/pkjs/config.js index f3a301e..fec6050 100644 --- a/src/pkjs/config.js +++ b/src/pkjs/config.js @@ -11,6 +11,14 @@ module.exports = [ "size": 3, "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", "type": "toggle",