diff --git a/package.json b/package.json index 5573c89..beea0fa 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,9 @@ "messageKeys": [ "dummy" ], + "capabilities": [ + "health" + ], "resources": { "media": [{ "type": "bitmap", @@ -36,6 +39,30 @@ "file": "character_odd.png", "memoryFormat": "Smallest", "spaceOptimization": "memory" + }, { + "type": "bitmap", + "name": "ICON_BATTERY_EMPTY", + "file": "icon_battery_empty.png", + "memoryFormat": "Smallest", + "spaceOptimization": "memory" + }, { + "type": "bitmap", + "name": "ICON_BATTERY_HALF", + "file": "icon_battery_half.png", + "memoryFormat": "Smallest", + "spaceOptimization": "memory" + }, { + "type": "bitmap", + "name": "ICON_BATTERY_FULL", + "file": "icon_battery_full.png", + "memoryFormat": "Smallest", + "spaceOptimization": "memory" + }, { + "type": "bitmap", + "name": "ICON_PAWS", + "file": "icon_paws.png", + "memoryFormat": "Smallest", + "spaceOptimization": "memory" }, { "type": "font", "name": "FONT_TORO_36", diff --git a/resources/icon_battery_empty.png b/resources/icon_battery_empty.png new file mode 100644 index 0000000..4ee026a Binary files /dev/null and b/resources/icon_battery_empty.png differ diff --git a/resources/icon_battery_full.png b/resources/icon_battery_full.png new file mode 100644 index 0000000..05b3180 Binary files /dev/null and b/resources/icon_battery_full.png differ diff --git a/resources/icon_battery_half.png b/resources/icon_battery_half.png new file mode 100644 index 0000000..841aca0 Binary files /dev/null and b/resources/icon_battery_half.png differ diff --git a/resources/icon_paws.png b/resources/icon_paws.png new file mode 100644 index 0000000..0237bbd Binary files /dev/null and b/resources/icon_paws.png differ diff --git a/src/c/clock_layer.c b/src/c/clock_layer.c index 665065e..83fc97b 100644 --- a/src/c/clock_layer.c +++ b/src/c/clock_layer.c @@ -60,6 +60,10 @@ void clock_layer_init(Layer *layer, int y) { layer_add_child(layer, s_clock_layer); } +GRect clock_layer_get_bounds() { + return layer_get_bounds(s_clock_layer); +} + void clock_layer_tick(void) { layer_mark_dirty(s_clock_layer); } diff --git a/src/c/clock_layer.h b/src/c/clock_layer.h index b76a9f7..dc9d8bf 100644 --- a/src/c/clock_layer.h +++ b/src/c/clock_layer.h @@ -4,7 +4,8 @@ #include void clock_layer_init(Layer *layer, int y); +GRect clock_layer_get_bounds(); void clock_layer_tick(void); void clock_layer_deinit(void); -#endif +#endif // CLOCK_LAYER_H_ diff --git a/src/c/information_layer.c b/src/c/information_layer.c new file mode 100644 index 0000000..ec0b09e --- /dev/null +++ b/src/c/information_layer.c @@ -0,0 +1,115 @@ +#include "information_layer.h" + +#include "resources_service.h" + +#define INFO_LAYER_ICON_WIDTH 18 +#define INFO_LAYER_PADDING 4 +#define INFO_LAYER_HEIGHT 24 + +typedef struct InformationLayerData { + int level; + int steps; +} InformationLayerData; + +static Layer *s_information_layer; +static char s_information_layer_level_text[3]; +static char s_information_layer_count_text[6]; + +static void information_layer_update_proc(Layer *layer, GContext *ctx) { + GRect rect = layer_get_unobstructed_bounds(layer); + GFont font = resources_service_get_custom_font(CustomFontKonekoToro); + InformationLayerData *data = layer_get_data(s_information_layer); + + int content_w = INFO_LAYER_ICON_WIDTH + INFO_LAYER_PADDING; + + snprintf(s_information_layer_level_text, sizeof(s_information_layer_level_text), "%d", + data->level); + GSize text_size = graphics_text_layout_get_content_size( + s_information_layer_level_text, font, rect, GTextOverflowModeFill, GTextAlignmentLeft); + content_w += text_size.w; + +#ifdef PBL_HEALTH + snprintf(s_information_layer_count_text, sizeof(s_information_layer_count_text), "%d", + data->steps); + text_size = graphics_text_layout_get_content_size(s_information_layer_count_text, font, rect, + GTextOverflowModeFill, GTextAlignmentLeft); + content_w += INFO_LAYER_ICON_WIDTH + INFO_LAYER_PADDING + text_size.w; +#endif + + int content_x = (rect.size.w - content_w) / 2; + + /* Draw the battery icon based on level percentage. */ + graphics_context_set_compositing_mode(ctx, GCompOpSet); + + GBitmap *battery_icon; + if (data->level < 20) { + battery_icon = resources_service_get_custom_icon(CustomIconBatteryEmpty); + } else if (data->level < 80) { + battery_icon = resources_service_get_custom_icon(CustomIconBatteryHalf); + } else { + battery_icon = resources_service_get_custom_icon(CustomIconBatteryFull); + } + + GRect battery_rect_inside = GRect(0, 0, INFO_LAYER_ICON_WIDTH, rect.size.h); + GRect battery_rect = gbitmap_get_bounds(battery_icon); + grect_align(&battery_rect, &battery_rect_inside, GAlignCenter, true); + battery_rect.origin.x += content_x; + graphics_draw_bitmap_in_rect(ctx, battery_icon, battery_rect); + + /* Draw the battery level in text. */ + GRect text_rect = + GRect(content_x + INFO_LAYER_ICON_WIDTH + INFO_LAYER_PADDING, -4, text_size.w, text_size.h); + + graphics_context_set_text_color(ctx, GColorBlack); + graphics_draw_text(ctx, s_information_layer_level_text, font, text_rect, GTextOverflowModeFill, + GTextAlignmentLeft, NULL); + +#ifdef PBL_HEALTH + /* Draw the paws icon. */ + GBitmap *paws_icon = resources_service_get_custom_icon(CustomIconPaws); + GRect paws_rect = gbitmap_get_bounds(paws_icon); + paws_rect.origin.x = content_x + text_rect.size.w; + graphics_draw_bitmap_in_rect(ctx, paws_icon, paws_rect); + + /* Draw the step count in text. */ + text_rect = GRect(paws_rect.origin.x + INFO_LAYER_ICON_WIDTH + INFO_LAYER_PADDING, -4, + text_size.w, text_size.h); + graphics_context_set_text_color(ctx, GColorBlack); + graphics_draw_text(ctx, s_information_layer_count_text, font, text_rect, GTextOverflowModeFill, + GTextAlignmentLeft, NULL); +#endif // PBL_HEALTH +} + +static void battery_state_service_callback(BatteryChargeState charge) { + InformationLayerData *data = layer_get_data(s_information_layer); + data->level = charge.charge_percent; + layer_mark_dirty(s_information_layer); +} + +void information_layer_init(Layer *layer, int y) { + GRect rect = layer_get_unobstructed_bounds(layer); + + s_information_layer = layer_create_with_data(GRect(0, y, rect.size.w, INFO_LAYER_HEIGHT), + sizeof(InformationLayerData)); + + InformationLayerData *data = layer_get_data(s_information_layer); + data->level = battery_state_service_peek().charge_percent; + data->steps = PBL_IF_HEALTH_ELSE(health_service_sum_today(HealthMetricStepCount), 0); + + layer_set_update_proc(s_information_layer, information_layer_update_proc); + layer_add_child(layer, s_information_layer); + + battery_state_service_subscribe(battery_state_service_callback); +} + +void information_layer_tick(void) { +#ifdef PBL_HEALTH + InformationLayerData *data = layer_get_data(s_information_layer); + data->steps = health_service_sum_today(HealthMetricStepCount); + layer_mark_dirty(s_information_layer); +#endif // PBL_HEALTH +} + +void information_layer_deinit(void) { + layer_destroy(s_information_layer); +} diff --git a/src/c/information_layer.h b/src/c/information_layer.h new file mode 100644 index 0000000..75216ca --- /dev/null +++ b/src/c/information_layer.h @@ -0,0 +1,10 @@ +#ifndef INFORMATION_LAYER_H_ +#define INFORMATION_LAYER_H_ + +#include + +void information_layer_init(Layer *layer, int y); +void information_layer_tick(void); +void information_layer_deinit(void); + +#endif // INFORMATION_LAYER_H_ diff --git a/src/c/main_window.c b/src/c/main_window.c index fd9089d..f7acd41 100644 --- a/src/c/main_window.c +++ b/src/c/main_window.c @@ -2,12 +2,14 @@ #include "date_layer.h" #include "character_layer.h" #include "clock_layer.h" +#include "information_layer.h" static Window *s_main_window; static void main_window_tick(struct tm *time, TimeUnits units) { date_layer_tick(); clock_layer_tick(); + information_layer_tick(); character_layer_tick(); } @@ -15,7 +17,11 @@ static void main_window_load(Window *window) { Layer *layer = window_get_root_layer(window); date_layer_init(layer, 0); - clock_layer_init(layer, 40); + clock_layer_init(layer, 30); + + GRect clock_layer_rect = clock_layer_get_bounds(); + information_layer_init(layer, 30 + clock_layer_rect.size.h + 4); + character_layer_init(layer); tick_timer_service_subscribe(SECOND_UNIT, main_window_tick); @@ -26,6 +32,7 @@ static void main_window_unload(Window *window) { clock_layer_deinit(); date_layer_deinit(); + information_layer_deinit(); character_layer_deinit(); } diff --git a/src/c/resources_service.c b/src/c/resources_service.c index 4c851af..744ca37 100644 --- a/src/c/resources_service.c +++ b/src/c/resources_service.c @@ -8,25 +8,41 @@ typedef struct ResourcesService { GFont font_toro; GBitmap *character_even; GBitmap *character_odd; + GBitmap *icon_battery_empty; + GBitmap *icon_battery_half; + GBitmap *icon_battery_full; + GBitmap *icon_paws; } ResourcesService; static ResourcesService *s_resources_service; void resources_service_init(void) { s_resources_service = malloc(sizeof(ResourcesService)); + s_resources_service->font_koneko_toro = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_KONEKO_TORO)); s_resources_service->font_toro = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_TORO)); + s_resources_service->character_even = gbitmap_create_with_resource(RESOURCE_ID_CHARACTER_EVEN); s_resources_service->character_odd = gbitmap_create_with_resource(RESOURCE_ID_CHARACTER_ODD); + + s_resources_service->icon_battery_empty = + gbitmap_create_with_resource(RESOURCE_ID_ICON_BATTERY_EMPTY); + s_resources_service->icon_battery_full = + gbitmap_create_with_resource(RESOURCE_ID_ICON_BATTERY_FULL); + s_resources_service->icon_battery_half = + gbitmap_create_with_resource(RESOURCE_ID_ICON_BATTERY_HALF); + s_resources_service->icon_paws = gbitmap_create_with_resource(RESOURCE_ID_ICON_PAWS); } void resources_service_deinit(void) { fonts_unload_custom_font(s_resources_service->font_koneko_toro); fonts_unload_custom_font(s_resources_service->font_toro); + gbitmap_destroy(s_resources_service->character_even); gbitmap_destroy(s_resources_service->character_odd); + free(s_resources_service); } @@ -37,6 +53,20 @@ GBitmap *resources_service_get_character(int ticks) { return s_resources_service->character_odd; } +GBitmap *resources_service_get_custom_icon(CustomIcon icon) { + switch (icon) { + case CustomIconBatteryEmpty: + return s_resources_service->icon_battery_empty; + case CustomIconBatteryFull: + return s_resources_service->icon_battery_full; + case CustomIconBatteryHalf: + return s_resources_service->icon_battery_half; + case CustomIconPaws: + return s_resources_service->icon_paws; + } + return NULL; +} + GFont resources_service_get_custom_font(CustomFont font) { switch (font) { case CustomFontKonekoToro: @@ -44,5 +74,5 @@ GFont resources_service_get_custom_font(CustomFont font) { case CustomFontToro: return s_resources_service->font_toro; } - return NULL; + return fonts_get_system_font(FONT_KEY_FONT_FALLBACK); } diff --git a/src/c/resources_service.h b/src/c/resources_service.h index b5217e9..d5c0f91 100644 --- a/src/c/resources_service.h +++ b/src/c/resources_service.h @@ -8,10 +8,18 @@ typedef enum CustomFont { CustomFontToro } CustomFont; +typedef enum CustomIcon { + CustomIconBatteryEmpty, + CustomIconBatteryFull, + CustomIconBatteryHalf, + CustomIconPaws +} CustomIcon; + void resources_service_init(void); void resources_service_deinit(void); GBitmap *resources_service_get_character(int ticks); +GBitmap *resources_service_get_custom_icon(CustomIcon icon); GFont resources_service_get_custom_font(CustomFont font); #endif