Skip to content
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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Utilities/space.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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."
150 changes: 69 additions & 81 deletions rwatch/ui/layer/single_notification_layer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}
Expand All @@ -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, &notif->attributes, rebble_attribute, node) {
list_foreach(a, &notif->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;
Copy link
Contributor

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...

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))
Expand All @@ -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)) {
Expand All @@ -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);
}

Expand All @@ -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)
Expand All @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The 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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this szrect.size.h also need to be changed?


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);
Expand All @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Loading