Overriding Bangle.on() not working?

Posted on
  • There is an app (swp2clk) which allows the user to swipe back to the clock. To avoid interfering with apps that use the swipe gesture for their own purposes, you have to use the settings menu to configure which apps get the treatment. I like swiping back to the clock, but I find this configuration to be annoying (and don't like that swp2clk writes the app name to storage every time an app is loaded), so I'm trying to make a similar app that does this automatically: apps that do not register any swipe handlers will have swiping go back to the clock, and apps that do register swipe handlers will just have their swipe handlers run as usual.

    To do this, I'm trying to override Bangle.on(). The custom Bangle.on will pass all calls with an event type other than 'swipe' to the native Bangle.on (which I will store somewhere else) to avoid unnecessary complexity, but when the event type is 'swipe' the supplied function will be stored in an array. The native (non-overridden) Bangle.on will be called with an event type of 'swipe' and a function that will either call load() if there are no functions in the array, or call each function in the array (and not call load) if there's at least one. This should result in things appearing mostly unchanged, except for the illusion of a "default" swipe handler that goes back to the clock.

    I have developed the following code (autoswp2clk.boot.js) to try to achieve this:

    const AUTOSWP2CLK = {
      on: Bangle.on,                                  // Original, unmodified Bangle.on()
      removeListener: Bangle.removeListener,          // Original Bangle.removeListener()
      removeAllListeners: Bangle.removeAllListeners,  // Original Bangle.removeAllListeners()
      swipeHandlers: []                               // Array of swipe handlers
    Bangle.on = (eventName, func) => {
      if (eventName == 'swipe') {
      } else {
        AUTOSWP2CLK.on(eventName, func);
    Bangle.removeListener = (eventName, func) => {
      if (eventName == 'swipe') {
        AUTOSWP2CLK.swipeHandlers.splice(AUTOSWP­2CLK.swipeHandlers.indexOf(func), 1);
      } else {
        AUTOSWP2CLK.removeListener(eventName, func);
    Bangle.removeAllListeners = (eventName) => {
      if (eventName == 'swipe') {
        AUTOSWP2CLK.swipeHandlers = [];
      } else {
    // Using the old Bangle.on rather than the new one
    AUTOSWP2CLK.on('swipe', (dirLR, dirUD) => {
      if (AUTOSWP2CLK.swipeHandlers.length == 0) {
      } else {
        for (let func of AUTOSWP2CLK.swipeHandlers) {
          func(dirLR, dirUD);

    (I also have a removeListener and removeAllListeners for compatibility, but have not tested them yet.)

    However, what actually happens is that this app breaks basically everything, because Bangle.on() no longer registers event listeners. If I manually call AUTOSWP2CLK.on() which should be the unmodified Bangle.on(), it also does nothing, implying that this has broken it. Is what I'm trying to do possible and I'm just doing it wrong, or is this not possible?

  • We actually do this in other places (I think in bthrm) and it works fine.

    The issue in your case is that A.on and B.on are the same function. The difference is the object they are called on. So your call to AUTOSWP2CLK.on adds an event listener, but to AUTOSWP2CLK, not to Bangle.

    You could use AUTOSWP2CLK.on.call(Bangle, eventName, func) instead and it should work though

  • Thanks for pointing out bthrm. (It's not a thing I personally use, so I never thought to check it out.) It does seem to have the same goal (modifying Bangle.on), but it goes about it in a different way. bthrm uses Bangle.origOn, and my own app uses AUTOSWP2CLK.on. I think both approaches have some advantages and disadvantages. With bthrm's approach, we don't create confusion about which object is getting an event listener and we don't need to use .call to force the event listener to be assigned to something it wouldn't normally be assigned to. (We clearly do have that issue with my approach.) However, my approach is less likely to create naming conflicts with other boot code, because it seems far more likely to me that another app will want to create an object called Bangle.origOn than an object called AUTOSWP2CLK.

    I went for a combination of the two approaches: create more children of Bangle rather than a top level object, but call them something like AUTOSWP2CLKORIG rather than just orig. This works as expected.

    Is there a proper convention for names of objects created by boot code, or am I too paranoid about naming conflicts? I feel like they should have the name of the app somewhere in there to allow different pieces of boot code to distinguish variables from each other, and also have some way to distinguish boot code variables from the main app's variables. Like maybe have boot code objects be named BOOT, and have the main apps avoid creating objects with names starting with BOOT_ which I don't think they'd really have any reason to anyways?

    TLDR: Thanks, that fixed it.

  • Is there a proper convention for names of objects created by boot code, or am I too paranoid about naming conflicts?

    Well, you could just "search all files" for your variable name, and if it doesn't turn up you should be fine.

    But do you really need a global variable? If possible it's cleaner to wrap stuff in a function scope:

            const origOn = Bangle.on;
            Bangle.on = function(event, listener) {
                  // do stuff
                  origOn.call(Bangle, event, listener);
  • So that's why I sometimes see (function(){ /* stuff */ })(); being done! That's a far better idea than using global variables with naming conventions. I'm going to use function scopes instead. Obviously with nice clean names because I can get away with them now.

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

Overriding Bangle.on() not working?

Posted by Avatar for user141569 @user141569