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

  • Got it. Thanks a lot again _psx_.

    But won't drawing the screen every onUpdate (every second) waste more battery?

  • search this forum and you'll see many posts about why you need to update the full screen on every call to onUpdate().  You only get about 10 1hz calls to onUpdate at a time, with the vast majority of the time it's only every minute.

    The impact on battery should be minimal.

    With the sim you have to switch in and out of low powers, but that's done on a real device based on a gesture (for about 10 seconds)

  • I have a fr245m (Forerunner 245 Music).

  • Thanks, jim_m_58. I'm learning a lot from everyone here in this post.

  • I've compiled your project.and first run and ....white screen for one minutes because I've run it just at full minute and you have infantilized _lastMinute = 0 (instead -1 :) ).

    Add this code to view:

    function onPartialUpdate(dc)
        {
            
             dc.setClip(dc.getWidth() / 2 -4 , dc.getHeight() / 2 -4, 8, 8);
             dc.setColor(0, 0xff0000);
             dc.clear();
             dc.clearClip();

        }

    It emulates system notification.

    Than

    - wait when system second is e.g. 5

    - run app in sim

    - choose menu in sim  [Settings][Low power mode]

    - after 5 second again [Settings][Low power mode]

    when will red dot disappear?

    Also run app on epix sim, choose theme with ligjht background and choose [Settings][Low power mode]

  • About _lastMinute, I eliminated this variable and that full-minute approach: I took your suggestion and my onUpdate() looks like this (I haven't uploaded it to GitHub yet):

    
    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);
    }
    

    About the red dot, from this new version of onUpdate(), when enabling "Low Power Mode", the red dot is shown instantly; and when disabling (with black background), the red dot disappears instantly too.

    BUT a black dot appears in its place. I still don't understand why this happens. Would I need to call dc.setClip(0, 0, dc.getWidth(), dc.getHeight()) right before drawing screen in the onUpdate()?

    About Epix, running the "24h simulation" (with white background) results in "24-Hour simulation finished, no screen burn-in detected." When disabling "Low Power Mode", the app seemed to work fine.

  • I've forgotten to add dc.clearClip(); in onPartialUpdate, see fixed code.

    onPartialUpdate is of course unnecessary, you can simple comment it unless you want to draw seconds or something small in low power

    epix shows error immediately due to big font, I've chosen theme lime light and simple went to low power.