diff --git a/README.md b/README.md index 4ec6249..5862dc9 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,51 @@ -A checklist—on your watch! +A checklist-on your watch! + + +Thanks to the Pebble's microphone and voice input, it's now possible to keep track of anything on your wrist! Designed to stay out of your way, use Checklist to keep track of anything you need without needing to hold your phone-things to-do, shopping, and more! -Thanks to the Pebble’s microphone and voice input, it’s now possible to keep track of anything on your wrist! Designed to stay out of your way, use Checklist to keep track of anything you need without needing to hold your phone. Grocery shopping will never be the same. + +- Want to add multiple items at once? Just separate those items with periods or commas! (For example, saying "milk comma eggs comma cheese" will create 3 list items!) + + +- Don't want to use voice? Now you can enter items manually via your phone on the configuration page! + + + +"...only one app, a simple list-making app called Checklist, which uses the watch's microphone to add tasks, really proved useful." +-MIT Technology Review + +"I loved the convenience of adding items to the Checklist app just by speaking" +-Tom's Guide + + +****** + +Note: If voice isn't working, make sure your Pebble Time app is the newest version (3.6.1 on Android, 3.3.1 on iOS) and that your Pebble is running firmware 3.6 or higher. You can check under "Support" in the Pebble Time app. + +In additon, ensure you are subscribed to a voice transcription service, for example http://rebble.io/ + + ## Want to try it? + Grab it from the Rebble/Pebble app store: + https://apps.rebble.io/en_US/application/5620e876768e7ada4e00007a?section=watchapps&dev_settings=true + +## Latest version with export + +Available from https://github.com/clach04/PebbleChecklist/releases + +To export notes: + + * open config + * add new note with title "config" (with no quotes), save + * open config again + * copy from the export field (which is the same format the add note field accepts + +NOTE check item status is NOT exported. Items end up in local storage. diff --git a/src/checklist.c b/src/checklist.c index 22b2356..a25715a 100644 --- a/src/checklist.c +++ b/src/checklist.c @@ -74,6 +74,8 @@ void read_data_from_storage() { // load checklist information from storage s_checklist_length = persist_read_int(PERSIST_KEY_CHECKLIST_LENGTH); s_checklist_num_checked = persist_read_int(PERSIST_KEY_CHECKLIST_NUM_CHECKED); + APP_LOG(APP_LOG_LEVEL_DEBUG, "clach04 s_checklist_length: %d", (int) s_checklist_length); + APP_LOG(APP_LOG_LEVEL_DEBUG, "clach04 s_checklist_num_checked: %d", (int) s_checklist_num_checked); // load the checklist by the block int num_blocks_required = s_checklist_length / s_items_per_block + 1; @@ -83,6 +85,10 @@ void read_data_from_storage() { &s_checklist_items[block * s_items_per_block], s_block_size); } + // index 0 is the bottom of the list, higher numbers are top of list + for(int i = 0; i < MAX_CHECKLIST_ITEMS && strlen(s_checklist_items[i].name) > 0; i++) { + APP_LOG(APP_LOG_LEVEL_DEBUG, "clach04 [%02d] %d %s", i, (int) s_checklist_items[i].is_checked, s_checklist_items[i].name); + } } void save_data_to_storage() { diff --git a/src/js/config.js b/src/js/config.js index 092fd2c..b53a7b1 100644 --- a/src/js/config.js +++ b/src/js/config.js @@ -1,6 +1,7 @@ // var BASE_CONFIG_URL = 'http://localhost:4000/'; // var BASE_CONFIG_URL = 'http://192.168.0.103:4000/'; -var BASE_CONFIG_URL = 'http://freakified.github.io/PebbleChecklist/'; +var BASE_CONFIG_URL = 'http://clach04.github.io/pebble/checklist/'; + Pebble.addEventListener('ready', function(e) { console.log('JS component loaded!'); @@ -9,6 +10,22 @@ Pebble.addEventListener('ready', function(e) { // open the config page when requested Pebble.addEventListener('showConfiguration', function(e) { var configURL = BASE_CONFIG_URL + 'config.html'; + var items_exported = localStorage.getItem('items_exported') || '[]'; + + try { + items_exported = JSON.parse(items_exported); + } + catch(error) { + console.error('items_exported parse failed, defaulting to [] - ' + error); + // expected output: ReferenceError: nonExistentFunction is not defined + // Note - error messages will vary depending on browser + items_exported = []; + } + + console.log('config items_exported = ', JSON.stringify(items_exported)); + //configURL = configURL + '?items_exported=test,notes,here'; + //configURL = configURL + '?items_exported=' + encodeURIComponent(items_exported.join(',')); + configURL = configURL + '?items_exported=' + items_exported.join(','); Pebble.openURL(configURL); }); @@ -35,6 +52,7 @@ Pebble.addEventListener('webviewclosed', function(e) { // Send settings to Pebble watchapp Pebble.sendAppMessage(dict, function(){ console.log('Sent config data to Pebble'); + //itemsToAdd = []; // reset export list }, function() { console.log('Failed to send config data!'); }); @@ -42,3 +60,18 @@ Pebble.addEventListener('webviewclosed', function(e) { console.log("No settings changed!"); } }); + +Pebble.addEventListener("appmessage", function(e) { + if (e.payload.KEY_ITEMS_TO_ADD) { + var items_exported = []; + + console.log('Message from Pebble: ' + JSON.stringify(e.payload)); + console.log('Message from Pebble value: ' + e.payload.KEY_ITEMS_TO_ADD); + console.log('pre add items_exported = ', JSON.stringify(items_exported)); + items_exported.push(e.payload.KEY_ITEMS_TO_ADD); + console.log('post add items_exported = ', JSON.stringify(items_exported)); + //items_exported.push('static'); + //console.log('post add static items_exported = ', JSON.stringify(items_exported)); + localStorage.setItem('items_exported', JSON.stringify(items_exported)); // global variables do not persist even when watch app is still running, so store now + } +}); diff --git a/src/messaging.c b/src/messaging.c index 5243cb7..47e34a6 100644 --- a/src/messaging.c +++ b/src/messaging.c @@ -2,7 +2,7 @@ #include "messaging.h" #include "checklist.h" -static char s_items_to_add_buffer[512]; +static char s_items_to_add_buffer[(MAX_NAME_LENGTH * MAX_CHECKLIST_ITEMS) + (MAX_CHECKLIST_ITEMS * 1) + 1]= ""; // space for comma and a space, and final trailing blank - total should be smaller than app_message_outbox void (*message_processed_callback)(void); @@ -17,7 +17,13 @@ void messaging_init(void (*processed_callback)(void)) { app_message_register_outbox_sent(outbox_sent_callback); // Open AppMessage - app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum()); + //app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum()); + //APP_LOG(APP_LOG_LEVEL_DEBUG, "clach04 app_message_inbox_size_maximum()=%d", app_message_inbox_size_maximum ()); // 8200 bytes + //APP_LOG(APP_LOG_LEVEL_DEBUG, "clach04 app_message_outbox_size_maximum()=%d", app_message_outbox_size_maximum()); // 8200 bytes + //APP_LOG(APP_LOG_LEVEL_DEBUG, "clach04 PERSIST_DATA_MAX_LENGTH=%d", PERSIST_DATA_MAX_LENGTH); // 256 bytes + //APP_LOG(APP_LOG_LEVEL_DEBUG, "clach04 PERSIST_STRING_MAX_LENGTH=%d", PERSIST_STRING_MAX_LENGTH); // 256 bytes + // with 52 * 50 bytes could send entire checklist as one string to phone.... + app_message_open(sizeof(s_items_to_add_buffer) + /* safety net buffer */ + 100, (MAX_NAME_LENGTH * MAX_CHECKLIST_ITEMS) + /* safety net buffer */ + 100); APP_LOG(APP_LOG_LEVEL_DEBUG, "Watch messaging is started!"); app_message_register_inbox_received(inbox_received_callback); @@ -29,8 +35,60 @@ void inbox_received_callback(DictionaryIterator *iterator, void *context) { if(items_to_add_tuple != NULL) { strncpy(s_items_to_add_buffer, items_to_add_tuple->value->cstring, sizeof(s_items_to_add_buffer) - 1); + APP_LOG(APP_LOG_LEVEL_DEBUG, "clach04 inbox value '%s'", s_items_to_add_buffer); + if (strncmp(s_items_to_add_buffer, "export", sizeof(s_items_to_add_buffer) - 1) == 0) + { + s_items_to_add_buffer[0] = '\0'; + ChecklistItem *item=NULL; - checklist_add_items(s_items_to_add_buffer); + // magic export mode keyword found + APP_LOG(APP_LOG_LEVEL_DEBUG, "clach04 got magic export mode keyword"); + + // index 0 is the bottom of the list, higher numbers are top of list + // dump entire contents, should really make use of success handler + // for array sending but data small enough to do in one go so cheat :-) + for(int id = 0; id < checklist_get_num_items() ; id++) { + item = checklist_get_item_by_id(id); + APP_LOG(APP_LOG_LEVEL_DEBUG, "clach04 [%02d] %d %s", id, (int) item->is_checked, item->name); + + // quick-n-dirty concat all items - ignore "checked" status + // FIXME below will get slower and slower as more items are added + strcat(s_items_to_add_buffer, item->name); + strcat(s_items_to_add_buffer, ","); + } + + // Declare the dictionary's iterator + DictionaryIterator *out_iter; + + // Prepare the outbox buffer for this message + AppMessageResult result = app_message_outbox_begin(&out_iter); + if(result == APP_MSG_OK) { + // Construct the message + DictionaryResult dict_result = dict_write_cstring(out_iter, KEY_ITEMS_TO_ADD, s_items_to_add_buffer); + + if (dict_result == DICT_OK) { + // Send this message + result = app_message_outbox_send(); + + // Check the result + if(result != APP_MSG_OK) { + APP_LOG(APP_LOG_LEVEL_ERROR, "Error sending the outbox: %d", (int)result); + } + } + else { + APP_LOG(APP_LOG_LEVEL_ERROR, "Error writing cstring to out dict: %d", (int)dict_result); + } + + + } else { + // The outbox cannot be used right now + APP_LOG(APP_LOG_LEVEL_ERROR, "Error preparing the outbox: %d", (int)result); + } + } + else + { + checklist_add_items(s_items_to_add_buffer); + } } // notify the main screen, in case something changed diff --git a/src/windows/checklist_window.c b/src/windows/checklist_window.c index 6c24e7b..ecb799e 100644 --- a/src/windows/checklist_window.c +++ b/src/windows/checklist_window.c @@ -280,6 +280,22 @@ static void select_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, } } +static void select_long_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context) { + if(cell_index->row == 0) { + // dump counts? export all non-checked? + APP_LOG(APP_LOG_LEVEL_DEBUG, "clach04 select_long_callback() TOP checklist_get_num_items()=%d", checklist_get_num_items()); + } else if(cell_index->row == checklist_get_num_items() + 1) { + // dump all check, export all checked? + APP_LOG(APP_LOG_LEVEL_DEBUG, "clach04 select_long_callback() BOTTOM (clear)"); + } else { + // Dump highlighted + int id = checklist_get_num_items() - (cell_index->row - 1) - 1; + + ChecklistItem *item = checklist_get_item_by_id(id); + APP_LOG(APP_LOG_LEVEL_DEBUG, "clach04 [%02d] %d %s", id, (int) item->is_checked, item->name); + } +} + static void window_load(Window *window) { checklist_init(); @@ -311,6 +327,7 @@ static void window_load(Window *window) { .draw_row = (MenuLayerDrawRowCallback)draw_row_callback, .get_cell_height = (MenuLayerGetCellHeightCallback)get_cell_height_callback, .select_click = (MenuLayerSelectCallback)select_callback, + .select_long_click = (MenuLayerSelectCallback)select_long_callback, }); window_set_background_color(window, BG_COLOR);