From 03896797ff0e36d64612490d1c75895339d4f1b9 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Thu, 10 Jul 2025 13:48:44 -0400 Subject: [PATCH 01/27] Add buzz when app selected --- apps/taglaunch/app.js | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/apps/taglaunch/app.js b/apps/taglaunch/app.js index 9318973c5..1309d695e 100644 --- a/apps/taglaunch/app.js +++ b/apps/taglaunch/app.js @@ -108,15 +108,21 @@ let showTagMenu = (tag) => { } }, select : i => { - let app = appsByTag[tag][i]; - if (!app) return; - if (!app.src || require("Storage").read(app.src)===undefined) { - Bangle.setUI(); - E.showMessage(/*LANG*/"App Source\nNot found"); - setTimeout(showMainMenu, 2000); - } else { - load(app.src); - } + Bangle.buzz(25); + //let the buzz have effect + setTimeout(() => { + let app = appsByTag[tag][i]; + + if (!app) return; + if (!app.src || require("Storage").read(app.src)===undefined) { + Bangle.setUI(); + E.showMessage(/*LANG*/"App Source\nNot found"); + setTimeout(showMainMenu, 2000); + } else { + load(app.src); + } + }, 27); + }, back : showMainMenu, remove: unload @@ -138,6 +144,7 @@ let showMainMenu = () => { } }, select : i => { + Bangle.buzz(25); let tag = tagKeys[i]; showTagMenu(tag); }, From fee258b3cf2d13266ff95bfcb9c9484b94ad8322 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Thu, 10 Jul 2025 13:49:39 -0400 Subject: [PATCH 02/27] Increase vector font size maximum, for people who have trouble seeing small text --- apps/taglaunch/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/taglaunch/settings.js b/apps/taglaunch/settings.js index 64045d37c..768eec744 100644 --- a/apps/taglaunch/settings.js +++ b/apps/taglaunch/settings.js @@ -21,7 +21,7 @@ }, /*LANG*/"Vector Font Size": { value: settings.vectorsize || 10, - min:10, max: 20,step:1,wrap:true, + min:10, max: 22,step:1,wrap:true, onchange: (m) => {save("vectorsize", m)} }, /*LANG*/"Show Clocks": { From 0ea0c91f3d87309edbe52414f585590ff674c855 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Thu, 10 Jul 2025 13:49:48 -0400 Subject: [PATCH 03/27] Update settings.js --- apps/taglaunch/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/taglaunch/settings.js b/apps/taglaunch/settings.js index 768eec744..002872d5b 100644 --- a/apps/taglaunch/settings.js +++ b/apps/taglaunch/settings.js @@ -21,7 +21,7 @@ }, /*LANG*/"Vector Font Size": { value: settings.vectorsize || 10, - min:10, max: 22,step:1,wrap:true, + min:10, max: 25,step:1,wrap:true, onchange: (m) => {save("vectorsize", m)} }, /*LANG*/"Show Clocks": { From 0e6b06cd1e7aef3649da5c6172885b644da62db0 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Thu, 10 Jul 2025 13:50:30 -0400 Subject: [PATCH 04/27] Update ChangeLog --- apps/taglaunch/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/taglaunch/ChangeLog b/apps/taglaunch/ChangeLog index e6bec3b85..e656392c1 100644 --- a/apps/taglaunch/ChangeLog +++ b/apps/taglaunch/ChangeLog @@ -6,3 +6,4 @@ 0.05: Make the "App source not found" warning less buggy 0.06: Fixed a crash if an app has no tags (app.tags is undefined) 0.07: Clear cached app list when updating showClocks setting +0.08: Add haptic feedback in menu, increase vector size limit in settings From daf5f926b30567f2dfd2308f57328920acdf0630 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Thu, 10 Jul 2025 13:50:50 -0400 Subject: [PATCH 05/27] Update metadata.json --- apps/taglaunch/metadata.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/taglaunch/metadata.json b/apps/taglaunch/metadata.json index d366d7ead..f8ead20f9 100644 --- a/apps/taglaunch/metadata.json +++ b/apps/taglaunch/metadata.json @@ -2,8 +2,8 @@ "id": "taglaunch", "name": "Tag Launcher", "shortName": "Taglauncher", - "version": "0.07", - "description": "Launcher that puts all applications into submenus based on their tag. With many applications installed this can result in a faster application selection than the linear access of the default launcher.", + "version": "0.08", + "description": "Launcher that puts all applications into submenus based on their tag. With many applications installed this can result in a faster application selection than the linear access from the default launcher.", "readme": "README.md", "icon": "app.png", "type": "launch", From 528b0f8adde67e81ba849c2e6fb2289a496bd58e Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Thu, 10 Jul 2025 21:33:51 -0400 Subject: [PATCH 06/27] Add zero brightness for LCD brightness --- apps/setting/settings.js | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/apps/setting/settings.js b/apps/setting/settings.js index 45b971e32..640c75bdd 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -326,20 +326,24 @@ function showThemeMenu(pop) { upd(th); } let rgb = {}; - rgb[/*LANG*/'black'] = "#000"; - rgb[/*LANG*/'white'] = "#fff"; - rgb[/*LANG*/'red'] = "#f00"; - rgb[/*LANG*/'green'] = "#0f0"; - rgb[/*LANG*/'blue'] = "#00f"; - rgb[/*LANG*/'cyan'] = "#0ff"; - rgb[/*LANG*/'magenta'] = "#f0f"; - rgb[/*LANG*/'yellow'] = "#ff0"; - if (!BANGLEJS2) { - // these would cause dithering, which is not great for e.g. text - rgb[/*LANG*/'orange'] = "#ff7f00"; - rgb[/*LANG*/'purple'] = "#7f00ff"; - rgb[/*LANG*/'grey'] = "#7f7f7f"; - } + rgb[/*LANG*/'Black'] = "#000"; + rgb[/*LANG*/'White'] = "#fff"; + rgb[/*LANG*/'Red'] = "#f00"; + rgb[/*LANG*/'Green'] = "#0f0"; + rgb[/*LANG*/'Blue'] = "#00f"; + rgb[/*LANG*/'Cyan'] = "#0ff"; + rgb[/*LANG*/'Magenta'] = "#f0f"; + rgb[/*LANG*/'Yellow'] = "#ff0"; + + // These would cause dithering, which is not great for e.g. text + rgb[/*LANG*/'Orange'] = "#ff7f00"; + rgb[/*LANG*/'Purple'] = "#7f00ff"; + rgb[/*LANG*/'Grey'] = "#7f7f7f"; + rgb[/*LANG*/'Maroon'] = "#3e1363"; + rgb[/*LANG*/'Indigo'] = "#3e1363"; + rgb[/*LANG*/'Dark Green'] = "#0e5c13"; + rgb[/*LANG*/'Navy'] = "#121e75"; + let colors = [], names = []; for(const c in rgb) { names.push(c); @@ -1077,4 +1081,4 @@ function showAltitude() { } // Show the main menu -pushMenu(mainMenu()); \ No newline at end of file +pushMenu(mainMenu()); From b6eb67ace22281fb10e7607836b78a824a1a4954 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Thu, 10 Jul 2025 21:36:50 -0400 Subject: [PATCH 07/27] Update ChangeLog --- apps/setting/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/setting/ChangeLog b/apps/setting/ChangeLog index 03ee8db12..e92ea7bb8 100644 --- a/apps/setting/ChangeLog +++ b/apps/setting/ChangeLog @@ -88,3 +88,4 @@ of 'Select Clock' 0.77: Save altitude calibration when user exits via reset 0.78: Fix menu scroll restore on BangleJS1 0.79: Ensure that tapping on pressure/altitude doesn't cause a menu to display temporarily +0.80: Add option to set LCD brightness to 0 From 3452d5e4de6e9a6b3e555eb05ed53c5052d8a8d0 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Thu, 10 Jul 2025 21:37:09 -0400 Subject: [PATCH 08/27] Update ChangeLog --- apps/setting/ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/setting/ChangeLog b/apps/setting/ChangeLog index e92ea7bb8..dafaef48c 100644 --- a/apps/setting/ChangeLog +++ b/apps/setting/ChangeLog @@ -88,4 +88,4 @@ of 'Select Clock' 0.77: Save altitude calibration when user exits via reset 0.78: Fix menu scroll restore on BangleJS1 0.79: Ensure that tapping on pressure/altitude doesn't cause a menu to display temporarily -0.80: Add option to set LCD brightness to 0 +0.80: Add option to set LCD brightness to 0, default brightness is now 0 as well. From 1aeeec65de4dad1d8f2a1f06b9d3bfd57a66caf4 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Thu, 10 Jul 2025 21:37:24 -0400 Subject: [PATCH 09/27] Update metadata.json --- apps/setting/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/setting/metadata.json b/apps/setting/metadata.json index e255a0dcb..c7ba12e09 100644 --- a/apps/setting/metadata.json +++ b/apps/setting/metadata.json @@ -1,7 +1,7 @@ { "id": "setting", "name": "Settings", - "version": "0.79", + "version": "0.80", "description": "A menu for setting up Bangle.js", "icon": "settings.png", "tags": "tool,system", From e95668da5217669cb3397182cc783dcec8ffd67b Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 11 Jul 2025 10:10:21 +0100 Subject: [PATCH 10/27] messages 0.88: Adjust padding calculation so messages are spaced out properly even when using international fonts --- apps/messagegui/ChangeLog | 1 + apps/messagegui/app.js | 6 +++--- apps/messagegui/metadata.json | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/messagegui/ChangeLog b/apps/messagegui/ChangeLog index 321c07be0..d849e9b6f 100644 --- a/apps/messagegui/ChangeLog +++ b/apps/messagegui/ChangeLog @@ -117,3 +117,4 @@ Remove workaround for 2v10 (>3 years ago) - assume everyone is on never firmware now 0.86: Default to showing message scroller (with title, bigger icon) 0.87: Make choosing of font size more repeatable +0.88: Adjust padding calculation so messages are spaced out properly even when using international fonts \ No newline at end of file diff --git a/apps/messagegui/app.js b/apps/messagegui/app.js index d47a43d65..d3c67401d 100644 --- a/apps/messagegui/app.js +++ b/apps/messagegui/app.js @@ -391,8 +391,6 @@ function showMessage(msgid, persist) { } } lines = g.setFont(bodyFont).wrapString(body, w); - if (lines.length<3) - lines.unshift(""); // if less lines, pad them out a bit at the top! } let negHandler,posHandler,rowLeftDraw,rowRightDraw; if (msg.negative) { @@ -432,12 +430,14 @@ function showMessage(msgid, persist) { } let fontHeight = g.setFont(bodyFont).getFontHeight(); let lineHeight = (fontHeight>25)?fontHeight:25; - if (title.includes("\n")) lineHeight=25; // ensure enough room for 2 lines of title in header + if (title.includes("\n") && lineHeight<25) lineHeight=25; // ensure enough room for 2 lines of title in header let linesPerRow = 2; if (fontHeight<17) { lineHeight = 16; linesPerRow = 3; } + if ((lines.length+4.5)*lineHeight < Bangle.appRect.h) + lines.unshift(""); // if less lines, pad them out a bit at the top! let rowHeight = lineHeight*linesPerRow; let textLineOffset = -(linesPerRow + ((rowLeftDraw||rowRightDraw)?1:0)); let msgIcon = require("messageicons").getImage(msg); diff --git a/apps/messagegui/metadata.json b/apps/messagegui/metadata.json index d2a2704c7..db54edb7e 100644 --- a/apps/messagegui/metadata.json +++ b/apps/messagegui/metadata.json @@ -2,7 +2,7 @@ "id": "messagegui", "name": "Message UI", "shortName": "Messages", - "version": "0.87", + "version": "0.88", "description": "Default app to display notifications from iOS and Gadgetbridge/Android", "icon": "app.png", "type": "app", From f1570224af2d7ee4149be6238cb98ffd9d22f156 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Fri, 11 Jul 2025 13:51:21 -0400 Subject: [PATCH 11/27] Change settings.js to changed version --- apps/setting/settings.js | 41 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/apps/setting/settings.js b/apps/setting/settings.js index 640c75bdd..7c2bcf33c 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -1,3 +1,5 @@ +Modules.addCached("widget_utils",function(){exports.offset=0,exports.hide=function(){if(exports.cleanup(),!global.WIDGETS)return;g.reset();for(var a of global.WIDGETS){if(a._draw)return;a._draw=a.draw,a.draw=()=>{},a._area=a.area,a.area='',a.x!=undefined&&g.clearRect(a.x,a.y,a.x+a.width-1,a.y+23)}},exports.show=function(){if(exports.cleanup(),!global.WIDGETS)return;for(var a of global.WIDGETS){if(!a._draw)return;a.draw=a._draw,a.area=a._area,delete a._draw,delete a._area,a.draw(a)}},exports.cleanupOverlay=function(){exports.offset=-24,Bangle.setLCDOverlay&&Bangle.setLCDOverlay(undefined,{id:'widget_utils'}),delete exports.autohide,delete Bangle.appRect,exports.animInterval&&(clearInterval(exports.animInterval),delete exports.animInterval),exports.hideTimeout&&(clearTimeout(exports.hideTimeout),delete exports.hideTimeout)},exports.cleanup=function(){exports.cleanupOverlay(),delete exports.offset,exports.swipeHandler&&(Bangle.removeListener('swipe',exports.swipeHandler),delete exports.swipeHandler),exports.origDraw&&(Bangle.drawWidgets=exports.origDraw,delete exports.origDraw)},exports.swipeOn=function(e){function f(){const b=exports.offset;Bangle.appRect.y=b+24,Bangle.appRect.h=1+Bangle.appRect.y2-Bangle.appRect.y,Bangle.setLCDOverlay&&(b>-24?Bangle.setLCDOverlay(a,0,b,{id:'widget_utils',remove:()=>{require('widget_utils').cleanupOverlay()}}):Bangle.setLCDOverlay(undefined,{id:'widget_utils'}))}function c(a,b){exports.animInterval&&clearInterval(exports.animInterval),exports.animInterval=setInterval(function(){exports.offset+=a;let c=!1;a>0&&exports.offset>=0?(c=!0,exports.offset=0):a<0&&exports.offset<-23&&(c=!0,exports.offset=-24),c&&(clearInterval(exports.animInterval),delete exports.animInterval,b&&b()),f()},50)}if(process.env.HWVERSION!==2)return exports.hide();if(exports.cleanup(),!global.WIDGETS)return;exports.autohide=e===undefined?2e3:e,Bangle.appRect={x:0,y:0,w:g.getWidth(),h:g.getHeight(),x2:g.getWidth()-1,y2:g.getHeight()-1};let a=Graphics.createArrayBuffer(g.getWidth(),26,16,{msb:!0});a.theme=g.theme,a._reset=a.reset,a.reset=function(){return this._reset().setColor(g.theme.fg).setBgColor(g.theme.bg)},a.reset().clearRect(0,0,a.getWidth(),23).fillRect(0,24,a.getWidth(),25);let d=g;exports.offset=-24;for(var b of global.WIDGETS)b._draw||(b._draw=b.draw,b.draw=function(){g=a,this._draw(this),g=d,exports.offset>-24&&f()},b._area=b.area,b.area.startsWith('b')&&(b.area='t'+b.area.substr(1)));exports.origDraw=Bangle.drawWidgets,Bangle.drawWidgets=()=>{g=a,exports.origDraw(),g=d},exports.swipeHandler=function(d,a){exports.hideTimeout&&(clearTimeout(exports.hideTimeout),delete exports.hideTimeout);let b;exports.autohide>0&&(b=function(){exports.hideTimeout=setTimeout(function(){c(-4)},exports.autohide)}),a>0&&exports.offset<0&&c(4,b),a<0&&exports.offset>-24&&c(-4)},Bangle.on('swipe',exports.swipeHandler),Bangle.drawWidgets()}}); +Modules.addCached("date_utils",function(){exports.dow=(c,a)=>{var b=require('locale').dow({getDay:()=>(c|0)%7},a).slice(0,a==2?1:100);return a==2?b.toUpperCase():b},exports.dows=(d,c)=>{var a=[];for(var b=0;b<7;b++)a.push(exports.dow(b+(d||0),c));return c==2?a.map(a=>a.toUpperCase()):a},exports.month=(c,a)=>{var b=require('locale').month({getMonth:()=>(11+(c|0))%12},a).slice(0,a==2?1:100);return a==2?b.toUpperCase():b},exports.months=c=>{var a=[],d=require('locale');for(var b=0;b<12;b++)a.push(d.month({getMonth:()=>b},c).slice(0,c==2?1:100));return c==2?a.map(a=>a.toUpperCase()):a}}); Bangle.loadWidgets(); Bangle.drawWidgets(); @@ -72,7 +74,7 @@ function resetSettings() { // clockHasWidgets: false, // Does the clock in 'clock' contain the string 'Bangle.loadWidgets' "12hour" : false, // 12 or 24 hour clock? firstDayOfWeek: 0, // 0 -> Sunday (default), 1 -> Monday - brightness: 1, // LCD brightness from 0 to 1 + brightness: 0, // LCD brightness from 0 to 1 // welcomed : undefined/true (whether welcome app should show) options: { wakeOnBTN1: true, @@ -326,24 +328,20 @@ function showThemeMenu(pop) { upd(th); } let rgb = {}; - rgb[/*LANG*/'Black'] = "#000"; - rgb[/*LANG*/'White'] = "#fff"; - rgb[/*LANG*/'Red'] = "#f00"; - rgb[/*LANG*/'Green'] = "#0f0"; - rgb[/*LANG*/'Blue'] = "#00f"; - rgb[/*LANG*/'Cyan'] = "#0ff"; - rgb[/*LANG*/'Magenta'] = "#f0f"; - rgb[/*LANG*/'Yellow'] = "#ff0"; - - // These would cause dithering, which is not great for e.g. text - rgb[/*LANG*/'Orange'] = "#ff7f00"; - rgb[/*LANG*/'Purple'] = "#7f00ff"; - rgb[/*LANG*/'Grey'] = "#7f7f7f"; - rgb[/*LANG*/'Maroon'] = "#3e1363"; - rgb[/*LANG*/'Indigo'] = "#3e1363"; - rgb[/*LANG*/'Dark Green'] = "#0e5c13"; - rgb[/*LANG*/'Navy'] = "#121e75"; - + rgb[/*LANG*/'black'] = "#000"; + rgb[/*LANG*/'white'] = "#fff"; + rgb[/*LANG*/'red'] = "#f00"; + rgb[/*LANG*/'green'] = "#0f0"; + rgb[/*LANG*/'blue'] = "#00f"; + rgb[/*LANG*/'cyan'] = "#0ff"; + rgb[/*LANG*/'magenta'] = "#f0f"; + rgb[/*LANG*/'yellow'] = "#ff0"; + if (!BANGLEJS2) { + // these would cause dithering, which is not great for e.g. text + rgb[/*LANG*/'orange'] = "#ff7f00"; + rgb[/*LANG*/'purple'] = "#7f00ff"; + rgb[/*LANG*/'grey'] = "#7f7f7f"; + } let colors = [], names = []; for(const c in rgb) { names.push(c); @@ -478,11 +476,12 @@ function LCDMenu() { Object.assign(lcdMenu, { /*LANG*/'LCD Brightness': { value: settings.brightness, - min: 0.1, + min: 0, max: 1, step: 0.1, onchange: v => { - settings.brightness = v || 1; + settings.brightness = v || 0; + print(settings.brightness); updateSettings(); Bangle.setLCDBrightness(settings.brightness); } From 7c5976b0ee24232dd249ab0e0de8b8cd566643a2 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Fri, 11 Jul 2025 15:16:15 -0400 Subject: [PATCH 12/27] Update settings.js --- apps/setting/settings.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/setting/settings.js b/apps/setting/settings.js index 7c2bcf33c..37944b638 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -1,5 +1,3 @@ -Modules.addCached("widget_utils",function(){exports.offset=0,exports.hide=function(){if(exports.cleanup(),!global.WIDGETS)return;g.reset();for(var a of global.WIDGETS){if(a._draw)return;a._draw=a.draw,a.draw=()=>{},a._area=a.area,a.area='',a.x!=undefined&&g.clearRect(a.x,a.y,a.x+a.width-1,a.y+23)}},exports.show=function(){if(exports.cleanup(),!global.WIDGETS)return;for(var a of global.WIDGETS){if(!a._draw)return;a.draw=a._draw,a.area=a._area,delete a._draw,delete a._area,a.draw(a)}},exports.cleanupOverlay=function(){exports.offset=-24,Bangle.setLCDOverlay&&Bangle.setLCDOverlay(undefined,{id:'widget_utils'}),delete exports.autohide,delete Bangle.appRect,exports.animInterval&&(clearInterval(exports.animInterval),delete exports.animInterval),exports.hideTimeout&&(clearTimeout(exports.hideTimeout),delete exports.hideTimeout)},exports.cleanup=function(){exports.cleanupOverlay(),delete exports.offset,exports.swipeHandler&&(Bangle.removeListener('swipe',exports.swipeHandler),delete exports.swipeHandler),exports.origDraw&&(Bangle.drawWidgets=exports.origDraw,delete exports.origDraw)},exports.swipeOn=function(e){function f(){const b=exports.offset;Bangle.appRect.y=b+24,Bangle.appRect.h=1+Bangle.appRect.y2-Bangle.appRect.y,Bangle.setLCDOverlay&&(b>-24?Bangle.setLCDOverlay(a,0,b,{id:'widget_utils',remove:()=>{require('widget_utils').cleanupOverlay()}}):Bangle.setLCDOverlay(undefined,{id:'widget_utils'}))}function c(a,b){exports.animInterval&&clearInterval(exports.animInterval),exports.animInterval=setInterval(function(){exports.offset+=a;let c=!1;a>0&&exports.offset>=0?(c=!0,exports.offset=0):a<0&&exports.offset<-23&&(c=!0,exports.offset=-24),c&&(clearInterval(exports.animInterval),delete exports.animInterval,b&&b()),f()},50)}if(process.env.HWVERSION!==2)return exports.hide();if(exports.cleanup(),!global.WIDGETS)return;exports.autohide=e===undefined?2e3:e,Bangle.appRect={x:0,y:0,w:g.getWidth(),h:g.getHeight(),x2:g.getWidth()-1,y2:g.getHeight()-1};let a=Graphics.createArrayBuffer(g.getWidth(),26,16,{msb:!0});a.theme=g.theme,a._reset=a.reset,a.reset=function(){return this._reset().setColor(g.theme.fg).setBgColor(g.theme.bg)},a.reset().clearRect(0,0,a.getWidth(),23).fillRect(0,24,a.getWidth(),25);let d=g;exports.offset=-24;for(var b of global.WIDGETS)b._draw||(b._draw=b.draw,b.draw=function(){g=a,this._draw(this),g=d,exports.offset>-24&&f()},b._area=b.area,b.area.startsWith('b')&&(b.area='t'+b.area.substr(1)));exports.origDraw=Bangle.drawWidgets,Bangle.drawWidgets=()=>{g=a,exports.origDraw(),g=d},exports.swipeHandler=function(d,a){exports.hideTimeout&&(clearTimeout(exports.hideTimeout),delete exports.hideTimeout);let b;exports.autohide>0&&(b=function(){exports.hideTimeout=setTimeout(function(){c(-4)},exports.autohide)}),a>0&&exports.offset<0&&c(4,b),a<0&&exports.offset>-24&&c(-4)},Bangle.on('swipe',exports.swipeHandler),Bangle.drawWidgets()}}); -Modules.addCached("date_utils",function(){exports.dow=(c,a)=>{var b=require('locale').dow({getDay:()=>(c|0)%7},a).slice(0,a==2?1:100);return a==2?b.toUpperCase():b},exports.dows=(d,c)=>{var a=[];for(var b=0;b<7;b++)a.push(exports.dow(b+(d||0),c));return c==2?a.map(a=>a.toUpperCase()):a},exports.month=(c,a)=>{var b=require('locale').month({getMonth:()=>(11+(c|0))%12},a).slice(0,a==2?1:100);return a==2?b.toUpperCase():b},exports.months=c=>{var a=[],d=require('locale');for(var b=0;b<12;b++)a.push(d.month({getMonth:()=>b},c).slice(0,c==2?1:100));return c==2?a.map(a=>a.toUpperCase()):a}}); Bangle.loadWidgets(); Bangle.drawWidgets(); From 7f78a473dd191be609966fecb05d2af89d298eae Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Fri, 11 Jul 2025 15:26:53 -0400 Subject: [PATCH 13/27] Respond to buzz setting --- apps/taglaunch/app.js | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/apps/taglaunch/app.js b/apps/taglaunch/app.js index 1309d695e..30a46ed57 100644 --- a/apps/taglaunch/app.js +++ b/apps/taglaunch/app.js @@ -17,7 +17,8 @@ let vectorval = 20; let font = g.getFonts().includes("12x20") ? "12x20" : "6x8:2"; let settings = Object.assign({ showClocks: true, - fullscreen: false + fullscreen: false, + buzz:false }, s.readJSON("taglaunch.json", true) || {}); if ("vectorsize" in settings) vectorval = parseInt(settings.vectorsize); @@ -108,9 +109,22 @@ let showTagMenu = (tag) => { } }, select : i => { - Bangle.buzz(25); - //let the buzz have effect - setTimeout(() => { + if(settings.buzz){ + Bangle.buzz(25); + //let the buzz have effect + setTimeout(() => { + let app = appsByTag[tag][i]; + + if (!app) return; + if (!app.src || require("Storage").read(app.src)===undefined) { + Bangle.setUI(); + E.showMessage(/*LANG*/"App Source\nNot found"); + setTimeout(showMainMenu, 2000); + } else { + load(app.src); + } + }, 27); + }else{ let app = appsByTag[tag][i]; if (!app) return; @@ -121,8 +135,7 @@ let showTagMenu = (tag) => { } else { load(app.src); } - }, 27); - + } }, back : showMainMenu, remove: unload @@ -144,7 +157,7 @@ let showMainMenu = () => { } }, select : i => { - Bangle.buzz(25); + if(settings.buzz)Bangle.buzz(25); let tag = tagKeys[i]; showTagMenu(tag); }, From 8bdd21fc40dbd6a554605c65461df69b60dbf36e Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Fri, 11 Jul 2025 15:27:34 -0400 Subject: [PATCH 14/27] Add toggle for haptic feedback --- apps/taglaunch/settings.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/taglaunch/settings.js b/apps/taglaunch/settings.js index 002872d5b..5faf9459f 100644 --- a/apps/taglaunch/settings.js +++ b/apps/taglaunch/settings.js @@ -2,7 +2,8 @@ (function(back) { let settings = Object.assign({ showClocks: true, - fullscreen: false + fullscreen: false, + buzz:false }, require("Storage").readJSON("taglaunch.json", true) || {}); let fonts = g.getFonts(); @@ -24,6 +25,13 @@ min:10, max: 25,step:1,wrap:true, onchange: (m) => {save("vectorsize", m)} }, + /*LANG*/"Haptic Feedback": { + value: settings.buzz == true, + onchange: (m) => { + save("buzz", m); + require("Storage").erase("taglaunch.cache.json"); //delete the cache app list + } + }, /*LANG*/"Show Clocks": { value: settings.showClocks == true, onchange: (m) => { From 680484732a8f4ff4213945ca8c8fabfbf36d644c Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Fri, 11 Jul 2025 15:28:05 -0400 Subject: [PATCH 15/27] Update ChangeLog --- apps/taglaunch/ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/taglaunch/ChangeLog b/apps/taglaunch/ChangeLog index e656392c1..6574fcb4d 100644 --- a/apps/taglaunch/ChangeLog +++ b/apps/taglaunch/ChangeLog @@ -6,4 +6,4 @@ 0.05: Make the "App source not found" warning less buggy 0.06: Fixed a crash if an app has no tags (app.tags is undefined) 0.07: Clear cached app list when updating showClocks setting -0.08: Add haptic feedback in menu, increase vector size limit in settings +0.08: Add haptic feedback option when selecting app or category in menu, increase vector size limit in settings From 1d8f64a15824895746ef3d8569fb6a7bf0c6e7a0 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Fri, 11 Jul 2025 15:30:21 -0400 Subject: [PATCH 16/27] Update README.md to show haptic feedback setting documentation --- apps/taglaunch/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/taglaunch/README.md b/apps/taglaunch/README.md index 89540c514..cc32f54a1 100644 --- a/apps/taglaunch/README.md +++ b/apps/taglaunch/README.md @@ -11,6 +11,7 @@ Settings - `Font` - The font used (`4x6`, `6x8`, `12x20`, `6x15` or `Vector`). Default `12x20`. - `Vector Font Size` - The size of the font if `Font` is set to `Vector`. Default `10`. +- `Haptic Feedback` - Whether or not to vibrate slightly when selecting an app or category in the launcher. Default `No`. - `Show Clocks` - If set to `No` then clocks won't appear in the app list. Default `Yes`. - `Fullscreen` - If set to `Yes` then widgets won't be loaded. Default `No`. @@ -28,3 +29,4 @@ Contributors - [atjn](https://github.com/atjn) - [BlueFox4](https://github.com/BlueFox4) +- [RKBoss6](https://github.com/RKBoss6) From dabe80f789066661597d676d15c5579ed41a6401 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Sat, 12 Jul 2025 09:31:37 -0400 Subject: [PATCH 17/27] Fixed for @bobrippling 's reviews --- apps/taglaunch/app.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/taglaunch/app.js b/apps/taglaunch/app.js index 30a46ed57..d5f128664 100644 --- a/apps/taglaunch/app.js +++ b/apps/taglaunch/app.js @@ -31,7 +31,9 @@ if ("font" in settings){ scaleval = (font.split("x")[1])/20; } } - +let buzz=function(){ + Bangle.buzz(25); +} let sort = (a, b) => { let n=(0|a.sortorder)-(0|b.sortorder); if (n) return n; // do sortorder first @@ -110,7 +112,7 @@ let showTagMenu = (tag) => { }, select : i => { if(settings.buzz){ - Bangle.buzz(25); + buzz(); //let the buzz have effect setTimeout(() => { let app = appsByTag[tag][i]; @@ -157,7 +159,7 @@ let showMainMenu = () => { } }, select : i => { - if(settings.buzz)Bangle.buzz(25); + if(settings.buzz)buzz(); let tag = tagKeys[i]; showTagMenu(tag); }, From 570d15daca49b8b2b95db509ada94702d6609d78 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Sat, 12 Jul 2025 09:32:26 -0400 Subject: [PATCH 18/27] Fix: Haptic feedback does not need cache clear --- apps/taglaunch/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/taglaunch/settings.js b/apps/taglaunch/settings.js index 5faf9459f..09290cf24 100644 --- a/apps/taglaunch/settings.js +++ b/apps/taglaunch/settings.js @@ -29,7 +29,7 @@ value: settings.buzz == true, onchange: (m) => { save("buzz", m); - require("Storage").erase("taglaunch.cache.json"); //delete the cache app list + } }, /*LANG*/"Show Clocks": { From 59b8cd31217a56b7502eed186cfcf5207e95d697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20K=C3=B6ll?= Date: Sat, 12 Jul 2025 22:10:12 +0200 Subject: [PATCH 19/27] [teatimer] - 0.07 - simplify ui - enable settings - adjustable time steps - remove stuttering timer - refactor code --- apps/teatimer/ChangeLog | 1 + apps/teatimer/README.md | 25 +- apps/teatimer/TeatimerHelp.jpg | Bin 5125 -> 0 bytes apps/teatimer/TeatimerHelp.png | Bin 0 -> 3043 bytes apps/teatimer/TeatimerPause.png | Bin 0 -> 2299 bytes apps/teatimer/TeatimerRun.jpg | Bin 3981 -> 0 bytes apps/teatimer/TeatimerRun.png | Bin 0 -> 2202 bytes apps/teatimer/TeatimerStart.jpg | Bin 4245 -> 0 bytes apps/teatimer/TeatimerStart.png | Bin 0 -> 2507 bytes apps/teatimer/TeatimerUp.jpg | Bin 3737 -> 0 bytes apps/teatimer/TeatimerUp.png | Bin 0 -> 2289 bytes apps/teatimer/app.js | 388 +++++++++++++++----------------- apps/teatimer/metadata.json | 15 +- apps/teatimer/settings.js | 47 ++++ 14 files changed, 258 insertions(+), 218 deletions(-) delete mode 100644 apps/teatimer/TeatimerHelp.jpg create mode 100644 apps/teatimer/TeatimerHelp.png create mode 100644 apps/teatimer/TeatimerPause.png delete mode 100644 apps/teatimer/TeatimerRun.jpg create mode 100644 apps/teatimer/TeatimerRun.png delete mode 100644 apps/teatimer/TeatimerStart.jpg create mode 100644 apps/teatimer/TeatimerStart.png delete mode 100644 apps/teatimer/TeatimerUp.jpg create mode 100644 apps/teatimer/TeatimerUp.png create mode 100644 apps/teatimer/settings.js diff --git a/apps/teatimer/ChangeLog b/apps/teatimer/ChangeLog index fcdcda875..cf68c1d69 100644 --- a/apps/teatimer/ChangeLog +++ b/apps/teatimer/ChangeLog @@ -4,3 +4,4 @@ 0.04: Get time zone from settings for showing the clock 0.05: Minor code improvements 0.06: Adjust format of title, save counter before leaving help screen +0.07: Refactor code, fix stuttering timer, add settings menu diff --git a/apps/teatimer/README.md b/apps/teatimer/README.md index b7ece6022..b7c628420 100644 --- a/apps/teatimer/README.md +++ b/apps/teatimer/README.md @@ -3,7 +3,7 @@ A simple timer. You can easily set up the time. The initial time is 2:30 On the first screen, you can -- tap to get help +- double tap to get help - swipe up/down to change the timer by +/- one minute - swipe left/right to change the time by +/- 15 seconds - press Btn1 to start @@ -12,24 +12,31 @@ Press Btn1 again to stop the timer - when time is up, your Bangle will buzz for 15 seconds - and it will count up to 60 seconds and stop after that -## Images -_1. Startscreen_ +The time changes can be adjusted in the settings menu. -![](TeatimerStart.jpg) +## Images +_1. Start screen_ + +![](TeatimerStart.png) Current time is displayed below the Title. Initial time is 2:30. _2. Help Screen_ -![](TeatimerHelp.jpg) +![](TeatimerHelp.png) _3. Tea Timer running_ -![](TeatimerRun.jpg) -Remainig time is shown in big font size. Above the initial time is shown. +![](TeatimerRun.png) +Remainig time is shown in big font size. -_4. When time is up_ +_4. Pause Timer -![](TeatimerUp.jpg) +![](TeatimerPause.png) +While the timer is running, you can pause and unpause it by pressing BTN1. + +_5. When time is up_ + +![](TeatimerUp.png) When time is up, the watch will buzz for 15 seconds. It will count up to 60 seconds. ## Requests diff --git a/apps/teatimer/TeatimerHelp.jpg b/apps/teatimer/TeatimerHelp.jpg deleted file mode 100644 index e22960c668b2e7e1b783163274d6c80cc3c90416..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5125 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 Z!0XbiQ=eYi86xsdy&oXAAyW26{|hcX0fqno diff --git a/apps/teatimer/TeatimerHelp.png b/apps/teatimer/TeatimerHelp.png new file mode 100644 index 0000000000000000000000000000000000000000..f26e290fbffd37d9f05d207ae0c047b1a76b0e8e GIT binary patch literal 3043 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 R|9IYk^R_NF)mHe_e*#F^yXOD^ literal 0 HcmV?d00001 diff --git a/apps/teatimer/TeatimerPause.png b/apps/teatimer/TeatimerPause.png new file mode 100644 index 0000000000000000000000000000000000000000..bb6738e5426f1841d36db931bfaef132b9017d01 GIT binary patch literal 2299 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 LlRLfv7yRQN=F{yD literal 0 HcmV?d00001 diff --git a/apps/teatimer/TeatimerStart.jpg b/apps/teatimer/TeatimerStart.jpg deleted file mode 100644 index 4fa8f2fc49b04d4a40328b8cd227a60c958e2325..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4245 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 Mj|KVpyVpkl3!DOAqyPW_ diff --git a/apps/teatimer/TeatimerStart.png b/apps/teatimer/TeatimerStart.png new file mode 100644 index 0000000000000000000000000000000000000000..2793c9bae81a681779d360dbcfdfe3b75f580c9f GIT binary patch literal 2507 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 literal 0 HcmV?d00001 diff --git a/apps/teatimer/TeatimerUp.jpg b/apps/teatimer/TeatimerUp.jpg deleted file mode 100644 index 80b8c3c8abdd8bce55727722ab9bcf782a5f6b65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3737 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 C|0R3? literal 0 HcmV?d00001 diff --git a/apps/teatimer/app.js b/apps/teatimer/app.js index a22000342..7e2e3dbb7 100644 --- a/apps/teatimer/app.js +++ b/apps/teatimer/app.js @@ -1,237 +1,217 @@ -// Tea Timer -// Button press stops timer, next press restarts timer -let drag; -var counter = 0; -var counterStart = 150; // 150 seconds -var counterInterval; -const states = { - init: 1, // unused - help: 2, // show help text - start: 4, // show/change initial counter - count: 8, // count down - countUp: 16, // count up after timer finished - stop: 32 // timer stopped +const FILE = "teatimer.json"; +const DEFAULTS = { + timerDuration: 150, + bigJump: 60, + smallJump: 15, + finishBuzzDuration: 1500, + overtimeBuzzDuration: 100, + overtimeBuzzLimit: 60, + overtimeBuzzSeconds: 15 }; -var state = states.start; -let setting = require("Storage").readJSON("setting.json",1); -E.setTimeZone(setting.timezone); -// Title showing current time -function appTitle() { - return "Tea Timer\n" + currentTime(); +// Enum for states +const STATES = { + INIT: "init", + RUNNING: "running", + PAUSED: "paused", + FINISHED: "finished", + OVERTIME: "overtime" +}; + +let savedSettings = require("Storage").readJSON(FILE, 1) || {}; +let settings = Object.assign({}, DEFAULTS, savedSettings); + +let state = STATES.INIT; +let showHelp = false; + +let startTime = 0; +let remaining = settings.timerDuration; +let target = 0; + +let drag = null; +let dragAdjusted = false; +let lastTapTime = 0; + +// === Helpers === +function formatTime(s) { + let m = Math.floor(s / 60); + let sec = (s % 60).toString().padStart(2, '0'); + return `${m}:${sec}`; } -function currentTime() { - let min = Date().getMinutes(); - if (min < 10) min = "0" + min; - return Date().getHours() + ":" + min; +function getTimeStr() { + let d = new Date(); + return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`; } -function timeFormated(sec) { - let min = Math.floor(sec / 60); - sec = sec % 60; - if (sec < 10) sec = "0" + sec; - return min + ":" + sec; +function isState(s) { + return state === s; } -// initialize timer and show timer value => state: start -function initTimer() { - counter = counterStart; - setState(states.start); - showCounter(true); +function setState(s) { + state = s; } -// timer value (counter) can be changed in state start -function changeCounter(diff) { - if (state == states.start) { - if (counter + diff > 0) { - counter = counter + diff; - showCounter(true); - } +// === UI Drawing === +function drawUI() { + g.reset(); + g.setBgColor(g.theme.bg).clear(); + g.setColor(g.theme.fg); + let cx = g.getWidth() / 2; + + // Time (top right) + g.setFont("6x8", 2); + g.setFontAlign(1, 0); + g.drawString(getTimeStr(), g.getWidth() - 4, 10); + + // Help text + if (showHelp) { + g.setFontAlign(0, 0); + g.setFont("Vector", 15); + g.drawString( + `Swipe up/down: ±${settings.bigJump}s\nSwipe left/right: ±${settings.smallJump}s\n\nBTN1: Start/Pause\nDouble Tap: Hide Help`, + cx, 80 + ); + return; + } + + // Title + g.setFont("Vector", 20); + g.setFontAlign(0, 0); + let label = (isState(STATES.OVERTIME)) ? "Time's Up!" : "Tea Timer"; + g.drawString(label, cx, 40); + + // Time remaining / overtime + g.setFont("Vector", 60); + g.setColor(isState(STATES.OVERTIME) ? "#f00" : g.theme.fg); + g.drawString(formatTime(remaining), cx, 100); + + // Bottom state text + g.setFontAlign(0, 0); + if (isState(STATES.PAUSED)) { + g.setFont("6x8", 2); + g.drawString("paused", cx, g.getHeight() - 20); + } else if (!isState(STATES.RUNNING) && !isState(STATES.OVERTIME)) { + g.setFont("Vector", 13); + g.drawString("double tap for help", cx, g.getHeight() - 20); } } -// start or restart timer => state: count +// === Timer Logic === function startTimer() { - counterStart = counter; - setState(states.count); - countDown(); - if (!counterInterval) - counterInterval = setInterval(countDown, 1000); + setState(STATES.RUNNING); + startTime = Date.now(); + target = startTime + remaining * 1000; } -/* show current counter value at start and while count down - Show - - Title with current time - - initial timer value - - remaining time - - hint for help in state start -*/ -function showCounter(withHint) { - g.reset(); // workaround for E.showMessage bg color in 2v14 and earlier - E.showMessage("", appTitle()); - g.reset().setFontAlign(0,0); // center font - // draw the current counter value - g.setBgColor(-1).setColor(0,0,1); // blue - g.setFont("Vector",20); // vector font, 20px - g.drawString("Timer: " + timeFormated(counterStart),80,55); - g.setFont("Vector",60); // vector font, 60px - g.drawString(timeFormated(counter),83,100); - if (withHint) { - g.setFont("Vector",20); // vector font, 80px - g.drawString("Tap for help",80,150); +function pauseTimer() { + if (isState(STATES.RUNNING)) { + remaining = Math.max(0, Math.ceil((target - Date.now()) / 1000)); + setState(STATES.PAUSED); } } -// count down and update every second -// when time is up, start counting up -function countDown() { - counter--; - // Out of time - if (counter<=0) { - outOfTime(); - countUp(); - counterInterval = setInterval(countUp, 1000); - return; +function resumeTimer() { + if (isState(STATES.PAUSED)) { + startTime = Date.now(); + target = startTime + remaining * 1000; + setState(STATES.RUNNING); } - showCounter(false); } -// -function outOfTime() { - E.showMessage("Time is up!",appTitle()); - setState(states.countUp); - resetTimer(); - Bangle.buzz(); - Bangle.buzz(); -} - -/* this counts up (one minute), after time is up - Show - - Title with current time - - initial timer value - - "Time is up!" - - time since timer finished -*/ -function countUp() { - // buzz for 15 seconds - counter++; - if (counter <=15) { - Bangle.buzz(); - } - // stop counting up after 60 seconds - if (counter > 60) { - outOfTime(); - return; - } - g.reset(); // workaround for E.showMessage bg color in 2v14 and earlier - E.showMessage("", appTitle()); - g.reset().setFontAlign(0,0); // center font - g.setBgColor(-1).setColor(0,0,1); // blue - g.setFont("Vector",20); // vector font, 20px - g.drawString("Timer: " + timeFormated(counterStart),80,55); - g.setFont("Vector",30); // vector font, 80px - g.setBgColor(-1).setColor(1,0,0); // red - g.drawString("Time is up!",85,85); - g.setFont("Vector",40); // vector font, 80px - // draw the current counter value - g.drawString(timeFormated(counter),80,130); -} - -// reset when interupted by user oder 60 seconds after timer finished function resetTimer() { - clearInterval(); - counterInterval = undefined; + setState(STATES.INIT); + remaining = settings.timerDuration; } -// timer is stopped by user => state: stop -function stopTimer() { - resetTimer(); - E.showMessage("Timer stopped!", appTitle()); - setState(states.stop); -} - -// timer is stopped by user while counting up => state: start -function stopTimer2() { - resetTimer(); - initTimer(); -} - - -function setState(st) { - state = st; -} - -function buttonPressed() { - switch(state) { - case states.init: - initTimer(); - break; - case states.help: - initTimer(); - break; - case states.start: - startTimer(); - break; - case states.count: - stopTimer(); - break; - case states.countUp: - stopTimer2(); - break; - case states.stop: - initTimer(); - break; - default: - initTimer(); - break; - } -} - -/* Change initial counter value by swiping - swipe up: +1 minute - swipe down: -1 minute - swipe right: +15 seconds - swipe left: -15 seconds */ -function initDragEvents() { - Bangle.on("drag", e => { - if (state == states.start) { - if (!drag) { // start dragging - drag = {x: e.x, y: e.y}; - } else if (!e.b) { // released - const dx = e.x-drag.x, dy = e.y-drag.y; - drag = null; - if (Math.abs(dx)>Math.abs(dy)+10) { - // horizontal - changeCounter(dx>0 ? 15 : -15); - } else if (Math.abs(dy)>Math.abs(dx)+10) { - // vertical - changeCounter(dy>0 ? -60 : 60); - } +function tick() { + if (isState(STATES.RUNNING)) { + remaining -= 1; + if (remaining <= 0) { + remaining = 0; + setState(STATES.OVERTIME); + startTime = Date.now(); + remaining = 0; // Start overtime count-up from 0 + Bangle.buzz(settings.finishBuzzDuration); + } + } else if (isState(STATES.OVERTIME)) { + remaining += 1; + if (remaining <= settings.overtimeBuzzSeconds) { + Bangle.buzz(settings.overtimeBuzzDuration, 0.3); + } + if (remaining >= settings.overtimeBuzzLimit) { + resetTimer(); // Stop overtime after max duration } } -}); + drawUI(); } -// show help text while in start state (see initDragEvents()) -function showHelp() { - if (state == states.start) { - state = states.help; - g.setBgColor(g.theme.bg); - g.setColor(g.theme.fg); - E.showMessage("Swipe up/down\n+/- one minute\n\nSwipe left/right\n+/- 15 seconds\n\nPress Btn1 to start","Tea timer help"); +// === UI Controls === +function toggleTimer() { + if (showHelp) { + showHelp = false; + } else if (isState(STATES.OVERTIME)) { + resetTimer(); + } else if (isState(STATES.INIT)) { + startTimer(); + } else if (isState(STATES.PAUSED)) { + resumeTimer(); + } else if (isState(STATES.RUNNING)) { + pauseTimer(); } - // return to start - else if (state == states.help) { - counterStart = counter; - initTimer(); + + drawUI(); +} + +function handleDoubleTap() { + if (isState(STATES.INIT)) { + let now = Date.now(); + if (now - lastTapTime < 400) { + showHelp = !showHelp; + drawUI(); + } + lastTapTime = now; } } -// drag events in start state (to change counter value) -initDragEvents(); -// Show help test in start state -Bangle.on('touch', function(button, xy) { showHelp(); }); -// event handling for button1 -setWatch(buttonPressed, BTN1, {repeat: true}); -initTimer(); \ No newline at end of file +function adjustTimer(diff) { + if (isState(STATES.INIT)) { + remaining = Math.max(5, remaining + diff); + settings.timerDuration = remaining; + drawUI(); + } +} + +function handleDrag(e) { + if (isState(STATES.INIT) && !showHelp) { + if (e.b) { + if (!drag) { + drag = { x: e.x, y: e.y }; + dragAdjusted = false; + } else if (!dragAdjusted) { + let dx = e.x - drag.x; + let dy = e.y - drag.y; + + if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > settings.smallJump) { + adjustTimer(dx > 0 ? settings.smallJump : -settings.smallJump); + dragAdjusted = true; + } else if (Math.abs(dy) > Math.abs(dx) && Math.abs(dy) > settings.bigJump) { + adjustTimer(dy > 0 ? -settings.bigJump : settings.bigJump); + dragAdjusted = true; + } + } + } else { + drag = null; + dragAdjusted = false; + } + } +} + +// === Init App === +setWatch(toggleTimer, BTN1, { repeat: true }); +Bangle.on("drag", handleDrag); +Bangle.on("touch", handleDoubleTap); + +resetTimer(); +drawUI(); +setInterval(tick, 1000); diff --git a/apps/teatimer/metadata.json b/apps/teatimer/metadata.json index 4d4ddd9e4..571053077 100644 --- a/apps/teatimer/metadata.json +++ b/apps/teatimer/metadata.json @@ -1,21 +1,26 @@ { "id": "teatimer", "name": "Tea Timer", - "version": "0.06", + "version": "0.07", "description": "A simple timer. You can easily set up the time.", "icon": "teatimer.png", "type": "app", "tags": "tool", "supports": ["BANGLEJS2"], "readme": "README.md", + "data": [ + { "name": "teatimer.json" } + ], "storage": [ {"name":"teatimer.app.js","url":"app.js"}, + {"name": "teatimer.settings.js", "url": "settings.js" }, {"name":"teatimer.img","url":"app-icon.js","evaluate":true} ], "screenshots": [ - {"url":"TeatimerStart.jpg"}, - {"url":"TeatimerHelp.jpg"}, - {"url":"TeatimerRun.jpg"}, - {"url":"TeatimerUp.jpg"} + {"url":"TeatimerStart.png"}, + {"url":"TeatimerHelp.png"}, + {"url":"TeatimerRun.png"}, + {"url":"TeatimerPause.png"}, + {"url":"TeatimerUp.png"} ] } diff --git a/apps/teatimer/settings.js b/apps/teatimer/settings.js new file mode 100644 index 000000000..8ed780344 --- /dev/null +++ b/apps/teatimer/settings.js @@ -0,0 +1,47 @@ +(function(back) { + const FILE = "teatimer.json"; + const DEFAULTS = { + timerDuration: 150, // Initial timer duration in seconds + bigJump: 60, // Jump for vertical swipes + smallJump: 15 // Jump for horizontal swipes + }; + + let settings = require("Storage").readJSON(FILE, 1) || DEFAULTS; + + function saveSettings() { + require("Storage").writeJSON(FILE, settings); + } + + function showSettingsMenu() { + E.showMenu({ + '': { title: 'Tea Timer Settings' }, + '< Back': back, + 'Default Duration (sec)': { + value: settings.timerDuration, + min: 5, max: 900, step: 5, + onchange: v => { + settings.timerDuration = v; + saveSettings(); + } + }, + 'Swipe Up/Down (sec)': { + value: settings.bigJump, + min: 5, max: 300, step: 5, + onchange: v => { + settings.bigJump = v; + saveSettings(); + } + }, + 'Swipe Left/Right (sec)': { + value: settings.smallJump, + min: 5, max: 60, step: 5, + onchange: v => { + settings.smallJump = v; + saveSettings(); + } + } + }); + } + + showSettingsMenu(); +}) From 117e2e099c44bf90df7492ba6afd488d1fa793e9 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Sun, 13 Jul 2025 11:10:50 -0400 Subject: [PATCH 20/27] Add loadApp function, making app more lightweight. --- apps/taglaunch/app.js | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/apps/taglaunch/app.js b/apps/taglaunch/app.js index d5f128664..3e322b5ba 100644 --- a/apps/taglaunch/app.js +++ b/apps/taglaunch/app.js @@ -31,9 +31,7 @@ if ("font" in settings){ scaleval = (font.split("x")[1])/20; } } -let buzz=function(){ - Bangle.buzz(25); -} + let sort = (a, b) => { let n=(0|a.sortorder)-(0|b.sortorder); if (n) return n; // do sortorder first @@ -111,24 +109,8 @@ let showTagMenu = (tag) => { } }, select : i => { - if(settings.buzz){ - buzz(); - //let the buzz have effect - setTimeout(() => { - let app = appsByTag[tag][i]; - - if (!app) return; - if (!app.src || require("Storage").read(app.src)===undefined) { - Bangle.setUI(); - E.showMessage(/*LANG*/"App Source\nNot found"); - setTimeout(showMainMenu, 2000); - } else { - load(app.src); - } - }, 27); - }else{ + const loadApp = () => { let app = appsByTag[tag][i]; - if (!app) return; if (!app.src || require("Storage").read(app.src)===undefined) { Bangle.setUI(); @@ -137,7 +119,15 @@ let showTagMenu = (tag) => { } else { load(app.src); } + }; + if(settings.buzz){ + Bangle.buzz(25); + //let the buzz have effect + setTimeout(loadApp,27); + }else{ + loadApp(); } + }, back : showMainMenu, remove: unload @@ -159,7 +149,7 @@ let showMainMenu = () => { } }, select : i => { - if(settings.buzz)buzz(); + if(settings.buzz)Bangle.buzz(25); let tag = tagKeys[i]; showTagMenu(tag); }, From a5e3330f7d331033ec224dbd60a72d215e4f8a8f Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 21 Jul 2025 10:36:52 +0100 Subject: [PATCH 21/27] clock_info 0.19: Fix Altitude ClockInfo after BLE added Make this kind of bug less likely to happen in future by searching for the correct clockinfo --- apps/clock_info/ChangeLog | 1 + apps/clock_info/lib.js | 11 ++++++----- apps/clock_info/metadata.json | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/clock_info/ChangeLog b/apps/clock_info/ChangeLog index 8915a702e..eef68647a 100644 --- a/apps/clock_info/ChangeLog +++ b/apps/clock_info/ChangeLog @@ -17,3 +17,4 @@ 0.16: Add BLE clkinfo entry 0.17: Fix BLE icon alignment and border on some clocks 0.18: Tweak BLE icon to add gap and ensure middle of B isn't filled +0.19: Fix Altitude ClockInfo after BLE added \ No newline at end of file diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index 756a0463b..40e989ffe 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -39,22 +39,23 @@ exports.load = function() { var hrm = 0; var alt = "--"; // callbacks (needed for easy removal of listeners) - function batteryUpdateHandler() { bangleItems[0].emit("redraw"); } - function stepUpdateHandler() { bangleItems[1].emit("redraw"); } + function batteryUpdateHandler() { bangleItems.find(i=>i.name=="Battery").emit("redraw"); } + function stepUpdateHandler() { bangleItems.find(i=>i.name=="Steps").emit("redraw"); } function hrmUpdateHandler(e) { if (e && e.confidence>60) hrm = Math.round(e.bpm); - bangleItems[2].emit("redraw"); + bangleItems.find(i=>i.name=="HRM").emit("redraw"); } function altUpdateHandler() { try { Bangle.getPressure().then(data=>{ if (!data) return; alt = Math.round(data.altitude) + "m"; - bangleItems[3].emit("redraw"); + bangleItems.find(i=>i.name=="Altitude").emit("redraw"); }); } catch (e) { print("Caught "+e+"\n in function altUpdateHandler in module clock_info"); - bangleItems[3].emit('redraw');} + bangleItems.find(i=>i.name=="Altitude").emit('redraw'); + } } // actual menu var menu = [{ diff --git a/apps/clock_info/metadata.json b/apps/clock_info/metadata.json index fcdaef2d0..4123fbac4 100644 --- a/apps/clock_info/metadata.json +++ b/apps/clock_info/metadata.json @@ -1,7 +1,7 @@ { "id": "clock_info", "name": "Clock Info Module", "shortName": "Clock Info", - "version":"0.18", + "version":"0.19", "description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)", "icon": "app.png", "type": "module", From 4595680e81dc916118ccb5f6c85a588161447aad Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Mon, 21 Jul 2025 11:23:26 -0400 Subject: [PATCH 22/27] Fixed for @gfwilliams' reviewed changes --- apps/setting/settings.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/setting/settings.js b/apps/setting/settings.js index 37944b638..777131b5e 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -1,3 +1,4 @@ + Bangle.loadWidgets(); Bangle.drawWidgets(); @@ -72,7 +73,7 @@ function resetSettings() { // clockHasWidgets: false, // Does the clock in 'clock' contain the string 'Bangle.loadWidgets' "12hour" : false, // 12 or 24 hour clock? firstDayOfWeek: 0, // 0 -> Sunday (default), 1 -> Monday - brightness: 0, // LCD brightness from 0 to 1 + brightness: 1, // LCD brightness from 0 to 1 // welcomed : undefined/true (whether welcome app should show) options: { wakeOnBTN1: true, @@ -474,12 +475,11 @@ function LCDMenu() { Object.assign(lcdMenu, { /*LANG*/'LCD Brightness': { value: settings.brightness, - min: 0, + min : BANGLEJS2 ? 0 : 0.1, max: 1, step: 0.1, onchange: v => { - settings.brightness = v || 0; - print(settings.brightness); + settings.brightness = v ?? 1; updateSettings(); Bangle.setLCDBrightness(settings.brightness); } From 74600dde8eddc8cc24e5463f32a2fa9250f3475c Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 22 Jul 2025 14:05:23 +0100 Subject: [PATCH 23/27] clockinfo: Tapping Altitude now updates the reading --- apps/clock_info/ChangeLog | 3 ++- apps/clock_info/lib.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/clock_info/ChangeLog b/apps/clock_info/ChangeLog index eef68647a..702d78bf1 100644 --- a/apps/clock_info/ChangeLog +++ b/apps/clock_info/ChangeLog @@ -17,4 +17,5 @@ 0.16: Add BLE clkinfo entry 0.17: Fix BLE icon alignment and border on some clocks 0.18: Tweak BLE icon to add gap and ensure middle of B isn't filled -0.19: Fix Altitude ClockInfo after BLE added \ No newline at end of file +0.19: Fix Altitude ClockInfo after BLE added + Tapping Altitude now updates the reading \ No newline at end of file diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index 40e989ffe..e0f48038b 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -157,6 +157,7 @@ exports.load = function() { min : 0, max : settings.maxAltitude, img : atob("GBiBAAAAAAAAAAAAAAAAAAAAAAACAAAGAAAPAAEZgAOwwAPwQAZgYAwAMBgAGBAACDAADGAABv///////wAAAAAAAAAAAAAAAAAAAA==") }), + run : function() { alt = "--"; this.emit("redraw"); altUpdateHandler(); }, show : function() { this.interval = setInterval(altUpdateHandler, 60000); alt = "--"; altUpdateHandler(); }, hide : function() { clearInterval(this.interval); delete this.interval; }, }); From 0cf8d53c52b086278fd40b26f458dab70cf98de5 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 22 Jul 2025 14:52:14 +0100 Subject: [PATCH 24/27] add clkinfodist --- apps/clkinfoclk/metadata.json | 2 +- apps/clkinfodist/ChangeLog | 1 + apps/clkinfodist/app.png | Bin 0 -> 4992 bytes apps/clkinfodist/clkinfo.js | 22 ++++++++++++++++++++++ apps/clkinfodist/icon.png | Bin 0 -> 4313 bytes apps/clkinfodist/metadata.json | 14 ++++++++++++++ apps/clkinfodist/screenshot.png | Bin 0 -> 5051 bytes 7 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 apps/clkinfodist/ChangeLog create mode 100644 apps/clkinfodist/app.png create mode 100644 apps/clkinfodist/clkinfo.js create mode 100644 apps/clkinfodist/icon.png create mode 100644 apps/clkinfodist/metadata.json create mode 100644 apps/clkinfodist/screenshot.png diff --git a/apps/clkinfoclk/metadata.json b/apps/clkinfoclk/metadata.json index 8d676d0e0..0160b59d9 100644 --- a/apps/clkinfoclk/metadata.json +++ b/apps/clkinfoclk/metadata.json @@ -5,7 +5,7 @@ "icon": "app.png", "screenshots": [{"url":"screenshot.png"}], "type": "clkinfo", - "tags": "clkinfo", + "tags": "clkinfo,clock", "supports" : ["BANGLEJS2"], "storage": [ {"name":"clkinfoclk.clkinfo.js","url":"clkinfo.js"} diff --git a/apps/clkinfodist/ChangeLog b/apps/clkinfodist/ChangeLog new file mode 100644 index 000000000..2286a7f70 --- /dev/null +++ b/apps/clkinfodist/ChangeLog @@ -0,0 +1 @@ +0.01: New App! \ No newline at end of file diff --git a/apps/clkinfodist/app.png b/apps/clkinfodist/app.png new file mode 100644 index 0000000000000000000000000000000000000000..84544fa8632d6cede926e95bb00c3564918509f7 GIT binary patch literal 4992 zcmeHLdr%YC8cz`ACCa5%y@+)QiXv>X*=(LBf|3XUg9uR(D6-jXV1+zL0tCdX;-eR_ zRI8vO)K@K9l=`S;kWxsMi_gnoi;UDVcxf-z2conpqu6^A5MkPxJI>Vq$js*Ke&_ps z=lg!=_npZmKPocR-POyLMx(jQ!=%w*jI~|PfYfgHf-c(n!dh zZ6G1iLTP9;OPe+-e!_WZ{PV6!^BgYFzfHf>Ca+8vUx z^TFN?t748YVAsiiOTQRZ7MQnHaAWSIgA0$o8<&Ap|LRtP&Gcs$=A~M#A2ocwvEWSQ zgvvbV8T~&@f&aO%{NE#7VU7Iu(;okl9Z=%+2uqi?m@iL3->WFC5~Th-H?@6d z9kS=U6~)z&CP$Y*m5Zlz&e21g&@?I4B0shLP`}0L_mlT_Up%tC!f{dISml8Qnf)E> zdTQbWg1EoT8rvECw4gJ0o5P0p4=gp6J|r5cQrgSpj&OZnKt|G&oxjFK6nN9u{!rhL z5fSrb{gvrScP?#L+$?&5ZO(nq5_DL9*zeY~s&!4+^XK%>8jLj?19$Y%XcOWoAh1})ViBR&GI6zDMKUc~15h4~CJwR~a3Ym7 zLn<E49M3D!xjgcE zLWcS4)s!at|9RV*2NDl#c^G8^>u1|d105AZrVl&~Jf>0hR)QdVQ;2Y4pac`1NviF6 z0<3{4A_dnali>6ilX=)51~%yzQA#m}JJ-r@=XdUTOD;&+s;QM%rUfj4pc%B2-HIl(v)hd*zu_cVKZbgsn8uXm0P=E;_BGbSf}9 zndJ(Z(=Sew-4Q{gOCI+X8)q50A2%ndx*hd3-DPsbOPu)A>PJV5d>p@s?70ty zHNMZFxvdkcD#te`Iad~hJP)ruzNo!r9zD$ zOmqC=kkyNdYnc7O@37TFygY?TV0^kY(YZJi2~X~euDi85!8x%giF(3n3BSbZN^H7& zbE0#%#Q$l|DygpZaMAOy#)mT0v8mD;*LwKu{_eH27xdhBjQA!4bKjEW%1~Xy<^)Eq`R~1HZ0d*J#^%XKR?9OZdUni7{dn(BnpFjJ%8uvEzr8Xb#6x^? z@%Y^xt!J9TcaN=2>DlIW>+bvF6Jc&XdomN4vXiok`qQ52Q(9U)(_5CyxpkR8Cb$yY z)^){QzF?T%T^m@mYyiUfxn?J^vf4XJj zCE54$5{{ZGqDG>kw literal 0 HcmV?d00001 diff --git a/apps/clkinfodist/clkinfo.js b/apps/clkinfodist/clkinfo.js new file mode 100644 index 000000000..93cbe9fcd --- /dev/null +++ b/apps/clkinfodist/clkinfo.js @@ -0,0 +1,22 @@ +(function() { + let strideLength = (require("Storage").readJSON("myprofile.json",1)||{}).strideLength ?? 0.79, + lastSteps = 0; + function stepUpdateHandler() { distance.emit("redraw"); } + var distance = { + name : "Distance", + get : () => { let v = (Bangle.getHealthStatus("day").steps - lastSteps)*strideLength; return { + text : require("locale").distance(v,1), + img : atob("GBiBAAMAAAeAAA/AAA/AAA/gAA/gwAfh4AfD4APD4AOH4AAH4ADj4AHjwAHhwADgAAACAAAHgAAPAAAHAAgCEBgAGD///BgAGAgAEA==") + };}, + run : function() { + lastSteps = (lastSteps>=Bangle.getHealthStatus("day").steps) ? 0 : Bangle.getHealthStatus("day").steps; + this.emit("redraw"); + }, + show : function() { Bangle.on("step", stepUpdateHandler); stepUpdateHandler(); }, + hide : function() { Bangle.removeListener("step", stepUpdateHandler); } + }; + return { + name: "Bangle", + items: [ distance ] + }; +}) diff --git a/apps/clkinfodist/icon.png b/apps/clkinfodist/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..70a6d496a973014a1555e5585930605e3c4a7e69 GIT binary patch literal 4313 zcmeHLYj6|S6_y>>PcTV{DKv4itV08&EA46}ue7!77)c-n%c!z2b}%*jv==1pvRYZP zJ2j4-7q~nEB`F@K5EDZ(j@!72O8`?-9)SdsKvSk9O@hY>G!QWHG(+fk2)(&UeqfqpNk^;zd)_f0C}#>883}4j*{VRIk)zaPN!SZh*&;I)9ns zqoR-`i@XrvAf;B~ATB2GI$i9epttbuBT#D3(E9wsgsdG*i?X}mi+>+T-m=cKeNA~; zmGkiR+TO!V(wwv{9nY0M>TqpcnC;l*JdZg)JhR@uI9$-0_~Nk>f%X}_Yrpd@?2DKYRD&9RBfuzWn}em9Op6=@On0 z>~^o)ZXZbpB+>foXKk({kKA{xvZNhGGG}Dn^|tqgD{b(`?sVtNcdmM}G`mQDv+<`> z-ObIr`W}(z7gOFjWxV*{ytMZRBhwzJyw;X?a_Hk7IWJxOS@I1>|4jVU z{K4eGQ~cS!o+bD3&hk0=5B(m!)Od7L#j=X}&nl#{zU>tLLd!#$)0>~nEG2)J<=GHx zv9zrsFJ#Pme(8J5Tf5~A-2-n|&OCML;gc16tc^)Cx{}i1d;acSFl(tpIP31(+;;QL zr`MhvT>QE3#L~o^ao!fS_C|euX>>N7w=YqEy4(U(7`p@g%bOnhe zx?8x^zW3&~WOT?~cd*mBvEeVlzW)84^L};cLMHk(dt&z$SAENZ{AoQq&Jf(=-!9)7 zefMf-_ofe0AkT_9VndUCWzQoYetG}3flF7ep2s`Ct=Db%tK8-D5ifzViwU5-$~;9R zBL?*pE7F`k7L-8U>2!GoF^OVUa|%RrRYJ%HUpvqXLjr4qmzh1NN3wI(f~!vEO6rRJ zOx3J~{5Cl1ef?~lyC``s|u*OS*chziwAq}Few!vi{FJu>G4l?OYdK7WS zgc>89pAO~8EKm9z3x_E{%LZ2~ibNU=(P&g3#r2|GWx%Xfs{u6{j79_?kZ^5Cp<+lV zoTF02IUHP=kp)Q+#1N!%QnVORY%mP^p^^B4lE*Vj9|{kv0Q4}#D9M27QA03j80!&M zoHc-CIH2$K2>Zb*HTbx&7?ByyS;K{toUs%vGwLrzD^!*e@qusS}; zij2UL+N+he;AR@jMYvqtgqSQ8j#zOcj+jk^86~Vnp2N{GRPIn%p+XF&QUP+k0C*S+ z#anqRg-{mSf|w|RM`(`65fe}68W|H|qRhk?iY2lDs*(zfjY_3r0hI;iF$QBT2ufQ4 z0gV#~&Er;tVR4itcp7J|lt#reW^)lBgfRl==_x8lu!pe-c`(iR0tG4T|_R1L>! zaRRP*7gJ4zsyMKF443POUHG0-z&PBOlDVZy*0y+Y%KzV9fL36czK0uT&A3FNzi8HN)!sC&jp#(9SCXp*M^ z#%wacFKz>y7uX98qnqKdW+2`l@-sXaKjaV)G&#tG^qrJzQmzRpFd^_{bxq1OAq6G` zo~*9_n_TI)k5gO-{0fSK!_wuXn+4#Yl}s;Q5c$scv8BtxWQri0mt2vu0&pFG`Y#kNiz3pTdtH z+Txl0kIpZiKbPWlKJ}OzO`Edkj#aF`nBE79Y ak`p{s_PwY12kr#n=-kd?#~&7~Z2T`==oa4q literal 0 HcmV?d00001 diff --git a/apps/clkinfodist/metadata.json b/apps/clkinfodist/metadata.json new file mode 100644 index 000000000..2a60547dd --- /dev/null +++ b/apps/clkinfodist/metadata.json @@ -0,0 +1,14 @@ +{ "id": "clkinfodist", + "name": "Clockinfo Distance", + "version":"0.01", + "description": "Uses the 'My Profile' app's Stride Length to calculate distance travelled based on step count. Tap to reset for measuring distances.", + "icon": "app.png", + "screenshots": [{"url":"screenshot.png"}], + "type": "clkinfo", + "tags": "clkinfo,distance,steps,outdoors,tool", + "dependencies": {"myprofile":"app"}, + "supports" : ["BANGLEJS2"], + "storage": [ + {"name":"clkinfodist.clkinfo.js","url":"clkinfo.js"} + ] +} diff --git a/apps/clkinfodist/screenshot.png b/apps/clkinfodist/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..9463e40642f80cd18c69b936c2affcf0e6e1eb26 GIT binary patch literal 5051 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 Mghk1}30ac!U#F9JhX4Qo literal 0 HcmV?d00001 From 3aa372bce17508c37d0fbdd8ef3d1f2fbdfc68cd Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 22 Jul 2025 14:52:24 +0100 Subject: [PATCH 25/27] minor tweak - hasRange not needed --- apps/clock_info/lib.js | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index e0f48038b..8f8f8c230 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -121,7 +121,6 @@ exports.load = function() { }, }, { name: "BLE", - hasRange: false, isOn: () => { const s = NRF.getSecurityStatus(); return s.advertising || s.connected; From 46bf6907a9ba618b579a2b28c6de8d055c3eccec Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 22 Jul 2025 14:52:45 +0100 Subject: [PATCH 26/27] health 0.36: Fix Distance graphs that used '1*' to remove the suffix --- apps/health/ChangeLog | 3 ++- apps/health/app.js | 2 +- apps/health/metadata.json | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/health/ChangeLog b/apps/health/ChangeLog index 167ee26df..99e2490c9 100644 --- a/apps/health/ChangeLog +++ b/apps/health/ChangeLog @@ -38,4 +38,5 @@ 0.33: Ensure readAllRecordsSince always includes the current day Speed improvements (put temporary functions in RAM where possible) 0.34: Fix readFullDatabase (was skipping first month of data) -0.35: Update boot/lib.min.js \ No newline at end of file +0.35: Update boot/lib.min.js +0.36: Fix Distance graphs that used '1*' to remove the suffix \ No newline at end of file diff --git a/apps/health/app.js b/apps/health/app.js index 6a461da2b..d3dda2e2f 100644 --- a/apps/health/app.js +++ b/apps/health/app.js @@ -32,7 +32,7 @@ function menuStepCount() { } function menuDistance() { - const distMult = 1*require("locale").distance(myprofile.strideLength, 2); // hackish: this removes the distance suffix, e.g. 'm' + const distMult = parseFloat(require("locale").distance(myprofile.strideLength, 2)); // this removes the distance suffix, e.g. 'm' E.showMenu({ "": { title:/*LANG*/"Distance" }, /*LANG*/"< Back": () => menuStepCount(), diff --git a/apps/health/metadata.json b/apps/health/metadata.json index b2ecb3e8b..36497c5ba 100644 --- a/apps/health/metadata.json +++ b/apps/health/metadata.json @@ -2,7 +2,7 @@ "id": "health", "name": "Health Tracking", "shortName": "Health", - "version": "0.35", + "version": "0.36", "description": "Logs health data and provides an app to view it", "icon": "app.png", "screenshots" : [ { "url":"screenshot.png" } ], From 169519a84ca994b6c2e988e7d90de10567999265 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 22 Jul 2025 14:53:08 +0100 Subject: [PATCH 27/27] tweak to example messages --- apps/messagegui/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/messagegui/app.js b/apps/messagegui/app.js index d3c67401d..7dcfec866 100644 --- a/apps/messagegui/app.js +++ b/apps/messagegui/app.js @@ -14,7 +14,7 @@ // a message require("messages").pushMessage({"t":"add","id":1575479849,"src":"WhatsApp","title":"My Friend","body":"Hey! How's everything going?",reply:1,negative:1}) -require("messages").pushMessage({"t":"add","id":1575479849,"src":"Skype","title":"My Friend","body":"Hey! How's everything going? This is a really really long message that is really so super long you'll have to scroll it lots and lots",positive:1,negative:1}) +require("messages").pushMessage({"t":"add","id":1575479850,"src":"Skype","title":"My Friend","body":"Hey! How's everything going? This is a really really long message that is really so super long you'll have to scroll it lots and lots",positive:1,negative:1}) require("messages").pushMessage({"t":"add","id":23232,"src":"Skype","title":"Mr. Bobby McBobFace","body":"Boopedy-boop",positive:1,negative:1}) require("messages").pushMessage({"t":"add","id":23233,"src":"Skype","title":"Thyttan test","body":"Nummerplåtsbelysning trodo",positive:1,negative:1}) require("messages").pushMessage({"t":"add","id":23234,"src":"Skype","title":"Thyttan test 2","body":"Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo",positive:1,negative:1})