diff --git a/apps/boot/ChangeLog b/apps/boot/ChangeLog index 05b7050c4..7b95d8686 100644 --- a/apps/boot/ChangeLog +++ b/apps/boot/ChangeLog @@ -56,4 +56,6 @@ 0.50: Allow setting of screen rotation Remove support for 2v11 and earlier firmware 0.51: Remove patches for 2v10 firmware (BEEPSET and setUI) - Add patch to ensure that compass heading is corrected + Add patch to ensure that compass heading is corrected on pre-2v15.68 firmware + Ensure clock is only fast-loaded if it doesn't contain widgets +0.52: Ensure heading patch for pre-2v15.68 firmware applies to getCompass diff --git a/apps/boot/bootloader.js b/apps/boot/bootloader.js index 1e0d22645..6e6466f48 100644 --- a/apps/boot/bootloader.js +++ b/apps/boot/bootloader.js @@ -1,5 +1,10 @@ // This runs after a 'fresh' boot var s = require("Storage").readJSON("setting.json",1)||{}; +/* If were being called from JS code in order to load the clock quickly (eg from a launcher) +and the clock in question doesn't have widgets, force a normal 'load' as this will then +reset everything and remove the widgets. */ +if (global.__FILE__ && !s.clockHasWidgets) {load();throw "Clock has no widgets, can't fast load";} +// Otherwise continue to try and load the clock var _clkApp = require("Storage").read(s.clock); if (!_clkApp) { _clkApp = require("Storage").list(/\.info$/) @@ -13,7 +18,8 @@ if (!_clkApp) { .sort((a, b) => a.sortorder - b.sortorder)[0]; if (_clkApp){ s.clock = _clkApp.src; - _clkApp = require("Storage").read(_clkApp.src); + _clkApp = require("Storage").read(_clkApp.src); + s.clockHasWidgets = _clkApp.includes("Bangle.loadWidgets"); require("Storage").writeJSON("setting.json", s); } } diff --git a/apps/boot/bootupdate.js b/apps/boot/bootupdate.js index d6fc3011c..e9a24f5f5 100644 --- a/apps/boot/bootupdate.js +++ b/apps/boot/bootupdate.js @@ -79,7 +79,8 @@ if (s.rotate) boot+=`g.setRotation(${s.rotate&3},${s.rotate>>2});\n` // screen r // ================================================== FIXING OLDER FIRMWARES // 2v15.68 and before had compass heading inverted. if (process.version.replace("v","")<215.68) - boot += `Bangle.on('mag',e=>{if(!isNaN(e.heading)) e.heading=360-e.heading;});`; + boot += `Bangle.on('mag',e=>{if(!isNaN(e.heading))e.heading=360-e.heading;}); +Bangle.getCompass=(c=>(()=>{e=c();if(!isNaN(e.heading))e.heading=360-e.heading;return e;}))(Bangle.getCompass);`; // ================================================== BOOT.JS // Append *.boot.js files diff --git a/apps/boot/metadata.json b/apps/boot/metadata.json index f94d25150..339f8503e 100644 --- a/apps/boot/metadata.json +++ b/apps/boot/metadata.json @@ -1,7 +1,7 @@ { "id": "boot", "name": "Bootloader", - "version": "0.51", + "version": "0.52", "description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings", "icon": "bootloader.png", "type": "bootloader", diff --git a/apps/iconlaunch/ChangeLog b/apps/iconlaunch/ChangeLog index b47cd6590..eca18b06c 100644 --- a/apps/iconlaunch/ChangeLog +++ b/apps/iconlaunch/ChangeLog @@ -7,3 +7,4 @@ 0.07: Read app icons on demand Add swipe-to-exit 0.08: Only use fast loading for switching to clock to prevent problems in full screen apps +0.09: Remove fast load option since clocks containing Bangle.loadWidgets are now always normally loaded diff --git a/apps/iconlaunch/README.md b/apps/iconlaunch/README.md index 1bee0b8d0..0d36fdeb4 100644 --- a/apps/iconlaunch/README.md +++ b/apps/iconlaunch/README.md @@ -10,7 +10,3 @@ This launcher shows 9 apps per screen, making it much faster to navigate versus ## Technical note The app uses `E.showScroller`'s code in the app but not the function itself because `E.showScroller` doesn't report the position of a press to the select function. - -### Fastload option - -Fastload clears up the memory used by the launcher and directly evals the code of the clock to load. diff --git a/apps/iconlaunch/app.js b/apps/iconlaunch/app.js index 6bd9cf7ac..8d2f1ec4c 100644 --- a/apps/iconlaunch/app.js +++ b/apps/iconlaunch/app.js @@ -1,6 +1,6 @@ { const s = require("Storage"); - const settings = s.readJSON("launch.json", true) || { showClocks: true, fullscreen: false,direct:false,swipeExit:false,oneClickExit:false,fastload:false }; + const settings = s.readJSON("launch.json", true) || { showClocks: true, fullscreen: false,direct:false,swipeExit:false,oneClickExit:false}; if (!settings.fullscreen) { Bangle.loadWidgets(); Bangle.drawWidgets(); @@ -180,24 +180,20 @@ }; const returnToClock = function() { - if (settings.fastload == true){ - Bangle.setUI(); - delete launchCache; - delete launchHash; - delete drawItemAuto; - delete drawText; - delete selectItem; - delete onDrag; - delete drawItems; - delete drawItem; - delete returnToClock; - delete idxToY; - delete YtoIdx; - delete settings; - setTimeout(eval, 0, s.read(".bootcde")); - } else { - load(); - } + Bangle.setUI(); + delete launchCache; + delete launchHash; + delete drawItemAuto; + delete drawText; + delete selectItem; + delete onDrag; + delete drawItems; + delete drawItem; + delete returnToClock; + delete idxToY; + delete YtoIdx; + delete settings; + setTimeout(eval, 0, s.read(".bootcde")); }; diff --git a/apps/iconlaunch/metadata.json b/apps/iconlaunch/metadata.json index c2335579f..e310ede4d 100644 --- a/apps/iconlaunch/metadata.json +++ b/apps/iconlaunch/metadata.json @@ -2,7 +2,7 @@ "id": "iconlaunch", "name": "Icon Launcher", "shortName" : "Icon launcher", - "version": "0.08", + "version": "0.09", "icon": "app.png", "description": "A launcher inspired by smartphones, with an icon-only scrollable menu.", "tags": "tool,system,launcher", diff --git a/apps/iconlaunch/settings.js b/apps/iconlaunch/settings.js index 02b65784f..49a49f700 100644 --- a/apps/iconlaunch/settings.js +++ b/apps/iconlaunch/settings.js @@ -5,8 +5,7 @@ fullscreen: false, direct: false, oneClickExit: false, - swipeExit: false, - fastload: false + swipeExit: false }, require("Storage").readJSON("launch.json", true) || {}); let fonts = g.getFonts(); @@ -36,10 +35,6 @@ /*LANG*/"Swipe exit": { value: settings.swipeExit == true, onchange: m => { save("swipeExit", m) } - }, - /*LANG*/"Fastload": { - value: settings.fastload == true, - onchange: (m) => { save("fastload", m) } } }; E.showMenu(appMenu); diff --git a/apps/launch/app.js b/apps/launch/app.js index 05f5bef43..3b33e530a 100644 --- a/apps/launch/app.js +++ b/apps/launch/app.js @@ -66,7 +66,7 @@ E.showScroller({ }); g.flip(); // force a render before widgets have finished drawing -function returnToClock() { +let returnToClock = function() { // unload everything manually // ... or we could just call `load();` but it will be slower Bangle.setUI(); // remove scroller's handling @@ -85,7 +85,7 @@ if (process.env.HWVERSION==2) { // 10s of inactivity goes back to clock Bangle.setLocked(false); // unlock initially let lockTimeout; -function lockHandler(locked) { +let lockHandler = function(locked) { if (lockTimeout) clearTimeout(lockTimeout); lockTimeout = undefined; if (locked) diff --git a/apps/setting/ChangeLog b/apps/setting/ChangeLog index 25adad359..cd97e1dda 100644 --- a/apps/setting/ChangeLog +++ b/apps/setting/ChangeLog @@ -57,4 +57,4 @@ 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 diff --git a/apps/setting/metadata.json b/apps/setting/metadata.json index 3a3094a4e..47d0a309f 100644 --- a/apps/setting/metadata.json +++ b/apps/setting/metadata.json @@ -1,7 +1,7 @@ { "id": "setting", "name": "Settings", - "version": "0.52", + "version": "0.53", "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 5d3bd3705..d7bb060ea 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -39,6 +39,7 @@ function resetSettings() { timezone: 0, // Set the timezone for the device HID: false, // BLE HID mode, off by default clock: null, // a string for the default clock's name + // clockHasWidgets: false, // Does the clock in 'clock' contain the string 'Bangle.loadWidgets' "12hour" : false, // 12 or 24 hour clock? firstDayOfWeek: 0, // 0 -> Sunday (default), 1 -> Monday brightness: 1, // LCD brightness from 0 to 1 @@ -674,11 +675,10 @@ function showClockMenu() { label = "* " + label; } clockMenu[label] = () => { - if (settings.clock !== app.src) { - settings.clock = app.src; - updateSettings(); - showMainMenu(); - } + settings.clock = app.src; + settings.clockHasWidgets = require("Storage").read(app.src).includes("Bangle.loadWidgets"); + updateSettings(); + showMainMenu(); }; }); if (clockApps.length === 0) { @@ -703,11 +703,9 @@ function showLauncherMenu() { label = "* " + label; } launcherMenu[label] = () => { - if (settings.launcher !== app.src) { - settings.launcher = app.src; - updateSettings(); - showMainMenu(); - } + settings.launcher = app.src; + updateSettings(); + showMainMenu(); }; }); if (launcherApps.length === 0) { diff --git a/apps/slopeclock/ChangeLog b/apps/slopeclock/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/slopeclock/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/slopeclock/app-icon.js b/apps/slopeclock/app-icon.js new file mode 100644 index 000000000..528758a7a --- /dev/null +++ b/apps/slopeclock/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4X/AAMfBIWSyhL/ACkD1cAlWq1WABYk6BYnABQcO1QLa3//FwgLEHIoABlQLO3WsBZHqHYwLGEooLF0ALHnW/BZMDDII8DmoLDAAILDmtVBZHV+oLEmEA1Ws6vAgNVh5EB+Eq/2lq4cCqqPBIYMqytVoALCioLD6tVBYMDqALEr4KBqotBqkAgsP/9VvkMhoLBhoLCKwQrCIoQLCBQITBM4QPB+ALBFYYLBhpHDBY0BIoILFuEPBYNcBY1XP4fAmgLENIYDBI4JGCNIlXqp3CCof/F4UPIIILEI4wtEIQVwAYIzCO4oLCSgIXFqpfCIwjTBCQXAEQgA/AAoA=")) diff --git a/apps/slopeclock/app.js b/apps/slopeclock/app.js new file mode 100644 index 000000000..621a61376 --- /dev/null +++ b/apps/slopeclock/app.js @@ -0,0 +1,117 @@ +Graphics.prototype.setFontPaytoneOne = function(scale) { + // Actual height 81 (91 - 11) + this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('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'))), + 46, + atob("ITZOMzs7SDxHNUdGIQ=="), + 113+(scale<<8)+(1<<16) + ); + return this; +}; + +{ // must be inside our own scope here so that when we are unloaded everything disappears + // we also define functions using 'let fn = function() {..}' for the same reason. function decls are global +let drawTimeout; + +let g2 = Graphics.createArrayBuffer(g.getWidth(),90,1,{msb:true}); +let g2img = { + width:g2.getWidth(), height:g2.getHeight(), bpp:1, + buffer:g2.buffer, transparent:0 +}; +const slope = 20; +const offsy = 20; // offset of numbers from middle +const fontBorder = 4; // offset from left/right +const slopeBorder = 10, slopeBorderUpper = 4; // fudge-factor to move minutes down from slope +let R,x,y; // middle of the clock face +let dateStr = ""; +let bgColors = g.theme.dark ? ["#ff0","#0ff","#f0f"] : ["#f00","#0f0","#00f"]; +let bgColor = bgColors[(Math.random()*bgColors.length)|0]; + + +// Draw the hour, and the minute into an offscreen buffer +let draw = function() { + R = Bangle.appRect; + 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); + dateStr = require("locale").dow(date, 1).toUpperCase()+ " "+ + require("locale").date(date, 0).toUpperCase(); + + // Draw hour + g.reset().clearRect(R); // clear whole background (w/o widgets) + g.setFontAlign(-1, 0).setFont("PaytoneOne"); + g.drawString(hourStr, fontBorder, y-offsy); + // add slope in background color + g.setColor(g.theme.bg).fillPoly([0,y+slope-slopeBorderUpper, R.w,y-slope-slopeBorderUpper, + R.w,y-slope, 0,y+slope]); + // Draw minute to offscreen buffer + g2.setColor(0).fillRect(0,0,g2.getWidth(),g2.getHeight()).setFontAlign(1, 0).setFont("PaytoneOne"); + g2.setColor(1).drawString(minStr, g2.getWidth()-fontBorder, g2.getHeight()/2); + g2.setColor(0).fillPoly([0,0, g2.getWidth(),0, 0,slope*2]); + // start the animation *in* + animate(true); + + // queue next draw + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + animate(false, function() { + draw(); + }); + }, 60000 - (Date.now() % 60000)); +}; + +let isAnimIn = true; +let animInterval; +// Draw *just* the minute image +let drawMinute = function() { + var yo = slopeBorder + offsy + y - 2*slope*minuteX/R.w; + // draw over the slanty bit + g.setColor(bgColor).fillPoly([0,y+slope, R.w,y-slope, R.w,R.h+R.y, 0,R.h+R.y]); + // draw the minutes + g.setColor(g.theme.bg).drawImage(g2img, x+minuteX-(g2.getWidth()/2), yo-(g2.getHeight()/2)); +}; +let animate = function(isIn, callback) { + if (animInterval) clearInterval(animInterval); + isAnimIn = isIn; + minuteX = isAnimIn ? -g2.getWidth() : 0; + drawMinute(); + animInterval = setInterval(function() { + minuteX += 8; + let stop = false; + if (isAnimIn && minuteX>=0) { + minuteX=0; + stop = true; + } else if (!isAnimIn && minuteX>=R.w) + stop = true; + drawMinute(); + if (stop) { + clearInterval(animInterval); + animInterval=undefined; + if (callback) callback(); + if (isAnimIn) { + // draw the date + g.setColor(g.theme.bg).setFontAlign(0, 0).setFont("6x15").drawString(dateStr, R.x + R.w/2, R.y+R.h-9);} + } + }, 20); +}; + + +// Show launcher when middle button pressed +Bangle.setUI({ + mode : "clock", + remove : function() { + // Called to unload all of the clock app + if (animInterval) clearInterval(animInterval); + animInterval = undefined; + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + delete Graphics.prototype.setFontPaytoneOne; + }}); +// Load widgets +Bangle.loadWidgets(); +draw(); +setTimeout(Bangle.drawWidgets,0); +} diff --git a/apps/slopeclock/app.png b/apps/slopeclock/app.png new file mode 100644 index 000000000..d72b8faf9 Binary files /dev/null and b/apps/slopeclock/app.png differ diff --git a/apps/slopeclock/metadata.json b/apps/slopeclock/metadata.json new file mode 100644 index 000000000..d880a0cf6 --- /dev/null +++ b/apps/slopeclock/metadata.json @@ -0,0 +1,14 @@ +{ "id": "slopeclock", + "name": "Slope Clock", + "version":"0.01", + "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"}], + "type": "clock", + "tags": "clock", + "supports" : ["BANGLEJS2"], + "storage": [ + {"name":"slopeclock.app.js","url":"app.js"}, + {"name":"slopeclock.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/slopeclock/screenshot.png b/apps/slopeclock/screenshot.png new file mode 100644 index 000000000..4a59f5c4a Binary files /dev/null and b/apps/slopeclock/screenshot.png differ