BangleApps/apps/messagesoverlay/lib.js

540 lines
13 KiB
JavaScript
Raw Normal View History

let lockListener;
2023-02-26 18:22:11 +00:00
let quiet;
2024-03-28 14:37:17 +00:00
var settings = Object.assign(
require('Storage').readJSON("messagesoverlay.default.json", true) || {},
require('Storage').readJSON("messagesoverlay.json", true) || {}
);
settings = Object.assign({
fontSmall:"6x8",
fontMedium:"Vector:14",
fontBig:g.getFonts().includes("Vector") ? "Vector:20" : "6x8:3",
2024-03-28 14:37:17 +00:00
fontLarge:"Vector:30",
reemit: true
}, settings);
const ovrx = settings.border;
const ovry = ovrx;
const ovrw = g.getWidth()-2*ovrx;
const ovrh = g.getHeight()-2*ovry;
2024-03-29 08:27:40 +00:00
let LOG=()=>{};
//LOG = function() { print.apply(null, arguments);};
let isQuiet = function(){
2023-02-26 18:22:11 +00:00
if (quiet == undefined) quiet = (require('Storage').readJSON('setting.json', 1) || {}).quiet;
return quiet;
};
2023-02-26 10:03:25 +00:00
let eventQueue = [];
let callInProgress = false;
let buzzing = false;
2023-02-26 20:17:58 +00:00
let show = function(ovr){
let img = ovr;
LOG("show", img.getBPP());
2023-02-26 20:17:58 +00:00
if (ovr.getBPP() == 1) {
img = ovr.asImage();
img.palette = new Uint16Array([g.theme.fg,g.theme.bg]);
2023-02-26 20:17:58 +00:00
}
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":
2023-02-26 10:03:25 +00:00
eventQueue.unshift(event);
if (!callInProgress)
showMessage(ovr, event);
break;
case "modify": {
let find = false;
2023-02-26 10:03:25 +00:00
eventQueue.forEach(element => {
if (element.id == event.id) {
find = true;
Object.assign(element, event);
}
});
if (!find)
2023-02-26 10:03:25 +00:00
eventQueue.unshift(event);
if (!callInProgress)
showMessage(ovr, event);
break;
}
case "remove":
2023-02-26 10:03:25 +00:00
if (eventQueue.length == 0 && !callInProgress)
next(ovr);
2023-02-26 10:03:25 +00:00
if (!callInProgress && eventQueue[0] !== undefined && eventQueue[0].id == event.id)
next(ovr);
else
eventQueue = [];
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.setColor(ovr.theme.fg);
ovr.setBgColor(ovr.theme.bg);
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);
2023-02-26 18:22:11 +00:00
if (title) ovr.drawString(title, textCenter, 38/2 + 5);
ovr.setColor(ovr.theme.fg);
ovr.setFont(settings.fontMedium);
roundedRect(ovr, ovr.getWidth()-26,5,22,30,false);
2023-02-26 18:22:11 +00:00
ovr.setFont("Vector:16");
ovr.drawString("X",ovr.getWidth()-14,21);
ovr.setColor("#888");
roundedRect(ovr, 5,5,30,30,true);
2023-02-26 20:17:58 +00:00
ovr.setColor(ovr.getBPP() != 1 ? iconcolor : ovr.theme.bg2);
ovr.drawImage(icon,8,8);
};
let showMessage = function(ovr, msg) {
LOG("showMessage");
2023-02-26 10:03:25 +00:00
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));
drawMessage(ovr, msg);
if (!isQuiet() && msg.new) {
msg.new = false;
if (!buzzing){
buzzing = true;
Bangle.buzz().then(()=>{setTimeout(()=>{buzzing = false;},2000);});
}
Bangle.setLCDPower(1);
}
};
let drawBorder = function(img) {
LOG("drawBorder", isQuiet());
ovr.setBgColor(ovr.theme.bg);
if (img) ovr=img;
if (Bangle.isLocked())
ovr.setColor(ovr.theme.fg);
else
ovr.setColor(ovr.theme.fgH);
ovr.drawRect(0,0,ovr.getWidth()-1,ovr.getHeight()-1);
ovr.drawRect(1,1,ovr.getWidth()-2,ovr.getHeight()-2);
ovr.drawRect(2,38,ovr.getWidth()-2,39);
2023-02-26 20:17:58 +00:00
show(ovr);
};
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));
2023-02-26 10:03:25 +00:00
stopCallBuzz();
if (!isQuiet()) {
if (msg.new) {
msg.new = false;
2023-02-26 14:53:00 +00:00
if (callBuzzTimer) clearInterval(callBuzzTimer);
2023-02-26 10:03:25 +00:00
callBuzzTimer = setInterval(function() {
Bangle.buzz(500);
}, 1000);
Bangle.buzz(500);
}
}
2023-02-26 10:03:25 +00:00
drawMessage(ovr, msg);
};
let next = function(ovr) {
LOG("next");
2023-02-26 10:03:25 +00:00
stopCallBuzz();
if (!callInProgress)
2023-02-26 10:03:25 +00:00
eventQueue.shift();
callInProgress = false;
2023-02-26 10:03:25 +00:00
if (eventQueue.length == 0) {
LOG("no element in queue - closing");
cleanup();
return false;
}
2023-02-26 10:03:25 +00:00
showMessage(ovr, eventQueue[0]);
return true;
};
2023-02-26 10:03:25 +00:00
let callBuzzTimer = null;
let stopCallBuzz = function() {
if (callBuzzTimer) {
clearInterval(callBuzzTimer);
2023-02-26 14:53:00 +00:00
callBuzzTimer = undefined;
}
};
2023-02-26 10:03:25 +00:00
let drawTriangleUp = function(ovr) {
ovr.fillPoly([ovr.getWidth()-9, 46,ovr.getWidth()-14, 56,ovr.getWidth()-4, 56]);
};
2023-02-26 10:03:25 +00:00
let drawTriangleDown = function(ovr) {
ovr.fillPoly([ovr.getWidth()-9, ovr.getHeight()-6, ovr.getWidth()-14, ovr.getHeight()-16, ovr.getWidth()-4, ovr.getHeight()-16]);
};
2023-02-26 10:03:25 +00:00
let scrollUp = function(ovr) {
let msg = eventQueue[0];
2023-02-26 10:03:25 +00:00
LOG("up", msg);
if (typeof msg.FirstLine === "undefined")
msg.FirstLine = 0;
2023-02-26 10:03:25 +00:00
if (typeof msg.CanscrollUp === "undefined")
msg.CanscrollUp = false;
2023-02-26 10:03:25 +00:00
if (!msg.CanscrollUp) return;
msg.FirstLine = msg.FirstLine > 0 ? msg.FirstLine - 1 : 0;
2023-02-26 10:03:25 +00:00
drawMessage(ovr, msg);
};
2023-02-26 10:03:25 +00:00
let scrollDown = function(ovr) {
let msg = eventQueue[0];
2023-02-26 10:03:25 +00:00
LOG("down", msg);
if (typeof msg.FirstLine === "undefined")
msg.FirstLine = 0;
2023-02-26 10:03:25 +00:00
if (typeof msg.CanscrollDown === "undefined")
msg.CanscrollDown = false;
2023-02-26 10:03:25 +00:00
if (!msg.CanscrollDown) return;
msg.FirstLine = msg.FirstLine + 1;
2023-02-26 10:03:25 +00:00
drawMessage(ovr, msg);
};
2023-02-26 10:03:25 +00:00
let drawMessage = function(ovr, msg) {
let wrapString = function(str, maxWidth) {
str = str.replace("\r\n", "\n").replace("\r", "\n");
return ovr.wrapString(str, maxWidth);
};
let wrappedStringHeight = function(strArray){
let r = 0;
strArray.forEach((line, i) => {
let metrics = ovr.stringMetrics(line);
r += Math.max(metrics.height, metrics.maxImageHeight);
});
return r;
};
ovr.setColor(ovr.theme.fg);
ovr.setBgColor(ovr.theme.bg);
if (typeof msg.FirstLine === "undefined") msg.FirstLine = 0;
let padding = eventQueue.length > 1 ? (eventQueue.length > 3 ? 7 : 5) : 3;
let yText = 40;
let yLine = yText + 3;
let maxTextHeight = ovr.getHeight() - yLine - padding;
if (typeof msg.lines === "undefined") {
let bodyFont = settings.fontBig;
ovr.setFont(bodyFont);
msg.lines = wrapString(msg.body, ovr.getWidth() - 4 - padding);
if (wrappedStringHeight(msg.lines) > maxTextHeight) {
bodyFont = settings.fontMedium;
ovr.setFont(bodyFont);
msg.lines = wrapString(msg.body, ovr.getWidth() - 4 - padding);
}
msg.bodyFont = bodyFont;
msg.lineHeights = [];
msg.lines.forEach((line, i) => {
let metrics = ovr.stringMetrics(line);
msg.lineHeights[i] = Math.max(metrics.height, metrics.maxImageHeight);
});
}
ovr.setFont(msg.bodyFont);
ovr.setColor(ovr.theme.fg);
ovr.setBgColor(ovr.theme.bg);
ovr.clearRect(2, yText, ovr.getWidth()-3, ovr.getHeight()-3);
let xText = 4;
if (msg.bodyFont == settings.fontBig) {
ovr.setFontAlign(0, -1);
xText = Math.round(ovr.getWidth() / 2 - (padding - 3) / 2) + 1;
yLine = (ovr.getHeight() + yLine) / 2 - (wrappedStringHeight(msg.lines) / 2);
ovr.drawString(msg.lines.join("\n"), xText, yLine);
} else {
ovr.setFontAlign(-1, -1);
}
let currentLine = msg.FirstLine;
let drawnHeight = 0;
while(drawnHeight < maxTextHeight) {
let lineHeight = msg.lineHeights[currentLine];
if (drawnHeight + lineHeight < maxTextHeight) {
ovr.drawString(msg.lines[currentLine], xText, yLine + drawnHeight);
drawnHeight += lineHeight;
currentLine++;
} else {
break;
}
}
if (eventQueue.length > 1){
ovr.drawLine(ovr.getWidth()-4,ovr.getHeight()/2,ovr.getWidth()-4,ovr.getHeight()-4);
ovr.drawLine(ovr.getWidth()/2,ovr.getHeight()-4,ovr.getWidth()-4,ovr.getHeight()-4);
}
if (eventQueue.length > 3){
ovr.drawLine(ovr.getWidth()-6,ovr.getHeight()*0.6,ovr.getWidth()-6,ovr.getHeight()-6);
ovr.drawLine(ovr.getWidth()*0.6,ovr.getHeight()-6,ovr.getWidth()-6,ovr.getHeight()-6);
}
ovr.setColor(ovr.theme.fg2);
if (msg.FirstLine != 0) {
2023-02-26 10:03:25 +00:00
msg.CanscrollUp = true;
drawTriangleUp(ovr);
} else
2023-02-26 10:03:25 +00:00
msg.CanscrollUp = false;
if (currentLine < msg.lines.length) {
2023-02-26 10:03:25 +00:00
msg.CanscrollDown = true;
drawTriangleDown(ovr);
} else
2023-02-26 10:03:25 +00:00
msg.CanscrollDown = false;
2023-02-26 20:17:58 +00:00
show(ovr);
};
let getDragHandler = function(ovr){
return (e) => {
if (e.dy > 0) {
2023-02-26 10:03:25 +00:00
scrollUp(ovr);
} else if (e.dy < 0){
2023-02-26 10:03:25 +00:00
scrollDown(ovr);
}
};
};
let getTouchHandler = function(ovr){
2023-02-26 10:03:25 +00:00
return (_, xy) => {
if (xy.y < ovry + 40){
next(ovr);
}
};
};
let restoreHandler = function(event){
LOG("Restore", backup[event]);
Bangle.removeAllListeners(event);
Bangle["#on" + event]=backup[event];
backup[event] = undefined;
};
let backupHandler = function(event){
if (backupDone) return; // do not backup, overlay is already up
backup[event] = Bangle["#on" + event];
LOG("Backed up", backup[event]);
Bangle.removeAllListeners(event);
};
let cleanup = function(){
if (lockListener) {
Bangle.removeListener("lock", lockListener);
lockListener = undefined;
}
restoreHandler("touch");
restoreHandler("swipe");
restoreHandler("drag");
Bangle.setLCDOverlay();
backupDone = false;
ovr = undefined;
2023-02-26 18:22:11 +00:00
quiet = undefined;
};
let backup = {};
let backupDone = false;
let main = function(ovr, event) {
LOG("Main", event.t);
2023-02-26 14:53:00 +00:00
if (!lockListener) {
lockListener = function (e){
updateClearingTimeout();
drawBorder();
};
Bangle.on('lock', lockListener);
}
backupHandler("touch");
backupHandler("swipe");
backupHandler("drag");
if (!backupDone){
Bangle.on('touch', getTouchHandler(ovr));
Bangle.on('drag', getDragHandler(ovr));
}
backupDone=true;
if (event !== undefined){
drawBorder(ovr);
manageEvent(ovr, event);
} else {
LOG("No event given");
cleanup();
}
};
let ovr;
let clearingTimeout;
let updateClearingTimeout = ()=>{
LOG("updateClearingTimeout");
if (settings.autoclear <= 0)
return;
LOG("Remove clearing timeout", clearingTimeout);
if (clearingTimeout) clearTimeout(clearingTimeout);
if (Bangle.isLocked()){
LOG("Set new clearing timeout");
clearingTimeout = setTimeout(()=>{
LOG("setNewTimeout");
let event = eventQueue.pop();
if (event)
drawMessage(ovr, event);
if (eventQueue.length > 0){
LOG("still got elements");
updateClearingTimeout();
} else {
cleanup();
}
}, settings.autoclear * 1000);
} else {
clearingTimeout = undefined;
}
};
exports.message = function(type, event) {
LOG("Got message", type, event);
// only handle some event types
if(!(type=="text" || type == "call")) return;
if(type=="text" && event.id == "nav") return;
if(event.handled) return;
if(event.messagesoverlayignore) return;
2023-02-26 20:17:58 +00:00
bpp = 4;
2024-03-28 14:37:17 +00:00
if (process.memory().free < settings.lowmwm)
bpp = 1;
2023-02-26 20:17:58 +00:00
2024-03-28 14:37:17 +00:00
while (process.memory().free < settings.minfreemem && eventQueue.length > 0){
let dropped = eventQueue.pop();
print("Dropped message because of memory constraints", dropped);
}
if (!ovr || ovr.getBPP() != bpp) {
2023-02-26 20:17:58 +00:00
ovr = Graphics.createArrayBuffer(ovrw, ovrh, bpp, {
msb: true
});
}
ovr.reset();
2023-02-26 20:17:58 +00:00
if (bpp == 4)
ovr.theme = g.theme;
else
ovr.theme = { fg:0, bg:1, fg2:1, bg2:0, fgH:1, bgH:0 };
ovr.clear();
main(ovr, event);
updateClearingTimeout();
if (!isQuiet()) Bangle.setLCDPower(1);
event.handled = true;
};