Positioning in background processes

Former Member
Former Member
I'm trying to acquire position in the background proces of a widget I'm creating, but having no luck. Whenever I invoke the following code in a background process, I get a "Failed invoking <symbol>" error with no stack trace.

function onTemporalEvent(){
Position.enableLocationEvents(Position.LOCATION_ONE_SHOT, method(:onPosition));
}

function onPosition(info) {
var myLocation = info.position.toDegrees();
System.println("Latitude: " + myLocation[0]); // e.g. 38.856147
System.println("Longitude: " + myLocation[1]); // e.g -94.800953
}


if I instead modify onTemporalEvent to call a member function in a module, such as this:

function onTemporalEvent(){
var myCallback = new Lang.Method(MyModule, :myMethod);
Position.enableLocationEvents(Position.LOCATION_ONE_SHOT, myCallback);
}


... the code fails, but in a new way. Rather than not finding the symbol, it's exiting with a "Permission required" error, even if I do have the app permissions correctly set in manifest.xml.

For what it's worth: I'm creating a widget that is looking up data from an API in a very broad radius the user (1000-2000m), and deliver a limited amount of data based on this. Is there any other relatively bulletproof way to get a low-precision location fix - I'd honestly be fine with something that was +-100m or used the last known fix if acquiring a GPS fix took more than a few seconds.
  • I'll start with a few reasons I'd not do it this way. One, is even if can fire up GPS in the background (I've not tried, but I kind of doubt it), the background can run at most 30 seconds, and it could take longer than that to get a fix. And with ONE_SHOT, you may only get the last known location, and there's a much easier way to do that....

    Activity.Info.currentLocation

    No need to fire up GPS, just access it. Firing up GPS all the time, if it can be done, at whatever the frequency of your temporal event is, will cause an impact top the battery, so you really don't want to do that anyway.

    But... that data in Activity.Info.currentLocation can go stale and be null....

    So, you want a fallback. A lat/lon you cached in the Object Store/Application Storage. But in the background, you can not save anything to the store, only read it. So, you want to cache the location in the main app so that it's available to the background.

    So in the widget itself, you want to get a location and cache it in the store. You could use GPS there (it wouldn't be that often), as on many watches the widget will run for two minutes or so before timing out.

    But you can also just check Activity.Info.currentLocation, and if it's not null, save it off.

    Since the background will be running when the widget isn't, in the background, you can check Activity.Info.currentLocation in case there's a newer location, and if that is null, get the last known location from the store.

    You'll still have the possibility the location will be unknown.

    So that might be a case where in the main app, if Activity.Info.currentLocation is null, and if nothing is in the store, then fire up GPS. But this is something that really should only happen when the widget is first installed. And you can make the widget a bit smarter to not even register the Temporal until there is a known location, but just display a message telling to user to start an activity that uses GPS and then come back to the widget.

    When accessing Activity.Info.currentLocation, your widget does require the Positioning permission. Otherwise, it will always be null.

    so:

    Main App:

    -check Activity.Info.currentLocation, and cache it if available
    -(possibly) don't register the temporal event if there's nothing cached

    Background:
    -check Activity.Info.currentLocation, and if it's null, use cached location.
  • Former Member
    Former Member over 6 years ago
    Thank you so much. While your exact means of solving this is probably not ideal for me, the pointer to Activity.Info.currentLocation in the background process, combined with some selective one shot requests onShow should be serviceable enough. I can't entirely get away from the background process, as it's fetching (live public transport) data from a GraphQL endpoint, and the payload is large enough to warrant this happening in the background.
  • If there's a large amount of data, you actually have more memory available in the main app than the background.

    Like I said, you may want to be careful with ONE_SHOT, in that on some devices/FW, you'll get QUALITY_LAST_KNOWN.

    In things were I just need a single location, what I do is start it as LOCATION_CONTINUOUS, and then in onPosition when I get a good accuracy (> QUALITY_POOR), I do a LOCATION_DISABLE to turn off GPS.
  • Former Member
    Former Member over 6 years ago
    there's a large amount of data, you actually have more memory available in the main app than the background


    Thing is, the data I receive from the server can have a "best before" date of as short as 5-10 minutes, in which case I think using a background process is more apt. Or is my thinking here entirely wrong? (I'm dealing with JSON payloads of around 3-4 kB - in the tests I've done on-device, fetching those in the background seems to have worked fine (Forerunner 935/Fenix 5).

    Like I said, you may want to be careful with ONE_SHOT, in that on some devices/FW, you'll get QUALITY_LAST_KNOWN.


    My thinking here may be completely harebrained, but I was thinking along these lines: Since I only care about approximate location, I'd cache ActivityMonitor.History for either of steps or distance, and only retrigger a GPS update if the activity history indicates that the user might have moved too much since the last fix?
  • Former Member
    Former Member over 6 years ago
    Background processes are not allowed to enable the GPS. The permission error you are receiving is a result of this restriction. Your application is allowed to request the permission because the main widget process is allowed to enable GPS. There is a table in the programmers guide that outlines which modules are available based on application type, but it appears background processes have not yet been added to this table.
  • using ActMon and Steps likely wouldn't work if you only took a few steps but rode for a couple k's.
    Using a background for the requests mean you'll likely make a bunch of unneeded request and using cell data.

    Why not just fire up GPS in the widget and make the request? You can get the actual location, and only request data as needed.

    I have a widget that needs the location, and I use my scheme to look for a fix instead of ONE_SHOT, and I'll get a one on an f5 within 10-15 seconds most of the time. (and could be faster if you allowed QUALITY_POOR)
  • Former Member
    Former Member over 6 years ago
    Why not just fire up GPS in the widget and make the request? You can get the actual location, and only request data as needed.

    I have a widget that needs the location, and I use my scheme to look for a fix instead of ONE_SHOT, and I'll get a one on an f5 within 10-15 seconds most of the time. (and could be faster if you allowed QUALITY_POOR)


    Due to user experience/responsiveness - the application is intended to be one you briefly glance at - think "Do I need to start walking or can I wait another minute?", Opening a widget to then have to wait for 10-15 seconds for the GPS fix and then another 20-30 for the web request to load, finish and update the display would pretty much leave the widget dead in the water.
  • Former Member
    Former Member over 6 years ago
    Background processes are not allowed to enable the GPS. The permission error you are receiving is a result of this restriction. Your application is allowed to request the permission because the main widget process is allowed to enable GPS. There is a table in the programmers guide that outlines which modules are available based on application type, but it appears background processes have not yet been added to this table.


    Thank you - that's pretty much what I figured after the permission error. Can I presume the symbol error is an unintentional side effect of this policy, or do I need to make special considerations for other callbacks?
  • Former Member
    Former Member over 6 years ago
    I didn't really follow that when I first read your post. I think you should be getting a permission error in both cases here, so the case that is reporting "Failed invoking <symbol>" could probably be reported as a bug from that perspective.
  • You can always do something like display cached data while you are firing up GPS, getting the current location and requesting data for it. That way the location is kept fairly current., and the user doesn't see a delay when doing a quick look The user sees info right away, but it might become more accurate as they are watching it.

    With a background, you'll also want to cache that data, as if you run the widget for the first time in a while, the data will come in onBackgroundData, but if you run the widget again before the next temporal event, there would be no data without a cache. And the temporal event could fire while the widget is running, so you'll want to catch that.