-
setFontMono(bool)
That seems like a good idea (maybe also add
setFontMono(int width)
?)g.setFontSize(...)
That looks good. (and font packs could override
Graphics.prototype.setFontSize
)try and find a font that would display the given text in the area. It might be good for messages...
On the one hand: that seems overly complicated. I think messages should just use a fixed size and handle overflow: that will happen anyway, and showing short messages in a larger font doesn't seem worth the hassle. (with variable character size, I think we'd need to "try all sizes until one fits", not sure that is a good idea¹)
On the other hand: if authors really want to use it, we don't want apps to "roll their own" if Bangle could do it for them.¹ even though I wrote that code myself, regretting it now 😉
-
A new font format seems interesting, especially if you could add e.g. emojis.
I don't get how it would be smaller than indexed bitmaps though?
Edit: oh, variable width and just a \n after the last black pixel per line would help.I guess sometimes you still want to pick an exact font, for e.g. clock layouts? Maybe add
Bangle.selectFont(size)
, and it gives you the best font to use for that size? Custom "font packs" could then override that function.
Seems a bit clunky thoughg.setFont(Bangle.selectFont('30px'));
:-( -
For me it's always kind of hard to tell from pictures instead of the real watch, but I think it looks good in the menu bar.
To me it looks just too small/large in the scroller, but we could maybe resize scroll items?say: "I want a bitmap font 20px high" and let Espruino choose?
This would be nice to have. (And people could customize fonts globally without messing up layouts.)
-
-
I don't have Signal installed.
Could you maybe install messagesdebug, and post the log after you get some broken messages? -
-
Do you want to add it to the main app loader now it can go in without conflicting?
I've still got some items on my TODO list before I'd feel happy adding it to the public app store.
Shouldn't take too long though: now the library stuff is sorted out, I'm planning to mostly just remove/disable Stuff, and it can be added/fixed/improved later.have some code in the app loader to automatically uninstall 'conflicting' apps first?
I hadn't thought of that, but maybe we should indeed have that first:
For the initial installation just saying "UninstallMessage UI
, then install this app" in the README seems fine, but (un)installing differentmessagegui
apps can lead to Interesting problems later on if anybody gets the order wrong. -
Updated to reflect recent changes (message library, fast loading)
I also renamed it to "Message List"(messagelist
), it now lives here, as alternative tomessagegui
: installation procedure is now simply- Install default message library/apps
- Uninstall "Message UI"(
messagegui
) - Install "Message List"(
messagelist
)
(After the initial installation you can skip step 1+2 when "updating".)
- Install default message library/apps
-
-
or
setUI({back})
on Bangle.js 1 ends up being a bit uselessI don't think it's quite that bad: touching the left-hand side of the screen works fine, and most apps do use the buttons anyway. But for those that don't, it would be nice if this worked.
BTN2 seemed natural, but now you mention BTN3, that would make sense
short-press: go back
long-press: go all the way back -
Right, I can see why we want to avoid changing Bangle.setUI.
But as far as I can tell on Bangle.js 1:
Bangle.setUI({mode:"clock",back:function() { Bangle.showClock(); }});
opens the launcher (instead of the clock) when you press the middle button.
And
Bangle.setUI({mode:"custom",back:function() { Bangle.showClock(); }});
does nothing for button presses.
So we end up needing something like this:
Bangle.setUI({mode:"custom", back: function() { // Bangle.js 2 also calls this for button presses Bangle.showClock(); }, btn: global.BTN2 ? function(b) { if (b==2) Bangle.showClock(); // only needed for Bangle.js 1 } : undefined });
Bangle.js 2
setUI
checks if there are any button listeners, and assigns theback
function otherwise, Bangle.js 1 doesn't :-(Maybe we could add similar code to the Bangle.js 1
setUI
:Bangle.on("touch", touchHandler); if (Bangle.btnWatches===undefined) // only add back button handler if there are no existing watches btnWatch = setWatch(function() { btnWatch = undefined; options.back(); }, BTN2, {edge:"falling"}); ... // and in widget.remove: if (btnWatch) clearWatch(btnWatch);
But I suppose that would lead to the same problem of it not working in older firmware... (On the other hand: it doesn't work now either, so apps that already assign their own BTN2 listeners should be fine?)
-
I'm not really sure I understand the reasoning behind making the weather app behave differently to every other app (going to launcher instead of the clock)
Probably because I just copied it from some clock code without thinking too much about it ;-)
And after that we just kept preserving existing behaviour, which is how we ended up withBangle.setUI("clock");delete Bangle.CLOCK;
my preference would be to make 'back' go back to the clock.
Yes, I agree.
Maybe we should even add aBangle.setUI("app")
mode, where the middle button goes back to the clock? -
Right now a bunch of clocks either never show widgets, or have a setting to toggle them.
As far as I can tell, this is all about display preference, so how would people feel about making it a global setting instead?My proposal would be to aim for:
- A global "Clock Widgets" setting:
- Always Visible
- When Unlocked (Bangle.js 2 only)
- On Swipe Down (Bangle.js 2 only)
- On Tap
- Always Hidden
- Always Visible
- Remove all per-clock "hide/load widgets" settings
I'm asking because I figured it wouldn't be too hard to create some boot-app that uses widget_utils to (kind of) do 1. But without cleaning out the apps it would increase the mess of settings even more, and I expect it will need some app support, which feels a bit out of place if it's just for some optional app.
- A global "Clock Widgets" setting:
-
I guess you could try the relay:
On your phone: connect from https://www.espruino.com/ide/relay
On your PC: enter the key under Settings>Communications>Relay Key -
it would be nice to split out the different GUI parts - so if you tapped on a message in the list in the Messages app, it could be handled by a different app.
On the one hand, it seems a nice idea.
On the other hand: I'm working on a GUI that doesn't have a separate list: you just start with the top message, and scroll down to the next message. And I'm not sure how that would work with custom displays... (but maybe I'll think of something once we get this sorted out, and I actually finish that GUI)Another thing we might look at is response actions, I'd like to have an easy way to just add custom response actions for e.g. Home-Assistant notifications, without having to code up a new screen. (But that's on the list below figuring out responses, and whether we can get GadgetBridge to pass action buttons)
-
I made a draft PR.
How it works now:
how to handle each of:
save message, go to app
Bangle.on("message", (type,message) => { if (message.handled) return; // already handled require("messages").save(message); if (wantToLoadAppfor(message)) require("messages").openGUI(message); });
do something, save message, don't go to app
Bangle.on("message", (type,message) => { if (message.handled) return; // already handled doSomethingWith(message); require("messages").save(message); message.handled = true; });
do something, don't save message, don't go to app
Bangle.on("message", (type,message) => { if (message.handled) return; // already handled doSomethingWith(message); message.handled = true; });
Note that the
if (message.handled) return; // already handled
is only meant to be used if the code does something like e.g. displaying the message, widgets just always show an icon for new messages.Some things I ran into:
- Depending on GUI apps/widgets as "module" seems a bit of a mismatch (right now the sanitychecker complains it expects e.g. a
messagewidget
file to exist if an app provides amessagewidget
module. We could work around that I guess, but it seems Wrong...) - What should we do about the settings? I guess split the menus out into e.g. "Message GUI", "Message Widget", "Message Behaviour" settings, but maybe we should still save them all in
messages.settings.json
? Even for custom apps, and just accept that it might pollute the file a bit?
The messages app (and notify icon) was an easy way to get to music though...
Yeah, probably best to leave it in, and custom music apps can catch the event before it leaves the default GUI.
- Depending on GUI apps/widgets as "module" seems a bit of a mismatch (right now the sanitychecker complains it expects e.g. a
-
although nobody is yet to make a PR to actually just improve the standard message app :(
Well, I'm working on my own messages app, but it makes big changes to how messages are displayed, so I'd be reluctant to make a PR to "completely replace" the messages app.
So maybe we want msg.handled and msg.noMessageApp?
My idea was that
msg.handled
= don't take any visible action (like loading apps)Or do we want to not store the message until there's a user interaction with the overlay? That could be tricky, and if something happens (maybe an alarm goes off and the alarm app is loaded?) the message could get lost.
We can save it in
on("kill")
split out the GUI for the existing music app in the messages app, that could maybe fix this more nicely, and would reduce duplication on the watch.
A problem with
music
specifically is that you expect a lot of those events to happen, we don't really want to write any of them to flash anyway, but need to for the app to pick them up.
An idea: maybe simply not handle them in the messages app at all, and we can have a separate overlay app to show music?the messages app probably wants to handle event.t=="remove" (...) but I guess it could do that and then check 'event.handled'?
Yeah, that was the idea. Same goes for widgets.
move all the logic ( saving messages, managing music... ) to an app/library/... and move the GUI to a separate app
I find it "wrong" that the pushMessage automatically saves the messages that arrive
I agree, and I think right now things are confusing because a) we bundle the library with the default GUI, and b) the library+GUI are too tightly coupled.
What I'd like to do:
- completely split out the default GUI from the
messages
library, and make it event-driven. - keep
pushMessage
in the library: it would be much simplified but just to deduplicateandroid
/ios
code (and combinemusicinfo
+musicstate
events)
I think that since this commit, we can split the library into a "module".
android
/ios
could still depend onmessages
(the default GUI), which would depend on the library, so fresh installations still work out of the box, and custom apps would set events tohandled
, without needing to uninstall the default.Proposed
require("messages")
API:pushMessage(event): emit appropriate "message" event, *does not save it* save(message): save message to flash (if not already saved), sets `message.saved=true` status(): checks saved messages, returns "new"/"old"/"none" getMessages(): returns array of saved messages accept(message): send positive message response dismiss(message): send negative message response, erase message from flash, only if it existed: emit "message" event with `{t:"remove"}` launch(message): launches the default GUI by emitting a `type="launch"` event write(messages): replace all stored messages with given array (for use by GUI on exit) // not 100% sure about these, but otherwise it would be duplicated in most message apps... buzz(message): start buzzing for new message stopBuzz(): stop buzzing
The overlay notification app can then
- listen to
on("message", (type,msg) => if (type==="text") // set msg.handled=true, display msg, etc.
- On tap: launch the full app by doing
require("messages").launch(msg)
- On swipe:
require("messages").dismiss(msg)
- Do
on("kill", ()=>require("messages").save(msg)
to prevent them getting
And we could think about adding music/call overlays either to the default GUI, or as separate apps.
- completely split out the default GUI from the
-
it would be convenient to be able to change the app that is called by the Android/IOS app
smessages
has this in a boot file:global.load = (_load => file => { if (file==="messages.app.js") file = "smessages.app.js"; _load(file); })(load);
It feels rather hacky, but it works, and is cleaned up automatically when you uninstall the app.
-
-
metadata.json
is documented in the BangleApps README -
Maybe it could be another option in the App Loader for now so that it can be turned on for those that are interested?
That sounds like a good idea.
But I was also asking because I really don't know enough about Espruino to estimate whether it is likely to have any real impact on top of the pretokenization. But if you think it might I'd be happy to spend some time investigating it. -
Not sure if it's worth it, but the app loader tried minifying all code a while ago. Back then it broke apps, because Espruino didn't support
let
/const
scopes, so variable mangling ended up with duplicates.
But now that scopes are properly supported, maybe it could be turned back on?
I know the code is already pretokenized, but at least variable mangling might speed things up a bit? (and maybe the minifier knows some other tricks) -
the message app still shows as installed, and listed in the launcher
Good point.
Do we even need a message library if we change to firing events rather than importing
message
?The library also handles storing messages and buzzing/icons, we don't want to duplicate all that code for every custom app. (And moving it to a module would mean it getting included into boot code)
Here's an idea: change the
metadata.json
format to (optionally) inlude app type:- split
messages
into the library:"id":"messages", "type": "messages"
, and the default app:"id":"msgapp", "type": "app/messages"
. - The
android
/ios
apps then depend on"dependencies": {"messages":"type", "app/messages":"type"}
. (Not sure if it already works like this)
- split
Yes, if you put that code in a boot file it should work, except you also want to check that
type === 'text'
: only show notifications/smsmsg.t !== 'remove'
: only show new/modified messagesThe messages library README has some example code using
E.showMessage()
.