Herold
Solutions
Blog
Preview for Customize your Garmin Watchface with Settings

Customize your Garmin Watchface with Settings

A guide on how to integrate settings to your garmin watchface

September 6, 20248 min read

In my previous article Build your own Garmin Watchface I wrote about how to get started with the watchface development on garmin devices. Now it is time to make the watchface customizable with some settings. I recommend reading the first article before continuing here, since it will cover the basics that we will need here.

Why we need Settings

The answer to that is straight forward. Not everybody wants to have the same look or data on their watch. Settings can create a unique user experience for your watchface. That also makes it great for a wide range of users to use your watchface to use your watchface, since everyone can customize it to their needs.

In theory, everything can be customized though settings. For example, you can customize data fields or colors or even layouts. Everything is possible and only your creativity is the limit.

Implement Settings

Now let's start directly with the implementation of settings. I will only cover some basic examples of how to implement settings. If you got some questions regarding more complex examples haven't hesitate to write to me and ask me directly.

Settings folder

To integrate settings we need to add a new folder resources/settings to our project. This folder will contain two files. The properties.xml file wich contains all properties of your watchface and also the default configuration that is enabled on first startup. And also the settings.xml file wich contains the layout for the user interface in the Garmin Connect IQ App.

properties.xml

For now, we will implement three simple settings for our watchface. A customizable title, an option if something should be shown and an option for what should be shown. For that we need to update our properties.xml to look like the following file. In the configuration, we add a property for every setting we need and set the corresponding data type. For example, our title is a string and our option if something should be displayed is a boolean.

<properties>
    <property id="Title" type="string">Hello</property>
    <property id="ShowBottom" type="boolean">true</property>
    <property id="DisplayOption" type="number">0</property>
</properties>

settings.xml

Now the properties wich we already setup in the previous step needs a settings configuration that reflects the user interface in the Connect IQ App. This file will also include titles for the settings and more. Let's have a look at the settings file that reflects our properties.

<settings>
    <setting propertyKey="@Properties.Title" title="@Strings.Title">
        <settingConfig type="alphaNumeric" />
    </setting>
    <setting propertyKey="@Properties.ShowBottom" title="@Strings.ShowBottom">
        <settingConfig type="boolean" />
    </setting>
    <setting propertyKey="@Properties.DisplayOption" title="@Strings.DisplayOption">
        <settingConfig type="list">
            <listEntry value="0">@Strings.None</listEntry>
            <listEntry value="1">@Strings.Blue</listEntry>
            <listEntry value="2">@Strings.Green</listEntry>
        </settingConfig>
    </setting>
</settings>

As you see for every property we add a setting with the corresponding propertyKey pointing to the property with @Properties.* and a title for the setting wich is a string from our strings.xml file. Inside of the setting tag we implement the configuration for the setting with the data type. Now we also see the type for our DisplayOption property. Here we use a list type wich will result in a easy to use select box for the user. For every option in this select box we implement a listEntry wich a value wich we will use in the code and a string to be readable to the user.

As you see, for every property we add a setting with the corresponding propertyKey pointing to the property with @Properties.* and a title for the setting, which is a string from our strings.xml file. Inside of the setting tag we implement the configuration for the setting with the data type. Now we also see the type of our DisplayOption property. Here we use a list type which will result in an easy to use select box for the user. For every option in this select box we implement a listEntry which is a value which we will use in the code and a string to be readable to the user.

Since we used @Strings.* in this file we need to add these string in the strings.xml file and our new strings file will look like this.

<strings>
    <string id="AppName">example</string>
    <string id="Current">CURRENT</string>

    <string id="Title">Custom Title</string>
    <string id="ShowBottom">Show bottom data</string>
    <string id="DisplayOption">Select color</string>
    <string id="None">None</string>
    <string id="Blue">Blue</string>
    <string id="Green">Green</string>
</strings>

Usage in code

The previous steps enabled the user to select the settings and customize them. Now we need to implement these settings in the code to react correct to the settings. Let't do that one at a time to understand the basics.

First lets habe a look at our current implementation of our onUpdate() function since that is the place where we need to implement the settings.

    // Update the view
    function onUpdate(dc as Dc) as Void {
        // Get and show the current time
        var clockTime = System.getClockTime();
        var timeString = Lang.format("$1$:$2$", [clockTime.hour, clockTime.min.format("%02d")]);
        var view = View.findDrawableById("TimeLabel") as Text;
        view.setText(timeString);

        // Get and show the current step count
        var stepCount = ActivityMonitor.getInfo().steps;
        System.println(stepCount);
        var displayedSteps = stepCount == null ? 0 : stepCount;
        var stepCountLabel = View.findDrawableById("StepCount") as Text;
        stepCountLabel.setText(displayedSteps.toString());

        // Call the parent onUpdate function to redraw the layout
        View.onUpdate(dc);
    }

Now let's change the code so that we update the title of our watchface to the settings title. To do this we need to add an id="Title" to the label in our layout.xml since we want to access it just like we do with the other labels.

    <!-- ... -->
    <label id="Title" text="@Strings.Current" x="center" y="25%" font="Graphics.FONT_XTINY" justification="Graphics.TEXT_JUSTIFY_CENTER" color="Graphics.COLOR_RED" />
    <!-- ... -->

