Watch face variations best pracice

I've got a bundle of requests in the forums (Germany & US) wanting me to create differently
colored clones of my scuba watchface. Of course i can easily apply different color sets to my
code.
So just a question to the mods: How should I fulfil those requests:

- create different IQ Apps witch the same face and different colorsets? - If everyone does it this way, the number of watch faces on the platform will explode...
- create different Versions e.g. Blue on April 18-19 and post availability into forums e.g. If you want to get the blue one download it between
monday and wednesday???

- Send the requesting User a prg file by email to install in GARMIN/APPS

- any possiblity for the enduser to configure colors that I have overseen?
- anything else I've overseen?

What's the best way to do?

Thanks for feedback!

Peter
  • I saw one watchface (don't recall which one) that did this:

    -One version in the app store, download it and run it once to create the object store with the defaults
    -there was a link to the object stores of the variations ("red scheme", "blue scheme", etc), and the the user was directed to download the scheme they wanted, and use that to replace the default Object Store for the watchface (the .STR)
    The developer could generate the different .STR files, allowing customization, and avoid issues of duplicate IDs in the manifest file for different .prgs versions, and not have to have a bunch of versions in the store. And when CIQ customization is available, the end user can just modify the Object store without downloading the variations!
    Thought this was an interesting idea!



    Well, I think you refer to my scuba watchface? Here's the thread: https://forums.garmin.com/showthread.php?236144-Watchface-Scuba&p=594649#post594649

    In fact, I don't get much Feedback on this, since the procedure seems to be too complicated for the user.

    Another approach I've seen to get data into the Face is to use some special body weights in the user profile.
  • The "body weight" approach seems like it could have an impact on things like calorie burn, and if you used "height" instead, it could mess up your stride length. Both seem a bit dangerous!

    Maybe in the future (even when the user is allowed to customize stuff in the object store), when installing from the app store, there is an option to pick a .str file in addition to the .prg?

    I think it would help if we knew how soon "user config" will be available. Just a ballpark - will it be next month, or next year?
  • Former Member
    Former Member over 10 years ago
    If you have some time, could you please explain the format / schema of the *.STR file?
    I had a look in your desktop app, but not very familiar with C++.
    What format is the data in the object store being stored as?


    Sorry, after my triathlon, I went on holiday.

    I thought my project had a text file explaining the format, but I guess I didn't.

    As long as you only work with strings (for the key and values), this is the format I worked out:

    Each segment starts with an 8 byte header, the first 4 identifying the block type, and the second being the length of the block

    Block Type 0xABCDABCD
    The String table. Starts with a 32bit integer with the number of strings in the table. Then you have one record per string which contains a 16bit integer with the length of the string, then the string, followed by a null terminator (char 0x00)

    Block Type 0xDA7ADA7A
    This is the key/value lookup table. It starts with an 8bit (single byte) marker, that I don't know the meaning of, followed by a 32bit integer record count.

    For each record there is the following:
    8bit integer - I assume this is the data type indicator of the Key. 0x03 is a String
    32bit integer - The record number associated with the data type, which is the value of the Key
    8bit integer - I assume this is the data type indicator of the Value. 0x03 is a String
    32bit integer - The record number associated with the data type, which is the value of the Value


    I haven't bothered looking at using different data types for both the key and values, so my program just limits to assuming everything is a string.
  • Sorry, after my triathlon, I went on holiday.

    I thought my project had a text file explaining the format, but I guess I didn't.

    As long as you only work with strings (for the key and values), this is the format I worked out:

    Each segment starts with an 8 byte header, the first 4 identifying the block type, and the second being the length of the block

    Block Type 0xABCDABCD
    The String table. Starts with a 32bit integer with the number of strings in the table. Then you have one record per string which contains a 16bit integer with the length of the string, then the string, followed by a null terminator (char 0x00)

    Block Type 0xDA7ADA7A
    This is the key/value lookup table. It starts with an 8bit (single byte) marker, that I don't know the meaning of, followed by a 32bit integer record count.

    For each record there is the following:
    8bit integer - I assume this is the data type indicator of the Key. 0x03 is a String
    32bit integer - The record number associated with the data type, which is the value of the Key
    8bit integer - I assume this is the data type indicator of the Value. 0x03 is a String
    32bit integer - The record number associated with the data type, which is the value of the Value


    I haven't bothered looking at using different data types for both the key and values, so my program just limits to assuming everything is a string.


    Thank you so much for the explanation!
    I figured half of it from trying to convert your code to c#,
    But with thisit should be a lot easier to understand!

    Thanks and well dine for figuring this out by yourself!
  • Former Member
    Former Member over 10 years ago
    Thank you so much for the explanation!
    I figured half of it from trying to convert your code to c#,
    But with thisit should be a lot easier to understand!

    Thanks and well dine for figuring this out by yourself!


    Make sure you cater for the correct endian format, as the watch's is not the same as your standard Intel desktop (hence why I have some write functions in my code)

    If you struggle with the C#, give me a shout, and I should be able to convert the code easy enough.

    It'll probably be helpful at some point to go through and sort all the other data types for the file, but for now, that code does what I need
  • Make sure you cater for the correct endian format, as the watch's is not the same as your standard Intel desktop (hence why I have some write functions in my code)

    If you struggle with the C#, give me a shout, and I should be able to convert the code easy enough.

    It'll probably be helpful at some point to go through and sort all the other data types for the file, but for now, that code does what I need


    First time I understand why you do all the shifting! 😀👍
  • While this can be done in C++ or C# (or REAL c! :) ), it can also be done as a CIQ app only meant to run in the simulator (a development tool built using Monkey c!). The simulator plays with "app.str", so a simple app could be written to build one, using key/value pairs, or to edit a few keys in an existing one (copy the real one to app.str first). You want a different format .str? Change the key/value pairs, and rerun!

    That way there is no worry about formats and "unknown" bytes, as it's all handled by CIQ.

    After the run, the app.str file just needs to be copied to the place needed with the proper name.

    Simple widget that does this. An interesting thing is when I look at the STR, if two keys have the same value ("Default" in this case), the sting only appears once, but with the two keys

    using Toybox.Graphics as Gfx;
    using Toybox.System as Sys;
    using Toybox.WatchUi as Ui;
    using Toybox.Application as App;

    enum {
    OS_SCHEME,
    OS_CLOCK,
    OS_BACKG,
    OS_FOREG,
    OS_WARN,
    OS_ICON
    }

    var data= [[OS_SCHEME,"DEFAULT"],
    [OS_CLOCK,"WHITE"],
    [OS_BACKG,"BLACK"],
    [OS_FOREG,"YELLOW"],
    [OS_ICON,"DEFAULT"],
    [OS_WARN,"RED"]];

    class OSEditView extends Ui.View {

    function initialize() {
    var app = App.getApp();

    app.clearProperties();

    for(var i=0;i <data.size();i++) {
    app.setProperty(data[0], data[1]);
    Sys.println("key "+data[0].toString()+" "+data[1]);
    app.setProperty(data[0], data[1]);
    }
    app.saveProperties();
    Sys.println("Saved OS!");
    Ui.requestUpdate();
    }

    //! Update the view
    function onUpdate(dc) {
    dc.setColor(Gfx.COLOR_BLACK,Gfx.COLOR_BLACK);
    dc.clear();

    dc.setColor(Gfx.COLOR_WHITE,Gfx.COLOR_TRANSPARENT);
    dc.drawText(dc.getWidth()/2, dc.getHeight()/2, Gfx.FONT_MEDIUM, "All Done!", Gfx.TEXT_JUSTIFY_CENTER+Gfx.TEXT_JUSTIFY_VCENTER);
    }
    }[/CODE]

    You can see how this can easily be changed to just modify one key, if that's what you want. (don't do the "clear" and just change the value for the key you want..)
  • Former Member
    Former Member over 10 years ago
    While this can be done in C++ or C# (or REAL c! :) ), it can also be done as a CIQ app only meant to run in the simulator (a development tool built using Monkey c!). The simulator plays with "app.str", so a simple app could be written to build one, using key/value pairs, or to edit a few keys in an existing one (copy the real one to app.str first). You want a different format .str? Change the key/value pairs, and rerun!

    I guess you could do that, or even have something on the watch, but what you'd have to do is use your "Configure" app on the watch to set your settings, connect it to your PC and then find your "Configure.STR" file and copy it over your "WatchFace.STR" file for it to make the changes.

    I guess my approach isn't that much different - except that I wrote a full UI which supports text entry via your keyboard rather than tapping the screen in the simulator.

    If your goal is just theming, then yeah, perhaps your approach is better, but it's pretty useless for my usage (My watch face, which currently isn't public, supports a "Message Of The Day" for each day of the year). I'll also be using it for my ToDo list app/widget (for when I eventually get around to doing it)
  • @pmprog - with the "user config" stuff coming to CIQ at some point in time, I'll guess that the format of the .STR will be changed, as there probably will be some way to indicate things like "protected", "type", etc, and it could be that somewhere in the STR is something like "CIQ version" so the code can handle old and "new" versions.

    That's just another reason I'd rather use the CIQ code to build/edit the Object Store as it will be the "latest" (at least until Garmin can document every byte in an OS), ans contain the proper values for "unknown bytes".

    I guess what I'm saying is that the format of the .STR isn't static, and by using CIQ, it will handle changes as the format of the OS changes over time.
  • Former Member
    Former Member over 10 years ago
    @pmprog - with the "user config" stuff coming to CIQ at some point in time, I'll guess that the format of the .STR will be changed, as there probably will be some way to indicate things like "protected", "type", etc, and it could be that somewhere in the STR is something like "CIQ version" so the code can handle old and "new" versions.


    Why do you believe that "user config" stuff will cause the .STR file format to change?

    Whilst it's certainly possible Garmin could change the format, I doubt that very much. The file format is pretty flexible, and can be extended to support any information quite easily without breaking existing files (I can detail how it would work if you want?), and I doubt that they'd want two "loader" functions, especially on space-constrained devices.

    Sure, it's better to use the "official" API to access the files, but unless I wanted RSI, it wasn't really an option for me...