Skip to content

Commit bab86e5

Browse files
author
thyttan
committed
Merge branch 'messagegui' into app-loader
2 parents 8da5332 + 11fdf63 commit bab86e5

File tree

3 files changed

+173
-48
lines changed

3 files changed

+173
-48
lines changed

apps/messagegui/ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,4 @@
113113
0.82: Stop buzzing when a message is removed (e.g. call answered)
114114
0.83: Add option to not open the first unread message
115115
0.84: Fix: Assign show message entry to the settings menu and not the message itself.
116+
0.85: (WIP) refactor to display a scroller with three messages loaded. When scrolling to the top or end new/older messages are loaded in.

apps/messagegui/app.js

Lines changed: 171 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ if (Graphics.prototype.setFontIntl) {
4545
fontVLarge = noScale?"Intl":"Intl:3";
4646
}
4747

48-
var active; // active screen (undefined/"list"/"music"/"map"/"message"/"scroller"/"settings")
48+
var active; // active screen (undefined/"list"/"music"/"map"/"overview"/"scroller"/"settings")
4949
var openMusic = false; // go back to music screen after we handle something else?
5050
var replying = false; // If we're replying to a message, don't interrupt
5151
// hack for 2v10 firmware's lack of ':size' font handling
@@ -90,7 +90,8 @@ var onMessagesModified = function(type,msg) {
9090
}
9191
if (msg && msg.id=="nav" && msg.t=="modify" && active!="map")
9292
return; // don't show an updated nav message if we're just in the menu
93-
showMessage(msg&&msg.id, false);
93+
let persist = "messagegui.new.js"===global.__FILE__?false:true;
94+
showMessageRouter(msg, persist, "dependsOnActive");
9495
};
9596
Bangle.on("message", onMessagesModified);
9697

@@ -99,6 +100,39 @@ function saveMessages() {
99100
}
100101
E.on("kill", saveMessages);
101102

103+
function showMessageRouter(msg, persist, explicitDestnation) {
104+
//explicitDestnation (undefined/"scroller"/"overview"/"dependsOnActive")
105+
106+
////var active; // active screen (undefined/"list"/"music"/"map"/"overview"/"scroller"/"settings")
107+
//if (active==undefined) { } else if (active=="list") ... //and so on.
108+
109+
if (persist) {cancelReloadTimeout()}
110+
111+
if (msg.id=="music") {
112+
cancelReloadTimeout(); // don't auto-reload to clock now
113+
return showMusicMessage(msg);
114+
}
115+
if (msg.id=="nav") {
116+
cancelReloadTimeout(); // don't auto-reload to clock now
117+
return showMapMessage(msg);
118+
}
119+
if (msg.id=="call") {
120+
return showMessageOverview(msg.id, persist);
121+
}
122+
if ("scroller"===explicitDestnation) {
123+
return showMessagesScroller(msg, persist);
124+
}
125+
if ("overview"===explicitDestnation) {
126+
return showMessageOverview(msg.id, persist);
127+
}
128+
if ("dependsOnActive"===explicitDestnation) {
129+
if ("scroller"===active) {return showMessagesScroller(msg, persist);} // reinit scroller with updated messages list.
130+
if ("list"===active) {return returnToMain();}
131+
if ("settings"===active || "overview"===active) {return;}
132+
}
133+
//if (false) {showMessageSettings(msg);}
134+
}
135+
102136
function showMapMessage(msg) {
103137
active = "map";
104138
require("messages").stopBuzz(); // stop repeated buzzing while the map is showing
@@ -246,44 +280,136 @@ function showMusicMessage(msg) {
246280
}, 400);
247281
}
248282

