mirror of https://github.com/espruino/BangleApps
Merge branch 'master' of github.com:espruino/BangleApps
commit
625e52514d
|
@ -10,6 +10,12 @@ Standard # of drag handlers: 0-10 (Default: 0, must be changed for backswipe to
|
||||||
|
|
||||||
Standard # of handlers settings are used to fine tune when backswipe should trigger the back function. E.g. when using a keyboard that works on drags, we don't want the backswipe to trigger when we just wanted to select a letter. This might not be able to cover all cases however.
|
Standard # of handlers settings are used to fine tune when backswipe should trigger the back function. E.g. when using a keyboard that works on drags, we don't want the backswipe to trigger when we just wanted to select a letter. This might not be able to cover all cases however.
|
||||||
|
|
||||||
|
To get an indication for standard # of handlers `Bangle["#onswipe"]` and `Bangle["#ondrag"]` can be entered in the [Espruino Web IDE](https://www.espruino.com/ide) console field. They return `undefined` if no handler is active, a function if one is active, or a list of functions if multiple are active. Calling this on the clock app is a good start.
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- Possibly add option to tweak standard # of handlers on per app basis.
|
||||||
|
|
||||||
## Creator
|
## Creator
|
||||||
Kedlub
|
Kedlub
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: Initial fork from messages_light
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Messages overlay app
|
||||||
|
|
||||||
|
This app handles the display of messages and message notifications as an overlay pop up.
|
||||||
|
|
||||||
|
It is a GUI replacement for the `messages` apps.
|
||||||
|
|
||||||
|
Messages are ephemeral and not stored on the Bangle.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Close app by tapping the X and scroll by swiping. The border of the pop up changes color if the Bangle is locked. The color depends on your currently active theme.
|
||||||
|
|
||||||
|
## Firmware hint
|
||||||
|
Current stable firmware draws incorrect colors for emojis. Nightly firmware builds correct this.
|
||||||
|
|
||||||
|
## Low memory mode
|
||||||
|
|
||||||
|
If free memory is below 2000 blocks, the overlay automatically only uses 1 bit depth. Default uses roundabout 1300 blocks, while low memory mode uses about 600.
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
|
||||||
|
[halemmerich](https://github.com/halemmerich)
|
||||||
|
Forked from messages_light by Rarder44
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,7 @@
|
||||||
|
//override require to filter require("message")
|
||||||
|
global.require_real=global.require;
|
||||||
|
global.require = (_require => file => {
|
||||||
|
if (file==="messages") file = "messagesoverlay";
|
||||||
|
return _require(file);
|
||||||
|
})(require);
|
||||||
|
|
|
@ -0,0 +1,495 @@
|
||||||
|
/* MESSAGES is a list of:
|
||||||
|
{id:int,
|
||||||
|
src,
|
||||||
|
title,
|
||||||
|
subject,
|
||||||
|
body,
|
||||||
|
sender,
|
||||||
|
tel:string,
|
||||||
|
new:true // not read yet
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ovrx = 10;
|
||||||
|
const ovry = 10;
|
||||||
|
const ovrw = g.getWidth()-2*ovrx;
|
||||||
|
const ovrh = g.getHeight()-2*ovry;
|
||||||
|
let _g = g;
|
||||||
|
|
||||||
|
let lockListener;
|
||||||
|
let quiet;
|
||||||
|
|
||||||
|
let LOG = function() {
|
||||||
|
//print.apply(null, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
let isQuiet = function(){
|
||||||
|
if (quiet == undefined) quiet = (require('Storage').readJSON('setting.json', 1) || {}).quiet;
|
||||||
|
return quiet;
|
||||||
|
};
|
||||||
|
|
||||||
|
let settings = {
|
||||||
|
fontSmall:"6x8",
|
||||||
|
fontMedium:"Vector:14",
|
||||||
|
fontBig:"Vector:20",
|
||||||
|
fontLarge:"Vector:30",
|
||||||
|
};
|
||||||
|
|
||||||
|
let eventQueue = [];
|
||||||
|
let callInProgress = false;
|
||||||
|
|
||||||
|
let show = function(ovr){
|
||||||
|
let img = ovr;
|
||||||
|
if (ovr.getBPP() == 1) {
|
||||||
|
img = ovr.asImage();
|
||||||
|
img.palette = new Uint16Array([_g.theme.fg,_g.theme.bg]);
|
||||||
|
}
|
||||||
|
Bangle.setLCDOverlay(img, ovrx, ovry);
|
||||||
|
};
|
||||||
|
|
||||||
|
let manageEvent = function(ovr, event) {
|
||||||
|
event.new = true;
|
||||||
|
|
||||||
|
LOG("manageEvent");
|
||||||
|
if (event.id == "call") {
|
||||||
|
showCall(ovr, event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (event.t) {
|
||||||
|
case "add":
|
||||||
|
eventQueue.unshift(event);
|
||||||
|
|
||||||
|
if (!callInProgress)
|
||||||
|
showMessage(ovr, event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "modify":
|
||||||
|
let find = false;
|
||||||
|
eventQueue.forEach(element => {
|
||||||
|
if (element.id == event.id) {
|
||||||
|
find = true;
|
||||||
|
Object.assign(element, event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!find)
|
||||||
|
eventQueue.unshift(event);
|
||||||
|
|
||||||
|
if (!callInProgress)
|
||||||
|
showMessage(ovr, event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "remove":
|
||||||
|
if (eventQueue.length == 0 && !callInProgress)
|
||||||
|
next(ovr);
|
||||||
|
|
||||||
|
if (!callInProgress && eventQueue[0] !== undefined && eventQueue[0].id == event.id)
|
||||||
|
next(ovr);
|
||||||
|
|
||||||
|
else {
|
||||||
|
eventQueue.length = 0; // empty existing queue
|
||||||
|
eventQueue.forEach(element => {
|
||||||
|
if (element.id != event.id)
|
||||||
|
neweventQueue.push(element);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "musicstate":
|
||||||
|
case "musicinfo":
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let roundedRect = function(ovr, x,y,w,h,filled){
|
||||||
|
var poly = [
|
||||||
|
x,y+4,
|
||||||
|
x+4,y,
|
||||||
|
x+w-5,y,
|
||||||
|
x+w-1,y+4,
|
||||||
|
x+w-1,y+h-5,
|
||||||
|
x+w-5,y+h-1,
|
||||||
|
x+4,y+h-1,
|
||||||
|
x,y+h-5,
|
||||||
|
x,y+4
|
||||||
|
];
|
||||||
|
ovr.drawPoly(poly,true);
|
||||||
|
if (filled) ovr.fillPoly(poly,true);
|
||||||
|
};
|
||||||
|
|
||||||
|
let drawScreen = function(ovr, title, titleFont, src, iconcolor, icon){
|
||||||
|
ovr.setBgColor(ovr.theme.bg2);
|
||||||
|
ovr.clearRect(2,2,ovr.getWidth()-3,37);
|
||||||
|
|
||||||
|
ovr.setColor(ovr.theme.fg2);
|
||||||
|
ovr.setFont(settings.fontSmall);
|
||||||
|
ovr.setFontAlign(0,-1);
|
||||||
|
|
||||||
|
let textCenter = (ovr.getWidth()+35-26)/2;
|
||||||
|
|
||||||
|
if (src) {
|
||||||
|
let shortened = src;
|
||||||
|
while (ovr.stringWidth(shortened) > ovr.getWidth()-80) shortened = shortened.substring(0,shortened.length-2);
|
||||||
|
if (shortened.length != src.length) shortened += "...";
|
||||||
|
ovr.drawString(shortened, textCenter, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
ovr.setFontAlign(0,0);
|
||||||
|
ovr.setFont(titleFont);
|
||||||
|
if (title) ovr.drawString(title, textCenter, 38/2 + 5);
|
||||||
|
|
||||||
|
ovr.setColor(ovr.theme.fg2);
|
||||||
|
|
||||||
|
ovr.setFont(settings.fontMedium);
|
||||||
|
roundedRect(ovr, ovr.getWidth()-26,5,22,30,false);
|
||||||
|
ovr.setFont("Vector:16");
|
||||||
|
ovr.drawString("X",ovr.getWidth()-14,21);
|
||||||
|
|
||||||
|
ovr.setColor("#888");
|
||||||
|
roundedRect(ovr, 5,5,30,30,true);
|
||||||
|
ovr.setColor(ovr.getBPP() != 1 ? iconcolor : ovr.theme.bg2);
|
||||||
|
ovr.drawImage(icon,8,8);
|
||||||
|
};
|
||||||
|
|
||||||
|
let showMessage = function(ovr, msg) {
|
||||||
|
LOG("showMessage");
|
||||||
|
ovr.setBgColor(ovr.theme.bg);
|
||||||
|
|
||||||
|
if (typeof msg.CanscrollDown === "undefined")
|
||||||
|
msg.CanscrollDown = false;
|
||||||
|
if (typeof msg.CanscrollUp === "undefined")
|
||||||
|
msg.CanscrollUp = false;
|
||||||
|
|
||||||
|
// Normal text message display
|
||||||
|
let title = msg.title,
|
||||||
|
titleFont = settings.fontLarge,
|
||||||
|
lines;
|
||||||
|
if (title) {
|
||||||
|
let w = ovr.getWidth() - 35 - 26;
|
||||||
|
if (ovr.setFont(titleFont).stringWidth(title) > w)
|
||||||
|
titleFont = settings.fontMedium;
|
||||||
|
if (ovr.setFont(titleFont).stringWidth(title) > w) {
|
||||||
|
lines = ovr.wrapString(title, w);
|
||||||
|
title = (lines.length > 2) ? lines.slice(0, 2).join("\n") + "..." : lines.join("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawScreen(ovr, title, titleFont, msg.src || /*LANG*/ "Message", require("messageicons").getColor(msg), require("messageicons").getImage(msg));
|
||||||
|
|
||||||
|
if (!isQuiet() && msg.new) {
|
||||||
|
msg.new = false;
|
||||||
|
Bangle.buzz();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawMessage(ovr, msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
let drawBorder = function(ovr) {
|
||||||
|
if (Bangle.isLocked())
|
||||||
|
ovr.setColor(ovr.theme.fgH);
|
||||||
|
else
|
||||||
|
ovr.setColor(ovr.theme.fg);
|
||||||
|
ovr.drawRect(0,0,ovr.getWidth()-1,ovr.getHeight()-1);
|
||||||
|
ovr.drawRect(1,1,ovr.getWidth()-2,ovr.getHeight()-2);
|
||||||
|
show(ovr);
|
||||||
|
if (!isQuiet()) Bangle.setLCDPower(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let showCall = function(ovr, msg) {
|
||||||
|
LOG("showCall");
|
||||||
|
LOG(msg);
|
||||||
|
|
||||||
|
if (msg.t == "remove") {
|
||||||
|
LOG("hide call screen");
|
||||||
|
next(ovr); //dont shift
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callInProgress = true;
|
||||||
|
|
||||||
|
let title = msg.title,
|
||||||
|
titleFont = settings.fontLarge,
|
||||||
|
lines;
|
||||||
|
if (title) {
|
||||||
|
let w = ovr.getWidth() - 35 -26;
|
||||||
|
if (ovr.setFont(titleFont).stringWidth(title) > w)
|
||||||
|
titleFont = settings.fontMedium;
|
||||||
|
if (ovr.setFont(titleFont).stringWidth(title) > w) {
|
||||||
|
lines = ovr.wrapString(title, w);
|
||||||
|
title = (lines.length > 2) ? lines.slice(0, 2).join("\n") + "..." : lines.join("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawScreen(ovr, title, titleFont, msg.src || /*LANG*/ "Message", require("messageicons").getColor(msg), require("messageicons").getImage(msg));
|
||||||
|
|
||||||
|
stopCallBuzz();
|
||||||
|
if (!isQuiet()) {
|
||||||
|
if (msg.new) {
|
||||||
|
msg.new = false;
|
||||||
|
if (callBuzzTimer) clearInterval(callBuzzTimer);
|
||||||
|
callBuzzTimer = setInterval(function() {
|
||||||
|
Bangle.buzz(500);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
Bangle.buzz(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawMessage(ovr, msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
let next = function(ovr) {
|
||||||
|
LOG("next");
|
||||||
|
stopCallBuzz();
|
||||||
|
|
||||||
|
if (!callInProgress)
|
||||||
|
eventQueue.shift();
|
||||||
|
|
||||||
|
callInProgress = false;
|
||||||
|
if (eventQueue.length == 0) {
|
||||||
|
LOG("no element in queue - closing");
|
||||||
|
cleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showMessage(ovr, eventQueue[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
let showMapMessage = function(ovr, msg) {
|
||||||
|
ovr.clearRect(2,2,ovr.getWidth()-3,ovr.getHeight()-3);
|
||||||
|
drawMessage(ovr, {
|
||||||
|
body: "Not implemented!"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let callBuzzTimer = null;
|
||||||
|
let stopCallBuzz = function() {
|
||||||
|
if (callBuzzTimer) {
|
||||||
|
clearInterval(callBuzzTimer);
|
||||||
|
callBuzzTimer = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let drawTriangleUp = function(ovr) {
|
||||||
|
ovr.reset();
|
||||||
|
ovr.fillPoly([ovr.getWidth()-9, 46,ovr.getWidth()-14, 56,ovr.getWidth()-4, 56]);
|
||||||
|
};
|
||||||
|
|
||||||
|
let drawTriangleDown = function(ovr) {
|
||||||
|
ovr.reset();
|
||||||
|
ovr.fillPoly([ovr.getWidth()-9, ovr.getHeight()-6, ovr.getWidth()-14, ovr.getHeight()-16, ovr.getWidth()-4, ovr.getHeight()-16]);
|
||||||
|
};
|
||||||
|
|
||||||
|
let scrollUp = function(ovr) {
|
||||||
|
msg = eventQueue[0];
|
||||||
|
LOG("up", msg);
|
||||||
|
if (typeof msg.FirstLine === "undefined")
|
||||||
|
msg.FirstLine = 0;
|
||||||
|
if (typeof msg.CanscrollUp === "undefined")
|
||||||
|
msg.CanscrollUp = false;
|
||||||
|
|
||||||
|
if (!msg.CanscrollUp) return;
|
||||||
|
|
||||||
|
msg.FirstLine = msg.FirstLine > 0 ? msg.FirstLine - 1 : 0;
|
||||||
|
|
||||||
|
drawMessage(ovr, msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
let scrollDown = function(ovr) {
|
||||||
|
msg = eventQueue[0];
|
||||||
|
LOG("down", msg);
|
||||||
|
if (typeof msg.FirstLine === "undefined")
|
||||||
|
msg.FirstLine = 0;
|
||||||
|
if (typeof msg.CanscrollDown === "undefined")
|
||||||
|
msg.CanscrollDown = false;
|
||||||
|
|
||||||
|
if (!msg.CanscrollDown) return;
|
||||||
|
|
||||||
|
msg.FirstLine = msg.FirstLine + 1;
|
||||||
|
drawMessage(ovr, msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
let drawMessage = function(ovr, msg) {
|
||||||
|
let MyWrapString = function(str, maxWidth) {
|
||||||
|
str = str.replace("\r\n", "\n").replace("\r", "\n");
|
||||||
|
return ovr.wrapString(str, maxWidth);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof msg.FirstLine === "undefined") msg.FirstLine = 0;
|
||||||
|
|
||||||
|
let bodyFont = typeof msg.bodyFont === "undefined" ? settings.fontMedium : msg.bodyFont;
|
||||||
|
let Padding = 3;
|
||||||
|
if (typeof msg.lines === "undefined") {
|
||||||
|
ovr.setFont(bodyFont);
|
||||||
|
msg.lines = MyWrapString(msg.body, ovr.getWidth() - (Padding * 2));
|
||||||
|
if (msg.lines.length <= 2) {
|
||||||
|
bodyFont = ovr.getFonts().includes("Vector") ? "Vector:20" : "6x8:3";
|
||||||
|
ovr.setFont(bodyFont);
|
||||||
|
msg.lines = MyWrapString(msg.body, ovr.getWidth() - (Padding * 2));
|
||||||
|
msg.bodyFont = bodyFont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let NumLines = 7;
|
||||||
|
|
||||||
|
let linesToPrint = (msg.lines.length > NumLines) ? msg.lines.slice(msg.FirstLine, msg.FirstLine + NumLines) : msg.lines;
|
||||||
|
|
||||||
|
let yText = 40;
|
||||||
|
|
||||||
|
ovr.setBgColor(ovr.theme.bg);
|
||||||
|
ovr.setColor(ovr.theme.fg);
|
||||||
|
ovr.clearRect(2, yText, ovrw-3, ovrh-3);
|
||||||
|
let xText = Padding;
|
||||||
|
yText += Padding;
|
||||||
|
ovr.setFont(bodyFont);
|
||||||
|
let HText = ovr.getFontHeight();
|
||||||
|
|
||||||
|
yText = ((ovrh - yText) / 2) - (linesToPrint.length * HText / 2) + yText;
|
||||||
|
|
||||||
|
if (linesToPrint.length <= 3) {
|
||||||
|
ovr.setFontAlign(0, -1);
|
||||||
|
xText = ovr.getWidth() / 2;
|
||||||
|
} else
|
||||||
|
ovr.setFontAlign(-1, -1);
|
||||||
|
|
||||||
|
|
||||||
|
linesToPrint.forEach((line, i) => {
|
||||||
|
ovr.drawString(line, xText, yText + HText * i);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (msg.FirstLine != 0) {
|
||||||
|
msg.CanscrollUp = true;
|
||||||
|
drawTriangleUp(ovr);
|
||||||
|
} else
|
||||||
|
msg.CanscrollUp = false;
|
||||||
|
|
||||||
|
if (msg.FirstLine + linesToPrint.length < msg.lines.length) {
|
||||||
|
msg.CanscrollDown = true;
|
||||||
|
drawTriangleDown(ovr);
|
||||||
|
} else
|
||||||
|
msg.CanscrollDown = false;
|
||||||
|
show(ovr);
|
||||||
|
if (!isQuiet()) Bangle.setLCDPower(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let getSwipeHandler = function(ovr){
|
||||||
|
return (lr, ud) => {
|
||||||
|
if (ud == 1) {
|
||||||
|
scrollUp(ovr);
|
||||||
|
} else if (ud == -1){
|
||||||
|
scrollDown(ovr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let getTouchHandler = function(ovr){
|
||||||
|
return (_, xy) => {
|
||||||
|
if (xy.y < ovry + 40){
|
||||||
|
next(ovr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let touchHandler;
|
||||||
|
let swipeHandler;
|
||||||
|
|
||||||
|
let restoreHandler = function(event){
|
||||||
|
if (backup[event]){
|
||||||
|
Bangle["#on" + event]=backup[event];
|
||||||
|
backup[event] = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let backupHandler = function(event){
|
||||||
|
if (eventQueue.length > 1 && ovr) return; // do not backup, overlay is already up
|
||||||
|
backup[event] = Bangle["#on" + event];
|
||||||
|
Bangle.removeAllListeners(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
let cleanup = function(){
|
||||||
|
if (lockListener) {
|
||||||
|
Bangle.removeListener("lock", lockListener);
|
||||||
|
lockListener = undefined;
|
||||||
|
}
|
||||||
|
restoreHandler("touch");
|
||||||
|
restoreHandler("swipe");
|
||||||
|
restoreHandler("drag");
|
||||||
|
|
||||||
|
if (touchHandler) {
|
||||||
|
Bangle.removeListener("touch", touchHandler);
|
||||||
|
touchHandler = undefined;
|
||||||
|
}
|
||||||
|
if (swipeHandler) {
|
||||||
|
Bangle.removeListener("swipe", swipeHandler);
|
||||||
|
swipeHandler = undefined;
|
||||||
|
}
|
||||||
|
Bangle.setLCDOverlay();
|
||||||
|
ovr = undefined;
|
||||||
|
quiet = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
let backup = {};
|
||||||
|
|
||||||
|
let main = function(ovr, event) {
|
||||||
|
LOG("Main", event, settings);
|
||||||
|
|
||||||
|
if (!lockListener) {
|
||||||
|
lockListener = function (){
|
||||||
|
drawBorder(ovr);
|
||||||
|
};
|
||||||
|
Bangle.on('lock', lockListener);
|
||||||
|
}
|
||||||
|
backupHandler("touch");
|
||||||
|
backupHandler("swipe");
|
||||||
|
backupHandler("drag");
|
||||||
|
|
||||||
|
if (touchHandler) Bangle.removeListener("touch",touchHandler);
|
||||||
|
if (swipeHandler) Bangle.removeListener("swipe",swipeHandler);
|
||||||
|
touchHandler = getTouchHandler(ovr);
|
||||||
|
swipeHandler = getSwipeHandler(ovr);
|
||||||
|
Bangle.on('touch', touchHandler);
|
||||||
|
Bangle.on('swipe', swipeHandler);
|
||||||
|
|
||||||
|
if (event !== undefined){
|
||||||
|
drawBorder(ovr);
|
||||||
|
manageEvent(ovr, event);
|
||||||
|
} else {
|
||||||
|
LOG("No event given");
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ovr;
|
||||||
|
|
||||||
|
exports.pushMessage = function(event) {
|
||||||
|
if( event.id=="music") return require_real("messages").pushMessage(event);
|
||||||
|
|
||||||
|
bpp = 4;
|
||||||
|
if (process.memory().free < 2000) bpp = 1;
|
||||||
|
|
||||||
|
if (!ovr) {
|
||||||
|
ovr = Graphics.createArrayBuffer(ovrw, ovrh, bpp, {
|
||||||
|
msb: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ovr.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
g = ovr;
|
||||||
|
|
||||||
|
if (bpp == 4)
|
||||||
|
ovr.theme = g.theme;
|
||||||
|
else
|
||||||
|
ovr.theme = { fg:0, bg:1, fg2:1, bg2:0, fgH:1, bgH:0 };
|
||||||
|
|
||||||
|
main(ovr, event);
|
||||||
|
|
||||||
|
g = _g;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//Call original message library
|
||||||
|
exports.clearAll = function() { return require_real("messages").clearAll();};
|
||||||
|
exports.getMessages = function() { return require_real("messages").getMessages();};
|
||||||
|
exports.status = function() { return require_real("messages").status();};
|
||||||
|
exports.buzz = function() { return require_real("messages").buzz(msgSrc);};
|
||||||
|
exports.stopBuzz = function() { return require_real("messages").stopBuzz();};
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"id": "messagesoverlay",
|
||||||
|
"name": "Messages Overlay",
|
||||||
|
"version": "0.01",
|
||||||
|
"description": "An overlay based implementation of a messages UI (display notifications from iOS and Gadgetbridge/Android)",
|
||||||
|
"icon": "app.png",
|
||||||
|
"type": "bootloader",
|
||||||
|
"tags": "tool,system",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"dependencies" : { "messageicons":"module","messages":"app" },
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"messagesoverlay.settings.js","url":"settings.js"},
|
||||||
|
{"name":"messagesoverlay","url":"lib.js"},
|
||||||
|
{"name":"messagesoverlay.boot.js","url":"boot.js"}
|
||||||
|
],
|
||||||
|
"screenshots": [{"url":"screen_call.png"} ,{"url":"screen_message.png"} ]
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
|
@ -0,0 +1 @@
|
||||||
|
eval(require("Storage").read("messages.settings.js"));
|
|
@ -7,3 +7,5 @@
|
||||||
0.06: Allow logging of some things using power
|
0.06: Allow logging of some things using power
|
||||||
Add widget for live monitoring of power use
|
Add widget for live monitoring of power use
|
||||||
0.07: Convert Yes/No On/Off in settings to checkboxes
|
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
|
|
@ -19,6 +19,14 @@ You can switch on logging in the options to diagnose unexpected power use. Curre
|
||||||
|
|
||||||
Do not use trace logging for extended time, it uses a lot of storage and can fill up the flash quite quick.
|
Do not use trace logging for extended time, it uses a lot of storage and can fill up the flash quite quick.
|
||||||
|
|
||||||
|
### TODO
|
||||||
|
|
||||||
|
* Wrap functions given as strings to setTimeout/setInterval
|
||||||
|
* Handle eval in setTimeout/setInterval
|
||||||
|
* Track functions executed as event handlers
|
||||||
|
* Track buzzer
|
||||||
|
* Modify browser interface to estimate power use like widget does
|
||||||
|
|
||||||
## Internals
|
## Internals
|
||||||
|
|
||||||
Battery calibration offset is set by writing `batFullVoltage` in setting.json
|
Battery calibration offset is set by writing `batFullVoltage` in setting.json
|
||||||
|
|
|
@ -77,14 +77,14 @@
|
||||||
|
|
||||||
let functions = {};
|
let functions = {};
|
||||||
let wrapDeferred = ((o,t) => (a) => {
|
let wrapDeferred = ((o,t) => (a) => {
|
||||||
if (a == eval){
|
if (a == eval || typeof a == "string") {
|
||||||
return o.apply(this, arguments);
|
return o.apply(this, arguments);
|
||||||
} else {
|
} else {
|
||||||
let wrapped = a;
|
let wrapped = a;
|
||||||
if (!a.__wrapped){
|
if (!a.__wrapped){
|
||||||
wrapped = ()=>{
|
wrapped = ()=>{
|
||||||
let start = Date.now();
|
let start = Date.now();
|
||||||
let result = a.apply(undefined, arguments.slice(1));
|
let result = a.apply(undefined, arguments.slice(2)); // function arguments for deferred calls start at index 2, first is function, second is time
|
||||||
let end = Date.now()-start;
|
let end = Date.now()-start;
|
||||||
let f = a.toString().substring(0,100);
|
let f = a.toString().substring(0,100);
|
||||||
if (settings.logDetails) logDeferred(t, end, f);
|
if (settings.logDetails) logDeferred(t, end, f);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "powermanager",
|
"id": "powermanager",
|
||||||
"name": "Power Manager",
|
"name": "Power Manager",
|
||||||
"shortName": "Power Manager",
|
"shortName": "Power Manager",
|
||||||
"version": "0.07",
|
"version": "0.08",
|
||||||
"description": "Allow configuration of warnings and thresholds for battery charging and display.",
|
"description": "Allow configuration of warnings and thresholds for battery charging and display.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "bootloader",
|
"type": "bootloader",
|
||||||
|
@ -20,6 +20,7 @@
|
||||||
"data": [
|
"data": [
|
||||||
{"name":"powermanager.hw.json"},
|
{"name":"powermanager.hw.json"},
|
||||||
{"name":"powermanager.def.json"},
|
{"name":"powermanager.def.json"},
|
||||||
|
{"name":"powermanager.json"},
|
||||||
{"name":"powermanager.log"}
|
{"name":"powermanager.log"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ currently-running apps */
|
||||||
let brightnessSetting = settings.brightness || 1;
|
let brightnessSetting = settings.brightness || 1;
|
||||||
Bangle.setLCDBrightness = ((o) => (a) => {
|
Bangle.setLCDBrightness = ((o) => (a) => {
|
||||||
brightnessSetting = a;
|
brightnessSetting = a;
|
||||||
draw(WIDGETS.powermanager);
|
WIDGETS.powermanager.draw(WIDGETS.powermanager);
|
||||||
return o(a);
|
return o(a);
|
||||||
})(Bangle.setLCDBrightness);
|
})(Bangle.setLCDBrightness);
|
||||||
|
|
||||||
|
|
|
@ -13,5 +13,7 @@
|
||||||
0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11
|
0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11
|
||||||
0.13: Revert #1578 (stop duplicate entries) as with 2v12 menus it causes other boxes to be wiped (fix #1643)
|
0.13: Revert #1578 (stop duplicate entries) as with 2v12 menus it causes other boxes to be wiped (fix #1643)
|
||||||
0.14: Fix Bangle.js 1 issue where after the 'overwrite track' menu, the start/stop button stopped working
|
0.14: Fix Bangle.js 1 issue where after the 'overwrite track' menu, the start/stop button stopped working
|
||||||
0.15: Diverge from the standard "Run" app. Swipe to intensity interface a la Karvonnen (curtesy of FTeacher at https://github.com/f-teacher)
|
0.15: Diverge from the standard "Run" app. Swipe to intensity interface a la Karvonen (curtesy of FTeacher at https://github.com/f-teacher)
|
||||||
Keep run state between runs (allowing you to exit and restart the app)
|
Keep run state between runs (allowing you to exit and restart the app)
|
||||||
|
0.16: Don't clear zone 2b indicator segment when updating HRM reading.
|
||||||
|
Write to correct settings file, fixing settings not working.
|
||||||
|
|
|
@ -67,3 +67,10 @@ app loader, the module is automatically included in the app's source. However
|
||||||
when developing via the IDE the module won't get pulled in by default.
|
when developing via the IDE the module won't get pulled in by default.
|
||||||
|
|
||||||
There are some options to fix this easily - please check out the [modules README.md file](https://github.com/espruino/BangleApps/blob/master/modules/README.md)
|
There are some options to fix this easily - please check out the [modules README.md file](https://github.com/espruino/BangleApps/blob/master/modules/README.md)
|
||||||
|
## Contributors (Run and Run+)
|
||||||
|
gfwilliams
|
||||||
|
hughbarney
|
||||||
|
GrandVizierOlaf
|
||||||
|
BartS23
|
||||||
|
f-teacher
|
||||||
|
thyttan
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
let wu = require("widget_utils");
|
let wu = require("widget_utils");
|
||||||
|
|
||||||
let runInterval;
|
let runInterval;
|
||||||
let karvonnenActive = false;
|
let karvonenActive = false;
|
||||||
// Run interface wrapped in a function
|
// Run interface wrapped in a function
|
||||||
let ExStats = require("exstats");
|
let ExStats = require("exstats");
|
||||||
let B2 = process.env.HWVERSION===2;
|
let B2 = process.env.HWVERSION===2;
|
||||||
|
@ -63,7 +63,6 @@ function setStatus(running) {
|
||||||
function onStartStop() {
|
function onStartStop() {
|
||||||
let running = !exs.state.active;
|
let running = !exs.state.active;
|
||||||
let prepPromises = [];
|
let prepPromises = [];
|
||||||
|
|
||||||
// start/stop recording
|
// start/stop recording
|
||||||
// Do this first in case recorder needs to prompt for
|
// Do this first in case recorder needs to prompt for
|
||||||
// an overwrite before we start tracking exstats
|
// an overwrite before we start tracking exstats
|
||||||
|
@ -155,7 +154,7 @@ Bangle.on("GPS", function(fix) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// run() function used to switch between traditional run UI and karvonnen UI
|
// run() function used to switch between traditional run UI and karvonen UI
|
||||||
function run() {
|
function run() {
|
||||||
wu.show();
|
wu.show();
|
||||||
layout.lazy = false;
|
layout.lazy = false;
|
||||||
|
@ -165,35 +164,35 @@ function run() {
|
||||||
if (!runInterval){
|
if (!runInterval){
|
||||||
runInterval = setInterval(function() {
|
runInterval = setInterval(function() {
|
||||||
layout.clock.label = locale.time(new Date(),1);
|
layout.clock.label = locale.time(new Date(),1);
|
||||||
if (!isMenuDisplayed && !karvonnenActive) layout.render();
|
if (!isMenuDisplayed && !karvonenActive) layout.render();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
run();
|
run();
|
||||||
|
|
||||||
///////////////////////////////////////////////
|
///////////////////////////////////////////////
|
||||||
// Karvonnen
|
// Karvonen
|
||||||
///////////////////////////////////////////////
|
///////////////////////////////////////////////
|
||||||
|
|
||||||
function stopRunUI() {
|
function stopRunUI() {
|
||||||
// stop updating and drawing the traditional run app UI
|
// stop updating and drawing the traditional run app UI
|
||||||
clearInterval(runInterval);
|
clearInterval(runInterval);
|
||||||
runInterval = undefined;
|
runInterval = undefined;
|
||||||
karvonnenActive = true;
|
karvonenActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopKarvonnenUI() {
|
function stopKarvonenUI() {
|
||||||
g.reset().clear();
|
g.reset().clear();
|
||||||
clearInterval(karvonnenInterval);
|
clearInterval(karvonenInterval);
|
||||||
karvonnenInterval = undefined;
|
karvonenInterval = undefined;
|
||||||
karvonnenActive = false;
|
karvonenActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let karvonnenInterval;
|
let karvonenInterval;
|
||||||
// Define the function to go back and forth between the different UI's
|
// Define the function to go back and forth between the different UI's
|
||||||
function swipeHandler(LR,_) {
|
function swipeHandler(LR,_) {
|
||||||
if (LR==-1 && karvonnenActive && !isMenuDisplayed) {stopKarvonnenUI(); run();}
|
if (LR==-1 && karvonenActive && !isMenuDisplayed) {stopKarvonenUI(); run();}
|
||||||
if (LR==1 && !karvonnenActive && !isMenuDisplayed) {stopRunUI(); karvonnenInterval = eval(require("Storage").read("runplus_karvonnen"))(settings.HRM, exs.stats.bpm);}
|
if (LR==1 && !karvonenActive && !isMenuDisplayed) {stopRunUI(); karvonenInterval = eval(require("Storage").read("runplus_karvonen"))(settings.HRM, exs.stats.bpm);}
|
||||||
}
|
}
|
||||||
// Listen for swipes with the swipeHandler
|
// Listen for swipes with the swipeHandler
|
||||||
Bangle.on("swipe", swipeHandler);
|
Bangle.on("swipe", swipeHandler);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
(function karvonnen(hrmSettings, exsHrmStats) {
|
(function karvonen(hrmSettings, exsHrmStats) {
|
||||||
//This app is an extra feature implementation for the Run.app of the bangle.js. It's called run+
|
//This app is an extra feature implementation for the Run.app of the bangle.js. It's called run+
|
||||||
//The calculation of the Heart Rate Zones is based on the Karvonnen method. It requires to know maximum and minimum heart rates. More precise calculation methods require a lab.
|
//The calculation of the Heart Rate Zones is based on the Karvonen method. It requires to know maximum and minimum heart rates. More precise calculation methods require a lab.
|
||||||
//Other methods are even more approximative.
|
//Other methods are even more approximative.
|
||||||
let wu = require("widget_utils");
|
let wu = require("widget_utils");
|
||||||
wu.hide();
|
wu.hide();
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
let hr = exsHrmStats.getValue();
|
let hr = exsHrmStats.getValue();
|
||||||
let hr1 = hr;
|
let hr1 = hr;
|
||||||
// These letiables display next and previous HR zone.
|
// These letiables display next and previous HR zone.
|
||||||
//get the hrzones right. The calculation of the Heart rate zones here is based on the Karvonnen method
|
//get the hrzones right. The calculation of the Heart rate zones here is based on the Karvonen method
|
||||||
//60-70% of HRR+minHR = zone2. //70-80% of HRR+minHR = zone3. //80-90% of HRR+minHR = zone4. //90-99% of HRR+minHR = zone5. //=>99% of HRR+minHR = serious risk of heart attack
|
//60-70% of HRR+minHR = zone2. //70-80% of HRR+minHR = zone3. //80-90% of HRR+minHR = zone4. //90-99% of HRR+minHR = zone5. //=>99% of HRR+minHR = serious risk of heart attack
|
||||||
let minzone2 = hrr * 0.6 + minhr;
|
let minzone2 = hrr * 0.6 + minhr;
|
||||||
let maxzone2 = hrr * 0.7 + minhr;
|
let maxzone2 = hrr * 0.7 + minhr;
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
// HR data: large, readable, in the middle of the screen
|
// HR data: large, readable, in the middle of the screen
|
||||||
function drawHR() {
|
function drawHR() {
|
||||||
g.setFontAlign(-1,0,0);
|
g.setFontAlign(-1,0,0);
|
||||||
g.clearRect(Rdiv(x,11/4),Rdiv(y,2)-25,Rdiv(x,11/4)+50*2,Rdiv(y,2)+25);
|
g.clearRect(Rdiv(x,11/4),Rdiv(y,2)-25,Rdiv(x,11/4)+50*2-14,Rdiv(y,2)+25);
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.setFont("Vector",50);
|
g.setFont("Vector",50);
|
||||||
g.drawString(hr, Rdiv(x,11/4), Rdiv(y,2)+4);
|
g.drawString(hr, Rdiv(x,11/4), Rdiv(y,2)+4);
|
||||||
|
@ -207,9 +207,9 @@
|
||||||
initDraw();
|
initDraw();
|
||||||
|
|
||||||
// check for updates every second.
|
// check for updates every second.
|
||||||
karvonnenInterval = setInterval(function() {
|
karvonenInterval = setInterval(function() {
|
||||||
if (!isMenuDisplayed && karvonnenActive) updateUI();
|
if (!isMenuDisplayed && karvonenActive) updateUI();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
return karvonnenInterval;
|
return karvonenInterval;
|
||||||
})
|
})
|
|
@ -1,10 +1,10 @@
|
||||||
{
|
{
|
||||||
"id": "runplus",
|
"id": "runplus",
|
||||||
"name": "Run+",
|
"name": "Run+",
|
||||||
"version": "0.15",
|
"version": "0.16",
|
||||||
"description": "Displays distance, time, steps, cadence, pace and more for runners. Based on the Run app, but extended with additional screen for heart rate interval training.",
|
"description": "Displays distance, time, steps, cadence, pace and more for runners. Based on the Run app, but extended with additional screen for heart rate interval training.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "run,running,fitness,outdoors,gps,karvonnen",
|
"tags": "run,running,fitness,outdoors,gps,karvonen,karvonnen",
|
||||||
"supports": [
|
"supports": [
|
||||||
"BANGLEJS2"
|
"BANGLEJS2"
|
||||||
],
|
],
|
||||||
|
@ -29,8 +29,8 @@
|
||||||
"url": "settings.js"
|
"url": "settings.js"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "runplus_karvonnen",
|
"name": "runplus_karvonen",
|
||||||
"url": "karvonnen.js"
|
"url": "karvonen.js"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
(function(back) {
|
(function(back) {
|
||||||
const SETTINGS_FILE = "run.json";
|
const SETTINGS_FILE = "runplus.json";
|
||||||
var ExStats = require("exstats");
|
var ExStats = require("exstats");
|
||||||
var statsList = ExStats.getList();
|
var statsList = ExStats.getList();
|
||||||
statsList.unshift({name:"-",id:""}); // add blank menu item
|
statsList.unshift({name:"-",id:""}); // add blank menu item
|
||||||
|
|
|
@ -6,3 +6,5 @@
|
||||||
Update to match the default alarm widget, and not show itself when an alarm is hidden.
|
Update to match the default alarm widget, and not show itself when an alarm is hidden.
|
||||||
0.04: Fix check for active alarm
|
0.04: Fix check for active alarm
|
||||||
0.05: Convert Yes/No On/Off in settings to checkboxes
|
0.05: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
0.06: Remember next alarm to reduce calculation amount
|
||||||
|
Redraw only every hour when no alarm in next 24h
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "widalarmeta",
|
"id": "widalarmeta",
|
||||||
"name": "Alarm & Timer ETA",
|
"name": "Alarm & Timer ETA",
|
||||||
"shortName": "Alarm ETA",
|
"shortName": "Alarm ETA",
|
||||||
"version": "0.05",
|
"version": "0.06",
|
||||||
"description": "A widget that displays the time to the next Alarm or Timer in hours and minutes, maximum 24h (configurable).",
|
"description": "A widget that displays the time to the next Alarm or Timer in hours and minutes, maximum 24h (configurable).",
|
||||||
"icon": "widget.png",
|
"icon": "widget.png",
|
||||||
"type": "widget",
|
"type": "widget",
|
||||||
|
|
|
@ -1,24 +1,43 @@
|
||||||
(() => {
|
(() => {
|
||||||
require("Font5x9Numeric7Seg").add(Graphics);
|
require("Font5x9Numeric7Seg").add(Graphics);
|
||||||
const alarms = require("Storage").readJSON("sched.json",1) || [];
|
|
||||||
const config = Object.assign({
|
const config = Object.assign({
|
||||||
maxhours: 24,
|
maxhours: 24,
|
||||||
drawBell: false,
|
drawBell: false,
|
||||||
showSeconds: 0, // 0=never, 1=only when display is unlocked, 2=for less than a minute
|
showSeconds: 0, // 0=never, 1=only when display is unlocked, 2=for less than a minute
|
||||||
}, require("Storage").readJSON("widalarmeta.json",1) || {});
|
}, require("Storage").readJSON("widalarmeta.json",1) || {});
|
||||||
|
|
||||||
function draw() {
|
function getNextAlarm(date) {
|
||||||
const times = alarms
|
const alarms = (require("Storage").readJSON("sched.json",1) || []).filter(alarm => alarm.on && alarm.hidden !== true);
|
||||||
.map(alarm =>
|
WIDGETS["widalarmeta"].numActiveAlarms = alarms.length;
|
||||||
alarm.hidden !== true
|
const times = alarms.map(alarm => require("sched").getTimeToAlarm(alarm, date) || Number.POSITIVE_INFINITY);
|
||||||
&& require("sched").getTimeToAlarm(alarm)
|
const eta = times.length > 0 ? Math.min.apply(null, times) : 0;
|
||||||
)
|
if (eta !== Number.POSITIVE_INFINITY) {
|
||||||
.filter(a => a !== undefined);
|
const idx = times.indexOf(eta);
|
||||||
const next = times.length > 0 ? Math.min.apply(null, times) : 0;
|
const alarm = alarms[idx];
|
||||||
|
delete alarm.msg; delete alarm.id; delete alarm.data; // free some memory
|
||||||
|
return alarm;
|
||||||
|
}
|
||||||
|
} // getNextAlarm
|
||||||
|
|
||||||
|
function draw(fromInterval) {
|
||||||
|
if (this.nextAlarm === undefined) {
|
||||||
|
let alarm = getNextAlarm();
|
||||||
|
if (alarm === undefined) {
|
||||||
|
// try again with next hour
|
||||||
|
const nextHour = new Date();
|
||||||
|
nextHour.setHours(nextHour.getHours()+1);
|
||||||
|
alarm = getNextAlarm(nextHour);
|
||||||
|
}
|
||||||
|
if (alarm !== undefined) {
|
||||||
|
this.nextAlarm = alarm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const next = this.nextAlarm !== undefined ? require("sched").getTimeToAlarm(this.nextAlarm) : 0;
|
||||||
|
|
||||||
let calcWidth = 0;
|
let calcWidth = 0;
|
||||||
let drawSeconds = false;
|
let drawSeconds = false;
|
||||||
|
|
||||||
if (next > 0 && next < config.maxhours*60*60*1000) {
|
if (next > 0 && next <= config.maxhours*60*60*1000) {
|
||||||
const hours = Math.floor((next-1) / 3600000).toString();
|
const hours = Math.floor((next-1) / 3600000).toString();
|
||||||
const minutes = Math.floor(((next-1) % 3600000) / 60000).toString();
|
const minutes = Math.floor(((next-1) % 3600000) / 60000).toString();
|
||||||
const seconds = Math.floor(((next-1) % 60000) / 1000).toString();
|
const seconds = Math.floor(((next-1) % 60000) / 1000).toString();
|
||||||
|
@ -39,10 +58,14 @@
|
||||||
if (drawSeconds) {
|
if (drawSeconds) {
|
||||||
calcWidth += 3*5;
|
calcWidth += 3*5;
|
||||||
}
|
}
|
||||||
} else if (config.drawBell && alarms.some(alarm=>alarm.on&&(alarm.hidden!==true))) {
|
this.bellVisible = false;
|
||||||
// next alarm too far in future, draw only widalarm bell
|
} else if (config.drawBell && this.numActiveAlarms > 0) {
|
||||||
g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
|
|
||||||
calcWidth = 24;
|
calcWidth = 24;
|
||||||
|
// next alarm too far in future, draw only widalarm bell
|
||||||
|
if (this.bellVisible !== true || fromInterval !== true) {
|
||||||
|
g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
|
||||||
|
this.bellVisible = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.width !== calcWidth) {
|
if (this.width !== calcWidth) {
|
||||||
|
@ -51,8 +74,8 @@
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
}
|
}
|
||||||
|
|
||||||
// redraw next full minute or second
|
// redraw next hour when no alarm else full minute or second
|
||||||
const period = drawSeconds ? 1000 : 60000;
|
const period = next === 0 ? 3600000 : (drawSeconds ? 1000 : 60000);
|
||||||
let timeout = next > 0 ? next % period : period - (Date.now() % period);
|
let timeout = next > 0 ? next % period : period - (Date.now() % period);
|
||||||
if (timeout === 0) {
|
if (timeout === 0) {
|
||||||
timeout += period;
|
timeout += period;
|
||||||
|
@ -62,8 +85,8 @@
|
||||||
clearTimeout(this.timeoutId);
|
clearTimeout(this.timeoutId);
|
||||||
}
|
}
|
||||||
this.timeoutId = setTimeout(()=>{
|
this.timeoutId = setTimeout(()=>{
|
||||||
this.timeoutId = undefined;
|
WIDGETS["widalarmeta"].timeoutId = undefined;
|
||||||
this.draw();
|
WIDGETS["widalarmeta"].draw(true);
|
||||||
}, timeout);
|
}, timeout);
|
||||||
} /* draw */
|
} /* draw */
|
||||||
|
|
||||||
|
|
|
@ -15,3 +15,4 @@
|
||||||
0.16: Increase screen update rate when charging
|
0.16: Increase screen update rate when charging
|
||||||
0.17: Add option 'Remove Jitter'='Drop only' to prevent percentage from getting up again when not charging
|
0.17: Add option 'Remove Jitter'='Drop only' to prevent percentage from getting up again when not charging
|
||||||
Add option to disable vibration when charger connects
|
Add option to disable vibration when charger connects
|
||||||
|
0.18: Only redraw when values change
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "widbatpc",
|
"id": "widbatpc",
|
||||||
"name": "Battery Level Widget (with percentage)",
|
"name": "Battery Level Widget (with percentage)",
|
||||||
"shortName": "Battery Widget",
|
"shortName": "Battery Widget",
|
||||||
"version": "0.17",
|
"version": "0.18",
|
||||||
"description": "Show the current battery level and charging status in the top right of the clock, with charge percentage",
|
"description": "Show the current battery level and charging status in the top right of the clock, with charge percentage",
|
||||||
"icon": "widget.png",
|
"icon": "widget.png",
|
||||||
"type": "widget",
|
"type": "widget",
|
||||||
|
|
|
@ -86,7 +86,7 @@
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw() {
|
function draw(fromInterval) {
|
||||||
// if hidden, don't draw
|
// if hidden, don't draw
|
||||||
if (!WIDGETS["batpc"].width) return;
|
if (!WIDGETS["batpc"].width) return;
|
||||||
// else...
|
// else...
|
||||||
|
@ -103,6 +103,14 @@
|
||||||
l = prevMin;
|
l = prevMin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fromInterval === true && this.prevLevel === l && this.prevCharging === Bangle.isCharging()) {
|
||||||
|
return; // unchanged, do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prevLevel = l;
|
||||||
|
this.prevCharging = Bangle.isCharging();
|
||||||
|
|
||||||
const c = levelColor(l);
|
const c = levelColor(l);
|
||||||
|
|
||||||
if (Bangle.isCharging() && setting('charger')) {
|
if (Bangle.isCharging() && setting('charger')) {
|
||||||
|
@ -173,7 +181,7 @@
|
||||||
if (on) update();
|
if (on) update();
|
||||||
});
|
});
|
||||||
|
|
||||||
var id = setInterval(()=>WIDGETS["batpc"].draw(), intervalLow);
|
var id = setInterval(()=>WIDGETS["batpc"].draw(true), intervalLow);
|
||||||
|
|
||||||
WIDGETS["batpc"]={area:"tr",width:40,draw:draw,reload:reload};
|
WIDGETS["batpc"]={area:"tr",width:40,draw:draw,reload:reload};
|
||||||
setWidth();
|
setWidth();
|
||||||
|
|
|
@ -44,6 +44,7 @@ declare module ClockInfo {
|
||||||
w: number,
|
w: number,
|
||||||
h: number,
|
h: number,
|
||||||
draw(itm: MenuItem, info: Item, options: InteractiveOptions): void,
|
draw(itm: MenuItem, info: Item, options: InteractiveOptions): void,
|
||||||
|
app?: string, // used to remember clock_info locations, per app
|
||||||
};
|
};
|
||||||
|
|
||||||
type InteractiveOptions =
|
type InteractiveOptions =
|
||||||
|
|
|
@ -8688,6 +8688,15 @@ interface ObjectConstructor {
|
||||||
*/
|
*/
|
||||||
entries(object: any): Array<[string, any]>;
|
entries(object: any): Array<[string, any]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms an array of key-value pairs into an object
|
||||||
|
*
|
||||||
|
* @param {any} entries - An array of `[key,value]` pairs to be used to create an object
|
||||||
|
* @returns {any} An object containing all the specified pairs
|
||||||
|
* @url http://www.espruino.com/Reference#l_Object_fromEntries
|
||||||
|
*/
|
||||||
|
fromEntries(entries: any): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new object with the specified prototype object and properties.
|
* Creates a new object with the specified prototype object and properties.
|
||||||
* properties are currently unsupported.
|
* properties are currently unsupported.
|
||||||
|
@ -8709,6 +8718,15 @@ interface ObjectConstructor {
|
||||||
*/
|
*/
|
||||||
getOwnPropertyDescriptor(obj: any, name: any): any;
|
getOwnPropertyDescriptor(obj: any, name: any): any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get information on all properties in the object (from `Object.getOwnPropertyDescriptor`), or just `{}` if no properties
|
||||||
|
*
|
||||||
|
* @param {any} obj - The object
|
||||||
|
* @returns {any} An object containing all the property descriptors of an object
|
||||||
|
* @url http://www.espruino.com/Reference#l_Object_getOwnPropertyDescriptors
|
||||||
|
*/
|
||||||
|
getOwnPropertyDescriptors(obj: any): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new property to the Object. 'Desc' is an object with the following fields:
|
* Add a new property to the Object. 'Desc' is an object with the following fields:
|
||||||
* * `configurable` (bool = false) - can this property be changed/deleted (not
|
* * `configurable` (bool = false) - can this property be changed/deleted (not
|
||||||
|
@ -8945,32 +8963,32 @@ interface Function {
|
||||||
/**
|
/**
|
||||||
* This executes the function with the supplied 'this' argument and parameters
|
* This executes the function with the supplied 'this' argument and parameters
|
||||||
*
|
*
|
||||||
* @param {any} this - The value to use as the 'this' argument when executing the function
|
* @param {any} thisArg - The value to use as the 'this' argument when executing the function
|
||||||
* @param {any} params - Optional Parameters
|
* @param {any} params - Optional Parameters
|
||||||
* @returns {any} The return value of executing this function
|
* @returns {any} The return value of executing this function
|
||||||
* @url http://www.espruino.com/Reference#l_Function_call
|
* @url http://www.espruino.com/Reference#l_Function_call
|
||||||
*/
|
*/
|
||||||
call(this: any, ...params: any[]): any;
|
call(thisArg: any, ...params: any[]): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This executes the function with the supplied 'this' argument and parameters
|
* This executes the function with the supplied 'this' argument and parameters
|
||||||
*
|
*
|
||||||
* @param {any} this - The value to use as the 'this' argument when executing the function
|
* @param {any} thisArg - The value to use as the 'this' argument when executing the function
|
||||||
* @param {any} args - Optional Array of Arguments
|
* @param {any} args - Optional Array of Arguments
|
||||||
* @returns {any} The return value of executing this function
|
* @returns {any} The return value of executing this function
|
||||||
* @url http://www.espruino.com/Reference#l_Function_apply
|
* @url http://www.espruino.com/Reference#l_Function_apply
|
||||||
*/
|
*/
|
||||||
apply(this: any, args: any): any;
|
apply(thisArg: any, args: ArrayLike<any>): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This executes the function with the supplied 'this' argument and parameters
|
* This executes the function with the supplied 'this' argument and parameters
|
||||||
*
|
*
|
||||||
* @param {any} this - The value to use as the 'this' argument when executing the function
|
* @param {any} thisArg - The value to use as the 'this' argument when executing the function
|
||||||
* @param {any} params - Optional Default parameters that are prepended to the call
|
* @param {any} params - Optional Default parameters that are prepended to the call
|
||||||
* @returns {any} The 'bound' function
|
* @returns {any} The 'bound' function
|
||||||
* @url http://www.espruino.com/Reference#l_Function_bind
|
* @url http://www.espruino.com/Reference#l_Function_bind
|
||||||
*/
|
*/
|
||||||
bind(this: any, ...params: any[]): any;
|
bind(thisArg: any, ...params: any[]): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue