Unhandled exception drawText on Edge 540/840 with firmware 20.x

I have a bunch of data fields running on most if not all Edge devices since a few years now.

A week ago or so I started to get many user reports of my data fields crashing on Edge 540/840 since firmware 20.x started to roll out. I have a 540 myself and I was able to confirm that after upgrading the firmware to version 20.19 I started to have the same problem.
The same data fields code remain unchanged since July 2023, and they worked fine up to firmware 19.22 on the same devices.

In ERA I don't see any other affected devices than 540/840 with 20.x firmware.

Running the same code in the simulator works just fine, so I suspect this is a firmware specific bug rather than an application code issue.
According to ERA crash reports the line of code that is causing the unhandled exception is the following:


dc.drawText(123, 12, Gfx.FONT_MEDIUM, Ui.loadResource(Rez.Strings.label), textCenter);

For the sake of completeness I'm adding a larger code snip:

using Toybox.Graphics as Gfx;
using Toybox.WatchUi as Ui;
hidden var textCenter = Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER;

dc.drawText(123, 12, Gfx.FONT_MEDIUM, Ui.loadResource(Rez.Strings.label), textCenter);

The ERA/app crash logs generated are very limited in actionable data, only providing the error type (unhandled exception in this case) and the line(s) of code involved, but no other details on what exactly is causing the exception.

I checked the firmware release notes and I couldn't find any documented change that can be causing this.

Any ideas?

  • What's the point of using format? You could just use monkeyVersion[0] IMHO.

    Also you wrote that this is how you differentiate between x360 and x40 devices. What do you do differently on them? There are probably better ways to differentiate depending on what you do differently.

  • E.g.: I need to use different fonts sizes because they changed them in the new Edge devices. This will also affect the layout of the data field.

  • Hmmmm I wouldn't bother figuring this out from monkey c. Just use constants. Create 2 directories (or more as you need):

    source-common/Constants.mc: const FONT = Graphics.FONT_MEDIUM;
    source-edge-old/Constants.mc: const FONT = Graphics.FONT_LARGE;

    in your monkey.jungle:

    base.sourcePath = source;source-common

    and for each of the "OLD" devices that need different font:
    edge530.sourcePath = source;source-edge-old
    This way you only need to edit the monkey.jungle once, because each new device you might add in the future will automatically get the default from source-common
  • Another simple way to override font per device model is creating a resource directory for this device, let's say "resources-edge1040", and override strings.xml there:

    <strings>
        <!-- FONT_MEDIUM -->
        <string id="topBarFontIndex">3</string>
    </strings>

    Then just access this font in Monkey C code as

    var topBarFont = Application.loadResource(Rez.Strings.topBarFontIndex).toNumber() as FontType;

    The pros are that this method requires no additional sourcePath configurations in jungle files

  • cons:

    - compiler warning because it's not translated (though you can add translatable="false")

    - more code

    - slower to load

  • Agree about the code loading speed (isn't a big difference though if done only once in the onLayout() method), but don't fully agree about "more code". For datafields with a lot of displayable data we may already have a separate resource folders for "outlier" devices where we keep their specific "layouts.xml" files. Contrary, the approach with separate sourcePath per device would require creation of more classes. So, it depends on the situation

  • No need for more classes. Just create a global constant. When it's a scalar value the newer compilers will just use the value and the code won't grow, but instead it will be less: 

    1. No loadResource code

    2. No additional item in Rez.Strings (you can see in memory monitor)