diff --git a/src/directions_window.c b/src/directions_window.c index 6ec31f9..7dd7500 100644 --- a/src/directions_window.c +++ b/src/directions_window.c @@ -3,11 +3,18 @@ #include "select_window.h" #include "directions_window.h" #include "error_window.h" -#include "loading_window.h" +#include "progress_layer.h" #define MAX_STEP_COUNT 20 #define MAX_STEP_CHARS 128 +// Progress display vals +#define PROGRESS_SEARCH_SEND 50 +#define PROGRESS_DISTANCE_RECIVED 80 +#define PROGRESS_TIME_RECIVED PROGRESS_DISTANCE_RECIVED +#define PROGRESS_STEP_RECIVED 95 +#define PROGRESS_SUCCESS_RECIVED 100 + // The RouteData struct struct RouteData { // App message state TODO: Is this necessary? (-> probably not!) @@ -30,9 +37,9 @@ static GColor selected_type_color; static DictationSession *dictation_session; static char *address; -// TODO: implement a timer, that kills the proccess if the message is never send (needed?) -// TODO: implement the loading window as a layer instead +// TODO: Maybe implement a timeout later struct RouteData *route_data; +ProgressLayer *progress_layer; // Function declarations static void app_message_send_search_data(); @@ -65,6 +72,8 @@ static void draw_row_callback(GContext *ctx, const Layer *cell_layer, MenuIndex switch (cell_index->row) { // Summary case 0: + // Store the display text + menu_cell_basic_draw(ctx, cell_layer, "11 min", "600 m", NULL); break; // Step description @@ -112,7 +121,7 @@ static void dictation_session_callback(DictationSession *session, DictationSessi // * APP MESSAGE STUFF * // ********************* -// Accept data from the watch +// Accept data from the watch (will also move to the correct progress indications) static void app_message_inbox_recived_callback(DictionaryIterator *iter, void *context) { // Test all possible message types Tuple *message; @@ -131,6 +140,8 @@ static void app_message_inbox_recived_callback(DictionaryIterator *iter, void *c // Test if the recived message is for key SUCCESS message = dict_find(iter, MESSAGE_KEY_SUCCESS); if (message) { + // Set the progress + progress_layer_set_progress(progress_layer, PROGRESS_SUCCESS_RECIVED, true); // Respond with the correct UI switch ((int)message->value->int32) { // Success @@ -153,12 +164,16 @@ static void app_message_inbox_recived_callback(DictionaryIterator *iter, void *c // Test if the recived message is for key DISTANCE message = dict_find(iter, MESSAGE_KEY_DISTANCE); if (message) { + // Set the progress + progress_layer_set_progress(progress_layer, PROGRESS_DISTANCE_RECIVED, true); route_data->distance = (int)message->value->int32; } // Test if the recived message is for key TIME message = dict_find(iter, MESSAGE_KEY_TIME); if (message) { + // Set the progress + progress_layer_set_progress(progress_layer, PROGRESS_TIME_RECIVED, true); route_data->time = (int)message->value->int32; } @@ -166,6 +181,8 @@ static void app_message_inbox_recived_callback(DictionaryIterator *iter, void *c for (int i = 0; i < MAX_STEP_COUNT; i++) { message = dict_find(iter, MESSAGE_KEY_INSTRUCTIONS + i); if (message) { + // Set the progress + progress_layer_set_progress(progress_layer, PROGRESS_STEP_RECIVED, true); // Copy the string into the string array FIXME strcpy(route_data->steps[i], message->value->cstring); route_data->steps[i][MAX_STEP_CHARS - 1] = '\0'; @@ -187,8 +204,16 @@ static void app_message_outbox_failed_callback(DictionaryIterator *iter, AppMess window_display_error(Network); } +static void app_message_outbox_sent_callback(DictionaryIterator *iter, void *context) { + // Display the progress + progress_layer_set_progress(progress_layer, PROGRESS_SEARCH_SEND, true); +} + // Send the search data to the phone static void app_message_send_search_data() { + // Display the progress view + progress_layer_set_progress(progress_layer, 0, false); + progress_layer_present(window, progress_layer); // Send the data to the phone if conn is ready if (route_data->ready) { // Create a string with the correct length (len is the length w/o the '/0' char) @@ -205,8 +230,6 @@ static void app_message_send_search_data() { dict_write_cstring(iter, MESSAGE_KEY_SEARCH, message); // Send the outbox app_message_outbox_send(); - // Display loading anim window - loading_window_push(); } else { // Display network error window_display_error(Network); @@ -231,11 +254,12 @@ static void app_message_start() { app_message_register_inbox_received(app_message_inbox_recived_callback); app_message_register_inbox_dropped(app_message_inbox_dropped_callback); app_message_register_outbox_failed(app_message_outbox_failed_callback); + app_message_register_outbox_sent(app_message_outbox_sent_callback); // Open the app-message app_message_open(APP_MESSAGE_INBOX_SIZE_MINIMUM, APP_MESSAGE_OUTBOX_SIZE_MINIMUM); } -static void app_message_destroy_resources() { +static void app_message_resources_destroy() { // Remove all app message callbacks app_message_deregister_callbacks(); // Clear the data @@ -249,8 +273,6 @@ static void app_message_destroy_resources() { // Network error callback void window_display_error(enum ErrorType err) { - // Remove the loading window - loading_window_finish(); // Show the error window error_window_push(err); // Remove this window from the window stack @@ -266,20 +288,25 @@ static void window_update_data() { menu_layer_set_selected_index(directions_list, (MenuIndex){ .section = 0, .row = 0 }, MenuRowAlignTop, false); #endif menu_layer_reload_data(directions_list); - // Hide the loading view - loading_window_finish(); + // Hide the progress layer + progress_layer_remove(progress_layer); } // Window unload handler static void window_unload() { // Destroy the menu layer menu_layer_destroy(directions_list); + directions_list = NULL; + + // Destroy the progress layer + progress_layer_destroy(progress_layer); + progress_layer = NULL; // Destroy the dictation session dictation_session_destroy(dictation_session); // Destroy app message stuff - app_message_destroy_resources(); + app_message_resources_destroy(); route_data = NULL; // Destroy the window @@ -287,6 +314,11 @@ static void window_unload() { window = NULL; } +static void window_disappear() { + // Remove the progress layer + progress_layer_remove(progress_layer); +} + // Window load stuff static void window_load() { Layer *window_layer = window_get_root_layer(window); @@ -335,6 +367,9 @@ static void window_load() { // Add the menu layer to the window layer_add_child(window_layer, menu_layer_get_layer(directions_list)); + + // Create the progress layer + progress_layer = progress_layer_create(bounds); } // Push the window to the window stack @@ -347,6 +382,7 @@ void directions_window_push() { window_set_window_handlers(window, (WindowHandlers) { .load = window_load, .unload = window_unload, + .disappear = window_disappear, }); } diff --git a/src/js/app.js b/src/js/app.js index 299f9bb..d57bf96 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -81,7 +81,11 @@ function sendRoute(success, distance, time, stepList) { // Transmit Pebble.sendAppMessage(dict, function() { // Success! - sendStepItem(stepList, 0); + setTimeout(function() { + // Some dummy loading time + sendStepItem(stepList, 0); + }, 3000); + //sendStepItem(stepList, 0); }, function() { // Error console.log('Transmission failed at [OVERVIEW]'); @@ -100,8 +104,8 @@ function fetchAndSendRoute(routeType, destination) { /* dummy data: */ setTimeout(function() { // Some dummy loading time - sendRoute(true, 560, 16, ['This is the first step', 'This is the second step', 'This is the third step', 'This is the final step']); - }, 10000); + sendRoute(true, 560, 16, ['This is the first step', 'This is the second step', 'This is the third step', 'This is the final step', destination]); + }, 3000); } // Accept data from the pebble watch diff --git a/src/loading_window.c b/src/loading_window.c deleted file mode 100644 index 02814bc..0000000 --- a/src/loading_window.c +++ /dev/null @@ -1,173 +0,0 @@ -#include -#include "loading_window.h" - -// TODO: Tidy this! (will do later) -// TODO: disconnect loading / display window to make things work better soon! (or move the 'loading window' to a 'loading layer'...) -// Some stuff added by me -static bool will_close; - -// The loading window, implementation stolen from pebble UI examples -typedef Layer ProgressLayer; - -#define MIN(a,b) (((a)<(b))?(a):(b)) - -ProgressLayer* progress_layer_create(GRect frame); -void progress_layer_destroy(ProgressLayer* progress_layer); -void progress_layer_increment_progress(ProgressLayer* progress_layer, int16_t progress); -void progress_layer_set_progress(ProgressLayer* progress_layer, int16_t progress_percent); -void progress_layer_set_corner_radius(ProgressLayer* progress_layer, uint16_t corner_radius); -void progress_layer_set_foreground_color(ProgressLayer* progress_layer, GColor color); -void progress_layer_set_background_color(ProgressLayer* progress_layer, GColor color); - -typedef struct { - int16_t progress_percent; - int16_t corner_radius; - GColor foreground_color; - GColor background_color; -} ProgressLayerData; - -static int16_t scale_progress_bar_width_px(unsigned int progress_percent, int16_t rect_width_px) { - return ((progress_percent * (rect_width_px)) / 100); -} - -static void progress_layer_update_proc(ProgressLayer* progress_layer, GContext* ctx) { - ProgressLayerData *data = (ProgressLayerData *)layer_get_data(progress_layer); - GRect bounds = layer_get_bounds(progress_layer); - - int16_t progress_bar_width_px = scale_progress_bar_width_px(data->progress_percent, bounds.size.w); - GRect progress_bar = GRect(bounds.origin.x, bounds.origin.y, progress_bar_width_px, bounds.size.h); - - graphics_context_set_fill_color(ctx, data->background_color); - graphics_fill_rect(ctx, bounds, data->corner_radius, GCornersAll); - - graphics_context_set_fill_color(ctx, data->foreground_color); - graphics_fill_rect(ctx, progress_bar, data->corner_radius, GCornersAll); - -#ifdef PBL_PLATFORM_APLITE - graphics_context_set_stroke_color(ctx, data->background_color); - graphics_draw_rect(ctx, progress_bar); -#endif -} - -ProgressLayer* progress_layer_create(GRect frame) { - ProgressLayer *progress_layer = layer_create_with_data(frame, sizeof(ProgressLayerData)); - layer_set_update_proc(progress_layer, progress_layer_update_proc); - layer_mark_dirty(progress_layer); - - ProgressLayerData *data = (ProgressLayerData *)layer_get_data(progress_layer); - data->progress_percent = 0; - data->corner_radius = 1; - data->foreground_color = GColorBlack; - data->background_color = GColorWhite; - - return progress_layer; -} - -void progress_layer_destroy(ProgressLayer* progress_layer) { - if (progress_layer) { - layer_destroy(progress_layer); - } -} - -void progress_layer_increment_progress(ProgressLayer* progress_layer, int16_t progress) { - ProgressLayerData *data = (ProgressLayerData *)layer_get_data(progress_layer); - data->progress_percent = MIN(100, data->progress_percent + progress); - layer_mark_dirty(progress_layer); -} - -void progress_layer_set_progress(ProgressLayer* progress_layer, int16_t progress_percent) { - ProgressLayerData *data = (ProgressLayerData *)layer_get_data(progress_layer); - data->progress_percent = MIN(100, progress_percent); - layer_mark_dirty(progress_layer); -} - -void progress_layer_set_corner_radius(ProgressLayer* progress_layer, uint16_t corner_radius) { - ProgressLayerData *data = (ProgressLayerData *)layer_get_data(progress_layer); - data->corner_radius = corner_radius; - layer_mark_dirty(progress_layer); -} - -void progress_layer_set_foreground_color(ProgressLayer* progress_layer, GColor color) { - ProgressLayerData *data = (ProgressLayerData *)layer_get_data(progress_layer); - data->foreground_color = color; - layer_mark_dirty(progress_layer); -} - -void progress_layer_set_background_color(ProgressLayer* progress_layer, GColor color) { - ProgressLayerData *data = (ProgressLayerData *)layer_get_data(progress_layer); - data->background_color = color; - layer_mark_dirty(progress_layer); -} - -static Window *s_window; -static ProgressLayer *s_progress_layer; - -static AppTimer *s_timer; -static int s_progress; - -static void progress_callback(void *context); - -static void next_timer() { - s_timer = app_timer_register(PROGRESS_LAYER_WINDOW_DELTA, progress_callback, NULL); -} - -static void progress_callback(void *context) { - s_progress += (s_progress < 85 || will_close) ? 1 : 0; - progress_layer_set_progress(s_progress_layer, s_progress); - next_timer(); - // Close when progress hits 100 - if (s_progress == 100) { - window_stack_remove(s_window, true); - } -} - -static void window_load(Window *window) { - will_close = false; - - Layer *window_layer = window_get_root_layer(window); - GRect bounds = layer_get_bounds(window_layer); - - s_progress_layer = progress_layer_create(GRect((bounds.size.w - PROGRESS_LAYER_WINDOW_WIDTH) / 2, (bounds.size.h - 3) / 2, PROGRESS_LAYER_WINDOW_WIDTH, 6)); - progress_layer_set_progress(s_progress_layer, 0); - progress_layer_set_corner_radius(s_progress_layer, 2); - progress_layer_set_foreground_color(s_progress_layer, GColorWhite); - progress_layer_set_background_color(s_progress_layer, GColorBlack); - layer_add_child(window_layer, s_progress_layer); -} - -static void window_unload(Window *window) { - progress_layer_destroy(s_progress_layer); - - window_destroy(window); - s_window = NULL; -} - -static void window_appear(Window *window) { - s_progress = 0; - next_timer(); -} - -static void window_disappear(Window *window) { - if(s_timer) { - app_timer_cancel(s_timer); - s_timer = NULL; - } -} - -void loading_window_push() { - if(!s_window) { - s_window = window_create(); - window_set_background_color(s_window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); - window_set_window_handlers(s_window, (WindowHandlers) { - .load = window_load, - .appear = window_appear, - .disappear = window_disappear, - .unload = window_unload - }); - } - window_stack_push(s_window, true); -} - -void loading_window_finish() { - will_close = true; -} diff --git a/src/loading_window.h b/src/loading_window.h deleted file mode 100644 index 74fccde..0000000 --- a/src/loading_window.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include - -#define PROGRESS_LAYER_WINDOW_DELTA 33 -#define PROGRESS_LAYER_WINDOW_WIDTH 80 - -void loading_window_push(); -void loading_window_finish(); diff --git a/src/progress_layer.c b/src/progress_layer.c new file mode 100644 index 0000000..d79c712 --- /dev/null +++ b/src/progress_layer.c @@ -0,0 +1,124 @@ +#include +#include "progress_layer.h" + +// Local state (used to ransfer the progress to the update proc) +int16_t l_progress = 0; + +// Local functions +void progress_layer_update_proc(Layer *layer, GContext *ctx) { + // Get the layers bounds + GRect bounds = layer_get_bounds(layer); + // Work out the colors + #ifdef PBL_COLOR + GColor color_bg = COLOR_PROGRESS_BG; + GColor color_bar = COLOR_PROGRESS_BAR; + GColor color_fill = COLOR_PROGRESS_FILL; + #else + GColor color_bg = GColorWhite; + GColor color_bar = GColorWhite; + GColor color_fill = GColorBlack; + #endif + + // Draw the bg + graphics_context_set_fill_color(ctx, color_bg); + graphics_fill_rect(ctx, bounds, 0, GCornerNone); + + // Draw the bar + GRect bar = GRect((bounds.size.w - PROGRESS_BAR_WIDTH) / 2, (bounds.size.h - PROGRESS_BAR_HEIGHT) / 2, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT); + graphics_context_set_fill_color(ctx, color_bar); + graphics_fill_rect(ctx, bar, 2, GCornersAll); + + // Draw the progress + l_progress = l_progress > 100 ? 100 : l_progress < 0 ? 0 : l_progress; // Cage l_progress between 0 <-> 100 + int16_t progress_width = PROGRESS_BAR_WIDTH * (l_progress / 100.0); + GRect fill = GRect((bounds.size.w - PROGRESS_BAR_WIDTH) / 2, (bounds.size.h - PROGRESS_BAR_HEIGHT) / 2, progress_width, PROGRESS_BAR_HEIGHT); + graphics_context_set_fill_color(ctx, color_fill); + graphics_fill_rect(ctx, fill, 2, GCornersAll); +} + +// Handle the mark dirty and the l_progress transfer +void progress_layer_mark_dirty(ProgressLayer *progress_layer) { + // Store the progress locally + l_progress = progress_layer->progress; + // Update the ui + layer_mark_dirty(progress_layer->layer); +} + +void progress_layer_animate(void *data) { + ProgressLayer *progress_layer = (ProgressLayer *)data; + if (progress_layer->progress < progress_layer->target) { + // Increment progress & call update proc + progress_layer->progress ++; + progress_layer_mark_dirty(progress_layer); + // Shedule the timer + progress_layer->timer = app_timer_register(PROGRESS_ANIM_DELTA, progress_layer_animate, progress_layer); + } else if (progress_layer->progress > progress_layer->target) { + // Decrement progress & call update proc + progress_layer->progress --; + progress_layer_mark_dirty(progress_layer); + // Shedule the timer + progress_layer->timer = app_timer_register(PROGRESS_ANIM_DELTA, progress_layer_animate, progress_layer); + } +} + +// ********************* +// * EXPOSED FUNCTIONS * +// ********************* + +// Create the progress layer +ProgressLayer *progress_layer_create(GRect frame) { + // Allocate the progress layer + ProgressLayer *progress_layer = malloc(sizeof(ProgressLayer)); + // Allocate the layer component and register its update proc + progress_layer->layer = layer_create(frame); + layer_set_update_proc(progress_layer->layer, progress_layer_update_proc); + // Set the timer to be NULL + progress_layer->timer = NULL; + // Set the initial progress + progress_layer->progress = 0; + progress_layer->target = 0; + progress_layer->present = false; + // Return the progress layer + return progress_layer; +} + +// Destroy the progress layer (call in window unload) +void progress_layer_destroy(ProgressLayer *progress_layer) { + // Destroy the layer component (the timer destroys itself I guess?) + if (progress_layer != NULL) { + layer_destroy(progress_layer->layer); + free(progress_layer); + } +} + +// Set the progress of the progress layer +void progress_layer_set_progress(ProgressLayer *progress_layer, int16_t progress, bool animated) { + if (animated && progress_layer->present) { + // Call the animate function + progress_layer->target = progress; + progress_layer_animate(progress_layer); + } else { + // Set the new progress & update the layer if present + progress_layer->progress = progress; + if (progress_layer->present) progress_layer_mark_dirty(progress_layer); + } +} + +// This allows the set_progress function to start timers +void progress_layer_present(Window *window, ProgressLayer *progress_layer) { + // Add to the windows root layer + Layer *window_root = window_get_root_layer(window); + layer_add_child(window_root, progress_layer->layer); + // Mark the layer as present + progress_layer->present = true; +} + +// This call should always be called in the windows disappear! (will stop the timer) +void progress_layer_remove(ProgressLayer *progress_layer) { + // Mark the layer as not present + progress_layer->present = false; + // Stop all timers + app_timer_cancel(progress_layer->timer); + // Remove the layer from the parent + layer_remove_from_parent(progress_layer->layer); +} diff --git a/src/progress_layer.h b/src/progress_layer.h new file mode 100644 index 0000000..91eded3 --- /dev/null +++ b/src/progress_layer.h @@ -0,0 +1,30 @@ +#pragma once +#include + +#define PROGRESS_BAR_WIDTH 80 +#define PROGRESS_BAR_HEIGHT 6 +#define PROGRESS_ANIM_DELTA 33 + +// For B/W Pebbels: bg white, bar white, fill black +#define COLOR_PROGRESS_BG GColorLightGray +#define COLOR_PROGRESS_BAR GColorBlack +#define COLOR_PROGRESS_FILL GColorWhite + +typedef struct { + Layer *layer; + AppTimer *timer; + int16_t progress; + int16_t target; + bool present; +} ProgressLayer; + +// Create the progress layer +ProgressLayer *progress_layer_create(GRect frame); +// Destroy the progress layer (call in window unload) +void progress_layer_destroy(ProgressLayer *progress_layer); +// Set the progress of the progress layer +void progress_layer_set_progress(ProgressLayer *progress_layer, int16_t progress, bool animated); +// This allows the set_progress function to start timers! +void progress_layer_present(Window *window, ProgressLayer *progress_layer); +// This call should always be called in the windows disappear! (will stop the timer) +void progress_layer_remove(ProgressLayer *progress_layer);