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 0000000..bc112df Binary files /dev/null and b/resources/0_m.png differ diff --git a/resources/1_m.png b/resources/1_m.png new file mode 100644 index 0000000..bdb8e5c Binary files /dev/null and b/resources/1_m.png differ diff --git a/resources/2_m.png b/resources/2_m.png new file mode 100644 index 0000000..dc7c3dd Binary files /dev/null and b/resources/2_m.png differ diff --git a/resources/3_m.png b/resources/3_m.png new file mode 100644 index 0000000..9055817 Binary files /dev/null and b/resources/3_m.png differ diff --git a/resources/4_m.png b/resources/4_m.png new file mode 100644 index 0000000..33d97be Binary files /dev/null and b/resources/4_m.png differ diff --git a/resources/5_m.png b/resources/5_m.png new file mode 100644 index 0000000..7bd8911 Binary files /dev/null and b/resources/5_m.png differ diff --git a/resources/6_m.png b/resources/6_m.png new file mode 100644 index 0000000..50a6c84 Binary files /dev/null and b/resources/6_m.png differ diff --git a/resources/7_m.png b/resources/7_m.png new file mode 100644 index 0000000..013243c Binary files /dev/null and b/resources/7_m.png differ diff --git a/resources/8_m.png b/resources/8_m.png new file mode 100644 index 0000000..6dac039 Binary files /dev/null and b/resources/8_m.png differ diff --git a/resources/9_m.png b/resources/9_m.png new file mode 100644 index 0000000..c4b160d Binary files /dev/null and b/resources/9_m.png differ diff --git a/resources/super8_m.png b/resources/super8_m.png new file mode 100644 index 0000000..90e3dd8 Binary files /dev/null and b/resources/super8_m.png differ 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",