diff options
author | bahbka <bahbka@bahbka.com> | 2014-07-06 15:52:18 +0400 |
---|---|---|
committer | bahbka <bahbka@bahbka.com> | 2014-07-06 15:52:18 +0400 |
commit | 665ae812b91b695f2a3bcd131b7f458a5b048b8c (patch) | |
tree | 885703a322f987584e351052c2c9b4184a396fab | |
parent | 696117deeba342a1e22f5bbe10a7b4189909d770 (diff) |
Changelog:
Append coordinates to URL (configurable)
Digital clock font
AM/PM support
Seconds dots blinking (configurable)
Configurable vibration on bluetooth loss
Turn on light (value in JSON)
Blink content (value in JSON)
Scroll-up content after update (value in JSON)
Improved configuration page
Some minor fixes
-rw-r--r-- | appinfo.json | 108 | ||||
-rw-r--r-- | resources/configuration.html | 119 | ||||
-rw-r--r-- | resources/digit_0.png | bin | 0 -> 183 bytes | |||
-rw-r--r-- | resources/digit_0am.png | bin | 0 -> 150 bytes | |||
-rw-r--r-- | resources/digit_0pm.png | bin | 0 -> 152 bytes | |||
-rw-r--r-- | resources/digit_1.png | bin | 0 -> 145 bytes | |||
-rw-r--r-- | resources/digit_1am.png | bin | 0 -> 183 bytes | |||
-rw-r--r-- | resources/digit_1pm.png | bin | 0 -> 174 bytes | |||
-rw-r--r-- | resources/digit_2.png | bin | 0 -> 200 bytes | |||
-rw-r--r-- | resources/digit_3.png | bin | 0 -> 190 bytes | |||
-rw-r--r-- | resources/digit_4.png | bin | 0 -> 193 bytes | |||
-rw-r--r-- | resources/digit_5.png | bin | 0 -> 199 bytes | |||
-rw-r--r-- | resources/digit_6.png | bin | 0 -> 196 bytes | |||
-rw-r--r-- | resources/digit_7.png | bin | 0 -> 176 bytes | |||
-rw-r--r-- | resources/digit_8.png | bin | 0 -> 196 bytes | |||
-rw-r--r-- | resources/digit_9.png | bin | 0 -> 203 bytes | |||
-rw-r--r-- | resources/digit_dots.png | bin | 0 -> 161 bytes | |||
-rw-r--r-- | resources/digit_dots_space.png | bin | 0 -> 131 bytes | |||
-rw-r--r-- | resources/empty.png | bin | 0 -> 126 bytes | |||
-rw-r--r-- | src/js/pebble-js-app.src.js | 68 | ||||
-rw-r--r-- | src/pebble-my-data.c | 485 | ||||
-rw-r--r-- | stuff/fonts/digits_15x25.png | bin | 0 -> 503 bytes |
22 files changed, 597 insertions, 183 deletions
diff --git a/appinfo.json b/appinfo.json index 4047a91..4b97424 100644 --- a/appinfo.json +++ b/appinfo.json @@ -3,18 +3,25 @@ "shortName": "My Data", "longName": "My Data", "companyName": "bahbka", - "versionCode": 2, - "versionLabel": "1.1.0", + "versionCode": 3, + "versionLabel": "1.2.0", "watchapp": { "watchface": false }, - "capabilities": [ "configurable" ], + "capabilities": [ "configurable", "location" ], "appKeys": { - "content": 0, - "refresh": 1, - "vibrate": 2, - "font": 3, - "theme": 4 + "msg_type": 0, + "content": 1, + "refresh": 2, + "vibrate": 3, + "font": 4, + "theme": 5, + "scroll": 6, + "light": 7, + "blink": 8, + "config_location": 9, + "config_vibrate": 10, + "config_seconds": 11 }, "resources": { "media": [ @@ -48,6 +55,91 @@ "type": "png-trans", "name": "UPDATE_ERROR", "file": "update_error.png" + }, + { + "type": "png-trans", + "name": "EMPTY", + "file": "empty.png" + }, + { + "type": "png-trans", + "name": "DIGIT_0", + "file": "digit_0.png" + }, + { + "type": "png-trans", + "name": "DIGIT_1", + "file": "digit_1.png" + }, + { + "type": "png-trans", + "name": "DIGIT_2", + "file": "digit_2.png" + }, + { + "type": "png-trans", + "name": "DIGIT_3", + "file": "digit_3.png" + }, + { + "type": "png-trans", + "name": "DIGIT_4", + "file": "digit_4.png" + }, + { + "type": "png-trans", + "name": "DIGIT_5", + "file": "digit_5.png" + }, + { + "type": "png-trans", + "name": "DIGIT_6", + "file": "digit_6.png" + }, + { + "type": "png-trans", + "name": "DIGIT_7", + "file": "digit_7.png" + }, + { + "type": "png-trans", + "name": "DIGIT_8", + "file": "digit_8.png" + }, + { + "type": "png-trans", + "name": "DIGIT_9", + "file": "digit_9.png" + }, + { + "type": "png-trans", + "name": "DIGIT_DOTS", + "file": "digit_dots.png" + }, + { + "type": "png-trans", + "name": "DIGIT_DOTS_SPACE", + "file": "digit_dots_space.png" + }, + { + "type": "png-trans", + "name": "DIGIT_0AM", + "file": "digit_0am.png" + }, + { + "type": "png-trans", + "name": "DIGIT_0PM", + "file": "digit_0pm.png" + }, + { + "type": "png-trans", + "name": "DIGIT_1AM", + "file": "digit_1am.png" + }, + { + "type": "png-trans", + "name": "DIGIT_1PM", + "file": "digit_1pm.png" } ] } diff --git a/resources/configuration.html b/resources/configuration.html index b2757f3..d86da87 100644 --- a/resources/configuration.html +++ b/resources/configuration.html @@ -1,53 +1,114 @@ <!DOCTYPE html> +<!-- -*-coding: utf-8 -*- +vim: sw=2 ts=2 expandtab ai --> + <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> body { background-color: black; text-align: center; color: white } - h1,h3 { margin: 0; padding: 0 } - h3 { color: gray } - h4 { text-align: center; margin: 0; padding: 0 } - input { height: 40px; font-size: 20px } - .url { width: 95% } - .submit { width: 50% } - .example { text-align: left } + h1 { margin: 0 } + small { color: gray } + a { color: white } + input { height: 1.5em; font-size: 1.2em; font-weight: bold } + .url { width: 93%; margin: 0.5em } + .submit { width: 93%; margin: 0.4em } + .param { display: inline-table; width: 95%; height: 3em } + .label,.checkbox { display: table-cell; vertical-align: middle } + .label { text-align: left } + .checkbox { text-align: right; width: 1.5em; height: 1.5em } + .example { width: 75%; display: inline-block; text-align: left; font-size: 0.6em } </style> + <script> + + var config = _CONFIG_; + + function put_config() { + for (var param in config) { + var element = document.getElementById(param); + if (element) { + if (typeof config[param] === 'boolean') { + element.checked = config[param]; + } else { + element.value = config[param]; + } + } + } + } + + function get_config() { + var form = document.getElementById('config_form'); + for (config = {}, i = 0; i < form.length ; i++) { + id = form[i].id; + if (id != "save") { + if (form[i].type === 'checkbox') { + config[id] = form[i].checked; + } else { + config[id] = form[i].value; + } + } + } + return window.location.href = "pebblejs://close#" + encodeURIComponent(JSON.stringify(config)); + } + </script> </head> - <body> + <body onload="put_config();"> <h1>My Data</h1> - <h3>v1.1.0, by bahbka</h3> - <br><br> - <form action="javascript: settings();" id="settings_form"> - <h4>my server URL:</h4> - <input type="text" id="url" class="url" value="_URL_"> + <small>v1.2.0, by bahbka</small> + <hr size="1" /> + + <form action="javascript: get_config();" id="config_form"> + Server URL: + <input type="text" id="url" class="url" value="your URL here"> + + <div class="param"> + <div class="label"> + Append GPS coordinates to URL:<br> + <small>(can eat your phone battery)</small> + </div> + <div class="checkbox"> + <input type="checkbox" id="config_location" class="checkbox"> + </div> + </div> + + <div class="param"> + <div class="label"> + Vibrate on bluetooth connection loss: + </div> + <div class="checkbox"> + <input type="checkbox" id="config_vibrate" class="checkbox"> + </div> + </div> + + <div class="param"> + <div class="label"> + Seconds dots blinking:<br> + <small>(can eat your pebble battery)</small> + </div> + <div class="checkbox"> + <input type="checkbox" id="config_seconds" class="checkbox"> + </div> + </div> <input type="submit" id="save" class="submit" value="save and apply"> </form> - <br><br><br> - sample server output: + <hr size="1" /> + + Sample server output:<br> + <small>(see documentation on <a href="https://github.com/bahbka/pebble-my-data/blob/master/README.md">github</a>)</small> <pre class="example"> { "content": "Hello\\nWorld!", "refresh": 300, // refresh delay, seconds "vibrate": 0, // 0..3, 0 - don't vibrate "font": 1, // 1..8, try them all - "theme": 0 // 0 - black, 1 - white + "theme": 0, // 0 - black, 1 - white + "scroll": 1, // scroll up after update + "light": 1, // turn on light + "blink": 1 // blink content (count) } GET param short=1 or long=1 added to URL on short or long select button update </pre> - - <script> - function settings() { - var form = document.getElementById('settings_form'); - for (configuration = {}, i = 0; i < form.length ; i++) { - id = form[i].id; - if (id != "save") { - configuration[id] = form[i].value; - } - } - return window.location.href = "pebblejs://close#" + encodeURIComponent(JSON.stringify(configuration)); - } - </script> </body> </html> diff --git a/resources/digit_0.png b/resources/digit_0.png Binary files differnew file mode 100644 index 0000000..27c4c3d --- /dev/null +++ b/resources/digit_0.png diff --git a/resources/digit_0am.png b/resources/digit_0am.png Binary files differnew file mode 100644 index 0000000..5576638 --- /dev/null +++ b/resources/digit_0am.png diff --git a/resources/digit_0pm.png b/resources/digit_0pm.png Binary files differnew file mode 100644 index 0000000..615ccfb --- /dev/null +++ b/resources/digit_0pm.png diff --git a/resources/digit_1.png b/resources/digit_1.png Binary files differnew file mode 100644 index 0000000..3c6d225 --- /dev/null +++ b/resources/digit_1.png diff --git a/resources/digit_1am.png b/resources/digit_1am.png Binary files differnew file mode 100644 index 0000000..93da58d --- /dev/null +++ b/resources/digit_1am.png diff --git a/resources/digit_1pm.png b/resources/digit_1pm.png Binary files differnew file mode 100644 index 0000000..f4474bc --- /dev/null +++ b/resources/digit_1pm.png diff --git a/resources/digit_2.png b/resources/digit_2.png Binary files differnew file mode 100644 index 0000000..3438d20 --- /dev/null +++ b/resources/digit_2.png diff --git a/resources/digit_3.png b/resources/digit_3.png Binary files differnew file mode 100644 index 0000000..1960708 --- /dev/null +++ b/resources/digit_3.png diff --git a/resources/digit_4.png b/resources/digit_4.png Binary files differnew file mode 100644 index 0000000..5c9c1cd --- /dev/null +++ b/resources/digit_4.png diff --git a/resources/digit_5.png b/resources/digit_5.png Binary files differnew file mode 100644 index 0000000..65950ec --- /dev/null +++ b/resources/digit_5.png diff --git a/resources/digit_6.png b/resources/digit_6.png Binary files differnew file mode 100644 index 0000000..abfc9a8 --- /dev/null +++ b/resources/digit_6.png diff --git a/resources/digit_7.png b/resources/digit_7.png Binary files differnew file mode 100644 index 0000000..6294a5a --- /dev/null +++ b/resources/digit_7.png diff --git a/resources/digit_8.png b/resources/digit_8.png Binary files differnew file mode 100644 index 0000000..165243b --- /dev/null +++ b/resources/digit_8.png diff --git a/resources/digit_9.png b/resources/digit_9.png Binary files differnew file mode 100644 index 0000000..0d0f7a3 --- /dev/null +++ b/resources/digit_9.png diff --git a/resources/digit_dots.png b/resources/digit_dots.png Binary files differnew file mode 100644 index 0000000..4d0b5e5 --- /dev/null +++ b/resources/digit_dots.png diff --git a/resources/digit_dots_space.png b/resources/digit_dots_space.png Binary files differnew file mode 100644 index 0000000..7862c83 --- /dev/null +++ b/resources/digit_dots_space.png diff --git a/resources/empty.png b/resources/empty.png Binary files differnew file mode 100644 index 0000000..b54ed3b --- /dev/null +++ b/resources/empty.png diff --git a/src/js/pebble-js-app.src.js b/src/js/pebble-js-app.src.js index cc3c3ad..8a40b8c 100644 --- a/src/js/pebble-js-app.src.js +++ b/src/js/pebble-js-app.src.js @@ -1,11 +1,18 @@ // -*-coding: utf-8 -*- // vim: sw=2 ts=2 expandtab ai -//TODO geolocation +var MSG = { + PERIODIC_UPDATE: 0, + SHORT_PRESS_UPDATE: 1, + LONG_PRESS_UPDATE: 2, + JSON_RESPONSE: 3, + CONFIG: 4, + ERROR: 5 +}; var config = {}; -function fetch_data(url) { +function http_request(url) { var req = new XMLHttpRequest(); req.open('GET', url, true); @@ -16,14 +23,17 @@ function fetch_data(url) { try { var response = JSON.parse(req.responseText); //console.log("success: " + JSON.stringify(response)); + response["msg_type"] = MSG.JSON_RESPONSE; Pebble.sendAppMessage(response); } catch(e) { console.log("json parse error"); + Pebble.sendAppMessage({ "msg_type": MSG.ERROR }); } } else { console.log("fetch error"); + Pebble.sendAppMessage({ "msg_type": MSG.ERROR }); } } @@ -32,6 +42,38 @@ function fetch_data(url) { req.send(null); } +function fetch_data(url) { + if (config["config_location"]) { + if(navigator.geolocation) { + navigator.geolocation.getCurrentPosition( + function(position) { + var latitude = position.coords.latitude; + var longitude = position.coords.longitude; + + var s = (url.indexOf("?")===-1)?"?":"&"; + http_request(url + s + 'lat=' + latitude + '&lon=' + longitude); + }, + function(error) { + //error error.message + /* + TODO inform user about error + PERMISSION_DENIED (numeric value 1) + POSITION_UNAVAILABLE (numeric value 2) + TIMEOUT (numeric value 3) + */ + http_request(url); + }, + { maximumAge: 1800000 } // 30 minutes + ); + } else { + //error geolocation not supported + http_request(url); + } + } else { + http_request(url); + } +} + Pebble.addEventListener("ready", function(e) { console.log("JavaScript app ready and running!"); @@ -44,7 +86,8 @@ Pebble.addEventListener("ready", //console.log("loaded config = " + JSON.stringify(config)); } catch(e) { - console.log("json parse error"); + console.log("stored config json parse error"); + Pebble.sendAppMessage({ "msg_type": MSG.ERROR }); } } } @@ -53,21 +96,25 @@ Pebble.addEventListener("ready", Pebble.addEventListener("appmessage", function(e) { //console.log("received message: " + JSON.stringify(e.payload)); + + config["msg_type"] = MSG.CONFIG; + Pebble.sendAppMessage(config); // send current config to pebble + if (config["url"]) { var url = config["url"]; var s = (url.indexOf("?")===-1)?"?":"&"; - if (e.payload["refresh"] == 1) { + if (e.payload["refresh"] == MSG.SHORT_PRESS_UPDATE) { url = url + s + "short=1"; - } else if (e.payload["refresh"] == 2) { + } else if (e.payload["refresh"] == MSG.LONG_PRESS_UPDATE) { url = url + s + "long=1"; } fetch_data(url); } else { - Pebble.sendAppMessage({ "content": "URL not defined, chech settings in Pebble App" }); + Pebble.sendAppMessage({ "msg_type": MSG.JSON_RESPONSE, "content": "URL not defined, check settings in Pebble App" }); } } ); @@ -78,8 +125,9 @@ Pebble.addEventListener('showConfiguration', function () { } else { url = ""; } + //console.log("put options = " + JSON.stringify(config)); - Pebble.openURL('data:text/html,'+encodeURI('_HTMLMARKER_<!--.html'.replace('"_URL_"', url, 'g'))); + Pebble.openURL('data:text/html,'+encodeURI('_HTMLMARKER_<!--.html'.replace('_CONFIG_', JSON.stringify(config), 'g'))); }); Pebble.addEventListener("webviewclosed", function(e) { @@ -89,11 +137,15 @@ Pebble.addEventListener("webviewclosed", function(e) { window.localStorage.setItem('pebble-my-data', e.response); + config["msg_type"] = MSG.CONFIG; + //console.log("push config = " + JSON.stringify(config)); + Pebble.sendAppMessage(config); // send current config to pebble + if (config["url"]) { fetch_data(config["url"]); } else { - Pebble.sendAppMessage({ "content": "URL not defined, chech settings in Pebble App" }); + Pebble.sendAppMessage({ "msg_type": MSG.JSON_RESPONSE, "content": "URL not defined, check settings in Pebble App" }); } } }); diff --git a/src/pebble-my-data.c b/src/pebble-my-data.c index d3cdcf9..1f088aa 100644 --- a/src/pebble-my-data.c +++ b/src/pebble-my-data.c @@ -12,7 +12,6 @@ vim: sw=2 ts=2 expandtab ai static Window *window; static TextLayer *text_date_layer; -static TextLayer *text_time_layer; static TextLayer *text_info_layer; static ScrollLayer *scroll_layer; @@ -20,6 +19,9 @@ static ScrollLayer *scroll_layer; static Layer *line_layer; static Layer *wbatt_level_layer; +static BitmapLayer *pos_layer[5]; +static GBitmap *digits_bitmap[16]; + static BitmapLayer *wbatt_icon_layer; static GBitmap *wbatt_icon_bitmap = NULL; static GBitmap *wbatt_charge_icon_bitmap = NULL; @@ -31,27 +33,53 @@ static BitmapLayer *update_icon_layer; static GBitmap *update_icon_bitmap = NULL; static GBitmap *update_error_icon_bitmap = NULL; +static GBitmap *empty_icon_bitmap = NULL; + static AppTimer *timer = NULL; static AppTimer *timeout_timer = NULL; +static AppTimer *blink_timer = NULL; static uint8_t theme = 255; +static uint8_t blink_theme_save; +static uint8_t blink_count; + +bool config_vibrate = true; +bool config_seconds = false; + #define DEFAULT_REFRESH 300*1000 #define RETRY_DELAY 60*1000 #define REQUEST_TIMEOUT 30*1000 +#define BLINK_INTERVAL 500 +#define BLINK_MAX 20 + +#define CONTENT_SIZE 1024 + +static char content[CONTENT_SIZE]; + enum { // AppMessage keys + KEY_MSG_TYPE, KEY_CONTENT, KEY_REFRESH, KEY_VIBRATE, KEY_FONT, - KEY_THEME + KEY_THEME, + KEY_SCROLL, + KEY_LIGHT, + KEY_BLINK, + KEY_CONFIG_LOCATION, + KEY_CONFIG_VIBRATE, + KEY_CONFIG_SECONDS }; -enum { // update types - PERIODIC_UPDATE, - SHORT_PRESS_UPDATE, - LONG_PRESS_UPDATE +enum { // msg type + MSG_PERIODIC_UPDATE, + MSG_SHORT_PRESS_UPDATE, + MSG_LONG_PRESS_UPDATE, + MSG_JSON_RESPONSE, + MSG_CONFIG, + MSG_ERROR }; enum { // themes @@ -59,7 +87,7 @@ enum { // themes THEME_WHITE }; -static uint8_t update_type = PERIODIC_UPDATE; +static uint8_t update_type = MSG_PERIODIC_UPDATE; // fill layer used for drawing line and battery charge static void fill_layer(Layer *layer, GContext* ctx) { @@ -77,7 +105,6 @@ static void request_timeout(); // proto static void request_update() { // show update icon bitmap_layer_set_bitmap(update_icon_layer, update_icon_bitmap); - layer_set_hidden(bitmap_layer_get_layer(update_icon_layer), false); // prepare request DictionaryIterator *send_message; @@ -109,41 +136,74 @@ static void schedule_update(uint32_t delay, uint8_t type) { // reschedule update if data waiting timed out, also set update_error icon static void request_timeout() { bitmap_layer_set_bitmap(update_icon_layer, update_error_icon_bitmap); - schedule_update(RETRY_DELAY, PERIODIC_UPDATE); + schedule_update(RETRY_DELAY, MSG_PERIODIC_UPDATE); } -// handle minute tick to update time/date (got from Simplicity watchface without modification) -static void handle_minute_tick(struct tm *tick_time, TimeUnits units_changed) { - // Need to be static because they're used by the system later. - static char time_text[] = "00:00"; - static char date_text[] = "Xxxxxxxxx 00"; - - char *time_format; - - if (!tick_time) { - time_t now = time(NULL); - tick_time = localtime(&now); +// draw digit on position layer +static void draw_digit(BitmapLayer *position, char digit) { + if (digit >= 48 && digit <= 63) { + bitmap_layer_set_bitmap(position, digits_bitmap[digit - 48]); + } else { + bitmap_layer_set_bitmap(position, digits_bitmap[11]); // space } +} - // TODO: Only update the date when it's changed. - strftime(date_text, sizeof(date_text), "%a %b %e", tick_time); - text_layer_set_text(text_date_layer, date_text); +// handle minute tick to update time/date +static void handle_timer_tick(struct tm *tick_time, TimeUnits units_changed) { + if (units_changed == SECOND_UNIT) { + if (tick_time->tm_sec % 2 == 0) { + draw_digit(pos_layer[2], ':'); + } else { + draw_digit(pos_layer[2], ' '); + } - if (clock_is_24h_style()) { - time_format = "%R"; } else { - time_format = "%I:%M"; - } + // Need to be static because they're used by the system later. + static char time_text[] = "00:00"; + static char date_text[] = "Mon Jan 1"; + static char ampm[] = "AM"; - strftime(time_text, sizeof(time_text), time_format, tick_time); + char *time_format; - // Kludge to handle lack of non-padded hour format string - // for twelve hour clock. - if (!clock_is_24h_style() && (time_text[0] == '0')) { - memmove(time_text, &time_text[1], sizeof(time_text) - 1); - } + if (!tick_time) { + time_t now = time(NULL); + tick_time = localtime(&now); + } - text_layer_set_text(text_time_layer, time_text); + strftime(date_text, sizeof(date_text), "%a %b %e", tick_time); + text_layer_set_text(text_date_layer, date_text); + + if (clock_is_24h_style()) { + time_format = "%R"; + } else { + time_format = "%I:%M"; + } + + strftime(time_text, sizeof(time_text), time_format, tick_time); + + // Kludge to handle lack of non-padded hour format string + // for twelve hour clock. + if (!clock_is_24h_style()) { + strftime(ampm, sizeof(ampm), "%p", tick_time); + if (time_text[0] == '0') { + if (strcmp(ampm, "AM") == 0) { + time_text[0] = '0' + 12; // 0 am + } else { + time_text[0] = '0' + 13; // 0 pm + } + } else { + if (strcmp(ampm, "AM") == 0) { + time_text[0] = '0' + 14; // 1 am + } else { + time_text[0] = '0' + 15; // 1 pm + } + } + } + + for (int i = 0; i < 5; i++) { + draw_digit(pos_layer[i], time_text[i]); + } + } } // handle battery status changes @@ -162,16 +222,18 @@ static void handle_battery(BatteryChargeState charge_state) { // handle bluetooth status changes static void handle_bluetooth(bool connected) { if (connected) { - layer_set_hidden(bitmap_layer_get_layer(no_phone_icon_layer), true); - schedule_update(5 * 1000, PERIODIC_UPDATE); // schedule update when connection made + bitmap_layer_set_bitmap(no_phone_icon_layer, empty_icon_bitmap); // hide update icon (layer_set_hidden function sometimes works strange) + schedule_update(5 * 1000, MSG_PERIODIC_UPDATE); // schedule update when connection made } else { - layer_set_hidden(bitmap_layer_get_layer(no_phone_icon_layer), false); - vibes_long_pulse(); // achtung! phone lost! + bitmap_layer_set_bitmap(no_phone_icon_layer, no_phone_icon_bitmap); + if (config_vibrate) { + vibes_long_pulse(); // achtung! phone lost! + } } } // update data in main layer (content, font) -static void update_info_layer(char *content, uint8_t font) { +static void update_info_layer(char *content, uint8_t font, bool scroll_up) { // change font switch (font) { case 1: @@ -204,9 +266,13 @@ static void update_info_layer(char *content, uint8_t font) { text_layer_set_text(text_info_layer, content); // set text GSize max_size = text_layer_get_content_size(text_info_layer); + max_size.w = 144; max_size.h += 4; text_layer_set_size(text_info_layer, max_size); // resize layer scroll_layer_set_content_size(scroll_layer, GSize(144, max_size.h)); // resize scroll layer + if (scroll_up) { + scroll_layer_set_content_offset(scroll_layer, GPoint(0, 0), true); // scroll to beginning with animation + } } // change theme (black/white), do things only when theme realy changed @@ -224,29 +290,25 @@ static void change_theme(uint8_t t) { // set foreground, background colors and update window, layers window_set_background_color(window, bg); - text_layer_set_text_color(text_time_layer, fg); text_layer_set_text_color(text_date_layer, fg); + + text_layer_set_background_color(text_info_layer, bg); text_layer_set_text_color(text_info_layer, fg); layer_mark_dirty(wbatt_level_layer); layer_mark_dirty(line_layer); // destroy and recreate bitmaps - if (wbatt_icon_bitmap) { - gbitmap_destroy(wbatt_icon_bitmap); - } - if (wbatt_charge_icon_bitmap) { - gbitmap_destroy(wbatt_charge_icon_bitmap); - } - if (no_phone_icon_bitmap) { - gbitmap_destroy(no_phone_icon_bitmap); - } - if (update_icon_bitmap) { - gbitmap_destroy(update_icon_bitmap); - } - if (update_error_icon_bitmap) { - gbitmap_destroy(update_error_icon_bitmap); - } + gbitmap_destroy(wbatt_icon_bitmap); + gbitmap_destroy(wbatt_charge_icon_bitmap); + gbitmap_destroy(no_phone_icon_bitmap); + gbitmap_destroy(update_icon_bitmap); + gbitmap_destroy(update_error_icon_bitmap); + gbitmap_destroy(empty_icon_bitmap); + + + for (int i = 0; i < 16; i++) + gbitmap_destroy(digits_bitmap[i]); if (theme == THEME_BLACK) { wbatt_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_WBATT_WHITE); @@ -254,15 +316,51 @@ static void change_theme(uint8_t t) { no_phone_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_NO_PHONE_WHITE); update_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_UPDATE_WHITE); update_error_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_UPDATE_ERROR_WHITE); + empty_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_EMPTY_WHITE); + + digits_bitmap[0] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_0_WHITE); + digits_bitmap[1] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_1_WHITE); + digits_bitmap[2] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_2_WHITE); + digits_bitmap[3] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_3_WHITE); + digits_bitmap[4] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_4_WHITE); + digits_bitmap[5] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_5_WHITE); + digits_bitmap[6] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_6_WHITE); + digits_bitmap[7] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_7_WHITE); + digits_bitmap[8] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_8_WHITE); + digits_bitmap[9] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_9_WHITE); + digits_bitmap[10] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_DOTS_WHITE); + digits_bitmap[11] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_DOTS_SPACE_WHITE); + digits_bitmap[12] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_0AM_WHITE); + digits_bitmap[13] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_0PM_WHITE); + digits_bitmap[14] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_1AM_WHITE); + digits_bitmap[15] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_1PM_WHITE); } else { wbatt_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_WBATT_BLACK); wbatt_charge_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_WBATT_CHARGE_BLACK); no_phone_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_NO_PHONE_BLACK); update_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_UPDATE_BLACK); update_error_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_UPDATE_ERROR_BLACK); + empty_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_EMPTY_BLACK); + + digits_bitmap[0] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_0_BLACK); + digits_bitmap[1] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_1_BLACK); + digits_bitmap[2] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_2_BLACK); + digits_bitmap[3] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_3_BLACK); + digits_bitmap[4] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_4_BLACK); + digits_bitmap[5] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_5_BLACK); + digits_bitmap[6] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_6_BLACK); + digits_bitmap[7] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_7_BLACK); + digits_bitmap[8] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_8_BLACK); + digits_bitmap[9] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_9_BLACK); + digits_bitmap[10] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_DOTS_BLACK); + digits_bitmap[11] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_DOTS_SPACE_BLACK); + digits_bitmap[12] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_0AM_BLACK); + digits_bitmap[13] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_0PM_BLACK); + digits_bitmap[14] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_1AM_BLACK); + digits_bitmap[15] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_1PM_BLACK); } - bitmap_layer_set_bitmap(no_phone_icon_layer, no_phone_icon_bitmap); // single place to set no_phone bitmap to layer + handle_timer_tick(NULL, MINUTE_UNIT); // update time // update bitmaps layers layer_mark_dirty(bitmap_layer_get_layer(wbatt_icon_layer)); @@ -271,6 +369,34 @@ static void change_theme(uint8_t t) { } } +// change only info layer theme (for blinking) +static void change_info_theme(uint8_t theme) { + if (theme == THEME_BLACK) { + text_layer_set_background_color(text_info_layer, GColorBlack); + text_layer_set_text_color(text_info_layer, GColorWhite); + } else { + text_layer_set_background_color(text_info_layer, GColorWhite); + text_layer_set_text_color(text_info_layer, GColorBlack); + } +} + +// blink info theme +static void blink_info() { + if (blink_timer) { + app_timer_cancel(blink_timer); + } + + if (blink_count > 0) { + change_info_theme((blink_count + !blink_theme_save) % 2); + + blink_count--; + blink_timer = app_timer_register(BLINK_INTERVAL, blink_info, NULL); + + } else { + change_info_theme(blink_theme_save); + } +} + // useful in the future probably void out_sent_handler(DictionaryIterator *sent, void *context) { } @@ -278,73 +404,150 @@ void out_sent_handler(DictionaryIterator *sent, void *context) { // reschedule update if request_update sending failed, also set update_error icon void out_failed_handler(DictionaryIterator *failed, AppMessageResult reason, void *context) { bitmap_layer_set_bitmap(update_icon_layer, update_error_icon_bitmap); - schedule_update(RETRY_DELAY, PERIODIC_UPDATE); + schedule_update(RETRY_DELAY, MSG_PERIODIC_UPDATE); } // reschedule update if received data dropped, also set update_error icon void in_dropped_handler(AppMessageResult reason, void *context) { bitmap_layer_set_bitmap(update_icon_layer, update_error_icon_bitmap); - schedule_update(RETRY_DELAY, PERIODIC_UPDATE); + schedule_update(RETRY_DELAY, MSG_PERIODIC_UPDATE); } // process received data -// TODO blink whith theme and light function void in_received_handler(DictionaryIterator *received, void *context) { - layer_set_hidden(bitmap_layer_get_layer(update_icon_layer), true); // hide update icon - // update main layer with received content and font - Tuple *content = dict_find(received, KEY_CONTENT); - if (content) { - Tuple *font = dict_find(received, KEY_FONT); - if (font) { - update_info_layer(content->value->cstring, font->value->uint8); - } else { - update_info_layer(content->value->cstring, 0); - } - } - - // maybe need to vibrate? - Tuple *vibrate = dict_find(received, KEY_VIBRATE); - if (vibrate) { - switch(vibrate->value->uint8) { - case 1: - vibes_short_pulse(); - break; - case 2: - vibes_double_pulse(); - break; - case 3: - vibes_long_pulse(); - break; - default: - break; + Tuple *msg_type_tuple = dict_find(received, KEY_MSG_TYPE); + if (msg_type_tuple) { + if(msg_type_tuple->value->uint8 == MSG_CONFIG) { + Tuple *config_vibrate_tuple = dict_find(received, KEY_CONFIG_VIBRATE); + if (config_vibrate_tuple) { + if (strcmp(config_vibrate_tuple->value->cstring, "true") == 0) { + config_vibrate = true; + } else { + config_vibrate = false; + } + } + + Tuple *config_seconds_tuple = dict_find(received, KEY_CONFIG_SECONDS); + if (config_seconds_tuple) { + if (strcmp(config_seconds_tuple->value->cstring, "true") == 0) { + if (!config_seconds) { + config_seconds = true; + tick_timer_service_subscribe(SECOND_UNIT, handle_timer_tick); + } + } else { + if (config_seconds) { + config_seconds = false; + tick_timer_service_subscribe(MINUTE_UNIT, handle_timer_tick); + handle_timer_tick(NULL, MINUTE_UNIT); + } + } + } + + // TODO location icon? + + } else if (msg_type_tuple->value->uint8 == MSG_ERROR) { + bitmap_layer_set_bitmap(update_icon_layer, update_error_icon_bitmap); + schedule_update(RETRY_DELAY, MSG_PERIODIC_UPDATE); + + } else if (msg_type_tuple->value->uint8 == MSG_JSON_RESPONSE) { + bitmap_layer_set_bitmap(update_icon_layer, empty_icon_bitmap); // hide update icon (layer_set_hidden function sometimes works strange) + + Tuple *content_tuple = dict_find(received, KEY_CONTENT); + if (content_tuple) { + memcpy(content, content_tuple->value->cstring, strlen(content_tuple->value->cstring)); + + Tuple *scroll_up_tuple = dict_find(received, KEY_SCROLL); + bool scroll_up = false; + if (scroll_up_tuple && scroll_up_tuple->value->uint8 == 1) { + scroll_up = true; + } + + Tuple *font = dict_find(received, KEY_FONT); + if (font) { + update_info_layer(content, font->value->uint8, scroll_up); + } else { + update_info_layer(content, 0, scroll_up); + } + } + + // maybe need to vibrate? + Tuple *vibrate = dict_find(received, KEY_VIBRATE); + if (vibrate) { + switch(vibrate->value->uint8) { + case 1: + vibes_short_pulse(); + break; + case 2: + vibes_double_pulse(); + break; + case 3: + vibes_long_pulse(); + break; + default: + break; + } + } + + // or switch theme? + Tuple *new_theme = dict_find(received, KEY_THEME); + if (new_theme) { + change_theme(new_theme->value->uint8); + } + + // or turn on light? + Tuple *light = dict_find(received, KEY_LIGHT); + if (light && light->value->uint8 == 1) { + light_enable_interaction(); + } + + // or blink content? + Tuple *blink = dict_find(received, KEY_BLINK); + if (blink) { + if (blink->value->uint8 > 0) { + if (blink->value->uint8 > BLINK_MAX) { + blink_count = BLINK_MAX; + } else { + blink_count = blink->value->uint8 * 2; + } + + blink_theme_save = theme; + blink_info(); + } + } + + // schedule next update + uint32_t delay = DEFAULT_REFRESH; + Tuple *refresh = dict_find(received, KEY_REFRESH); + if (refresh) { + delay = refresh->value->uint32 * 1000; + } + + schedule_update(delay, MSG_PERIODIC_UPDATE); } } - - // or switch theme? - Tuple *new_theme = dict_find(received, KEY_THEME); - if (new_theme) { - change_theme(new_theme->value->uint8); - } - - // schedule next update - uint32_t delay = DEFAULT_REFRESH; - Tuple *refresh = dict_find(received, KEY_REFRESH); - if (refresh) { - delay = refresh->value->uint32 * 1000; - } - - schedule_update(delay, PERIODIC_UPDATE); } // force update on select short click static void select_click_handler(ClickRecognizerRef recognizer, void *context) { - schedule_update(0, SHORT_PRESS_UPDATE); + if (blink_count > 0) { // if blinking - stop it! + blink_count = 0; + blink_info(); + + } else { + schedule_update(0, MSG_SHORT_PRESS_UPDATE); + } } // force update on select long click static void select_long_click_handler(ClickRecognizerRef recognizer, void *context) { - schedule_update(0, LONG_PRESS_UPDATE); + if (blink_count > 0) { // if blinking - stop it! + blink_count = 0; + blink_info(); + + } else { + schedule_update(0, MSG_LONG_PRESS_UPDATE); + } } // handle select button clicks @@ -357,15 +560,25 @@ static void click_config_provider(void *context) { static void window_load(Window *window) { Layer *window_layer = window_get_root_layer(window); - // time layer - text_time_layer = text_layer_create(GRect(1, -7, 142, 30)); - text_layer_set_background_color(text_time_layer, GColorClear); - text_layer_set_font(text_time_layer, fonts_get_system_font(FONT_KEY_BITHAM_30_BLACK)); - text_layer_set_text_alignment(text_time_layer, GTextAlignmentRight); - layer_add_child(window_layer, text_layer_get_layer(text_time_layer)); + // time layers + // pos1 + pos_layer[0] = bitmap_layer_create(GRect(65, 2, 16, 25)); + layer_add_child(window_layer, bitmap_layer_get_layer(pos_layer[0])); + // pos2 + pos_layer[1] = bitmap_layer_create(GRect(82, 2, 16, 25)); + layer_add_child(window_layer, bitmap_layer_get_layer(pos_layer[1])); + // pos3 + pos_layer[2] = bitmap_layer_create(GRect(101, 2, 6, 25)); + layer_add_child(window_layer, bitmap_layer_get_layer(pos_layer[2])); + // pos4 + pos_layer[3] = bitmap_layer_create(GRect(109, 2, 16, 25)); + layer_add_child(window_layer, bitmap_layer_get_layer(pos_layer[3])); + // pos5 + pos_layer[4] = bitmap_layer_create(GRect(126, 2, 16, 25)); + layer_add_child(window_layer, bitmap_layer_get_layer(pos_layer[4])); // date layer - text_date_layer = text_layer_create(GRect(1, 9, 142, 14)); + text_date_layer = text_layer_create(GRect(0, 13, 142, 14)); text_layer_set_background_color(text_date_layer, GColorClear); text_layer_set_font(text_date_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD)); layer_add_child(window_layer, text_layer_get_layer(text_date_layer)); @@ -380,30 +593,30 @@ static void window_load(Window *window) { layer_add_child(window_layer, wbatt_level_layer); // no phone icon layer - no_phone_icon_layer = bitmap_layer_create(GRect(41, 2, 10, 10)); - layer_set_hidden(bitmap_layer_get_layer(no_phone_icon_layer), true); + no_phone_icon_layer = bitmap_layer_create(GRect(33, 2, 10, 10)); + bitmap_layer_set_bitmap(no_phone_icon_layer, empty_icon_bitmap); // hide update icon (layer_set_hidden function sometimes works strange) layer_add_child(window_layer, bitmap_layer_get_layer(no_phone_icon_layer)); // update icon layer - update_icon_layer = bitmap_layer_create(GRect(53, 2, 10, 10)); - layer_set_hidden(bitmap_layer_get_layer(update_icon_layer), true); + update_icon_layer = bitmap_layer_create(GRect(44, 2, 10, 10)); + bitmap_layer_set_bitmap(update_icon_layer, empty_icon_bitmap); // hide update icon (layer_set_hidden function sometimes works strange) layer_add_child(window_layer, bitmap_layer_get_layer(update_icon_layer)); // line layer - GRect line_frame = GRect(0, 25, 144, 2); + GRect line_frame = GRect(0, 29, 144, 2); line_layer = layer_create(line_frame); layer_set_update_proc(line_layer, fill_layer); layer_add_child(window_layer, line_layer); // main info layer - scroll_layer = scroll_layer_create(GRect(0, 26, 144, 168-26)); + scroll_layer = scroll_layer_create(GRect(0, 31, 145, 168-31)); scroll_layer_set_click_config_onto_window(scroll_layer, window); scroll_layer_set_callbacks(scroll_layer, (ScrollLayerCallbacks) { .click_config_provider = &click_config_provider }); - text_info_layer = text_layer_create(GRect(0, 0, 144, 2000)); - text_layer_set_background_color(text_info_layer, GColorClear); + text_info_layer = text_layer_create(GRect(0, 0, 145, 2000)); + //text_layer_set_background_color(text_info_layer, GColorClear); scroll_layer_add_child(scroll_layer, text_layer_get_layer(text_info_layer)); layer_add_child(window_layer, scroll_layer_get_layer(scroll_layer)); @@ -418,8 +631,8 @@ static void window_load(Window *window) { bluetooth_connection_service_subscribe(&handle_bluetooth); handle_bluetooth(bluetooth_connection_service_peek()); - tick_timer_service_subscribe(MINUTE_UNIT, handle_minute_tick); - handle_minute_tick(NULL, MINUTE_UNIT); + tick_timer_service_subscribe(MINUTE_UNIT, handle_timer_tick); + handle_timer_tick(NULL, MINUTE_UNIT); } static void window_unload(Window *window) { @@ -427,34 +640,30 @@ static void window_unload(Window *window) { battery_state_service_unsubscribe(); bluetooth_connection_service_unsubscribe(); - text_layer_destroy(text_time_layer); text_layer_destroy(text_date_layer); text_layer_destroy(text_info_layer); scroll_layer_destroy(scroll_layer); - if (wbatt_icon_bitmap) { - gbitmap_destroy(wbatt_icon_bitmap); - } - if (wbatt_charge_icon_bitmap) { - gbitmap_destroy(wbatt_charge_icon_bitmap); - } - if (no_phone_icon_bitmap) { - gbitmap_destroy(no_phone_icon_bitmap); - } - if (update_icon_bitmap) { - gbitmap_destroy(update_icon_bitmap); - } - if (update_error_icon_bitmap) { - gbitmap_destroy(update_error_icon_bitmap); - } + gbitmap_destroy(wbatt_icon_bitmap); + gbitmap_destroy(wbatt_charge_icon_bitmap); + gbitmap_destroy(no_phone_icon_bitmap); + gbitmap_destroy(update_icon_bitmap); + gbitmap_destroy(update_error_icon_bitmap); + gbitmap_destroy(empty_icon_bitmap); + + for (int i = 0; i < 16; i++) + gbitmap_destroy(digits_bitmap[i]); bitmap_layer_destroy(wbatt_icon_layer); bitmap_layer_destroy(no_phone_icon_layer); bitmap_layer_destroy(update_icon_layer); - layer_destroy(wbatt_level_layer); + for (int i = 0; i < 5; i++) + bitmap_layer_destroy(pos_layer[i]); + layer_destroy(line_layer); + layer_destroy(wbatt_level_layer); } static void init(void) { @@ -471,7 +680,7 @@ static void init(void) { app_message_register_outbox_sent(out_sent_handler); app_message_register_outbox_failed(out_failed_handler); - const uint32_t inbound_size = 1024; + const uint32_t inbound_size = CONTENT_SIZE; const uint32_t outbound_size = 64; app_message_open(inbound_size, outbound_size); diff --git a/stuff/fonts/digits_15x25.png b/stuff/fonts/digits_15x25.png Binary files differnew file mode 100644 index 0000000..89ecddc --- /dev/null +++ b/stuff/fonts/digits_15x25.png |