Initial version

master
Alinson S. Xavier 9 years ago
commit c07d86512a

11
.gitignore vendored

@ -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/

@ -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})

@ -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"
}
]
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

@ -0,0 +1,28 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <pebble.h>
#include "windows/list_window.h"
int main(void)
{
LIST_WINDOW_push();
app_event_loop();
LIST_WINDOW_destroy();
}

@ -0,0 +1,185 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <pebble.h>
#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;
}

@ -0,0 +1,26 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
int NETWORK_register();
int NETWORK_request_list();
int NETWORK_request_toggle(int id);

@ -0,0 +1,31 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <pebble.h>
#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; }

@ -0,0 +1,225 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <pebble.h>
#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);
}

@ -0,0 +1,39 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);

@ -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')