From 9af3f7156238af9659ed75c072860205ba5af5bd Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Mon, 13 Apr 2015 12:22:52 +0300 Subject: First commit --- appinfo.json | 26 ++++++ src/dembel.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/pebble-js-app.js | 62 +++++++++++++++ wscript | 62 +++++++++++++++ 4 files changed, 368 insertions(+) create mode 100644 appinfo.json create mode 100644 src/dembel.c create mode 100644 src/pebble-js-app.js create mode 100644 wscript diff --git a/appinfo.json b/appinfo.json new file mode 100644 index 0000000..c80a825 --- /dev/null +++ b/appinfo.json @@ -0,0 +1,26 @@ +{ + "appKeys": { + "end_time": 1, + "start_time": 0 + }, + "capabilities": [ + "configurable" + ], + "companyName": "Cluster", + "longName": "DEMBEL WATCHFACE", + "projectType": "native", + "resources": { + "media": [] + }, + "sdkVersion": "3", + "shortName": "DEMBEL WATCHFACE", + "targetPlatforms": [ + "aplite" + ], + "uuid": "3ac71f3b-063c-470a-b94b-cb09154adc78", + "versionCode": 1, + "versionLabel": "1.0", + "watchapp": { + "watchface": true + } +} diff --git a/src/dembel.c b/src/dembel.c new file mode 100644 index 0000000..4e11246 --- /dev/null +++ b/src/dembel.c @@ -0,0 +1,218 @@ +#include + +static time_t start_time; +static time_t end_time; + +static Window *window; +static TextLayer *time_layer; +static TextLayer *left_time_layer; +static TextLayer *left_time_layer2; +static TextLayer *percent_layer; +static Layer *progress_layer; + +static float percent = 0; + +#define MSG_START_TIME 0 +#define MSG_END_TIME 1 + +static void fucking_russian(int number, char* text, char* def, char* ending1, char* ending234) +{ + if (number >= 10 && number <= 19) + strcat(text, def); + else + switch (number % 10) + { + case 0: + case 5: + case 6: + case 7: + case 8: + case 9: + strcat(text, def); + break; + case 1: + strcat(text, ending1); + break; + case 2: + case 3: + case 4: + strcat(text, ending234); + break; + } +} + +static void handle_tick(struct tm *tick_time, TimeUnits units_changed) +{ + time_t now = time(NULL); + + long int left_total = end_time - now; + if (left_total < 0) left_total *= -1; + int left_mins = (left_total / 60) % 60; + int left_hours = (left_total / (60*60)) % 24; + int left_days = left_total / (60*60*24); + + static char left_time_str[200]; + char minut[15]; + char chas[15]; + char den[15]; + + strcpy(minut, "минут"); + strcpy(chas, "час"); + strcpy(den, "д"); + if (end_time > now) + fucking_russian(left_mins, minut, "", "а", "ы"); + else + fucking_russian(left_mins, minut, "", "у", "ы"); + fucking_russian(left_hours, chas, "ов", "", "а"); + fucking_russian(left_days, den, "ней", "ень", "ня"); + + if (end_time > now) + text_layer_set_text(left_time_layer, "До дембеля осталось"); + else + text_layer_set_text(left_time_layer, "Ты дембель уже"); + snprintf(left_time_str, sizeof(left_time_str), "%d %s,\n%d %s\nи %d %s", + left_days, den, left_hours, chas, left_mins, minut); + text_layer_set_text(left_time_layer2, left_time_str); + + percent = 100.0 * (now - start_time) / (end_time - start_time); + if (percent > 100) percent = 100; + if (percent < 0) percent = 0; + static char percent_str[200]; + snprintf(percent_str, sizeof(percent_str), "Ты отслужил %d.%02d%%", (int)percent, (int)(percent*100)%100); + text_layer_set_text(percent_layer, percent_str); + layer_mark_dirty(progress_layer); + + static char time_str[20]; + if (clock_is_24h_style()) + strftime(time_str, sizeof(time_str), "%H:%M", tick_time); + else + strftime(time_str, sizeof(time_str), "%I:%M", tick_time); + text_layer_set_text(time_layer, time_str); +} + +static void progress_layer_update_callback(Layer *layer, GContext *ctx) +{ + graphics_context_set_stroke_color(ctx, GColorBlack); + graphics_context_set_fill_color(ctx, GColorBlack); + GRect rect = layer_get_bounds(layer); + graphics_draw_round_rect(ctx, rect, 0); + rect.origin.x += 2; + rect.origin.y += 2; + rect.size.h -= 4; + rect.size.w = (rect.size.w-4)*percent / 100; + graphics_fill_rect(ctx, rect, 0, GCornerNone); +} + +static void in_received_handler(DictionaryIterator *received, void *context) { + APP_LOG(APP_LOG_LEVEL_DEBUG, "Received config"); + Tuple *tuple = dict_find(received, MSG_START_TIME); + if (tuple) { + start_time = tuple->value->int32; + persist_write_int(MSG_START_TIME, start_time); + } + tuple = dict_find(received, MSG_END_TIME); + if (tuple) { + end_time = tuple->value->int32; + persist_write_int(MSG_END_TIME, end_time); + } + time_t now = time(NULL); + handle_tick(localtime(&now), 0); +} + +static void handle_init(void) { + struct tm tick_time; + tick_time.tm_year = 2014 - 1900; + tick_time.tm_mon = 12 - 1; + tick_time.tm_mday = 9; + tick_time.tm_hour = 12 - 1; + tick_time.tm_min = 0; + tick_time.tm_sec = 0; + start_time = mktime(&tick_time); + tick_time.tm_year = 2015 - 1900; + end_time = mktime(&tick_time); + + // Read stored settings + if (persist_exists(MSG_START_TIME)) + start_time = persist_read_int(MSG_START_TIME); + if (persist_exists(MSG_END_TIME)) + end_time = persist_read_int(MSG_END_TIME); + + // Open communication channel + app_message_register_inbox_received(in_received_handler); + app_message_open(64, 64); + + // Create a window and text layer + window = window_create(); + + // Current time layer + time_layer = text_layer_create(GRect(0, 0, 144, 50)); + // Set the text, font, and text alignment + text_layer_set_font(time_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD)); + text_layer_set_background_color(time_layer, GColorClear); + text_layer_set_text_alignment(time_layer, GTextAlignmentCenter); + // Add the text layer to the window + layer_add_child(window_get_root_layer(window), text_layer_get_layer(time_layer)); + + // Left time layer + left_time_layer = text_layer_create(GRect(0, 47, 144, 80)); + // Set the text, font, and text alignment + text_layer_set_font(left_time_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18)); + text_layer_set_background_color(left_time_layer, GColorClear); + text_layer_set_text_alignment(left_time_layer, GTextAlignmentCenter); + // Add the text layer to the window + layer_add_child(window_get_root_layer(window), text_layer_get_layer(left_time_layer)); + + // Another Left time layer + left_time_layer2 = text_layer_create(GRect(0, 67, 144, 80)); + // Set the text, font, and text alignment + text_layer_set_font(left_time_layer2, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); + text_layer_set_background_color(left_time_layer2, GColorClear); + text_layer_set_text_alignment(left_time_layer2, GTextAlignmentCenter); + // Add the text layer to the window + layer_add_child(window_get_root_layer(window), text_layer_get_layer(left_time_layer2)); + + // Percent layer + percent_layer = text_layer_create(GRect(0, 130, 144, 80)); + // Set the text, font, and text alignment + text_layer_set_font(percent_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD)); + text_layer_set_background_color(percent_layer, GColorClear); + text_layer_set_text_alignment(percent_layer, GTextAlignmentCenter); + // Add the text layer to the window + layer_add_child(window_get_root_layer(window), text_layer_get_layer(percent_layer)); + + // Create progress bar + progress_layer = layer_create(GRect(2, 150, 140, 15)); + layer_set_update_proc(progress_layer, &progress_layer_update_callback); + layer_add_child(window_get_root_layer(window), progress_layer); + + time_t now = time(NULL); + handle_tick(localtime(&now), 0); + + // Push the window + window_stack_push(window, true); + + // Subscribe to time updates + tick_timer_service_subscribe(MINUTE_UNIT, handle_tick); + + //light_enable(true); // For taking photos only! +} + +static void handle_deinit(void) { + tick_timer_service_unsubscribe(); + + // Destroy layers + text_layer_destroy(time_layer); + text_layer_destroy(left_time_layer); + text_layer_destroy(left_time_layer2); + text_layer_destroy(percent_layer); + layer_destroy(progress_layer); + + // Destroy the window + window_destroy(window); +} + +int main(void) { + handle_init(); + app_event_loop(); + handle_deinit(); +} diff --git a/src/pebble-js-app.js b/src/pebble-js-app.js new file mode 100644 index 0000000..e5d12db --- /dev/null +++ b/src/pebble-js-app.js @@ -0,0 +1,62 @@ +var initialized = false; +var options = { + "config_start_year": 2014, + "config_start_month": 12, + "config_start_day": 9, + "config_start_hour": 12, + "config_start_min": 0, + "config_end_year": 2015, + "config_end_month": 12, + "config_end_day": 9, + "config_end_hour": 12, + "config_end_min": 0 +}; + +function java_mktime(hour,minute,day,month,year) +{ + var localOffset = new Date().getTimezoneOffset() * 60; + return new Date(year, month - 1, day, hour, minute).getTime() / 1000 - localOffset; +} + +function send_config() +{ + var config = { + "start_time": java_mktime(options["config_start_hour"], options["config_start_min"], options["config_start_day"], options["config_start_month"], options["config_start_year"]), + "end_time": java_mktime(options["config_end_hour"], options["config_end_min"], options["config_end_day"], options["config_end_month"], options["config_end_year"]) + }; + Pebble.sendAppMessage(config); +} + +Pebble.addEventListener("ready", function() { + initialized = true; + var json = window.localStorage.getItem('dembel-config'); + if (typeof json === 'string') { + try { + options = JSON.parse(json); + send_config(); + console.log("Loaded stored config: " + json); + } catch(e) { + console.log("stored config json parse error: " + json + ' - ' + e); + } + } +}); + +Pebble.addEventListener("showConfiguration", function() { + console.log("showing configuration"); + var cfg = '?config=' + encodeURI(JSON.stringify(options)); + Pebble.openURL("http://clusterrr.com/pebble_configs/dembel.php" + cfg); +}); + +Pebble.addEventListener("webviewclosed", function(e) { + var response = decodeURIComponent(e.response); + if (response.charAt(0) == "{" && response.slice(-1) == "}" && response.length > 5) { + try { + options = JSON.parse(response); + send_config(); + window.localStorage.setItem('dembel-config', response); + } catch(e) { + console.log("Response config json parse error: " + response + ' - ' + e); + } + console.log("Options = " + response); + } +}); diff --git a/wscript b/wscript new file mode 100644 index 0000000..b20f58f --- /dev/null +++ b/wscript @@ -0,0 +1,62 @@ + + # +# This file is the default set of rules to compile a Pebble project. +# +# Feel free to customize this to your needs. +# + +import os.path +try: + from sh import CommandNotFound, jshint, cat, ErrorReturnCode_2 + hint = jshint +except (ImportError, CommandNotFound): + hint = None + +top = '.' +out = 'build' + +def options(ctx): + ctx.load('pebble_sdk') + +def configure(ctx): + ctx.load('pebble_sdk') + +def build(ctx): + if False and hint is not None: + try: + hint([node.abspath() for node in ctx.path.ant_glob("src/**/*.js")], _tty_out=False) # no tty because there are none in the cloudpebble sandbox. + except ErrorReturnCode_2 as e: + ctx.fatal("\nJavaScript linting failed (you can disable this in Project Settings):\n" + e.stdout) + + # Concatenate all our JS files (but not recursively), and only if any JS exists in the first place. + ctx.path.make_node('src/js/').mkdir() + js_paths = ctx.path.ant_glob(['src/*.js', 'src/**/*.js']) + if js_paths: + ctx(rule='cat ${SRC} > ${TGT}', source=js_paths, target='pebble-js-app.js') + has_js = True + else: + has_js = False + + ctx.load('pebble_sdk') + + build_worker = os.path.exists('worker_src') + binaries = [] + + for p in ctx.env.TARGET_PLATFORMS: + ctx.set_env(ctx.all_envs[p]) + ctx.set_group(ctx.env.PLATFORM_NAME) + app_elf='{}/pebble-app.elf'.format(p) + ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'), + target=app_elf) + + if build_worker: + worker_elf='{}/pebble-worker.elf'.format(p) + binaries.append({'platform': p, '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': p, 'app_elf': app_elf}) + + ctx.set_group('bundle') + ctx.pbl_bundle(binaries=binaries, js='pebble-js-app.js' if has_js else []) + \ No newline at end of file -- cgit v1.2.3