From 3ca84a252d004419eee5e26cf7b3bd9c42f3d1c8 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 22 Aug 2026 20:14:56 +0300 Subject: [PATCH] First --- .gitignore | 2 + README.md | 24 + design/Percentage.pxo | Bin 0 -> 32211 bytes design/PercentageGreen.pxo | Bin 0 -> 31026 bytes design/PercentageRed.pxo | Bin 0 -> 31015 bytes jshintrc | 46 ++ package.json | 137 +++++ resources/fonts/slkscr.ttf | Bin 0 -> 17068 bytes resources/images/background.png | Bin 0 -> 1090 bytes resources/images/background_minus_pressed.png | Bin 0 -> 428 bytes resources/images/background_plus_pressed.png | Bin 0 -> 427 bytes resources/images/item_background.png | Bin 0 -> 419 bytes resources/images/menu_icon.png | Bin 0 -> 257 bytes resources/images/minus_button.png | Bin 0 -> 165 bytes resources/images/plus_button.png | Bin 0 -> 174 bytes resources/images/scroll_bar_full.png | Bin 0 -> 158 bytes resources/images/scroll_bar_half.png | Bin 0 -> 146 bytes resources/images/scroll_bar_third.png | Bin 0 -> 142 bytes screenshots/pic1.png | Bin 0 -> 7027 bytes src/c/battery.c | 98 +++ src/c/battery.h | 12 + src/c/colors.h | 21 + src/c/items.c | 342 +++++++++++ src/c/items.h | 25 + src/c/percentage.c | 81 +++ src/c/scrolllayer.c | 560 ++++++++++++++++++ src/c/scrolllayer.h | 49 ++ src/c/time.c | 58 ++ src/c/time.h | 12 + src/pkjs/config.js | 312 ++++++++++ src/pkjs/index.js | 3 + wscript | 57 ++ 32 files changed, 1839 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 design/Percentage.pxo create mode 100644 design/PercentageGreen.pxo create mode 100644 design/PercentageRed.pxo create mode 100644 jshintrc create mode 100644 package.json create mode 100644 resources/fonts/slkscr.ttf create mode 100644 resources/images/background.png create mode 100644 resources/images/background_minus_pressed.png create mode 100644 resources/images/background_plus_pressed.png create mode 100644 resources/images/item_background.png create mode 100644 resources/images/menu_icon.png create mode 100644 resources/images/minus_button.png create mode 100644 resources/images/plus_button.png create mode 100644 resources/images/scroll_bar_full.png create mode 100644 resources/images/scroll_bar_half.png create mode 100644 resources/images/scroll_bar_third.png create mode 100644 screenshots/pic1.png create mode 100644 src/c/battery.c create mode 100644 src/c/battery.h create mode 100644 src/c/colors.h create mode 100644 src/c/items.c create mode 100644 src/c/items.h create mode 100644 src/c/percentage.c create mode 100644 src/c/scrolllayer.c create mode 100644 src/c/scrolllayer.h create mode 100644 src/c/time.c create mode 100644 src/c/time.h create mode 100644 src/pkjs/config.js create mode 100644 src/pkjs/index.js create mode 100644 wscript diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f078361 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +Debug/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a078e9f --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# Percentage + +This is source code for "Percentage" pebble app. It tracks success/fail rates of 9 items. It was designed in cloudpebble. + + + +## Features + +* Shows time +* Shows battery +* Side up/down buttons to scroll +* Scrollbar +* Touch buttons to add success/fail +* Phone config to add items +* Color coded success rate +* MAX 9 items = 3 pages +* MAX 10 characters +* Choose between percentage and success/total display + +## Resources + +* Pixelorama project files (Blue/Red/Green) +* C code +* Silkscreen font \ No newline at end of file diff --git a/design/Percentage.pxo b/design/Percentage.pxo new file mode 100644 index 0000000000000000000000000000000000000000..c80ca4066e37f318c0ffab07e0e3517e373bd650 GIT binary patch literal 32211 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 literal 0 HcmV?d00001 diff --git a/design/PercentageGreen.pxo b/design/PercentageGreen.pxo new file mode 100644 index 0000000000000000000000000000000000000000..7ad5aa77af7e47b93f5d9ba9d66331590a94969a GIT binary patch literal 31026 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 WxSPMtn*;K{&B!wZlxM#E-~Rwz^3ity literal 0 HcmV?d00001 diff --git a/design/PercentageRed.pxo b/design/PercentageRed.pxo new file mode 100644 index 0000000000000000000000000000000000000000..aabe6b94b52cd98479d1984fde242b1afd61b26c GIT binary patch literal 31015 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 literal 0 HcmV?d00001 diff --git a/jshintrc b/jshintrc new file mode 100644 index 0000000..4c5376d --- /dev/null +++ b/jshintrc @@ -0,0 +1,46 @@ + +/* + * Example jshint configuration file for Pebble development. + * + * Check out the full documentation at http://www.jshint.com/docs/options/ + */ +{ + // Declares the existence of the globals available in PebbleKit JS. + "globals": {"console": true, "setTimeout": true, "setInterval": true, "Int8Array": true, "Uint8Array": true, "Uint8ClampedArray": true, "Int16Array": true, "Uint16Array": true, "Int32Array": true, "Uint32Array": true, "Float32Array": true, "Float64Array": true, "Pebble": true, "WebSocket": true, "XMLHttpRequest": true, "navigator": true, "localStorage": true, "require": true, "exports": true, "module": true}, + + // Do not mess with standard JavaScript objects (Array, Date, etc) + "freeze": true, + + // Do not use eval! Keep this warning turned on (ie: false) + "evil": false, + + /* + * The options below are more style/developer dependent. + * Customize to your liking. + */ + + // All variables should be in camelcase - too specific for CloudPebble builds to fail + // "camelcase": true, + + // Do not allow blocks without { } - too specific for CloudPebble builds to fail. + // "curly": true, + + // Prohibits the use of immediate function invocations without wrapping them in parentheses + "immed": true, + + // Don't enforce indentation, because it's not worth failing builds over + // (especially given our somewhat lacklustre support for it) + "indent": false, + + // Do not use a variable before it's defined + "latedef": "nofunc", + + // Spot undefined variables + "undef": "true", + + // Spot unused variables + "unused": "true", + + // Enable ES6 syntax (const, let, arrow functions, etc) + "esversion": 6 +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..235f581 --- /dev/null +++ b/package.json @@ -0,0 +1,137 @@ +{ + "author": "aintmina@protonmail.com", + "dependencies": { + "@rebble/clay": "^1.0.10" + }, + "keywords": [], + "name": "percentage", + "pebble": { + "capabilities": [ + "configurable" + ], + "displayName": "Percentage", + "enableMultiJS": true, + "messageKeys": [ + "ITEM_1", + "ITEM_2", + "ITEM_3", + "ITEM_4", + "ITEM_5", + "ITEM_6", + "ITEM_7", + "ITEM_8", + "ITEM_9", + "ITEM_1_SUCCESS", + "ITEM_1_FAIL", + "ITEM_1_PERCENTAGE", + "ITEM_2_SUCCESS", + "ITEM_2_FAIL", + "ITEM_2_PERCENTAGE", + "ITEM_3_SUCCESS", + "ITEM_3_FAIL", + "ITEM_3_PERCENTAGE", + "ITEM_4_SUCCESS", + "ITEM_4_FAIL", + "ITEM_4_PERCENTAGE", + "ITEM_5_SUCCESS", + "ITEM_5_FAIL", + "ITEM_5_PERCENTAGE", + "ITEM_6_SUCCESS", + "ITEM_6_FAIL", + "ITEM_6_PERCENTAGE", + "ITEM_7_SUCCESS", + "ITEM_7_FAIL", + "ITEM_7_PERCENTAGE", + "ITEM_8_SUCCESS", + "ITEM_8_FAIL", + "ITEM_8_PERCENTAGE", + "ITEM_9_SUCCESS", + "ITEM_9_FAIL", + "ITEM_9_PERCENTAGE" + ], + "projectType": "native", + "resources": { + "media": [ + { + "file": "images/scroll_bar_half.png", + "name": "IMAGE_SCROLL_BAR_HALF", + "targetPlatforms": null, + "type": "bitmap" + }, + { + "file": "images/scroll_bar_full.png", + "name": "IMAGE_SCROLL_BAR_FULL", + "targetPlatforms": null, + "type": "bitmap" + }, + { + "file": "images/item_background.png", + "name": "IMAGE_ITEM_BACKGROUND", + "spaceOptimization": "storage", + "storageFormat": "png", + "targetPlatforms": null, + "type": "bitmap" + }, + { + "file": "images/background_minus_pressed.png", + "name": "IMAGE_BACKGROUND_MINUS_PRESSED", + "targetPlatforms": null, + "type": "bitmap" + }, + { + "file": "images/background_plus_pressed.png", + "name": "IMAGE_BACKGROUND_PLUS_PRESSED", + "targetPlatforms": null, + "type": "bitmap" + }, + { + "file": "images/plus_button.png", + "name": "IMAGE_PLUS_BUTTON", + "targetPlatforms": null, + "type": "bitmap" + }, + { + "file": "images/minus_button.png", + "name": "IMAGE_MINUS_BUTTON", + "targetPlatforms": null, + "type": "bitmap" + }, + { + "file": "images/background.png", + "name": "IMAGE_BACKGROUND", + "spaceOptimization": "storage", + "targetPlatforms": null, + "type": "bitmap" + }, + { + "file": "fonts/slkscr.ttf", + "name": "FONT_SLKSCR_16", + "targetPlatforms": null, + "type": "font" + }, + { + "file": "images/scroll_bar_third.png", + "name": "IMAGE_SCROLL_BAR_THIRD", + "targetPlatforms": null, + "type": "bitmap" + }, + { + "file": "images/menu_icon.png", + "menuIcon": true, + "name": "IMAGE_MENU_ICON", + "targetPlatforms": null, + "type": "bitmap" + } + ] + }, + "sdkVersion": "3", + "targetPlatforms": [ + "emery" + ], + "uuid": "7fbfd849-da80-4589-9e81-22f67339edc3", + "watchapp": { + "watchface": false + } + }, + "version": "1.0.0" +} diff --git a/resources/fonts/slkscr.ttf b/resources/fonts/slkscr.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b110719f116f3c2298b0159f193d736843294ddb GIT binary patch literal 17068 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 literal 0 HcmV?d00001 diff --git a/resources/images/background.png b/resources/images/background.png new file mode 100644 index 0000000000000000000000000000000000000000..1197d026cbaea4105b6ec9daaf3b3d803771859a GIT binary patch literal 1090 zcmeAS@N?(olHy`uVBq!ia0vp^CxG|~2OE&QYnFW(NHG=%xjQkeJ16s!fr0str;B4q z#hkZy9P@5_h&VV_P0$W%7vX;yd1H%0)sH)y7I&@)$!)UWS>S5_CvVcH()UJ2)t?T(Lw!~Ne-!NBV@zH~VJ?rv?Sq`1vV%fYVHMe_Kdb+^8IawC_{_O-G0S^7=pMRFl(yLy# z_vA*M`bUos?$rse&%6G&O3ctv;mth9Z#nTNKQ+vC|91InR+Gby%BD5b`khbynQg~) z=Y09~)tpQZpK%u5JUmmzX5ajE;leCUyCZB&%QrsgyL0r>`UiSqcN$>=Nlkl1%Palw z9eFUbXRasEYUcC_TMgnl9?tA}+X3W$ockp=@tne+S|EG3aLPy3628omyK(P3?B36~ zH$y>S-(Nj3n`y7_`+a(PwfX#apei*E_V4%iUw@VTddW-Aq$URe4wlAIVo-#^kFU$^ z8UCEH@x5%Kd-~Ywy_#0xV397)0CrCF|dBxbsUz zNbY2+Y=0~;w^Ynr_Of)=vX_76U3+P}_N(r_i{GC;j97O+EdKh>ZOp|K`)%yP#iQ?}l1UR$F+H+|Dwnd;Z~SQ&5#W(*!rMi<)vDNe&D&ZrIG)-TI1i(hU~QpO)rU%bgkz9i6Uyd5`&( zO^@~WH*e)Fmbd#-$MvIsXIFn{=w*}AI*}h{eaXK+E(zrJo-+UIJe}8xHaQ<{0zyN7 zXU2z4_s&l2G`ezV)3!s>-KS?wtFn%d0;)TnUH{?RzrfJY-BnS46CXt7i%rw$=DvMT zQAgYM&sKA8Z;^vSt{Yqgbplx^WA3b~dKh)?JhSTQu;;AN5sOOVZ_M2Mn)Az_Pfjs@M1O8{h7yR7Ct{nb`S>_3LjZ-j=(Y R#eu=k;OXk;vd$@?2>=PWwABCr literal 0 HcmV?d00001 diff --git a/resources/images/background_plus_pressed.png b/resources/images/background_plus_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..5cbfea7c4004f747d39e9ba1819622f475e43810 GIT binary patch literal 427 zcmeAS@N?(olHy`uVBq!ia0vp^Yk=5-gAGU~924#ZQjEnx?oJHr&dIz4vQs@>978JR zyuEAaci2J1^8I+sec{;fUJA3CAlhT84^f#Kg?=F*d%k z=dZkd^HSd6_dc&H>dE1YDb_fm4 zHJ`o8DmIGSd&{ivTgifW1MI6&KnkZw=vE5ssdHi|fq|~*an}817rT^y0&95v~Rdp*@ zt?ECwJR0a2uq|HS%Wr(UpIXhX6#TyY`Z2J3rte#xy>@@Vy30%VMn){!vAlcrqj`FH zUAOn_^ve7EOpQnQzO0V!nuisU(m+41Y|Z_4cOH|U@3zdu&QF}40*)^){G2-*82SvJ Lu6{1-oD!Ms<8m^u6Q7U-`FI8k=xcayG8;4J+&Kir;P+duRFmk&RF%3c4yeOvza zEgM_}bplzur#wr)QB}4!UBPIp-R>~4X&S4!cYn8yb^qI1xNPmHUdSZ}_3PU literal 0 HcmV?d00001 diff --git a/resources/images/menu_icon.png b/resources/images/menu_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..de6f3585f78b1d0d024520b147e371768c4560e6 GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%1|*NXY)uAIjKx9jP7LeL$-D$|c6+)whIn+Y zo#xGVK!L;M__FW&H{A_MF8mRxmYJmRiDl)j525aJrHsRvGHfzmJo?R=^?rdTyP&T< z(=N%GEHaXn3ReHQ7{X?UT3s#A>{}9cVA~0<4$c(~I~X`OzB>0!n}vblX6)jnk}o(I zJ`_dXoyx{gt#&mbOM5_q%Kag{bQ;R&1HM>~%#6>mZ%n3#fY?`bY z6F3w~SvJl#Tzo?J*7k#n9@YFi`aAc}`go&1WU1tD#>-9CazV??-UGeC;OXk;vd$@? F2>{^EV7&kU literal 0 HcmV?d00001 diff --git a/resources/images/minus_button.png b/resources/images/minus_button.png new file mode 100644 index 0000000000000000000000000000000000000000..b73a7b6441ab073acbb6dd1dba7d57251fd15fe8 GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VV{wqX6T`Z5GB1IgcuyC{5R2aA zl!m7NJUss;H5ht$cz}q9$7hN})769-rw=V)TBstmL8{l^T71!M7p;bu%Xe|J?{Cz3 z!PKlb4aks4^vJA|`4hJ+R^rg1ybDX6-6WK!91^NFUF(&@uuz0gQ)xo&QlM1~p00i_ I>zopr0A;;1ZvX%Q literal 0 HcmV?d00001 diff --git a/resources/images/plus_button.png b/resources/images/plus_button.png new file mode 100644 index 0000000000000000000000000000000000000000..edba71e55eca491685afb287c58797da15ace2ae GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VV{wqX6T`Z5GB1Ig3{Mxw5R2aA zl!m6z|C0ZV0~iiTNCKgR#0(8X7U_hRojN)^N&-?|vlv&0FaDJI+^J-C0>j;-JFXl& zvhv-bgZT>FyBHQmsEI6VzJGcPzw;LZMKjq$2OfO?A8Xy_0BGuw5!vxPR+W(Sv5z2KRY^83>@pdAdJu6{1-oD!M<9Lh62 literal 0 HcmV?d00001 diff --git a/resources/images/scroll_bar_half.png b/resources/images/scroll_bar_half.png new file mode 100644 index 0000000000000000000000000000000000000000..0431b40d0d071a2f0945116698276669558275a3 GIT binary patch literal 146 zcmeAS@N?(olHy`uVBq!ia0vp^Aa)Q38<5O>@2U=@7>k44ofy`glX(f`_;|WFhIn+o zy>7^NK!L|OaJIEsdyDg>g|0eoS>NAldd=H(yXx}qIb16@sjgg_+;x5VszQbH|314b s2yn14H97z(pO13uZ=^5zxc>YdnJ?Vme(Akf05q4u)78&qol`;+0E1UFp8x;= literal 0 HcmV?d00001 diff --git a/resources/images/scroll_bar_third.png b/resources/images/scroll_bar_third.png new file mode 100644 index 0000000000000000000000000000000000000000..2b6dbb904c5cafb76ccd93b0d6ee5d2def079f59 GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^Ahrny8<5=Wv@!umF%}28J29*~C-V}>@$htU4DslE zd(DuKL4kw$z%~0*${#0MtSCI_B7D3|sUT6$@UdK@N}3A8ls7Y4Ur&y-p0IKMKMe@@ kAzjpOn#fgdmLW5(EM1?oe7}gbYS1 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 Jsk|BL{{U4=wm|>@ literal 0 HcmV?d00001 diff --git a/src/c/battery.c b/src/c/battery.c new file mode 100644 index 0000000..9ff37fa --- /dev/null +++ b/src/c/battery.c @@ -0,0 +1,98 @@ +#include "battery.h" +#include "src/c/colors.h" + +static Layer *s_battery_layer; +static int s_battery_percent; + + +static void battery_draw(Layer * layer, GContext *ctx) { + GRect bounds = layer_get_bounds(layer); + + int width = (bounds.size.w - 2) * s_battery_percent / 100; + + if (width == 0) { + return; + } + + // Get color + GColor normal; + GColor light; + GColor dark; + + if (s_battery_percent > 20) { + normal = GREEN; + light = GREEN_LIGHT; + dark = GREEN_DARK; + } + else if (s_battery_percent > 10) { + normal = ORANGE; + light = ORANGE_LIGHT; + dark = ORANGE_DARK; + } + else { + normal = RED; + light = RED_LIGHT; + dark = RED_DARK; + } + + // Draw infill + graphics_context_set_fill_color(ctx, normal); + graphics_fill_rect( + ctx, + GRect(2, 2, width-2, bounds.size.h-4), + 0, + GCornerNone + ); + + // Draw normal color conrers + graphics_fill_rect( + ctx, + GRect(0, bounds.size.h-2, 2, 2), + 0, + GCornerNone + ); + graphics_fill_rect( + ctx, + GRect(width, 0, 2, 2), + 0, + GCornerNone + ); + + // Draw light edges + graphics_context_set_stroke_color(ctx, light); + graphics_draw_line(ctx, GPoint(0, 0), GPoint(width-1, 0)); + graphics_draw_line(ctx, GPoint(0, 1), GPoint(width-1, 1)); + graphics_draw_line(ctx,GPoint(0, 0), GPoint(0, bounds.size.h-3)); + graphics_draw_line(ctx,GPoint(1, 0), GPoint(1, bounds.size.h-3)); + + // Draw dark edges + graphics_context_set_stroke_color(ctx, dark); + graphics_draw_line(ctx, GPoint(2, bounds.size.h-2), GPoint(width+1, bounds.size.h-2)); + graphics_draw_line(ctx, GPoint(2, bounds.size.h-1), GPoint(width+1, bounds.size.h-1)); + graphics_draw_line(ctx,GPoint(width, 2), GPoint(width, bounds.size.h-1)); + graphics_draw_line(ctx,GPoint(width+1, 2), GPoint(width+1, bounds.size.h-1)); +} + +static void battery_changed(BatteryChargeState state) { + s_battery_percent = state.charge_percent; + layer_mark_dirty(s_battery_layer); +} + +void battery_init(Layer *parent) { + GRect frame = BATTERY_RECT; + s_battery_layer = layer_create(frame); + + layer_set_update_proc(s_battery_layer, battery_draw); + layer_add_child(parent, s_battery_layer); + + battery_state_service_subscribe(battery_changed); + battery_changed(battery_state_service_peek()); +} + +void battery_deinit() { + // Battery unsubscribe + battery_state_service_unsubscribe(); + + // Destroy battery layer + layer_destroy(s_battery_layer); +} \ No newline at end of file diff --git a/src/c/battery.h b/src/c/battery.h new file mode 100644 index 0000000..59749f0 --- /dev/null +++ b/src/c/battery.h @@ -0,0 +1,12 @@ +#pragma once + + +#include + + +// BATTERY POSITION +#define BATTERY_RECT GRect(12, 206, 176, 10) + + +void battery_init(Layer *parent); +void battery_deinit(); \ No newline at end of file diff --git a/src/c/colors.h b/src/c/colors.h new file mode 100644 index 0000000..852b60d --- /dev/null +++ b/src/c/colors.h @@ -0,0 +1,21 @@ +#pragma once + + +// BLACK/WHITE +#define BLACK GColorFromHEX(0x000000) +#define WHITE GColorFromHEX(0xffffff) + +// GREEN +#define GREEN GColorFromHEX(0x55aa00) +#define GREEN_LIGHT GColorFromHEX(0xaaff00) +#define GREEN_DARK GColorFromHEX(0x005500) + +// ORANGE +#define ORANGE GColorFromHEX(0xffaa00) +#define ORANGE_LIGHT GColorFromHEX(0xffff00) +#define ORANGE_DARK GColorFromHEX(0xaa5500) + +// RED +#define RED GColorFromHEX(0xaa0000) +#define RED_LIGHT GColorFromHEX(0xff0000) +#define RED_DARK GColorFromHEX(0x550000) \ No newline at end of file diff --git a/src/c/items.c b/src/c/items.c new file mode 100644 index 0000000..3b530e8 --- /dev/null +++ b/src/c/items.c @@ -0,0 +1,342 @@ +#include +#include "items.h" +#include "src/c/scrolllayer.h" + + +item_list_t s_items; + + +// ------------ NVM ------------ +static void items_nvm_load() { + if (persist_exists(ITEMS_NVM_KEY)) { + persist_read_data(ITEMS_NVM_KEY, s_items.items, sizeof(s_items.items)); + } + else { + memset(s_items.items, 0, sizeof(s_items.items)); + } +} + +static void items_nvm_save() { + persist_write_data(ITEMS_NVM_KEY, s_items.items, sizeof(s_items.items)); +} + +// ------------ ITEMS ------------ +static void items_organize(item_list_t *item_list) { + item_list->num_items = 0; + + for (int i = 0; i < MAX_ITEMS; i++) { + if (item_list->items[i].name[0] != 0) { + if (item_list->num_items != i) { + // Copy item up + memcpy(&item_list->items[item_list->num_items], &item_list->items[i], sizeof(item_list->items[i])); + // Delete item + memset(&item_list->items[i], 0, sizeof(item_list->items[i])); + } + item_list->num_items++; + } + } +} + +item_list_t *items_get() { + return &s_items; +} + +// ------------ PHONE ------------ +static void update_items(item_list_t *items, const item_list_t *buffer) { + if (!items || !buffer) { + return; + } + + // Check existing items + for (int i = 0; i < MAX_ITEMS; i++) { + if (items->items[i].name[0] == '\0') { + continue; + } + + bool found = false; + for (int j = 0; j < MAX_ITEMS; j++) { + if (buffer->items[j].name[0] != '\0' && strcmp(items->items[i].name, buffer->items[j].name) == 0) { + + // Item still exists + items->items[i].show_percentage = buffer->items[j].show_percentage; + found = true; + break; + } + } + + // Item no longer exists + if (!found) { + memset(&items->items[i], 0, sizeof(items->items[i])); + } + } + + // Add items + for (int i = 0; i < MAX_ITEMS; i++) { + if (buffer->items[i].name[0] == '\0') { + continue; + } + + bool found = false; + for (int j = 0; j < MAX_ITEMS; j++) { + if (items->items[j].name[0] != '\0' && strcmp(items->items[j].name, buffer->items[i].name) == 0) { + found = true; + break; + } + } + + if (!found) { + for (int j = 0; j < MAX_ITEMS; j++) { + if (items->items[j].name[0] == '\0') { + memcpy(&items->items[j], &buffer->items[i], sizeof(items->items[j])); + break; + } + } + } + } + + items_organize(items); +} + +static void inbox_received_handler(DictionaryIterator *iter, void *context) { + // Remove scroll layer items + scroll_layer_remove_items(); + + Tuple *tuple; + item_list_t item_buffer; + + // Get the item 1 + tuple = dict_find(iter, MESSAGE_KEY_ITEM_1); + if (tuple) { + strncpy(item_buffer.items[0].name, tuple->value->cstring, MAX_CHARACTERS); + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_1_SUCCESS); + if (tuple) { + int success = atoi(tuple->value->cstring); + if (success < 0) { success = 0; } + item_buffer.items[0].successes = success; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_1_FAIL); + if (tuple) { + int fail = atoi(tuple->value->cstring); + if (fail < 0) { fail = 0; } + item_buffer.items[0].fails = fail; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_1_PERCENTAGE); + if (tuple) { + bool show_percentage = tuple->value->int32 != 0; + item_buffer.items[0].show_percentage = show_percentage; + } + + // Get the item 2 + tuple = dict_find(iter, MESSAGE_KEY_ITEM_2); + if (tuple) { + strncpy(item_buffer.items[1].name, tuple->value->cstring, MAX_CHARACTERS); + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_2_SUCCESS); + if (tuple) { + int success = atoi(tuple->value->cstring); + if (success < 0) { success = 0; } + item_buffer.items[1].successes = success; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_2_FAIL); + if (tuple) { + int fail = atoi(tuple->value->cstring); + if (fail < 0) { fail = 0; } + item_buffer.items[1].fails = fail; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_2_PERCENTAGE); + if (tuple) { + bool show_percentage = tuple->value->int32 != 0; + item_buffer.items[1].show_percentage = show_percentage; + } + + // Get the item 3 + tuple = dict_find(iter, MESSAGE_KEY_ITEM_3); + if (tuple) { + strncpy(item_buffer.items[2].name, tuple->value->cstring, MAX_CHARACTERS); + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_3_SUCCESS); + if (tuple) { + int success = atoi(tuple->value->cstring); + if (success < 0) { success = 0; } + item_buffer.items[2].successes = success; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_3_FAIL); + if (tuple) { + int fail = atoi(tuple->value->cstring); + if (fail < 0) { fail = 0; } + item_buffer.items[2].fails = fail; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_3_PERCENTAGE); + if (tuple) { + bool show_percentage = tuple->value->int32 != 0; + item_buffer.items[2].show_percentage = show_percentage; + } + + // Get the item 4 + tuple = dict_find(iter, MESSAGE_KEY_ITEM_4); + if (tuple) { + strncpy(item_buffer.items[3].name, tuple->value->cstring, MAX_CHARACTERS); + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_4_SUCCESS); + if (tuple) { + int success = atoi(tuple->value->cstring); + if (success < 0) { success = 0; } + item_buffer.items[3].successes = success; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_4_FAIL); + if (tuple) { + int fail = atoi(tuple->value->cstring); + if (fail < 0) { fail = 0; } + item_buffer.items[3].fails = fail; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_4_PERCENTAGE); + if (tuple) { + bool show_percentage = tuple->value->int32 != 0; + item_buffer.items[3].show_percentage = show_percentage; + } + + // Get the item 5 + tuple = dict_find(iter, MESSAGE_KEY_ITEM_5); + if (tuple) { + strncpy(item_buffer.items[4].name, tuple->value->cstring, MAX_CHARACTERS); + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_5_SUCCESS); + if (tuple) { + int success = atoi(tuple->value->cstring); + if (success < 0) { success = 0; } + item_buffer.items[4].successes = success; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_5_FAIL); + if (tuple) { + int fail = atoi(tuple->value->cstring); + if (fail < 0) { fail = 0; } + item_buffer.items[4].fails = fail; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_5_PERCENTAGE); + if (tuple) { + bool show_percentage = tuple->value->int32 != 0; + item_buffer.items[4].show_percentage = show_percentage; + } + + // Get the item 6 + tuple = dict_find(iter, MESSAGE_KEY_ITEM_6); + if (tuple) { + strncpy(item_buffer.items[5].name, tuple->value->cstring, MAX_CHARACTERS); + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_6_SUCCESS); + if (tuple) { + int success = atoi(tuple->value->cstring); + if (success < 0) { success = 0; } + item_buffer.items[5].successes = success; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_6_FAIL); + if (tuple) { + int fail = atoi(tuple->value->cstring); + if (fail < 0) { fail = 0; } + item_buffer.items[5].fails = fail; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_6_PERCENTAGE); + if (tuple) { + bool show_percentage = tuple->value->int32 != 0; + item_buffer.items[5].show_percentage = show_percentage; + } + + // Get the item 7 + tuple = dict_find(iter, MESSAGE_KEY_ITEM_7); + if (tuple) { + strncpy(item_buffer.items[6].name, tuple->value->cstring, MAX_CHARACTERS); + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_7_SUCCESS); + if (tuple) { + int success = atoi(tuple->value->cstring); + if (success < 0) { success = 0; } + item_buffer.items[6].successes = success; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_7_FAIL); + if (tuple) { + int fail = atoi(tuple->value->cstring); + if (fail < 0) { fail = 0; } + item_buffer.items[6].fails = fail; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_7_PERCENTAGE); + if (tuple) { + bool show_percentage = tuple->value->int32 != 0; + item_buffer.items[6].show_percentage = show_percentage; + } + + // Get the item 8 + tuple = dict_find(iter, MESSAGE_KEY_ITEM_8); + if (tuple) { + strncpy(item_buffer.items[7].name, tuple->value->cstring, MAX_CHARACTERS); + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_8_SUCCESS); + if (tuple) { + int success = atoi(tuple->value->cstring); + if (success < 0) { success = 0; } + item_buffer.items[7].successes = success; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_8_FAIL); + if (tuple) { + int fail = atoi(tuple->value->cstring); + if (fail < 0) { fail = 0; } + item_buffer.items[7].fails = fail; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_8_PERCENTAGE); + if (tuple) { + bool show_percentage = tuple->value->int32 != 0; + item_buffer.items[7].show_percentage = show_percentage; + } + + // Get the item 9 + tuple = dict_find(iter, MESSAGE_KEY_ITEM_9); + if (tuple) { + strncpy(item_buffer.items[8].name, tuple->value->cstring, MAX_CHARACTERS); + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_9_SUCCESS); + if (tuple) { + int success = atoi(tuple->value->cstring); + if (success < 0) { success = 0; } + item_buffer.items[8].successes = success; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_9_FAIL); + if (tuple) { + int fail = atoi(tuple->value->cstring); + if (fail < 0) { fail = 0; } + item_buffer.items[8].fails = fail; + } + tuple = dict_find(iter, MESSAGE_KEY_ITEM_9_PERCENTAGE); + if (tuple) { + bool show_percentage = tuple->value->int32 != 0; + item_buffer.items[8].show_percentage = show_percentage; + } + + // Organize buffer + items_organize(&item_buffer); + + // Update list from buffer + update_items(&s_items, &item_buffer); + + // Save the items to NVM + items_organize(&s_items); + items_nvm_save(); + + // Update the ScrollLayer + scroll_layer_add_items(); + scroll_layer_update_scrollbar(); +} + +// ------------ INITS ------------ +void items_init() { + // Read items from NVM + items_nvm_load(); + items_organize(&s_items); + + // Register received handler + app_message_register_inbox_received(inbox_received_handler); + app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum()); +} + +void items_deinit() { + items_nvm_save(); +} \ No newline at end of file diff --git a/src/c/items.h b/src/c/items.h new file mode 100644 index 0000000..88e799e --- /dev/null +++ b/src/c/items.h @@ -0,0 +1,25 @@ +#pragma once + + +// NVM +#define ITEMS_NVM_KEY 1 +#define MAX_ITEMS 9 +#define MAX_CHARACTERS 10 + + +typedef struct { + char name[MAX_CHARACTERS+1]; + int successes; + int fails; + bool show_percentage; +} item_t; + +typedef struct { + int num_items; + item_t items[MAX_ITEMS]; +} item_list_t; + + +item_list_t *items_get(); +void items_init(); +void items_deinit(); \ No newline at end of file diff --git a/src/c/percentage.c b/src/c/percentage.c new file mode 100644 index 0000000..7e33d59 --- /dev/null +++ b/src/c/percentage.c @@ -0,0 +1,81 @@ +#include + +#include "src/c/battery.h" +#include "src/c/time.h" +#include "src/c/scrolllayer.h" + + +static Window *s_main_window; +static Layer *s_background_layer; +static GBitmap *s_background_bitmap; + + +static void update_background(Layer *layer, GContext *ctx) { + GRect bounds = gbitmap_get_bounds(s_background_bitmap); + graphics_draw_bitmap_in_rect( + ctx, + s_background_bitmap, + GRect(0, 0, bounds.size.w, bounds.size.h) + ); +} + +static void main_window_load(Window *window) { + Layer *window_layer = window_get_root_layer(window); + GRect bounds = layer_get_bounds(window_layer); + + // Create background layer + s_background_layer = layer_create(bounds); + layer_set_update_proc(s_background_layer, update_background); + layer_add_child(window_layer, s_background_layer); + + // Load background bitmap + s_background_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_BACKGROUND); + + // Init items + items_init(); + + // Init and draw the battery + battery_init(window_layer); + + // Init and draw the time + time_init(window_layer); + + // Init and draw the ScrollLayer + scrolllayer_init(window_layer, window); +} + +static void main_window_unload(Window *window) { + // Deinit the items + items_deinit(); + + // Deinit the battery + battery_deinit(); + + // Deinit the time + time_deinit(); + + // Deinit the ScrollLayer + scrolllayer_deinit(); +} + +static void init() { + // MAIN WINDOW CREATE + s_main_window = window_create(); + window_set_background_color(s_main_window, GColorBlack); + window_set_window_handlers(s_main_window, (WindowHandlers) { + .load = main_window_load, + .unload = main_window_unload + }); + window_stack_push(s_main_window, true); +} + +static void deinit() { + // MAIN WINDOW DESTROY + window_destroy(s_main_window); +} + +int main() { + init(); + app_event_loop(); + deinit(); +} \ No newline at end of file diff --git a/src/c/scrolllayer.c b/src/c/scrolllayer.c new file mode 100644 index 0000000..d85ab5a --- /dev/null +++ b/src/c/scrolllayer.c @@ -0,0 +1,560 @@ +#include +#include "scrolllayer.h" + +#include "src/c/colors.h" + +static ScrollLayer *s_scroll_layer; +static Layer *s_scroll_bar_layer; +static GFont s_scrolllayer_font; +static GBitmap *s_background_bitmap; +static GBitmap *s_plus_bitmap; +static GBitmap *s_minus_bitmap; +static GBitmap *s_background_plus_pressed_bitmap; +static GBitmap *s_background_minus_pressed_bitmap; +static GBitmap *s_scroll_bar_current_bitmap; +static GBitmap *s_scroll_bar_full_bitmap; +static GBitmap *s_scroll_bar_half_bitmap; +static GBitmap *s_scroll_bar_third_bitmap; + +// TOUCH BUTTONS +static GRect item_1_plus_button; +static GRect item_1_minus_button; +static GRect item_2_plus_button; +static GRect item_2_minus_button; +static GRect item_3_plus_button; +static GRect item_3_minus_button; + +// SCROLL +static int s_scroll_y; +static int s_scroll_page; +static scroll_items_t s_scroll_items; + + +// ------------ SCROLL ------------ +static void scroll_up(ClickRecognizerRef recognizer, void *context) { + s_scroll_y -= MAX_UI_ITEMS * ITEM_UI_HEIGHT; + s_scroll_page -= 1; + + if (s_scroll_y < 0) { + s_scroll_y = 0; + } + + if (s_scroll_page < 0) { + s_scroll_page = 0; + } + + // Move scroll bar + GRect scroll_frame = layer_get_frame(s_scroll_bar_layer); + int scroll_frame_y = scroll_frame.origin.y - scroll_frame.size.h; + if (scroll_frame_y < SCROLL_BAR_ORIGIN_HEIGHT) { + scroll_frame_y = SCROLL_BAR_ORIGIN_HEIGHT; + } + scroll_frame.origin.y = scroll_frame_y; + layer_set_frame(s_scroll_bar_layer, scroll_frame); + layer_mark_dirty(s_scroll_bar_layer); + + scroll_layer_set_content_offset( + s_scroll_layer, + GPoint(0, -s_scroll_y), + true + ); +} + +static void scroll_down(ClickRecognizerRef recognizer, void *context) { + s_scroll_y += MAX_UI_ITEMS * ITEM_UI_HEIGHT; + s_scroll_page += 1; + int max_scroll = MAX_UI_ITEMS * ITEM_UI_HEIGHT * (s_scroll_items.page_count - 1); + + if (s_scroll_y > max_scroll) { + s_scroll_y = max_scroll; + } + + if (s_scroll_page > ((MAX_ITEMS / MAX_UI_ITEMS) - 1)) { + s_scroll_page = ((MAX_ITEMS / MAX_UI_ITEMS) - 1); + } + + // Move scroll bar + GRect scroll_frame = layer_get_frame(s_scroll_bar_layer); + int scroll_frame_y = scroll_frame.origin.y + scroll_frame.size.h; + if (scroll_frame_y > (SCROLL_BAR_ORIGIN_HEIGHT_END - scroll_frame.size.h)) { + scroll_frame_y = (SCROLL_BAR_ORIGIN_HEIGHT_END - scroll_frame.size.h); + } + scroll_frame.origin.y = scroll_frame_y; + layer_set_frame(s_scroll_bar_layer, scroll_frame); + layer_mark_dirty(s_scroll_bar_layer); + + scroll_layer_set_content_offset( + s_scroll_layer, + GPoint(0, -s_scroll_y), + true + ); +} + +// ------------ BUTTONS ------------ +static void click_config_provider(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, scroll_up); + window_single_click_subscribe(BUTTON_ID_DOWN, scroll_down); +} + +// ------------ HELPERS ------------ +static GColor get_color_float(float val) { + GColor color; + + if (val < RED_LIMIT) { + color = RED; + } + else if (val < ORANGE_LIMIT) { + color = ORANGE; + } + else { + color = GREEN; + } + + return color; +} + +static void bar_draw(Layer *layer, GContext *ctx) { + GColor *color = layer_get_data(layer); + + graphics_context_set_fill_color(ctx, *color); + graphics_fill_rect(ctx, layer_get_bounds(layer), 0, GCornerNone); +} + +static void scroll_bar_draw(Layer *layer, GContext *ctx) { + GRect bounds = gbitmap_get_bounds(s_scroll_bar_current_bitmap); + graphics_draw_bitmap_in_rect(ctx, s_scroll_bar_current_bitmap, GRect(0, 0, bounds.size.w, bounds.size.h)); +} + +static void fill_percentage_buffer(scroll_items_t *items, int element, int total, float percentage) { + if (total > 1000) { items->items->items[element].show_percentage = true; } + + if (items->items->items[element].show_percentage) { + int percentage_int = (percentage * 100); + snprintf( + items->numbers_buffer[element], + sizeof(items->numbers_buffer[element]), + "%d%%", + percentage_int + ); + } + else { + snprintf( + items->numbers_buffer[element], + sizeof(items->numbers_buffer[element]), + "%d/%d", + items->items->items[element].successes, + total + ); + } +} + +void scroll_layer_add_items() { + GRect bounds = SCROLLLAYER_FRAME_RECT; + int n_items = s_scroll_items.items->num_items-1; + if (n_items < 0) { n_items = 0; } + s_scroll_items.page_count = (n_items / MAX_UI_ITEMS) + 1; + + // Add entries + for (int i = 0; i < s_scroll_items.items->num_items; i++) { + // Add background + s_scroll_items.backgrounds[i] = bitmap_layer_create(GRect(0, i * ITEM_UI_HEIGHT, bounds.size.w, ITEM_UI_HEIGHT)); + bitmap_layer_set_bitmap(s_scroll_items.backgrounds[i], s_background_bitmap); + bitmap_layer_set_compositing_mode(s_scroll_items.backgrounds[i], GCompOpSet); // Set transparency + scroll_layer_add_child(s_scroll_layer, bitmap_layer_get_layer(s_scroll_items.backgrounds[i])); + + // Add text + s_scroll_items.texts[i] = text_layer_create(GRect(26, (i * ITEM_UI_HEIGHT) + 4, 124, 16)); + text_layer_set_text(s_scroll_items.texts[i], s_scroll_items.items->items[i].name); + text_layer_set_font(s_scroll_items.texts[i], s_scrolllayer_font); + text_layer_set_text_alignment(s_scroll_items.texts[i], GTextAlignmentCenter); + text_layer_set_background_color(s_scroll_items.texts[i], GColorClear); + text_layer_set_text_color(s_scroll_items.texts[i], WHITE); + scroll_layer_add_child(s_scroll_layer, text_layer_get_layer(s_scroll_items.texts[i])); + + // Add percentage + int total = s_scroll_items.items->items[i].successes + s_scroll_items.items->items[i].fails; + float percentage = (float)s_scroll_items.items->items[i].successes / total; + GColor color = get_color_float(percentage); + s_scroll_items.numbers[i] = text_layer_create(GRect(26, (i * ITEM_UI_HEIGHT) + 18, 124, 16)); + fill_percentage_buffer(&s_scroll_items, i, total, percentage); + text_layer_set_text(s_scroll_items.numbers[i], s_scroll_items.numbers_buffer[i]); + text_layer_set_font(s_scroll_items.numbers[i], s_scrolllayer_font); + text_layer_set_text_alignment(s_scroll_items.numbers[i], GTextAlignmentCenter); + text_layer_set_background_color(s_scroll_items.numbers[i], GColorClear); + text_layer_set_text_color(s_scroll_items.numbers[i], color); + scroll_layer_add_child(s_scroll_layer, text_layer_get_layer(s_scroll_items.numbers[i])); + + // Add plus button + s_scroll_items.plus_buttons[i] = bitmap_layer_create(GRect(146, (i * ITEM_UI_HEIGHT) + 18, 20, 20)); + bitmap_layer_set_bitmap(s_scroll_items.plus_buttons[i], s_plus_bitmap); + bitmap_layer_set_compositing_mode(s_scroll_items.plus_buttons[i], GCompOpSet); // Set transparency + scroll_layer_add_child(s_scroll_layer, bitmap_layer_get_layer(s_scroll_items.plus_buttons[i])); + + // Add minus button + s_scroll_items.minus_buttons[i] = bitmap_layer_create(GRect(7, (i * ITEM_UI_HEIGHT) + 18, 20, 20)); + bitmap_layer_set_bitmap(s_scroll_items.minus_buttons[i], s_minus_bitmap); + bitmap_layer_set_compositing_mode(s_scroll_items.minus_buttons[i], GCompOpSet); // Set transparency + scroll_layer_add_child(s_scroll_layer, bitmap_layer_get_layer(s_scroll_items.minus_buttons[i])); + + // Add bar + int width = (PERCENTAGE_BAR_WIDTH) * percentage; + GRect bar = GRect(31, (i * ITEM_UI_HEIGHT) + 40, width, 4); + s_scroll_items.success_bars[i] = layer_create_with_data(bar, sizeof(GColor)); + GColor *color_ptr = layer_get_data(s_scroll_items.success_bars[i]); + *color_ptr = color; + layer_set_update_proc(s_scroll_items.success_bars[i], bar_draw); + scroll_layer_add_child(s_scroll_layer, s_scroll_items.success_bars[i]); + } +} + +void scroll_layer_remove_items() { + // Destroy items + for (int i = 0; i < MAX_ITEMS; i++) { + // TextLayers + text_layer_destroy(s_scroll_items.texts[i]); + text_layer_destroy(s_scroll_items.numbers[i]); + + // BitmapLayers + bitmap_layer_destroy(s_scroll_items.backgrounds[i]); + bitmap_layer_destroy(s_scroll_items.plus_buttons[i]); + bitmap_layer_destroy(s_scroll_items.minus_buttons[i]); + + // Layers + layer_destroy(s_scroll_items.success_bars[i]); + + // Destroy the pointers + s_scroll_items.texts[i] = NULL; + s_scroll_items.numbers[i] = NULL; + s_scroll_items.backgrounds[i] = NULL; + s_scroll_items.plus_buttons[i] = NULL; + s_scroll_items.minus_buttons[i] = NULL; + s_scroll_items.success_bars[i] = NULL; + } +} + +void scroll_layer_update_scrollbar() { + GRect scroll_box = layer_get_bounds(s_scroll_bar_layer); + GRect scroll_frame = layer_get_frame(s_scroll_bar_layer); + + if (s_scroll_items.page_count == 2) { + scroll_box.size.h = 82; + scroll_frame.size.h = 82; + s_scroll_bar_current_bitmap = s_scroll_bar_half_bitmap; + } + else if (s_scroll_items.page_count == 3) { + scroll_box.size.h = 52; + scroll_frame.size.h = 52; + s_scroll_bar_current_bitmap = s_scroll_bar_third_bitmap; + } + else { + scroll_box.size.h = 160; + scroll_frame.size.h = 160; + s_scroll_bar_current_bitmap = s_scroll_bar_full_bitmap; + } + + layer_set_bounds(s_scroll_bar_layer, scroll_box); + layer_set_frame(s_scroll_bar_layer, scroll_frame); + layer_mark_dirty(s_scroll_bar_layer); +} + +// ------------ TOUCH ------------ +static void touch_handler(const TouchEvent *event, void *context) { + // Get touch point + GPoint point = GPoint(event->x, event->y); + + // First item buttons + if (grect_contains_point(&item_1_plus_button, &point)) { + int element = 0 + (s_scroll_page * MAX_UI_ITEMS); + + if (element >= s_scroll_items.items->num_items) { + return; + } + + if (event->type == TouchEvent_Touchdown) { + // Change background to pressed + bitmap_layer_set_bitmap(s_scroll_items.backgrounds[element], s_background_plus_pressed_bitmap); + + // Update numbers + s_scroll_items.items->items[element].successes++; + int total = s_scroll_items.items->items[element].successes + s_scroll_items.items->items[element].fails; + float percentage = (float)s_scroll_items.items->items[element].successes / total; + GColor color = get_color_float(percentage); + fill_percentage_buffer(&s_scroll_items, element, total, percentage); + text_layer_set_text_color(s_scroll_items.numbers[element], color); + text_layer_set_text(s_scroll_items.numbers[element], s_scroll_items.numbers_buffer[element]); + + // Update the percentage bar + int width = (PERCENTAGE_BAR_WIDTH) * percentage; + GRect bar = GRect(31, (element * ITEM_UI_HEIGHT) + 40, width, 4); + GColor *color_ptr = layer_get_data(s_scroll_items.success_bars[element]); + *color_ptr = color; + layer_set_frame(s_scroll_items.success_bars[element], bar); + } + else if (event->type == TouchEvent_Liftoff) { + // Reset the background + bitmap_layer_set_bitmap(s_scroll_items.backgrounds[element], s_background_bitmap); + } + } + + if (grect_contains_point(&item_1_minus_button, &point)) { + int element = 0 + (s_scroll_page * MAX_UI_ITEMS); + + if (element >= s_scroll_items.items->num_items) { + return; + } + + if (event->type == TouchEvent_Touchdown) { + // Change background to pressed + bitmap_layer_set_bitmap(s_scroll_items.backgrounds[element], s_background_minus_pressed_bitmap); + + s_scroll_items.items->items[element].fails++; + int total = s_scroll_items.items->items[element].successes + s_scroll_items.items->items[element].fails; + float percentage = (float)s_scroll_items.items->items[element].successes / total; + GColor color = get_color_float(percentage); + fill_percentage_buffer(&s_scroll_items, element, total, percentage); + text_layer_set_text_color(s_scroll_items.numbers[element], color); + text_layer_set_text(s_scroll_items.numbers[element], s_scroll_items.numbers_buffer[element]); + + // Update the percentage bar + int width = (PERCENTAGE_BAR_WIDTH) * percentage; + GRect bar = GRect(31, (element * ITEM_UI_HEIGHT) + 40, width, 4); + GColor *color_ptr = layer_get_data(s_scroll_items.success_bars[element]); + *color_ptr = color; + layer_set_frame(s_scroll_items.success_bars[element], bar); + } + else if (event->type == TouchEvent_Liftoff) { + // Reset the background + bitmap_layer_set_bitmap(s_scroll_items.backgrounds[element], s_background_bitmap); + } + } + + // Second item buttons + if (grect_contains_point(&item_2_plus_button, &point)) { + int element = 1 + (s_scroll_page * MAX_UI_ITEMS); + + if (element >= s_scroll_items.items->num_items) { + return; + } + + if (event->type == TouchEvent_Touchdown) { + // Change background to pressed + bitmap_layer_set_bitmap(s_scroll_items.backgrounds[element], s_background_plus_pressed_bitmap); + + s_scroll_items.items->items[element].successes++; + int total = s_scroll_items.items->items[element].successes + s_scroll_items.items->items[element].fails; + float percentage = (float)s_scroll_items.items->items[element].successes / total; + GColor color = get_color_float(percentage); + fill_percentage_buffer(&s_scroll_items, element, total, percentage); + text_layer_set_text_color(s_scroll_items.numbers[element], color); + text_layer_set_text(s_scroll_items.numbers[element], s_scroll_items.numbers_buffer[element]); + + // Update the percentage bar + int width = (PERCENTAGE_BAR_WIDTH) * percentage; + GRect bar = GRect(31, (element * ITEM_UI_HEIGHT) + 40, width, 4); + GColor *color_ptr = layer_get_data(s_scroll_items.success_bars[element]); + *color_ptr = color; + layer_set_frame(s_scroll_items.success_bars[element], bar); + } + else if (event->type == TouchEvent_Liftoff) { + // Reset the background + bitmap_layer_set_bitmap(s_scroll_items.backgrounds[element], s_background_bitmap); + } + } + + if (grect_contains_point(&item_2_minus_button, &point)) { + int element = 1 + (s_scroll_page * MAX_UI_ITEMS); + + if (element >= s_scroll_items.items->num_items) { + return; + } + + if (event->type == TouchEvent_Touchdown) { + // Change background to pressed + bitmap_layer_set_bitmap(s_scroll_items.backgrounds[element], s_background_minus_pressed_bitmap); + + s_scroll_items.items->items[element].fails++; + int total = s_scroll_items.items->items[element].successes + s_scroll_items.items->items[element].fails; + float percentage = (float)s_scroll_items.items->items[element].successes / total; + GColor color = get_color_float(percentage); + fill_percentage_buffer(&s_scroll_items, element, total, percentage); + text_layer_set_text_color(s_scroll_items.numbers[element], color); + text_layer_set_text(s_scroll_items.numbers[element], s_scroll_items.numbers_buffer[element]); + + // Update the percentage bar + int width = (PERCENTAGE_BAR_WIDTH) * percentage; + GRect bar = GRect(31, (element * ITEM_UI_HEIGHT) + 40, width, 4); + GColor *color_ptr = layer_get_data(s_scroll_items.success_bars[element]); + *color_ptr = color; + layer_set_frame(s_scroll_items.success_bars[element], bar); + } + else if (event->type == TouchEvent_Liftoff) { + // Reset the background + bitmap_layer_set_bitmap(s_scroll_items.backgrounds[element], s_background_bitmap); + } + } + + // Thisrd item buttons + if (grect_contains_point(&item_3_plus_button, &point)) { + int element = 2 + (s_scroll_page * MAX_UI_ITEMS); + + if (element >= s_scroll_items.items->num_items) { + return; + } + + if (event->type == TouchEvent_Touchdown) { + // Change background to pressed + bitmap_layer_set_bitmap(s_scroll_items.backgrounds[element], s_background_plus_pressed_bitmap); + + s_scroll_items.items->items[element].successes++; + int total = s_scroll_items.items->items[element].successes + s_scroll_items.items->items[element].fails; + float percentage = (float)s_scroll_items.items->items[element].successes / total; + GColor color = get_color_float(percentage); + fill_percentage_buffer(&s_scroll_items, element, total, percentage); + text_layer_set_text_color(s_scroll_items.numbers[element], color); + text_layer_set_text(s_scroll_items.numbers[element], s_scroll_items.numbers_buffer[element]); + + // Update the percentage bar + int width = (PERCENTAGE_BAR_WIDTH) * percentage; + GRect bar = GRect(31, (element * ITEM_UI_HEIGHT) + 40, width, 4); + GColor *color_ptr = layer_get_data(s_scroll_items.success_bars[element]); + *color_ptr = color; + layer_set_frame(s_scroll_items.success_bars[element], bar); + } + else if (event->type == TouchEvent_Liftoff) { + // Reset the background + bitmap_layer_set_bitmap(s_scroll_items.backgrounds[element], s_background_bitmap); + } + } + + if (grect_contains_point(&item_3_minus_button, &point)) { + int element = 2 + (s_scroll_page * MAX_UI_ITEMS); + + if (element >= s_scroll_items.items->num_items) { + return; + } + + if (event->type == TouchEvent_Touchdown) { + // Change background to pressed + bitmap_layer_set_bitmap(s_scroll_items.backgrounds[element], s_background_minus_pressed_bitmap); + + s_scroll_items.items->items[element].fails++; + int total = s_scroll_items.items->items[element].successes + s_scroll_items.items->items[element].fails; + float percentage = (float)s_scroll_items.items->items[element].successes / total; + GColor color = get_color_float(percentage); + fill_percentage_buffer(&s_scroll_items, element, total, percentage); + text_layer_set_text_color(s_scroll_items.numbers[element], color); + text_layer_set_text(s_scroll_items.numbers[element], s_scroll_items.numbers_buffer[element]); + + // Update the percentage bar + int width = (PERCENTAGE_BAR_WIDTH) * percentage; + GRect bar = GRect(31, (element * ITEM_UI_HEIGHT) + 40, width, 4); + GColor *color_ptr = layer_get_data(s_scroll_items.success_bars[element]); + *color_ptr = color; + layer_set_frame(s_scroll_items.success_bars[element], bar); + } + else if (event->type == TouchEvent_Liftoff) { + // Reset the background + bitmap_layer_set_bitmap(s_scroll_items.backgrounds[element], s_background_bitmap); + } + } +} + +// ------------ INITS ------------ +void scrolllayer_init(Layer *parent, Window *window) { + s_scroll_y = 0; + s_scroll_page = 0; + GRect bounds = SCROLLLAYER_FRAME_RECT; + + // Load custom font + s_scrolllayer_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_SLKSCR_16)); + + // Add touch areas + item_1_plus_button = ITEM_1_PLUS_BUTTON_RECT; + item_1_minus_button = ITEM_1_MINUS_BUTTON_RECT; + item_2_plus_button = ITEM_2_PLUS_BUTTON_RECT; + item_2_minus_button = ITEM_2_MINUS_BUTTON_RECT; + item_3_plus_button = ITEM_3_PLUS_BUTTON_RECT; + item_3_minus_button = ITEM_3_MINUS_BUTTON_RECT; + + // Load bitmaps + s_background_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_ITEM_BACKGROUND); + s_plus_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_PLUS_BUTTON); + s_minus_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_MINUS_BUTTON); + s_background_plus_pressed_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_BACKGROUND_PLUS_PRESSED); + s_background_minus_pressed_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_BACKGROUND_MINUS_PRESSED); + s_scroll_bar_full_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_SCROLL_BAR_FULL); + s_scroll_bar_half_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_SCROLL_BAR_HALF); + s_scroll_bar_third_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_SCROLL_BAR_THIRD); + + // Create ScrollLayer + s_scroll_layer = scroll_layer_create(bounds); + scroll_layer_set_content_size( + s_scroll_layer, + GSize(bounds.size.w, MAX_ITEMS * ITEM_UI_HEIGHT) + ); + + // Remove shadow + scroll_layer_set_shadow_hidden(s_scroll_layer, true); + // Button scroll + window_set_click_config_provider(window, click_config_provider); + + // Get entries + s_scroll_items.items = items_get(); + + // Add items + scroll_layer_add_items(); + + // Add ScrollLayer + layer_add_child(parent, scroll_layer_get_layer(s_scroll_layer)); + + // Add scroll bar + GRect scroll_box = SCROLL_BAR_FRAME_RECT; + if (s_scroll_items.page_count == 2) { + scroll_box.size.h = 82; + s_scroll_bar_layer = layer_create(scroll_box); + s_scroll_bar_current_bitmap = s_scroll_bar_half_bitmap; + + } + else if (s_scroll_items.page_count == 3) { + scroll_box.size.h = 52; + s_scroll_bar_layer = layer_create(scroll_box); + s_scroll_bar_current_bitmap = s_scroll_bar_third_bitmap; + } + else { + scroll_box.size.h = 160; + s_scroll_bar_layer = layer_create(scroll_box); + s_scroll_bar_current_bitmap = s_scroll_bar_full_bitmap; + } + layer_set_update_proc(s_scroll_bar_layer, scroll_bar_draw); + layer_add_child(parent, s_scroll_bar_layer); + + // Register touch handler + touch_service_subscribe(touch_handler, NULL); +} + +void scrolllayer_deinit() { + // Unload font + fonts_unload_custom_font(s_scrolllayer_font); + + // Unsubscribe touch + touch_service_unsubscribe(); + + // Destroy ScrollLayer + scroll_layer_destroy(s_scroll_layer); + + // Destroy bitmaps + gbitmap_destroy(s_background_bitmap); + gbitmap_destroy(s_plus_bitmap); + gbitmap_destroy(s_minus_bitmap); + gbitmap_destroy(s_background_plus_pressed_bitmap); + gbitmap_destroy(s_background_minus_pressed_bitmap); + gbitmap_destroy(s_scroll_bar_full_bitmap); + gbitmap_destroy(s_scroll_bar_half_bitmap); + gbitmap_destroy(s_scroll_bar_third_bitmap); + + // Destroy scroll bar + layer_destroy(s_scroll_bar_layer); + + // Destroy items + scroll_layer_remove_items(); +} \ No newline at end of file diff --git a/src/c/scrolllayer.h b/src/c/scrolllayer.h new file mode 100644 index 0000000..0ebe812 --- /dev/null +++ b/src/c/scrolllayer.h @@ -0,0 +1,49 @@ +#pragma once + + +#include + +#include "src/c/items.h" + + +// SCROLLLAYER POSITION +#define SCROLLLAYER_FRAME_RECT GRect(5, 30, 174, 168) +#define SCROLL_BAR_FRAME_RECT GRect(180, 34, 10, 160) +#define SCROLL_BAR_ORIGIN_HEIGHT 34 +#define SCROLL_BAR_ORIGIN_HEIGHT_END 34 + 160 +#define PERCENTAGE_BAR_WIDTH 112 + +// TOUCH BUTTON RECTS +#define ITEM_1_PLUS_BUTTON_RECT GRect(110, 32, 70, 56) +#define ITEM_1_MINUS_BUTTON_RECT GRect(8, 32, 70, 56) +#define ITEM_2_PLUS_BUTTON_RECT GRect(110, 88, 70, 56) +#define ITEM_2_MINUS_BUTTON_RECT GRect(8, 88, 70, 56) +#define ITEM_3_PLUS_BUTTON_RECT GRect(110, 144, 70, 56) +#define ITEM_3_MINUS_BUTTON_RECT GRect(8, 144, 70, 56) + +// UI +#define MAX_UI_ITEMS 3 +#define MAX_UI_PAGES (MAX_ITEMS / MAX_UI_ITEMS) +#define ITEM_UI_HEIGHT 56 + +// LIMITS +#define RED_LIMIT 0.1 +#define ORANGE_LIMIT 0.4 + +typedef struct { + item_list_t *items; + int page_count; + TextLayer *texts[MAX_ITEMS]; + TextLayer *numbers[MAX_ITEMS]; + char numbers_buffer[MAX_ITEMS][MAX_CHARACTERS]; + BitmapLayer *backgrounds[MAX_ITEMS]; + BitmapLayer *plus_buttons[MAX_ITEMS]; + BitmapLayer *minus_buttons[MAX_ITEMS]; + Layer *success_bars[MAX_ITEMS]; +} scroll_items_t; + +void scroll_layer_add_items(); +void scroll_layer_remove_items(); +void scroll_layer_update_scrollbar(); +void scrolllayer_init(Layer *parent, Window *window); +void scrolllayer_deinit(); \ No newline at end of file diff --git a/src/c/time.c b/src/c/time.c new file mode 100644 index 0000000..d15cbe2 --- /dev/null +++ b/src/c/time.c @@ -0,0 +1,58 @@ +#include "time.h" + +#include "src/c/colors.h" + +static TextLayer *s_time_layer; +static GFont s_time_font; + + +static void update_time() { + // Get tm structure + time_t temp = time(NULL); + struct tm *tick_time = localtime(&temp); + + // Write the current time + static char s_time_buffer[8]; + strftime(s_time_buffer, sizeof(s_time_buffer), + clock_is_24h_style() ? "%H:%M" : "%I:%M", + tick_time); + + // Display the time + text_layer_set_text(s_time_layer, s_time_buffer); +} + +static void tick_handler(struct tm *tick_time, TimeUnits units_changed) { + update_time(); +} + +void time_init(Layer *parent) { + // Load custom font + s_time_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_SLKSCR_16)); + + // Create the time TextLayer + s_time_layer = text_layer_create(TIME_RECT); + text_layer_set_background_color(s_time_layer, GColorClear); + text_layer_set_text_color(s_time_layer, WHITE); + text_layer_set_font(s_time_layer, s_time_font); + text_layer_set_text_alignment(s_time_layer, GTextAlignmentCenter); + + // Add child + layer_add_child(parent, text_layer_get_layer(s_time_layer)); + + // Display the time on start + update_time(); + + // Register with TickTimerService + tick_timer_service_subscribe(MINUTE_UNIT, tick_handler); +} + +void time_deinit() { + // Destroy time layer + text_layer_destroy(s_time_layer); + + // Unload font + fonts_unload_custom_font(s_time_font); + + // Unsubscribe timer + tick_timer_service_unsubscribe(); +} \ No newline at end of file diff --git a/src/c/time.h b/src/c/time.h new file mode 100644 index 0000000..9b4bc77 --- /dev/null +++ b/src/c/time.h @@ -0,0 +1,12 @@ +#pragma once + + +#include + + +// TIME POSITION +#define TIME_RECT GRect(8, 6, 186, 20) + + +void time_init(Layer *parent); +void time_deinit(); \ No newline at end of file diff --git a/src/pkjs/config.js b/src/pkjs/config.js new file mode 100644 index 0000000..6be4d1a --- /dev/null +++ b/src/pkjs/config.js @@ -0,0 +1,312 @@ +module.exports = [ + { + "type": "heading", + "defaultValue": "App configuration" + }, + { + "type": "text", + "defaultValue": "Character limit is 10. If numbers go higher than 1000, use percentage." + }, + + { + "type": "section", + "items": [ + { + "type": "heading", + "defaultValue": "Item 1" + }, + { + "type": "input", + "messageKey": "ITEM_1", + "defaultValue": "" + }, + { + "type": "input", + "messageKey": "ITEM_1_SUCCESS", + "label": "Success", + "defaultValue": "0" + }, + { + "type": "input", + "messageKey": "ITEM_1_FAIL", + "label": "Fail", + "defaultValue": "0" + }, + { + "type": "toggle", + "messageKey": "ITEM_1_PERCENTAGE", + "label": "Show percentage", + "defaultValue": false + } + ] + }, + + { + "type": "section", + "items": [ + { + "type": "heading", + "defaultValue": "Item 2" + }, + { + "type": "input", + "messageKey": "ITEM_2", + "defaultValue": "" + }, + { + "type": "input", + "messageKey": "ITEM_2_SUCCESS", + "label": "Success", + "defaultValue": "0" + }, + { + "type": "input", + "messageKey": "ITEM_2_FAIL", + "label": "Fail", + "defaultValue": "0" + }, + { + "type": "toggle", + "messageKey": "ITEM_2_PERCENTAGE", + "label": "Show percentage", + "defaultValue": false + } + ] + }, + + { + "type": "section", + "items": [ + { + "type": "heading", + "defaultValue": "Item 3" + }, + { + "type": "input", + "messageKey": "ITEM_3", + "defaultValue": "" + }, + { + "type": "input", + "messageKey": "ITEM_3_SUCCESS", + "label": "Success", + "defaultValue": "0" + }, + { + "type": "input", + "messageKey": "ITEM_3_FAIL", + "label": "Fail", + "defaultValue": "0" + }, + { + "type": "toggle", + "messageKey": "ITEM_3_PERCENTAGE", + "label": "Show percentage", + "defaultValue": false + } + ] + }, + + { + "type": "section", + "items": [ + { + "type": "heading", + "defaultValue": "Item 4" + }, + { + "type": "input", + "messageKey": "ITEM_4", + "defaultValue": "" + }, + { + "type": "input", + "messageKey": "ITEM_4_SUCCESS", + "label": "Success", + "defaultValue": "0" + }, + { + "type": "input", + "messageKey": "ITEM_4_FAIL", + "label": "Fail", + "defaultValue": "0" + }, + { + "type": "toggle", + "messageKey": "ITEM_4_PERCENTAGE", + "label": "Show percentage", + "defaultValue": false + } + ] + }, + + { + "type": "section", + "items": [ + { + "type": "heading", + "defaultValue": "Item 5" + }, + { + "type": "input", + "messageKey": "ITEM_5", + "defaultValue": "" + }, + { + "type": "input", + "messageKey": "ITEM_5_SUCCESS", + "label": "Success", + "defaultValue": "0" + }, + { + "type": "input", + "messageKey": "ITEM_5_FAIL", + "label": "Fail", + "defaultValue": "0" + }, + { + "type": "toggle", + "messageKey": "ITEM_5_PERCENTAGE", + "label": "Show percentage", + "defaultValue": false + } + ] + }, + + { + "type": "section", + "items": [ + { + "type": "heading", + "defaultValue": "Item 6" + }, + { + "type": "input", + "messageKey": "ITEM_6", + "defaultValue": "" + }, + { + "type": "input", + "messageKey": "ITEM_6_SUCCESS", + "label": "Success", + "defaultValue": "0" + }, + { + "type": "input", + "messageKey": "ITEM_6_FAIL", + "label": "Fail", + "defaultValue": "0" + }, + { + "type": "toggle", + "messageKey": "ITEM_6_PERCENTAGE", + "label": "Show percentage", + "defaultValue": false + } + ] + }, + + { + "type": "section", + "items": [ + { + "type": "heading", + "defaultValue": "Item 7" + }, + { + "type": "input", + "messageKey": "ITEM_7", + "defaultValue": "" + }, + { + "type": "input", + "messageKey": "ITEM_7_SUCCESS", + "label": "Success", + "defaultValue": "0" + }, + { + "type": "input", + "messageKey": "ITEM_7_FAIL", + "label": "Fail", + "defaultValue": "0" + }, + { + "type": "toggle", + "messageKey": "ITEM_7_PERCENTAGE", + "label": "Show percentage", + "defaultValue": false + } + ] + }, + + { + "type": "section", + "items": [ + { + "type": "heading", + "defaultValue": "Item 8" + }, + { + "type": "input", + "messageKey": "ITEM_8", + "defaultValue": "" + }, + { + "type": "input", + "messageKey": "ITEM_8_SUCCESS", + "label": "Success", + "defaultValue": "0" + }, + { + "type": "input", + "messageKey": "ITEM_8_FAIL", + "label": "Fail", + "defaultValue": "0" + }, + { + "type": "toggle", + "messageKey": "ITEM_8_PERCENTAGE", + "label": "Show percentage", + "defaultValue": false + } + ] + }, + + { + "type": "section", + "items": [ + { + "type": "heading", + "defaultValue": "Item 9" + }, + { + "type": "input", + "messageKey": "ITEM_9", + "defaultValue": "" + }, + { + "type": "input", + "messageKey": "ITEM_9_SUCCESS", + "label": "Success", + "defaultValue": "0" + }, + { + "type": "input", + "messageKey": "ITEM_9_FAIL", + "label": "Fail", + "defaultValue": "0" + }, + { + "type": "toggle", + "messageKey": "ITEM_9_PERCENTAGE", + "label": "Show percentage", + "defaultValue": false + } + ] + }, + + { + "type": "submit", + "defaultValue": "Save Settings" + } +]; \ No newline at end of file diff --git a/src/pkjs/index.js b/src/pkjs/index.js new file mode 100644 index 0000000..db8cf38 --- /dev/null +++ b/src/pkjs/index.js @@ -0,0 +1,3 @@ +var Clay = require('@rebble/clay'); +var clayConfig = require('./config'); +var clay = new Clay(clayConfig); diff --git a/wscript b/wscript new file mode 100644 index 0000000..c18af6c --- /dev/null +++ b/wscript @@ -0,0 +1,57 @@ +# +# 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, Exception): + 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(['--config', 'pebble-jshintrc'] + [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) + + 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(ctx.env.BUILD_DIR) + ctx.pbl_program(source=ctx.path.ant_glob('src/c/**/*.c'), target=app_elf) + + if build_worker: + worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR) + binaries.append({'platform': p, 'app_elf': app_elf, 'worker_elf': worker_elf}) + ctx.pbl_worker(source=ctx.path.ant_glob('worker_src/c/**/*.c'), target=worker_elf) + else: + binaries.append({'platform': p, 'app_elf': app_elf}) + + ctx(features='subst', + source='package.json', + target='js/package.json', + is_copy=True) + + ctx.set_group('bundle') + ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob(['src/pkjs/**/*.js', 'src/pkjs/**/*.json']), js_entry_file='src/pkjs/index.js')