diff --git a/appinfo.json b/appinfo.json index 9e0d25b..b777ecb 100644 --- a/appinfo.json +++ b/appinfo.json @@ -1,32 +1,43 @@ { - "uuid": "f50e354a-e8d9-46be-be74-0c5f488b3a4d", - "shortName": "Readebble", - "longName": "Readebble", - "companyName": "Neal", - "versionCode": 1, - "versionLabel": "2.2", - "capabilities": [ "configurable" ], - "watchapp": { - "watchface": false - }, - "appKeys": { - "type": 0, - "method": 1, - "index": 2, - "title": 3 - }, - "config": { - "keen": { "projectId": "54532b53383144606491d740", "writeKey": "3d2051dfda71165e1f4693d7ad0787911713fcc4156da4556fb3211c4aee9efa6b7a76b5078e785565a4554b18186ed83dd8bcb59eeaf7931f53a576b2fc2b69cb9c936fb97ef1c83d79239165e0b1682ad56887ad3031ca476b9ed536a9d1edfb129ff85dd3193cc43e2b0308024b97" } - }, - "debug": true, - "resources": { - "media": [ - { - "menuIcon": true, - "type": "png", - "name": "IMAGE_MENU_ICON", - "file": "icon.png" - } - ] - } -} + "uuid": "f50e354a-e8d9-46be-be74-0c5f488b3a4d", + "shortName": "Readebble", + "longName": "Readebble", + "companyName": "Neal", + "versionCode": 1, + "versionLabel": "2.2", + "capabilities": [ + "configurable" + ], + "watchapp": { + "watchface": false + }, + "appKeys": { + "type": 0, + "method": 1, + "index": 2, + "title": 3 + }, + "config": { + "keen": { + "projectId": "54532b53383144606491d740", + "writeKey": "3d2051dfda71165e1f4693d7ad0787911713fcc4156da4556fb3211c4aee9efa6b7a76b5078e785565a4554b18186ed83dd8bcb59eeaf7931f53a576b2fc2b69cb9c936fb97ef1c83d79239165e0b1682ad56887ad3031ca476b9ed536a9d1edfb129ff85dd3193cc43e2b0308024b97" + } + }, + "debug": true, + "resources": { + "media": [ + { + "menuIcon": true, + "type": "png", + "name": "IMAGE_MENU_ICON", + "file": "icon.png" + } + ] + }, + "targetPlatforms": [ + "aplite", + "basalt", + "chalk" + ], + "sdkVersion": "3" +} \ No newline at end of file diff --git a/src/headlines.c b/src/headlines.c index cda95d6..7e51d47 100644 --- a/src/headlines.c +++ b/src/headlines.c @@ -10,10 +10,12 @@ static Headline *headlines = NULL; static uint8_t num_headlines = 0; static uint8_t current_headline = 0; +static uint8_t progress = 0; static char *error = NULL; void headlines_init(void) { + progress = 0; win_headlines_init(); } @@ -33,6 +35,7 @@ void headlines_in_received_handler(DictionaryIterator *iter) { if (!tuple) break; error = malloc(tuple->length); strncpy(error, tuple->value->cstring, tuple->length); + progress = 100; headlines_reload_data_and_mark_dirty(); break; } @@ -41,6 +44,7 @@ void headlines_in_received_handler(DictionaryIterator *iter) { tuple = dict_find(iter, APP_KEY_INDEX); if (!tuple) break; num_headlines = tuple->value->uint8; + progress = (num_headlines == 0 ? 100 : 0); headlines = malloc(sizeof(Headline) * num_headlines); if (headlines == NULL) num_headlines = 0; break; @@ -55,6 +59,7 @@ void headlines_in_received_handler(DictionaryIterator *iter) { if (tuple) { strncpy(headline->title, tuple->value->cstring, sizeof(headline->title) - 1); } + progress = ((int)(index + 1) * 100) / num_headlines; LOG("headline: %d '%s'", headline->index, headline->title); headlines_reload_data_and_mark_dirty(); break; @@ -66,6 +71,10 @@ void headlines_reload_data_and_mark_dirty() { win_headlines_reload_data_and_mark_dirty(); } +uint8_t headlines_progress() { + return progress; +} + uint8_t headlines_count() { return num_headlines; } @@ -105,3 +114,4 @@ void headlines_request() { app_message_outbox_send(); headlines_reload_data_and_mark_dirty(); } + diff --git a/src/headlines.h b/src/headlines.h index 05f5914..12c8652 100644 --- a/src/headlines.h +++ b/src/headlines.h @@ -12,6 +12,7 @@ void headlines_deinit(void); void headlines_in_received_handler(DictionaryIterator *iter); void headlines_reload_data_and_mark_dirty(); uint8_t headlines_count(); +uint8_t headlines_progress(); char* headlines_get_error(); Headline* headlines_get(uint8_t index); Headline* headlines_get_current(); diff --git a/src/js/src/keen.js b/src/js/src/keen.js index 0e22908..f13b8d1 100644 --- a/src/js/src/keen.js +++ b/src/js/src/keen.js @@ -1,6 +1,6 @@ var Keen = { addEvent: function(name, data) { - if (!data || !data instanceof Object) data = {}; + if ((!data) || !(data instanceof Object)) data = {}; data.app = { name: AppInfo.shortName, version: AppInfo.versionLabel, uuid: AppInfo.uuid }; data.user = { accountToken: Pebble.getAccountToken() }; if (AppInfo.debug) { diff --git a/src/js/src/main.js b/src/js/src/main.js index 2fcfb17..b1b00a6 100644 --- a/src/js/src/main.js +++ b/src/js/src/main.js @@ -10,8 +10,8 @@ Readebble.bufferSize = 440; Readebble.init = function() { Readebble.subscriptions = JSON.parse(localStorage.getItem('subscriptions')) || []; Readebble.pocket = JSON.parse(localStorage.getItem('pocket')) || {}; - if (!Readebble.subscriptions instanceof Array) Readebble.subscriptions = []; - if (!Readebble.pocket instanceof Object) Readebble.pocket = {}; + if (!(Readebble.subscriptions instanceof Array)) Readebble.subscriptions = []; + if (!(Readebble.pocket instanceof Object)) Readebble.pocket = {}; Readebble.sendSubscriptions(); setTimeout(function() { Keen.addEvent('init', { subscriptions: { count: Readebble.subscriptions.length }, hasPocketToken: (Readebble.pocket.access_token !== 'undefined') }); }, 100); }; @@ -23,7 +23,7 @@ Readebble.sendError = function(type, err) { Readebble.sendSubscriptions = function() { if (!this.subscriptions.length) { - return Readebble.sendError(TYPE.SUBSCRIPTION, 'No subscriptions found. Use the app settings in the Pebble mobile app to add subscriptions.'); + return Readebble.sendError(TYPE.SUBSCRIPTION, 'No feeds configured'); } appMessageQueue.send({type:TYPE.SUBSCRIPTION, method:METHOD.SIZE, index:Readebble.subscriptions.length}); for (var i = 0; i < Readebble.subscriptions.length; i++) { @@ -45,6 +45,7 @@ Readebble.sendHeadlines = function() { Readebble.sendStory = function(story) { var maxStoryBuffer = Readebble.bufferSize - 32; + appMessageQueue.send({type:TYPE.STORY, method:METHOD.SIZE, index:story.length}); for (var i = 0; i <= Math.floor(story.length/maxStoryBuffer); i++) { appMessageQueue.send({type:TYPE.STORY, method:METHOD.DATA, title:story.substring(i * maxStoryBuffer, i * maxStoryBuffer + maxStoryBuffer)}); } @@ -95,7 +96,7 @@ Readebble.addToPocket = function() { if (!Readebble.pocket.access_token) return Pebble.showSimpleNotificationOnPebble('Readebble', 'No Pocket account found. Please log in to your Pocket account via the Pebble mobile app.'); var url = 'https://ineal.me/pebble/readebble/pocket/add'; var data = { access_token: Readebble.pocket.access_token, url: Readebble.currentHeadline.link }; - http('POST', url, serialize(data), null, function (e) { + http('POST', url, serialize(data), {"Content-Type": "application/x-www-form-urlencoded"}, function (e) { var res = JSON.parse(e.responseText); debugLog(res); if (res.item && res.status == 1) { diff --git a/src/layers/progress_bar.c b/src/layers/progress_bar.c new file mode 100644 index 0000000..dfbc99f --- /dev/null +++ b/src/layers/progress_bar.c @@ -0,0 +1,156 @@ +#include +#include "layers/progress_bar.h" + +typedef struct _ProgressBarData { + uint8_t progress; + ProgressBarLayerUpdateProc update_proc; + GColor complete; + GColor remaining; +} ProgressBarData; + +static void progress_bar_layer_update_proc(ProgressBarLayer *progress_bar_layer, GContext *ctx); +#ifdef PBL_ROUND +float math_sqrt(const float num); +#endif + +ProgressBarLayer * progress_bar_layer_create_fullscreen(Window * window) { + GRect window_bounds = layer_get_bounds(window_get_root_layer(window)); + GPoint start = GPoint(0, STATUS_BAR_LAYER_HEIGHT - 2); + int16_t width = window_bounds.size.w; + + ProgressBarLayer *progress_bar_layer = progress_bar_layer_create(start, width); + + Layer *window_layer = window_get_root_layer(window); + layer_add_child(window_layer, progress_bar_layer_get_layer(progress_bar_layer)); + + return progress_bar_layer; +} + +ProgressBarLayer * progress_bar_layer_create(GPoint start, int16_t width) { + // Set up the progress layer + GRect bounds = (GRect){ + .origin = start, + .size = GSize(width, 1) + }; + + ProgressBarLayer *progress_bar_layer = layer_create_with_data(bounds, sizeof(ProgressBarData)); + ProgressBarData *progress_bar_data = (ProgressBarData *)layer_get_data((Layer *)progress_bar_layer); + progress_bar_data->progress = 0; + progress_bar_data->complete = GColorBlack; + progress_bar_data->remaining = GColorWhite; + progress_bar_data->update_proc = NULL; + + layer_set_update_proc(progress_bar_layer, progress_bar_layer_update_proc); + + return progress_bar_layer; +} + +void progress_bar_layer_stretch(ProgressBarLayer * progress_bar_layer) { + GRect window_bounds = layer_get_bounds(window_get_root_layer(layer_get_window((Layer *)progress_bar_layer))); + GRect layer_bounds = layer_get_bounds((Layer *)progress_bar_layer); + + layer_bounds.origin.x = 0; + layer_bounds.size.w = window_bounds.size.w; + layer_set_frame((Layer *)progress_bar_layer, layer_bounds); +} + +void progress_bar_layer_add_to_window(ProgressBarLayer * progress_bar_layer, Window *window) { + layer_add_child(window_get_root_layer(window), (Layer *)progress_bar_layer); +} + +void progress_bar_layer_destroy(ProgressBarLayer * progress_bar_layer) { + // Disconnect from other layers + layer_remove_child_layers((Layer *)progress_bar_layer); + layer_remove_from_parent((Layer *)progress_bar_layer); + + // Free all memory + layer_destroy((Layer *)progress_bar_layer); +} + +void progress_bar_layer_set_pos(ProgressBarLayer * progress_bar_layer, int16_t y) { + GRect window_bounds = layer_get_bounds(window_get_root_layer(layer_get_window((Layer *)progress_bar_layer))); + +#ifdef PBL_ROUND + float radius = ((float)window_bounds.size.h / 2.0); + float offset = (radius - (float)y); + int16_t width = (int16_t)math_sqrt((radius * radius) - (offset * offset)); +#else + int16_t width = window_bounds.size.w / 2; +#endif + + GRect layer_bounds = (GRect){ + .origin = GPoint((window_bounds.size.w / 2) - width, y), + .size = GSize(width * 2, 1) + }; + layer_set_frame((Layer *)progress_bar_layer, layer_bounds); +} + +void progress_bar_layer_set_progress(ProgressBarLayer * progress_bar_layer, uint8_t percent) { + ProgressBarData *progress_bar_data = (ProgressBarData *)layer_get_data((Layer *)progress_bar_layer); + + progress_bar_data->progress = percent; + layer_mark_dirty((Layer *)progress_bar_layer); +} + +void progress_bar_layer_set_colors(ProgressBarLayer * progress_bar_layer, GColor complete, GColor remaining) { + ProgressBarData *progress_bar_data = (ProgressBarData *)layer_get_data((Layer *)progress_bar_layer); + + progress_bar_data->complete = complete; + progress_bar_data->remaining = remaining; + layer_mark_dirty((Layer *)progress_bar_layer); +} + +GColor progress_bar_layer_get_complete_color(ProgressBarLayer * progress_bar_layer) { + ProgressBarData *progress_bar_data = (ProgressBarData *)layer_get_data((Layer *)progress_bar_layer); + + return progress_bar_data->complete; +} + +GColor progress_bar_layer_get_remaining_color(ProgressBarLayer * progress_bar_layer) { + ProgressBarData *progress_bar_data = (ProgressBarData *)layer_get_data((Layer *)progress_bar_layer); + + return progress_bar_data->remaining; +} + +void progress_bar_layer_set_update_proc(ProgressBarLayer * progress_bar_layer, ProgressBarLayerUpdateProc update_proc) { + ProgressBarData *progress_bar_data = (ProgressBarData *)layer_get_data((Layer *)progress_bar_layer); + + progress_bar_data->update_proc = update_proc; +} + +static void progress_bar_layer_update_proc(Layer *layer, GContext *ctx) { + ProgressBarData *progress_bar_data = (ProgressBarData *)layer_get_data(layer); + + if (progress_bar_data->update_proc != NULL) { + progress_bar_data->update_proc((ProgressBarLayer *)layer, ctx); + } + else { + GRect bounds = layer_get_bounds(layer); + + int width = (int)(float)(((float)progress_bar_data->progress / 100.0f) * bounds.size.w); + // Complete + graphics_context_set_stroke_color(ctx, progress_bar_data->complete); + graphics_draw_line(ctx, GPointZero, GPoint(width, 0)); + // Remaining + graphics_context_set_stroke_color(ctx, progress_bar_data->remaining); + graphics_draw_line(ctx, GPoint(width, 0), GPoint(bounds.size.w, 0)); + } +} + +#ifdef PBL_ROUND +// See https://forums.getpebble.com/discussion/comment/79122/#Comment_79122 +float math_sqrt(const float num) { + const uint MAX_STEPS = 40; + const float MAX_ERROR = 0.001; + + float answer = num; + float ans_sqr = answer * answer; + uint step = 0; + while((ans_sqr - num > MAX_ERROR) && (step++ < MAX_STEPS)) { + answer = (answer + (num / answer)) / 2; + ans_sqr = answer * answer; + } + return answer; +} +#endif + diff --git a/src/layers/progress_bar.h b/src/layers/progress_bar.h new file mode 100644 index 0000000..7bb6f62 --- /dev/null +++ b/src/layers/progress_bar.h @@ -0,0 +1,20 @@ +#pragma once + +typedef Layer ProgressBarLayer; +typedef void(* ProgressBarLayerUpdateProc)(ProgressBarLayer *layer, GContext *ctx); + +#define progress_bar_layer_get_layer(layer) (Layer *)layer +#define progress_bar_layer_destroy_safe(layer) if (layer != NULL) { progress_bar_layer_destroy(layer); layer = NULL; } + +ProgressBarLayer * progress_bar_layer_create(GPoint start, int16_t width); +void progress_bar_layer_destroy(ProgressBarLayer * progress_bar_layer); +void progress_bar_layer_set_pos(ProgressBarLayer * progress_bar_layer, int16_t y); +void progress_bar_layer_set_progress(ProgressBarLayer * progress_bar_layer, uint8_t percent); +void progress_bar_layer_set_colors(ProgressBarLayer * progress_bar_layer, GColor complete, GColor remaining); +GColor progress_bar_layer_get_complete_color(ProgressBarLayer * progress_bar_layer); +GColor progress_bar_layer_get_remaining_color(ProgressBarLayer * progress_bar_layer); +void progress_bar_layer_set_update_proc(ProgressBarLayer * progress_bar_layer, ProgressBarLayerUpdateProc update_proc); +void progress_bar_layer_stretch(ProgressBarLayer * progress_bar_layer); +ProgressBarLayer * progress_bar_layer_create_fullscreen(Window * window); + + diff --git a/src/layers/title_bar.c b/src/layers/title_bar.c new file mode 100644 index 0000000..f308820 --- /dev/null +++ b/src/layers/title_bar.c @@ -0,0 +1,180 @@ +#include +#include "layers/title_bar.h" +#include "libs/pebble-assist.h" + +#define TITLE_LAYER_PAD (8) +#define TITLE_TEXT_PAD (4) + +typedef struct _TitleBarData { + TitleBarLayerUpdateProc update_proc; + GColor foreground; + GColor background; + TextLayer * text_layer; +} TitleBarData; + +static void title_bar_layer_update_proc(TitleBarLayer *title_bar_layer, GContext *ctx); +void title_bar_layer_centre_text_vertically(TitleBarLayer * title_bar_layer); + +TitleBarLayer * title_bar_layer_create_fullscreen(Window * window) { + GRect window_bounds = layer_get_bounds(window_get_root_layer(window)); + + GRect layer_bounds = (GRect){ + .origin = GPoint(0, 0), + .size = GSize(window_bounds.size.w, TITLE_BAR_DEFAULT_HEIGHT) + }; + + TitleBarLayer *title_bar_layer = title_bar_layer_create(layer_bounds); + + Layer *window_layer = window_get_root_layer(window); + layer_add_child(window_layer, title_bar_layer_get_layer(title_bar_layer)); + + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data((Layer *)title_bar_layer); + text_layer_enable_screen_text_flow_and_paging(title_bar_data->text_layer, 2); + title_bar_layer_centre_text_vertically(title_bar_layer); + + return title_bar_layer; +} + +TitleBarLayer * title_bar_layer_create(GRect bounds) { + // Set up the title layer + TitleBarLayer *title_bar_layer = layer_create_with_data(bounds, sizeof(TitleBarData)); + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data((Layer *)title_bar_layer); + title_bar_data->foreground = GColorBlack; + title_bar_data->background = GColorWhite; + title_bar_data->update_proc = NULL; + + title_bar_data->text_layer = text_layer_create(bounds); + text_layer_set_text_alignment(title_bar_data->text_layer, GTextAlignmentCenter); + text_layer_set_text_color(title_bar_data->text_layer, GColorBlack); + text_layer_set_background_color(title_bar_data->text_layer, GColorClear); + text_layer_set_font(title_bar_data->text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD)); + + layer_add_child((Layer*)title_bar_layer, text_layer_get_layer(title_bar_data->text_layer)); + + layer_set_update_proc(title_bar_layer, title_bar_layer_update_proc); + + return title_bar_layer; +} + +void title_bar_layer_stretch(TitleBarLayer * title_bar_layer) { + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data((Layer *)title_bar_layer); + GRect window_bounds = layer_get_bounds(window_get_root_layer(layer_get_window((Layer *)title_bar_layer))); + GRect layer_bounds = layer_get_bounds((Layer *)title_bar_layer); + + layer_bounds.origin.x = 0; + layer_bounds.size.w = window_bounds.size.w; + layer_set_frame((Layer *)title_bar_layer, layer_bounds); + + layer_set_frame(text_layer_get_layer(title_bar_data->text_layer), layer_bounds); + title_bar_layer_centre_text_vertically(title_bar_layer); +} + +void title_bar_layer_add_to_window(TitleBarLayer * title_bar_layer, Window *window) { + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data((Layer *)title_bar_layer); + layer_add_child(window_get_root_layer(window), (Layer *)title_bar_layer); + text_layer_enable_screen_text_flow_and_paging(title_bar_data->text_layer, 2); + title_bar_layer_centre_text_vertically(title_bar_layer); +} + +void title_bar_layer_destroy(TitleBarLayer * title_bar_layer) { + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data((Layer *)title_bar_layer); + + // Remove text layer + layer_remove_child_layers(text_layer_get_layer(title_bar_data->text_layer)); + layer_remove_from_parent(text_layer_get_layer(title_bar_data->text_layer)); + text_layer_destroy(title_bar_data->text_layer); + + // Disconnect from other layers + layer_remove_child_layers((Layer *)title_bar_layer); + layer_remove_from_parent((Layer *)title_bar_layer); + + // Free all memory + layer_destroy((Layer *)title_bar_layer); +} + +void title_bar_layer_set_height(TitleBarLayer * title_bar_layer, int16_t height) { + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data((Layer *)title_bar_layer); + GRect window_bounds = layer_get_bounds(window_get_root_layer(layer_get_window((Layer *)title_bar_layer))); + + GRect layer_bounds = (GRect){ + .origin = GPoint(0, 0), + .size = GSize(window_bounds.size.w, height) + }; + layer_set_frame((Layer *)title_bar_layer, layer_bounds); + layer_set_frame(text_layer_get_layer(title_bar_data->text_layer), layer_bounds); + title_bar_layer_centre_text_vertically(title_bar_layer); +} + +int16_t title_bar_layer_get_height(TitleBarLayer * title_bar_layer) { + GRect layer_bounds = layer_get_bounds(title_bar_layer_get_layer(title_bar_layer)); + return layer_bounds.size.h; +} + +void title_bar_layer_reduce_height(TitleBarLayer * title_bar_layer) { + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data((Layer *)title_bar_layer); + + GSize title_layer_size = text_layer_get_content_size(title_bar_data->text_layer); + int16_t height = title_layer_size.h + TITLE_LAYER_PAD; + title_bar_layer_set_height(title_bar_layer, height); +} + +void title_bar_layer_set_colors(TitleBarLayer * title_bar_layer, GColor foreground, GColor background) { + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data((Layer *)title_bar_layer); + + text_layer_set_text_color(title_bar_data->text_layer, foreground); + title_bar_data->foreground = foreground; + title_bar_data->background = background; + layer_mark_dirty((Layer *)title_bar_layer); +} + +GColor title_bar_layer_get_foreground_color(TitleBarLayer * title_bar_layer) { + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data((Layer *)title_bar_layer); + + return title_bar_data->foreground; +} + +GColor title_bar_layer_get_background_color(TitleBarLayer * title_bar_layer) { + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data((Layer *)title_bar_layer); + + return title_bar_data->background; +} + +void title_bar_layer_set_update_proc(TitleBarLayer * title_bar_layer, TitleBarLayerUpdateProc update_proc) { + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data((Layer *)title_bar_layer); + + title_bar_data->update_proc = update_proc; +} + +static void title_bar_layer_update_proc(Layer *layer, GContext *ctx) { + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data(layer); + + if (title_bar_data->update_proc != NULL) { + title_bar_data->update_proc((TitleBarLayer *)layer, ctx); + } + else { + GRect bounds = layer_get_bounds(layer); + graphics_context_set_fill_color(ctx, title_bar_data->background); + graphics_fill_rect(ctx, bounds, 0, GCornerNone); + } +} + +void title_bar_layer_set_text(TitleBarLayer * title_bar_layer, const char * text) { + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data((Layer *)title_bar_layer); + + text_layer_set_text(title_bar_data->text_layer, text); + title_bar_layer_centre_text_vertically(title_bar_layer); +} + +void title_bar_layer_centre_text_vertically(TitleBarLayer * title_bar_layer) { + TitleBarData *title_bar_data = (TitleBarData *)layer_get_data((Layer *)title_bar_layer); + + GRect title_bar_bounds = layer_get_bounds((Layer *)title_bar_layer); + GRect text_bounds = layer_get_bounds(text_layer_get_layer(title_bar_data->text_layer)); + GSize text_size = text_layer_get_content_size(title_bar_data->text_layer); + + text_bounds.origin.y = (title_bar_bounds.size.h - text_size.h - TITLE_TEXT_PAD) / 2; + + layer_set_frame(text_layer_get_layer(title_bar_data->text_layer), text_bounds); +} + + diff --git a/src/layers/title_bar.h b/src/layers/title_bar.h new file mode 100644 index 0000000..8e8e844 --- /dev/null +++ b/src/layers/title_bar.h @@ -0,0 +1,25 @@ +#pragma once + +#define TITLE_BAR_DEFAULT_HEIGHT PBL_IF_ROUND_ELSE ((STATUS_BAR_LAYER_HEIGHT + 12), (STATUS_BAR_LAYER_HEIGHT + 24)) + +#define TITLE_BAR_DEFAULT_WIDTH PBL_IF_RECT_ELSE(144, 180) + +typedef Layer TitleBarLayer; +typedef void(* TitleBarLayerUpdateProc)(TitleBarLayer *layer, GContext *ctx); + +#define title_bar_layer_get_layer(layer) (Layer *)layer +#define title_bar_layer_destroy_safe(layer) if (layer != NULL) { title_bar_layer_destroy(layer); layer = NULL; } + +TitleBarLayer * title_bar_layer_create(GRect bounds); +void title_bar_layer_destroy(TitleBarLayer * title_bar_layer); +void title_bar_layer_set_height(TitleBarLayer * title_bar_layer, int16_t height); +int16_t title_bar_layer_get_height(TitleBarLayer * title_bar_layer); +void title_bar_layer_reduce_height(TitleBarLayer * title_bar_layer); +void title_bar_layer_set_colors(TitleBarLayer * title_bar_layer, GColor foreground, GColor background); +GColor title_bar_layer_get_foreground_color(TitleBarLayer * title_bar_layer); +GColor title_bar_layer_get_background_color(TitleBarLayer * title_bar_layer); +void title_bar_layer_set_update_proc(TitleBarLayer * title_bar_layer, TitleBarLayerUpdateProc update_proc); +void title_bar_layer_stretch(TitleBarLayer * title_bar_layer); +TitleBarLayer * title_bar_layer_create_fullscreen(Window * window); +void title_bar_layer_set_text(TitleBarLayer * title_bar_layer, const char * text); + diff --git a/src/rss.c b/src/rss.c index 1854d88..fcc9a78 100644 --- a/src/rss.c +++ b/src/rss.c @@ -8,6 +8,9 @@ #include "headlines.h" #include "story.h" +#define INBOX_SIZE_MAX (440) +#define OUTBOX_SIZE_MAX (440) + static void in_received_handler(DictionaryIterator *iter, void *context); static void out_failed_handler(DictionaryIterator *failed, AppMessageResult reason, void *context); static void timer_callback(void *data); @@ -18,7 +21,7 @@ static char *error = NULL; void rss_init(void) { app_message_register_inbox_received(in_received_handler); app_message_register_outbox_failed(out_failed_handler); - app_message_open_max(); + app_message_open(INBOX_SIZE_MAX, OUTBOX_SIZE_MAX); timer = app_timer_register(1000, timer_callback, NULL); @@ -101,7 +104,13 @@ static void timer_callback(void *data) { DictionaryIterator *iter; app_message_outbox_begin(&iter); dict_write_uint8(iter, APP_KEY_METHOD, KEY_METHOD_BUFFERSIZE); - dict_write_uint32(iter, APP_KEY_INDEX, app_message_inbox_size_maximum()); + //dict_write_uint32(iter, APP_KEY_INDEX, app_message_inbox_size_maximum()); + // Reducing the size of the buffer allows the story to be sent in chunks + uint32_t buffersize = INBOX_SIZE_MAX; + if (buffersize > app_message_inbox_size_maximum()) { + buffersize = app_message_inbox_size_maximum(); + } + dict_write_uint32(iter, APP_KEY_INDEX, buffersize); dict_write_end(iter); app_message_outbox_send(); } diff --git a/src/settings.c b/src/settings.c index 7ecfe41..e8839d3 100644 --- a/src/settings.c +++ b/src/settings.c @@ -3,11 +3,100 @@ #include "persist.h" #include "windows/win-settings.h" +const char * font_size_string[] = { + "Small", + "Medium", + "Large" +}; + +const char * font_color_string[] = { + "Dark", + "Light" +}; + +const char * headlines_font_size_value[] = { + FONT_KEY_GOTHIC_14, + FONT_KEY_GOTHIC_18, + FONT_KEY_GOTHIC_24 +}; + +const char * story_body_font_size_value[] = { + FONT_KEY_GOTHIC_14, + FONT_KEY_GOTHIC_18, + FONT_KEY_GOTHIC_24 +}; + +const char * story_title_font_size_value[] = { + FONT_KEY_GOTHIC_14_BOLD, + FONT_KEY_GOTHIC_18_BOLD, + FONT_KEY_GOTHIC_24_BOLD +}; + Settings _settings = { .headlines_font_size = FONT_SIZE_SMALL, .story_font_size = FONT_SIZE_SMALL, - .story_font_color = FONT_COLOR_WHITE, + .story_font_color = FONT_COLOR_LIGHT, +}; + +#ifdef PBL_COLOR +static const InterfaceColors s_interface_colors[] = { + // Dark + { + .main_background = {GColorBlackARGB8}, + .main_text = {GColorWhiteARGB8}, + .inverted_text = {GColorBlackARGB8}, + .selection_highlight = {GColorIcterineARGB8}, + .title_bar_background = {GColorOrangeARGB8}, + .title_bar_text = {GColorWhiteARGB8}, + .progress_bar_complete = {GColorIcterineARGB8}, + .progress_bar_remaining = {GColorBlackARGB8}, + .settings_text = {GColorCelesteARGB8}, + .settings_inverted_text = {GColorBlueARGB8} + }, + // Light + { + .main_background = {GColorWhiteARGB8}, + .main_text = {GColorBlackARGB8}, + .inverted_text = {GColorWhiteARGB8}, + .selection_highlight = {GColorOrangeARGB8}, + .title_bar_background = {GColorPastelYellowARGB8}, + .title_bar_text = {GColorBlackARGB8}, + .progress_bar_complete = {GColorOrangeARGB8}, + .progress_bar_remaining = {GColorWhiteARGB8}, + .settings_text = {GColorBlueARGB8}, + .settings_inverted_text = {GColorCelesteARGB8} + } +}; +#else +static const InterfaceColors s_interface_colors[] = { + // Dark + { + .main_background = {GColorBlackARGB8}, + .main_text = {GColorWhiteARGB8}, + .inverted_text = {GColorBlackARGB8}, + .selection_highlight = {GColorWhiteARGB8}, + .title_bar_background = {GColorBlackARGB8}, + .title_bar_text = {GColorWhiteARGB8}, + .progress_bar_complete = {GColorWhiteARGB8}, + .progress_bar_remaining = {GColorBlackARGB8}, + .settings_text = {GColorWhiteARGB8}, + .settings_inverted_text = {GColorBlackARGB8} + }, + // Light + { + .main_background = {GColorWhiteARGB8}, + .main_text = {GColorBlackARGB8}, + .inverted_text = {GColorWhiteARGB8}, + .selection_highlight = {GColorBlackARGB8}, + .title_bar_background = {GColorWhiteARGB8}, + .title_bar_text = {GColorBlackARGB8}, + .progress_bar_complete = {GColorBlackARGB8}, + .progress_bar_remaining = {GColorWhiteARGB8}, + .settings_text = {GColorBlackARGB8}, + .settings_inverted_text = {GColorWhiteARGB8} + } }; +#endif void settings_init(void) { persist_read_data(KEY_PERSIST_SETTINGS, &_settings, sizeof(_settings)); @@ -22,3 +111,53 @@ void settings_deinit(void) { Settings* settings() { return &_settings; } + +GFont settings_get_story_title_font_size(void) { + return fonts_get_system_font(story_title_font_size_value[_settings.story_font_size]); +} + +GFont settings_get_story_body_font_size(void) { + return fonts_get_system_font(story_body_font_size_value[_settings.story_font_size]); +} + +GFont settings_get_headlines_font_size(void) { + return fonts_get_system_font(headlines_font_size_value[_settings.headlines_font_size]); +} + +const char * settings_get_story_font_size_string(void) { + return font_size_string[_settings.story_font_size]; +} + +const char * settings_get_headlines_font_size_string(void) { + return font_size_string[_settings.headlines_font_size]; +} + +const char * settings_get_story_font_color_string(void) { + return font_color_string[_settings.story_font_color]; +} + +const InterfaceColors * settings_get_colors(void) { + return & s_interface_colors[_settings.story_font_color]; +} + +void settings_bump_headlines_font_size(void) { + _settings.headlines_font_size++; + if (_settings.headlines_font_size >= FONT_SIZE_NUM) { + _settings.headlines_font_size = 0; + } +} + +void settings_bump_story_font_size(void) { + _settings.story_font_size++; + if (_settings.story_font_size >= FONT_SIZE_NUM) { + _settings.story_font_size = 0; + } +} + +void settings_bump_story_font_color(void) { + _settings.story_font_color++; + if (_settings.story_font_color >= FONT_COLOR_NUM) { + _settings.story_font_color = 0; + } +} + diff --git a/src/settings.h b/src/settings.h index 3465da8..13f1c71 100644 --- a/src/settings.h +++ b/src/settings.h @@ -1,13 +1,22 @@ #pragma once typedef enum { + FONT_SIZE_INVALID = -1, + FONT_SIZE_SMALL, + FONT_SIZE_MEDIUM, FONT_SIZE_LARGE, + + FONT_SIZE_NUM } FONT_SIZE; typedef enum { - FONT_COLOR_BLACK, - FONT_COLOR_WHITE, + FONT_COLOR_INVALID = -1, + + FONT_COLOR_DARK, + FONT_COLOR_LIGHT, + + FONT_COLOR_NUM } FONT_COLOR; typedef struct { @@ -16,6 +25,35 @@ typedef struct { FONT_COLOR story_font_color; } Settings; +typedef struct { + GColor main_background; + GColor main_text; + GColor inverted_text; + GColor selection_highlight; + GColor title_bar_background; + GColor title_bar_text; + GColor progress_bar_complete; + GColor progress_bar_remaining; + GColor settings_text; + GColor settings_inverted_text; +} InterfaceColors; + void settings_init(void); void settings_deinit(void); Settings* settings(); + +GFont settings_get_story_title_font_size(void); +GFont settings_get_story_body_font_size(void); +GFont settings_get_headlines_font_size(void); + +const char * settings_get_headlines_font_size_string(void); +const char * settings_get_story_font_size_string(void); +const char * settings_get_story_font_color_string(void); + +void settings_bump_headlines_font_size(void); +void settings_bump_story_font_size(void); +void settings_bump_story_font_color(void); + +const InterfaceColors * settings_get_colors(void); + + diff --git a/src/story.c b/src/story.c index ee41642..0e71779 100644 --- a/src/story.c +++ b/src/story.c @@ -9,8 +9,15 @@ #define STORY_MAX_LEN 2000 static char story[STORY_MAX_LEN]; +static uint32_t story_size; +static uint32_t story_loaded; +static uint8_t progress = 0; + +void story_update_progress(); void story_init(void) { + story_size = 0u; + story_loaded = 0u; win_story_init(); } @@ -22,13 +29,25 @@ void story_in_received_handler(DictionaryIterator *iter) { Tuple *tuple = dict_find(iter, APP_KEY_METHOD); if (!tuple) return; switch (tuple->value->uint8) { + case KEY_METHOD_SIZE: + tuple = dict_find(iter, APP_KEY_INDEX); + if (!tuple) break; + story_size = tuple->value->uint32; + progress = (story_size == 0 ? 100 : 0); + break; case KEY_METHOD_DATA: tuple = dict_find(iter, APP_KEY_TITLE); if (!tuple) break; - if (strlen(story) + tuple->length < STORY_MAX_LEN) + if (strlen(story) + tuple->length < STORY_MAX_LEN) { strcat(story, tuple->value->cstring); + story_loaded += tuple->length; + progress = (story_loaded * 100) / story_size; + story_update_progress(); + LOG("loaded: %u", (unsigned int)story_loaded); + } break; case KEY_METHOD_READY: + progress = 100; win_story_reload_data_and_mark_dirty(); break; } @@ -40,6 +59,8 @@ char* story_get() { void story_request() { story[0] = '\0'; + story_size = 0u; + story_loaded = 0u; DictionaryIterator *iter; app_message_outbox_begin(&iter); dict_write_uint8(iter, APP_KEY_METHOD, KEY_METHOD_REQUESTSTORY); @@ -47,3 +68,12 @@ void story_request() { dict_write_end(iter); app_message_outbox_send(); } + +void story_update_progress() { + win_story_update_progress(); +} + +uint8_t story_progress() { + return progress; +} + diff --git a/src/story.h b/src/story.h index 5fc29c4..bcc33fd 100644 --- a/src/story.h +++ b/src/story.h @@ -8,3 +8,5 @@ void story_in_received_handler(DictionaryIterator *iter); char* story_get(); void story_free(); void story_request(); +uint8_t story_progress(); + diff --git a/src/subscriptions.c b/src/subscriptions.c index ecdc2e7..b2b8217 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -54,7 +54,7 @@ void subscriptions_in_received_handler(DictionaryIterator *iter) { if (tuple) { strncpy(subscription->title, tuple->value->cstring, sizeof(subscription->title) - 1); } - LOG("subscription: %d '%s'", subscription->index, subscription->title); + //LOG("subscription: %d '%s'", subscription->index, subscription->title); subscriptions_reload_data_and_mark_dirty(); break; } diff --git a/src/windows/win-headlines.c b/src/windows/win-headlines.c index 32769fe..5be3339 100644 --- a/src/windows/win-headlines.c +++ b/src/windows/win-headlines.c @@ -6,6 +6,12 @@ #include "subscriptions.h" #include "rss.h" #include "win-story.h" +#include "layers/progress_bar.h" +#include "layers/title_bar.h" + +#define TEXT_BORDER_INSET (16) + +#define GRECT_EDGE_TRIM(RECT, AMOUNT) RECT.origin.x += AMOUNT; RECT.size.w -= (AMOUNT * 2); static uint16_t menu_get_num_rows_callback(struct MenuLayer *menu_layer, uint16_t section_index, void *callback_context); static int16_t menu_get_header_height_callback(struct MenuLayer *menu_layer, uint16_t section_index, void *callback_context); @@ -16,15 +22,21 @@ static void menu_select_callback(struct MenuLayer *menu_layer, MenuIndex *cell_i static void menu_select_long_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context); static void window_load(Window *window); static void window_unload(Window *window); +static void window_set_colors(Window * window); +static void window_appear(Window *window); static Window *window = NULL; static MenuLayer *menu_layer = NULL; +static ProgressBarLayer *s_progress_bar_layer; +static int16_t s_title_bar_height; +static TitleBarLayer *s_status_bar; void win_headlines_init(void) { window = window_create(); window_set_window_handlers(window, (WindowHandlers) { .load = window_load, .unload = window_unload, + .appear = window_appear, }); } @@ -39,6 +51,7 @@ void win_headlines_deinit(void) { void win_headlines_reload_data_and_mark_dirty(void) { if (!window_is_loaded(window)) return; + progress_bar_layer_set_progress(s_progress_bar_layer, headlines_progress()); menu_layer_reload_data_and_mark_dirty(menu_layer); } @@ -49,26 +62,31 @@ static uint16_t menu_get_num_rows_callback(struct MenuLayer *menu_layer, uint16_ } static int16_t menu_get_header_height_callback(struct MenuLayer *menu_layer, uint16_t section_index, void *callback_context) { - return MENU_CELL_BASIC_HEADER_HEIGHT; + return 0;//MENU_CELL_BASIC_HEADER_HEIGHT; } static int16_t menu_get_cell_height_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context) { + GRect bounds = layer_get_bounds(menu_layer_get_layer(menu_layer)); + GRECT_EDGE_TRIM (bounds, TEXT_BORDER_INSET) if (headlines_get_error()) { - return graphics_text_layout_get_content_size(headlines_get_error(), fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), GRect(4, 2, 136, 128), GTextOverflowModeFill, GTextAlignmentLeft).h + 12; + return graphics_text_layout_get_content_size(headlines_get_error(), fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), bounds, GTextOverflowModeFill, GTextAlignmentLeft).h + 12; } - return graphics_text_layout_get_content_size(headlines_get(cell_index->row)->title, fonts_get_system_font(settings()->headlines_font_size ? FONT_KEY_GOTHIC_24 : FONT_KEY_GOTHIC_18), GRect(2, 0, 140, 128), GTextOverflowModeFill, GTextAlignmentLeft).h + (settings()->headlines_font_size ? 10 : 8); + return graphics_text_layout_get_content_size(headlines_get(cell_index->row)->title, settings_get_headlines_font_size(), bounds, GTextOverflowModeFill, GTextAlignmentLeft).h + (settings()->headlines_font_size ? 10 : 8); } static void menu_draw_header_callback(GContext *ctx, const Layer *cell_layer, uint16_t section_index, void *callback_context) { - menu_cell_basic_header_draw(ctx, cell_layer, subscriptions_get_current()->title); + //menu_cell_basic_header_draw(ctx, cell_layer, subscriptions_get_current()->title); } static void menu_draw_row_callback(GContext *ctx, const Layer *cell_layer, MenuIndex *cell_index, void *callback_context) { - graphics_context_set_text_color(ctx, GColorBlack); + GRect bounds = layer_get_bounds(menu_layer_get_layer(menu_layer)); + //bounds.origin.y -= s_title_bar_height; + GRECT_EDGE_TRIM (bounds, TEXT_BORDER_INSET) + graphics_context_set_text_color(ctx, (menu_cell_layer_is_highlighted(cell_layer) ? settings_get_colors()->inverted_text : settings_get_colors()->main_text)); if (headlines_get_error()) { - graphics_draw_text(ctx, headlines_get_error(), fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), GRect(4, 2, 136, 128), GTextOverflowModeFill, GTextAlignmentLeft, NULL); + graphics_draw_text(ctx, headlines_get_error(), fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), bounds, GTextOverflowModeFill, PBL_IF_ROUND_ELSE(GTextAlignmentCenter, GTextAlignmentLeft), NULL); } else { - graphics_draw_text(ctx, headlines_get(cell_index->row)->title, fonts_get_system_font(settings()->headlines_font_size ? FONT_KEY_GOTHIC_24 : FONT_KEY_GOTHIC_18), GRect(2, 0, 140, 128), GTextOverflowModeFill, GTextAlignmentLeft, NULL); + graphics_draw_text(ctx, headlines_get(cell_index->row)->title, settings_get_headlines_font_size(), bounds, GTextOverflowModeFill, PBL_IF_ROUND_ELSE(GTextAlignmentCenter, GTextAlignmentLeft), NULL); } } @@ -84,6 +102,10 @@ static void menu_select_long_callback(struct MenuLayer *menu_layer, MenuIndex *c rss_add_to_pocket(); } +static void window_appear(Window *window) { + window_set_colors(window); +} + static void window_load(Window *window) { menu_layer = menu_layer_create_fullscreen(window); menu_layer_set_callbacks(menu_layer, NULL, (MenuLayerCallbacks) { @@ -97,8 +119,39 @@ static void window_load(Window *window) { }); menu_layer_set_click_config_onto_window(menu_layer, window); menu_layer_add_to_window(menu_layer, window); + + // Set up the status bar if it's needed + s_status_bar = title_bar_layer_create_fullscreen(window); + title_bar_layer_set_text(s_status_bar, subscriptions_get_current()->title); + + title_bar_layer_reduce_height(s_status_bar); + + s_title_bar_height = title_bar_layer_get_height(s_status_bar); + +#ifdef PBL_RECT + GRect menu_bounds = layer_get_frame(menu_layer_get_layer(menu_layer)); + menu_bounds.origin.y += s_title_bar_height; + menu_bounds.size.h -= s_title_bar_height; + scroll_layer_set_frame(menu_layer_get_scroll_layer(menu_layer), menu_bounds); +#endif + + // Set up the progress layer + s_progress_bar_layer = progress_bar_layer_create_fullscreen(window); + progress_bar_layer_set_pos(s_progress_bar_layer, s_title_bar_height); } static void window_unload(Window *window) { menu_layer_destroy_safe(menu_layer); + // Destroy the status bar if there is one + title_bar_layer_destroy(s_status_bar); + // Destroy the progress bar + progress_bar_layer_destroy(s_progress_bar_layer); } + +static void window_set_colors(Window * window) { + title_bar_layer_set_colors(s_status_bar, settings_get_colors()->title_bar_text, settings_get_colors()->title_bar_background); + menu_layer_set_normal_colors(menu_layer, settings_get_colors()->main_background, settings_get_colors()->main_text); + menu_layer_set_highlight_colors(menu_layer, settings_get_colors()->selection_highlight, settings_get_colors()->inverted_text); + progress_bar_layer_set_colors(s_progress_bar_layer, settings_get_colors()->progress_bar_complete, settings_get_colors()->progress_bar_remaining); +} + diff --git a/src/windows/win-settings.c b/src/windows/win-settings.c index b019fa0..f3d1be4 100644 --- a/src/windows/win-settings.c +++ b/src/windows/win-settings.c @@ -3,16 +3,17 @@ #include "libs/pebble-assist.h" #include "settings.h" -#define MENU_NUM_SECTIONS 2 -#define MENU_SECTION_HEADLINES 0 -#define MENU_SECTION_SUMMARY 1 - -#define MENU_SECTION_ROWS_HEADLINES 1 -#define MENU_SECTION_ROWS_SUMMARY 2 - -#define MENU_ROW_HEADLINES_SIZE 0 -#define MENU_ROW_SUMMARY_SIZE 0 -#define MENU_ROW_SUMMARY_COLOR 1 +#define MENU_NUM_SECTIONS 1 + +enum { + MENU_ROW_INVALID = -1, + + MENU_ROW_HEADLINES_SIZE, + MENU_ROW_SUMMARY_SIZE, + MENU_ROW_SUMMARY_COLOR, + + MENU_ROW_NUM +} MENU_ROW; static uint16_t menu_get_num_sections_callback(struct MenuLayer *menu_layer, void *callback_context); static uint16_t menu_get_num_rows_callback(struct MenuLayer *menu_layer, uint16_t section_index, void *callback_context); @@ -23,15 +24,28 @@ static void menu_draw_row_callback(GContext *ctx, const Layer *cell_layer, MenuI static void menu_select_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context); static void window_load(Window *window); static void window_unload(Window *window); +static void window_set_colors(Window * window); +static void window_appear(Window *window); static Window *window = NULL; static MenuLayer *menu_layer = NULL; +#ifdef PBL_SDK_3 +static StatusBarLayer *s_status_bar; +static TextLayer *s_lower_bar; +#endif + +static char *settings_labels[MENU_ROW_NUM] = { + "Headline font size", + "Story font fize", + "Story text color" +}; void win_settings_init(void) { window = window_create(); window_set_window_handlers(window, (WindowHandlers) { .load = window_load, .unload = window_unload, + .appear = window_appear, }); } @@ -50,89 +64,72 @@ static uint16_t menu_get_num_sections_callback(struct MenuLayer *menu_layer, voi } static uint16_t menu_get_num_rows_callback(struct MenuLayer *menu_layer, uint16_t section_index, void *callback_context) { - switch (section_index) { - case MENU_SECTION_HEADLINES: - return MENU_SECTION_ROWS_HEADLINES; - case MENU_SECTION_SUMMARY: - return MENU_SECTION_ROWS_SUMMARY; - default: - return 0; - } + return MENU_ROW_NUM; } static int16_t menu_get_header_height_callback(struct MenuLayer *menu_layer, uint16_t section_index, void *callback_context) { - return MENU_CELL_BASIC_HEADER_HEIGHT; + return 0; } static int16_t menu_get_cell_height_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context) { +#ifdef PBL_RECT return 38; +#else + if (menu_layer_is_index_selected(menu_layer, cell_index)) { + return MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT; + } + else { + return MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT; + } +#endif } static void menu_draw_header_callback(GContext *ctx, const Layer *cell_layer, uint16_t section_index, void *callback_context) { - switch (section_index) { - case MENU_SECTION_HEADLINES: - menu_cell_basic_header_draw(ctx, cell_layer, "Headlines"); - break; - case MENU_SECTION_SUMMARY: - menu_cell_basic_header_draw(ctx, cell_layer, "Story"); - break; - } + // Do nothing } static void menu_draw_row_callback(GContext *ctx, const Layer *cell_layer, MenuIndex *cell_index, void *callback_context) { - char label[12] = ""; - char value[6] = ""; - switch (cell_index->section) { - case MENU_SECTION_HEADLINES: - switch (cell_index->row) { - case MENU_ROW_HEADLINES_SIZE: - strcpy(label, "Font Size"); - strcpy(value, settings()->headlines_font_size ? "Large": "Small"); - break; - } + char const *value = NULL; + switch (cell_index->row) { + case MENU_ROW_HEADLINES_SIZE: + value = settings_get_headlines_font_size_string(); + break; + case MENU_ROW_SUMMARY_SIZE: + value = settings_get_story_font_size_string(); break; - case MENU_SECTION_SUMMARY: - switch (cell_index->row) { - case MENU_ROW_SUMMARY_SIZE: - strcpy(label, "Font Size"); - strcpy(value, settings()->story_font_size ? "Large": "Small"); - break; - case MENU_ROW_SUMMARY_COLOR: - strcpy(label, "Font Color"); - strcpy(value, settings()->story_font_color ? "White": "Black"); - break; - } + case MENU_ROW_SUMMARY_COLOR: + value = settings_get_story_font_color_string(); break; } - graphics_context_set_text_color(ctx, GColorBlack); - graphics_draw_text(ctx, label, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), GRect(4, 2, 136, 28), GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL); - graphics_draw_text(ctx, value, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), GRect(4, 7, 134, 24), GTextOverflowModeTrailingEllipsis, GTextAlignmentRight, NULL); + menu_cell_basic_draw(ctx, cell_layer, settings_labels[cell_index->row], value, NULL); } static void menu_select_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context) { - switch (cell_index->section) { - case MENU_SECTION_HEADLINES: - switch (cell_index->row) { - case MENU_ROW_HEADLINES_SIZE: - settings()->headlines_font_size = !settings()->headlines_font_size; - break; - } + switch (cell_index->row) { + case MENU_ROW_HEADLINES_SIZE: + settings_bump_headlines_font_size(); break; - case MENU_SECTION_SUMMARY: - switch (cell_index->row) { - case MENU_ROW_SUMMARY_SIZE: - settings()->story_font_size = !settings()->story_font_size; - break; - case MENU_ROW_SUMMARY_COLOR: - settings()->story_font_color = !settings()->story_font_color; - break; - } + case MENU_ROW_SUMMARY_SIZE: + settings_bump_story_font_size(); + break; + case MENU_ROW_SUMMARY_COLOR: + settings_bump_story_font_color(); + window_set_colors(window); break; } menu_layer_reload_data(menu_layer); } +static void window_appear(Window *window) { + window_set_colors(window); +} + static void window_load(Window *window) { + Layer *window_layer = window_get_root_layer(window); +#ifdef PBL_ROUND + GRect bounds = layer_get_bounds(window_layer); +#endif + menu_layer = menu_layer_create_fullscreen(window); menu_layer_set_callbacks(menu_layer, NULL, (MenuLayerCallbacks) { .get_num_sections = menu_get_num_sections_callback, @@ -145,8 +142,42 @@ static void window_load(Window *window) { }); menu_layer_set_click_config_onto_window(menu_layer, window); menu_layer_add_to_window(menu_layer, window); + + // Set up the status bar if it's needed + s_status_bar = status_bar_layer_create(); + layer_add_child(window_layer, status_bar_layer_get_layer(s_status_bar)); + +#ifdef PBL_ROUND + // Set up the lower message bar + const GEdgeInsets message_insets = {.top = bounds.size.h - STATUS_BAR_LAYER_HEIGHT}; + s_lower_bar = text_layer_create(grect_inset(bounds, message_insets)); + text_layer_set_text_alignment(s_lower_bar, GTextAlignmentCenter); + text_layer_set_text(s_lower_bar, ""); + layer_add_child(window_layer, text_layer_get_layer(s_lower_bar)); +#else + GRect menu_bounds = layer_get_frame(menu_layer_get_layer(menu_layer)); + menu_bounds.origin.y += STATUS_BAR_LAYER_HEIGHT; + menu_bounds.size.h -= STATUS_BAR_LAYER_HEIGHT; + scroll_layer_set_frame(menu_layer_get_scroll_layer(menu_layer), menu_bounds); +#endif } static void window_unload(Window *window) { menu_layer_destroy_safe(menu_layer); + +#ifdef PBL_SDK_3 + // Destroy the status bar if there is one + status_bar_layer_destroy(s_status_bar); + text_layer_destroy(s_lower_bar); +#endif +} + +static void window_set_colors(Window * window) { + status_bar_layer_set_colors(s_status_bar, settings_get_colors()->main_background, settings_get_colors()->main_text); + menu_layer_set_normal_colors(menu_layer, settings_get_colors()->main_background, settings_get_colors()->main_text); + menu_layer_set_highlight_colors(menu_layer, settings_get_colors()->selection_highlight, settings_get_colors()->inverted_text); +#ifdef PBL_ROUND + text_layer_set_colors(s_lower_bar, settings_get_colors()->main_text, settings_get_colors()->main_background); +#endif } + diff --git a/src/windows/win-story.c b/src/windows/win-story.c index 63ac0bd..471106f 100644 --- a/src/windows/win-story.c +++ b/src/windows/win-story.c @@ -6,6 +6,10 @@ #include "headlines.h" #include "subscriptions.h" #include "rss.h" +#include "layers/progress_bar.h" + +#define TITLE_LAYER_PAD (8) +#define STORY_LAYER_PAD (8) static void select_long_click_handler(ClickRecognizerRef recognizer, void *context); static void click_config_provider(void *context); @@ -16,12 +20,16 @@ static Window *window = NULL; static ScrollLayer *scroll_layer = NULL; static TextLayer *title_text_layer = NULL; static TextLayer *story_text_layer = NULL; +static ProgressBarLayer *progress_bar_layer = NULL; +static void window_set_colors(Window * window); +static void window_appear(Window *window); void win_story_init(void) { window = window_create(); window_set_window_handlers(window, (WindowHandlers) { .load = window_load, .unload = window_unload, + .appear = window_appear, }); } @@ -37,22 +45,33 @@ void win_story_deinit(void) { void win_story_reload_data_and_mark_dirty(void) { if (!window_is_loaded(window)) return; - text_layer_set_text(story_text_layer, story_get()); + GRect window_bounds = layer_get_bounds(window_get_root_layer(window)); + GRect title_layer_bounds = layer_get_bounds(text_layer_get_layer(title_text_layer)); - GSize title_layer_size = text_layer_get_content_size(title_text_layer); - GSize story_layer_size = text_layer_get_content_size(story_text_layer); + text_layer_set_text(story_text_layer, story_get()); - title_layer_size.h += 8; - story_layer_size.h += 8; + GSize story_layer_size = window_bounds.size; + story_layer_size.h *= 10; + text_layer_set_size(story_text_layer, story_layer_size); - text_layer_set_size(title_text_layer, title_layer_size); + story_layer_size = text_layer_get_content_size(story_text_layer); + story_layer_size.w = window_bounds.size.w; + story_layer_size.h += STORY_LAYER_PAD; text_layer_set_size(story_text_layer, story_layer_size); - scroll_layer_set_content_size(scroll_layer, GSize(144, title_layer_size.h + story_layer_size.h)); + scroll_layer_set_content_size(scroll_layer, GSize(window_bounds.size.w, story_layer_size.h + title_layer_bounds.size.h)); + scroll_layer_set_paging(scroll_layer, true); + + progress_bar_layer_set_progress(progress_bar_layer, 100); scroll_layer_mark_dirty(scroll_layer); } +void win_story_update_progress(void) { + if (!window_is_loaded(window)) return; + progress_bar_layer_set_progress(progress_bar_layer, story_progress()); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // static void select_long_click_handler(ClickRecognizerRef recognizer, void *context) { @@ -63,8 +82,24 @@ static void click_config_provider(void *context) { window_long_click_subscribe(BUTTON_ID_SELECT, 500, select_long_click_handler, NULL); } +static void window_appear(Window *window) { + window_set_colors(window); +} + static void window_load(Window *window) { - window_set_background_color(window, settings()->story_font_color ? GColorBlack : GColorWhite); + GRect window_bounds = layer_get_bounds(window_get_root_layer(window)); + + story_text_layer = text_layer_create(window_bounds); + text_layer_set_font(story_text_layer, settings_get_story_body_font_size()); + text_layer_set_text_alignment(story_text_layer, GTextAlignmentCenter); + text_layer_set_text(story_text_layer, "Loading..."); + + + title_text_layer = text_layer_create(window_bounds); + text_layer_set_font(title_text_layer, settings_get_story_title_font_size()); + text_layer_set_text_alignment(title_text_layer, GTextAlignmentCenter); + text_layer_set_text(title_text_layer, headlines_get_current()->title); + scroll_layer = scroll_layer_create_fullscreeen(window); scroll_layer_set_click_config_onto_window(scroll_layer, window); @@ -73,34 +108,60 @@ static void window_load(Window *window) { }); scroll_layer_add_to_window(scroll_layer, window); - title_text_layer = text_layer_create(GRect(3, -2, 140, 96)); - text_layer_set_colors(title_text_layer, settings()->story_font_color ? GColorWhite : GColorBlack, GColorClear); - text_layer_set_font(title_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text(title_text_layer, headlines_get_current()->title); + scroll_layer_add_child(scroll_layer, text_layer_get_layer(story_text_layer)); + scroll_layer_add_child(scroll_layer, text_layer_get_layer(title_text_layer)); - GSize title_layer_size = text_layer_get_content_size(title_text_layer); - title_layer_size.h += 8; + text_layer_enable_screen_text_flow_and_paging(story_text_layer, 2); + text_layer_enable_screen_text_flow_and_paging(title_text_layer, 2); + GSize title_layer_size = window_bounds.size; + title_layer_size.h *= 10; + text_layer_set_size(title_text_layer, title_layer_size); + title_layer_size = text_layer_get_content_size(title_text_layer); + title_layer_size.w = window_bounds.size.w; + title_layer_size.h += TITLE_LAYER_PAD; text_layer_set_size(title_text_layer, title_layer_size); - story_text_layer = text_layer_create(GRect(3, title_layer_size.h, 140, 1024)); - text_layer_set_colors(story_text_layer, settings()->story_font_color ? GColorWhite : GColorBlack, GColorClear); - text_layer_set_font(story_text_layer, fonts_get_system_font(settings()->story_font_size ? FONT_KEY_GOTHIC_24_BOLD : FONT_KEY_GOTHIC_18_BOLD)); - text_layer_set_text(story_text_layer, "Loading..."); - - GSize story_layer_size = text_layer_get_content_size(story_text_layer); - story_layer_size.h = story_layer_size.h + 8; + GSize story_layer_size = window_bounds.size; + story_layer_size.h *= 10; + text_layer_set_size(story_text_layer, story_layer_size); + story_layer_size = text_layer_get_content_size(story_text_layer); + story_layer_size.w = window_bounds.size.w; + story_layer_size.h += STORY_LAYER_PAD; text_layer_set_size(story_text_layer, story_layer_size); - scroll_layer_set_content_size(scroll_layer, GSize(144, title_layer_size.h + story_layer_size.h)); + GRect story_layer_frame = layer_get_frame(text_layer_get_layer(story_text_layer)); + story_layer_frame.origin.y = title_layer_size.h; + layer_set_frame((Layer*)story_text_layer, story_layer_frame); + text_layer_enable_screen_text_flow_and_paging(story_text_layer, 2); - scroll_layer_add_child(scroll_layer, text_layer_get_layer(title_text_layer)); - scroll_layer_add_child(scroll_layer, text_layer_get_layer(story_text_layer)); + GSize scroll_layer_size = window_bounds.size; + scroll_layer_size.h = title_layer_size.h + story_layer_size.h; + scroll_layer_set_content_size(scroll_layer, scroll_layer_size); + + scroll_layer_set_paging(scroll_layer, true); + + // Set up the progress layer + progress_bar_layer = progress_bar_layer_create_fullscreen(window); + progress_bar_layer_set_pos(progress_bar_layer, title_layer_size.h); + progress_bar_layer_set_progress(progress_bar_layer, 0); + scroll_layer_add_child(scroll_layer, progress_bar_layer_get_layer(progress_bar_layer)); } static void window_unload(Window *window) { text_layer_destroy_safe(title_text_layer); text_layer_destroy_safe(story_text_layer); scroll_layer_destroy_safe(scroll_layer); + + // Destroy the progress bar + progress_bar_layer_destroy(progress_bar_layer); +} + +static void window_set_colors(Window * window) { + window_set_background_color(window, settings_get_colors()->main_background); + text_layer_set_colors(story_text_layer, settings_get_colors()->main_text, settings_get_colors()->main_background); + text_layer_set_colors(title_text_layer, settings_get_colors()->title_bar_text, settings_get_colors()->title_bar_background); + progress_bar_layer_set_colors(progress_bar_layer, settings_get_colors()->progress_bar_complete, settings_get_colors()->progress_bar_remaining); } + diff --git a/src/windows/win-story.h b/src/windows/win-story.h index ca1fbc1..5a45d1f 100644 --- a/src/windows/win-story.h +++ b/src/windows/win-story.h @@ -4,3 +4,4 @@ void win_story_init(void); void win_story_push(void); void win_story_deinit(void); void win_story_reload_data_and_mark_dirty(void); +void win_story_update_progress(void); diff --git a/src/windows/win-subscriptions.c b/src/windows/win-subscriptions.c index 61e9f02..3817fef 100644 --- a/src/windows/win-subscriptions.c +++ b/src/windows/win-subscriptions.c @@ -5,6 +5,12 @@ #include "rss.h" #include "win-headlines.h" #include "win-settings.h" +#include "settings.h" + +#define MENU_NUM_SECTIONS (1) +#define TEXT_BORDER_INSET (16) + +#define GRECT_EDGE_TRIM(RECT, AMOUNT) RECT.origin.x += AMOUNT; RECT.size.w -= (AMOUNT * 2); static uint16_t menu_get_num_sections_callback(struct MenuLayer *menu_layer, void *callback_context); static uint16_t menu_get_num_rows_callback(struct MenuLayer *menu_layer, uint16_t section_index, void *callback_context); @@ -16,15 +22,42 @@ static void menu_select_callback(struct MenuLayer *menu_layer, MenuIndex *cell_i static void menu_select_long_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context); static void window_load(Window *window); static void window_unload(Window *window); +int menu_item_count(); +int menu_item_settings_pos(); +static void window_set_colors(Window * window); +static void window_appear(Window *window); static Window *window = NULL; static MenuLayer *menu_layer = NULL; +static StatusBarLayer *s_status_bar; +#ifdef PBL_ROUND +static TextLayer *s_lower_bar; +#endif + +int menu_item_count() { + int items; + items = subscriptions_count() + 1; + if (items < 2) { + items = 2; + } + return items; +} + +int menu_item_settings_pos() { + int position; + position = subscriptions_count(); + if (position < 1) { + position = 1; + } + return position; +} void win_subscriptions_init(void) { window = window_create(); window_set_window_handlers(window, (WindowHandlers) { .load = window_load, .unload = window_unload, + .appear = window_appear, }); window_stack_push(window, true); } @@ -38,53 +71,57 @@ void win_subscriptions_reload_data_and_mark_dirty(void) { menu_layer_reload_data_and_mark_dirty(menu_layer); } + + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // static uint16_t menu_get_num_sections_callback(struct MenuLayer *menu_layer, void *callback_context) { - return 2; + return MENU_NUM_SECTIONS; } static uint16_t menu_get_num_rows_callback(struct MenuLayer *menu_layer, uint16_t section_index, void *callback_context) { - if (section_index == 1) return 1; - return subscriptions_count() ? subscriptions_count() : 1; + return menu_item_count(); } static int16_t menu_get_header_height_callback(struct MenuLayer *menu_layer, uint16_t section_index, void *callback_context) { - return MENU_CELL_BASIC_HEADER_HEIGHT; + return 0; } static int16_t menu_get_cell_height_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context) { - if (cell_index->section == 1) return MENU_CELL_BASIC_CELL_HEIGHT; + if (cell_index->row == menu_item_settings_pos()) return MENU_CELL_BASIC_CELL_HEIGHT; if (subscriptions_get_error()) { - return graphics_text_layout_get_content_size(subscriptions_get_error(), fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), GRect(4, 2, 136, 128), GTextOverflowModeFill, GTextAlignmentLeft).h + 12; + GRect bounds = layer_get_bounds(menu_layer_get_layer(menu_layer)); + GRECT_EDGE_TRIM (bounds, TEXT_BORDER_INSET) + return graphics_text_layout_get_content_size(subscriptions_get_error(), fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), bounds, GTextOverflowModeFill, GTextAlignmentLeft).h + 12; } return MENU_CELL_BASIC_CELL_HEIGHT; } static void menu_draw_header_callback(GContext *ctx, const Layer *cell_layer, uint16_t section_index, void *callback_context) { - switch (section_index) { - case 0: - menu_cell_basic_header_draw(ctx, cell_layer, "Readebble"); - break; - case 1: - menu_cell_basic_header_draw(ctx, cell_layer, "Other"); - break; - } + // Do nothing } static void menu_draw_row_callback(GContext *ctx, const Layer *cell_layer, MenuIndex *cell_index, void *callback_context) { - if (cell_index->section == 1) { + if (cell_index->row == menu_item_settings_pos()) { +#ifdef PBL_COLOR + graphics_context_set_text_color(ctx, (menu_cell_layer_is_highlighted(cell_layer) ? settings_get_colors()->settings_inverted_text : settings_get_colors()->settings_text)); +#endif menu_cell_basic_draw(ctx, cell_layer, "Settings", NULL, NULL); } else if (subscriptions_get_error()) { - graphics_context_set_text_color(ctx, GColorBlack); - graphics_draw_text(ctx, subscriptions_get_error(), fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), GRect(4, 2, 136, 128), GTextOverflowModeFill, GTextAlignmentLeft, NULL); + GRect bounds = layer_get_bounds(cell_layer); + GRECT_EDGE_TRIM (bounds, TEXT_BORDER_INSET) + + graphics_context_set_text_color(ctx, (menu_cell_layer_is_highlighted(cell_layer) ? settings_get_colors()->inverted_text : settings_get_colors()->main_text)); + + graphics_draw_text(ctx, subscriptions_get_error(), fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), bounds, GTextOverflowModeFill, PBL_IF_ROUND_ELSE (GTextAlignmentCenter, GTextAlignmentLeft), NULL); } else { menu_cell_basic_draw(ctx, cell_layer, subscriptions_get(cell_index->row)->title, NULL, NULL); } } static void menu_select_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context) { - if (cell_index->section == 1) { + if (cell_index->row == menu_item_settings_pos()) { return win_settings_push(); } if (!subscriptions_count()) return; @@ -96,22 +133,65 @@ static void menu_select_long_callback(struct MenuLayer *menu_layer, MenuIndex *c rss_request_subscriptions(); } +static void window_appear(Window *window) { + window_set_colors(window); +} + static void window_load(Window *window) { + Layer *window_layer = window_get_root_layer(window); +#ifdef PBL_ROUND + GRect bounds = layer_get_bounds(window_layer); +#endif + menu_layer = menu_layer_create_fullscreen(window); menu_layer_set_callbacks(menu_layer, NULL, (MenuLayerCallbacks) { .get_num_sections = menu_get_num_sections_callback, .get_num_rows = menu_get_num_rows_callback, .get_header_height = menu_get_header_height_callback, - .get_cell_height = menu_get_cell_height_callback, .draw_header = menu_draw_header_callback, + .get_cell_height = menu_get_cell_height_callback, .draw_row = menu_draw_row_callback, .select_click = menu_select_callback, .select_long_click = menu_select_long_callback, }); menu_layer_set_click_config_onto_window(menu_layer, window); menu_layer_add_to_window(menu_layer, window); + + // Set up the status bar if it's needed + s_status_bar = status_bar_layer_create(); + layer_add_child(window_layer, status_bar_layer_get_layer(s_status_bar)); + +#ifdef PBL_ROUND + // Set up the lower message bar + const GEdgeInsets message_insets = {.top = bounds.size.h - STATUS_BAR_LAYER_HEIGHT}; + s_lower_bar = text_layer_create(grect_inset(bounds, message_insets)); + text_layer_set_text_alignment(s_lower_bar, GTextAlignmentCenter); + text_layer_set_text(s_lower_bar, ""); + layer_add_child(window_layer, text_layer_get_layer(s_lower_bar)); +#else + GRect menu_bounds = layer_get_frame(menu_layer_get_layer(menu_layer)); + menu_bounds.origin.y += STATUS_BAR_LAYER_HEIGHT; + menu_bounds.size.h -= STATUS_BAR_LAYER_HEIGHT; + scroll_layer_set_frame(menu_layer_get_scroll_layer(menu_layer), menu_bounds); +#endif } static void window_unload(Window *window) { menu_layer_destroy_safe(menu_layer); + + // Destroy the status bar if there is one + status_bar_layer_destroy(s_status_bar); +#ifdef PBL_ROUND + text_layer_destroy(s_lower_bar); +#endif } + +static void window_set_colors(Window * window) { + status_bar_layer_set_colors(s_status_bar, settings_get_colors()->main_background, settings_get_colors()->main_text); + menu_layer_set_normal_colors(menu_layer, settings_get_colors()->main_background, settings_get_colors()->main_text); + menu_layer_set_highlight_colors(menu_layer, settings_get_colors()->selection_highlight, settings_get_colors()->inverted_text); +#ifdef PBL_ROUND + text_layer_set_colors(s_lower_bar, settings_get_colors()->main_text, settings_get_colors()->main_background); +#endif +} + diff --git a/wscript b/wscript index 257dc6a..44778f2 100644 --- a/wscript +++ b/wscript @@ -1,4 +1,4 @@ -import os +import os.path import json import shutil from sh import uglifyjs @@ -8,100 +8,126 @@ top = '.' out = 'build' def options(ctx): - ctx.load('pebble_sdk') + ctx.load('pebble_sdk') def configure(ctx): - ctx.load('pebble_sdk') + ctx.load('pebble_sdk') def distclean(ctx): - ctx.load('pebble_sdk') - for p in ['build', 'src/generated', 'src/js/src/generated', 'src/js/pebble-js-app.js']: - try: - if os.path.isfile(p): os.remove(p) - elif os.path.isdir(p): shutil.rmtree(p) - except OSError as e: - pass + ctx.load('pebble_sdk') + for p in ['build', 'src/generated', 'src/js/src/generated', 'src/js/pebble-js-app.js']: + try: + if os.path.isfile(p): os.remove(p) + elif os.path.isdir(p): shutil.rmtree(p) + except OSError as e: + pass def build(ctx): - ctx.load('pebble_sdk') + # Create a group to ensure the generated files are created first + ctx.add_group('pregenerate') - # Run jshint on appinfo.json - ctx(rule=js_jshint, source='appinfo.json') + ctx.load('pebble_sdk') - # Run jshint on all the JavaScript files - ctx(rule=js_jshint, source=ctx.path.ant_glob('src/js/src/**/*.js')) + build_worker = os.path.exists('worker_src') + binaries = [] - # Generate appinfo.h - ctx(rule=generate_appinfo_h, source='appinfo.json', target='../src/generated/appinfo.h') + # The following must be generated first + ctx.set_group('pregenerate') - # Generate keys.h - ctx(rule=generate_keys_h, source='src/keys.json', target='../src/generated/keys.h') + # Run jshint on appinfo.json + ctx(rule=js_jshint, source='appinfo.json') - # Generate appinfo.js - ctx(rule=generate_appinfo_js, source='appinfo.json', target='../src/js/src/generated/appinfo.js') + # Run jshint on all the JavaScript files + ctx(rule=js_jshint, source=ctx.path.ant_glob('src/js/src/**/*.js')) - # Generate keys.js - ctx(rule=generate_keys_js, source='src/keys.json', target='../src/js/src/generated/keys.js') + # Generate appinfo.h + ctx(rule=generate_appinfo_h, source='appinfo.json', target='../src/generated/appinfo.h') - # Combine the source JS files into a single JS file. - ctx(rule=concatenate_js, source=ctx.path.ant_glob('src/js/src/**/*.js'), target='../src/js/pebble-js-app.js') + # Generate keys.h + ctx(rule=generate_keys_h, source='src/keys.json', target='../src/generated/keys.h') - # Build and bundle the Pebble app. - ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'), target='pebble-app.elf') - ctx.pbl_bundle(elf='pebble-app.elf', js='../src/js/pebble-js-app.js') + # Generate appinfo.js + ctx(rule=generate_appinfo_js, source='appinfo.json', target='../src/js/src/generated/appinfo.js') + + # Generate keys.js + ctx(rule=generate_keys_js, source='src/keys.json', target='../src/js/src/generated/keys.js') + + # Combine the source JS files into a single JS file. + ctx(rule=concatenate_js, source=ctx.path.ant_glob('src/js/src/**/*.js'), target='../src/js/pebble-js-app.js') + + # Now the main build process can happen, building multiple platforms + for p in ctx.env.TARGET_PLATFORMS: + ctx.set_env(ctx.all_envs[p]) + ctx.set_group(ctx.env.PLATFORM_NAME) + app_elf='{}/pebble-app.elf'.format(ctx.env.BUILD_DIR) + ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'), + target=app_elf) + + if build_worker: + worker_elf='{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR) + binaries.append({'platform': p, 'app_elf': app_elf, 'worker_elf': worker_elf}) + ctx.pbl_worker(source=ctx.path.ant_glob('worker_src/**/*.c'), + target=worker_elf) + else: + binaries.append({'platform': p, 'app_elf': app_elf}) + + # Bundle everything needed into the pbw file + ctx.set_group('bundle') + ctx.pbl_bundle(binaries=binaries, js='../src/js/pebble-js-app.js') def generate_appinfo_h(task): - src = task.inputs[0].abspath() - target = task.outputs[0].abspath() - appinfo = json.load(open(src)) - f = open(target, 'w') - f.write('#pragma once\n\n') - f.write('#define DEBUG {0}\n'.format('true' if appinfo['debug'] else 'false')) - f.write('#define VERSION_LABEL "{0}"\n'.format(appinfo['versionLabel'])) - f.write('#define UUID "{0}"\n'.format(appinfo['uuid'])) - for key in appinfo['appKeys']: - f.write('#define APP_KEY_{0} {1}\n'.format(key.upper(), appinfo['appKeys'][key])) - f.close() + src = task.inputs[0].abspath() + target = task.outputs[0].abspath() + appinfo = json.load(open(src)) + f = open(target, 'w') + f.write('#pragma once\n\n') + f.write('#define DEBUG {0}\n'.format('true' if appinfo['debug'] else 'false')) + f.write('#define VERSION_LABEL "{0}"\n'.format(appinfo['versionLabel'])) + f.write('#define UUID "{0}"\n'.format(appinfo['uuid'])) + for key in appinfo['appKeys']: + f.write('#define APP_KEY_{0} {1}\n'.format(key.upper(), appinfo['appKeys'][key])) + f.close() def generate_keys_h(task): - src = task.inputs[0].abspath() - target = task.outputs[0].abspath() - keys = json.load(open(src)) - f = open(target, 'w') - f.write('#pragma once\n\n') - for key in keys: - f.write('enum {\n') - for key2 in keys[key]: - f.write('\tKEY_{0}_{1},\n'.format(key, key2)) - f.write('};\n') - f.close() + src = task.inputs[0].abspath() + target = task.outputs[0].abspath() + keys = json.load(open(src)) + f = open(target, 'w') + f.write('#pragma once\n\n') + for key in keys: + f.write('enum {\n') + for key2 in keys[key]: + f.write('\tKEY_{0}_{1},\n'.format(key, key2)) + f.write('};\n') + f.close() def generate_appinfo_js(task): - src = task.inputs[0].abspath() - target = task.outputs[0].abspath() - data = open(src).read().strip() - f = open(target, 'w') - f.write('var AppInfo = ') - f.write(data) - f.write(';') - f.close() + src = task.inputs[0].abspath() + target = task.outputs[0].abspath() + data = open(src).read().strip() + f = open(target, 'w') + f.write('var AppInfo = ') + f.write(data) + f.write(';') + f.close() def generate_keys_js(task): - src = task.inputs[0].abspath() - target = task.outputs[0].abspath() - keys = json.load(open(src)) - f = open(target, 'w') - for key in keys: - f.write('var {0} = {{\n'.format(key)) - for i, key2 in enumerate(keys[key]): - f.write('\t{0}: {1},\n'.format(key2, i)) - f.write('};\n') - f.close() + src = task.inputs[0].abspath() + target = task.outputs[0].abspath() + keys = json.load(open(src)) + f = open(target, 'w') + for key in keys: + f.write('var {0} = {{\n'.format(key)) + for i, key2 in enumerate(keys[key]): + f.write('\t{0}: {1},\n'.format(key2, i)) + f.write('};\n') + f.close() def concatenate_js(task): - inputs = (input.abspath() for input in task.inputs) - uglifyjs(*inputs, o=task.outputs[0].abspath(), b=True) + inputs = (input.abspath() for input in task.inputs) + uglifyjs(*inputs, o=task.outputs[0].abspath(), b=True) def js_jshint(task): - inputs = (input.abspath() for input in task.inputs) - jshint(*inputs) + inputs = (input.abspath() for input in task.inputs) + jshint(*inputs) +