You are reading a single comment by @cha0s and its replies. Click here to read the full conversation.
  • There's a random clock app, but the way it works is that you have to reload the bootloader manually to actually flush the new random face. That isn't what I want -- I want a real-time random watch face that will show me a different face every time I wake up the display.

    This has proven to be rather more difficult than I expected, but I have finally come up with something that, while hackish, is actually functional. I figured I'd share it here instead of making a PR, because I'm pretty sure it would never actually go into the code as-is. Anyway,

    The easier of the changes was modifying the settings app to allow a random clock. This was accomplished by simply adding an else case to the existing code:

    if (clockApps.length === 0) {
      clockMenu["No Clocks Found"] = () => { };
    } else {
      clockMenu[`${settings.clock === " random" ? "* " : ""}[random]`] = () => {
        if (settings.clock !== " random") {
          settings.clock = " random";
          updateSettings();
          showMainMenu();
        }
      };
    }
    

    NB: I used " random" because the clockApp is a string name of the application set, and I think adding a leading space ensures that there won't be a collision, but I would appreciate guidance if I'm wrong about that.

    The bootloader part was a little more tricky (and I'm still not 100% satisfied, though I'd say it does work about 90% of the way I'd like it to). First off I set about refactoring the code just a bit to make selecting a random clock easier.

    const clockApp = (require("Storage").readJSON("setting.js­on",1) || {}).clock;
    const evaluateCurrentClockApp = () => {
      const allClockApps = () => (
        require("Storage").list(/\.info$/)
          .map((file) => {
            const app = require("Storage").readJSON(file,1);
            if (app && app.type == "clock") {
              return app;
            }
          })
          .filter((x) => x)
      );
      const clockNotFound = `
        E.showMessage("No Clock Found");
        setWatch(() => {
          Bangle.showLauncher();
        }, BTN2, {repeat:false,edge:"falling"});)
      `;
      const currentClockApp = () => {
        if (" random" === clockApp) {
          const apps = allClockApps();
          return apps.length > 0
            ? require("Storage").read(apps[Math.floor(­Math.random() * apps.length)].src)
            : clockNotFound;
        } else if (clockApp) {
          return require("Storage").read(clockApp);
        }
        // Fallback: first clock app we discover on the system
        const [discoveredApp] = allClockApps().sort((a, b) => a.sortorder - b.sortorder);
        return discoveredApp
          ? require("Storage").read(discoveredApp.sr­c)
          // Fallback: not found
          : clockNotFound;
      };
      delete evaluateCurrentClockApp;
      eval(currentClockApp());
    };
    

    Instead of running

          eval(clockApp);
          delete clockApp;
    

    to enter the clock app as before, we just: evaluateCurrentClockApp();.

    I was also made aware of the fact that all code in the bootloader persists through the passing of execution to the clock -- that is why delete evaluateCurrentClockApp; is at the end of the function. I think it's pretty crazy that you can delete a function that is currently in calling scope, but it actually works and I verified by trace() in the web IDE that it does indeed clean up the memory.

    So this is fairly straightforward, but we end up at the original problem: this runs once when the bootloader is executed, but then the selected "random" clock stays as the watch face unless the bootloader is manually run again (e.g. long press BTN3).

    I tried a couple solutions to this problem, but the one that seems the most promising is this: set an LCD power off listener in the bootloader. When the LCD is powered off and the current clock app is set as random, reload the bootloader:

    Bangle.on("lcdPower", function onLcdPower(on) {
      if (!on && " random" === clockApp) {
        Bangle.removeListener("lcdPower", onLcdPower);
        load();
      }
    });
    

    This actually works fairly well for what it does... the only problem is that reloading the bootloader turns the LCD back on, so you can probably guess what happens next: a never-ending loop of the LCD going off, coming back on with a random clock face, going off, coming back on, etc. Still, I was kinda happy when it happened :)

    After this happened, I tried this:

    Bangle.setLCDPower(0);
    Bangle.on("lcdPower", function onLcdPower(on) {
      if (!on && " random" === clockApp) {
        Bangle.removeListener("lcdPower", onLcdPower);
        load();
      }
    });
    

    This actually solves the never-ending wake loop, but it introduces another problem: the LCD screen is off upon initial execution. It's honestly a small price to pay for a true random clock face in my opinion, but I wanted to do better. This is also when I really noticed how after the LCD turns off there is a flicker due to reloading the bootloader, before my LCD power off occurs. Annoying, but acceptable (for me).

About

Avatar for cha0s @cha0s started