Skip to content

Commit bb37799

Browse files
author
thyttan
committed
Merge branch 'messagegui' into app-loader
2 parents fbc7cb0 + 40b5f4c commit bb37799

File tree

3 files changed

+135
-48
lines changed

3 files changed

+135
-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: 133 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,98 @@ 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+
msgIter.new = false; // FIXME: Update "new" status in the scrollers draw method instead. A message should not be marked as read if it has not been displayed on screen.
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+
if (i==0) firstTitleLinePerMsg.push(i + allLines.length);
313+
}
314+
lines = lines.concat(g.wrapString(msgIter.body, APP_RECT.w-10),
315+
["-".repeat(12)]);
316+
allLines = allLines.concat(lines);
317+
}
318+
319+
if (allLines.length == 0) {
320+
cancelReloadTimeout();
321+
returnToClockIfEmpty();
322+
}
323+
259324
E.showScroller({
260-
h : g.getFontHeight(), // height of each menu item in pixels
261-
c : lines.length, // number of menu items
325+
scroll : initScroll,
326+
h : FONT_HEIGHT, // height of each menu item in pixels
327+
c : allLines.length, // number of menu items
262328
// 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);
329+
draw : function(scrollIdx, r) {"ram";
330+
g.setBgColor(titleLines.find(e=>e==scrollIdx)!==undefined ? g.theme.bg2 : g.theme.bg).
331+
setColor(titleLines.find(e=>e==scrollIdx)!==undefined ? g.theme.fg2 : g.theme.fg).
332+
clearRect(r);
333+
g.setFont(bodyFont).setFontAlign(0,-1).drawString(allLines[scrollIdx], r.x+r.w/2, r.y);
334+
if (!persist) {resetReloadTimeout();}
272335
},
273-
back : () => showMessage(msg.id, true)
336+
select : function(scrollIdx, touch) {
337+
WU.show();
338+
clearBtnHandler();
339+
for (let i=firstTitleLinePerMsg.length-1; i>=0 ; i--) {
340+
if (scrollIdx>=firstTitleLinePerMsg[i]) {
341+
if (!touch || touch.type===0) {showMessageRouter(MESSAGES[i], true,
342+
"overview"); return;}
343+
if (touch.type == 2) {showMessageSettings(MESSAGES[i]);}
344+
break;
345+
}
346+
}
347+
}
274348
});
349+
350+
// If Bangle.js 2 add an external select hw button handler.
351+
let btnHandler = ((2===process.env.HWVERSION) && (setWatch(()=>{
352+
Bangle.emit("drag", {dy:0}); // Compatibility with `kineticscroll`, stopping the scroller so it doesn't continue scrolling when the `showMessageOverview` screen is loaded.
353+
// Zero ms timeout as to not move on before the scroller has registered the emitted drag event.
354+
setTimeout(()=>{
355+
if ("messagegui.new.js"===global.__FILE__) {return load();}
356+
Bangle.emit("touch", 1, {x:APP_RECT.x2/2, y:APP_RECT.y2/2, type:0});
357+
},0);
358+
}, BTN, {edge:'rising', repeat:true})));
359+
360+
function clearBtnHandler() {
361+
if (btnHandler) {clearWatch(btnHandler); btnHandler=undefined;}
362+
}
275363
}
276364

