Overlay Notifications

Posted on
Page
of 2
/ 2
Next
  • I've been wanted to change the way notifications work for a while, and finally have a proof of concept ready to work with, using the new Bangle.SetLCDOverlay from Gordon.
    Firstly, big thanks to Gordon for this new feature , it's fabulous! Exactly what I was looking for, though I didn't know it before!

    Goals:

    • Fast notifications (when you hear a notification on the phone, it should be on the watch as soon as you look at it.
    • Large notifications (as much text as possible on display at once)
    • Separate the comms to gadgetbridge, the message view, music controls, and call handling. Easier to tweak and change if they are separate.

    My new notification system replaces the Android app on the watch with a very slightly tweaked version. Instead of linking into the Messages app it emits events, which can then be picked up by other apps.

    The notifications themselves use SetLCDOverlay to show a message over the top of whatever you have on screen at the time. They're scrollable (sort of) for long messages, but have no actions, they simply show you the notification.

    It is very, very low quality at the moment, lots of missing features, but does show notifications. To try it out, remove your Android and Messages app from your Bangle, and install the two apps here: https://sir-indy.github.io/BangleApps/?q­=aa
    Be aware that I'm actively working on this and it could change or break without warning!

    Things to do:

    1. Make notifications prettier: maybe a coloured outline, centred text.
    2. Fix scrolling: currently for long messages they drop off the screen.
    3. Change notifications to a notify type app, so others can use it.
    4. Change the Comms app to log the messages somewhere.
    5. Write a new message viewing app.
    6. Add actionable call notifications.
    7. Write a music control app.

    If anyone has any thoughts they'd like to share about my way of doing this, or suggestions to make things work better, please let me know. Just be aware that I'm primarily making this for myself and my use case, so may not implement every idea.

    PS. there was supposed to be a screenshot attached, but I can't use g.dump() to get an image of the Overlay. Sorry!

  • Using SetLCDOverlay seems pretty awesome :-)

    I'm working on my own messages app (also WIP), some thoughts:

    I took the route of changing the messages library to emit events, that way the Android and iOS app both keep working.

    In general, what I think would be the thing to aim for:

    • Keep the separate android and ios apps, to handle the different events phones can send, and pass on message events with require("messages").pushMessage()
    • Convert the messages app to a library-only "app", which handles storing/retrieving messages, emits a "message" event for every new message, and knows how to do something like require("messages").openApp()
    • Separate widgets/apps which handle "message" events, so users can combine/pick their favourites

    Especially hoping we can get "fast app switching" to work, so we could have e.g. different music players/message viewing apps working smoothly together.

  • Hi rigrig,
    Yes, I tried your smessages app and liked it! I thought it was improvement over the standard messages, but still not quite exactly what I wanted. It did give me the nudge I needed to try and make my own.

    I like your ideas, and different apps handling message events sounds like a good idea. The reason I've gone for Bangle.on('notification' style events was simply so you don't need an app called messages. Would messages become a new class of app, like clock or keyboard?
    Though now I look at the different classes of app, there is a notify class. That sounds more like what I'm making, so I will convert my notification app into a proper notification library.

    I've not been keeping up with fast app switching, it sounds promising!

  • Hi! Didn't know about setLCDOverlay, it's awesome!

    Just installed your test apps, really nice concept, I really like it, thank you!

  • I guess from my point of view, quite a lot of work has gone into trying to de-duplicate code and use the messages library for Gadgetbridge and iOS, so ideally we'd keep that. Also @rigrig has some code (which I really do want to get in soon, honest!) that uses a different way of storing messages which should be a lot better.

    Sending events sounds good though... Or potentially we could do:

    if (Bangle.onNewMessage) Bangle.onNewMessage(msg);
    else load("messages.app.js");
    

    And then if something was installed that could handle messages we could defer to that. That could be a good solution that worked without causing trouble for everyone else that just had the default apps installed.

    Though now I look at the different classes of app, there is a notify class. That sounds more like what I'm making, so I will convert my notification app into a proper notification library.

    That would be great - I think right now there is only a fullscreen option for Bangle.js 2 which really isn't very good at all.

  • I like the idea of just keeping the default messages app/widget with the library and deferring to custom apps if installed.

    Maybe we could even do something like this:

    // in message library:
    Bangle.emit("message", event);
    setTimeout(()=>{if (!event.handled) load("messages.app.js");});
    
    // some music widget
    Bangle.on("message", event=> {
            if (event.handled || event.id != 'music') return;
            event.handled = true;
            /* display music info */
    });
    
    // custom messages boot.js
    Bangle.on("message", event => {
            if (event.handled) return;
            event.handled = true; // probably not even needed?
            load('custommessages.app.js');
    });
    
  • I like the idea of just keeping the default messages app/widget with the library and deferring to custom apps if installed.

    Yes, I think this makes a lot of sense. Having one extra file (for the app) is not a big deal even if it's not used, and it would still be good for managing messages.

    Maybe later we can split it out, but right now I think it makes a lot of sense to keep it.

    Maybe we could even do something like this:

    Yes - that looks great to me. That seems like a really neat pattern that we could probably use in other places too.

  • Thanks for your input everyone.

    I agree that we shouldn't duplicate code, I only made my own version rather than changed the Android app so I could go back to the standard one if I needed to (i.e. when I mucked it up!).

    The Overlay Notification is coming together well, at least well enough for a first version.

    I really like the idea of adding something that a custom app can tie into, but would fall back on the default if nothing is there.

    Let me see if I understand @rigrig's idea:

    1. A message is sent from Gadgetbridge to the Bangle
    2. The Android app receives the Gadgetbridge message
    3. The Android app emits a signal saying it has a new message.
      THEN
    4. A custom app acts on the signal, doing whatever it does.
    5. The Android app sees that something acted on the signal, and does nothing else.
      OR
    6. Nothing receives the signal
    7. The Android app sees that nothing acted on the signal, and launches the default app.

    I'll have a go at incorporating that into the Android app code and trying it!

  • That's the basic idea, except we don't want to do it in the android app, but in the messages library, so it also works with ios.

    So it becomes

    1. A message is sent from Gadgetbridge to the Bangle.
    2. The Android app receives the Gadgetbridge message.
    3. The Android app tells the messages library it has a new message.
    4. The messages library emits a signal saying it has a new message.
      THEN
    5. A custom app acts on the signal, doing whatever it does.
    6. The messages library sees that something acted on the signal, and does nothing else.
      OR
    7. Nothing receives the signal
    8. The messages app sees that nothing acted on the signal, and launches the default app.

    I've pretty much already done this here, except that is written as a replacement messages app, so it needs to be adapted into messages/lib.js changes instead. (And then the replacement app won't need its own library anymore, yay)

    And as @Gordon noted above, there is a PR waiting which also makes some drastic changes to the library. Also rewriting it into an event driven system might complicate merging "a bit" ;-)

  • I'm confused, let me try and explain more what I'm trying to do.

    My use case for notifications on the Bangle is to see if it's something I need to do immediately, or if it will keep. When I hear the bing on the phone, or feel the buzz of the bangle, I can look at the Bangle, read the whole message, ideally as much text as possible without having to touch the screen. Then, I can ignore it, wait for it to time out or manually dismiss it, or if it's something I need to do something about, I go to the phone.

    I can see that the new messages app will be great, but I'd like something much, much simpler: no storage, no responses, no widget, just very fast and clear text.

    So the idea to put a switch in the Android app (and match it in the iOS app) to emit a signal, check if it's handled in a custom app, launch the Messages app if it is not handled, sounds ideal to me. As a bonus, it creates a way for anyone to make custom apps that tie into messages, or music, or calls, but the standard Messages will still load for everyone by default.

    I'll try making the changes to make the Android/ios app emit messages. If I do it correctly, it won't make any difference to anyone else. If it turns out to be a terrible idea I won't submit it to be merged, but I'd like to try.

  • the idea to put a switch in the Android app (and match it in the iOS app)

    The point is that the Android and iOS apps both call require("messages").pushMessage(...), which launches the default messages app.
    So if you edit messages/lib.js, you only need to change that instead of two apps.

  • So the idea to put a switch in the Android app (and match it in the iOS app)

    This is really not something I want to include in the app loader, given we've spent a lot of time trying to avoid duplication.

    If you actually benchmark it, I bet you'll find that the actual time saved by duplicating the code vs just putting it in the messages app is 1/1000th of a second or so... Really not worth it.

    I can see that the new messages app will be great, but I'd like something much, much simpler: no storage, no responses, no widget, just very fast and clear text.

    Ahh, sorry - I misunderstood... So if you don't want any storage, I think the best bet is for you to come up with a complete replacement for the messages app like @rigrig did with https://git.tubul.net/rigrig/BangleApps/­src/branch/personal/apps/smessages/lib.j­s . Basically implement just require("messages").pushMessage that doesn't store the message but instead puts it on the screen right away in an overlay.

    Right now, Android and iOS apps have a "dependencies": {"messages":"app"}, which requires the specific messages app - but it's trivial to change this to "dependencies": {"messages":"type"}, and as you mentioned make "messages" a new app type so that it's happy with any app that has "type":"messages" in its JSON. It looks like I'll end up having to do this anyway for @rigrig's.

    So then all anyone has to do is install the Android app, and your new Instant Messages app, and everything should work great.

    It's worth adding that some of the delay between the phone buzz and the Bangle showing the message can also be from the phone sending the message to Bangle.js. You could do NRF.setConnectionInterval(7.5) which would lower the Bangle's battery life, but would make message reception much faster (for the first message received after a few minutes when the bluetooth connection would normally go into low power mode).

  • it's trivial to change this to "dependencies": {"messages":"type"},
    It looks like I'll end up having to do this anyway for @rigrig's.

    I tried that and reverted it, as it turned out to be a bit tricky: if you change messages to "type":"messages" it won't show up in app launchers anymore...

    And hopefully you won't have to anyway, my Plan now is:

    • PR for master messages/lib.js so it does the emit+custom app stuff
      (I thought about it some more, and think this wouldn't be that big a change after all, so wouldn't really cause merge problems)
    • modify my smessages to work with that, so It won't need a duplicate messages library :-)
    • @Sir_Indy could just add a boot.js to aanotifi to listen for message events, show them with a SetLCDOverlay and prevent the default app from launching.
  • Thank you @Gordon, I think I understand now. I will leave the Android and iOS apps alone, make a new app that replaces messages with my own code. This means I won't break anything in the Android app, or anything in the message app, which I was a bit worried about.

    Changing the "type":"messages" sounds useful, if it can be fixed to show up in the launcher.

    So @rigrig, thanks for looking into it, but don't worry about making any changes to your smessages app on my account, since I'll be replacing it anyway.

    Sorry for causing confusion!

    As for speed, in my tests so far I have a bit of boot code that listens for a message and shows a notification. It feels instant, certainly fast enough. I think it's just avoiding launching a whole app and waiting for the 'Loading...' screen to pass that takes the time. I'll avoid tweaks that would reduce battery life, that's one of the best things about the Bangle!

  • @Sir_Indy Oh, it isn't solely for your benefit ;-) It would also allow me to remove duplicate code from smessages, and just use the default messages library.
    I made a PR for message events. With those changes you could do something like this:

    // aanotifi.boot.js
    Bangle.on("message", (type, message)=> {
          if (type !== "text") return; // let default app handle e.g. "music" or "call"
          require("notify").show(message);
          require("messages").buzz(message.src);
          message.handled = true; // prevent default app from launching    
    });
    
  • sorry, english is not my mother tongue and i may have missed something.
    Currently I have seen that the emit of the "message" event is done by the "messages" library and if no app "manages" the event, the library itself calls the display of the message through its own code.
    so something like this:
    GB -> Android -> Message -> custom app

    Couldn't you implant the message event emit directly from the Android app?
    It would then be enough to modify the Message app by implementing listening to the event (Bangle.on ("message", ...).
    and have something like this:
    GB -> Android -> (Message || custom app)
    In this way the management part of the event is disconnected from the graphic part;
    it is up to the installed "message" app (custom or not) to bind to the event and manage it and not to the android app to call the program "statically"

  • Yeah, it's a bit complicated :-(

    1. We also have iOS support, the message library exists for shared code between Android and iOS.
    2. We could disconnect the library from the default app, but bundling them makes sure there is an app installed: We can just tell new users to "Install the android/ios app", both automatically pull in messages, so it works right away.

    I am trying to separate the library/app code some more, so that it will really be like just two things (library and app) that just happen to be installed together.
    So then you have:
    Phone -> android/ios -> message library -> (custom app || message app)

  • Yeah, it's a bit complicated :-(

    I agree that breaking out the message library functions would be a good idea. My problem with using the message app to pass events on to a custom app, is that the message app still shows as installed, and listed in the launcher. I think this would confuse users.

    Thank you @Rarder44, I've been thinking similar things.

    Is there anything eslse we could do to make it less complicated? Ignoring for a moment what we already have, what would be the best way to handle communications? Do we even need a message library if we change to firing events rather than importing message?

  • 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)
  • Couldn't you implant the message event emit directly from the Android app?

    You could yes, but then the messages library would have to be loaded at the start every time so it could respond to the event. pushing to the messages library avoids that, and just loads the messages lib as and when it's needed.

    Given the effort we've just spent reducing loading times, adding more time+complexity to the boot process seems like a step backwards.

    However maybe the way messages are stored could be swapped out by different apps (eg @rigrig's code for using a StorageFile instead of Storage, or even just not storing messages at all).

    Here's an idea: change the metadata.json format to (optionally) inlude app type:

    Yes, this might work - I think rather than adding app/messages to type and then requiring launchers to understand that app/messages is still an app, it might be better to just add something like "features":["messages_app"] and then have "dependencies": {"messages_app":"feature"}?

    This is probably more flexible going forward and I think there are a few existing dependencies we could probably implement better that way too?

    There is the potential to split things out into special apps that handle each bit :

    • messages library - handles storage and forwarding of messages from Android/iOS
    • message_icons library - message colors and images
    • messages app - displays messages and message list

    However I would rather do this at the point where we can merge in replacement apps/libraries that folks can actually use. Right now, as @rigrig has proved, absolutely nothing stops you from forking the app loader and providing your own messages app implementation (or even a separate app that overrides some functionality).

    Until contributors have things they feel ready to contribute to the main app loader, I think we're just solving a problem that doesn't exist.

  • Thank you Gordon for taking the time to explain that. I think I understand slightly better now. I will try and work with the current system, and if that doesn't work run it from my own app loader. I haven't had time recently to work on this but hope to get back to it soon.

    The thing I really want to avoid is going in a different direction that can't be merged in!

  • Phone -> android/ios -> message library -> (custom app || message app)

    I believe this can be a good solution.

    absolutely nothing stops you from forking the app loader and providing your own messages app implementation (or even a separate app that overrides some functionality).

    you are right; this is the solution i am currently using.
    the problem is that in order to have different graphics, a lot of code is duplicated between the apps, making it very difficult to maintain them. (I currently have a modified version of android and message, "updated" a few months ago).
    I think it can be an interesting thing to divide the logic from the graphics in order to further customize the device.

    another solution (a little less event-driver, I don't know if you like it) is to divide the graphics from the logic into 2 separate files (or something similar) in the message app; the graphics file could be a standard name (message.GUI.js) and so if everyone wants to customize the graphics, they could "just" write their own app, which overwrites that file.

  • divide the graphics from the logic into 2 separate files

    Interesting - so you mean the code itself that displays the message on the screen? So there'd be an app/file that shows the list of messages, and then that calls out to another one to actually display them?

  • a premise:
    the message is long and I helped myself with google translate for some pieces, I hope there are no blunders!

    I was thinking more of an app that manages the receiption of the message, (saves in the storage, sets the additional parameters to the msg, etc.) and another that ONLY implements the show functions
    (showMessage, showMusic, showMap) which obviously are recalled if necessary by the first.

    Actually these days I was thinking about it, especially the optimization speech and two ideas came to mind that could be used (at least partially):
    1)

    to manage everything in events, it is not necessary to move all the code during the boot phase, but only the "pushMessage" function which at this point could be cleaned of all those references to the actions to be done (load message, turn on the screen, etc.) leave only the code that "understands" the type of message and launches the appropriate event.

    The message app, (or who will intercept the event) will not necessarily have to load everything in the boot, but something similar will suffice

    Bangle.on ("message", msg => {
    eval (require ("Storage"). read ("myMessage.app.js")) (msg);
    }
    (I don't know what the performances could be; I should do some tests.)

    so in the boot area only a few references will remain and each app will manage the events independently.
    (obviously the message app will also register for events like all other apps)

    we would only have to think about a question of "priority" and handling of the event, but I think that is the least of the problems.

    the advantage of this methodology is that multiple apps could register and handle the same event (for example an app that tracks a specific notification, along with the standard messsage app)

    2)
    if instead we want to keep the performances to the maximum, then we could:
    leave the Android / IOS app as it is
    structure the message app in a similar way:

    • message.core.js (containing all the message app logic, getMessages, buzz,...)
    • message.GUI.message.js (containing only the showMessage function)
    • message.GUI.map.js (containing only the showMusic function)
    • message.GUI.music.js (containing only the showMap function)

    When loading the app from the appstore, we can merge all these files to create a single file that works exactly like the actual message app.

    we could therefore think of a way to "choose" the GUI part that you want to install (such as customizing the app from the AppStore, or a separate app that does the same procedure during installation).

    I know it's a bit of an overkill but it should allow you to logically divide the "graphics" from the "code" without practically affecting performance (the message app would work exactly as it does now)
    I doubt however that this method could be a bit "tight" in some applications

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Overlay Notifications

Posted by Avatar for Sir_Indy @Sir_Indy

Actions