Now we are able to access the label and replace it with our setting.

    // Update the view
    function onUpdate(dc as Dc) as Void {
        // get title label and replace with setting
        var titleLabel = View.findDrawableById("Title") as Text;
        var titleSetting = Application.Properties.getValue("Title") as Lang.String;
        titleLabel.setText(titleSetting);

        // ...
    }

You can see that we access the title setting with Application.Properties.getValue("Title"). The same way, we could also set the setting from within the code if we need it with the function Application.Properties.setValue("Title", "New Value"). Since we now know how to do it, let's also use the setting for boolean on the bottom data.

    // Update the view
    function onUpdate(dc as Dc) as Void {
        // ...

        // Get and show the current step count at the bottom
        var stepCount = ActivityMonitor.getInfo().steps;
        System.println(stepCount);
        var displayedSteps = stepCount == null ? 0 : stepCount;
        var stepCountLabel = View.findDrawableById("StepCount") as Text;
        var showBottom = Application.Properties.getValue("ShowBottom") as Lang.Boolean;
        if (showBottom) {
            stepCountLabel.setText(displayedSteps.toString());
        } else {
            stepCountLabel.setText("");
        }

        // ...
    }

In the previous code you can see that we access the ShowBottom setting. This is used to display the steps on true and display an empty string or also nothing on false. Now the last step is to implement our DisplayOption setting into the code, which contains a color selection as code.

    // Update the view
    function onUpdate(dc as Dc) as Void {
        // ...

        // Get and show the current time
        var clockTime = System.getClockTime();
        var timeString = Lang.format("$1$:$2$", [clockTime.hour, clockTime.min.format("%02d")]);
        var view = View.findDrawableById("TimeLabel") as Text;
        view.setText(timeString);
        var displayOption = Application.Properties.getValue("DisplayOption") as Lang.Number;
        switch (displayOption) {
            case 0: // none
                view.setColor(Graphics.COLOR_TRANSPARENT);
                break;
            case 1: // blue
                view.setColor(Graphics.COLOR_BLUE);
                break;
            case 2: // green
                view.setColor(Graphics.COLOR_GREEN);
                break;
        }

        // ...
    }

Here you can see that we access the DisplayOption setting and, depending on the value, we change the color of our label to the selected setting. Our changed code in the onUpdate function will now look like the following.

    // Update the view
    function onUpdate(dc as Dc) as Void {
        // get title label and replace with setting
        var titleLabel = View.findDrawableById("Title") as Text;
        var titleSetting = Application.Properties.getValue("Title") as Lang.String;
        titleLabel.setText(titleSetting);

        // Get and show the current time
        var clockTime = System.getClockTime();
        var timeString = Lang.format("$1$:$2$", [clockTime.hour, clockTime.min.format("%02d")]);
        var view = View.findDrawableById("TimeLabel") as Text;
        view.setText(timeString);
        var displayOption = Application.Properties.getValue("DisplayOption") as Lang.Number;
        switch (displayOption) {
            case 0: // none
                view.setColor(Graphics.COLOR_TRANSPARENT);
                break;
            case 1: // blue
                view.setColor(Graphics.COLOR_BLUE);
                break;
            case 2: // green
                view.setColor(Graphics.COLOR_GREEN);
                break;
        }

        // Get and show the current step count at the bottom
        var stepCount = ActivityMonitor.getInfo().steps;
        System.println(stepCount);
        var displayedSteps = stepCount == null ? 0 : stepCount;
        var stepCountLabel = View.findDrawableById("StepCount") as Text;
        var showBottom = Application.Properties.getValue("ShowBottom") as Lang.Boolean;
        if (showBottom) {
            stepCountLabel.setText(displayedSteps.toString());
        } else {
            stepCountLabel.setText("");
        }

        // Call the parent onUpdate function to redraw the layout
        View.onUpdate(dc);
    }

Test the Settings

Now it is time to test our changes in the simulator and change some settings to see if everything works like we expected. For that, we start the simulator with F5 in the debug mode and in the menu of the simulator we navigate to File > Edit Persistent Storage > Edit Application Properties data or we use the shortcut which is Ctrl + P on Windows. This will open a basic settings window where we are able to change the settings. After changing some settings, we press save and if everything works correctly, we should already see that the watchface will change its design according to our settings.

Conclusion

Now let's wrap up our learning. We need to add the settings files under the folder resources/settings. First, the properties.xml, which contains the default configuration for our settings then the settings.xml, which contains the layout configuration for our settings and all options for select box settings. After that, we can use the settings in the code by accessing them with Application.Properties.getValue("MySetting") and change the layout according to the value.

If you want to learn more about the properties and settings on Garmin watchfaces, you can read the official documentation here. You can also reach me out if you have further questions or want to know more about other topics. I am always happy to connect with like-minded developers to help each other and create amazing applications together. And just like the last time, I would be happy if you shared your great watchfaces with me if you get them ready and approved in the store.

Selfie of Felix Herold

The article was helpful, or are you feeling a bit unsure about anything?