Compare commits

...

21 Commits

Author SHA1 Message Date
Tanner 4c506c7913 Make weather or temp data grey if it's old 2026-06-13 11:24:21 -06:00
tanner 8275abdb57 Swap stop watch positions 2026-03-12 12:58:52 -06:00
tanner 1c433fc56b Display both feels like and outdoor temperatures 2026-03-12 12:52:51 -06:00
tanner 14bda5c29f Use clearInterval to clear the stopwatch interval 2026-03-10 16:58:57 -06:00
tanner ea58f2101f Increase buzz duration 2026-03-10 16:54:44 -06:00
tanner 07bb582ddd feat: Initialize buzz state for stopwatches
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-10 16:22:44 -06:00
tanner 6d965dd016 feat: Add timed buzzing alerts to stopwatches
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-10 16:22:18 -06:00
tanner 04511d029c Disable submenu logging 2026-03-09 14:15:18 -06:00
tanner d2d89b361e fix: Load default stopwatch data if file is missing
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-09 14:12:12 -06:00
tanner 0b4d8d84c4 refactor: Improve stopwatch management and display 2026-03-09 14:12:06 -06:00
tanner dc44ac9910 fix: Ensure stopwatches display when paused and on app load
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-09 13:40:19 -06:00
tanner f96bddf0ba feat: Implement pause and stop functions for stopwatches
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-09 13:29:58 -06:00
tanner dda3130c56 Begin writing stop watch 2026-03-09 13:26:41 -06:00
tanner 9091d7a783 Remove old grid code 2026-03-09 12:02:39 -06:00
tanner 4a4be26977 Remove Anton font, unindent 1 level 2026-03-09 11:55:37 -06:00
tanner b4c6f1b0c8 Add UTC time to watch face 2026-02-25 19:44:58 -07:00
tanner 0f343e753f Dont cancel message timeout 2024-09-10 15:42:51 -06:00
tanner 6bd1f2f05b Add pull menu command, token auth to bangle requests 2024-09-09 13:59:01 -06:00
tanner fc8894015a Replace messagegui.app.js with updated app.js 2024-09-09 12:42:52 -06:00
tanner 0ba99a590a Apply my changes to messagegui app:
- skip the entire message display UI, go directly to
  showMessageScroller()
