diff --git a/apps/_example_app/README.md b/apps/_example_app/README.md index dc139bc9a..eff3919c6 100644 --- a/apps/_example_app/README.md +++ b/apps/_example_app/README.md @@ -1,5 +1,10 @@ # App Name +More information on making apps: + +* http://www.espruino.com/Bangle.js+First+App +* http://www.espruino.com/Bangle.js+App+Loader + Describe the app... Add screen shots (if possible) to the app folder and link then into this file with ![](.png) diff --git a/apps/_example_clkinfo/ChangeLog b/apps/_example_clkinfo/ChangeLog new file mode 100644 index 000000000..4c21f3ace --- /dev/null +++ b/apps/_example_clkinfo/ChangeLog @@ -0,0 +1 @@ +0.01: New Widget! diff --git a/apps/_example_clkinfo/README.md b/apps/_example_clkinfo/README.md new file mode 100644 index 000000000..3d5970e39 --- /dev/null +++ b/apps/_example_clkinfo/README.md @@ -0,0 +1,27 @@ +# Clock Info Name + +More info on making Clock Infos and what they are: http://www.espruino.com/Bangle.js+Clock+Info + +Describe the clock info... + +Add screen shots (if possible) to the app folder and link then into this file with ![](.png) + +## Usage + +Describe how to use it + +## Features + +Name the function + +## Controls + +Name the buttons and what they are used for + +## Requests + +Name who should be contacted for support/update requests + +## Creator + +Your name diff --git a/apps/_example_clkinfo/clkinfo.js b/apps/_example_clkinfo/clkinfo.js new file mode 100644 index 000000000..b653709ce --- /dev/null +++ b/apps/_example_clkinfo/clkinfo.js @@ -0,0 +1,16 @@ +(function() { + return { + name: "Bangle", + // img: 24x24px image for this list of items. The default "Bangle" list has its own image so this is not needed + items: [ + { name : "Item1", + get : function() { return { text : "TextOfItem1", + // v : 10, min : 0, max : 100, - optional + img : atob("GBiBAAAAAAAAAAAYAAD/AAOBwAYAYAwAMAgAEBgAGBAACBCBCDHDjDCBDBAACBAACBhCGAh+EAwYMAYAYAOBwAD/AAAYAAAAAAAAAA==") }}, + show : function() {}, + hide : function() {}, + // run : function() {} optional (called when tapped) + } + ] + }; +}) // must not have a semi-colon! \ No newline at end of file diff --git a/apps/_example_widget/widget.png b/apps/_example_clkinfo/icon.png similarity index 100% rename from apps/_example_widget/widget.png rename to apps/_example_clkinfo/icon.png diff --git a/apps/_example_clkinfo/metadata.json b/apps/_example_clkinfo/metadata.json new file mode 100644 index 000000000..83b8184d8 --- /dev/null +++ b/apps/_example_clkinfo/metadata.json @@ -0,0 +1,14 @@ +{ "id": "7chname", + "name": "My clock info's human readable name", + "shortName":"Short Name", + "version":"0.01", + "description": "A detailed description of my clock info", + "icon": "icon.png", + "type": "clkinfo", + "tags": "clkinfo", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"7chname.clkinfo.js","url":"clkinfo.js"} + ] +} diff --git a/apps/_example_widget/README.md b/apps/_example_widget/README.md index a909e9e7e..c786d3be8 100644 --- a/apps/_example_widget/README.md +++ b/apps/_example_widget/README.md @@ -1,5 +1,7 @@ # Widget Name +More info on making Widgets and what they are: http://www.espruino.com/Bangle.js+Widgets + Describe the app... Add screen shots (if possible) to the app folder and link then into this file with ![](.png) diff --git a/apps/_example_widget/icon.png b/apps/_example_widget/icon.png new file mode 100644 index 000000000..582cb2e08 Binary files /dev/null and b/apps/_example_widget/icon.png differ diff --git a/apps/_example_widget/metadata.json b/apps/_example_widget/metadata.json index ad4b7537d..8dc7c75c6 100644 --- a/apps/_example_widget/metadata.json +++ b/apps/_example_widget/metadata.json @@ -3,7 +3,7 @@ "shortName":"Short Name", "version":"0.01", "description": "A detailed description of my great widget", - "icon": "widget.png", + "icon": "icon.png", "type": "widget", "tags": "widget", "supports" : ["BANGLEJS2"], diff --git a/apps/_example_widget/widget.js b/apps/_example_widget/widget.js index 226aea589..198be7b07 100644 --- a/apps/_example_widget/widget.js +++ b/apps/_example_widget/widget.js @@ -1,16 +1,14 @@ -/* run widgets in their own function scope so they don't interfere with -currently-running apps */ +/* run widgets in their own function scope if they need to define local +variables so they don't interfere with currently-running apps */ (() => { - function draw() { - g.reset(); // reset the graphics context to defaults (color/font/etc) - // add your code - g.drawString("X", this.x, this.y); - } - // add your widget WIDGETS["mywidget"]={ area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right), be aware that not all apps support widgets at the bottom of the screen width: 28, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout - draw:draw // called to draw the widget + draw:function() { + g.reset(); // reset the graphics context to defaults (color/font/etc) + // add your code + g.drawString("X", this.x, this.y); + } // called to draw the widget }; })() diff --git a/apps/clkinfomag/ChangeLog b/apps/clkinfomag/ChangeLog new file mode 100644 index 000000000..4c21f3ace --- /dev/null +++ b/apps/clkinfomag/ChangeLog @@ -0,0 +1 @@ +0.01: New Widget! diff --git a/apps/clkinfomag/clkinfo.js b/apps/clkinfomag/clkinfo.js new file mode 100644 index 000000000..7a6062a2f --- /dev/null +++ b/apps/clkinfomag/clkinfo.js @@ -0,0 +1,48 @@ +(function() { + var heading, cnt; + function magHandler(m) { + var h = m.heading; + if (isNaN(heading) || isNaN(h)) + heading = h; + else { + // Average + if (Math.abs(heading-h)>180) { + if (h<180 && heading>180) h+=360; + if (h>180 && heading<180) h-=360; + } + heading = heading*0.8 + h*0.2; + if (heading<0) heading+=360; + if (heading>=360) heading-=360; + } + // only draw 1 in 2 to try and save some power! + if (!(1&cnt++)) ci.items[0].emit('redraw'); + } + var ci = { + name: "Bangle", + items: [ + { name : "Compass", + get : function() { + var g = Graphics.createArrayBuffer(24,24,1,{msb:true}); + if (isNaN(heading)) + g.drawLine(8,12,16,12); + else + g.fillPoly(g.transformVertices([0,-10,4,10,-4,10],{x:12,y:12,rotate:-heading/57})); + return { text : isNaN(heading)?"--":Math.round(heading), + v : 0|heading, min : 0, max : 360, + img : g.asImage("string") }}, + show : function() { + Bangle.setCompassPower(1,"clkinfomag"); + Bangle.on('mag',magHandler); + cnt=0; + heading = undefined; + }, + hide : function() { + Bangle.removeListener('mag', magHandler); + Bangle.setCompassPower(0,"clkinfomag"); + }, + run : function() { Bangle.resetCompass(); } + } + ] + }; + return ci; +}) // must not have a semi-colon! \ No newline at end of file diff --git a/apps/clkinfomag/icon.png b/apps/clkinfomag/icon.png new file mode 100644 index 000000000..aaa9c7027 Binary files /dev/null and b/apps/clkinfomag/icon.png differ diff --git a/apps/clkinfomag/metadata.json b/apps/clkinfomag/metadata.json new file mode 100644 index 000000000..84d6bc16c --- /dev/null +++ b/apps/clkinfomag/metadata.json @@ -0,0 +1,12 @@ +{ "id": "clkinfomag", + "name": "Compass Clockinfo", + "version":"0.01", + "description": "Extra information to add to clock screens. When selected, displays the compass heading and an arrow pointing North", + "icon": "icon.png", + "type": "clkinfo", + "tags": "clkinfo,compass,mag,magnetometer", + "supports" : ["BANGLEJS2"], + "storage": [ + {"name":"clkinfomag.clkinfo.js","url":"clkinfo.js"} + ] +} diff --git a/apps/clock_info/ChangeLog b/apps/clock_info/ChangeLog index 7d51e0da5..0081c49d4 100644 --- a/apps/clock_info/ChangeLog +++ b/apps/clock_info/ChangeLog @@ -1,2 +1,3 @@ 0.01: Moved from modules/clock_info.js 0.02: Fix settings page +0.03: Reported image for battery now reflects charge level \ No newline at end of file diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index 7251f1b00..e849be256 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -53,10 +53,17 @@ exports.load = function() { items: [ { name : "Battery", hasRange : true, - get : () => { let v = E.getBattery(); return { - text : v + "%", v : v, min:0, max:100, - img : atob(Bangle.isCharging() ? "GBiBAAABgAADwAAHwAAPgACfAAHOAAPkBgHwDwP4Hwf8Pg/+fB//OD//kD//wD//4D//8D//4B//QB/+AD/8AH/4APnwAHAAACAAAA==" : "GBiBAAAAAAAAAAAAAAAAAAAAAD//+P///IAAAr//Ar//Ar//A7//A7//A7//A7//Ar//AoAAAv///D//+AAAAAAAAAAAAAAAAAAAAA==") - }}, + get : () => { let v = E.getBattery(); + var img; + if (!Bangle.isCharging()) { + var s=24,g = Graphics.createArrayBuffer(24,24,1,{msb:true}); + g.fillRect(0,6,s-3,18).clearRect(2,8,s-5,16).fillRect(s-2,10,s,15).fillRect(3,9,3+v*(s-9)/100,15); + img = g.asImage("string"); + } else img=atob("GBiBAAABgAADwAAHwAAPgACfAAHOAAPkBgHwDwP4Hwf8Pg/+fB//OD//kD//wD//4D//8D//4B//QB/+AD/8AH/4APnwAHAAACAAAA=="); + return { + text : v + "%", v : v, min:0, max:100, img : img + } + }, show : function() { this.interval = setInterval(()=>this.emit('redraw'), 60000); Bangle.on("charging", batteryUpdateHandler); batteryUpdateHandler(); }, hide : function() { clearInterval(this.interval); delete this.interval; Bangle.removeListener("charging", batteryUpdateHandler); }, }, diff --git a/apps/clock_info/metadata.json b/apps/clock_info/metadata.json index 6cc3e1233..703cf9909 100644 --- a/apps/clock_info/metadata.json +++ b/apps/clock_info/metadata.json @@ -1,7 +1,7 @@ { "id": "clock_info", "name": "Clock Info Module", "shortName": "Clock Info", - "version":"0.02", + "version":"0.03", "description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)", "icon": "app.png", "type": "module", diff --git a/apps/drained/ChangeLog b/apps/drained/ChangeLog index 1a3bc1757..c7fd27981 100644 --- a/apps/drained/ChangeLog +++ b/apps/drained/ChangeLog @@ -1 +1,4 @@ 0.01: New app! +0.02: Allow boot exceptions, e.g. to load DST +0.03: Permit exceptions to load in low-power mode, e.g. daylight saving time. + Also avoid polluting global scope. diff --git a/apps/drained/app.js b/apps/drained/app.js index f9ae04605..e27fcb1d1 100644 --- a/apps/drained/app.js +++ b/apps/drained/app.js @@ -35,14 +35,16 @@ var draw = function () { var dateStr = require("locale").date(date, 0).toUpperCase() + "\n" + require("locale").dow(date, 0).toUpperCase(); + var x2 = x + 6; + var y2 = y + 66; g.reset() .clearRect(Bangle.appRect) .setFont("Vector", 55) .setFontAlign(0, 0) .drawString(timeStr, x, y) .setFont("Vector", 24) - .drawString(dateStr, x, y + 56) - .drawString("".concat(E.getBattery(), "%"), x, y + 104); + .drawString(dateStr, x2, y2) + .drawString("".concat(E.getBattery(), "%"), x2, y2 + 48); if (nextDraw) clearTimeout(nextDraw); nextDraw = setTimeout(function () { @@ -75,9 +77,9 @@ var reload = function () { }; reload(); Bangle.emit("drained", E.getBattery()); -var _a = require("Storage").readJSON("".concat(app, ".setting.json"), true) || {}, _b = _a.disableBoot, disableBoot = _b === void 0 ? false : _b, _c = _a.restore, restore = _c === void 0 ? 20 : _c; +var _a = require("Storage").readJSON("".concat(app, ".setting.json"), true) || {}, _b = _a.keepStartup, keepStartup = _b === void 0 ? true : _b, _c = _a.restore, restore = _c === void 0 ? 20 : _c, _d = _a.exceptions, exceptions = _d === void 0 ? ["widdst.0"] : _d; function drainedRestore() { - if (disableBoot) { + if (!keepStartup) { try { eval(require('Storage').read('bootupdate.js')); } @@ -87,16 +89,28 @@ function drainedRestore() { } load(); } -if (disableBoot) { - var checkCharge_1 = function () { - if (E.getBattery() < restore) - return; - drainedRestore(); - }; - if (Bangle.isCharging()) - checkCharge_1(); - Bangle.on("charging", function (charging) { - if (charging) - checkCharge_1(); - }); +var checkCharge = function () { + if (E.getBattery() < restore) + return; + drainedRestore(); +}; +if (Bangle.isCharging()) + checkCharge(); +Bangle.on("charging", function (charging) { + if (charging) + checkCharge(); +}); +if (!keepStartup) { + var storage = require("Storage"); + for (var _i = 0, exceptions_1 = exceptions; _i < exceptions_1.length; _i++) { + var boot = exceptions_1[_i]; + try { + var js = storage.read("".concat(boot, ".boot.js")); + if (js) + eval(js); + } + catch (e) { + console.log("error loading boot exception \"".concat(boot, "\": ").concat(e)); + } + } } diff --git a/apps/drained/app.ts b/apps/drained/app.ts index 9fc2665ee..3adfbec02 100644 --- a/apps/drained/app.ts +++ b/apps/drained/app.ts @@ -52,6 +52,8 @@ const draw = () => { const dateStr = require("locale").date(date, 0).toUpperCase() + "\n" + require("locale").dow(date, 0).toUpperCase(); + const x2 = x + 6; + const y2 = y + 66; g.reset() .clearRect(Bangle.appRect) @@ -59,8 +61,8 @@ const draw = () => { .setFontAlign(0, 0) .drawString(timeStr, x, y) .setFont("Vector", 24) - .drawString(dateStr, x, y + 56) - .drawString(`${E.getBattery()}%`, x, y + 104); + .drawString(dateStr, x2, y2) + .drawString(`${E.getBattery()}%`, x2, y2 + 48); if(nextDraw) clearTimeout(nextDraw); nextDraw = setTimeout(() => { @@ -97,12 +99,12 @@ reload(); Bangle.emit("drained", E.getBattery()); // restore normal boot on charge -const { disableBoot = false, restore = 20 }: DrainedSettings +const { keepStartup = true, restore = 20, exceptions = ["widdst.0"] }: DrainedSettings = require("Storage").readJSON(`${app}.setting.json`, true) || {}; // re-enable normal boot code when we're above a threshold: function drainedRestore() { // "public", to allow users to call - if(disableBoot){ + if(!keepStartup){ try{ eval(require('Storage').read('bootupdate.js')); }catch(e){ @@ -112,16 +114,26 @@ function drainedRestore() { // "public", to allow users to call load(); // necessary after updating boot.0 } -if(disableBoot){ - const checkCharge = () => { - if(E.getBattery() < restore) return; - drainedRestore(); - }; +const checkCharge = () => { + if(E.getBattery() < restore) return; + drainedRestore(); +}; - if (Bangle.isCharging()) - checkCharge(); +if (Bangle.isCharging()) + checkCharge(); - Bangle.on("charging", charging => { - if(charging) checkCharge(); - }); +Bangle.on("charging", charging => { + if(charging) checkCharge(); +}); + +if(!keepStartup){ + const storage = require("Storage"); + for(const boot of exceptions){ + try{ + const js = storage.read(`${boot}.boot.js`); + if(js) eval(js); + }catch(e){ + console.log(`error loading boot exception "${boot}": ${e}`); + } + } } diff --git a/apps/drained/boot.js b/apps/drained/boot.js index 97f405123..48c1572bd 100644 --- a/apps/drained/boot.js +++ b/apps/drained/boot.js @@ -1,13 +1,13 @@ -{ - var _a = require("Storage").readJSON("drained.setting.json", true) || {}, _b = _a.battery, threshold_1 = _b === void 0 ? 5 : _b, _c = _a.interval, interval = _c === void 0 ? 10 : _c, _d = _a.disableBoot, disableBoot_1 = _d === void 0 ? false : _d; +(function () { + var _a = require("Storage").readJSON("drained.setting.json", true) || {}, _b = _a.battery, threshold = _b === void 0 ? 5 : _b, _c = _a.interval, interval = _c === void 0 ? 10 : _c, _d = _a.keepStartup, keepStartup = _d === void 0 ? true : _d; drainedInterval = setInterval(function () { if (Bangle.isCharging()) return; - if (E.getBattery() > threshold_1) + if (E.getBattery() > threshold) return; var app = "drained.app.js"; - if (disableBoot_1) + if (!keepStartup) require("Storage").write(".boot0", "if(typeof __FILE__ === \"undefined\" || __FILE__ !== \"".concat(app, "\") setTimeout(load, 100, \"").concat(app, "\");")); load(app); }, interval * 60 * 1000); -} +})(); diff --git a/apps/drained/boot.ts b/apps/drained/boot.ts index 4dc885c20..1fcb0591b 100644 --- a/apps/drained/boot.ts +++ b/apps/drained/boot.ts @@ -1,5 +1,5 @@ -{ -const { battery: threshold = 5, interval = 10, disableBoot = false }: DrainedSettings +(() => { +const { battery: threshold = 5, interval = 10, keepStartup = true }: DrainedSettings = require("Storage").readJSON(`drained.setting.json`, true) || {}; drainedInterval = setInterval(() => { @@ -10,7 +10,7 @@ drainedInterval = setInterval(() => { const app = "drained.app.js"; - if(disableBoot) + if(!keepStartup) require("Storage").write( ".boot0", `if(typeof __FILE__ === "undefined" || __FILE__ !== "${app}") setTimeout(load, 100, "${app}");` @@ -18,4 +18,4 @@ drainedInterval = setInterval(() => { load(app); }, interval * 60 * 1000); -} +})() diff --git a/apps/drained/metadata.json b/apps/drained/metadata.json index 89a069b2f..6dfdac78d 100644 --- a/apps/drained/metadata.json +++ b/apps/drained/metadata.json @@ -1,7 +1,7 @@ { "id": "drained", "name": "Drained", - "version": "0.01", + "version": "0.03", "description": "Switches to displaying a simple clock when the battery percentage is low, and disables some peripherals", "readme": "README.md", "icon": "icon.png", @@ -14,5 +14,8 @@ {"name":"drained.app.js","url":"app.js"}, {"name":"drained.settings.js","url":"settings.js"}, {"name":"drained.img","url":"app-icon.js","evaluate":true} + ], + "data": [ + {"name":"drained.setting.json"} ] } diff --git a/apps/drained/settings.js b/apps/drained/settings.js index fe4ab9c4b..ce72f215f 100644 --- a/apps/drained/settings.js +++ b/apps/drained/settings.js @@ -1,26 +1,19 @@ (function (back) { - var _a, _b, _c, _d; + var _a, _b, _c, _d, _e; var SETTINGS_FILE = "drained.setting.json"; var storage = require("Storage"); var settings = storage.readJSON(SETTINGS_FILE, true) || {}; (_a = settings.battery) !== null && _a !== void 0 ? _a : (settings.battery = 5); (_b = settings.restore) !== null && _b !== void 0 ? _b : (settings.restore = 20); (_c = settings.interval) !== null && _c !== void 0 ? _c : (settings.interval = 10); - (_d = settings.disableBoot) !== null && _d !== void 0 ? _d : (settings.disableBoot = false); + (_d = settings.keepStartup) !== null && _d !== void 0 ? _d : (settings.keepStartup = true); + (_e = settings.exceptions) !== null && _e !== void 0 ? _e : (settings.exceptions = ["widdst.0"]); var save = function () { storage.writeJSON(SETTINGS_FILE, settings); }; - E.showMenu({ + var menu = { "": { "title": "Drained" }, "< Back": back, - "Keep startup code": { - value: settings.disableBoot, - format: function () { return settings.disableBoot ? "No" : "Yes"; }, - onchange: function () { - settings.disableBoot = !settings.disableBoot; - save(); - }, - }, "Trigger at batt%": { value: settings.battery, min: 0, @@ -54,5 +47,44 @@ save(); }, }, - }); + "Keep startup code": { + value: settings.keepStartup, + onchange: function (b) { + settings.keepStartup = b; + save(); + updateAndRedraw(); + }, + }, + }; + var updateAndRedraw = function () { + setTimeout(function () { E.showMenu(menu); }, 10); + if (settings.keepStartup) { + delete menu["Startup exceptions"]; + return; + } + menu["Startup exceptions"] = function () { return E.showMenu(bootExceptions); }; + var bootExceptions = { + "": { "title": "Startup exceptions" }, + "< Back": function () { return E.showMenu(menu); }, + }; + storage.list(/\.boot\.js/) + .map(function (name) { return name.replace(".boot.js", ""); }) + .forEach(function (name) { + bootExceptions[name] = { + value: settings.exceptions.indexOf(name) >= 0, + onchange: function (b) { + if (b) { + settings.exceptions.push(name); + } + else { + var i = settings.exceptions.indexOf(name); + if (i >= 0) + settings.exceptions.splice(i, 1); + } + save(); + }, + }; + }); + }; + updateAndRedraw(); }); diff --git a/apps/drained/settings.ts b/apps/drained/settings.ts index c79e6605c..f972f51a7 100644 --- a/apps/drained/settings.ts +++ b/apps/drained/settings.ts @@ -2,7 +2,8 @@ type DrainedSettings = { battery?: number, restore?: number, interval?: number, - disableBoot?: ShortBoolean, + keepStartup?: ShortBoolean, + exceptions?: string[], }; (back => { @@ -13,23 +14,16 @@ type DrainedSettings = { settings.battery ??= 5; settings.restore ??= 20; settings.interval ??= 10; - settings.disableBoot ??= false; + settings.keepStartup ??= true; + settings.exceptions ??= ["widdst.0"]; // daylight savings const save = () => { storage.writeJSON(SETTINGS_FILE, settings) }; - E.showMenu({ + const menu: Menu = { "": { "title": "Drained" }, "< Back": back, - "Keep startup code": { - value: settings.disableBoot, - format: () => settings.disableBoot ? "No" : "Yes", - onchange: () => { - settings.disableBoot = !settings.disableBoot; - save(); - }, - }, "Trigger at batt%": { value: settings.battery, min: 0, @@ -63,5 +57,48 @@ type DrainedSettings = { save(); }, }, - }); + "Keep startup code": { + value: settings.keepStartup as boolean, + onchange: (b: boolean) => { + settings.keepStartup = b; + save(); + updateAndRedraw(); + }, + }, + }; + + const updateAndRedraw = () => { + // will change the menu, queue redraw: + setTimeout(() => { E.showMenu(menu) }, 10); + + if (settings.keepStartup) { + delete menu["Startup exceptions"]; + return; + } + menu["Startup exceptions"] = () => E.showMenu(bootExceptions); + + const bootExceptions: Menu = { + "": { "title" : "Startup exceptions" }, + "< Back": () => E.showMenu(menu), + }; + + storage.list(/\.boot\.js/) + .map(name => name.replace(".boot.js", "")) + .forEach((name: string) => { + bootExceptions[name] = { + value: settings.exceptions!.indexOf(name) >= 0, + onchange: (b: boolean) => { + if (b) { + settings.exceptions!.push(name); + } else { + const i = settings.exceptions!.indexOf(name); + if (i >= 0) settings.exceptions!.splice(i, 1); + } + save(); + }, + }; + }); + }; + + updateAndRedraw(); }) satisfies SettingsFunc diff --git a/apps/pebblepp/ChangeLog b/apps/pebblepp/ChangeLog new file mode 100644 index 000000000..7b83706bf --- /dev/null +++ b/apps/pebblepp/ChangeLog @@ -0,0 +1 @@ +0.01: First release diff --git a/apps/pebblepp/app.js b/apps/pebblepp/app.js new file mode 100644 index 000000000..d296cdc1a --- /dev/null +++ b/apps/pebblepp/app.js @@ -0,0 +1,124 @@ +Graphics.prototype.setFontLECO1976Regular42 = function(scale) { + // Actual height 42 (41 - 0) + return g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAA/AAAAAAAAH/AAAAAAAA//AAAAAAAP//AAAAAAB///AAAAAAP///AAAAAB////AAAAAf////AAAAD////4AAAAf////AAAAH////4AAAA////+AAAAA////wAAAAA///+AAAAAA///gAAAAAA//8AAAAAAA//gAAAAAAA/4AAAAAAAA/AAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////gD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4B/gH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAH+AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("ERkmHyYmJiYmJCYmEQ=="), 60+(scale<<8)+(1<<16)); +}; + +Graphics.prototype.setFontLECO1976Regular22 = function(scale) { + // Actual height 22 (21 - 0) + return g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nA/+cD/5wP/nAAAAAAAAPwAA/gAD+AAPwAAAAAD+AAP4AA/gAAAAAAAAAAAAAcOAP//A//8D//wP//AHDgAcOAP//A//8D//wP//AHDgAAAAAAAAH/jgf+OB/44H/jj8OP/w4//Dj/8OPxw/4HD/gcP+Bw/4AAAAAAAP+AA/8AD/wQOHHA4c8D//wP/8A//gAD4AAfAAH/8A//wP//A84cDjhwIP/AA/8AB/wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8ABwAAAAAAAAD8AAP4AA/gAD8AAAAAAAAAAAEAAD+AB//A///v/D//gB/wABwAAAAAADgAA/wAf/4P8///wf/4AP8AAOAAAAAAAAAyAAHcAAPwAD/gAP/AA/8AA/AAH8AAMwAAAAAAAAAAAAADgAAOAAA4AAf8AD/wAP/AA/8AAOAAA4AADgAAAAAAAAAAD8AAfwAB/AAD8AAAAAAAADgAAOAAA4AADgAAOAAA4AADgAAAAAAAAAADgAAOAAA4AADgAAAAAAAAABwAB/AA/8A//gP/gA/wADwAAIAAAAAAD//wP//A//8D//wOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA4AcDgBwOAHA//8D//wP//A//8AABwAAHAAAcAAAAAAAA+f8D5/wPn/A+f8DhxwOHHA4ccDhxwP/HA/8cD/xwP/HAAAAAAAAOAHA4AcDhxwOHHA4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/wAP/AA/8AD/wAAHAAAcAABwAAHAA//8D//wP//A//8AAAAAAAA/98D/3wP/fA/98DhxwOHHA4ccDhxwOH/A4f8Dh/wOH/AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccDh/wOH/A4f8Dh/wAAAAAAAD4AAPgAA+AADgAAOAAA4AADgAAP//A//8D//wP//AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA//8D//wP//A//8AAAAAAAAOA4A4DgDgOAOA4AAAAAAAAOA/A4H8DgfwOA/AAAAAAAAB4AAPwAA/AAD8AAf4ABzgAPPAA8cAHh4AAAAAAAAAAAAHHAAccABxwAHHAAccABxwAHHAAccABxwAHHAAAAAAAAAOHAA4cADzwAPPAAf4AB/gAD8AAPwAAeAAB4AAAAAAAAA+AAD4AAPgAA+ecDh9wOH3A4fcDhwAP/AA/8AD/wAP/AAAAAAAAAP//4///j//+P//44ADjn/OOf845/zjnHOP8c4//zj//OP/84AAAAAAAP//A//8D//wP//A4cADhwAOHAA4cAD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA//8D//wP9/A/j8AAAAAAAA//8D//wP//A//8DgBwOAHA4AcDgBwOAHA4AcDgBwOAHAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA8A8D//wH/+AP/wAf+AAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4ccDhxwOAHA4AcAAAAAAAA//8D//wP//A//8DhwAOHAA4cADhwAOHAA4cADgAAOAAAAAAD//wP//A//8D//wOAHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA//8D//wP//A//8ABwAAHAAAcAABwAP//A//8D//wP//AAAAAAAAP//A//8D//wP//AAAAAAAAOAHA4AcDgBwOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA//8D//wP//A//8AHwAA/AAP8AB/wAPn/A8f8DB/wIH/AAAAAAAAP//A//8D//wP//AAAcAABwAAHAAAcAABwAAHAAAAAAAAP//A//8D//wP//Af8AAP+AAH/AAD8AAHwAD/AB/wAf8AP+AA//8D//wP//AAAAAAAAP//A//8D//wP//AfwAAfwAAfwAAfwAAfwP//A//8D//wAAAAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHAA4cADhwAOHAA/8AD/wAP/AA/8AAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//+P//4///j//+AAA4AADgAAAP//A//8D//wP//A4eADh+AOH8A4f4D/3wP/HA/8MD/wQAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA4AADgAAOAAA//8D//wP//A//8DgAAOAAA4AADgAAAAAA//8D//wP//A//8AABwAAHAAAcAABwP//A//8D//wP//AAAADAAAPgAA/wAD/4AB/8AA/8AAfwAB/AA/8Af+AP/AA/wAD4AAMAAA4AAD+AAP/gA//8AH/wAB/AAf8Af/wP/4A/4AD/gAP/4AH/8AB/wAB/AB/8D//wP/gA/gADgAAIABA4AcDwDwPw/Afn4Af+AA/wAD/AA//AH5+A/D8DwDwOAHAgAEAAAAP/AA/8AD/wAP/AAAf8AB/wAH/AAf8D/wAP/AA/8AD/wAAAAAAAADh/wOH/A4f8Dh/wOHHA4ccDhxwOHHA/8cD/xwP/HA/8cAAAAAAAAf//9///3///f//9wAA3AADcAAMAAAOAAA/gAD/wAH/8AB/8AA/wAAPAAAEAAAAHAADcAANwAB3///f//9///wAA"), 32, atob("BwYLDg4UDwYJCQwMBgkGCQ4MDg4ODg4NDg4GBgwMDA4PDg4ODg4NDg4GDQ4MEg8ODQ8ODgwODhQODg4ICQg="), 22+(scale<<8)+(1<<16)); +}; + +{ +const SETTINGS_FILE = "pebblepp.json"; +let settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#0f0', 'color': 'Green', 'theme':'System', 'showlock':false}; +let theme; +let drawTimeout; + +const h = g.getHeight(); +const w = g.getWidth(); +const ha = 2*h/5 - 4; +const h2 = 3*h/5 - 10; +const h3 = 7*h/8; + +let draw = function() { + let locale = require("locale"); + let date = new Date(); + let time = locale.time(date, 1); + + g.reset(); + g.setBgColor(theme.bg).clearRect(0, h2, w, h3); + g.setFontLECO1976Regular42().setFontAlign(0, -1); + g.setColor(theme.fg); + g.drawString(time, w/2, h2 + 8); + + // queue next draw + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); +}; + +let loadThemeColors = function() { + theme = {fg: g.theme.fg, bg: g.theme.bg, day: g.toColor(0,0,0)}; + if (settings.theme === "Dark") { + theme.fg = g.toColor(1,1,1); + theme.bg = g.toColor(0,0,0); + } else if (settings.theme === "Light") { + theme.fg = g.toColor(0,0,0); + theme.bg = g.toColor(1,1,1); + } + // day and steps + if (settings.color == 'Blue' || settings.color == 'Red') + theme.day = g.toColor(1,1,1); // white on blue or red best contrast +}; +loadThemeColors(); + +// Load the clock infos +let clockInfoItems = require("clock_info").load(); +let clockInfoDraw = (itm, info, options) => { + // itm: the item containing name/hasRange/etc + // info: data returned from itm.get() containing text/img/etc + // options: options passed into addInteractive + // Clear the background - if focussed, add a border + g.reset().setBgColor(theme.bg).setColor(theme.fg); + var b = 0; // border + if (options.focus) { // white border + b = 4; + g.clearRect(options.x, options.y, options.x+options.w-1, options.y+options.h-1); + } + g.setBgColor(settings.bg).clearRect(options.x+b, options.y+b, options.x+options.w-1-b, options.y+options.h-1-b); + // we're drawing center-aligned here + var midx = options.x+options.w/2; + if (info.img) { // draw the image + // TODO: we could replace certain images with our own ones here... + var y = options.y+8; + if (g.floodFill) { + /* img is (usually) a black and white transparent image. But we really would like the bits in + the middle of it to be white. So what we do is we draw a slightly bigger rectangle in white, + draw the image, and then flood-fill the rectangle back to the background color. floodFill + was only added in 2v18 so we have to check for it and fallback if not. */ + g.setBgColor(theme.bg).clearRect(midx-25,y-1,midx+24,y+48); + g.drawImage(info.img, midx-24,y,{scale:2}); + g.floodFill(midx-25,y,settings.bg); + } else { // fallback + g.drawImage(info.img, midx-24,y,{scale:2}); + } + } + g.setFontLECO1976Regular22().setFontAlign(0, 0); + g.drawString(info.text, midx,options.y+options.h-12); // draw the text +}; + +let clockInfoMenuA = require("clock_info").addInteractive(clockInfoItems, { + x : 0, y: 0, w: w/2, h:h/2, + draw : clockInfoDraw +}); +let clockInfoMenuB = require("clock_info").addInteractive(clockInfoItems, { + x : w/2, y: 0, w: w/2, h:h/2, + draw : clockInfoDraw +}); + +// Show launcher when middle button pressed +Bangle.setUI({ + mode : "clock", + remove : function() { + // Called to unload all of the clock app + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + clockInfoMenuA.remove(); + clockInfoMenuB.remove(); + delete Graphics.prototype.setFontLECO1976Regular22; + delete Graphics.prototype.setFontLECO1976Regular42; + require("widget_utils").show(); // re-show widgets + }}); + +Bangle.loadWidgets(); +require("widget_utils").swipeOn(); // hide widgets, make them visible with a swipe +g.setBgColor(settings.bg).clear(); // start off with completely clear background +// contrast bar (top) +g.setColor(theme.fg).fillRect(0, h2 - 6, w, h2); +// contrast bar (bottom) +g.setColor(theme.fg).fillRect(0, h3, w, h3 + 6); + +draw(); +} diff --git a/apps/pebblepp/app.png b/apps/pebblepp/app.png new file mode 100644 index 000000000..cfdf584a1 Binary files /dev/null and b/apps/pebblepp/app.png differ diff --git a/apps/pebblepp/icon.js b/apps/pebblepp/icon.js new file mode 100644 index 000000000..34cb133c6 --- /dev/null +++ b/apps/pebblepp/icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwcBpMkyQC/AUVIBZEh4QROoMP/QIFoEkiQIFjl//4IEwUIkGChIIDyH/AAI+Ggg1Fw4RCDQgCBkARJ8J0MHwcIEZgC/ATJrCwDRFBALXHWwIIFQYwjDyAjNEAMECILREfZACgagYCNgAAQ/4Aj+PHAQUcv///lxAoQCBuIRBwEH//wHgIRBBAMAgf+BAMBEZHHEYUAv0HEYf+EYIAB8F/4AFC8YLEAAIjGAoQjJI4oFBI4wjCCI4jBCIgjDn6IF8YDCwJHFTQxHC/gjCQwQ1DNYKPICI8ccgRHFAGikBd4QCMJ4QAOpMkyQC/AQg=")) \ No newline at end of file diff --git a/apps/pebblepp/metadata.json b/apps/pebblepp/metadata.json new file mode 100644 index 000000000..aede7ae12 --- /dev/null +++ b/apps/pebblepp/metadata.json @@ -0,0 +1,18 @@ +{ + "id": "pebblepp", + "name": "Pebble++ Clock", + "shortName": "Pebble++", + "version": "0.01", + "description": "A pebble style clock (based on the 'Pebble Clock' app) but with two configurable ClockInfo items at the top", + "icon": "app.png", + "screenshots": [{"url":"screenshot.png"}], + "type": "clock", + "tags": "clock,clkinfo", + "supports": ["BANGLEJS2"], + "allow_emulator": true, + "storage": [ + {"name":"pebblepp.app.js","url":"app.js"}, + {"name":"pebblepp.settings.js","url":"settings.js"}, + {"name":"pebblepp.img","url":"icon.js","evaluate":true} + ] +} diff --git a/apps/pebblepp/screenshot.png b/apps/pebblepp/screenshot.png new file mode 100644 index 000000000..5c36309a7 Binary files /dev/null and b/apps/pebblepp/screenshot.png differ diff --git a/apps/pebblepp/settings.js b/apps/pebblepp/settings.js new file mode 100644 index 000000000..b84a477e1 --- /dev/null +++ b/apps/pebblepp/settings.js @@ -0,0 +1,50 @@ +(function(back) { + const SETTINGS_FILE = "pebblepp.json"; + + // TODO Only the color/theme indices should be written in the settings file so the labels can be translated + + // Initialize with default settings... + let s = {'bg': '#0f0', 'color': 'Green', 'theme':'System', 'showlock':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'); + let settings = storage.readJSON(SETTINGS_FILE, 1) || s; + const saved = settings || {}; + for (const key in saved) { + s[key] = saved[key] + } + + function save() { + settings = s; + storage.write(SETTINGS_FILE, settings); + } + + var color_options = ['Green','Orange','Cyan','Purple','Red','Blue']; + var bg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f']; + var theme_options = ['System', 'Light', 'Dark']; + + E.showMenu({ + '': { 'title': 'Pebble++ Clock' }, + /*LANG*/'< Back': back, + /*LANG*/'Colour': { + value: 0 | color_options.indexOf(s.color), + min: 0, max: 5, + format: v => color_options[v], + onchange: v => { + s.color = color_options[v]; + s.bg = bg_code[v]; + save(); + } + }, + /*LANG*/'Theme': { + value: 0 | theme_options.indexOf(s.theme), + min: 0, max: theme_options.length - 1, + format: v => theme_options[v], + onchange: v => { + s.theme = theme_options[v]; + save(); + } + } + }); +}); diff --git a/apps/popconlaunch/ChangeLog b/apps/popconlaunch/ChangeLog index 8a2124e33..c430b4412 100644 --- a/apps/popconlaunch/ChangeLog +++ b/apps/popconlaunch/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: Trim old entries from the popcon app cache +0.03: Avoid polluting global scope diff --git a/apps/popconlaunch/boot.js b/apps/popconlaunch/boot.js index 9124344b2..0080a66f9 100644 --- a/apps/popconlaunch/boot.js +++ b/apps/popconlaunch/boot.js @@ -1,18 +1,18 @@ -{ - var oldRead_1 = require("Storage").readJSON; - var oneMonth_1 = 1000 * 86400 * 28; - var monthAgo_1 = Date.now() - oneMonth_1; - var cache_1; - var ensureCache_1 = function () { - if (!cache_1) { - cache_1 = oldRead_1("popcon.cache.json", true); - if (!cache_1) - cache_1 = {}; +(function () { + var oldRead = require("Storage").readJSON; + var oneMonth = 1000 * 86400 * 28; + var monthAgo = Date.now() - oneMonth; + var cache; + var ensureCache = function () { + if (!cache) { + cache = oldRead("popcon.cache.json", true); + if (!cache) + cache = {}; } - return cache_1; + return cache; }; - var trimCache_1 = function (cache) { - var threeMonthsBack = Date.now() - oneMonth_1 * 3; + var trimCache = function (cache) { + var threeMonthsBack = Date.now() - oneMonth * 3; var del = []; for (var k in cache) if (cache[k].last < threeMonthsBack) @@ -22,21 +22,21 @@ delete cache[k]; } }; - var saveCache_1 = function (cache, orderChanged) { - trimCache_1(cache); + var saveCache = function (cache, orderChanged) { + trimCache(cache); require("Storage").writeJSON("popcon.cache.json", cache); if (orderChanged) { - var info = oldRead_1("popcon.info", true); + var info = oldRead("popcon.info", true); info.cacheBuster = !info.cacheBuster; require("Storage").writeJSON("popcon.info", info); } }; - var sortCache_1 = function () { - var ents = Object.values(cache_1); + var sortCache = function () { + var ents = Object.values(cache); ents.sort(function (a, b) { var n; - var am = (a.last > monthAgo_1); - var bm = (b.last > monthAgo_1); + var am = (a.last > monthAgo); + var bm = (b.last > monthAgo); n = bm - am; if (n) return n; @@ -64,31 +64,31 @@ }; require("Storage").readJSON = (function (fname, skipExceptions) { var _a; - var j = oldRead_1(fname, skipExceptions); + var j = oldRead(fname, skipExceptions); if (/\.info$/.test(fname)) { - var cache_2 = ensureCache_1(); + var cache_1 = ensureCache(); var so = void 0; - if (j.src && (so = (_a = cache_2[j.src]) === null || _a === void 0 ? void 0 : _a.sortorder) != null) + if (j.src && (so = (_a = cache_1[j.src]) === null || _a === void 0 ? void 0 : _a.sortorder) != null) j.sortorder = so; else j.sortorder = 99; } return j; }); - var oldLoad_1 = load; + var oldLoad = load; global.load = function (src) { if (src) { - var cache_3 = ensureCache_1(); - var ent = cache_3[src] || (cache_3[src] = { + var cache_2 = ensureCache(); + var ent = cache_2[src] || (cache_2[src] = { pop: 0, last: 0, sortorder: -10, }); ent.pop++; ent.last = Date.now(); - var orderChanged = sortCache_1(); - saveCache_1(cache_3, orderChanged); + var orderChanged = sortCache(); + saveCache(cache_2, orderChanged); } - return oldLoad_1(src); + return oldLoad(src); }; -} +})(); diff --git a/apps/popconlaunch/boot.ts b/apps/popconlaunch/boot.ts index 306d4844a..4fe638f75 100644 --- a/apps/popconlaunch/boot.ts +++ b/apps/popconlaunch/boot.ts @@ -1,4 +1,4 @@ -{ +(() => { type Timestamp = number; type Cache = { [key: string]: { @@ -113,4 +113,4 @@ global.load = (src: string) => { return oldLoad(src); }; -} +})() diff --git a/apps/popconlaunch/metadata.json b/apps/popconlaunch/metadata.json index 1acab2b6c..be3bc6a92 100644 --- a/apps/popconlaunch/metadata.json +++ b/apps/popconlaunch/metadata.json @@ -2,7 +2,7 @@ "id": "popconlaunch", "name": "Popcon Launcher", "shortName": "Popcon", - "version": "0.02", + "version": "0.03", "description": "Launcher modification - your launchers will display your favourite (popular) apps first. Overrides `readJSON`, may slow down your watch", "readme": "README.md", "icon": "app.png", diff --git a/apps/powermanager/ChangeLog b/apps/powermanager/ChangeLog index de3fd021c..87bbc80c7 100644 --- a/apps/powermanager/ChangeLog +++ b/apps/powermanager/ChangeLog @@ -9,4 +9,6 @@ 0.07: Convert Yes/No On/Off in settings to checkboxes 0.08: Fix the wrapping of intervals/timeouts with parameters Fix the widget drawing if widgets are hidden and Bangle.setLCDBrightness is called -0.09: Accidental version bump \ No newline at end of file +0.09: Accidental version bump +0.10: Use charging state on boot for auto calibration + Log additional timestamp for trace log diff --git a/apps/powermanager/boot.js b/apps/powermanager/boot.js index f3e3f718f..c35ec1546 100644 --- a/apps/powermanager/boot.js +++ b/apps/powermanager/boot.js @@ -42,10 +42,10 @@ let logPower = (type, oldstate, state, app) => { - logFile.write("p," + type + ',' + (oldstate?1:0) + ',' + (state?1:0) + ',' + app + "\n"); + logFile.write(Date.now() + ",p," + type + ',' + (oldstate?1:0) + ',' + (state?1:0) + ',' + app + "\n"); }; let logDeferred = (type, duration, source) => { - logFile.write(type + ',' + duration + ',' + source.replace(/\n/g, " ").replace(/,/g,"") + "\n"); + logFile.write(Date.now() + "," + type + ',' + duration + ',' + source.replace(/\n/g, " ").replace(/,/g,"") + "\n"); }; let lastPowerOn = {}; @@ -152,13 +152,14 @@ return v; }; } - + if (settings.autoCalibration){ let chargeStart; + if (Bangle.isCharging()) chargeStart = Date.now(); Bangle.on("charging", (charging)=>{ - if (charging) chargeStart = Date.now(); + if (!chargeStart && charging) chargeStart = Date.now(); if (chargeStart && !charging && (Date.now() - chargeStart > 1000*60*60*3)) require("powermanager").setCalibration(); if (!charging) chargeStart = undefined; }); } -})(); \ No newline at end of file +})(); diff --git a/apps/powermanager/interface.html b/apps/powermanager/interface.html index fac49694c..2cab4561a 100644 --- a/apps/powermanager/interface.html +++ b/apps/powermanager/interface.html @@ -1,6 +1,15 @@ + +
@@ -119,18 +128,21 @@ function viewDeferredTable(filename) {
This are functions used in timeouts and intervals and their accumulated execution times. Recorded in a time span of ${timeFormat(duration)}. Timeouts/intervals have run for ${timeFormat(sum)} (${(sum/duration*100).toFixed(2)}%). Percentages are calculated from summarized timeout/interval running time. -
- - - - - - - - - \n`; + + +
+ +
TimePercentageFunction
+ + + + + + + + \n`; htmlOverview += tableRows; - htmlOverview += `
TimePercentageFunction
`; + htmlOverview += ``; domContent.innerHTML = htmlOverview; domContent.querySelector("#back").addEventListener("click",event => { show(); @@ -234,24 +246,46 @@ function viewDetailsTable(filename) { Util.hideModal(); var htmlOverview = `

Detailed logging

- - This is a trace log of all logged power entries, first column denotes the type. p for power, i for interval and t for timeout. Power is logged with old state, new state and calling app if available. Functions are logged with execution duraion and source if available. - - - \n`; +
+ This is a trace log of all logged entries. Power is logged with type, state transition (old → new) and calling app if available. Functions are logged with execution duration and source if available. +
+
+ +
+ + + + + + + + \n`; let rows = data.trim().split("\n"); + let firstTimestamp; for (var row of rows) { let cols = row.split(","); - htmlOverview += ` - - - - - - ` + let col = 0; + if (!firstTimestamp) firstTimestamp = cols[0]; + + if (cols[1] == "p"){ + cols[1] = "Power"; + htmlOverview += ` + + + + ` + } else { + htmlOverview += ` + + + + ` + } } - htmlOverview += `
TimeTypeInfo
${cols[0]}${cols[1]}${cols[2]}${cols[3]}${cols[4]}
${timeFormat(cols[col++]-firstTimestamp)}${cols[col++]}${cols[col++]}
${cols[col++]} → ${cols[col++]}
${cols[col++]}
${timeFormat(cols[col++]-firstTimestamp)}${cols[col++]=="t"?"Timeout":"Interval"}${new Number(cols[col++]).toFixed(0)}ms
${cols[col++]}
`; + htmlOverview += ``; + + domContent.innerHTML = htmlOverview; domContent.querySelector("#back").addEventListener("click",event => { show(); diff --git a/apps/powermanager/metadata.json b/apps/powermanager/metadata.json index 92c471fd6..55c7a662e 100644 --- a/apps/powermanager/metadata.json +++ b/apps/powermanager/metadata.json @@ -2,7 +2,7 @@ "id": "powermanager", "name": "Power Manager", "shortName": "Power Manager", - "version": "0.09", + "version": "0.10", "description": "Allow configuration of warnings and thresholds for battery charging and display.", "icon": "app.png", "type": "bootloader", diff --git a/apps/powermanager/settings.js b/apps/powermanager/settings.js index fa186bfac..d2b2097f8 100644 --- a/apps/powermanager/settings.js +++ b/apps/powermanager/settings.js @@ -140,6 +140,19 @@ onchange: v => { writeSettings("logDetails", v); } + }, + 'Clear logs': function (){ + E.showPrompt("Delete logs and reload?").then((v)=>{ + if (v) { + require('Storage').open("powermanager.log","w").erase(); + require("Storage").erase("powermanager.def.json"); + require("Storage").erase("powermanager.hw.json"); + load(); + } else + E.showMenu(submenu_logging); + }).catch(()=>{ + E.showMenu(submenu_logging); + }); } } diff --git a/apps/shadowclk/ChangeLog b/apps/shadowclk/ChangeLog index 5a654d5a1..7ba343b2f 100644 --- a/apps/shadowclk/ChangeLog +++ b/apps/shadowclk/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: New 'Settings Menu' to choose your favorite color and switch between light or dark themes +0.03: New 'Leading Zero' and 'Date Suffix' options in 'Settings Menu' diff --git a/apps/shadowclk/README.md b/apps/shadowclk/README.md index 8b8dd2302..9e78d8e03 100644 --- a/apps/shadowclk/README.md +++ b/apps/shadowclk/README.md @@ -12,14 +12,21 @@ Shadow Clock uses the "Londrina" font in a user selectable color and surrounds i ## Configuration -Anton Clock is configured by the standard settings mechanism of Bangle.js's operating system: +Shadow Clock is configured by the standard settings mechanism of Bangle.js's operating system: Open the `Settings` app, then the `Apps` submenu and below it the `Shadow Clock` menu. -You configure Shadow Clock by selecting a `Light` or `Dark` system wide theme and then selecting the `Color` of the clock numbers. +You can configure Shadow Clock by selecting a `Light` or `Dark` system wide theme, selecting the `Color` of the clock numbers, enabling or disabling the suffix, and enabling or disabling the leading zero. + +### Configuration Options + +* Theme: Choose between `Light` and `Dark` themes. +* Color: Choose the color of the clock numbers from the available options. +* Date Suffix: `Yes` will show "10th" and `No` will show "10". +* Lead Zero: `Yes` to show the leading zero in 24hr mode, `No` to hide. ## Compatibility -Shadow Clock should be fully compatible with with Bangle.js 1 and Bangle.js 2. However, it was built and tested with Bangle.js 2 +Shadow Clock should be fully compatible with with Bangle.js 1 and Bangle.js 2. However, it was built and tested with Bangle.js 2. ## Creator -[stweedo](https://github.com/stweedo) +[stweedo](https://github.com/stweedo) \ No newline at end of file diff --git a/apps/shadowclk/app-icon.js b/apps/shadowclk/app-icon.js index afbb8269a..b11a518c0 100644 --- a/apps/shadowclk/app-icon.js +++ b/apps/shadowclk/app-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEwxH+64Afq17AAlWq2sBYOtB4Urq2BFzeBwONAAeyHAQvCrPX1g3BlYvb02BjwADF4QmBwdZF4MrleBgAvlgAwBgqQBlcAF72HF46PBwaPDwK/dVoLpBAAwMBweDFTYA/AH4A/AH4A/AF2lAAOsBRIAF6AMDp9Wp+lFyNWkYABitWFwoKCAAsHBgMyBAkVwIuO6EVCod6F50A6+BBI0VMRxGEF6BfBIwZqHABRGCo5EIF4cOgEHAAUA0scBYQDDCAIvMhx7CCYTfDAYIvDK4McNoaODh1PPgcO1jsNiskOYQTBh0cjlW1iDHfwJfDjmeoDKEABSlCgAlCF4QJGcgwPEfQ5eMFQIvDRYIfClesioADEgcrSAguEwJeNh1AAgQkBwOlkkkQoOBvQACIgRAB685F4wJBF5oAGp4UJ1i6CeQK/Dg4ECZYQvTComsnNdrp+B0rADlaKD1l6X52BgAACJIgvEFIcO0sOF4cAAgeISgYvLMg8HKoOBAAIvEAAsOL4bwGFxyuDg/QGoYZBT5AKBXQYvVCYUH1gvFqwjGTwWBGA0AOwIAN6EOhytBAwIFBh1PAoN6g8VskVmUAZoelBQVABQIuPMAS6DfYWBMgI9CwMrq2lDA2Bq1PFqLpCHBAWKMIJGBAgRDEABiKBlYTDAYcA1gKBqwgFlYMBLoIaBCIKZDABh+BDIQVBC4WsF4YOBGAmsBQSgCAIIvQFYIiBKwReDFQI3DEIlWFIIsBCwIABF6JhClZhBZQo8BAYIOBIoh0CBIJ5DAH4AEPYKBDWwSGCAoKIDAoIwcEwJ/CRYICCFoI8BQ4YKBMDhWBEwIjBYQcAAYQKBZIRfdAIJVCQgLkDAYJjCcX4A/AH4A/AFwA==")) +require("heatshrink").decompress(atob("mEwxH+64Ae1mBAA+sBgOt1oPBqwJBF7lcsQAEw+HEwWtwetwIvBqwvcsUeAAlDLAOs1tZwfXlcrq0ANIQviFAOtl4viw9DAAlo1l7L4ODR4OIZISPcb4QAGBgOJL4IAg1gAHFUIA/AH4A/AH4A51lkAA+ICI2IBYWB1msxAAH1gvNjkjAA0VDAuICAcVwIGCjgADAoMyF7msoALEF4IWHkYvOioACGYgvFro7FF5JfO6+BqwACioYChwvDNwwvFhwAEqz0RWYcclbjBGIOzWwYvHBIJ6BrruNAAs5JgeBXQMcnOIwM5hxsCR5McwJeZAwMVPgWlL5CaFMCJeFXYR/BJodAF4iKBAARgDrouPcYZeCBANdmVWAoSWBF4fXwIADJIcyF59dLwo+IX4gLFwJKChyQOLwcVlYPOfgNkAAOIJQIvSLxwvGd4c5LwYvPDwlWBQmIAAIvMaoIEDF51kCQUHCQgoCW4aPGFYgzDVRReOF4QJCF4q6EFwcHVRQvDmUOh0rOIusisVY4YRBA4IkDrsVnNAisylYuNDwQACBQ+BQwIREAwgOCq160otOAH4A/ACdWldWVYK/GqwBBAoIKCBAQLDAIIOBFx+sF4QCDcgIJBAgMrlYCBHwIUDGAQPDF6QADGAQvDEQZiCOYQtCHYQDBF6AwBAARcBJIQHEBQaTCAoQACAgTDVFYYGDB5LycXIbdELQQJCAAKJCLoQ0YXIatBYYQGCgDPGAIKMXF4YdCAIZlCGohrEMgKUcACCQBF9oA/AH4Ad")) diff --git a/apps/shadowclk/app.js b/apps/shadowclk/app.js index 45d64e35d..7ffdc4683 100644 --- a/apps/shadowclk/app.js +++ b/apps/shadowclk/app.js @@ -1,95 +1,113 @@ // Clock with large colored digits using the "Londrina" font and a slightly larger "Londrina Shadow" font on top -Graphics.prototype.setFontLondrinaSolid = function() { - // Actual height 59 (64 - 6) - return this.setFontCustom( - E.toString(require('heatshrink').decompress(atob('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'))), - 46, - atob("DyEqHigoJikpJygqEQ=="), - 81 | 65536 - ); - }; - - Graphics.prototype.setFontLondrinaShadow = function() { - // Actual height 63 (67 - 5) - return this.setFontCustom( - E.toString(require('heatshrink').decompress(atob('ADX/8AJH4EQBI9AhwJHkEHBI8QgeABI0IgPABI0EgA8HgUAuAJGgMAnAyHwEcKBH+BI8f/4JHg//KA49CDxATInFgBI/wKA8D4BQHh8AUI88I4IJG/AfBHgsBSgKhGg4QCUIseAYShFGAJbCK4oDC/hXFGgQJEK4I0CBIgmDh5SBEw0/IoYmDgF/AgYmDgLIEvgwDbggmDj4wEXAd/OwkwAYX/RQkYHwT5FhgmCMIkAgwmHDQJNCXYxNBEwoABwAmGKAV/LgYADiJNFQQYmHV4wAa/4ABMwwGCv49FWI8AjgEDhyiGP4SGDWwbGFBIsIAYUQBIkCBJAoDBIooDHYgABnArFcooCCg/4AYToDbwP/BQIyCH4MDRoQHBYoLoDwE/SANwCwSXDvA8D4EDbwUESYdggITCFoKYCQQIJCFoRkDRwYtBHwJaBWweAgItBEQMHAgIRCEYIiBbYJaBAgJQBDAIKCJARSBYYiXFVYw3CS4QJGgYJFjxLDBJAyFBIbUFBIZ8CAAU+ewZXCBIpUDAAP+AgcPDAf8BIcBeAUPAYQACn/wdQPwBIjyDGwgUC/4wEKISPGAAMOR4yRCgwJHjEBR4r9DHIyXCZgwHCPQgACDYI8HBII8HHIMDHg1ABII8GkBvB8EPQgKYCOwMHA4YsChAaFFgUEBIraCgRhIgJ/IKASdFKAaxFKAbFFKAZGHKAxGCKA0A8BQI+BQIuACB//9QQIACRoU/BAn//gJBToQJGAFIzBAYMf/5kBAAM8f4V8gEYLwixBBYKhCPgUYgKUBUIQPDBIShCBIUHBIShCBIUDwIQCHgQJBB4IJCS4T1BB4IaCnAJEuAJCeQT/CuD2CKAToCnAXCKAToCjgJCKAUMAoN+kDyGgf/FYIVBeQYJB2EAbgJQBeQMD/A1BaQJQCwEB8CdB/xQDoAJBH4P5KAY4BBIV4wBQCEgMwJIN4oBQCCAMcBIUgKAkHFoM8DIJQDJoU8DIJQCU4UAjwZBKASdCBIIZBKAR/CgEPNQKhFBIJqBKAQiBVAWAKAY8CBIRQDDAKyC4BQDbwYJBKAcASgIJCKAkGBIXgKAgrCBIJQEbgI7BFwP//5XDAoIJBn//IYUBBIIgBg4JDABV/CQIXBBIngmADBz/wBASsBdoZFDBILtDXgYDBdoiyCBIKcCPoJ/CS4JwCegJ/CUIRjBRgIYCG4KcCXQS1CBIKcBRgKrDGoJQCDYKrCMQMOv/4DAI0BJYUPsEGIgI8CZwMP0EBww8DIIMPyEB8ZVDCwMPxBSBFAJVBgaxBwhDBG4JGBPAWCIYItBIwXAgfBIYItBKoVgFgOAgxvBUwQiB8AWBwYtBBIJVBmA5BEAJQFfYRQDEQIECDYQOBSQQAES4SuCAAd4UIYAELQSXBAAhaCUgQADjwCBZ4QJGQYIJEh4DCMQIJHGQsfAYTNCBIwoFv4EE/4ADDAgID/wJDgYJD+CJGABIgBBI6JBL4qnDSoQAEXYKVCAAbKCeAQJGagRREOAICCAAYQCCwQADEgY0BBI7wCbAkBKA0YGARQFmAzB4BQFEYMH8BQFsDaB6BQFIIMfxBQFAgMfghQEBwU/gUAu//CoQiBvxQBj/AIQKwCvgNCUYcgeYQaCKQUQTgpSChCmIIQK6HIQLYHIQLsHIQYADUYRQBWIxQCYo5QGj5QDgf/+EA///F4MHAgP//AJCKAMBBIX8PgP+EIQRCLwLoFNYREDAAuAvwJHUYIJHj5ECACpiBAAPwL4JCEAAMMKIL9DFgUHBwMMBIShCaAOAgYJCUITQBBwL1CUIfgBwMweQtwBwIoCeQc4WAQFBeQccBwMBIYJQDhwOCKIRQFGQZQFeQxQDeQjmB8E8EIJQDvBQBh48DIIIQBnAfBN4RBBFgIBBFoNwKAQsBAIItBegWAFgIBBFoJGCoBOBAIKBBIwUgFwYJBIwRQDmAJCIwJQDg/8OQRQECwQjCKAMefQiXBKAMPTIQWDKASZCZoRQD2AJDG4JQC5AJDG4RQB8wJGKANxGQZBCKAM4sAJFUISICgEfaASHBOgQJDKALGB/AFBn4JCv//CAJ1BAgIXC/6rB////wJCg//CIM/BIgADgP/FQQAWFIIABBIs/WAMDZQKlGBQJABAAS0ESwQJGgYJIgAeDBIsYAYSpDAAMGAYUgOIqlCmBWFFATeBAAgQCFYYACZwToBGQ7oBAAhbCBgKpBFwUBAYLyBS4KLDSQLyBh4JESYTyB8BhDnBTCg+AEIIHBIwVggeAEIN+gEOLoQ2BOoM/wEHMgY2B4E8oAZBgEMOYVAj0gKAQ4Bh6aBhyIBcYRSB8EQg4jBKAZBBhEDxEAvDJDhyGBMwJaCGAMHLQyhBgeBYwUeS4nAS4RkCNYJBBcIRLBGQdwZoRuCGQU4YYSSBEIccEIQJDgfABYIyBBIcAvhBBJ4MPH4UAj//CIUfCYbnBWwP/AYL3DL4U/C4IAITwICB/6lBAAQ8CJgJiCKwSrDPoTaCUIVAOYZ0BUIUgcQSQCDIUQcQSkCDIS1BHgQXBDISTBGwQRBDITQDXoYZBKAI2CAQRQGCwRQGCARQGHwRQFYQKxCKAhwDn5QEQgd/AQJQCFoUAWwRQCIIUB/wNCwEGIgUD/gJCoEDQYf4BIUggKhCg/wBIUQWgcPbAZQBAAUfLgRQCLAYJDKAJiFKAZiFKAYDCLAZQCC4RYDKARiGKAkHMQZQEh5iDKAkfMQZQELAhQEv5JDKAixCKAqxEOgn/JwpNC/5OFBw40Fj5ZBn4rFv0/gHwgJTEMQPgA4MYLYYjBjoCBgwJFgYCBdocDXItgBIoACFATUEAAIoCBIwoCFYYADKIQJGuDoEGQw/CAAcOAQMwA4YEBg4WDh6GCNAcMBIKEBawQ8BKYMHWwM8G4IvBNwMDwgJBkEAnBaCgPCgEciACBLofhEQMIIwYgBuIgBghGDJYMfAgPMIwbDDGQIJBEoJQBS4oJBgT/GL4KrGS4ahGvCXIMgMAL4IAEHwI8HjwCBHgYhCBITeDFwQJCH4a0BOYaQDvphBn4JCg4eB/geBv42D/EH/kf9/+BIc///4gf/BIgGB+AcBa4IADh57GABYaCGgKvE8BIBgTICGIQEBuCwBbQIJCSAeASYYJCQwNAAoQJDS4MggB7BagkYXQIoCUIcGhC8DBIcDggkEEIUA4QQEoAJCuICB8DyFjARBGQRBBAAMODALGCfgcHCIMOAoJBBIAWcOosPHwPHHgcGBIK/BuAMBHgIWBh+8gE4G4NwCwUHwQ5BG4M4MgUDwI5BG4JGCsEB4GAh43BIwRLB8HAg4RBg5qCBYIgBcALQCDAMcR4YjBKATkEKAS/DAAZQBcYIJFvCrFAAU8UIoACUIwACjwCBbIIAEh4CBgQJIHg0PAwQyFBIZvBAAcfBIRtFv4FD/6CCgP/DAn/AAX+BIcDBIf8JgoJCSoIAig5DCv/wBIbMBK4NgfwIACR4LGBkDyCMIUAnCxBOouAXgMIeQQACoEOXYRcEEgQrDAAQkCFYYACEgYrCAAQkDFYRQEMIMgbwaXC/DTB/5QEn6pBWAJQEh4FCWwwAFA'))), - 46, - atob("DyEqHigoJikpJygqEQ=="), - 81 | 65536 - ); - }; - - Graphics.prototype.setFontDotGothic16 = function() { - // Actual height 20 (19 - 0) - return this.setFontCustom( - E.toString(require('heatshrink').decompress(atob('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'))), - 32, - atob("CgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoA"), - 20|65536 - ); - }; - - let storage = require('Storage'); - - var settings = Object.assign({ - // default values - color: "#0ff", - theme: "light", - }, storage.readJSON("shadowclk.json", true) || {}); - - (function() { - let drawTimeout; - - function draw() { - var x = g.getWidth() / 2; - var y = g.getHeight() / 2; - g.reset().clearRect(Bangle.appRect); - var date = new Date(); - var hour = String(date.getHours()).padStart(2, '0'); - if (hour[0] === '0') hour = hour[1]; - var minutes = String(date.getMinutes()).padStart(2, '0'); - var timeStr = hour + ':' + minutes; - var color = settings.color; - g.setFontAlign(0, 0).setFont("LondrinaSolid").setColor(color).drawString(timeStr, x - 1, y); - g.reset().setFontAlign(0, 0).setFont("LondrinaShadow").drawString(timeStr, x - 1, y); - - var locale = require("locale"); - var dayOfMonth = date.getDate(); - var month = locale.month(date, 1).slice(0, 1).toUpperCase() + locale.month(date, 1).slice(1).toLowerCase(); - var year = date.getFullYear(); - var dayOfMonthStr = dayOfMonth.toString(); - if (dayOfMonth === 1 || dayOfMonth === 21 || dayOfMonth === 31) { - dayOfMonthStr += "st"; - } else if (dayOfMonth === 2 || dayOfMonth === 22) { - dayOfMonthStr += "nd"; - } else if (dayOfMonth === 3 || dayOfMonth === 23) { - dayOfMonthStr += "rd"; - } else { - dayOfMonthStr += "th"; +Graphics.prototype.setFontLondrinaSolid = function () { + // Actual height 59 (64 - 6) + return this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('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'))), + 46, + atob("DyEqHigoJikpJygqEQ=="), + 81 | 65536 + ); +}; + +Graphics.prototype.setFontLondrinaShadow = function () { + // Actual height 63 (67 - 5) + return this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('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'))), + 46, + atob("DyEqHigoJikpJygqEQ=="), + 81 | 65536 + ); +}; + +Graphics.prototype.setFontRighteous = function () { + // Actual height 16 (15 - 0) + return this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('ABN//ABBv0QA4N4AIVwAogACoEC+EH+EP4EOAovACIcPsEfsF7vABBjfghPAjEAv0As0Yv14n/gg+Aj34vP4sPYgH4gFgEgMP8Ef+F7nABBjcwjEwIwZNFEYM//H//3wn3AgwLBwEC8EO/3+v/8E4IfDmMAn+A/8A/0AAYIHBncAGQMDAIUHgEf4ABBBIYAEjwBChgGBB4YBJAAU4AIS1BhAFBgP4h/wHoKPBXYIlDj/gn/wuF4uE4sEYAYLXBBYK9BSwLhDAIKODuP4uYBB3ABDv84n84jgSCEoNzAIIRFFoUc8DFBv/gAIN+A4IBCWoIBBj7XEFoIBDu4BCH4XwsPggPAdIItBf4N+nFOnEOnB/Bg4tBwAmBQINwOoP4u5rB4CNEsEdS4ILBLIMYAIIFBBIINBgFAhznCn/AuPIuHYRIN7N4TnBZQcHnABBgTLBXoOeAIMBdYUHgEPwAZBnvgnHgiFAD4cNwABLCIcwsAbBdILbBg+AeIJoCPIfYTIR7BgABBv8AnwTCgIZB8EHuEP7ABE4EOoEH4CzBCoLNBQ4N48FwAIjvCboYACBIIBDuapBAIQJDnypBQYKBBcIU4vFwnABHmEwFI4PFXoSBCHpoVBsEYCJGAAISZDJY14JZD7BmJhCE48DVIIBEBYYVJgE4AIYJC+F/MYQVHJIXAJISBCoEIFJoBEgDRBToRxCU4wJECYI/Jn8AWIMD8EBG4rjQUILTNuHgAId/EYPAj+AFZ0+vF+n/2FaBhD7ArBjiHBXIM4ue4AI34uLtBoCLDS5YBBHIRbBDIN4dJXgCYNggF+C4JvB/CjBgZLBCIOADwNAFIYJBc4jxJF4YXBoEIRYN8E4SHBToa/BBoIRBvgtBAIOAgfAIIJDCwAJBBoIRBsAnCToKJBGoW4vzEBnFgIIMA//+AIfgh3AgxhBRoJ1BcoMH+AxBLYUACIIVBAIIdDBYMYCAQdBAYMQQoUAgYBMAAUwDY0BHYUP/EOnEOjADBA4ILDWYR5BWoPwCYQBECYMDboQnIAIUPnEGmA3NcogjH/ADBh+4EYcMTIK3C//49kA5gbHnYBCn8P/8H/0P/iFDaoQTBgABDPYRBB/EgCIgFBkEPv/+v/8A4IhFgPggY9Ch14hiwBhARFHYZlCHYgHFNYJBDgYZCB45THXJjhCZoaBC/7fHCYYjBEp3/EIJjLJoIBBgcYh/YAIMMAIf4AIVwCIN/8D3DSoMMjABBh+Ah4/CgChBAIQ1DHYKRCCYUBQISFBh/Ah66CAoQjGEIgHFCIPAhEIOYV4TIaBDJZE7gE/AIMP/0P/hLChBlCGYQBCNYUAjEDRoOAv/8//+/n+8EOAIIfBBIIBBv/+4EGBoYJC/kHwAfBCoMHAIgrBAYMGgA='))), + 32, + atob("BAQHCQcLCAQGBgkIBAgECAoFCQkKCQoICgoEBAgJCAoKCwsKCgkJCwsEBwoJDQsMCgwKCgkLCg0KCgkGCAYFCgQKCggKCAYKCQQECQQMCQkKCgcIBwkJDAgJCAcEBwcA"), + 16 | 65536 + ); +}; + +let appSettings = require("Storage").readJSON("shadowclk.json", 1) || {}; +let settings = require("Storage").readJSON("setting.json", 1) || {}; +let is12Hour = settings["12hour"] !== undefined ? settings["12hour"] : false; +let color = appSettings.color !== undefined ? appSettings.color : "#0ff"; +let enableLeadingZero = appSettings.enableLeadingZero !== undefined ? appSettings.enableLeadingZero : false; +let enableSuffix = appSettings.enableSuffix !== undefined ? appSettings.enableSuffix : true; + +// Draw the time and date +(function () { + let drawTimeout; + + function draw() { + var x = g.getWidth() / 2; + var y = g.getHeight() / 2; + g.reset().clearRect(Bangle.appRect); + var date = new Date(); + var hour = date.getHours(); + var minutes = String(date.getMinutes()).padStart(2, '0'); + + // Handle 12-hour format + if (is12Hour) { + hour = hour % 12 || 12; // Convert 0 to 12 for 12-hour format + } else { + // If the leading zero option is enabled and hour is less than 10, add leading zero + if (enableLeadingZero && hour < 10) { + hour = '0' + hour; } - var dayOfWeek = locale.dow(date, 0).slice(0, 1).toUpperCase() + locale.dow(date, 0).slice(1).toLowerCase(); - var dateStr = month + " " + dayOfMonthStr + ", " + year + "\n" + dayOfWeek; - g.setFontAlign(0, 0).setFont("DotGothic16").drawString(dateStr, x, y + 48); - - if (drawTimeout) clearTimeout(drawTimeout); - drawTimeout = setTimeout(() => { - drawTimeout = undefined; - draw(); - }, 60000 - (Date.now() % 60000)); } - - Bangle.setUI({ - mode: "clock", - remove: function() { - if (drawTimeout) clearTimeout(drawTimeout); - drawTimeout = undefined; - delete Graphics.prototype.setFontLondrinaSolid; - delete Graphics.prototype.setFontLondrinaShadow; - delete Graphics.prototype.setFontDotGothic16; + + var timeStr = hour + ':' + minutes; + + // Handle midnight in 12-hour format specifically + if (is12Hour && hour === 0) { + timeStr = '12' + timeStr.substring(2); + } + + g.setFontAlign(0, 0).setFont("LondrinaSolid").setColor(color).drawString(timeStr, x - 1, y); + g.reset().setFontAlign(0, 0).setFont("LondrinaShadow").drawString(timeStr, x - 1, y); + + var locale = require("locale"); + var dayOfMonth = date.getDate(); + var month = locale.month(date, 1).slice(0, 1).toUpperCase() + locale.month(date, 1).slice(1).toLowerCase(); + var year = date.getFullYear(); + var dayOfMonthStr = dayOfMonth.toString(); + if (enableSuffix) { + var suffix = ""; + if (dayOfMonth === 1 || dayOfMonth === 21 || dayOfMonth === 31) { + suffix = "st"; + } else if (dayOfMonth === 2 || dayOfMonth === 22) { + suffix = "nd"; + } else if (dayOfMonth === 3 || dayOfMonth === 23) { + suffix = "rd"; + } else { + suffix = "th"; } - }); - Bangle.loadWidgets(); - draw(); - setTimeout(Bangle.drawWidgets, 0); - })(); + dayOfMonthStr += suffix; + } + var dayOfWeek = locale.dow(date, 0).slice(0, 1).toUpperCase() + locale.dow(date, 0).slice(1).toLowerCase(); + var dateStr = month + " " + dayOfMonthStr + ", " + year + "\n" + dayOfWeek; + g.setFontAlign(0, 0).setFont("Righteous").drawString(dateStr, x, y + 56); + + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(() => { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); + } + Bangle.setUI({ + mode: "clock", + remove: function () { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + delete Graphics.prototype.setFontLondrinaSolid; + delete Graphics.prototype.setFontLondrinaShadow; + delete Graphics.prototype.setFontRighteous; + } + }); + Bangle.loadWidgets(); + draw(); + setTimeout(Bangle.drawWidgets, 0); +})(); \ No newline at end of file diff --git a/apps/shadowclk/app.png b/apps/shadowclk/app.png index c50f7ed61..a8f7d05ac 100644 Binary files a/apps/shadowclk/app.png and b/apps/shadowclk/app.png differ diff --git a/apps/shadowclk/icon.png b/apps/shadowclk/icon.png deleted file mode 100644 index 447dffb51..000000000 Binary files a/apps/shadowclk/icon.png and /dev/null differ diff --git a/apps/shadowclk/interface.html b/apps/shadowclk/interface.html index fd29b7cee..53d206ecd 100644 --- a/apps/shadowclk/interface.html +++ b/apps/shadowclk/interface.html @@ -6,15 +6,30 @@ - - 3-Bit Color Picker + + Shadow Clock Customizer -
-

3-Bit Color Picker

+

Clock Customizer

- +
+
+ + +
+
+ + +
+
- +
+ +
- + \ No newline at end of file diff --git a/apps/shadowclk/metadata.json b/apps/shadowclk/metadata.json index e1debfb6d..4e47b9845 100644 --- a/apps/shadowclk/metadata.json +++ b/apps/shadowclk/metadata.json @@ -1,7 +1,7 @@ { "id": "shadowclk", "name": "Shadow Clock", - "version": "0.02", + "version": "0.03", "description": "A simple clock using the Londrina font in color with a shadowed outline. Based on the Anton Clock.", "icon": "app.png", "screenshots": [{ diff --git a/apps/shadowclk/screenshot-1.png b/apps/shadowclk/screenshot-1.png index 2d4933110..2ebc7a65c 100644 Binary files a/apps/shadowclk/screenshot-1.png and b/apps/shadowclk/screenshot-1.png differ diff --git a/apps/shadowclk/screenshot.png b/apps/shadowclk/screenshot.png index 790294ca3..996be6384 100644 Binary files a/apps/shadowclk/screenshot.png and b/apps/shadowclk/screenshot.png differ diff --git a/apps/shadowclk/settings.js b/apps/shadowclk/settings.js index fd16d92e2..384710318 100644 --- a/apps/shadowclk/settings.js +++ b/apps/shadowclk/settings.js @@ -1,18 +1,17 @@ (function(back) { let teletextColors = ["#000", "#f00", "#0f0", "#ff0", "#00f", "#f0f", "#0ff", "#fff"]; let teletextColorNames = ["Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White"]; + let sysSettings = require('Storage').readJSON("setting.json", 1) || {}; // Load and set default settings let appSettings = Object.assign({ color: teletextColors[6], theme: 'light', + enableSuffix: true, + enableLeadingZero: false, + enable12Hour: '24hour' // default time mode }, require('Storage').readJSON("shadowclk.json", true) || {}); - // Save settings to storage - function writeSettings() { - require('Storage').writeJSON("shadowclk.json", appSettings); - } - // Colors from 'Light BW' and 'Dark BW' themes function createThemeColors(mode) { let cl = x => g.setColor(x).getColor(); @@ -66,15 +65,36 @@ // Read the current system theme function getCurrentTheme() { - let s = require('Storage').readJSON("setting.json", 1) || {}; - if (!s.theme) { + if (!sysSettings.theme) { return appSettings.theme; // fallback to appSettings.theme (light or dark) } - return s.theme.dark ? 'dark' : 'light'; + return sysSettings.theme.dark ? 'dark' : 'light'; + } + + // Read the current time mode + function getCurrentTimeMode() { + if (!sysSettings['12hour']) { + return appSettings.enable12Hour; // fallback to appSettings.enable12Hour + } + return sysSettings['12hour'] ? '12hour' : '24hour'; + } + + // Save settings to storage + function writeSettings() { + appSettings.enable12Hour = appSettings.enable12Hour === '12hour' ? '12hour' : '24hour'; + require('Storage').writeJSON("shadowclk.json", appSettings); + } + + // Save time mode to system settings + function writeTimeModeSetting() { + sysSettings['12hour'] = appSettings.enable12Hour === '12hour'; + require('Storage').writeJSON("setting.json", sysSettings); } function showMenu() { appSettings.theme = getCurrentTheme(); + appSettings.enable12Hour = getCurrentTimeMode(); + E.showMenu({ "": { "title": "Shadow Clock" @@ -96,9 +116,34 @@ writeSettings(); }, format: v => teletextColorNames[v] + }, + 'Date Suffix:': { + value: appSettings.enableSuffix, + format: v => v ? 'Yes' : 'No', + onchange: v => { + appSettings.enableSuffix = v; + writeSettings(); + } + }, + 'Lead Zero:': { + value: appSettings.enableLeadingZero, + format: v => v ? 'Yes' : 'No', + onchange: v => { + appSettings.enableLeadingZero = v; + writeSettings(); + } + }, + 'Time Mode:': { + value: (appSettings.enable12Hour === '12hour'), + format: v => v ? '12 Hr' : '24 Hr', + onchange: v => { + appSettings.enable12Hour = v ? '12hour' : '24hour'; + writeSettings(); + writeTimeModeSetting(); + } } }); } // Initially show the menu showMenu(); -}); +}); \ No newline at end of file diff --git a/apps/widminbat/ChangeLog b/apps/widminbat/ChangeLog index 1d06820f6..d4143d802 100644 --- a/apps/widminbat/ChangeLog +++ b/apps/widminbat/ChangeLog @@ -1,3 +1,3 @@ 0.01: Initial Version: Display at under 30% battery 0.02: Display while charging - +0.03: Do not clear outside of widget bar diff --git a/apps/widminbat/metadata.json b/apps/widminbat/metadata.json index 9c72fa38f..7aa200282 100644 --- a/apps/widminbat/metadata.json +++ b/apps/widminbat/metadata.json @@ -1,7 +1,7 @@ { "id": "widminbat", "name": "Minimal Battery", "shortName":"MinBat", - "version":"0.02", + "version":"0.03", "description": "A minimal version of the battery widget that only appears if the battery is running low (below 30%)", "icon": "widget.png", "type": "widget", diff --git a/apps/widminbat/widget.js b/apps/widminbat/widget.js index 8274b9f38..27453f7cd 100644 --- a/apps/widminbat/widget.js +++ b/apps/widminbat/widget.js @@ -8,7 +8,7 @@ var bat = E.getBattery(); var x = this.x, y = this.y; g.reset(); - g.clearRect(x,y,x+s,y+24); + g.clearRect(x,y,x+s,y+23); g.setColor(g.theme.fg).fillRect(x,y+2,x+s-4,y+21).clearRect(x+2,y+4,x+s-6,y+19).fillRect(x+s-3,y+10,x+s,y+14); var barWidth = bat*(s-12)/100; var color = bat < 15 ? "#f00" : (bat <= 30 ? "#f80" : "#0f0"); diff --git a/typescript/types/sched.d.ts b/typescript/types/sched.d.ts index 6dd25e666..78ad0c8d4 100644 --- a/typescript/types/sched.d.ts +++ b/typescript/types/sched.d.ts @@ -9,30 +9,67 @@ declare module Sched { SAT = 64, } - type VibratePattern = "." | "," | "-" | ":" | ";" | "="; + type Dows = number; + type Milliseconds = number; + + // slight hack - all objects have a `on()`, this unions with that type so we can add it to an object + type OnBoolean + = T | Object["on"]; + + type VibratePattern = string; // "." | "," | "-" | ":" | ";" | "=" + + type DateString = `${number}-${number}-${number}`; + + type NewSched = { + msg?: string, + appid?: string, + dow?: Dows, + on?: OnBoolean, + js?: string, + } & (NewTimer | NewAlarm); + + type NewTimer = { timer: number }; + type NewAlarm = { t: number, date?: DateString }; + + type DefaultSched = { + on: OnBoolean, + del: boolean, + rp: false, + as: false, + dow: Dows, + last: number, + vibrate: VibratePattern, + }; + + type DefaultAlarm = DefaultSched & { t: number }; + + type DefaultTimer = DefaultSched & { timer: number }; type Sched = { - id?: string, - appid?: string, - on: boolean, - dow?: number, + // from NewSched / set in setAlarm() msg: string, - last: number, + appid?: string, + dow: Dows, + on: OnBoolean, + timer?: Milliseconds, // this is a timer + + // setAlarm adds: + id: string, + t: Milliseconds, // time of day since midnight (in ms) + + // optional NewSched vibrate?: VibratePattern, hidden?: boolean, as?: boolean, // auto snooze del?: boolean, js?: string, data?: unknown, + + // set by sched + last?: number, } & ( { - t: number, // time of day since midnight (in ms, set automatically when timer starts) - } | { - timer: number, // this is a timer - the time in ms - } - ) & ( - { - date: `${number}-${number}-${number}`, + date: DateString, rp?: Repeat, } | { date: undefined, @@ -62,18 +99,18 @@ declare module Sched { function getAlarm(id: string): Sched | undefined; - function getActiveAlarms (alarms: Sched[], time?: Date): Sched[]; + function getActiveAlarms(alarms: Sched[], time?: Date): Sched[]; - function setAlarm(id: string, alarm?: Sched): void; + function setAlarm(id: string, alarm?: NewSched): void; - function getTimeToAlarm(alarm: Sched, time?: Date): number | undefined; + function getTimeToAlarm(alarm: Sched | undefined | null, time?: Date): number | undefined; function getTimeToAlarm(alarm?: undefined | null, time?: Date): undefined; function reload(): void; - function newDefaultAlarm(): Sched; + function newDefaultAlarm(): DefaultAlarm; - function newDefaultTimer(): Sched; + function newDefaultTimer(): DefaultTimer; function getSettings(): SchedSettings;