From 85cc32c6ea22bb18689d6746b7b2d92c53ec5732 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Thu, 12 Jun 2014 13:41:37 -0700 Subject: [PATCH 01/21] Minor cleanup; don't toss unchanged icons. --- .lock-waf_darwin_build | 8 -------- appinfo.json | 2 +- src/weather_layer.c | 18 ++++++++++++++++-- src/weather_layer.h | 8 -------- 4 files changed, 17 insertions(+), 19 deletions(-) delete mode 100644 .lock-waf_darwin_build diff --git a/.lock-waf_darwin_build b/.lock-waf_darwin_build deleted file mode 100644 index 41f7705..0000000 --- a/.lock-waf_darwin_build +++ /dev/null @@ -1,8 +0,0 @@ -argv = ['/Users/Niknam/pebble-dev/PebbleSDK-2.0.0/Pebble/waf', 'configure', 'build'] -environ = {'TERM_SESSION_ID': '6085AA21-F4F4-4FEA-A5AC-F98E666E3400', 'SSH_AUTH_SOCK': '/tmp/launch-1HKDfh/Listeners', 'VERSIONER_PYTHON_PREFER_32_BIT': 'no', 'TERM_PROGRAM_VERSION': '309', 'SHELL': '/bin/bash', 'LOGNAME': 'Niknam', 'USER': 'Niknam', 'HOME': '/Users/Niknam', 'PATH': '/Users/Niknam/pebble-dev/PebbleSDK-2.0.0/arm-cs-tools/bin:/Users/Niknam/pebble-dev/PebbleSDK-2.0.0/bin:/Users/Niknam/pebble-dev/PebbleSDK-2.0-BETA7/bin:/Users/Niknam/pebble-dev/PebbleSDK-2.0-BETA6/bin:/Users/Niknam/pebble-dev/PebbleSDK-2.0-BETA4/bin:/Users/Niknam/pebble-dev/PebbleSDK-2.0-BETA3/bin:/Users/Niknam/pebble-dev/PebbleSDK-2.0-BETA2/bin:/Users/Niknam/pebble-dev/PebbleSDK-2.0-BETA1/bin:/Users/Niknam/pebble-dev/PebbleSDK-2.0-DP3/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/MacGPG2/bin:/usr/texbin', 'DISPLAY': '/tmp/launch-KCmV28/org.macosforge.xquartz:0', 'TMPDIR': '/var/folders/tc/ghbqqsc175s3ks3l8pnw3pbw0000gn/T/', 'TERM_PROGRAM': 'Apple_Terminal', 'LANG': 'en_US.UTF-8', 'TERM': 'xterm-256color', 'Apple_PubSub_Socket_Render': '/tmp/launch-xmN7D3/Render', 'VERSIONER_PYTHON_VERSION': '2.7', 'SHLVL': '2', 'SECURITYSESSIONID': '186a5', '__CF_USER_TEXT_ENCODING': '0x1F5:0:0', 'Apple_Ubiquity_Message': '/tmp/launch-oIJc9C/Apple_Ubiquity_Message', 'PWD': '/Users/Niknam/Dropbox/Backup/Pebble/futura-weather-sdk2.0', '_': '/Users/Niknam/pebble-dev/PebbleSDK-2.0.0/Pebble/waf', 'COMMAND_MODE': 'unix2003'} -files = ['/Users/Niknam/Dropbox/Backup/Pebble/futura-weather-sdk2.0/wscript'] -hash = 4672844372416070989 -options = {'files': '', 'jobs': 2, 'verbose': 0, 'nocache': False, 'progress_bar': 0, 'timestamp': None, 'distcheck_args': None, 'top': '', 'destdir': '', 'keep': 0, 'zones': '', 'debug': False, 'prefix': '/usr/local/', 'download': False, 'force': False, 'targets': '', 'out': ''} -out_dir = '/Users/Niknam/Dropbox/Backup/Pebble/futura-weather-sdk2.0/build' -run_dir = '/Users/Niknam/Dropbox/Backup/Pebble/futura-weather-sdk2.0' -top_dir = '/Users/Niknam/Dropbox/Backup/Pebble/futura-weather-sdk2.0' diff --git a/appinfo.json b/appinfo.json index dcb6779..2dfc8d5 100644 --- a/appinfo.json +++ b/appinfo.json @@ -4,7 +4,7 @@ "longName": "Futura Weather", "companyName": "Pebble Community", "versionCode": 8, - "versionLabel": "2.0.3", + "versionLabel": "2.1.0", "watchapp": { "watchface": true }, diff --git a/src/weather_layer.c b/src/weather_layer.c index 90ecbe1..a4b8e98 100644 --- a/src/weather_layer.c +++ b/src/weather_layer.c @@ -27,6 +27,15 @@ static uint8_t WEATHER_ICONS[] = { RESOURCE_ID_ICON_LOADING3, }; +typedef struct { + TextLayer *temp_layer_background; + TextLayer *temp_layer; + GBitmap *icon; + BitmapLayer *icon_layer; + char temp_str[6]; + WeatherIcon current_icon; +} WeatherLayerData; + // Keep pointers to the two fonts we use. static GFont large_font, small_font; @@ -63,16 +72,21 @@ WeatherLayer *weather_layer_create(GRect frame) void weather_layer_set_icon(WeatherLayer* weather_layer, WeatherIcon icon) { WeatherLayerData *wld = layer_get_data(weather_layer); + // Let's not waste power doing nothing of value. + if(wld->current_icon == icon) { + return; + } + GBitmap *new_icon = gbitmap_create_with_resource(WEATHER_ICONS[icon]); // Display the new bitmap bitmap_layer_set_bitmap(wld->icon_layer, new_icon); // Destroy the ex-current icon if it existed if (wld->icon != NULL) { - // A cast is needed here to get rid of the const-ness gbitmap_destroy(wld->icon); } wld->icon = new_icon; + wld->current_icon = icon; } void weather_layer_set_temperature(WeatherLayer* weather_layer, int16_t t, bool is_stale) { @@ -115,7 +129,7 @@ void weather_layer_destroy(WeatherLayer* weather_layer) { text_layer_destroy(wld->temp_layer_background); bitmap_layer_destroy(wld->icon_layer); - // Destroy the previous bitmap if we have one + // Destroy the bitmap if we have one if (wld->icon != NULL) { gbitmap_destroy(wld->icon); } diff --git a/src/weather_layer.h b/src/weather_layer.h index 0e5f3cf..cbd6295 100644 --- a/src/weather_layer.h +++ b/src/weather_layer.h @@ -1,14 +1,6 @@ #ifndef WEATHER_LAYER_H #define WEATHER_LAYER_H -typedef struct { - TextLayer *temp_layer_background; - TextLayer *temp_layer; - GBitmap *icon; - BitmapLayer *icon_layer; - char temp_str[6]; -} WeatherLayerData; - typedef enum { WEATHER_ICON_CLEAR_DAY = 0, WEATHER_ICON_CLEAR_NIGHT, From d7fa5df53db960579e85d09e0cd2e276838f8274 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Thu, 12 Jun 2014 13:49:03 -0700 Subject: [PATCH 02/21] No reason for weather_data to be a pointer. --- src/main.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main.c b/src/main.c index 3187b8e..c4aff95 100644 --- a/src/main.c +++ b/src/main.c @@ -7,10 +7,10 @@ #define TIME_FRAME (GRect(0, 2, 144, 168-6)) #define DATE_FRAME (GRect(1, 66, 144, 168-62)) -/* Keep a pointer to the current weather data as a global variable */ -static WeatherData *weather_data; +/* Keep a pointer to the current weather data as a static variable */ +static WeatherData weather_data; -/* Global variables to keep track of the UI elements */ +/* Static variables to keep track of the UI elements */ static Window *window; static TextLayer *date_layer; static TextLayer *time_layer; @@ -53,7 +53,7 @@ static void handle_tick(struct tm *tick_time, TimeUnits units_changed) // Update the bottom half of the screen: icon and temperature static int animation_step = 0; - if (weather_data->updated == 0 && weather_data->error == WEATHER_E_OK) + if (weather_data.updated == 0 && weather_data.error == WEATHER_E_OK) { // 'Animate' loading icon until the first successful weather request if (animation_step == 0) { @@ -69,22 +69,22 @@ static void handle_tick(struct tm *tick_time, TimeUnits units_changed) } else { // Update the weather icon and temperature - if (weather_data->error) { + if (weather_data.error) { weather_layer_set_icon(weather_layer, WEATHER_ICON_PHONE_ERROR); } else { // Show the temperature as 'stale' if it has not been updated in 30 minutes bool stale = false; - if (weather_data->updated > time(NULL) + 1800) { + if (weather_data.updated > time(NULL) + 1800) { stale = true; } - weather_layer_set_temperature(weather_layer, weather_data->temperature, stale); + weather_layer_set_temperature(weather_layer, weather_data.temperature, stale); // Day/night check bool night_time = false; - if (weather_data->current_time < weather_data->sunrise || weather_data->current_time > weather_data->sunset) + if (weather_data.current_time < weather_data.sunrise || weather_data.current_time > weather_data.sunset) night_time = true; - weather_layer_set_icon(weather_layer, weather_icon_for_condition(weather_data->condition, night_time)); + weather_layer_set_icon(weather_layer, weather_icon_for_condition(weather_data.condition, night_time)); } } @@ -100,8 +100,7 @@ static void init(void) { window_stack_push(window, true /* Animated */); window_set_background_color(window, GColorBlack); - weather_data = malloc(sizeof(WeatherData)); - init_network(weather_data); + init_network(&weather_data); font_date = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_18)); font_time = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_CONDENSED_53)); @@ -141,8 +140,6 @@ static void deinit(void) { fonts_unload_custom_font(font_date); fonts_unload_custom_font(font_time); - - free(weather_data); } int main(void) { From 4b65da362bd439502eed7ed4ff112436411518e2 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Thu, 12 Jun 2014 14:01:30 -0700 Subject: [PATCH 03/21] Better and more consistent variable naming. --- src/main.c | 120 ++++++++++++++++++++++---------------------- src/weather_layer.c | 18 +++---- 2 files changed, 69 insertions(+), 69 deletions(-) diff --git a/src/main.c b/src/main.c index c4aff95..3cccdbf 100644 --- a/src/main.c +++ b/src/main.c @@ -8,83 +8,83 @@ #define DATE_FRAME (GRect(1, 66, 144, 168-62)) /* Keep a pointer to the current weather data as a static variable */ -static WeatherData weather_data; +static WeatherData s_weather_data; /* Static variables to keep track of the UI elements */ -static Window *window; -static TextLayer *date_layer; -static TextLayer *time_layer; -static WeatherLayer *weather_layer; +static Window *s_window; +static TextLayer *s_date_layer; +static TextLayer *s_time_layer; +static WeatherLayer *s_weather_layer; -static char date_text[] = "XXX 00"; -static char time_text[] = "00:00"; +static char s_date_text[] = "XXX 00"; +static char s_time_text[] = "00:00"; /* Preload the fonts */ -GFont font_date; -GFont font_time; +static GFont s_font_date; +static GFont s_font_time; static void handle_tick(struct tm *tick_time, TimeUnits units_changed) { if (units_changed & MINUTE_UNIT) { // Update the time - Fix to deal with 12 / 24 centering bug - time_t currentTime = time(0); - struct tm *currentLocalTime = localtime(¤tTime); + time_t current_time = time(0); + struct tm *current_local_time = localtime(¤t_time); // Manually format the time as 12 / 24 hour, as specified - strftime( time_text, - sizeof(time_text), + strftime( s_time_text, + sizeof(s_time_text), clock_is_24h_style() ? "%R" : "%I:%M", - currentLocalTime); + current_local_time); // Drop the first char of time_text if needed - if (!clock_is_24h_style() && (time_text[0] == '0')) { - memmove(time_text, &time_text[1], sizeof(time_text) - 1); + if (!clock_is_24h_style() && (s_time_text[0] == '0')) { + memmove(s_time_text, &s_time_text[1], sizeof(s_time_text) - 1); } - text_layer_set_text(time_layer, time_text); + text_layer_set_text(s_time_layer, s_time_text); } if (units_changed & DAY_UNIT) { // Update the date - Without a leading 0 on the day of the month char day_text[4]; strftime(day_text, sizeof(day_text), "%a", tick_time); - snprintf(date_text, sizeof(date_text), "%s %i", day_text, tick_time->tm_mday); - text_layer_set_text(date_layer, date_text); + snprintf(s_date_text, sizeof(s_date_text), "%s %i", day_text, tick_time->tm_mday); + text_layer_set_text(s_date_layer, s_date_text); } // Update the bottom half of the screen: icon and temperature - static int animation_step = 0; - if (weather_data.updated == 0 && weather_data.error == WEATHER_E_OK) + static int s_animation_step = 0; + if (s_weather_data.updated == 0 && s_weather_data.error == WEATHER_E_OK) { // 'Animate' loading icon until the first successful weather request - if (animation_step == 0) { - weather_layer_set_icon(weather_layer, WEATHER_ICON_LOADING1); + if (s_animation_step == 0) { + weather_layer_set_icon(s_weather_layer, WEATHER_ICON_LOADING1); } - else if (animation_step == 1) { - weather_layer_set_icon(weather_layer, WEATHER_ICON_LOADING2); + else if (s_animation_step == 1) { + weather_layer_set_icon(s_weather_layer, WEATHER_ICON_LOADING2); } - else if (animation_step >= 2) { - weather_layer_set_icon(weather_layer, WEATHER_ICON_LOADING3); + else if (s_animation_step >= 2) { + weather_layer_set_icon(s_weather_layer, WEATHER_ICON_LOADING3); } - animation_step = (animation_step + 1) % 3; + s_animation_step = (s_animation_step + 1) % 3; } else { // Update the weather icon and temperature - if (weather_data.error) { - weather_layer_set_icon(weather_layer, WEATHER_ICON_PHONE_ERROR); + if (s_weather_data.error) { + weather_layer_set_icon(s_weather_layer, WEATHER_ICON_PHONE_ERROR); } else { // Show the temperature as 'stale' if it has not been updated in 30 minutes bool stale = false; - if (weather_data.updated > time(NULL) + 1800) { + if (s_weather_data.updated > time(NULL) + 1800) { stale = true; } - weather_layer_set_temperature(weather_layer, weather_data.temperature, stale); + weather_layer_set_temperature(s_weather_layer, s_weather_data.temperature, stale); // Day/night check bool night_time = false; - if (weather_data.current_time < weather_data.sunrise || weather_data.current_time > weather_data.sunset) + if (s_weather_data.current_time < s_weather_data.sunrise || s_weather_data.current_time > s_weather_data.sunset) night_time = true; - weather_layer_set_icon(weather_layer, weather_icon_for_condition(weather_data.condition, night_time)); + weather_layer_set_icon(s_weather_layer, weather_icon_for_condition(s_weather_data.condition, night_time)); } } @@ -96,32 +96,32 @@ static void handle_tick(struct tm *tick_time, TimeUnits units_changed) } static void init(void) { - window = window_create(); - window_stack_push(window, true /* Animated */); - window_set_background_color(window, GColorBlack); + s_window = window_create(); + window_stack_push(s_window, true /* Animated */); + window_set_background_color(s_window, GColorBlack); - init_network(&weather_data); + init_network(&s_weather_data); - font_date = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_18)); - font_time = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_CONDENSED_53)); + s_font_date = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_18)); + s_font_time = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_CONDENSED_53)); - time_layer = text_layer_create(TIME_FRAME); - text_layer_set_text_color(time_layer, GColorWhite); - text_layer_set_background_color(time_layer, GColorClear); - text_layer_set_font(time_layer, font_time); - text_layer_set_text_alignment(time_layer, GTextAlignmentCenter); - layer_add_child(window_get_root_layer(window), text_layer_get_layer(time_layer)); + s_time_layer = text_layer_create(TIME_FRAME); + text_layer_set_text_color(s_time_layer, GColorWhite); + text_layer_set_background_color(s_time_layer, GColorClear); + text_layer_set_font(s_time_layer, s_font_time); + text_layer_set_text_alignment(s_time_layer, GTextAlignmentCenter); + layer_add_child(window_get_root_layer(s_window), text_layer_get_layer(s_time_layer)); - date_layer = text_layer_create(DATE_FRAME); - text_layer_set_text_color(date_layer, GColorWhite); - text_layer_set_background_color(date_layer, GColorClear); - text_layer_set_font(date_layer, font_date); - text_layer_set_text_alignment(date_layer, GTextAlignmentCenter); - layer_add_child(window_get_root_layer(window), text_layer_get_layer(date_layer)); + s_date_layer = text_layer_create(DATE_FRAME); + text_layer_set_text_color(s_date_layer, GColorWhite); + text_layer_set_background_color(s_date_layer, GColorClear); + text_layer_set_font(s_date_layer, s_font_date); + text_layer_set_text_alignment(s_date_layer, GTextAlignmentCenter); + layer_add_child(window_get_root_layer(s_window), text_layer_get_layer(s_date_layer)); // Add weather layer - weather_layer = weather_layer_create(GRect(0, 90, 144, 80)); - layer_add_child(window_get_root_layer(window), weather_layer); + s_weather_layer = weather_layer_create(GRect(0, 90, 144, 80)); + layer_add_child(window_get_root_layer(s_window), s_weather_layer); // Update the screen right away time_t now = time(NULL); @@ -131,15 +131,15 @@ static void init(void) { } static void deinit(void) { - window_destroy(window); + window_destroy(s_window); tick_timer_service_unsubscribe(); - text_layer_destroy(time_layer); - text_layer_destroy(date_layer); - weather_layer_destroy(weather_layer); + text_layer_destroy(s_time_layer); + text_layer_destroy(s_date_layer); + weather_layer_destroy(s_weather_layer); - fonts_unload_custom_font(font_date); - fonts_unload_custom_font(font_time); + fonts_unload_custom_font(s_font_date); + fonts_unload_custom_font(s_font_time); } int main(void) { diff --git a/src/weather_layer.c b/src/weather_layer.c index a4b8e98..018fc47 100644 --- a/src/weather_layer.c +++ b/src/weather_layer.c @@ -37,7 +37,7 @@ typedef struct { } WeatherLayerData; // Keep pointers to the two fonts we use. -static GFont large_font, small_font; +static GFont s_large_font, s_small_font; WeatherLayer *weather_layer_create(GRect frame) { @@ -45,8 +45,8 @@ WeatherLayer *weather_layer_create(GRect frame) WeatherLayer *weather_layer = layer_create_with_data(frame, sizeof(WeatherLayerData)); WeatherLayerData *wld = layer_get_data(weather_layer); - large_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_40)); - small_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_35)); + s_large_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_40)); + s_small_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_35)); // Add background layer wld->temp_layer_background = text_layer_create(GRect(0, 10, 144, 68)); @@ -57,7 +57,7 @@ WeatherLayer *weather_layer_create(GRect frame) wld->temp_layer = text_layer_create(GRect(70, 19, 72, 80)); text_layer_set_background_color(wld->temp_layer, GColorClear); text_layer_set_text_alignment(wld->temp_layer, GTextAlignmentCenter); - text_layer_set_font(wld->temp_layer, large_font); + text_layer_set_font(wld->temp_layer, s_large_font); layer_add_child(weather_layer, text_layer_get_layer(wld->temp_layer)); // Add bitmap layer @@ -96,7 +96,7 @@ void weather_layer_set_temperature(WeatherLayer* weather_layer, int16_t t, bool // Temperature between -9° -> 9° or 20° -> 99° if ((t >= -9 && t <= 9) || (t >= 20 && t < 100)) { - text_layer_set_font(wld->temp_layer, large_font); + text_layer_set_font(wld->temp_layer, s_large_font); text_layer_set_text_alignment(wld->temp_layer, GTextAlignmentCenter); // Is the temperature below zero? @@ -111,12 +111,12 @@ void weather_layer_set_temperature(WeatherLayer* weather_layer, int16_t t, bool } // Temperature between 10° -> 19° else if (t >= 10 && t < 20) { - text_layer_set_font(wld->temp_layer, large_font); + text_layer_set_font(wld->temp_layer, s_large_font); text_layer_set_text_alignment(wld->temp_layer, GTextAlignmentLeft); } // Temperature above 99° or below -9° else { - text_layer_set_font(wld->temp_layer, small_font); + text_layer_set_font(wld->temp_layer, s_small_font); text_layer_set_text_alignment(wld->temp_layer, GTextAlignmentCenter); } text_layer_set_text(wld->temp_layer, wld->temp_str); @@ -135,8 +135,8 @@ void weather_layer_destroy(WeatherLayer* weather_layer) { } layer_destroy(weather_layer); - fonts_unload_custom_font(large_font); - fonts_unload_custom_font(small_font); + fonts_unload_custom_font(s_large_font); + fonts_unload_custom_font(s_small_font); } /* From 997352cfa76b39b0b9374f607b1c2a3662aa7451 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Thu, 12 Jun 2014 14:52:10 -0700 Subject: [PATCH 04/21] Simplify time display. --- src/main.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/main.c b/src/main.c index 3cccdbf..fa82a50 100644 --- a/src/main.c +++ b/src/main.c @@ -26,19 +26,11 @@ static GFont s_font_time; static void handle_tick(struct tm *tick_time, TimeUnits units_changed) { if (units_changed & MINUTE_UNIT) { - // Update the time - Fix to deal with 12 / 24 centering bug - time_t current_time = time(0); - struct tm *current_local_time = localtime(¤t_time); - - // Manually format the time as 12 / 24 hour, as specified - strftime( s_time_text, - sizeof(s_time_text), - clock_is_24h_style() ? "%R" : "%I:%M", - current_local_time); - - // Drop the first char of time_text if needed - if (!clock_is_24h_style() && (s_time_text[0] == '0')) { - memmove(s_time_text, &s_time_text[1], sizeof(s_time_text) - 1); + clock_copy_time_string(s_time_text, sizeof(s_time_text)); + // Despite Matt's assurances otherwise, the above will actually attempt to include + // an AM/PM string even if it will not fit. Truncate a trailing space if necessary. + if (s_time_text[4] == ' ') { + s_time_text[4] = '\0'; } text_layer_set_text(s_time_layer, s_time_text); From 7efbbf1d98475cbd3d2425a4e441204d21eb4a34 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Thu, 12 Jun 2014 14:55:27 -0700 Subject: [PATCH 05/21] Void all the things! --- src/network.c | 4 ++-- src/network.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/network.c b/src/network.c index 07c9202..71878a9 100644 --- a/src/network.c +++ b/src/network.c @@ -80,12 +80,12 @@ void init_network(WeatherData *weather_data) } -void close_network() +void close_network(void) { app_message_deregister_callbacks(); } -void request_weather() +void request_weather(void) { DictionaryIterator *iter; app_message_outbox_begin(&iter); diff --git a/src/network.h b/src/network.h index 6710190..ad79485 100644 --- a/src/network.h +++ b/src/network.h @@ -25,6 +25,6 @@ typedef struct { } WeatherData; void init_network(WeatherData *weather_data); -void close_network(); +void close_network(void); -void request_weather(); +void request_weather(void); From 84aaa15702e9d708d1ad582adeb98a94bb0747bf Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Thu, 12 Jun 2014 16:28:46 -0700 Subject: [PATCH 06/21] Evented weather updates. --- src/main.c | 55 ++++++++++++++++++++-------------------- src/network.c | 61 +++++++++++++++++++++++++++++---------------- src/network.h | 9 +++++-- src/weather_layer.c | 28 +++++++++++++-------- src/weather_layer.h | 3 ++- 5 files changed, 93 insertions(+), 63 deletions(-) diff --git a/src/main.c b/src/main.c index fa82a50..9438ae8 100644 --- a/src/main.c +++ b/src/main.c @@ -7,9 +7,6 @@ #define TIME_FRAME (GRect(0, 2, 144, 168-6)) #define DATE_FRAME (GRect(1, 66, 144, 168-62)) -/* Keep a pointer to the current weather data as a static variable */ -static WeatherData s_weather_data; - /* Static variables to keep track of the UI elements */ static Window *s_window; static TextLayer *s_date_layer; @@ -19,12 +16,16 @@ static WeatherLayer *s_weather_layer; static char s_date_text[] = "XXX 00"; static char s_time_text[] = "00:00"; +static time_t s_last_weather_update = 0; +static bool s_weather_loaded = false; + /* Preload the fonts */ static GFont s_font_date; static GFont s_font_time; static void handle_tick(struct tm *tick_time, TimeUnits units_changed) { + APP_LOG(APP_LOG_LEVEL_DEBUG, "Tick handler."); if (units_changed & MINUTE_UNIT) { clock_copy_time_string(s_time_text, sizeof(s_time_text)); // Despite Matt's assurances otherwise, the above will actually attempt to include @@ -43,9 +44,9 @@ static void handle_tick(struct tm *tick_time, TimeUnits units_changed) text_layer_set_text(s_date_layer, s_date_text); } - // Update the bottom half of the screen: icon and temperature + // Run the animation if we haven't loaded the weather yet. static int s_animation_step = 0; - if (s_weather_data.updated == 0 && s_weather_data.error == WEATHER_E_OK) + if (!s_weather_loaded) { // 'Animate' loading icon until the first successful weather request if (s_animation_step == 0) { @@ -58,41 +59,41 @@ static void handle_tick(struct tm *tick_time, TimeUnits units_changed) weather_layer_set_icon(s_weather_layer, WEATHER_ICON_LOADING3); } s_animation_step = (s_animation_step + 1) % 3; - } - else { - // Update the weather icon and temperature - if (s_weather_data.error) { - weather_layer_set_icon(s_weather_layer, WEATHER_ICON_PHONE_ERROR); - } - else { - // Show the temperature as 'stale' if it has not been updated in 30 minutes - bool stale = false; - if (s_weather_data.updated > time(NULL) + 1800) { - stale = true; - } - weather_layer_set_temperature(s_weather_layer, s_weather_data.temperature, stale); - - // Day/night check - bool night_time = false; - if (s_weather_data.current_time < s_weather_data.sunrise || s_weather_data.current_time > s_weather_data.sunset) - night_time = true; - weather_layer_set_icon(s_weather_layer, weather_icon_for_condition(s_weather_data.condition, night_time)); + } else { + // Show the temperature as 'stale' if it has not been updated in 30 minutes + bool stale = (s_last_weather_update > time(NULL) + 1800); + if (stale) { + weather_layer_mark_stale(s_weather_layer); } } // Refresh the weather info every 15 minutes - if (units_changed & MINUTE_UNIT && (tick_time->tm_min % 15) == 0) - { + if (units_changed & MINUTE_UNIT && (tick_time->tm_min % 15) == 0) { request_weather(); } } +static void handle_weather_update(WeatherData* weather) { + s_last_weather_update = time(NULL); + weather_layer_set_temperature(s_weather_layer, weather->temperature); + + const bool is_night = (weather->current_time < weather->sunrise || weather->current_time > weather->sunset); + weather_layer_set_icon(s_weather_layer, weather_icon_for_condition(weather->condition, is_night)); + + if(!s_weather_loaded) { + s_weather_loaded = true; + // We don't need to run this every second any more. + tick_timer_service_subscribe(MINUTE_UNIT, handle_tick); + } +} + static void init(void) { s_window = window_create(); window_stack_push(s_window, true /* Animated */); window_set_background_color(s_window, GColorBlack); - init_network(&s_weather_data); + init_network(); + set_weather_update_handler(handle_weather_update); s_font_date = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_18)); s_font_time = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_CONDENSED_53)); diff --git a/src/network.c b/src/network.c index 71878a9..1701bc1 100644 --- a/src/network.c +++ b/src/network.c @@ -1,10 +1,13 @@ #include #include "network.h" +static WeatherUpdateHandler s_weather_update_handler = NULL; +static WeatherErrorHandler s_weather_error_handler = NULL; + static void appmsg_in_received(DictionaryIterator *received, void *context) { APP_LOG(APP_LOG_LEVEL_DEBUG, "In received."); - WeatherData *weather = (WeatherData*) context; + WeatherData weather; Tuple *temperature_tuple = dict_find(received, KEY_TEMPERATURE); Tuple *condition_tuple = dict_find(received, KEY_CONDITION); @@ -14,23 +17,30 @@ static void appmsg_in_received(DictionaryIterator *received, void *context) { Tuple *error_tuple = dict_find(received, KEY_ERROR); if (temperature_tuple && condition_tuple) { - weather->temperature = temperature_tuple->value->int32; - weather->condition = condition_tuple->value->int32; - weather->sunrise = sunrise_tuple->value->int32; - weather->sunset = sunset_tuple->value->int32; - weather->current_time = current_time_tuple->value->int32; - weather->error = WEATHER_E_OK; - weather->updated = time(NULL); - APP_LOG(APP_LOG_LEVEL_DEBUG, "Got temperature %i and condition %i", weather->temperature, weather->condition); + weather.temperature = temperature_tuple->value->int32; + weather.condition = condition_tuple->value->int32; + weather.sunrise = sunrise_tuple->value->int32; + weather.sunset = sunset_tuple->value->int32; + weather.current_time = current_time_tuple->value->int32; + weather.updated = time(NULL); + APP_LOG(APP_LOG_LEVEL_DEBUG, "Got temperature %i and condition %i", weather.temperature, weather.condition); + + if(s_weather_update_handler) { + s_weather_update_handler(&weather); + } } else if (error_tuple) { - weather->error = WEATHER_E_NETWORK; APP_LOG(APP_LOG_LEVEL_DEBUG, "Got error %s", error_tuple->value->cstring); + if(s_weather_error_handler) { + s_weather_error_handler(WEATHER_E_NETWORK); + } } else { - weather->error = WEATHER_E_PHONE; APP_LOG(APP_LOG_LEVEL_DEBUG, "Got message with unknown keys... temperature=%p condition=%p error=%p", temperature_tuple, condition_tuple, error_tuple); + if(s_weather_error_handler) { + s_weather_error_handler(WEATHER_E_PHONE); + } } } @@ -45,39 +55,38 @@ static void appmsg_out_sent(DictionaryIterator *sent, void *context) { } static void appmsg_out_failed(DictionaryIterator *failed, AppMessageResult reason, void *context) { - WeatherData *weather = (WeatherData*) context; - APP_LOG(APP_LOG_LEVEL_DEBUG, "Out failed: %i", reason); + WeatherError error; + switch (reason) { case APP_MSG_NOT_CONNECTED: - weather->error = WEATHER_E_DISCONNECTED; + error = WEATHER_E_DISCONNECTED; request_weather(); break; case APP_MSG_SEND_REJECTED: case APP_MSG_SEND_TIMEOUT: - weather->error = WEATHER_E_PHONE; + error = WEATHER_E_PHONE; request_weather(); break; default: - weather->error = WEATHER_E_PHONE; + error = WEATHER_E_PHONE; request_weather(); break; } + + if(s_weather_error_handler) { + s_weather_error_handler(error); + } } -void init_network(WeatherData *weather_data) +void init_network(void) { app_message_register_inbox_received(appmsg_in_received); app_message_register_inbox_dropped(appmsg_in_dropped); app_message_register_outbox_sent(appmsg_out_sent); app_message_register_outbox_failed(appmsg_out_failed); - app_message_set_context(weather_data); app_message_open(124, 256); - - weather_data->error = WEATHER_E_OK; - weather_data->updated = 0; - } void close_network(void) @@ -94,3 +103,11 @@ void request_weather(void) app_message_outbox_send(); } + +void set_weather_update_handler(WeatherUpdateHandler handler) { + s_weather_update_handler = handler; +} + +void set_weather_error_handler(WeatherErrorHandler handler) { + s_weather_error_handler = handler; +} diff --git a/src/network.h b/src/network.h index ad79485..ddf7d02 100644 --- a/src/network.h +++ b/src/network.h @@ -21,10 +21,15 @@ typedef struct { int sunset; int current_time; time_t updated; - WeatherError error; } WeatherData; -void init_network(WeatherData *weather_data); +typedef void (*WeatherUpdateHandler)(WeatherData*); +typedef void (*WeatherErrorHandler)(WeatherError); + +void init_network(void); void close_network(void); void request_weather(void); + +void set_weather_update_handler(WeatherUpdateHandler handler); +void set_weather_error_handler(WeatherErrorHandler handler); diff --git a/src/weather_layer.c b/src/weather_layer.c index 018fc47..36b27e5 100644 --- a/src/weather_layer.c +++ b/src/weather_layer.c @@ -89,25 +89,25 @@ void weather_layer_set_icon(WeatherLayer* weather_layer, WeatherIcon icon) { wld->current_icon = icon; } -void weather_layer_set_temperature(WeatherLayer* weather_layer, int16_t t, bool is_stale) { +void weather_layer_set_temperature(WeatherLayer* weather_layer, int16_t t) { WeatherLayerData *wld = layer_get_data(weather_layer); - snprintf(wld->temp_str, sizeof(wld->temp_str), "%i%s", t, is_stale ? " " : "°"); + snprintf(wld->temp_str, sizeof(wld->temp_str), "%i", t); // Temperature between -9° -> 9° or 20° -> 99° if ((t >= -9 && t <= 9) || (t >= 20 && t < 100)) { text_layer_set_font(wld->temp_layer, s_large_font); text_layer_set_text_alignment(wld->temp_layer, GTextAlignmentCenter); - // Is the temperature below zero? - if (wld->temp_str[0] == '-') { - memmove( - wld->temp_str + 1 + 1, - wld->temp_str + 1, - 5 - (1 + 1) - ); - memcpy(&wld->temp_str[1], " ", 1); - } + // Is the temperature below zero? + if (wld->temp_str[0] == '-') { + memmove( + wld->temp_str + 1 + 1, + wld->temp_str + 1, + 5 - (1 + 1) + ); + memcpy(&wld->temp_str[1], " ", 1); + } } // Temperature between 10° -> 19° else if (t >= 10 && t < 20) { @@ -122,6 +122,12 @@ void weather_layer_set_temperature(WeatherLayer* weather_layer, int16_t t, bool text_layer_set_text(wld->temp_layer, wld->temp_str); } +void weather_layer_mark_stale(WeatherLayer* weather_layer) { + WeatherLayerData *wld = layer_get_data(weather_layer); + + // Don't do anything here for now. +} + void weather_layer_destroy(WeatherLayer* weather_layer) { WeatherLayerData *wld = layer_get_data(weather_layer); diff --git a/src/weather_layer.h b/src/weather_layer.h index cbd6295..11d47f1 100644 --- a/src/weather_layer.h +++ b/src/weather_layer.h @@ -33,7 +33,8 @@ typedef Layer WeatherLayer; WeatherLayer *weather_layer_create(GRect frame); void weather_layer_destroy(WeatherLayer* weather_layer); void weather_layer_set_icon(WeatherLayer* weather_layer, WeatherIcon icon); -void weather_layer_set_temperature(WeatherLayer* weather_layer, int16_t temperature, bool is_stale); +void weather_layer_set_temperature(WeatherLayer* weather_layer, int16_t temperature); +void weather_layer_mark_stale(WeatherLayer* weather_layer); uint8_t weather_icon_for_condition(int condition, bool night_time); #endif From 74433ebbe12571c45da0f84929d62c7825515ff8 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 13 Jun 2014 01:56:32 -0700 Subject: [PATCH 07/21] Error handling that doesn't eat 100% CPU. --- src/js/pebble-js-app.js | 1 - src/main.c | 29 ++++++++++++++++++++++------- src/network.c | 8 +++----- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/js/pebble-js-app.js b/src/js/pebble-js-app.js index d6767b0..814d567 100644 --- a/src/js/pebble-js-app.js +++ b/src/js/pebble-js-app.js @@ -1,6 +1,5 @@ Pebble.addEventListener("ready", function(e) { console.log("Starting ..."); - updateWeather(); }); Pebble.addEventListener("appmessage", function(e) { diff --git a/src/main.c b/src/main.c index 9438ae8..59df835 100644 --- a/src/main.c +++ b/src/main.c @@ -68,23 +68,34 @@ static void handle_tick(struct tm *tick_time, TimeUnits units_changed) } // Refresh the weather info every 15 minutes - if (units_changed & MINUTE_UNIT && (tick_time->tm_min % 15) == 0) { + if ((units_changed & MINUTE_UNIT) && (tick_time->tm_min % 15 == 0)) { request_weather(); } } +static void mark_weather_loaded(void) { + if(!s_weather_loaded) { + s_weather_loaded = true; + // We don't need to run this every second any more. + tick_timer_service_subscribe(MINUTE_UNIT, handle_tick); + } +} + static void handle_weather_update(WeatherData* weather) { s_last_weather_update = time(NULL); weather_layer_set_temperature(s_weather_layer, weather->temperature); const bool is_night = (weather->current_time < weather->sunrise || weather->current_time > weather->sunset); weather_layer_set_icon(s_weather_layer, weather_icon_for_condition(weather->condition, is_night)); - - if(!s_weather_loaded) { - s_weather_loaded = true; - // We don't need to run this every second any more. - tick_timer_service_subscribe(MINUTE_UNIT, handle_tick); - } + + mark_weather_loaded(); +} + +static void handle_weather_error(WeatherError error) { + // We apparently don't actually care what the error was at all. + weather_layer_set_icon(s_weather_layer, WEATHER_ICON_PHONE_ERROR); + + mark_weather_loaded(); } static void init(void) { @@ -94,6 +105,7 @@ static void init(void) { init_network(); set_weather_update_handler(handle_weather_update); + set_weather_error_handler(handle_weather_error); s_font_date = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_18)); s_font_time = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FUTURA_CONDENSED_53)); @@ -121,6 +133,9 @@ static void init(void) { handle_tick(localtime(&now), SECOND_UNIT | MINUTE_UNIT | HOUR_UNIT | DAY_UNIT ); // And then every second tick_timer_service_subscribe(SECOND_UNIT, handle_tick); + + // Get the weather. + request_weather(); } static void deinit(void) { diff --git a/src/network.c b/src/network.c index 1701bc1..810b0d2 100644 --- a/src/network.c +++ b/src/network.c @@ -59,19 +59,17 @@ static void appmsg_out_failed(DictionaryIterator *failed, AppMessageResult reaso WeatherError error; + // It's not clear that there's actually any value in retry logic here. + // Most of these are not transient failures. + switch (reason) { case APP_MSG_NOT_CONNECTED: error = WEATHER_E_DISCONNECTED; - request_weather(); break; case APP_MSG_SEND_REJECTED: case APP_MSG_SEND_TIMEOUT: - error = WEATHER_E_PHONE; - request_weather(); - break; default: error = WEATHER_E_PHONE; - request_weather(); break; } From 644282a810f993495f3b7c7f3c61b47ac0641b8a Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 13 Jun 2014 02:00:22 -0700 Subject: [PATCH 08/21] Restored the degrees symbol. --- src/weather_layer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/weather_layer.c b/src/weather_layer.c index 36b27e5..4c0f222 100644 --- a/src/weather_layer.c +++ b/src/weather_layer.c @@ -92,7 +92,7 @@ void weather_layer_set_icon(WeatherLayer* weather_layer, WeatherIcon icon) { void weather_layer_set_temperature(WeatherLayer* weather_layer, int16_t t) { WeatherLayerData *wld = layer_get_data(weather_layer); - snprintf(wld->temp_str, sizeof(wld->temp_str), "%i", t); + snprintf(wld->temp_str, sizeof(wld->temp_str), "%i°", t); // Temperature between -9° -> 9° or 20° -> 99° if ((t >= -9 && t <= 9) || (t >= 20 && t < 100)) { From c7c3a2068933f1b94d2aa7dd84da56bbccf97f27 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 13 Jun 2014 02:25:34 -0700 Subject: [PATCH 09/21] Trigger the load from JS, and add loading animation timeout. --- src/js/pebble-js-app.js | 1 + src/main.c | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/js/pebble-js-app.js b/src/js/pebble-js-app.js index 814d567..d6767b0 100644 --- a/src/js/pebble-js-app.js +++ b/src/js/pebble-js-app.js @@ -1,5 +1,6 @@ Pebble.addEventListener("ready", function(e) { console.log("Starting ..."); + updateWeather(); }); Pebble.addEventListener("appmessage", function(e) { diff --git a/src/main.c b/src/main.c index 59df835..9c73d30 100644 --- a/src/main.c +++ b/src/main.c @@ -6,6 +6,7 @@ #define TIME_FRAME (GRect(0, 2, 144, 168-6)) #define DATE_FRAME (GRect(1, 66, 144, 168-62)) +#define LOADING_TIMEOUT 30000 /* Static variables to keep track of the UI elements */ static Window *s_window; @@ -19,6 +20,8 @@ static char s_time_text[] = "00:00"; static time_t s_last_weather_update = 0; static bool s_weather_loaded = false; +static AppTimer *s_loading_timeout = NULL; + /* Preload the fonts */ static GFont s_font_date; static GFont s_font_time; @@ -79,6 +82,10 @@ static void mark_weather_loaded(void) { // We don't need to run this every second any more. tick_timer_service_subscribe(MINUTE_UNIT, handle_tick); } + if(s_loading_timeout != NULL) { + app_timer_cancel(s_loading_timeout); + s_loading_timeout = NULL; + } } static void handle_weather_update(WeatherData* weather) { @@ -98,6 +105,13 @@ static void handle_weather_error(WeatherError error) { mark_weather_loaded(); } +static void handle_loading_timeout(void* unused) { + s_loading_timeout = NULL; + if(!s_weather_loaded) { + handle_weather_error(WEATHER_E_PHONE); + } +} + static void init(void) { s_window = window_create(); window_stack_push(s_window, true /* Animated */); @@ -134,8 +148,10 @@ static void init(void) { // And then every second tick_timer_service_subscribe(SECOND_UNIT, handle_tick); - // Get the weather. - request_weather(); + // We don't trigger the weather from here, because it is possible for the other end + // to not yet be running when we finish startup. However, we do set a timeout so we + // don't sit around animating forever. + s_loading_timeout = app_timer_register(LOADING_TIMEOUT, handle_loading_timeout, NULL); } static void deinit(void) { From 41be4f81a371970e1c7b6c9ff7b285a33a7cab56 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 13 Jun 2014 02:53:33 -0700 Subject: [PATCH 10/21] Handle bluetooth (dis)connection properly. --- src/network.c | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/network.c b/src/network.c index 810b0d2..198a907 100644 --- a/src/network.c +++ b/src/network.c @@ -3,6 +3,14 @@ static WeatherUpdateHandler s_weather_update_handler = NULL; static WeatherErrorHandler s_weather_error_handler = NULL; +static bool s_is_connected = false; // because of BLE confusion. + + +static void report_error(WeatherError error) { + if(s_weather_error_handler) { + s_weather_error_handler(error); + } +} static void appmsg_in_received(DictionaryIterator *received, void *context) { APP_LOG(APP_LOG_LEVEL_DEBUG, "In received."); @@ -38,22 +46,20 @@ static void appmsg_in_received(DictionaryIterator *received, void *context) { else { APP_LOG(APP_LOG_LEVEL_DEBUG, "Got message with unknown keys... temperature=%p condition=%p error=%p", temperature_tuple, condition_tuple, error_tuple); - if(s_weather_error_handler) { - s_weather_error_handler(WEATHER_E_PHONE); - } + report_error(WEATHER_E_PHONE); } } +static void appmsg_out_sent(DictionaryIterator *sent, void *context) { + APP_LOG(APP_LOG_LEVEL_DEBUG, "Out sent."); +} + static void appmsg_in_dropped(AppMessageResult reason, void *context) { APP_LOG(APP_LOG_LEVEL_DEBUG, "In dropped: %i", reason); // Request a new update... request_weather(); } -static void appmsg_out_sent(DictionaryIterator *sent, void *context) { - APP_LOG(APP_LOG_LEVEL_DEBUG, "Out sent."); -} - static void appmsg_out_failed(DictionaryIterator *failed, AppMessageResult reason, void *context) { APP_LOG(APP_LOG_LEVEL_DEBUG, "Out failed: %i", reason); @@ -73,8 +79,19 @@ static void appmsg_out_failed(DictionaryIterator *failed, AppMessageResult reaso break; } - if(s_weather_error_handler) { - s_weather_error_handler(error); + report_error(error); +} + +static void bluetooth_connection_service_handler(bool connected) { + if(connected == s_is_connected) { + return; + } + s_is_connected = connected; + + if(connected) { + request_weather(); + } else { + report_error(WEATHER_E_DISCONNECTED); } } @@ -84,12 +101,16 @@ void init_network(void) app_message_register_inbox_dropped(appmsg_in_dropped); app_message_register_outbox_sent(appmsg_out_sent); app_message_register_outbox_failed(appmsg_out_failed); + bluetooth_connection_service_subscribe(bluetooth_connection_service_handler); app_message_open(124, 256); + + s_is_connected = bluetooth_connection_service_peek(); } void close_network(void) { app_message_deregister_callbacks(); + bluetooth_connection_service_unsubscribe(); } void request_weather(void) From 241894b506150099161d0fe1537a88499fe9cff9 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 13 Jun 2014 02:57:04 -0700 Subject: [PATCH 11/21] Formatting/comment/logging cleanup. --- src/main.c | 5 ++--- src/network.c | 15 +++++---------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/main.c b/src/main.c index 9c73d30..16b62c0 100644 --- a/src/main.c +++ b/src/main.c @@ -28,11 +28,10 @@ static GFont s_font_time; static void handle_tick(struct tm *tick_time, TimeUnits units_changed) { - APP_LOG(APP_LOG_LEVEL_DEBUG, "Tick handler."); if (units_changed & MINUTE_UNIT) { clock_copy_time_string(s_time_text, sizeof(s_time_text)); - // Despite Matt's assurances otherwise, the above will actually attempt to include - // an AM/PM string even if it will not fit. Truncate a trailing space if necessary. + // clock_copy_time_string may including a trailing space as it tries to fit in + // the AM/PM in 24-hour time; truncate it if so. if (s_time_text[4] == ' ') { s_time_text[4] = '\0'; } diff --git a/src/network.c b/src/network.c index 198a907..d5b1298 100644 --- a/src/network.c +++ b/src/network.c @@ -36,14 +36,12 @@ static void appmsg_in_received(DictionaryIterator *received, void *context) { if(s_weather_update_handler) { s_weather_update_handler(&weather); } - } - else if (error_tuple) { + } else if (error_tuple) { APP_LOG(APP_LOG_LEVEL_DEBUG, "Got error %s", error_tuple->value->cstring); if(s_weather_error_handler) { s_weather_error_handler(WEATHER_E_NETWORK); } - } - else { + } else { APP_LOG(APP_LOG_LEVEL_DEBUG, "Got message with unknown keys... temperature=%p condition=%p error=%p", temperature_tuple, condition_tuple, error_tuple); report_error(WEATHER_E_PHONE); @@ -95,8 +93,7 @@ static void bluetooth_connection_service_handler(bool connected) { } } -void init_network(void) -{ +void init_network(void) { app_message_register_inbox_received(appmsg_in_received); app_message_register_inbox_dropped(appmsg_in_dropped); app_message_register_outbox_sent(appmsg_out_sent); @@ -107,14 +104,12 @@ void init_network(void) s_is_connected = bluetooth_connection_service_peek(); } -void close_network(void) -{ +void close_network(void) { app_message_deregister_callbacks(); bluetooth_connection_service_unsubscribe(); } -void request_weather(void) -{ +void request_weather(void) { DictionaryIterator *iter; app_message_outbox_begin(&iter); From a55e49d9fa6c7794046eba628a4a4b750f47f683 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 13 Jun 2014 02:57:55 -0700 Subject: [PATCH 12/21] Add waf junk to .gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ed86553..27d8c45 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store build/ +.lock-waf_darwin_build From 9378578619a77cb54ac171d8e309525b1b6a9368 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 13 Jun 2014 03:03:11 -0700 Subject: [PATCH 13/21] Pull loading animation out of tick handler. --- src/main.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/main.c b/src/main.c index 16b62c0..deace5c 100644 --- a/src/main.c +++ b/src/main.c @@ -26,6 +26,21 @@ static AppTimer *s_loading_timeout = NULL; static GFont s_font_date; static GFont s_font_time; +static void step_loading_animation() { + static int s_animation_step = 0; + // 'Animate' loading icon until the first successful weather request + if (s_animation_step == 0) { + weather_layer_set_icon(s_weather_layer, WEATHER_ICON_LOADING1); + } + else if (s_animation_step == 1) { + weather_layer_set_icon(s_weather_layer, WEATHER_ICON_LOADING2); + } + else if (s_animation_step >= 2) { + weather_layer_set_icon(s_weather_layer, WEATHER_ICON_LOADING3); + } + s_animation_step = (s_animation_step + 1) % 3; +} + static void handle_tick(struct tm *tick_time, TimeUnits units_changed) { if (units_changed & MINUTE_UNIT) { @@ -47,26 +62,14 @@ static void handle_tick(struct tm *tick_time, TimeUnits units_changed) } // Run the animation if we haven't loaded the weather yet. - static int s_animation_step = 0; - if (!s_weather_loaded) - { - // 'Animate' loading icon until the first successful weather request - if (s_animation_step == 0) { - weather_layer_set_icon(s_weather_layer, WEATHER_ICON_LOADING1); - } - else if (s_animation_step == 1) { - weather_layer_set_icon(s_weather_layer, WEATHER_ICON_LOADING2); - } - else if (s_animation_step >= 2) { - weather_layer_set_icon(s_weather_layer, WEATHER_ICON_LOADING3); - } - s_animation_step = (s_animation_step + 1) % 3; - } else { + if (s_weather_loaded) { // Show the temperature as 'stale' if it has not been updated in 30 minutes bool stale = (s_last_weather_update > time(NULL) + 1800); if (stale) { weather_layer_mark_stale(s_weather_layer); } + } else { + step_loading_animation(); } // Refresh the weather info every 15 minutes From 7d931af4eee860ed4e3e3917ea5bbd8ed0925901 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 13 Jun 2014 03:05:12 -0700 Subject: [PATCH 14/21] Removed redundant stale logic (always shows an error anyway) --- src/main.c | 10 +--------- src/weather_layer.c | 6 ------ src/weather_layer.h | 1 - 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/main.c b/src/main.c index deace5c..e9d01f4 100644 --- a/src/main.c +++ b/src/main.c @@ -17,7 +17,6 @@ static WeatherLayer *s_weather_layer; static char s_date_text[] = "XXX 00"; static char s_time_text[] = "00:00"; -static time_t s_last_weather_update = 0; static bool s_weather_loaded = false; static AppTimer *s_loading_timeout = NULL; @@ -62,13 +61,7 @@ static void handle_tick(struct tm *tick_time, TimeUnits units_changed) } // Run the animation if we haven't loaded the weather yet. - if (s_weather_loaded) { - // Show the temperature as 'stale' if it has not been updated in 30 minutes - bool stale = (s_last_weather_update > time(NULL) + 1800); - if (stale) { - weather_layer_mark_stale(s_weather_layer); - } - } else { + if (!s_weather_loaded) { step_loading_animation(); } @@ -91,7 +84,6 @@ static void mark_weather_loaded(void) { } static void handle_weather_update(WeatherData* weather) { - s_last_weather_update = time(NULL); weather_layer_set_temperature(s_weather_layer, weather->temperature); const bool is_night = (weather->current_time < weather->sunrise || weather->current_time > weather->sunset); diff --git a/src/weather_layer.c b/src/weather_layer.c index 4c0f222..15269c8 100644 --- a/src/weather_layer.c +++ b/src/weather_layer.c @@ -122,12 +122,6 @@ void weather_layer_set_temperature(WeatherLayer* weather_layer, int16_t t) { text_layer_set_text(wld->temp_layer, wld->temp_str); } -void weather_layer_mark_stale(WeatherLayer* weather_layer) { - WeatherLayerData *wld = layer_get_data(weather_layer); - - // Don't do anything here for now. -} - void weather_layer_destroy(WeatherLayer* weather_layer) { WeatherLayerData *wld = layer_get_data(weather_layer); diff --git a/src/weather_layer.h b/src/weather_layer.h index 11d47f1..5731ef4 100644 --- a/src/weather_layer.h +++ b/src/weather_layer.h @@ -34,7 +34,6 @@ WeatherLayer *weather_layer_create(GRect frame); void weather_layer_destroy(WeatherLayer* weather_layer); void weather_layer_set_icon(WeatherLayer* weather_layer, WeatherIcon icon); void weather_layer_set_temperature(WeatherLayer* weather_layer, int16_t temperature); -void weather_layer_mark_stale(WeatherLayer* weather_layer); uint8_t weather_icon_for_condition(int condition, bool night_time); #endif From 2c89c0781d311ae86daa4b53249681ff05d0b462 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 13 Jun 2014 03:07:16 -0700 Subject: [PATCH 15/21] Space after 'if'. --- src/js/pebble-js-app.js | 2 +- src/network.c | 10 +++++----- src/weather_layer.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/js/pebble-js-app.js b/src/js/pebble-js-app.js index d6767b0..54ed075 100644 --- a/src/js/pebble-js-app.js +++ b/src/js/pebble-js-app.js @@ -40,7 +40,7 @@ function fetchWeather(latitude, longitude) { "lat=" + latitude + "&lon=" + longitude + "&cnt=1", true); req.onload = function(e) { if (req.readyState == 4) { - if(req.status == 200) { + if (req.status == 200) { console.log(req.responseText); response = JSON.parse(req.responseText); var temperature, icon, city, sunrise, sunset, condition; diff --git a/src/network.c b/src/network.c index d5b1298..b26eb41 100644 --- a/src/network.c +++ b/src/network.c @@ -7,7 +7,7 @@ static bool s_is_connected = false; // because of BLE confusion. static void report_error(WeatherError error) { - if(s_weather_error_handler) { + if (s_weather_error_handler) { s_weather_error_handler(error); } } @@ -33,12 +33,12 @@ static void appmsg_in_received(DictionaryIterator *received, void *context) { weather.updated = time(NULL); APP_LOG(APP_LOG_LEVEL_DEBUG, "Got temperature %i and condition %i", weather.temperature, weather.condition); - if(s_weather_update_handler) { + if (s_weather_update_handler) { s_weather_update_handler(&weather); } } else if (error_tuple) { APP_LOG(APP_LOG_LEVEL_DEBUG, "Got error %s", error_tuple->value->cstring); - if(s_weather_error_handler) { + if (s_weather_error_handler) { s_weather_error_handler(WEATHER_E_NETWORK); } } else { @@ -81,12 +81,12 @@ static void appmsg_out_failed(DictionaryIterator *failed, AppMessageResult reaso } static void bluetooth_connection_service_handler(bool connected) { - if(connected == s_is_connected) { + if (connected == s_is_connected) { return; } s_is_connected = connected; - if(connected) { + if (connected) { request_weather(); } else { report_error(WEATHER_E_DISCONNECTED); diff --git a/src/weather_layer.c b/src/weather_layer.c index 15269c8..2eaa7ff 100644 --- a/src/weather_layer.c +++ b/src/weather_layer.c @@ -73,7 +73,7 @@ void weather_layer_set_icon(WeatherLayer* weather_layer, WeatherIcon icon) { WeatherLayerData *wld = layer_get_data(weather_layer); // Let's not waste power doing nothing of value. - if(wld->current_icon == icon) { + if (wld->current_icon == icon) { return; } From 1f8dc7ba6e3e050b15feed49edbf93e4e7ec37e3 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 13 Jun 2014 03:19:40 -0700 Subject: [PATCH 16/21] Check the existence of every tuple before we assume it. --- src/network.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network.c b/src/network.c index b26eb41..3ae2aa8 100644 --- a/src/network.c +++ b/src/network.c @@ -24,7 +24,7 @@ static void appmsg_in_received(DictionaryIterator *received, void *context) { Tuple *current_time_tuple = dict_find(received, KEY_CURRENT_TIME); Tuple *error_tuple = dict_find(received, KEY_ERROR); - if (temperature_tuple && condition_tuple) { + if (temperature_tuple && condition_tuple && sunset_tuple && sunset_tuple && current_time_tuple) { weather.temperature = temperature_tuple->value->int32; weather.condition = condition_tuple->value->int32; weather.sunrise = sunrise_tuple->value->int32; From 26425e4d92c9a0108c326f2d7a1cfc7aa276518b Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 13 Jun 2014 03:34:52 -0700 Subject: [PATCH 17/21] Stray tabs -> spaces. --- README.md | 8 +++---- appinfo.json | 6 +++--- src/js/pebble-js-app.js | 2 +- src/main.c | 6 +++--- src/weather_layer.c | 18 ++++++++-------- src/weather_layer.h | 48 ++++++++++++++++++++--------------------- 6 files changed, 44 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 3d867a6..17200e0 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@ To do: - Add error handling features as in the [SDK 1.x versions](https://github.com/Niknam/futura-weather) - Switch weather information provider from OpenWeatherMap back to Yahoo! Weather - Add configuration settings - - Temperature format - - Fixed location - - Language - - Phone link monitor control + - Temperature format + - Fixed location + - Language + - Phone link monitor control Based on work by: - "WeatherWatch" by Katharine - https://github.com/Katharine/WeatherWatch diff --git a/appinfo.json b/appinfo.json index 2dfc8d5..9a413ff 100644 --- a/appinfo.json +++ b/appinfo.json @@ -11,9 +11,9 @@ "appKeys": { "temperature": 0, "condition": 1, - "sunrise": 2, - "sunset": 3, - "current_time": 4, + "sunrise": 2, + "sunset": 3, + "current_time": 4, "error": 5, "request_update": 42 }, diff --git a/src/js/pebble-js-app.js b/src/js/pebble-js-app.js index 54ed075..4da263d 100644 --- a/src/js/pebble-js-app.js +++ b/src/js/pebble-js-app.js @@ -54,7 +54,7 @@ function fetchWeather(latitude, longitude) { else { // Otherwise, convert temperature to Celsius temperature = Math.round(tempResult - 273.15); - } + } condition = response.weather[0].id; sunrise = response.sys.sunrise; sunset = response.sys.sunset; diff --git a/src/main.c b/src/main.c index e9d01f4..456dfbb 100644 --- a/src/main.c +++ b/src/main.c @@ -72,12 +72,12 @@ static void handle_tick(struct tm *tick_time, TimeUnits units_changed) } static void mark_weather_loaded(void) { - if(!s_weather_loaded) { + if (!s_weather_loaded) { s_weather_loaded = true; // We don't need to run this every second any more. tick_timer_service_subscribe(MINUTE_UNIT, handle_tick); } - if(s_loading_timeout != NULL) { + if (s_loading_timeout != NULL) { app_timer_cancel(s_loading_timeout); s_loading_timeout = NULL; } @@ -101,7 +101,7 @@ static void handle_weather_error(WeatherError error) { static void handle_loading_timeout(void* unused) { s_loading_timeout = NULL; - if(!s_weather_loaded) { + if (!s_weather_loaded) { handle_weather_error(WEATHER_E_PHONE); } } diff --git a/src/weather_layer.c b/src/weather_layer.c index 2eaa7ff..28df367 100644 --- a/src/weather_layer.c +++ b/src/weather_layer.c @@ -99,15 +99,15 @@ void weather_layer_set_temperature(WeatherLayer* weather_layer, int16_t t) { text_layer_set_font(wld->temp_layer, s_large_font); text_layer_set_text_alignment(wld->temp_layer, GTextAlignmentCenter); - // Is the temperature below zero? - if (wld->temp_str[0] == '-') { - memmove( - wld->temp_str + 1 + 1, - wld->temp_str + 1, - 5 - (1 + 1) - ); - memcpy(&wld->temp_str[1], " ", 1); - } + // Is the temperature below zero? + if (wld->temp_str[0] == '-') { + memmove( + wld->temp_str + 1 + 1, + wld->temp_str + 1, + 5 - (1 + 1) + ); + memcpy(&wld->temp_str[1], " ", 1); + } } // Temperature between 10° -> 19° else if (t >= 10 && t < 20) { diff --git a/src/weather_layer.h b/src/weather_layer.h index 5731ef4..692a5d8 100644 --- a/src/weather_layer.h +++ b/src/weather_layer.h @@ -2,30 +2,30 @@ #define WEATHER_LAYER_H typedef enum { - WEATHER_ICON_CLEAR_DAY = 0, - WEATHER_ICON_CLEAR_NIGHT, - WEATHER_ICON_RAIN, - WEATHER_ICON_SNOW, - WEATHER_ICON_SLEET, - WEATHER_ICON_WIND, - WEATHER_ICON_FOG, - WEATHER_ICON_CLOUDY, - WEATHER_ICON_PARTLY_CLOUDY_DAY, - WEATHER_ICON_PARTLY_CLOUDY_NIGHT, - WEATHER_ICON_THUNDER, - WEATHER_ICON_RAIN_SNOW, - WEATHER_ICON_RAIN_SLEET, - WEATHER_ICON_SNOW_SLEET, - WEATHER_ICON_COLD, - WEATHER_ICON_HOT, - WEATHER_ICON_DRIZZLE, - WEATHER_ICON_NOT_AVAILABLE, - WEATHER_ICON_PHONE_ERROR, - WEATHER_ICON_CLOUD_ERROR, - WEATHER_ICON_LOADING1, - WEATHER_ICON_LOADING2, - WEATHER_ICON_LOADING3, - WEATHER_ICON_COUNT + WEATHER_ICON_CLEAR_DAY = 0, + WEATHER_ICON_CLEAR_NIGHT, + WEATHER_ICON_RAIN, + WEATHER_ICON_SNOW, + WEATHER_ICON_SLEET, + WEATHER_ICON_WIND, + WEATHER_ICON_FOG, + WEATHER_ICON_CLOUDY, + WEATHER_ICON_PARTLY_CLOUDY_DAY, + WEATHER_ICON_PARTLY_CLOUDY_NIGHT, + WEATHER_ICON_THUNDER, + WEATHER_ICON_RAIN_SNOW, + WEATHER_ICON_RAIN_SLEET, + WEATHER_ICON_SNOW_SLEET, + WEATHER_ICON_COLD, + WEATHER_ICON_HOT, + WEATHER_ICON_DRIZZLE, + WEATHER_ICON_NOT_AVAILABLE, + WEATHER_ICON_PHONE_ERROR, + WEATHER_ICON_CLOUD_ERROR, + WEATHER_ICON_LOADING1, + WEATHER_ICON_LOADING2, + WEATHER_ICON_LOADING3, + WEATHER_ICON_COUNT } WeatherIcon; typedef Layer WeatherLayer; From dbd6fa9a647c18fffc52a0b76ccbc316b407bb77 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 13 Jun 2014 03:44:20 -0700 Subject: [PATCH 18/21] Don't bother with the loading indicator if bluetooth is missing. --- src/main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index 456dfbb..c5599ed 100644 --- a/src/main.c +++ b/src/main.c @@ -144,8 +144,13 @@ static void init(void) { // We don't trigger the weather from here, because it is possible for the other end // to not yet be running when we finish startup. However, we do set a timeout so we - // don't sit around animating forever. - s_loading_timeout = app_timer_register(LOADING_TIMEOUT, handle_loading_timeout, NULL); + // don't sit around animating forever. However, if Bluetooth is not connected, then + // this cannot possibly succeed and we fail instantly. + if(bluetooth_connection_service_peek()) { + s_loading_timeout = app_timer_register(LOADING_TIMEOUT, handle_loading_timeout, NULL); + } else { + handle_weather_error(WEATHER_E_DISCONNECTED); + } } static void deinit(void) { From 24a7b750d5a6129ad4c69612a1e15cea1b8dd74b Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 13 Jun 2014 03:47:54 -0700 Subject: [PATCH 19/21] Remove defunct config.h. --- src/config.h | 3 --- src/main.c | 1 - 2 files changed, 4 deletions(-) delete mode 100644 src/config.h diff --git a/src/config.h b/src/config.h deleted file mode 100644 index b5523bd..0000000 --- a/src/config.h +++ /dev/null @@ -1,3 +0,0 @@ -// "c" for temperature in Celsius -// "f" for temperature in Fahrenheit -#define UNIT_SYSTEM "c" \ No newline at end of file diff --git a/src/main.c b/src/main.c index c5599ed..16a68c0 100644 --- a/src/main.c +++ b/src/main.c @@ -2,7 +2,6 @@ #include "weather_layer.h" #include "network.h" -#include "config.h" #define TIME_FRAME (GRect(0, 2, 144, 168-6)) #define DATE_FRAME (GRect(1, 66, 144, 168-62)) From f455a4dae8efd1fdf217496274905a39e04c4740 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Mon, 23 Jun 2014 12:22:10 -0700 Subject: [PATCH 20/21] Err on the side of no infinite loops. --- src/network.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network.c b/src/network.c index 3ae2aa8..33317ce 100644 --- a/src/network.c +++ b/src/network.c @@ -54,8 +54,8 @@ static void appmsg_out_sent(DictionaryIterator *sent, void *context) { static void appmsg_in_dropped(AppMessageResult reason, void *context) { APP_LOG(APP_LOG_LEVEL_DEBUG, "In dropped: %i", reason); - // Request a new update... - request_weather(); + // Err on the side of caution here because infinite loops are bad. + report_error(WEATHER_E_PHONE); } static void appmsg_out_failed(DictionaryIterator *failed, AppMessageResult reason, void *context) { From 2ccb87a7045e27436501cb6c056eda72b3be6d50 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Mon, 23 Jun 2014 12:22:24 -0700 Subject: [PATCH 21/21] Bump versionCode because it turns out we actually care. --- appinfo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appinfo.json b/appinfo.json index 9a413ff..7f44d9f 100644 --- a/appinfo.json +++ b/appinfo.json @@ -3,7 +3,7 @@ "shortName": "Futura Weather", "longName": "Futura Weather", "companyName": "Pebble Community", - "versionCode": 8, + "versionCode": 9, "versionLabel": "2.1.0", "watchapp": { "watchface": true