From c54a6dbaaa503f0768c3006c6d241143326d1b2c Mon Sep 17 00:00:00 2001 From: "Marko.Kl.Berkenbusch@gmail.com" Date: Sat, 28 May 2022 11:26:12 -0400 Subject: [PATCH 001/107] Add lightning mode --- apps/f9lander/ChangeLog | 1 + apps/f9lander/app.js | 34 +++++++++++++++++++++++++++++++--- apps/f9lander/metadata.json | 3 ++- apps/f9lander/settings.js | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 apps/f9lander/settings.js diff --git a/apps/f9lander/ChangeLog b/apps/f9lander/ChangeLog index 5560f00bc..a13f2a313 100644 --- a/apps/f9lander/ChangeLog +++ b/apps/f9lander/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Add lightning diff --git a/apps/f9lander/app.js b/apps/f9lander/app.js index 7e52104c0..2f17a5bd5 100644 --- a/apps/f9lander/app.js +++ b/apps/f9lander/app.js @@ -46,6 +46,9 @@ var booster = { x : g.getWidth()/4 + Math.random()*g.getWidth()/2, var exploded = false; var nExplosions = 0; var landed = false; +var lightning = 0; + +var settings = require("Storage").readJSON('f9settings.json', 1) || {}; const gravity = 4; const dt = 0.1; @@ -61,18 +64,40 @@ function flameImageGen (throttle) { function drawFalcon(x, y, throttle, angle) { g.setColor(1, 1, 1).drawImage(falcon9, x, y, {rotate:angle}); - if (throttle>0) { + if (throttle>0 || lightning>0) { var flameImg = flameImageGen(throttle); var r = falcon9.height/2 + flameImg.height/2-1; var xoffs = -Math.sin(angle)*r; var yoffs = Math.cos(angle)*r; if (Math.random()>0.7) g.setColor(1, 0.5, 0); else g.setColor(1, 1, 0); - g.drawImage(flameImg, x+xoffs, y+yoffs, {rotate:angle}); + if (throttle>0) g.drawImage(flameImg, x+xoffs, y+yoffs, {rotate:angle}); + if (lightning>1 && lightning<30) { + for (var i=0; i<6; ++i) { + var r = Math.random()*6; + var x = Math.random()*5 - xoffs; + var y = Math.random()*5 - yoffs; + g.setColor(1, Math.random()*0.5+0.5, 0).fillCircle(booster.x+x, booster.y+y, r); + } + } } } +function drawLightning() { + var c = {x:cloudOffs+50, y:30}; + var dx = c.x-booster.x; + var dy = c.y-booster.y; + var m1 = {x:booster.x+0.6*dx+Math.random()*20, y:booster.y+0.6*dy+Math.random()*10}; + var m2 = {x:booster.x+0.4*dx+Math.random()*20, y:booster.y+0.4*dy+Math.random()*10}; + g.setColor(1, 1, 1).drawLine(c.x, c.y, m1.x, m1.y).drawLine(m1.x, m1.y, m2.x, m2.y).drawLine(m2.x, m2.y, booster.x, booster.y); +} + function drawBG() { + if (lightning==1) { + g.setBgColor(1, 1, 1).clear(); + Bangle.buzz(200); + return; + } g.setBgColor(0.2, 0.2, 1).clear(); g.setColor(0, 0, 1).fillRect(0, g.getHeight()-oceanHeight, g.getWidth()-1, g.getHeight()-1); g.setColor(0.5, 0.5, 1).fillCircle(cloudOffs+34, 30, 15).fillCircle(cloudOffs+60, 35, 20).fillCircle(cloudOffs+75, 20, 10); @@ -88,6 +113,7 @@ function renderScreen(input) { drawBG(); showFuel(); drawFalcon(booster.x, booster.y, Math.floor(input.throttle*12), input.angle); + if (lightning>1 && lightning<6) drawLightning(); } function getInputs() { @@ -97,6 +123,7 @@ function getInputs() { if (t > 1) t = 1; if (t < 0) t = 0; if (booster.fuel<=0) t = 0; + if (lightning>0 && lightning<20) t = 0; return {throttle: t, angle: a}; } @@ -121,7 +148,6 @@ function gameStep() { else { var input = getInputs(); if (booster.y >= targetY) { -// console.log(booster.x + " " + booster.y + " " + booster.vy + " " + droneX + " " + input.angle); if (Math.abs(booster.x-droneX-droneShip.width/2)40) && Math.random()>0.98) lightning = 1; booster.x += booster.vx*dt; booster.y += booster.vy*dt; booster.vy += gravity*dt; diff --git a/apps/f9lander/metadata.json b/apps/f9lander/metadata.json index 75c6a0164..5e7031b45 100644 --- a/apps/f9lander/metadata.json +++ b/apps/f9lander/metadata.json @@ -1,7 +1,7 @@ { "id": "f9lander", "name": "Falcon9 Lander", "shortName":"F9lander", - "version":"0.01", + "version":"0.02", "description": "Land a rocket booster", "icon": "f9lander.png", "screenshots" : [ { "url":"f9lander_screenshot1.png" }, { "url":"f9lander_screenshot2.png" }, { "url":"f9lander_screenshot3.png" }], @@ -11,5 +11,6 @@ "storage": [ {"name":"f9lander.app.js","url":"app.js"}, {"name":"f9lander.img","url":"app-icon.js","evaluate":true} + {"name":"f9lander.settings.js", "url":"settings.js"} ] } diff --git a/apps/f9lander/settings.js b/apps/f9lander/settings.js new file mode 100644 index 000000000..0f9fba302 --- /dev/null +++ b/apps/f9lander/settings.js @@ -0,0 +1,36 @@ +// This file should contain exactly one function, which shows the app's settings +/** + * @param {function} back Use back() to return to settings menu + */ +const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off"; +(function(back) { + const SETTINGS_FILE = 'f9settings.json' + // initialize with default settings... + let settings = { + 'lightning': false, + } + // ...and overwrite them with any saved values + // This way saved values are preserved if a new version adds more settings + const storage = require('Storage') + const saved = storage.readJSON(SETTINGS_FILE, 1) || {} + for (const key in saved) { + settings[key] = saved[key]; + } + // creates a function to safe a specific setting, e.g. save('color')(1) + function save(key) { + return function (value) { + settings[key] = value; + storage.write(SETTINGS_FILE, settings); + } + } + const menu = { + '': { 'title': 'OpenWind' }, + '< Back': back, + 'Lightning': { + value: settings.lightning, + format: boolFormat, + onchange: save('lightning'), + } + } + E.showMenu(menu); +}) From 3c121a808f845906ce4732662bb381fdeb09060f Mon Sep 17 00:00:00 2001 From: "Marko.Kl.Berkenbusch@gmail.com" Date: Sat, 28 May 2022 11:29:51 -0400 Subject: [PATCH 002/107] Fix missing comma --- apps/f9lander/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/f9lander/metadata.json b/apps/f9lander/metadata.json index 5e7031b45..1db777099 100644 --- a/apps/f9lander/metadata.json +++ b/apps/f9lander/metadata.json @@ -10,7 +10,7 @@ "supports" : ["BANGLEJS", "BANGLEJS2"], "storage": [ {"name":"f9lander.app.js","url":"app.js"}, - {"name":"f9lander.img","url":"app-icon.js","evaluate":true} + {"name":"f9lander.img","url":"app-icon.js","evaluate":true}, {"name":"f9lander.settings.js", "url":"settings.js"} ] } From 74787f7b5ef5f05cad87d7efc268e944878469e3 Mon Sep 17 00:00:00 2001 From: storm64 Date: Mon, 12 Sep 2022 23:15:39 +0200 Subject: [PATCH 003/107] [sleeplog] Add option for onChange functions Execute functions in `sleeplog.onChange[fn({timestamp, status, consecutive})]` on a status change --- apps/sleeplog/ChangeLog | 1 + apps/sleeplog/boot.js | 23 +++++++++++++++++++++-- apps/sleeplog/metadata.json | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/sleeplog/ChangeLog b/apps/sleeplog/ChangeLog index a0698d471..bff278e60 100644 --- a/apps/sleeplog/ChangeLog +++ b/apps/sleeplog/ChangeLog @@ -7,3 +7,4 @@ 0.10: Complete rework off this app! 0.10beta: Add interface.html to view saved log data, add "View log" function for debugging log, send data for gadgetbridge, change caching for global getStats 0.11: Prevent module not found error +0.12: Execute functions in `sleeplog.onChange[fn({timestamp, status, consecutive})]` on a status change \ No newline at end of file diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index c1f8a2d2d..f4d9dff46 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -258,6 +258,18 @@ if (sleeplog.conf.enabled) { // check if the status has changed if (data.status !== this.status || data.consecutive !== this.consecutive) { + // check for onChange functions + if ((this.onChange || []).length) { + this.onChange.forEach(fn => { + // setup timeouts to start onChange functions if fn is a function + if (typeof fn === "function") setTimeout(fn, 100, { + timestamp: new Date(data.timestamp), + status: data.status === this.status ? undefined : data.status, + consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive + }); + }); + } + // append status this.appendStatus(data.timestamp, data.status, data.consecutive); @@ -268,7 +280,7 @@ if (sleeplog.conf.enabled) { // reset saveUpToDate status delete this.info.saveUpToDate; } - + // send status to gadgetbridge var gb_kinds = "unknown,not_worn,activity,light_sleep,deep_sleep"; Bluetooth.println(JSON.stringify({ @@ -319,7 +331,14 @@ if (sleeplog.conf.enabled) { } // return stats cache return this.statsCache; - } + }, + + // define array for functions to execute after a status change (changes had hapened 10min earlier) + // changed values will be passed as object with the following properties: + // timestamp: as date object, + // status: if changed 0-4 else undefined, + // consecutive: if changed 0-2 else undefined + onChange: [] }, sleeplog); // initial starting diff --git a/apps/sleeplog/metadata.json b/apps/sleeplog/metadata.json index f6ce661e8..353476446 100644 --- a/apps/sleeplog/metadata.json +++ b/apps/sleeplog/metadata.json @@ -2,7 +2,7 @@ "id":"sleeplog", "name":"Sleep Log", "shortName": "SleepLog", - "version": "0.11", + "version": "0.12", "description": "Log and view your sleeping habits. This app is using the built in movement calculation.", "icon": "app.png", "type": "app", From 99663e4583ae34cacffe0e4d72d749f4e1c9741a Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 10 Nov 2022 00:33:51 +0100 Subject: [PATCH 004/107] [sleeplog] onChange: + previous values and readme --- apps/sleeplog/README.md | 25 +++++++++++++++++++++++-- apps/sleeplog/boot.js | 10 ++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 45aeb316b..7f122add3 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -87,7 +87,7 @@ Available through the App Loader when your watch is connected. Deletes the logfile from the watch. __Please backup your data first!__ --- -### Timestamps and files +### Timestamps and Files --- 1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as Bangle timestamps: @@ -110,8 +110,10 @@ Available through the App Loader when your watch is connected. --- -### Access statistics (developer information) +### Developer Information --- + +#### Access statistics - Last Asleep Time [Date]: `Date(sleeplog.awakeSince)` - Last Awake Duration [ms]: @@ -149,6 +151,25 @@ Available through the App Loader when your watch is connected. require("sleeplog").getStats(0, 0, require("sleeplog").readLog()); ``` +#### Add functions triggered by status changes +With the following code it is possible to add functions that will be called on status changes. +``` +// first ensure that the sleeplog object is available +if (typeof (global.sleeplog || {}).onChange === "object") { + // then add your function to the onChange-array + sleeplog.onChange.push( function(data) { print(data); } ); +} +``` +The passed data object has the following properties: +- timestamp: of the status change as date object, + (should be around 10min. before "now", the actual call of the function) +- status: if changed the value of the new status (0-4) else undefined, + (0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep) +- consecutive: if changed the value of the new status (0-2) else undefined, + (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) +- prevStatus: value of the previous status (0-4), +- prevConsecutive: value of the previous status (0-2) + --- ### Worth Mentioning diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index f4d9dff46..3d4685ea4 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -265,7 +265,9 @@ if (sleeplog.conf.enabled) { if (typeof fn === "function") setTimeout(fn, 100, { timestamp: new Date(data.timestamp), status: data.status === this.status ? undefined : data.status, - consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive + consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, + prevStatus: this.status, + prevConsecutive: this.consecutive }); }); } @@ -337,7 +339,11 @@ if (sleeplog.conf.enabled) { // changed values will be passed as object with the following properties: // timestamp: as date object, // status: if changed 0-4 else undefined, - // consecutive: if changed 0-2 else undefined + // (0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep) + // consecutive: if changed 0-2 else undefined, + // (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) + // prevStatus: value of the previous status 0-4, + // prevConsecutive: value of the previous status 0-2 onChange: [] }, sleeplog); From 81aece08ee34867530cf621f091654ab312d6dc7 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 10 Nov 2022 01:27:37 +0100 Subject: [PATCH 005/107] [sleeplog] Correct README.md --- apps/sleeplog/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 7f122add3..1c14865ae 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -161,11 +161,11 @@ if (typeof (global.sleeplog || {}).onChange === "object") { } ``` The passed data object has the following properties: -- timestamp: of the status change as date object, +- timestamp: of the status change as date object, (should be around 10min. before "now", the actual call of the function) -- status: if changed the value of the new status (0-4) else undefined, +- status: if changed the value of the new status (0-4) else undefined, (0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep) -- consecutive: if changed the value of the new status (0-2) else undefined, +- consecutive: if changed the value of the new status (0-2) else undefined, (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) - prevStatus: value of the previous status (0-4), - prevConsecutive: value of the previous status (0-2) From 58330df11c95aaac8ed880be37c2d3cff6d43fac Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 10 Nov 2022 17:04:54 +0100 Subject: [PATCH 006/107] [sleeplog] Change .onChange from array to object --- apps/sleeplog/ChangeLog | 2 +- apps/sleeplog/README.md | 110 ++++++++++++++++++++-------------------- apps/sleeplog/boot.js | 29 ++++++----- 3 files changed, 71 insertions(+), 70 deletions(-) diff --git a/apps/sleeplog/ChangeLog b/apps/sleeplog/ChangeLog index bff278e60..66eeccfc2 100644 --- a/apps/sleeplog/ChangeLog +++ b/apps/sleeplog/ChangeLog @@ -7,4 +7,4 @@ 0.10: Complete rework off this app! 0.10beta: Add interface.html to view saved log data, add "View log" function for debugging log, send data for gadgetbridge, change caching for global getStats 0.11: Prevent module not found error -0.12: Execute functions in `sleeplog.onChange[fn({timestamp, status, consecutive})]` on a status change \ No newline at end of file +0.12: Execute functions in `sleeplog.onChange` object on a status change \ No newline at end of file diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 1c14865ae..00c07ef4a 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -1,6 +1,6 @@ # Sleep Log -This app logs and displays the following states: +This app logs and displays the following states: - sleepling status: _unknown, not worn, awake, light sleep, deep sleep_ - consecutive sleep status: _unknown, not consecutive, consecutive_ @@ -20,55 +20,55 @@ Logfiles are not removed on un-/reinstall to prevent data loss. --- #### On the main app screen: - - __swipe left & right__ + - __swipe left & right__ to change the displayed day - - __touch the "title"__ (e.g. `Night to Fri 20/05/2022`) + - __touch the "title"__ (e.g. `Night to Fri 20/05/2022`) to enter day selection prompt - - __touch the info area__ - to change the displayed information + - __touch the info area__ + to change the displayed information (by default: consecutive & true sleeping) - - __touch the wrench__ (upper right corner) + - __touch the wrench__ (upper right corner) to enter the settings - - __use back button widget__ (upper left corner) + - __use back button widget__ (upper left corner) exit the app #### Inside the settings: - - __Thresholds__ submenu + - __Thresholds__ submenu Changes take effect from now on, not retrospective! - - __Max Awake__ | maximal awake duration + - __Max Awake__ | maximal awake duration _10min_ / _20min_ / ... / __60min__ / ... / _120min_ - - __Min Consecutive__ | minimal consecutive sleep duration + - __Min Consecutive__ | minimal consecutive sleep duration _10min_ / _20min_ / ... / __30min__ / ... / _120min_ - - __Deep Sleep__ | deep sleep threshold + - __Deep Sleep__ | deep sleep threshold _30_ / _31_ / ... / __100__ / ... / _200_ - - __Light Sleep__ | light sleep threshold - _100_ / _110_ / ... / __200__ / ... / _400_ + - __Light Sleep__ | light sleep threshold + _100_ / _110_ / ... / __200__ / ... / _400_ - __Reset to Default__ | reset to bold values above - - __BreakToD__ | time of day to break view + - __BreakToD__ | time of day to break view _0:00_ / _1:00_ / ... / __12:00__ / ... / _23:00_ - - __App Timeout__ | app specific lock timeout + - __App Timeout__ | app specific lock timeout __0s__ / _10s_ / ... / _120s_ - - __Enabled__ | completely en-/disables the background service + - __Enabled__ | completely en-/disables the background service __on__ / _off_ - - __Debugging__ submenu - - __View log__ | display logfile data - Select the logfile by its starting time. - Thresholds are shown as line with its value. - - __swipe left & right__ + - __Debugging__ submenu + - __View log__ | display logfile data + Select the logfile by its starting time. + Thresholds are shown as line with its value. + - __swipe left & right__ to change displayed duration - - __swipe up & down__ + - __swipe up & down__ to change displayed value range - - __touch the graph__ + - __touch the graph__ to change between light & dark colors - - __use back button widget__ (upper left corner) + - __use back button widget__ (upper left corner) to go back to the logfile selection - - __Enabled__ | en-/disables debugging + - __Enabled__ | en-/disables debugging _on_ / __off__ - - __write File__ | toggles if a logfile is written + - __write File__ | toggles if a logfile is written _on_ / __off__ - - __Duration__ | duration for writing into logfile - _1h_ / _2h_ / ... / __12h__ / _96_ - - The following data is logged to a csv-file: + - __Duration__ | duration for writing into logfile + _1h_ / _2h_ / ... / __12h__ / _96_ + - The following data is logged to a csv-file: _timestamp_ (in days since 1900-01-01 00:00 UTC used by office software) _, movement, status, consecutive, asleepSince, awakeSince, bpm, bpmConfidence_ @@ -78,34 +78,34 @@ Logfiles are not removed on un-/reinstall to prevent data loss. Available through the App Loader when your watch is connected. -- __view data__ +- __view data__ Display the data to each timestamp in a table. -- __save csv-file__ - Download a csv-file with the data to each timestamp. - The time format is chooseable beneath the file list. -- __delete file__ +- __save csv-file__ + Download a csv-file with the data to each timestamp. + The time format is chooseable beneath the file list. +- __delete file__ Deletes the logfile from the watch. __Please backup your data first!__ --- ### Timestamps and Files --- -1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as Bangle timestamps: +1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as Bangle timestamps: seconds since 1970-01-01 00:00 UTC -2. internally used and logged (to `sleeplog.log (StorageFile)`) is within the highest available resolution: +2. internally used and logged (to `sleeplog.log (StorageFile)`) is within the highest available resolution: 10 minutes since 1970-01-01 00:00 UTC (`Bangle / (10 * 60 * 1000)`) 3. debug .csv file ID (`sleeplog_123456.csv`) has a hourly resolution: hours since 1970-01-01 00:00 UTC (`Bangle / (60 * 60 * 1000)`) -4. logged timestamps inside the debug .csv file are formatted for office calculation software: +4. logged timestamps inside the debug .csv file are formatted for office calculation software: days since 1900-01-01 00:00 UTC (`Bangle / (24 * 60 * 60 * 1000) + 25569`) -5. every 14 days the `sleeplog.log (StorageFile)` is reduced and old entries are moved into separat files for each fortnight (`sleeplog_1234.log`) but still accessible though the app: +5. every 14 days the `sleeplog.log (StorageFile)` is reduced and old entries are moved into separat files for each fortnight (`sleeplog_1234.log`) but still accessible though the app: fortnights since 1970-01-04 12:00 UTC (converted with `require("sleeplog").msToFn(Bangle)` and `require("sleeplog").fnToMs(fortnight)`) -- __Logfiles from before 0.10:__ +- __Logfiles from before 0.10:__ timestamps and sleeping status of old logfiles are automatically converted on your first consecutive sleep or manually by `require("sleeplog").convertOldLog()` -- __View logged data:__ - if you'd like to view your logged data in the IDE, you can access it with `require("sleeplog").printLog(since, until)` or `require("sleeplog").readLog(since, until)` to view the raw data +- __View logged data:__ + if you'd like to view your logged data in the IDE, you can access it with `require("sleeplog").printLog(since, until)` or `require("sleeplog").readLog(since, until)` to view the raw data since & until in Bangle timestamp, e.g. `require("sleeplog").printLog(Date()-24*60*60*1000, Date())` for the last 24h @@ -114,14 +114,14 @@ Available through the App Loader when your watch is connected. --- #### Access statistics -- Last Asleep Time [Date]: +- Last Asleep Time [Date]: `Date(sleeplog.awakeSince)` -- Last Awake Duration [ms]: +- Last Awake Duration [ms]: `Date() - sleeplog.awakeSince` -- Last Statistics [object]: +- Last Statistics [object]: ``` // get stats of the last night (period as displayed inside the app) - // as this might be the mostly used function the data is cached inside the global object + // as this might be the mostly used function the data is cached inside the global object sleeplog.getStats(); // get stats of the last 24h @@ -132,20 +132,20 @@ Available through the App Loader when your watch is connected. ={ calculatedAt: 1653123553810, deepSleep: 250, lightSleep: 150, awakeSleep: 10, consecSleep: 320, awakeTime: 1030, notWornTime: 0, unknownTime: 0, logDuration: 1440, firstDate: 1653036600000, lastDate: 1653111600000 } - + // to get the start of a period defined by "Break TOD" of any date var startOfBreak = require("sleeplog").getLastBreak(); // same as var startOfBreak = require("sleeplog").getLastBreak(Date.now()); // output as date =Date: Sat May 21 2022 12:00:00 GMT+0200 - + // get stats of this period as displayed inside the app require("sleeplog").getStats(require("sleeplog").getLastBreak(), 24*60*60*1000); // or any other day require("sleeplog").getStats(require("sleeplog").getLastBreak(Date(2022,4,10)), 24*60*60*1000); ``` -- Total Statistics [object]: +- Total Statistics [object]: ``` // use with caution, may take a long time ! require("sleeplog").getStats(0, 0, require("sleeplog").readLog()); @@ -154,18 +154,18 @@ Available through the App Loader when your watch is connected. #### Add functions triggered by status changes With the following code it is possible to add functions that will be called on status changes. ``` -// first ensure that the sleeplog object is available +// first ensure that the sleeplog onChange object is available if (typeof (global.sleeplog || {}).onChange === "object") { - // then add your function to the onChange-array - sleeplog.onChange.push( function(data) { print(data); } ); + // then add your function to the onChange object + sleeplog.onChange["my app name"] = function(data) { print(data); }; } ``` The passed data object has the following properties: -- timestamp: of the status change as date object, +- timestamp: of the status change as date object, (should be around 10min. before "now", the actual call of the function) -- status: if changed the value of the new status (0-4) else undefined, +- status: if changed the value of the new status (0-4) else undefined, (0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep) -- consecutive: if changed the value of the new status (0-2) else undefined, +- consecutive: if changed the value of the new status (0-2) else undefined, (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) - prevStatus: value of the previous status (0-4), - prevConsecutive: value of the previous status (0-2) @@ -177,7 +177,7 @@ The passed data object has the following properties: #### To do list - Check translations. - Add more functionallities to interface.html. -- Enable recieving data on the Gadgetbridge side + testing. +- Enable recieving data on the Gadgetbridge side + testing. __Help appreciated!__ #### Requests, Bugs and Feedback diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 3d4685ea4..33452ff91 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -124,7 +124,7 @@ if (sleeplog.conf.enabled) { if (!sleeplog.info.saveUpToDate || force) { // save status, consecutive status and info timestamps to restore on reload var save = [sleeplog.info.lastCheck, sleeplog.info.awakeSince, sleeplog.info.asleepSince]; - // add debuging status if active + // add debuging status if active if (sleeplog.debug) save.push(sleeplog.debug.writeUntil, sleeplog.debug.fileid); // stringify entries @@ -258,19 +258,20 @@ if (sleeplog.conf.enabled) { // check if the status has changed if (data.status !== this.status || data.consecutive !== this.consecutive) { - // check for onChange functions - if ((this.onChange || []).length) { - this.onChange.forEach(fn => { - // setup timeouts to start onChange functions if fn is a function - if (typeof fn === "function") setTimeout(fn, 100, { - timestamp: new Date(data.timestamp), - status: data.status === this.status ? undefined : data.status, - consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, - prevStatus: this.status, - prevConsecutive: this.consecutive - }); + // read and check for onChange functions + var onChange = Object.keys(this.onChange) || []; + if (onChange.length) onChange.forEach(key => { + // read function to key + var fn = this.onChange[key]; + // setup timeouts to start onChange functions if fn is a function + if (typeof fn === "function") setTimeout(fn, 100, { + timestamp: new Date(data.timestamp), + status: data.status === this.status ? undefined : data.status, + consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, + prevStatus: this.status, + prevConsecutive: this.consecutive }); - } + }); // append status this.appendStatus(data.timestamp, data.status, data.consecutive); @@ -335,7 +336,7 @@ if (sleeplog.conf.enabled) { return this.statsCache; }, - // define array for functions to execute after a status change (changes had hapened 10min earlier) + // define object for functions to execute after a status change (changes had hapened 10min earlier) // changed values will be passed as object with the following properties: // timestamp: as date object, // status: if changed 0-4 else undefined, From 56e69cc32cdbd918dcfbc1378fe574bf00a05e01 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 10 Nov 2022 17:16:44 +0100 Subject: [PATCH 007/107] [sleeplog] Initialise .onChange as object --- apps/sleeplog/boot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 33452ff91..6a9c7c6e6 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -345,7 +345,7 @@ if (sleeplog.conf.enabled) { // (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) // prevStatus: value of the previous status 0-4, // prevConsecutive: value of the previous status 0-2 - onChange: [] + onChange: {} }, sleeplog); // initial starting From 9105c94e1b8f002bd9b6cff08ff9db3f9239a598 Mon Sep 17 00:00:00 2001 From: storm64 Date: Wed, 16 Nov 2022 22:46:49 +0100 Subject: [PATCH 008/107] [sleeplog] Replace onChange with trigger object --- apps/sleeplog/README.md | 18 ++++++++----- apps/sleeplog/boot.js | 58 ++++++++++++++++++++++------------------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 00c07ef4a..9e32ad37f 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -151,13 +151,19 @@ Available through the App Loader when your watch is connected. require("sleeplog").getStats(0, 0, require("sleeplog").readLog()); ``` -#### Add functions triggered by status changes -With the following code it is possible to add functions that will be called on status changes. +#### Add functions triggered by status changes or inside a specified time period +With the following code it is possible to add functions that will be called every 10 minutes after new movement data when meeting the specified parameters on each : ``` -// first ensure that the sleeplog onChange object is available -if (typeof (global.sleeplog || {}).onChange === "object") { - // then add your function to the onChange object - sleeplog.onChange["my app name"] = function(data) { print(data); }; +// first ensure that the sleeplog trigger object is available (sleeplog is enabled) +if (typeof (global.sleeplog || {}).trigger === "object") { + // then add your parameters with the function to call as object into the trigger object + sleeplog.trigger["my app name"] = { + onChange: false, // false as default, if true call fn only on a status change + from: 0, // 0 as default, in ms, first time fn will be called + to: 24*60*60*1000, // 24h as default, in ms, last time fn will be called + // reference time to from & to is rounded to full minutes + fn: function(data) { print(data); } // function to be executed + }; } ``` The passed data object has the following properties: diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 6a9c7c6e6..d787b8dff 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -253,26 +253,38 @@ if (sleeplog.conf.enabled) { } } + // check if the status has changed + var changed = data.status !== this.status || data.consecutive !== this.consecutive; + + // read and check trigger entries + var triggers = Object.keys(this.trigger) || []; + if (triggers.length) { + // calculate time from timestamp in ms on full minutes + var time = data.timestamp; + time = ((time.getHours() * 60) + time.getMinutes() * 60) * 1000; + // go through all triggers + triggers.forEach(key => { + // read entry to key + var entry = this.trigger[key]; + // check if the event matches the entries requirements + if (typeof entry.fn === "function" && (changed || !entry.onChange) && + (entry.from || 0) <= time && (entry.to || 24 * 60 * 60 * 1000) >= time) + // and call afterwards with status data + setTimeout(fn, 100, { + timestamp: new Date(data.timestamp), + status: data.status === this.status ? undefined : data.status, + consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, + prevStatus: this.status, + prevConsecutive: this.consecutive + }); + }); + } + // cache change into a known consecutive state var changeIntoConsec = data.consecutive; - // check if the status has changed - if (data.status !== this.status || data.consecutive !== this.consecutive) { - // read and check for onChange functions - var onChange = Object.keys(this.onChange) || []; - if (onChange.length) onChange.forEach(key => { - // read function to key - var fn = this.onChange[key]; - // setup timeouts to start onChange functions if fn is a function - if (typeof fn === "function") setTimeout(fn, 100, { - timestamp: new Date(data.timestamp), - status: data.status === this.status ? undefined : data.status, - consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, - prevStatus: this.status, - prevConsecutive: this.consecutive - }); - }); - + // actions on a status change + if (changed) { // append status this.appendStatus(data.timestamp, data.status, data.consecutive); @@ -336,16 +348,8 @@ if (sleeplog.conf.enabled) { return this.statsCache; }, - // define object for functions to execute after a status change (changes had hapened 10min earlier) - // changed values will be passed as object with the following properties: - // timestamp: as date object, - // status: if changed 0-4 else undefined, - // (0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep) - // consecutive: if changed 0-2 else undefined, - // (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) - // prevStatus: value of the previous status 0-4, - // prevConsecutive: value of the previous status 0-2 - onChange: {} + // define trigger object + trigger: {} }, sleeplog); // initial starting From a2fd46e35ccc8412b807b884fb54fe2605862b8e Mon Sep 17 00:00:00 2001 From: storm64 Date: Wed, 16 Nov 2022 23:26:23 +0100 Subject: [PATCH 009/107] [sleeplog] Correct calculation of time in trigger --- apps/sleeplog/boot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index d787b8dff..17388da44 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -261,7 +261,7 @@ if (sleeplog.conf.enabled) { if (triggers.length) { // calculate time from timestamp in ms on full minutes var time = data.timestamp; - time = ((time.getHours() * 60) + time.getMinutes() * 60) * 1000; + time = (time.getHours() * 60 + time.getMinutes()) * 60 * 1000; // go through all triggers triggers.forEach(key => { // read entry to key From abb719319f871363da0394ae4f6c9007c8a968a4 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 17 Nov 2022 08:36:22 +0100 Subject: [PATCH 010/107] [sleeplog] Correct time as date for trigger --- apps/sleeplog/boot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 17388da44..75383c7df 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -260,7 +260,7 @@ if (sleeplog.conf.enabled) { var triggers = Object.keys(this.trigger) || []; if (triggers.length) { // calculate time from timestamp in ms on full minutes - var time = data.timestamp; + var time = new Date(data.timestamp); time = (time.getHours() * 60 + time.getMinutes()) * 60 * 1000; // go through all triggers triggers.forEach(key => { From 528716322a0656e9530d657b7960912bb8a92b51 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 17 Nov 2022 09:26:51 +0100 Subject: [PATCH 011/107] [sleeplog] Correct fn call for trigger --- apps/sleeplog/boot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 75383c7df..17e5fe70a 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -270,7 +270,7 @@ if (sleeplog.conf.enabled) { if (typeof entry.fn === "function" && (changed || !entry.onChange) && (entry.from || 0) <= time && (entry.to || 24 * 60 * 60 * 1000) >= time) // and call afterwards with status data - setTimeout(fn, 100, { + setTimeout(entry.fn, 100, { timestamp: new Date(data.timestamp), status: data.status === this.status ? undefined : data.status, consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, From dfe57c0e77f2095c823fbb77a6ac5500119ad57e Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 17 Nov 2022 10:04:57 +0100 Subject: [PATCH 012/107] [sleeplog] Correct generation of time in trigger --- apps/sleeplog/boot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 17e5fe70a..2ab8f9eff 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -260,7 +260,7 @@ if (sleeplog.conf.enabled) { var triggers = Object.keys(this.trigger) || []; if (triggers.length) { // calculate time from timestamp in ms on full minutes - var time = new Date(data.timestamp); + var time = new Date(); time = (time.getHours() * 60 + time.getMinutes()) * 60 * 1000; // go through all triggers triggers.forEach(key => { From 855a2c4263d2cfa5a22701707d1db82b18623fd9 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 17 Nov 2022 10:26:52 +0100 Subject: [PATCH 013/107] [sleeplog] Switch unchanged data behav. in trigger --- apps/sleeplog/README.md | 8 ++++---- apps/sleeplog/boot.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 9e32ad37f..66d26e7f0 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -169,12 +169,12 @@ if (typeof (global.sleeplog || {}).trigger === "object") { The passed data object has the following properties: - timestamp: of the status change as date object, (should be around 10min. before "now", the actual call of the function) -- status: if changed the value of the new status (0-4) else undefined, +- status: value of the new status (0-4), (0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep) -- consecutive: if changed the value of the new status (0-2) else undefined, +- consecutive: value of the new status (0-2), (0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep) -- prevStatus: value of the previous status (0-4), -- prevConsecutive: value of the previous status (0-2) +- prevStatus: if changed the value of the previous status (0-4) else undefined, +- prevConsecutive: if changed the value of the previous status (0-2) else undefined --- diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index 2ab8f9eff..78fd23450 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -272,10 +272,10 @@ if (sleeplog.conf.enabled) { // and call afterwards with status data setTimeout(entry.fn, 100, { timestamp: new Date(data.timestamp), - status: data.status === this.status ? undefined : data.status, - consecutive: data.consecutive === this.consecutive ? undefined : data.consecutive, - prevStatus: this.status, - prevConsecutive: this.consecutive + status: data.status, + consecutive: data.consecutive, + prevStatus: data.status === this.status ? undefined : this.status, + prevConsecutive: data.consecutive === this.consecutive ? undefined : this.consecutive }); }); } From 361efb6c25d55e50f0b379dd2f68ab4079d2240d Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 17 Nov 2022 18:12:59 +0100 Subject: [PATCH 014/107] [sleeplog] Add more details to README.md --- apps/sleeplog/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 66d26e7f0..4310ead58 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -6,6 +6,14 @@ This app logs and displays the following states: It is using the built in movement calculation to decide your sleeping state. While charging it is assumed that you are not wearing the watch and if the status changes to _deep sleep_ the internal heartrate sensor is used to detect if you are wearing the watch. +#### Explanations +* __Detection of Sleep__ + The movement value of bangle's build in health event that is triggered every 10 minutes is checked against the thresholds for light and deep sleep. If the measured movement is lower or eaqual to the __Deep Sleep__-threshold a deep sleep phase is detected for the last 10 minutes. If the threshold is exceeded but not the __Light Sleep__-threshold than the last timeperiod is detected as light sleep phase. On exceeding even this threshold it is assumed that you were awake. +* __True Sleep__ + The true sleep value is a simple addition of all registert sleeping periods. +* __Consecutive Sleep__ + In addition the consecutive sleep value tries to predict the complete time you were asleep, even the very light sleeping periods when an awake period is detected based on the registered movements. All periods after a sleeping period will be summarized until the first following non sleeping period that is longer then the maximal awake duration (__Max Awake__). If this sum is lower than the minimal consecutive sleep duration (__Min Consecutive__) it is not considered, otherwise it will be added to the consecutive sleep value. + Logfiles are not removed on un-/reinstall to prevent data loss. | Filename (* _example_) | Content | Removeable in | From d5e68798979d54b9ad1c8cdd6d33ea21d49021b9 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sun, 6 Nov 2022 12:31:13 +0100 Subject: [PATCH 015/107] add handling of gps events --- apps/android/boot.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/android/boot.js b/apps/android/boot.js index 0d1edae99..8bcc1ba0d 100644 --- a/apps/android/boot.js +++ b/apps/android/boot.js @@ -130,6 +130,13 @@ request.j(event.err); //r = reJect function else request.r(event); //r = resolve function + }, + "gps": function() { + delete event.t; + event.satellites = NaN; + event.course = NaN; + event.fix = 1; + Bangle.emit('gps', event); } }; var h = HANDLERS[event.t]; From 31bed21581242731de263bad8b5f43becbaccb3a Mon Sep 17 00:00:00 2001 From: Lukas Date: Sun, 6 Nov 2022 13:41:49 +0100 Subject: [PATCH 016/107] add setting to set overwrite of internal gps --- apps/android/boot.js | 28 ++++++++++++++++++++++++++++ apps/android/settings.js | 13 +++++++++++++ 2 files changed, 41 insertions(+) diff --git a/apps/android/boot.js b/apps/android/boot.js index 8bcc1ba0d..a8455cf35 100644 --- a/apps/android/boot.js +++ b/apps/android/boot.js @@ -197,6 +197,34 @@ if (isFinite(msg.id)) return gbSend({ t: "notify", n:response?"OPEN":"DISMISS", id: msg.id }); // error/warn here? }; + + // GPS overwrite logic + // Save current logic + const originalSetGpsPower = Bangle.setGPSPower; + const originalIsGpsOn = Bangle.isGPSOn; + + // Replace set GPS power logic to suppress activation of gps, if the overwrite option is active + Bangle.setGPSPower = (isOn, appID) => { + const currentSettings = require("Storage").readJSON("android.settings.json",1)||{}; + if (!currentSettings.overwriteGps) { + originalSetGpsPower(isOn, appID); + } else { + const logMessage = 'Ignore gps power change due to the gps overwrite from android integration app'; + console.log(logMessage); + Bluetooth.println(logMessage); + } + } + + // Replace check if the GPS is on, to show it as always active, if the overwrite option is set + Bangle.isGPSOn = () => { + const currentSettings = require("Storage").readJSON("android.settings.json",1)||{}; + if (!currentSettings.overwriteGps) { + return originalIsGpsOn(); + } else { + return true; + } + } + // remove settings object so it's not taking up RAM delete settings; })(); diff --git a/apps/android/settings.js b/apps/android/settings.js index c7c34a76f..94a1eba0b 100644 --- a/apps/android/settings.js +++ b/apps/android/settings.js @@ -1,4 +1,13 @@ (function(back) { + + function onGpsOverwriteChange(newValue) { + if (newValue) { + Bangle.setGPSPower(false, 'android'); + } + settings.overwriteGps = newValue; + updateSettings(); + } + function gb(j) { Bluetooth.println(JSON.stringify(j)); } @@ -23,6 +32,10 @@ updateSettings(); } }, + /*LANG*/"Overwrite GPS" : { + value : !!settings.overwriteGps, + onchange: onGpsOverwriteChange + }, /*LANG*/"Messages" : ()=>load("messages.app.js"), }; E.showMenu(mainmenu); From c68c14a9ed82502d906c2e2274a02441b7878cf6 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sun, 6 Nov 2022 19:25:25 +0100 Subject: [PATCH 017/107] add check if gps data should be overwritten, before the gps data is emitted --- apps/android/boot.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/android/boot.js b/apps/android/boot.js index a8455cf35..5cdc1f044 100644 --- a/apps/android/boot.js +++ b/apps/android/boot.js @@ -132,6 +132,8 @@ request.r(event); //r = resolve function }, "gps": function() { + const settings = require("Storage").readJSON("android.settings.json",1)||{}; + if (!settings.overwriteGps) return; delete event.t; event.satellites = NaN; event.course = NaN; From 7561f8d7c245ac68dc222e2c87355693ce8e7f26 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 21 Nov 2022 20:39:41 +0100 Subject: [PATCH 018/107] add gps request handling --- apps/android/boot.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/android/boot.js b/apps/android/boot.js index 5cdc1f044..7973456cd 100644 --- a/apps/android/boot.js +++ b/apps/android/boot.js @@ -139,6 +139,10 @@ event.course = NaN; event.fix = 1; Bangle.emit('gps', event); + }, + "is_gps_active": function() { + const gpsActive = originalIsGpsOn(); + sendGPSPowerStatus(gpsActive); } }; var h = HANDLERS[event.t]; @@ -205,12 +209,15 @@ const originalSetGpsPower = Bangle.setGPSPower; const originalIsGpsOn = Bangle.isGPSOn; + function sendGPSPowerStatus(status) { gbSend({ t: "gps_power", status: status }); } + // Replace set GPS power logic to suppress activation of gps, if the overwrite option is active Bangle.setGPSPower = (isOn, appID) => { const currentSettings = require("Storage").readJSON("android.settings.json",1)||{}; if (!currentSettings.overwriteGps) { originalSetGpsPower(isOn, appID); } else { + sendGPSPowerStatus(Bangle.isGPSOn()); const logMessage = 'Ignore gps power change due to the gps overwrite from android integration app'; console.log(logMessage); Bluetooth.println(logMessage); From a7ad62a030c46162b8e0c44e7597dccdb9028177 Mon Sep 17 00:00:00 2001 From: Kedlub Date: Tue, 22 Nov 2022 13:52:21 +0100 Subject: [PATCH 019/107] qcenter: New app --- apps/qcenter/ChangeLog | 1 + apps/qcenter/README.md | 20 +++++++ apps/qcenter/app-icon.js | 1 + apps/qcenter/app.js | 114 +++++++++++++++++++++++++++++++++++++ apps/qcenter/app.png | Bin 0 -> 265 bytes apps/qcenter/metadata.json | 14 +++++ apps/qcenter/settings.js | 114 +++++++++++++++++++++++++++++++++++++ 7 files changed, 264 insertions(+) create mode 100644 apps/qcenter/ChangeLog create mode 100644 apps/qcenter/README.md create mode 100644 apps/qcenter/app-icon.js create mode 100644 apps/qcenter/app.js create mode 100644 apps/qcenter/app.png create mode 100644 apps/qcenter/metadata.json create mode 100644 apps/qcenter/settings.js diff --git a/apps/qcenter/ChangeLog b/apps/qcenter/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/qcenter/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/qcenter/README.md b/apps/qcenter/README.md new file mode 100644 index 000000000..eb40e63ed --- /dev/null +++ b/apps/qcenter/README.md @@ -0,0 +1,20 @@ +# Quick Center + +App with status bar showing various info, and up to six shortcuts for your favorite apps! +Meant to be used with any kind of quick launcher, such as Quick Launch or Pattern Launcher + +Add screen shots (if possible) to the app folder and link then into this file with ![](.png) + +## Usage + +Pin apps using settings, and then run this using your favorite quick launcher to access them quickly +If you don't have any pinned apps, it shows setting and about app as an example + +## Features + +Showing battery and temperature (for now) +Up to six shortcuts to your favorite apps + +## Upcoming features +- Quick toggles for toggleable functions, such as Bluetooth, or it's HID mode +- Customizable status info \ No newline at end of file diff --git a/apps/qcenter/app-icon.js b/apps/qcenter/app-icon.js new file mode 100644 index 000000000..bfc94d10a --- /dev/null +++ b/apps/qcenter/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4UB6cA/4ACBYNVAElQHAsFBYZFHCxIYEoALHgILNOxILChWqAAmgBYNUBZMVBYIAIBc0C1WAlWoAgQL/O96D/Qf4LZqoLJqoLMoAKHgILNqALHgoLBGBAKCDA4WDAEQA=")) \ No newline at end of file diff --git a/apps/qcenter/app.js b/apps/qcenter/app.js new file mode 100644 index 000000000..0493805ac --- /dev/null +++ b/apps/qcenter/app.js @@ -0,0 +1,114 @@ +require("Font8x12").add(Graphics); + +// load pinned apps from config +var settings = require("Storage").readJSON("qcenter.json", 1) || {}; +var pinnedApps = settings.pinnedApps || []; +var exitGesture = settings.exitGesture || "swipeup"; + +// if empty load a default set of apps as an example +if (pinnedApps.length == 0) { + pinnedApps = [ + { src: "setting.app.js", icon: "setting.img" }, + { src: "about.app.js", icon: "about.img" }, + ]; +} + +// button drawing from Layout.js, edited to have completely custom button size with icon +function drawButton(l) { + var x = l.x + (0 | l.pad), + y = l.y + (0 | l.pad), + w = l.w - (l.pad << 1), + h = l.h - (l.pad << 1); + var poly = [ + x, + y + 4, + x + 4, + y, + x + w - 5, + y, + x + w - 1, + y + 4, + x + w - 1, + y + h - 5, + x + w - 5, + y + h - 1, + x + 4, + y + h - 1, + x, + y + h - 5, + x, + y + 4, + ], + bg = l.selected ? g.theme.bgH : g.theme.bg2; + g.setColor(bg) + .fillPoly(poly) + .setColor(l.selected ? g.theme.fgH : g.theme.fg2) + .drawPoly(poly); + if (l.src) + g.setBgColor(bg).drawImage( + "function" == typeof l.src ? l.src() : l.src, + l.x + l.w / 2, + l.y + l.h / 2, + { scale: l.scale || undefined, rotate: Math.PI * 0.5 * (l.r || 0) } + ); +} + +// function to split array into group of 3, for button placement +function groupBy3(data) { + var result = []; + for (var i = 0; i < data.length; i += 3) result.push(data.slice(i, i + 3)); + return result; +} + +// generate object with buttons for apps by group of 3 +var appButtons = groupBy3(pinnedApps).map((appGroup, i) => { + return appGroup.map((app, j) => { + return { + type: "custom", + render: drawButton, + width: 50, + height: 50, + pad: 5, + src: require("Storage").read(app.icon), + scale: 0.75, + cb: (l) => Bangle.load(app.src), + }; + }); +}); + +// create basic layout content with status info on top +var layoutContent = [ + { + type: "h", + pad: 5, + c: [ + { type: "txt", font: "8x12", label: E.getBattery() + "%" }, + { type: "txt", font: "8x12", label: " " + E.getTemperature() + "°C" }, + ], + }, +]; + +// create rows for buttons and add them to layoutContent +appButtons.forEach((appGroup) => { + layoutContent.push({ + type: "h", + pad: 2, + c: appGroup, + }); +}); + +var Layout = require("Layout"); +var layout = new Layout({ + type: "v", + c: layoutContent, +}); +g.clear(); +layout.render(); + +// add swipe event listener for exit gesture +Bangle.on("swipe", function (lr, ud) { + if(exitGesture == "swipeup" && ud == -1) Bangle.showClock(); + if(exitGesture == "swipedown" && ud == 1) Bangle.showClock(); + if(exitGesture == "swipeleft" && lr == -1) Bangle.showClock(); + if(exitGesture == "swiperight" && lr == 1) Bangle.showClock(); +}); \ No newline at end of file diff --git a/apps/qcenter/app.png b/apps/qcenter/app.png new file mode 100644 index 0000000000000000000000000000000000000000..27ec75f1c09f4503465e233ec2f3183a072d9476 GIT binary patch literal 265 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$1AIbUnWg6a|NkGzlQ=%#3`jAT1o;I61+Jgs1*&D>EbxddW? z8eR=R!75J|#}E(iw-X%s8Wea~HvX^QazJky^R=rk$x|$^bQ!bzludGP7I9GBy!Ou~ z?yX`UOj^{Iv4r?=uGrPThVYYmuN?b(Y0 z?|L^zI2@hQ5Vbty!jZ=o3!gd82{>7@`q { + var a = require("Storage").readJSON(app, 1); + return ( + a && { name: a.name, type: a.type, sortorder: a.sortorder, src: a.src, icon: a.icon } + ); + }) + .filter( + (app) => + app && + (app.type == "app" || + app.type == "launch" || + app.type == "clock" || + !app.type) + ); + apps.sort((a, b) => { + var n = (0 | a.sortorder) - (0 | b.sortorder); + if (n) return n; // do sortorder first + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; + return 0; + }); + + function save(key, value) { + settings[key] = value; + require("Storage").write("qcenter.json", settings); + } + + var pinnedApps = settings.pinnedApps || []; + var exitGesture = settings.exitGesture || "swipeup"; + + function showMainMenu() { + var mainmenu = { + "" : { "title" : "Quick Center" }, + "< Back" : ()=>{load();} + }; + + // Set exit gesture + mainmenu["Exit Gesture: " + exitGesture] = function() { + E.showMenu(exitGestureMenu); + }; + + //List all pinned apps + for (let i = 0; i < pinnedApps.length; i++) { + mainmenu[pinnedApps[i].name] = function() { + E.showMenu({ + "" : { "title" : pinnedApps[i].name }, + "< Back" : showMainMenu, + "Unpin" : function() { + pinnedApps.splice(i, 1); + save("pinnedApps", pinnedApps); + showMainMenu(); + } + }); + }; + } + + // Show pin app button only if there is less than 6 pinned apps, else show the button that shows alert that max apps has been pinned + if (pinnedApps.length < 6) { + mainmenu["Pin App"] = pinAppMenu; + } + else { + mainmenu["Pin App"] = function() { + E.showAlert("You can only pin 6 apps"); + }; + } + + return E.showMenu(mainmenu); + } + + // menu for adding apps to the quick launch menu, listing all apps + var pinAppMenu = { + "" : { "title" : "Add App" }, + "< Back" : showMainMenu + }; + apps.forEach((a)=>{ + pinAppMenu[a.name] = function() { + // strip unncecessary properties + delete a.type; + delete a.sortorder; + delete a.name; + pinnedApps.push(a); + save("pinnedApps", pinnedApps); + showMainMenu(); + }; + }); + + // menu for setting exit gesture + var exitGestureMenu = { + "" : { "title" : "Exit Gesture" }, + "< Back" : showMainMenu + }; + exitGestureMenu["Swipe Up"] = function() { + save("exitGesture", "swipeup"); + showMainMenu(); + } + exitGestureMenu["Swipe Down"] = function() { + save("exitGesture", "swipedown"); + showMainMenu(); + } + exitGestureMenu["Swipe Left"] = function() { + save("exitGesture", "swipeleft"); + showMainMenu(); + } + exitGestureMenu["Swipe Right"] = function() { + save("exitGesture", "swiperight"); + showMainMenu(); + } + +}); From 1c38505c9fc331c073b782e6fdeb3eac7fbb4cfe Mon Sep 17 00:00:00 2001 From: Kedlub Date: Tue, 22 Nov 2022 13:57:13 +0100 Subject: [PATCH 020/107] qcenter: Fix metadata --- apps/qcenter/metadata.json | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/qcenter/metadata.json b/apps/qcenter/metadata.json index b5e580bf0..736db2024 100644 --- a/apps/qcenter/metadata.json +++ b/apps/qcenter/metadata.json @@ -9,6 +9,7 @@ "readme": "README.md", "storage": [ {"name":"qcenter.app.js","url":"app.js"}, + {"name":"qcenter.settings.js","url":"settings.js"}, {"name":"qcenter.img","url":"app-icon.js","evaluate":true} ] } From db6b143c65e6b50e9d33f4d5e80c197d8b06e9c3 Mon Sep 17 00:00:00 2001 From: Kedlub Date: Tue, 22 Nov 2022 16:33:32 +0100 Subject: [PATCH 021/107] qcenter: Fix settings --- apps/qcenter/settings.js | 163 +++++++++++++++++++++------------------ 1 file changed, 87 insertions(+), 76 deletions(-) diff --git a/apps/qcenter/settings.js b/apps/qcenter/settings.js index 1bda89036..5550fd149 100644 --- a/apps/qcenter/settings.js +++ b/apps/qcenter/settings.js @@ -6,7 +6,13 @@ .map((app) => { var a = require("Storage").readJSON(app, 1); return ( - a && { name: a.name, type: a.type, sortorder: a.sortorder, src: a.src, icon: a.icon } + a && { + name: a.name, + type: a.type, + sortorder: a.sortorder, + src: a.src, + icon: a.icon, + } ); }) .filter( @@ -30,85 +36,90 @@ require("Storage").write("qcenter.json", settings); } - var pinnedApps = settings.pinnedApps || []; - var exitGesture = settings.exitGesture || "swipeup"; + var pinnedApps = settings.pinnedApps || []; + var exitGesture = settings.exitGesture || "swipeup"; - function showMainMenu() { - var mainmenu = { - "" : { "title" : "Quick Center" }, - "< Back" : ()=>{load();} - }; + function showMainMenu() { + var mainmenu = { + "": { title: "Quick Center" }, + "< Back": () => { + load(); + }, + }; - // Set exit gesture - mainmenu["Exit Gesture: " + exitGesture] = function() { - E.showMenu(exitGestureMenu); - }; - - //List all pinned apps - for (let i = 0; i < pinnedApps.length; i++) { - mainmenu[pinnedApps[i].name] = function() { - E.showMenu({ - "" : { "title" : pinnedApps[i].name }, - "< Back" : showMainMenu, - "Unpin" : function() { - pinnedApps.splice(i, 1); - save("pinnedApps", pinnedApps); - showMainMenu(); - } - }); - }; - } + // Set exit gesture + mainmenu["Exit Gesture: " + exitGesture] = function () { + E.showMenu(exitGestureMenu); + }; - // Show pin app button only if there is less than 6 pinned apps, else show the button that shows alert that max apps has been pinned - if (pinnedApps.length < 6) { - mainmenu["Pin App"] = pinAppMenu; - } - else { - mainmenu["Pin App"] = function() { - E.showAlert("You can only pin 6 apps"); - }; - } - - return E.showMenu(mainmenu); - } + //List all pinned apps + for (let i = 0; i < pinnedApps.length; i++) { + mainmenu[pinnedApps[i].name] = function () { + E.showMenu({ + "": { title: pinnedApps[i].name }, + "< Back": showMainMenu, + Unpin: function () { + pinnedApps.splice(i, 1); + save("pinnedApps", pinnedApps); + showMainMenu(); + }, + }); + }; + } - // menu for adding apps to the quick launch menu, listing all apps - var pinAppMenu = { - "" : { "title" : "Add App" }, - "< Back" : showMainMenu - }; - apps.forEach((a)=>{ - pinAppMenu[a.name] = function() { - // strip unncecessary properties - delete a.type; - delete a.sortorder; - delete a.name; - pinnedApps.push(a); - save("pinnedApps", pinnedApps); - showMainMenu(); - }; - }); + // Show pin app menu, or show alert if max amount of apps are pinned + mainmenu["Pin App"] = function () { + if (pinnedApps.length < 6) { + E.showMenu(pinAppMenu); + } else { + E.showAlert("Max apps pinned").then(showMainMenu); + } + }; - // menu for setting exit gesture - var exitGestureMenu = { - "" : { "title" : "Exit Gesture" }, - "< Back" : showMainMenu - }; - exitGestureMenu["Swipe Up"] = function() { - save("exitGesture", "swipeup"); - showMainMenu(); - } - exitGestureMenu["Swipe Down"] = function() { - save("exitGesture", "swipedown"); - showMainMenu(); - } - exitGestureMenu["Swipe Left"] = function() { - save("exitGesture", "swipeleft"); - showMainMenu(); - } - exitGestureMenu["Swipe Right"] = function() { - save("exitGesture", "swiperight"); - showMainMenu(); - } + return E.showMenu(mainmenu); + } + // menu for adding apps to the quick launch menu, listing all apps + var pinAppMenu = { + "": { title: "Add App" }, + "< Back": showMainMenu, + }; + apps.forEach((a) => { + pinAppMenu[a.name] = function () { + // strip unncecessary properties + delete a.type; + delete a.sortorder; + pinnedApps.push(a); + save("pinnedApps", pinnedApps); + showMainMenu(); + }; + }); + + // menu for setting exit gesture + var exitGestureMenu = { + "": { title: "Exit Gesture" }, + "< Back": showMainMenu, + }; + exitGestureMenu["Swipe Up"] = function () { + exitGesture = "swipeup"; + save("exitGesture", "swipeup"); + showMainMenu(); + }; + exitGestureMenu["Swipe Down"] = function () { + exitGesture = "swipedown"; + save("exitGesture", "swipedown"); + showMainMenu(); + }; + exitGestureMenu["Swipe Left"] = function () { + exitGesture = "swipeleft"; + save("exitGesture", "swipeleft"); + showMainMenu(); + }; + exitGestureMenu["Swipe Right"] = function () { + exitGesture = "swiperight"; + save("exitGesture", "swiperight"); + showMainMenu(); + }; + + showMainMenu(); }); From 8cf2f9957a278e6639902b96482cea3a43024cd8 Mon Sep 17 00:00:00 2001 From: Kedlub Date: Thu, 24 Nov 2022 18:30:29 +0100 Subject: [PATCH 022/107] qcenter: Added widgets & fix remove --- apps/qcenter/app.js | 11 ++++++++--- apps/qcenter/settings.js | 18 ++++++------------ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/apps/qcenter/app.js b/apps/qcenter/app.js index 0493805ac..a6499f508 100644 --- a/apps/qcenter/app.js +++ b/apps/qcenter/app.js @@ -82,8 +82,8 @@ var layoutContent = [ type: "h", pad: 5, c: [ - { type: "txt", font: "8x12", label: E.getBattery() + "%" }, - { type: "txt", font: "8x12", label: " " + E.getTemperature() + "°C" }, + { type: "txt", font: "8x12", scale: 2, label: E.getBattery() + "%" }, + { type: "txt", font: "8x12", scale: 2, label: " " + E.getTemperature() + "°C" }, ], }, ]; @@ -97,6 +97,10 @@ appButtons.forEach((appGroup) => { }); }); +// create layout with content + +Bangle.loadWidgets(); + var Layout = require("Layout"); var layout = new Layout({ type: "v", @@ -104,8 +108,9 @@ var layout = new Layout({ }); g.clear(); layout.render(); +Bangle.drawWidgets(); -// add swipe event listener for exit gesture +// swipe event listener for exit gesture Bangle.on("swipe", function (lr, ud) { if(exitGesture == "swipeup" && ud == -1) Bangle.showClock(); if(exitGesture == "swipedown" && ud == 1) Bangle.showClock(); diff --git a/apps/qcenter/settings.js b/apps/qcenter/settings.js index 5550fd149..10484bc8f 100644 --- a/apps/qcenter/settings.js +++ b/apps/qcenter/settings.js @@ -53,19 +53,13 @@ }; //List all pinned apps - for (let i = 0; i < pinnedApps.length; i++) { - mainmenu[pinnedApps[i].name] = function () { - E.showMenu({ - "": { title: pinnedApps[i].name }, - "< Back": showMainMenu, - Unpin: function () { - pinnedApps.splice(i, 1); - save("pinnedApps", pinnedApps); - showMainMenu(); - }, - }); + pinnedApps.forEach((app, i) => { + mainmenu["Remove " + app.name] = function () { + pinnedApps.splice(i, 1); + save("pinnedApps", pinnedApps); + showMainMenu(); }; - } + }); // Show pin app menu, or show alert if max amount of apps are pinned mainmenu["Pin App"] = function () { From 30abb6706baf7691d956dbdfdb2b4d72eaebd291 Mon Sep 17 00:00:00 2001 From: storm64 Date: Thu, 24 Nov 2022 23:56:16 +0100 Subject: [PATCH 023/107] [sleeplog] Improve README, remove convertOldLog --- apps/sleeplog/ChangeLog | 2 +- apps/sleeplog/README.md | 22 +++++++++-- apps/sleeplog/lib.js | 85 ----------------------------------------- 3 files changed, 19 insertions(+), 90 deletions(-) diff --git a/apps/sleeplog/ChangeLog b/apps/sleeplog/ChangeLog index 66eeccfc2..aa700f931 100644 --- a/apps/sleeplog/ChangeLog +++ b/apps/sleeplog/ChangeLog @@ -7,4 +7,4 @@ 0.10: Complete rework off this app! 0.10beta: Add interface.html to view saved log data, add "View log" function for debugging log, send data for gadgetbridge, change caching for global getStats 0.11: Prevent module not found error -0.12: Execute functions in `sleeplog.onChange` object on a status change \ No newline at end of file +0.12: Improve README, option to add functions triggered by status changes or time periods, remove old log (<0.10) conversion \ No newline at end of file diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 4310ead58..1970d8dde 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -24,10 +24,20 @@ Logfiles are not removed on un-/reinstall to prevent data loss. --- -### App Usage +### Main App Usage --- -#### On the main app screen: +#### View: +| Status | Color | Height | +|-------------|:------:|----------:| +| unknown | black | 0% | +| not worn | red | 40% | +| awake | green | 60% | +| light sleep | cyan | 80% | +| deep sleep | blue | 100% | +| consecutive | violet | as status | + +#### Controls: - __swipe left & right__ to change the displayed day - __touch the "title"__ (e.g. `Night to Fri 20/05/2022`) @@ -40,7 +50,11 @@ Logfiles are not removed on un-/reinstall to prevent data loss. - __use back button widget__ (upper left corner) exit the app -#### Inside the settings: + +--- +### Settings Usage +--- + - __Thresholds__ submenu Changes take effect from now on, not retrospective! - __Max Awake__ | maximal awake duration @@ -198,7 +212,7 @@ The passed data object has the following properties: Please leave requests and bug reports by raising an issue at [github.com/storm64/BangleApps](https://github.com/storm64/BangleApps) (or send me a [mail](mailto:banglejs@storm64.de)). #### Creator -Storm64 ([Mail](mailto:banglejs@storm64.de), [github](https://github.com/storm64)) +Storm64 ([mail](mailto:banglejs@storm64.de), [github](https://github.com/storm64)) #### Contributors myxor ([github](https://github.com/myxor)) diff --git a/apps/sleeplog/lib.js b/apps/sleeplog/lib.js index 1919e7483..83c45de66 100644 --- a/apps/sleeplog/lib.js +++ b/apps/sleeplog/lib.js @@ -149,14 +149,6 @@ exports = { // define move log function, move StorageFile content into files seperated by fortnights moveLog: function(force) { - /** convert old logfile (< v0.10) if present **/ - if (require("Storage").list("sleeplog.log", { - sf: false - }).length) { - convertOldLog(); - } - /** may be removed in later versions **/ - // first day of this fortnight period var thisFirstDay = this.fnToMs(this.msToFn(Date.now())); @@ -384,82 +376,5 @@ exports = { "unknown,not worn,awake,light sleep,deep sleep".split(",")[entry[1]].padEnd(12) + "for" + (duration + "min").padStart(8)); }); - }, - - /** convert old (< v0.10) to new logfile data **/ - convertOldLog: function() { - // read old logfile - var oldLog = require("Storage").read("sleeplog.log") || ""; - // decode data if needed - if (!oldLog.startsWith("[")) oldLog = atob(oldLog); - // delete old logfile and return if it is empty or corrupted - if (!oldLog.startsWith("[[") || !oldLog.endsWith("]]")) { - require("Storage").erase("sleeplog.log"); - return; - } - - // transform into StorageFile and clear oldLog to have more free ram accessable - require("Storage").open("sleeplog_old.log", "w").write(JSON.parse(oldLog).reverse().join("\n")); - oldLog = undefined; - - // calculate fortnight from now - var fnOfNow = this.msToFn(Date.now()); - - // open StorageFile with old log data - var file = require("Storage").open("sleeplog_old.log", "r"); - // define active fortnight and file cache - var activeFn = true; - var fileCache = []; - // loop through StorageFile entries - while (activeFn) { - // define fortnight for this entry - var thisFn = false; - // cache new line - var line = file.readLine(); - // check if line is filled - if (line) { - // parse line - line = line.substr(0, 15).split(",").map(e => parseInt(e)); - // calculate fortnight for this entry - thisFn = this.msToFn(line[0]); - // convert timestamp into 10min steps - line[0] = line[0] / 6E5 | 0; - // set consecutive to unknown - line.push(0); - } - // check if active fortnight and file cache is set, fortnight has changed and - // active fortnight is not fortnight from now - if (activeFn && fileCache.length && activeFn !== thisFn && activeFn !== fnOfNow) { - // write file cache into new file according to fortnight - require("Storage").writeJSON("sleeplog_" + activeFn + ".log", fileCache); - // clear file cache - fileCache = []; - } - // add line to file cache if it is filled - if (line) fileCache.push(line); - // set active fortnight - activeFn = thisFn; - } - // check if entries are leftover - if (fileCache.length) { - // format fileCache entries into a string - fileCache = fileCache.map(e => e.join(",")).join("\n"); - // read complete new log StorageFile as string - file = require("Storage").open("sleeplog.log", "r"); - var newLogString = file.read(file.getLength()); - // add entries at the beginning of the new log string - newLogString = fileCache + "\n" + newLogString; - // rewrite new log StorageFile - require("Storage").open("sleeplog.log", "w").write(newLogString); - } - - // free ram - file = undefined; - fileCache = undefined; - - // clean up old files - require("Storage").erase("sleeplog.log"); - require("Storage").open("sleeplog_old.log", "w").erase(); } - /** may be removed in later versions **/ }; From bc052c3aced0ce524b6ef0946964d81edf2e7136 Mon Sep 17 00:00:00 2001 From: Kedlub Date: Fri, 25 Nov 2022 14:42:10 +0100 Subject: [PATCH 024/107] qcenter: Grammar fixes & Final touches --- apps/qcenter/README.md | 16 ++++++++-------- apps/qcenter/app.js | 4 ++-- apps/qcenter/metadata.json | 30 ++++++++++++++++-------------- apps/qcenter/screenshot.png | Bin 0 -> 3647 bytes apps/qcenter/settings.js | 32 +++++++++++++++++++++++++++----- 5 files changed, 53 insertions(+), 29 deletions(-) create mode 100644 apps/qcenter/screenshot.png diff --git a/apps/qcenter/README.md b/apps/qcenter/README.md index eb40e63ed..4931b9c7f 100644 --- a/apps/qcenter/README.md +++ b/apps/qcenter/README.md @@ -1,20 +1,20 @@ # Quick Center -App with status bar showing various info, and up to six shortcuts for your favorite apps! -Meant to be used with any kind of quick launcher, such as Quick Launch or Pattern Launcher +An app with a status bar showing various information and up to six shortcuts for your favorite apps! +Designed for use with any kind of quick launcher, such as Quick Launch or Pattern Launcher. -Add screen shots (if possible) to the app folder and link then into this file with ![](.png) +![](screenshot.png) ## Usage -Pin apps using settings, and then run this using your favorite quick launcher to access them quickly -If you don't have any pinned apps, it shows setting and about app as an example +Pin your apps with settings, then launch them with your favorite quick launcher to access them quickly. +If you don't have any apps pinned, the settings and about apps will be shown as an example. ## Features -Showing battery and temperature (for now) +Battery and GPS status display (for now) Up to six shortcuts to your favorite apps ## Upcoming features -- Quick toggles for toggleable functions, such as Bluetooth, or it's HID mode -- Customizable status info \ No newline at end of file +- Quick switches for toggleable features such as Bluetooth or HID mode +- Customizable status information \ No newline at end of file diff --git a/apps/qcenter/app.js b/apps/qcenter/app.js index a6499f508..876d4aba9 100644 --- a/apps/qcenter/app.js +++ b/apps/qcenter/app.js @@ -76,14 +76,14 @@ var appButtons = groupBy3(pinnedApps).map((appGroup, i) => { }); }); -// create basic layout content with status info on top +// create basic layout content with status info and sensor status on top var layoutContent = [ { type: "h", pad: 5, c: [ { type: "txt", font: "8x12", scale: 2, label: E.getBattery() + "%" }, - { type: "txt", font: "8x12", scale: 2, label: " " + E.getTemperature() + "°C" }, + { type: "txt", font: "8x12", scale: 2, label: "GPS: " + (Bangle.isGPSOn() ? "ON" : "OFF") }, ], }, ]; diff --git a/apps/qcenter/metadata.json b/apps/qcenter/metadata.json index 736db2024..abef0fc36 100644 --- a/apps/qcenter/metadata.json +++ b/apps/qcenter/metadata.json @@ -1,15 +1,17 @@ -{ "id": "qcenter", - "name": "Quick Center", - "shortName":"QCenter", - "version":"0.01", - "description": "Shortcut for running your favorite apps & more", - "icon": "app.png", - "tags": "", - "supports" : ["BANGLEJS2"], - "readme": "README.md", - "storage": [ - {"name":"qcenter.app.js","url":"app.js"}, - {"name":"qcenter.settings.js","url":"settings.js"}, - {"name":"qcenter.img","url":"app-icon.js","evaluate":true} - ] +{ + "id": "qcenter", + "name": "Quick Center", + "shortName": "QCenter", + "version": "0.01", + "description": "Shortcut app for quickly running your favorite apps", + "icon": "app.png", + "tags": "", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "screenshots": [{ "url": "screenshot.png" }], + "storage": [ + { "name": "qcenter.app.js", "url": "app.js" }, + { "name": "qcenter.settings.js", "url": "settings.js" }, + { "name": "qcenter.img", "url": "app-icon.js", "evaluate": true } + ] } diff --git a/apps/qcenter/screenshot.png b/apps/qcenter/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..8c0a335aa58ba70ca0223116ca014294119c2036 GIT binary patch literal 3647 zcmcInXH%1l5`GgBL69bf-i!2r1qcL*bfhXFkxoQWks6{%QAj{U6e&s(2t{cs5)cSQ z0!9T;T=b7EvK0C8X=N+y2c_n!P0N_7oW8r)ttN$gQ zLkGO7VaoeJfC$dkW&njDJ$qpE`<#WTON94g{?FW6jU)FOwqw7KS!-)6UOKA$0I^rg zxucjidtyH2~2@Bi8NEc!g&pO9R=7}w29n5k$>E#rqE5~zcnN`l+#XEljv zQ;PyMst#D|gDunwUbx<9C2SXTnqyb+l%dB(mLmGMk?CdFy9yef%QTc8Nm>Lu-Tk<5 zD*%ij+e2dxRcnJx_(Go9F;fk>y^USjKh!hMPyYfr^K7dj>UFSSW@&G5l7IN`% znBiPUbA|G4c5e#r^J|2{y>J4?+-X?ND6V7;$wv{nfVZPOZK%4Zzg3K$8Volt_Y?2a7dZ}fXM=3h8NR(ZxA^qcN&20cN)>!a^{l@KBGNsh~)yye}5Kc|GsSe ze^8;h$k7sAM;&HamFYvY1)gr^R~xD)%iL7)D~-akVW zl5a46_Hx!I%8vxOH*`OAUqr52g{Zz8Hm#D0RxaJhOW2y>e}A!Gj2G&HCPn+7^O{DIWK%A zmrt(3s0?JGZtFN*YW(Bf4h{G21iB;IsR%$NR(+dp#?*#TnrFR zvKqXr`;Ic~56TK-@36k-A<`x7^q74f?fo=Z!uZKUAgl2yd=ySs(7uu$|2wxIzXE{5 zLp7QgI3Vz7&#?5@4_uIu53d$#3b}bgQ9cA^Zb2a&cX>@$jI2C;?@o8RwDDP@47n4& z8%YGV*Y&K!{(A>>e)h(PuQA7il%AKX*R4O7ad*o6L49a#^pKCPWX$aI70~{JklGg| zT>sv|6F&8;SDIZ|JN`%j9-`R1u)OP4)mB%0ca+g)?taz*Bzvi}{Ql6lSHH-$ya|k| z9CWwgb=hmF4F%{^#B|?Mo$20sCz$74`9PuHbaCX^w3MGHvNsM7i1VwwtLE4-l3UzTkL%v4}MNpQgi1+ z-e@%)@1lOw-&tD5x>a*JMUg_10zk1sY{z2RvJM|r$qjp_DadGY?MUCV0FEX#;96qP z@kcUE^Q-Q{^&_OKbHK{gpmPetndBI(&NPbEkgCe5e40AGEX8b(6-%go6H!t(v*Y;t z;xsc(uGR3yG0oooK7lUdm-NHgiBK=%?bx}1T~Rv-sM5LXVMr-moA4RugG8ovY;_QL zjqeJO(Vz3z!0ldD61F&NSuJ&Z092q$mAbsWES?fHw*{HgfChW@G>njFvuS2u%wx>A zGF1)z&H`2ban4D58Jgd4wdS3${-muhGTQn|HtupE|28Aluo@c~U;Odz`U`NP&#xp} z9(ux?m)4C*{WX6}5IT&Ps2#c(XkOml@g_g%dPGa`2gS@?7o%ii}*ig6U zwi=IbW`A#YO^rUk13fzvF~_uQ=I9a$7V&Pt zIOM0;!02GB3a7?J!dK;C{2PpK0DSaQO3GN%dEGl(ZGA zhk7oL{jH|CdLNyC)MEnlzVb)8&=WYR#+7{Ijx+eCu$Yt>=VJ>DfgKgMEoplfsK0jY zm%(BQ;DLctJQ5>+=2`Gm{f(a=wJmPcSb6v2Q^y5TYUIjm8J~lh?&L~GpxIT=Z>Y$g zBVByElOYuoEqy2KlAUqyQ9OXCqUX+i+WzL`*nBtP=W9zFWQkdpU%Ll$Y3S-&Q0~W^ z3diJ^bbPuwO#RUtOJ=p&umseK1!sC*TzlFsa4b6U*oz{%!p0Z#>qB2HWtn1u7mA%d za>gdPQk$I75xPe)swICyHIZr2-(?V8#$lK9&!Cq2L2Q~?d$!Ld7CcPZSRnlQ-~!6B z)6XY*VlgxD*2E2{BZ1X8m&Ww2Xlp>TLtqMfYt;S~KlfTe1jT!%>dUR@;7SaS`uqr| zZCWMZ0^IJ$fKy-7tCQv=Vab7S4w4ml;3_Ap5+|(zFgenwsqWEe%|trbZqn7^ux<1` zGsfGN#a^|*H@&H7+=Zm#QAA5M-|d(%C#A$6-8P4{G{^g}G`@zc2U0crLmbY@%7B zIc@T=(`K+=Y1sRgOuN0QvaQ2TmPkRUfK!l;tW{_92-oVl%$v7X-|hV+1|;8Kh&?Dx zJ1E-2_u&36ehDvX(nGT`N~mR_YKl$ImL91@CKvhm)?u83v8|8+bcg%xrW;osV=ko- zKa@k5zp7by>ni*jL*}c1KfD_xYnkK$35KYY$xQoAfkTOno4oZK_b$;M|D3p9Hnh2nf`wa~R0L$cFkN*FR;)NJB`D-7{kE8e)JOo5QJm)vWb%nBZM(j+1W4-a?g~MGB?uxMOU94PMggK|dy%0pw zN1M*PSiw)?g5Oer<&)fCEwHEDi;Zr*Ypg3GSIuB1pYH(%Slp42vE5@IrEa>PnR0t? z++p+-K$AKY`w&v#Q8!Z0)W{nv-2~!%vt>b2KM&$@V7w@yLMn2!_9fj4YT}(1WT+j7 zHPRVK*Xy*0mVwRH)nAce&I2ZuaQOtwj^}t0{UUIo3l8GHxH>9hqM-{DztJlJ#j>#b zw0ANe@*T5y?TP%r@;sHNeyHOWUDqXmt4!#jk!~Eik~Z7J1E`dNFF!b3eUjTsC|ZWK z_vibcyknz$!imX%<}ugTqU^q5Q}bHXG580kgXot9)nJIqVa4{r{nt;9EaQ716+*NJ z@n-`l8;hMDSl`89z3$N>(o;Pg?4jlrTu=5r&@vAk#I=@t4x?antxLt z_Dmh3INx0UK`C)?A!lY&>Qk?kzS@eyMSj%%J3Be97tApj9*ydlO(C40Kyuhc z7l-PC9wgrAAYC((@ST~D{4&>MT)6`%FiIkW(A0GfT}mJ-F!gW( zp6OglVrWu&B2z|{?yI5*?}Hfpd@t@Dy(G2l8zA<_FiMebCT-nmV4d4_eME$9W}zQC zm$73{k$Rt#zmk=G1a#<04RCWhG4;C$Tl4qi2t&3Aj6^;g+l?XuyMMA { - mainmenu["Remove " + app.name] = function () { - pinnedApps.splice(i, 1); - save("pinnedApps", pinnedApps); - showMainMenu(); + mainmenu[app.name] = function () { + E.showMenu({ + "": { title: app.name }, + "< Back": () => { + showMainMenu(); + }, + "Unpin": () => { + pinnedApps.splice(i, 1); + save("pinnedApps", pinnedApps); + showMainMenu(); + }, + "Move Up": () => { + if (i > 0) { + pinnedApps.splice(i - 1, 0, pinnedApps.splice(i, 1)[0]); + save("pinnedApps", pinnedApps); + showMainMenu(); + } + }, + "Move Down": () => { + if (i < pinnedApps.length - 1) { + pinnedApps.splice(i + 1, 0, pinnedApps.splice(i, 1)[0]); + save("pinnedApps", pinnedApps); + showMainMenu(); + } + }, + }); }; }); From fb0dcb82cd99e7a44b4e95e80d59a31c7ea2bee4 Mon Sep 17 00:00:00 2001 From: Kedlub Date: Fri, 25 Nov 2022 17:53:34 +0100 Subject: [PATCH 025/107] qcenter: Update description --- apps/qcenter/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/qcenter/metadata.json b/apps/qcenter/metadata.json index abef0fc36..96d8fa9f7 100644 --- a/apps/qcenter/metadata.json +++ b/apps/qcenter/metadata.json @@ -3,7 +3,7 @@ "name": "Quick Center", "shortName": "QCenter", "version": "0.01", - "description": "Shortcut app for quickly running your favorite apps", + "description": "An app for quickly launching your favourite apps, inspired by the control centres of other watches.", "icon": "app.png", "tags": "", "supports": ["BANGLEJS2"], From c8ee05a4b60fb573b20ce886efdbda405914e43c Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Thu, 11 Aug 2022 13:07:28 +0200 Subject: [PATCH 026/107] clkinfo: added sched and ranges in weather --- apps/sched/ChangeLog | 1 + apps/sched/clkinfo.js | 71 ++++++++++++++++++++++++++++++++++++++++ apps/sched/metadata.json | 5 +-- apps/weather/clkinfo.js | 16 +++++---- modules/clock_info.js | 11 ++++++- 5 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 apps/sched/clkinfo.js diff --git a/apps/sched/ChangeLog b/apps/sched/ChangeLog index f23cf93cb..3bb321ccf 100644 --- a/apps/sched/ChangeLog +++ b/apps/sched/ChangeLog @@ -14,3 +14,4 @@ Improve timer message using formatDuration Fix wrong fallback for buzz pattern 0.13: Ask to delete a timer after stopping it +0.14: Added clkinfo here diff --git a/apps/sched/clkinfo.js b/apps/sched/clkinfo.js new file mode 100644 index 000000000..578089c1e --- /dev/null +++ b/apps/sched/clkinfo.js @@ -0,0 +1,71 @@ +(function() { + const alarm = require('sched'); + const iconAlarmOn = atob("GBiBAAAAAAAAAAYAYA4AcBx+ODn/nAP/wAf/4A/n8A/n8B/n+B/n+B/n+B/n+B/h+B/4+A/+8A//8Af/4AP/wAH/gAB+AAAAAAAAAA=="); + const iconAlarmOff = + atob("GBjBAP////8AAAAAAAAGAGAOAHAcfjg5/5wD/8AH/+AP5/AP5/Af5/gf5/gf5wAf5gAf4Hgf+f4P+bYP8wMH84cD84cB8wMAebYAAf4AAHg="); + //atob("GBjBAP//AAAAAAAAAAAGAGAOAHAcfjg5/5wD/8AH/+AP5/AP5/Af5/gf5/gf5wAf5gAf4Hgf+f4P+bYP8wMH84cD84cB8wMAebYAAf4AAHg="); + + const iconTimerOn = + atob("GBjBAP////8AAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5wAAwwABgYABgYABgYAH/+AH/+AAAAAAAAAAAAA="); + //atob("GBjBAP//AAAAAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5wAAwwABgYABgYABgYAH/+AH/+AAAAAAAAAAAAA="); + const iconTimerOff = + atob("GBjBAP////8AAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5HgAwf4BgbYBgwMBg4cH84cH8wMAAbYAAf4AAHg="); + //atob("GBjBAP//AAAAAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5HgAwf4BgbYBgwMBg4cH84cH8wMAAbYAAf4AAHg="); + + //from 0 to max, the higher the closer to fire (as in a progress bar) + function getAlarmValue(a){ + let min = Math.round(alarm.getTimeToAlarm(a)/(60*1000)); + if(!min) return 0; //not active or more than a day + return getAlarmMax(a)-min; + } + + function getAlarmMax(a) { + if(a.timer) + return Math.round(a.timer/(60*1000)); + //minutes cannot be more than a full day + return 1440; + } + + function getAlarmIcon(a) { + if(a.on) { + if(a.timer) return iconTimerOn; + return iconAlarmOn; + } else { + if(a.timer) return iconTimerOff; + return iconAlarmOff; + } + } + + function getAlarmText(a){ + if(a.timer) { + if(!a.on) return "off"; + let time = Math.round(alarm.getTimeToAlarm(a)/(60*1000)); + if(time > 60) + time = Math.round(time / 60) + "h"; + else + time += "m"; + return time; + } + return require("time_utils").formatTime(a.t); + } + + var img = iconAlarmOn; + //get only alarms not created by other apps + var alarmItems = { + name: "Alarms", + img: img, + items: alarm.getAlarms().filter(a=>!a.appid) + .sort((a,b)=>alarm.getTimeToAlarm(a)-alarm.getTimeToAlarm(b)) + //.sort((a,b)=>getAlarmMinutes(a)-getAlarmMinutes(b)) + .map((a, i)=>({ + name: null, + get: () => ({ text: getAlarmText(a), img: getAlarmIcon(a), + hasRange: true, v: getAlarmValue(a), min:0, max:getAlarmMax(a)}), + show: function() { alarmItems.items[i].emit("redraw"); }, + hide: function () {}, + run: function() { } + })), + }; + + return alarmItems; +}) diff --git a/apps/sched/metadata.json b/apps/sched/metadata.json index 163d2f552..4b38ee653 100644 --- a/apps/sched/metadata.json +++ b/apps/sched/metadata.json @@ -1,7 +1,7 @@ { "id": "sched", "name": "Scheduler", - "version": "0.13", + "version": "0.14", "description": "Scheduling library for alarms and timers", "icon": "app.png", "type": "scheduler", @@ -13,7 +13,8 @@ {"name":"sched.js","url":"sched.js"}, {"name":"sched.img","url":"app-icon.js","evaluate":true}, {"name":"sched","url":"lib.js"}, - {"name":"sched.settings.js","url":"settings.js"} + {"name":"sched.settings.js","url":"settings.js"}, + {"name":"sched.clkinfo.js","url":"clkinfo.js"} ], "data": [{"name":"sched.json"}, {"name":"sched.settings.json"}] } diff --git a/apps/weather/clkinfo.js b/apps/weather/clkinfo.js index caaebf273..2d1966c82 100644 --- a/apps/weather/clkinfo.js +++ b/apps/weather/clkinfo.js @@ -5,34 +5,38 @@ wind: "?", }; - var weatherJson = storage.readJSON('weather.json'); + var weatherJson = require("Storage").readJSON('weather.json'); if(weatherJson !== undefined && weatherJson.weather !== undefined){ weather = weatherJson.weather; - weather.temp = locale.temp(weather.temp-273.15); + weather.temp = require("locale").temp(weather.temp-273.15); weather.hum = weather.hum + "%"; - weather.wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/); + weather.wind = require("locale").speed(weather.wind).match(/^(\D*\d*)(.*)$/); weather.wind = Math.round(weather.wind[1]) + "kph"; } + //FIXME ranges are somehow arbitrary var weatherItems = { name: "Weather", img: atob("GBiBAf+///u5//n7//8f/9wHP8gDf/gB//AB/7AH/5AcP/AQH/DwD/uAD84AD/4AA/wAAfAAAfAAAfAAAfgAA/////+bP/+zf/+zfw=="), items: [ { name: "temperature", - get: () => ({ text: weather.temp, img: atob("GBiBAAA8AAB+AADnAADDAADDAADDAADDAADDAADbAADbAADbAADbAADbAADbAAHbgAGZgAM8wAN+wAN+wAM8wAGZgAHDgAD/AAA8AA==")}), + get: () => ({ text: weather.temp, img: atob("GBiBAAA8AAB+AADnAADDAADDAADDAADDAADDAADbAADbAADbAADbAADbAADbAAHbgAGZgAM8wAN+wAN+wAM8wAGZgAHDgAD/AAA8AA=="), + hasRange: true, v: parseInt(weather.temp), min: -30, max: 55}), show: function() { weatherItems.items[0].emit("redraw"); }, hide: function () {} }, { name: "humidity", - get: () => ({ text: weather.hum, img: atob("GBiBAAAEAAAMAAAOAAAfAAAfAAA/gAA/gAI/gAY/AAcfAA+AQA+A4B/A4D/B8D/h+D/j+H/n/D/n/D/n/B/H/A+H/AAH/AAD+AAA8A==")}), + get: () => ({ text: weather.hum, img: atob("GBiBAAAEAAAMAAAOAAAfAAAfAAA/gAA/gAI/gAY/AAcfAA+AQA+A4B/A4D/B8D/h+D/j+H/n/D/n/D/n/B/H/A+H/AAH/AAD+AAA8A=="), + hasRange: true, v: parseInt(weather.hum), min: 0, max: 100}), show: function() { weatherItems.items[1].emit("redraw"); }, hide: function () {} }, { name: "wind", - get: () => ({ text: weather.wind, img: atob("GBiBAAHgAAPwAAYYAAwYAAwMfAAY/gAZh3/xg//hgwAAAwAABg///g//+AAAAAAAAP//wH//4AAAMAAAMAAYMAAYMAAMcAAP4AADwA==")}), + get: () => ({ text: weather.wind, img: atob("GBiBAAHgAAPwAAYYAAwYAAwMfAAY/gAZh3/xg//hgwAAAwAABg///g//+AAAAAAAAP//wH//4AAAMAAAMAAYMAAYMAAMcAAP4AADwA=="), + hasRange: true, v: parseInt(weather.wind), min: 0, max: 118}), show: function() { weatherItems.items[2].emit("redraw"); }, hide: function () {} }, diff --git a/modules/clock_info.js b/modules/clock_info.js index cdd3c7520..d2f1dea7c 100644 --- a/modules/clock_info.js +++ b/modules/clock_info.js @@ -48,6 +48,15 @@ example.clkinfo.js : */ +let storage = require("Storage"); +let stepGoal = undefined; +// Load step goal from health app and pedometer widget +let d = storage.readJSON("health.json", true) || {}; +stepGoal = d != undefined && d.settings != undefined ? d.settings.stepGoal : undefined; +if (stepGoal == undefined) { + d = storage.readJSON("wpedom.json", true) || {}; + stepGoal = d != undefined && d.settings != undefined ? d.settings.goal : 10000; +} exports.load = function() { // info used for drawing... @@ -81,7 +90,7 @@ exports.load = function() { { name : "Steps", hasRange : true, get : () => { let v = Bangle.getHealthStatus("day").steps; return { - text : v, v : v, min : 0, max : 10000, // TODO: do we have a target step amount anywhere? + text : v, v : v, min : 0, max : stepGoal, img : atob("GBiBAAcAAA+AAA/AAA/AAB/AAB/gAA/g4A/h8A/j8A/D8A/D+AfH+AAH8AHn8APj8APj8AHj4AHg4AADAAAHwAAHwAAHgAAHgAADAA==") }}, show : function() { Bangle.on("step", stepUpdateHandler); stepUpdateHandler(); }, From 4ff8e9a52cefa526a52c81cf4f2599c46d01b60f Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Fri, 25 Nov 2022 08:59:07 +0100 Subject: [PATCH 027/107] Fixed sched clkinfo icons and sorting --- apps/sched/clkinfo.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/sched/clkinfo.js b/apps/sched/clkinfo.js index 578089c1e..52f553988 100644 --- a/apps/sched/clkinfo.js +++ b/apps/sched/clkinfo.js @@ -1,16 +1,9 @@ (function() { const alarm = require('sched'); const iconAlarmOn = atob("GBiBAAAAAAAAAAYAYA4AcBx+ODn/nAP/wAf/4A/n8A/n8B/n+B/n+B/n+B/n+B/h+B/4+A/+8A//8Af/4AP/wAH/gAB+AAAAAAAAAA=="); - const iconAlarmOff = - atob("GBjBAP////8AAAAAAAAGAGAOAHAcfjg5/5wD/8AH/+AP5/AP5/Af5/gf5/gf5wAf5gAf4Hgf+f4P+bYP8wMH84cD84cB8wMAebYAAf4AAHg="); - //atob("GBjBAP//AAAAAAAAAAAGAGAOAHAcfjg5/5wD/8AH/+AP5/AP5/Af5/gf5/gf5wAf5gAf4Hgf+f4P+bYP8wMH84cD84cB8wMAebYAAf4AAHg="); - - const iconTimerOn = - atob("GBjBAP////8AAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5wAAwwABgYABgYABgYAH/+AH/+AAAAAAAAAAAAA="); - //atob("GBjBAP//AAAAAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5wAAwwABgYABgYABgYAH/+AH/+AAAAAAAAAAAAA="); - const iconTimerOff = - atob("GBjBAP////8AAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5HgAwf4BgbYBgwMBg4cH84cH8wMAAbYAAf4AAHg="); - //atob("GBjBAP//AAAAAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5HgAwf4BgbYBgwMBg4cH84cH8wMAAbYAAf4AAHg="); + const iconAlarmOff = atob("GBiBAAAAAAAAAAYAYA4AcBx+ODn/nAP/wAf/4A/n8A/n8B/n+B/n+B/nAB/mAB/geB/5/g/5tg/zAwfzhwPzhwHzAwB5tgAB/gAAeA=="); + const iconTimerOn = atob("GBiBAAAAAAAAAAAAAAf/4Af/4AGBgAGBgAGBgAD/AAD/AAB+AAA8AAA8AAB+AADnAADDAAGBgAGBgAGBgAf/4Af/4AAAAAAAAAAAAA=="); + const iconTimerOff = atob("GBiBAAAAAAAAAAAAAAf/4Af/4AGBgAGBgAGBgAD/AAD/AAB+AAA8AAA8AAB+AADkeADB/gGBtgGDAwGDhwfzhwfzAwABtgAB/gAAeA=="); //from 0 to max, the higher the closer to fire (as in a progress bar) function getAlarmValue(a){ @@ -49,14 +42,21 @@ return require("time_utils").formatTime(a.t); } + //workaround for sorting undefined values + function getAlarmOrder(a) { + let val = alarm.getTimeToAlarm(a); + if(typeof val == "undefined") return 86400*1000; + return val; + } + var img = iconAlarmOn; //get only alarms not created by other apps var alarmItems = { name: "Alarms", img: img, items: alarm.getAlarms().filter(a=>!a.appid) - .sort((a,b)=>alarm.getTimeToAlarm(a)-alarm.getTimeToAlarm(b)) - //.sort((a,b)=>getAlarmMinutes(a)-getAlarmMinutes(b)) + //.sort((a,b)=>alarm.getTimeToAlarm(a)-alarm.getTimeToAlarm(b)) + .sort((a,b)=>getAlarmOrder(a)-getAlarmOrder(b)) .map((a, i)=>({ name: null, get: () => ({ text: getAlarmText(a), img: getAlarmIcon(a), From 18d91bf5f95d02014c52a40a53a2c79d06bfd81a Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Fri, 25 Nov 2022 18:28:20 +0100 Subject: [PATCH 028/107] Added short field and general clkinfo improvements --- apps/agenda/ChangeLog | 1 + apps/agenda/agenda.clkinfo.js | 29 +++++++++++++++++++---------- apps/sched/ChangeLog | 2 +- apps/sched/clkinfo.js | 4 +++- apps/weather/ChangeLog | 3 ++- apps/weather/clkinfo.js | 9 ++++++--- apps/weather/metadata.json | 2 +- modules/clock_info.js | 3 ++- 8 files changed, 35 insertions(+), 18 deletions(-) diff --git a/apps/agenda/ChangeLog b/apps/agenda/ChangeLog index 7f749ff25..6384fe887 100644 --- a/apps/agenda/ChangeLog +++ b/apps/agenda/ChangeLog @@ -7,3 +7,4 @@ 0.07: Clkinfo improvements. 0.08: Fix error in clkinfo (didn't require Storage & locale) Fix clkinfo icon +0.09: Clkinfo new fields and filter past events. diff --git a/apps/agenda/agenda.clkinfo.js b/apps/agenda/agenda.clkinfo.js index baa8b9516..43b7cf57e 100644 --- a/apps/agenda/agenda.clkinfo.js +++ b/apps/agenda/agenda.clkinfo.js @@ -1,25 +1,34 @@ (function() { var agendaItems = { - name: "Agenda", - img: atob("GBiBAAAAAAAAAADGMA///w///wf//wAAAA///w///w///w///x///h///h///j///D///X//+f//8wAABwAADw///w///wf//gAAAA=="), - items: [] - }; + name: "Agenda", + img: atob("GBiBAAAAAAAAAADGMA///w///wf//wAAAA///w///w///w///x///h///h///j///D///X//+f//8wAABwAADw///w///wf//gAAAA=="), + dynamic: true, + items: [] + }; + var storage = require("Storage"); var locale = require("locale"); var now = new Date(); - var agenda = require("Storage").readJSON("android.calendar.json") - .filter(ev=>ev.timestamp + ev.durationInSeconds > now/1000) - .sort((a,b)=>a.timestamp - b.timestamp); + var agenda = (storage.readJSON("android.calendar.json",true)||[]) + .filter(ev=>ev.timestamp + ev.durationInSeconds > now/1000) + .sort((a,b)=>a.timestamp - b.timestamp); + var settings = storage.readJSON("agenda.settings.json",true)||{}; agenda.forEach((entry, i) => { - var title = entry.title.slice(0,12); var date = new Date(entry.timestamp*1000); var dateStr = locale.date(date).replace(/\d\d\d\d/,""); + var dateStrToday = locale.date(new Date()).replace(/\d\d\d\d/,""); + var timeStr = locale.time(date); + //maybe not the most efficient.. + var shortTxt = (dateStrToday == dateStr) ? timeStr : dateStr; dateStr += entry.durationInSeconds < 86400 ? "/ " + locale.time(date,1) : ""; + if(!settings.pastEvents && entry.timestamp + entry.durationInSeconds < (new Date())/1000) + return; + agendaItems.items.push({ - name: "Agenda "+i, - get: () => ({ text: title + "\n" + dateStr, img: null}), + name: null, + get: () => ({ text: title + "\n" + dateStr, short: shortTxt, img: null}), show: function() { agendaItems.items[i].emit("redraw"); }, hide: function () {} }); diff --git a/apps/sched/ChangeLog b/apps/sched/ChangeLog index 3bb321ccf..f2dd54338 100644 --- a/apps/sched/ChangeLog +++ b/apps/sched/ChangeLog @@ -14,4 +14,4 @@ Improve timer message using formatDuration Fix wrong fallback for buzz pattern 0.13: Ask to delete a timer after stopping it -0.14: Added clkinfo here +0.14: Added clkinfo for alarms and timers diff --git a/apps/sched/clkinfo.js b/apps/sched/clkinfo.js index 52f553988..71992dbb8 100644 --- a/apps/sched/clkinfo.js +++ b/apps/sched/clkinfo.js @@ -54,13 +54,15 @@ var alarmItems = { name: "Alarms", img: img, + dynamic: true, items: alarm.getAlarms().filter(a=>!a.appid) //.sort((a,b)=>alarm.getTimeToAlarm(a)-alarm.getTimeToAlarm(b)) .sort((a,b)=>getAlarmOrder(a)-getAlarmOrder(b)) .map((a, i)=>({ name: null, + hasRange: true, get: () => ({ text: getAlarmText(a), img: getAlarmIcon(a), - hasRange: true, v: getAlarmValue(a), min:0, max:getAlarmMax(a)}), + v: getAlarmValue(a), min:0, max:getAlarmMax(a)}), show: function() { alarmItems.items[i].emit("redraw"); }, hide: function () {}, run: function() { } diff --git a/apps/weather/ChangeLog b/apps/weather/ChangeLog index da28d8d5a..b11ed24ff 100644 --- a/apps/weather/ChangeLog +++ b/apps/weather/ChangeLog @@ -13,4 +13,5 @@ 0.14: Use weather condition code for icon selection 0.15: Fix widget icon 0.16: Don't mark app as clock -0.17: Added clkinfo for clocks. \ No newline at end of file +0.17: Added clkinfo for clocks. +0.18: Added hasRange to clkinfo. diff --git a/apps/weather/clkinfo.js b/apps/weather/clkinfo.js index 2d1966c82..6191c6dbe 100644 --- a/apps/weather/clkinfo.js +++ b/apps/weather/clkinfo.js @@ -21,22 +21,25 @@ items: [ { name: "temperature", + hasRange : true, get: () => ({ text: weather.temp, img: atob("GBiBAAA8AAB+AADnAADDAADDAADDAADDAADDAADbAADbAADbAADbAADbAADbAAHbgAGZgAM8wAN+wAN+wAM8wAGZgAHDgAD/AAA8AA=="), - hasRange: true, v: parseInt(weather.temp), min: -30, max: 55}), + v: parseInt(weather.temp), min: -30, max: 55}), show: function() { weatherItems.items[0].emit("redraw"); }, hide: function () {} }, { name: "humidity", + hasRange : true, get: () => ({ text: weather.hum, img: atob("GBiBAAAEAAAMAAAOAAAfAAAfAAA/gAA/gAI/gAY/AAcfAA+AQA+A4B/A4D/B8D/h+D/j+H/n/D/n/D/n/B/H/A+H/AAH/AAD+AAA8A=="), - hasRange: true, v: parseInt(weather.hum), min: 0, max: 100}), + v: parseInt(weather.hum), min: 0, max: 100}), show: function() { weatherItems.items[1].emit("redraw"); }, hide: function () {} }, { name: "wind", + hasRange : true, get: () => ({ text: weather.wind, img: atob("GBiBAAHgAAPwAAYYAAwYAAwMfAAY/gAZh3/xg//hgwAAAwAABg///g//+AAAAAAAAP//wH//4AAAMAAAMAAYMAAYMAAMcAAP4AADwA=="), - hasRange: true, v: parseInt(weather.wind), min: 0, max: 118}), + v: parseInt(weather.wind), min: 0, max: 118}), show: function() { weatherItems.items[2].emit("redraw"); }, hide: function () {} }, diff --git a/apps/weather/metadata.json b/apps/weather/metadata.json index e28a282d6..4a8751302 100644 --- a/apps/weather/metadata.json +++ b/apps/weather/metadata.json @@ -1,7 +1,7 @@ { "id": "weather", "name": "Weather", - "version": "0.17", + "version": "0.18", "description": "Show Gadgetbridge weather report", "icon": "icon.png", "screenshots": [{"url":"screenshot.png"}], diff --git a/modules/clock_info.js b/modules/clock_info.js index d2f1dea7c..238888b1c 100644 --- a/modules/clock_info.js +++ b/modules/clock_info.js @@ -2,9 +2,9 @@ that can be scrolled through on the clock face. `load()` returns an array of menu objects, where each object contains a list of menu items: - * `name` : text to display and identify menu object (e.g. weather) * `img` : a 24x24px image +* `dynamic` : if `true`, items are not constant but are sorted (e.g. calendar events sorted by date) * `items` : menu items such as temperature, humidity, wind etc. Note that each item is an object with: @@ -15,6 +15,7 @@ Note that each item is an object with: { 'text' // the text to display for this item + 'short' : (optional) a shorter text to display for this item (at most 6 characters) 'img' // optional: a 24x24px image to display for this item 'v' // (if hasRange==true) a numerical value 'min','max' // (if hasRange==true) a minimum and maximum numerical value (if this were to be displayed as a guage) From 93c2429bf57aeb3a16c3212982a97efaab359d01 Mon Sep 17 00:00:00 2001 From: eyecreate Date: Fri, 25 Nov 2022 12:51:08 -0500 Subject: [PATCH 029/107] Add locale time support to watch --- apps/slopeclockpp/app.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/slopeclockpp/app.js b/apps/slopeclockpp/app.js index 013c25733..bf719344e 100644 --- a/apps/slopeclockpp/app.js +++ b/apps/slopeclockpp/app.js @@ -51,8 +51,9 @@ let draw = function() { x = R.w / 2; y = R.y + R.h / 2 - 12; // 12 = room for date var date = new Date(); - var hourStr = date.getHours(); - var minStr = date.getMinutes().toString().padStart(2,0); + var local_time = require("locale").time(date, 1); + var hourStr = local_time.split(":")[0].trim().padStart(2,'0'); + var minStr = local_time.split(":")[1].trim().padStart(2, '0'); dateStr = require("locale").dow(date, 1).toUpperCase()+ " "+ require("locale").date(date, 0).toUpperCase(); From ebc2091c2994e8aea228da0a2e379258e74103d8 Mon Sep 17 00:00:00 2001 From: eyecreate Date: Fri, 25 Nov 2022 12:52:13 -0500 Subject: [PATCH 030/107] Update metadata --- apps/slopeclockpp/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/slopeclockpp/metadata.json b/apps/slopeclockpp/metadata.json index b419ef898..bebed4b71 100644 --- a/apps/slopeclockpp/metadata.json +++ b/apps/slopeclockpp/metadata.json @@ -1,6 +1,6 @@ { "id": "slopeclockpp", "name": "Slope Clock ++", - "version":"0.05", + "version":"0.06", "description": "A clock where hours and minutes are divided by a sloping line. When the minute changes, the numbers slide off the screen. This is a clone of the original Slope Clock which shows extra information and allows the colors to be selected.", "icon": "app.png", "screenshots": [{"url":"screenshot.png"}], From 58fbe0455eb4ae11e038ed534a2da3b5e71e665d Mon Sep 17 00:00:00 2001 From: eyecreate Date: Fri, 25 Nov 2022 12:52:37 -0500 Subject: [PATCH 031/107] Update ChangeLog --- apps/slopeclockpp/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/slopeclockpp/ChangeLog b/apps/slopeclockpp/ChangeLog index b5e455f78..9ae4ccc96 100644 --- a/apps/slopeclockpp/ChangeLog +++ b/apps/slopeclockpp/ChangeLog @@ -5,3 +5,4 @@ Made fonts smaller to avoid overlap when (eg) 22:00 Allowed black/white background (as that can look nice too) 0.05: Images in clkinfo are optional now +0.06: Added support for locale based time From e35f3a26bb629c61b8da535a3e8e369ed1616acf Mon Sep 17 00:00:00 2001 From: Kedlub Date: Fri, 25 Nov 2022 21:37:54 +0100 Subject: [PATCH 032/107] qcenter: Add padding --- apps/qcenter/app.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/qcenter/app.js b/apps/qcenter/app.js index 876d4aba9..1530cc5fb 100644 --- a/apps/qcenter/app.js +++ b/apps/qcenter/app.js @@ -81,9 +81,10 @@ var layoutContent = [ { type: "h", pad: 5, + fillx: 1, c: [ - { type: "txt", font: "8x12", scale: 2, label: E.getBattery() + "%" }, - { type: "txt", font: "8x12", scale: 2, label: "GPS: " + (Bangle.isGPSOn() ? "ON" : "OFF") }, + { type: "txt", font: "8x12", pad: 3, scale: 2, label: E.getBattery() + "%" }, + { type: "txt", font: "8x12", pad: 3, scale: 2, label: "GPS: " + (Bangle.isGPSOn() ? "ON" : "OFF") }, ], }, ]; From 21e9fc1e24b3d4d6fca04829ddfb105b6dfa0f41 Mon Sep 17 00:00:00 2001 From: "Marko.Kl.Berkenbusch@gmail.com" Date: Fri, 25 Nov 2022 20:06:29 -0500 Subject: [PATCH 033/107] Add tetris app, first commit --- apps/tetris/README.md | 8 ++ apps/tetris/app-icon.js | 1 + apps/tetris/metadata.json | 14 ++++ apps/tetris/tetris.app.js | 170 ++++++++++++++++++++++++++++++++++++++ apps/tetris/tetris.png | Bin 0 -> 492 bytes 5 files changed, 193 insertions(+) create mode 100644 apps/tetris/README.md create mode 100644 apps/tetris/app-icon.js create mode 100644 apps/tetris/metadata.json create mode 100644 apps/tetris/tetris.app.js create mode 100644 apps/tetris/tetris.png diff --git a/apps/tetris/README.md b/apps/tetris/README.md new file mode 100644 index 000000000..2c41657f4 --- /dev/null +++ b/apps/tetris/README.md @@ -0,0 +1,8 @@ +# Tetris + +Bangle version of the classic game of Tetris. + +## Controls + +Tapping the screen rotates the pieces once, swiping left, right or down moves the +piece in that direction, if possible. \ No newline at end of file diff --git a/apps/tetris/app-icon.js b/apps/tetris/app-icon.js new file mode 100644 index 000000000..b87ef84f4 --- /dev/null +++ b/apps/tetris/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+If4A/AH4A/AH4A/ABe5AA0jABwvYAIovBgABEFAQHFL7IuEL4QuFA45fcF4YuNL7i/FFwoHHL7QvFFxpfaF4wAOF/4nHF5+0AAy3SXYoHGW4QBDF4MAAIgvRFwwHHdAbqDFIQuDL6ouJL4ovDFwpfUAAoHFL4a/FFwhfTFxZfDF4ouFL6QANFopfDF/4vNjwAGF8ABFF4MAAIgvBX4IBDX4YBDL6TyFFIIuEL4QuEL4QuEL6ovDFwpfFF4YuFL6i/FFwhfEX4ouEL6YvFFwpfDF4ouFL6QvGAAwtFL4Yv/AAonHAB4vHG563CAIbuDA5i/CAIb2DA4hfJEwoHPFApZEGwpfLFyJfFFxJfMAAoHNFAa5GX54uTL4YuLL5QAVFowAIF+4A/AH4A/AH4A/AHY")) diff --git a/apps/tetris/metadata.json b/apps/tetris/metadata.json new file mode 100644 index 000000000..778e55c24 --- /dev/null +++ b/apps/tetris/metadata.json @@ -0,0 +1,14 @@ +{ "id": "tetris", + "name": "Tetris", + "shortName":"Tetris", + "version":"0.01", + "description": "Tetris", + "icon": "tetris.png", + "readme": "README.md", + "tags": "games", + "supports" : ["BANGLEJS2"], + "storage": [ + {"name":"tetris.app.js","url":"app.js"}, + {"name":"tetris.img","url":"app-icon.js","evaluate":true}, + ] +} diff --git a/apps/tetris/tetris.app.js b/apps/tetris/tetris.app.js new file mode 100644 index 000000000..e24a731a9 --- /dev/null +++ b/apps/tetris/tetris.app.js @@ -0,0 +1,170 @@ +const block = Graphics.createImage(` +######## +# # # ## +## # ### +# # #### +## ##### +# ###### +######## +######## +`); +const tcols = [ {r:0, g:0, b:1}, {r:0, g:1, b:0}, {r:0, g:1, b:1}, {r:1, g:0, b:0}, {r:1, g:0, b:1}, {r:1, g:1, b:0}, {r:1, g:0.5, b:0.5} ]; +const tiles = [ + [[0, 0, 0, 0], + [0, 0, 0, 0], + [1, 1, 1, 1], + [0, 0, 0, 0]], + [[0, 0, 0], + [0, 1, 0], + [1, 1, 1]], + [[0, 0, 0], + [1, 0, 0], + [1, 1, 1]], + [[0, 0, 0], + [0, 0, 1], + [1, 1, 1]], + [[0, 0, 0], + [1, 1, 0], + [0, 1, 1]], + [[0, 0, 0], + [0, 1, 1], + [1, 1, 0]], + [[1, 1], + [1, 1]] +]; + +const ox = 176/2 - 5*8; +const oy = 8; + +var pf = Array(23).fill().map(()=>Array(12).fill(0)); // field is really 10x20, but adding a border for collision checks +pf[20].fill(1); +pf[21].fill(1); +pf[22].fill(1); +pf.forEach((x,i) => { pf[i][0] = 1; pf[i][11] = 1; }); + +function rotateTile(t, r) { + var nt = JSON.parse(JSON.stringify(t)); + if (t.length==2) return nt; + var s = t.length; + for (m=0; m0) + if (qClear) g.fillRect(x+8*i, y+8*j, x+8*(i+1)-1, y+8*(j+1)-1); + else g.drawImage(block, x+8*i, y+8*j); +} + +function showNext(n, r) { + var nt = rotateTile(tiles[n], r); + g.setColor(0).fillRect(176-33, 40, 176-33+33, 82); + drawTile(nt, ntn, 176-33, 40); +} + +var time = Date.now(); +var px=4, py=0; +var ctn = Math.floor(Math.random()*7); // current tile number +var ntn = Math.floor(Math.random()*7); // next tile number +var ntr = Math.floor(Math.random()*4); // next tile rotation +var ct = rotateTile(tiles[ctn], Math.floor(Math.random()*4)); // current tile (rotated) +var dropInterval = 450; +var nlines = 0; + +function redrawPF(ly) { + for (y=0; y<=ly; ++y) + for (x=1; x<11; ++x) { + c = pf[y][x]; + if (c>0) g.setColor(tcols[c-1].r, tcols[c-1].g, tcols[c-1].b).drawImage(block, ox+(x-1)*8, oy+y*8); + else g.setColor(0, 0, 0).fillRect(ox+(x-1)*8, oy+y*8, ox+x*8-1, oy+(y+1)*8-1); + } +} + +function insertAndCheck() { + for (y=0; y0) pf[py+y][px+x+1] = ctn+1; + // check for full lines + for (y=19; y>0; y--) { + var qFull = true; + for (x=1; x<11; ++x) qFull &= pf[y][x]>0; + if (qFull) { + nlines++; + dropInterval -= 5; + Bangle.buzz(30); + for (ny=y; ny>0; ny--) pf[ny] = JSON.parse(JSON.stringify(pf[ny-1])); + redrawPF(y); + g.setColor(0).fillRect(5, 30, 41, 80).setColor(1, 1, 1).drawString(nlines.toString(), 22, 50); + } + } + // spawn new tile + px = 4; py = 0; + ctn = ntn; + ntn = Math.floor(Math.random()*7); + ct = rotateTile(tiles[ctn], ntr); + ntr = Math.floor(Math.random()*4); + showNext(ntn, ntr); +} + +function moveOk(t, dx, dy) { + var ok = true; + for (y=0; y 0) ok = false; + return ok; +} + +function gameStep() { + if (Date.now()-time > dropInterval) { // drop one step + time = Date.now(); + if (moveOk(ct, 0, 1)) { + drawTile(ct, ctn, ox+px*8, oy+py*8, true); + py++; + } + else { // reached the bottom + insertAndCheck(ct, ctn, px, py); + } + drawTile(ct, ctn, ox+px*8, oy+py*8, false); + } +} + +Bangle.setUI(); +Bangle.on("touch", (e) => { + t = rotateTile(ct, 3); + if (moveOk(t, 0, 0)) { + drawTile(ct, ctn, ox+px*8, oy+py*8, true); + ct = t; + drawTile(ct, ctn, ox+px*8, oy+py*8, false); + } +}); + +Bangle.on("swipe", (x,y) => { + if (y<0) y = 0; + if (moveOk(ct, x, y)) { + drawTile(ct, ctn, ox+px*8, oy+py*8, true); + px += x; + py += y; + drawTile(ct, ctn, ox+px*8, oy+py*8, false); + } +}); + +drawBoundingBox(); +g.setColor(1, 1, 1).setFontAlign(0, 1, 0).setFont("6x15", 1).drawString("Lines", 22, 30).drawString("Next", 176-22, 30); +showNext(ntn, ntr); +g.setColor(0).fillRect(5, 30, 41, 80).setColor(1, 1, 1).drawString(nlines.toString(), 22, 50); +var gi = setInterval(gameStep, 20); diff --git a/apps/tetris/tetris.png b/apps/tetris/tetris.png new file mode 100644 index 0000000000000000000000000000000000000000..8e884eaf35c9bfa6103591255bba423228c12192 GIT binary patch literal 492 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDB3?!H8JlO)ISkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq8r{zDi(Vu$sZZAYL$MSD+10LMXr|#1%;Y0Kz{%egIj2 z82zm0Xkxq!^40jEr;*4RsAoLkvx=jEt=e%(V>+tPBihXXOZ> dXvob^$xN%nt>KmVl{G*O44$rjF6*2UngE)~sv`gZ literal 0 HcmV?d00001 From 5a1c701adde86a7de92e81ef8a426648b29f7218 Mon Sep 17 00:00:00 2001 From: "Marko.Kl.Berkenbusch@gmail.com" Date: Fri, 25 Nov 2022 20:24:30 -0500 Subject: [PATCH 034/107] Fix metadata typo --- apps/tetris/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tetris/metadata.json b/apps/tetris/metadata.json index 778e55c24..374f97ac8 100644 --- a/apps/tetris/metadata.json +++ b/apps/tetris/metadata.json @@ -9,6 +9,6 @@ "supports" : ["BANGLEJS2"], "storage": [ {"name":"tetris.app.js","url":"app.js"}, - {"name":"tetris.img","url":"app-icon.js","evaluate":true}, + {"name":"tetris.img","url":"app-icon.js","evaluate":true} ] } From 43fdeecdb105ceaa559d1cc66919f5306340e94a Mon Sep 17 00:00:00 2001 From: "Marko.Kl.Berkenbusch@gmail.com" Date: Fri, 25 Nov 2022 20:27:44 -0500 Subject: [PATCH 035/107] Another mistake in metadata --- apps/tetris/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tetris/metadata.json b/apps/tetris/metadata.json index 374f97ac8..5669d8953 100644 --- a/apps/tetris/metadata.json +++ b/apps/tetris/metadata.json @@ -8,7 +8,7 @@ "tags": "games", "supports" : ["BANGLEJS2"], "storage": [ - {"name":"tetris.app.js","url":"app.js"}, + {"name":"tetris.app.js","url":"tetris.app.js"}, {"name":"tetris.img","url":"app-icon.js","evaluate":true} ] } From 2a506e7421f2320ba65afcadeb9d3458a8958dd1 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Sun, 20 Nov 2022 15:41:02 +0100 Subject: [PATCH 036/107] messages: split library/gui/widget into separate apps --- apps/android/ChangeLog | 1 + apps/android/metadata.json | 4 +- apps/android/settings.js | 2 +- apps/hcclock/ChangeLog | 3 +- apps/hcclock/hcclock.app.js | 2 +- apps/hcclock/metadata.json | 2 +- apps/ios/ChangeLog | 1 + apps/ios/app.js | 2 +- apps/ios/metadata.json | 4 +- apps/messagegui/ChangeLog | 79 +++++ apps/messagegui/README.md | 68 +++++ apps/{messages => messagegui}/app-icon.js | 0 .../app-newmessage.js | 4 +- apps/{messages => messagegui}/app.js | 25 +- apps/messagegui/app.png | Bin 0 -> 917 bytes apps/messagegui/boot.js | 3 + apps/messagegui/lib.js | 60 ++++ apps/messagegui/metadata.json | 22 ++ apps/{messages => messagegui}/screenshot.png | Bin apps/messages/ChangeLog | 78 +---- apps/messages/README.md | 89 ++---- apps/messages/lib.js | 283 +++++++++++------- apps/messages/metadata.json | 21 +- apps/messages/widget.js | 56 ---- apps/messagesmusic/ChangeLog | 1 + apps/messagesmusic/README.md | 6 - apps/messagesmusic/app.js | 15 +- apps/messagesmusic/metadata.json | 4 +- apps/widmessages/ChangeLog | 1 + apps/widmessages/README.md | 30 ++ apps/widmessages/app.png | Bin 0 -> 917 bytes apps/widmessages/lib.js | 8 + apps/widmessages/metadata.json | 18 ++ .../screenshot.gif} | Bin apps/widmessages/widget.js | 73 +++++ apps/widmsggrid/ChangeLog | 1 + apps/widmsggrid/README.md | 6 +- apps/widmsggrid/lib.js | 8 + apps/widmsggrid/metadata.json | 9 +- apps/widmsggrid/widget.js | 19 +- bin/sanitycheck.js | 3 +- 41 files changed, 642 insertions(+), 369 deletions(-) create mode 100644 apps/messagegui/ChangeLog create mode 100644 apps/messagegui/README.md rename apps/{messages => messagegui}/app-icon.js (100%) rename apps/{messages => messagegui}/app-newmessage.js (50%) rename apps/{messages => messagegui}/app.js (96%) create mode 100644 apps/messagegui/app.png create mode 100644 apps/messagegui/boot.js create mode 100644 apps/messagegui/lib.js create mode 100644 apps/messagegui/metadata.json rename apps/{messages => messagegui}/screenshot.png (100%) delete mode 100644 apps/messages/widget.js create mode 100644 apps/widmessages/ChangeLog create mode 100644 apps/widmessages/README.md create mode 100644 apps/widmessages/app.png create mode 100644 apps/widmessages/lib.js create mode 100644 apps/widmessages/metadata.json rename apps/{messages/screenshot-notify.gif => widmessages/screenshot.gif} (100%) create mode 100644 apps/widmessages/widget.js create mode 100644 apps/widmsggrid/lib.js diff --git a/apps/android/ChangeLog b/apps/android/ChangeLog index fcb139c94..f75638265 100644 --- a/apps/android/ChangeLog +++ b/apps/android/ChangeLog @@ -15,3 +15,4 @@ 0.15: Allow method/body/headers to be specified for `http` (needs Gadgetbridge 0.68.0b or later) 0.16: Bangle.http now fails immediately if there is no Bluetooth connection (fix #2152) 0.17: Now kick off Calendar sync as soon as connected to Gadgetbridge +0.18: Use new message library diff --git a/apps/android/metadata.json b/apps/android/metadata.json index ac47449d6..45c08a75a 100644 --- a/apps/android/metadata.json +++ b/apps/android/metadata.json @@ -2,11 +2,11 @@ "id": "android", "name": "Android Integration", "shortName": "Android", - "version": "0.17", + "version": "0.18", "description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.", "icon": "app.png", "tags": "tool,system,messages,notifications,gadgetbridge", - "dependencies": {"messages":"app"}, + "dependencies": {"messages":"module"}, "supports": ["BANGLEJS","BANGLEJS2"], "readme": "README.md", "storage": [ diff --git a/apps/android/settings.js b/apps/android/settings.js index c7c34a76f..aede4b413 100644 --- a/apps/android/settings.js +++ b/apps/android/settings.js @@ -23,7 +23,7 @@ updateSettings(); } }, - /*LANG*/"Messages" : ()=>load("messages.app.js"), + /*LANG*/"Messages" : ()=>require("message").openGUI(), }; E.showMenu(mainmenu); }) diff --git a/apps/hcclock/ChangeLog b/apps/hcclock/ChangeLog index 289c7ac2d..e2eb18be3 100644 --- a/apps/hcclock/ChangeLog +++ b/apps/hcclock/ChangeLog @@ -1,4 +1,5 @@ 0.01: Base code 0.02: Saved settings when switching color scheme 0.03: Added Button 3 opening messages (if app is installed) -0.04: Use `messages` library to check for new messages \ No newline at end of file +0.04: Use `messages` library to check for new messages +0.05: Use `messages` library to open message GUI \ No newline at end of file diff --git a/apps/hcclock/hcclock.app.js b/apps/hcclock/hcclock.app.js index 9558c052b..f12a4733e 100644 --- a/apps/hcclock/hcclock.app.js +++ b/apps/hcclock/hcclock.app.js @@ -234,7 +234,7 @@ function handleMessages() { if(!hasMessages()) return; E.showMessage("Loading Messages..."); - load("messages.app.js"); + require("messages").openGUI(); } function hasMessages() diff --git a/apps/hcclock/metadata.json b/apps/hcclock/metadata.json index b8f8c14b9..407114e25 100644 --- a/apps/hcclock/metadata.json +++ b/apps/hcclock/metadata.json @@ -1,7 +1,7 @@ { "id": "hcclock", "name": "Hi-Contrast Clock", - "version": "0.04", + "version": "0.05", "description": "Hi-Contrast Clock : A simple yet very bold clock that aims to be readable in high luninosity environments. Uses big 10x5 pixel digits. Use BTN 1 to switch background and foreground colors.", "icon": "hcclock-icon.png", "type": "clock", diff --git a/apps/ios/ChangeLog b/apps/ios/ChangeLog index 85aafb34f..5763801f5 100644 --- a/apps/ios/ChangeLog +++ b/apps/ios/ChangeLog @@ -9,3 +9,4 @@ 0.09: Enable 'ams' on new firmwares (ams/ancs can now be enabled individually) (fix #1365) 0.10: Added more bundleIds 0.11: Added letters with caron to unicodeRemap, to properly display messages in Czech language +0.12: Use new message library diff --git a/apps/ios/app.js b/apps/ios/app.js index b210886fd..2a9e8f322 100644 --- a/apps/ios/app.js +++ b/apps/ios/app.js @@ -1,2 +1,2 @@ // Config app not implemented yet -setTimeout(()=>load("messages.app.js"),10); +setTimeout(()=>require("messages").openGUI(),10); diff --git a/apps/ios/metadata.json b/apps/ios/metadata.json index a26d22cb1..5042ff1c0 100644 --- a/apps/ios/metadata.json +++ b/apps/ios/metadata.json @@ -1,11 +1,11 @@ { "id": "ios", "name": "iOS Integration", - "version": "0.11", + "version": "0.12", "description": "Display notifications/music/etc from iOS devices", "icon": "app.png", "tags": "tool,system,ios,apple,messages,notifications", - "dependencies": {"messages":"app"}, + "dependencies": {"messages":"module"}, "supports": ["BANGLEJS","BANGLEJS2"], "readme": "README.md", "storage": [ diff --git a/apps/messagegui/ChangeLog b/apps/messagegui/ChangeLog new file mode 100644 index 000000000..3dcb20fe8 --- /dev/null +++ b/apps/messagegui/ChangeLog @@ -0,0 +1,79 @@ +0.01: New App! +0.02: Add 'messages' library +0.03: Fixes for Bangle.js 1 +0.04: Add require("messages").clearAll() +0.05: Handling of message actions (ok/clear) +0.06: New messages now go at the start (fix #898) + Answering true/false now exits the messages app if no new messages + Back now marks a message as read + Clicking top-left opens a menu which allows you to delete a message or mark unread +0.07: Added settings menu with option to choose vibrate pattern and frequency (fix #909) +0.08: Fix rendering of long messages (fix #969) + buzz on new message (fix #999) +0.09: Message now disappears after 60s if no action taken and clock loads (fix 922) + Fix phone icon (#1014) +0.10: Respect the 'new' attribute if it was set from iOS integrations +0.11: Open app when touching the widget (Bangle.js 2 only) +0.12: Extra app-specific notification icons + New animated notification icon (instead of large blinking 'MESSAGES') + Added screenshots +0.13: Add /*LANG*/ comments for internationalisation + Add 'Delete All' option to message options + Now update correctly when 'require("messages").clearAll()' is called +0.14: Hide widget when all unread notifications are dismissed from phone +0.15: Don't buzz when Quiet Mode is active +0.16: Fix text wrapping so it fits the screen even if title is big (fix #1147) +0.17: Fix: Get dynamic dimensions of notify icon, fixed notification font +0.18: Use app-specific icon colors + Spread message action buttons out + Back button now goes back to list of messages + If showMessage called with no message (eg all messages deleted) now return to the clock (fix #1267) +0.19: Use a larger font for message text if it'll fit +0.20: Allow tapping on the body to show a scrollable view of the message and title in a bigger font (fix #1405, #1031) +0.21: Improve list readability on dark theme +0.22: Add Home Assistant icon + Allow repeat to be switched Off, so there is no buzzing repetition. + Also gave the widget a pixel more room to the right +0.23: Change message colors to match current theme instead of using green + Now attempt to use Large/Big/Medium fonts, and allow minimum font size to be configured +0.24: Remove left-over debug statement +0.25: Fix widget memory usage issues if message received and watch repeatedly calls Bangle.drawWidgets (fix #1550) +0.26: Setting to auto-open music +0.27: Add 'mark all read' option to popup menu (fix #1624) +0.28: Option to auto-unlock the watch when a new message arrives +0.29: Fix message list overwrites on Bangle.js 1 (fix #1642) +0.30: Add new Icons (Youtube, Twitch, MS TODO, Teams, Snapchat, Signal, Post & DHL, Nina, Lieferando, Kalender, Discord, Corona Warn, Bibel) +0.31: Option to disable icon flashing +0.32: Added an option to allow quiet mode to override message auto-open +0.33: Timeout from the message list screen if the message being displayed is removed and there is a timer going +0.34: Don't buzz for 'map' update messages +0.35: Reset graphics colors before rendering a message (possibly fix #1752) +0.36: Ensure a new message plus an almost immediate deletion of that message doesn't load the messages app (fix #1362) +0.37: Now use the setUI 'back' icon in the top left rather than specific buttons/menu items +0.38: Add telegram foss handling +0.39: Set default color for message icons according to theme +0.40: Use default Bangle formatter for booleans +0.41: Add notification icons in the widget +0.42: Fix messages ignoring "Vibrate: Off" setting +0.43: Add new Icons (Airbnb, warnwetter) +0.44: Separate buzz pattern for incoming calls +0.45: Added new app colors and icons +0.46: Add 'Vibrate Timer' option to set how long to vibrate for, and fix Repeat:off + Fix message removal from widget bar (previously caused exception as .hide has been removed) +0.47: Add new Icons (Nextbike, Mattermost, etc.) +0.48: When getting new message from the clock, only buzz once the messages app is loaded +0.49: Change messages icon (to fit within 24px) and ensure widget renders icons centrally +0.50: Add `getMessages` and `status` functions to library + Option to disable auto-open of messages + Option to make message icons monochrome (not colored) + messages widget buzz now returns a promise +0.51: Emit "message events" + Setting to hide widget + Add custom event handlers to prevent default app form loading + Move WIDGETS.messages.buzz() to require("messages").buzz() +0.52: Fix require("messages").buzz() regression + Fix background color in messages list after one unread message is shown +0.53: Messages now uses Bangle.load() to load messages app faster (if possible) +0.54: Move icons out to messageicons module +0.55: Rename to messagegui, move global message handling library to message module + Move widget to widmessage diff --git a/apps/messagegui/README.md b/apps/messagegui/README.md new file mode 100644 index 000000000..699588e1b --- /dev/null +++ b/apps/messagegui/README.md @@ -0,0 +1,68 @@ +# Messages app + +Default app to handle the display of messages and message notifications. It allows +them to be listed, viewed, and responded to. +It is installed automatically if you install `Android Integration` or `iOS Integration`. + +It is a replacement for the old `notify`/`gadgetbridge` apps. + + +## Settings + +You can change settings by going to the global `Settings` app, then `App Settings` +and `Messages`: + +* `Vibrate` - This is the pattern of buzzes that should be made when a new message is received +* `Vibrate for calls` - This is the pattern of buzzes that should be made when an incoming call is received +* `Repeat` - How often should buzzes repeat - the default of 4 means the Bangle will buzz every 4 seconds +* `Vibrate Timer` - When a new message is received when in a non-clock app, we display the message icon and +buzz every `Repeat` seconds. This is how long we continue to do that. +* `Unread Timer` - When a new message is received when showing the clock we go into the Messages app. +If there is no user input for this amount of time then the app will exit and return +to the clock where a ringing bell will be shown in the Widget bar. +* `Min Font` - The minimum font size used when displaying messages on the screen. A bigger font +is chosen if there isn't much message text, but this specifies the smallest the font should get before +it starts getting clipped. +* `Auto-Open Music` - Should the app automatically open when the phone starts playing music? +* `Unlock Watch` - Should the app unlock the watch when a new message arrives, so you can touch the buttons at the bottom of the app? + +## New Messages + +When a new message is received: + +* If you're in an app, the Bangle will buzz and a message icon appears in the Widget bar. You can tap this icon to view the message. +* If you're in a clock, the Messages app will automatically start and show the message + +When a message is shown, you'll see a screen showing the message title and text. + +* The 'back-arrow' button (or physical button on Bangle.js 2) goes back to Messages, marking the current message as read. +* The top-left icon shows more options, for instance deleting the message of marking unread +* On Bangle.js 2 you can tap on the message body to view a scrollable version of the title and text (or can use the top-left icon + `View Message`) +* If shown, the 'tick' button: + * **Android** opens the notification on the phone + * **iOS** responds positively to the notification (accept call/etc) +* If shown, the 'cross' button: + * **Android** dismisses the notification on the phone + * **iOS** responds negatively to the notification (dismiss call/etc) + +## Images +_1. Screenshot of a notification_ + +![](screenshot.png) + + +## Requests + +Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=messages%20app + +## Creator + +Gordon Williams + +## Contributors + +[Jeroen Peters](https://github.com/jeroenpeters1986) + +## Attributions + +Icons used in this app are from https://icons8.com diff --git a/apps/messages/app-icon.js b/apps/messagegui/app-icon.js similarity index 100% rename from apps/messages/app-icon.js rename to apps/messagegui/app-icon.js diff --git a/apps/messages/app-newmessage.js b/apps/messagegui/app-newmessage.js similarity index 50% rename from apps/messages/app-newmessage.js rename to apps/messagegui/app-newmessage.js index 328927c70..73d9a79c1 100644 --- a/apps/messages/app-newmessage.js +++ b/apps/messagegui/app-newmessage.js @@ -1,5 +1,5 @@ /* Called when we have a new message when we're in the clock... -BUZZ_ON_NEW_MESSAGE is set so when messages.app.js loads it knows +BUZZ_ON_NEW_MESSAGE is set so when messagegui.app.js loads it knows that it should buzz */ global.BUZZ_ON_NEW_MESSAGE = true; -eval(require("Storage").read("messages.app.js")); +eval(require("Storage").read("messagegui.app.js")); diff --git a/apps/messages/app.js b/apps/messagegui/app.js similarity index 96% rename from apps/messages/app.js rename to apps/messagegui/app.js index bebd92816..bf086dd3d 100644 --- a/apps/messages/app.js +++ b/apps/messagegui/app.js @@ -19,7 +19,6 @@ require("messages").pushMessage({"t":"add","id":1,"src":"Maps","title":"0 yd - H // call require("messages").pushMessage({"t":"add","id":"call","src":"Phone","title":"Bob","body":"12421312",positive:true,negative:true}) */ - var Layout = require("Layout"); var settings = require('Storage').readJSON("messages.settings.json", true) || {}; var fontSmall = "6x8"; @@ -49,8 +48,11 @@ to the clock. */ var unreadTimeout; /// List of all our messages var MESSAGES = require("messages").getMessages(); -if (!Array.isArray(MESSAGES)) MESSAGES=[]; -var onMessagesModified = function(msg) { + +var onMessagesModified = function(type,msg) { + if (msg.handled) return; + msg.handled = true; + require("messages").apply(msg, MESSAGES); // TODO: if new, show this new one if (msg && msg.id!=="music" && msg.new && active!="map" && !((require('Storage').readJSON('setting.json', 1) || {}).quiet)) { @@ -62,9 +64,15 @@ var onMessagesModified = function(msg) { } showMessage(msg&&msg.id); }; +Bangle.on("message", onMessagesModified); + function saveMessages() { - require("Storage").writeJSON("messages.json",MESSAGES) + require("messages").write(MESSAGES.map(m => { + delete m.show; + return m; + })); } +E.on("kill", saveMessages); function showMapMessage(msg) { active = "map"; @@ -355,12 +363,16 @@ function checkMessages(options) { } // we have >0 messages var newMessages = MESSAGES.filter(m=>m.new&&m.id!="music"); + var toShow = MESSAGES.find(m=>m.show); + if (toShow) { + newMessages.unshift(toShow); + } // If we have a new message, show it - if (options.showMsgIfUnread && newMessages.length) { + if ((toShow||options.showMsgIfUnread) && newMessages.length) { showMessage(newMessages[0].id); // buzz after showMessage, so being busy during layout doesn't affect the buzz pattern if (global.BUZZ_ON_NEW_MESSAGE) { - // this is set if we entered the messages app by loading `messages.new.js` + // this is set if we entered the messages app by loading `messagegui.new.js` // ... but only buzz the first time we view a new message global.BUZZ_ON_NEW_MESSAGE = false; // messages.buzz respects quiet mode - no need to check here @@ -428,6 +440,7 @@ function cancelReloadTimeout() { g.clear(); Bangle.loadWidgets(); +require("messages").toggleWidget(false); Bangle.drawWidgets(); setTimeout(() => { diff --git a/apps/messagegui/app.png b/apps/messagegui/app.png new file mode 100644 index 0000000000000000000000000000000000000000..c9177692e282e1247ced30f6ec0e2d14dc6dfa25 GIT binary patch literal 917 zcmV;G18V$CmK~!jg?U~I_6G0fppJ|s5%ZG(_jU{sLoS)M|Tx5HNwbU93{dM|XETG(}U{r87GP zP5QgOGds^S`_B9Bv_O#}MT#6JB;SE&AFiJ#PVFW@+5j{Fs1U4W3&1i!=cz7@tv)#Y zDW6G)8t?@prO7h)FeSJHz+qQqp6HZfw0bu&7zz6JtOi;d@C75Ko8|7;0Ims@mp=#J3E*{IZSCaRo7jz0My7S0nqA z?9Rp%4b8HI>M{r3e%-^}7vKLHd-Y5ye(oBGDVaVJ^4o8QwhZKo5Ba?~SxzwZA%&Tb z+lVS@06>def}RU5^jtiFA3Jn^jtCRn26CIwMDK4Qfz}EHS`WVSdt3w|zjyyIcTdI< z_Iq%ulJDxlW!-Ky5!DO<4g;b}p(qo~D|bzZD}}iwxHqISKZAMo>{p|R3Ib$Ig#6z9 zuUuBR4)M~4hRY-CJX3}9-`~ir3~U~mio^M77O*m~S^yz@5V~R(vM@mB3ZaDu3db9> zn1uo77y$nJqBwM-ljmkZQv)kQbrDK2S{O|%(5EZ+>pq)BEvr!VZekF?f^bdwGcVVy z-?JKEX&@5x?N#k0IslB|Xwyjp=o7hSt>fM8D`~5NdH=;!|7gueKnEx>+CfPJ#Q*e| r1fk1>I_9WBo>`?$ks?Kk{5$*tT^fbQe@cvs00000NkvXXu0mjfYw(!I literal 0 HcmV?d00001 diff --git a/apps/messagegui/boot.js b/apps/messagegui/boot.js new file mode 100644 index 000000000..4592f825d --- /dev/null +++ b/apps/messagegui/boot.js @@ -0,0 +1,3 @@ +(function() { + Bangle.on("message", (type, msg) => require("messagegui").listener(type, msg)); +})(); \ No newline at end of file diff --git a/apps/messagegui/lib.js b/apps/messagegui/lib.js new file mode 100644 index 000000000..5b07dd160 --- /dev/null +++ b/apps/messagegui/lib.js @@ -0,0 +1,60 @@ +/** + * Listener set up in boot.js, calls into here to keep boot.js short + */ +exports.listener = function(type, msg) { + // Default handler: Launch the GUI for all unhandled messages (except music if disabled in settings) + if (msg.handled || (global.__FILE__ && __FILE__.startsWith('messagegui.'))) return; // already handled or app open + + // if no new messages now, make sure we don't load the messages app + if (exports.messageTimeout && !msg.new && require("messages").status(msg) !== "new") { + clearTimeout(exports.messageTimeout); + delete exports.messageTimeout; + } + if (msg.t==="remove") return; + + const appSettings = require("Storage").readJSON("messages.settings.json", 1) || {}; + let loadMessages = (Bangle.CLOCK || event.important); + if (type==="music") { + if (Bangle.CLOCK && msg.new && appSettings.openMusic) loadMessages = true; + else return; + } + require("messages").save(msg); + msg.handled = true; + if (msg.t!=="add" || !msg.new) { + return; + } + + const quiet = (require("Storage").readJSON("setting.json", 1) || {}).quiet; + const unlockWatch = appSettings.unlockWatch; + // don't auto-open messages in quiet mode if quietNoAutOpn is true + if ((quiet && appSettings.quietNoAutOpn) || appSettings.noAutOpn) + loadMessages = false; + + // after a delay load the app, to ensure we have all the messages + if (exports.messageTimeout) clearTimeout(exports.messageTimeout); + exports.messageTimeout = setTimeout(function() { + delete exports.messageTimeout; + if (type!=="music") { + if (!loadMessages) return require("messages").buzz(msg.src); // no opening the app, just buzz + if (!quiet && unlockWatch) { + Bangle.setLocked(false); + Bangle.setLCDPower(1); // turn screen on + } + } + exports.open(msg); + }, 500); +}; + +/** + * Launch GUI app with given message + * @param {object} msg + */ +exports.open = function(msg) { + if (msg && msg.id && !msg.show) { + // store which message to load + msg.show = 1; + require("messages").save(msg, {force: 1}); + } + + Bangle.load((msg && msg.new && msg.id!=="music") ? "messagegui.new.js" : "messagegui.app.js"); +}; diff --git a/apps/messagegui/metadata.json b/apps/messagegui/metadata.json new file mode 100644 index 000000000..7444050fd --- /dev/null +++ b/apps/messagegui/metadata.json @@ -0,0 +1,22 @@ +{ + "id": "messagegui", + "name": "Messages", + "version": "0.55", + "description": "Default app to display notifications from iOS and Gadgetbridge/Android", + "icon": "app.png", + "type": "app", + "tags": "tool,system", + "supports": ["BANGLEJS","BANGLEJS2"], + "dependencies" : { "messageicons":"module" }, + "provides_modules": ["messagegui"], + "readme": "README.md", + "storage": [ + {"name":"messagegui","url":"lib.js"}, + {"name":"messagegui.app.js","url":"app.js"}, + {"name":"messagegui.new.js","url":"app-newmessage.js"}, + {"name":"messagegui.boot.js","url":"boot.js"}, + {"name":"messagegui.img","url":"app-icon.js","evaluate":true} + ], + "screenshots": [{"url":"screenshot.png"}], + "sortorder": -9 +} diff --git a/apps/messages/screenshot.png b/apps/messagegui/screenshot.png similarity index 100% rename from apps/messages/screenshot.png rename to apps/messagegui/screenshot.png diff --git a/apps/messages/ChangeLog b/apps/messages/ChangeLog index ee27d41c6..b0a84783b 100644 --- a/apps/messages/ChangeLog +++ b/apps/messages/ChangeLog @@ -1,77 +1 @@ -0.01: New App! -0.02: Add 'messages' library -0.03: Fixes for Bangle.js 1 -0.04: Add require("messages").clearAll() -0.05: Handling of message actions (ok/clear) -0.06: New messages now go at the start (fix #898) - Answering true/false now exits the messages app if no new messages - Back now marks a message as read - Clicking top-left opens a menu which allows you to delete a message or mark unread -0.07: Added settings menu with option to choose vibrate pattern and frequency (fix #909) -0.08: Fix rendering of long messages (fix #969) - buzz on new message (fix #999) -0.09: Message now disappears after 60s if no action taken and clock loads (fix 922) - Fix phone icon (#1014) -0.10: Respect the 'new' attribute if it was set from iOS integrations -0.11: Open app when touching the widget (Bangle.js 2 only) -0.12: Extra app-specific notification icons - New animated notification icon (instead of large blinking 'MESSAGES') - Added screenshots -0.13: Add /*LANG*/ comments for internationalisation - Add 'Delete All' option to message options - Now update correctly when 'require("messages").clearAll()' is called -0.14: Hide widget when all unread notifications are dismissed from phone -0.15: Don't buzz when Quiet Mode is active -0.16: Fix text wrapping so it fits the screen even if title is big (fix #1147) -0.17: Fix: Get dynamic dimensions of notify icon, fixed notification font -0.18: Use app-specific icon colors - Spread message action buttons out - Back button now goes back to list of messages - If showMessage called with no message (eg all messages deleted) now return to the clock (fix #1267) -0.19: Use a larger font for message text if it'll fit -0.20: Allow tapping on the body to show a scrollable view of the message and title in a bigger font (fix #1405, #1031) -0.21: Improve list readability on dark theme -0.22: Add Home Assistant icon - Allow repeat to be switched Off, so there is no buzzing repetition. - Also gave the widget a pixel more room to the right -0.23: Change message colors to match current theme instead of using green - Now attempt to use Large/Big/Medium fonts, and allow minimum font size to be configured -0.24: Remove left-over debug statement -0.25: Fix widget memory usage issues if message received and watch repeatedly calls Bangle.drawWidgets (fix #1550) -0.26: Setting to auto-open music -0.27: Add 'mark all read' option to popup menu (fix #1624) -0.28: Option to auto-unlock the watch when a new message arrives -0.29: Fix message list overwrites on Bangle.js 1 (fix #1642) -0.30: Add new Icons (Youtube, Twitch, MS TODO, Teams, Snapchat, Signal, Post & DHL, Nina, Lieferando, Kalender, Discord, Corona Warn, Bibel) -0.31: Option to disable icon flashing -0.32: Added an option to allow quiet mode to override message auto-open -0.33: Timeout from the message list screen if the message being displayed is removed and there is a timer going -0.34: Don't buzz for 'map' update messages -0.35: Reset graphics colors before rendering a message (possibly fix #1752) -0.36: Ensure a new message plus an almost immediate deletion of that message doesn't load the messages app (fix #1362) -0.37: Now use the setUI 'back' icon in the top left rather than specific buttons/menu items -0.38: Add telegram foss handling -0.39: Set default color for message icons according to theme -0.40: Use default Bangle formatter for booleans -0.41: Add notification icons in the widget -0.42: Fix messages ignoring "Vibrate: Off" setting -0.43: Add new Icons (Airbnb, warnwetter) -0.44: Separate buzz pattern for incoming calls -0.45: Added new app colors and icons -0.46: Add 'Vibrate Timer' option to set how long to vibrate for, and fix Repeat:off - Fix message removal from widget bar (previously caused exception as .hide has been removed) -0.47: Add new Icons (Nextbike, Mattermost, etc.) -0.48: When getting new message from the clock, only buzz once the messages app is loaded -0.49: Change messages icon (to fit within 24px) and ensure widget renders icons centrally -0.50: Add `getMessages` and `status` functions to library - Option to disable auto-open of messages - Option to make message icons monochrome (not colored) - messages widget buzz now returns a promise -0.51: Emit "message events" - Setting to hide widget - Add custom event handlers to prevent default app form loading - Move WIDGETS.messages.buzz() to require("messages").buzz() -0.52: Fix require("messages").buzz() regression - Fix background color in messages list after one unread message is shown -0.53: Messages now uses Bangle.load() to load messages app faster (if possible) -0.54: Move icons out to messageicons module +0.01: Moved messages library into standalone library \ No newline at end of file diff --git a/apps/messages/README.md b/apps/messages/README.md index 72a989146..1a6758311 100644 --- a/apps/messages/README.md +++ b/apps/messages/README.md @@ -1,62 +1,25 @@ -# Messages app +# Messages library -This app handles the display of messages and message notifications. It stores -a list of currently received messages and allows them to be listed, viewed, -and responded to. +This library handles the passing of messages. It can storess a list of messages +and allows them to be retrieved by other apps. -It is a replacement for the old `notify`/`gadgetbridge` apps. +## Example -## Settings +Assuming you are using GadgetBridge and "overlay notifications": -You can change settings by going to the global `Settings` app, then `App Settings` -and `Messages`: - -* `Vibrate` - This is the pattern of buzzes that should be made when a new message is received -* `Vibrate for calls` - This is the pattern of buzzes that should be made when an incoming call is received -* `Repeat` - How often should buzzes repeat - the default of 4 means the Bangle will buzz every 4 seconds -* `Vibrate Timer` - When a new message is received when in a non-clock app, we display the message icon and -buzz every `Repeat` seconds. This is how long we continue to do that. -* `Unread Timer` - When a new message is received when showing the clock we go into the Messages app. -If there is no user input for this amount of time then the app will exit and return -to the clock where a ringing bell will be shown in the Widget bar. -* `Min Font` - The minimum font size used when displaying messages on the screen. A bigger font -is chosen if there isn't much message text, but this specifies the smallest the font should get before -it starts getting clipped. -* `Auto-Open Music` - Should the app automatically open when the phone starts playing music? -* `Unlock Watch` - Should the app unlock the watch when a new message arrives, so you can touch the buttons at the bottom of the app? -* `Flash Icon` - Toggle flashing of the widget icon. -* `Widget messages` - The maximum amount of message icons to show on the widget, or `Hide` the widget completely. - -## New Messages - -When a new message is received: - -* If you're in an app, the Bangle will buzz and a message icon appears in the Widget bar. You can tap this icon to view the message. -* If you're in a clock, the Messages app will automatically start and show the message - -When a message is shown, you'll see a screen showing the message title and text. - -* The 'back-arrow' button (or physical button on Bangle.js 2) goes back to Messages, marking the current message as read. -* The top-left icon shows more options, for instance deleting the message of marking unread -* On Bangle.js 2 you can tap on the message body to view a scrollable version of the title and text (or can use the top-left icon + `View Message`) -* If shown, the 'tick' button: - * **Android** opens the notification on the phone - * **iOS** responds positively to the notification (accept call/etc) -* If shown, the 'cross' button: - * **Android** dismisses the notification on the phone - * **iOS** responds negatively to the notification (dismiss call/etc) - -## Images -_1. Screenshot of a notification_ - -![](screenshot.png) - -_2. What the notify icon looks like (it's touchable on Bangle.js2!)_ - -![](screenshot-notify.gif) +1. Gadgetbridge sends an event to your watch for an incoming message +2. The `android` app parses the message, and calls `require("messages").pushMessage({/** the message */})` +3. `require("messages")` (provided by `messagelib`) calls `Bangle.emit("message", "text", {/** the message */})` +4. Overlay Notifications shows the message in an overlay, and marks it as `handled` +5. The default GUI app (`messages`) sees the event is marked as `handled`, so does nothing. +6. The default widget (`widmessages`) does nothing with `handled`, and shows a notification icon. +7. You tap the notification, in order to open the full GUI Overlay Notifications + calls `require("messages").openGUI({/** the message */})` +8. The default GUI app (`messages`) sees the "messageGUI" event, and launches itself -## Events (for app/widget developers) + +## Events When a new message arrives, a `"message"` event is emitted, you can listen for it like this: @@ -64,9 +27,8 @@ it like this: ```js myMessageListener = Bangle.on("message", (type, message)=>{ if (message.handled) return; // another app already handled this message - // is one of "text", "call", "alarm", "map", "music", or "clearAll" - if (type === "clearAll") return; // not a message - // see `messages/lib.js` for possible formats + // is one of "text", "call", "alarm", "map", or "music" + // see `messagelib/lib.js` for possible formats // message.t could be "add", "modify" or "remove" E.showMessage(`${message.title}\n${message.body}`, `${message.t} ${type} message`); // You can prevent the default `message` app from loading by setting `message.handled = true`: @@ -74,10 +36,23 @@ myMessageListener = Bangle.on("message", (type, message)=>{ }); ``` +Apps can launch the full GUI by calling `require("messages").openGUI()`, if you +want to write your own GUI, it should include boot code that listens for +`"messageGUI"` events: + +```js +Bangle.on("messageGUI", message=>{ + if (message.handled) return; // another app already opened it's GUI + message.handled = true; // prevent other apps form launching + Bangle.load("my_message_gui.app.js"); +}) + +``` + ## Requests -Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=messages%20app +Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=messagelib%library ## Creator diff --git a/apps/messages/lib.js b/apps/messages/lib.js index 0ed03e4b6..fa1419c95 100644 --- a/apps/messages/lib.js +++ b/apps/messages/lib.js @@ -1,10 +1,16 @@ -function openMusic() { - // only read settings file for first music message - if ("undefined"==typeof exports._openMusic) { - exports._openMusic = !!((require('Storage').readJSON("messages.settings.json", true) || {}).openMusic); - } - return exports._openMusic; +exports.music = {}; +/** + * Emit "message" event with appropriate type from Bangle + * @param {object} msg + */ +function emit(msg) { + let type = "text"; + if (["call", "music", "map"].includes(msg.id)) type = msg.id; + if (type==="music" && msg.t!=="remove" && (!("state" in msg) || (!("track" in msg)))) return; // wait for complete music info + if (msg.src && msg.src.toLowerCase().startsWith("alarm")) type = "alarm"; + Bangle.emit("message", type, msg); } + /* Push a new message onto messages queue, event is: {t:"add",id:int, src,title,subject,body,sender,tel, important:bool, new:bool} {t:"add",id:int, id:"music", state, artist, track, etc} // add new @@ -12,125 +18,178 @@ function openMusic() { {t:"modify",id:int, title:string} // modified */ exports.pushMessage = function(event) { - var messages = exports.getMessages(); // now modify/delete as appropriate - var mIdx = messages.findIndex(m=>m.id==event.id); - if (event.t=="remove") { - if (mIdx>=0) messages.splice(mIdx, 1); // remove item - mIdx=-1; + if (event.t==="remove") { + if (event.id==="music") exports.music = {}; } else { // add/modify - if (event.t=="add"){ - if(event.new === undefined ) { // If 'new' has not been set yet, set it - event.new=true; // Assume it should be new - } + if (event.t==="add") { + if (event.new===undefined) event.new = true; // Assume it should be new + } else if (event.t==="modify") { + const old = exports.getMessages().find(m => m.id===event.id); + if (old) event = Object.assign(old, event); } - if (mIdx<0) { - mIdx=0; - messages.unshift(event); // add new messages to the beginning - } - else Object.assign(messages[mIdx], event); - if (event.id=="music" && messages[mIdx].state=="play") { - messages[mIdx].new = true; // new track, or playback (re)started - type = 'music'; + + // combine musicinfo and musicstate events + if (event.id==="music") { + if (event.state==="play") event.new = true; // new track, or playback (re)started + event = Object.assign(exports.music, event); } } - require("Storage").writeJSON("messages.json",messages); - var message = mIdx<0 ? {id:event.id, t:'remove'} : messages[mIdx]; - // if in app, process immediately - if ("undefined"!=typeof MESSAGES) return onMessagesModified(message); - // emit message event - var type = 'text'; - if (["call", "music", "map"].includes(message.id)) type = message.id; - if (message.src && message.src.toLowerCase().startsWith("alarm")) type = "alarm"; - Bangle.emit("message", type, message); - // update the widget icons shown - if (global.WIDGETS && WIDGETS.messages) WIDGETS.messages.update(messages,true); - var handleMessage = () => { - // if no new messages now, make sure we don't load the messages app - if (event.t=="remove" && exports.messageTimeout && !messages.some(m => m.new)) { - clearTimeout(exports.messageTimeout); - delete exports.messageTimeout; - } - // ok, saved now - if (event.id=="music" && Bangle.CLOCK && messages[mIdx].new && openMusic()) { - // just load the app to display music: no buzzing - Bangle.load("messages.app.js"); - } else if (event.t!="add") { - // we only care if it's new - return; - } else if (event.new==false) { - return; - } - // otherwise load messages/show widget - var loadMessages = Bangle.CLOCK || event.important; - var quiet = (require('Storage').readJSON('setting.json', 1) || {}).quiet; - var appSettings = require('Storage').readJSON('messages.settings.json', 1) || {}; - var unlockWatch = appSettings.unlockWatch; - // don't auto-open messages in quiet mode if quietNoAutOpn is true - if ((quiet && appSettings.quietNoAutOpn) || appSettings.noAutOpn) - loadMessages = false; - delete appSettings; - // after a delay load the app, to ensure we have all the messages - if (exports.messageTimeout) clearTimeout(exports.messageTimeout); - exports.messageTimeout = setTimeout(function() { - exports.messageTimeout = undefined; - // if we're in a clock or it's important, go straight to messages app - if (loadMessages) { - if (!quiet && unlockWatch) { - Bangle.setLocked(false); - Bangle.setLCDPower(1); // turn screen on - } - // we will buzz when we enter the messages app - return Bangle.load("messages.new.js"); - } - if (global.WIDGETS && WIDGETS.messages) WIDGETS.messages.update(messages); - exports.buzz(message.src); - }, 500); - }; - setTimeout(()=>{ - if (!message.handled) handleMessage(); - },0); -} -/// Remove all messages + // reset state (just in case) + delete event.handled; + delete event.saved; + emit(event); +}; + +/** + * Save a single message to flash + * Also sets msg.saved=true + * + * @param {object} msg + * @param {object} [options={}] Options: + * {boolean} [force=false] Force save even if msg.saved is already set + */ +exports.save = function(msg, options) { + if (!options) options = {}; + if (msg.saved && !options.force) return; //already saved + let messages = exports.getMessages(); + exports.apply(msg, messages); + exports.write(messages); + msg.saved = true; +}; + +/** + * Apply incoming event to array of messages + * + * @param {object} event Event to apply + * @param {array} messages Array of messages, *will be modified in-place* + * @return {array} Modified messages array + */ +exports.apply = function(event, messages) { + if (!event || !event.id) return messages; + const mIdx = messages.findIndex(m => m.id===event.id); + if (event.t==="remove") { + if (mIdx<0) return messages; // already gone -> nothing to do + messages.splice(mIdx, 1); + } else if (event.t==="add") { + if (mIdx>=0) messages.splice(mIdx, 1); // duplicate ID! erase previous version + messages.unshift(event); + } else if (event.t==="modify") { + if (mIdx>=0) messages[mIdx] = Object.assign(messages[mIdx], event); + else messages.unshift(event); + } + return messages; +}; + +/** + * Accept a call (or other acceptable event) + * @param {object} msg + */ +exports.accept = function(msg) { + if (msg.positive) Bangle.messageResponse(msg, true); +}; + +/** + * Dismiss a message (if applicable), and erase it from flash + * Emits a "message" event with t="remove", only if message existed + * + * @param {object} msg + */ +exports.dismiss = function(msg) { + if (msg.negative) Bangle.messageResponse(msg, false); + let messages = exports.getMessages(); + const mIdx = messages.findIndex(m=>m.id===msg.id); + if (mIdx<0) return; + messages.splice(mIdx, 1); + exports.write(messages); + if (msg.t==="remove") return; // already removed, don't re-emit + msg.t = "remove"; + emit(msg); // emit t="remove", so e.g. widgets know to update +}; + +/** + * Emit a "type=openGUI" event, to open GUI app + * + * @param {object} [msg={}] Message the app should show + */ +exports.openGUI = function(msg) { + if (!require("Storage").read("messagegui")) return; // "messagegui" module is missing! + // Mark the event as unhandled for GUI, but leave passed arguments intact + let copy = Object.assign({}, msg); + delete copy.handled; + require("messagegui").open(copy); +}; + +/** + * Show/hide the messages widget + * + * @param {boolean} show + */ +exports.toggleWidget = function(show) { + if (!require("Storage").read("messagewidget")) return; // "messagewidget" module is missing! + if (show) require("messagewidget").show(); + else require("messagewidget").hide(); +}; + +/** + * Replace all stored messages + * @param {array} messages Messages to save + */ +exports.write = function(messages) { + require("Storage").writeJSON("messages.json", messages.map(m => { + // we never want to save saved/handled status to file; + delete m.saved; + delete m.handled; + return m; + })); +}; +/** + * Erase all messages + */ exports.clearAll = function() { - if ("undefined"!= typeof MESSAGES) { // we're in a messages app, clear that as well - MESSAGES = []; - } - // Clear all messages - require("Storage").writeJSON("messages.json", []); - // if we have a widget, update it - if (global.WIDGETS && WIDGETS.messages) - WIDGETS.messages.update([]); - // let message listeners know - Bangle.emit("message", "clearAll", {}); // guarantee listeners an object as `message` - // clearAll cannot be marked as "handled" - // update app if in app - if ("function"== typeof onMessagesModified) onMessagesModified(); + exports.write([]); + Bangle.emit("message", "clearAll", {}); } /** + * Get saved messages + * + * Optionally pass in a message to apply to the list, this is for event handlers: + * By passing the message from the event, you can make sure the list is up-to-date, + * even if the message has not been saved (yet) + * + * Example: + * Bangle.on("message", (type, msg) => { + * console.log("All messages:", require("messages").getMessages(msg)); + * }); + * + * @param {object} [withMessage] Apply this event to messages * @returns {array} All messages */ -exports.getMessages = function() { - if ("undefined"!=typeof MESSAGES) return MESSAGES; // loaded/managed by app - return require("Storage").readJSON("messages.json",1)||[]; -} +exports.getMessages = function(withMessage) { + let messages = require("Storage").readJSON("messages.json", true); + messages = Array.isArray(messages) ? messages : []; // make sure we always return an array + if (withMessage && withMessage.id) exports.apply(withMessage, messages); + return messages; +}; /** * Check if there are any messages + * + * @param {object} [withMessage] Apply this event to messages, see getMessages * @returns {string} "new"/"old"/"none" */ - exports.status = function() { +exports.status = function(withMessage) { try { - let status= "none"; - for(const m of exports.getMessages()) { + let status = "none"; + for(const m of exports.getMessages(withMessage)) { if (["music", "map"].includes(m.id)) continue; if (m.new) return "new"; status = "old"; } return status; } catch(e) { - return "none"; // don't bother e.g. the widget with errors + return "none"; // don't bother callers with errors } }; @@ -141,24 +200,24 @@ exports.getMessages = function() { */ exports.buzz = function(msgSrc) { exports.stopBuzz(); // cancel any previous buzz timeouts - if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return Promise.resolve(); // never buzz during Quiet Mode - var msgSettings = require('Storage').readJSON("messages.settings.json", true) || {}; - var pattern; - if (msgSrc && msgSrc.toLowerCase() === "phone") { + if ((require("Storage").readJSON("setting.json", 1) || {}).quiet) return Promise.resolve(); // never buzz during Quiet Mode + const msgSettings = require("Storage").readJSON("messages.settings.json", true) || {}; + let pattern; + if (msgSrc && msgSrc.toLowerCase()==="phone") { // special vibration pattern for incoming calls pattern = msgSettings.vibrateCalls; } else { pattern = msgSettings.vibrate; } - if (pattern === undefined) { pattern = ":"; } // pattern may be "", so we can't use || ":" here + if (pattern===undefined) { pattern = ":"; } // pattern may be "", so we can't use || ":" here if (!pattern) return Promise.resolve(); - var repeat = msgSettings.repeat; - if (repeat===undefined) repeat=4; // repeat may be zero + let repeat = msgSettings.repeat; + if (repeat===undefined) repeat = 4; // repeat may be zero if (repeat) { - exports.buzzTimeout = setTimeout(()=>require("buzz").pattern(pattern), repeat*1000); - var vibrateTimeout = msgSettings.vibrateTimeout; - if (vibrateTimeout===undefined) vibrateTimeout=60; + exports.buzzTimeout = setTimeout(() => require("buzz").pattern(pattern), repeat*1000); + let vibrateTimeout = msgSettings.vibrateTimeout; + if (vibrateTimeout===undefined) vibrateTimeout = 60; if (vibrateTimeout && !exports.stopTimeout) exports.stopTimeout = setTimeout(exports.stopBuzz, vibrateTimeout*1000); } return require("buzz").pattern(pattern); diff --git a/apps/messages/metadata.json b/apps/messages/metadata.json index f3051958e..f94f01c26 100644 --- a/apps/messages/metadata.json +++ b/apps/messages/metadata.json @@ -1,23 +1,18 @@ { "id": "messages", "name": "Messages", - "version": "0.54", - "description": "App to display notifications from iOS and Gadgetbridge/Android", + "version": "0.01", + "description": "Library to handle message events", "icon": "app.png", - "type": "app", + "type": "module", "tags": "tool,system", "supports": ["BANGLEJS","BANGLEJS2"], - "dependencies" : { "messageicons":"module" }, + "provides_modules" : ["messages"], + "dependencies" : { "messagegui":"module","messagewidget":"module" }, "readme": "README.md", "storage": [ - {"name":"messages.app.js","url":"app.js"}, - {"name":"messages.new.js","url":"app-newmessage.js"}, - {"name":"messages.settings.js","url":"settings.js"}, - {"name":"messages.img","url":"app-icon.js","evaluate":true}, - {"name":"messages.wid.js","url":"widget.js"}, - {"name":"messages","url":"lib.js"} + {"name":"messages","url":"lib.js"}, + {"name":"messages.settings.js","url":"settings.js"} ], - "data": [{"name":"messages.json"},{"name":"messages.settings.json"}], - "screenshots": [{"url":"screenshot.png"},{"url":"screenshot-notify.gif"}], - "sortorder": -9 + "data": [{"name":"messages.json"},{"name":"messages.settings.json"}] } diff --git a/apps/messages/widget.js b/apps/messages/widget.js deleted file mode 100644 index c0dcd132f..000000000 --- a/apps/messages/widget.js +++ /dev/null @@ -1,56 +0,0 @@ -(() => { -if ((require('Storage').readJSON("messages.settings.json", true) || {}).maxMessages===0) return; - -function filterMessages(msgs) { - return msgs.filter(msg => msg.new && msg.id != "music") - .map(m => m.src) // we only need this for icon/color - .filter((msg, i, arr) => arr.findIndex(nmsg => msg.src == nmsg.src) == i); -} - -WIDGETS["messages"]={area:"tl", width:0, draw:function(recall) { - // If we had a setTimeout queued from the last time we were called, remove it - if (WIDGETS["messages"].i) { - clearTimeout(WIDGETS["messages"].i); - delete WIDGETS["messages"].i; - } - Bangle.removeListener('touch', this.touch); - if (!this.width) return; - let settings = Object.assign({flash:true, maxMessages:3},require('Storage').readJSON("messages.settings.json", true) || {}); - if (recall !== true || settings.flash) { - var msgsShown = E.clip(this.msgs.length, 0, settings.maxMessages); - g.reset().clearRect(this.x, this.y, this.x+this.width, this.y+23); - for(let i = 0;i < msgsShown;i++) { - const msg = this.msgs[i]; - const colors = [g.theme.bg, - require("messageicons").getColor(msg, {settings:settings})]; - if (settings.flash && ((Date.now()/1000)&1)) { - if (colors[1] == g.theme.fg) { - colors.reverse(); - } else { - colors[1] = g.theme.fg; - } - } - g.setColor(colors[1]).setBgColor(colors[0]); - // draw the icon, or '...' if too many messages - g.drawImage(i == (settings.maxMessages - 1) && this.msgs.length > settings.maxMessages ? atob("EASBAGGG88/zz2GG") : require("messageicons").getImage(msg), - this.x + 12 + i * 24, this.y + 12, {rotate:0/*force centering*/}); - } - } - WIDGETS["messages"].i=setTimeout(()=>WIDGETS["messages"].draw(true), 1000); - if (process.env.HWVERSION>1) Bangle.on('touch', this.touch); -},update:function(rawMsgs) { - const settings = Object.assign({maxMessages:3},require('Storage').readJSON("messages.settings.json", true) || {}); - this.msgs = filterMessages(rawMsgs); - this.width = 24 * E.clip(this.msgs.length, 0, settings.maxMessages); - Bangle.drawWidgets(); -},touch:function(b,c) { - var w=WIDGETS["messages"]; - if (!w||!w.width||c.xw.x+w.width||c.yw.y+24) return; - load("messages.app.js"); -}}; - -/* We might have returned here if we were in the Messages app for a -message but then the watch was never viewed. */ -if (global.MESSAGES===undefined) - WIDGETS["messages"].update(require("messages").getMessages()); -})(); diff --git a/apps/messagesmusic/ChangeLog b/apps/messagesmusic/ChangeLog index 9f4cafb0e..65fd09686 100644 --- a/apps/messagesmusic/ChangeLog +++ b/apps/messagesmusic/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: Remove one line of code that didn't do anything other than in some instances hinder the function of the app. +0.03: Use the new messages library \ No newline at end of file diff --git a/apps/messagesmusic/README.md b/apps/messagesmusic/README.md index 85608118d..9a50de93e 100644 --- a/apps/messagesmusic/README.md +++ b/apps/messagesmusic/README.md @@ -1,15 +1,9 @@ Hacky app that uses Messages app and it's library to push a message that triggers the music controls. It's nearly not an app, and yet it moves. -This app require Messages setting 'Auto-open Music' to be 'Yes'. If it isn't, the app will change it to 'Yes' and let it stay that way. - Making the music controls accessible this way lets one start a music stream on the phone in some situations even though the message app didn't receive a music message from gadgetbridge to begin with. (I think.) It is suggested to use Messages Music along side the app Quick Launch. -Messages Music v0.02 has been verified to work with Messages v0.41 on Bangle.js 2 fw2v14. - -Messages Music should work with forks of the original Messages app. At least as long as functions pushMessage() in the library and showMusicMessage() in app.js hasn't been changed too much. - Messages app is created by Gordon Williams with contributions from [Jeroen Peters](https://github.com/jeroenpeters1986). The icon used for this app is from [https://icons8.com](https://icons8.com). diff --git a/apps/messagesmusic/app.js b/apps/messagesmusic/app.js index 27f3f6e4d..11e7e5a4e 100644 --- a/apps/messagesmusic/app.js +++ b/apps/messagesmusic/app.js @@ -1,14 +1 @@ -let showMusic = () => { - Bangle.CLOCK = 1; // To pass condition in messages library - require('messages').pushMessage({"t":"add","artist":" ","album":" ","track":" ","dur":0,"c":-1,"n":-1,"id":"music","title":"Music","state":"play","new":true}); -}; - -var settings = require('Storage').readJSON('messages.settings.json', true) || {}; //read settings if they exist else set to empty dict -if (!settings.openMusic) { - settings.openMusic = true; // This app/hack works as intended only if this setting is true - require('Storage').writeJSON('messages.settings.json', settings); - E.showMessage("First run:\n\nMessages setting\n\n 'Auto-Open Music'\n\n set to 'Yes'"); - setTimeout(()=>{showMusic();}, 5000); -} else { - showMusic(); -} +require('messages').openGUI({"t":"add","artist":" ","album":" ","track":" ","dur":0,"c":-1,"n":-1,"id":"music","title":"Music","state":"play","new":true}); diff --git a/apps/messagesmusic/metadata.json b/apps/messagesmusic/metadata.json index c29ffbc34..ce5281563 100644 --- a/apps/messagesmusic/metadata.json +++ b/apps/messagesmusic/metadata.json @@ -1,7 +1,7 @@ { "id": "messagesmusic", "name":"Messages Music", - "version":"0.02", + "version":"0.03", "description": "Uses Messages library to push a music message which in turn displays Messages app music controls", "icon":"app.png", "type": "app", @@ -13,6 +13,6 @@ {"name":"messagesmusic.app.js","url":"app.js"}, {"name":"messagesmusic.img","url":"app-icon.js","evaluate":true} ], - "dependencies": {"messages":"app"} + "dependencies" : { "messagelib":"module" } } diff --git a/apps/widmessages/ChangeLog b/apps/widmessages/ChangeLog new file mode 100644 index 000000000..3a41005e9 --- /dev/null +++ b/apps/widmessages/ChangeLog @@ -0,0 +1 @@ +0.01: Moved messages widget into standalone widget app \ No newline at end of file diff --git a/apps/widmessages/README.md b/apps/widmessages/README.md new file mode 100644 index 000000000..398cb4fa8 --- /dev/null +++ b/apps/widmessages/README.md @@ -0,0 +1,30 @@ +# Messages widget + +The default widget to show icons for new messages +It is installed automatically if you install `Android Integration` or `iOS Integration`. + +![screenshot](screenshot.gif) + +## Settings +You can change settings by going to the global `Settings` app, then `App Settings` +and `Messages`: + +* `Flash icon` Toggle flashing of the widget icons. + +* `Widget messages` Not used by this widget. + +## Requests + +Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=widmessages%widget + +## Creator + +Gordon Williams + +## Contributors + +[Jeroen Peters](https://github.com/jeroenpeters1986) + +## Attributions + +Icons used in this app are from https://icons8.com diff --git a/apps/widmessages/app.png b/apps/widmessages/app.png new file mode 100644 index 0000000000000000000000000000000000000000..c9177692e282e1247ced30f6ec0e2d14dc6dfa25 GIT binary patch literal 917 zcmV;G18V$CmK~!jg?U~I_6G0fppJ|s5%ZG(_jU{sLoS)M|Tx5HNwbU93{dM|XETG(}U{r87GP zP5QgOGds^S`_B9Bv_O#}MT#6JB;SE&AFiJ#PVFW@+5j{Fs1U4W3&1i!=cz7@tv)#Y zDW6G)8t?@prO7h)FeSJHz+qQqp6HZfw0bu&7zz6JtOi;d@C75Ko8|7;0Ims@mp=#J3E*{IZSCaRo7jz0My7S0nqA z?9Rp%4b8HI>M{r3e%-^}7vKLHd-Y5ye(oBGDVaVJ^4o8QwhZKo5Ba?~SxzwZA%&Tb z+lVS@06>def}RU5^jtiFA3Jn^jtCRn26CIwMDK4Qfz}EHS`WVSdt3w|zjyyIcTdI< z_Iq%ulJDxlW!-Ky5!DO<4g;b}p(qo~D|bzZD}}iwxHqISKZAMo>{p|R3Ib$Ig#6z9 zuUuBR4)M~4hRY-CJX3}9-`~ir3~U~mio^M77O*m~S^yz@5V~R(vM@mB3ZaDu3db9> zn1uo77y$nJqBwM-ljmkZQv)kQbrDK2S{O|%(5EZ+>pq)BEvr!VZekF?f^bdwGcVVy z-?JKEX&@5x?N#k0IslB|Xwyjp=o7hSt>fM8D`~5NdH=;!|7gueKnEx>+CfPJ#Q*e| r1fk1>I_9WBo>`?$ks?Kk{5$*tT^fbQe@cvs00000NkvXXu0mjfYw(!I literal 0 HcmV?d00001 diff --git a/apps/widmessages/lib.js b/apps/widmessages/lib.js new file mode 100644 index 000000000..897611ad1 --- /dev/null +++ b/apps/widmessages/lib.js @@ -0,0 +1,8 @@ +exports.hide = function() { + if (!global.WIDGETS||!WIDGETS["messages"]) return; + WIDGETS["messages"].hide(); +} +exports.show = function() { + if (!global.WIDGETS||!WIDGETS["messages"]) return; + WIDGETS["messages"].show(); +} \ No newline at end of file diff --git a/apps/widmessages/metadata.json b/apps/widmessages/metadata.json new file mode 100644 index 000000000..0c3ac7e05 --- /dev/null +++ b/apps/widmessages/metadata.json @@ -0,0 +1,18 @@ +{ + "id": "widmessages", + "name": "Message Widget", + "version": "0.01", + "description": "Widget showing new messages", + "icon": "app.png", + "type": "widget", + "tags": "tool,system", + "supports": ["BANGLEJS","BANGLEJS2"], + "screenshots": [{"url": "screenshot.gif"}], + "dependencies" : { "messageicons":"module" }, + "provides_modules" : ["messagewidget"], + "readme": "README.md", + "storage": [ + {"name":"messagewidget","url":"lib.js"}, + {"name":"widmessages.wid.js","url":"widget.js"} + ] +} diff --git a/apps/messages/screenshot-notify.gif b/apps/widmessages/screenshot.gif similarity index 100% rename from apps/messages/screenshot-notify.gif rename to apps/widmessages/screenshot.gif diff --git a/apps/widmessages/widget.js b/apps/widmessages/widget.js new file mode 100644 index 000000000..2ee11b690 --- /dev/null +++ b/apps/widmessages/widget.js @@ -0,0 +1,73 @@ +(() => { + if ((require("Storage").readJSON("messages.settings.json", true) || {}).maxMessages===0) return; + + function filterMessages(msgs) { + return msgs.filter(msg => msg.new && msg.id != "music") + .map(m => m.src) // we only need this for icon/color + .filter((msg, i, arr) => arr.findIndex(nmsg => msg.src == nmsg.src) == i); + } + + WIDGETS["messages"] = { + area: "tl", width: 0, srcs: [], draw: function(recall) { + // If we had a setTimeout queued from the last time we were called, remove it + if (WIDGETS["messages"].i) { + clearTimeout(WIDGETS["messages"].i); + delete WIDGETS["messages"].i; + } + Bangle.removeListener("touch", this.touch); + if (!this.width) return; + let settings = Object.assign({flash: true, maxMessages: 3}, require("Storage").readJSON("messages.settings.json", true) || {}); + if (recall!==true || settings.flash) { + const msgsShown = E.clip(this.srcs.length, 0, settings.maxMessages), + srcs = Object.keys(this.srcs); + g.reset().clearRect(this.x, this.y, this.x+this.width, this.y+23); + for(let i = 0; isettings.maxMessages ? atob("EASBAGGG88/zz2GG") : require("messageicons").getImage(src), + this.x+12+i*24, this.y+12, {rotate: 0/*force centering*/}); + } + } + WIDGETS["messages"].i = setTimeout(() => WIDGETS["messages"].draw(true), 1000); + if (process.env.HWVERSION>1) Bangle.on("touch", this.touch); + }, onMsg: function(type, msg) { + if (this.hidden) return; + if (type==="music") return; + if (msg.id && !msg.new && msg.t!=="remove") return; + this.srcs = filterMessages(require("messages").getMessages(msg)); + const settings = Object.assign({maxMessages:3},require('Storage').readJSON("messages.settings.json", true) || {}); + this.width = 24 * E.clip(this.srcs.length, 0, settings.maxMessages); + if (type!=="init") Bangle.drawWidgets(); // "init" is not a real message type: see below + }, touch: function(b, c) { + var w = WIDGETS["messages"]; + if (!w || !w.width || c.xw.x+w.width || c.yw.y+24) return; + require("messages").openGUI(); + }, hide() { + this.hidden=true; + if (this.width) { + // hide widget + this.width = 0; + Bangle.drawWidgets(); + } + }, show() { + delete this.hidden + this.onMsg("show", {}); // reload messages+redraw + } + }; + + Bangle.on("message", WIDGETS["messages"].onMsg); + this.srcs = {}; + WIDGETS["messages"].onMsg("init", {}); // abuse type="init" to prevent Bangle.drawWidgets(); +})(); diff --git a/apps/widmsggrid/ChangeLog b/apps/widmsggrid/ChangeLog index 75259c4f0..9612da729 100644 --- a/apps/widmsggrid/ChangeLog +++ b/apps/widmsggrid/ChangeLog @@ -1,2 +1,3 @@ 0.01: New widget! 0.02: Adjust to message icons moving to messageicons lib +0.03: Use new message library \ No newline at end of file diff --git a/apps/widmsggrid/README.md b/apps/widmsggrid/README.md index 86a80c403..274858d66 100644 --- a/apps/widmsggrid/README.md +++ b/apps/widmsggrid/README.md @@ -20,9 +20,9 @@ You probably want to disable the default widget, to do so: 3. Scroll down to the `Widget messages` entry, and change it to `Hide` ## Settings -This widget uses the `Widget` settings from the `messages` app: +You can change settings by going to the global `Settings` app, then `App Settings` +and `Messages`: -### Widget * `Flash icon` Toggle flashing of the widget icons. -* `Widget messages` Not used by this widget, but you should select `Hide` to hide the default widget. \ No newline at end of file +* `Widget messages` Not used by this widget. \ No newline at end of file diff --git a/apps/widmsggrid/lib.js b/apps/widmsggrid/lib.js new file mode 100644 index 000000000..430577209 --- /dev/null +++ b/apps/widmsggrid/lib.js @@ -0,0 +1,8 @@ +exports.hide = function() { + if (!global.WIDGETS||!WIDGETS["msggrid"]) return; + WIDGETS["msggrid"].hide(); +} +exports.show = function() { + if (!global.WIDGETS||!WIDGETS["msggrid"]) return; + WIDGETS["msggrid"].show(); +} \ No newline at end of file diff --git a/apps/widmsggrid/metadata.json b/apps/widmsggrid/metadata.json index c9ed5bbe0..68c2c3771 100644 --- a/apps/widmsggrid/metadata.json +++ b/apps/widmsggrid/metadata.json @@ -1,16 +1,17 @@ { "id": "widmsggrid", "name": "Messages Grid Widget", - "version": "0.02", - "description": "Widget that display notification icons in a grid", + "version": "0.03", + "description": "Widget that displays notification icons in a grid", "icon": "widget.png", "type": "widget", - "dependencies": {"messages":"app"}, "tags": "tool,system", "supports": ["BANGLEJS","BANGLEJS2"], - "dependencies" : { "messageicons":"module" }, + "dependencies" : { "messages":"module" }, + "provides_modules" : ["messagewidget"], "readme": "README.md", "storage": [ + {"name":"messagewidget","url":"lib.js"}, {"name":"widmsggrid.wid.js","url":"widget.js"} ], "screenshots": [{"url":"screenshot.png"}] diff --git a/apps/widmsggrid/widget.js b/apps/widmsggrid/widget.js index 431adf479..786f590b5 100644 --- a/apps/widmsggrid/widget.js +++ b/apps/widmsggrid/widget.js @@ -24,7 +24,7 @@ clearTimeout(w.t); delete w.t; } - if (!w.width) return; + if (!w.width || this.hidden) return; const b = w.flash && w.status === "new" && ((Date.now() / 1000) & 1), // Blink(= inverse colors) on this second? // show multiple icons in a grid, by scaling them down cols = Math.ceil(Math.sqrt(w.srcs.length - 0.1)); // cols===rows, -0.1 to work around rounding error @@ -57,9 +57,10 @@ .drawString(w.total, w.x + w.width - 1, w.y + 24, w.total > 9); } if (w.flash && w.status === "new") w.t = setTimeout(w.draw, 1000); // schedule redraw while blinking - }, show: function () { + }, show: function (m) { + delete w.hidden; w.width = 24; - w.srcs = require("messages").getMessages() + w.srcs = require("messages").getMessages(m) .filter(m => !['call', 'map', 'music'].includes(m.id)) .filter(m => m.new || w.showRead) .map(m => m.src); @@ -68,6 +69,7 @@ Bangle.drawWidgets(); Bangle.setLCDPower(1); // turns screen on }, hide: function () { + w.hidden = true; w.width = 0; w.srcs = []; w.total = 0; @@ -82,13 +84,16 @@ } // Bangle.js 2: open app when touching the widget else if (c.x < w.x || c.x > w.x + w.width || c.y < w.y || c.y > w.y + 24) return; - load("messages.app.js"); - }, listener: function () { - w.status = require("messages").status(); - if (w.status === "new" || (w.status === "old" && w.showRead)) w.show(); + require("messages").openGUI(); + }, listener: function (t,m) { + if (this.hidden) return; + w.status = require("messages").status(m); + if (w.status === "new" || (w.status === "old" && w.showRead)) w.show(m); else w.hide(); + delete w.hidden; // always set by w.hide(), but we checked it wasn't there before } }; delete s; const w = WIDGETS["msggrid"]; + Bangle.on("message", w.listener); })(); diff --git a/bin/sanitycheck.js b/bin/sanitycheck.js index 7ee07bebc..0a4765d9c 100755 --- a/bin/sanitycheck.js +++ b/bin/sanitycheck.js @@ -92,7 +92,8 @@ const INTERNAL_FILES_IN_APP_TYPE = { // list of app types and files they SHOULD }; /* These are warnings we know about but don't want in our output */ var KNOWN_WARNINGS = [ -"App gpsrec data file wildcard .gpsrc? does not include app ID" +"App gpsrec data file wildcard .gpsrc? does not include app ID", +"App widmessages storage file messagewidget is also listed as storage file for app widmsggrid", ]; function globToRegex(pattern) { From 77e270d356542e341ea4a69d16486735e62298c3 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Sun, 27 Nov 2022 19:05:40 +0100 Subject: [PATCH 037/107] hrmmar: Add fft elimination algorithm to remove MA The fft with size=256 is calculated over a window of 8 seconds with step size 2 seconds. Sample rate of acceleration (12.5Hz) and PPG sensor (25Hz) is unchanged, so the algorithm runs with 12.5Hz. Generally the maximum peak in fft spectrum in an interval of -5..+10BPM of last one is used. The low fft resolution limits the accuracy to ~3BPM, but mean error is ~5-10BPM. If firmware reports confidence >= 90% its value is used. --- apps/hrmmar/README.md | 11 +++ apps/hrmmar/app.png | Bin 0 -> 1192 bytes apps/hrmmar/boot.js | 40 ++++++++ apps/hrmmar/fftelim.js | 190 ++++++++++++++++++++++++++++++++++++++ apps/hrmmar/metadata.json | 18 ++++ apps/hrmmar/settings.js | 26 ++++++ 6 files changed, 285 insertions(+) create mode 100644 apps/hrmmar/README.md create mode 100644 apps/hrmmar/app.png create mode 100644 apps/hrmmar/boot.js create mode 100644 apps/hrmmar/fftelim.js create mode 100644 apps/hrmmar/metadata.json create mode 100644 apps/hrmmar/settings.js diff --git a/apps/hrmmar/README.md b/apps/hrmmar/README.md new file mode 100644 index 000000000..ff90d9156 --- /dev/null +++ b/apps/hrmmar/README.md @@ -0,0 +1,11 @@ +# HRM Motion Artifacts removal + +Measurements from the build in PPG-Sensor (Photoplethysmograph) is sensitive to motion and can be corrupted with Motion Artifacts (MA). This module allows to remove these. + +## Settings + +* **MA removal** + +Select the algorithm to Remove Motion artifacts: + - None: (default) No Motion Artifact removal. + - fft elim: (*experimental*) Remove Motion Artifacts by cutting out the frequencies from the HRM frequency spectrum that are noisy in acceleration spectrum. Under motion this can report a heart rate that is closer to the real one but will fail if motion frequency and heart rate overlap. diff --git a/apps/hrmmar/app.png b/apps/hrmmar/app.png new file mode 100644 index 0000000000000000000000000000000000000000..9db19be376fcfbd57a41ed0a04eee292b34c828e GIT binary patch literal 1192 zcmV;Z1XufsP)7N8&wpCzj16QO=3?ye$>y9#7#&f2&F`l*fYj2&35nxJ-PlfTCr)jTJuJo^JBi0_eQgSo zztx>NbI-ZInRD)AV8n69>sIT^kJmm)b~9ScdZ`Vr^6Uo>pp4bk56z6G0yK#WB2lO0 z1Cj1W(agtUDnKOXC!J+xU)=7udpBe=gQ)-!LW?Opf&4IBpTIf{r=e&XS72`#u1_!& z0l|edlg=_o8fZOdYGDSRLQ`@X*;B-3SCQSNLAyV}zy*XBQbyYy9DW@PTtM(_+Gs2P z2LZCDc;vcMfG5;lz^Um&CLM>Op#ebw-bj|{xxbC;Y1aX=r%0o&yH!l4((v;sB1;L8 z&;~*RqAzdapUW86p|g7aZ`__k)4=I2VxOp;=oem0{x<~7uWac8Bn@b%&-R@eN_!3@ zsry$5Eu@)QN+4M@k???k0Pjo|*;8bEvV!YO0nq1vfK<#+E~)PW-Nyu+_7!j_CB~;J z_#(UghBxS2K=j+yW+^Qcp90m#P59c`4c#=&h`+cFUL_@V;u{N4@ zpx;Yx_yNeJnqMH0N0LC(z%te#6iuTwQ{Oj}W%;$g9y|D6mdY_qGp{lPV4 zZ-Md2D$xD{g`9(0#fE*NhQn2&n0MfdWU-&Bl1nKx>K43_EU&-$GXOWge1r8{(H_yd zXN1r!RI6Ia-*+KN`r=f`Iyrbar5iPgT-pVIyIBXV^x3%=B7P?VfYqk5ci+YOuk(G4 zL3kz3Z2OFMs`>U?wCcbR&@ABOT8Q}Fi1FMn-=FV$*D5UF!Isx(pAe8}*MZHha%ScM z4b?(=!%rseo>bpjNR_$T^TbMHXL)0g$*CGVIxe*x+G6=Yh!+@5fC>QkWaP$jU{6NW2-$V26CuRt_4tQ-Cbonay+>f>iUcb19=^0g9nUF9l%aU z0S}6y{8s6twCA9DXs1@S@i23G;Qog$K-2W~ppbPA#ea_!l8WZGrz4+INQk9R!OYM@ zfObpyCn%|B-;7bpAep0|u?SIms20)F`Vv$eX_ z`Vtb~h^Et{{fRz$NkufiwOF)QZ+!JdOeFq@5hF$nkADCxICO5y(}2|g0000 { + bpm_corrected = bpm; + }; + + Bangle.on('HRM', (hrm) => { + if (bpm_corrected > 0) { + // replace bpm data in event + hrm.bpm_orig = hrm.bpm; + hrm.confidence_orig = hrm.confidence; + hrm.bpm = bpm_corrected; + hrm.confidence = 0; + } + }); + + let run = () => { + const settings = Object.assign({ + mAremoval: 0 + }, require("Storage").readJSON("hrmmar.json", true) || {}); + + // select motion artifact removal algorithm + switch(settings.mAremoval) { + case 1: + require("hrmfftelim").run(settings, updateHrm); + break; + } + } + + // override setHRMPower so we can run our code on HRM enable + const oldSetHRMPower = Bangle.setHRMPower; + Bangle.setHRMPower = function(on, id) { + if (on && run !== undefined) { + run(); + run = undefined; // Make sure we run only once + } + return oldSetHRMPower(on, id); + }; +} diff --git a/apps/hrmmar/fftelim.js b/apps/hrmmar/fftelim.js new file mode 100644 index 000000000..98b7f33ad --- /dev/null +++ b/apps/hrmmar/fftelim.js @@ -0,0 +1,190 @@ +exports.run = (settings, updateHrm) => { + const SAMPLE_RATE = 12.5; + const NUM_POINTS = 256; // fft size + const ACC_PEAKS = 2; // remove this number of ACC peaks + + // ringbuffers + const hrmvalues = new Int16Array(8*SAMPLE_RATE); + const accvalues = new Int16Array(8*SAMPLE_RATE); + // fft buffers + const hrmfftbuf = new Int16Array(NUM_POINTS); + const accfftbuf = new Int16Array(NUM_POINTS); + let BPM_est_1 = 0; + let BPM_est_2 = 0; + + let hrmdata; + let idx=0, wraps=0; + + // init settings + Bangle.setOptions({hrmPollInterval: 40, powerSave: false}); // hrm=25Hz + Bangle.setPollInterval(80); // 12.5Hz + + calcfft = (values, idx, normalize, fftbuf) => { + fftbuf.fill(0); + let i_out=0; + let avg = 0; + if (normalize) { + const sum = values.reduce((a, b) => a + b, 0); + avg = sum/values.length; + } + // sort ringbuffer to fft buffer + for(let i_in=idx; i_in { + let maxVal = -Number.MAX_VALUE; + let maxIdx = 0; + + values.forEach((value,i) => { + if (value > maxVal) { + maxVal = value; + maxIdx = i; + } + }); + return {idx: maxIdx, val: maxVal}; + }; + + getSign = (value) => { + return value < 0 ? -1 : 1; + }; + + // idx in fft buffer to frequency + getFftFreq = (idx, rate, size) => { + return idx*rate/(size-1); + }; + + // frequency to idx in fft buffer + getFftIdx = (freq, rate, size) => { + return Math.round(freq*(size-1)/rate); + }; + + calc2ndDeriative = (values) => { + const result = new Int16Array(values.length-2); + for(let i=1; i { + // fft + const ppg_fft = calcfft(hrmvalues, idx, true, hrmfftbuf).subarray(minFreqIdx, maxFreqIdx+1); + const acc_fft = calcfft(accvalues, idx, false, accfftbuf).subarray(minFreqIdx, maxFreqIdx+1); + + // remove spectrum that have peaks in acc fft from ppg fft + const accGlobalMax = getMax(acc_fft); + const acc2nddiff = calc2ndDeriative(acc_fft); // calculate second derivative + for(let iClean=0; iClean < ACC_PEAKS; iClean++) { + // get max peak in ACC + const accMax = getMax(acc_fft); + + if (accMax.val >= 10 && accMax.val/accGlobalMax.val > 0.75) { + // set all values in PPG FFT to zero until second derivative of ACC has zero crossing + for (let k = accMax.idx-1; k>=0; k--) { + ppg_fft[k] = 0; + acc_fft[k] = -Math.abs(acc_fft[k]); // max(acc_fft) should no longer find this + if (k-2 > 0 && getSign(acc2nddiff[k-1-2]) != getSign(acc2nddiff[k-2]) && Math.abs(acc_fft[k]) < accMax.val*0.75) { + break; + } + } + // set all values in PPG FFT to zero until second derivative of ACC has zero crossing + for (let k = accMax.idx; k < acc_fft.length-1; k++) { + ppg_fft[k] = 0; + acc_fft[k] = -Math.abs(acc_fft[k]); // max(acc_fft) should no longer find this + if (k-2 >= 0 && getSign(acc2nddiff[k+1-2]) != getSign(acc2nddiff[k-2]) && Math.abs(acc_fft[k]) < accMax.val*0.75) { + break; + } + } + } + } + + // bpm result is maximum peak in PPG fft + const hrRangeMax = getMax(ppg_fft.subarray(rangeIdx[0], rangeIdx[1])); + const hrTotalMax = getMax(ppg_fft); + const maxDiff = hrTotalMax.val/hrRangeMax.val; + let idxMaxPPG = hrRangeMax.idx+rangeIdx[0]; // offset range limit + + if ((maxDiff > 3 && idxMaxPPG != hrTotalMax.idx) || hrRangeMax.val === 0) { // prevent tracking from loosing the real heart rate by checking the full spectrum + if (hrTotalMax.idx > idxMaxPPG) { + idxMaxPPG = idxMaxPPG+Math.ceil(6/freqStep); // step 6 BPM up into the direction of max peak + } else { + idxMaxPPG = idxMaxPPG-Math.ceil(2/freqStep); // step 2 BPM down into the direction of max peak + } + } + + idxMaxPPG = idxMaxPPG + minFreqIdx; + const BPM_est_0 = getFftFreq(idxMaxPPG, SAMPLE_RATE, NUM_POINTS)*60; + + // smooth with moving average + let BPM_est_res; + if (BPM_est_2 > 0) { + BPM_est_res = 0.9*BPM_est_0 + 0.05*BPM_est_1 + 0.05*BPM_est_2; + } else { + BPM_est_res = BPM_est_0; + } + + return BPM_est_res.toFixed(1); + }; + + Bangle.on('HRM-raw', (hrm) => { + hrmdata = hrm; + }); + + Bangle.on('accel', (acc) => { + if (hrmdata !== undefined) { + hrmvalues[idx] = hrmdata.filt; + accvalues[idx] = acc.x*1000 + acc.y*1000 + acc.z*1000; + idx++; + if (idx >= 8*SAMPLE_RATE) { + idx = 0; + wraps++; + } + + if (idx % (SAMPLE_RATE*2) == 0) { // every two seconds + if (wraps === 0) { // use rate of firmware until hrmvalues buffer is filled + updateHrm(undefined); + BPM_est_2 = BPM_est_1; + BPM_est_1 = hrmdata.bpm; + } else { + let bpm_result; + if (hrmdata.confidence >= 90) { // display firmware value if good + bpm_result = hrmdata.bpm; + updateHrm(undefined); + } else { + bpm_result = calculate(idx); + bpm_corrected = bpm_result; + updateHrm(bpm_result); + } + BPM_est_2 = BPM_est_1; + BPM_est_1 = bpm_result; + + // set search range of next BPM + const est_res_idx = getFftIdx(bpm_result/60, SAMPLE_RATE, NUM_POINTS)-minFreqIdx; + rangeIdx = [est_res_idx-maxBpmDiffIdxDown, est_res_idx+maxBpmDiffIdxUp]; + if (rangeIdx[0] < 0) { + rangeIdx[0] = 0; + } + if (rangeIdx[1] > maxFreqIdx-minFreqIdx) { + rangeIdx[1] = maxFreqIdx-minFreqIdx; + } + } + } + } + }); +}; diff --git a/apps/hrmmar/metadata.json b/apps/hrmmar/metadata.json new file mode 100644 index 000000000..232ff64a7 --- /dev/null +++ b/apps/hrmmar/metadata.json @@ -0,0 +1,18 @@ +{ + "id": "hrmmar", + "name": "HRM Motion Artifacts removal", + "shortName":"HRM MA removal", + "icon": "app.png", + "version":"0.01", + "description": "Removes Motion Artifacts in Bangle.js's heart rate sensor data.", + "type": "bootloader", + "tags": "health", + "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"hrmmar.boot.js","url":"boot.js"}, + {"name":"hrmfftelim","url":"fftelim.js"}, + {"name":"hrmmar.settings.js","url":"settings.js"} + ], + "data": [{"name":"hrmmar.json"}] +} diff --git a/apps/hrmmar/settings.js b/apps/hrmmar/settings.js new file mode 100644 index 000000000..3c6e62c91 --- /dev/null +++ b/apps/hrmmar/settings.js @@ -0,0 +1,26 @@ +(function(back) { + var FILE = "hrmmar.json"; + // Load settings + var settings = Object.assign({ + mAremoval: 0, + }, require('Storage').readJSON(FILE, true) || {}); + + function writeSettings() { + require('Storage').writeJSON(FILE, settings); + } + + // Show the menu + E.showMenu({ + "" : { "title" : "HRM MA removal" }, + "< Back" : () => back(), + 'MA removal': { + value: settings.mAremoval, + min: 0, max: 1, + format: v => ["None", "fft elim."][v], + onchange: v => { + settings.mAremoval = v; + writeSettings(); + } + }, + }); +}) From bc28900e60ee9cea407b1c21693064fa1c1210ab Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 27 Nov 2022 14:58:39 +0100 Subject: [PATCH 038/107] launch - Use Bangle.showClock --- apps/launch/ChangeLog | 1 + apps/launch/app.js | 19 +++++++------------ apps/launch/metadata.json | 2 +- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/apps/launch/ChangeLog b/apps/launch/ChangeLog index 5da1b2661..0aff8c49f 100644 --- a/apps/launch/ChangeLog +++ b/apps/launch/ChangeLog @@ -19,3 +19,4 @@ 0.17: Don't display 'Loading...' now the watch has its own loading screen 0.18: Add 'back' icon in top-left to go back to clock 0.19: Fix regression after back button added (returnToClock was called twice!) +0.20: Use Bangle.showClock for changing to clock diff --git a/apps/launch/app.js b/apps/launch/app.js index b8e598f73..36f3aaf4b 100644 --- a/apps/launch/app.js +++ b/apps/launch/app.js @@ -42,16 +42,6 @@ let apps = launchCache.apps; if (!settings.fullscreen) Bangle.loadWidgets(); -let returnToClock = function() { - // unload everything manually - // ... or we could just call `load();` but it will be slower - Bangle.setUI(); // remove scroller's handling - if (lockTimeout) clearTimeout(lockTimeout); - Bangle.removeListener("lock", lockHandler); - // now load the default clock - just call .bootcde as this has the code already - setTimeout(eval,0,s.read(".bootcde")); -} - E.showScroller({ h : 64*scaleval, c : apps.length, draw : (i, r) => { @@ -74,7 +64,12 @@ E.showScroller({ load(app.src); } }, - back : returnToClock // button press or tap in top left calls returnToClock now + back : Bangle.showClock, // button press or tap in top left shows clock now + remove : () => { + // cleanup the timeout to not leave anything behind after being removed from ram + if (lockTimeout) clearTimeout(lockTimeout); + Bangle.removeListener("lock", lockHandler); + } }); g.flip(); // force a render before widgets have finished drawing @@ -85,7 +80,7 @@ let lockHandler = function(locked) { if (lockTimeout) clearTimeout(lockTimeout); lockTimeout = undefined; if (locked) - lockTimeout = setTimeout(returnToClock, 10000); + lockTimeout = setTimeout(Bangle.showClock, 10000); } Bangle.on("lock", lockHandler); if (!settings.fullscreen) // finally draw widgets diff --git a/apps/launch/metadata.json b/apps/launch/metadata.json index ce9b1f801..4d0270e37 100644 --- a/apps/launch/metadata.json +++ b/apps/launch/metadata.json @@ -2,7 +2,7 @@ "id": "launch", "name": "Launcher", "shortName": "Launcher", - "version": "0.19", + "version": "0.20", "description": "This is needed to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.", "readme": "README.md", "icon": "app.png", From a011f929fd7e29f67fe3b01e86bc41477f1c4ed0 Mon Sep 17 00:00:00 2001 From: glemco Date: Sat, 26 Nov 2022 20:12:08 +0100 Subject: [PATCH 039/107] Rolled back agenda clkinfo --- apps/agenda/ChangeLog | 1 - apps/agenda/agenda.clkinfo.js | 29 ++++++++++------------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/apps/agenda/ChangeLog b/apps/agenda/ChangeLog index 6384fe887..7f749ff25 100644 --- a/apps/agenda/ChangeLog +++ b/apps/agenda/ChangeLog @@ -7,4 +7,3 @@ 0.07: Clkinfo improvements. 0.08: Fix error in clkinfo (didn't require Storage & locale) Fix clkinfo icon -0.09: Clkinfo new fields and filter past events. diff --git a/apps/agenda/agenda.clkinfo.js b/apps/agenda/agenda.clkinfo.js index 43b7cf57e..baa8b9516 100644 --- a/apps/agenda/agenda.clkinfo.js +++ b/apps/agenda/agenda.clkinfo.js @@ -1,34 +1,25 @@ (function() { var agendaItems = { - name: "Agenda", - img: atob("GBiBAAAAAAAAAADGMA///w///wf//wAAAA///w///w///w///x///h///h///j///D///X//+f//8wAABwAADw///w///wf//gAAAA=="), - dynamic: true, - items: [] - }; - var storage = require("Storage"); + name: "Agenda", + img: atob("GBiBAAAAAAAAAADGMA///w///wf//wAAAA///w///w///w///x///h///h///j///D///X//+f//8wAABwAADw///w///wf//gAAAA=="), + items: [] + }; var locale = require("locale"); var now = new Date(); - var agenda = (storage.readJSON("android.calendar.json",true)||[]) - .filter(ev=>ev.timestamp + ev.durationInSeconds > now/1000) - .sort((a,b)=>a.timestamp - b.timestamp); - var settings = storage.readJSON("agenda.settings.json",true)||{}; + var agenda = require("Storage").readJSON("android.calendar.json") + .filter(ev=>ev.timestamp + ev.durationInSeconds > now/1000) + .sort((a,b)=>a.timestamp - b.timestamp); agenda.forEach((entry, i) => { + var title = entry.title.slice(0,12); var date = new Date(entry.timestamp*1000); var dateStr = locale.date(date).replace(/\d\d\d\d/,""); - var dateStrToday = locale.date(new Date()).replace(/\d\d\d\d/,""); - var timeStr = locale.time(date); - //maybe not the most efficient.. - var shortTxt = (dateStrToday == dateStr) ? timeStr : dateStr; dateStr += entry.durationInSeconds < 86400 ? "/ " + locale.time(date,1) : ""; - if(!settings.pastEvents && entry.timestamp + entry.durationInSeconds < (new Date())/1000) - return; - agendaItems.items.push({ - name: null, - get: () => ({ text: title + "\n" + dateStr, short: shortTxt, img: null}), + name: "Agenda "+i, + get: () => ({ text: title + "\n" + dateStr, img: null}), show: function() { agendaItems.items[i].emit("redraw"); }, hide: function () {} }); From 7d253a2d5ffc9eeb302ba3586fed432103f8d946 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 28 Nov 2022 09:46:25 +0000 Subject: [PATCH 040/107] let users know about new firmware --- loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loader.js b/loader.js index 251f94ff1..c525fd963 100644 --- a/loader.js +++ b/loader.js @@ -16,7 +16,7 @@ if (window.location.host=="banglejs.com") { 'This is not the official Bangle.js App Loader - you can try the Official Version here.'; } -var RECOMMENDED_VERSION = "2v15"; +var RECOMMENDED_VERSION = "2v16"; // could check http://www.espruino.com/json/BANGLEJS.json for this // We're only interested in Bangles From e2da2a87a8dc7845c5c39fcac01ec59467fbce92 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 28 Nov 2022 10:09:55 +0000 Subject: [PATCH 041/107] 0.03: Added support for locale based time ref #2311, fix #2317 --- apps/slopeclock/ChangeLog | 1 + apps/slopeclock/app.js | 5 +++-- apps/slopeclock/metadata.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/slopeclock/ChangeLog b/apps/slopeclock/ChangeLog index af766d95d..2eb04a4f0 100644 --- a/apps/slopeclock/ChangeLog +++ b/apps/slopeclock/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: Reset font to save some memory during remove +0.03: Added support for locale based time diff --git a/apps/slopeclock/app.js b/apps/slopeclock/app.js index 178084fb0..cc3dce630 100644 --- a/apps/slopeclock/app.js +++ b/apps/slopeclock/app.js @@ -34,8 +34,9 @@ let draw = function() { x = R.w / 2; y = R.y + R.h / 2 - 12; // 12 = room for date var date = new Date(); - var hourStr = date.getHours(); - var minStr = date.getMinutes().toString().padStart(2,0); + var local_time = require("locale").time(date, 1); + var hourStr = local_time.split(":")[0].trim().padStart(2,'0'); + var minStr = local_time.split(":")[1].trim().padStart(2, '0'); dateStr = require("locale").dow(date, 1).toUpperCase()+ " "+ require("locale").date(date, 0).toUpperCase(); diff --git a/apps/slopeclock/metadata.json b/apps/slopeclock/metadata.json index 18820b2cc..6ee78350f 100644 --- a/apps/slopeclock/metadata.json +++ b/apps/slopeclock/metadata.json @@ -1,6 +1,6 @@ { "id": "slopeclock", "name": "Slope Clock", - "version":"0.02", + "version":"0.03", "description": "A clock where hours and minutes are divided by a sloping line. When the minute changes, the numbers slide off the screen", "icon": "app.png", "screenshots": [{"url":"screenshot.png"}], From 83279f4cf729da0e172f2d50e270ea4422a9cf07 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 28 Nov 2022 10:54:05 +0000 Subject: [PATCH 042/107] Ensure you don't have to favourite bootloader/etc - they just get installed automatically --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index aba9b6a51..f3f54106b 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit aba9b6a51fe02dfbde307c303560b8382857916d +Subproject commit f3f54106b3d7f84927ff734b715023a49a87cc6f From eba77c8a9915180d1b822b04c292b7f68a4952fd Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 28 Nov 2022 11:16:36 +0000 Subject: [PATCH 043/107] openstmap 0.15: Make track drawing an option (default off) --- apps/openstmap/ChangeLog | 1 + apps/openstmap/README.md | 9 +++++++-- apps/openstmap/app.js | 22 +++++++++++++++------- apps/openstmap/metadata.json | 3 ++- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/apps/openstmap/ChangeLog b/apps/openstmap/ChangeLog index 7f22014ae..7f788c139 100644 --- a/apps/openstmap/ChangeLog +++ b/apps/openstmap/ChangeLog @@ -15,3 +15,4 @@ 0.14: Added ability to upload multiple sets of map tiles Support for zooming in on map Satellite count moved to widget bar to leave more room for the map +0.15: Make track drawing an option (default off) diff --git a/apps/openstmap/README.md b/apps/openstmap/README.md index 707dbc7f8..f19b13bd1 100644 --- a/apps/openstmap/README.md +++ b/apps/openstmap/README.md @@ -26,11 +26,16 @@ can change settings, move the map around, and click `Get Map` again. ## Bangle.js App The Bangle.js app allows you to view a map - it also turns the GPS on and marks -the path that you've been travelling. +the path that you've been travelling (if enabled). * Drag on the screen to move the map * Press the button to bring up a menu, where you can zoom, go to GPS location -or put the map back in its default location +, put the map back in its default location, or choose whether to draw the currently +recording GPS track (from the `Recorder` app). + +**Note:** If enabled, drawing the currently recorded GPS track can take a second +or two (which happens after you've finished scrolling the screen with your finger). + ## Library diff --git a/apps/openstmap/app.js b/apps/openstmap/app.js index 9df4fa83f..89e2d2ddb 100644 --- a/apps/openstmap/app.js +++ b/apps/openstmap/app.js @@ -4,19 +4,23 @@ var R; var fix = {}; var mapVisible = false; var hasScrolled = false; +var settings = require("Storage").readJSON("openstmap.json",1)||{}; // Redraw the whole page function redraw() { g.setClipRect(R.x,R.y,R.x2,R.y2); m.draw(); drawMarker(); - if (HASWIDGETS && WIDGETS["gpsrec"] && WIDGETS["gpsrec"].plotTrack) { - g.setColor("#f00").flip(); // force immediate draw on double-buffered screens - track will update later - WIDGETS["gpsrec"].plotTrack(m); - } - if (HASWIDGETS && WIDGETS["recorder"] && WIDGETS["recorder"].plotTrack) { - g.setColor("#f00").flip(); // force immediate draw on double-buffered screens - track will update later - WIDGETS["recorder"].plotTrack(m); + // if track drawing is enabled... + if (settings.drawTrack) { + if (HASWIDGETS && WIDGETS["gpsrec"] && WIDGETS["gpsrec"].plotTrack) { + g.setColor("#f00").flip(); // force immediate draw on double-buffered screens - track will update later + WIDGETS["gpsrec"].plotTrack(m); + } + if (HASWIDGETS && WIDGETS["recorder"] && WIDGETS["recorder"].plotTrack) { + g.setColor("#f00").flip(); // force immediate draw on double-buffered screens - track will update later + WIDGETS["recorder"].plotTrack(m); + } } g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1); } @@ -76,6 +80,10 @@ function showMap() { m.scale *= 2; showMap(); }, + /*LANG*/"Draw Track": { + value : !!settings.drawTrack, + onchange : v => { settings.drawTrack=v; require("Storage").writeJSON("openstmap.json",settings); } + }, /*LANG*/"Center Map": () =>{ m.lat = m.map.lat; m.lon = m.map.lon; diff --git a/apps/openstmap/metadata.json b/apps/openstmap/metadata.json index 09b4c68e3..819dc4122 100644 --- a/apps/openstmap/metadata.json +++ b/apps/openstmap/metadata.json @@ -2,7 +2,7 @@ "id": "openstmap", "name": "OpenStreetMap", "shortName": "OpenStMap", - "version": "0.14", + "version": "0.15", "description": "Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are. Once installed this also adds map functionality to `GPS Recorder` and `Recorder` apps", "readme": "README.md", "icon": "app.png", @@ -15,6 +15,7 @@ {"name":"openstmap.app.js","url":"app.js"}, {"name":"openstmap.img","url":"app-icon.js","evaluate":true} ], "data": [ + {"name":"openstmap.json"}, {"wildcard":"openstmap.*.json"}, {"wildcard":"openstmap.*.img"} ] From b097b93bbfde10cd2e76ebcb9911f337c4cbc0a5 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 28 Nov 2022 13:55:02 +0000 Subject: [PATCH 044/107] active pedometer notes --- apps/activepedom/README.md | 8 +++++++- apps/activepedom/metadata.json | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/activepedom/README.md b/apps/activepedom/README.md index ac32a1dd6..06ad280ee 100644 --- a/apps/activepedom/README.md +++ b/apps/activepedom/README.md @@ -1,6 +1,11 @@ # Active Pedometer + Pedometer that filters out arm movement and displays a step goal progress. +**Note:** Since creation of this app, Bangle.js's step counting algorithm has +improved significantly - and as a result the algorithm in this app (which + runs *on top* of Bangle.js's algorithm) may no longer be accurate. + I changed the step counting algorithm completely. Now every step is counted when in status 'active', if the time difference between two steps is not too short or too long. To get in 'active' mode, you have to reach the step threshold before the active timer runs out. @@ -9,6 +14,7 @@ When you reach the step threshold, the steps needed to reach the threshold are c Steps are saved to a datafile every 5 minutes. You can watch a graph using the app. ## Screenshots + * 600 steps ![](600.png) @@ -70,4 +76,4 @@ Steps are saved to a datafile every 5 minutes. You can watch a graph using the a ## Requests -If you have any feature requests, please post in this forum thread: http://forum.espruino.com/conversations/345754/ \ No newline at end of file +If you have any feature requests, please post in this forum thread: http://forum.espruino.com/conversations/345754/ diff --git a/apps/activepedom/metadata.json b/apps/activepedom/metadata.json index 4deb7006d..81bafb573 100644 --- a/apps/activepedom/metadata.json +++ b/apps/activepedom/metadata.json @@ -3,7 +3,7 @@ "name": "Active Pedometer", "shortName": "Active Pedometer", "version": "0.09", - "description": "Pedometer that filters out arm movement and displays a step goal progress. Steps are saved to a daily file and can be viewed as graph.", + "description": "(NOT RECOMMENDED) Pedometer that filters out arm movement and displays a step goal progress. Steps are saved to a daily file and can be viewed as graph. The `Health` app now provides step logging and graphs.", "icon": "app.png", "tags": "outdoors,widget", "supports": ["BANGLEJS"], From 8678d0cc2137c0d2344f71f92912df46daa2302f Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 28 Nov 2022 14:22:35 +0000 Subject: [PATCH 045/107] slight tweaks to https://github.com/espruino/BangleApps/pull/2304/ --- apps/android/ChangeLog | 1 + apps/android/README.md | 2 ++ apps/android/boot.js | 48 ++++++++++++++++---------------------- apps/android/metadata.json | 2 +- apps/android/settings.js | 16 ++++++------- 5 files changed, 32 insertions(+), 37 deletions(-) diff --git a/apps/android/ChangeLog b/apps/android/ChangeLog index fcb139c94..f71e94cf7 100644 --- a/apps/android/ChangeLog +++ b/apps/android/ChangeLog @@ -15,3 +15,4 @@ 0.15: Allow method/body/headers to be specified for `http` (needs Gadgetbridge 0.68.0b or later) 0.16: Bangle.http now fails immediately if there is no Bluetooth connection (fix #2152) 0.17: Now kick off Calendar sync as soon as connected to Gadgetbridge +0.18: If connected to Gadgetbridge, allow GPS forwarding from phone (Gadgetbridge code still not merged) diff --git a/apps/android/README.md b/apps/android/README.md index f9ab73699..c76e6e528 100644 --- a/apps/android/README.md +++ b/apps/android/README.md @@ -20,6 +20,8 @@ It contains: of Gadgetbridge - making your phone make noise so you can find it. * `Keep Msgs` - default is `Off`. When Gadgetbridge disconnects, should Bangle.js keep any messages it has received, or should it delete them? +* `Overwrite GPS` - when GPS is requested by an app, this doesn't use Bangle.js's GPS +but instead asks Gadgetbridge on the phone to use the phone's GPS * `Messages` - launches the messages app, showing a list of messages ## How it works diff --git a/apps/android/boot.js b/apps/android/boot.js index 8d9270b2a..8e75241e7 100644 --- a/apps/android/boot.js +++ b/apps/android/boot.js @@ -137,8 +137,7 @@ Bangle.emit('gps', event); }, "is_gps_active": function() { - const gpsActive = originalIsGpsOn(); - sendGPSPowerStatus(gpsActive); + gbSend({ t: "gps_power", status: Bangle._PWR && Bangle._PWR.GPS && Bangle._PWR.GPS.length>0 }); } }; var h = HANDLERS[event.t]; @@ -202,34 +201,27 @@ if (isFinite(msg.id)) return gbSend({ t: "notify", n:response?"OPEN":"DISMISS", id: msg.id }); // error/warn here? }; - // GPS overwrite logic - // Save current logic - const originalSetGpsPower = Bangle.setGPSPower; - const originalIsGpsOn = Bangle.isGPSOn; - - function sendGPSPowerStatus(status) { gbSend({ t: "gps_power", status: status }); } - - // Replace set GPS power logic to suppress activation of gps, if the overwrite option is active - Bangle.setGPSPower = (isOn, appID) => { - const currentSettings = require("Storage").readJSON("android.settings.json",1)||{}; - if (!currentSettings.overwriteGps) { - originalSetGpsPower(isOn, appID); - } else { - sendGPSPowerStatus(Bangle.isGPSOn()); - const logMessage = 'Ignore gps power change due to the gps overwrite from android integration app'; - console.log(logMessage); - Bluetooth.println(logMessage); + if (settings.overwriteGps) { // if the overwrite option is set../ + // Save current logic + const originalSetGpsPower = Bangle.setGPSPower; + // Replace set GPS power logic to suppress activation of gps (and instead request it from the phone) + Bangle.setGPSPower = (isOn, appID) => { + // if not connected, use old logic + if (!NRF.getSecurityStatus().connected) return originalSetGpsPower(isOn, appID); + // Emulate old GPS power logic + if (!Bangle._PWR) Bangle._PWR={}; + if (!Bangle._PWR.GPS) Bangle._PWR.GPS=[]; + if (!appID) appID="?"; + if (isOn && !Bangle._PWR.GPS.includes(appID)) Bangle._PWR.GPS.push(appID); + if (!isOn && Bangle._PWR.GPS.includes(appID)) Bangle._PWR.GPS.splice(Bangle._PWR.GPS.indexOf(appID),1); + let pwr = Bangle._PWR.GPS.length>0; + gbSend({ t: "gps_power", status: pwr }); + return pwr; } - } - - // Replace check if the GPS is on, to show it as always active, if the overwrite option is set - Bangle.isGPSOn = () => { - const currentSettings = require("Storage").readJSON("android.settings.json",1)||{}; - if (!currentSettings.overwriteGps) { - return originalIsGpsOn(); - } else { - return true; + // Replace check if the GPS is on to check the _PWR variable + Bangle.isGPSOn = () => { + return Bangle._PWR && Bangle._PWR.GPS && Bangle._PWR.GPS.length>0; } } diff --git a/apps/android/metadata.json b/apps/android/metadata.json index ac47449d6..ad5f09243 100644 --- a/apps/android/metadata.json +++ b/apps/android/metadata.json @@ -2,7 +2,7 @@ "id": "android", "name": "Android Integration", "shortName": "Android", - "version": "0.17", + "version": "0.18", "description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.", "icon": "app.png", "tags": "tool,system,messages,notifications,gadgetbridge", diff --git a/apps/android/settings.js b/apps/android/settings.js index 94a1eba0b..b3057e888 100644 --- a/apps/android/settings.js +++ b/apps/android/settings.js @@ -1,12 +1,6 @@ (function(back) { - function onGpsOverwriteChange(newValue) { - if (newValue) { - Bangle.setGPSPower(false, 'android'); - } - settings.overwriteGps = newValue; - updateSettings(); - } + function gb(j) { Bluetooth.println(JSON.stringify(j)); @@ -34,7 +28,13 @@ }, /*LANG*/"Overwrite GPS" : { value : !!settings.overwriteGps, - onchange: onGpsOverwriteChange + onchange: newValue => { + if (newValue) { + Bangle.setGPSPower(false, 'android'); + } + settings.overwriteGps = newValue; + updateSettings(); + } }, /*LANG*/"Messages" : ()=>load("messages.app.js"), }; From d0d35ecec7f101d38c3db02603e1a0a202d485ff Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 28 Nov 2022 14:48:46 +0000 Subject: [PATCH 046/107] renaming --- apps/messagegui/metadata.json | 2 +- apps/messages/metadata.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/messagegui/metadata.json b/apps/messagegui/metadata.json index 7444050fd..c92fa90f5 100644 --- a/apps/messagegui/metadata.json +++ b/apps/messagegui/metadata.json @@ -1,6 +1,6 @@ { "id": "messagegui", - "name": "Messages", + "name": "Message UI", "version": "0.55", "description": "Default app to display notifications from iOS and Gadgetbridge/Android", "icon": "app.png", diff --git a/apps/messages/metadata.json b/apps/messages/metadata.json index f94f01c26..89140efab 100644 --- a/apps/messages/metadata.json +++ b/apps/messages/metadata.json @@ -2,7 +2,7 @@ "id": "messages", "name": "Messages", "version": "0.01", - "description": "Library to handle message events", + "description": "Library to handle, load and store message events received from Android/iOS", "icon": "app.png", "type": "module", "tags": "tool,system", From a7364f716b488f0294d92a71e6dcf5baa0120989 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 28 Nov 2022 15:03:00 +0000 Subject: [PATCH 047/107] Keep messages version rising so it'll actually update to the new version automatically --- apps/messages/ChangeLog | 2 +- apps/messages/metadata.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/messages/ChangeLog b/apps/messages/ChangeLog index b0a84783b..c984e4a0f 100644 --- a/apps/messages/ChangeLog +++ b/apps/messages/ChangeLog @@ -1 +1 @@ -0.01: Moved messages library into standalone library \ No newline at end of file +0.55: Moved messages library into standalone library diff --git a/apps/messages/metadata.json b/apps/messages/metadata.json index 89140efab..74c89b1b4 100644 --- a/apps/messages/metadata.json +++ b/apps/messages/metadata.json @@ -1,7 +1,7 @@ { "id": "messages", "name": "Messages", - "version": "0.01", + "version": "0.55", "description": "Library to handle, load and store message events received from Android/iOS", "icon": "app.png", "type": "module", From e8163a6f14986d4ee47e897d94bba6daf234fb81 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 28 Nov 2022 15:04:16 +0000 Subject: [PATCH 048/107] tweaks to stop errors if removing >1 app --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index f3f54106b..f15e99fbe 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit f3f54106b3d7f84927ff734b715023a49a87cc6f +Subproject commit f15e99fbe25b2991719011e6da9bc9c7be401a7e From 03f40ffca15b085287f2292cc6c1ff33a611c14d Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Mon, 28 Nov 2022 19:33:50 +0100 Subject: [PATCH 049/107] taglaunch: Backport PR 2316 for launch --- apps/taglaunch/ChangeLog | 1 + apps/taglaunch/app.js | 19 +++++++------------ apps/taglaunch/metadata.json | 2 +- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/apps/taglaunch/ChangeLog b/apps/taglaunch/ChangeLog index 5560f00bc..981f50386 100644 --- a/apps/taglaunch/ChangeLog +++ b/apps/taglaunch/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Use Bangle.showClock for changing to clock (Backport from launch) diff --git a/apps/taglaunch/app.js b/apps/taglaunch/app.js index 07a7021db..c940284c2 100644 --- a/apps/taglaunch/app.js +++ b/apps/taglaunch/app.js @@ -69,16 +69,6 @@ let tagKeys = Object.keys(tags).filter(tag => tag !== "clock" || settings.showCl if (!settings.fullscreen) Bangle.loadWidgets(); -let returnToClock = function() { - // unload everything manually - // ... or we could just call `load();` but it will be slower - Bangle.setUI(); // remove scroller's handling - if (lockTimeout) clearTimeout(lockTimeout); - Bangle.removeListener("lock", lockHandler); - // now load the default clock - just call .bootcde as this has the code already - setTimeout(eval,0,s.read(".bootcde")); -}; - let showTagMenu = (tag) => { E.showScroller({ h : 64*scaleval, c : appsByTag[tag].length, @@ -121,7 +111,12 @@ let showMainMenu = () => { let tag = tagKeys[i]; showTagMenu(tag); }, - back : returnToClock // button press or tap in top left calls returnToClock now + back : Bangle.showClock, // button press or tap in top left shows clock now + remove : () => { + // cleanup the timeout to not leave anything behind after being removed from ram + if (lockTimeout) clearTimeout(lockTimeout); + Bangle.removeListener("lock", lockHandler); + } }); }; showMainMenu(); @@ -134,7 +129,7 @@ let lockHandler = function(locked) { if (lockTimeout) clearTimeout(lockTimeout); lockTimeout = undefined; if (locked) { - lockTimeout = setTimeout(returnToClock, 10000); + lockTimeout = setTimeout(Bangle.showClock, 10000); } }; Bangle.on("lock", lockHandler); diff --git a/apps/taglaunch/metadata.json b/apps/taglaunch/metadata.json index aded51314..d7f1954b1 100644 --- a/apps/taglaunch/metadata.json +++ b/apps/taglaunch/metadata.json @@ -2,7 +2,7 @@ "id": "taglaunch", "name": "Tag Launcher", "shortName": "Taglauncher", - "version": "0.01", + "version": "0.02", "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.", "readme": "README.md", "icon": "app.png", From 13bbf669b4a768b16b9ed629ee97d27a210f6e03 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sat, 29 Oct 2022 14:17:40 +0200 Subject: [PATCH 050/107] fastload - Add fastload app --- apps/fastload/ChangeLog | 1 + apps/fastload/README.md | 1 + apps/fastload/boot.js | 22 ++++++++++++++++++++++ apps/fastload/icon.png | Bin 0 -> 1076 bytes apps/fastload/metadata.json | 14 ++++++++++++++ 5 files changed, 38 insertions(+) create mode 100644 apps/fastload/ChangeLog create mode 100644 apps/fastload/README.md create mode 100644 apps/fastload/boot.js create mode 100644 apps/fastload/icon.png create mode 100644 apps/fastload/metadata.json diff --git a/apps/fastload/ChangeLog b/apps/fastload/ChangeLog new file mode 100644 index 000000000..2286a7f70 --- /dev/null +++ b/apps/fastload/ChangeLog @@ -0,0 +1 @@ +0.01: New App! \ No newline at end of file diff --git a/apps/fastload/README.md b/apps/fastload/README.md new file mode 100644 index 000000000..95b87f0fe --- /dev/null +++ b/apps/fastload/README.md @@ -0,0 +1 @@ +# Fastload diff --git a/apps/fastload/boot.js b/apps/fastload/boot.js new file mode 100644 index 000000000..7787aaa83 --- /dev/null +++ b/apps/fastload/boot.js @@ -0,0 +1,22 @@ +{ +let loadingScreen = function(){ + g.reset(); + + let x = g.getWidth()/2; + let y = g.getHeight()/2; + g.setColor(g.theme.bg); + g.fillRect(x-49, y-19, x+49, y+19); + g.setColor(g.theme.fg); + g.drawRect(x-50, y-20, x+50, y+20); + g.setFont("6x8"); + g.setFontAlign(0,0); + g.drawString("Fastloading...", x, y); + g.flip(true); +}; + +// only needed to show "Fastloading..." on starting the launcher +Bangle.load = (o => (name) => { + if (Bangle.uiRemove) loadingScreen(); + setTimeout(o,0,name); +})(Bangle.load); +} diff --git a/apps/fastload/icon.png b/apps/fastload/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7fe9afe6ec8c6f7fd0f639a99c578b2247b716f8 GIT binary patch literal 1076 zcmV-41k3x0P)~AYu0?%tog6?U;p9%sRT2TB)ukSp#dz$xi|w0qRoi)GcxY$nU2A7JdK}G zX<}oT)EmrtEyNS}Lv!d_+uak=2|5RF3U9Mr@Tz^%1KuM#5zpxkylOAQl#;>WYCMCJ zkFuZP99TM1f`>4!`?p?eE6RkQjAwHKui7bP!6(8elPCPwWx>N^Im1`5zAX5=n?Q_X zqcFf1@OK9TxTFmDTVy-zUM!u^{&alY0=$BqO4xx{V6+Lu4H@^%)p{l#EfO3Caj*%% z?}B4z>BT1ZjtVn%M0roeJiLKpo3z;-VY2jFle*WjqYJLKG#y`P{SEI!f0KSTW!!fd zXBWh7GTvzcUTcqJ^s`L1<3CjtJgjRIylP{DGgfC}5^j_oPVdAC-GD8`huAj3-ox>+ z&8iOJYi&Qi#GV#|y`mfVnfSIrhlg=_Y;!3_j$#1q*!|tWPr>^c9o`w+%$2SvMcMP3 zFS(g|GNZ$nV*NC1FBpDJeEzzR|UcE z!jin;8z~Wjs3PFE5Phy*U8emLa04Q7~m2iYHg&fpE zh!40aqkeg;|3wI^TWO_w_;tEG-?pQz@ga*E)Fo2KUfpJYv!$!nzrre3sCLAMojRdB z!Gp2xd*Ls7yV@c|uove6*hre#S+F(uES8Vpf)4xa(>z0+tNxL_YW#=`%L3^} uiLj<05`wk^_({lDZWdObv7XVjq5lAm2P;)U!_=Ap0000 Date: Tue, 22 Nov 2022 19:05:12 +0100 Subject: [PATCH 051/107] fastload - Allow redirecting to launcher for back function of setUI --- apps/fastload/boot.js | 55 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/apps/fastload/boot.js b/apps/fastload/boot.js index 7787aaa83..11ce0563b 100644 --- a/apps/fastload/boot.js +++ b/apps/fastload/boot.js @@ -1,4 +1,6 @@ { +const SETTINGS = require("Storage").readJSON("fastload.json") || {}; + let loadingScreen = function(){ g.reset(); @@ -14,9 +16,58 @@ let loadingScreen = function(){ g.flip(true); }; -// only needed to show "Fastloading..." on starting the launcher +let cache = require("Storage").readJSON("fastload.cache") || {}; + +function checkApp(n){ + print("checking", n); + // no widgets, no problem + if (!global.WIDGETS) return true; + let app = require("Storage").read(n); + if (cache[n] && E.CRC32(app) == cache[n].crc) + return cache[n].fast + cache[n] = {}; + cache[n].fast = app.includes("Bangle.loadWidgets"); + cache[n].crc = E.CRC32(app); + require("Storage").writeJSON("fastload.cache", cache); + print("updated cache", cache[n]); + return cache[n].fast; +} + +global._load = load; + +function slowload(n){ + print("slowload", n); + global._load(n); +} + +function fastload(n){ + print("fastload", n) + if (!n || checkApp(n)){ + print("actually try fastloading using Bangle.load"); + // Bangle.load can call load, to prevent recursion this must be the system load + global.load = slowload; + Bangle.load(n); + // if fastloading worked, we need to set load back to this method + global.load = fastload; + } + else + slowload(n); +}; +global.load = fastload; + Bangle.load = (o => (name) => { + print("Bangle.load", name); if (Bangle.uiRemove) loadingScreen(); - setTimeout(o,0,name); + if (SETTINGS.autoloadLauncher && !name){ + print("redirect to launcher"); + let orig = Bangle.load; + Bangle.load = (n)=>{ + Bangle.load = orig; + fastload(n); + } + Bangle.showLauncher(); + Bangle.load = orig; + } else + o(name); })(Bangle.load); } From 9a59935b218bc0d4274c13bba01a3f62cc3a87d6 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Tue, 22 Nov 2022 20:52:09 +0100 Subject: [PATCH 052/107] fastload - Bump version --- apps/fastload/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/fastload/metadata.json b/apps/fastload/metadata.json index 8bb37d138..01dab4a7c 100644 --- a/apps/fastload/metadata.json +++ b/apps/fastload/metadata.json @@ -1,7 +1,7 @@ { "id": "fastload", "name": "Fastload Utils", "shortName" : "Fastload Utils", - "version": "0.01", + "version": "0.02", "icon": "icon.png", "description": "Utilities for fastloading and switching between apps", "type":"bootloader", From 64567228a68892eeb58231c43f5892f3763f5eb3 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 27 Nov 2022 14:50:22 +0100 Subject: [PATCH 053/107] fastload - Adds setting for always loading to launcher --- apps/fastload/metadata.json | 8 +++++--- apps/fastload/settings.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 apps/fastload/settings.js diff --git a/apps/fastload/metadata.json b/apps/fastload/metadata.json index 01dab4a7c..5b46be322 100644 --- a/apps/fastload/metadata.json +++ b/apps/fastload/metadata.json @@ -1,7 +1,7 @@ { "id": "fastload", "name": "Fastload Utils", "shortName" : "Fastload Utils", - "version": "0.02", + "version": "0.03", "icon": "icon.png", "description": "Utilities for fastloading and switching between apps", "type":"bootloader", @@ -9,6 +9,8 @@ "supports": ["BANGLEJS2"], "readme": "README.md", "storage": [ - {"name":"fastload.5.boot.js","url":"boot.js"} - ] + {"name":"fastload.5.boot.js","url":"boot.js"}, + {"name":"fastload.settings.js","url":"settings.js"} + ], + "data": [{"name":"fastload.json"}] } diff --git a/apps/fastload/settings.js b/apps/fastload/settings.js new file mode 100644 index 000000000..69064c3d7 --- /dev/null +++ b/apps/fastload/settings.js @@ -0,0 +1,32 @@ +(function(back) { + var FILE="fastload.json"; + var settings; + + function writeSettings(key, value) { + var s = require('Storage').readJSON(FILE, true) || {}; + s[key] = value; + require('Storage').writeJSON(FILE, s); + readSettings(); + } + + function readSettings(){ + settings = require('Storage').readJSON(FILE, true) || {}; + } + + readSettings(); + + function buildMainMenu(){ + var mainmenu = { + '': { 'title': 'Fastload', back: back }, + 'Force load to launcher': { + value: !!settings.autoloadLauncher, + onchange: v => { + writeSettings("autoloadLauncher",v); + } + } + }; + return mainmenu; + } + + E.showMenu(buildMainMenu()); +}) From 65034377c69b56cadbbb60dd0206bc4a4a1a0924 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 27 Nov 2022 19:19:33 +0100 Subject: [PATCH 054/107] fastload - Adds setting for hiding the loading screen --- apps/fastload/boot.js | 2 +- apps/fastload/settings.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/fastload/boot.js b/apps/fastload/boot.js index 11ce0563b..c36c0921d 100644 --- a/apps/fastload/boot.js +++ b/apps/fastload/boot.js @@ -57,7 +57,7 @@ global.load = fastload; Bangle.load = (o => (name) => { print("Bangle.load", name); - if (Bangle.uiRemove) loadingScreen(); + if (Bangle.uiRemove && !SETTINGS.hideLoading) loadingScreen(); if (SETTINGS.autoloadLauncher && !name){ print("redirect to launcher"); let orig = Bangle.load; diff --git a/apps/fastload/settings.js b/apps/fastload/settings.js index 69064c3d7..4904e057e 100644 --- a/apps/fastload/settings.js +++ b/apps/fastload/settings.js @@ -23,6 +23,12 @@ onchange: v => { writeSettings("autoloadLauncher",v); } + }, + 'Hide "Fastloading..."': { + value: !!settings.hideLoading, + onchange: v => { + writeSettings("hideLoading",v); + } } }; return mainmenu; From 30b05f8d8eb70487d87d6f21be33cbaed4ffeeb2 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 27 Nov 2022 19:29:28 +0100 Subject: [PATCH 055/107] fastload - Adds a README --- apps/fastload/README.md | 22 +++++++++++++++++++++- apps/fastload/metadata.json | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/fastload/README.md b/apps/fastload/README.md index 95b87f0fe..a1feedcf8 100644 --- a/apps/fastload/README.md +++ b/apps/fastload/README.md @@ -1 +1,21 @@ -# Fastload +# Fastload Utils + +*EXPERIMENTAL* Use this with caution. When you find something misbehaving please check if the problem actually persists when removing this app. + +This allows fast loading of all apps with two conditions: +* Loaded app contains `Bangle.loadWidgets`. This is needed to prevent problems with apps not expecting widgets to be already loaded. +* Current app can be removed completely from RAM. + +## Settings + +* Allows to redirect all loads usually loading the clock to the launcher instead +* The "Fastloading..." screen can be switched off + +## Technical infos + +This is still experimental but it uses the same mechanism as `.bootcde` does. +It checks the app to be loaded for widget use and stores the result of that and a hash of the js in a cache. + +# Creator + +[halemmerich](https://github.com/halemmerich) diff --git a/apps/fastload/metadata.json b/apps/fastload/metadata.json index 5b46be322..15adcb7e3 100644 --- a/apps/fastload/metadata.json +++ b/apps/fastload/metadata.json @@ -3,7 +3,7 @@ "shortName" : "Fastload Utils", "version": "0.03", "icon": "icon.png", - "description": "Utilities for fastloading and switching between apps", + "description": "Enable experimental fastloading for more apps", "type":"bootloader", "tags": "system", "supports": ["BANGLEJS2"], From e86b716093deae94321a05a39650dd5620c7080c Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Mon, 28 Nov 2022 20:02:58 +0100 Subject: [PATCH 056/107] fastload - Remove debug prints --- apps/fastload/boot.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apps/fastload/boot.js b/apps/fastload/boot.js index c36c0921d..0d8a888ac 100644 --- a/apps/fastload/boot.js +++ b/apps/fastload/boot.js @@ -19,7 +19,6 @@ let loadingScreen = function(){ let cache = require("Storage").readJSON("fastload.cache") || {}; function checkApp(n){ - print("checking", n); // no widgets, no problem if (!global.WIDGETS) return true; let app = require("Storage").read(n); @@ -29,21 +28,17 @@ function checkApp(n){ cache[n].fast = app.includes("Bangle.loadWidgets"); cache[n].crc = E.CRC32(app); require("Storage").writeJSON("fastload.cache", cache); - print("updated cache", cache[n]); return cache[n].fast; } global._load = load; function slowload(n){ - print("slowload", n); global._load(n); } function fastload(n){ - print("fastload", n) if (!n || checkApp(n)){ - print("actually try fastloading using Bangle.load"); // Bangle.load can call load, to prevent recursion this must be the system load global.load = slowload; Bangle.load(n); @@ -56,10 +51,8 @@ function fastload(n){ global.load = fastload; Bangle.load = (o => (name) => { - print("Bangle.load", name); if (Bangle.uiRemove && !SETTINGS.hideLoading) loadingScreen(); if (SETTINGS.autoloadLauncher && !name){ - print("redirect to launcher"); let orig = Bangle.load; Bangle.load = (n)=>{ Bangle.load = orig; From 88b9f7eac8bd7bd20bf863888b3fe3810c415cfb Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Mon, 28 Nov 2022 20:17:10 +0100 Subject: [PATCH 057/107] fastload - Add versions to changelog --- apps/fastload/ChangeLog | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/fastload/ChangeLog b/apps/fastload/ChangeLog index 2286a7f70..53e3c2591 100644 --- a/apps/fastload/ChangeLog +++ b/apps/fastload/ChangeLog @@ -1 +1,3 @@ -0.01: New App! \ No newline at end of file +0.01: New App! +0.02: Allow redirection of loads to the launcher +0.03: Allow hiding the fastloading info screen From a8ba0658d9d3b2c287bbd96a5e9b44450b9be365 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Mon, 28 Nov 2022 20:26:46 +0100 Subject: [PATCH 058/107] fastload - Fix build warnings --- apps/fastload/boot.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/fastload/boot.js b/apps/fastload/boot.js index 0d8a888ac..c9271abbf 100644 --- a/apps/fastload/boot.js +++ b/apps/fastload/boot.js @@ -18,7 +18,7 @@ let loadingScreen = function(){ let cache = require("Storage").readJSON("fastload.cache") || {}; -function checkApp(n){ +let checkApp = function(n){ // no widgets, no problem if (!global.WIDGETS) return true; let app = require("Storage").read(n); @@ -33,11 +33,11 @@ function checkApp(n){ global._load = load; -function slowload(n){ +let slowload = function(n){ global._load(n); } -function fastload(n){ +let fastload = function(n){ if (!n || checkApp(n)){ // Bangle.load can call load, to prevent recursion this must be the system load global.load = slowload; From 7d8f813e2c46e5d5f912d352e3f1f466ec886a2f Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Mon, 28 Nov 2022 21:04:47 +0100 Subject: [PATCH 059/107] tetris: fix tag --- apps/tetris/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tetris/metadata.json b/apps/tetris/metadata.json index 5669d8953..f683a5be7 100644 --- a/apps/tetris/metadata.json +++ b/apps/tetris/metadata.json @@ -5,7 +5,7 @@ "description": "Tetris", "icon": "tetris.png", "readme": "README.md", - "tags": "games", + "tags": "game", "supports" : ["BANGLEJS2"], "storage": [ {"name":"tetris.app.js","url":"tetris.app.js"}, From 4f78515a716b7edb9933e5e44e960a4197f42469 Mon Sep 17 00:00:00 2001 From: storm64 Date: Mon, 28 Nov 2022 21:21:36 +0100 Subject: [PATCH 060/107] [sleeplog] Reorder in README.md --- apps/sleeplog/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 1970d8dde..ceb3418d9 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -27,16 +27,6 @@ Logfiles are not removed on un-/reinstall to prevent data loss. ### Main App Usage --- -#### View: -| Status | Color | Height | -|-------------|:------:|----------:| -| unknown | black | 0% | -| not worn | red | 40% | -| awake | green | 60% | -| light sleep | cyan | 80% | -| deep sleep | blue | 100% | -| consecutive | violet | as status | - #### Controls: - __swipe left & right__ to change the displayed day @@ -50,6 +40,16 @@ Logfiles are not removed on un-/reinstall to prevent data loss. - __use back button widget__ (upper left corner) exit the app +#### View: +| Status | Color | Height | +|-------------|:------:|----------:| +| unknown | black | 0% | +| not worn | red | 40% | +| awake | green | 60% | +| light sleep | cyan | 80% | +| deep sleep | blue | 100% | +| consecutive | violet | as status | + --- ### Settings Usage From 4d48b8c05bf1f5c27252ff0770ef47d6086e6263 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Mon, 28 Nov 2022 22:07:50 +0100 Subject: [PATCH 061/107] messagesmusic: Fix dependency on messages library (whoops) --- apps/messagesmusic/ChangeLog | 3 ++- apps/messagesmusic/metadata.json | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/messagesmusic/ChangeLog b/apps/messagesmusic/ChangeLog index 65fd09686..1b9622f24 100644 --- a/apps/messagesmusic/ChangeLog +++ b/apps/messagesmusic/ChangeLog @@ -1,3 +1,4 @@ 0.01: New App! 0.02: Remove one line of code that didn't do anything other than in some instances hinder the function of the app. -0.03: Use the new messages library \ No newline at end of file +0.03: Use the new messages library +0.04: Fix dependency on messages library \ No newline at end of file diff --git a/apps/messagesmusic/metadata.json b/apps/messagesmusic/metadata.json index ce5281563..22e0eff52 100644 --- a/apps/messagesmusic/metadata.json +++ b/apps/messagesmusic/metadata.json @@ -1,7 +1,7 @@ { "id": "messagesmusic", "name":"Messages Music", - "version":"0.03", + "version":"0.04", "description": "Uses Messages library to push a music message which in turn displays Messages app music controls", "icon":"app.png", "type": "app", @@ -13,6 +13,5 @@ {"name":"messagesmusic.app.js","url":"app.js"}, {"name":"messagesmusic.img","url":"app-icon.js","evaluate":true} ], - "dependencies" : { "messagelib":"module" } - + "dependencies":{"messages":"module"} } From 9c42820dd12c4d6afce3d46df41fdc0e2a3fea49 Mon Sep 17 00:00:00 2001 From: storm64 Date: Mon, 28 Nov 2022 22:08:11 +0100 Subject: [PATCH 062/107] [sleeplog] Correct typos found by @thyttan --- apps/sleeplog/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index ceb3418d9..71a7ddde2 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -8,9 +8,9 @@ It is using the built in movement calculation to decide your sleeping state. Whi #### Explanations * __Detection of Sleep__ - The movement value of bangle's build in health event that is triggered every 10 minutes is checked against the thresholds for light and deep sleep. If the measured movement is lower or eaqual to the __Deep Sleep__-threshold a deep sleep phase is detected for the last 10 minutes. If the threshold is exceeded but not the __Light Sleep__-threshold than the last timeperiod is detected as light sleep phase. On exceeding even this threshold it is assumed that you were awake. + The movement value of bangle's build in health event that is triggered every 10 minutes is checked against the thresholds for light and deep sleep. If the measured movement is lower or equal to the __Deep Sleep__-threshold a deep sleep phase is detected for the last 10 minutes. If the threshold is exceeded but not the __Light Sleep__-threshold than the last timeperiod is detected as light sleep phase. On exceeding even this threshold it is assumed that you were awake. * __True Sleep__ - The true sleep value is a simple addition of all registert sleeping periods. + The true sleep value is a simple addition of all registered sleeping periods. * __Consecutive Sleep__ In addition the consecutive sleep value tries to predict the complete time you were asleep, even the very light sleeping periods when an awake period is detected based on the registered movements. All periods after a sleeping period will be summarized until the first following non sleeping period that is longer then the maximal awake duration (__Max Awake__). If this sum is lower than the minimal consecutive sleep duration (__Min Consecutive__) it is not considered, otherwise it will be added to the consecutive sleep value. @@ -205,7 +205,7 @@ The passed data object has the following properties: #### To do list - Check translations. - Add more functionallities to interface.html. -- Enable recieving data on the Gadgetbridge side + testing. +- Enable receiving data on the Gadgetbridge side + testing. __Help appreciated!__ #### Requests, Bugs and Feedback From f75cfa573ea7d56ce14938cdfd0d23f9fd26c435 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Mon, 28 Nov 2022 22:38:16 +0100 Subject: [PATCH 063/107] messagesmusic: Fix loading message UI --- apps/messagesmusic/ChangeLog | 3 ++- apps/messagesmusic/app.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/messagesmusic/ChangeLog b/apps/messagesmusic/ChangeLog index 1b9622f24..8cc3079a3 100644 --- a/apps/messagesmusic/ChangeLog +++ b/apps/messagesmusic/ChangeLog @@ -1,4 +1,5 @@ 0.01: New App! 0.02: Remove one line of code that didn't do anything other than in some instances hinder the function of the app. 0.03: Use the new messages library -0.04: Fix dependency on messages library \ No newline at end of file +0.04: Fix dependency on messages library + Fix loading message UI \ No newline at end of file diff --git a/apps/messagesmusic/app.js b/apps/messagesmusic/app.js index 11e7e5a4e..26fedecc1 100644 --- a/apps/messagesmusic/app.js +++ b/apps/messagesmusic/app.js @@ -1 +1 @@ -require('messages').openGUI({"t":"add","artist":" ","album":" ","track":" ","dur":0,"c":-1,"n":-1,"id":"music","title":"Music","state":"play","new":true}); +setTimeout(()=>require('messages').openGUI({"t":"add","artist":" ","album":" ","track":" ","dur":0,"c":-1,"n":-1,"id":"music","title":"Music","state":"play","new":true})); From 6b9e6eb4284142c6e3c60d111894dbb3b58ecaee Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Mon, 28 Nov 2022 22:59:52 +0100 Subject: [PATCH 064/107] messages: Fix/improve README It wasn't up-to-date anymore with how the library/gui now work, and still mentioned `messagelib`. --- apps/messages/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/messages/README.md b/apps/messages/README.md index 1a6758311..83524d7c8 100644 --- a/apps/messages/README.md +++ b/apps/messages/README.md @@ -9,13 +9,14 @@ Assuming you are using GadgetBridge and "overlay notifications": 1. Gadgetbridge sends an event to your watch for an incoming message 2. The `android` app parses the message, and calls `require("messages").pushMessage({/** the message */})` -3. `require("messages")` (provided by `messagelib`) calls `Bangle.emit("message", "text", {/** the message */})` +3. `require("messages")` calls `Bangle.emit("message", "text", {/** the message */})` 4. Overlay Notifications shows the message in an overlay, and marks it as `handled` -5. The default GUI app (`messages`) sees the event is marked as `handled`, so does nothing. +5. The default UI app (Message UI, `messagegui`) sees the event is marked as `handled`, so does nothing. 6. The default widget (`widmessages`) does nothing with `handled`, and shows a notification icon. -7. You tap the notification, in order to open the full GUI Overlay Notifications +7. You tap the notification, in order to open the full GUI: Overlay Notifications calls `require("messages").openGUI({/** the message */})` -8. The default GUI app (`messages`) sees the "messageGUI" event, and launches itself +8. `openGUI` calls `require("messagegui").open(/** copy of the message */)`. +9. The `messagegui` library loads the Message UI app. @@ -28,7 +29,7 @@ it like this: myMessageListener = Bangle.on("message", (type, message)=>{ if (message.handled) return; // another app already handled this message // is one of "text", "call", "alarm", "map", or "music" - // see `messagelib/lib.js` for possible formats + // see `messages/lib.js` for possible formats // message.t could be "add", "modify" or "remove" E.showMessage(`${message.title}\n${message.body}`, `${message.t} ${type} message`); // You can prevent the default `message` app from loading by setting `message.handled = true`: @@ -52,7 +53,7 @@ Bangle.on("messageGUI", message=>{ ## Requests -Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=messagelib%library +Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=[messages]%20library ## Creator From 52c807aea5dcd888bbc90a8997038446b23da334 Mon Sep 17 00:00:00 2001 From: glemco Date: Sun, 27 Nov 2022 15:03:28 +0100 Subject: [PATCH 065/107] weather: added weather condition to clkinfo picture is dynamically generated with the weather lib --- apps/weather/ChangeLog | 1 + apps/weather/clkinfo.js | 24 +++++++++++-- apps/weather/lib.js | 72 +++++++++++++++++++++++++------------- apps/weather/metadata.json | 2 +- 4 files changed, 70 insertions(+), 29 deletions(-) diff --git a/apps/weather/ChangeLog b/apps/weather/ChangeLog index b11ed24ff..d8a7440c5 100644 --- a/apps/weather/ChangeLog +++ b/apps/weather/ChangeLog @@ -15,3 +15,4 @@ 0.16: Don't mark app as clock 0.17: Added clkinfo for clocks. 0.18: Added hasRange to clkinfo. +0.19: Added weather condition to clkinfo. diff --git a/apps/weather/clkinfo.js b/apps/weather/clkinfo.js index 6191c6dbe..8cdc37be0 100644 --- a/apps/weather/clkinfo.js +++ b/apps/weather/clkinfo.js @@ -3,6 +3,7 @@ temp: "?", hum: "?", wind: "?", + txt: "?", }; var weatherJson = require("Storage").readJSON('weather.json'); @@ -14,17 +15,34 @@ weather.wind = Math.round(weather.wind[1]) + "kph"; } + function weatherIcon(code) { + var ovr = Graphics.createArrayBuffer(24,24,1,{msb:true}); + require("weather").drawIcon({code:code},12,12,12,ovr); + var img = ovr.asImage(); + img.transparent = 0; + //for (var i=0;i ({ text: weather.txt, img: weatherIcon(weather.code), + v: weather.code}), + show: function() { weatherItems.items[0].emit("redraw"); }, + hide: function () {} + }, { name: "temperature", hasRange : true, get: () => ({ text: weather.temp, img: atob("GBiBAAA8AAB+AADnAADDAADDAADDAADDAADDAADbAADbAADbAADbAADbAADbAAHbgAGZgAM8wAN+wAN+wAM8wAGZgAHDgAD/AAA8AA=="), v: parseInt(weather.temp), min: -30, max: 55}), - show: function() { weatherItems.items[0].emit("redraw"); }, + show: function() { weatherItems.items[1].emit("redraw"); }, hide: function () {} }, { @@ -32,7 +50,7 @@ hasRange : true, get: () => ({ text: weather.hum, img: atob("GBiBAAAEAAAMAAAOAAAfAAAfAAA/gAA/gAI/gAY/AAcfAA+AQA+A4B/A4D/B8D/h+D/j+H/n/D/n/D/n/B/H/A+H/AAH/AAD+AAA8A=="), v: parseInt(weather.hum), min: 0, max: 100}), - show: function() { weatherItems.items[1].emit("redraw"); }, + show: function() { weatherItems.items[2].emit("redraw"); }, hide: function () {} }, { @@ -40,7 +58,7 @@ hasRange : true, get: () => ({ text: weather.wind, img: atob("GBiBAAHgAAPwAAYYAAwYAAwMfAAY/gAZh3/xg//hgwAAAwAABg///g//+AAAAAAAAP//wH//4AAAMAAAMAAYMAAYMAAMcAAP4AADwA=="), v: parseInt(weather.wind), min: 0, max: 118}), - show: function() { weatherItems.items[2].emit("redraw"); }, + show: function() { weatherItems.items[3].emit("redraw"); }, hide: function () {} }, ] diff --git a/apps/weather/lib.js b/apps/weather/lib.js index 1d48116e1..8c59fd3e3 100644 --- a/apps/weather/lib.js +++ b/apps/weather/lib.js @@ -62,12 +62,29 @@ scheduleExpiry(storage.readJSON('weather.json')||{}); * @param x Left * @param y Top * @param r Icon Size + * @param ovr Graphics instance (or undefined for g) */ -exports.drawIcon = function(cond, x, y, r) { +exports.drawIcon = function(cond, x, y, r, ovr) { var palette; - + var monochrome=1; + if(!ovr) { + ovr = g; + monochrome=0; + } + if(monochrome) { + palette = { + sun: '#FFF', + cloud: '#FFF', + bgCloud: '#FFF', + rain: '#FFF', + lightning: '#FFF', + snow: '#FFF', + mist: '#FFF', + background: '#000' + }; + } else if (B2) { - if (g.theme.dark) { + if (ovr.theme.dark) { palette = { sun: '#FF0', cloud: '#FFF', @@ -89,7 +106,7 @@ exports.drawIcon = function(cond, x, y, r) { }; } } else { - if (g.theme.dark) { + if (ovr.theme.dark) { palette = { sun: '#FE0', cloud: '#BBB', @@ -113,19 +130,19 @@ exports.drawIcon = function(cond, x, y, r) { } function drawSun(x, y, r) { - g.setColor(palette.sun); - g.fillCircle(x, y, r); + ovr.setColor(palette.sun); + ovr.fillCircle(x, y, r); } function drawCloud(x, y, r, c) { const u = r/12; if (c==null) c = palette.cloud; - g.setColor(c); - g.fillCircle(x-8*u, y+3*u, 4*u); - g.fillCircle(x-4*u, y-2*u, 5*u); - g.fillCircle(x+4*u, y+0*u, 4*u); - g.fillCircle(x+9*u, y+4*u, 3*u); - g.fillPoly([ + ovr.setColor(c); + ovr.fillCircle(x-8*u, y+3*u, 4*u); + ovr.fillCircle(x-4*u, y-2*u, 5*u); + ovr.fillCircle(x+4*u, y+0*u, 4*u); + ovr.fillCircle(x+9*u, y+4*u, 3*u); + ovr.fillPoly([ x-8*u, y+7*u, x-8*u, y+3*u, x-4*u, y-2*u, @@ -137,19 +154,23 @@ exports.drawIcon = function(cond, x, y, r) { function drawBrokenClouds(x, y, r) { drawCloud(x+1/8*r, y-1/8*r, 7/8*r, palette.bgCloud); + if(monochrome) + drawCloud(x-1/8*r, y+2/16*r, r, palette.background); drawCloud(x-1/8*r, y+1/8*r, 7/8*r); } function drawFewClouds(x, y, r) { drawSun(x+3/8*r, y-1/8*r, 5/8*r); + if(monochrome) + drawCloud(x-1/8*r, y+2/16*r, r, palette.background); drawCloud(x-1/8*r, y+1/8*r, 7/8*r); } function drawRainLines(x, y, r) { - g.setColor(palette.rain); + ovr.setColor(palette.rain); const y1 = y+1/2*r; const y2 = y+1*r; - const poly = g.fillPolyAA ? p => g.fillPolyAA(p) : p => g.fillPoly(p); + const poly = ovr.fillPolyAA ? p => ovr.fillPolyAA(p) : p => ovr.fillPoly(p); poly([ x-6/12*r, y1, x-8/12*r, y2, @@ -182,8 +203,8 @@ exports.drawIcon = function(cond, x, y, r) { function drawThunderstorm(x, y, r) { function drawLightning(x, y, r) { - g.setColor(palette.lightning); - g.fillPoly([ + ovr.setColor(palette.lightning); + ovr.fillPoly([ x-2/6*r, y-r, x-4/6*r, y+1/6*r, x-1/6*r, y+1/6*r, @@ -194,8 +215,9 @@ exports.drawIcon = function(cond, x, y, r) { ]); } - drawBrokenClouds(x, y-1/3*r, r); + if(monochrome) drawBrokenClouds(x, y-1/3*r, r); drawLightning(x-1/12*r, y+1/2*r, 1/2*r); + drawBrokenClouds(x, y-1/3*r, r); } function drawSnow(x, y, r) { @@ -210,7 +232,7 @@ exports.drawIcon = function(cond, x, y, r) { } } - g.setColor(palette.snow); + ovr.setColor(palette.snow); const w = 1/12*r; for(let i = 0; i<=6; ++i) { const points = [ @@ -220,7 +242,7 @@ exports.drawIcon = function(cond, x, y, r) { x+w, y+r, ]; rotatePoints(points, x, y, i/3*Math.PI); - g.fillPoly(points); + ovr.fillPoly(points); for(let j = -1; j<=1; j += 2) { const points = [ @@ -231,7 +253,7 @@ exports.drawIcon = function(cond, x, y, r) { ]; rotatePoints(points, x, y+7/12*r, j/3*Math.PI); rotatePoints(points, x, y, i/3*Math.PI); - g.fillPoly(points); + ovr.fillPoly(points); } } } @@ -245,18 +267,18 @@ exports.drawIcon = function(cond, x, y, r) { [-0.2, 0.3], ]; - g.setColor(palette.mist); + ovr.setColor(palette.mist); for(let i = 0; i<5; ++i) { - g.fillRect(x+layers[i][0]*r, y+(0.4*i-0.9)*r, x+layers[i][1]*r, + ovr.fillRect(x+layers[i][0]*r, y+(0.4*i-0.9)*r, x+layers[i][1]*r, y+(0.4*i-0.7)*r-1); - g.fillCircle(x+layers[i][0]*r, y+(0.4*i-0.8)*r-0.5, 0.1*r-0.5); - g.fillCircle(x+layers[i][1]*r, y+(0.4*i-0.8)*r-0.5, 0.1*r-0.5); + ovr.fillCircle(x+layers[i][0]*r, y+(0.4*i-0.8)*r-0.5, 0.1*r-0.5); + ovr.fillCircle(x+layers[i][1]*r, y+(0.4*i-0.8)*r-0.5, 0.1*r-0.5); } } function drawUnknown(x, y, r) { drawCloud(x, y, r, palette.bgCloud); - g.setColor(g.theme.fg).setFontAlign(0, 0).setFont('Vector', r*2).drawString("?", x+r/10, y+r/6); + ovr.setColor(ovr.theme.fg).setFontAlign(0, 0).setFont('Vector', r*2).drawString("?", x+r/10, y+r/6); } /* diff --git a/apps/weather/metadata.json b/apps/weather/metadata.json index 4a8751302..c679bdc2e 100644 --- a/apps/weather/metadata.json +++ b/apps/weather/metadata.json @@ -1,7 +1,7 @@ { "id": "weather", "name": "Weather", - "version": "0.18", + "version": "0.19", "description": "Show Gadgetbridge weather report", "icon": "icon.png", "screenshots": [{"url":"screenshot.png"}], From 5eda7ae889ef54b88508efe86c5606082940cd58 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Tue, 29 Nov 2022 18:07:12 +0100 Subject: [PATCH 066/107] noteify interface Use write callback to wait for reload --- apps/noteify/interface.html | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/apps/noteify/interface.html b/apps/noteify/interface.html index 027c98860..4d7974ad9 100644 --- a/apps/noteify/interface.html +++ b/apps/noteify/interface.html @@ -18,6 +18,11 @@ var notesElement = document.getElementById("notes"); var notes = {}; +function disableFormInput() { + document.querySelectorAll(".form-input").forEach(el => el.disabled = true); + document.querySelectorAll(".btn").forEach(el => el.disabled = true); +} + function getData() { // show loading window Util.showModal("Loading..."); @@ -53,8 +58,10 @@ function getData() { buttonSave.classList.add('btn-default'); buttonSave.onclick = function() { notes[i].note = textarea.value; - Util.writeStorage("noteify.json", JSON.stringify(notes)); - location.reload(); + disableFormInput(); + Util.writeStorage("noteify.json", JSON.stringify(notes), () => { + location.reload(); // reload so we see current data + }); } divColumn2.appendChild(buttonSave); @@ -64,8 +71,10 @@ function getData() { buttonDelete.onclick = function() { notes[i].note = textarea.value; notes.splice(i, 1); - Util.writeStorage("noteify.json", JSON.stringify(notes)); - location.reload(); // reload so we see current data + disableFormInput(); + Util.writeStorage("noteify.json", JSON.stringify(notes), () => { + location.reload(); // reload so we see current data + }); } divColumn2.appendChild(buttonDelete); divColumn.appendChild(divColumn2); @@ -77,8 +86,10 @@ function getData() { document.getElementById("btnAdd").addEventListener("click", function() { const note = document.getElementById("note-new").value; notes.push({"note": note}); - Util.writeStorage("noteify.json", JSON.stringify(notes)); - location.reload(); // reload so we see current data + disableFormInput(); + Util.writeStorage("noteify.json", JSON.stringify(notes), () => { + location.reload(); // reload so we see current data + }); }); }); } From 52d71ab0cac3d04a3d4a329cd5ebbe1b291c99f0 Mon Sep 17 00:00:00 2001 From: Leon Matthes Date: Tue, 29 Nov 2022 23:14:51 +0100 Subject: [PATCH 067/107] CalClock: Version 0.5 - Use event colors - Improved all day event support --- apps/calclock/ChangeLog | 1 + apps/calclock/calclock.js | 51 +++++++++++++++++++++------------ apps/calclock/metadata.json | 2 +- apps/calclock/screenshot.patch | 32 +++++++++++++++++++++ apps/calclock/screenshot.png | Bin 2922 -> 3212 bytes 5 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 apps/calclock/screenshot.patch diff --git a/apps/calclock/ChangeLog b/apps/calclock/ChangeLog index f4a0c96f5..5c1b7c4bc 100644 --- a/apps/calclock/ChangeLog +++ b/apps/calclock/ChangeLog @@ -2,3 +2,4 @@ 0.02: More compact rendering & app icon 0.03: Tell clock widgets to hide. 0.04: Improve current time readability in light theme. +0.05: Show calendar colors & improved all day events. diff --git a/apps/calclock/calclock.js b/apps/calclock/calclock.js index a55dc05f9..5a13a202f 100644 --- a/apps/calclock/calclock.js +++ b/apps/calclock/calclock.js @@ -20,41 +20,55 @@ function zp(str) { } function drawEventHeader(event, y) { - g.setFont("Vector", 24); - + var x = 0; var time = isActive(event) ? new Date() : new Date(event.timestamp * 1000); - var timeStr = zp(time.getHours()) + ":" + zp(time.getMinutes()); - g.drawString(timeStr, 5, y); - y += 24; + + //Don't need to know what time the event is at if its all day + if (isActive(event) || !event.allDay) { + g.setFont("Vector", 24); + var timeStr = zp(time.getHours()) + ":" + zp(time.getMinutes()); + g.drawString(timeStr, 0, y); + y += 3; + x = 13*timeStr.length+5; + } g.setFont("12x20", 1); + if (isActive(event)) { - g.drawString(zp(time.getDate())+". " + require("locale").month(time,1),15*timeStr.length,y-21); + g.drawString(zp(time.getDate())+". " + require("locale").month(time,1),x,y); } else { var offset = 0-time.getTimezoneOffset()/1440; var days = Math.floor((time.getTime()/1000)/86400+offset)-Math.floor(getTime()/86400+offset); - if(days > 0) { + if(days > 0 || event.allDay) { var daysStr = days===1?/*LANG*/"tomorrow":/*LANG*/"in "+days+/*LANG*/" days"; - g.drawString(daysStr,15*timeStr.length,y-21); + g.drawString(daysStr,x,y); } } + y += 21; return y; } function drawEventBody(event, y) { g.setFont("12x20", 1); - var lines = g.wrapString(event.title, g.getWidth()-10); + var lines = g.wrapString(event.title, g.getWidth()-15); + var yStart = y; if (lines.length > 2) { lines = lines.slice(0,2); lines[1] = lines[1].slice(0,-3)+"..."; } - g.drawString(lines.join('\n'), 5, y); + g.drawString(lines.join('\n'),10,y); y+=20 * lines.length; if(event.location) { - g.drawImage(atob("DBSBAA8D/H/nDuB+B+B+B3Dn/j/B+A8A8AYAYAYAAAAAAA=="),5,y); - g.drawString(event.location, 20, y); + g.drawImage(atob("DBSBAA8D/H/nDuB+B+B+B3Dn/j/B+A8A8AYAYAYAAAAAAA=="),10,y); + g.drawString(event.location,25,y); y+=20; } + if (event.color) { + var oldColor = g.getColor(); + g.setColor("#"+(0x1000000+Number(event.color)).toString(16).padStart(6,"0")); + g.fillRect(0,yStart,5,y-3); + g.setColor(oldColor); + } y+=5; return y; } @@ -68,19 +82,19 @@ function drawEvent(event, y) { var curEventHeight = 0; function drawCurrentEvents(y) { - g.setColor(g.theme.dark ? "#0ff" : "#0000ff"); - g.clearRect(5, y, g.getWidth() - 5, y + curEventHeight); + g.setColor(g.theme.dark ? "#0ff" : "#00f"); + g.clearRect(0,y,g.getWidth()-5,y+curEventHeight); curEventHeight = y; if(current.length === 0) { y = drawEvent({timestamp: getTime(), durationInSeconds: 100}, y); } else { - y = drawEventHeader(current[0], y); + y = drawEventHeader(current[0],y); for (var e of current) { - y = drawEventBody(e, y); + y = drawEventBody(e,y); } } - curEventHeight = y - curEventHeight; + curEventHeight = y-curEventHeight; return y; } @@ -94,7 +108,7 @@ function drawFutureEvents(y) { } function fullRedraw() { - g.clearRect(5,24,g.getWidth()-5,g.getHeight()); + g.clearRect(0,24,g.getWidth()-5,g.getHeight()); updateCalendar(); var y = 30; y = drawCurrentEvents(y); @@ -117,3 +131,4 @@ var minuteInterval = setInterval(redraw, 60 * 1000); Bangle.setUI("clock"); Bangle.loadWidgets(); Bangle.drawWidgets(); + diff --git a/apps/calclock/metadata.json b/apps/calclock/metadata.json index 3aab55186..be0a1bdd8 100644 --- a/apps/calclock/metadata.json +++ b/apps/calclock/metadata.json @@ -2,7 +2,7 @@ "id": "calclock", "name": "Calendar Clock", "shortName": "CalClock", - "version": "0.04", + "version": "0.05", "description": "Show the current and upcoming events synchronized from Gadgetbridge", "icon": "calclock.png", "type": "clock", diff --git a/apps/calclock/screenshot.patch b/apps/calclock/screenshot.patch new file mode 100644 index 000000000..3fdbf79d1 --- /dev/null +++ b/apps/calclock/screenshot.patch @@ -0,0 +1,32 @@ +diff --git a/apps/calclock/calclock.js b/apps/calclock/calclock.js +index cb8c6100e..2092c1a4e 100644 +--- a/apps/calclock/calclock.js ++++ b/apps/calclock/calclock.js +@@ -3,9 +3,24 @@ var current = []; + var next = []; + + function updateCalendar() { +- calendar = require("Storage").readJSON("android.calendar.json",true)||[]; +- calendar = calendar.filter(e => isActive(e) || getTime() <= e.timestamp); +- calendar.sort((a,b) => a.timestamp - b.timestamp); ++ calendar = [ ++ { ++ t: "calendar", ++ id: 2, type: 0, timestamp: getTime(), durationInSeconds: 200, ++ title: "Capture Screenshot", ++ description: "Capture Screenshot", ++ location: "", ++ calName: "", ++ color: -7151168, allDay: true }, ++ { ++ t: "calendar", ++ id: 7186, type: 0, timestamp: getTime() + 2000, durationInSeconds: 100, ++ title: "Upload to BangleApps", ++ description: "", ++ location: "", ++ calName: "", ++ color: -509406, allDay: false } ++ ]; + + current = calendar.filter(isActive); + next = calendar.filter(e=>!isActive(e)); diff --git a/apps/calclock/screenshot.png b/apps/calclock/screenshot.png index 4ab503f2bc209f3e5993d2d54cc98a8ffc8b6a32..8b2e39784c75f9c6301234454871dc29cd29a755 100644 GIT binary patch literal 3212 zcmb_fc{CJU|DVN7J(DdNAxkJRLy;QGRG8PkjK-KTWgD~)QC{*8%~Ve!QKBplk?dv$ zV_(Pq+LbJs!62lJr5HQEdCu?u_pkSldq3yg@45G!?>+ak+{nvVOEH){3;+O#SzDPo z3bE*)aS$q;nHAsMg#Zk2w8Q`yy$bUHfT)GFnWK1N>4i9q^;}u!mr>Jo>;KPpn98 z0O&o@IzDNsrP3?wIGXyXkI|qE_D}Q?tSSC%7hKm5Jg7BGq6cY8 zJ?@tmUG+~F6hO1e^LtqfEd6_VFgE*bXoBV&g8Q}}?_ztmX*9s0H8sEJ{N<9ts~w;0 zI1*G_P~%5-G7`7$cpzzo50nlcz?}p?Tm=((>6Y!2YQ~(K%u+Mq3{%n+^oiW6w6(#egy4G__`SuZR=wwDfyJyw- zg0B2k%V*vn@A8-JmRk=#ej=yl8m@EcwSABJ=?VqUIaXXM5PJ=5+M=$z+Pq zK<6YZ}P zdVsu7Ot%1nT{F|RvCeFOg98nmi(>taal&Nod~inXydd=Kp+Jc4GtFcM+~vHWw8Y68zy7IyCB zF-sA=&yL=>E1a-%=XOzG9;g}V&j7Wl>q?PJMReF%iEBTH%Rre9Qnuk5EAb!Nr9d4% z)Yq74P7Q%9Q{<>Y+Rx3;Troz8zVp~iwT0(@n;v*Lak$2bEYcc9Aw!MMF}J-+7Q2%P zJV%Yg(wReSXt0|V>i}q>LK1eX&c~Y>u=d=3%&f$FF==l%GaFeH0_|kruGCz5+ZGU{4-VhNig4d{Yk!>-msrAmVUF1Kt30jhcx2 zaRHqB$=e2Q0C&|m4YIc`@p6HQL^-dZ|JnV?tJLD321W?D)IrS zROi2;ujEF8YJ`SAbJygO z80@*_ydE51Tb4yM*Hz$WyyxCvTw@JVjzrA1?evx-Rio0v`sJs~zG}84`d^HKZ#bDS z*fRaF!jIJbBzi*0B%FzVqK=MIVOrd>%34I+Q)0(_9CfUmGaosJ{(x zT_qz{z6QmB9s%mr34dAeyRe3WSI4PhrwSIwgW4M&3VI126!cCjTK#JmGjp&i zlDSm?%9c_%-d5U~mquEc4wYw%j%0}a$Q)gIqi&h(2mnQ=Ytrlgj zP!O*miCmp~mZ8$*paE$t?h5m*(_J{vJx4Y4qd*RK!*A-# zZ^taqso`@;DJBqdX$QLP0eoEf*FW0r_eZpyvV}Vwvnr|MJ$geC5~qI;AuIYS+Cgx` zAq@Su02-{gan(!;k_ddM=n82AusTNU|@lw|>Fln!o*Kh5Eb&`n( zp)R@syCYGtfW2^4yz=Q1&;mxnzpL#9=$p=~x&Jf&^?#TB(;SWq1IP~{tAAwVy zJC1n&?k55eWNL;PigZPo0`3PX9CKm={uSv{R3gj`GYbZ&T($(e{phs>_Xk zF-r@Ia|Je99~yj;isrVc07hmWPvyMReuEG+dg>>3CzfyGcqsS?!1%~K zr;sMLj0eipTX^^B*}CTyF6r4XoIP>fLn^b6HXLg89j5SD5_@v}#j1^u+@rDzN_QkA zr6iI0*|5la6iWgly)uTYrn<_B$8&_DCub<8lNslGRm0>xM&pSb*<715q``yzzqzH` zLeefF4=l?Hy@z|h{SszOXMA+&F8()j4UKt$jx}}Wy`9TegZLIJIrrsS_AbqPcfd)T zZ4V&#C_sx{+%`;}YxqnZz9D&Q&}^vfudPf0x|!9)71|CrIjJhcsP%RrCwn8ZwY~Pw z_^`)bq1uv~x6-O*{2XZs2I|Bxn0OA;cIL^zsrDg>3Xp8Q zUXpTapLn^N$(6w9Nq8mH2BHqGMcntDjzj9OWJD)B2)@F!P=20m6||Ol{wa1(SxzpAG9SLW{b z^4$uJ@G;xicxGE6X$WS zzA_xqzjF$hsa7)FsoCQGR7OBxKuntiEkBZD?eM4604S+k90j1n>m z5wef{G4(4E6T;Ye=J)=8&i~Ex;@;1>_nvd_x#ymH?uBLe}IC-g~;;JbTNmC_sXHG=m>W zW;M6#JsOVhemK3dySBErQn+7$v4VYo1r2m7EI7lssxZbCM8NFjMVJb)4dVmj3i`dO z5Drn|wo*J!v@SMp;d4Ph!9pO}7oy=fASreT&dVH{vt9!24;I*AfMT=UI1xJjI1L0f zZ*K`T0tje{Fh9Lyu$T#}LjI3ZAEWEOb#~d^JB!{qH^BZq^Cy08OY$FMha#0R?NWgI z9TySyj_2^aKA;OXdhm4ucni2Q-O42Yna`J*U$k-*)`Uh$qZts%iku1$eFJr`*Oj(` zz7wQYQm22lhm{yBbuQFR{NU25Inr&Vnl(%^fz2g((_tlQQ}L8OYZ2kVo$eiTE%(Jl zEhM1nfu*n;(bezI#~ZBtaZ;x*#KvxWyC87xKH>3d)A?Iz-@chT)^oo+ph{guMxRK& z#$B)@afM4h5=BfKZ|+@zOo|~&Z|R)xD<~*v^w+S2v9HN+?acnTB>q$WvAwcM!#d!o z6)Qh2Y5c7JB$_{h4Xg*hN=TG5@3|fr@+u+z?8_a33eh2+B`X0z4=`lzlD%t7sBy6i z&b8;b9`#GK!JmVYoFV zjI8|PYg<7u&G+$>kizylys8~l3r5um(0^VS;|t=$)@pcdQ;8Z~chK3_!oYACy{Rw> z@YM7lFXut}zxR2a_p?RNmx;^Hc?vvhzhOK4U8HS;9!kjQ<{Ex)XXqcr$|I135fxHl zlg1fcPdhqK5EaFxZpo)G?$dgnjd-1;>pW2sSMN=@Cw#VRET9X~v}qBGT_TvlF8964 ze8OyJ!vCt%QO|Fo-Oa#Z5g1`xN%Bv-zV8&gf+32n+~NIDJG-LV=e8= zjCew!sI^y&5PiS=!|fAwNAVCPKXLe~idx_OPp2QjssIEX&wLEuI2}~N6?HKFgV&qg zB<@?ZgwmDWh50;E#mqTIm_kdsr@| zBuk#rq15r*0RrikguIuv`$UGf68KsIYV5VT#2O~wJZ^+2?Uw5G{zzv_tgC1J6v`8p z3`Io+c1>nFvAYA+hrZ(jMQ?U`AWP`ui?Oyenb1?tUHi5*0OD2){0+#%cRI|Mv*^BS zc7ZMMa+s!Oc>Pl_xR}4#w;mteoR{hzk$j%VOk-Bz8-?g=C}rJB{&j0dE&%6OuU+}C zquwTP=Uwi-n&RO5o{C#lsXP6M4{*Qw>e4BaQ;hFa2>P5~zOKi3`qb8^ac`yd7xbFm zwB_}ki-|+yU)=!DW*I7>jolx+=37l8q{g0>)XK?{>KaAKS!nx?jpd!&mZR7jW==nm zS3JDJif>!=nP@=pq7Zkw+$X`;UhzoX%lZjrvYRXCR!vcV=V3AfmB3vRZsaY$EWpcq zHC?$5+z1Z8!%Ghn^osoqdX!<;pS_QfOAzw7SmI}DQ9GwEIF?8DicW6d8~C*iO(uyo zsk7?%9M$FBYKn@XB>t+m$r%e3WZHccB}P>l#C4ldY?reW`l$I7P#ar;wM~Piq!l3Cd_yW8 zJxsWvu?)u$buFqY=gh-9G~jpy({{PF__CxZEDM+}6OALz%?6zL9q1lmopk%Gp4_l$#A2}=lC`fMuJYp!(we5?!MA6F6w7@H0Si%x?G@>4oSzA zu#eH^Iz?!bxi4fQ78RC|^zPte)BrZn9RS?_x(C$P@Ij5HGn$j<5AVE0O3nN z9V7%lziZ*k%+|LaIwgOcXx{NR>9ai1IeSv@f!zOPjp|(1|LnA2Ia27ns_bZhqhyX& z+p|vKpQYA;S0SiNp0CMdjN%i!ZE|(j`p9!&Z~gki(aw{^^(+n09#}LiKxerU`yXo2 zzA(!0$}yxP4QdeJ60mv($(IWL!mq(L z1iATyunu{@)}^QYEwxq~pM|)q*>8Uz45t(VhaI6OF#Ri`=+#thb$a)>(53jCGI0AF zwqH$|zEE}yl9}s8y`@OGxK%P6Hh{r4VV z*c)1~<7D{&m~8lo4|szz$q$%Ny6dxyZhq)mKSmw>&vcOT(B+YhK)um}RfX`3I!55p zyoIsb%3|W%>1ID=hj(gRb+*Spv$c`x>udM3SDL@Zw ztJOiBp;uV*dcO3E9l(npQvJxJa?@HxZYJTiSP{JR1+2PSnjg(jaQ;JVws3A+rABa_ zg66?0+4EFCqti=HlpD)8cJfxGo!{(EHjhd3;IuT~{ZDmI9rwm)xi_;G1T#rj9%!9DP7Lc##OHBdORbICq$LE> z=w>G*$f2l3h3Vf?IOJ1;nwRMf<5E@5I2dMw0Twfb+YC4t?`DbnyL=D~|6DFF$Dym& muF8Ir;3U!VG(`8qccAav(9f+Nec|JfXTZeJ%z&cj68B$xy>~?b From 938f881653fed9c3e81a6a1b6785bdde81e11f9b Mon Sep 17 00:00:00 2001 From: m-p-3 Date: Tue, 29 Nov 2022 21:17:24 -0500 Subject: [PATCH 068/107] Added several icons to MessageIcons' lib.js Also added some "colors" from the official branding guidelines with a reference to the source info. Related to #2189 --- apps/messageicons/lib.js | 112 +++++++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 39 deletions(-) diff --git a/apps/messageicons/lib.js b/apps/messageicons/lib.js index ff9f4b680..d10982b63 100644 --- a/apps/messageicons/lib.js +++ b/apps/messageicons/lib.js @@ -5,44 +5,73 @@ exports.getImage = function(msg) { */ if (msg.img) return atob(msg.img); const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase(); - if (s=="airbnb") return atob("GBgBAAAAAAAAAAAAADwAAH4AAGYAAMMAAIEAAYGAAYGAAzzAA2bABmZgBmZgDGYwDDwwCDwQCBgQDDwwB+fgA8PAAAAAAAAAAAAA"); - if (s=="alarm" || s =="alarmclockreceiver") return atob("GBjBAP////8AAAAAAAACAEAHAOAefng5/5wTgcgHAOAOGHAMGDAYGBgYGBgYGBgYGBgYDhgYBxgMATAOAHAHAOADgcAB/4AAfgAAAAAAAAA="); + if (s=="airbnb") return atob("GBgBAAAAADwAAH4AAMMAAIMAAYGAAQGAAwDAAwDABjxgBn5gDMMwDMMwGMMYGMMYMGYMMGYMIDwEIBgEIDwEMH4MHee4D4HwAAAA"); // icons/airbnb.png + if (s=="alarm" || s =="alarmclockreceiver") return atob("GBgBAAAAAAAAAgBABwDgHn54Of+cE8PIBwDgDhhwDBgwHBg4GBgYGBgYGBgYGA4YHAc4DAEwDgBwBwDgA8PAAf+AAH4AAAAAAAAA"); // icons/alarm.png + if (s=="amazon shopping") return atob("GBgBAAAAAP8AAf+AA//AA+fAA8PAAIPAAD/AAP/AA//AA+PAB8PAB8fAB8fgB//gA//gA/3AAPCecAAeOAAeDwH0B//kAf+AAAAA"); // icons/amazon.png if (s=="bibel") return atob("GBgBAAAAA//wD//4D//4H//4H/f4H/f4H+P4H4D4H4D4H/f4H/f4H/f4H/f4H/f4H//4H//4H//4GAAAEAAAEAAACAAAB//4AAAA"); + if (s=="bitwarden" || s=="1password" || s=="lastpass" || s=="dashlane") return atob("GBgBAAAAABgAAP8AA//AD4/wHg/4GA/4GA/4GA/4GA/4GA/4GA/4H/AYH/AYH/A4D/AwD/BwB/BgB/DgA/HAAfeAAP8AADwAAAAA"); // icons/security.png if (s=="bring") return atob("GBgBAAAAAAAAAAAAAAAAAHwAAFoAAf+AA/+AA/+AA/+AA/eAA+eAA0+AAx+AA7+AA/+AA//AA/+AAf8AAAIAAAAAAAAAAAAAAAAA"); if (s=="calendar" || s=="etar") return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B9m2B//+B//+Btm2B//+B//+Btm+B//+B//+A//8AAAAAAAAAAAAA=="); - if (s=="corona-warn") return atob("GBgBAAAAABwAAP+AAf/gA//wB/PwD/PgDzvAHzuAP8EAP8AAPAAAPMAAP8AAH8AAHzsADzuAB/PAB/PgA//wAP/gAH+AAAwAAAAA"); - if (s=="discord") return atob("GBgBAAAAAAAAAAAAAIEABwDgDP8wH//4H//4P//8P//8P//8Pjx8fhh+fzz+f//+f//+e//ePH48HwD4AgBAAAAAAAAAAAAAAAAA"); - if (s=="facebook" || s=="messenger") return atob("GBiBAAAAAAAAAAAYAAD/AAP/wAf/4A/48A/g8B/g+B/j+B/n+D/n/D8A/B8A+B+B+B/n+A/n8A/n8Afn4APnwADnAAAAAAAAAAAAAA=="); + if (s=="chat") return atob("GBgBAAAAAf/8A//+A//+A//+OAB+e/8+e/++e/++e/++e/++e/++e/++ef+8fAAAf//Af//Af//Af//Af/+AcAAAYAAAQAAAAAAA"); // icons/google chat.png + if (s=="chrome") return atob("GBgBAAAAAAAAAP8AA//AB+fgDwDwHgB4HAA4Pj/8OmYcO8McMYEMMYEMOMMcOGccOD4cHAw4Hgx4DxjwB//gA//AAP8AAAAAAAAA"); // icons/chrome.png + if (s=="corona-warn") return atob("GBgBAAAAAAAAABgAABgABhhgDn5wD//wA8PAA+fAB2bgBgBgPpl8Ppl8BgBgB2bgA+fAA8PAD//wDn5wBhhgABgAABgAAAAAAAAA"); // icons/coronavirus.png + if (s=="bmo" || s=="desjardins" || s=="rbc mobile" || s=="nbc" || s=="rabobank" || s=="scotiabank" || s=="td (canada)") return atob("GBgBAAAAADgAAP4AAe8AB4PAHgDwP//4P//4AAAAAAAADjjgDjjgDjjgDjjgDjjgDjjgDjjgAAAAAAAAP//4P//4AAAAAAAAAAAA"); // icons/bank.png + if (s=="discord") return atob("GBgBAAAAAAAAAAAAAAAAA4HAD//wH//4H//4P//8P//8P//8fn5+fDw+fDw+fn5+f//+f//+ff++PgB8DgBwAAAAAAAAAAAAAAAA"); // icons/discord.png + if (s=="drive") return atob("GBgBAAAAAAAAAH8AAH8AAT+AA7/AA9/AB8/gB+/gD+fwD+fwH8P4P8P8P4H8fwAAf3/+Pn/8Pv/8HP/4Df/wC//wAAAAAAAAAAAA"); // icons/google drive.png + if (s=="element") return atob("GBgBAAAAAHwAAH4AAH8AAAeAAePAB+HAD+DgHgDgPADuOADucAAOcAAOdwAcdwA8BwB4BwfwA4fgA8eAAeAAAP4AAH4AAD4AAAAA"); // icons/matrix element.png + if (s=="facebook") return atob("GBgBAAAAAAAAAH4AAf+AB//gD//wD/DwH+D4H+P4P+f8P+f8P+f8PwD8PwD8PwD8H+f4H+f4D+fwD+fwB+fgAeeAAOcAAAAAAAAA"); // icons/facebook.png + if (s=="messenger") return atob("GBgBAAAAAAAAAP8AA//AB//gD//wH//4H//4P//8P9+8P458PwB8PgD8PnH8Pfv8H//4H//4D//wB//gB//AB/8AAwAAAAAAAAAA"); // icons/facebook messenger.png + if (s=="firefox" || s=="firefox beta" || s=="firefox nightly") return atob("GBgBAAAAAAAAAAMAAAcAAAeABA/ADY/gH4P4H4H4H8H8P/H8P+D8PwD8PwD8PwD8H4H4H8P4H//4D//wB//gA//AAP8AAAAAAAAA"); // icons/firefox.png + if (s=="f-droid" || s=="neo store" || s=="aurora droid") return atob("GBgBAAAAQAACYAAGP//8H//4H//4HH44HH44H//4AAAAH//4H8P4H734H374HsN4Hvl4Hv14Hvl4HsN4H374H734H8P4D//wAAAA"); // icons/security.png + if (s=="github") return atob("GBgBAAAAAAAAAH4AAf+AB//gD//wDv9wHgB4HgB4PAA8PAA8PAA8PAA8PAA8PgB8HwD4G8P4DcPwDgPwB4PgAcOAAAAAAAAAAAAA"); // icons/github.png + if (s=="gitlab") return atob("GBgBAAAABAAgDAAwDAAwHgB4HgB4PgB8PwD8P//8f//+f//+f//+f//+f//+f//+P//8H//4D//wA//AAf+AAP8AADwAABgAAAAA"); // icons/gitlab.png if (s=="gmx") return atob("GBgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEJmfmd8Zuc85v847/88Z9s8fttmHIHiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); - if (s=="google") return atob("GBiBAAAAAAD/AAP/wAf/4A/D4B8AwDwAADwAAHgAAHgAAHAAAHAH/nAH/nAH/ngH/ngAHjwAPDwAfB8A+A/D8Af/4AP/wAD/AAAAAA=="); - if (s=="google home") return atob("GBiCAAAAAAAAAAAAAAAAAAAAAoAAAAAACqAAAAAAKqwAAAAAqroAAAACquqAAAAKq+qgAAAqr/qoAACqv/6qAAKq//+qgA6r///qsAqr///6sAqv///6sAqv///6sAqv///6sA6v///6sA6v///qsA6qqqqqsA6qqqqqsA6qqqqqsAP7///vwAAAAAAAAAAAAAAAAA=="); // 2 bit unpaletted + if (s=="google") return atob("GBgBAAAAAP8AA//AB//gD//gH+fAP4CAPwAAPgAAfAAAfA/+fA/+fA/+fA/+fAA+PgA+PwB8P4D8H+f4D//4B//wA//AAP8AAAAA"); // icons/google.png + if (s=="google home") return atob("GBgBAAAAABgAADwAAH4AAf4AA/zAB/vgD/fwH+f4P4H8fwD+fgB+fAA+eAA+cAA+bAA+HAA+PAA+ff++ff++ff++ff++Pf+8AAAA"); // icons/google home.png + if (s=="google play store") return atob("GBgBAAAAAAAAAH4AAP8AAMMAAMMAP//8P//8MAAMMAAMMGAMMHgMMH4MMH8MMH4MMHgMMGAMMAAMMAAMP//8H//4AAAAAAAAAAAA"); // icons/google play store.png if (s=="home assistant") return atob("FhaBAAAAAADAAAeAAD8AAf4AD/3AfP8D7fwft/D/P8ec572zbzbNsOEhw+AfD8D8P4fw/z/D/P8P8/w/z/AAAAA="); - if (s=="instagram") return atob("GBiBAAAAAAAAAAAAAAAAAAP/wAYAYAwAMAgAkAh+EAjDEAiBEAiBEAiBEAiBEAjDEAh+EAgAEAwAMAYAYAP/wAAAAAAAAAAAAAAAAA=="); + if (s=="instagram") return atob("GBgBAAAAD//wH//4OAAccAAOYABmYDxmYP8GYeeGYYGGY4HGYwDGYwDGY4HGYYGGYeeGYP8GYDwGYAAGcAAOOAAcH//4D//wAAAA"); // icons/instagram.png if (s=="kalender") return atob("GBgBBgBgBQCgff++RQCiRgBiQAACf//+QAACQAACR//iRJkiRIEiR//iRNsiRIEiRJkiR//iRIEiRIEiR//iQAACQAACf//+AAAA"); - if (s=="lieferando") return atob("GBgBABgAAH5wAP9wAf/4A//4B//4D//4H//4P/88fV8+fV4//V4//Vw/HVw4HVw4HBg4HBg4HBg4HDg4Hjw4Hj84Hj44Hj44Hj44"); - if (s=="mattermost") return atob("GBgBAAAAAPAAA+EAB4MADgcYHAcYOA8MOB8OeD8GcD8GcH8GcD8HcD8HeBwHeAAOfAAOfgAePwA8P8D8H//4D//wB//gAf/AAH4A"); + if (s=="keep notes") return atob("GBgBAAAAAAAAH//4P//8P8P8Pzz8P378Pv98Pv98Pv98Pv98P378Pzz8P738P4H8P738P738P4GMP8OYP/+wP//gH//AAAAAAAAA"); // icons/google keep.png + if (s=="lieferando") return atob("GBgBAAAAADwAAH4AAP/gAf/wA//wB//wD//wH//4H/98Pt58ft5+Ptx8DtxwDtxwDhxwDhhwDhhwDzhwD75wD75wD75wB77gAAAA"); // icons/lieferando.png + if (s=="linkedin") return atob("GBgBAAAAf//+f//+f//+ef/+cf/+cf/+f//+f//+ccw+ccAeccAecccOcceOcceOcceOcceOcceOcceOec+ef//+f//+f//+AAAA"); // icons/linkedin.png + if (s=="maps" || s=="organic maps" || s=="osmand") return atob("GBgBAAAAAAAAAAAAAeAYD/z4H//4GMeYGMMYGMMYGMMYGMMYGMMYGMMYGMMYGMMYGMMYGMMYGeMYH//4Hz/wGAeAAAAAAAAAAAAA"); // icons/map.png + if (s=="mastodon" || s=="fedilab" || s=="tooot" || s=="tusky") return atob("GBgBAAAAB//gD//4H//4P//8PBg8PAA8fOMeeOeeeOeeOOeeOOecOP+cOP+cP//8P//4P//4P//gHwAAH4AAD+cAB/8AAf4AAAAA"); // icons/mastodon.png + if (s=="mattermost") return atob("GBgBAAAAAPAAA+EAB4GADgOQHAeYOA+cOB+MeB+OcD+GcD+GcD+GeD8OeB4OeAAOfAAePgA8P4B8H/f4D//wB//gA//AAP8AAAAA"); // icons/mattermost.png if (s=="n26") return atob("GBgBAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAOIAAOIAAPIAANoAANoAAM4AAMYAAMYAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAA"); + if (s=="netflix") return atob("GBgBAAAAA8PAA+PAAePAAePAAfPAAvPAA/PAAvvAAn/AA/nAA3/AA/7AA5/AA/5AA99AA8/AA89AA8+AA8eAA8eAA8fAA8PAAAAA"); // icons/netflix.png + if (s=="news" || s=="cbc news" || s=="rc info" || s=="reuters" || s=="ap news" || s=="la presse" || s=="nbc news") return atob("GBgBAAAAAAAAAAAALaW0P//8P//8P//8P//8MAAMMAAMMAAMP//8P//8MBwcMBwcMB/8MB/8MBwcMBwcP//8P//8AAAAAAAAAAAA"); // icons/news.png if (s=="nextbike") return atob("GBgBAAAAAAAAAAAAAAAAAAAAAACAfgDAPwDAP4HAH4N4H8f8D82GMd8CMDsDMGMDMGGGGMHOD4D8AAAAAAAAAAAAAAAAAAAAAAAA"); if (s=="nina") return atob("GBgBAAAABAAQCAAICAAIEAAEEgAkJAgSJBwSKRxKSj4pUn8lVP+VVP+VUgAlSgApKQBKJAASJAASEgAkEAAECAAICAAIBAAQAAAA"); - if (s=="outlook mail") return atob("HBwBAAAAAAAAAAAIAAAfwAAP/gAB/+AAP/5/A//v/D/+/8P/7/g+Pv8Dye/gPd74w5znHDnOB8Oc4Pw8nv/Dwe/8Pj7/w//v/D/+/8P/7/gf/gAA/+AAAfwAAACAAAAAAAAAAAA="); - if (s=="paypal") return atob("GBgBAAAAAAAAAAAAAf+AAf/AAf/gA//gA//gA//wA//wA//wA//wB//wB//wB//gB/+AB/gAB/gAB/gAAPgAAPgAAAAAAAAAAAAA"); - if (s=="phone") return atob("FxeBABgAAPgAAfAAB/AAD+AAH+AAP8AAP4AAfgAA/AAA+AAA+AAA+AAB+AAB+AAB+OAB//AB//gB//gA//AA/8AAf4AAPAA="); - if (s=="post & dhl") return atob("GBgBAPgAE/5wMwZ8NgN8NgP4NgP4HgP4HgPwDwfgD//AB/+AAf8AAAAABs7AHcdgG4MwAAAAGESAFESAEkSAEnyAEkSAFESAGETw"); - if (s=="signal") return atob("GBgBAAAAAGwAAQGAAhggCP8QE//AB//oJ//kL//wD//0D//wT//wD//wL//0J//kB//oA//ICf8ABfxgBYBAADoABMAABAAAAAAA"); - if (s=="skype") return atob("GhoBB8AAB//AA//+Af//wH//+D///w/8D+P8Afz/DD8/j4/H4fP5/A/+f4B/n/gP5//B+fj8fj4/H8+DB/PwA/x/A/8P///B///gP//4B//8AD/+AAA+AA=="); - if (s=="slack") return atob("GBiBAAAAAAAAAABAAAHvAAHvAADvAAAPAB/PMB/veD/veB/mcAAAABzH8B3v+B3v+B3n8AHgAAHuAAHvAAHvAADGAAAAAAAAAAAAAA=="); - if (s=="snapchat") return atob("GBgBAAAAAAAAAH4AAf+AAf+AA//AA//AA//AA//AA//AH//4D//wB//gA//AB//gD//wH//4f//+P//8D//wAf+AAH4AAAAAAAAA"); - if (s=="steam") return atob("GBgBAAAAAAAAAAAAAAAAAAAAAAfgAAwwAAvQABvQABvQADvQgDww4H/g+f8A/zwAf9gAH9AAB8AAACAAAcAAAAAAAAAAAAAAAAAA"); - if (s=="teams") return atob("GBgBAAAAAAAAAAQAAB4AAD8IAA8cP/M+f/scf/gIeDgAfvvefvvffvvffvvffvvff/vff/veP/PeAA/cAH/AAD+AAD8AAAQAAAAA"); - if (s=="telegram" || s=="telegram foss") return atob("GBiBAAAAAAAAAAAAAAAAAwAAHwAA/wAD/wAf3gD/Pgf+fh/4/v/z/P/H/D8P/Acf/AM//AF/+AF/+AH/+ADz+ADh+ADAcAAAMAAAAA=="); - if (s=="threema") return atob("GBjB/4Yx//8AAAAAAAAAAAAAfgAB/4AD/8AH/+AH/+AP//AP2/APw/APw/AHw+AH/+AH/8AH/4AH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); - if (s=="to do" || s=="opentasks") return atob("GBgBAAAAAAAAAAAwAAB4AAD8AAH+AAP/DAf/Hg//Px/+f7/8///4///wf//gP//AH/+AD/8AB/4AA/wAAfgAAPAAAGAAAAAAAAAA"); - if (s=="twitch") return atob("GBgBH//+P//+P//+eAAGeAAGeAAGeDGGeDOGeDOGeDOGeDOGeDOGeDOGeAAOeAAOeAAcf4/4f5/wf7/gf//Af/+AA/AAA+AAAcAA"); - if (s=="twitter") return atob("GhYBAABgAAB+JgA/8cAf/ngH/5+B/8P8f+D///h///4f//+D///g///wD//8B//+AP//gD//wAP/8AB/+AB/+AH//AAf/AAAYAAA"); + if (s=="outlook mail") return atob("GBgBAAAAAAAAAP/8AP/8AP/8AJjMf/jMf//8f//8cHjMd3jMZz/+Zz/+d3jecHj+f//mf/eGf/PGAwDmAwA+A//+Af/+AAAAAAAA"); // icons/outlook.png + if (s=="paypal") return atob("GBgBAAAAA/+AA//gA//wB//wB//wB//wB//wB//wB//gD//gD//ID/+ID/wwD4BwD5/gD74AH7gAHzAAHzAAHzAAAHAAAHAAAAAA"); // icons/paypal.png + if (s=="phone") return atob("GBgBAAAAAAAAH4AAP8AAP8AAP8AAH8AAH8AAH8AAH4AADwAADwAABwAAA4AAA8HwAeP8AP/8AH/8AD/8AA/8AAP8AAB4AAAAAAAA"); // icons/phone.png + if (s=="plex") return atob("GBgBAAAAB/gAB/gAA/wAAf4AAf4AAP8AAH+AAH+AAD/AAB/gAB/gAB/gAB/gAD/AAH+AAH+AAP8AAf4AAf4AA/wAB/gAB/gAAAAA"); // icons/plex.png + if (s=="pocket") return atob("GBgBAAAAAAAAP//8f//+f//+f//+f//+f//+fP8+eH4efDw+fhh+fwD+f4H+P8P8P+f8H//4H//4D//wB//gAf+AAH4AAAAAAAAA"); // icons/pocket.png + if (s=="post & dhl") return atob("GBgBAAAAAAAAAAAAAAAAP/+Af/+AYAGAYAGAYAHwYAH4YAGMYAGGYAH+YAH+bwH+f//+ef+eGYGYH4H4DwDwAAAAAAAAAAAAAAAA"); // icons/delivery.png + if (s=="proton mail") return atob("GBgBAAAAAAAAAAAAQAACYAAGcAAOeAAePABeXgDebwHed4Pee/fefe/efh/ef//ef//ef//ef//ef//ef//eP//cAAAAAAAAAAAA"); // icons/protonmail.png + if (s=="reddit" || s=="sync pro" || s=="sync dev" || s=="boost" || s=="infinity" || s=="slide") return atob("GBgBAAAAAAAAAAYwAAX4AAh4AAgwAAgAAAgAAH4AAf+AN//sf//+fn5+PDw8HDw4Hn54H//4H//4DzzwB4HgAf+AAH4AAAAAAAAA"); // icons/reddit.png + if (s=="signal") return atob("GBgBAAAAAL0AAYGABH4gCf+QE//IB//gL//0b//2H//4X//6X//6X//6X//6H//4b//2L//0D//gL//ID/+QYH4gVYGAcL0AAAAA"); // icons/signal.png + if (s=="skype") return atob("GBgBAAAAB8AAH/8AP//AP//gf8fwfwD4fgB4fjx8fj/8Pg/8PwH8P4B8P/h8Pnx+Pjx+Hhh+HwD+D8P+B//8A//8AP/4AAPgAAAA"); // icons/skype.png + if (s=="slack") return atob("GBgBAAAAAOcAAeeAAeeAAeeAAGeAAAeAP+ecf+eef+e+f+e+AAAAAAAAfef+fef+eef+Oef8AeAAAeYAAeeAAeeAAeeAAOcAAAAA"); // icons/slack.png + if (s=="snapchat") return atob("GBgBAAAAAAAAAAAAAH4AAf+AAYGAAwDAAwDAAwDADwDwDwDwDgBwBwDgBwDgDgBwHAA4OAAcHAA4D4HwB//gAH4AAAAAAAAAAAAA"); // icons/snapchat.png + if (ss="starbucks") return atob("GBgBAAAAAAAAAAAAD//4D//8DADMDADMDADMDAD8DAD4DADADADADADADADADgHAB/+AA/8AAAAAAAAAP//wP//wAAAAAAAAAAAA"); // icons/cafe.png + if (s=="steam") return atob("GBgBAAAAAAAAAf+AA//AD//wD//wH/g4P/OcP/RcP+RcP+ReH8OeB4A+AAH+AMP8IC/8OS/8HN/4Dj/wD//wA//AAf+AAAAAAAAA"); // icons/steam.png + if (s=="teams") return atob("GBgBAAAAAAgAAD4AADcYAGM8AGNmP/dmP/48MDAYMD/+PP/+PPBmPPBmPPBmPPBmP/BmP/BmH+B+AYD4AMDAAOOAAH8AABwAAAAA"); // icons/teams.png + if (s=="telegram" || s=="telegram foss") return atob("GBgBAAAAAAAAAAAAAAAeAAB+AAP+AA/+AD/+Af9+B/z+H/n8f+P8f8f8Dw/8AB/8AB/8AB/4AAf4AAP4AAD4AABwAAAAAAAAAAAA"); // icons/telegram.png + if (s=="threema") return atob("GBgBAAAAAP8AA//AB//gD//wH8P4H9v4H734P5n8P4H8P4H8H4H4H4H4D//wD//gD//AH/8AHDwAAAAAAAAABhhgDzzwBhhgAAAA"); // icons/threema.png + if (s=="tiktok") return atob("GBgBAAAAAAAAAAcAAAcAAAeAAAfAAAfwAAf4AAf4AMd4A8cAB8cAD8cADwcAHgcAHgcAHg8ADw8AD/4AB/4AA/wAAfAAAAAAAAAA"); // icons/tiktok.png + if (s=="to do" || s=="opentasks" || s=="tasks") return atob("GBgBAAAAAHwAAf+AA//ID4GcHwA8HAB4PADwOAHgcAPGcAeOcY8Oc94OcfwOcPgOOHAcOCAcHAA4DgB4D4HwB//gAf+AAH4AAAAA"); // icons/task.png + if (s=="transit") return atob("GBgBAAAAD//wP//8P//8f//+f/j+ffA+eOA+eOMef+cefef+eOe+fecef+e+eOf+eOcefAcefA++fx/+f//+P//8P//8D//wAAAA"); // icons/transit.png + if (s=="twitch") return atob("GBgBAAAAA//8B//8DgAMHgAMPhjMPhjMPhjMPhjMPhjMPgAMPgAMPgAYPgAwP+fgP+/AP/+AP/8AP/4AAeAAAcAAAYAAAQAAAAAA"); // icons/twitch.png + if (s=="twitter") return atob("GBgBAAAAAAAAAAAAAAPAIAf8MA/4PA/8Pg/4H//4H//4P//4P//wH//wD//wD//gD//AA//AAf+AB/8AP/wAD/AAAAAAAAAAAAAA"); // icons/twitter.png + if (s=="uber" || s=="lyft") return atob("GBgBAAAAAAAAAAAAAH4AAH4AB//gB//gDgBwDAAwDAAwH//4H//4GAAYG4HYG4HYG4HYGAAYH//4H//4HAA4HAA4AAAAAAAAAAAA"); // icons/taxi.png + if (s=="vlc") return atob("GBgBAAAAABgAABgAADwAADwAAAAAAAAAAAAAAAAAAIEAAP8AAP8AAf+AAP8AAAAADAAwDAAwHAA4HwD4H//4P//8P//8P//8AAAA"); // icons/vlc.png if (s=="warnapp") return atob("GBgBAAAAAAAAAAAAAH4AAP8AA//AA//AD//gP//gf//4f//+/+P+/8H//8n//4n/fxh/fzg+Pj88Dn44AA4AAAwAAAwAAAgAAAAA"); - if (s=="whatsapp") return atob("GBiBAAB+AAP/wAf/4A//8B//+D///H9//n5//nw//vw///x///5///4///8e//+EP3/APn/wPn/+/j///H//+H//8H//4H//wMB+AA=="); + if (s=="whatsapp") return atob("GBgBAAAAAP8AA//AB4HwDgB4HAA4OAAcMYAMc8AOc8AGY8AGYcAGYeAGYPOGcH/OcD/OMA+MOAAcMAA4MgBwf8Pgf//AcP8AAAAA"); // icons/whatsapp.png if (s=="wordfeud") return atob("GBgCWqqqqqqlf//////9v//////+v/////++v/////++v8///Lu+v8///L++v8///P/+v8v//P/+v9v//P/+v+fx/P/+v+Pk+P/+v/PN+f/+v/POuv/+v/Ofdv/+v/NvM//+v/I/Y//+v/k/k//+v/i/w//+v/7/6//+v//////+v//////+f//////9Wqqqqqql"); - if (s=="youtube" || s=="newpipe") return atob("GBgBAAAAAAAAAAAAAAAAAf8AH//4P//4P//8P//8P5/8P4/8f4P8f4P8P4/8P5/8P//8P//8P//4H//4Af8AAAAAAAAAAAAAAAAA"); + if (s=="youtube" || s=="newpipe") return atob("GBgBAAAAAAAAAAAAAAAAAAAAH//4P//8P//8f//+f8/+f8P+f8D+f8D+f8P+f8/+f//+P//8P//8H//4AAAAAAAAAAAAAAAAAAAA"); // icons/youtube.png + if (s=="zoom" || s=="meet") return atob("GBgBAAAAAAAAAAAAP/+Af//Af//AcADicADmcADucAD+cAD+cAD+cAD+cAD+cAD+cADucADmcADif//Af//AP/+AAAAAAAAAAAAA"); // icons/videoconf.png if (msg.id=="music") return atob("FhaBAH//+/////////////h/+AH/4Af/gB/+H3/7/f/v9/+/3/7+f/vB/w8H+Dwf4PD/x/////////////3//+A="); // if (s=="sms message" || s=="mail" || s=="gmail") // .. default icon (below) return atob("FhKBAH//+P//yf/+c//z5/+fz/z/n+f/Pz/+ef/8D///////////////////////f//4///A"); @@ -57,7 +86,7 @@ exports.getColor = function(msg,options) { return { // generic colors, using B2-safe colors // DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used - "airbnb": "#f00", + "airbnb": "#ff385c", // https://news.airbnb.com/media-assets/category/brand/ "mail": "#ff0", "music": "#f0f", "phone": "#0f0", @@ -66,39 +95,44 @@ exports.getColor = function(msg,options) { // all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?) "bibel": "#54342c", "bring": "#455a64", - "discord": "#738adb", + "discord": "#5865f2", // https://discord.com/branding "etar": "#36a18b", - "facebook": "#4267b2", + "facebook": "#1877f2", // https://www.facebook.com/brand/resources/facebookapp/logo "gmail": "#ea4335", "gmx": "#1c449b", "google": "#4285F4", "google home": "#fbbc05", // "home assistant": "#41bdf5", // ha-blue is #41bdf5, but that's the background - "instagram": "#dd2a7b", - "lieferando": "#ee5c00", + "instagram": "#ff0069", // https://about.instagram.com/brand/gradient + "lieferando": "#ff8000", + "linkedin": "#0a66c2", // https://brand.linkedin.com/ "messenger": "#0078ff", + "mastodon": "#563acc", // https://www.joinmastodon.org/branding "mattermost": "#00f", "n26": "#36a18b", "nextbike": "#00f", "newpipe": "#f00", "nina": "#e57004", "opentasks": "#409f8f", - "outlook mail": "#0072c6", + "outlook mail": "#0078d4", // https://developer.microsoft.com/en-us/fluentui#/styles/web/colors/products "paypal": "#003087", + "pocket": "#ef4154f", // https://blog.getpocket.com/press/ "post & dhl": "#f2c101", - "signal": "#00f", - "skype": "#00aff0", + "reddit": "#ff4500", // https://www.redditinc.com/brand + "signal": "#3a76f0", // https://github.com/signalapp/Signal-Desktop/blob/main/images/signal-logo.svg + "skype": "#0078d4", // https://developer.microsoft.com/en-us/fluentui#/styles/web/colors/products "slack": "#e51670", "snapchat": "#ff0", "steam": "#171a21", - "teams": "#464eb8", + "teams": "#6264a7", // https://developer.microsoft.com/en-us/fluentui#/styles/web/colors/products "telegram": "#0088cc", "telegram foss": "#0088cc", "to do": "#3999e5", - "twitch": "#6441A4", - "twitter": "#1da1f2", + "twitch": "#9146ff", // https://brand.twitch.tv/ + "twitter": "#1d9bf0, // https://about.twitter.com/en/who-we-are/brand-toolkit + "vlc": "#ff8800", "whatsapp": "#4fce5d", "wordfeud": "#e7d3c7", - "youtube": "#f00", + "youtube": "#f00", // https://www.youtube.com/howyoutubeworks/resources/brand-resources/#logos-icons-and-colors }[s]||options.default; }; From 70307b32675a6f374e8f6677c6137069f0417dd1 Mon Sep 17 00:00:00 2001 From: m-p-3 Date: Tue, 29 Nov 2022 21:20:47 -0500 Subject: [PATCH 069/107] Fixed typo At 132:16 --- apps/messageicons/lib.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/messageicons/lib.js b/apps/messageicons/lib.js index d10982b63..e24a07a62 100644 --- a/apps/messageicons/lib.js +++ b/apps/messageicons/lib.js @@ -129,7 +129,7 @@ exports.getColor = function(msg,options) { "telegram foss": "#0088cc", "to do": "#3999e5", "twitch": "#9146ff", // https://brand.twitch.tv/ - "twitter": "#1d9bf0, // https://about.twitter.com/en/who-we-are/brand-toolkit + "twitter": "#1d9bf0", // https://about.twitter.com/en/who-we-are/brand-toolkit "vlc": "#ff8800", "whatsapp": "#4fce5d", "wordfeud": "#e7d3c7", From bfbd86bc331d3a8c6b4ad33cd798128b1e26eafe Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Wed, 30 Nov 2022 09:34:17 +0100 Subject: [PATCH 070/107] sleepphasealarm: Minor tweaks --- apps/sleepphasealarm/ChangeLog | 1 + apps/sleepphasealarm/app.js | 15 +++++++++------ apps/sleepphasealarm/metadata.json | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/apps/sleepphasealarm/ChangeLog b/apps/sleepphasealarm/ChangeLog index 9f2b07d49..80b2e554b 100644 --- a/apps/sleepphasealarm/ChangeLog +++ b/apps/sleepphasealarm/ChangeLog @@ -11,3 +11,4 @@ Add setting to defer start of algorithm Add setting to disable scheduler alarm 0.10: Fix: Do not wake when falling asleep +0.11: Minor tweaks diff --git a/apps/sleepphasealarm/app.js b/apps/sleepphasealarm/app.js index b3aacc80d..a5193b244 100644 --- a/apps/sleepphasealarm/app.js +++ b/apps/sleepphasealarm/app.js @@ -21,8 +21,8 @@ let logs = []; // // Function needs to be called for every measurement but returns a value at maximum once a second (see winwidth) // start of sleep marker is delayed by sleepthresh due to continous data reading -const winwidth=13; -const nomothresh=0.03; // 0.006 was working on Bangle1, but Bangle2 has higher noise. +const winwidth=13; // Actually 12.5 Hz, rounded +const nomothresh=0.023; // Original implementation: 6, resolution 11 bit, scale +-4G = 6/(2^(11-1))*4 = 0.023438 in G const sleepthresh=600; var ess_values = []; var slsnds = 0; @@ -69,6 +69,9 @@ active.forEach(alarm => { } }); +const LABEL_ETA = /*LANG*/"ETA"; +const LABEL_WAKEUP_TIME = /*LANG*/"Alarm at"; + var layout = new Layout({ type:"v", c: [ {type:"txt", font:"10%", label:"Sleep Phase Alarm", bgCol:g.theme.bgH, fillx: true, height:Bangle.appRect.h/6}, @@ -84,7 +87,7 @@ function drawApp() { var alarmMinute = nextAlarmDate.getMinutes(); if (alarmHour < 10) alarmHour = "0" + alarmHour; if (alarmMinute < 10) alarmMinute = "0" + alarmMinute; - layout.alarm_date.label = "Alarm at " + alarmHour + ":" + alarmMinute; + layout.alarm_date.label = `${LABEL_WAKEUP_TIME}: ${alarmHour}:${alarmMinute}`; layout.render(); function drawTime() { @@ -94,7 +97,7 @@ function drawApp() { const diff = nextAlarmDate - now; const diffHour = Math.floor((diff % 86400000) / 3600000).toString(); const diffMinutes = Math.floor(((diff % 86400000) % 3600000) / 60000).toString(); - layout.eta.label = "ETA: -"+ diffHour + ":" + diffMinutes.padStart(2, '0'); + layout.eta.label = `${LABEL_ETA}: ${diffHour}:${diffMinutes.padStart(2, '0')}`; layout.render(); } @@ -139,7 +142,7 @@ if (nextAlarmDate !== undefined) { // minimum alert 30 minutes early minAlarm.setTime(nextAlarmDate.getTime() - (30*60*1000)); run = () => { - layout.state.label = "Start"; + layout.state.label = /*LANG*/"Start"; layout.render(); Bangle.setOptions({powerSave: false}); // do not dynamically change accelerometer poll interval Bangle.setPollInterval(80); // 12.5Hz @@ -150,7 +153,7 @@ if (nextAlarmDate !== undefined) { if (swest !== undefined) { if (Bangle.isLCDOn()) { - layout.state.label = swest ? "Sleep" : "Awake"; + layout.state.label = swest ? /*LANG*/"Sleep" : /*LANG*/"Awake"; layout.render(); } // log diff --git a/apps/sleepphasealarm/metadata.json b/apps/sleepphasealarm/metadata.json index 35eea7466..fd3366812 100644 --- a/apps/sleepphasealarm/metadata.json +++ b/apps/sleepphasealarm/metadata.json @@ -2,7 +2,7 @@ "id": "sleepphasealarm", "name": "SleepPhaseAlarm", "shortName": "SleepPhaseAlarm", - "version": "0.10", + "version": "0.11", "description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.", "icon": "app.png", "tags": "alarm", From 1777799ae677cb55f3b0c2466529439a7472c79f Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 30 Nov 2022 09:37:39 +0000 Subject: [PATCH 071/107] 0.04: Fix 'Uncaught ReferenceError: "__FILE__" is not defined' error (fix #2326) --- apps/swp2clk/ChangeLog | 1 + apps/swp2clk/boot.js | 11 ++++++----- apps/swp2clk/metadata.json | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/swp2clk/ChangeLog b/apps/swp2clk/ChangeLog index 083b20858..d6f9f6e8c 100644 --- a/apps/swp2clk/ChangeLog +++ b/apps/swp2clk/ChangeLog @@ -1,3 +1,4 @@ 0.01: Initial creation of "Swipe back to the Clock" App. Let's you swipe from left to right on any app to return back to the clock face. 0.02: Fix deleting from white and black lists. 0.03: Adapt to availability of Bangle.showClock and Bangle.load +0.04: Fix 'Uncaught ReferenceError: "__FILE__" is not defined' error (fix #2326) diff --git a/apps/swp2clk/boot.js b/apps/swp2clk/boot.js index 389c3dddf..f2c642adf 100644 --- a/apps/swp2clk/boot.js +++ b/apps/swp2clk/boot.js @@ -29,12 +29,13 @@ })(Bangle.load); let swipeHandler = (dir) => { - log("swipe:" + dir + " on app: " + __FILE__); + let currentFile = global.__FILE__||"default"; + log("swipe:" + dir + " on app: " + currentFile); - if (!inhibit && dir === 1 && !Bangle.CLOCK && __FILE__ != ".bootcde") { - log("on a not clock app " + __FILE__); - if ((settings.mode === 1 && settings.whiteList.includes(__FILE__)) || // "White List" - (settings.mode === 2 && !settings.blackList.includes(__FILE__)) || // "Black List" + if (!inhibit && dir === 1 && !Bangle.CLOCK) { + log("on a not clock app " + currentFile); + if ((settings.mode === 1 && settings.whiteList.includes(currentFile)) || // "White List" + (settings.mode === 2 && !settings.blackList.includes(currentFile)) || // "Black List" settings.mode === 3) { // "Always" log("load clock"); Bangle.showClock(); diff --git a/apps/swp2clk/metadata.json b/apps/swp2clk/metadata.json index 7552de26c..b4436bd39 100644 --- a/apps/swp2clk/metadata.json +++ b/apps/swp2clk/metadata.json @@ -2,7 +2,7 @@ "id": "swp2clk", "name": "Swipe back to the Clock", "shortName": "Swipe to Clock", - "version": "0.03", + "version": "0.04", "description": "Let's you swipe from left to right on any app to return back to the clock face. Please configure in the settings app after installing to activate, since its disabled by default.", "icon": "app.png", "type": "bootloader", From 45c5e15f20b2e1765af3813a1bf81bdb9ba949e2 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 30 Nov 2022 09:55:02 +0000 Subject: [PATCH 072/107] fix https://github.com/espruino/BangleApps/issues/953 - new lock widget --- apps/widlock/ChangeLog | 1 + apps/widlock/metadata.json | 2 +- apps/widlock/widget.js | 19 ++++++++----------- apps/widlockunlock/ChangeLog | 1 + apps/widlockunlock/metadata.json | 13 +++++++++++++ apps/widlockunlock/widget.js | 6 ++++++ apps/widlockunlock/widget.png | Bin 0 -> 726 bytes 7 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 apps/widlockunlock/ChangeLog create mode 100644 apps/widlockunlock/metadata.json create mode 100644 apps/widlockunlock/widget.js create mode 100644 apps/widlockunlock/widget.png diff --git a/apps/widlock/ChangeLog b/apps/widlock/ChangeLog index bb84c2d44..665750150 100644 --- a/apps/widlock/ChangeLog +++ b/apps/widlock/ChangeLog @@ -4,3 +4,4 @@ 0.04: Set sortorder to -1 so that widget always takes up the furthest left position 0.05: Set sortorder to -10 so that others can take -1 etc 0.06: Set sortorder to -10 in widget code +0.07: Remove check for .isLocked (extremely old firmwares), speed up widget loading diff --git a/apps/widlock/metadata.json b/apps/widlock/metadata.json index 8635a5434..db532851a 100644 --- a/apps/widlock/metadata.json +++ b/apps/widlock/metadata.json @@ -1,7 +1,7 @@ { "id": "widlock", "name": "Lock Widget", - "version": "0.06", + "version": "0.07", "description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked", "icon": "widget.png", "type": "widget", diff --git a/apps/widlock/widget.js b/apps/widlock/widget.js index 592361cd9..abcedde20 100644 --- a/apps/widlock/widget.js +++ b/apps/widlock/widget.js @@ -1,11 +1,8 @@ -(function(){ - if (!Bangle.isLocked) return; // bail out on old firmware - Bangle.on("lock", function(on) { - WIDGETS["lock"].width = Bangle.isLocked()?16:0; - Bangle.drawWidgets(); - }); - WIDGETS["lock"]={area:"tl",sortorder:10,width:Bangle.isLocked()?16:0,draw:function(w) { - if (Bangle.isLocked()) - g.reset().drawImage(atob("DhABH+D/wwMMDDAwwMf/v//4f+H/h/8//P/z///f/g=="), w.x+1, w.y+4); - }}; -})() +Bangle.on("lock", function() { + WIDGETS["lock"].width = Bangle.isLocked()?16:0; + Bangle.drawWidgets(); +}); +WIDGETS["lock"]={area:"tl",sortorder:10,width:Bangle.isLocked()?16:0,draw:function(w) { + if (Bangle.isLocked()) + g.reset().drawImage(atob("DhABH+D/wwMMDDAwwMf/v//4f+H/h/8//P/z///f/g=="), w.x+1, w.y+4); +}}; diff --git a/apps/widlockunlock/ChangeLog b/apps/widlockunlock/ChangeLog new file mode 100644 index 000000000..b4d1ae593 --- /dev/null +++ b/apps/widlockunlock/ChangeLog @@ -0,0 +1 @@ +0.01: First commit diff --git a/apps/widlockunlock/metadata.json b/apps/widlockunlock/metadata.json new file mode 100644 index 000000000..d701279b9 --- /dev/null +++ b/apps/widlockunlock/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "widlockunlock", + "name": "Lock/Unlock Widget", + "version": "0.01", + "description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked, or an unlock icon otherwise", + "icon": "widget.png", + "type": "widget", + "tags": "widget,lock", + "supports": ["BANGLEJS","BANGLEJS2"], + "storage": [ + {"name":"widlockunlock.wid.js","url":"widget.js"} + ] +} diff --git a/apps/widlockunlock/widget.js b/apps/widlockunlock/widget.js new file mode 100644 index 000000000..0716a9edf --- /dev/null +++ b/apps/widlockunlock/widget.js @@ -0,0 +1,6 @@ +Bangle.on("lockunlock", function() { + Bangle.drawWidgets(); +}); +WIDGETS["lockunlock"]={area:"tl",sortorder:10,width:14,draw:function(w) { + g.reset().drawImage(atob(Bangle.isLocked() ? "DBGBAAAA8DnDDCBCBP////////n/n/n//////z/A" : "DBGBAAAA8BnDDCBABP///8A8A8Y8Y8Y8A8A//z/A"), w.x+1, w.y+3); +}}; diff --git a/apps/widlockunlock/widget.png b/apps/widlockunlock/widget.png new file mode 100644 index 0000000000000000000000000000000000000000..3a6b9816163709af76a5d9d7f748094e600df5c7 GIT binary patch literal 726 zcmV;{0xA88P)$wOZ|csQO_x+qRtr@Mt7_1mHO{-!Keg*)WV{DdkN7I{0Ast*nyvbX_{FK zi$5AC9sr8P;zs~?F#IIe+_M4^*npIBV{lDGFUE=QQ!14{F!Rg77&A}b01;gs5%JpZ zUS@tZ61o;tN0=c_4lbmWUnbc5Y1~GaV1yHqMEz195^>NWZ=5E+;?zWSCQ zWA*m}o&i_~a89oz`~IbR5b68f~C?cyd!FmVl(G z_)jPoEe0BG;79lGy8n6AR9{O=3pEe@#Z;ihz-g_b>(`DJ1BoSnF%@W;fu!5(Ew^p@ zL#zWW1_&nut7B+KnfrSnGB9f*1GC1Dfv$IoHacDjehd^92nou*SAwU39$^bWQ;D3( zX!QiPyplW(#O~v(A#j<2T}-E)x&Yqv1g^(6@oftAA|mFRKe3Rs5C*t-J^%m!07*qo IM6N<$f_of9R{#J2 literal 0 HcmV?d00001 From 1c475097e0c0dbf556f56f15a26504a5c7748848 Mon Sep 17 00:00:00 2001 From: m-p-3 Date: Wed, 30 Nov 2022 06:57:30 -0500 Subject: [PATCH 073/107] Fixed another typo Line 58 --- apps/messageicons/lib.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/messageicons/lib.js b/apps/messageicons/lib.js index e24a07a62..5a24ac098 100644 --- a/apps/messageicons/lib.js +++ b/apps/messageicons/lib.js @@ -55,7 +55,7 @@ exports.getImage = function(msg) { if (s=="skype") return atob("GBgBAAAAB8AAH/8AP//AP//gf8fwfwD4fgB4fjx8fj/8Pg/8PwH8P4B8P/h8Pnx+Pjx+Hhh+HwD+D8P+B//8A//8AP/4AAPgAAAA"); // icons/skype.png if (s=="slack") return atob("GBgBAAAAAOcAAeeAAeeAAeeAAGeAAAeAP+ecf+eef+e+f+e+AAAAAAAAfef+fef+eef+Oef8AeAAAeYAAeeAAeeAAeeAAOcAAAAA"); // icons/slack.png if (s=="snapchat") return atob("GBgBAAAAAAAAAAAAAH4AAf+AAYGAAwDAAwDAAwDADwDwDwDwDgBwBwDgBwDgDgBwHAA4OAAcHAA4D4HwB//gAH4AAAAAAAAAAAAA"); // icons/snapchat.png - if (ss="starbucks") return atob("GBgBAAAAAAAAAAAAD//4D//8DADMDADMDADMDAD8DAD4DADADADADADADADADgHAB/+AA/8AAAAAAAAAP//wP//wAAAAAAAAAAAA"); // icons/cafe.png + if (s=="starbucks") return atob("GBgBAAAAAAAAAAAAD//4D//8DADMDADMDADMDAD8DAD4DADADADADADADADADgHAB/+AA/8AAAAAAAAAP//wP//wAAAAAAAAAAAA"); // icons/cafe.png if (s=="steam") return atob("GBgBAAAAAAAAAf+AA//AD//wD//wH/g4P/OcP/RcP+RcP+ReH8OeB4A+AAH+AMP8IC/8OS/8HN/4Dj/wD//wA//AAf+AAAAAAAAA"); // icons/steam.png if (s=="teams") return atob("GBgBAAAAAAgAAD4AADcYAGM8AGNmP/dmP/48MDAYMD/+PP/+PPBmPPBmPPBmPPBmP/BmP/BmH+B+AYD4AMDAAOOAAH8AABwAAAAA"); // icons/teams.png if (s=="telegram" || s=="telegram foss") return atob("GBgBAAAAAAAAAAAAAAAeAAB+AAP+AA/+AD/+Af9+B/z+H/n8f+P8f8f8Dw/8AB/8AB/8AB/4AAf4AAP4AAD4AABwAAAAAAAAAAAA"); // icons/telegram.png From dda81fe199ba8c7f3d8d768b0fe9bb91bba4f434 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 30 Nov 2022 16:54:27 +0000 Subject: [PATCH 074/107] fix alignment of non-7x7 maps, non-flickery uploads, and allow huge (1MB) map uploads --- apps/openstmap/interface.html | 36 ++++++++++++++++++++++------------- core | 2 +- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/apps/openstmap/interface.html b/apps/openstmap/interface.html index e44474d63..4a4bd5336 100644 --- a/apps/openstmap/interface.html +++ b/apps/openstmap/interface.html @@ -48,16 +48,18 @@

3 bit

+ + @@ -312,11 +314,13 @@ TODO: var zoom = map.getZoom(); var centerlatlon = map.getBounds().getCenter(); - var center = map.project(centerlatlon, zoom).divideBy(OSMTILESIZE); - // Reason for 16px adjustment below not 100% known, but it seems to - // align everything perfectly: https://github.com/espruino/BangleApps/issues/984 - var ox = Math.round((center.x - Math.floor(center.x)) * OSMTILESIZE) + 16; - var oy = Math.round((center.y - Math.floor(center.y)) * OSMTILESIZE) + 16; + var center = map.project(centerlatlon, zoom).divideBy(OSMTILESIZE); // the center of our map + // ox/oy = offset in pixels + var ox = Math.round((center.x - Math.floor(center.x)) * OSMTILESIZE); + var oy = Math.round((center.y - Math.floor(center.y)) * OSMTILESIZE); + // adjust offset because we want to center our map + ox -= MAPTILES * TILESIZE / 2; + oy -= MAPTILES * TILESIZE / 2; center = center.floor(); // make sure we're in the middle of a tile // JS version of Bangle.js's projection function bproject(lat, lon) { @@ -353,10 +357,12 @@ TODO: var ctx = canvas.getContext('2d'); canvas.width = MAPSIZE; canvas.height = MAPSIZE; - for (var i = 0; i < OSMTILECOUNT; i++) { - for (var j = 0; j < OSMTILECOUNT; j++) { + var tileMin = Math.round(-OSMTILECOUNT/2); + var tileMax = Math.round(OSMTILECOUNT/2); + for (var i = tileMin; i <= tileMax; i++) { + for (var j = tileMin; j <= tileMax; j++) { (function(i,j){ - var coords = new L.Point(center.x+i-1, center.y+j-1); + var coords = new L.Point(center.x+i, center.y+j); coords.z = zoom; var img = new Image(); img.crossOrigin = "Anonymous"; @@ -368,6 +374,8 @@ TODO: ctx.fillRect(testPt.x-1, testPt.y-5, 3,10); ctx.fillRect(testPt.x-5, testPt.y-1, 10,3); }*/ + /*ctx.fillStyle="black"; + ctx.fillRect(i*OSMTILESIZE - ox, j*OSMTILESIZE - oy, 6,6);*/ resolve(); }; })); @@ -395,6 +403,8 @@ TODO: h : Math.round(canvas.height / TILESIZE), // height in tiles fn : mapImageFile })}); + var mapSizeInK = Math.round(mapFiles.reduce((r,m)=>m.content.length+r,0)/1000); + document.getElementById("mapstats").innerText = "Size : "+ (mapSizeInK+"kb"); console.log(mapFiles); }); }); diff --git a/core b/core index f15e99fbe..3a953179b 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit f15e99fbe25b2991719011e6da9bc9c7be401a7e +Subproject commit 3a953179b7bb9f574d4e77d5f34b6b7deee1e884 From 97ce93a907531b20c77b1891e1467131cd3683ac Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Wed, 30 Nov 2022 18:11:29 +0100 Subject: [PATCH 075/107] qcenter - Fix fast loading to clock and prevent fast loading all apps --- apps/qcenter/app.js | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/apps/qcenter/app.js b/apps/qcenter/app.js index 1530cc5fb..ef495ea17 100644 --- a/apps/qcenter/app.js +++ b/apps/qcenter/app.js @@ -1,9 +1,10 @@ +{ require("Font8x12").add(Graphics); // load pinned apps from config -var settings = require("Storage").readJSON("qcenter.json", 1) || {}; -var pinnedApps = settings.pinnedApps || []; -var exitGesture = settings.exitGesture || "swipeup"; +let settings = require("Storage").readJSON("qcenter.json", 1) || {}; +let pinnedApps = settings.pinnedApps || []; +let exitGesture = settings.exitGesture || "swipeup"; // if empty load a default set of apps as an example if (pinnedApps.length == 0) { @@ -14,12 +15,12 @@ if (pinnedApps.length == 0) { } // button drawing from Layout.js, edited to have completely custom button size with icon -function drawButton(l) { - var x = l.x + (0 | l.pad), +let drawButton = function(l) { + let x = l.x + (0 | l.pad), y = l.y + (0 | l.pad), w = l.w - (l.pad << 1), h = l.h - (l.pad << 1); - var poly = [ + let poly = [ x, y + 4, x + 4, @@ -54,14 +55,14 @@ function drawButton(l) { } // function to split array into group of 3, for button placement -function groupBy3(data) { - var result = []; - for (var i = 0; i < data.length; i += 3) result.push(data.slice(i, i + 3)); +let groupBy3 = function(data) { + let result = []; + for (let i = 0; i < data.length; i += 3) result.push(data.slice(i, i + 3)); return result; } // generate object with buttons for apps by group of 3 -var appButtons = groupBy3(pinnedApps).map((appGroup, i) => { +let appButtons = groupBy3(pinnedApps).map((appGroup, i) => { return appGroup.map((app, j) => { return { type: "custom", @@ -71,13 +72,13 @@ var appButtons = groupBy3(pinnedApps).map((appGroup, i) => { pad: 5, src: require("Storage").read(app.icon), scale: 0.75, - cb: (l) => Bangle.load(app.src), + cb: (l) => load(app.src), }; }); }); // create basic layout content with status info and sensor status on top -var layoutContent = [ +let layoutContent = [ { type: "h", pad: 5, @@ -102,19 +103,27 @@ appButtons.forEach((appGroup) => { Bangle.loadWidgets(); -var Layout = require("Layout"); -var layout = new Layout({ +let Layout = require("Layout"); +let layout = new Layout({ type: "v", - c: layoutContent, + c: layoutContent +}, { + remove: ()=>{ + Bangle.removeListener("swipe", onSwipe); + delete Graphics.prototype.setFont8x12; + } }); g.clear(); layout.render(); Bangle.drawWidgets(); // swipe event listener for exit gesture -Bangle.on("swipe", function (lr, ud) { +let onSwipe = function (lr, ud) { if(exitGesture == "swipeup" && ud == -1) Bangle.showClock(); if(exitGesture == "swipedown" && ud == 1) Bangle.showClock(); if(exitGesture == "swipeleft" && lr == -1) Bangle.showClock(); if(exitGesture == "swiperight" && lr == 1) Bangle.showClock(); -}); \ No newline at end of file +} + +Bangle.on("swipe", onSwipe); +} From dec95eb281e47d375f1c73f914c96061229f9f8d Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 23 Oct 2022 16:12:09 +0200 Subject: [PATCH 076/107] layout - Allow providing a remove handler in options --- modules/Layout.js | 4 ++-- modules/Layout.md | 1 + modules/Layout.min.js | 28 ++++++++++++++-------------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/modules/Layout.js b/modules/Layout.js index cfc9ee79f..f8e27b66b 100644 --- a/modules/Layout.js +++ b/modules/Layout.js @@ -83,7 +83,7 @@ Layout.prototype.setUI = function() { let uiSet; if (this.buttons) { // multiple buttons so we'll jus use back/next/select - Bangle.setUI({mode:"updown", back:this.options.back}, dir=>{ + Bangle.setUI({mode:"updown", back:this.options.back, remove:this.options.remove}, dir=>{ var s = this.selectedButton, l=this.buttons.length; if (dir===undefined && this.buttons[s]) return this.buttons[s].cb(); @@ -100,7 +100,7 @@ Layout.prototype.setUI = function() { }); uiSet = true; } - if (this.options.back && !uiSet) Bangle.setUI({mode: "custom", back: this.options.back}); + if ((this.options.back || this.options.remove) && !uiSet) Bangle.setUI({mode: "custom", back: this.options.back, remove: this.options.remove}); // physical buttons -> actual applications if (this.b) { // Handler for button watch events diff --git a/modules/Layout.md b/modules/Layout.md index 7a4177957..67db21858 100644 --- a/modules/Layout.md +++ b/modules/Layout.md @@ -59,6 +59,7 @@ layout.render(); - `cb` - a callback function - `cbl` - a callback function for long presses - `back` - a callback function, passed as `back` into Bangle.setUI (which usually adds an icon in the top left) +- `remove` - a cleanup function, passed as `remove` into Bangle.setUI (allows to cleanly remove the app from memory) If automatic lazy rendering is enabled, calls to `layout.render()` will attempt to automatically determine what objects have changed or moved, clear their previous locations, and re-render just those objects. diff --git a/modules/Layout.min.js b/modules/Layout.min.js index f801177aa..19e60f7a0 100644 --- a/modules/Layout.min.js +++ b/modules/Layout.min.js @@ -1,14 +1,14 @@ -function p(d,h){function b(e){"ram";e.id&&(a[e.id]=e);e.type||(e.type="");e.c&&e.c.forEach(b)}this._l=this.l=d;this.options=h||{};this.lazy=this.options.lazy||!1;this.physBtns=1;let f;if(2!=process.env.HWVERSION){this.physBtns=3;f=[];function e(l){"ram";"btn"==l.type&&f.push(l);l.c&&l.c.forEach(e)}e(d);f.length&&(this.physBtns=0,this.buttons=f,this.selectedButton=-1)}if(this.options.btns)if(d=this.options.btns,this.physBtns>=d.length){this.b=d;let e=Math.floor(Bangle.appRect.h/ -this.physBtns);for(2d.length;)d.push({label:""});this._l.width=g.getWidth()-8;this._l={type:"h",filly:1,c:[this._l,{type:"v",pad:1,filly:1,c:d.map(l=>(l.type="txt",l.font="6x8",l.height=e,l.r=1,l))}]}}else this._l.width=g.getWidth()-32,this._l={type:"h",c:[this._l,{type:"v",c:d.map(e=>(e.type="btn",e.filly=1,e.width=32,e.r=1,e))}]},f&&f.push.apply(f,this._l.c[1].c);this.setUI();var a=this;b(this._l);this.updateNeeded=!0}function t(d, -h,b,f,a){var e=null==d.bgCol?a:g.toColor(d.bgCol);if(e!=a||"txt"==d.type||"btn"==d.type||"img"==d.type||"custom"==d.type){var l=d.c;delete d.c;var k="H"+E.CRC32(E.toJS(d));l&&(d.c=l);delete h[k]||((f[k]=[d.x,d.y,d.x+d.w-1,d.y+d.h-1]).bg=null==a?g.theme.bg:a,b&&(b.push(d),b=null))}if(d.c)for(var c of d.c)t(c,h,b,f,e)}p.prototype.setUI=function(){Bangle.setUI();let d;this.buttons&&(Bangle.setUI({mode:"updown",back:this.options.back},h=>{var b=this.selectedButton,f=this.buttons.length;if(void 0===h&& -this.buttons[b])return this.buttons[b].cb();this.buttons[b]&&(delete this.buttons[b].selected,this.render(this.buttons[b]));b=(b+f+h)%f;this.buttons[b]&&(this.buttons[b].selected=1,this.render(this.buttons[b]));this.selectedButton=b}),d=!0);this.options.back&&!d&&Bangle.setUI({mode:"custom",back:this.options.back});if(this.b){function h(b,f){.75=b.x&&f.y>=b.y&&f.x<=b.x+b.w&&f.y<=b.y+b.h&&(2==f.type&&b.cbl?b.cbl(f):b.cb&&b.cb(f));b.c&&b.c.forEach(a=>h(a,f))}Bangle.touchHandler=(b,f)=>h(this._l,f);Bangle.on("touch",Bangle.touchHandler)}}; -p.prototype.render=function(d){function h(c){"ram";b.reset();void 0!==c.col&&b.setColor(c.col);void 0!==c.bgCol&&b.setBgColor(c.bgCol).clearRect(c.x,c.y,c.x+c.w-1,c.y+c.h-1);f[c.type](c)}d||(d=this._l);this.updateNeeded&&this.update();var b=g,f={"":function(){},txt:function(c){"ram";if(c.wrap){var m=b.setFont(c.font).setFontAlign(0,-1).wrapString(c.label,c.w),n=c.y+(c.h-b.getFontHeight()*m.length>>1);b.drawString(m.join("\n"),c.x+(c.w>>1),n)}else b.setFont(c.font).setFontAlign(0,0,c.r).drawString(c.label, -c.x+(c.w>>1),c.y+(c.h>>1))},btn:function(c){"ram";var m=c.x+(0|c.pad),n=c.y+(0|c.pad),q=c.w-(c.pad<<1),r=c.h-(c.pad<<1);m=[m,n+4,m+4,n,m+q-5,n,m+q-1,n+4,m+q-1,n+r-5,m+q-5,n+r-1,m+4,n+r-1,m,n+r-5,m,n+4];n=c.selected?b.theme.bgH:b.theme.bg2;b.setColor(n).fillPoly(m).setColor(c.selected?b.theme.fgH:b.theme.fg2).drawPoly(m);void 0!==c.col&&b.setColor(c.col);c.src?b.setBgColor(n).drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)}):b.setFont(c.font|| -"6x8:2").setFontAlign(0,0,c.r).drawString(c.label,c.x+c.w/2,c.y+c.h/2)},img:function(c){"ram";b.drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)})},custom:function(c){"ram";c.render(c)},h:function(c){"ram";c.c.forEach(h)},v:function(c){"ram";c.c.forEach(h)}};if(this.lazy){this.rects||(this.rects={});var a=this.rects.clone(),e=[];t(d,a,e,this.rects,null);for(var l in a)delete this.rects[l];d=Object.keys(a).map(c=>a[c]).reverse(); -for(var k of d)b.setBgColor(k.bg).clearRect.apply(g,k);e.forEach(h)}else h(d)};p.prototype.forgetLazyState=function(){this.rects={}};p.prototype.layout=function(d){var h={h:function(b){"ram";var f=b.x+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.fillx),0);e||(f+=b.w-b._w>>1,e=1);var l=f;b.c.forEach(k=>{k.x=0|l;f+=k._w;a+=0|k.fillx;l=f+Math.floor(a*(b.w-b._w)/e);k.w=0|l-k.x;k.h=0|(k.filly?b.h-(b.pad<<1):k._h);k.y=0|b.y+(0|b.pad)+((1+(0|k.valign))*(b.h-(b.pad<<1)-k.h)>>1);if(k.c)h[k.type](k)})},v:function(b){"ram"; -var f=b.y+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.filly),0);e||(f+=b.h-b._h>>1,e=1);var l=f;b.c.forEach(k=>{k.y=0|l;f+=k._h;a+=0|k.filly;l=f+Math.floor(a*(b.h-b._h)/e);k.h=0|l-k.y;k.w=0|(k.fillx?b.w-(b.pad<<1):k._w);k.x=0|b.x+(0|b.pad)+((1+(0|k.halign))*(b.w-(b.pad<<1)-k.w)>>1);if(k.c)h[k.type](k)})}};h[d.type](d)};p.prototype.debug=function(d,h){d||(d=this._l);h=h||1;g.setColor(h&1,h&2,h&4).drawRect(d.x+h-1,d.y+h-1,d.x+d.w-h,d.y+d.h-h);d.pad&&g.drawRect(d.x+d.pad-1,d.y+d.pad-1,d.x+d.w-d.pad, -d.y+d.h-d.pad);h++;d.c&&d.c.forEach(b=>this.debug(b,h))};p.prototype.update=function(){function d(a){"ram";b[a.type](a);if(a.r&1){var e=a._w;a._w=a._h;a._h=e}a._w=Math.max(a._w+(a.pad<<1),0|a.width);a._h=Math.max(a._h+(a.pad<<1),0|a.height)}delete this.updateNeeded;var h=g,b={txt:function(a){"ram";a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*a.font.slice(0,-1)/100));if(a.wrap)a._h=a._w=0;else{var e=g.setFont(a.font).stringMetrics(a.label);a._w=e.width;a._h=e.height}},btn:function(a){"ram"; -a.font&&a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*a.font.slice(0,-1)/100));var e=a.src?h.imageMetrics("function"==typeof a.src?a.src():a.src):h.setFont(a.font||"6x8:2").stringMetrics(a.label);a._h=16+e.height;a._w=20+e.width},img:function(a){"ram";var e=h.imageMetrics("function"==typeof a.src?a.src():a.src),l=a.scale||1;a._w=e.width*l;a._h=e.height*l},"":function(a){"ram";a._w=0;a._h=0},custom:function(a){"ram";a._w=0;a._h=0},h:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e, -l)=>Math.max(e,l._h),0);a._w=a.c.reduce((e,l)=>e+l._w,0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)},v:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>e+l._h,0);a._w=a.c.reduce((e,l)=>Math.max(e,l._w),0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)}},f=this._l;d(f);delete b;f.fillx||f.filly?(f.w=Bangle.appRect.w,f.h=Bangle.appRect.h,f.x=Bangle.appRect.x,f.y=Bangle.appRect.y):(f.w=f._w, -f.h=f._h,f.x=Bangle.appRect.w-f.w>>1,f.y=Bangle.appRect.y+(Bangle.appRect.h-f.h>>1));this.layout(f)};p.prototype.clear=function(d){d||(d=this._l);g.reset();void 0!==d.bgCol&&g.setBgColor(d.bgCol);g.clearRect(d.x,d.y,d.x+d.w-1,d.y+d.h-1)};exports=p \ No newline at end of file +function p(d,h){function b(e){"ram";e.id&&(a[e.id]=e);e.type||(e.type="");e.c&&e.c.forEach(b)}this._l=this.l=d;this.options=h||{};this.lazy=this.options.lazy||!1;this.physBtns=1;let f;if(2!=process.env.HWVERSION){this.physBtns=3;f=[];function e(l){"ram";"btn"==l.type&&f.push(l);l.c&&l.c.forEach(e)}e(d);f.length&&(this.physBtns=0,this.buttons=f,this.selectedButton=-1)}if(this.options.btns)if(d=this.options.btns,this.physBtns>=d.length){this.b=d;let e=Math.floor(Bangle.appRect.h/this.physBtns); +for(2d.length;)d.push({label:""});this._l.width=g.getWidth()-8;this._l={type:"h",filly:1,c:[this._l,{type:"v",pad:1,filly:1,c:d.map(l=>(l.type="txt",l.font="6x8",l.height=e,l.r=1,l))}]}}else this._l.width=g.getWidth()-32,this._l={type:"h",c:[this._l,{type:"v",c:d.map(e=>(e.type="btn",e.filly=1,e.width=32,e.r=1,e))}]},f&&f.push.apply(f,this._l.c[1].c);this.setUI();var a=this;b(this._l);this.updateNeeded=!0}function t(d,h,b,f,a){var e= +null==d.bgCol?a:g.toColor(d.bgCol);if(e!=a||"txt"==d.type||"btn"==d.type||"img"==d.type||"custom"==d.type){var l=d.c;delete d.c;var k="H"+E.CRC32(E.toJS(d));l&&(d.c=l);delete h[k]||((f[k]=[d.x,d.y,d.x+d.w-1,d.y+d.h-1]).bg=null==a?g.theme.bg:a,b&&(b.push(d),b=null))}if(d.c)for(var c of d.c)t(c,h,b,f,e)}p.prototype.setUI=function(){Bangle.setUI();let d;this.buttons&&(Bangle.setUI({mode:"updown",back:this.options.back,remove:this.options.remove},h=>{var b=this.selectedButton,f=this.buttons.length;if(void 0=== +h&&this.buttons[b])return this.buttons[b].cb();this.buttons[b]&&(delete this.buttons[b].selected,this.render(this.buttons[b]));b=(b+f+h)%f;this.buttons[b]&&(this.buttons[b].selected=1,this.render(this.buttons[b]));this.selectedButton=b}),d=!0);!this.options.back&&!this.options.remove||d||Bangle.setUI({mode:"custom",back:this.options.back,remove:this.options.remove});if(this.b){function h(b,f){.75=b.x&&f.y>=b.y&&f.x<=b.x+b.w&&f.y<=b.y+b.h&&(2==f.type&&b.cbl?b.cbl(f):b.cb&&b.cb(f));b.c&&b.c.forEach(a=>h(a,f))}Bangle.touchHandler=(b,f)=>h(this._l,f);Bangle.on("touch", +Bangle.touchHandler)}};p.prototype.render=function(d){function h(c){"ram";b.reset();void 0!==c.col&&b.setColor(c.col);void 0!==c.bgCol&&b.setBgColor(c.bgCol).clearRect(c.x,c.y,c.x+c.w-1,c.y+c.h-1);f[c.type](c)}d||(d=this._l);this.updateNeeded&&this.update();var b=g,f={"":function(){},txt:function(c){"ram";if(c.wrap){var m=b.setFont(c.font).setFontAlign(0,-1).wrapString(c.label,c.w),n=c.y+(c.h-b.getFontHeight()*m.length>>1);b.drawString(m.join("\n"),c.x+(c.w>>1),n)}else b.setFont(c.font).setFontAlign(0, +0,c.r).drawString(c.label,c.x+(c.w>>1),c.y+(c.h>>1))},btn:function(c){"ram";var m=c.x+(0|c.pad),n=c.y+(0|c.pad),q=c.w-(c.pad<<1),r=c.h-(c.pad<<1);m=[m,n+4,m+4,n,m+q-5,n,m+q-1,n+4,m+q-1,n+r-5,m+q-5,n+r-1,m+4,n+r-1,m,n+r-5,m,n+4];n=c.selected?b.theme.bgH:b.theme.bg2;b.setColor(n).fillPoly(m).setColor(c.selected?b.theme.fgH:b.theme.fg2).drawPoly(m);void 0!==c.col&&b.setColor(c.col);c.src?b.setBgColor(n).drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5* +Math.PI*(c.r||0)}):b.setFont(c.font||"6x8:2").setFontAlign(0,0,c.r).drawString(c.label,c.x+c.w/2,c.y+c.h/2)},img:function(c){"ram";b.drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)})},custom:function(c){"ram";c.render(c)},h:function(c){"ram";c.c.forEach(h)},v:function(c){"ram";c.c.forEach(h)}};if(this.lazy){this.rects||(this.rects={});var a=this.rects.clone(),e=[];t(d,a,e,this.rects,null);for(var l in a)delete this.rects[l];d= +Object.keys(a).map(c=>a[c]).reverse();for(var k of d)b.setBgColor(k.bg).clearRect.apply(g,k);e.forEach(h)}else h(d)};p.prototype.forgetLazyState=function(){this.rects={}};p.prototype.layout=function(d){var h={h:function(b){"ram";var f=b.x+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.fillx),0);e||(f+=b.w-b._w>>1,e=1);var l=f;b.c.forEach(k=>{k.x=0|l;f+=k._w;a+=0|k.fillx;l=f+Math.floor(a*(b.w-b._w)/e);k.w=0|l-k.x;k.h=0|(k.filly?b.h-(b.pad<<1):k._h);k.y=0|b.y+(0|b.pad)+((1+(0|k.valign))*(b.h-(b.pad<< +1)-k.h)>>1);if(k.c)h[k.type](k)})},v:function(b){"ram";var f=b.y+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.filly),0);e||(f+=b.h-b._h>>1,e=1);var l=f;b.c.forEach(k=>{k.y=0|l;f+=k._h;a+=0|k.filly;l=f+Math.floor(a*(b.h-b._h)/e);k.h=0|l-k.y;k.w=0|(k.fillx?b.w-(b.pad<<1):k._w);k.x=0|b.x+(0|b.pad)+((1+(0|k.halign))*(b.w-(b.pad<<1)-k.w)>>1);if(k.c)h[k.type](k)})}};h[d.type](d)};p.prototype.debug=function(d,h){d||(d=this._l);h=h||1;g.setColor(h&1,h&2,h&4).drawRect(d.x+h-1,d.y+h-1,d.x+d.w-h,d.y+d.h-h); +d.pad&&g.drawRect(d.x+d.pad-1,d.y+d.pad-1,d.x+d.w-d.pad,d.y+d.h-d.pad);h++;d.c&&d.c.forEach(b=>this.debug(b,h))};p.prototype.update=function(){function d(a){"ram";b[a.type](a);if(a.r&1){var e=a._w;a._w=a._h;a._h=e}a._w=Math.max(a._w+(a.pad<<1),0|a.width);a._h=Math.max(a._h+(a.pad<<1),0|a.height)}delete this.updateNeeded;var h=g,b={txt:function(a){"ram";a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*a.font.slice(0,-1)/100));if(a.wrap)a._h=a._w=0;else{var e=g.setFont(a.font).stringMetrics(a.label); +a._w=e.width;a._h=e.height}},btn:function(a){"ram";a.font&&a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*a.font.slice(0,-1)/100));var e=a.src?h.imageMetrics("function"==typeof a.src?a.src():a.src):h.setFont(a.font||"6x8:2").stringMetrics(a.label);a._h=16+e.height;a._w=20+e.width},img:function(a){"ram";var e=h.imageMetrics("function"==typeof a.src?a.src():a.src),l=a.scale||1;a._w=e.width*l;a._h=e.height*l},"":function(a){"ram";a._w=0;a._h=0},custom:function(a){"ram";a._w=0;a._h=0}, +h:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>Math.max(e,l._h),0);a._w=a.c.reduce((e,l)=>e+l._w,0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)},v:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>e+l._h,0);a._w=a.c.reduce((e,l)=>Math.max(e,l._w),0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)}},f=this._l;d(f);delete b;f.fillx||f.filly?(f.w=Bangle.appRect.w,f.h=Bangle.appRect.h, +f.x=Bangle.appRect.x,f.y=Bangle.appRect.y):(f.w=f._w,f.h=f._h,f.x=Bangle.appRect.w-f.w>>1,f.y=Bangle.appRect.y+(Bangle.appRect.h-f.h>>1));this.layout(f)};p.prototype.clear=function(d){d||(d=this._l);g.reset();void 0!==d.bgCol&&g.setBgColor(d.bgCol);g.clearRect(d.x,d.y,d.x+d.w-1,d.y+d.h-1)};exports=p \ No newline at end of file From afa797591638f5394b4690536523c059038c8d46 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Wed, 30 Nov 2022 18:11:59 +0100 Subject: [PATCH 077/107] qcenter - Go back to settings menu instead of clock --- apps/qcenter/settings.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/apps/qcenter/settings.js b/apps/qcenter/settings.js index 544d85301..e4cd5458b 100644 --- a/apps/qcenter/settings.js +++ b/apps/qcenter/settings.js @@ -41,11 +41,8 @@ function showMainMenu() { var mainmenu = { - "": { title: "Quick Center" }, - "< Back": () => { - load(); - }, - }; + "": { title: "Quick Center", back: back} + }; // Set exit gesture mainmenu["Exit Gesture: " + exitGesture] = function () { @@ -56,10 +53,7 @@ pinnedApps.forEach((app, i) => { mainmenu[app.name] = function () { E.showMenu({ - "": { title: app.name }, - "< Back": () => { - showMainMenu(); - }, + "": { title: app.name, back: showMainMenu }, "Unpin": () => { pinnedApps.splice(i, 1); save("pinnedApps", pinnedApps); @@ -97,8 +91,7 @@ // menu for adding apps to the quick launch menu, listing all apps var pinAppMenu = { - "": { title: "Add App" }, - "< Back": showMainMenu, + "": { title: "Add App", back: showMainMenu } }; apps.forEach((a) => { pinAppMenu[a.name] = function () { @@ -113,8 +106,7 @@ // menu for setting exit gesture var exitGestureMenu = { - "": { title: "Exit Gesture" }, - "< Back": showMainMenu, + "": { title: "Exit Gesture", back: showMainMenu } }; exitGestureMenu["Swipe Up"] = function () { exitGesture = "swipeup"; From e82f64b69608928d9e107a7844b33cc21a797738 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Wed, 30 Nov 2022 18:16:05 +0100 Subject: [PATCH 078/107] qcenter - Add settings json to data array --- apps/qcenter/metadata.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/qcenter/metadata.json b/apps/qcenter/metadata.json index 96d8fa9f7..448b7271a 100644 --- a/apps/qcenter/metadata.json +++ b/apps/qcenter/metadata.json @@ -13,5 +13,6 @@ { "name": "qcenter.app.js", "url": "app.js" }, { "name": "qcenter.settings.js", "url": "settings.js" }, { "name": "qcenter.img", "url": "app-icon.js", "evaluate": true } - ] + ], + "data": [{"name":"qcenter.json"}] } From 557190b666f47aabc8e270d69f533d53bcb069ff Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Wed, 30 Nov 2022 18:18:31 +0100 Subject: [PATCH 079/107] qcenter - Change tabs to spaces according to code style --- apps/qcenter/app.js | 150 +++++++++++------------ apps/qcenter/metadata.json | 30 ++--- apps/qcenter/settings.js | 238 ++++++++++++++++++------------------- 3 files changed, 209 insertions(+), 209 deletions(-) diff --git a/apps/qcenter/app.js b/apps/qcenter/app.js index ef495ea17..be28db3b6 100644 --- a/apps/qcenter/app.js +++ b/apps/qcenter/app.js @@ -8,95 +8,95 @@ let exitGesture = settings.exitGesture || "swipeup"; // if empty load a default set of apps as an example if (pinnedApps.length == 0) { - pinnedApps = [ - { src: "setting.app.js", icon: "setting.img" }, - { src: "about.app.js", icon: "about.img" }, - ]; + pinnedApps = [ + { src: "setting.app.js", icon: "setting.img" }, + { src: "about.app.js", icon: "about.img" }, + ]; } // button drawing from Layout.js, edited to have completely custom button size with icon let drawButton = function(l) { - let x = l.x + (0 | l.pad), - y = l.y + (0 | l.pad), - w = l.w - (l.pad << 1), - h = l.h - (l.pad << 1); - let poly = [ - x, - y + 4, - x + 4, - y, - x + w - 5, - y, - x + w - 1, - y + 4, - x + w - 1, - y + h - 5, - x + w - 5, - y + h - 1, - x + 4, - y + h - 1, - x, - y + h - 5, - x, - y + 4, - ], - bg = l.selected ? g.theme.bgH : g.theme.bg2; - g.setColor(bg) - .fillPoly(poly) - .setColor(l.selected ? g.theme.fgH : g.theme.fg2) - .drawPoly(poly); - if (l.src) - g.setBgColor(bg).drawImage( - "function" == typeof l.src ? l.src() : l.src, - l.x + l.w / 2, - l.y + l.h / 2, - { scale: l.scale || undefined, rotate: Math.PI * 0.5 * (l.r || 0) } - ); + let x = l.x + (0 | l.pad), + y = l.y + (0 | l.pad), + w = l.w - (l.pad << 1), + h = l.h - (l.pad << 1); + let poly = [ + x, + y + 4, + x + 4, + y, + x + w - 5, + y, + x + w - 1, + y + 4, + x + w - 1, + y + h - 5, + x + w - 5, + y + h - 1, + x + 4, + y + h - 1, + x, + y + h - 5, + x, + y + 4, + ], + bg = l.selected ? g.theme.bgH : g.theme.bg2; + g.setColor(bg) + .fillPoly(poly) + .setColor(l.selected ? g.theme.fgH : g.theme.fg2) + .drawPoly(poly); + if (l.src) + g.setBgColor(bg).drawImage( + "function" == typeof l.src ? l.src() : l.src, + l.x + l.w / 2, + l.y + l.h / 2, + { scale: l.scale || undefined, rotate: Math.PI * 0.5 * (l.r || 0) } + ); } // function to split array into group of 3, for button placement let groupBy3 = function(data) { - let result = []; - for (let i = 0; i < data.length; i += 3) result.push(data.slice(i, i + 3)); - return result; + let result = []; + for (let i = 0; i < data.length; i += 3) result.push(data.slice(i, i + 3)); + return result; } // generate object with buttons for apps by group of 3 let appButtons = groupBy3(pinnedApps).map((appGroup, i) => { - return appGroup.map((app, j) => { - return { - type: "custom", - render: drawButton, - width: 50, - height: 50, - pad: 5, - src: require("Storage").read(app.icon), - scale: 0.75, - cb: (l) => load(app.src), - }; - }); + return appGroup.map((app, j) => { + return { + type: "custom", + render: drawButton, + width: 50, + height: 50, + pad: 5, + src: require("Storage").read(app.icon), + scale: 0.75, + cb: (l) => load(app.src), + }; + }); }); // create basic layout content with status info and sensor status on top let layoutContent = [ - { - type: "h", - pad: 5, - fillx: 1, - c: [ - { type: "txt", font: "8x12", pad: 3, scale: 2, label: E.getBattery() + "%" }, - { type: "txt", font: "8x12", pad: 3, scale: 2, label: "GPS: " + (Bangle.isGPSOn() ? "ON" : "OFF") }, - ], - }, + { + type: "h", + pad: 5, + fillx: 1, + c: [ + { type: "txt", font: "8x12", pad: 3, scale: 2, label: E.getBattery() + "%" }, + { type: "txt", font: "8x12", pad: 3, scale: 2, label: "GPS: " + (Bangle.isGPSOn() ? "ON" : "OFF") }, + ], + }, ]; // create rows for buttons and add them to layoutContent appButtons.forEach((appGroup) => { - layoutContent.push({ - type: "h", - pad: 2, - c: appGroup, - }); + layoutContent.push({ + type: "h", + pad: 2, + c: appGroup, + }); }); // create layout with content @@ -105,8 +105,8 @@ Bangle.loadWidgets(); let Layout = require("Layout"); let layout = new Layout({ - type: "v", - c: layoutContent + type: "v", + c: layoutContent }, { remove: ()=>{ Bangle.removeListener("swipe", onSwipe); @@ -119,10 +119,10 @@ Bangle.drawWidgets(); // swipe event listener for exit gesture let onSwipe = function (lr, ud) { - if(exitGesture == "swipeup" && ud == -1) Bangle.showClock(); - if(exitGesture == "swipedown" && ud == 1) Bangle.showClock(); - if(exitGesture == "swipeleft" && lr == -1) Bangle.showClock(); - if(exitGesture == "swiperight" && lr == 1) Bangle.showClock(); + if(exitGesture == "swipeup" && ud == -1) Bangle.showClock(); + if(exitGesture == "swipedown" && ud == 1) Bangle.showClock(); + if(exitGesture == "swipeleft" && lr == -1) Bangle.showClock(); + if(exitGesture == "swiperight" && lr == 1) Bangle.showClock(); } Bangle.on("swipe", onSwipe); diff --git a/apps/qcenter/metadata.json b/apps/qcenter/metadata.json index 448b7271a..b07b9430e 100644 --- a/apps/qcenter/metadata.json +++ b/apps/qcenter/metadata.json @@ -1,18 +1,18 @@ { - "id": "qcenter", - "name": "Quick Center", - "shortName": "QCenter", - "version": "0.01", - "description": "An app for quickly launching your favourite apps, inspired by the control centres of other watches.", - "icon": "app.png", - "tags": "", - "supports": ["BANGLEJS2"], - "readme": "README.md", - "screenshots": [{ "url": "screenshot.png" }], - "storage": [ - { "name": "qcenter.app.js", "url": "app.js" }, - { "name": "qcenter.settings.js", "url": "settings.js" }, - { "name": "qcenter.img", "url": "app-icon.js", "evaluate": true } - ], + "id": "qcenter", + "name": "Quick Center", + "shortName": "QCenter", + "version": "0.01", + "description": "An app for quickly launching your favourite apps, inspired by the control centres of other watches.", + "icon": "app.png", + "tags": "", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "screenshots": [{ "url": "screenshot.png" }], + "storage": [ + { "name": "qcenter.app.js", "url": "app.js" }, + { "name": "qcenter.settings.js", "url": "settings.js" }, + { "name": "qcenter.img", "url": "app-icon.js", "evaluate": true } + ], "data": [{"name":"qcenter.json"}] } diff --git a/apps/qcenter/settings.js b/apps/qcenter/settings.js index e4cd5458b..2c97f8a5f 100644 --- a/apps/qcenter/settings.js +++ b/apps/qcenter/settings.js @@ -1,133 +1,133 @@ // make sure to enclose the function in parentheses (function (back) { - let settings = require("Storage").readJSON("qcenter.json", 1) || {}; - var apps = require("Storage") - .list(/\.info$/) - .map((app) => { - var a = require("Storage").readJSON(app, 1); - return ( - a && { - name: a.name, - type: a.type, - sortorder: a.sortorder, - src: a.src, - icon: a.icon, - } - ); - }) - .filter( - (app) => - app && - (app.type == "app" || - app.type == "launch" || - app.type == "clock" || - !app.type) - ); - apps.sort((a, b) => { - var n = (0 | a.sortorder) - (0 | b.sortorder); - if (n) return n; // do sortorder first - if (a.name < b.name) return -1; - if (a.name > b.name) return 1; - return 0; - }); + let settings = require("Storage").readJSON("qcenter.json", 1) || {}; + var apps = require("Storage") + .list(/\.info$/) + .map((app) => { + var a = require("Storage").readJSON(app, 1); + return ( + a && { + name: a.name, + type: a.type, + sortorder: a.sortorder, + src: a.src, + icon: a.icon, + } + ); + }) + .filter( + (app) => + app && + (app.type == "app" || + app.type == "launch" || + app.type == "clock" || + !app.type) + ); + apps.sort((a, b) => { + var n = (0 | a.sortorder) - (0 | b.sortorder); + if (n) return n; // do sortorder first + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; + return 0; + }); - function save(key, value) { - settings[key] = value; - require("Storage").write("qcenter.json", settings); - } + function save(key, value) { + settings[key] = value; + require("Storage").write("qcenter.json", settings); + } - var pinnedApps = settings.pinnedApps || []; - var exitGesture = settings.exitGesture || "swipeup"; + var pinnedApps = settings.pinnedApps || []; + var exitGesture = settings.exitGesture || "swipeup"; - function showMainMenu() { - var mainmenu = { - "": { title: "Quick Center", back: back} + function showMainMenu() { + var mainmenu = { + "": { title: "Quick Center", back: back}, }; - // Set exit gesture - mainmenu["Exit Gesture: " + exitGesture] = function () { - E.showMenu(exitGestureMenu); - }; + // Set exit gesture + mainmenu["Exit Gesture: " + exitGesture] = function () { + E.showMenu(exitGestureMenu); + }; - //List all pinned apps, redirecting to menu with options to unpin and reorder - pinnedApps.forEach((app, i) => { - mainmenu[app.name] = function () { - E.showMenu({ - "": { title: app.name, back: showMainMenu }, - "Unpin": () => { - pinnedApps.splice(i, 1); - save("pinnedApps", pinnedApps); - showMainMenu(); - }, - "Move Up": () => { - if (i > 0) { - pinnedApps.splice(i - 1, 0, pinnedApps.splice(i, 1)[0]); - save("pinnedApps", pinnedApps); - showMainMenu(); - } - }, - "Move Down": () => { - if (i < pinnedApps.length - 1) { - pinnedApps.splice(i + 1, 0, pinnedApps.splice(i, 1)[0]); - save("pinnedApps", pinnedApps); - showMainMenu(); - } - }, - }); - }; - }); + //List all pinned apps, redirecting to menu with options to unpin and reorder + pinnedApps.forEach((app, i) => { + mainmenu[app.name] = function () { + E.showMenu({ + "": { title: app.name, back: showMainMenu }, + "Unpin": () => { + pinnedApps.splice(i, 1); + save("pinnedApps", pinnedApps); + showMainMenu(); + }, + "Move Up": () => { + if (i > 0) { + pinnedApps.splice(i - 1, 0, pinnedApps.splice(i, 1)[0]); + save("pinnedApps", pinnedApps); + showMainMenu(); + } + }, + "Move Down": () => { + if (i < pinnedApps.length - 1) { + pinnedApps.splice(i + 1, 0, pinnedApps.splice(i, 1)[0]); + save("pinnedApps", pinnedApps); + showMainMenu(); + } + }, + }); + }; + }); - // Show pin app menu, or show alert if max amount of apps are pinned - mainmenu["Pin App"] = function () { - if (pinnedApps.length < 6) { - E.showMenu(pinAppMenu); - } else { - E.showAlert("Max apps pinned").then(showMainMenu); - } - }; + // Show pin app menu, or show alert if max amount of apps are pinned + mainmenu["Pin App"] = function () { + if (pinnedApps.length < 6) { + E.showMenu(pinAppMenu); + } else { + E.showAlert("Max apps pinned").then(showMainMenu); + } + }; - return E.showMenu(mainmenu); - } + return E.showMenu(mainmenu); + } - // menu for adding apps to the quick launch menu, listing all apps - var pinAppMenu = { - "": { title: "Add App", back: showMainMenu } - }; - apps.forEach((a) => { - pinAppMenu[a.name] = function () { - // strip unncecessary properties - delete a.type; - delete a.sortorder; - pinnedApps.push(a); - save("pinnedApps", pinnedApps); - showMainMenu(); - }; - }); + // menu for adding apps to the quick launch menu, listing all apps + var pinAppMenu = { + "": { title: "Add App", back: showMainMenu } + }; + apps.forEach((a) => { + pinAppMenu[a.name] = function () { + // strip unncecessary properties + delete a.type; + delete a.sortorder; + pinnedApps.push(a); + save("pinnedApps", pinnedApps); + showMainMenu(); + }; + }); - // menu for setting exit gesture - var exitGestureMenu = { - "": { title: "Exit Gesture", back: showMainMenu } - }; - exitGestureMenu["Swipe Up"] = function () { - exitGesture = "swipeup"; - save("exitGesture", "swipeup"); - showMainMenu(); - }; - exitGestureMenu["Swipe Down"] = function () { - exitGesture = "swipedown"; - save("exitGesture", "swipedown"); - showMainMenu(); - }; - exitGestureMenu["Swipe Left"] = function () { - exitGesture = "swipeleft"; - save("exitGesture", "swipeleft"); - showMainMenu(); - }; - exitGestureMenu["Swipe Right"] = function () { - exitGesture = "swiperight"; - save("exitGesture", "swiperight"); - showMainMenu(); - }; + // menu for setting exit gesture + var exitGestureMenu = { + "": { title: "Exit Gesture", back: showMainMenu } + }; + exitGestureMenu["Swipe Up"] = function () { + exitGesture = "swipeup"; + save("exitGesture", "swipeup"); + showMainMenu(); + }; + exitGestureMenu["Swipe Down"] = function () { + exitGesture = "swipedown"; + save("exitGesture", "swipedown"); + showMainMenu(); + }; + exitGestureMenu["Swipe Left"] = function () { + exitGesture = "swipeleft"; + save("exitGesture", "swipeleft"); + showMainMenu(); + }; + exitGestureMenu["Swipe Right"] = function () { + exitGesture = "swiperight"; + save("exitGesture", "swiperight"); + showMainMenu(); + }; - showMainMenu(); + showMainMenu(); }); From 36e331e3600add4904d400d259eb9e6f9ab835a5 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Wed, 30 Nov 2022 18:18:56 +0100 Subject: [PATCH 080/107] qcenter - Bump version --- apps/qcenter/ChangeLog | 1 + apps/qcenter/metadata.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/qcenter/ChangeLog b/apps/qcenter/ChangeLog index 5560f00bc..900b9017c 100644 --- a/apps/qcenter/ChangeLog +++ b/apps/qcenter/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Fix fast loading on swipe to clock diff --git a/apps/qcenter/metadata.json b/apps/qcenter/metadata.json index b07b9430e..a325de10f 100644 --- a/apps/qcenter/metadata.json +++ b/apps/qcenter/metadata.json @@ -2,7 +2,7 @@ "id": "qcenter", "name": "Quick Center", "shortName": "QCenter", - "version": "0.01", + "version": "0.02", "description": "An app for quickly launching your favourite apps, inspired by the control centres of other watches.", "icon": "app.png", "tags": "", From 88da8fbc24620e7474a44d1ac5a280c9a2c86826 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Wed, 30 Nov 2022 19:08:18 +0100 Subject: [PATCH 081/107] gallery: Add interface Based on imageconverter.html with added Util.writeStorage()/Util.eraseStorage() --- apps/gallery/README.md | 4 +- apps/gallery/interface.html | 165 ++++++++++++++++++++++++++++++++++++ apps/gallery/metadata.json | 3 +- 3 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 apps/gallery/interface.html diff --git a/apps/gallery/README.md b/apps/gallery/README.md index b70fa07c2..65a0c36d3 100644 --- a/apps/gallery/README.md +++ b/apps/gallery/README.md @@ -8,6 +8,8 @@ Upon opening the gallery app, you will be presented with a list of images that y ## Adding images +Once this app is installed you can manage images by pressing the Disk icon next to it or by following the manual steps below: + 1. The gallery app does not perform any scaling, and does not support panning. Therefore, you should use your favorite image editor to produce an image of the appropriate size for your watch. (240x240 for Bangle 1 or 176x176 for Bangle 2.) How you achieve this is up to you. If on a Bangle 2, I recommend adjusting the colors here to comply with the color restrictions. 2. Upload your image to the [Espruino image converter](https://www.espruino.com/Image+Converter). I recommend enabling compression and choosing one of the following color settings: @@ -15,4 +17,4 @@ Upon opening the gallery app, you will be presented with a list of images that y * 3 bit RGB for Bangle 2 * 1 bit black/white for monochrome images that you want to respond to your system theme. (White will be rendered as your foreground color and black will be rendered as your background color.) -3. Set the output format to an image string, copy it into the [IDE](https://www.espruino.com/ide/), and set the destination to a file in storage. The file name should begin with "gal-" (without the quotes) and end with ".img" (without the quotes) to appear in the gallery. Note that the gal- prefix and .img extension will be removed in the UI. Upload the file. \ No newline at end of file +3. Set the output format to an image string, copy it into the [IDE](https://www.espruino.com/ide/), and set the destination to a file in storage. The file name should begin with "gal-" (without the quotes) and end with ".img" (without the quotes) to appear in the gallery. Note that the gal- prefix and .img extension will be removed in the UI. Upload the file. diff --git a/apps/gallery/interface.html b/apps/gallery/interface.html new file mode 100644 index 000000000..f309270ca --- /dev/null +++ b/apps/gallery/interface.html @@ -0,0 +1,165 @@ + + + + + + + + + +

Existing Images

+
    +
+ +

Convert & Upload Images

+ +
+ Use Compression?
+ Transparency to Color
+ Transparency?
+ Inverted?
+ Crop?
+ Diffusion:
+ + Brightness: +
+ Contrast: +
+ Colours:
+ + + + + + + + diff --git a/apps/gallery/metadata.json b/apps/gallery/metadata.json index 89f7606aa..0dc8d1613 100644 --- a/apps/gallery/metadata.json +++ b/apps/gallery/metadata.json @@ -12,6 +12,7 @@ "BANGLEJS" ], "allow_emulator": true, + "interface": "interface.html", "storage": [ { "name": "gallery.app.js", @@ -23,4 +24,4 @@ "evaluate": true } ] -} \ No newline at end of file +} From f21a0c6a520a201ddb7a1dca0d017415143bea9f Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Wed, 30 Nov 2022 20:44:19 +0100 Subject: [PATCH 082/107] add loadWidgets to work with Fastload Utils app --- apps/torch/ChangeLog | 1 + apps/torch/app.js | 1 + apps/torch/metadata.json | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/torch/ChangeLog b/apps/torch/ChangeLog index fa69d5d04..9c8521265 100644 --- a/apps/torch/ChangeLog +++ b/apps/torch/ChangeLog @@ -8,3 +8,4 @@ 0.08: Force background of widget field to the torch colour 0.09: Change code taking FW tweaks into account 0.10: Introduce fast switching. +0.11: Make compatible with Fastload Utils by loading widgets. diff --git a/apps/torch/app.js b/apps/torch/app.js index 5d288579c..a10943794 100644 --- a/apps/torch/app.js +++ b/apps/torch/app.js @@ -18,6 +18,7 @@ g.setTheme({bg:settings.bg,fg:"#000"}); g.setColor(settings.bg); g.fillRect(0,0,g.getWidth(),g.getHeight()); + Bangle.loadWidgets(); Bangle.setUI({ mode : 'custom', back : Bangle.showClock, // B2: SW back button to exit diff --git a/apps/torch/metadata.json b/apps/torch/metadata.json index 48399de96..4e8794663 100644 --- a/apps/torch/metadata.json +++ b/apps/torch/metadata.json @@ -2,7 +2,7 @@ "id": "torch", "name": "Torch", "shortName": "Torch", - "version": "0.10", + "version": "0.11", "description": "Turns screen white to help you see in the dark. Select from the launcher or press BTN1,BTN3,BTN1,BTN3 quickly to start when in any app that shows widgets on Bangle.js 1. You can also set the color through the app's setting menu.", "icon": "app.png", "tags": "tool,torch", From 95c5fd8bcff453f80096a5f755b9767e0ff205d1 Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Wed, 30 Nov 2022 21:10:06 +0100 Subject: [PATCH 083/107] only load widgets if fastload is installed --- apps/torch/ChangeLog | 3 ++- apps/torch/app.js | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/torch/ChangeLog b/apps/torch/ChangeLog index 9c8521265..a3665a562 100644 --- a/apps/torch/ChangeLog +++ b/apps/torch/ChangeLog @@ -8,4 +8,5 @@ 0.08: Force background of widget field to the torch colour 0.09: Change code taking FW tweaks into account 0.10: Introduce fast switching. -0.11: Make compatible with Fastload Utils by loading widgets. +0.11: Make compatible with Fastload Utils by loading widgets. No change if +Fastload Utils is not installed. diff --git a/apps/torch/app.js b/apps/torch/app.js index a10943794..a65a2ff49 100644 --- a/apps/torch/app.js +++ b/apps/torch/app.js @@ -1,14 +1,15 @@ { const SETTINGS_FILE = "torch.json"; let settings; + let s = require("Storage"); let loadSettings = function() { - settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#FFFFFF', 'color': 'White'}; + settings = s.readJSON(SETTINGS_FILE,1)|| {'bg': '#FFFFFF', 'color': 'White'}; }; loadSettings(); - let brightnessBackup = require("Storage").readJSON('setting.json').brightness; + let brightnessBackup = s.readJSON('setting.json').brightness; let optionsBackup = Bangle.getOptions(); Bangle.setLCDBrightness(1); Bangle.setLCDPower(1); @@ -18,7 +19,8 @@ g.setTheme({bg:settings.bg,fg:"#000"}); g.setColor(settings.bg); g.fillRect(0,0,g.getWidth(),g.getHeight()); - Bangle.loadWidgets(); + // if Fastload Utils is installed, make Torch compatible with it. + if (s.readJSON('fastload.info') != undefined) Bangle.loadWidgets(); Bangle.setUI({ mode : 'custom', back : Bangle.showClock, // B2: SW back button to exit From c4cbac0898edaa45b3f7484cd379c9ad5972d871 Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Wed, 30 Nov 2022 21:45:57 +0100 Subject: [PATCH 084/107] use widget_utils slideOn - needs further work --- apps/torch/app.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/torch/app.js b/apps/torch/app.js index a65a2ff49..0b7dc53ff 100644 --- a/apps/torch/app.js +++ b/apps/torch/app.js @@ -20,7 +20,10 @@ g.setColor(settings.bg); g.fillRect(0,0,g.getWidth(),g.getHeight()); // if Fastload Utils is installed, make Torch compatible with it. - if (s.readJSON('fastload.info') != undefined) Bangle.loadWidgets(); + if (s.readJSON('fastload.info') != undefined) { + Bangle.loadWidgets(); + require('widget_utils').swipeOn(); + } Bangle.setUI({ mode : 'custom', back : Bangle.showClock, // B2: SW back button to exit From e01e08db220eed861469d5d3e6c6388854adfefa Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Wed, 30 Nov 2022 23:40:47 +0100 Subject: [PATCH 085/107] no swipeOn for HW=1 --- apps/torch/app.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/torch/app.js b/apps/torch/app.js index 0b7dc53ff..1c96d50f3 100644 --- a/apps/torch/app.js +++ b/apps/torch/app.js @@ -2,6 +2,7 @@ const SETTINGS_FILE = "torch.json"; let settings; let s = require("Storage"); + let wu = require("widget_utils"); let loadSettings = function() { settings = s.readJSON(SETTINGS_FILE,1)|| {'bg': '#FFFFFF', 'color': 'White'}; @@ -19,10 +20,10 @@ g.setTheme({bg:settings.bg,fg:"#000"}); g.setColor(settings.bg); g.fillRect(0,0,g.getWidth(),g.getHeight()); - // if Fastload Utils is installed, make Torch compatible with it. - if (s.readJSON('fastload.info') != undefined) { + if (s.read('fastload.cache')!==undefined) { Bangle.loadWidgets(); - require('widget_utils').swipeOn(); + if (process.env.HWVERSION==1) wu.hide(); + if (process.env.HWVERSION==2) wu.swipeOn(); } Bangle.setUI({ mode : 'custom', @@ -32,6 +33,8 @@ Bangle.setLCDBrightness(brightnessBackup); Bangle.setOptions(optionsBackup); g.setTheme(themeBackup); + wu.show(); } }); } + From e9c673e119bdae72442bebbd9f6af356ab146496 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Wed, 30 Nov 2022 23:58:36 +0100 Subject: [PATCH 086/107] messages,messagegui: fix handling of music The library combines musicinfo+musicstate messages, and waited for a "complete" message before emitting the event. But once you are playing music you don't get new music state events. The problem was that when the first "complete" message arrives, the UI is load()ed, so the library forgets its state, and doesn't emit anymore music events until the play state changes. This - changes the library to keep combining music messages, but always emit an event - makes the UI only launch itself if it sees a "complete" music event - makes the UI also combine musicinfo+musicstate messages --- apps/messagegui/ChangeLog | 1 + apps/messagegui/app.js | 4 +++- apps/messagegui/lib.js | 4 ++-- apps/messagegui/metadata.json | 2 +- apps/messages/ChangeLog | 1 + apps/messages/lib.js | 1 - apps/messages/metadata.json | 2 +- 7 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/messagegui/ChangeLog b/apps/messagegui/ChangeLog index 3dcb20fe8..0fd43347d 100644 --- a/apps/messagegui/ChangeLog +++ b/apps/messagegui/ChangeLog @@ -77,3 +77,4 @@ 0.54: Move icons out to messageicons module 0.55: Rename to messagegui, move global message handling library to message module Move widget to widmessage +0.56: Fix handling of music messages diff --git a/apps/messagegui/app.js b/apps/messagegui/app.js index bf086dd3d..50dc9182a 100644 --- a/apps/messagegui/app.js +++ b/apps/messagegui/app.js @@ -112,9 +112,11 @@ function showMapMessage(msg) { Bangle.setUI({mode:"updown", back: back}, back); // any input takes us back } -var updateLabelsInterval; +let updateLabelsInterval, + music = {artist: "", album: "", title: ""}; // defaults, so e.g. msg.title.length doesn't error function showMusicMessage(msg) { active = "music"; + msg = Object.assign(music, msg); // combine+remember "musicinfo" and "musicstate" messages openMusic = msg.state=="play"; var trackScrollOffset = 0; var artistScrollOffset = 0; diff --git a/apps/messagegui/lib.js b/apps/messagegui/lib.js index 5b07dd160..e45e6e5a0 100644 --- a/apps/messagegui/lib.js +++ b/apps/messagegui/lib.js @@ -15,12 +15,12 @@ exports.listener = function(type, msg) { const appSettings = require("Storage").readJSON("messages.settings.json", 1) || {}; let loadMessages = (Bangle.CLOCK || event.important); if (type==="music") { - if (Bangle.CLOCK && msg.new && appSettings.openMusic) loadMessages = true; + if (Bangle.CLOCK && msg.state && msg.title && appSettings.openMusic) loadMessages = true; else return; } require("messages").save(msg); msg.handled = true; - if (msg.t!=="add" || !msg.new) { + if ((msg.t!=="add" || !msg.new) && (type!=="music")) { // music always has t:"modify" return; } diff --git a/apps/messagegui/metadata.json b/apps/messagegui/metadata.json index c92fa90f5..361ce9b3d 100644 --- a/apps/messagegui/metadata.json +++ b/apps/messagegui/metadata.json @@ -1,7 +1,7 @@ { "id": "messagegui", "name": "Message UI", - "version": "0.55", + "version": "0.56", "description": "Default app to display notifications from iOS and Gadgetbridge/Android", "icon": "app.png", "type": "app", diff --git a/apps/messages/ChangeLog b/apps/messages/ChangeLog index c984e4a0f..3df056d62 100644 --- a/apps/messages/ChangeLog +++ b/apps/messages/ChangeLog @@ -1 +1,2 @@ 0.55: Moved messages library into standalone library +0.56: Fix handling of music messages diff --git a/apps/messages/lib.js b/apps/messages/lib.js index fa1419c95..e5c81f3fd 100644 --- a/apps/messages/lib.js +++ b/apps/messages/lib.js @@ -6,7 +6,6 @@ exports.music = {}; function emit(msg) { let type = "text"; if (["call", "music", "map"].includes(msg.id)) type = msg.id; - if (type==="music" && msg.t!=="remove" && (!("state" in msg) || (!("track" in msg)))) return; // wait for complete music info if (msg.src && msg.src.toLowerCase().startsWith("alarm")) type = "alarm"; Bangle.emit("message", type, msg); } diff --git a/apps/messages/metadata.json b/apps/messages/metadata.json index 74c89b1b4..8bcf3da25 100644 --- a/apps/messages/metadata.json +++ b/apps/messages/metadata.json @@ -1,7 +1,7 @@ { "id": "messages", "name": "Messages", - "version": "0.55", + "version": "0.56", "description": "Library to handle, load and store message events received from Android/iOS", "icon": "app.png", "type": "module", From ef74a18566a1f7c2859422dce3e694a293398a0b Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Thu, 1 Dec 2022 00:45:48 +0100 Subject: [PATCH 087/107] load widgets and hide them --- apps/torch/ChangeLog | 3 +-- apps/torch/app.js | 8 ++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/apps/torch/ChangeLog b/apps/torch/ChangeLog index a3665a562..35d3f5927 100644 --- a/apps/torch/ChangeLog +++ b/apps/torch/ChangeLog @@ -8,5 +8,4 @@ 0.08: Force background of widget field to the torch colour 0.09: Change code taking FW tweaks into account 0.10: Introduce fast switching. -0.11: Make compatible with Fastload Utils by loading widgets. No change if -Fastload Utils is not installed. +0.11: Make compatible with Fastload Utils by loading and hiding widgets. diff --git a/apps/torch/app.js b/apps/torch/app.js index 1c96d50f3..b44cfb929 100644 --- a/apps/torch/app.js +++ b/apps/torch/app.js @@ -20,11 +20,8 @@ g.setTheme({bg:settings.bg,fg:"#000"}); g.setColor(settings.bg); g.fillRect(0,0,g.getWidth(),g.getHeight()); - if (s.read('fastload.cache')!==undefined) { - Bangle.loadWidgets(); - if (process.env.HWVERSION==1) wu.hide(); - if (process.env.HWVERSION==2) wu.swipeOn(); - } + Bangle.loadWidgets(); + wu.hide(); Bangle.setUI({ mode : 'custom', back : Bangle.showClock, // B2: SW back button to exit @@ -37,4 +34,3 @@ } }); } - From 1d4fb1301a65b0f94eab0e4f6ae173112a68c627 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Thu, 1 Dec 2022 00:54:38 +0100 Subject: [PATCH 088/107] ClockFace: support Fast Loading, by adding a remove() function to clocks --- modules/ClockFace.js | 36 ++++++++++++++++++++++++++++++------ modules/ClockFace.md | 5 +++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/modules/ClockFace.js b/modules/ClockFace.js index c12360626..b1b007be9 100644 --- a/modules/ClockFace.js +++ b/modules/ClockFace.js @@ -9,7 +9,7 @@ function ClockFace(options) { if (![ "precision", "init", "draw", "update", - "pause", "resume", + "pause", "resume", "remove", "up", "down", "upDown", "settingsFile", ].includes(k)) throw `Invalid ClockFace option: ${k}`; @@ -27,6 +27,7 @@ function ClockFace(options) { if (options.init) this.init = options.init; if (options.pause) this._pause = options.pause; if (options.resume) this._resume = options.resume; + if (options.remove) this._remove = options.remove; if ((options.up || options.down) && options.upDown) throw "ClockFace up/down and upDown cannot be used together"; if (options.up || options.down) this._upDown = (dir) => { if (dir<0 && options.up) options.up.apply(this); @@ -44,8 +45,15 @@ function ClockFace(options) { ["showDate", "loadWidgets"].forEach(k => { if (this[k]===undefined) this[k] = true; }); + let s = require("Storage").readJSON("setting.json",1)||{}; + if ((global.__FILE__===undefined || global.__FILE__===s.clock) + && s.clockHasWidgets!==this.loadWidgets) { + // save whether we can Fast Load + s.clockHasWidgets = this.loadWidgets; + require("Storage").writeJSON("setting.json", s); + } // use global 24/12-hour setting if not set by clock-settings - if (!('is12Hour' in this)) this.is12Hour = !!(require("Storage").readJSON("setting.json", true) || {})["12hour"]; + if (!('is12Hour' in this)) this.is12Hour = !!(s["12hour"]); } ClockFace.prototype.tick = function() { @@ -85,16 +93,27 @@ ClockFace.prototype.start = function() { Bangle.CLOCK = 1; if (this.loadWidgets) Bangle.loadWidgets(); if (this.init) this.init.apply(this); - if (this._upDown) Bangle.setUI("clockupdown", d=>this._upDown.apply(this,[d])); - else Bangle.setUI("clock"); + const uiRemove = this._remove ? () => this.remove() : undefined; + if (this._upDown) { + Bangle.setUI({ + mode: "clockupdown", + remove: uiRemove, + }, d => this._upDown.apply(this, [d])); + } else { + Bangle.setUI({ + mode: "clock", + remove: uiRemove, + }); + } delete this._last; this.paused = false; this.tick(); - Bangle.on("lcdPower", on => { + this._onLcd = on => { if (on) this.resume(); else this.pause(); - }); + }; + Bangle.on("lcdPower", this._onLcd); }; ClockFace.prototype.pause = function() { @@ -111,6 +130,11 @@ ClockFace.prototype.resume = function() { if (this._resume) this._resume.apply(this); this.tick(); }; +ClockFace.prototype.remove = function() { + if (this._timeout) clearTimeout(this._timeout); + Bangle.removeListener("lcdPower", this._onLcd); + if (this._remove) this._remove.apply(this); +}; /** * Force a complete redraw diff --git a/modules/ClockFace.md b/modules/ClockFace.md index 85482213c..f123d38c0 100644 --- a/modules/ClockFace.md +++ b/modules/ClockFace.md @@ -77,6 +77,11 @@ var clock = new ClockFace({ resume: function() { // optional, called when the screen turns on // for example: turn GPS/compass back on }, + remove: function() { // optional, used for Fast Loading + // for example: remove listeners + // Fast Loading will not be used unless this function is present, + // if there is nothing to clean up, you can just leave it empty. + }, up: function() { // optional, up handler }, down: function() { // optional, down handler From 050139c763ab9705b9630184418410012f5493a6 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Thu, 1 Dec 2022 01:16:23 +0100 Subject: [PATCH 089/107] barclock: add Fast Loading --- apps/barclock/ChangeLog | 3 +- apps/barclock/clock-bar.js | 234 ++++++++++++++++++------------------ apps/barclock/metadata.json | 2 +- 3 files changed, 122 insertions(+), 117 deletions(-) diff --git a/apps/barclock/ChangeLog b/apps/barclock/ChangeLog index a00ae9325..88f4eaf00 100644 --- a/apps/barclock/ChangeLog +++ b/apps/barclock/ChangeLog @@ -12,4 +12,5 @@ 0.12: Add settings to hide date,widgets 0.13: Add font setting 0.14: Use ClockFace_menu.addItems -0.15: Add Power saving option \ No newline at end of file +0.15: Add Power saving option +0.16: Support Fast Loading diff --git a/apps/barclock/clock-bar.js b/apps/barclock/clock-bar.js index 5a7dfc8c0..f2499189b 100644 --- a/apps/barclock/clock-bar.js +++ b/apps/barclock/clock-bar.js @@ -1,124 +1,128 @@ /* jshint esversion: 6 */ -/** - * A simple digital clock showing seconds as a bar - **/ +{ + /** + * A simple digital clock showing seconds as a bar + **/ // Check settings for what type our clock should be -let locale = require("locale"); -{ // add some more info to locale - let date = new Date(); - date.setFullYear(1111); - date.setMonth(1, 3); // februari: months are zero-indexed - const localized = locale.date(date, true); - locale.dayFirst = /3.*2/.test(localized); - locale.hasMeridian = (locale.meridian(date)!==""); -} - -let barW = 0, prevX = 0; -function renderBar(l) { - "ram"; - if (l) prevX = 0; // called from Layout: drawing area was cleared - else l = clock.layout.bar; - let x2 = l.x+barW; - if (clock.powerSave && Bangle.isLocked()) x2 = 0; // hide bar - if (x2===prevX) return; // nothing to do - if (x2===0) x2--; // don't leave 1px line - if (x212) { - date12.setHours(hours-12); - } - return locale.time(date12, true); -} -function ampmText(date) { - return (clock.is12Hour && locale.hasMeridian) ? locale.meridian(date) : ""; -} -function dateText(date) { - const dayName = locale.dow(date, true), - month = locale.month(date, true), - day = date.getDate(); - const dayMonth = locale.dayFirst ? `${day} ${month}` : `${month} ${day}`; - return `${dayName} ${dayMonth}`; -} -const ClockFace = require("ClockFace"), - clock = new ClockFace({ - precision: 1, - settingsFile: "barclock.settings.json", - init: function() { - const Layout = require("Layout"); - this.layout = new Layout({ - type: "v", c: [ - { - type: "h", c: [ - {id: "time", label: "88:88", type: "txt", font: "6x8:5", col: g.theme.fg, bgCol: g.theme.bg}, // updated below - {id: "ampm", label: " ", type: "txt", font: "6x8:2", col: g.theme.fg, bgCol: g.theme.bg}, - ], - }, - {id: "bar", type: "custom", fillx: 1, height: 6, col: g.theme.fg2, render: renderBar}, - this.showDate ? {height: 40} : {}, - this.showDate ? {id: "date", type: "txt", font: "10%", valign: 1} : {}, - ], - }, {lazy: true}); - // adjustments based on screen size and whether we display am/pm - let thickness; // bar thickness, same as time font "pixel block" size - if (this.is12Hour && locale.hasMeridian) { - // Maximum font size = ( - ) / (5chars * 6px) - thickness = Math.floor((Bangle.appRect.w-24)/(5*6)); - } else { - this.layout.ampm.label = ""; - thickness = Math.floor(Bangle.appRect.w/(5*6)); - } - let bar = this.layout.bar; - bar.height = thickness+1; - if (this.font===1) { // vector - const B2 = process.env.HWVERSION>1; + let barW = 0, prevX = 0; + const renderBar = function (l) { + "ram"; + if (l) prevX = 0; // called from Layout: drawing area was cleared + else l = clock.layout.bar; + let x2 = l.x+barW; + if (clock.powerSave && Bangle.isLocked()) x2 = 0; // hide bar + if (x2===prevX) return; // nothing to do + if (x2===0) x2--; // don't leave 1px line + if (x212) { + date12.setHours(hours-12); + } + return locale.time(date12, true); + } + const ampmText = date => (clock.is12Hour && locale.hasMeridian) ? locale.meridian(date) : ""; + const dateText = date => { + const dayName = locale.dow(date, true), + month = locale.month(date, true), + day = date.getDate(); + const dayMonth = locale.dayFirst ? `${day} ${month}` : `${month} ${day}`; + return `${dayName} ${dayMonth}`; + }; + + const ClockFace = require("ClockFace"), + clock = new ClockFace({ + precision: 1, + settingsFile: "barclock.settings.json", + init: function() { + const Layout = require("Layout"); + this.layout = new Layout({ + type: "v", c: [ + { + type: "h", c: [ + {id: "time", label: "88:88", type: "txt", font: "6x8:5", col: g.theme.fg, bgCol: g.theme.bg}, // updated below + {id: "ampm", label: " ", type: "txt", font: "6x8:2", col: g.theme.fg, bgCol: g.theme.bg}, + ], + }, + {id: "bar", type: "custom", fillx: 1, height: 6, col: g.theme.fg2, render: renderBar}, + this.showDate ? {height: 40} : {}, + this.showDate ? {id: "date", type: "txt", font: "10%", valign: 1} : {}, + ], + }, {lazy: true}); + // adjustments based on screen size and whether we display am/pm + let thickness; // bar thickness, same as time font "pixel block" size if (this.is12Hour && locale.hasMeridian) { - this.layout.time.font = "Vector:"+(B2 ? 50 : 60); - this.layout.ampm.font = "Vector:"+(B2 ? 20 : 40); + // Maximum font size = ( - ) / (5chars * 6px) + thickness = Math.floor((Bangle.appRect.w-24)/(5*6)); } else { - this.layout.time.font = "Vector:"+(B2 ? 60 : 80); + this.layout.ampm.label = ""; + thickness = Math.floor(Bangle.appRect.w/(5*6)); } - } else { - this.layout.time.font = "6x8:"+thickness; - } - this.layout.update(); - bar.y2 = bar.y+bar.height-1; - }, - update: function(date, c) { - "ram"; - if (c.m) this.layout.time.label = timeText(date); - if (c.h) this.layout.ampm.label = ampmText(date); - if (c.d && this.showDate) this.layout.date.label = dateText(date); - if (c.m) this.layout.render(); - if (c.s) { - barW = Math.round(date.getSeconds()/60*this.layout.bar.w); - renderBar(); - } - }, - resume: function() { - prevX = 0; // force redraw of bar - this.layout.forgetLazyState(); - }, - }); + let bar = this.layout.bar; + bar.height = thickness+1; + if (this.font===1) { // vector + const B2 = process.env.HWVERSION>1; + if (this.is12Hour && locale.hasMeridian) { + this.layout.time.font = "Vector:"+(B2 ? 50 : 60); + this.layout.ampm.font = "Vector:"+(B2 ? 20 : 40); + } else { + this.layout.time.font = "Vector:"+(B2 ? 60 : 80); + } + } else { + this.layout.time.font = "6x8:"+thickness; + } + this.layout.update(); + bar.y2 = bar.y+bar.height-1; + }, + update: function(date, c) { + "ram"; + if (c.m) this.layout.time.label = timeText(date); + if (c.h) this.layout.ampm.label = ampmText(date); + if (c.d && this.showDate) this.layout.date.label = dateText(date); + if (c.m) this.layout.render(); + if (c.s) { + barW = Math.round(date.getSeconds()/60*this.layout.bar.w); + renderBar(); + } + }, + resume: function() { + prevX = 0; // force redraw of bar + this.layout.forgetLazyState(); + }, + remove: function() { + if (this.onLock) Bangle.removeListener("lock", this.onLock); + }, + }); -// power saving: only update once a minute while locked, hide bar -if (clock.powerSave) { - Bangle.on("lock", lock => { - clock.precision = lock ? 60 : 1; - clock.tick(); - renderBar(); // hide/redraw bar right away - }); -} + // power saving: only update once a minute while locked, hide bar + if (clock.powerSave) { + clock.onLock = lock => { + clock.precision = lock ? 60 : 1; + clock.tick(); + renderBar(); // hide/redraw bar right away + } + Bangle.on("lock", clock.onLock); + } -clock.start(); + clock.start(); +} \ No newline at end of file diff --git a/apps/barclock/metadata.json b/apps/barclock/metadata.json index 5b783dbda..785c228b0 100644 --- a/apps/barclock/metadata.json +++ b/apps/barclock/metadata.json @@ -1,7 +1,7 @@ { "id": "barclock", "name": "Bar Clock", - "version": "0.15", + "version": "0.16", "description": "A simple digital clock showing seconds as a bar", "icon": "clock-bar.png", "screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}], From 41046b9bb1b7aa986c3a684751dc075149e61dae Mon Sep 17 00:00:00 2001 From: Dustin Richards Date: Thu, 1 Dec 2022 02:03:23 -0700 Subject: [PATCH 090/107] [alarm] add confirm button to edit alarm menu --- apps/alarm/app.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/alarm/app.js b/apps/alarm/app.js index ed5aa608a..a0cde1074 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -128,6 +128,11 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) { value: alarm.hidden || false, onchange: v => alarm.hidden = v }, + /*LANG*/"Confirm": () { + prepareAlarmForSave(alarm, alarmIndex, time); + saveAndReload(); + showMainMenu(); + }, /*LANG*/"Cancel": () => showMainMenu() }; From 6fe79f47b678a5f13951a6fa28d5e8cf075938a1 Mon Sep 17 00:00:00 2001 From: Dustin Richards Date: Thu, 1 Dec 2022 02:10:00 -0700 Subject: [PATCH 091/107] [alarm] fix broken lambda --- apps/alarm/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/alarm/app.js b/apps/alarm/app.js index a0cde1074..b7d9ff82a 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -128,7 +128,7 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) { value: alarm.hidden || false, onchange: v => alarm.hidden = v }, - /*LANG*/"Confirm": () { + /*LANG*/"Confirm": () => { prepareAlarmForSave(alarm, alarmIndex, time); saveAndReload(); showMainMenu(); From 245c15646839118021760904708a293ecb9e784c Mon Sep 17 00:00:00 2001 From: Dustin Richards Date: Thu, 1 Dec 2022 02:19:41 -0700 Subject: [PATCH 092/107] [alarm] version bump --- apps/alarm/ChangeLog | 1 + apps/alarm/metadata.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/alarm/ChangeLog b/apps/alarm/ChangeLog index 52ee8bf9c..6e648d42a 100644 --- a/apps/alarm/ChangeLog +++ b/apps/alarm/ChangeLog @@ -34,4 +34,5 @@ 0.32: Fix wrong hidden filter Add option for auto-delete a timer after it expires 0.33: Allow hiding timers&alarms +0.34: Add "Confirm" option to alarm/timer edit menus diff --git a/apps/alarm/metadata.json b/apps/alarm/metadata.json index 31dd58ece..2e4ea9f94 100644 --- a/apps/alarm/metadata.json +++ b/apps/alarm/metadata.json @@ -2,7 +2,7 @@ "id": "alarm", "name": "Alarms & Timers", "shortName": "Alarms", - "version": "0.33", + "version": "0.34", "description": "Set alarms and timers on your Bangle", "icon": "app.png", "tags": "tool,alarm,widget", From edcfd198feb2eb9ebcd70164726e99a1106e1b50 Mon Sep 17 00:00:00 2001 From: Dustin Richards Date: Thu, 1 Dec 2022 02:29:00 -0700 Subject: [PATCH 093/107] [alarm] add confirm button to edit timer menu --- apps/alarm/app.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/alarm/app.js b/apps/alarm/app.js index b7d9ff82a..efcabfb6d 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -128,12 +128,12 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) { value: alarm.hidden || false, onchange: v => alarm.hidden = v }, + /*LANG*/"Cancel": () => showMainMenu(), /*LANG*/"Confirm": () => { prepareAlarmForSave(alarm, alarmIndex, time); saveAndReload(); showMainMenu(); - }, - /*LANG*/"Cancel": () => showMainMenu() + } }; if (!isNew) { @@ -298,7 +298,12 @@ function showEditTimerMenu(selectedTimer, timerIndex) { onchange: v => timer.hidden = v }, /*LANG*/"Vibrate": require("buzz_menu").pattern(timer.vibrate, v => timer.vibrate = v), - /*LANG*/"Cancel": () => showMainMenu() + /*LANG*/"Cancel": () => showMainMenu(), + /*LANG*/"Confirm": () => { + prepareTimerForSave(timer, timerIndex, time); + saveAndReload(); + showMainMenu(); + } }; if (!isNew) { From 218e2d5214736498aaaef56fab33eafefec34442 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Thu, 1 Dec 2022 10:32:49 +0000 Subject: [PATCH 094/107] health 0.16: Code tidyup, add back button in top left of health app graphs --- apps/health/ChangeLog | 1 + apps/health/app.js | 87 +++++++-------------------------------- apps/health/metadata.json | 2 +- 3 files changed, 18 insertions(+), 72 deletions(-) diff --git a/apps/health/ChangeLog b/apps/health/ChangeLog index 62d93e606..25909891a 100644 --- a/apps/health/ChangeLog +++ b/apps/health/ChangeLog @@ -14,3 +14,4 @@ 0.13: Add support for internationalization 0.14: Move settings 0.15: Fix charts (fix #1366) +0.16: Code tidyup, add back button in top left of health app graphs diff --git a/apps/health/app.js b/apps/health/app.js index c0a40bd93..576e1d02f 100644 --- a/apps/health/app.js +++ b/apps/health/app.js @@ -1,6 +1,4 @@ function menuMain() { - swipe_enabled = false; - clearButton(); E.showMenu({ "": { title: /*LANG*/"Health Tracking" }, /*LANG*/"< Back": () => load(), @@ -12,8 +10,6 @@ function menuMain() { } function menuStepCount() { - swipe_enabled = false; - clearButton(); E.showMenu({ "": { title:/*LANG*/"Steps" }, /*LANG*/"< Back": () => menuMain(), @@ -23,8 +19,6 @@ function menuStepCount() { } function menuMovement() { - swipe_enabled = false; - clearButton(); E.showMenu({ "": { title:/*LANG*/"Movement" }, /*LANG*/"< Back": () => menuMain(), @@ -34,8 +28,6 @@ function menuMovement() { } function menuHRM() { - swipe_enabled = false; - clearButton(); E.showMenu({ "": { title:/*LANG*/"Heart Rate" }, /*LANG*/"< Back": () => menuMain(), @@ -48,9 +40,6 @@ function stepsPerHour() { E.showMessage(/*LANG*/"Loading..."); var data = new Uint16Array(24); require("health").readDay(new Date(), h=>data[h.hr]+=h.steps); - g.clear(1); - Bangle.drawWidgets(); - g.reset(); setButton(menuStepCount); barChart("HOUR", data); } @@ -59,9 +48,6 @@ function stepsPerDay() { E.showMessage(/*LANG*/"Loading..."); var data = new Uint16Array(31); require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps); - g.clear(1); - Bangle.drawWidgets(); - g.reset(); setButton(menuStepCount); barChart("DAY", data); } @@ -75,9 +61,6 @@ function hrmPerHour() { if (h.bpm) cnt[h.hr]++; }); data.forEach((d,i)=>data[i] = d/cnt[i]); - g.clear(1); - Bangle.drawWidgets(); - g.reset(); setButton(menuHRM); barChart("HOUR", data); } @@ -91,9 +74,6 @@ function hrmPerDay() { if (h.bpm) cnt[h.day]++; }); data.forEach((d,i)=>data[i] = d/cnt[i]); - g.clear(1); - Bangle.drawWidgets(); - g.reset(); setButton(menuHRM); barChart("DAY", data); } @@ -102,9 +82,6 @@ function movementPerHour() { E.showMessage(/*LANG*/"Loading..."); var data = new Uint16Array(24); require("health").readDay(new Date(), h=>data[h.hr]+=h.movement); - g.clear(1); - Bangle.drawWidgets(); - g.reset(); setButton(menuMovement); barChart("HOUR", data); } @@ -113,15 +90,11 @@ function movementPerDay() { E.showMessage(/*LANG*/"Loading..."); var data = new Uint16Array(31); require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement); - g.clear(1); - Bangle.drawWidgets(); - g.reset(); setButton(menuMovement); barChart("DAY", data); } // Bar Chart Code - const w = g.getWidth(); const h = g.getHeight(); @@ -130,13 +103,10 @@ var chart_index; var chart_max_datum; var chart_label; var chart_data; -var swipe_enabled = false; -var btn; // find the max value in the array, using a loop due to array size function max(arr) { var m = -Infinity; - for(var i=0; i< arr.length; i++) if(arr[i] > m) m = arr[i]; return m; @@ -145,10 +115,8 @@ function max(arr) { // find the end of the data, the array might be for 31 days but only have 2 days of data in it function get_data_length(arr) { var nlen = arr.length; - for(var i = arr.length - 1; i > 0 && arr[i] == 0; i--) nlen--; - return nlen; } @@ -167,15 +135,11 @@ function drawBarChart() { const bar_width = (w - 2) / 9; // we want 9 bars, bar 5 in the centre var bar_top; var bar; - - g.setColor(g.theme.bg); - g.fillRect(0,24,w,h); + g.reset().clearRect(0,24,w,h); for (bar = 1; bar < 10; bar++) { if (bar == 5) { - g.setFont('6x8', 2); - g.setFontAlign(0,-1); - g.setColor(g.theme.fg); + g.setFont('6x8', 2).setFontAlign(0,-1).setColor(g.theme.fg); g.drawString(chart_label + " " + (chart_index + bar -1) + " " + chart_data[chart_index + bar - 1], g.getWidth()/2, 150); g.setColor("#00f"); } else { @@ -189,45 +153,26 @@ function drawBarChart() { bar_top = bar_bot; g.fillRect( 1 + (bar - 1)* bar_width, bar_bot, 1 + bar*bar_width, bar_top); - g.setColor(g.theme.fg); - g.drawRect( 1 + (bar - 1)* bar_width, bar_bot, 1 + bar*bar_width, bar_top); + g.setColor(g.theme.fg).drawRect( 1 + (bar - 1)* bar_width, bar_bot, 1 + bar*bar_width, bar_top); } } -function next_bar() { - chart_index = Math.min(data_len - 5, chart_index + 1); -} - -function prev_bar() { - // HOUR data starts at index 0, DAY data starts at index 1 - chart_index = Math.max((chart_label == "DAY") ? -3 : -4, chart_index - 1); -} - -Bangle.on('swipe', dir => { - if (!swipe_enabled) return; - if (dir == 1) prev_bar(); else next_bar(); - drawBarChart(); -}); - -// use setWatch() as Bangle.setUI("updown",..) interacts with swipes function setButton(fn) { - // cancel callback, otherwise a slight up down movement will show the E.showMenu() - Bangle.setUI("updown", undefined); - - if (process.env.HWVERSION == 1) - btn = setWatch(fn, BTN2); - else - btn = setWatch(fn, BTN1); -} - -function clearButton() { - if (btn !== undefined) { - clearWatch(btn); - btn = undefined; - } + Bangle.setUI({mode:"custom", + back:fn, + swipe:(lr,ud) => { + if (lr == 1) { + // HOUR data starts at index 0, DAY data starts at index 1 + chart_index = Math.max((chart_label == "DAY") ? -3 : -4, chart_index - 1); + } else if (lr<0) { + chart_index = Math.min(data_len - 5, chart_index + 1); + } else { + return fn(); + } + drawBarChart(); + }}); } Bangle.loadWidgets(); Bangle.drawWidgets(); - menuMain(); diff --git a/apps/health/metadata.json b/apps/health/metadata.json index a038f67b5..82ae09356 100644 --- a/apps/health/metadata.json +++ b/apps/health/metadata.json @@ -1,7 +1,7 @@ { "id": "health", "name": "Health Tracking", - "version": "0.15", + "version": "0.16", "description": "Logs health data and provides an app to view it", "icon": "app.png", "tags": "tool,system,health", From 0bd33f0efed76d9a1d1996cd2e5055dca0d7fdb9 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Fri, 25 Nov 2022 18:34:57 +0100 Subject: [PATCH 095/107] circlesclock: improved clkinfo handling --- apps/circlesclock/ChangeLog | 1 + apps/circlesclock/app.js | 13 +++++++------ apps/circlesclock/metadata.json | 2 +- apps/circlesclock/settings.js | 1 - 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/circlesclock/ChangeLog b/apps/circlesclock/ChangeLog index ea7266442..f840ca447 100644 --- a/apps/circlesclock/ChangeLog +++ b/apps/circlesclock/ChangeLog @@ -31,3 +31,4 @@ 0.16: Fix const error Use widget_utils if available 0.17: Load circles from clkinfo +0.18: Improved clkinfo handling diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index d4a170ce8..b5770df6c 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -690,17 +690,18 @@ function drawClkInfo(index, w) { return; } var item = info.items[circleItemNum[index-1]]; - //TODO do hide()+get() here item.show(); item.hide(); - item=item.get(); - var img = item.img; + var data=item.get(); + var img = data.img; + var percent = 1; //fill up if no range + var txt = data.text; if(!img) img = info.img; - let percent = (item.v-item.min) / item.max; - if(isNaN(percent)) percent = 1; //fill it up + if(item.hasRange) percent = (data.v-data.min) / (data.max-data.min); + if(item.short) txt = item.short; drawGauge(w, h3, percent, color); drawInnerCircleAndTriangle(w); - writeCircleText(w, item.text); + writeCircleText(w, txt); g.setColor(getCircleIconColor(type, color, percent)) .drawImage(img, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24}); } diff --git a/apps/circlesclock/metadata.json b/apps/circlesclock/metadata.json index 490d6c4fb..fb743ae4c 100644 --- a/apps/circlesclock/metadata.json +++ b/apps/circlesclock/metadata.json @@ -1,7 +1,7 @@ { "id": "circlesclock", "name": "Circles clock", "shortName":"Circles clock", - "version":"0.17", + "version":"0.18", "description": "A clock with three or four circles for different data at the bottom in a probably familiar style", "icon": "app.png", "screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}], diff --git a/apps/circlesclock/settings.js b/apps/circlesclock/settings.js index ca26cb295..2006790b8 100644 --- a/apps/circlesclock/settings.js +++ b/apps/circlesclock/settings.js @@ -17,7 +17,6 @@ var valuesCircleTypes = ["empty","weather", "sunprogress"]; var namesCircleTypes = ["empty","weather", "sun"]; clock_info.load().forEach(e=>{ - //TODO filter for hasRange and other if(!e.items.length || !e.items[0].name) { //suppose unnamed are varying (like timers or events), pick the first item = e.items[0]; From 80bd13c1a2fc2a5387bc676b05f56260ca6a4c99 Mon Sep 17 00:00:00 2001 From: glemco <32201227+glemco@users.noreply.github.com> Date: Fri, 25 Nov 2022 19:13:38 +0100 Subject: [PATCH 096/107] Fixed short label and dynamic --- apps/circlesclock/app.js | 7 +++---- apps/circlesclock/settings.js | 4 +--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index b5770df6c..80fda279e 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -659,9 +659,8 @@ function reloadMenu() { let parts = settings['circle'+i].split("/"); let infoName = parts[0], itemName = parts[1]; let infoNum = menu.findIndex(e=>e.name==infoName); - let itemNum = 0; - //suppose unnamed are varying (like timers or events), pick the first - if(itemName) + let itemNum = 0; //get first if dynamic + if(!menu[infoNum].dynamic) itemNum = menu[infoNum].items.findIndex(it=>it.name==itemName); circleInfoNum[i-1] = infoNum; circleItemNum[i-1] = itemNum; @@ -698,7 +697,7 @@ function drawClkInfo(index, w) { var txt = data.text; if(!img) img = info.img; if(item.hasRange) percent = (data.v-data.min) / (data.max-data.min); - if(item.short) txt = item.short; + if(data.short) txt = data.short; drawGauge(w, h3, percent, color); drawInnerCircleAndTriangle(w); writeCircleText(w, txt); diff --git a/apps/circlesclock/settings.js b/apps/circlesclock/settings.js index 2006790b8..9a8b43c49 100644 --- a/apps/circlesclock/settings.js +++ b/apps/circlesclock/settings.js @@ -17,9 +17,7 @@ var valuesCircleTypes = ["empty","weather", "sunprogress"]; var namesCircleTypes = ["empty","weather", "sun"]; clock_info.load().forEach(e=>{ - if(!e.items.length || !e.items[0].name) { - //suppose unnamed are varying (like timers or events), pick the first - item = e.items[0]; + if(e.dynamic) { valuesCircleTypes = valuesCircleTypes.concat([e.name+"/"]); namesCircleTypes = namesCircleTypes.concat([e.name]); } else { From 92bf3579f208b05d83e7318480e3b5c7cd9f4664 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Wed, 30 Nov 2022 18:34:29 +0100 Subject: [PATCH 097/107] circlesclock: using the weather clkinfo instead of reading from the file --- apps/circlesclock/ChangeLog | 2 +- apps/circlesclock/app.js | 56 +++++++++++++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/apps/circlesclock/ChangeLog b/apps/circlesclock/ChangeLog index f840ca447..8ec578fb6 100644 --- a/apps/circlesclock/ChangeLog +++ b/apps/circlesclock/ChangeLog @@ -31,4 +31,4 @@ 0.16: Fix const error Use widget_utils if available 0.17: Load circles from clkinfo -0.18: Improved clkinfo handling +0.18: Improved clkinfo handling and using it for the weather circle diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index 80fda279e..bb537a819 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -106,6 +106,10 @@ let circleItemNum = [ 2, // circle3 3, // circle4 ]; +let weatherCircleNum = 0; +let weatherCircleDataNum = 0; +let weatherCircleCondNum = 0; +let weatherCircleTempNum = 0; function hideWidgets() { /* @@ -323,6 +327,49 @@ function getImage(graphic, color) { } function drawWeather(w) { + if (!w) w = getCircleXPosition("weather"); + let weatherInfo = menu[weatherCircleNum]; + let weatherCond = weatherCircleCondNum >= 0? weatherInfo.items[weatherCircleCondNum]: undefined; + let weatherData = weatherCircleDataNum >= 0? weatherInfo.items[weatherCircleDataNum]: undefined; + let weatherTemp = weatherCircleTempNum >= 0? weatherInfo.items[weatherCircleTempNum]: undefined; + let color = getCircleColor("weather"); + let percent = 0; + let data = settings.weatherCircleData; + let tempString = "?", icon = undefined; + + if(weatherCond) { + weatherCond.show() + weatherCond.hide() + icon = weatherCond.get().img; + } + if(weatherTemp) { + weatherTemp.show() + weatherTemp.hide() + tempString = weatherTemp.get().text; + } + + drawCircleBackground(w); + + if(weatherData) { + weatherData.show(); + weatherData.hide(); + let data = weatherData.get(); + if(weatherData.hasRange) percent = (data.v-data.min) / (data.max-data.min); + drawGauge(w, h3, percent, color); + } + + drawInnerCircleAndTriangle(w); + + writeCircleText(w, tempString); + + if(icon) { + g.setColor(getCircleIconColor("weather", color, percent)) + .drawImage(icon, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24}); + } else { + g.drawString("?", w, h3 + radiusOuter); + } +} +function drawWeatherOld(w) { if (!w) w = getCircleXPosition("weather"); let weather = getWeather(); let tempString = weather ? locale.temp(weather.temp - 273.15) : undefined; @@ -664,6 +711,11 @@ function reloadMenu() { itemNum = menu[infoNum].items.findIndex(it=>it.name==itemName); circleInfoNum[i-1] = infoNum; circleItemNum[i-1] = itemNum; + } else if(settings['circle'+i] == "weather") { + weatherCircleNum = menu.findIndex(e=>e.name.toLowerCase() == "weather"); + weatherCircleDataNum = menu[weatherCircleNum].items.findIndex(it=>it.name==settings.weatherCircleData); + weatherCircleCondNum = menu[weatherCircleNum].items.findIndex(it=>it.name=="condition"); + weatherCircleTempNum = menu[weatherCircleNum].items.findIndex(it=>it.name=="temperature"); } } //reload periodically for changes? @@ -684,11 +736,11 @@ function drawClkInfo(index, w) { if (!w) w = getCircleXPosition(type); drawCircleBackground(w); const color = getCircleColor(type); - if(!info || !info.items.length) { + var item = info.items[circleItemNum[index-1]]; + if(!info || !item) { drawEmpty(info? info.img : null, w, color); return; } - var item = info.items[circleItemNum[index-1]]; item.show(); item.hide(); var data=item.get(); From bd6f3c93048b61e6b314db08f2ddfa9cac977084 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Thu, 1 Dec 2022 08:48:10 +0100 Subject: [PATCH 098/107] circlesclock: optional legacy weather icons --- apps/circlesclock/app.js | 10 ++++++++-- apps/circlesclock/settings.js | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index bb537a819..ee6741398 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -336,11 +336,17 @@ function drawWeather(w) { let percent = 0; let data = settings.weatherCircleData; let tempString = "?", icon = undefined; + let scale = 16/24; //our icons are 16x16 while clkinfo's are 24x24 if(weatherCond) { weatherCond.show() weatherCond.hide() - icon = weatherCond.get().img; + let data = weatherCond.get() + if(settings.legacyWeatherIcons) { //may disappear in future + icon = getWeatherIconByCode(data.v); + scale = 1; + } else + icon = data.img; } if(weatherTemp) { weatherTemp.show() @@ -364,7 +370,7 @@ function drawWeather(w) { if(icon) { g.setColor(getCircleIconColor("weather", color, percent)) - .drawImage(icon, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24}); + .drawImage(icon, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: scale}); } else { g.drawString("?", w, h3 + radiusOuter); } diff --git a/apps/circlesclock/settings.js b/apps/circlesclock/settings.js index 9a8b43c49..2ab655f01 100644 --- a/apps/circlesclock/settings.js +++ b/apps/circlesclock/settings.js @@ -82,6 +82,12 @@ }, onchange: x => save('updateInterval', x), }, + //TODO deprecated local icons, may disappear in future + /*LANG*/'legacy weather icons': { + value: !!settings.legacyWeatherIcons, + format: () => (settings.legacyWeatherIcons ? 'Yes' : 'No'), + onchange: x => save('legacyWeatherIcons', x), + }, /*LANG*/'show big weather': { value: !!settings.showBigWeather, format: () => (settings.showBigWeather ? 'Yes' : 'No'), From 546cfa79962181dab1c16773338ed48776245a0a Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Thu, 1 Dec 2022 13:47:48 +0000 Subject: [PATCH 099/107] fix grey maps --- apps/openstmap/interface.html | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/openstmap/interface.html b/apps/openstmap/interface.html index 4a4bd5336..0bf2268a4 100644 --- a/apps/openstmap/interface.html +++ b/apps/openstmap/interface.html @@ -176,12 +176,14 @@ TODO: `; - let map = L.map(`tile-map-${mapNumber}`); - L.tileLayer(PREVIEWTILELAYER, { - maxZoom: 18 - }).addTo(map); - let marker = new L.marker(latlon).addTo(map); - map.fitBounds(latlon.toBounds(2000/*meters*/), {animation: false}); + setTimeout(function() { + let map = L.map(`tile-map-${mapNumber}`); + L.tileLayer(PREVIEWTILELAYER, { + maxZoom: 18 + }).addTo(map); + let marker = new L.marker(latlon).addTo(map); + map.fitBounds(latlon.toBounds(2000/*meters*/), {animation: false}); + }, 100); } resolve(); }); From d547ec97a6af647387a3d749005e6b12f959e19d Mon Sep 17 00:00:00 2001 From: Marco H Date: Thu, 1 Dec 2022 15:16:50 +0100 Subject: [PATCH 100/107] Update clkinfo.js --- apps/weather/clkinfo.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/weather/clkinfo.js b/apps/weather/clkinfo.js index 8cdc37be0..339ff39c3 100644 --- a/apps/weather/clkinfo.js +++ b/apps/weather/clkinfo.js @@ -30,11 +30,18 @@ name: "Weather", img: atob("GBiBAf+///u5//n7//8f/9wHP8gDf/gB//AB/7AH/5AcP/AQH/DwD/uAD84AD/4AA/wAAfAAAfAAAfAAAfgAA/////+bP/+zf/+zfw=="), items: [ + { + name: "conditionWithTemperature", + get: () => ({ text: weather.temp, img: weatherIcon(weather.code), + v: parseInt(weather.temp), min: -30, max: 55}), + show: function() { this.emit("redraw"); }, + hide: function () {} + }, { name: "condition", get: () => ({ text: weather.txt, img: weatherIcon(weather.code), v: weather.code}), - show: function() { weatherItems.items[0].emit("redraw"); }, + show: function() { this.emit("redraw"); }, hide: function () {} }, { @@ -42,7 +49,7 @@ hasRange : true, get: () => ({ text: weather.temp, img: atob("GBiBAAA8AAB+AADnAADDAADDAADDAADDAADDAADbAADbAADbAADbAADbAADbAAHbgAGZgAM8wAN+wAN+wAM8wAGZgAHDgAD/AAA8AA=="), v: parseInt(weather.temp), min: -30, max: 55}), - show: function() { weatherItems.items[1].emit("redraw"); }, + show: function() { this.emit("redraw"); }, hide: function () {} }, { @@ -50,7 +57,7 @@ hasRange : true, get: () => ({ text: weather.hum, img: atob("GBiBAAAEAAAMAAAOAAAfAAAfAAA/gAA/gAI/gAY/AAcfAA+AQA+A4B/A4D/B8D/h+D/j+H/n/D/n/D/n/B/H/A+H/AAH/AAD+AAA8A=="), v: parseInt(weather.hum), min: 0, max: 100}), - show: function() { weatherItems.items[2].emit("redraw"); }, + show: function() { this.emit("redraw"); }, hide: function () {} }, { @@ -58,7 +65,7 @@ hasRange : true, get: () => ({ text: weather.wind, img: atob("GBiBAAHgAAPwAAYYAAwYAAwMfAAY/gAZh3/xg//hgwAAAwAABg///g//+AAAAAAAAP//wH//4AAAMAAAMAAYMAAYMAAMcAAP4AADwA=="), v: parseInt(weather.wind), min: 0, max: 118}), - show: function() { weatherItems.items[3].emit("redraw"); }, + show: function() { this.emit("redraw"); }, hide: function () {} }, ] From cb84cce2032d776bd0b0df16b6e18a244de36bac Mon Sep 17 00:00:00 2001 From: Marco H Date: Thu, 1 Dec 2022 15:17:18 +0100 Subject: [PATCH 101/107] Update ChangeLog --- apps/weather/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/weather/ChangeLog b/apps/weather/ChangeLog index d8a7440c5..aefb903b9 100644 --- a/apps/weather/ChangeLog +++ b/apps/weather/ChangeLog @@ -16,3 +16,4 @@ 0.17: Added clkinfo for clocks. 0.18: Added hasRange to clkinfo. 0.19: Added weather condition to clkinfo. +0.20: Added weather condition with temperature to clkinfo. From d5fde1f046642032da280219f411c5ec3a1e8ace Mon Sep 17 00:00:00 2001 From: Marco H Date: Thu, 1 Dec 2022 15:17:27 +0100 Subject: [PATCH 102/107] Update metadata.json --- apps/weather/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/weather/metadata.json b/apps/weather/metadata.json index c679bdc2e..dd8b6c293 100644 --- a/apps/weather/metadata.json +++ b/apps/weather/metadata.json @@ -1,7 +1,7 @@ { "id": "weather", "name": "Weather", - "version": "0.19", + "version": "0.20", "description": "Show Gadgetbridge weather report", "icon": "icon.png", "screenshots": [{"url":"screenshot.png"}], From 6ea11508ef925e2230fea24380458e8d0718921d Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Thu, 1 Dec 2022 19:26:40 +0100 Subject: [PATCH 103/107] use widget_utils .hide() --- apps/aiclock/ChangeLog | 3 ++- apps/aiclock/aiclock.app.js | 3 +-- apps/aiclock/metadata.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/aiclock/ChangeLog b/apps/aiclock/ChangeLog index 31c55aef1..96b389f6e 100644 --- a/apps/aiclock/ChangeLog +++ b/apps/aiclock/ChangeLog @@ -1,3 +1,4 @@ 0.01: New app! 0.02: Design improvements and fixes. -0.03: Indicate battery level through line occurrence. \ No newline at end of file +0.03: Indicate battery level through line occurrence. +0.04: Use widget_utils module. diff --git a/apps/aiclock/aiclock.app.js b/apps/aiclock/aiclock.app.js index dbd053f2c..5d4e98fcd 100644 --- a/apps/aiclock/aiclock.app.js +++ b/apps/aiclock/aiclock.app.js @@ -215,8 +215,7 @@ Bangle.loadWidgets(); * so we will blank out the draw() functions of each widget and change the * area to the top bar doesn't get cleared. */ -for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} - +require('widget_utils').hide(); // Clear the screen once, at startup and draw clock g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear(); draw(); diff --git a/apps/aiclock/metadata.json b/apps/aiclock/metadata.json index 2124b1b7e..0da23e8ca 100644 --- a/apps/aiclock/metadata.json +++ b/apps/aiclock/metadata.json @@ -3,7 +3,7 @@ "name": "AI Clock", "shortName":"AI Clock", "icon": "aiclock.png", - "version":"0.03", + "version":"0.04", "readme": "README.md", "supports": ["BANGLEJS2"], "description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.", From 481ddb6a225751689f62f9fc5374138aee0ffa76 Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Thu, 1 Dec 2022 21:03:15 +0100 Subject: [PATCH 104/107] add more /*LANG*/ to some of the most installed apps --- apps/about/ChangeLog | 1 + apps/about/app-bangle1.js | 10 +++--- apps/about/app-bangle2.js | 10 +++--- apps/about/metadata.json | 2 +- apps/alarm/ChangeLog | 1 + apps/alarm/app.js | 2 +- apps/alarm/metadata.json | 2 +- apps/health/ChangeLog | 1 + apps/health/app.js | 14 ++++----- apps/health/metadata.json | 2 +- apps/sched/ChangeLog | 1 + apps/sched/clkinfo.js | 4 +-- apps/sched/metadata.json | 2 +- apps/setting/' | 62 ++++++++++++++++++++++++++++++++++++++ apps/setting/ChangeLog | 1 + apps/setting/metadata.json | 2 +- apps/setting/settings.js | 17 ++++++----- 17 files changed, 101 insertions(+), 33 deletions(-) create mode 100644 apps/setting/' diff --git a/apps/about/ChangeLog b/apps/about/ChangeLog index ffe9de081..e236e4b34 100644 --- a/apps/about/ChangeLog +++ b/apps/about/ChangeLog @@ -11,3 +11,4 @@ 0.11: Bangle.js2: New pixels, btn1 to exit 0.12: Actual pixels as of 29th Nov 2021 0.13: Bangle.js 2: Use setUI to add software back button +0.14: Add automatic translation of more strings diff --git a/apps/about/app-bangle1.js b/apps/about/app-bangle1.js index 28a292376..dd94c1e84 100644 --- a/apps/about/app-bangle1.js +++ b/apps/about/app-bangle1.js @@ -11,8 +11,8 @@ g.drawString("BANGLEJS.COM",120,y-4); } else { y=-(4+h); // small screen, start right at top } -g.drawString("Powered by Espruino",0,y+=4+h); -g.drawString("Version "+ENV.VERSION,0,y+=h); +g.drawString(/*LANG*/"Powered by Espruino",0,y+=4+h); +g.drawString(/*LANG*/"Version "+ENV.VERSION,0,y+=h); g.drawString("Commit "+ENV.GIT_COMMIT,0,y+=h); function getVersion(name,file) { var j = s.readJSON(file,1); @@ -24,9 +24,9 @@ getVersion("Launcher","launch.info"); getVersion("Settings","setting.info"); y+=h; -g.drawString(MEM.total+" JS Variables available",0,y+=h); -g.drawString("Storage: "+(require("Storage").getFree()>>10)+"k free",0,y+=h); -if (ENV.STORAGE) g.drawString(" "+(ENV.STORAGE>>10)+"k total",0,y+=h); +g.drawString(MEM.total+/*LANG*/" JS Variables available",0,y+=h); +g.drawString("Storage: "+(require("Storage").getFree()>>10)+/*LANG*/"k free",0,y+=h); +if (ENV.STORAGE) g.drawString(" "+(ENV.STORAGE>>10)+/*LANG*/"k total",0,y+=h); if (ENV.SPIFLASH) g.drawString("SPI Flash: "+(ENV.SPIFLASH>>10)+"k",0,y+=h); g.setFontAlign(0,-1); g.flip(); diff --git a/apps/about/app-bangle2.js b/apps/about/app-bangle2.js index 471b0670f..ccffd183f 100644 --- a/apps/about/app-bangle2.js +++ b/apps/about/app-bangle2.js @@ -35,17 +35,17 @@ function drawInfo() { g.setFont("4x6").setFontAlign(0,0).drawString("BANGLEJS.COM",W-30,56); var h=8, y = 24-h; g.setFont("6x8").setFontAlign(-1,-1); - g.drawString("Powered by Espruino",0,y+=4+h); - g.drawString("Version "+ENV.VERSION,0,y+=h); + g.drawString(/*LANG*/"Powered by Espruino",0,y+=4+h); + g.drawString(/*LANG*/"Version "+ENV.VERSION,0,y+=h); g.drawString("Commit "+ENV.GIT_COMMIT,0,y+=h); getVersion("Bootloader","boot.info"); getVersion("Launcher","launch.info"); getVersion("Settings","setting.info"); - g.drawString(MEM.total+" JS Vars",0,y+=h); - g.drawString("Storage: "+(require("Storage").getFree()>>10)+"k free",0,y+=h); - if (ENV.STORAGE) g.drawString(" "+(ENV.STORAGE>>10)+"k total",0,y+=h); + g.drawString(MEM.total+/*LANG*/" JS Vars",0,y+=h); + g.drawString("Storage: "+(require("Storage").getFree()>>10)+/*LANG*/"k free",0,y+=h); + if (ENV.STORAGE) g.drawString(" "+(ENV.STORAGE>>10)+/*LANG*/"k total",0,y+=h); if (ENV.SPIFLASH) g.drawString("SPI Flash: "+(ENV.SPIFLASH>>10)+"k",0,y+=h); imageTop = y+h; imgScroll = imgHeight-imageTop; diff --git a/apps/about/metadata.json b/apps/about/metadata.json index 648576576..52cd37b7d 100644 --- a/apps/about/metadata.json +++ b/apps/about/metadata.json @@ -1,7 +1,7 @@ { "id": "about", "name": "About", - "version": "0.13", + "version": "0.14", "description": "Bangle.js About page - showing software version, stats, and a collaborative mural from the Bangle.js KickStarter backers", "icon": "app.png", "tags": "tool,system", diff --git a/apps/alarm/ChangeLog b/apps/alarm/ChangeLog index 6e648d42a..6ce6147ca 100644 --- a/apps/alarm/ChangeLog +++ b/apps/alarm/ChangeLog @@ -35,4 +35,5 @@ Add option for auto-delete a timer after it expires 0.33: Allow hiding timers&alarms 0.34: Add "Confirm" option to alarm/timer edit menus +0.35: Add automatic translation of more strings diff --git a/apps/alarm/app.js b/apps/alarm/app.js index efcabfb6d..1414c0b90 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -183,7 +183,7 @@ function decodeDOW(alarm) { .map((day, index) => alarm.dow & (1 << (index + firstDayOfWeek)) ? day : "_") .join("") .toLowerCase() - : "Once" + : /*LANG*/"Once" } function showEditRepeatMenu(repeat, dow, dowChangeCallback) { diff --git a/apps/alarm/metadata.json b/apps/alarm/metadata.json index 2e4ea9f94..a02985851 100644 --- a/apps/alarm/metadata.json +++ b/apps/alarm/metadata.json @@ -2,7 +2,7 @@ "id": "alarm", "name": "Alarms & Timers", "shortName": "Alarms", - "version": "0.34", + "version": "0.35", "description": "Set alarms and timers on your Bangle", "icon": "app.png", "tags": "tool,alarm,widget", diff --git a/apps/health/ChangeLog b/apps/health/ChangeLog index 25909891a..fc8f2c950 100644 --- a/apps/health/ChangeLog +++ b/apps/health/ChangeLog @@ -15,3 +15,4 @@ 0.14: Move settings 0.15: Fix charts (fix #1366) 0.16: Code tidyup, add back button in top left of health app graphs +0.17: Add automatic translation of bar chart labels diff --git a/apps/health/app.js b/apps/health/app.js index 576e1d02f..844dd7241 100644 --- a/apps/health/app.js +++ b/apps/health/app.js @@ -41,7 +41,7 @@ function stepsPerHour() { var data = new Uint16Array(24); require("health").readDay(new Date(), h=>data[h.hr]+=h.steps); setButton(menuStepCount); - barChart("HOUR", data); + barChart(/*LANG*/"HOUR", data); } function stepsPerDay() { @@ -49,7 +49,7 @@ function stepsPerDay() { var data = new Uint16Array(31); require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps); setButton(menuStepCount); - barChart("DAY", data); + barChart(/*LANG*/"DAY", data); } function hrmPerHour() { @@ -62,7 +62,7 @@ function hrmPerHour() { }); data.forEach((d,i)=>data[i] = d/cnt[i]); setButton(menuHRM); - barChart("HOUR", data); + barChart(/*LANG*/"HOUR", data); } function hrmPerDay() { @@ -75,7 +75,7 @@ function hrmPerDay() { }); data.forEach((d,i)=>data[i] = d/cnt[i]); setButton(menuHRM); - barChart("DAY", data); + barChart(/*LANG*/"DAY", data); } function movementPerHour() { @@ -83,7 +83,7 @@ function movementPerHour() { var data = new Uint16Array(24); require("health").readDay(new Date(), h=>data[h.hr]+=h.movement); setButton(menuMovement); - barChart("HOUR", data); + barChart(/*LANG*/"HOUR", data); } function movementPerDay() { @@ -91,7 +91,7 @@ function movementPerDay() { var data = new Uint16Array(31); require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement); setButton(menuMovement); - barChart("DAY", data); + barChart(/*LANG*/"DAY", data); } // Bar Chart Code @@ -163,7 +163,7 @@ function setButton(fn) { swipe:(lr,ud) => { if (lr == 1) { // HOUR data starts at index 0, DAY data starts at index 1 - chart_index = Math.max((chart_label == "DAY") ? -3 : -4, chart_index - 1); + chart_index = Math.max((chart_label == /*LANG*/"DAY") ? -3 : -4, chart_index - 1); } else if (lr<0) { chart_index = Math.min(data_len - 5, chart_index + 1); } else { diff --git a/apps/health/metadata.json b/apps/health/metadata.json index 82ae09356..d4eab1f38 100644 --- a/apps/health/metadata.json +++ b/apps/health/metadata.json @@ -1,7 +1,7 @@ { "id": "health", "name": "Health Tracking", - "version": "0.16", + "version": "0.17", "description": "Logs health data and provides an app to view it", "icon": "app.png", "tags": "tool,system,health", diff --git a/apps/sched/ChangeLog b/apps/sched/ChangeLog index f2dd54338..711326200 100644 --- a/apps/sched/ChangeLog +++ b/apps/sched/ChangeLog @@ -15,3 +15,4 @@ Fix wrong fallback for buzz pattern 0.13: Ask to delete a timer after stopping it 0.14: Added clkinfo for alarms and timers +0.15: Automatic translation of some string in clkinfo diff --git a/apps/sched/clkinfo.js b/apps/sched/clkinfo.js index 71992dbb8..3bd11f70b 100644 --- a/apps/sched/clkinfo.js +++ b/apps/sched/clkinfo.js @@ -31,7 +31,7 @@ function getAlarmText(a){ if(a.timer) { - if(!a.on) return "off"; + if(!a.on) return /*LANG*/"off"; let time = Math.round(alarm.getTimeToAlarm(a)/(60*1000)); if(time > 60) time = Math.round(time / 60) + "h"; @@ -52,7 +52,7 @@ var img = iconAlarmOn; //get only alarms not created by other apps var alarmItems = { - name: "Alarms", + name: /*LANG*/"Alarms", img: img, dynamic: true, items: alarm.getAlarms().filter(a=>!a.appid) diff --git a/apps/sched/metadata.json b/apps/sched/metadata.json index 4b38ee653..a457d0f44 100644 --- a/apps/sched/metadata.json +++ b/apps/sched/metadata.json @@ -1,7 +1,7 @@ { "id": "sched", "name": "Scheduler", - "version": "0.14", + "version": "0.15", "description": "Scheduling library for alarms and timers", "icon": "app.png", "type": "scheduler", diff --git a/apps/setting/' b/apps/setting/' new file mode 100644 index 000000000..bf78c50b6 --- /dev/null +++ b/apps/setting/' @@ -0,0 +1,62 @@ +0.02: Add support for 30-minute timezones. +0.03: Add support for Welcome app +0.04: Add setting to disable log messages +0.05: Fix Settings json +0.06: Remove distance setting as there's a separate app for Locale now +0.07: Added vibrate as beep workaround +0.08: Add support for app/widget settings +0.09: Move Welcome into App/widget settings +0.10: Added LCD wake-up settings + Adds LCD brightness setting +0.11: Make LCD brightness work after leaving settings +0.12: Fix memory leak (#206) + Bring App settings nearer the top + Move LCD Timeout to wakeup menu +0.13: Fix memory leak for App settings + Make capitalization more consistent + Move LCD Brightness menu into more general LCD menu +0.14: Reduce memory usage when running app settings page +0.15: Reduce memory usage when running default clock chooser (#294) +0.16: Reduce memory usage further when running app settings page +0.17: Remove need for "settings" in appid.info +0.18: Don't overwrite existing settings on app update +0.19: Allow BLE HID settings, add README.md +0.20: Fix set time menu, allow dates to roll over +0.21: Add passkey pairing option (BETA) + Add whitelist option (fix #78) +0.22: Move HID to BLE menu +0.23: Change max time offset to 13 for NZ summer daylight time (NZDT) +0.24: Add Quiet Mode settings +0.25: Move boot.js code into 'boot' app itself +0.26: Use Bangle.softOff if available as this keeps the time +0.27: Add Theme menu +0.28: Update Quiet Mode widget (if present) +0.29: Add Customize to Theme menu +0.30: Move '< Back' to the top of menus +0.31: Remove Bangle 1 settings when running on Bangle 2 +0.32: Fix 'beep' menu on Bangle.js 2 +0.33: Really fix 'beep' menu on Bangle.js 2 this time +0.34: Remove Quiet Mode LCD settings: now handled by Quiet Mode Schedule app +0.35: Change App/Widget settings to 'App Settings' so it fits on Bangle screen +0.36: Added 'Utils' menu with helpful utilities for restoring Bangle.js +0.37: Going into passkey menu now saves settings with passkey +0.38: Restructed menus as per forum discussion +0.39: Fix misbehaving debug info option +0.40: Moved off into Utils, put System after Apps +0.41: Stop users disabling all wake-up methods and locking themselves out (fix #1272) +0.42: Fix theme customizer on new Bangle 2 firmware +0.43: Add some Bangle 1 colours to theme customizer +0.44: Add "Start Week On X" option (#1780) + UI improvements to Locale and Date & Time menu +0.45: Add calibrate battery option +0.46: Fix regression after making 'calibrate battery' only for Bangle.js 2 +0.47: Allow colors to be translated + Improve "Turn Off" user experience +0.48: Allow reading custom themes from files +0.49: Now reloads settings properly after 'Calibrate Battery' +0.50: Add Bangle.js 2 touchscreen calibration - for 2v16 or 2v15 cutting edge builds +0.51: Add setting for configuring a launcher +0.52: Add option for left-handed users +0.53: Ensure that when clock is set, clockHasWidgets is set correctly too +0.54: If setting.json is corrupt, ensure it gets re-written +0.55: More strings tagged for automatic translation. diff --git a/apps/setting/ChangeLog b/apps/setting/ChangeLog index 55b61c46a..bf78c50b6 100644 --- a/apps/setting/ChangeLog +++ b/apps/setting/ChangeLog @@ -59,3 +59,4 @@ 0.52: Add option for left-handed users 0.53: Ensure that when clock is set, clockHasWidgets is set correctly too 0.54: If setting.json is corrupt, ensure it gets re-written +0.55: More strings tagged for automatic translation. diff --git a/apps/setting/metadata.json b/apps/setting/metadata.json index 08544cff6..80db81f65 100644 --- a/apps/setting/metadata.json +++ b/apps/setting/metadata.json @@ -1,7 +1,7 @@ { "id": "setting", "name": "Settings", - "version": "0.54", + "version": "0.55", "description": "A menu for setting up Bangle.js", "icon": "settings.png", "tags": "tool,system", diff --git a/apps/setting/settings.js b/apps/setting/settings.js index 2350a8965..b58615913 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -146,7 +146,7 @@ function showAlertsMenu() { }, /*LANG*/"Quiet Mode": { value: settings.quiet|0, - format: v => ["Off", "Alarms", "Silent"][v%3], + format: v => [/*LANG*/"Off", /*LANG*/"Alarms", /*LANG*/"Silent"][v%3], onchange: v => { settings.quiet = v%3; updateSettings(); @@ -162,9 +162,9 @@ function showAlertsMenu() { function showBLEMenu() { var hidV = [false, "kbmedia", "kb", "com", "joy"]; - var hidN = ["Off", "Kbrd & Media", "Kbrd", "Kbrd & Mouse" ,"Joystick"]; + var hidN = [/*LANG*/"Off", /*LANG*/"Kbrd & Media", /*LANG*/"Kbrd", /*LANG*/"Kbrd & Mouse", /*LANG*/"Joystick"]; E.showMenu({ - '': { 'title': 'Bluetooth' }, + '': { 'title': /*LANG*/'Bluetooth' }, '< Back': ()=>showMainMenu(), /*LANG*/'Make Connectable': ()=>makeConnectable(), /*LANG*/'BLE': { @@ -193,11 +193,11 @@ function showBLEMenu() { } }, /*LANG*/'Passkey BETA': { - value: settings.passkey?settings.passkey:"none", + value: settings.passkey?settings.passkey:/*LANG*/"none", onchange: () => setTimeout(showPasskeyMenu) // graphical_menu redraws after the call }, /*LANG*/'Whitelist': { - value: settings.whitelist?(settings.whitelist.length+" devs"):"off", + value: settings.whitelist?(settings.whitelist.length+/*LANG*/" devs"):/*LANG*/"off", onchange: () => setTimeout(showWhitelistMenu) // graphical_menu redraws after the call } }); @@ -606,7 +606,7 @@ function showUtilMenu() { menu[/*LANG*/'Reset Settings'] = () => { E.showPrompt(/*LANG*/'Reset to Defaults?',{title:/*LANG*/"Settings"}).then((v) => { if (v) { - E.showMessage('Resetting'); + E.showMessage(/*LANG*/'Resetting'); resetSettings(); setTimeout(showMainMenu, 50); } else showUtilMenu(); @@ -824,6 +824,7 @@ function showAppSettings(app) { function showTouchscreenCalibration() { Bangle.setUI(); + require('widget_utils').hide(); // disable touchscreen calibration (passed coords right through) Bangle.setOptions({touchX1: 0, touchY1: 0, touchX2: g.getWidth(), touchY2: g.getHeight() }); @@ -847,7 +848,7 @@ function showTouchscreenCalibration() { g.drawLine(spot[0],spot[1]-32,spot[0],spot[1]+32); g.drawCircle(spot[0],spot[1], 16); var tapsLeft = (1-currentTry)*4+(4-currentCorner); - g.setFont("6x8:2").setFontAlign(0,0).drawString(tapsLeft+" taps\nto go", g.getWidth()/2, g.getHeight()/2); + g.setFont("6x8:2").setFontAlign(0,0).drawString(tapsLeft+/*LANG*/" taps\nto go", g.getWidth()/2, g.getHeight()/2); } function calcCalibration() { @@ -870,7 +871,7 @@ function showTouchscreenCalibration() { var s = storage.readJSON("setting.json",1)||{}; s.touch = calib; storage.writeJSON("setting.json",s); - g.setFont("6x8:2").setFontAlign(0,0).drawString("Calibrated!", g.getWidth()/2, g.getHeight()/2); + g.setFont("6x8:2").setFontAlign(0,0).drawString(/*LANG*/"Calibrated!", g.getWidth()/2, g.getHeight()/2); // now load the main menu again setTimeout(showLCDMenu, 500); } From 525b23a2965a81cfa1c47fcc3654df31b5722db0 Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Thu, 1 Dec 2022 21:08:46 +0100 Subject: [PATCH 105/107] remove accidentally created file --- apps/setting/' | 62 -------------------------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 apps/setting/' diff --git a/apps/setting/' b/apps/setting/' deleted file mode 100644 index bf78c50b6..000000000 --- a/apps/setting/' +++ /dev/null @@ -1,62 +0,0 @@ -0.02: Add support for 30-minute timezones. -0.03: Add support for Welcome app -0.04: Add setting to disable log messages -0.05: Fix Settings json -0.06: Remove distance setting as there's a separate app for Locale now -0.07: Added vibrate as beep workaround -0.08: Add support for app/widget settings -0.09: Move Welcome into App/widget settings -0.10: Added LCD wake-up settings - Adds LCD brightness setting -0.11: Make LCD brightness work after leaving settings -0.12: Fix memory leak (#206) - Bring App settings nearer the top - Move LCD Timeout to wakeup menu -0.13: Fix memory leak for App settings - Make capitalization more consistent - Move LCD Brightness menu into more general LCD menu -0.14: Reduce memory usage when running app settings page -0.15: Reduce memory usage when running default clock chooser (#294) -0.16: Reduce memory usage further when running app settings page -0.17: Remove need for "settings" in appid.info -0.18: Don't overwrite existing settings on app update -0.19: Allow BLE HID settings, add README.md -0.20: Fix set time menu, allow dates to roll over -0.21: Add passkey pairing option (BETA) - Add whitelist option (fix #78) -0.22: Move HID to BLE menu -0.23: Change max time offset to 13 for NZ summer daylight time (NZDT) -0.24: Add Quiet Mode settings -0.25: Move boot.js code into 'boot' app itself -0.26: Use Bangle.softOff if available as this keeps the time -0.27: Add Theme menu -0.28: Update Quiet Mode widget (if present) -0.29: Add Customize to Theme menu -0.30: Move '< Back' to the top of menus -0.31: Remove Bangle 1 settings when running on Bangle 2 -0.32: Fix 'beep' menu on Bangle.js 2 -0.33: Really fix 'beep' menu on Bangle.js 2 this time -0.34: Remove Quiet Mode LCD settings: now handled by Quiet Mode Schedule app -0.35: Change App/Widget settings to 'App Settings' so it fits on Bangle screen -0.36: Added 'Utils' menu with helpful utilities for restoring Bangle.js -0.37: Going into passkey menu now saves settings with passkey -0.38: Restructed menus as per forum discussion -0.39: Fix misbehaving debug info option -0.40: Moved off into Utils, put System after Apps -0.41: Stop users disabling all wake-up methods and locking themselves out (fix #1272) -0.42: Fix theme customizer on new Bangle 2 firmware -0.43: Add some Bangle 1 colours to theme customizer -0.44: Add "Start Week On X" option (#1780) - UI improvements to Locale and Date & Time menu -0.45: Add calibrate battery option -0.46: Fix regression after making 'calibrate battery' only for Bangle.js 2 -0.47: Allow colors to be translated - Improve "Turn Off" user experience -0.48: Allow reading custom themes from files -0.49: Now reloads settings properly after 'Calibrate Battery' -0.50: Add Bangle.js 2 touchscreen calibration - for 2v16 or 2v15 cutting edge builds -0.51: Add setting for configuring a launcher -0.52: Add option for left-handed users -0.53: Ensure that when clock is set, clockHasWidgets is set correctly too -0.54: If setting.json is corrupt, ensure it gets re-written -0.55: More strings tagged for automatic translation. From 1e9b3a6d9a095844dd5a3081c8296d038b6b6195 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 2 Dec 2022 09:04:03 +0000 Subject: [PATCH 106/107] lightswitch 0.06: Fix issue where .draw was being called by reference (not allowing widgets to be hidden) ref https://github.com/espruino/BangleApps/pull/2334 --- apps/lightswitch/ChangeLog | 1 + apps/lightswitch/metadata.json | 2 +- apps/lightswitch/widget.js | 22 +++++++++++----------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/lightswitch/ChangeLog b/apps/lightswitch/ChangeLog index 4c89bae76..c4aeb2c1e 100644 --- a/apps/lightswitch/ChangeLog +++ b/apps/lightswitch/ChangeLog @@ -3,3 +3,4 @@ 0.03: Settings page now uses built-in min/max/wrap (fix #1607) 0.04: Add masking widget input to other apps (using espruino/Espruino#2151), add a oversize option to increase the touch area. 0.05: Prevent drawing into app area. +0.06: Fix issue where .draw was being called by reference (not allowing widgets to be hidden) diff --git a/apps/lightswitch/metadata.json b/apps/lightswitch/metadata.json index b8da2f759..d1a8d6e2a 100644 --- a/apps/lightswitch/metadata.json +++ b/apps/lightswitch/metadata.json @@ -2,7 +2,7 @@ "id": "lightswitch", "name": "Light Switch Widget", "shortName": "Light Switch", - "version": "0.05", + "version": "0.06", "description": "A fast way to switch LCD backlight on/off, change the brightness and show the lock status. All in one widget.", "icon": "images/app.png", "screenshots": [ diff --git a/apps/lightswitch/widget.js b/apps/lightswitch/widget.js index d9d4d421d..9eb488aca 100644 --- a/apps/lightswitch/widget.js +++ b/apps/lightswitch/widget.js @@ -224,28 +224,20 @@ // main widget function // // display and setup/reset function - draw: function(locked) { + draw: function() { // setup shortcut to this widget var w = WIDGETS.lightswitch; - // set lcd brightness on unlocking - // all other cases are catched by the boot file - if (locked === false) Bangle.setLCDBrightness(w.isOn ? w.value : 0); - // read lock status - locked = Bangle.isLocked(); + var locked = Bangle.isLocked(); // remove listeners to prevent uncertainties - Bangle.removeListener("lock", w.draw); Bangle.removeListener("touch", w.touchListener); Bangle.removeListener("tap", require("lightswitch.js").tapListener); // draw widget icon w.drawIcon(locked); - // add lock listener - Bangle.on("lock", w.draw); - // add touch listener to control the light depending on settings at first position if (w.touchOn === "always" || !global.__FILE__ || w.touchOn.includes(__FILE__) || @@ -259,7 +251,15 @@ w = undefined; } }); + + Bangle.on("lock", locked => { + var w = WIDGETS.lightswitch; + // set lcd brightness on unlocking + // all other cases are catched by the boot file + if (locked === false) Bangle.setLCDBrightness(w.isOn ? w.value : 0); + w.draw() + }); // clear variable - settings = undefined; + delete settings; })() From 11e5dc590585b70e0786fc7e15999b5c3bc62b5f Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 2 Dec 2022 16:31:20 +0000 Subject: [PATCH 107/107] messagesgui 0.57: Fix "unread Timeout" = off (previously defaulted to 60s) --- apps/messagegui/ChangeLog | 1 + apps/messagegui/app.js | 7 +++---- apps/messagegui/metadata.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/messagegui/ChangeLog b/apps/messagegui/ChangeLog index 0fd43347d..854979d18 100644 --- a/apps/messagegui/ChangeLog +++ b/apps/messagegui/ChangeLog @@ -78,3 +78,4 @@ 0.55: Rename to messagegui, move global message handling library to message module Move widget to widmessage 0.56: Fix handling of music messages +0.57: Fix "unread Timeout" = off (previously defaulted to 60s) diff --git a/apps/messagegui/app.js b/apps/messagegui/app.js index 50dc9182a..f6f7779eb 100644 --- a/apps/messagegui/app.js +++ b/apps/messagegui/app.js @@ -446,10 +446,9 @@ require("messages").toggleWidget(false); Bangle.drawWidgets(); setTimeout(() => { - var unreadTimeoutMillis = (settings.unreadTimeout || 60) * 1000; - if (unreadTimeoutMillis) { - unreadTimeout = setTimeout(load, unreadTimeoutMillis); - } + if (!isFinite(settings.unreadTimeout)) settings.unreadTimeout=60; + if (settings.unreadTimeout) + unreadTimeout = setTimeout(load, settings.unreadTimeout*1000); // only openMusic on launch if music is new var newMusic = MESSAGES.some(m => m.id === "music" && m.new); checkMessages({ clockIfNoMsg: 0, clockIfAllRead: 0, showMsgIfUnread: 1, openMusic: newMusic && settings.openMusic }); diff --git a/apps/messagegui/metadata.json b/apps/messagegui/metadata.json index 361ce9b3d..b3e70d5d4 100644 --- a/apps/messagegui/metadata.json +++ b/apps/messagegui/metadata.json @@ -1,7 +1,7 @@ { "id": "messagegui", "name": "Message UI", - "version": "0.56", + "version": "0.57", "description": "Default app to display notifications from iOS and Gadgetbridge/Android", "icon": "app.png", "type": "app",