From d5fe6d4101aece73237310f25ac49650bb2d1046 Mon Sep 17 00:00:00 2001 From: wildhart Date: Tue, 2 Feb 2016 08:41:21 +1300 Subject: [PATCH 1/6] first attempt at timeline SDK3 --- appinfo.json | 9 +- src/job_menu.c | 17 ++- src/jobs.c | 8 +- src/main.c | 36 +++++- src/main.h | 31 ++++++ src/main_menu.c | 75 ++++++++++++- src/pebble-js-app.js | 259 ++++++++++++++++++++++++++++++++++++++----- src/tertiary_text.c | 19 +++- src/tertiary_text.h | 4 + src/update.c | 2 +- 10 files changed, 413 insertions(+), 47 deletions(-) diff --git a/appinfo.json b/appinfo.json index 2705ddc..36c8a60 100644 --- a/appinfo.json +++ b/appinfo.json @@ -6,6 +6,7 @@ "KEY_EXPORT": 6, "KEY_MEDICATIONS": 100, "KEY_MODE": 1, + "KEY_PINS_DONE": 8, "KEY_SORT": 3, "KEY_TIMESTAMP": 7, "KEY_VERSION": 4 @@ -31,11 +32,13 @@ } ] }, - "sdkVersion": "2", + "sdkVersion": "3", "shortName": "Meds Timer", + "targetPlatforms": [ + "aplite" + ], "uuid": "95d07e1a-2451-4ffa-aa44-836e523a7648", - "versionCode": 1, - "versionLabel": "1.3", + "versionLabel": "1.4", "watchapp": { "watchface": false } diff --git a/src/job_menu.c b/src/job_menu.c index a407ae1..00f5885 100644 --- a/src/job_menu.c +++ b/src/job_menu.c @@ -6,6 +6,9 @@ bool job_changed; // BEGIN AUTO-GENERATED UI CODE; DO NOT MODIFY static Window *s_window; static MenuLayer *s_menulayer; +#ifdef PBL_SDK_3 +static StatusBarLayer *s_status_bar; +#endif static void initialise_ui(void) { s_window = window_create(); @@ -13,15 +16,27 @@ static void initialise_ui(void) { window_set_fullscreen(s_window, false); #endif + GRect bounds = layer_get_bounds(window_get_root_layer(s_window)); + // s_menulayer - s_menulayer = menu_layer_create(GRect(0, 0, 144, 152)); + s_menulayer = menu_layer_create(GRect(0, STATUS_BAR_LAYER_HEIGHT, bounds.size.w, bounds.size.h-STATUS_BAR_LAYER_HEIGHT)); menu_layer_set_click_config_onto_window(s_menulayer, s_window); layer_add_child(window_get_root_layer(s_window), (Layer *)s_menulayer); + + #ifdef PBL_SDK_3 + // Set up the status bar last to ensure it is on top of other Layers + s_status_bar = status_bar_layer_create(); + layer_add_child(window_get_root_layer(s_window), status_bar_layer_get_layer(s_status_bar)); + #endif } static void destroy_ui(void) { window_destroy(s_window); menu_layer_destroy(s_menulayer); + + #ifdef PBL_SDK_3 + status_bar_layer_destroy(s_status_bar); + #endif } // END AUTO-GENERATED UI CODE diff --git a/src/jobs.c b/src/jobs.c index de66df0..1852515 100644 --- a/src/jobs.c +++ b/src/jobs.c @@ -311,11 +311,11 @@ void jobs_reset_and_save(uint8_t *index) { void jobs_add_minutes(uint8_t *index, int minutes) { Job* job=jobs_list_get_index(*index); int seconds = (int) job->Seconds; - if (seconds + 60*minutes < time(NULL)) { + //if (seconds + 60*minutes < time(NULL)) { seconds += 60*minutes; - } else { - seconds = time(NULL); - } + //} else { + // seconds = time(NULL); + //} job->Seconds = seconds; } diff --git a/src/main.c b/src/main.c index 389022f..7b5eb70 100644 --- a/src/main.c +++ b/src/main.c @@ -23,6 +23,10 @@ static bool data_loaded_from_watch = false; static uint32_t data_timestamp = 0; uint8_t stored_version=0; bool export_after_save=false; +#ifdef PBL_SDK_3 + bool waiting_for_pins=false; + bool quit_after_pins=false; +#endif // ***************************************************************************************************** // MESSAGES @@ -37,6 +41,7 @@ bool export_after_save=false; #define KEY_APP_VERSION 5 #define KEY_EXPORT 6 #define KEY_TIMESTAMP 7 +#define KEY_PINS_DONE 8 static void send_settings_to_phone() { if (!JS_ready) return; @@ -60,12 +65,26 @@ static void send_settings_to_phone() { dict_write_end(iter); app_message_outbox_send(); + #ifdef PBL_SDK_3 + waiting_for_pins=true; + #endif } static void inbox_received_handler(DictionaryIterator *iter, void *context) { LOG("Inbox received..."); JS_ready = true; Tuple *tuple_t; + + #ifdef PBL_SDK_3 + if (waiting_for_pins && dict_find(iter, KEY_PINS_DONE)) { + waiting_for_pins=false; + if (quit_after_pins) { + main_menu_hide(); + } + return; + } + #endif + bool new_data_from_config_page = dict_find(iter, KEY_CONFIG_DATA); tuple_t= dict_find(iter, KEY_TIMESTAMP); uint32_t inbox_timestamp = tuple_t ? tuple_t->value->int32 : 0; @@ -163,9 +182,22 @@ void init(void) { bitmap_tick=gbitmap_create_as_sub_bitmap(bitmap_matrix, ICON_RECT_TICK); main_menu_show(); if (data_loaded_from_watch && stored_version < CURRENT_STORAGE_VERSION) update_show(stored_version); - + + app_message_register_inbox_received(inbox_received_handler); - app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum()); + app_message_open(2048, 2048); + + #ifdef PBL_SDK_3 + if (launch_reason() == APP_LAUNCH_TIMELINE_ACTION) { + uint8_t reason=launch_get_args(); + LOG("launch code: %d", reason); + if (reason>=10) { + reason-=10; + jobs_reset_and_save(&reason); + quit_after_pins=true; + } + } + #endif } void deinit(void) { diff --git a/src/main.h b/src/main.h index fe0bbe6..9e53f53 100644 --- a/src/main.h +++ b/src/main.h @@ -24,6 +24,31 @@ #define ERROR(...) app_log(APP_LOG_LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__) #endif +#ifdef PBL_SDK_3 +#define PBL_IF_SDK_3(X) (X) +#define PBL_IF_SDK_3_ELSE(X, Y) (X) + #ifdef PBL_PLATFORM_APLITE + #define PBL_SDK_3_APLITE 1 + #define PBL_IF_SDK_3_APLITE_ELSE(X, Y) (X) + #else + #define PBL_IF_SDK_3_APLITE_ELSE(X, Y) (Y) + #endif +#else // PBL_SDK_3 + #define PBL_IF_SDK_3_APLITE_ELSE(X, Y) (Y) +#define PBL_IF_SDK_3(X) +#define PBL_IF_SDK_3_ELSE(X, Y) (Y) +#define PBL_IF_COLOR_ELSE(X, Y) (Y) +#define PBL_IF_ROUND_ELSE(X, Y) (Y) +#define PBL_IF_BW_ELSE(X, Y) (X) +#define gbitmap_set_bounds(bmp, new_bounds) ((bmp)->bounds = (new_bounds)) +#define GColorLightGray GColorBlack +#define GColorDarkGray GColorBlack +#endif // PBL_SDK_3 +#define ROUND_MARGIN PBL_IF_ROUND_ELSE(2,0) +#ifndef STATUS_BAR_LAYER_HEIGHT +#define STATUS_BAR_LAYER_HEIGHT 0 +#endif + #define ANIMATED true #define HIDDEN true @@ -95,4 +120,10 @@ enum { MODE_NEXT_TIME }; + +#ifdef PBL_SDK_3 + extern bool waiting_for_pins; + extern bool quit_after_pins; +#endif + void main_save_data(void); \ No newline at end of file diff --git a/src/main_menu.c b/src/main_menu.c index 33752e9..3b2d3c8 100644 --- a/src/main_menu.c +++ b/src/main_menu.c @@ -2,6 +2,41 @@ static Window *s_window; static MenuLayer *s_menulayer; +#ifdef PBL_SDK_3 +static StatusBarLayer *s_status_bar; +#endif + +static bool check_phone_message = false; + +/*************************************** Back Button Override ********************************/ +// https://gist.github.com/sarfata/10574031 +#ifdef PBL_SDK_3 +// Define what you want to do when the back button is pressed +void back_button_handler(ClickRecognizerRef recognizer, void *context) { + if (waiting_for_pins) { + quit_after_pins=true; + } else { + main_menu_hide(); + } +} + +// We need to save a reference to the ClickConfigProvider originally set by the menu layer +ClickConfigProvider previous_ccp; + +// This is the new ClickConfigProvider we will set, it just calls the old one and then subscribe +// for back button events. +void new_ccp(void *context) { + previous_ccp(context); + window_single_click_subscribe(BUTTON_ID_BACK, back_button_handler); +} + +// Call this from your init function to do the hack +void force_back_button(Window *window, MenuLayer *menu_layer) { + previous_ccp = window_get_click_config_provider(window); + window_set_click_config_provider_with_context(window, new_ccp, menu_layer); +} +#endif +/********************************* end of Back Button Override ********************************/ static void initialise_ui(void) { s_window = window_create(); @@ -12,14 +47,27 @@ static void initialise_ui(void) { GRect bounds = layer_get_bounds(window_get_root_layer(s_window)); // s_menulayer - s_menulayer = menu_layer_create(GRect(0, 0, bounds.size.w, bounds.size.h)); + s_menulayer = menu_layer_create(GRect(0, STATUS_BAR_LAYER_HEIGHT, bounds.size.w, bounds.size.h-STATUS_BAR_LAYER_HEIGHT)); menu_layer_set_click_config_onto_window(s_menulayer, s_window); layer_add_child(window_get_root_layer(s_window), (Layer *)s_menulayer); + #ifdef PBL_SDK_3 + force_back_button(s_window,s_menulayer); + #endif + + #ifdef PBL_SDK_3 + // Set up the status bar last to ensure it is on top of other Layers + s_status_bar = status_bar_layer_create(); + layer_add_child(window_get_root_layer(s_window), status_bar_layer_get_layer(s_status_bar)); + #endif } static void destroy_ui(void) { window_destroy(s_window); menu_layer_destroy(s_menulayer); + + #ifdef PBL_SDK_3 + status_bar_layer_destroy(s_status_bar); + #endif } void handle_ticktimer_tick(struct tm *tick_time, TimeUnits units_changed) { @@ -91,8 +139,11 @@ static int16_t menu_get_cell_height_callback(MenuLayer *menu_layer, MenuIndex *c void menu_cell_draw_job(GContext* ctx, const Layer *cell_layer, const uint8_t index) { GRect bounds = layer_get_frame(cell_layer); - + + #ifndef PBL_SDK_3 graphics_context_set_text_color(ctx, GColorBlack); + #endif + graphics_draw_text(ctx, jobs_get_job_name(index), FONT_GOTHIC_24_BOLD, GRect(4, -4, bounds.size.w-8, 4+18), GTextOverflowModeFill, GTextAlignmentLeft, NULL); graphics_draw_text(ctx, jobs_get_job_clock_as_text(index), FONT_GOTHIC_18, GRect(4, 20, bounds.size.w-8, 14), GTextOverflowModeFill, GTextAlignmentRight, NULL); graphics_draw_text(ctx, jobs_get_job_repeat_as_text(index), FONT_GOTHIC_14, GRect(4, 20+4, bounds.size.w-8, 14), GTextOverflowModeFill, GTextAlignmentLeft, NULL); @@ -102,8 +153,11 @@ void menu_cell_draw_job(GContext* ctx, const Layer *cell_layer, const uint8_t in void menu_cell_draw_other(GContext* ctx, const Layer *cell_layer, const char *title, const char *sub_title, GBitmap * icon) { GRect bounds = layer_get_frame(cell_layer); - + + #ifndef PBL_SDK_3 graphics_context_set_text_color(ctx, GColorBlack); + #endif + graphics_draw_text(ctx, title, FONT_GOTHIC_24_BOLD, GRect(28, -4, bounds.size.w-28, 4+18), GTextOverflowModeFill, GTextAlignmentLeft, NULL); if (sub_title) graphics_draw_text(ctx, sub_title, FONT_GOTHIC_18, GRect(28, 20, bounds.size.w-28-4, 14), GTextOverflowModeFill, GTextAlignmentLeft, NULL); @@ -112,8 +166,11 @@ void menu_cell_draw_other(GContext* ctx, const Layer *cell_layer, const char *ti static void menu_cell_draw_setting(GContext* ctx, const Layer *cell_layer, const char *title, const char *setting, const char *hint) { GRect bounds = layer_get_frame(cell_layer); - + + #ifndef PBL_SDK_3 graphics_context_set_text_color(ctx, GColorBlack); + #endif + graphics_draw_text(ctx, title, FONT_GOTHIC_24_BOLD, GRect(4, -4, bounds.size.w-8, 4+18), GTextOverflowModeFill, GTextAlignmentLeft, NULL); graphics_draw_text(ctx, setting, FONT_GOTHIC_18_BOLD, GRect(4, 2, bounds.size.w-8, 18), GTextOverflowModeFill, GTextAlignmentRight, NULL); graphics_draw_text(ctx, hint, FONT_GOTHIC_18, GRect(4, 20, bounds.size.w-8, 14), GTextOverflowModeFill, GTextAlignmentLeft, NULL); @@ -134,11 +191,16 @@ static void menu_draw_row_callback(GContext* ctx, const Layer *cell_layer, MenuI break; case MENU_SETTINGS_ALARM: menu_cell_draw_setting(ctx, cell_layer, "Alarm", settings.Alarm ? "YES" : "NO",NULL); break; case MENU_SETTINGS_SORT: menu_cell_draw_setting(ctx, cell_layer, "Sort", settings.Sort ? "YES" : "NO",NULL); break; - case MENU_SETTINGS_CONFIG: menu_cell_draw_other(ctx, cell_layer, "Config/Donate", NULL , bitmap_settings); break; + case MENU_SETTINGS_CONFIG: menu_cell_draw_other(ctx, cell_layer, check_phone_message ? "Check phone..." : "Config/Donate", NULL , bitmap_settings); break; } } } +static void timer_callback(void *data) { + check_phone_message=false; + menu_layer_reload_data(s_menulayer); +} + static void menu_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *data) { switch (cell_index->section) { case MENU_SECTION_JOBS: @@ -165,6 +227,9 @@ static void menu_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, v case MENU_SETTINGS_CONFIG: export_after_save=true; main_save_data(); + check_phone_message=true; + menu_layer_reload_data(s_menulayer); + app_timer_register(2000 /* milliseconds */, timer_callback, NULL); break; } } diff --git a/src/pebble-js-app.js b/src/pebble-js-app.js index 53ca245..69b7fa4 100644 --- a/src/pebble-js-app.js +++ b/src/pebble-js-app.js @@ -3,21 +3,18 @@ Pebble.addEventListener('ready', function() { console.log('PebbleKit JS ready!'); var settings=localStorage.getItem("settings"); - //settings='{"1":2,"2":1,"3":1,"4":3,"5":"1.2","100":"Ibuprofin|1453318208|12|1","101":"Omeprazole|1453275012|24|1","102":"Paracetamol|1453347465|6|0","103":"Tremadol|1453347472|8|0","104":"Zopiclone|1453330855|24|1","KEY_MODE":2,"KEY_ALARM":1,"KEY_SORT":1,"KEY_VERSION":3,"KEY_APP_VERSION":"1.2","KEY_MEDICATIONS":"Ibuprofin|1453318208|12|1"}'; + //settings='{"101":"Ibuprofen|1454302849|6|1","102":"Tremadol|1454306400|8|1","103":"Omeprazole|1454265012|24|1","KEY_APP_VERSION":"1.4","KEY_SORT":1,"KEY_MEDICATIONS":"Paracetamol|1454292053|6|1","KEY_VERSION":3,"KEY_MODE":2,"KEY_TIMESTAMP":1454311036,"KEY_ALARM":0}'; + //settings='{"KEY_MEDICATIONS":"Paracetamol|1454292053|6|1","101":"Ibuprofen|1454302849|6|1","102":"Tremadol|1454306400|8|1","103":"Omeprazole|1454265012|24|1","KEY_APP_VERSION":"1.4","KEY_SORT":1,"KEY_VERSION":3,"KEY_MODE":2,"KEY_TIMESTAMP":1454311036,"KEY_ALARM":0}'; var dict=settings ? JSON.parse(settings) : {}; - if (!dict.KEY_TIMESTAMP) { - var d=new Date(); + if (!dict.KEY_TIMESTAMP) { + var d=new Date();$ dict.KEY_TIMESTAMP = Math.floor(d.getTime()/1000 - d.getTimezoneOffset()*60); } - Pebble.sendAppMessage(dict, function() { - console.log('Send successful: ' + JSON.stringify(dict)); - }, function() { - console.log('Send failed!'); - }); + sendDict(dict); }); -Pebble.addEventListener("appmessage", function(e) { - console.log("Received message: " + JSON.stringify(e.payload)); +Pebble.addEventListener("appmessage", function(e) { + console.log("Received message: " + JSON.stringify(e.payload)); if (e.payload.KEY_EXPORT) { delete e.payload.KEY_EXPORT; localStorage.setItem("settings",JSON.stringify(e.payload)); @@ -25,8 +22,17 @@ Pebble.addEventListener("appmessage", function(e) { } else { localStorage.setItem("settings",JSON.stringify(e.payload)); } + createAllPins(e.payload,0); }); +function sendDict(dict) { + Pebble.sendAppMessage(dict, function() { + console.log('Send successful: ' + JSON.stringify(dict)); + }, function() { + console.log('Send failed!'); + }); +} + function padZero(i) { return (i<10 ? "0":"")+i; } @@ -47,12 +53,14 @@ function showConfiguration() { var med=0; var setting; + var d; + var secs; while (setting=(med===0) ? settings.KEY_MEDICATIONS : settings[100+med]) { setting=setting.split("|"); - var d = new Date(0); // The 0 there is the key, which sets the date to the epoch - d.setUTCSeconds(setting[1]*1.0 +setting[2]*3600); - var secs=d.getSeconds(); - d.setUTCSeconds(d.getTimezoneOffset()*60); // this sets seconds to zero + d = new Date(0); // The 0 there is the key, which sets the date to the epoch + d.setUTCSeconds(setting[1]*1.0 +setting[2]*3600); // setting[1] = last time, setting[2] = repeat_hrs + secs=d.getSeconds(); + /*d.setUTCSeconds(d.getTimezoneOffset()*60); // this sets seconds to zero */ setting[1]=padZero(d.getHours())+":"+padZero(d.getMinutes()); setting.push(secs); config+="&med_"+med+"="+encodeURIComponent(setting.join("|")); @@ -75,28 +83,229 @@ Pebble.addEventListener('webviewclosed', function(e) { dict.KEY_SORT = configData.sort ? 1 : 0; // Send a boolean as an integer dict.KEY_VERSION = configData.data_version; var d=new Date(); - dict.KEY_TIMESTAMP = Math.floor(d.getTime()/1000 - d.getTimezoneOffset()*60); + dict.KEY_TIMESTAMP = Math.floor(d.getTime()/1000 /*- d.getTimezoneOffset()*60 */); var med=0; + var data; + var hhmm; + var secs; while (configData["med_"+med]) { - var data=decodeURIComponent(configData["med_"+med]).split("|"); - var hhmm=data[1].split(":"); + data=decodeURIComponent(configData["med_"+med]).split("|"); + hhmm=data[1].split(":"); d = new Date(); // The 0 there is the key, which sets the date to the epoch d.setHours(hhmm[0]); d.setMinutes(hhmm[1]); d.setSeconds(data[4]); d.setMilliseconds(0); if (d < new Date()) d.setHours(hhmm[0]*1.0 + 24); - var secs=d.getTime()/1000; - data[1]=secs - d.getTimezoneOffset()*60 - data[2]*3600; + secs=d.getTime()/1000; + data[1]=secs /*- d.getTimezoneOffset()*60 */- data[2]*3600; dict[100+med]=data.join("|"); med++; } // Send to watchapp - Pebble.sendAppMessage(dict, function() { - console.log('Send successful: ' + JSON.stringify(dict)); - }, function() { - console.log('Send failed!'); - }); -}); \ No newline at end of file + sendDict(dict); +}); + +/****************************** Custom Timeline Stuff ************************/ + +// Pin template +var pin = { +// "id": "example-pin-0", +// "time": date.toISOString(), + "layout": { + "type": "genericPin", +// "title": "Take Ibuprofen", + "tinyIcon": "system://images/NOTIFICATION_REMINDER" + }, + "reminders": [ + { +// "time": date.toISOString(), + "layout": { + "type": "genericReminder", + "tinyIcon": "system://images/NOTIFICATION_REMINDER", +// "title": "Take Ibuprofen" + } + } + ], + "actions": [ + { + "title": "Med Taken", + "type": "openWatchApp", + "launchCode": 10 /*+med number*/ + }, + { + "title": "Open Meds Timer", + "type": "openWatchApp", + "launchCode": 0 + } + ], +}; + +var last_pin=0; +var total_pins=0; +var last_total_pins=0; +var dict_copy=null; +var pin_prefix="MedTimer-pin-"; + +function pinInsertedCallback(responseText) { + console.log('insertUserPin Result: ' + responseText); + createAllPins(dict_copy,last_pin+1); +} + +function createAllPins(dict,next_pin) { + if (!dict) return; + dict_copy=dict; + if (next_pin===0) { + last_total_pins=localStorage.getItem("pins")*1.0; + last_total_pins=8; + total_pins=0; + } + last_pin=next_pin; + var setting; + setting=(last_pin===0) ? dict.KEY_MEDICATIONS : dict[100+last_pin]; + if (setting) { + // get the time + setting=setting.split("|"); + var d = new Date(0); // The 0 there is the key, which sets the date to the epoch + d.setUTCSeconds(setting[1]*1.0 +setting[2]*3600); // setting[1] = last time, setting[2] = repeat_hrs + d.setSeconds(0); + d.setMilliseconds(0); + //var secs=d.getSeconds(); + /*d.setUTCSeconds(d.getTimezoneOffset()*60); // this sets seconds to zero */ + //setting[1]=padZero(d.getHours())+":"+padZero(d.getMinutes()); + //setting.push(secs); + + // configure the pin + pin.id=pin_prefix+last_pin; + pin.time=pin.reminders[0].time=d.toISOString(); + pin.layout.title=pin.reminders[0].layout.title="Take "+setting[0]; + pin.actions[0].launchCode=10+last_pin; + total_pins++; + // Push the pin + console.log('Inserting pin in the future: ' + JSON.stringify(pin)); + insertUserPin(pin, pinInsertedCallback); + } else { + // no more meds to send + localStorage.setItem("pins",total_pins); + console.log("remembering pins: "+total_pins); + // delete any old pins + deleteAllPins(last_pin); + } +} + +function pinDeletedCallback(responseText) { + console.log('deleteUserPin Result: ' + responseText); + deleteAllPins(last_pin+1); +} + +function deleteAllPins(next_pin) { + last_pin=next_pin; + if (last_pin +#ifndef PBL_SDK_3 +#define PBL_IF_ROUND_ELSE(X, Y) (Y) +#endif + // A callback which is called when a user submits data // // Params: diff --git a/src/update.c b/src/update.c index f8422b0..6cf71c3 100644 --- a/src/update.c +++ b/src/update.c @@ -24,7 +24,7 @@ static void initialise_ui(void) { // s_textlayer_features s_textlayer_features = text_layer_create(GRect(2, 27, 140, 125)); - text_layer_set_text(s_textlayer_features, "* Configuration page for easier medication entry.\n\n * DONATIONS can be accepted from the config page!"); + text_layer_set_text(s_textlayer_features, "* Next medicatins now appear on the timeline"); text_layer_set_font(s_textlayer_features, s_res_gothic_14); layer_add_child(window_get_root_layer(s_window), (Layer *)s_textlayer_features); } From f9963769b93214aa9bbf1b85201ebb8f84d96dcd Mon Sep 17 00:00:00 2001 From: wildhart Date: Tue, 2 Feb 2016 16:17:42 +1300 Subject: [PATCH 2/6] Only 3 pins appearing --- src/pebble-js-app.js | 153 ++++++++++++++++++------------------------- 1 file changed, 64 insertions(+), 89 deletions(-) diff --git a/src/pebble-js-app.js b/src/pebble-js-app.js index 69b7fa4..d4aa684 100644 --- a/src/pebble-js-app.js +++ b/src/pebble-js-app.js @@ -3,11 +3,10 @@ Pebble.addEventListener('ready', function() { console.log('PebbleKit JS ready!'); var settings=localStorage.getItem("settings"); - //settings='{"101":"Ibuprofen|1454302849|6|1","102":"Tremadol|1454306400|8|1","103":"Omeprazole|1454265012|24|1","KEY_APP_VERSION":"1.4","KEY_SORT":1,"KEY_MEDICATIONS":"Paracetamol|1454292053|6|1","KEY_VERSION":3,"KEY_MODE":2,"KEY_TIMESTAMP":1454311036,"KEY_ALARM":0}'; - //settings='{"KEY_MEDICATIONS":"Paracetamol|1454292053|6|1","101":"Ibuprofen|1454302849|6|1","102":"Tremadol|1454306400|8|1","103":"Omeprazole|1454265012|24|1","KEY_APP_VERSION":"1.4","KEY_SORT":1,"KEY_VERSION":3,"KEY_MODE":2,"KEY_TIMESTAMP":1454311036,"KEY_ALARM":0}'; + //settings='{"101":"Tremadol|1454364000|8|1","102":"Paracetamol|1454378453|6|1","103":"Omeprazole|1454351412|24|1","KEY_APP_VERSION":"1.4","KEY_SORT":1,"KEY_MEDICATIONS":"Ibuprofen|1454367649|6|1","KEY_VERSION":3,"KEY_MODE":2,"KEY_TIMESTAMP":1454380831,"KEY_ALARM":1}'; var dict=settings ? JSON.parse(settings) : {}; if (!dict.KEY_TIMESTAMP) { - var d=new Date();$ + var d=new Date(); dict.KEY_TIMESTAMP = Math.floor(d.getTime()/1000 - d.getTimezoneOffset()*60); } sendDict(dict); @@ -22,7 +21,7 @@ Pebble.addEventListener("appmessage", function(e) { } else { localStorage.setItem("settings",JSON.stringify(e.payload)); } - createAllPins(e.payload,0); + createAllPins(e.payload); }); function sendDict(dict) { @@ -110,103 +109,78 @@ Pebble.addEventListener('webviewclosed', function(e) { /****************************** Custom Timeline Stuff ************************/ -// Pin template -var pin = { -// "id": "example-pin-0", -// "time": date.toISOString(), - "layout": { - "type": "genericPin", -// "title": "Take Ibuprofen", - "tinyIcon": "system://images/NOTIFICATION_REMINDER" - }, - "reminders": [ - { -// "time": date.toISOString(), - "layout": { - "type": "genericReminder", - "tinyIcon": "system://images/NOTIFICATION_REMINDER", -// "title": "Take Ibuprofen" - } - } - ], - "actions": [ - { - "title": "Med Taken", - "type": "openWatchApp", - "launchCode": 10 /*+med number*/ - }, - { - "title": "Open Meds Timer", - "type": "openWatchApp", - "launchCode": 0 - } - ], -}; - -var last_pin=0; -var total_pins=0; -var last_total_pins=0; -var dict_copy=null; -var pin_prefix="MedTimer-pin-"; - -function pinInsertedCallback(responseText) { - console.log('insertUserPin Result: ' + responseText); - createAllPins(dict_copy,last_pin+1); -} - -function createAllPins(dict,next_pin) { +function createAllPins(dict) { if (!dict) return; - dict_copy=dict; - if (next_pin===0) { - last_total_pins=localStorage.getItem("pins")*1.0; - last_total_pins=8; - total_pins=0; - } - last_pin=next_pin; - var setting; - setting=(last_pin===0) ? dict.KEY_MEDICATIONS : dict[100+last_pin]; - if (setting) { + var med=0; + var setting; + var pin_prefix="MedTimer-pin-"; + var requests_outstanding=0; + while (setting=(med===0) ? dict.KEY_MEDICATIONS : dict[100+med]) { // get the time setting=setting.split("|"); var d = new Date(0); // The 0 there is the key, which sets the date to the epoch d.setUTCSeconds(setting[1]*1.0 +setting[2]*3600); // setting[1] = last time, setting[2] = repeat_hrs d.setSeconds(0); d.setMilliseconds(0); - //var secs=d.getSeconds(); - /*d.setUTCSeconds(d.getTimezoneOffset()*60); // this sets seconds to zero */ - //setting[1]=padZero(d.getHours())+":"+padZero(d.getMinutes()); - //setting.push(secs); // configure the pin - pin.id=pin_prefix+last_pin; - pin.time=pin.reminders[0].time=d.toISOString(); - pin.layout.title=pin.reminders[0].layout.title="Take "+setting[0]; - pin.actions[0].launchCode=10+last_pin; - total_pins++; + var pin = { + "id": pin_prefix+med, + "time": d.toISOString(), + "layout": { + "type": "genericPin", + "title": "Take "+setting[0], + "tinyIcon": "system://images/NOTIFICATION_REMINDER" + }, + "reminders": [ + { + "time": d.toISOString(), + "layout": { + "type": "genericReminder", + "tinyIcon": "system://images/NOTIFICATION_REMINDER", + "title": "Take "+setting[0] + } + } + ], + "actions": [ + { + "title": "Med Taken", + "type": "openWatchApp", + "launchCode": 10+med + }, + { + "title": "Open Meds Timer", + "type": "openWatchApp", + "launchCode": 0 + } + ], + }; + // Push the pin console.log('Inserting pin in the future: ' + JSON.stringify(pin)); - insertUserPin(pin, pinInsertedCallback); - } else { - // no more meds to send - localStorage.setItem("pins",total_pins); - console.log("remembering pins: "+total_pins); - // delete any old pins - deleteAllPins(last_pin); + requests_outstanding++; + insertUserPin(pin, function(responseText) { + console.log('insertUserPin Result: ' + responseText); + if (--requests_outstanding===0) sendDict({KEY_PINS_DONE:1}); + }); + med++; } -} - -function pinDeletedCallback(responseText) { - console.log('deleteUserPin Result: ' + responseText); - deleteAllPins(last_pin+1); -} - -function deleteAllPins(next_pin) { - last_pin=next_pin; - if (last_pin Date: Tue, 2 Feb 2016 16:37:08 +1300 Subject: [PATCH 3/6] only 3 pins appearing on timeline --- src/pebble-js-app.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pebble-js-app.js b/src/pebble-js-app.js index d4aa684..192163f 100644 --- a/src/pebble-js-app.js +++ b/src/pebble-js-app.js @@ -167,7 +167,6 @@ function createAllPins(dict) { } var last_pins=localStorage.getItem("pins"); - last_pins=6; console.log("remembering pins: "+med); localStorage.setItem("pins",med); From ed8cf0d4096745fb2d79dce928ba477d287cca36 Mon Sep 17 00:00:00 2001 From: wildhart Date: Tue, 2 Feb 2016 21:57:52 +1300 Subject: [PATCH 4/6] timeline working --- appinfo.json | 1 + src/jobs.c | 1 - src/main.c | 26 +++++++++---- src/main.h | 8 +++- src/main_menu.c | 34 ++++++++++++++--- src/pebble-js-app.js | 88 ++++++++++++++++++++++++++------------------ src/update.c | 2 +- 7 files changed, 110 insertions(+), 50 deletions(-) diff --git a/appinfo.json b/appinfo.json index 36c8a60..95931ec 100644 --- a/appinfo.json +++ b/appinfo.json @@ -8,6 +8,7 @@ "KEY_MODE": 1, "KEY_PINS_DONE": 8, "KEY_SORT": 3, + "KEY_TIMELINE": 9, "KEY_TIMESTAMP": 7, "KEY_VERSION": 4 }, diff --git a/src/jobs.c b/src/jobs.c index 1852515..7cb0ad4 100644 --- a/src/jobs.c +++ b/src/jobs.c @@ -189,7 +189,6 @@ void jobs_rename_job(uint8_t index) { } void jobs_delete_all_jobs(void) { - Job_ptr* job_ptr = first_job_ptr; while (first_job_ptr) { Job_ptr * next_job=first_job_ptr->Next_ptr; free(first_job_ptr->Job); diff --git a/src/main.c b/src/main.c index 7b5eb70..9ee1c42 100644 --- a/src/main.c +++ b/src/main.c @@ -26,6 +26,7 @@ bool export_after_save=false; #ifdef PBL_SDK_3 bool waiting_for_pins=false; bool quit_after_pins=false; + uint8_t timeline_settings=TIMELINE_FLAG_ON | TIMELINE_FLAG_NOTIFICATIONS; #endif // ***************************************************************************************************** @@ -42,6 +43,7 @@ bool export_after_save=false; #define KEY_EXPORT 6 #define KEY_TIMESTAMP 7 #define KEY_PINS_DONE 8 +#define KEY_TIMELINE 9 static void send_settings_to_phone() { if (!JS_ready) return; @@ -52,9 +54,12 @@ static void send_settings_to_phone() { dict_write_cstring(iter, KEY_APP_VERSION, app_version); dummy_int=CURRENT_STORAGE_VERSION; dict_write_int(iter, KEY_VERSION, &dummy_int, sizeof(int), true); dummy_int=data_timestamp; dict_write_int(iter, KEY_TIMESTAMP, &dummy_int, sizeof(int), true); - dummy_int=settings.Mode; dict_write_int(iter, KEY_MODE, &dummy_int, sizeof(int), true); - dummy_int=settings.Alarm; dict_write_int(iter, KEY_ALARM, &dummy_int, sizeof(int), true); - dummy_int=settings.Sort; dict_write_int(iter, KEY_SORT, &dummy_int, sizeof(int), true); + dummy_int=settings.Mode; dict_write_int(iter, KEY_MODE, &dummy_int, sizeof(int), true); + dummy_int=settings.Alarm; dict_write_int(iter, KEY_ALARM, &dummy_int, sizeof(int), true); + dummy_int=settings.Sort; dict_write_int(iter, KEY_SORT, &dummy_int, sizeof(int), true); + #ifdef PBL_SDK_3 + dummy_int=timeline_settings; dict_write_int(iter, KEY_TIMELINE, &dummy_int, sizeof(int), true); + #endif jobs_list_write_dict(iter, KEY_MEDICATIONS); if (export_after_save) { @@ -76,11 +81,9 @@ static void inbox_received_handler(DictionaryIterator *iter, void *context) { Tuple *tuple_t; #ifdef PBL_SDK_3 - if (waiting_for_pins && dict_find(iter, KEY_PINS_DONE)) { + if (dict_find(iter, KEY_PINS_DONE)) { waiting_for_pins=false; - if (quit_after_pins) { - main_menu_hide(); - } + if (quit_after_pins) main_menu_hide(); return; } #endif @@ -96,6 +99,9 @@ static void inbox_received_handler(DictionaryIterator *iter, void *context) { tuple_t=dict_find(iter, KEY_MODE); if (tuple_t) settings.Mode = tuple_t->value->int32; tuple_t=dict_find(iter, KEY_ALARM); if (tuple_t) settings.Alarm = tuple_t->value->int8 > 0; // convert int to boolean tuple_t=dict_find(iter, KEY_SORT); if (tuple_t) settings.Sort = tuple_t->value->int8 > 0; // convert int to boolean + #ifdef PBL_SDK_3 + tuple_t=dict_find(iter, KEY_TIMELINE); if (tuple_t) timeline_settings = tuple_t->value->int32; + #endif jobs_delete_all_jobs(); jobs_list_read_dict(iter, KEY_MEDICATIONS, stored_version); @@ -137,6 +143,9 @@ void main_save_data(void) { persist_write_int(STORAGE_KEY_VERSION, CURRENT_STORAGE_VERSION); data_timestamp=time(NULL); persist_write_int(STORAGE_KEY_TIMESTAMP, data_timestamp); + #ifdef PBL_IF_SDK_3 + persist_write_int(STORAGE_KEY_TIMELINE, timeline_settings); + #endif persist_write_data(STORAGE_KEY_SETTINGS, &settings, sizeof(Settings)); if (settings.Sort) jobs_list_sort(); jobs_list_save(STORAGE_KEY_FIRST_MED); @@ -150,6 +159,9 @@ static void main_load_data(void) { if (stored_version) { data_loaded_from_watch = true; if (persist_exists(STORAGE_KEY_TIMESTAMP)) data_timestamp=persist_read_int(STORAGE_KEY_TIMESTAMP); + #ifdef PBL_IF_SDK_3 + timeline_settings = persist_exists(STORAGE_KEY_TIMELINE) ? persist_read_int(STORAGE_KEY_TIMELINE) : (TIMELINE_FLAG_ON | TIMELINE_FLAG_NOTIFICATIONS); + #endif persist_read_data(STORAGE_KEY_SETTINGS, &settings, sizeof(Settings)); jobs_list_load(STORAGE_KEY_FIRST_MED, stored_version); if (stored_version < CURRENT_STORAGE_VERSION) { diff --git a/src/main.h b/src/main.h index 9e53f53..d0dcc47 100644 --- a/src/main.h +++ b/src/main.h @@ -65,6 +65,7 @@ #define FONT_GOTHIC_14 fonts_get_system_font(FONT_KEY_GOTHIC_14) #define FONT_BITHAM_30_BLACK fonts_get_system_font(FONT_KEY_BITHAM_30_BLACK) #define FONT_BITHAM_34_MEDIUM_NUMBERS fonts_get_system_font(FONT_KEY_BITHAM_34_MEDIUM_NUMBERS) +#define FONT_ROBOTO_21_CONDENSED fonts_get_system_font(FONT_KEY_ROBOTO_CONDENSED_21) #define ICON_RECT_PLAY (GRect) { { 0, 0 }, { 16, 16 } } #define ICON_RECT_PAUSE (GRect) { { 16, 0 }, { 16, 16 } } @@ -99,11 +100,13 @@ extern GBitmap *bitmap_tick; #define STORAGE_KEY_VERSION 1 #define STORAGE_KEY_SETTINGS 2 #define STORAGE_KEY_TIMESTAMP 3 +#define STORAGE_KEY_TIMELINE 4 #define STORAGE_KEY_FIRST_MED 100 -#define CURRENT_STORAGE_VERSION 3 +#define CURRENT_STORAGE_VERSION 4 //changes in storage version: 2 added bool Fixed to end of every Job struct //changes in storage version: 3 configuration +//changes in storage version: 4 timeline typedef struct { uint8_t Mode; @@ -124,6 +127,9 @@ enum { #ifdef PBL_SDK_3 extern bool waiting_for_pins; extern bool quit_after_pins; + extern uint8_t timeline_settings; +#define TIMELINE_FLAG_ON 1 +#define TIMELINE_FLAG_NOTIFICATIONS 2 #endif void main_save_data(void); \ No newline at end of file diff --git a/src/main_menu.c b/src/main_menu.c index 3b2d3c8..ea0baa5 100644 --- a/src/main_menu.c +++ b/src/main_menu.c @@ -4,6 +4,7 @@ static Window *s_window; static MenuLayer *s_menulayer; #ifdef PBL_SDK_3 static StatusBarLayer *s_status_bar; +static TextLayer *s_textlayer_wait; #endif static bool check_phone_message = false; @@ -15,6 +16,7 @@ static bool check_phone_message = false; void back_button_handler(ClickRecognizerRef recognizer, void *context) { if (waiting_for_pins) { quit_after_pins=true; + layer_set_hidden((Layer *)s_textlayer_wait,false); } else { main_menu_hide(); } @@ -50,11 +52,16 @@ static void initialise_ui(void) { s_menulayer = menu_layer_create(GRect(0, STATUS_BAR_LAYER_HEIGHT, bounds.size.w, bounds.size.h-STATUS_BAR_LAYER_HEIGHT)); menu_layer_set_click_config_onto_window(s_menulayer, s_window); layer_add_child(window_get_root_layer(s_window), (Layer *)s_menulayer); - #ifdef PBL_SDK_3 - force_back_button(s_window,s_menulayer); - #endif #ifdef PBL_SDK_3 + force_back_button(s_window,s_menulayer); + // s_textlayer_wait + s_textlayer_wait = text_layer_create(GRect(14, (bounds.size.h-5*26)/2, bounds.size.w-2*14, 5*26)); + text_layer_set_text(s_textlayer_wait, "\nUpdating timeline before closing..."); + text_layer_set_text_alignment(s_textlayer_wait, GTextAlignmentCenter); + text_layer_set_font(s_textlayer_wait, FONT_ROBOTO_21_CONDENSED); + layer_set_hidden((Layer *)s_textlayer_wait,true); + layer_add_child(window_get_root_layer(s_window), (Layer *)s_textlayer_wait); // Set up the status bar last to ensure it is on top of other Layers s_status_bar = status_bar_layer_create(); layer_add_child(window_get_root_layer(s_window), status_bar_layer_get_layer(s_status_bar)); @@ -67,6 +74,7 @@ static void destroy_ui(void) { #ifdef PBL_SDK_3 status_bar_layer_destroy(s_status_bar); + text_layer_destroy(s_textlayer_wait); #endif } @@ -96,8 +104,12 @@ enum { // main menu structure MENU_SETTINGS_MODE=MENU_SECTION_SETTINGS*100, MENU_SETTINGS_ALARM, MENU_SETTINGS_SORT, + #ifdef PBL_SDK_3 + MENU_SETTINGS_TIMELINE, + MENU_SETTINGS_TL_NOTIFICATIONS, + #endif MENU_SETTINGS_CONFIG, - NUM_MENU_ITEMS_SETTINGS=4 + NUM_MENU_ITEMS_SETTINGS=PBL_IF_SDK_3_ELSE(6, 4) }; static uint16_t menu_get_num_sections_callback(MenuLayer *menu_layer, void *data) { @@ -189,8 +201,10 @@ static void menu_draw_row_callback(GContext* ctx, const Layer *cell_layer, MenuI case MENU_SETTINGS_MODE: menu_cell_draw_setting(ctx, cell_layer, "Mode", mode[settings.Mode],NULL); break; - case MENU_SETTINGS_ALARM: menu_cell_draw_setting(ctx, cell_layer, "Alarm", settings.Alarm ? "YES" : "NO",NULL); break; + case MENU_SETTINGS_ALARM: menu_cell_draw_setting(ctx, cell_layer, "Alarm", settings.Alarm ? "ON" : "OFF",NULL); break; case MENU_SETTINGS_SORT: menu_cell_draw_setting(ctx, cell_layer, "Sort", settings.Sort ? "YES" : "NO",NULL); break; + case MENU_SETTINGS_TIMELINE: menu_cell_draw_setting(ctx, cell_layer, "Timeline", timeline_settings&TIMELINE_FLAG_ON ? "ON" : "OFF",NULL); break; + case MENU_SETTINGS_TL_NOTIFICATIONS: menu_cell_draw_setting(ctx, cell_layer, "..Notifications", timeline_settings&TIMELINE_FLAG_NOTIFICATIONS ? "ON" : "OFF",NULL); break; case MENU_SETTINGS_CONFIG: menu_cell_draw_other(ctx, cell_layer, check_phone_message ? "Check phone..." : "Config/Donate", NULL , bitmap_settings); break; } } @@ -224,6 +238,16 @@ static void menu_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, v main_save_data(); menu_layer_reload_data(s_menulayer); break; + case MENU_SETTINGS_TIMELINE: + timeline_settings^=TIMELINE_FLAG_ON ; // ^ = XOR + main_save_data(); + menu_layer_reload_data(s_menulayer); + break; + case MENU_SETTINGS_TL_NOTIFICATIONS: + timeline_settings^=TIMELINE_FLAG_NOTIFICATIONS ; // ^ = XOR + main_save_data(); + menu_layer_reload_data(s_menulayer); + break; case MENU_SETTINGS_CONFIG: export_after_save=true; main_save_data(); diff --git a/src/pebble-js-app.js b/src/pebble-js-app.js index 192163f..33003c3 100644 --- a/src/pebble-js-app.js +++ b/src/pebble-js-app.js @@ -1,5 +1,8 @@ // https://github.com/pebble-examples/slate-config-example/blob/master/src/js/pebble-js-app.js +var TIMELINE_FLAG_ON = 1; +var TIMELINE_FLAG_NOTIFICATIONS = 2; + Pebble.addEventListener('ready', function() { console.log('PebbleKit JS ready!'); var settings=localStorage.getItem("settings"); @@ -21,7 +24,10 @@ Pebble.addEventListener("appmessage", function(e) { } else { localStorage.setItem("settings",JSON.stringify(e.payload)); } - createAllPins(e.payload); + if (typeof(e.payload.KEY_TIMELINE)!="undefined") { + deleteAllPins(); + if (e.payload.KEY_TIMELINE & TIMELINE_FLAG_ON) createAllPins(e.payload); else sendDict({KEY_PINS_DONE:1}); + } }); function sendDict(dict) { @@ -49,6 +55,7 @@ function showConfiguration() { config+="&mode="+settings.KEY_MODE; config+="&alarm="+settings.KEY_ALARM; config+="&sort="+settings.KEY_SORT; + if (settings.KEY_TIMELINE) config+="&timeline="+settings.KEY_TIMELINE; var med=0; var setting; @@ -77,12 +84,13 @@ Pebble.addEventListener('webviewclosed', function(e) { var dict = {}; dict.KEY_CONFIG_DATA = 1; - dict.KEY_MODE = configData.mode; + dict.KEY_MODE = configData.mode*1.0; // convert to integer dict.KEY_ALARM = configData.alarm ? 1 : 0; // Send a boolean as an integer dict.KEY_SORT = configData.sort ? 1 : 0; // Send a boolean as an integer dict.KEY_VERSION = configData.data_version; var d=new Date(); dict.KEY_TIMESTAMP = Math.floor(d.getTime()/1000 /*- d.getTimezoneOffset()*60 */); + if (typeof(configData.timeline)!="undefined") dict.KEY_TIMELINE=configData.timeline*1.0; var med=0; var data; @@ -109,12 +117,34 @@ Pebble.addEventListener('webviewclosed', function(e) { /****************************** Custom Timeline Stuff ************************/ +var pin_prefix="MedTimer-pin-"; +var requests_outstanding=0; + +function deleteAllPins() { + // delete any old pins + var pin; + var last_pins=localStorage.getItem("pins"); + var pin_old_now=localStorage.getItem("pin_now"); + var med=0; + console.log("last pins: "+pin_old_now+"-"+last_pins); + while (med Date: Wed, 3 Feb 2016 08:44:08 +1300 Subject: [PATCH 5/6] Improved for Pebble Round --- appinfo.json | 2 +- src/main.c | 4 ++-- src/main.h | 1 + src/main_menu.c | 49 ++++++++++++++++++++++++++------------------ src/pebble-js-app.js | 3 ++- src/update.c | 36 +++++++++++++++++++++++--------- 6 files changed, 61 insertions(+), 34 deletions(-) diff --git a/appinfo.json b/appinfo.json index 95931ec..24dd564 100644 --- a/appinfo.json +++ b/appinfo.json @@ -36,7 +36,7 @@ "sdkVersion": "3", "shortName": "Meds Timer", "targetPlatforms": [ - "aplite" + "basalt" ], "uuid": "95d07e1a-2451-4ffa-aa44-836e523a7648", "versionLabel": "1.4", diff --git a/src/main.c b/src/main.c index 9ee1c42..42dbd49 100644 --- a/src/main.c +++ b/src/main.c @@ -143,7 +143,7 @@ void main_save_data(void) { persist_write_int(STORAGE_KEY_VERSION, CURRENT_STORAGE_VERSION); data_timestamp=time(NULL); persist_write_int(STORAGE_KEY_TIMESTAMP, data_timestamp); - #ifdef PBL_IF_SDK_3 + #ifdef PBL_SDK_3 persist_write_int(STORAGE_KEY_TIMELINE, timeline_settings); #endif persist_write_data(STORAGE_KEY_SETTINGS, &settings, sizeof(Settings)); @@ -159,7 +159,7 @@ static void main_load_data(void) { if (stored_version) { data_loaded_from_watch = true; if (persist_exists(STORAGE_KEY_TIMESTAMP)) data_timestamp=persist_read_int(STORAGE_KEY_TIMESTAMP); - #ifdef PBL_IF_SDK_3 + #ifdef PBL_SDK_3 timeline_settings = persist_exists(STORAGE_KEY_TIMELINE) ? persist_read_int(STORAGE_KEY_TIMELINE) : (TIMELINE_FLAG_ON | TIMELINE_FLAG_NOTIFICATIONS); #endif persist_read_data(STORAGE_KEY_SETTINGS, &settings, sizeof(Settings)); diff --git a/src/main.h b/src/main.h index d0dcc47..3c9d777 100644 --- a/src/main.h +++ b/src/main.h @@ -59,6 +59,7 @@ #define END_TIME(JOB) ((time_t) (JOB)->Seconds + (time_t) (JOB)->Repeat_hrs*3600) #define FONT_GOTHIC_24_BOLD fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD) +#define FONT_GOTHIC_28_BOLD fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD) #define FONT_GOTHIC_18_BOLD fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD) #define FONT_GOTHIC_14_BOLD fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD) #define FONT_GOTHIC_18 fonts_get_system_font(FONT_KEY_GOTHIC_18) diff --git a/src/main_menu.c b/src/main_menu.c index ea0baa5..f927c90 100644 --- a/src/main_menu.c +++ b/src/main_menu.c @@ -45,8 +45,8 @@ static void initialise_ui(void) { #ifndef PBL_SDK_3 window_set_fullscreen(s_window, false); #endif - GRect bounds = layer_get_bounds(window_get_root_layer(s_window)); + uint8_t margin=(bounds.size.w-144)/2; // s_menulayer s_menulayer = menu_layer_create(GRect(0, STATUS_BAR_LAYER_HEIGHT, bounds.size.w, bounds.size.h-STATUS_BAR_LAYER_HEIGHT)); @@ -56,7 +56,9 @@ static void initialise_ui(void) { #ifdef PBL_SDK_3 force_back_button(s_window,s_menulayer); // s_textlayer_wait - s_textlayer_wait = text_layer_create(GRect(14, (bounds.size.h-5*26)/2, bounds.size.w-2*14, 5*26)); + s_textlayer_wait = text_layer_create(GRect(14+margin, (bounds.size.h-5*26)/2, bounds.size.w-2*(14+margin), 5*26)); + text_layer_set_background_color(s_textlayer_wait,GColorBlack); + text_layer_set_text_color(s_textlayer_wait,GColorWhite); text_layer_set_text(s_textlayer_wait, "\nUpdating timeline before closing..."); text_layer_set_text_alignment(s_textlayer_wait, GTextAlignmentCenter); text_layer_set_font(s_textlayer_wait, FONT_ROBOTO_21_CONDENSED); @@ -137,7 +139,7 @@ static void menu_draw_header_callback(GContext* ctx, const Layer *cell_layer, ui case MENU_SECTION_JOBS: case MENU_SECTION_OTHER: break; - case MENU_SECTION_SETTINGS: menu_cell_basic_header_draw(ctx, cell_layer, "Options"); break; + case MENU_SECTION_SETTINGS: menu_cell_basic_header_draw(ctx, cell_layer, PBL_IF_ROUND_ELSE(" Options","Options")); break; } } @@ -155,10 +157,11 @@ void menu_cell_draw_job(GContext* ctx, const Layer *cell_layer, const uint8_t in #ifndef PBL_SDK_3 graphics_context_set_text_color(ctx, GColorBlack); #endif + uint8_t margin=(bounds.size.w-144)/2; - graphics_draw_text(ctx, jobs_get_job_name(index), FONT_GOTHIC_24_BOLD, GRect(4, -4, bounds.size.w-8, 4+18), GTextOverflowModeFill, GTextAlignmentLeft, NULL); - graphics_draw_text(ctx, jobs_get_job_clock_as_text(index), FONT_GOTHIC_18, GRect(4, 20, bounds.size.w-8, 14), GTextOverflowModeFill, GTextAlignmentRight, NULL); - graphics_draw_text(ctx, jobs_get_job_repeat_as_text(index), FONT_GOTHIC_14, GRect(4, 20+4, bounds.size.w-8, 14), GTextOverflowModeFill, GTextAlignmentLeft, NULL); + graphics_draw_text(ctx, jobs_get_job_name(index), FONT_GOTHIC_24_BOLD, GRect(4+margin, -4, bounds.size.w-2*(4+margin), 4+18), GTextOverflowModeFill, GTextAlignmentLeft, NULL); + graphics_draw_text(ctx, jobs_get_job_clock_as_text(index), FONT_GOTHIC_18, GRect(4+margin, 20, bounds.size.w-2*(4+margin), 14), GTextOverflowModeFill, GTextAlignmentRight, NULL); + graphics_draw_text(ctx, jobs_get_job_repeat_as_text(index), FONT_GOTHIC_14, GRect(4+margin, 20+4, bounds.size.w-2*(4+margin), 14), GTextOverflowModeFill, GTextAlignmentLeft, NULL); //graphics_draw_bitmap_in_rect(ctx, timer.Active && timer.Job==index ? bitmap_play : bitmap_pause, GRect(6, (bounds.size.h-16)/2, 16, 16)); } @@ -169,11 +172,12 @@ void menu_cell_draw_other(GContext* ctx, const Layer *cell_layer, const char *ti #ifndef PBL_SDK_3 graphics_context_set_text_color(ctx, GColorBlack); #endif + uint8_t margin=(bounds.size.w-144)/2; - graphics_draw_text(ctx, title, FONT_GOTHIC_24_BOLD, GRect(28, -4, bounds.size.w-28, 4+18), GTextOverflowModeFill, GTextAlignmentLeft, NULL); - if (sub_title) graphics_draw_text(ctx, sub_title, FONT_GOTHIC_18, GRect(28, 20, bounds.size.w-28-4, 14), GTextOverflowModeFill, GTextAlignmentLeft, NULL); + graphics_draw_text(ctx, title, FONT_GOTHIC_24_BOLD, GRect(28+margin, -4, bounds.size.w-28-2*margin, 4+18), GTextOverflowModeFill, GTextAlignmentLeft, NULL); + if (sub_title) graphics_draw_text(ctx, sub_title, FONT_GOTHIC_18, GRect(28+margin, 20, bounds.size.w-28-margin-4, 14), GTextOverflowModeFill, GTextAlignmentLeft, NULL); - if (icon) graphics_draw_bitmap_in_rect(ctx, icon, GRect(6,(bounds.size.h-16)/2, 16, 16)); + if (icon) graphics_draw_bitmap_in_rect(ctx, icon, GRect(6+margin,(bounds.size.h-16)/2, 16, 16)); } static void menu_cell_draw_setting(GContext* ctx, const Layer *cell_layer, const char *title, const char *setting, const char *hint) { @@ -182,10 +186,11 @@ static void menu_cell_draw_setting(GContext* ctx, const Layer *cell_layer, const #ifndef PBL_SDK_3 graphics_context_set_text_color(ctx, GColorBlack); #endif + uint8_t margin=(bounds.size.w-144)/2; - graphics_draw_text(ctx, title, FONT_GOTHIC_24_BOLD, GRect(4, -4, bounds.size.w-8, 4+18), GTextOverflowModeFill, GTextAlignmentLeft, NULL); - graphics_draw_text(ctx, setting, FONT_GOTHIC_18_BOLD, GRect(4, 2, bounds.size.w-8, 18), GTextOverflowModeFill, GTextAlignmentRight, NULL); - graphics_draw_text(ctx, hint, FONT_GOTHIC_18, GRect(4, 20, bounds.size.w-8, 14), GTextOverflowModeFill, GTextAlignmentLeft, NULL); + graphics_draw_text(ctx, title, FONT_GOTHIC_24_BOLD, GRect(4+margin, -4, bounds.size.w-2*(4+margin), 4+18), GTextOverflowModeFill, GTextAlignmentLeft, NULL); + graphics_draw_text(ctx, setting, FONT_GOTHIC_18_BOLD, GRect(4+margin, 2, bounds.size.w-2*(4+margin), 18), GTextOverflowModeFill, GTextAlignmentRight, NULL); + graphics_draw_text(ctx, hint, FONT_GOTHIC_18, GRect(4+margin, 20, bounds.size.w-2*(4+margin), 14), GTextOverflowModeFill, GTextAlignmentLeft, NULL); } static void menu_draw_row_callback(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, void *data) { @@ -203,9 +208,11 @@ static void menu_draw_row_callback(GContext* ctx, const Layer *cell_layer, MenuI break; case MENU_SETTINGS_ALARM: menu_cell_draw_setting(ctx, cell_layer, "Alarm", settings.Alarm ? "ON" : "OFF",NULL); break; case MENU_SETTINGS_SORT: menu_cell_draw_setting(ctx, cell_layer, "Sort", settings.Sort ? "YES" : "NO",NULL); break; + case MENU_SETTINGS_CONFIG: menu_cell_draw_other(ctx, cell_layer, check_phone_message ? "Check phone..." : "Config/Donate", NULL , bitmap_settings); break; + #ifdef PBL_SDK_3 case MENU_SETTINGS_TIMELINE: menu_cell_draw_setting(ctx, cell_layer, "Timeline", timeline_settings&TIMELINE_FLAG_ON ? "ON" : "OFF",NULL); break; case MENU_SETTINGS_TL_NOTIFICATIONS: menu_cell_draw_setting(ctx, cell_layer, "..Notifications", timeline_settings&TIMELINE_FLAG_NOTIFICATIONS ? "ON" : "OFF",NULL); break; - case MENU_SETTINGS_CONFIG: menu_cell_draw_other(ctx, cell_layer, check_phone_message ? "Check phone..." : "Config/Donate", NULL , bitmap_settings); break; + #endif } } } @@ -238,6 +245,14 @@ static void menu_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, v main_save_data(); menu_layer_reload_data(s_menulayer); break; + case MENU_SETTINGS_CONFIG: + export_after_save=true; + main_save_data(); + check_phone_message=true; + menu_layer_reload_data(s_menulayer); + app_timer_register(2000 /* milliseconds */, timer_callback, NULL); + break; + #ifdef PBL_SDK_3 case MENU_SETTINGS_TIMELINE: timeline_settings^=TIMELINE_FLAG_ON ; // ^ = XOR main_save_data(); @@ -248,13 +263,7 @@ static void menu_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, v main_save_data(); menu_layer_reload_data(s_menulayer); break; - case MENU_SETTINGS_CONFIG: - export_after_save=true; - main_save_data(); - check_phone_message=true; - menu_layer_reload_data(s_menulayer); - app_timer_register(2000 /* milliseconds */, timer_callback, NULL); - break; + #endif } } } diff --git a/src/pebble-js-app.js b/src/pebble-js-app.js index 33003c3..7e91fdb 100644 --- a/src/pebble-js-app.js +++ b/src/pebble-js-app.js @@ -26,7 +26,8 @@ Pebble.addEventListener("appmessage", function(e) { } if (typeof(e.payload.KEY_TIMELINE)!="undefined") { deleteAllPins(); - if (e.payload.KEY_TIMELINE & TIMELINE_FLAG_ON) createAllPins(e.payload); else sendDict({KEY_PINS_DONE:1}); + if (e.payload.KEY_TIMELINE & TIMELINE_FLAG_ON) createAllPins(e.payload); + if (requests_outstanding===0) sendDict({KEY_PINS_DONE:1}); } }); diff --git a/src/update.c b/src/update.c index 5cf70e0..d734949 100644 --- a/src/update.c +++ b/src/update.c @@ -3,30 +3,46 @@ // BEGIN AUTO-GENERATED UI CODE; DO NOT MODIFY static Window *s_window; -static GFont s_res_gothic_28_bold; -static GFont s_res_gothic_14; static TextLayer *s_textlayer_heading; static TextLayer *s_textlayer_features; +#define MARGIN PBL_IF_ROUND_ELSE(12,0) +#define HEIGHT PBL_IF_ROUND_ELSE(56,32) + +void select_button_handler(ClickRecognizerRef recognizer, void *context) { + update_hide(); +} + +void click_config_provider(void *context) { + window_single_click_subscribe(BUTTON_ID_SELECT, select_button_handler); +} + static void initialise_ui(void) { s_window = window_create(); #ifndef PBL_SDK_3 window_set_fullscreen(s_window, false); #endif - s_res_gothic_28_bold = fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD); - s_res_gothic_14 = fonts_get_system_font(FONT_KEY_GOTHIC_18); + GRect bounds = layer_get_bounds(window_get_root_layer(s_window)); + // s_textlayer_heading - s_textlayer_heading = text_layer_create(GRect(2, -1, 140, 28)); - text_layer_set_text(s_textlayer_heading, "New Feature:"); - text_layer_set_font(s_textlayer_heading, s_res_gothic_28_bold); + s_textlayer_heading = text_layer_create(GRect(0, -1, bounds.size.w, HEIGHT+MARGIN)); + text_layer_set_text(s_textlayer_heading, PBL_IF_ROUND_ELSE("New\nFeatures:","New Features:")); + text_layer_set_background_color(s_textlayer_heading,GColorBlack); + text_layer_set_text_alignment(s_textlayer_heading, GTextAlignmentCenter); + text_layer_set_text_color(s_textlayer_heading,GColorWhite); + text_layer_set_font(s_textlayer_heading, FONT_GOTHIC_28_BOLD); layer_add_child(window_get_root_layer(s_window), (Layer *)s_textlayer_heading); // s_textlayer_features - s_textlayer_features = text_layer_create(GRect(2, 27, 140, 125)); - text_layer_set_text(s_textlayer_features, "* Medications now appear on the timeline"); - text_layer_set_font(s_textlayer_features, s_res_gothic_14); + s_textlayer_features = text_layer_create(GRect(2+MARGIN, HEIGHT+MARGIN, bounds.size.w-2*(2+MARGIN), bounds.size.h-HEIGHT-MARGIN)); + text_layer_set_text(s_textlayer_features, "* Medications now appear on the timeline\n\n* Optimised for Pebble Time Round"); + text_layer_set_font(s_textlayer_features, FONT_GOTHIC_18); + #ifdef PBL_ROUND + text_layer_set_text_alignment(s_textlayer_features, GTextAlignmentCenter); + #endif layer_add_child(window_get_root_layer(s_window), (Layer *)s_textlayer_features); + window_set_click_config_provider(s_window,(void*)click_config_provider); } static void destroy_ui(void) { From 264ef15b841cc54c5035d47ce223248cd2ecfe91 Mon Sep 17 00:00:00 2001 From: wildhart Date: Sat, 6 Feb 2016 19:55:02 +1300 Subject: [PATCH 6/6] timeline and Round done. --- appinfo.json | 16 +- resources/images/icons-inv.png | Bin 0 -> 763 bytes src/job_adjust.c | 68 +++++---- src/job_menu.c | 12 +- src/jobs.c | 266 ++++++++++----------------------- src/jobs.h | 15 +- src/main.c | 95 ++++++------ src/main.h | 33 ++-- src/main_menu.c | 59 ++------ src/main_menu.h | 2 +- src/pebble-js-app.js | 90 +++++++---- src/update.c | 2 +- 12 files changed, 283 insertions(+), 375 deletions(-) create mode 100644 resources/images/icons-inv.png diff --git a/appinfo.json b/appinfo.json index 24dd564..97645ae 100644 --- a/appinfo.json +++ b/appinfo.json @@ -6,9 +6,8 @@ "KEY_EXPORT": 6, "KEY_MEDICATIONS": 100, "KEY_MODE": 1, - "KEY_PINS_DONE": 8, "KEY_SORT": 3, - "KEY_TIMELINE": 9, + "KEY_TIMELINE": 8, "KEY_TIMESTAMP": 7, "KEY_VERSION": 4 }, @@ -16,10 +15,17 @@ "configurable" ], "companyName": "cmorison@gmail.com", + "enableMultiJS": false, "longName": "Medication Timer", "projectType": "native", "resources": { "media": [ + { + "file": "images/icons-inv.png", + "name": "IMAGE_ICON_MATRIX_INV", + "targetPlatforms": null, + "type": "png" + }, { "file": "images/menu_icon.png", "menuIcon": true, @@ -36,10 +42,12 @@ "sdkVersion": "3", "shortName": "Meds Timer", "targetPlatforms": [ - "basalt" + "aplite", + "basalt", + "chalk" ], "uuid": "95d07e1a-2451-4ffa-aa44-836e523a7648", - "versionLabel": "1.4", + "versionLabel": "1.6", "watchapp": { "watchface": false } diff --git a/resources/images/icons-inv.png b/resources/images/icons-inv.png new file mode 100644 index 0000000000000000000000000000000000000000..b454339fb1044afd71f7ce6527f79f264b61d411 GIT binary patch literal 763 zcmVPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2jBt% z76lXQJ3x>C00MhSL_t(|+U;3ElEfegRLlQ=*}d#+Weq3|=tK?2j3LrO(=xG?QbGtJ z#uzmA`})mlf807xLt`kVgj{b>3&cuxJ&er#xFZl$7UtgK9e`++UzWbSA`lYZ$*8jx zfItX0Yn?w42u-KSGe7o3OW|-+0}x}}qC;d&;7_&B-iDCiN~etgk#`DQ14Q02a0H0F zbKnvn@(F>rfQTW4B2{^_RG@UmfcW6vO&W(DDzKDt^FuvDejg@;sE=AUBJuYTT9n}O z1R-hDQM-{|@SMV|pL>A-=>RJS^ft|%^!0g+ac%}^Y8eUMyOgF~hpEl!mwugN0kftrZw9Omz>b-csFrh*ng1NMX2Q1tPuk7yk}2t= tdVjmX2o6}i6w*oc{+7TCzrow) { case MENU_JOB: menu_cell_draw_job(ctx, cell_layer, job_index); break; - case MENU_RESET: menu_cell_draw_other(ctx, cell_layer, "Reset Timer", NULL, bitmap_reset); break; - case MENU_ADD10: menu_cell_draw_other(ctx, cell_layer, "Add 10 Minutes", NULL, bitmap_add); break; - case MENU_SUB10: menu_cell_draw_other(ctx, cell_layer, "Sub 10 Minutes", NULL, bitmap_minus); break; - case MENU_RENAME: menu_cell_draw_other(ctx, cell_layer, "Rename", NULL, bitmap_edit); break; - case MENU_ADJUST: menu_cell_draw_other(ctx, cell_layer, "Set Repeat", NULL, bitmap_adjust); break; - case MENU_DELETE: menu_cell_draw_other(ctx, cell_layer, "Delete", NULL, bitmap_delete); break; + case MENU_RESET: menu_cell_draw_other(ctx, cell_layer, "Reset Timer", NULL, bitmaps[BITMAP_RESET]); break; + case MENU_ADD10: menu_cell_draw_other(ctx, cell_layer, "Add 10 Minutes", NULL, bitmaps[BITMAP_ADD]); break; + case MENU_SUB10: menu_cell_draw_other(ctx, cell_layer, "Sub 10 Minutes", NULL, bitmaps[BITMAP_MINUS]); break; + case MENU_RENAME: menu_cell_draw_other(ctx, cell_layer, "Rename", NULL, bitmaps[BITMAP_EDIT]); break; + case MENU_ADJUST: menu_cell_draw_other(ctx, cell_layer, "Set Repeat", NULL, bitmaps[BITMAP_ADJUST]); break; + case MENU_DELETE: menu_cell_draw_other(ctx, cell_layer, "Delete", NULL, bitmaps[BITMAP_DELETE]); break; } } diff --git a/src/jobs.c b/src/jobs.c index 7cb0ad4..c3ad2e3 100644 --- a/src/jobs.c +++ b/src/jobs.c @@ -1,19 +1,6 @@ #include "main.h" -#define JOB_NAME_LENGTH 24 -typedef struct { - char Name[JOB_NAME_LENGTH]; - time_t Seconds; - uint8_t Repeat_hrs; - bool Fixed; -} Job; - -typedef struct Job_ptr { - Job* Job; - struct Job_ptr* Next_ptr; -} Job_ptr ; - -static Job_ptr* first_job_ptr=NULL; +struct Job jobs[MAX_JOBS]; uint8_t jobs_count=0; // ***************************************************************************************************** @@ -21,89 +8,60 @@ uint8_t jobs_count=0; // ***************************************************************************************************** static void jobs_list_append_job(const char* name, time_t seconds, uint8_t repeat, int fixed) { - Job* new_job = malloc(sizeof(Job)); - Job_ptr* new_job_ptr = malloc(sizeof(Job_ptr)); - - new_job_ptr->Job = new_job; - new_job_ptr->Next_ptr = NULL; - strncpy(new_job->Name, name, JOB_NAME_LENGTH); + if (jobs_count==MAX_JOBS) return; + struct Job* new_job=&jobs[jobs_count++]; + strncpy(new_job->Name, name, JOB_NAME_SIZE)[JOB_NAME_SIZE-1]=0; new_job->Seconds = seconds; new_job->Repeat_hrs = repeat; new_job->Fixed = fixed ? true : false; - - if (first_job_ptr) { - Job_ptr* last_job_ptr = first_job_ptr; - while (last_job_ptr->Next_ptr) last_job_ptr=last_job_ptr->Next_ptr; - last_job_ptr->Next_ptr = new_job_ptr; - } else { - first_job_ptr = new_job_ptr; - } LOG("appended job: %s, seconds=%ld, repeat=%d, fixed=%d", new_job->Name, new_job->Seconds, new_job->Repeat_hrs, new_job->Fixed); - jobs_count++; } void jobs_list_sort(void) { - time_t end_time; - - Job_ptr* job_ptr_before = first_job_ptr; - while (job_ptr_before && job_ptr_before->Next_ptr) { - end_time = END_TIME(job_ptr_before->Job); - Job_ptr* job_ptr_min = job_ptr_before; + for (uint8_t before=0; beforeNext_ptr; - while (job_ptr_loop) { - if (END_TIME(job_ptr_loop->Job) < end_time) { - end_time = END_TIME(job_ptr_loop->Job); - job_ptr_min = job_ptr_loop; + for (uint8_t loop=before+1; loopNext_ptr; } - if (job_ptr_min != job_ptr_before) { + if (job_min!=before) { // swap med - Job* temp_job = job_ptr_before->Job; - job_ptr_before->Job = job_ptr_min->Job; - job_ptr_min->Job = temp_job; + struct Job temp_job = jobs[before]; + jobs[before] = jobs[job_min]; + jobs[job_min] = temp_job; } - - job_ptr_before = job_ptr_before->Next_ptr; } } void jobs_list_save(uint8_t first_key) { - Job_ptr* job_ptr = first_job_ptr; - while (job_ptr) { - persist_write_data(first_key++, job_ptr->Job, sizeof(Job)); - job_ptr=job_ptr->Next_ptr; - } + for (uint8_t a=0; aJob; - snprintf(buffer,JOB_NAME_LENGTH+30,"%s|%ld|%u|%d",job->Name, job->Seconds, job->Repeat_hrs, job->Fixed ? 1:0); + char buffer[JOB_NAME_SIZE+30]; + for (uint8_t a=0; aNext_ptr; } } void jobs_list_read_dict(DictionaryIterator *iter, uint8_t first_key, const uint8_t version) { - if (first_job_ptr!=NULL) return; - + if (jobs_count) return; Tuple *tuple_t; uint8_t fields=4; - char buffer[fields][JOB_NAME_LENGTH]; - + char buffer[fields][JOB_NAME_SIZE]; while ((tuple_t=dict_find(iter, first_key++))) { char *source = tuple_t->value->cstring; for (int c=0; cFixed = false; // this flag was not included before storage version 2 - LOG("loaded job: %s, seconds=%ld, repeat=%d, fixed=%d, version=%d", new_job->Name, new_job->Seconds, new_job->Repeat_hrs, new_job->Fixed, version); - new_job_ptr = malloc(sizeof(Job_ptr)); - new_job_ptr->Job = new_job; - new_job_ptr->Next_ptr = NULL; - if (prev_job_ptr) prev_job_ptr->Next_ptr = new_job_ptr; - prev_job_ptr = new_job_ptr; - if (NULL==first_job_ptr) first_job_ptr = new_job_ptr; - jobs_count++; - first_key++; } LOG("Loaded %d jobs.",jobs_count); } -Job* jobs_list_get_index(uint8_t index) { - if (index>=jobs_count) return NULL; - Job_ptr* job_ptr = first_job_ptr; - while (index--) job_ptr=job_ptr->Next_ptr; - return job_ptr->Job; -} - -void jobs_list_move_to_top(uint8_t index) { +void jobs_list_move_to_top_not_used(uint8_t index) { if (index==0 || index>=jobs_count) return; - Job_ptr* job_ptr = first_job_ptr; - Job_ptr* prev_job_ptr=NULL; - while (index--) { - prev_job_ptr=job_ptr; - job_ptr=job_ptr->Next_ptr; + + struct Job removed_job=jobs[index]; + while (index) { + jobs[index]=jobs[index-1]; + index--; } - // remove job_ptr from list - prev_job_ptr->Next_ptr = job_ptr->Next_ptr; - // Insert before first_job_ptr - job_ptr->Next_ptr = first_job_ptr; - first_job_ptr=job_ptr; + jobs[0]=removed_job; } // ***************************************************************************************************** // PUBLIC FUNCTIONS // ***************************************************************************************************** +static uint8_t jobs_find_job_index_from_name(const char *name) { + // get new index of job after sort + uint8_t index=0; + while (strcmp(jobs[index].Name,name)) index++; + return index; +} + static void callback(const char* result, size_t result_length, void* extra) { // Do something with result int index = (int) extra; if (index==-1) { - jobs_list_append_job(result, time(NULL), 0, 0); + jobs_list_append_job(result, time(NULL), 0, 0); } else { - snprintf(jobs_list_get_index(index)->Name,JOB_NAME_LENGTH, result); + strncpy(jobs[index].Name, result, JOB_NAME_SIZE)[JOB_NAME_SIZE-1]=0; } main_save_data(); main_menu_update(); + if (index==-1) { + job_menu_show(jobs_find_job_index_from_name(result)); + job_adjust_show(); + } } void jobs_add_job() { @@ -185,81 +131,31 @@ void jobs_add_job() { } void jobs_rename_job(uint8_t index) { - tertiary_text_prompt(jobs_get_job_name(index), callback, (void*) (int) index); + tertiary_text_prompt(jobs[index].Name, callback, (void*) (int) index); } void jobs_delete_all_jobs(void) { - while (first_job_ptr) { - Job_ptr * next_job=first_job_ptr->Next_ptr; - free(first_job_ptr->Job); - free(first_job_ptr); - first_job_ptr=next_job; - } jobs_count=0; } void jobs_delete_job_and_save(uint8_t index) { - if (index>=jobs_count) return; - - Job_ptr* job_ptr = first_job_ptr; - - if (index) { - Job_ptr* prev_job_ptr = NULL; - while (index--) { - prev_job_ptr=job_ptr; - job_ptr=job_ptr->Next_ptr; - } - prev_job_ptr->Next_ptr = job_ptr->Next_ptr; - } else { - first_job_ptr = job_ptr->Next_ptr; + jobs_count--; + while (indexJob); - free(job_ptr); - jobs_count--; main_save_data(); main_menu_update(); } -char* jobs_get_job_name(uint8_t index) { - Job* job=jobs_list_get_index(index); - return (job) ? job->Name : NULL; -} - -uint32_t jobs_get_job_seconds(uint8_t index) { - Job* job=jobs_list_get_index(index); - return (job) ? time(NULL) - job->Seconds : 0; -} - -uint8_t jobs_get_job_repeat(uint8_t index) { - Job* job=jobs_list_get_index(index); - return (job) ? job->Repeat_hrs : 0; -} - -bool jobs_get_job_fixed(uint8_t index) { - Job* job=jobs_list_get_index(index); - return (job) ? job->Fixed : 0; -} -static void jobs_update_job_index(Job* job, uint8_t *index) { - // check if job was moved during sort - if (job==jobs_list_get_index(*index)) return; - // get new index - Job_ptr* job_ptr = first_job_ptr; - *index=0; - while (job_ptr->Job != job) { - (*index)++; - job_ptr=job_ptr->Next_ptr; - } -} - void jobs_set_job_repeat(uint8_t *index, uint8_t repeat, uint8_t fixed) { - Job* job=jobs_list_get_index(*index); - if (job) { - job->Repeat_hrs=repeat; - job->Fixed = fixed ? true : false; - main_save_data(); - jobs_update_job_index(job, index); - } + char name[JOB_NAME_SIZE]; + strncpy(name,jobs[*index].Name,JOB_NAME_SIZE); + jobs[*index].Repeat_hrs=repeat; + jobs[*index].Fixed = fixed ? true : false; + main_save_data(); // this might resort the jobs. + *index=jobs_find_job_index_from_name(name); } #define MAX_CLOCK_LENGTH 24 @@ -267,22 +163,21 @@ char clock_buffer[MAX_CLOCK_LENGTH]; char repeat_buffer[MAX_CLOCK_LENGTH]; char* jobs_get_job_clock_as_text(uint8_t index) { - Job* job=jobs_list_get_index(index); time_t seconds; switch (settings.Mode) { case MODE_COUNT_DOWN: - seconds = job->Seconds + job->Repeat_hrs*3600 - time(NULL); + seconds = END_TIME(jobs[index]) - time(NULL); bool minus = seconds < 0; if (minus) seconds = -seconds; snprintf(clock_buffer,MAX_CLOCK_LENGTH,"%s%ld:%02ld:%02ld",minus?"+":"-",(seconds/3600) /*hours*/,(seconds / 60) % 60 /*mins*/,seconds % 60 /*secs*/); break; case MODE_COUNT_UP: - seconds = time(NULL) - job->Seconds; + seconds = time(NULL) - jobs[index].Seconds; snprintf(clock_buffer,MAX_CLOCK_LENGTH,"%ld:%02ld:%02ld",(seconds/3600) /*hours*/,(seconds / 60) % 60 /*mins*/,seconds % 60 /*secs*/); break; case MODE_NEXT_TIME: - ; time_t next = END_TIME(job); + ; time_t next = END_TIME(jobs[index]); strftime(clock_buffer,MAX_CLOCK_LENGTH,clock_is_24h_style() ? "%H:%M" : "%I:%M %p",localtime(&next)); break; } @@ -290,42 +185,33 @@ char* jobs_get_job_clock_as_text(uint8_t index) { } char* jobs_get_job_repeat_as_text(uint8_t index) { - Job* job=jobs_list_get_index(index); - - snprintf(repeat_buffer,MAX_CLOCK_LENGTH,"Every %d%s hrs", job->Repeat_hrs, job->Fixed ? "" : "+"); + snprintf(repeat_buffer,MAX_CLOCK_LENGTH,"Every %d%s hrs", jobs[index].Repeat_hrs, jobs[index].Fixed ? "" : "+"); return repeat_buffer; } void jobs_reset_and_save(uint8_t *index) { - Job* job=jobs_list_get_index(*index); - if (job->Fixed) { - job->Seconds+=job->Repeat_hrs*3600; + char name[JOB_NAME_SIZE]; + strncpy(name,jobs[*index].Name,JOB_NAME_SIZE); + + if (jobs[*index].Fixed) { + jobs[*index].Seconds+=jobs[*index].Repeat_hrs*3600; } else { - job->Seconds=time(NULL); + jobs[*index].Seconds=time(NULL); } - main_save_data(); - jobs_update_job_index(job, index); + main_save_data(); // this might resort the jobs. + *index=jobs_find_job_index_from_name(name); } void jobs_add_minutes(uint8_t *index, int minutes) { - Job* job=jobs_list_get_index(*index); - int seconds = (int) job->Seconds; - //if (seconds + 60*minutes < time(NULL)) { - seconds += 60*minutes; - //} else { - // seconds = time(NULL); - //} - job->Seconds = seconds; + jobs[*index].Seconds += 60*minutes; } time_t jobs_get_next_wakeup_time(void) { - Job_ptr* job_ptr = first_job_ptr; - time_t min_time = (END_TIME(job_ptr->Job) > time(NULL)) ? END_TIME(job_ptr->Job) : 0; - - while (job_ptr) { - if (job_ptr->Job->Repeat_hrs && END_TIME(job_ptr->Job) > time(NULL) && (min_time==0 || END_TIME(job_ptr->Job)Job); - job_ptr = job_ptr->Next_ptr; + time_t min_time = 0; + for (uint8_t a=0; a time(NULL) && (min_time==0 || END_TIME(jobs[a])Job->Repeat_hrs && END_TIME(job_ptr->Job) <= time(NULL)) new_alarm_count++; - job_ptr=job_ptr->Next_ptr; + for (uint8_t a=0; ajobs_alarm_count) vibrate(); jobs_alarm_count=new_alarm_count; diff --git a/src/jobs.h b/src/jobs.h index 946badd..3b31c26 100644 --- a/src/jobs.h +++ b/src/jobs.h @@ -1,5 +1,16 @@ #pragma once +#define MAX_JOBS 20 +#define JOB_NAME_SIZE 24 + +extern uint8_t jobs_count; +struct Job { + char Name[JOB_NAME_SIZE]; + time_t Seconds; + uint8_t Repeat_hrs; + bool Fixed; +}; +extern struct Job jobs[MAX_JOBS]; extern uint8_t jobs_count; void jobs_list_sort(void); @@ -12,11 +23,7 @@ void jobs_delete_all_jobs(void); void jobs_delete_job_and_save(uint8_t index); void jobs_add_job(); void jobs_rename_job(uint8_t index); -uint32_t jobs_get_job_seconds(uint8_t index); -uint8_t jobs_get_job_repeat(uint8_t index); -bool jobs_get_job_fixed(uint8_t index); void jobs_set_job_repeat(uint8_t *index, uint8_t repeat, uint8_t fixed); -char* jobs_get_job_name(uint8_t index); char* jobs_get_job_clock_as_text(uint8_t index); char* jobs_get_job_repeat_as_text(uint8_t index); void jobs_reset_and_save(uint8_t *index); diff --git a/src/main.c b/src/main.c index 42dbd49..e39a14d 100644 --- a/src/main.c +++ b/src/main.c @@ -5,17 +5,7 @@ extern const PebbleProcessInfo __pbl_app_info; #define APP_VERSION_LENGTH 10 char app_version[APP_VERSION_LENGTH]; -GBitmap *bitmap_matrix; -//GBitmap *bitmap_pause; -GBitmap *bitmap_play; -GBitmap *bitmap_add; -GBitmap *bitmap_settings; -GBitmap *bitmap_delete; -GBitmap *bitmap_edit; -GBitmap *bitmap_adjust; -GBitmap *bitmap_reset; -GBitmap *bitmap_minus; -GBitmap *bitmap_tick; +GBitmap *bitmaps[N_BITMAPS][PBL_IF_SDK_3_ELSE(2,1)]; Settings settings={MODE_COUNT_UP, false /*alarm*/, true /*sort*/}; static bool JS_ready = false; @@ -24,9 +14,8 @@ static uint32_t data_timestamp = 0; uint8_t stored_version=0; bool export_after_save=false; #ifdef PBL_SDK_3 - bool waiting_for_pins=false; - bool quit_after_pins=false; uint8_t timeline_settings=TIMELINE_FLAG_ON | TIMELINE_FLAG_NOTIFICATIONS; + uint8_t quit_after_secs=0; #endif // ***************************************************************************************************** @@ -42,8 +31,7 @@ bool export_after_save=false; #define KEY_APP_VERSION 5 #define KEY_EXPORT 6 #define KEY_TIMESTAMP 7 -#define KEY_PINS_DONE 8 -#define KEY_TIMELINE 9 +#define KEY_TIMELINE 8 static void send_settings_to_phone() { if (!JS_ready) return; @@ -69,10 +57,9 @@ static void send_settings_to_phone() { } dict_write_end(iter); + + //LOG("ended, dict_size=%d", (int) dict_size(iter)); app_message_outbox_send(); - #ifdef PBL_SDK_3 - waiting_for_pins=true; - #endif } static void inbox_received_handler(DictionaryIterator *iter, void *context) { @@ -80,14 +67,6 @@ static void inbox_received_handler(DictionaryIterator *iter, void *context) { JS_ready = true; Tuple *tuple_t; - #ifdef PBL_SDK_3 - if (dict_find(iter, KEY_PINS_DONE)) { - waiting_for_pins=false; - if (quit_after_pins) main_menu_hide(); - return; - } - #endif - bool new_data_from_config_page = dict_find(iter, KEY_CONFIG_DATA); tuple_t= dict_find(iter, KEY_TIMESTAMP); uint32_t inbox_timestamp = tuple_t ? tuple_t->value->int32 : 0; @@ -181,35 +160,65 @@ void init(void) { snprintf(app_version,APP_VERSION_LENGTH,"%d.%d",__pbl_app_info.process_version.major, __pbl_app_info.process_version.minor); main_load_data(); - bitmap_matrix=gbitmap_create_with_resource(RESOURCE_ID_IMAGE_ICON_MATRIX); - //bitmap_pause=gbitmap_create_as_sub_bitmap(bitmap_matrix, ICON_RECT_PAUSE); - bitmap_play=gbitmap_create_as_sub_bitmap(bitmap_matrix, ICON_RECT_PLAY); - bitmap_add=gbitmap_create_as_sub_bitmap(bitmap_matrix, ICON_RECT_ADD); - bitmap_settings=gbitmap_create_as_sub_bitmap(bitmap_matrix, ICON_RECT_SETTINGS); - bitmap_delete=gbitmap_create_as_sub_bitmap(bitmap_matrix, ICON_RECT_DELETE); - bitmap_edit=gbitmap_create_as_sub_bitmap(bitmap_matrix, ICON_RECT_EDIT); - bitmap_adjust=gbitmap_create_as_sub_bitmap(bitmap_matrix, ICON_RECT_ADJUST); - bitmap_reset=gbitmap_create_as_sub_bitmap(bitmap_matrix, ICON_RECT_RESET); - bitmap_minus=gbitmap_create_as_sub_bitmap(bitmap_matrix, ICON_RECT_MINUS); - bitmap_tick=gbitmap_create_as_sub_bitmap(bitmap_matrix, ICON_RECT_TICK); - main_menu_show(); - if (data_loaded_from_watch && stored_version < CURRENT_STORAGE_VERSION) update_show(stored_version); + bitmaps[BITMAP_MATRIX][0] =gbitmap_create_with_resource(RESOURCE_ID_IMAGE_ICON_MATRIX); + bitmaps[BITMAP_ADD][0] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][0], ICON_RECT_ADD); + bitmaps[BITMAP_MINUS][0] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][0], ICON_RECT_MINUS); + bitmaps[BITMAP_SETTINGS][0]=gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][0], ICON_RECT_SETTINGS); + bitmaps[BITMAP_DELETE][0] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][0], ICON_RECT_DELETE); + bitmaps[BITMAP_EDIT][0] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][0], ICON_RECT_EDIT); + bitmaps[BITMAP_ADJUST][0] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][0], ICON_RECT_ADJUST); + bitmaps[BITMAP_RESET][0] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][0], ICON_RECT_RESET); + bitmaps[BITMAP_PLAY][0] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][0], ICON_RECT_PLAY); + bitmaps[BITMAP_TICK][0] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][0], ICON_RECT_TICK); + #ifdef PBL_SDK_3 + // SDK 3 doesn't invert highligted menu icon, so need to use pre-inverted image... + bitmaps[BITMAP_MATRIX][1] =gbitmap_create_with_resource(RESOURCE_ID_IMAGE_ICON_MATRIX_INV); + bitmaps[BITMAP_ADD][1] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][1], ICON_RECT_ADD); + bitmaps[BITMAP_MINUS][1] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][1], ICON_RECT_MINUS); + bitmaps[BITMAP_SETTINGS][1]=gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][1], ICON_RECT_SETTINGS); + bitmaps[BITMAP_DELETE][1] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][1], ICON_RECT_DELETE); + bitmaps[BITMAP_EDIT][1] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][1], ICON_RECT_EDIT); + bitmaps[BITMAP_ADJUST][1] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][1], ICON_RECT_ADJUST); + bitmaps[BITMAP_RESET][1] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][1], ICON_RECT_RESET); + bitmaps[BITMAP_PLAY][1] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][1], ICON_RECT_PLAY); + bitmaps[BITMAP_TICK][1] =gbitmap_create_as_sub_bitmap(bitmaps[BITMAP_MATRIX][1], ICON_RECT_TICK); + #endif + + if (data_loaded_from_watch && stored_version < CURRENT_STORAGE_VERSION) update_show(stored_version); app_message_register_inbox_received(inbox_received_handler); - app_message_open(2048, 2048); + app_message_open(636, 636); // should be enough for 23 meds: 78bytes+23meds*24bytes = 630 #ifdef PBL_SDK_3 if (launch_reason() == APP_LAUNCH_TIMELINE_ACTION) { uint8_t reason=launch_get_args(); LOG("launch code: %d", reason); if (reason>=10) { - reason-=10; - jobs_reset_and_save(&reason); - quit_after_pins=true; + // let list of med names, to find alphabetical order + uint8_t names[MAX_JOBS]; + uint8_t temp; + for (uint8_t a=0; aSeconds + (time_t) (JOB)->Repeat_hrs*3600) +#define END_TIME(JOB) ((time_t) (JOB).Seconds + (time_t) (JOB).Repeat_hrs*3600) #define FONT_GOTHIC_24_BOLD fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD) #define FONT_GOTHIC_28_BOLD fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD) @@ -85,17 +85,21 @@ #define ICON_RECT_MINUS (GRect) { { 16, 48 }, { 16, 16 } } #define ICON_RECT_CLOCK (GRect) { { 32, 16 }, { 16, 16 } } -extern GBitmap *bitmap_matrix; -//extern GBitmap *bitmap_pause; -extern GBitmap *bitmap_play; -extern GBitmap *bitmap_add; -extern GBitmap *bitmap_settings; -extern GBitmap *bitmap_delete; -extern GBitmap *bitmap_edit; -extern GBitmap *bitmap_adjust; -extern GBitmap *bitmap_reset; -extern GBitmap *bitmap_minus; -extern GBitmap *bitmap_tick; +enum { + BITMAP_MATRIX, + BITMAP_ADD, + BITMAP_MINUS, + BITMAP_SETTINGS, + BITMAP_DELETE, + BITMAP_EDIT, + BITMAP_ADJUST, + BITMAP_RESET, + BITMAP_TICK, + BITMAP_PLAY, + N_BITMAPS +}; + +extern GBitmap *bitmaps[N_BITMAPS][PBL_IF_SDK_3_ELSE(2,1)]; // Persistent Storage Keys #define STORAGE_KEY_VERSION 1 @@ -126,9 +130,8 @@ enum { #ifdef PBL_SDK_3 - extern bool waiting_for_pins; - extern bool quit_after_pins; extern uint8_t timeline_settings; + extern uint8_t quit_after_secs; #define TIMELINE_FLAG_ON 1 #define TIMELINE_FLAG_NOTIFICATIONS 2 #endif diff --git a/src/main_menu.c b/src/main_menu.c index f927c90..c86b8a2 100644 --- a/src/main_menu.c +++ b/src/main_menu.c @@ -4,42 +4,10 @@ static Window *s_window; static MenuLayer *s_menulayer; #ifdef PBL_SDK_3 static StatusBarLayer *s_status_bar; -static TextLayer *s_textlayer_wait; #endif static bool check_phone_message = false; -/*************************************** Back Button Override ********************************/ -// https://gist.github.com/sarfata/10574031 -#ifdef PBL_SDK_3 -// Define what you want to do when the back button is pressed -void back_button_handler(ClickRecognizerRef recognizer, void *context) { - if (waiting_for_pins) { - quit_after_pins=true; - layer_set_hidden((Layer *)s_textlayer_wait,false); - } else { - main_menu_hide(); - } -} - -// We need to save a reference to the ClickConfigProvider originally set by the menu layer -ClickConfigProvider previous_ccp; - -// This is the new ClickConfigProvider we will set, it just calls the old one and then subscribe -// for back button events. -void new_ccp(void *context) { - previous_ccp(context); - window_single_click_subscribe(BUTTON_ID_BACK, back_button_handler); -} - -// Call this from your init function to do the hack -void force_back_button(Window *window, MenuLayer *menu_layer) { - previous_ccp = window_get_click_config_provider(window); - window_set_click_config_provider_with_context(window, new_ccp, menu_layer); -} -#endif -/********************************* end of Back Button Override ********************************/ - static void initialise_ui(void) { s_window = window_create(); #ifndef PBL_SDK_3 @@ -54,16 +22,6 @@ static void initialise_ui(void) { layer_add_child(window_get_root_layer(s_window), (Layer *)s_menulayer); #ifdef PBL_SDK_3 - force_back_button(s_window,s_menulayer); - // s_textlayer_wait - s_textlayer_wait = text_layer_create(GRect(14+margin, (bounds.size.h-5*26)/2, bounds.size.w-2*(14+margin), 5*26)); - text_layer_set_background_color(s_textlayer_wait,GColorBlack); - text_layer_set_text_color(s_textlayer_wait,GColorWhite); - text_layer_set_text(s_textlayer_wait, "\nUpdating timeline before closing..."); - text_layer_set_text_alignment(s_textlayer_wait, GTextAlignmentCenter); - text_layer_set_font(s_textlayer_wait, FONT_ROBOTO_21_CONDENSED); - layer_set_hidden((Layer *)s_textlayer_wait,true); - layer_add_child(window_get_root_layer(s_window), (Layer *)s_textlayer_wait); // Set up the status bar last to ensure it is on top of other Layers s_status_bar = status_bar_layer_create(); layer_add_child(window_get_root_layer(s_window), status_bar_layer_get_layer(s_status_bar)); @@ -76,12 +34,17 @@ static void destroy_ui(void) { #ifdef PBL_SDK_3 status_bar_layer_destroy(s_status_bar); - text_layer_destroy(s_textlayer_wait); #endif } void handle_ticktimer_tick(struct tm *tick_time, TimeUnits units_changed) { //LOG("tick timer, units changed=%d",(int) units_changed); + #ifdef PBL_SDK_3 + if (quit_after_secs) { + if (--quit_after_secs==1) main_menu_hide(); + return; + } + #endif if (units_changed & SECOND_UNIT) { main_menu_update(); job_menu_update(); @@ -159,14 +122,14 @@ void menu_cell_draw_job(GContext* ctx, const Layer *cell_layer, const uint8_t in #endif uint8_t margin=(bounds.size.w-144)/2; - graphics_draw_text(ctx, jobs_get_job_name(index), FONT_GOTHIC_24_BOLD, GRect(4+margin, -4, bounds.size.w-2*(4+margin), 4+18), GTextOverflowModeFill, GTextAlignmentLeft, NULL); + graphics_draw_text(ctx, jobs[index].Name, FONT_GOTHIC_24_BOLD, GRect(4+margin, -4, bounds.size.w-2*(4+margin), 4+18), GTextOverflowModeFill, GTextAlignmentLeft, NULL); graphics_draw_text(ctx, jobs_get_job_clock_as_text(index), FONT_GOTHIC_18, GRect(4+margin, 20, bounds.size.w-2*(4+margin), 14), GTextOverflowModeFill, GTextAlignmentRight, NULL); graphics_draw_text(ctx, jobs_get_job_repeat_as_text(index), FONT_GOTHIC_14, GRect(4+margin, 20+4, bounds.size.w-2*(4+margin), 14), GTextOverflowModeFill, GTextAlignmentLeft, NULL); //graphics_draw_bitmap_in_rect(ctx, timer.Active && timer.Job==index ? bitmap_play : bitmap_pause, GRect(6, (bounds.size.h-16)/2, 16, 16)); } -void menu_cell_draw_other(GContext* ctx, const Layer *cell_layer, const char *title, const char *sub_title, GBitmap * icon) { +void menu_cell_draw_other(GContext* ctx, const Layer *cell_layer, const char *title, const char *sub_title, GBitmap ** icon) { GRect bounds = layer_get_frame(cell_layer); #ifndef PBL_SDK_3 @@ -177,7 +140,7 @@ void menu_cell_draw_other(GContext* ctx, const Layer *cell_layer, const char *ti graphics_draw_text(ctx, title, FONT_GOTHIC_24_BOLD, GRect(28+margin, -4, bounds.size.w-28-2*margin, 4+18), GTextOverflowModeFill, GTextAlignmentLeft, NULL); if (sub_title) graphics_draw_text(ctx, sub_title, FONT_GOTHIC_18, GRect(28+margin, 20, bounds.size.w-28-margin-4, 14), GTextOverflowModeFill, GTextAlignmentLeft, NULL); - if (icon) graphics_draw_bitmap_in_rect(ctx, icon, GRect(6+margin,(bounds.size.h-16)/2, 16, 16)); + if (icon) graphics_draw_bitmap_in_rect(ctx, icon[PBL_IF_SDK_3_ELSE(menu_cell_layer_is_highlighted(cell_layer), 0)], GRect(6+margin,(bounds.size.h-16)/2, 16, 16)); } static void menu_cell_draw_setting(GContext* ctx, const Layer *cell_layer, const char *title, const char *setting, const char *hint) { @@ -202,13 +165,13 @@ static void menu_draw_row_callback(GContext* ctx, const Layer *cell_layer, MenuI default: switch (MENU_SECTION_CELL) { - case MENU_OTHER_ADD: menu_cell_draw_other(ctx, cell_layer, "Add Medication", NULL, bitmap_add); break; + case MENU_OTHER_ADD: menu_cell_draw_other(ctx, cell_layer, "Add Medication", NULL, bitmaps[BITMAP_ADD]); break; case MENU_SETTINGS_MODE: menu_cell_draw_setting(ctx, cell_layer, "Mode", mode[settings.Mode],NULL); break; case MENU_SETTINGS_ALARM: menu_cell_draw_setting(ctx, cell_layer, "Alarm", settings.Alarm ? "ON" : "OFF",NULL); break; case MENU_SETTINGS_SORT: menu_cell_draw_setting(ctx, cell_layer, "Sort", settings.Sort ? "YES" : "NO",NULL); break; - case MENU_SETTINGS_CONFIG: menu_cell_draw_other(ctx, cell_layer, check_phone_message ? "Check phone..." : "Config/Donate", NULL , bitmap_settings); break; + case MENU_SETTINGS_CONFIG: menu_cell_draw_other(ctx, cell_layer, check_phone_message ? "Check phone..." : "Config/Donate", NULL , bitmaps[BITMAP_SETTINGS]); break; #ifdef PBL_SDK_3 case MENU_SETTINGS_TIMELINE: menu_cell_draw_setting(ctx, cell_layer, "Timeline", timeline_settings&TIMELINE_FLAG_ON ? "ON" : "OFF",NULL); break; case MENU_SETTINGS_TL_NOTIFICATIONS: menu_cell_draw_setting(ctx, cell_layer, "..Notifications", timeline_settings&TIMELINE_FLAG_NOTIFICATIONS ? "ON" : "OFF",NULL); break; diff --git a/src/main_menu.h b/src/main_menu.h index 5a3474e..c13c437 100644 --- a/src/main_menu.h +++ b/src/main_menu.h @@ -1,7 +1,7 @@ #pragma once void menu_cell_draw_job(GContext* ctx, const Layer *cell_layer, const uint8_t index); -void menu_cell_draw_other(GContext* ctx, const Layer *cell_layer, const char *title, const char *sub_title, GBitmap * icon); +void menu_cell_draw_other(GContext* ctx, const Layer *cell_layer, const char *title, const char *sub_title, GBitmap ** icon); void handle_ticktimer_tick(struct tm *tick_time, TimeUnits units_changed); void main_menu_show(void); void main_menu_hide(void); diff --git a/src/pebble-js-app.js b/src/pebble-js-app.js index 7e91fdb..0977d61 100644 --- a/src/pebble-js-app.js +++ b/src/pebble-js-app.js @@ -1,18 +1,20 @@ -// https://github.com/pebble-examples/slate-config-example/blob/master/src/js/pebble-js-app.js - var TIMELINE_FLAG_ON = 1; var TIMELINE_FLAG_NOTIFICATIONS = 2; Pebble.addEventListener('ready', function() { console.log('PebbleKit JS ready!'); var settings=localStorage.getItem("settings"); - //settings='{"101":"Tremadol|1454364000|8|1","102":"Paracetamol|1454378453|6|1","103":"Omeprazole|1454351412|24|1","KEY_APP_VERSION":"1.4","KEY_SORT":1,"KEY_MEDICATIONS":"Ibuprofen|1454367649|6|1","KEY_VERSION":3,"KEY_MODE":2,"KEY_TIMESTAMP":1454380831,"KEY_ALARM":1}'; - var dict=settings ? JSON.parse(settings) : {}; - if (!dict.KEY_TIMESTAMP) { + //settings='{"101":"Ibuprofen|1454731249|6|1","102":"Tremadol|1454738400|8|1","103":"Omeprazole|1454697012|24|1","KEY_TIMELINE":0,"KEY_APP_VERSION":"1.5","KEY_SORT":1,"KEY_MEDICATIONS":"Paracetamol|1454720453|6|1","KEY_VERSION":4,"KEY_MODE":2,"KEY_TIMESTAMP":1454739389,"KEY_ALARM":0}'; + settings=settings ? JSON.parse(settings) : {}; + if (!settings.KEY_TIMESTAMP) { var d=new Date(); - dict.KEY_TIMESTAMP = Math.floor(d.getTime()/1000 - d.getTimezoneOffset()*60); + settings.KEY_TIMESTAMP = Math.floor(d.getTime()/1000 - d.getTimezoneOffset()*60); + } + sendDict(settings); + if (localStorage.getItem("old_pins")) { + deleteAllPins(); + if (settings.KEY_TIMELINE & TIMELINE_FLAG_ON) createAllPins(settings); } - sendDict(dict); }); Pebble.addEventListener("appmessage", function(e) { @@ -27,7 +29,6 @@ Pebble.addEventListener("appmessage", function(e) { if (typeof(e.payload.KEY_TIMELINE)!="undefined") { deleteAllPins(); if (e.payload.KEY_TIMELINE & TIMELINE_FLAG_ON) createAllPins(e.payload); - if (requests_outstanding===0) sendDict({KEY_PINS_DONE:1}); } }); @@ -119,33 +120,58 @@ Pebble.addEventListener('webviewclosed', function(e) { /****************************** Custom Timeline Stuff ************************/ var pin_prefix="MedTimer-pin-"; -var requests_outstanding=0; function deleteAllPins() { // delete any old pins var pin; - var last_pins=localStorage.getItem("pins"); - var pin_old_now=localStorage.getItem("pin_now"); - var med=0; - console.log("last pins: "+pin_old_now+"-"+last_pins); - while (med -1) old_pins.splice(index, 1); + if (old_pins.length) { + localStorage.setItem("old_pins",JSON.stringify(old_pins)); + } else { + localStorage.removeItem("old_pins"); + } + console.log("remaining old pins: "+JSON.stringify(old_pins)); }); - med++; } } function createAllPins(dict) { if (!dict) return; var pin; + var med; var setting; - var pin_now=(new Date()).valueOf(); - var med=0; + var pin_now=(new Date()).valueOf()+"-"; + var pins=JSON.parse(localStorage.getItem("pins")) || []; + + // get list of med names and sort them alphabetically. + var new_pins=[]; + med=0; + while (setting=(med===0) ? dict.KEY_MEDICATIONS : dict[100+med]) { + setting=setting.split("|"); + new_pins.push(setting[0]); // med name + med++; + } + new_pins.sort(); + + // create and send pins + med=0; while (setting=(med===0) ? dict.KEY_MEDICATIONS : dict[100+med]) { // get the time setting=setting.split("|"); @@ -156,7 +182,7 @@ function createAllPins(dict) { // configure the pin pin = { - "id": pin_prefix+pin_now+"-"+med,//pin_prefix+med, + "id": pin_prefix+pin_now+med,//pin_prefix+med, "time": d.toISOString(), "layout": { "type": "genericPin", @@ -167,7 +193,7 @@ function createAllPins(dict) { { "title": "Med Taken", "type": "openWatchApp", - "launchCode": 10+med + "launchCode": 10+new_pins.indexOf(setting[0]) }, { "title": "Open Meds Timer", @@ -188,19 +214,17 @@ function createAllPins(dict) { } ]; } - + // remember pin, to be deleted later + pins.push(pin_now+med); // Push the pin console.log('Inserting pin in the future: ' + JSON.stringify(pin)); - requests_outstanding++; - insertUserPin(pin, function(responseText) { - console.log('insertUserPin Result: ' + responseText); - if (--requests_outstanding===0) sendDict({KEY_PINS_DONE:1}); + insertUserPin(pin, function(responseText, id) { + //console.log('insertUserPin Result: ' + responseText+" "+id); }); med++; } - console.log("remembering pins: "+pin_now+"-"+med); - localStorage.setItem("pins",med); - localStorage.setItem("pin_now",pin_now); + console.log("remembering pins: "+JSON.stringify(pins)); + localStorage.setItem("pins",JSON.stringify(pins)); } @@ -227,7 +251,7 @@ function timelineRequest(pin, type, topics, apiKey, callback) { var xhr = new XMLHttpRequest(); xhr.onload = function () { //console.log('timeline: response received: ' + this.responseText); - callback(this.responseText); + callback(this.responseText, pin.id); }; xhr.onerror=function(error) { console.log('timeline: error: ' +JSON.stringify(error)); diff --git a/src/update.c b/src/update.c index d734949..2d48d8b 100644 --- a/src/update.c +++ b/src/update.c @@ -36,7 +36,7 @@ static void initialise_ui(void) { // s_textlayer_features s_textlayer_features = text_layer_create(GRect(2+MARGIN, HEIGHT+MARGIN, bounds.size.w-2*(2+MARGIN), bounds.size.h-HEIGHT-MARGIN)); - text_layer_set_text(s_textlayer_features, "* Medications now appear on the timeline\n\n* Optimised for Pebble Time Round"); + text_layer_set_text(s_textlayer_features, "* Medications now appear on the timeline\n\n* Improved for Pebble Time Round"); text_layer_set_font(s_textlayer_features, FONT_GOTHIC_18); #ifdef PBL_ROUND text_layer_set_text_alignment(s_textlayer_features, GTextAlignmentCenter);