diff --git a/Utilities/space.sh b/Utilities/space.sh index c276ab5d..a5f9a62f 100755 --- a/Utilities/space.sh +++ b/Utilities/space.sh @@ -14,4 +14,4 @@ if [[ $(getsym _ccm_top) ]]; then fi echo "$RAM_REMAIN bytes of RAM available for heap." -echo "$FLASH_REMAIN bytes of flash unused." +echo "$FLASH_REMAIN bytes of flash unused." \ No newline at end of file diff --git a/rwatch/ui/layer/single_notification_layer.c b/rwatch/ui/layer/single_notification_layer.c index 5efd4b9b..771a4ce5 100644 --- a/rwatch/ui/layer/single_notification_layer.c +++ b/rwatch/ui/layer/single_notification_layer.c @@ -17,14 +17,16 @@ #include "single_notification_layer.h" #include "minilib.h" +#include "status_bar_layer.h" + static void single_notification_layer_update_proc(Layer *layer, GContext *ctx); #define X_PADDING 4 #define APPNAME_HEIGHT 28 -#define APPNAME_PADDING 6 -#define ELEMENT_PADDING 5 -#define APPNAME_FONT FONT_KEY_GOTHIC_28_BOLD +#define APPNAME_PADDING 8 +#define ELEMENT_PADDING 8 +#define APPNAME_FONT FONT_KEY_GOTHIC_24_BOLD #define TITLE_FONT FONT_KEY_GOTHIC_18_BOLD #define SUBTITLE_FONT FONT_KEY_GOTHIC_18 #define BODY_FONT FONT_KEY_GOTHIC_24_BOLD @@ -33,7 +35,7 @@ static void single_notification_layer_update_proc(Layer *layer, GContext *ctx); void single_notification_layer_ctor(SingleNotificationLayer *l, GRect frame) { layer_ctor(&l->layer, frame); layer_set_update_proc(&l->layer, single_notification_layer_update_proc); - + l->title = l->subtitle = l->body = l->timestamp = NULL; l->icon = NULL; } @@ -54,25 +56,26 @@ extern void single_notification_layer_set_notification(SingleNotificationLayer * free(l->timestamp); if (l->icon) gbitmap_destroy(l->icon); - + const char *sender = NULL, *subject = NULL, *message = NULL; uint32_t sourcetype = 0; - + rebble_attribute *a; - list_foreach(a, ¬if->attributes, rebble_attribute, node) { + list_foreach(a, ¬if->attributes, rebble_attribute, node) + { switch (a->timeline_attribute.attribute_id) { - case TimelineAttributeType_Sender: sender = (const char *) a->data; break; - case TimelineAttributeType_Subject: subject = (const char *) a->data; break; - case TimelineAttributeType_Message: message = (const char *) a->data; break; - case TimelineAttributeType_SourceType: - sourcetype = *(uint32_t *)a->data; - break; - default: - /* we don't care */ - ; + case TimelineAttributeType_Sender: sender = (const char *)a->data; break; + case TimelineAttributeType_Subject: subject = (const char *)a->data; break; + case TimelineAttributeType_Message: message = (const char *)a->data; break; + case TimelineAttributeType_SourceType: + sourcetype = *(uint32_t *)a->data; + break; + default: + /* we don't care */ + ; } } - + if (sender && strlen(sender)) { l->title = strdup(sender); if (subject && strlen(subject)) @@ -83,29 +86,34 @@ extern void single_notification_layer_set_notification(SingleNotificationLayer * l->title = strdup(subject); l->body = message ? strdup(message) : NULL; } - + + // since the 'source' attribute is useful just to set its respective icon, there's no need + // to hard code the app name using it. 'subject' will be used for that instead. switch (sourcetype) { case TimelineNotificationSource_SMS: - l->source = "SMS"; l->icon = gbitmap_create_with_resource(RESOURCE_ID_NOTIFICATION); break; case TimelineNotificationSource_Email: - l->source = "Email"; - l->icon = gbitmap_create_with_resource(RESOURCE_ID_NOTIFICATION); + l->icon = gbitmap_create_with_resource(RESOURCE_ID_NOTIFICATION); // Change to proper Email icon. + break; + case TimelineNotificationSource_Twitter: + l->icon = gbitmap_create_with_resource(RESOURCE_ID_UNKNOWN); // Change to Twitter icon. + break; + case TimelineNotificationSource_Facebook: + l->icon = gbitmap_create_with_resource(RESOURCE_ID_NOTIFICATION); // Change to Facebook icon. break; default: - l->source = NULL; l->icon = gbitmap_create_with_resource(RESOURCE_ID_UNKNOWN); break; } - + time_t now = rcore_get_time(); time_t ts = notif->timeline_item.timestamp; struct tm tm_ts; localtime_r(&ts, &tm_ts); - + char buf[32]; - + if (ts > now) l->timestamp = strdup("The future"); else if (ts > (now - 60 * 60)) { @@ -115,15 +123,15 @@ extern void single_notification_layer_set_notification(SingleNotificationLayer * sfmt(buf, sizeof(buf), "%d hours ago", (ts - now) / (60 * 60)); l->timestamp = strdup(buf); } else if (ts > (now - 7 * 24 * 60 * 60)) { - char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; sfmt(buf, sizeof(buf), "%s, %02d:%02d", days[tm_ts.tm_mday], tm_ts.tm_hour, tm_ts.tm_min); l->timestamp = strdup(buf); } else { - char *mons[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + char *mons[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; sfmt(buf, sizeof(buf), "%s %d, %02d:%02d", mons[tm_ts.tm_mon], tm_ts.tm_mday, tm_ts.tm_hour, tm_ts.tm_min); l->timestamp = strdup(buf); } - + layer_mark_dirty(&l->layer); } @@ -135,11 +143,11 @@ Layer *single_notification_layer_get_layer(SingleNotificationLayer *l) { uint16_t single_notification_layer_height(SingleNotificationLayer *l) { uint16_t height = 0; - + GRect szrect = layer_get_frame(&l->layer); szrect.size.h = 1000; szrect.size.w -= X_PADDING * 2; - + height += APPNAME_HEIGHT; height += APPNAME_PADDING; if (l->title) @@ -161,7 +169,7 @@ uint16_t single_notification_layer_height(SingleNotificationLayer *l) { l->timestamp, fonts_get_system_font(TIMESTAMP_FONT), szrect, GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, 0).h + ELEMENT_PADDING; - + return height; } @@ -169,73 +177,53 @@ static void single_notification_layer_update_proc(Layer *layer, GContext *ctx) { SingleNotificationLayer *l = container_of(layer, SingleNotificationLayer, layer); GRect szrect = layer_get_frame(layer); GSize outsz; - + graphics_context_set_fill_color(ctx, GColorWhite); graphics_fill_rect(ctx, szrect, 0, GCornerNone); - + graphics_context_set_text_color(ctx, GColorBlack); - + szrect.origin.x = 0; - szrect.origin.y = 0; - + szrect.origin.y = STATUS_BAR_LAYER_HEIGHT; + szrect.origin.x += X_PADDING; szrect.size.w -= X_PADDING * 2; - - if (l->source) { - GRect tmpsz = szrect; - tmpsz.origin.x += 1; - tmpsz.origin.y += 6; - tmpsz.size.h = 25; - tmpsz.size.w = 25; - graphics_context_set_compositing_mode(ctx, GCompOpSet); - graphics_draw_bitmap_in_rect(ctx, l->icon, tmpsz); - - tmpsz = szrect; - tmpsz.size.h = APPNAME_HEIGHT; - tmpsz.size.w -= APPNAME_HEIGHT + ELEMENT_PADDING; - tmpsz.origin.x += APPNAME_HEIGHT + ELEMENT_PADDING; - graphics_draw_text(ctx, l->source, fonts_get_system_font(APPNAME_FONT), - tmpsz, GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, 0); - szrect.origin.y += APPNAME_HEIGHT; - szrect.size.h -= APPNAME_HEIGHT; - } else { - /* no source, just a centered icon */ - GRect tmpsz = szrect; - tmpsz.origin.x += (tmpsz.size.w - 25) / 2; - tmpsz.origin.y += 5; - tmpsz.size.h = 25; - tmpsz.size.w = 25; - graphics_context_set_compositing_mode(ctx, GCompOpSet); - graphics_draw_bitmap_in_rect(ctx, l->icon, tmpsz); + GRect tmpsz = szrect; + tmpsz.size.h = 25; + tmpsz.size.w = 25; + graphics_context_set_compositing_mode(ctx, GCompOpSet); + graphics_draw_bitmap_in_rect(ctx, l->icon, tmpsz); + + tmpsz = szrect; + tmpsz.size.h = APPNAME_HEIGHT; // Align text vertically + tmpsz.size.w -= APPNAME_HEIGHT + ELEMENT_PADDING; + tmpsz.origin.x += APPNAME_HEIGHT; + + if (l->title) { + graphics_draw_text(ctx, l->title, fonts_get_system_font(APPNAME_FONT), + tmpsz, GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, 0); szrect.origin.y += APPNAME_HEIGHT; szrect.size.h -= APPNAME_HEIGHT; } - + graphics_context_set_stroke_color(ctx, GColorBlack); - graphics_context_set_stroke_width(ctx, 3); + graphics_context_set_stroke_width(ctx, 1); + /* XXX: make this a drawrect */ graphics_draw_line(ctx, - GPoint(szrect.origin.x, szrect.origin.y + 5), - GPoint(szrect.origin.x + szrect.size.w, szrect.origin.y + 5)); - szrect.origin.y += APPNAME_PADDING; + GPoint(szrect.origin.x, szrect.origin.y), + GPoint(szrect.origin.x + szrect.size.w, szrect.origin.y)); + szrect.origin.y += 0; szrect.size.h -= APPNAME_PADDING; - - if (l->title) { - graphics_draw_text_ex(ctx, - l->title, fonts_get_system_font(TITLE_FONT), - szrect, GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, 0, &outsz); - szrect.origin.y += outsz.h + ELEMENT_PADDING; - szrect.size.h -= outsz.h + ELEMENT_PADDING; - } + if (l->subtitle) { graphics_draw_text_ex(ctx, l->subtitle, fonts_get_system_font(SUBTITLE_FONT), szrect, GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, 0, &outsz); - szrect.origin.y += outsz.h + ELEMENT_PADDING; - szrect.size.h -= outsz.h + ELEMENT_PADDING; - } - if (l->body) { + szrect.origin.y += outsz.h; + szrect.size.h -= outsz.h; + } if (l->body) { graphics_draw_text_ex(ctx, l->body, fonts_get_system_font(BODY_FONT), szrect, GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, 0, &outsz); @@ -249,6 +237,6 @@ static void single_notification_layer_update_proc(Layer *layer, GContext *ctx) { szrect.size.h -= outsz.h + ELEMENT_PADDING; } -#else /* !PBL_RECT */ +#else !PBL_RECT # error single_notification_layer not implemented on non-rectangular Pebbles #endif diff --git a/rwatch/ui/notification_window.c b/rwatch/ui/notification_window.c index 34a78914..a7cd78d8 100644 --- a/rwatch/ui/notification_window.c +++ b/rwatch/ui/notification_window.c @@ -5,12 +5,17 @@ * Author: Joshua Wise */ +#include "rebbleos.h" #include "notification_window.h" #include "timeline.h" +#include "status_bar_layer.h" + static void _notification_window_load(Window *window); static void _notification_window_unload(Window *window); +static StatusBarLayer *_notif_window_status; + #ifndef PBL_RECT # error notification_window not implemented on round Pebble #endif @@ -28,17 +33,17 @@ void notification_window_ctor(NotificationWindow *w, Window *win) { frame.origin.y = 0; frame.size.w = DISPLAY_COLS; frame.size.h = 2048; - + w->uuids = NULL; w->nuuids = 0; - + w->curnotif = (size_t) -1; w->curnotif_nudging = 0; - + single_notification_layer_ctor(&w->n1, frame); single_notification_layer_ctor(&w->n2, frame); - - window_set_window_handlers(w->window, (WindowHandlers) { + + window_set_window_handlers(w->window, (WindowHandlers){ .load = _notification_window_load, .unload = _notification_window_unload }); @@ -59,12 +64,13 @@ void notification_window_set_notifications(NotificationWindow *w, Uuid *uuids, s return; } w->uuids = newuuids; - + memcpy(w->uuids, uuids, count * sizeof(Uuid)); w->nuuids = count; - + if (curnotif >= count) { w->curnotif = (size_t) -1; + return; } @@ -79,15 +85,16 @@ void notification_window_set_notifications(NotificationWindow *w, Uuid *uuids, s w->curnotif_nudging = 0; w->curnotif = curnotif; - + rebble_notification *notif = timeline_get_notification(w->uuids + curnotif); if (!notif) { - w->curnotif = (size_t) -1; + w->curnotif = (size_t)-1; + return; } single_notification_layer_set_notification(&w->n1, notif); timeline_destroy(notif); - + w->curnotif_height = single_notification_layer_height(&w->n1); } @@ -108,22 +115,23 @@ void notification_window_push_to_top(NotificationWindow *w, Uuid *uuid) { #endif *w->uuids = *uuid; w->nuuids++; - + if (w->curnotif || w->curnotif_scroll) { w->curnotif++; return; } - + /* We're the top notification and haven't scrolled, so we reset the * notification to view the new one. */ rebble_notification *notif = timeline_get_notification(w->uuids); if (!notif) { w->curnotif = (size_t) -1; + return; } single_notification_layer_set_notification(&w->n1, notif); timeline_destroy(notif); - + w->curnotif_height = single_notification_layer_height(&w->n1); } @@ -133,16 +141,16 @@ Window *notification_window_get_window(NotificationWindow *w) { static void _down_single_click_handler(ClickRecognizerRef _, void *_w) { NotificationWindow *w = _w; - + w->curnotif_scroll += SCROLL_INCR; if (w->curnotif == -1) return; - + int16_t curnotif_maxscroll = w->curnotif_height - VIEWPORT_HEIGHT; if (curnotif_maxscroll < 0) curnotif_maxscroll = 0; - int16_t curnotif_nudge = curnotif_maxscroll + NUDGE_HEIGHT; + int16_t curnotif_nudge = curnotif_maxscroll + NUDGE_HEIGHT; if (w->curnotif_nudging) { /* We are nudging and moving still scrolling down -- swap the * notifications to the next. We can only be nudging if there is a @@ -150,7 +158,7 @@ static void _down_single_click_handler(ClickRecognizerRef _, void *_w) { w->curnotif++; w->curnotif_scroll = 0; w->curnotif_nudging = 0; - + rebble_notification *notif = timeline_get_notification(w->uuids + w->curnotif); if (!notif) { w->curnotif = (size_t) -1; @@ -160,20 +168,19 @@ static void _down_single_click_handler(ClickRecognizerRef _, void *_w) { timeline_destroy(notif); layer_remove_from_parent(single_notification_layer_get_layer(&w->n2)); - + GRect frame = layer_get_frame(single_notification_layer_get_layer(&w->n1)); frame.origin.y = 0; layer_set_frame(single_notification_layer_get_layer(&w->n1), frame); - + w->curnotif_height = single_notification_layer_height(&w->n1); - } else if ((w->curnotif_scroll > curnotif_maxscroll) && !w->curnotif_nudging && ((w->curnotif + 1) < w->nuuids)) { /* We're moving down and about to run out, and there's another * notification ready. Load it into the nudge box at the bottom. */ w->curnotif_scroll = curnotif_nudge; w->curnotif_nudging = 1; - + /* Load the next one in place. */ rebble_notification *notif = timeline_get_notification(w->uuids + w->curnotif + 1); if (!notif) { @@ -188,15 +195,14 @@ static void _down_single_click_handler(ClickRecognizerRef _, void *_w) { layer_set_frame(single_notification_layer_get_layer(&w->n2), frame); layer_add_child(window_get_root_layer(w->window), single_notification_layer_get_layer(&w->n2)); - + frame = layer_get_frame(single_notification_layer_get_layer(&w->n1)); frame.origin.y = -curnotif_nudge; layer_set_frame(single_notification_layer_get_layer(&w->n1), frame); - } else { if (w->curnotif_scroll > curnotif_maxscroll) w->curnotif_scroll = curnotif_maxscroll; - + GRect frame = layer_get_frame(single_notification_layer_get_layer(&w->n1)); frame.origin.y = -w->curnotif_scroll; layer_set_frame(single_notification_layer_get_layer(&w->n1), frame); @@ -205,26 +211,25 @@ static void _down_single_click_handler(ClickRecognizerRef _, void *_w) { static void _up_single_click_handler(ClickRecognizerRef _, void *_w) { NotificationWindow *w = _w; - + int16_t curnotif_maxscroll = w->curnotif_height - VIEWPORT_HEIGHT; if (curnotif_maxscroll < 0) curnotif_maxscroll = 0; - + if (w->curnotif == (size_t) -1) return; - + w->curnotif_scroll -= SCROLL_INCR; - + if (w->curnotif_nudging) { /* Un-nudge. */ w->curnotif_scroll = curnotif_maxscroll; GRect frame = layer_get_frame(single_notification_layer_get_layer(&w->n1)); frame.origin.y = -w->curnotif_scroll; layer_set_frame(single_notification_layer_get_layer(&w->n1), frame); - + w->curnotif_nudging = 0; layer_remove_from_parent(single_notification_layer_get_layer(&w->n2)); - } else if (w->curnotif_scroll == -SCROLL_INCR && w->curnotif > 0) { /* Kick ourselves to the bottom of the screen, make the previous one * active. Load ourselves first. */ @@ -235,19 +240,19 @@ static void _up_single_click_handler(ClickRecognizerRef _, void *_w) { } single_notification_layer_set_notification(&w->n2, notif); timeline_destroy(notif); - + GRect frame = layer_get_frame(single_notification_layer_get_layer(&w->n2)); frame.origin.y = VIEWPORT_HEIGHT - NUDGE_HEIGHT; layer_set_frame(single_notification_layer_get_layer(&w->n2), frame); - + layer_add_child(window_get_root_layer(w->window), single_notification_layer_get_layer(&w->n2)); w->curnotif_nudging = 1; - + /* Now load the previous one. */ w->curnotif--; notif = timeline_get_notification(w->uuids + w->curnotif); if (!notif) { - w->curnotif = (size_t) -1; + w->curnotif = (size_t)-1; return; } single_notification_layer_set_notification(&w->n1, notif); @@ -258,16 +263,15 @@ static void _up_single_click_handler(ClickRecognizerRef _, void *_w) { if (curnotif_maxscroll < 0) curnotif_maxscroll = 0; w->curnotif_scroll = curnotif_maxscroll + NUDGE_HEIGHT; - + frame = layer_get_frame(single_notification_layer_get_layer(&w->n1)); frame.origin.y = -w->curnotif_scroll; layer_set_frame(single_notification_layer_get_layer(&w->n1), frame); - } else { /* Normal scroll. */ if (w->curnotif_scroll < 0) w->curnotif_scroll = 0; - + GRect frame = layer_get_frame(single_notification_layer_get_layer(&w->n1)); frame.origin.y = -w->curnotif_scroll; layer_set_frame(single_notification_layer_get_layer(&w->n1), frame); @@ -276,10 +280,10 @@ static void _up_single_click_handler(ClickRecognizerRef _, void *_w) { static void _notification_window_click_config_provider(NotificationWindow *w) { window_single_repeating_click_subscribe(BUTTON_ID_DOWN, 500, _down_single_click_handler); - window_single_repeating_click_subscribe(BUTTON_ID_UP , 500, _up_single_click_handler ); + window_single_repeating_click_subscribe(BUTTON_ID_UP, 500, _up_single_click_handler); window_set_click_context(BUTTON_ID_DOWN, w); - window_set_click_context(BUTTON_ID_UP , w); - + window_set_click_context(BUTTON_ID_UP, w); + if (w->clickconfig) w->clickconfig(w->clickconfigcontext); } @@ -292,11 +296,21 @@ void notification_window_set_click_config(NotificationWindow *w, ClickConfigProv static void _notification_window_load(Window *window) { NotificationWindow *w = window->user_data; Layer *window_layer = window_get_root_layer(window); - + layer_add_child(window_layer, single_notification_layer_get_layer(&w->n1)); - - window_set_click_config_provider_with_context(window, (ClickConfigProvider) _notification_window_click_config_provider, w); + + _notif_window_status = status_bar_layer_create(); + // By defauld, keep the status bar with these colors. + status_bar_layer_set_colors(_notif_window_status, + GColorWhite, /* Get the color of the notification window. Until now, it's just white. w->n1.layer.sibling->window->background_color */ + GColorBlack); + status_bar_layer_set_separator_mode(_notif_window_status, StatusBarLayerSeparatorModeNone); + + layer_add_child(window_layer, status_bar_layer_get_layer(_notif_window_status)); + + window_set_click_config_provider_with_context(window, (ClickConfigProvider)_notification_window_click_config_provider, w); } static void _notification_window_unload(Window *window) { + }