-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved Notifications: enhanced user interface #149
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,81 +169,61 @@ 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; | ||
} | ||
|
||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, wait, is this correct when notifications start scrolling? I think this needs to be 0, and the offset should be specified in notification_window, right? Otherwise, the secondary notification that you scroll up into will start too low on the screen? |
||
|
||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this |
||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that syntactically valid? I guess it builds in CI, so ... |
||
# error single_notification_layer not implemented on non-rectangular Pebbles | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whatever is doing this auto-reformatting, please configure it to not do that...