diff --git a/apps/about/app-bangle2.js b/apps/about/app-bangle2.js index 0c40314a8..471b0670f 100644 --- a/apps/about/app-bangle2.js +++ b/apps/about/app-bangle2.js @@ -10,7 +10,7 @@ var img = atob("sIwDkm2S66DYwA2AAAAAHAHGSRxJEkAAgmGGBxDIADIdAFJIbAHF9HP00kBUC6Dt var imgHeight = g.imageMetrics(img).height; var imgScroll = Math.floor(Math.random()*imgHeight); -g.reset().setFont("6x15").setFontAlign(0,0); +g.clear(1).setFont("6x15").setFontAlign(0,0); g.drawString(ENV.VERSION + " " + NRF.getAddress(), g.getWidth()/2, 171); g.drawImage(img,0,24); diff --git a/apps/aiclock/ChangeLog b/apps/aiclock/ChangeLog index 759f68777..68f77be10 100644 --- a/apps/aiclock/ChangeLog +++ b/apps/aiclock/ChangeLog @@ -1 +1,2 @@ -0.01: New app! \ No newline at end of file +0.01: New app! +0.02: Design improvements and fixes. \ No newline at end of file diff --git a/apps/aiclock/aiclock.app.js b/apps/aiclock/aiclock.app.js index d58da6e88..ecac5f2dc 100644 --- a/apps/aiclock/aiclock.app.js +++ b/apps/aiclock/aiclock.app.js @@ -57,6 +57,17 @@ function drawCircle(isLocked){ g.fillCircle(cx, cy, 6); } +function toAngle(a){ + if (a < 0){ + return 360 + a; + } + + if(a > 360) { + return 360 - a; + } + + return a +} function drawTime(){ var drawHourHand = g.drawRotRect.bind(g,8,12,R-38); @@ -74,10 +85,10 @@ function drawTime(){ // Draw minute and hour bg g.setColor(g.theme.bg); - drawHourHand(Math.max(0, h - 2)); - drawHourHand(Math.min(360, h + 2)); - drawMinuteHand(Math.max(0, m - 2)); - drawMinuteHand(Math.min(360, m + 2)); + drawHourHand(toAngle(h-3)); + drawHourHand(toAngle(h+3)); + drawMinuteHand(toAngle(m-2)); + drawMinuteHand(toAngle(m+3)); // Draw minute and hour fg g.setColor(g.theme.fg); @@ -95,13 +106,15 @@ function drawDate(){ var text = ("0"+date.getDate()).substr(-2) + "/" + ("0"+date.getMonth()).substr(-2); var w = g.stringWidth(text); g.setColor(g.theme.bg); - g.fillRect(cx-w/2-4, 20, cx+w/2+2, 40+12); + g.fillRect(cx-w/2-4, 20, cx+w/2+4, 40+12); g.setColor(g.theme.fg); - g.drawLine(cx+w/2+1, 20, cx+w/2+1, 40+12); - g.drawLine(cx+w/2+2, 20, cx+w/2+2, 40+12); - g.drawLine(cx+w/2+3, 20, cx+w/2+3, 40+12); - g.drawLine(cx+w/2+4, 20, cx+w/2+4, 40+12); + // Draw right line as designed by stable diffusion + g.drawLine(cx+w/2+5, 20, cx+w/2+5, 40+12); + g.drawLine(cx+w/2+6, 20, cx+w/2+6, 40+12); + g.drawLine(cx+w/2+7, 20, cx+w/2+7, 40+12); + + // And finally the text g.drawString(text, cx, 40); } @@ -115,13 +128,21 @@ function drawDigits(){ var text = ("0"+date.getHours()).substr(-2) + ":" + ("0"+date.getMinutes()).substr(-2); //Bangle.getHealthStatus("day").steps; var w = g.stringWidth(text); g.setColor(g.theme.bg); - g.fillRect(cx-w/2-4, 120, cx+w/2+2, 140+20); + g.fillRect(cx-w/2-4, 120, cx+w/2+4, 140+20); + + // Draw right line as designed by stable diffusion + g.setColor(g.theme.fg); + g.drawLine(cx+w/2+5, 120, cx+w/2+5, 140+20); + g.drawLine(cx+w/2+6, 120, cx+w/2+6, 140+20); + g.drawLine(cx+w/2+7, 120, cx+w/2+7, 140+20); + + // And the 7set text + g.setColor("#BBB"); + g.drawString("88:88", cx, 140); + g.drawString("88:88", cx+1, 140); + g.drawString("88:88", cx, 141); g.setColor(g.theme.fg); - g.drawLine(cx+w/2+1, 120, cx+w/2+1, 140+20); - g.drawLine(cx+w/2+2, 120, cx+w/2+2, 140+20); - g.drawLine(cx+w/2+3, 120, cx+w/2+3, 140+20); - g.drawLine(cx+w/2+4, 120, cx+w/2+4, 140+20); g.drawString(text, cx, 140); g.drawString(text, cx+1, 140); g.drawString(text, cx, 141); diff --git a/apps/aiclock/aiclock.icon.js b/apps/aiclock/aiclock.icon.js index 72d916455..0033b3848 100644 --- a/apps/aiclock/aiclock.icon.js +++ b/apps/aiclock/aiclock.icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEwxH+lYACq2s64Ai1gqDFwgND6OswIABqwAUDAWsKIlWF4oKDFQI2BAA1XD4QAHCY4cBGQJhHBAZFEDAgAOlhCHAgInCwKNF2QtCFIMlkosPABQgCGAYJCwKMDHAMAFjQACDwIkCMAg1BRocACALsVAA0lGAYqCRwYvBLwaiB1gABAYYACxGIAQIBBeAeCCwogBgMBSAlQRwheDDoYcEGAmsw+HBguBq97C4ZgBF4QrDGYOzBAJeCBgIdDwRPCKYIIDG4umH4xgCkqQEwOzRwj2CDIwAP2QHGMAMrvbADMQOIZoQvBGgIAJroLKAA8rkgiBSAa+ETgJnCAChYBBAyDClgvFXwRfBLxJ0BVoIUBwClCeAQbBDA4kBQYVWIIa+CF4dXCQOBvYiCDQQWCAYOsb4QxDF5TwDF4oVCwWmwWCa4XXCoQvBLQhnCqxFBd42CGAQvGq0BLwIqDAAg5BH4QFBMISXDSYYAGYARCCdwg5CCo68ECoOCAgYCEC5IvJXwIWJACLjDAAYvD1YvGao4ASUQQeEQoQpBPgQvDVYRLHLh+HlcAgAuCFQQvGXwUkF5ClCCQQ0FA4YOBgErroJB6xdCL44vCL4J9IBgIgBQQQICRAZaBVIwkBEwQvIdwJRCJwQAEDYKACld0pIVBFgQVIPQgvD6wvDJYLXDSZAxCyWSLQbIKwQvGR4lQR5CVHkkkIgIAOqC/TDhEsCSEsF4pMCF4J4KADAnBF41Qkp7QACbmBF41WgNW1oHBAD5fE1gvEgBfB1mIIgoQCDooHHComCRwRfECwQ3BF4YAHCAIXBAQPXDIQdDI4QZILwINBF5LVICAIvBAIQHBAQIdBqwdBABGBEoQvFwMlBQI0BAApaBEgQBCIAJzCGIRNCAA0rEgOAF4wKCR5AgCF4VQP4QXBAYKPKEgRNCAASZCSBRfDSIZGCFwTvCRxEAlgvHYAMADoTvGFAToDCoQJCE4TuHEYSOC2QgDBYUrwTvIAYiICDIQDDHYKHBC4SDBgDuDQAiQDEgQAVF4YFCKQQnB2SMBSQTADSBAASrrKDRwYrCAgI7CSAhpBxCtCew4AJwOICgaOG65sBcIgODAAIZBcBILCAYWCwOHAoISCKAeAXwYvBSAgPCCAIACBAIARC4YeCEgIpDZgoVCCIQAJEAQjECJRdBRwqQDGAd7I4QvKKYoNHdQQuELwQwGB4QUCVAQAPCY7ZBEoQuEHoIKCBYLoDXyYxDFgIiEFwphFAAuyDAIAQDY6MFA=")) \ No newline at end of file +require("heatshrink").decompress(atob("mEwgP/ACfAEZU/ECZELIKhSR/+PAoWAv4FDhk/x/ggP+j0fx/AgP8n8PCIX8CwIFC/F/w4FBgP4gEHC4QFE//w//DC4QFB8YFC+P/8IdCAoYdBAoPxDoQAd+CiKh4dQwDhfAA4A=")) \ No newline at end of file diff --git a/apps/aiclock/aiclock.png b/apps/aiclock/aiclock.png index cfe98b530..104261254 100644 Binary files a/apps/aiclock/aiclock.png and b/apps/aiclock/aiclock.png differ diff --git a/apps/aiclock/impl.png b/apps/aiclock/impl.png index 15415ef19..92374b680 100644 Binary files a/apps/aiclock/impl.png and b/apps/aiclock/impl.png differ diff --git a/apps/aiclock/metadata.json b/apps/aiclock/metadata.json index 813f83b12..5ab0c5bb3 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.01", + "version":"0.02", "readme": "README.md", "supports": ["BANGLEJS2"], "description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.", diff --git a/apps/boot/ChangeLog b/apps/boot/ChangeLog index a43ecf86e..7da8aef26 100644 --- a/apps/boot/ChangeLog +++ b/apps/boot/ChangeLog @@ -52,3 +52,6 @@ 0.46: Fix no clock found error on Bangle.js 2 0.47: Add polyfill for setUI with an object as an argument (fix regression for 2v12 devices after Layout module changed) 0.48: Workaround for BTHRM issues on Bangle.js 1 (write .boot files in chunks) +0.49: Store first found clock as a setting to speed up further boots +0.50: Allow setting of screen rotation + Remove support for 2v11 and earlier firmware diff --git a/apps/boot/bootloader.js b/apps/boot/bootloader.js index 45e271f30..1e0d22645 100644 --- a/apps/boot/bootloader.js +++ b/apps/boot/bootloader.js @@ -1,8 +1,8 @@ // This runs after a 'fresh' boot -var clockApp=(require("Storage").readJSON("setting.json",1)||{}).clock; -if (clockApp) clockApp = require("Storage").read(clockApp); -if (!clockApp) { - clockApp = require("Storage").list(/\.info$/) +var s = require("Storage").readJSON("setting.json",1)||{}; +var _clkApp = require("Storage").read(s.clock); +if (!_clkApp) { + _clkApp = require("Storage").list(/\.info$/) .map(file => { const app = require("Storage").readJSON(file,1); if (app && app.type == "clock") { @@ -11,9 +11,13 @@ if (!clockApp) { }) .filter(x=>x) .sort((a, b) => a.sortorder - b.sortorder)[0]; - if (clockApp) - clockApp = require("Storage").read(clockApp.src); + if (_clkApp){ + s.clock = _clkApp.src; + _clkApp = require("Storage").read(_clkApp.src); + require("Storage").writeJSON("setting.json", s); + } } -if (!clockApp) clockApp=`E.showMessage("No Clock Found");setWatch(()=>{Bangle.showLauncher();}, global.BTN2||BTN, {repeat:false,edge:"falling"});`; -eval(clockApp); -delete clockApp; +delete s; +if (!_clkApp) _clkApp=`E.showMessage("No Clock Found");setWatch(()=>{Bangle.showLauncher();}, global.BTN2||BTN, {repeat:false,edge:"falling"});`; +eval(_clkApp); +delete _clkApp; diff --git a/apps/boot/bootupdate.js b/apps/boot/bootupdate.js index 4cb3c52e4..fca1c9adc 100644 --- a/apps/boot/bootupdate.js +++ b/apps/boot/bootupdate.js @@ -92,6 +92,7 @@ if (s.options) boot+=`Bangle.setOptions(${E.toJS(s.options)});\n`; if (s.brightness && s.brightness!=1) boot+=`Bangle.setLCDBrightness(${s.brightness});\n`; if (s.passkey!==undefined && s.passkey.length==6) boot+=`NRF.setSecurity({passkey:${E.toJS(s.passkey.toString())}, mitm:1, display:1});\n`; if (s.whitelist) boot+=`NRF.on('connect', function(addr) { if (!(require('Storage').readJSON('setting.json',1)||{}).whitelist.includes(addr)) NRF.disconnect(); });\n`; +if (s.rotate) boot+=`g.setRotation(${s.rotate&3},${s.rotate>>2});\n` // screen rotation // Pre-2v10 firmwares without a theme/setUI delete g.theme; // deleting stops us getting confused by our own decl. builtins can't be deleted if (!g.theme) { @@ -113,54 +114,6 @@ Bangle.setUI=function(mode, cb) { Bangle._setUI(mode, cb); };\n`; } -delete E.showScroller; // deleting stops us getting confused by our own decl. builtins can't be deleted -if (!E.showScroller) { // added in 2v11 - this is a limited functionality polyfill - boot += `E.showScroller = (function(a){function n(){g.reset();b>=l+c&&(c=1+b-l);bm||m>=a.c)break;var f=24+d*a.h;a.draw(m,{x:0,y:f,w:h,h:a.h});d+c==b&&g.setColor(g.theme.fg).drawRect(0,f,h-1,f+a.h-1).drawRect(1,f+1,h-2,f+a.h-2)}g.setColor(c?g.theme.fg:g.theme.bg);g.fillPoly([e,6,e-14,20,e+14,20]);g.setColor(a.c>l+c?g.theme.fg:g.theme.bg);g.fillPoly([e,k-7,e-14,k-21,e+14,k-21])}if(!a)return Bangle.setUI();var b=0,c=0,h=g.getWidth(), -k=g.getHeight(),e=h/2,l=Math.floor((k-48)/a.h);g.reset().clearRect(0,24,h-1,k-1);n();Bangle.setUI("updown",d=>{d?(b+=d,0>b&&(b=a.c-1),b>=a.c&&(b=0),n()):a.select(b)})});\n`; -} -delete g.imageMetrics; // deleting stops us getting confused by our own decl. builtins can't be deleted -if (!g.imageMetrics) { // added in 2v11 - this is a limited functionality polyfill - boot += `Graphics.prototype.imageMetrics=function(src) { - if (src[0]) return {width:src[0],height:src[1]}; - else if ('object'==typeof src) return { - width:("width" in src) ? src.width : src.getWidth(), - height:("height" in src) ? src.height : src.getHeight()}; - var im = E.toString(src); - return {width:im.charCodeAt(0), height:im.charCodeAt(1)}; -};\n`; -} -delete g.stringMetrics; // deleting stops us getting confused by our own decl. builtins can't be deleted -if (!g.stringMetrics) { // added in 2v11 - this is a limited functionality polyfill - boot += `Graphics.prototype.stringMetrics=function(txt) { - txt = txt.toString().split("\\n"); - return {width:Math.max.apply(null,txt.map(x=>g.stringWidth(x))), height:this.getFontHeight()*txt.length}; -};\n`; -} -delete g.wrapString; // deleting stops us getting confused by our own decl. builtins can't be deleted -if (!g.wrapString) { // added in 2v11 - this is a limited functionality polyfill - boot += `Graphics.prototype.wrapString=function(str, maxWidth) { - var lines = []; - for (var unwrappedLine of str.split("\\n")) { - var words = unwrappedLine.split(" "); - var line = words.shift(); - for (var word of words) { - if (g.stringWidth(line + " " + word) > maxWidth) { - lines.push(line); - line = word; - } else { - line += " " + word; - } - } - lines.push(line); - } - return lines; -};\n`; -} -delete Bangle.appRect; // deleting stops us getting confused by our own decl. builtins can't be deleted -if (!Bangle.appRect) { // added in 2v11 - polyfill for older firmwares - boot += `Bangle.appRect = ((y,w,h)=>({x:0,y:0,w:w,h:h,x2:w-1,y2:h-1}))(g.getWidth(),g.getHeight()); - (lw=>{ Bangle.loadWidgets = () => { lw(); Bangle.appRect = ((y,w,h)=>({x:0,y:y,w:w,h:h-y,x2:w-1,y2:h-(1+h)}))(global.WIDGETS?24:0,g.getWidth(),g.getHeight()); }; })(Bangle.loadWidgets);\n`; -} // Append *.boot.js files // These could change bleServices/bleServiceOptions if needed diff --git a/apps/boot/metadata.json b/apps/boot/metadata.json index 62adc4db1..802856245 100644 --- a/apps/boot/metadata.json +++ b/apps/boot/metadata.json @@ -1,7 +1,7 @@ { "id": "boot", "name": "Bootloader", - "version": "0.48", + "version": "0.50", "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 858d13b80..afea3263a 100644 --- a/apps/iconlaunch/ChangeLog +++ b/apps/iconlaunch/ChangeLog @@ -3,3 +3,4 @@ 0.03: Use default Bangle formatter for booleans 0.04: Support new fast app switching 0.05: Allow to directly eval apps instead of loading +0.06: Cache apps for faster start diff --git a/apps/iconlaunch/app.js b/apps/iconlaunch/app.js index fd58176db..0f20efa67 100644 --- a/apps/iconlaunch/app.js +++ b/apps/iconlaunch/app.js @@ -5,34 +5,24 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); } - var apps = s - .list(/\.info$/) - .map((app) => { - var a = s.readJSON(app, 1); - return ( - a && { - name: a.name, - type: a.type, - icon: a.icon, - sortorder: a.sortorder, - src: a.src, - } - ); - }) - .filter( - (app) => - app && - (app.type == "app" || - (app.type == "clock" && settings.showClocks) || - !app.type) - ); - apps.sort((a, b) => { - var n = (0 | a.sortorder) - (0 | b.sortorder); - if (n) return n; - if (a.name < b.name) return -1; - if (a.name > b.name) return 1; - return 0; - }); + let launchCache = s.readJSON("launch.cache.json", true)||{}; + let launchHash = require("Storage").hash(/\.info/); + if (launchCache.hash!=launchHash) { + launchCache = { + hash : launchHash, + apps : s.list(/\.info$/) + .map(app=>{var a=s.readJSON(app,1);return a&&{name:a.name,type:a.type,icon:a.icon,sortorder:a.sortorder,src:a.src};}) + .filter(app=>app && (app.type=="app" || (app.type=="clock" && settings.showClocks) || !app.type)) + .sort((a,b)=>{ + var n=(0|a.sortorder)-(0|b.sortorder); + if (n) return n; // do sortorder first + if (a.nameb.name) return 1; + return 0; + }) }; + s.writeJSON("launch.cache.json", launchCache); + } + let apps = launchCache.apps; apps.forEach((app) => { if (app.icon) app.icon = s.read(app.icon); }); diff --git a/apps/iconlaunch/metadata.json b/apps/iconlaunch/metadata.json index 82d2e7578..d544da73e 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.05", + "version": "0.06", "icon": "app.png", "description": "A launcher inspired by smartphones, with an icon-only scrollable menu.", "tags": "tool,system,launcher", diff --git a/apps/launch/ChangeLog b/apps/launch/ChangeLog index 36852e0b7..65f671bbd 100644 --- a/apps/launch/ChangeLog +++ b/apps/launch/ChangeLog @@ -15,3 +15,5 @@ 0.13: Add fullscreen mode 0.14: Use default Bangle formatter for booleans 0.15: Support for unload and quick return to the clock on 2v16 +0.16: Use a cache of app.info files to speed up loading the launcher +0.17: Don't display 'Loading...' now the watch has its own loading screen diff --git a/apps/launch/app.js b/apps/launch/app.js index d53f0dcdf..05f5bef43 100644 --- a/apps/launch/app.js +++ b/apps/launch/app.js @@ -1,5 +1,6 @@ { // must be inside our own scope here so that when we are unloaded everything disappears let s = require("Storage"); +// handle customised launcher let scaleval = 1; let vectorval = 20; let font = g.getFonts().includes("12x20") ? "12x20" : "6x8:2"; @@ -7,56 +8,51 @@ let settings = Object.assign({ showClocks: true, fullscreen: false }, s.readJSON("launch.json", true) || {}); - -if ("vectorsize" in settings) { - vectorval = parseInt(settings.vectorsize); -} +if ("vectorsize" in settings) + vectorval = parseInt(settings.vectorsize); if ("font" in settings){ - if(settings.font == "Vector"){ - scaleval = vectorval/20; - font = "Vector"+(vectorval).toString(); - } - else{ - font = settings.font; - scaleval = (font.split("x")[1])/20; - } + if(settings.font == "Vector"){ + scaleval = vectorval/20; + font = "Vector"+(vectorval).toString(); + } else{ + font = settings.font; + scaleval = (font.split("x")[1])/20; + } } -let apps = s.list(/\.info$/).map(app=>{var a=s.readJSON(app,1);return a&&{name:a.name,type:a.type,icon:a.icon,sortorder:a.sortorder,src:a.src};}).filter(app=>app && (app.type=="app" || (app.type=="clock" && settings.showClocks) || !app.type)); -apps.sort((a,b)=>{ - var n=(0|a.sortorder)-(0|b.sortorder); - if (n) return n; // do sortorder first - if (a.nameb.name) return 1; - return 0; -}); -apps.forEach(app=>{ - if (app.icon) - app.icon = s.read(app.icon); // should just be a link to a memory area -}); -// FIXME: check not needed after 2v11 -if (g.wrapString) { - g.setFont(font); - apps.forEach(app=>app.name = g.wrapString(app.name, g.getWidth()-64).join("\n")); +// cache app list so launcher loads more quickly +let launchCache = s.readJSON("launch.cache.json", true)||{}; +let launchHash = require("Storage").hash(/\.info/); +if (launchCache.hash!=launchHash) { + launchCache = { + hash : launchHash, + apps : s.list(/\.info$/) + .map(app=>{var a=s.readJSON(app,1);return a&&{name:a.name,type:a.type,icon:a.icon,sortorder:a.sortorder,src:a.src};}) + .filter(app=>app && (app.type=="app" || (app.type=="clock" && settings.showClocks) || !app.type)) + .sort((a,b)=>{ + var n=(0|a.sortorder)-(0|b.sortorder); + if (n) return n; // do sortorder first + if (a.nameb.name) return 1; + return 0; + }) }; + s.writeJSON("launch.cache.json", launchCache); } - -function drawApp(i, r) { - var app = apps[i]; - if (!app) return; - g.clearRect((r.x),(r.y),(r.x+r.w-1), (r.y+r.h-1)); - g.setFont(font).setFontAlign(-1,0).drawString(app.name,64*scaleval,r.y+(32*scaleval)); - if (app.icon) try {g.drawImage(app.icon,8*scaleval, r.y+(8*scaleval), {scale: scaleval});} catch(e){} -} - -g.clear(); - -if (!settings.fullscreen) { +let apps = launchCache.apps; +// Now apps list is loaded - render +if (!settings.fullscreen) Bangle.loadWidgets(); - Bangle.drawWidgets(); -} - E.showScroller({ h : 64*scaleval, c : apps.length, - draw : drawApp, + draw : (i, r) => { + var app = apps[i]; + if (!app) return; + g.clearRect((r.x),(r.y),(r.x+r.w-1), (r.y+r.h-1)); + g.setFont(font).setFontAlign(-1,0).drawString(app.name,64*scaleval,r.y+(32*scaleval)); + if (app.icon) { + if (!app.img) app.img = s.read(app.icon); // load icon if it wasn't loaded + try {g.drawImage(app.img,8*scaleval, r.y+(8*scaleval), {scale: scaleval});} catch(e){} + } + }, select : i => { var app = apps[i]; if (!app) return; @@ -64,11 +60,11 @@ E.showScroller({ E.showMessage(/*LANG*/"App Source\nNot found"); setTimeout(drawMenu, 2000); } else { - E.showMessage(/*LANG*/"Loading..."); load(app.src); } } }); +g.flip(); // force a render before widgets have finished drawing function returnToClock() { // unload everything manually @@ -96,4 +92,6 @@ function lockHandler(locked) { lockTimeout = setTimeout(returnToClock, 10000); } Bangle.on("lock", lockHandler); +if (!settings.fullscreen) // finally draw widgets + Bangle.drawWidgets(); } diff --git a/apps/launch/metadata.json b/apps/launch/metadata.json index ec070e44e..8d6d90fb1 100644 --- a/apps/launch/metadata.json +++ b/apps/launch/metadata.json @@ -2,7 +2,7 @@ "id": "launch", "name": "Launcher", "shortName": "Launcher", - "version": "0.15", + "version": "0.17", "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", @@ -13,6 +13,6 @@ {"name":"launch.app.js","url":"app.js"}, {"name":"launch.settings.js","url":"settings.js"} ], - "data": [{"name":"launch.json"}], + "data": [{"name":"launch.json"},{"name":"launch.cache.json"}], "sortorder": -10 } diff --git a/apps/messages/ChangeLog b/apps/messages/ChangeLog index da3b3ab5c..166ff64ae 100644 --- a/apps/messages/ChangeLog +++ b/apps/messages/ChangeLog @@ -70,4 +70,6 @@ 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() \ No newline at end of file + 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 diff --git a/apps/messages/app.js b/apps/messages/app.js index 20fa8aaa3..f6226d178 100644 --- a/apps/messages/app.js +++ b/apps/messages/app.js @@ -68,8 +68,7 @@ function saveMessages() { function showMapMessage(msg) { active = "map"; - var m; - var distance, street, target, eta; + var m, distance, street, target, eta; m=msg.title.match(/(.*) - (.*)/); if (m) { distance = m[1]; @@ -379,7 +378,7 @@ function checkMessages(options) { draw : function(idx, r) {"ram" var msg = MESSAGES[idx]; if (msg && msg.new) g.setBgColor(g.theme.bgH).setColor(g.theme.fgH); - else g.setColor(g.theme.fg); + else g.setBgColor(g.theme.bg).setColor(g.theme.fg); g.clearRect(r.x,r.y,r.x+r.w, r.y+r.h); if (!msg) return; var x = r.x+2, title = msg.title, body = msg.body; diff --git a/apps/messages/lib.js b/apps/messages/lib.js index ed71ec04b..0188342ee 100644 --- a/apps/messages/lib.js +++ b/apps/messages/lib.js @@ -159,7 +159,7 @@ exports.buzz = function(msgSrc) { exports.buzzTimeout = setTimeout(()=>require("buzz").pattern(pattern), repeat*1000); var vibrateTimeout = (require('Storage').readJSON("messages.settings.json", true) || {}).vibrateTimeout; if (vibrateTimeout===undefined) vibrateTimeout=60; - if (vibrateTimeout && !exports.stopTimeout) exports.stopTimeout = setTimeout(exports.stopTimeout, vibrateTimeout*1000); + 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 057a95026..a31c21e03 100644 --- a/apps/messages/metadata.json +++ b/apps/messages/metadata.json @@ -1,7 +1,7 @@ { "id": "messages", "name": "Messages", - "version": "0.51", + "version": "0.52", "description": "App to display notifications from iOS and Gadgetbridge/Android", "icon": "app.png", "type": "app", diff --git a/apps/setting/ChangeLog b/apps/setting/ChangeLog index 9b88f0073..25adad359 100644 --- a/apps/setting/ChangeLog +++ b/apps/setting/ChangeLog @@ -54,3 +54,7 @@ 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 + diff --git a/apps/setting/README.md b/apps/setting/README.md index 657b96f71..e5ea2b43d 100644 --- a/apps/setting/README.md +++ b/apps/setting/README.md @@ -29,10 +29,12 @@ This is Bangle.js's settings menu * **LCD Brightness** set how bright the LCD is. Due to hardware limitations in the LCD backlight, you may notice flicker if the LCD is not at 100% brightness. * **LCD Timeout** how long should the LCD stay on for if no activity is detected. 0=stay on forever +* **Rotation** allows you to rotate (or mirror) what's displayed on the screen, eg. for left-handed wearers (needs 2v16 or 2v15 cutting edge firmware to work reliably) * **Wake on X** should the given activity wake up the Bangle.js LCD? * On Bangle.js 2 when locked the touchscreen is turned off to save power. Because of this, `Wake on Touch` actually uses the accelerometer, and you need to actually tap the display to wake Bangle.js. * **Twist X** these options adjust the sensitivity of `Wake on Twist` to ensure Bangle.js wakes up with just the right amount of wrist movement. +* **Calibrate** on Bangle.js 2, pop up a screen allowing you to calibrate the touchscreen (calibration only works on 2v16 or 2v15 cutting edge builds) ## Locale diff --git a/apps/setting/metadata.json b/apps/setting/metadata.json index d209eafc3..3a3094a4e 100644 --- a/apps/setting/metadata.json +++ b/apps/setting/metadata.json @@ -1,7 +1,7 @@ { "id": "setting", "name": "Settings", - "version": "0.49", + "version": "0.52", "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 77e3fdd87..5d3bd3705 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -87,6 +87,7 @@ function showSystemMenu() { /*LANG*/'LCD': ()=>showLCDMenu(), /*LANG*/'Locale': ()=>showLocaleMenu(), /*LANG*/'Select Clock': ()=>showClockMenu(), + /*LANG*/'Select Launcher': ()=>showLauncherMenu(), /*LANG*/'Date & Time': ()=>showSetTimeMenu() }; @@ -383,6 +384,8 @@ function showLCDMenu() { // converts Espruino internal unit to g function internalToG(u) { return u / 8192; } + var rotNames = [/*LANG*/"No",/*LANG*/"Rotate CW",/*LANG*/"Left Handed",/*LANG*/"Rotate CCW",/*LANG*/"Mirror"]; + const lcdMenu = { '': { 'title': 'LCD' }, '< Back': ()=>showSystemMenu(), @@ -408,6 +411,18 @@ function showLCDMenu() { Bangle.setLCDTimeout(settings.timeout); } }, + /*LANG*/'Rotate': { + value: 0|settings.rotate, + min: 0, + max: rotNames.length-1, + format: v=> rotNames[v], + onchange: v => { + settings.rotate = 0 | v; + updateSettings(); + g.setRotation(settings.rotate&3,settings.rotate>>2).clear(); + Bangle.drawWidgets(); + } + }, /*LANG*/'Wake on BTN1': { value: settings.options.wakeOnBTN1, format: boolFormat, @@ -491,6 +506,10 @@ function showLCDMenu() { } } }); + if (BANGLEJS2) + Object.assign(lcdMenu, { + /*LANG*/'Calibrate': () => showTouchscreenCalibration() + }); return E.showMenu(lcdMenu) } @@ -667,6 +686,35 @@ function showClockMenu() { } return E.showMenu(clockMenu); } +function showLauncherMenu() { + var launcherApps = require("Storage").list(/\.info$/) + .map(app => {var a=storage.readJSON(app, 1);return (a&&a.type == "launch")?a:undefined}) + .filter(app => app) // filter out any undefined apps + .sort((a, b) => a.sortorder - b.sortorder); + const launcherMenu = { + '': { + 'title': /*LANG*/'Select Launcher', + }, + '< Back': ()=>showSystemMenu(), + }; + launcherApps.forEach((app, index) => { + var label = app.name; + if ((!settings.launcher && index === 0) || (settings.launcher === app.src)) { + label = "* " + label; + } + launcherMenu[label] = () => { + if (settings.launcher !== app.src) { + settings.launcher = app.src; + updateSettings(); + showMainMenu(); + } + }; + }); + if (launcherApps.length === 0) { + launcherMenu[/*LANG*/"No Launchers Found"] = () => { }; + } + return E.showMenu(launcherMenu); +} function showSetTimeMenu() { d = new Date(); @@ -774,4 +822,85 @@ function showAppSettings(app) { } } +function showTouchscreenCalibration() { + Bangle.setUI(); + // disable touchscreen calibration (passed coords right through) + Bangle.setOptions({touchX1: 0, touchY1: 0, touchX2: g.getWidth(), touchY2: g.getHeight() }); + + var P = 32; + var corners = [ + [P,P], + [g.getWidth()-P,P], + [g.getWidth()-P,g.getHeight()-P], + [P,g.getHeight()-P], + ]; + var currentCorner = 0; + var currentTry = 0; + var pt = { + x1 : 0, y1 : 0, x2 : 0, y2 : 0 + }; + + function showTapSpot() { + var spot = corners[currentCorner]; + g.clear(1); + g.drawLine(spot[0]-32,spot[1],spot[0]+32,spot[1]); + 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); + } + + function calcCalibration() { + g.clear(1); + // we should now have 4 of each tap in 'pt' + pt.x1 /= 4; + pt.y1 /= 4; + pt.x2 /= 4; + pt.y2 /= 4; + // work out final values + var calib = { + x1 : Math.round(pt.x1 - (pt.x2-pt.x1)*P/(g.getWidth()-P*2)), + y1 : Math.round(pt.y1 - (pt.y2-pt.y1)*P/(g.getHeight()-P*2)), + x2 : Math.round(pt.x2 + (pt.x2-pt.x1)*P/(g.getWidth()-P*2)), + y2 : Math.round(pt.y2 + (pt.y2-pt.y1)*P/(g.getHeight()-P*2)) + }; + Bangle.setOptions({ + touchX1: calib.x1, touchY1: calib.y1, touchX2: calib.x2, touchY2: calib.y2 + }); + var s = require("Storage").readJSON("setting.json",1)||{}; + s.touch = calib; + require("Storage").writeJSON("setting.json",s); + g.setFont("6x8:2").setFontAlign(0,0).drawString("Calibrated!", g.getWidth()/2, g.getHeight()/2); + // now load the main menu again + setTimeout(showLCDMenu, 500); + } + + function touchHandler(_,e) { + var spot = corners[currentCorner]; + // store averages + if (spot[0]*2 < g.getWidth()) + pt.x1 += e.x; + else + pt.x2 += e.x; + if (spot[1]*2 < g.getHeight()) + pt.y1 += e.y; + else + pt.y2 += e.y; + // go to next corner + currentCorner++; + if (currentCorner>=corners.length) { + currentCorner = 0; + currentTry++; + if (currentTry==2) { + Bangle.removeListener('touch', touchHandler); + return calcCalibration(); + } + } + showTapSpot(); + } + Bangle.on('touch', touchHandler); + + showTapSpot(); +} + showMainMenu(); diff --git a/core b/core index 764197500..80de03d8e 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 76419750083a88ee7a569db3975ae1bdd6dc155a +Subproject commit 80de03d8e665c210dc3443d6869176c848ab103f diff --git a/index.html b/index.html index b141cffc9..a322da77b 100644 --- a/index.html +++ b/index.html @@ -151,6 +151,10 @@ Bluetooth Compatibility mode (limit to 20 byte writes) +