From c07d86512a77fa43c665ce374cd2ac180a5e2589 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Sat, 9 Jul 2016 06:36:51 -0400 Subject: [PATCH] Initial version --- .gitignore | 11 ++ CMakeLists.txt | 16 +++ package.json | 51 +++++++ resources/images/cross_black.png | Bin 0 -> 257 bytes resources/images/cross_white.png | Bin 0 -> 257 bytes resources/images/tick_black.png | Bin 0 -> 270 bytes resources/images/tick_white.png | Bin 0 -> 273 bytes src/main.c | 28 ++++ src/network.c | 185 +++++++++++++++++++++++++ src/network.h | 26 ++++ src/util.h | 31 +++++ src/windows/list_window.c | 225 +++++++++++++++++++++++++++++++ src/windows/list_window.h | 39 ++++++ wscript | 50 +++++++ 14 files changed, 662 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 package.json create mode 100644 resources/images/cross_black.png create mode 100644 resources/images/cross_white.png create mode 100644 resources/images/tick_black.png create mode 100644 resources/images/tick_white.png create mode 100644 src/main.c create mode 100644 src/network.c create mode 100644 src/network.h create mode 100644 src/util.h create mode 100644 src/windows/list_window.c create mode 100644 src/windows/list_window.h create mode 100644 wscript diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a882ed3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Ignore build generated files +build/ +dist/ +dist.zip + +# Ignore waf lock file +.lock-waf* + +# Ignore installed node modules +node_modules/ +.idea/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7d8b877 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.5) +project(hello) + +SET(PEBBLE_FLAVOUR basalt) +SET(PEBBLE_SDK_VER 3.13.1) +SET(PEBBLE_SDK "/opt/pebble-sdk") +INCLUDE_DIRECTORIES("${PEBBLE_SDK}/.pebble-sdk/SDKs/${PEBBLE_SDK_VER}/sdk-core/pebble/${PEBBLE_FLAVOUR}/include") +INCLUDE_DIRECTORIES("build/${PEBBLE_FLAVOUR}/src") +INCLUDE_DIRECTORIES("build/include") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +set(SOURCE_FILES + src/main.c src/network.c src/network.h src/windows/list_window.c src/windows/list_window.h src/util.h) + +add_executable(hello ${SOURCE_FILES}) \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..8884f5c --- /dev/null +++ b/package.json @@ -0,0 +1,51 @@ +{ + "name": "Loop Habit Tracker", + "author": "Álinson S Xavier", + "version": "1.0.0", + "keywords": [ + "pebble-app" + ], + "private": true, + "dependencies": {}, + "pebble": { + "displayName": "Loop Habit Tracker", + "uuid": "82629d99-8ea6-4631-a022-9ca77a12a058", + "sdkVersion": "3", + "enableMultiJS": true, + "targetPlatforms": [ + "aplite", + "basalt", + "chalk" + ], + "watchapp": { + "watchface": false + }, + "messageKeys": [ + "dummy" + ], + "resources": { + "media": [ + { + "type": "bitmap", + "name": "TICK_BLACK", + "file": "images/tick_black.png" + }, + { + "type": "bitmap", + "name": "TICK_WHITE", + "file": "images/tick_white.png" + }, + { + "type": "bitmap", + "name": "CROSS_WHITE", + "file": "images/cross_white.png" + }, + { + "type": "bitmap", + "name": "CROSS_BLACK", + "file": "images/cross_black.png" + } + ] + } + } +} diff --git a/resources/images/cross_black.png b/resources/images/cross_black.png new file mode 100644 index 0000000000000000000000000000000000000000..bf16c8680211c977ac2fdd481f23fc29c9421ca3 GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPNJBN^h(3RQgnn0mGPZ!4!i_=SkPjekG;P9@JTKxb2 z(#3C{`AJ{oU649-QVi6yBi z3gww484B*6z5ywEsq8@UB2O2`5R21CC-3Dupuo|>ua;c%U)}WBSwFKaP9M`Xx|wU# z%iX#(A1sk*->Mo=lzW>=_MQ6L6oq^_GmV{khYeYiF6K<@U}a`t%!o>63v-^*z&hm8ib6Mw<&;$U} Cr%&qu literal 0 HcmV?d00001 diff --git a/resources/images/tick_white.png b/resources/images/tick_white.png new file mode 100644 index 0000000000000000000000000000000000000000..75090130172546db1c16e700e45b2b21534002dc GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkEh-Zup%4^&08ws|*?~)LHX_Zmf=& z#d7${$>~8qBYc@|7j1|q-1!bhCSbU=UWrepov@W*FR?c{>`pmTRhtg + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include "windows/list_window.h" + +int main(void) +{ + LIST_WINDOW_push(); + app_event_loop(); + LIST_WINDOW_destroy(); +} diff --git a/src/network.c b/src/network.c new file mode 100644 index 0000000..899f65c --- /dev/null +++ b/src/network.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include "network.h" +#include "util.h" +#include "windows/list_window.h" + +#define MAX_HABIT_COUNT 50 +static int HABIT_COUNT; +static int HABIT_CURRENT; + +// LIST WINDOW +//------------------------------------------------------------------------------ + +static int request_next_habit() +{ + int rval = 0; + + if(HABIT_CURRENT >= HABIT_COUNT) + goto CLEANUP; + + DictionaryIterator *dict; + rval = app_message_outbox_begin(&dict); + abort_if(rval, "app_message_outbox_begin failed"); + + rval = dict_write_cstring(dict, 0, "FETCH"); + abort_if(rval, "dict_write_cstring failed"); + + rval = dict_write_int32(dict, 1, HABIT_CURRENT); + abort_if(rval, "dict_write_cstring failed"); + + rval = app_message_outbox_send(); + abort_if(rval, "app_message_outbox_send failed"); + + HABIT_CURRENT++; + +CLEANUP: + return rval; +} + +static int process_count(DictionaryIterator *iter) +{ + int rval = 0; + + Tuple *tuple = dict_find(iter, 1); + abort_if(!tuple, "tuple is null"); + + int count = tuple->value->int32; + + rval = LIST_WINDOW_allocate(count); + abort_if(rval, "LIST_WINDOW_allocate failed"); + + if(count > MAX_HABIT_COUNT) count = MAX_HABIT_COUNT; + HABIT_COUNT = count; + HABIT_CURRENT = 0; + + rval = request_next_habit(); + abort_if(rval, "request_next_habit failed"); + + CLEANUP: + return rval; +} + +static int process_habit(DictionaryIterator *iter) +{ + int rval = 0; + + // id + Tuple *tuple = dict_find(iter, 1); + abort_if(!tuple, "tuple is null"); + int id = tuple->value->int32; + + // name + tuple = dict_find(iter, 2); + abort_if(!tuple, "tuple is null"); + char *name = tuple->value->cstring; + + // checkmark value + tuple = dict_find(iter, 3); + abort_if(!tuple, "tuple is null"); + int checkmark = tuple->value->int32; + + rval = LIST_WINDOW_add_habit(id, name, checkmark); + abort_if(rval, "LIST_WINDOW_add_habit failed"); + + rval = request_next_habit(); + abort_if(rval, "request_next_habit failed"); + +CLEANUP: + return rval; +} + +int NETWORK_request_list() +{ + int rval = 0; + + DictionaryIterator *dict; + rval = app_message_outbox_begin(&dict); + abort_if(rval, "app_message_outbox_begin failed"); + + rval = dict_write_cstring(dict, 0, "COUNT"); + abort_if(rval, "dict_write_cstring failed"); + + rval = app_message_outbox_send(); + abort_if(rval, "app_message_outbox_send failed"); + +CLEANUP: + return rval; +} + +int NETWORK_request_toggle(int id) +{ + int rval = 0; + + DictionaryIterator *dict; + rval = app_message_outbox_begin(&dict); + abort_if(rval, "app_message_outbox_begin failed"); + + rval = dict_write_cstring(dict, 0, "TOGGLE"); + abort_if(rval, "dict_write_cstring failed"); + + rval = dict_write_int32(dict, 1, id); + abort_if(rval, "dict_write_cstring failed"); + + rval = app_message_outbox_send(); + abort_if(rval, "app_message_outbox_send failed"); + + CLEANUP: + return rval; +} + +//------------------------------------------------------------------------------ + +static void NETWORK_on_received(DictionaryIterator *iter, void *context) +{ + int rval = 0; + + Tuple *tuple = dict_find(iter, 0); + abort_if(!tuple, "tuple is null"); + + char *action = tuple->value->cstring; + + if(strcmp(action, "COUNT") == 0) + { + rval = process_count(iter); + abort_if(rval, "process_count failed"); + } + else if(strcmp(action, "HABIT") == 0) + { + rval = process_habit(iter); + abort_if(rval, "process_habit failed"); + } + else if(strcmp(action, "OK") == 0) + { + rval = NETWORK_request_list(); + abort_if(rval, "NETWORK_request_list failed"); + } + +CLEANUP: + return; +} + +int NETWORK_register() +{ + app_message_register_inbox_received(NETWORK_on_received); + app_message_open(1024, 1024); + return 0; +} \ No newline at end of file diff --git a/src/network.h b/src/network.h new file mode 100644 index 0000000..3636443 --- /dev/null +++ b/src/network.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#pragma once + +int NETWORK_register(); + +int NETWORK_request_list(); + +int NETWORK_request_toggle(int id); \ No newline at end of file diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..6246a66 --- /dev/null +++ b/src/util.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#pragma once + +#include + +#define abort_if(cond, msg) if(cond) { \ + APP_LOG(APP_LOG_LEVEL_ERROR, msg " (%d) (%s:%d)", rval, __FILE__, __LINE__); \ + rval = 1; goto CLEANUP; } + +#define abort_iff(cond, msg, ...) if(cond) { \ + APP_LOG(APP_LOG_LEVEL_ERROR, msg " (%d) (%s:%d)", __VA_ARGS__, rval, __FILE__, \ + __LINE__); \ + rval = 1; goto CLEANUP; } diff --git a/src/windows/list_window.c b/src/windows/list_window.c new file mode 100644 index 0000000..24fe3a6 --- /dev/null +++ b/src/windows/list_window.c @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include "../network.h" +#include "../util.h" +#include "list_window.h" + +static Window *WINDOW = 0; +static MenuLayer *MENU_LAYER = 0; + +static uint16_t HABIT_COUNT = 0; +static char **HABIT_NAMES = 0; +static int *HABIT_IDS = 0; +static int *HABIT_CHECKMARKS = 0; +static size_t MAX_NAME_LENGTH = 20; + +static GBitmap *TICK_WHITE; +static GBitmap *TICK_BLACK; +static GBitmap *CROSS_BLACK; +static GBitmap *CROSS_WHITE; + +int LIST_WINDOW_allocate(int count); + +int LIST_WINDOW_add_habit(int new_id, char *new_name, int new_checkmark); + +void free_habits() +{ + HABIT_COUNT = 0; + for (int i = 0; i < HABIT_COUNT; i++) free(HABIT_NAMES[i]); + free(HABIT_NAMES); + free(HABIT_IDS); +} + +// MENU LAYER CALLBACKS +//------------------------------------------------------------------------------ + +static uint16_t get_num_rows(MenuLayer *menu_layer, + uint16_t section_index, + void *context) +{ + return HABIT_COUNT; +} + +GBitmap *getTickBitmap(int highlighted) +{ + return highlighted ? TICK_WHITE : TICK_BLACK; +} + +GBitmap *getCrossBitmap(int highlighted) +{ + return highlighted ? CROSS_WHITE : CROSS_BLACK; +} + +GBitmap *getIcon(int is_checked, int is_highlighted) +{ + if(is_checked == 0) return getCrossBitmap(is_highlighted); + else return getTickBitmap(is_highlighted); +} + +static void draw_row(GContext *ctx, + const Layer *cell_layer, + MenuIndex *cell_index, + void *context) +{ + int n = cell_index->row; + char *name = HABIT_NAMES[n]; + int is_checked = HABIT_CHECKMARKS[n]; + int is_highlighted = menu_cell_layer_is_highlighted(cell_layer); + + GBitmap *icon = getIcon(is_checked, is_highlighted); + menu_cell_basic_draw(ctx, cell_layer, name, NULL, NULL); +} + +static int16_t get_cell_height(struct MenuLayer *menu_layer, + MenuIndex *cell_index, + void *context) +{ + return CELL_HEIGHT; +} + +static void select_click(MenuLayer *menu_layer, MenuIndex *index, void *context) +{ + int n = index->row; + int id = HABIT_IDS[n]; + NETWORK_request_toggle(id); +} + +static void request_list(void *context) +{ + LIST_WINDOW_allocate(5); + LIST_WINDOW_add_habit(0, "Wake up early", 0); + LIST_WINDOW_add_habit(1, "Meditate", 1); + LIST_WINDOW_add_habit(2, "Exercise", 1); + LIST_WINDOW_add_habit(3, "Go to school", 0); + + NETWORK_request_list(); +} + +// WINDOWS HANDLERS +//------------------------------------------------------------------------------ + +static void on_load(Window *window) +{ + Layer *root_layer = window_get_root_layer(window); + GRect bounds = layer_get_bounds(root_layer); + + MenuLayerCallbacks callbacks = { + .get_num_rows = get_num_rows, + .draw_row = draw_row, + .get_cell_height = get_cell_height, + .select_click = select_click, + }; + + MENU_LAYER = menu_layer_create(bounds); + menu_layer_set_click_config_onto_window(MENU_LAYER, window); + menu_layer_set_callbacks(MENU_LAYER, NULL, callbacks); + menu_layer_set_highlight_colors(MENU_LAYER, HIGHLIGHT_BACKGROUND_COLOR, + HIGHLIGHT_FOREGROUND_COLOR); + menu_layer_set_normal_colors(MENU_LAYER, NORMAL_BACKGROUND_COLOR, + NORMAL_FOREGROUND_COLOR); + + layer_add_child(root_layer, menu_layer_get_layer(MENU_LAYER)); + app_timer_register(500, request_list, 0); +} + +static void on_unload(Window *window) +{ + menu_layer_destroy(MENU_LAYER); +} + +//-------------------------------------------------------------------------------- + +int LIST_WINDOW_allocate(int count) +{ + int rval = 0; + + if(HABIT_COUNT > 0) free_habits(); + + HABIT_NAMES = (char**) malloc(count * sizeof(char*)); + abort_if(!HABIT_NAMES, "could not allocate HABIT_NAMES"); + + for(int i = 0; i < count; i++) + { + HABIT_NAMES[i] = (char*) malloc(MAX_NAME_LENGTH * sizeof(char)); + abort_iff(!HABIT_NAMES[i], "could not allocate HABIT_NAMES[%d]", i); + } + + HABIT_CHECKMARKS = (int *) malloc(count * sizeof(int)); + abort_if(!HABIT_CHECKMARKS, "could not allocate HABIT_CHECKMARKS"); + + HABIT_IDS = (int *) malloc(count * sizeof(int)); + abort_if(!HABIT_IDS, "could not allocate HABIT_IDS"); + +CLEANUP: + return rval; +} + +int LIST_WINDOW_add_habit(int new_id, char *new_name, int new_checkmark) +{ + char *name = HABIT_NAMES[HABIT_COUNT]; + strncpy(name, new_name, MAX_NAME_LENGTH); + menu_layer_reload_data(MENU_LAYER); + + HABIT_IDS[HABIT_COUNT] = new_id; + HABIT_CHECKMARKS[HABIT_COUNT] = new_checkmark; + HABIT_COUNT++; + + return 0; +} + +void create_bitmaps() +{ + CROSS_BLACK = gbitmap_create_with_resource(RESOURCE_ID_CROSS_BLACK); + CROSS_WHITE = gbitmap_create_with_resource(RESOURCE_ID_CROSS_WHITE); + TICK_WHITE = gbitmap_create_with_resource(RESOURCE_ID_TICK_WHITE); + TICK_BLACK = gbitmap_create_with_resource(RESOURCE_ID_TICK_BLACK); +} + +void free_bitmaps() +{ + gbitmap_destroy(CROSS_BLACK); + gbitmap_destroy(CROSS_WHITE); + gbitmap_destroy(TICK_WHITE); + gbitmap_destroy(TICK_BLACK); +} + +void LIST_WINDOW_push() +{ + NETWORK_register(); + + create_bitmaps(); + + WindowHandlers handlers = { + .load = on_load, + .unload = on_unload + }; + + WINDOW = window_create(); + window_set_window_handlers(WINDOW, handlers); + window_stack_push(WINDOW, true); +} + +void LIST_WINDOW_destroy() +{ + free_bitmaps(); + free_habits(); + window_destroy(WINDOW); +} \ No newline at end of file diff --git a/src/windows/list_window.h b/src/windows/list_window.h new file mode 100644 index 0000000..4739c6b --- /dev/null +++ b/src/windows/list_window.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#pragma once + +// STYLE +//-------------------------------------------------------------------------------- + +#define HIGHLIGHT_BACKGROUND_COLOR GColorFolly +#define HIGHLIGHT_FOREGROUND_COLOR GColorWhite +#define NORMAL_BACKGROUND_COLOR GColorBlack +#define NORMAL_FOREGROUND_COLOR GColorWhite +#define CELL_HEIGHT 36 + +//-------------------------------------------------------------------------------- + +void LIST_WINDOW_push(); + +void LIST_WINDOW_destroy(); + +int LIST_WINDOW_allocate(int count); + +int LIST_WINDOW_add_habit(int new_id, char *new_checkmark, int i); diff --git a/wscript b/wscript new file mode 100644 index 0000000..e721ba8 --- /dev/null +++ b/wscript @@ -0,0 +1,50 @@ +# +# This file is the default set of rules to compile a Pebble application. +# +# Feel free to customize this to your needs. +# +import os.path + +top = '.' +out = 'build' + + +def options(ctx): + ctx.load('pebble_sdk') + + +def configure(ctx): + """ + This method is used to configure your build. ctx.load(`pebble_sdk`) automatically configures + a build for each valid platform in `targetPlatforms`. Platform-specific configuration: add your + change after calling ctx.load('pebble_sdk') and make sure to set the correct environment first. + Universal configuration: add your change prior to calling ctx.load('pebble_sdk'). + """ + ctx.load('pebble_sdk') + + +def build(ctx): + ctx.load('pebble_sdk') + + build_worker = os.path.exists('worker_src') + binaries = [] + + cached_env = ctx.env + for platform in ctx.env.TARGET_PLATFORMS: + ctx.env = ctx.all_envs[platform] + ctx.set_group(ctx.env.PLATFORM_NAME) + app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR) + ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'), target=app_elf) + + if build_worker: + worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR) + binaries.append({'platform': platform, 'app_elf': app_elf, 'worker_elf': worker_elf}) + ctx.pbl_worker(source=ctx.path.ant_glob('worker_src/**/*.c'), target=worker_elf) + else: + binaries.append({'platform': platform, 'app_elf': app_elf}) + ctx.env = cached_env + + ctx.set_group('bundle') + ctx.pbl_bundle(binaries=binaries, + js=ctx.path.ant_glob(['src/js/**/*.js', 'src/js/**/*.json']), + js_entry_file='src/js/app.js')