How can a watchface display an image downloaded in the background by makeImageRequest?

The subject really says it all. I'm successfully downloading an image using makeImageRequest in a background process. I believe the image is stored in the graphics pool, and the background process has a reference to it as a Graphics.BitmapReference.

How then can I access this image from the main watchface view?  I can't used Background.exit() to return the BitmapReference since it isn't one of the allowed types for exit(). It should be obvious how to do this, but I can't see how.

Thanks in advance for any help.

Michael

  • We should see what VAW.BE is doing with Daily Flowers. The ai generated flowers are likely loaded from some server. Unless he is redeploying the app constantly with new flowers. 

  • > We should see what VAW.BE is doing with Daily Flowers.

    Good idea. I tried to find watcfaces that might be downloading images, and I couldn't find any, but Daily Flowers is clearly doing something interesting. Looking at the permissions it needs, they include:

        Read and display data from other apps.

        Run in the background when it is not active (potentially affecting battery life.

    These aren't permissions that are available to Watchfaces (WRONG: see post from FlowState below),  which leads me to believe that Daily Flowers isn't a Watchface but is an App (WRONG: see post from FlowState).   Apps have no restrictions on using makeImageRequest in the foreground.

    So I think this is more proof that running makeImageRequest from a Watchface isn't possible.

    I'm thinking of making a bug report to Garmin to see if they respond. I'm also trying to think what their reasons might be for not allowing makeImageRequest. A likely possibility is that they don't want the load on their servers from people running Watchfaces that download images every five minutes. The bandwidth required to send images out to a million watches is of order 100 Mbps. But these images have to be generated from user-supplied images that might be 1000 times larger, and need rescaling. So the load on their servers could be very significant. The solution would be to allow images to be provided directly from non-Garmin servers.

  • Daily Flowers is clearly doing something interesting. Looking at the permissions it needs, they include:

        Read and display data from other apps.

        Run in the background when it is not active (potentially affecting battery life.

    These aren't permissions that are available to Watchfaces,  which leads me to believe that Daily Flowers isn't a Watchface but is an App.   Apps have no restrictions on using makeImageRequest in the foreground.

    Daily Flower by VAW.BE is a watchface, as you can see by the app type icon on the app page in the store website (the "watch face" tooltip appears when you hover over the icon.)

    [https://apps.garmin.com/apps/e095a324-6fd7-4f0a-a819-4612c51e01bd]

    > Read and display data from other apps.

    I think this refers to the ability to read complication data that's published by other apps. In fact, only watchfaces can read complication data, and there is no other way to share data between apps. The app description also refers to the ability to read data from another app called Rain, and instructs the user to enable complications in the Rain app.

    > Run in the background when it is not active (potentially affecting battery life)

    I think this is just the ability to include a background process in the app, as watchfaces (and all other app types) can do. There is no other type of background app/permission btw, if you're thinking that the description refers to anything other than having a background process along with the normal foreground process. (For example, there is no such thing as an app that purely runs in the background, with no foreground component.)

    See: https://developer.garmin.com/connect-iq/core-topics/manifest-and-permissions/

    You can validate what I'm saying by uploading a beta watchface with the following 2 permissions: Background and ComplicationSubscriber. See if the human-readable description of the permissions on the store matches what I said.

    You can also look at the app info JSON request (that's made when the store page is loaded) in your browser's dev tools. For Daily Flower, the URL is:

    https://apps.garmin.com/api/appsLibraryExternalServices/api/asw/apps/e095a324-6fd7-4f0a-a819-4612c51e01bd?

    The permissions key/value pair looks like this:

        "permissions": [
            "Communications",
            "Positioning",
            "ComplicationSubscriber",
            "Background",
            "SensorHistory",
            "UserProfile"
        ]

  • Daily Flower by VAW.BE is a watchface, 

    Thanks for going to all that trouble! 

    I wonder if the developer would be willing to describe how they downloaded the image? I have just emailed them.

    Meanwhile, I tried my earlier suggestion of using makeWebRequest to download a JSON array of the image pixels, and this works. I created a bufferedBitmap in the foreground, and used dc.setColor and dc.drawPoint to set each pixel based on the JSON data from the background. The only issue is the maximum size of the JSON data (8k bytes?), but that can be worked around by making multiple requests. This isn't such a crazy solution if you aren't loading images frequently.

  • I did manage to get this working on a simulated venu2s (CIQ 5.0.0), by:

    - calling Application.Storage.setValue() on the received data (without modification) in the makeImageRequest callback, in the background process

    - calling getValue() in the foreground process and passing the return value (without modification) to dc.drawBitmap

    I'll try on a real fr955 (CIQ 5.0.2) at some point and get back to you.

    EDIT: yeah it also works on a real fr955.

    I'll clean up my code and post a working example later, but there isn't much more to it than what I said above.

    One thing I noticed is that while the code definitely works, it does produce a typecheck warning at level 2 (error at level 3) for the Storage.setValue() line, because as previously discussed, setValue() is not defined as taking a BitmapReference for the 2nd argument. I think it's worth filing a bug report over this.

    EDIT: I won't bother posting the code since the issue is resolved.

  • This might be a clearer example of a watch face.  It allows users to use their own photos from the internet.

    https://apps.garmin.com/apps/bcb5f977-83f2-4c2c-b6c3-fc1cdfddab74

    And you see that in permissions:

    • Send/receive information to/from the Internet
  • > I did manage to get this working on a simulated venu2s (CIQ 5.0.0)

    And I just got it going on my simulated Epix 2! FlowState, you are a genius, thank you!

    My original problem was that I was doing:

    if (data instanceof Graphics.BitmapReference) {
          Application.Storage.setValue(0, data);
    }

    and this fails at the compilation stage saying that "data" isn't an allowed type. However, if you remove the check for the instance type, then the call succeeds. And everything works.

    So, it looks like there is a bug in the compiler checking for setValue, and also the API documentation is wrong since Graphics.BitmapReference is not listed as a type that can be sent to setValue. I will inform Garmin of these issues.

    Thanks everyone for your very helpful suggestions.

  • Yeah, I will file a bug report for the issue where setValue() isn't typed to take a BitmapReference as its 2nd argument.

    Glad it's working for you now!

    Tbh it would be very hard to believe that Garmin would just let a major bug like not being able to use makeImageRequest from a background process on CIQ 4+ devices linger for years and years. So I'm not surprised that it works (but I'm also not surprised about the type check issue.)

  • > it would be very hard to believe that Garmin would just let a major bug like not being able to use makeImageRequest on CIQ 4+ devices linger for years and years

    I agree, but I couldn't find any examples anywhere of people successfully using makeImageRequest in the background with CIQ 4+, and several people having problems. Also, the API documentation being wrong makes it difficult.