249-
function showMessageScroller(msg) {
250-
cancelReloadTimeout();
283+
function showMessagesScroller(msg, persist) {
284+
if (persist===undefined) {persist = true;}
285+
const MSG_IDX = msg ? MESSAGES.findIndex((m)=>m.id==msg.id) : undefined;
286+
287+
if (replying) { return; }
251288
active = "scroller";
289+
if (persist) {cancelReloadTimeout();} else {resetReloadTimeout();}
290+
291+
const WU = require("widget_utils");
292+
WU.hide();
293+
const APP_RECT = Bangle.appRect;
294+
252295
var bodyFont = fontBig;
253296
g.setFont(bodyFont);
254-
var lines = [];
255-
if (msg.title) lines = g.wrapString(msg.title, g.getWidth()-10);
256-
var titleCnt = lines.length;
257-
if (titleCnt) lines.push(""); // add blank line after title
258-
lines = lines.concat(g.wrapString(msg.body, g.getWidth()-10),["",/*LANG*/"< Back"]);
297+
const FONT_HEIGHT = g.getFontHeight();
298+
let initScroll;
299+
var titleLines = [];
300+
let allLines = [];
301+
let firstTitleLinePerMsg = [];
302+
for (let i=0 ; i<MESSAGES.length ; i++) {
303+
if (MSG_IDX === i) {initScroll = allLines.length*FONT_HEIGHT}
304+
let msgIter = MESSAGES[i];
305+
306+
var lines = [];
307+
const TITLE_STRING = msgIter.title||msgIter.sender||msgIter.subject||msgIter.src||/*LANG*/"No Title";
308+
lines = g.wrapString(TITLE_STRING, APP_RECT.w-10);
309+
for (let i=0; i<lines.length; i++) {
310+
titleLines.push(i + allLines.length);
311+
if (i==0) firstTitleLinePerMsg.push(i + allLines.length);
312+
}
313+
lines = lines.concat(g.wrapString(msgIter.body, APP_RECT.w-10),
314+
["-".repeat(12)]);
315+
allLines = allLines.concat(lines);
316+
}
317+
318+
if (allLines.length == 0) {
319+
cancelReloadTimeout();
320+
returnToClockIfEmpty();
321+
}
322+
323+
let shownIdxFirst = allLines.length
324+
let shownIdxLast = 0;
325+
259326
E.showScroller({
260-
h : g.getFontHeight(), // height of each menu item in pixels
261-
c : lines.length, // number of menu items
327+
scroll : initScroll,
328+
h : FONT_HEIGHT, // height of each menu item in pixels
329+
c : allLines.length, // number of menu items
262330
// a function to draw a menu item
263-
draw : function(idx, r) {
264-
// FIXME: in 2v13 onwards, clearRect(r) will work fine. There's a bug in 2v12
265-
g.setBgColor(idx<titleCnt ? g.theme.bg2 : g.theme.bg).
266-
setColor(idx<titleCnt ? g.theme.fg2 : g.theme.fg).
267-
clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
268-
g.setFont(bodyFont).setFontAlign(0,-1).drawString(lines[idx], r.x+r.w/2, r.y);
269-
}, select : function(idx) {
270-
if (idx>=lines.length-2)
271-
showMessage(msg.id, true);
331+
draw : function(scrollIdx, r) {"ram";
332+
//print(scrollIdx)
333+
g.setBgColor(titleLines.find(e=>e==scrollIdx)!==undefined ? g.theme.bg2 : g.theme.bg).
334+
setColor(titleLines.find(e=>e==scrollIdx)!==undefined ? g.theme.fg2 : g.theme.fg).
335+
clearRect(r);
336+
g.setFont(bodyFont).setFontAlign(0,-1).drawString(allLines[scrollIdx], r.x+r.w/2, r.y);
337+
if (scrollIdx<shownIdxFirst) {shownIdxFirst = scrollIdx;}
338+
if (scrollIdx>shownIdxLast) {shownIdxLast = scrollIdx;}
339+
if (!persist) {resetReloadTimeout();}
272340
},
273-
back : () => showMessage(msg.id, true)
341+
select : function(scrollIdx, touch) {
342+
WU.show();
343+
for (let i=firstTitleLinePerMsg.length-1; i>=0 ; i--) {
344+
if (scrollIdx>=firstTitleLinePerMsg[i]) {
345+
if (!touch || touch.type===0) {
346+
showMessageRouter(MESSAGES[i], true, "overview")
347+
} else if (touch.type == 2) {
348+
showMessageSettings(MESSAGES[i]);
349+
}
350+
break;
351+
}
352+
}
353+
clearBtnHandler();
354+
updateReadMessages();
355+
}
274356
});
357+
358+
359+
// If Bangle.js 2 add an external select hw button handler.
360+
let btnHandler = ((2===process.env.HWVERSION) && (setWatch(()=>{
361+
Bangle.emit("drag", {dy:0}); // Compatibility with `kineticscroll`, stopping the scroller so it doesn't continue scrolling when the `showMessageOverview` screen is loaded.
362+
// Zero ms timeout as to not move on before the scroller has registered the emitted drag event.
363+
setTimeout(()=>{
364+
if ("messagegui.new.js"===global.__FILE__) {return load();}
365+
Bangle.emit("touch", 1, {x:APP_RECT.x2/2, y:APP_RECT.y2/2, type:0});
366+
},0);
367+
}, BTN, {edge:'rising', repeat:true})));
368+
369+
function clearBtnHandler() {
370+
if (btnHandler) {clearWatch(btnHandler); btnHandler=undefined;}
371+
}
372+
373+
function updateReadMessages() {
374+
let shownMsgIdxFirst, shownMsgIdxLast;
375+
const LINES_PER_SCREEN = APP_RECT.h/FONT_HEIGHT;
376+
//print(firstTitleLinePerMsg)
377+
//print(shownIdxFirst, shownIdxLast)
378+
379+
for (let i=0; i<firstTitleLinePerMsg.length-1 ; i++) {
380+
381+
if (shownIdxFirst<=firstTitleLinePerMsg[i] && shownIdxFirst+LINES_PER_SCREEN>firstTitleLinePerMsg[i]) {
382+
shownMsgIdxFirst = i;
383+
}
384+
385+
if (shownIdxLast>=firstTitleLinePerMsg[i+1] && shownIdxLast-LINES_PER_SCREEN<firstTitleLinePerMsg[i+1]) {
386+
shownMsgIdxLast = i;
387+
//print(i)
388+
}
389+
390+
}
391+
if (shownIdxLast===allLines.length-1) {shownMsgIdxLast = MESSAGES.length-1;}
392+
393+
//print(shownIdxFirst, shownIdxLast)
394+
//print(shownMsgIdxFirst, shownMsgIdxLast)
395+
//print(MESSAGES)
396+
for (let i=shownMsgIdxFirst; i<shownMsgIdxLast+1; i++) {
397+
MESSAGES[i].new = false;
398+
}
399+
//print(MESSAGES)
400+
}
275401
}
276402

277403
function showMessageSettings(msg) {
278404
active = "settings";
279405
var menu = {"":{
280406
"title":/*LANG*/"Message",
281-
back:() => showMessage(msg.id, true)
407+
back:() => showMessageOverview(msg.id, true)
282408
},
283409
};
284410

285411
if (msg.id!="music")
286-
menu[/*LANG*/"View Message"] = () => showMessageScroller(msg);
412+
menu[/*LANG*/"View Message"] = () => showMessagesScroller(msg);
287413

288414
if (msg.reply && reply) {
289415
menu[/*LANG*/"Reply"] = () => {
@@ -292,11 +418,11 @@ function showMessageSettings(msg) {
292418
.then(result => {
293419
Bluetooth.println(JSON.stringify(result));
294420
replying = false;
295-
showMessage(msg.id);
421+
showMessageOverview(msg.id);
296422
})
297423
.catch(() => {
298424
replying = false;
299-
showMessage(msg.id);
425+
showMessageOverview(msg.id);
300426
});
301427
};
302428
}
@@ -340,7 +466,7 @@ function showMessageSettings(msg) {
340466
E.showMenu(menu);
341467
}
342468

343-
function showMessage(msgid, persist) {
469+
function showMessageOverview(msgid, persist) {
344470
if (replying) { return; }
345471
if(!persist) resetReloadTimeout();
346472
let idx = MESSAGES.findIndex(m=>m.id==msgid);
@@ -350,15 +476,7 @@ function showMessage(msgid, persist) {
350476
updateLabelsInterval=undefined;
351477
}
352478
if (!msg) return returnToClockIfEmpty(); // go home if no message found
353-
if (msg.id=="music") {
354-
cancelReloadTimeout(); // don't auto-reload to clock now
355-
return showMusicMessage(msg);
356-
}
357-
if (msg.id=="nav") {
358-
cancelReloadTimeout(); // don't auto-reload to clock now
359-
return showMapMessage(msg);
360-
}
361-
active = "message";
479+
active = "overview";
362480
// Normal text message display
363481
var title=msg.title, titleFont = fontLarge, lines;
364482
var body=msg.body, bodyFont = fontLarge;
@@ -426,7 +544,7 @@ function showMessage(msgid, persist) {
426544
.catch(() => {
427545
replying = false;
428546
layout.render();
429-
showMessage(msg.id);
547+
showMessageOverview(msg.id);
430548
});
431549
}; footer.push({type:"img",src:atob("QRABAAAAAAAH//+AAAAABgP//8AAAAADgf//4AAAAAHg4ABwAAAAAPh8APgAAAAAfj+B////////geHv///////hf+f///////GPw///////8cGBwAAAAAPx/gDgAAAAAfD/gHAAAAAA8DngOAAAAABwDHP8AAAAADACGf4AAAAAAAAM/w=="),col:"#0f0", cb:posHandler});
432550
}
@@ -456,16 +574,16 @@ function showMessage(msgid, persist) {
456574
]},
457575
{type:"txt", font:bodyFont, label:body, fillx:1, filly:1, pad:2, cb:()=>{
458576
// allow tapping to show a larger version
459-
showMessageScroller(msg);
577+
showMessagesScroller(msg);
460578
} },
461579
{type:"h",fillx:1, c: footer}
462580
]},{back:goBack});
463581

464582
Bangle.swipeHandler = (lr,ud) => {
465583
if (lr>0 && posHandler) posHandler();
466584
if (lr<0 && negHandler) negHandler();
467-
if (ud>0 && idx<MESSAGES.length-1) showMessage(MESSAGES[idx+1].id, true);
468-
if (ud<0 && idx>0) showMessage(MESSAGES[idx-1].id, true);
585+
if (ud>0 && idx<MESSAGES.length-1) showMessageOverview(MESSAGES[idx+1].id, true);
586+
if (ud<0 && idx>0) showMessageOverview(MESSAGES[idx-1].id, true);
469587
};
470588
Bangle.on("swipe", Bangle.swipeHandler);
471589
g.reset().clearRect(Bangle.appRect);
@@ -502,8 +620,8 @@ function checkMessages(options) {
502620
// If we have a new message, show it
503621
if (!options.ignoreUnread && newMessages.length) {
504622
delete newMessages[0].show; // stop us getting stuck here if we're called a second time
505-
showMessage(newMessages[0].id, false);
506-
// buzz after showMessage, so being busy during layout doesn't affect the buzz pattern
623+
showMessagesScroller(newMessages[0], false);
624+
// buzz after showMessagesScroller, so being busy during scroller setup doesn't affect the buzz pattern
507625
if (global.BUZZ_ON_NEW_MESSAGE) {
508626
// this is set if we entered the messages app by loading `messagegui.new.js`
509627
// ... but only buzz the first time we view a new message
@@ -515,7 +633,7 @@ function checkMessages(options) {
515633
}
516634
// no new messages: show playing music? Only if we have playing music, or state=="show" (set by messagesmusic)
517635
if (options.openMusic && MESSAGES.some(m=>m.id=="music" && ((m.track && m.state=="play") || m.state=="show")))
518-
return showMessage('music', true);
636+
return showMessageOverview('music', true);
519637
// no new messages - go to clock?
520638
if (options.clockIfAllRead && newMessages.length==0)
521639
return load();
@@ -524,7 +642,7 @@ function checkMessages(options) {
524642
E.showScroller({
525643
h : 48,
526644
c : Math.max(MESSAGES.length,3), // workaround for 2v10.219 firmware (min 3 not needed for 2v11)
527-
draw : function(idx, r) {"ram"
645+
draw : function(idx, r) {"ram";
528646
var msg = MESSAGES[idx];
529647
if (msg && msg.new) g.setBgColor(g.theme.bgH).setColor(g.theme.fgH);
530648
else g.setBgColor(g.theme.bg).setColor(g.theme.fg);
@@ -564,13 +682,13 @@ function checkMessages(options) {
564682
},
565683
select : idx => {
566684
if (idx < MESSAGES.length)
567-
showMessage(MESSAGES[idx].id, true);
685+
showMessageRouter(MESSAGES[idx], true, "overview");
568686
},
569687
back : () => load()
570688
});
571689
}
572690

573-
function returnToCheckMessages(clock) {
691+
function returnToCheckMessages() {
574692
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,ignoreUnread:settings.ignoreUnread,openMusic});
575693
}
576694

@@ -611,8 +729,14 @@ setTimeout(() => {
611729
}, 10); // if checkMessages wants to 'load', do that
612730

613731
/* If the Bangle is unlocked by the user, treat that
614-
as a queue to stop repeated buzzing */
732+
as a queue to stop repeated buzzing.
733+
Also suspend the reload timeout while the watch is unlocked. */
615734
Bangle.on('lock',locked => {
616-
if (!locked)
735+
if (!locked) {
617736
require("messages").stopBuzz();
737+
cancelReloadTimeout();
738+
}
739+
if (locked) {
740+
if ("messagegui.new.js"===global.__FILE__) {resetReloadTimeout();}
741+
}
618742
});

apps/messagegui/metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"id": "messagegui",
33
"name": "Message UI",
44
"shortName": "Messages",
5-
"version": "0.84",
5+
"version": "0.85",
66
"description": "Default app to display notifications from iOS and Gadgetbridge/Android",
77
"icon": "app.png",
88
"type": "app",

0 commit comments

Comments
 (0)