- modify message scroller font and alignment
- remove extra lines and < Back button
- make tapping the header open message settings
2024-09-09 12:38:17 -06:00
tanner 6167de7456 Add new messagegui app.js, indent 2024-09-09 12:16:55 -06:00
3 changed files with 695 additions and 491 deletions
+1
View File
@@ -0,0 +1 @@
.aider*
+403 -318
View File
File diff suppressed because one or more lines are too long
+291 -173
View File
@@ -1,34 +1,56 @@
/* MESSAGES is a list of: /* MESSAGES is a list of:
{id:int, {id:int,
src, src,
title, title,
subject, subject,
body, body,
sender, sender,
tel:string, tel:string,
new:true // not read yet new:true // not read yet
} }
*/ */
/* For example for maps: /* For example for maps:
// a message // a message
require("messages").pushMessage({"t":"add","id":1575479849,"src":"Clock","title":"Timer","body":"test"}) require("messages").pushMessage({"t":"add","id":1575479849,"src":"Skype","title":"My Friend","body":"Hey! How's everything going?",positive:1,negative:1})
// maps // maps
require("messages").pushMessage({"t":"add","id":1,"src":"Maps","title":"0 yd - High St","body":"Campton - 11:48 ETA","img":"GhqBAAAMAAAHgAAD8AAB/gAA/8AAf/gAP/8AH//gD/98B//Pg/4B8f8Afv+PP//n3/f5//j+f/wfn/4D5/8Aef+AD//AAf/gAD/wAAf4AAD8AAAeAAADAAA="}); GB({t:"nav",src:"maps",title:"Navigation",instr:"High St towards Tollgate Rd",distance:"966yd",action:"continue",eta:"08:39"})
GB({t:"nav",src:"maps",title:"Navigation",instr:"High St",distance:"12km",action:"left_slight",eta:"08:39"})
GB({t:"nav",src:"maps",title:"Navigation",instr:"Main St / I-29 ALT / Centerpoint Dr",distance:12345,action:"left_slight",eta:"08:39"})
// call // call
require("messages").pushMessage({"t":"add","id":"call","src":"Phone","title":"Bob","body":"12421312",positive:true,negative:true}) require("messages").pushMessage({"t":"add","id":"call","src":"Phone","title":"Bob","body":"12421312",positive:true,negative:true})
// message
// tanner
require("messages").pushMessage({"t":"add","id":"1234","src":"Telegram","title":"testing","body":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",positive:true,negative:true}) require("messages").pushMessage({"t":"add","id":"1234","src":"Telegram","title":"testing","body":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",positive:true,negative:true})
*/ */
var Layout = require("Layout"); var Layout = require("Layout");
var layout; // global var containing the layout for the currently displayed message
var settings = require('Storage').readJSON("messages.settings.json", true) || {}; var settings = require('Storage').readJSON("messages.settings.json", true) || {};
var reply;
try { reply = require("reply"); } catch (e) {}
var fontSmall = "6x8"; var fontSmall = "6x8";
var fontMedium = g.getFonts().includes("6x15")?"6x15":"6x8:2"; var fontMedium = g.getFonts().includes("6x15")?"6x15":"6x8:2";
var fontBig = g.getFonts().includes("12x20")?"12x20":"6x8:2"; var fontBig = g.getFonts().includes("12x20")?"12x20":"6x8:2";
var fontLarge = g.getFonts().includes("6x15")?"6x15:2":"6x8:4"; var fontLarge = g.getFonts().includes("6x15")?"6x15:2":"6x8:4";
var active; // active screen var fontVLarge = g.getFonts().includes("6x15")?"12x20:2":"6x8:5";
// If a font library is installed, just switch to using that for everything in messages
if (Graphics.prototype.setFontIntl) {
fontSmall = "Intl";
fontMedium = "Intl";
fontBig = "Intl";
/* 2v21 and before have a bug where the scale factor for PBF fonts wasn't
taken into account in metrics, so we can't have big fonts on those firmwares.
Having 'PBF' listed as a font was a bug fixed at the same time so we check for that. */
let noScale = g.getFonts().includes("PBF");
fontLarge = noScale?"Intl":"Intl:2";
fontVLarge = noScale?"Intl":"Intl:3";
}
var active; // active screen (undefined/"list"/"music"/"map"/"message"/"scroller"/"settings")
var openMusic = false; // go back to music screen after we handle something else? var openMusic = false; // go back to music screen after we handle something else?
var replying = false; // If we're replying to a message, don't interrupt
// hack for 2v10 firmware's lack of ':size' font handling // hack for 2v10 firmware's lack of ':size' font handling
try { try {
g.setFont("6x8:2"); g.setFont("6x8:2");
@@ -61,15 +83,17 @@ var onMessagesModified = function(type,msg) {
msg.handled = true; msg.handled = true;
require("messages").apply(msg, MESSAGES); require("messages").apply(msg, MESSAGES);
// TODO: if new, show this new one // TODO: if new, show this new one
if (msg && msg.id!=="music" && msg.new && active!="map" && if (msg && msg.id!=="music" && msg.id!=="nav" && msg.new &&
!((require('Storage').readJSON('setting.json', 1) || {}).quiet)) { !((require('Storage').readJSON('setting.json', 1) || {}).quiet)) {
require("messages").buzz(msg.src); require("messages").buzz(msg.src);
} }
if (msg && msg.id=="music") { if (msg && msg.id=="music") {
if (msg.state && msg.state!="play") openMusic = false; // no longer playing music to go back to if (msg.state && msg.state!="play") openMusic = false; // no longer playing music to go back to
if (active!="music") return; // don't open music over other screens if ((active!=undefined) && (active!="list") && (active!="music")) return; // don't open music over other screens (but do if we're in the main menu)
} }
showMessage(msg&&msg.id); if (msg && msg.id=="nav" && msg.t=="modify" && active!="map")
return; // don't show an updated nav message if we're just in the menu
showMessage(msg&&msg.id, false);
}; };
Bangle.on("message", onMessagesModified); Bangle.on("message", onMessagesModified);
@@ -80,30 +104,54 @@ E.on("kill", saveMessages);
function showMapMessage(msg) { function showMapMessage(msg) {
active = "map"; active = "map";
var m, distance, street, target, eta; require("messages").stopBuzz(); // stop repeated buzzing while the map is showing
m=msg.title.match(/(.*) - (.*)/); var m, distance, street, target, img;
if (m) { if ("string"==typeof msg.distance) // new gadgetbridge
distance = m[1]; distance = msg.distance;
street = m[2]; else if ("number"==typeof msg.distance) // 0.74 gadgetbridge
} else street=msg.title; distance = require("locale").distance(msg.distance);
m=msg.body.match(/(.*) - (.*)/); if (msg.instr) {
if (m) { var instr = msg.instr.replace(/\s*\/\s*/g," \/\n"); // convert slashes to newlines
target = m[1]; if (instr.includes("towards") || instr.includes("toward")) {
eta = m[2]; m = instr.split(/towards|toward/);
} else target=msg.body; target = m[0].trim();
street = m[1].trim();
}else
target = instr;
}
var carIsRHD = !!settings.carIsRHD;
switch (msg.action) {
case "continue": img = "EBgBAIABwAPgD/Af+D/8f/773/PPY8cDwAPAA8ADwAPAA8AAAAPAA8ADwAAAA8ADwAPA";break;
case "left": img = "GhcBAYAAAPAAAHwAAD4AAB8AAA+AAAf//8P///x///+PAAPx4AA8fAAHD4ABwfAAcDwAHAIABwAAAcAAAHAAABwAAAcAAAHAAABwAAAc";break;
case "right": img = "GhcBAABgAAA8AAAPgAAB8AAAPgAAB8D///j///9///+/AAPPAAHjgAD44AB8OAA+DgAPA4ABAOAAADgAAA4AAAOAAADgAAA4AAAOAAAA";break;
case "left_slight": img = "ERgB//B/+D/8H4AP4Af4A74Bz4Dj4HD4OD4cD4AD4ADwADwADgAHgAPAAOAAcAA4ABwADgAH";break;
case "right_slight": img = "ERgBB/+D/8H/4APwA/gD/APuA+cD44Phw+Dj4HPgAeAB4ADgAPAAeAA4ABwADgAHAAOAAcAA";break;
case "left_sharp": img = "GBaBAAAA+AAB/AAH/gAPjgAeBwA8BwB4B+DwB+HgB+PAB+eAB+8AB+4AB/wAB/gAB//gB//gB//gBwAABwAABwAABwAABw=="; break;
case "right_sharp": img = "GBaBAB8AAD+AAH/gAHHwAOB4AOA8AOAeAOAPB+AHh+ADx+AB5+AA9+AAd+AAP+AAH+AH/+AH/+AH/+AAAOAAAOAAAOAAAA==";break;
case "keep_left": img = "ERmBAACAAOAB+AD+AP+B/+H3+PO+8c8w4wBwADgAHgAPAAfAAfAAfAAfAAeAAeAAcAA8AA4ABwADgA==";break;
case "keep_right": img = "ERmBAACAAOAA/AD+AP+A//D/fPueeceY4YBwADgAPAAeAB8AHwAfAB8ADwAPAAcAB4ADgAHAAOAAAA==";break;
case "uturn_left": img = "GRiBAAAH4AAP/AAP/wAPj8APAfAPAHgHgB4DgA8BwAOA4AHAcADsOMB/HPA7zvgd9/gOf/gHH/gDh/gBwfgA4DgAcBgAOAAAHAAADgAABw==";break;
case "uturn_right": img = "GRiBAAPwAAf+AAf/gAfj4AfAeAPAHgPADwHgA4DgAcBwAOA4AHAcBjhuB5x/A+57gP99wD/84A/8cAP8OAD8HAA4DgAMBwAAA4AAAcAAAA==";break;
case "finish": img = "HhsBAcAAAD/AAAH/wAAPB4AAeA4AAcAcAAYIcAA4cMAA48MAA4cMAAYAcAAcAcAAcA4AAOA4AAOBxjwHBzjwHjj/4Dnn/4B3P/4B+Pj4A8fj8Acfj8AI//8AA//+AA/j+AB/j+AB/j/A";break;
case "roundabout_left": img = carIsRHD ? "HBaCAAADwAAAAAAAD/AAAVUAAD/wABVVUAD/wABVVVQD/wAAVABUD/wAAVAAFT/////wABX/////8AAF//////AABT/////wABUP/AAD/AAVA/8AA/8AVAD/wAD//VQAP/AAP/1QAA/wAA/9AAADwAAD/AAAAAAAA/wAAAAAAAP8AAAAAAAD/AAAAAAAA/wAAAAAAAP8AAAAAAAD/AA=" : "HRYCAAPAAAAAAAAD/AAD//AAA/8AD///AAP/AA////AD/wAD/wP/A/8AA/wAP8P/////AAP//////8AA///////AAD/P////8AAP8P/AABUAD/AP/AAFUA/8AP/AAFX//AAP/AAFf/wAAP8AAB/8AAAPAAAD8AAAAAAAAPwAAAAAAAA/AAAAAAAAD8AAAAAAAAPwAAAAAAAA/AAAAAAAAD8AAA==";break;
case "roundabout_right": img = carIsRHD ? "HRaCAAAAAAAA8AAAP/8AAP8AAD///AA/8AA////AA/8AP/A/8AA/8A/wAP8AA/8P8AA/////8/wAD///////AAD//////8AAP////8P8ABUAAP/A/8AVQAD/wA//1UAA/8AA//VAAP/AAA/9AAA/wAAAPwAAA8AAAA/AAAAAAAAD8AAAAAAAAPwAAAAAAAA/AAAAAAAAD8AAAAAAAAPwAAAAAAA=" : "HBYCAAAAAAPAAABVQAAP8AAFVVQAD/wAFVVVAAP/ABUAFQAA/8BUAAVAAD/wVAAP/////FAAD/////9QAA//////VAAP/////FQAP8AAP/AVAP/AAP/AFX//AAP/AAV//AAP/AAAf/AAD/AAAD/AAAPAAAA/wAAAAAAAP8AAAAAAAD/AAAAAAAA/wAAAAAAAP8AAAAAAAD/AAAAAAA==";break;
case "roundabout_straight": img = carIsRHD ? "EBuCAAADwAAAD/AAAD/8AAD//wAD///AD///8D/P8/z/D/D//A/wPzAP8AwA//UAA//1QA//9VA/8AFUP8AAVD8AAFQ/AABUPwAAVD8AAFQ/wABUP/ABVA//9VAD//VAAP/1AAAP8AAAD/AAAA/wAA==" : "EBsCAAPAAAAP8AAAP/wAAP//AAP//8AP///wP8/z/P8P8P/8D/A/MA/wDABf/wABX//ABV//8BVAD/wVAAP8FQAA/BUAAPwVAAD8FQAA/BUAA/wVQA/8BV//8AFf/8AAX/8AAA/wAAAP8AAAD/AA";break;
case "roundabout_uturn": img = carIsRHD ? "ICCBAAAAAAAAAAAAAAAAAAAP4AAAH/AAAD/4AAB4fAAA8DwAAPAcAADgHgAA4B4AAPAcAADwPAAAeHwAADz4AAAc8AAABPAAAADwAAAY8YAAPPPAAD73gAAf/4AAD/8AABf8AAAb+AAAHfAAABzwAAAcYAAAAAAAAAAAAAAAAAAAAAAA" : "ICABAAAAAAAAAAAAAAAAAAfwAAAP+AAAH/wAAD4eAAA8DwAAOA8AAHgHAAB4BwAAOA8AADwPAAA+HgAAHzwAAA84AAAPIAAADwAAAY8YAAPPPAAB73wAAf/4AAD/8AAAP+gAAB/YAAAPuAAADzgAAAY4AAAAAAAAAAAAAAAAAAAAAAA=";break;
}
layout = new Layout({ type:"v", c: [ layout = new Layout({ type:"v", c: [
{type:"txt", font:fontMedium, label:target, bgCol:g.theme.bg2, col: g.theme.fg2, fillx:1, pad:2 }, {type:"txt", font:street?fontMedium:fontLarge, label:target, bgCol:g.theme.bg2, col: g.theme.fg2, fillx:1, pad:3 },
{type:"h", bgCol:g.theme.bg2, col: g.theme.fg2, fillx:1, c: [ street?{type:"h", bgCol:g.theme.bg2, col: g.theme.fg2, fillx:1, c: [
{type:"txt", font:"6x8", label:"Towards" }, {type:"txt", font:"6x8", label:"Towards" },
{type:"txt", font:fontLarge, label:street } {type:"txt", font:fontLarge, label:street }
]}, ]}:{},
{type:"h",fillx:1, filly:1, c: [ {type:"h",fillx:1, filly:1, c: [
msg.img?{type:"img",src:atob(msg.img), scale:2}:{}, img?{type:"img",src:atob(img), scale:2, pad:6}:{},
{type:"v", fillx:1, c: [ {type:"v", fillx:1, c: [
{type:"txt", font:fontLarge, label:distance||"" } {type:"txt", font:fontVLarge, label:distance||"" }
]}, ]},
]}, ]},
{type:"txt", font:"6x8:2", label:eta } {type:"txt", font:"6x8:2", label:msg.eta?`ETA ${msg.eta}`:"" }
]}); ]});
g.reset().clearRect(Bangle.appRect); g.reset().clearRect(Bangle.appRect);
layout.render(); layout.render();
@@ -164,7 +212,7 @@ function showMusicMessage(msg) {
updateLabels(); updateLabels();
layout = new Layout({ type:"v", c: [ layout = new Layout({ type:"v", c: [
{type:"h", fillx:1, bgCol:g.theme.bg2, col: g.theme.fg2, c: [ {type:"h", fillx:1, bgCol:g.theme.bg2, col: g.theme.fg2, c: [
{ type:"v", fillx:1, c: [ { type:"v", fillx:1, c: [
{ type:"txt", font:fontMedium, bgCol:g.theme.bg2, label:artistName, pad:2, id:"artist" }, { type:"txt", font:fontMedium, bgCol:g.theme.bg2, label:artistName, pad:2, id:"artist" },
{ type:"txt", font:fontMedium, bgCol:g.theme.bg2, label:albumName, pad:2, id:"album" } { type:"txt", font:fontMedium, bgCol:g.theme.bg2, label:albumName, pad:2, id:"album" }
@@ -191,6 +239,7 @@ function showMusicMessage(msg) {
} }
function showMessageScroller(msg) { function showMessageScroller(msg) {
//cancelReloadTimeout(); // commented out because this is the new message display and we want it to disappear
active = "scroller"; active = "scroller";
var bodyFont = fontLarge; var bodyFont = fontLarge;
g.setFont(bodyFont); g.setFont(bodyFont);
@@ -210,27 +259,62 @@ function showMessageScroller(msg) {
setColor(idx<titleCnt ? g.theme.fg2 : g.theme.fg). setColor(idx<titleCnt ? g.theme.fg2 : g.theme.fg).
clearRect(r.x,r.y,r.x+r.w, r.y+r.h); clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
g.setFont(bodyFont).drawString(lines[idx], r.x, r.y); g.setFont(bodyFont).drawString(lines[idx], r.x, r.y);
//}, select : function(idx) { }, select : function(idx) {
// if (idx>=lines.length-2) if (idx==0) {
// showMessage(msg.id); cancelReloadTimeout(); // don't auto-reload to clock now
//}, showMessageSettings(msg);
//back : () => showMessage(msg.id) } else {
}, select : () => load(), load();
}
},
back : () => load() back : () => load()
}); });
} }
function showMessageSettings(msg) { function showMessageSettings(msg) {
active = "settings"; active = "settings";
E.showMenu({"":{"title":/*LANG*/"Message"}, var menu = {"":{"title":/*LANG*/"Message"},
"< Back" : () => showMessage(msg.id), "< Back" : () => showMessage(msg.id, true),
/*LANG*/"View Message" : () => { /*LANG*/"View Message" : () => {
showMessageScroller(msg); showMessageScroller(msg);
}, },
};
if (msg.reply && reply) {
menu[/*LANG*/"Reply"] = () => {
replying = true;
reply.reply({msg: msg})
.then(result => {
Bluetooth.println(JSON.stringify(result));
replying = false;
showMessage(msg.id);
})
.catch(() => {
replying = false;
showMessage(msg.id);
});
};
}
menu = Object.assign(menu, {
/*LANG*/"Delete" : () => { /*LANG*/"Delete" : () => {
MESSAGES = MESSAGES.filter(m=>m.id!=msg.id); MESSAGES = MESSAGES.filter(m=>m.id!=msg.id);
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0}); checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
}, },
});
if (Bangle.messageIgnore && msg.src)
menu[/*LANG*/"Ignore"] = () => {
E.showPrompt(/*LANG*/"Ignore all messages from "+E.toJS(msg.src)+"?", {title:/*LANG*/"Ignore"}).then(isYes => {
if (isYes) {
Bangle.messageIgnore(msg);
MESSAGES = MESSAGES.filter(m=>m.id!=msg.id);
}
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
});
};
menu = Object.assign(menu, {
/*LANG*/"Mark Unread" : () => { /*LANG*/"Mark Unread" : () => {
msg.new = true; msg.new = true;
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0}); checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
@@ -248,10 +332,14 @@ function showMessageSettings(msg) {
}); });
}, },
}); });
E.showMenu(menu);
} }
function showMessage(msgid) { function showMessage(msgid, persist) {
var msg = MESSAGES.find(m=>m.id==msgid); if (replying) { return; }
if(!persist) resetReloadTimeout();
let idx = MESSAGES.findIndex(m=>m.id==msgid);
var msg = MESSAGES[idx];
if (updateLabelsInterval) { if (updateLabelsInterval) {
clearInterval(updateLabelsInterval); clearInterval(updateLabelsInterval);
updateLabelsInterval=undefined; updateLabelsInterval=undefined;
@@ -261,146 +349,157 @@ function showMessage(msgid) {
cancelReloadTimeout(); // don't auto-reload to clock now cancelReloadTimeout(); // don't auto-reload to clock now
return showMusicMessage(msg); return showMusicMessage(msg);
} }
if (msg.src=="Maps") { if (msg.id=="nav") {
cancelReloadTimeout(); // don't auto-reload to clock now cancelReloadTimeout(); // don't auto-reload to clock now
return showMapMessage(msg); return showMapMessage(msg);
} }
active = "message"; // active = "message";
// Normal text message display // // Normal text message display
//var title=msg.title, titleFont = fontLarge, lines; // var title=msg.title, titleFont = fontLarge, lines;
//var body=msg.body, bodyFont = fontLarge; // var body=msg.body, bodyFont = fontLarge;
//// If no body, use the title text instead... // // If no body, use the title text instead...
//if (body===undefined) { // if (body===undefined) {
// body = title; // body = title;
// title = undefined; // title = undefined;
//} // }
//if (title) { // if (title) {
// var w = g.getWidth()-48; // var w = g.getWidth()-48;
// if (g.setFont(titleFont).stringWidth(title) > w) { // if (g.setFont(titleFont).stringWidth(title) > w) {
// titleFont = fontBig; // titleFont = fontBig;
// if (settings.fontSize!=1 && g.setFont(titleFont).stringWidth(title) > w) // if (settings.fontSize!=1 && g.setFont(titleFont).stringWidth(title) > w)
// titleFont = fontMedium; // titleFont = fontMedium;
// } // }
// if (g.setFont(titleFont).stringWidth(title) > w) { // if (g.setFont(titleFont).stringWidth(title) > w) {
// lines = g.wrapString(title, w); // lines = g.wrapString(title, w);
// title = (lines.length>2) ? lines.slice(0,2).join("\n")+"..." : lines.join("\n"); // title = (lines.length>2) ? lines.slice(0,2).join("\n")+"..." : lines.join("\n");
// } // }
//} // }
//// If body of message is only two lines long w/ large font, use large font. // if (body) { // Try and find a font that fits...
// var w = g.getWidth()-2, h = Bangle.appRect.h-60;
//if (body) { // if (g.setFont(bodyFont).wrapString(body, w).length*g.getFontHeight() > h) {
// var w = g.getWidth()-10; // bodyFont = fontBig;
// if (false && g.setFont(bodyFont).stringWidth(body) > w * 2) { // if (settings.fontSize!=1 && g.setFont(bodyFont).wrapString(body, w).length*g.getFontHeight() > h) {
// bodyFont = fontBig; // bodyFont = fontMedium;
// if (settings.fontSize!=1 && g.setFont(bodyFont).stringWidth(body) > w * 3) // }
// bodyFont = fontMedium; // }
// } // // Now crop, given whatever font we have available
// if (g.setFont(bodyFont).stringWidth(body) > w) { // lines = g.setFont(bodyFont).wrapString(body, w);
// lines = g.setFont(bodyFont).wrapString(msg.body, w); // var maxLines = Math.floor(h / g.getFontHeight());
// var maxLines = Math.floor((g.getHeight()-75) / g.getFontHeight()); // if (lines.length>maxLines) // if too long, wrap with a bit less spae so we have room for '...'
// body = (lines.length>maxLines) ? lines.slice(0,maxLines).join("\n")+"..." : lines.join("\n"); // body = g.setFont(bodyFont).wrapString(body, w-10).slice(0,maxLines).join("\n")+"...";
// } // else
//} // body = lines.join("\n");
//function goBack() { // }
// layout = undefined; // function goBack() {
// msg.new = false; // read mail // layout = undefined;
// cancelReloadTimeout(); // don't auto-reload to clock now // msg.new = false; // read mail
// checkMessages({clockIfNoMsg:1,clockIfAllRead:0,showMsgIfUnread:0,openMusic:openMusic}); // cancelReloadTimeout(); // don't auto-reload to clock now
//} // checkMessages({clockIfNoMsg:1,clockIfAllRead:0,showMsgIfUnread:0,openMusic:openMusic});
//var buttons = [ // }
//]; // var negHandler,posHandler,footer = [ ];
//if (false && msg.positive) { // if (msg.negative) {
// buttons.push({type:"btn", src:atob("GRSBAAAAAYAAAcAAAeAAAfAAAfAAAfAAAfAAAfAAAfBgAfA4AfAeAfAPgfAD4fAA+fAAP/AAD/AAA/AAAPAAADAAAA=="), cb:()=>{ // negHandler = ()=>{
// msg.new = false; // msg.new = false;
// cancelReloadTimeout(); // don't auto-reload to clock now // cancelReloadTimeout(); // don't auto-reload to clock now
// Bangle.messageResponse(msg,true); // Bangle.messageResponse(msg,false);
// checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1,openMusic:openMusic}); // checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1,openMusic:openMusic});
// }}); // }; footer.push({type:"img",src:atob("PhAB4A8AAAAAAAPAfAMAAAAAD4PwHAAAAAA/H4DwAAAAAH78B8AAAAAA/+A/AAAAAAH/Af//////w/gP//////8P4D///////H/Af//////z/4D8AAAAAB+/AfAAAAAA/H4DwAAAAAPg/AcAAAAADwHwDAAAAAA4A8AAAAAAAA=="),col:"#f00",cb:negHandler});
//} // }
//if (false && msg.negative) { // footer.push({fillx:1}); // push images to left/right
// if (buttons.length) buttons.push({width:32}); // nasty hack... // if (msg.reply && reply) {
// buttons.push({type:"btn", src:atob("FhaBADAAMeAB78AP/4B/fwP4/h/B/P4D//AH/4AP/AAf4AB/gAP/AB/+AP/8B/P4P4fx/A/v4B//AD94AHjAAMA="), cb:()=>{ // posHandler = ()=>{
// msg.new = false; // replying = true;
// cancelReloadTimeout(); // don't auto-reload to clock now // msg.new = false;
// Bangle.messageResponse(msg,false); // cancelReloadTimeout(); // don't auto-reload to clock now
// checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1,openMusic:openMusic}); // reply.reply({msg: msg})
// }}); // .then(result => {
//} // Bluetooth.println(JSON.stringify(result));
// replying = false;
// layout.render();
//layout = new Layout({ type:"v", c: [ // checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1,openMusic:openMusic});
// {type:"h", fillx:1, bgCol:g.theme.bg2, col: g.theme.fg2, c: [ // })
// { type:"v", fillx:1, c: [ // .catch(() => {
// {type:"txt", font:fontSmall, label:msg.src||/*LANG*/"Message", bgCol:g.theme.bg2, col: g.theme.fg2, fillx:1, pad:2, halign:1 }, // replying = false;
// title?{type:"txt", font:titleFont, label:title, bgCol:g.theme.bg2, col: g.theme.fg2, fillx:1, pad:2 }:{}, // layout.render();
// ]}, // showMessage(msg.id);
// { type:"btn", // });
// src:require("messageicons").getImage(msg), // }; footer.push({type:"img",src:atob("QRABAAAAAAAH//+AAAAABgP//8AAAAADgf//4AAAAAHg4ABwAAAAAPh8APgAAAAAfj+B////////geHv///////hf+f///////GPw///////8cGBwAAAAAPx/gDgAAAAAfD/gHAAAAAA8DngOAAAAABwDHP8AAAAADACGf4AAAAAAAAM/w=="),col:"#0f0", cb:posHandler});
// col:require("messageicons").getColor(msg, {settings:settings, default:g.theme.fg2}), // }
// pad: 3, cb:()=>{ // else if (msg.positive) {
// cancelReloadTimeout(); // don't auto-reload to clock now // posHandler = ()=>{
// showMessageSettings(msg); // msg.new = false;
// } // cancelReloadTimeout(); // don't auto-reload to clock now
// }, // Bangle.messageResponse(msg,true);
// ]}, // checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1,openMusic:openMusic});
// {type:"txt", font:bodyFont, label:body, fillx:1, filly:1, pad:2, cb:()=>{ // }; footer.push({type:"img",src:atob("QRABAAAAAAAAAAOAAAAABgAAA8AAAAADgAAD4AAAAAHgAAPgAAAAAPgAA+AAAAAAfgAD4///////gAPh///////gA+D///////AD4H//////8cPgAAAAAAPw8+AAAAAAAfB/4AAAAAAA8B/gAAAAAABwB+AAAAAAADAB4AAAAAAAAABgAA=="),col:"#0f0",cb:posHandler});
// // allow tapping to show a larger version // }
// showMessageScroller(msg); //
// } }, // layout = new Layout({ type:"v", c: [
// {type:"h",fillx:1, c: buttons} // {type:"h", fillx:1, bgCol:g.theme.bg2, col: g.theme.fg2, c: [
//]},{back:goBack}); // { type:"v", fillx:1, c: [
//g.reset().clearRect(Bangle.appRect); // {type:"txt", font:fontSmall, label:msg.src||/*LANG*/"Message", bgCol:g.theme.bg2, col: g.theme.fg2, fillx:1, pad:2, halign:1 },
//layout.render(); // title?{type:"txt", font:titleFont, label:title, bgCol:g.theme.bg2, col: g.theme.fg2, fillx:1, pad:2 }:{},
// ]},
// { type:"btn",
// src:require("messageicons").getImage(msg),
// col:require("messageicons").getColor(msg, {settings:settings, default:g.theme.fg2}),
// pad: 3, cb:()=>{
// cancelReloadTimeout(); // don't auto-reload to clock now
// showMessageSettings(msg);
// }
// },
// ]},
// {type:"txt", font:bodyFont, label:body, fillx:1, filly:1, pad:2, cb:()=>{
// // allow tapping to show a larger version
// showMessageScroller(msg);
// } },
// {type:"h",fillx:1, c: footer}
// ]},{back:goBack});
//
// Bangle.swipeHandler = (lr,ud) => {
// if (lr>0 && posHandler) posHandler();
// if (lr<0 && negHandler) negHandler();
// if (ud>0 && idx<MESSAGES.length-1) showMessage(MESSAGES[idx+1].id, true);
// if (ud<0 && idx>0) showMessage(MESSAGES[idx-1].id, true);
// };
// Bangle.on("swipe", Bangle.swipeHandler);
// g.reset().clearRect(Bangle.appRect);
// layout.render();
showMessageScroller(msg); showMessageScroller(msg);
} }
/* options = { /* options = {
clockIfNoMsg : bool clockIfNoMsg : bool
clockIfAllRead : bool clockIfAllRead : bool
showMsgIfUnread : bool showMsgIfUnread : bool
openMusic : bool // open music if it's playing openMusic : bool // open music if it's playing
dontStopBuzz : bool // don't stuf buzzing (any time other than the first this is undefined/false)
} }
*/ */
function checkMessages(options) { function checkMessages(options) {
options=options||{}; options=options||{};
// If there's been some user interaction, it's time to stop repeated buzzing
var goBack = false; if (!options.dontStopBuzz)
while (MESSAGES.length && ( require("messages").stopBuzz();
MESSAGES[0].title == "Timer" ||
MESSAGES[0].title == "Clock" ||
MESSAGES[0].title == "Stopwatch" ||
MESSAGES[0].title == "Navigation" ||
MESSAGES[0].title.startsWith("Protostable Chatspace") ||
MESSAGES[0].title.includes("Jason") ||
MESSAGES[0].body.toLowerCase().startsWith("!chat") ||
MESSAGES[0].body.toLowerCase().startsWith("! chat")
)) {
MESSAGES.pop(0);
goBack = true;
}
if (goBack) {
return load();
}
// If no messages, just show 'no messages' and return // If no messages, just show 'no messages' and return
if (!MESSAGES.length) { if (!MESSAGES.length) {
active=undefined; // no messages
if (!options.clockIfNoMsg) return E.showPrompt(/*LANG*/"No Messages",{ if (!options.clockIfNoMsg) return E.showPrompt(/*LANG*/"No Messages",{
title:/*LANG*/"Messages", title:/*LANG*/"Messages",
img:require("heatshrink").decompress(atob("kkk4UBrkc/4AC/tEqtACQkBqtUDg0VqAIGgoZFDYQIIM1sD1QAD4AIBhnqA4WrmAIBhc6BAWs8AIBhXOBAWz0AIC2YIC5wID1gkB1c6BAYFBEQPqBAYXBEQOqBAnDAIQaEnkAngaEEAPDFgo+IKA5iIOhCGIAFb7RqAIGgtUBA0VqobFgNVA")), img:require("heatshrink").decompress(atob("kkk4UBrkc/4AC/tEqtACQkBqtUDg0VqAIGgoZFDYQIIM1sD1QAD4AIBhnqA4WrmAIBhc6BAWs8AIBhXOBAWz0AIC2YIC5wID1gkB1c6BAYFBEQPqBAYXBEQOqBAnDAIQaEnkAngaEEAPDFgo+IKA5iIOhCGIAFb7RqAIGgtUBA0VqobFgNVA")),
buttons : {/*LANG*/"Ok":1} buttons : {/*LANG*/"Ok":1},
}).then(() => { load() }); back: () => load()
}).then(() => load());
return load(); return load();
} }
// we have >0 messages // we have >0 messages
var newMessages = MESSAGES.filter(m=>m.new&&m.id!="music"); var newMessages = MESSAGES.filter(m=>m.new&&m.id!="music");
// If we have a new message, show it // If we have a new message, show it
if (options.showMsgIfUnread && newMessages.length) { if (options.showMsgIfUnread && newMessages.length) {
delete newMessages[0].show; // stop us getting stuck here if we're called a second time delete newMessages[0].show; // stop us getting stuck here if we're called a second time
showMessage(newMessages[0].id); showMessage(newMessages[0].id, false);
// buzz after showMessage, so being busy during layout doesn't affect the buzz pattern // buzz after showMessage, so being busy during layout doesn't affect the buzz pattern
if (global.BUZZ_ON_NEW_MESSAGE) { if (global.BUZZ_ON_NEW_MESSAGE) {
// this is set if we entered the messages app by loading `messagegui.new.js` // this is set if we entered the messages app by loading `messagegui.new.js`
@@ -413,12 +512,12 @@ function checkMessages(options) {
} }
// no new messages: show playing music? Only if we have playing music, or state=="show" (set by messagesmusic) // no new messages: show playing music? Only if we have playing music, or state=="show" (set by messagesmusic)
if (options.openMusic && MESSAGES.some(m=>m.id=="music" && ((m.track && m.state=="play") || m.state=="show"))) if (options.openMusic && MESSAGES.some(m=>m.id=="music" && ((m.track && m.state=="play") || m.state=="show")))
return showMessage('music'); return showMessage('music', true);
// no new messages - go to clock? // no new messages - go to clock?
if (options.clockIfAllRead && newMessages.length==0) if (options.clockIfAllRead && newMessages.length==0)
return load(); return load();
active = "main"; active = "list";
// Otherwise show a menu // Otherwise show a list of messages
E.showScroller({ E.showScroller({
h : 48, h : 48,
c : Math.max(MESSAGES.length,3), // workaround for 2v10.219 firmware (min 3 not needed for 2v11) c : Math.max(MESSAGES.length,3), // workaround for 2v10.219 firmware (min 3 not needed for 2v11)
@@ -441,22 +540,29 @@ function checkMessages(options) {
.setColor(fg); // only color the icon .setColor(fg); // only color the icon
x += 50; x += 50;
} }
var m = msg.title+"\n"+msg.body, longBody=false;
if (title) g.setFontAlign(-1,-1).setFont(fontBig).drawString(title, x,r.y+2); if (title) g.setFontAlign(-1,-1).setFont(fontBig).drawString(title, x,r.y+2);
var longBody = false;
if (body) { if (body) {
g.setFontAlign(-1,-1).setFont("6x8"); g.setFontAlign(-1,-1).setFont(fontSmall);
// if the body includes an image, it probably won't be small enough to allow>1 line
let maxLines = Math.floor(34/g.getFontHeight()), pady = 0;
if (body.includes("\0")) { maxLines=1; pady=4; }
var l = g.wrapString(body, r.w-(x+14)); var l = g.wrapString(body, r.w-(x+14));
if (l.length>3) { if (l.length>maxLines) {
l = l.slice(0,3); l = l.slice(0,maxLines);
l[l.length-1]+="..."; l[l.length-1]+="...";
} }
longBody = l.length>2; longBody = l.length>2;
g.drawString(l.join("\n"), x+10,r.y+20); // draw the body
g.drawString(l.join("\n"), x+10,r.y+20+pady);
} }
if (!longBody && msg.src) g.setFontAlign(1,1).setFont("6x8").drawString(msg.src, r.x+r.w-2, r.y+r.h-2); if (!longBody && msg.src) g.setFontAlign(1,1).setFont("6x8").drawString(msg.src, r.x+r.w-2, r.y+r.h-2);
g.setColor("#888").fillRect(r.x,r.y+r.h-1,r.x+r.w-1,r.y+r.h-1); // dividing line between items g.setColor("#888").fillRect(r.x,r.y+r.h-1,r.x+r.w-1,r.y+r.h-1); // dividing line between items
}, },
select : idx => showMessage(MESSAGES[idx].id), select : idx => {
if (idx < MESSAGES.length)
showMessage(MESSAGES[idx].id, true);
},
back : () => load() back : () => load()
}); });
} }
@@ -468,6 +574,13 @@ function cancelReloadTimeout() {
unreadTimeout = undefined; unreadTimeout = undefined;
} }
function resetReloadTimeout(){
cancelReloadTimeout();
if (!isFinite(settings.unreadTimeout)) settings.unreadTimeout=60;
if (settings.unreadTimeout)
unreadTimeout = setTimeout(load, settings.unreadTimeout*1000);
}
g.clear(); g.clear();
Bangle.loadWidgets(); Bangle.loadWidgets();
@@ -475,12 +588,17 @@ require("messages").toggleWidget(false);
Bangle.drawWidgets(); Bangle.drawWidgets();
setTimeout(() => { setTimeout(() => {
if (!isFinite(settings.unreadTimeout)) settings.unreadTimeout=60;
if (settings.unreadTimeout)
unreadTimeout = setTimeout(load, settings.unreadTimeout*1000);
// only openMusic on launch if music is new, or state=="show" (set by messagesmusic) // only openMusic on launch if music is new, or state=="show" (set by messagesmusic)
var musicMsg = MESSAGES.find(m => m.id === "music"); var musicMsg = MESSAGES.find(m => m.id === "music");
checkMessages({ checkMessages({
clockIfNoMsg: 0, clockIfAllRead: 0, showMsgIfUnread: 1, clockIfNoMsg: 0, clockIfAllRead: 0, showMsgIfUnread: 1,
openMusic: ((musicMsg&&musicMsg.new) && settings.openMusic) || (musicMsg&&musicMsg.state=="show") }); openMusic: ((musicMsg&&musicMsg.new) && settings.openMusic) || (musicMsg&&musicMsg.state=="show"),
dontStopBuzz: 1 });
}, 10); // if checkMessages wants to 'load', do that }, 10); // if checkMessages wants to 'load', do that
/* If the Bangle is unlocked by the user, treat that
as a queue to stop repeated buzzing */
Bangle.on('lock',locked => {
if (!locked)
require("messages").stopBuzz();
});