I need to update my WatchFace view only when settings change. How do I do it?

Hi I'm trying to implement settings on my watch face but I don't want to load settings on every onUpdate() but just onLayout() or when they changed.

My code works in the emulator but it's not working on a device.

Here is how I implemented it:


class MyApp extends Application.AppBase {
    hidden var _view = new MyView();
    ...
    // Return the initial view of your application here
    function getInitialView() as Array? {
        return [ _view ] as Array;
    }
    // New app settings have been received so trigger a UI update
    function onSettingsChanged() as Void {
        _view.onSettingsChanged();
    }
    ...
}
    

class MyApp extends Application.AppBase {
    hidden var _view = new MyView();
    ...
    // Return the initial view of your application here
    function getInitialView() as Array? {
        return [ _view ] as Array;
    }
    // New app settings have been received so trigger a UI update
    function onSettingsChanged() as Void {
        _view.onSettingsChanged();
    }
    ...
}

Could you help me?

EDITED - January 14th, 2023:
[SOLVED]

To make the code example simpler, and focused on the investigated problem, I omitted here in this post some lines of my full code.

Unfortunately, the real problem was in one of those lines, where the _loadResources() method was called.

As _psx_ pointed out, the problem with this is the attempt to load resources (a storage operation) outside of onLayout().

Also, _psx_ also pointed out the problem with instantiating my View outside of my App class's getInitialView().

Below is my incorrect code, still simplified, but with these problems (red lines):


class MyApp extends Application.AppBase {
    hidden var _view = new MyView();
    ...
    // Return the initial view of your application here
    function getInitialView() as Array? {
        return [ _view ] as Array;
    }
    // New app settings have been received so trigger a UI update
    function onSettingsChanged() as Void {
        _view.onSettingsChanged();
    }
    ...
}

class MyView extends WatchUi.WatchFace {
    private var _lastMinute = 0;
    private var _propertiesChanged = false;
    ...
    function onLayout(dc as Dc) as Void {
        _loadResources();
        _loadSettings();
    }
    
    public function onSettingsChanged() as Void {
        _loadResources();
        _loadSettings();
        _propertiesChanged = true;
        requestUpdate();
    }
    function onUpdate(dc as Dc) as Void {
        var clockTime = System.getClockTime();
        if ((clockTime.min != _lastMinute) || _propertiesChanged) {
            _propertiesChanged = false;
            _draw(dc);
        }
        _lastMinute = clockTime.min;
    }
    ...
}

And below is the correct code:


class MyApp extends Application.AppBase {
    hidden var _view;
    ...
    // Return the initial view of your application here
    function getInitialView() as Array? {
         _view = new MyView();
        return [ _view ] as Array;
    }
    // New app settings have been received so trigger a UI update
    function onSettingsChanged() as Void {
        _view.onSettingsChanged();
    }
    ...
}

class MyView extends WatchUi.WatchFace {
    private var _lastMinute = 0;
    private var _propertiesChanged = false;
    ...
    function onLayout(dc as Dc) as Void {
         _loadResources();
        _loadSettings();
    }
    
    public function onSettingsChanged() as Void {
         // REMOVED _loadResources()
         _loadSettings();
        _propertiesChanged = true;
        requestUpdate();
    }
    function onUpdate(dc as Dc) as Void {
        var clockTime = System.getClockTime();
        if ((clockTime.min != _lastMinute) || _propertiesChanged) {
            _propertiesChanged = false;
            _draw(dc);
        }
        _lastMinute = clockTime.min;
    }
    ...
}

The full source code can be seen here:
https://github.com/eldes/presbyopia-watch-face

  • yes, and storage and other IO operation, the best in low power do nothing Slight smile

  •  Sorry, but I'm not sure I understand.

    Could you tell me why it works on the simulator but does not work on the device?

    Right after changing the value of a settings item (via ConnectIC app) the app goes into low power mode and are storage operations not allowed?

  • SIM is not 100% compatible with device, sometimes something run well on sim but not on device.

    No, when watch goes into low power (on sim you can emulate it by choosing [Settings][Low power mode]) IO operation is not allowed (but you can read properties because they are in memory).
    Maybe from some version of SDK there is already no crash but I have had some collection of errors connected with ii so simple avoid it the best read resource/storage only once (initialize, onLayout...).

    And remember about:

    developer.garmin.com/.../

  • But it doesn't crash on the real device, so probably this isn't the problem

  • So add the logs to see where it gets on the real devi

  • You are confusing low power mode with onPartialUpdate.  You can do these things in low power but not in onPartialUpdate.

    You can do things that hit the file system from onUpdate in in low power mode, but you can't do that in onPartialUpdate without exceeding the power budget.

  • As I said before, one thing the sim doesn't do is show what happens before onUpdate() is called for all devices, as on some real devices, the app is handed a cleared dc.

    That is why when ever onUpdate is called, you want to draw the full screen, and not just do bits of the screen or skip doing anything in the call, like only updating the screen every minute when onUpdate is being called every second.

  • i know that it should be ONLY during onPartialUpdate but it didn't because when this'feature' was introduced WF immediately started to crash.

     Maybe they have fixed it but I don't want to experiment and see era errors especially that e.g. APAC devices are updated with big delay.

  • It's good that others don't encounter problems. But usually to time... to time when the condition will be fulfilled e.g. filling memory and trying reading settings. I've reported bug months ago, nobody reacted...

    So maybe it's my fault maybe not :-)

  • Believe me, your code won't run well. I wanted also to do the same and it's IMPOSSIBLE due to limitation of platform. It was the first bug report -  there is no information of invalid area of screen to draw (area that was covered by system).

    System things use 'your' dc and draw on your drawing. You can see it only on device especially mips where screen is always on.

    You can meet partial (info about connection to phone) or whole screen (low battery, storm alert) notification. After  this disappear your drawing will be broken.

    make simple test, mips.

    - draw line (halwidth, 0, halfwidth, height)

    - few second after full minute switch off Bluetooth

    - look into screen after notification will disappear...

    your code also won't run well on many devices especially for amoled (but some mips also)  because SYSTEM CLEARS THE SCREEN before onUpdate (next bug I've reported that sim doesn't clear like on device).

    To test this add as first line to onUpdate dc.setColor(0,0); dc.clear(); If you don't see drawing your user won't see it also. Correct onUpdaate is:

    function onUpdate(dc as Dc) as Void {

          var clockTime = System.getClockTime();

          var now = new Time.Moment(Time.now().value() + clockTime.timeZoneOffset);

          var dateInfo = Gregorian.info(now, Time.FORMAT_SHORT);

          _drawBackground(dc);

          _drawTime(dc, clockTime);

          _drawDate(dc, dateInfo);

          _drawBattery(dc);

     }