From d75dd596bac0f1339339b563277e49f271382a39 Mon Sep 17 00:00:00 2001 From: wildhart Date: Sat, 16 Jan 2016 06:18:50 +1300 Subject: [PATCH] v1.0 First released version --- appinfo.json | 17 +++- resources/images/menu_icon.png | Bin 0 -> 1039 bytes src/job_adjust.c | 25 +++--- src/job_adjust.h | 2 +- src/job_menu.c | 8 +- src/job_menu.h | 2 + src/jobs.c | 156 ++++++++++++++++++++++++++++++--- src/jobs.h | 9 +- src/main.c | 98 +++++++++++++++++---- src/main.h | 27 ++++-- src/main_menu.c | 67 ++++++++++++-- src/main_menu.h | 3 +- src/pebble-js-app.js | 18 ++++ 13 files changed, 367 insertions(+), 65 deletions(-) create mode 100644 resources/images/menu_icon.png create mode 100644 src/pebble-js-app.js diff --git a/appinfo.json b/appinfo.json index e93a62a..733d716 100644 --- a/appinfo.json +++ b/appinfo.json @@ -1,13 +1,24 @@ { - "appKeys": {}, + "appKeys": { + "KEY_ALARM": 2, + "KEY_MEDICATIONS": 100, + "KEY_MODE": 1, + "KEY_SORT": 3 + }, "capabilities": [ - "" + "configurable" ], "companyName": "cmorison@gmail.com", "longName": "Medication Timer", "projectType": "native", "resources": { "media": [ + { + "file": "images/menu_icon.png", + "menuIcon": true, + "name": "IMAGE_MENU_ICON", + "type": "png" + }, { "file": "images/icons.png", "name": "IMAGE_ICON_MATRIX", @@ -16,7 +27,7 @@ ] }, "sdkVersion": "2", - "shortName": "Medication Timer", + "shortName": "Meds Timer", "uuid": "95d07e1a-2451-4ffa-aa44-836e523a7648", "versionCode": 1, "versionLabel": "1.0", diff --git a/resources/images/menu_icon.png b/resources/images/menu_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fdb13f84b9f4917d295a4c81585d4072e3a6320e GIT binary patch literal 1039 zcmV+q1n~QbP)K#=7$HeXCdxpR0VNE)2N;OF@{XiPNkZQ5 z$Nqk2?SK3H&$;)UbML*i?z!ilz4o`)THpHCk(iCo=jSIoIyzzl0|Tt7sfo3;w6M|9 zQFeBA#(sW&n4=$wzJ7myp=a`j*xTDfNl6L9!orY}l7iIKR76HbA|oRMlarHt-QVAz z;v;^m|5HCx0O>}UxZl{=KtMnMs;jFxxY}8c7{0!~7#tk5dS(eKLnwP|B2=I1`S}^q z(b4Gc?$&Iiw$;^DczSw5PC1OSWEs6CLiM$^wG}xzIr#Ya&~+9mrvsIimO`$zZiHlp z5ej>IdqZ+^GWz@b4Z(z6TwK82-5n1P4>qGT1e~6pMqFGRUSD5LYeU!D+gnJIgzM{T zLol0w<>lq*?CdmUoUp5_E4aA0;QszzlWO%LTy%ium2n-Cw;^LxJ3+SDg*3o)f zkr_rfGBUzXtZoG}6{@PLLTzoW6*26JkZM4!;OFNDSub&VdTI)Ie0+?EhzQemFpcp1 z{G8KUSWr+9ujTywysk5`xU8(qP!RO43LyPaQ&Xb>R2#8yXlMwRmzUi6<>dvjv9Xw$ znX%fGdiUn$hW96>)T$5VFB&z5U}~dwX=rHRDNjmD(g*{Akj&-g<{~C02J!LnC@wCB zJjxW=O#@bD1r?d=E+ z4b}MaEV#P5Is{0%Dbl&JvZ8sGn3#yZzCPa5L?%jiZ*MO$Gc)1kZ26o!O^nEH@FY;JDi z>FMdewpdzPVn#()8?7Pru#~v%?QOdbORY!Qk-omZ7^kZZX3{I8TK$Dw(eI9q4o-Ws zJ;cc4T1p$qks>=go4+oIMIIg=oPkseTa#dHYz%2>X&QezXhA^%c6WFGmo@-Q2rSY4 z;NXD2M9t35a#B!#Sq@_(qfxc9vxBLrDa_5y;pF6mw~6{S@gF20&zD|2^Edzi002ov JPDHLkV1oS<_MZR% literal 0 HcmV?d00001 diff --git a/src/job_adjust.c b/src/job_adjust.c index b42b626..7a9078a 100644 --- a/src/job_adjust.c +++ b/src/job_adjust.c @@ -3,11 +3,10 @@ #define N_LAYERS 1 #define BUFFER_LENGTH 6 static uint8_t active_layer; -static uint16_t limits[N_LAYERS]={24}; +static uint16_t limits[N_LAYERS]={25}; static uint16_t values[N_LAYERS]; static const char* formats[N_LAYERS]={"%d"}; static char buffers[N_LAYERS][BUFFER_LENGTH]; -static uint8_t job_index; static Window *s_window; static GFont s_res_gothic_24_bold; @@ -15,8 +14,8 @@ static GFont s_res_gothic_18; static GFont s_res_gothic_14; static ActionBarLayer *s_actionbarlayer; static TextLayer *s_textlayer_name; +static TextLayer *s_textlayer_hrs; static TextLayer *layers[N_LAYERS]; -static TextLayer *s_textlayer_help; static void action_bar_up_click_handler() { values[active_layer]=(values[active_layer]+1) % limits[active_layer]; @@ -32,7 +31,7 @@ static void action_bar_down_click_handler() { static void action_bar_select_click_handler() { if (active_layer==N_LAYERS-1) { - jobs_set_job_repeat(job_index,values[0]); + jobs_set_job_repeat(&job_index,values[0]); // this updates job_index if it gets moved during sort job_adjust_hide(); return; } @@ -68,25 +67,24 @@ static void initialise_ui(void) { text_layer_set_font(s_textlayer_name, s_res_gothic_24_bold); layer_add_child(window_get_root_layer(s_window), (Layer *)s_textlayer_name); + // s_textlayer_hrs + s_textlayer_hrs = text_layer_create(GRect(30+20+4, 60, 30, 24)); + text_layer_set_font(s_textlayer_hrs, s_res_gothic_18); + text_layer_set_text(s_textlayer_hrs, "hrs"); + layer_add_child(window_get_root_layer(s_window), (Layer *)s_textlayer_hrs); + for (uint8_t l=0; lrow) { case MENU_RESET: jobs_reset_and_save(job_index); - menu_layer_reload_data(s_menulayer); + job_menu_hide(); break; case MENU_ADD10: - jobs_add_minutes(job_index, -10); + jobs_add_minutes(&job_index, (settings.Mode==MODE_NEXT_TIME) ? 10:-10); // this updates job_index incase it gets sorted menu_layer_reload_data(s_menulayer); break; case MENU_SUB10: - jobs_add_minutes(job_index, 10); + jobs_add_minutes(&job_index, (settings.Mode==MODE_NEXT_TIME) ? -10:10); // this updates job_index incase it gets sorted menu_layer_reload_data(s_menulayer); break; case MENU_RENAME: jobs_rename_job(job_index); break; - case MENU_ADJUST: job_adjust_show(job_index); break; + case MENU_ADJUST: job_adjust_show(); break; case MENU_DELETE: jobs_delete_job_and_save(job_index); job_menu_hide(); diff --git a/src/job_menu.h b/src/job_menu.h index 8bc9de1..38610ac 100644 --- a/src/job_menu.h +++ b/src/job_menu.h @@ -1,5 +1,7 @@ #pragma once +extern uint8_t job_index; + void job_menu_show(uint8_t index); void job_menu_hide(void); void job_menu_update(void); diff --git a/src/jobs.c b/src/jobs.c index 5bc1978..157a870 100644 --- a/src/jobs.c +++ b/src/jobs.c @@ -3,7 +3,7 @@ #define JOB_NAME_LENGTH 24 typedef struct { char Name[JOB_NAME_LENGTH]; - uint32_t Seconds; + time_t Seconds; uint8_t Repeat_hrs; } Job; @@ -19,15 +19,15 @@ uint8_t jobs_count=0; // JOB LIST FUNCTIONS // ***************************************************************************************************** -static void jobs_list_append_job(const char* name) { +static void jobs_list_append_job(const char* name, time_t seconds, uint8_t repeat) { 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); - new_job->Seconds = time(NULL); - new_job->Repeat_hrs = 0; + new_job->Seconds = seconds; + new_job->Repeat_hrs = repeat; if (first_job_ptr) { Job_ptr* last_job_ptr = first_job_ptr; @@ -40,6 +40,34 @@ static void jobs_list_append_job(const char* name) { main_save_data(); } +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; + + Job_ptr* job_ptr_loop = job_ptr_before->Next_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; + } + job_ptr_loop = job_ptr_loop->Next_ptr; + } + + if (job_ptr_min != job_ptr_before) { + // swap med + Job* temp_job = job_ptr_before->Job; + job_ptr_before->Job = job_ptr_min->Job; + job_ptr_min->Job = 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) { @@ -50,15 +78,49 @@ void jobs_list_save(uint8_t first_key) { persist_delete(first_key); } +void jobs_list_write_dict(DictionaryIterator *iter, uint8_t first_key) { + Job_ptr* job_ptr = first_job_ptr; + Job * job; + char buffer[JOB_NAME_LENGTH+30]; + while (job_ptr) { + job=job_ptr->Job; + snprintf(buffer,JOB_NAME_LENGTH+30,"%s|%ld|%u",job->Name, job->Seconds, job->Repeat_hrs); + dict_write_cstring(iter, first_key++, buffer); + job_ptr=job_ptr->Next_ptr; + } +} + +void jobs_list_read_dict(DictionaryIterator *iter, uint8_t first_key) { + if (first_job_ptr!=NULL) return; + + Tuple *tuple_t; + char buffer[3][JOB_NAME_LENGTH]; + + while ((tuple_t=dict_find(iter, first_key++))) { + char *source = tuple_t->value->cstring; + for (int c=0; c<3; c++) { + uint d=0; // destination offset + while (*source && *source!='|' && dName,JOB_NAME_LENGTH, result); main_save_data(); @@ -159,10 +221,23 @@ uint8_t jobs_get_job_repeat(uint8_t index) { return (job) ? job->Repeat_hrs : 0; } -void jobs_set_job_repeat(uint8_t index, uint8_t repeat) { - Job* job=jobs_list_get_index(index); +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) { + Job* job=jobs_list_get_index(*index); if (job) job->Repeat_hrs=repeat; main_save_data(); + jobs_update_job_index(job, index); } #define MAX_CLOCK_LENGTH 24 @@ -170,9 +245,25 @@ char clock_buffer[MAX_CLOCK_LENGTH]; char repeat_buffer[MAX_CLOCK_LENGTH]; char* jobs_get_job_clock_as_text(uint8_t index) { - int seconds = jobs_get_job_seconds(index); + Job* job=jobs_list_get_index(index); + time_t seconds; - snprintf(clock_buffer,MAX_CLOCK_LENGTH,"%d:%02d:%02d",(seconds/3600) /*hours*/,(seconds / 60) % 60 /*mins*/,seconds % 60 /*secs*/); + switch (settings.Mode) { + case MODE_COUNT_DOWN: + seconds = job->Seconds + job->Repeat_hrs*3600 - 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; + 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); + strftime(clock_buffer,MAX_CLOCK_LENGTH,clock_is_24h_style() ? "%H:%M" : "%I:%M %p",localtime(&next)); + break; + } return clock_buffer; } @@ -188,8 +279,8 @@ void jobs_reset_and_save(uint8_t index) { main_save_data(); } -void jobs_add_minutes(uint8_t index, int minutes) { - Job* job=jobs_list_get_index(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)) { seconds += 60*minutes; @@ -198,4 +289,41 @@ void jobs_add_minutes(uint8_t index, int minutes) { } job->Seconds = seconds; main_save_data(); + jobs_update_job_index(job, index); +} + +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; + } + return min_time; +} + +static void vibrate(void) { + // Vibe pattern: ON for 200ms, OFF for 100ms, ON for 400ms: + LOG("VIBRATE!!!"); + static const uint32_t segments[] = { 400,200, 400,200, 400,200, 400,200, 400 }; + VibePattern pat = { + .durations = segments, + .num_segments = ARRAY_LENGTH(segments), + }; + vibes_enqueue_custom_pattern(pat); +} + +static uint8_t jobs_alarm_count = 0; + +void jobs_check_alarms(void) { + if (!jobs_count || !settings.Alarm) return; + uint8_t new_alarm_count=0; + Job_ptr* job_ptr = first_job_ptr; + while (job_ptr) { + if (job_ptr->Job->Repeat_hrs && END_TIME(job_ptr->Job) <= time(NULL)) new_alarm_count++; + job_ptr=job_ptr->Next_ptr; + } + if (new_alarm_count>jobs_alarm_count) vibrate(); + jobs_alarm_count=new_alarm_count; } \ No newline at end of file diff --git a/src/jobs.h b/src/jobs.h index 0af3388..fb7486a 100644 --- a/src/jobs.h +++ b/src/jobs.h @@ -2,17 +2,22 @@ extern uint8_t jobs_count; +void jobs_list_sort(void); void jobs_list_save(uint8_t first_key); void jobs_list_load(uint8_t first_key, const uint8_t version); +void jobs_list_write_dict(DictionaryIterator *iter, uint8_t first_key); +void jobs_list_read_dict(DictionaryIterator *iter, uint8_t first_key); 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); -void jobs_set_job_repeat(uint8_t index, uint8_t repeat); +void jobs_set_job_repeat(uint8_t *index, uint8_t repeat); 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); -void jobs_add_minutes(uint8_t index, int minutes); \ No newline at end of file +void jobs_add_minutes(uint8_t *index, int minutes); +time_t jobs_get_next_wakeup_time(void); +void jobs_check_alarms(void); \ No newline at end of file diff --git a/src/main.c b/src/main.c index f4437ff..27389d1 100644 --- a/src/main.c +++ b/src/main.c @@ -1,10 +1,10 @@ #include "main.h" GBitmap *bitmap_matrix; -GBitmap *bitmap_pause; -GBitmap *bitmap_play; +//GBitmap *bitmap_pause; +//GBitmap *bitmap_play; GBitmap *bitmap_add; -GBitmap *bitmap_settings; +//GBitmap *bitmap_settings; GBitmap *bitmap_delete; GBitmap *bitmap_edit; GBitmap *bitmap_adjust; @@ -12,28 +12,92 @@ GBitmap *bitmap_reset; GBitmap *bitmap_minus; GBitmap *bitmap_tick; +Settings settings={MODE_COUNT_UP, false /*alarm*/, true /*sort*/}; +static bool JS_ready = false; +static bool data_loaded_from_watch = false; + +// ***************************************************************************************************** +// MESSAGES +// ***************************************************************************************************** + +#define KEY_MEDICATIONS 100 +#define KEY_MODE 1 +#define KEY_ALARM 2 +#define KEY_SORT 3 + +static void inbox_received_handler(DictionaryIterator *iter, void *context) { + LOG("Inbox received..."); + JS_ready = true; + Tuple *tuple_t; + + if (!data_loaded_from_watch) { + LOG("Loading settings from phone..."); + 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 + jobs_list_read_dict(iter, KEY_MEDICATIONS); + } + + LOG("Inbox processed."); + main_menu_highlight_top(); +} + +static void send_settings_to_phone() { + if (!JS_ready) return; + DictionaryIterator *iter; + app_message_outbox_begin(&iter); + int dummy_int; + + 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); + jobs_list_write_dict(iter, KEY_MEDICATIONS); + + dict_write_end(iter); + app_message_outbox_send(); +} + +// ***************************************************************************************************** +// WAKEUP +// ***************************************************************************************************** + +#define WAKEUP_REASON 0 +#define WAKEUP_ID_KEY 0 +static WakeupId wakeup_id; + +void main_wakeup_set() { + wakeup_cancel_all(); + if (!settings.Alarm || jobs_count==0) return; + time_t next_alarm = jobs_get_next_wakeup_time(); + if (next_alarm) wakeup_id = wakeup_schedule(next_alarm, WAKEUP_REASON, true); + //LOG("now=%ld, wakeup=%ld, diff=%ld", time(NULL), jobs_get_next_wakeup_time(), jobs_get_next_wakeup_time()-time(NULL)); +} + // ***************************************************************************************************** // DATA STORAGE // ***************************************************************************************************** void main_save_data(void) { persist_write_int(STORAGE_KEY_VERSION, CURRENT_STORAGE_VERSION); - //persist_write_data(STORAGE_KEY_SETTINGS, &settings, sizeof(Settings)); - jobs_list_save(STORAGE_KEY_FIRST_JOB); + persist_write_data(STORAGE_KEY_SETTINGS, &settings, sizeof(Settings)); + if (settings.Sort) jobs_list_sort(); + jobs_list_save(STORAGE_KEY_FIRST_MED); + send_settings_to_phone(); + main_wakeup_set(); } static void main_load_data(void) { uint8_t stored_version = persist_read_int(STORAGE_KEY_VERSION); // defaults to 0 if key is missing - /* - if (persist_exists(STORAGE_KEY_SETTINGS)) { + + if (stored_version) { + data_loaded_from_watch = true; persist_read_data(STORAGE_KEY_SETTINGS, &settings, sizeof(Settings)); - } else { - settings.Show_clock=true; + jobs_list_load(STORAGE_KEY_FIRST_MED, stored_version); + if (stored_version < CURRENT_STORAGE_VERSION) { + LOG("Saving data in new version"); + main_save_data(); + } } - */ - jobs_list_load(STORAGE_KEY_FIRST_JOB, stored_version); - - if (stored_version < STORAGE_KEY_VERSION) main_save_data(); } // ***************************************************************************************************** @@ -43,8 +107,8 @@ static void main_load_data(void) { void init(void) { 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_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); @@ -54,10 +118,14 @@ void init(void) { 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(); + + app_message_register_inbox_received(inbox_received_handler); + app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum()); } void deinit(void) { main_menu_hide(); + main_wakeup_set(); } int main(void) { diff --git a/src/main.h b/src/main.h index b764cba..39c6db8 100644 --- a/src/main.h +++ b/src/main.h @@ -7,7 +7,7 @@ #include "job_adjust.h" #include "tertiary_text.h" -#define DISABLE_LOGGING false +#define DISABLE_LOGGING true #if DISABLE_LOGGING #define LOG(...) @@ -30,6 +30,8 @@ #define MENU_HEIGHT_SINGLE 28 #define MENU_HEIGHT_DOUBLE 42 +#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_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) @@ -55,8 +57,8 @@ #define ICON_RECT_CLOCK (GRect) { { 32, 16 }, { 16, 16 } } extern GBitmap *bitmap_matrix; -extern GBitmap *bitmap_pause; -extern GBitmap *bitmap_play; +//extern GBitmap *bitmap_pause; +//extern GBitmap *bitmap_play; extern GBitmap *bitmap_add; //extern GBitmap *bitmap_settings; extern GBitmap *bitmap_delete; @@ -68,10 +70,23 @@ extern GBitmap *bitmap_tick; // Persistent Storage Keys #define STORAGE_KEY_VERSION 1 -#define STORAGE_KEY_TIMER NO LONGER USED -//#define STORAGE_KEY_SETTINGS NO LONGER USED -#define STORAGE_KEY_FIRST_JOB 100 +#define STORAGE_KEY_SETTINGS 2 +#define STORAGE_KEY_FIRST_MED 100 #define CURRENT_STORAGE_VERSION 1 +typedef struct { + uint8_t Mode; + bool Alarm; + bool Sort; +} Settings; + +extern Settings settings; + +enum { + MODE_COUNT_UP, + MODE_COUNT_DOWN, + MODE_NEXT_TIME +}; + void main_save_data(void); \ No newline at end of file diff --git a/src/main_menu.c b/src/main_menu.c index 761b1ec..190c28f 100644 --- a/src/main_menu.c +++ b/src/main_menu.c @@ -27,6 +27,7 @@ void handle_ticktimer_tick(struct tm *tick_time, TimeUnits units_changed) { if (units_changed & SECOND_UNIT) { main_menu_update(); job_menu_update(); + jobs_check_alarms(); } } @@ -37,11 +38,17 @@ void handle_ticktimer_tick(struct tm *tick_time, TimeUnits units_changed) { enum { // main menu structure MENU_SECTION_JOBS, MENU_SECTION_OTHER, + MENU_SECTION_SETTINGS, NUM_MENU_SECTIONS, MENU_OTHER_ADD=MENU_SECTION_OTHER*100, - NUM_MENU_ITEMS_OTHER=1 + NUM_MENU_ITEMS_OTHER=1, + + MENU_SETTINGS_MODE=MENU_SECTION_SETTINGS*100, + MENU_SETTINGS_ALARM, + MENU_SETTINGS_SORT, + NUM_MENU_ITEMS_SETTINGS=3 }; static uint16_t menu_get_num_sections_callback(MenuLayer *menu_layer, void *data) { @@ -52,11 +59,27 @@ static uint16_t menu_get_num_rows_callback(MenuLayer *menu_layer, uint16_t secti switch (section_index) { case MENU_SECTION_JOBS: return jobs_count; case MENU_SECTION_OTHER: return NUM_MENU_ITEMS_OTHER; + case MENU_SECTION_SETTINGS: return NUM_MENU_ITEMS_SETTINGS; default: return 0; } } +static int16_t menu_get_header_height_callback(MenuLayer *menu_layer, uint16_t section_index, void *data) { + // This is a define provided in pebble.h that you may use for the default height + return (section_index==MENU_SECTION_SETTINGS) ? MENU_CELL_BASIC_HEADER_HEIGHT : 0; +} + +static void menu_draw_header_callback(GContext* ctx, const Layer *cell_layer, uint16_t section_index, void *data) { + // Determine which section we're working with + switch (section_index) { + case MENU_SECTION_JOBS: + case MENU_SECTION_OTHER: + break; + case MENU_SECTION_SETTINGS: menu_cell_basic_header_draw(ctx, cell_layer, "Options"); break; + } +} + static int16_t menu_get_cell_height_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *data) { if (cell_index->section == MENU_SECTION_JOBS) { return MENU_HEIGHT_DOUBLE; @@ -86,7 +109,17 @@ void menu_cell_draw_other(GContext* ctx, const Layer *cell_layer, const char *ti if (icon) graphics_draw_bitmap_in_rect(ctx, icon, GRect(6,(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) { + GRect bounds = layer_get_frame(cell_layer); + + graphics_context_set_text_color(ctx, GColorBlack); + 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); +} + static void menu_draw_row_callback(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, void *data) { + char *mode[3]={"COUNT UP", "COUNT DOWN", "NEXT TIME"}; switch (cell_index->section) { case MENU_SECTION_JOBS: menu_cell_draw_job(ctx, cell_layer, cell_index->row); @@ -95,6 +128,11 @@ 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_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_SORT: menu_cell_draw_setting(ctx, cell_layer, "Sort", settings.Sort ? "YES" : "NO",NULL); break; } } } @@ -109,12 +147,27 @@ static void menu_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, v default: switch (MENU_SECTION_CELL) { case MENU_OTHER_ADD: jobs_add_job(); break; + case MENU_SETTINGS_MODE: + settings.Mode = (settings.Mode + 1) % 3; + main_save_data(); + menu_layer_reload_data(s_menulayer); + break; + case MENU_SETTINGS_ALARM: + settings.Alarm = !settings.Alarm; + main_save_data(); + menu_layer_reload_data(s_menulayer); + break; + case MENU_SETTINGS_SORT: + settings.Sort = !settings.Sort; + main_save_data(); + menu_layer_reload_data(s_menulayer); + break; } } } static void menu_select_long_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *data) { - //if (cell_index->section == MENU_SECTION_JOBS) job_menu_show(cell_index->row); + if (cell_index->section == MENU_SECTION_JOBS) jobs_reset_and_save(cell_index->row); } // ***************************************************************************************************** @@ -139,12 +192,12 @@ void main_menu_show(void) { menu_layer_set_callbacks(s_menulayer, NULL, (MenuLayerCallbacks){ .get_num_sections = menu_get_num_sections_callback, .get_num_rows = menu_get_num_rows_callback, - .get_header_height = NULL, //menu_get_header_height_callback, + .get_header_height = menu_get_header_height_callback, .get_cell_height = menu_get_cell_height_callback, - .draw_header = NULL, //menu_draw_header_callback, + .draw_header = menu_draw_header_callback, .draw_row = menu_draw_row_callback, .select_click = menu_select_callback, - .select_long_click = NULL //menu_select_long_callback + .select_long_click = menu_select_long_callback }); window_stack_push(s_window, ANIMATED); tick_timer_service_subscribe(SECOND_UNIT, handle_ticktimer_tick); @@ -152,4 +205,8 @@ void main_menu_show(void) { void main_menu_hide(void) { window_stack_remove(s_window, ANIMATED); +} + +void main_menu_highlight_top(void) { + menu_layer_set_selected_index(s_menulayer, MenuIndex(0,0), MenuRowAlignTop, ANIMATED); } \ No newline at end of file diff --git a/src/main_menu.h b/src/main_menu.h index c34c1e4..5a3474e 100644 --- a/src/main_menu.h +++ b/src/main_menu.h @@ -6,4 +6,5 @@ void handle_ticktimer_tick(struct tm *tick_time, TimeUnits units_changed); void main_menu_show(void); void main_menu_hide(void); void main_menu_update(void); -void main_menu_toggle_clock(void); \ No newline at end of file +void main_menu_toggle_clock(void); +void main_menu_highlight_top(void); \ No newline at end of file diff --git a/src/pebble-js-app.js b/src/pebble-js-app.js new file mode 100644 index 0000000..c710a5a --- /dev/null +++ b/src/pebble-js-app.js @@ -0,0 +1,18 @@ +// https://github.com/pebble-examples/slate-config-example/blob/master/src/js/pebble-js-app.js + +Pebble.addEventListener('ready', function() { + console.log('PebbleKit JS ready!'); + var settings=localStorage.getItem("settings"); + //settings='{"101":"Tremadol|1452850614|8","102":"Ibuprofin|1452841367|12","103":"Zopiclone|1452813635|24","104":"Omeprazole|1452841369|24","KEY_SORT":1,"KEY_MEDICATIONS":"Paracetamol|1452850611|6","KEY_MODE":2,"KEY_ALARM":1}'; + var dict=settings ? JSON.parse(settings) : {}; + Pebble.sendAppMessage(dict, function() { + console.log('Send successful: ' + JSON.stringify(dict)); + }, function() { + console.log('Send failed!'); + }); +}); + +Pebble.addEventListener("appmessage", function(e) { + console.log("Received message: " + JSON.stringify(e.payload)); + localStorage.setItem("settings",JSON.stringify(e.payload)); +}); \ No newline at end of file