Skip to content

Commit 843963d

Browse files
author
thyttan
committed
Merge branch 'messagegui' into app-loader
2 parents fbc7cb0 + 244a7c3 commit 843963d

File tree

3 files changed

+154
-48
lines changed

3 files changed

+154
-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: 152 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,117 @@ 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+
const LINES_PER_SCREEN = APP_RECT.h/FONT_HEIGHT;
299+
let initScroll;
300+
var titleLines = [];
301+
let allLines = [];
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+
msgIter.new = false;
306+
307+
var lines = [];
308+
const TITLE_STRING = msgIter.title||msgIter.sender||msgIter.subject||msgIter.src||/*LANG*/"No Title";
309+
lines = g.wrapString(TITLE_STRING, APP_RECT.w-10);
310+
for (let i=0; i<lines.length; i++) {
311+
titleLines.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+
function identifyDisplayedMsg(scrollIdx) {
324+
let firstTitleLinePerMsg = [titleLines[0]];
325+
for (let i=1; i<titleLines.length; i++) {
326+
if (titleLines[i]-titleLines[i-1] === 1) {continue;}
327+
firstTitleLinePerMsg.push(titleLines[i]);
328+
}
329+
for (let i=titleLines.length-1; i>=0 ; i--) {
330+
if (scrollIdx>=firstTitleLinePerMsg[i]) {
331+
return MESSAGES[i];
332+
}
333+
}
334+
}
335+
336+
// Used to choose how to identify displayed message when selecting with hw button.
337+
let prevScrollIdxs = [undefined, undefined]; // [prevIdx, prevPrevIdx]
338+
259339
E.showScroller({
260-
h : g.getFontHeight(), // height of each menu item in pixels
261-
c : lines.length, // number of menu items
340+
scroll : initScroll,
341+
h : FONT_HEIGHT, // height of each menu item in pixels
342+
c : allLines.length, // number of menu items
262343
// 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);
344+
draw : function(scrollIdx, r) {"ram";
345+
g.setBgColor(titleLines.find(e=>e==scrollIdx)!==undefined ? g.theme.bg2 : g.theme.bg).
346+
setColor(titleLines.find(e=>e==scrollIdx)!==undefined ? g.theme.fg2 : g.theme.fg).
347+
clearRect(r);
348+
g.setFont(bodyFont).setFontAlign(0,-1).drawString(allLines[scrollIdx], r.x+r.w/2, r.y);
349+
if (prevScrollIdxs[1]!==prevScrollIdxs[0]) {
350+
prevScrollIdxs[1] = prevScrollIdxs[0];
351+
if (!persist) {resetReloadTimeout();}
352+
}
353+
prevScrollIdxs[0] = scrollIdx;
272354
},
273-
back : () => showMessage(msg.id, true)
355+
select : function(scrollIdx, touch) {
356+
WU.show();
357+
const MSG_SELECT = identifyDisplayedMsg(scrollIdx);
358+
clearBtnHandler();
359+
if (!touch) {showMessageRouter(MSG_SELECT, true, "overview"); return;}
360+
if (touch.type == 0) {showMessageRouter(MSG_SELECT, true, "overview");}
361+
if (touch.type == 2) {showMessageSettings(MSG_SELECT);}
362+
}
274363
});
364+
365+
function clearBtnHandler() {
366+
if (Bangle.btnHandler) {clearWatch(Bangle.btnHandler); Bangle.btnHandler=undefined;}
367+
}
368+
clearBtnHandler();
369+
370+
// If Bangle.js 2 add an external select hw button handler.
371+
Bangle.btnHandler = ((2===process.env.HWVERSION) && (setWatch(()=>{
372+
Bangle.emit("drag", {dy:0}); // Compatibility with `kineticscroll`, stopping the scroller so it doesn't continue scrolling when the `showMessageOverview` screen is loaded.
373+
// Zero ms timeout as to not move on before the scroller has registered the emitted drag event.
374+
setTimeout(()=>{
375+
if ("messagegui.new.js"===global.__FILE__) {return load();}
376+
const SCROLL_IDX_CENTER_SCREEN = prevScrollIdxs[0]>prevScrollIdxs[1] ?
377+
prevScrollIdxs[0]-LINES_PER_SCREEN/2:prevScrollIdxs[0]+LINES_PER_SCREEN/2;
378+
WU.show();
379+
showMessageOverview(identifyDisplayedMsg(SCROLL_IDX_CENTER_SCREEN).id, true);
380+
},0);
381+
}, BTN, {edge:'rising'})));
275382
}
276383

