diff --git a/apps/backswipe/ChangeLog b/apps/backswipe/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/backswipe/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/backswipe/app.png b/apps/backswipe/app.png new file mode 100644 index 000000000..93019e1e2 Binary files /dev/null and b/apps/backswipe/app.png differ diff --git a/apps/backswipe/boot.js b/apps/backswipe/boot.js new file mode 100644 index 000000000..523149e8c --- /dev/null +++ b/apps/backswipe/boot.js @@ -0,0 +1,50 @@ +(function () { + var DEFAULTS = { + mode: 0, + apps: [], + }; + var settings = require("Storage").readJSON("backswipe.json", 1) || DEFAULTS; + + // Overrride the default setUI method, so we can save the back button callback + var setUI = Bangle.setUI; + Bangle.setUI = function (mode, cb) { + var options = {}; + if ("object"==typeof mode) { + options = mode; + } + + var currentFile = global.__FILE__ || ""; + + if(global.BACK) delete global.BACK; + if (options && options.back && enabledForApp(currentFile)) { + global.BACK = options.back; + } + setUI(mode, cb); + }; + + function goBack(lr, ud) { + // if it is a left to right swipe + if (lr === 1) { + // if we're in an app that has a back button, run the callback for it + if (global.BACK) { + global.BACK(); + } + } + } + + // Check if the back button should be enabled for the current app + // app is the src file of the app + function enabledForApp(app) { + if (!settings) return true; + if (settings.mode === 0) { + return !(settings.apps.filter((a) => a.src === app).length > 0); + } else if (settings.mode === 1) { + return settings.apps.filter((a) => a.src === app).length > 0; + } else { + return settings.mode === 2 ? true : false; + } + } + + // Listen to left to right swipe + Bangle.on("swipe", goBack); +})(); diff --git a/apps/backswipe/metadata.json b/apps/backswipe/metadata.json new file mode 100644 index 000000000..7aa9f6247 --- /dev/null +++ b/apps/backswipe/metadata.json @@ -0,0 +1,17 @@ +{ "id": "backswipe", + "name": "Back Swipe", + "shortName":"BackSwipe", + "version":"0.01", + "description": "Service that allows you to use an app's back button using left to right swipe gesture", + "icon": "app.png", + "tags": "back,gesture,swipe", + "supports" : ["BANGLEJS2"], + "type": "bootloader", + "storage": [ + {"name":"backswipe.boot.js","url":"boot.js"}, + {"name":"backswipe.settings.js","url":"settings.js"} + ], + "data": [ + {"name":"backswipe.json"} + ] +} diff --git a/apps/backswipe/settings.js b/apps/backswipe/settings.js new file mode 100644 index 000000000..2c29e86f8 --- /dev/null +++ b/apps/backswipe/settings.js @@ -0,0 +1,104 @@ +(function(back) { + var FILE = 'backswipe.json'; + // Mode can be 'blacklist', 'whitelist', 'on' or 'disabled' + // Apps is an array of app info objects, where all the apps that are there are either blocked or allowed, depending on the mode + var DEFAULTS = { + 'mode': 0, + 'apps': [] + }; + + var settings = {}; + + var loadSettings = function() { + settings = require('Storage').readJSON(FILE, 1) || DEFAULTS; + } + + var saveSettings = function(settings) { + require('Storage').write(FILE, settings); + } + + // Get all app info files + var getApps = function() { + var apps = require('Storage').list(/\.info$/).map(appInfoFileName => { + var appInfo = require('Storage').readJSON(appInfoFileName, 1); + return appInfo && { + 'name': appInfo.name, + 'sortorder': appInfo.sortorder, + 'src': appInfo.src + }; + }).filter(app => app && !!app.src); + apps.sort((a, b) => { + var n = (0 | a.sortorder) - (0 | b.sortorder); + if (n) return n; // do sortorder first + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; + return 0; + }); + return apps; + } + + var showMenu = function() { + var menu = { + '': { 'title': 'Backswipe' }, + '< Back': () => { + back(); + }, + 'Mode': { + value: settings.mode, + min: 0, + max: 3, + format: v => ["Blacklist", "Whitelist", "Always On", "Disabled"][v], + onchange: v => { + settings.mode = v; + saveSettings(settings); + }, + }, + 'App List': () => { + showAppSubMenu(); + } + }; + + E.showMenu(menu); + } + + var showAppSubMenu = function() { + var menu = { + '': { 'title': 'Backswipe' }, + '< Back': () => { + showMenu(); + }, + 'Add App': () => { + showAppList(); + } + }; + settings.apps.forEach(app => { + menu[app.name] = () => { + settings.apps.splice(settings.apps.indexOf(app), 1); + saveSettings(settings); + showAppSubMenu(); + } + }); + E.showMenu(menu); + } + + var showAppList = function() { + var apps = getApps(); + var menu = { + '': { 'title': 'Backswipe' }, + '< Back': () => { + showMenu(); + } + }; + apps.forEach(app => { + menu[app.name] = () => { + settings.apps.push(app); + saveSettings(settings); + showAppSubMenu(); + } + }); + E.showMenu(menu); + } + + loadSettings(); + showMenu(); +}) \ No newline at end of file diff --git a/apps/magnav/ChangeLog b/apps/magnav/ChangeLog index 2b2782c7b..14a6eb2a2 100644 --- a/apps/magnav/ChangeLog +++ b/apps/magnav/ChangeLog @@ -2,6 +2,5 @@ 0.02: Course marker 0.03: Tilt compensation and calibration 0.04: Fix Font size -0.05: Inital portable version - - +0.05: Initial portable version +0.06: Outsource tilt compensation to library diff --git a/apps/magnav/lib.js b/apps/magnav/lib.js new file mode 100644 index 000000000..e3fe8fccb --- /dev/null +++ b/apps/magnav/lib.js @@ -0,0 +1,41 @@ +exports.calibrate = () => { + var max={x:-32000, y:-32000, z:-32000}, + min={x:32000, y:32000, z:32000}; + var ref = setInterval(()=>{ + var m = Bangle.getCompass(); + max.x = m.x>max.x?m.x:max.x; + max.y = m.y>max.y?m.y:max.y; + max.z = m.z>max.z?m.z:max.z; + min.x = m.x { + setTimeout(()=>{ + if(ref) clearInterval(ref); + var offset = {x:(max.x+min.x)/2,y:(max.y+min.y)/2,z:(max.z+min.z)/2}; + var delta = {x:(max.x-min.x)/2,y:(max.y-min.y)/2,z:(max.z-min.z)/2}; + var avg = (delta.x+delta.y+delta.z)/3; + var scale = {x:avg/delta.x, y:avg/delta.y, z:avg/delta.z}; + resolve({offset:offset,scale:scale}); + },20000); + }); +} + +exports.tiltfixread = (O,S) => { + "ram" + var m = Bangle.getCompass(); + var g = Bangle.getAccel(); + m.dx =(m.x-O.x)*S.x; m.dy=(m.y-O.y)*S.y; m.dz=(m.z-O.z)*S.z; + var d = Math.atan2(-m.dx,m.dy)*180/Math.PI; + if (d<0) d+=360; + var phi = Math.atan(-g.x/-g.z); + var cosphi = Math.cos(phi), sinphi = Math.sin(phi); + var theta = Math.atan(-g.y/(-g.x*sinphi-g.z*cosphi)); + var costheta = Math.cos(theta), sintheta = Math.sin(theta); + var xh = m.dy*costheta + m.dx*sinphi*sintheta + m.dz*cosphi*sintheta; + var yh = m.dz*sinphi - m.dx*cosphi; + var psi = Math.atan2(yh,xh)*180/Math.PI; + if (psi<0) psi+=360; + return psi; +} diff --git a/apps/magnav/magnav_b1.js b/apps/magnav/magnav_b1.js index 739ff881f..0ec49014f 100644 --- a/apps/magnav/magnav_b1.js +++ b/apps/magnav/magnav_b1.js @@ -61,24 +61,7 @@ function newHeading(m,h){ var candraw = false; var CALIBDATA = require("Storage").readJSON("magnav.json",1)||null; - -function tiltfixread(O,S){ - "ram" - var m = Bangle.getCompass(); - var g = Bangle.getAccel(); - m.dx =(m.x-O.x)*S.x; m.dy=(m.y-O.y)*S.y; m.dz=(m.z-O.z)*S.z; - var d = Math.atan2(-m.dx,m.dy)*180/Math.PI; - if (d<0) d+=360; - var phi = Math.atan(-g.x/-g.z); - var cosphi = Math.cos(phi), sinphi = Math.sin(phi); - var theta = Math.atan(-g.y/(-g.x*sinphi-g.z*cosphi)); - var costheta = Math.cos(theta), sintheta = Math.sin(theta); - var xh = m.dy*costheta + m.dx*sinphi*sintheta + m.dz*cosphi*sintheta; - var yh = m.dz*sinphi - m.dx*cosphi; - var psi = Math.atan2(yh,xh)*180/Math.PI; - if (psi<0) psi+=360; - return psi; -} +const tiltfixread = require("magnav").tiltfixread; // Note actual mag is 360-m, error in firmware function reading() { @@ -97,30 +80,6 @@ function reading() { flip(buf,Yoff+80); } -function calibrate(){ - var max={x:-32000, y:-32000, z:-32000}, - min={x:32000, y:32000, z:32000}; - var ref = setInterval(()=>{ - var m = Bangle.getCompass(); - max.x = m.x>max.x?m.x:max.x; - max.y = m.y>max.y?m.y:max.y; - max.z = m.z>max.z?m.z:max.z; - min.x = m.x { - setTimeout(()=>{ - if(ref) clearInterval(ref); - var offset = {x:(max.x+min.x)/2,y:(max.y+min.y)/2,z:(max.z+min.z)/2}; - var delta = {x:(max.x-min.x)/2,y:(max.y-min.y)/2,z:(max.z-min.z)/2}; - var avg = (delta.x+delta.y+delta.z)/3; - var scale = {x:avg/delta.x, y:avg/delta.y, z:avg/delta.z}; - resolve({offset:offset,scale:scale}); - },20000); - }); -} - var calibrating=false; function docalibrate(first){ calibrating=true; @@ -139,7 +98,7 @@ function docalibrate(first){ buf.drawString("Fig 8s to",120,0); buf.drawString("Calibrate",120,26); flip(buf,Yoff); - calibrate().then((r)=>{ + require("magnav").calibrate().then((r)=>{ CALIBDATA=r; require("Storage").write("magnav.json",r); restart() diff --git a/apps/magnav/magnav_b2.js b/apps/magnav/magnav_b2.js index e54280796..319860159 100644 --- a/apps/magnav/magnav_b2.js +++ b/apps/magnav/magnav_b2.js @@ -53,24 +53,7 @@ function newHeading(m,h){ var candraw = false; var CALIBDATA = require("Storage").readJSON("magnav.json",1)||null; - -function tiltfixread(O,S){ - "ram" - var m = Bangle.getCompass(); - var g = Bangle.getAccel(); - m.dx =(m.x-O.x)*S.x; m.dy=(m.y-O.y)*S.y; m.dz=(m.z-O.z)*S.z; - var d = Math.atan2(-m.dx,m.dy)*180/Math.PI; - if (d<0) d+=360; - var phi = Math.atan(-g.x/-g.z); - var cosphi = Math.cos(phi), sinphi = Math.sin(phi); - var theta = Math.atan(-g.y/(-g.x*sinphi-g.z*cosphi)); - var costheta = Math.cos(theta), sintheta = Math.sin(theta); - var xh = m.dy*costheta + m.dx*sinphi*sintheta + m.dz*cosphi*sintheta; - var yh = m.dz*sinphi - m.dx*cosphi; - var psi = Math.atan2(yh,xh)*180/Math.PI; - if (psi<0) psi+=360; - return psi; -} +const tiltfixread = require("magnav").tiltfixread; // Note actual mag is 360-m, error in firmware function reading() { @@ -94,30 +77,6 @@ function reading() { g.flip(); } -function calibrate(){ - var max={x:-32000, y:-32000, z:-32000}, - min={x:32000, y:32000, z:32000}; - var ref = setInterval(()=>{ - var m = Bangle.getCompass(); - max.x = m.x>max.x?m.x:max.x; - max.y = m.y>max.y?m.y:max.y; - max.z = m.z>max.z?m.z:max.z; - min.x = m.x { - setTimeout(()=>{ - if(ref) clearInterval(ref); - var offset = {x:(max.x+min.x)/2,y:(max.y+min.y)/2,z:(max.z+min.z)/2}; - var delta = {x:(max.x-min.x)/2,y:(max.y-min.y)/2,z:(max.z-min.z)/2}; - var avg = (delta.x+delta.y+delta.z)/3; - var scale = {x:avg/delta.x, y:avg/delta.y, z:avg/delta.z}; - resolve({offset:offset,scale:scale}); - },20000); - }); -} - var calibrating=false; function docalibrate(first){ calibrating=true; @@ -137,7 +96,7 @@ function docalibrate(first){ g.drawString("Fig 8s to",88,Ypos); g.drawString("Calibrate",88,Ypos+18); g.flip(); - calibrate().then((r)=>{ + require("magnav").calibrate().then((r)=>{ CALIBDATA=r; require("Storage").write("magnav.json",r); restart(); diff --git a/apps/magnav/metadata.json b/apps/magnav/metadata.json index cba9a1ac3..bd40a08fd 100644 --- a/apps/magnav/metadata.json +++ b/apps/magnav/metadata.json @@ -1,7 +1,7 @@ { "id": "magnav", "name": "Navigation Compass", - "version": "0.05", + "version": "0.06", "description": "Compass with linear display as for GPSNAV. Has Tilt compensation and remembers calibration.", "screenshots": [{"url":"screenshot-b2.png"},{"url":"screenshot-light-b2.png"}], "icon": "magnav.png", @@ -11,6 +11,7 @@ "storage": [ {"name":"magnav.app.js","url":"magnav_b1.js","supports":["BANGLEJS"]}, {"name":"magnav.app.js","url":"magnav_b2.js","supports":["BANGLEJS2"]}, + {"name":"magnav","url":"lib.js"}, {"name":"magnav.img","url":"magnav-icon.js","evaluate":true} ], "data": [{"name":"magnav.json"}] diff --git a/apps/waypointer/ChangeLog b/apps/waypointer/ChangeLog index 8613ef799..60e8bfab0 100644 --- a/apps/waypointer/ChangeLog +++ b/apps/waypointer/ChangeLog @@ -5,3 +5,4 @@ 0.05: Fix not displaying of wpindex = 0 0.06: Added adjustment for Bangle.js magnetometer heading fix 0.07: Add settings file with the option to disable the slow direction updates +0.08: Use tilt compensation from new magnav library diff --git a/apps/waypointer/app.js b/apps/waypointer/app.js index de6bfdcaa..40e4cf974 100644 --- a/apps/waypointer/app.js +++ b/apps/waypointer/app.js @@ -85,31 +85,22 @@ function newHeading(m,h){ } var CALIBDATA = require("Storage").readJSON("magnav.json",1) || {}; - -function tiltfixread(O,S){ - var m = Bangle.getCompass(); - if (O === undefined || S === undefined) { - // no valid calibration from magnav, use built in - return m.heading; - } - var g = Bangle.getAccel(); - m.dx =(m.x-O.x)*S.x; m.dy=(m.y-O.y)*S.y; m.dz=(m.z-O.z)*S.z; - var d = Math.atan2(-m.dx,m.dy)*180/Math.PI; - if (d<0) d+=360; - var phi = Math.atan(-g.x/-g.z); - var cosphi = Math.cos(phi), sinphi = Math.sin(phi); - var theta = Math.atan(-g.y/(-g.x*sinphi-g.z*cosphi)); - var costheta = Math.cos(theta), sintheta = Math.sin(theta); - var xh = m.dy*costheta + m.dx*sinphi*sintheta + m.dz*cosphi*sintheta; - var yh = m.dz*sinphi - m.dx*cosphi; - var psi = Math.atan2(yh,xh)*180/Math.PI; - if (psi<0) psi+=360; - return psi; +let tiltfixread; +try { + tiltfixread = require("magnav").tiltfixread; +} catch(e) { + // magnav not installed } // Note actual mag is 360-m, error in firmware function read_compass() { - var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale); + let d; + if (tiltfixread === undefined || CALIBDATA.offset === undefined || CALIBDATA.scale === undefined) { + // magnav not installed or no valid calibration, use built in + d = Bangle.getCompass().heading; + } else { + d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale); + } if (isNaN(d)) return; // built in compass heading can return NaN when uncalibrated heading = newHeading(d,heading); direction = wp_bearing - heading; @@ -278,6 +269,9 @@ function doselect(){ wp = waypoints[wpindex]; require("waypoints").save(waypoints); } + if (selected) { + Bangle.resetCompass(); // reset built in compass when a waypoint is selected + } selected=!selected; drawN(); } @@ -294,6 +288,7 @@ Bangle.drawWidgets(); // load widgets can turn off GPS Bangle.setGPSPower(1); Bangle.setCompassPower(1); +Bangle.resetCompass() // reset built in compass on start in case we are not using tilt compensation drawAll(); startTimers(); Bangle.on('GPS', onGPS); diff --git a/apps/waypointer/metadata.json b/apps/waypointer/metadata.json index 0bbc42322..e21c5f10f 100644 --- a/apps/waypointer/metadata.json +++ b/apps/waypointer/metadata.json @@ -1,12 +1,12 @@ { "id": "waypointer", "name": "Way Pointer", - "version": "0.07", + "version": "0.08", "description": "Navigate to a waypoint using the GPS for bearing and compass to point way, uses the same waypoint interface as GPS Navigation", "icon": "waypointer.png", "tags": "tool,outdoors,gps", "supports": ["BANGLEJS", "BANGLEJS2"], - "dependencies" : { "waypoints":"type" }, + "dependencies" : { "waypoints":"type", "magnav" : "app" }, "readme": "README.md", "storage": [ {"name":"waypointer.app.js","url":"app.js"}, diff --git a/modules/Layout.js b/modules/Layout.js index 4cf8752a3..5afcce410 100644 --- a/modules/Layout.js +++ b/modules/Layout.js @@ -186,8 +186,12 @@ Layout.prototype.render = function (l) { x+4,y+h-1, x,y+h-5, x,y+4 - ], bg = l.selected?gfx.theme.bgH:gfx.theme.bg2; - gfx.setColor(bg).fillPoly(poly).setColor(l.selected ? gfx.theme.fgH : gfx.theme.fg2).drawPoly(poly); + ], bg = l.bgCol!==undefined?l.bgCol:gfx.theme.bg2, + btnborder = l.btnBorder!==undefined?l.btnBorder:gfx.theme.fg2; + if(l.selected){ + bg = gfx.theme.bgH, btnborder = gfx.theme.fgH; + } + gfx.setColor(bg).fillPoly(poly).setColor(btnborder).drawPoly(poly); if (l.col!==undefined) gfx.setColor(l.col); if (l.src) gfx.setBgColor(bg).drawImage( "function"==typeof l.src?l.src():l.src, diff --git a/modules/Layout.md b/modules/Layout.md index 67db21858..95bf116dc 100644 --- a/modules/Layout.md +++ b/modules/Layout.md @@ -46,7 +46,8 @@ layout.render(); - A `r` field to set rotation of text or images (0: 0°, 1: 90°, 2: 180°, 3: 270°). - A `wrap` field to enable line wrapping. Requires some combination of `width`/`height` and `fillx`/`filly` to be set. Not compatible with text rotation. - A `col` field, eg `#f00` for red -- A `bgCol` field for background color (will automatically fill on render) +- A `bgCol` field for background color (will automatically fill on render). When `type:"btn"`, this sets the background color of the button, and will not change color on press +- A `btnBorder` field for button border color (will default to theme if not set) - A `halign` field to set horizontal alignment WITHIN a `v` container. `-1`=left, `1`=right, `0`=center - A `valign` field to set vertical alignment WITHIN a `h` container. `-1`=top, `1`=bottom, `0`=center - A `pad` integer field to set pixels padding diff --git a/modules/clock_info.js b/modules/clock_info.js index 0a6035ccf..f185a494c 100644 --- a/modules/clock_info.js +++ b/modules/clock_info.js @@ -54,7 +54,7 @@ let storage = require("Storage"); let stepGoal = undefined; // Load step goal from health app and pedometer widget let d = storage.readJSON("health.json", true) || {}; -stepGoal = d != undefined && d.settings != undefined ? d.settings.stepGoal : undefined; +stepGoal = d.stepGoal; if (stepGoal == undefined) { d = storage.readJSON("wpedom.json", true) || {}; stepGoal = d != undefined && d.settings != undefined ? d.settings.goal : 10000; @@ -120,8 +120,10 @@ exports.load = function() { if (Bangle.getPressure){ // Altimeter may not exist bangleItems.push({ name : "Altitude", + hasRange : true, get : () => ({ - text : alt, v : alt, + text : alt, v : parseInt(alt), + min : 0, max : 3000, img : atob("GBiBAAAAAAAAAAAAAAAAAAAAAAACAAAGAAAPAAEZgAOwwAPwQAZgYAwAMBgAGBAACDAADGAABv///////wAAAAAAAAAAAAAAAAAAAA==") }), show : function() { this.interval = setInterval(altUpdateHandler, 60000); alt = "--"; altUpdateHandler(); }, @@ -266,7 +268,14 @@ exports.addInteractive = function(menu, options) { } Bangle.on("swipe",swipeHandler); var touchHandler; + var lockHandler; if (options.x!==undefined && options.y!==undefined && options.w && options.h) { + lockHandler = function() { + if(options.focus) { + options.focus=false; + options.redraw(); + } + }; touchHandler = function(_,e) { if (e.x(options.x+options.w) || e.y>(options.y+options.h)) { @@ -278,7 +287,7 @@ exports.addInteractive = function(menu, options) { } if (!options.focus) { options.focus=true; // if not focussed, set focus - options.redraw(); + options.redraw(); } else if (menu[options.menuA].items[options.menuB].run) { Bangle.buzz(100, 0.7); menu[options.menuA].items[options.menuB].run(); // allow tap on an item to run it (eg home assistant) @@ -287,6 +296,7 @@ exports.addInteractive = function(menu, options) { } }; Bangle.on("touch",touchHandler); + Bangle.on("lock", lockHandler); } // draw the first item menuShowItem(menu[options.menuA].items[options.menuB]); @@ -294,6 +304,7 @@ exports.addInteractive = function(menu, options) { options.remove = function() { Bangle.removeListener("swipe",swipeHandler); if (touchHandler) Bangle.removeListener("touch",touchHandler); + if (lockHandler) Bangle.removeListener("lock", lockHandler); menuHideItem(menu[options.menuA].items[options.menuB]); exports.loadCount--; }; diff --git a/modules/graphics_utils.js b/modules/graphics_utils.js index 5c08188bc..f573cf0cf 100644 --- a/modules/graphics_utils.js +++ b/modules/graphics_utils.js @@ -1,6 +1,6 @@ // draw an arc between radii minR and maxR, and between angles minAngle and maxAngle centered at X,Y. All angles are radians. exports.fillArc = function(graphics, X, Y, minR, maxR, minAngle, maxAngle, stepAngle) { - var step = stepAngle || 0.2; + var step = stepAngle || 0.21; var angle = minAngle; var inside = []; var outside = []; @@ -31,5 +31,5 @@ exports.degreesToRadians = function(degrees){ } exports.radiansToDegrees = function(radians){ - return 180/Math.PI * degrees; + return 180/Math.PI * radians; } \ No newline at end of file