277365
function showMessageSettings(msg) {
278366
active = "settings";
279367
var menu = {"":{
280368
"title":/*LANG*/"Message",
281-
back:() => showMessage(msg.id, true)
369+
back:() => showMessageOverview(msg.id, true)
282370
},
283371
};
284372

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

288376
if (msg.reply && reply) {
289377
menu[/*LANG*/"Reply"] = () => {
@@ -292,11 +380,11 @@ function showMessageSettings(msg) {
292380
.then(result => {
293381
Bluetooth.println(JSON.stringify(result));
294382
replying = false;
295-
showMessage(msg.id);
383+
showMessageOverview(msg.id);
296384
})
297385
.catch(() => {
298386
replying = false;
299-
showMessage(msg.id);
387+
showMessageOverview(msg.id);
300388
});
301389
};
302390
}
@@ -340,7 +428,7 @@ function showMessageSettings(msg) {
340428
E.showMenu(menu);
341429
}
342430

343-
function showMessage(msgid, persist) {
431+
function showMessageOverview(msgid, persist) {
344432
if (replying) { return; }
345433
if(!persist) resetReloadTimeout();
346434
let idx = MESSAGES.findIndex(m=>m.id==msgid);
@@ -350,15 +438,7 @@ function showMessage(msgid, persist) {
350438
updateLabelsInterval=undefined;
351439
}
352440
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";
441+
active = "overview";
362442
// Normal text message display
363443
var title=msg.title, titleFont = fontLarge, lines;
364444
var body=msg.body, bodyFont = fontLarge;
@@ -426,7 +506,7 @@ function showMessage(msgid, persist) {
426506
.catch(() => {
427507
replying = false;
428508
layout.render();
429-
showMessage(msg.id);
509+
showMessageOverview(msg.id);
430510
});
431511
}; footer.push({type:"img",src:atob("QRABAAAAAAAH//+AAAAABgP//8AAAAADgf//4AAAAAHg4ABwAAAAAPh8APgAAAAAfj+B////////geHv///////hf+f///////GPw///////8cGBwAAAAAPx/gDgAAAAAfD/gHAAAAAA8DngOAAAAABwDHP8AAAAADACGf4AAAAAAAAM/w=="),col:"#0f0", cb:posHandler});
432512
}
@@ -456,16 +536,16 @@ function showMessage(msgid, persist) {
456536
]},
457537
{type:"txt", font:bodyFont, label:body, fillx:1, filly:1, pad:2, cb:()=>{
458538
// allow tapping to show a larger version
459-
showMessageScroller(msg);
539+
showMessagesScroller(msg);
460540
} },
461541
{type:"h",fillx:1, c: footer}
462542
]},{back:goBack});
463543

464544
Bangle.swipeHandler = (lr,ud) => {
465545
if (lr>0 && posHandler) posHandler();
466546
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);
547+
if (ud>0 && idx<MESSAGES.length-1) showMessageOverview(MESSAGES[idx+1].id, true);
548+
if (ud<0 && idx>0) showMessageOverview(MESSAGES[idx-1].id, true);
469549
};
470550
Bangle.on("swipe", Bangle.swipeHandler);
471551
g.reset().clearRect(Bangle.appRect);
@@ -502,8 +582,8 @@ function checkMessages(options) {
502582
// If we have a new message, show it
503583
if (!options.ignoreUnread && newMessages.length) {
504584
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
585+
showMessagesScroller(newMessages[0], false);
586+
// buzz after showMessagesScroller, so being busy during scroller setup doesn't affect the buzz pattern
507587
if (global.BUZZ_ON_NEW_MESSAGE) {
508588
// this is set if we entered the messages app by loading `messagegui.new.js`
509589
// ... but only buzz the first time we view a new message
@@ -515,7 +595,7 @@ function checkMessages(options) {
515595
}
516596
// no new messages: show playing music? Only if we have playing music, or state=="show" (set by messagesmusic)
517597
if (options.openMusic && MESSAGES.some(m=>m.id=="music" && ((m.track && m.state=="play") || m.state=="show")))
518-
return showMessage('music', true);
598+
return showMessageOverview('music', true);
519599
// no new messages - go to clock?
520600
if (options.clockIfAllRead && newMessages.length==0)
521601
return load();
@@ -524,7 +604,7 @@ function checkMessages(options) {
524604
E.showScroller({
525605
h : 48,
526606
c : Math.max(MESSAGES.length,3), // workaround for 2v10.219 firmware (min 3 not needed for 2v11)
527-
draw : function(idx, r) {"ram"
607+
draw : function(idx, r) {"ram";
528608
var msg = MESSAGES[idx];
529609
if (msg && msg.new) g.setBgColor(g.theme.bgH).setColor(g.theme.fgH);
530610
else g.setBgColor(g.theme.bg).setColor(g.theme.fg);
@@ -564,13 +644,13 @@ function checkMessages(options) {
564644
},
565645
select : idx => {
566646
if (idx < MESSAGES.length)
567-
showMessage(MESSAGES[idx].id, true);
647+
showMessageRouter(MESSAGES[idx], true, "overview");
568648
},
569649
back : () => load()
570650
});
571651
}
572652

573-
function returnToCheckMessages(clock) {
653+
function returnToCheckMessages() {
574654
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,ignoreUnread:settings.ignoreUnread,openMusic});
575655
}
576656

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

613693
/* If the Bangle is unlocked by the user, treat that
614-
as a queue to stop repeated buzzing */
694+
as a queue to stop repeated buzzing.
695+
Also suspend the reload timeout while the watch is unlocked. */
615696
Bangle.on('lock',locked => {
616-
if (!locked)
697+
if (!locked) {
617698
require("messages").stopBuzz();
699+
cancelReloadTimeout();
700+
}
701+
if (locked) {
702+
if ("messagegui.new.js"===global.__FILE__) {resetReloadTimeout();}
703+
}
618704
});

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)