277384
function showMessageSettings(msg) {
278385
active = "settings";
279386
var menu = {"":{
280387
"title":/*LANG*/"Message",
281-
back:() => showMessage(msg.id, true)
388+
back:() => showMessageOverview(msg.id, true)
282389
},
283390
};
284391

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

288395
if (msg.reply && reply) {
289396
menu[/*LANG*/"Reply"] = () => {
@@ -292,11 +399,11 @@ function showMessageSettings(msg) {
292399
.then(result => {
293400
Bluetooth.println(JSON.stringify(result));
294401
replying = false;
295-
showMessage(msg.id);
402+
showMessageOverview(msg.id);
296403
})
297404
.catch(() => {
298405
replying = false;
299-
showMessage(msg.id);
406+
showMessageOverview(msg.id);
300407
});
301408
};
302409
}
@@ -340,7 +447,7 @@ function showMessageSettings(msg) {
340447
E.showMenu(menu);
341448
}
342449

343-
function showMessage(msgid, persist) {
450+
function showMessageOverview(msgid, persist) {
344451
if (replying) { return; }
345452
if(!persist) resetReloadTimeout();
346453
let idx = MESSAGES.findIndex(m=>m.id==msgid);
@@ -350,15 +457,7 @@ function showMessage(msgid, persist) {
350457
updateLabelsInterval=undefined;
351458
}
352459
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";
460+
active = "overview";
362461
// Normal text message display
363462
var title=msg.title, titleFont = fontLarge, lines;
364463
var body=msg.body, bodyFont = fontLarge;
@@ -426,7 +525,7 @@ function showMessage(msgid, persist) {
426525
.catch(() => {
427526
replying = false;
428527
layout.render();
429-
showMessage(msg.id);
528+
showMessageOverview(msg.id);
430529
});
431530
}; footer.push({type:"img",src:atob("QRABAAAAAAAH//+AAAAABgP//8AAAAADgf//4AAAAAHg4ABwAAAAAPh8APgAAAAAfj+B////////geHv///////hf+f///////GPw///////8cGBwAAAAAPx/gDgAAAAAfD/gHAAAAAA8DngOAAAAABwDHP8AAAAADACGf4AAAAAAAAM/w=="),col:"#0f0", cb:posHandler});
432531
}
@@ -456,16 +555,16 @@ function showMessage(msgid, persist) {
456555
]},
457556
{type:"txt", font:bodyFont, label:body, fillx:1, filly:1, pad:2, cb:()=>{
458557
// allow tapping to show a larger version
459-
showMessageScroller(msg);
558+
showMessagesScroller(msg);
460559
} },
461560
{type:"h",fillx:1, c: footer}
462561
]},{back:goBack});
463562

464563
Bangle.swipeHandler = (lr,ud) => {
465564
if (lr>0 && posHandler) posHandler();
466565
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);
566+
if (ud>0 && idx<MESSAGES.length-1) showMessageOverview(MESSAGES[idx+1].id, true);
567+
if (ud<0 && idx>0) showMessageOverview(MESSAGES[idx-1].id, true);
469568
};
470569
Bangle.on("swipe", Bangle.swipeHandler);
471570
g.reset().clearRect(Bangle.appRect);
@@ -502,8 +601,8 @@ function checkMessages(options) {
502601
// If we have a new message, show it
503602
if (!options.ignoreUnread && newMessages.length) {
504603
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
604+
showMessagesScroller(newMessages[0], false);
605+
// buzz after showMessagesScroller, so being busy during scroller setup doesn't affect the buzz pattern
507606
if (global.BUZZ_ON_NEW_MESSAGE) {
508607
// this is set if we entered the messages app by loading `messagegui.new.js`
509608
// ... but only buzz the first time we view a new message
@@ -515,7 +614,7 @@ function checkMessages(options) {
515614
}
516615
// no new messages: show playing music? Only if we have playing music, or state=="show" (set by messagesmusic)
517616
if (options.openMusic && MESSAGES.some(m=>m.id=="music" && ((m.track && m.state=="play") || m.state=="show")))
518-
return showMessage('music', true);
617+
return showMessageOverview('music', true);
519618
// no new messages - go to clock?
520619
if (options.clockIfAllRead && newMessages.length==0)
521620
return load();
@@ -524,7 +623,7 @@ function checkMessages(options) {
524623
E.showScroller({
525624
h : 48,
526625
c : Math.max(MESSAGES.length,3), // workaround for 2v10.219 firmware (min 3 not needed for 2v11)
527-
draw : function(idx, r) {"ram"
626+
draw : function(idx, r) {"ram";
528627
var msg = MESSAGES[idx];
529628
if (msg && msg.new) g.setBgColor(g.theme.bgH).setColor(g.theme.fgH);
530629
else g.setBgColor(g.theme.bg).setColor(g.theme.fg);
@@ -564,13 +663,13 @@ function checkMessages(options) {
564663
},
565664
select : idx => {
566665
if (idx < MESSAGES.length)
567-
showMessage(MESSAGES[idx].id, true);
666+
showMessageRouter(MESSAGES[idx], true, "overview");
568667
},
569668
back : () => load()
570669
});
571670
}
572671

573-
function returnToCheckMessages(clock) {
672+
function returnToCheckMessages() {
574673
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,ignoreUnread:settings.ignoreUnread,openMusic});
575674
}
576675

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

613712
/* If the Bangle is unlocked by the user, treat that
614-
as a queue to stop repeated buzzing */
713+
as a queue to stop repeated buzzing.
714+
Also suspend the reload timeout while the watch is unlocked. */
615715
Bangle.on('lock',locked => {
616-
if (!locked)
716+
if (!locked) {
617717
require("messages").stopBuzz();
718+
cancelReloadTimeout();
719+
}
720+
if (locked) {
721+
if ("messagegui.new.js"===global.__FILE__) {resetReloadTimeout();}
722+
}
618723
});

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)