Add Features

The Movement SDK also offers features which you can use to augment your in-app experience, bringing location to the forefront to offer more compelling interactions to your users.

Journeys

Journeys allows partners to build powerful, location-aware experiences. You can use Journeys to power in-store & curbside pickup, delivery tracking, location-based marketing, and more.

When a user is ready to embark on a journey (i.e. they tap on "I'm on my way"), Journeys will start monitoring for their arrival, provide live ETAs throughout the way, and automatically detect when a user has arrived at their destination.

Requirements

Required NotificationHandler

Include the following delegate methods to track journey state/eta/etc. Foursquare will return one of the following journey states:

public abstract class NotificationHandler {
…
    public void handleJourneyUpdate(@NonNull Context context, Journey journey)
…
}

Available Methods

Include the following methods to ensure the correct Journeys behaviors are tracked.

Start JourneyGet Current JourneyCancel JourneyCheckin JourneyComplete Journey

// method
MovementSdk.startJourney(destinationId: String, destinationType: JourneyDestinationType, metadata: Map<String, String> = emptyMap(), callback: ((Result<Journey, JourneyException>) -> Unit)? = null)
// usage
MovementSdk.get().startJourney(venueId, JourneyDestinationType.VENUE) { result ->
    if (result.isErr) {
        // handle error
        return@startJourney
    }
}
// method
fun getCurrentJourney(context: Context): Journey?
// usage
MovementSdk.get().getCurrentJourney(this)?.let { journey ->
    // do something with journey
}
// method
fun completeJourney(callback: ((JourneyException?) -> Unit)? = null)
//usage
MovementSdk.get().completeJourney { err ->
    if (err != null) {
        // handle error
        return@checkinJourney
    }
}
// method
fun checkinJourney(callback: ((JourneyException?) -> Unit)? = null)
//usage
MovementSdk.get().checkinJourney { err ->
    if (err != null) {
        // handle error
        return@checkinJourney
    }
}

Error Handling

Using the exception handler:

MovementSdk.with(
    MovementSdk.Builder(this)
        …
        .exceptionHandler { ex ->
            // handle error
        }
)

Get Current Location

Current Location is the most comprehensive of the in-app features, allowing you to get precise place information for any user who has given your app permission to use location.

For e.g you may want to display a nearby venue to your user, or you may want to determine whether or not to send an in-app notification or display a modal if your user is at or near a specific geofence.

Using Current Location you can:

Get Current Place

Get the same place name, category, and chain information you would receive in the background callbacks in real time in your app.

if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            Result<CurrentLocation, Exception> currentLocationResult = MovementSdk.get().getCurrentLocation();
            if (currentLocationResult.isOk()) {
                CurrentLocation currentLocation = currentLocationResult.getResult();
                Log.d("MovementSdk", "Currently at " + currentLocation.getCurrentPlace().toString() + " and inside " + currentLocation.getMatchedGeofences().size() + " geofence(s)");
            } else {
                Log.e("MovementSdk", currentLocationResult.getErr().getMessage(), currentLocationResult.getErr());
            }
        }
    }).start();
}

Get Matched Geofences

If you utilize the explicit geofence functionality, Current Location will return any Geofence Events, e.g if your user is currently inside one (or many) of your drawn geofences.

if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            Result<CurrentLocation, Exception> currentLocationResult = MovementSdk.get().getCurrentLocation();
            if (currentLocationResult.isOk()) {
                CurrentLocation currentLocation = currentLocationResult.getResult();
                Log.d(currentLocation.getMatchedGeofences().size() + " geofence(s)");
            } else {
                Log.e("MovementSdk", currentLocationResult.getErr().getMessage(), currentLocationResult.getErr());
            }
        }
    }).start();
}

Get Last Known User State

The general location context information delivered through the SDK’s User States features return city, state, zip and country level information - including lat/long coordinates - for all users, in addition to home, work, traveling, commuting for users who have enabled always on location. You can access User State in one of two ways:

 @Override
    public void handleUserStateChange(@NonNull Context context, @NonNull UserStateNotification notification) {
        // Process the new user state however you'd like:
        UserState userState = notification.getUserState()
        for (UserState.Component component : notification.getChangedComponents()) {
            Log.d("Changed Component", component.name)
        }
    }
MovementSdk.get().userState()

Receive Geofence Events

The Movement SDK allows geofencing around a configurable set of venues or points. Geofences can be set for the venues, categories, or chains of your choosing. By default, Foursquare will use its knowledge of the venue's location to return the right fence for a given place. You may also configure a radius of your choosing, but we recommend letting the SDK pick the right one to give you the optimal experience.

Support for polygon shapes and arbitrary latitude/longitude points are also available. This allows greater customization and removes the reliance upon geofencing only Foursquare venue locations.

Why Use Geofences

For use cases that rely on accuracy, regular Movement SDK place visit detection is superior to geofencing. However, there are certain instances where geofencing is preferable:

Geofencing runs independently alongside regular Movement SDK visit detection so you can still get the benefit of understanding everywhere your users go while enabling additional use cases.

Event Types

Geofences have five potential event types that are delivered directly to the client and through an optional webhook:

Event Type Description
entrance Triggered on the first GPS signal that is received inside of the geofence.
dwell Triggered after the user has "dwelled" within the geofence for a configurable length of time. Default is 1 minute.
venue confirmed Triggered when the device has dwelled inside a geofence radius and confirmed a stop at the venue within the radius. Only available in SDK versions 2.1.2 or greater
exit Triggered on the first GPS signal that is received outside of the geofence.
presence Triggered when the device is in a geofence radius during a get location request. Only available in SDK versions 2.1 or greater

Receiving Events

To receive geofence events on the client, add the event handler below:

// In your implementation of the NotificationHandler, add:
@Override
public void handleGeofenceEventNotification(Context context, GeofenceEventNotification GeofenceEventNotification) {
    List<GeofenceEvent> geofenceEvents = GeofenceEventNotification.getGeofenceEvents();
    // Code to handle geofenceEvents...
}

Geofences will need to be set to a specific Foursquare venue, chain, category or shape. For example, a partner could set up geofences for Foursquare HQ, all coffee shops, and all Chick-fil-a’s. You can also set up a geofence for an arbitrary lat/lng or custom polygon shapes. For Foursquare venues, this means that some venue may need to take place beforehand if you already have a specific list of places you want to geofence.

A geofence event will contain the following:

Field Description
eventType entrance, dwell, venueConfirmed, exit, or presence.
venue Same as regular Movement SDK venue object.
categoryIDs Array of categoryIDs used by triggered geofence.
chainIDs Array of chainIDs used by triggered geofence.
partnerVenueID String of harmonized venueId.
location Object containing location information about the geofence event.
timestamp Unix/epoch timestamp in milliseconds of when the event occurred.

Webhook Examples

Webhook Examples
Enter

{
  "eventType": "geofenceEnter",
  "timestamp": 1545078269223,
  "geofenceEvent": {
    "eventType": "entrance",
    "eventLat": 41.8893897,
    "eventLng": -87.6297896,
    "radius": 100.0,
    "geofenceId": "5c649335af2c3c526c06a898",
    "geofenceProperties": {
      "customGeofenceKey": "Custom Geofence Value"
    },
    "venueId": "4a9037fef964a5209b1620e3",
    "categoryIds": "4bf58dd8d48988d1e0931735",
    "chainIds": "",
    "partnerVenueId": "",
    "venues": [
      {
        "id": "4a9037fef964a5209b1620e3",
        "name": "Einstein Bros Bagels",
        "location": {
          "address": "400 N Dearborn St",
          "crossStreet": "",
          "city": "Chicago",
          "state": "IL",
          "postalCode": "60654",
          "country": "US",
          "lat": 41.8893897,
          "lng": -87.6297896
        },
        "categories": [
          {
            "id": "4bf58dd8d48988d179941735",
            "name": "Bagel Shop",
            "pluralName": "Bagel Shops",
            "shortName": "Bagels",
            "icon": {
              "prefix": "https://ss3.4sqi.net/img/categories_v2/food/bagels_",
              "suffix": ".png"
            }
          },
          {
            "id": "4bf58dd8d48988d1e0931735",
            "name": "Coffee Shop",
            "pluralName": "Coffee Shops",
            "shortName": "Coffee Shop",
            "icon": {
              "prefix": "https://ss3.4sqi.net/img/categories_v2/food/coffeeshop_",
              "suffix": ".png"
            }
          }
        ],
        "venueChains": [
          {
            "id": "556ce8d9aceaff43eb0588d6",
            "name": "Einstein Bros."
          }
        ]
      }
    ]
  },
  "lat": 41.88928251303856,
  "lng": -87.62870316744883,
  "user": {
    "adid": "40C23EBD-E21A-4ACC-A915-B1C80BDAF4FE",
    "userId": "CB25EFFB-C2B2-4E0F-B0E9-55C05BEFA4D4"
  },
  "installId": "CB25EFFB-C2B2-4E0F-B0E9-55C05BEFA4D4",
  "sdkType": "iossdk",
  "sdkBuild": "2.4.2"
}

Webhook Examples
Dwell

{
  "eventType": "geofenceDwell",
  "timestamp": 1545078408140,
  "geofenceEvent": {
    "eventType": "dwell",
    "eventLat": 41.8893897,
    "eventLng": -87.6297896,
    "radius": 100.0,
    "geofenceId": "5c649335af2c3c526c06a898",
    "geofenceProperties": {
      "customGeofenceKey": "Custom Geofence Value"
    },
    "venueId": "4a9037fef964a5209b1620e3",
    "categoryIds": "4bf58dd8d48988d1e0931735",
    "chainIds": "",
    "partnerVenueId": "",
    "venues": [
      {
        "id": "4a9037fef964a5209b1620e3",
        "name": "Einstein Bros Bagels",
        "location": {
          "address": "400 N Dearborn St",
          "crossStreet": "",
          "city": "Chicago",
          "state": "IL",
          "postalCode": "60654",
          "country": "US",
          "lat": 41.8893897,
          "lng": -87.6297896
        },
        "categories": [
          {
            "id": "4bf58dd8d48988d179941735",
            "name": "Bagel Shop",
            "pluralName": "Bagel Shops",
            "shortName": "Bagels",
            "icon": {
              "prefix": "https://ss3.4sqi.net/img/categories_v2/food/bagels_",
              "suffix": ".png"
            }
          },
          {
            "id": "4bf58dd8d48988d1e0931735",
            "name": "Coffee Shop",
            "pluralName": "Coffee Shops",
            "shortName": "Coffee Shop",
            "icon": {
              "prefix": "https://ss3.4sqi.net/img/categories_v2/food/coffeeshop_",
              "suffix": ".png"
            }
          }
        ],
        "venueChains": [
          {
            "id": "556ce8d9aceaff43eb0588d6",
            "name": "Einstein Bros."
          }
        ]
      }
    ]
  },
  "lat": 41.889335266663636,
  "lng": -87.62856037188838,
  "user": {
    "adid": "40C23EBD-E21A-4ACC-A915-B1C80BDAF4FE",
    "userId": "CB25EFFB-C2B2-4E0F-B0E9-55C05BEFA4D4"
  },
  "installId": "CB25EFFB-C2B2-4E0F-B0E9-55C05BEFA4D4",
  "sdkType": "iossdk",
  "sdkBuild": "2.4.2"
}

Webhook Examples
Venue Confirmed

{
  "eventType": "geofenceVenueConfirmed",
  "timestamp": 1545078428140,
  "geofenceEvent": {
    "eventType": "venueConfirmed",
    "eventLat": 41.8893897,
    "eventLng": -87.6297896,
    "radius": 100.0,
    "venueId": "4a9037fef964a5209b1620e3",
    "categoryIds": "4bf58dd8d48988d1e0931735",
    "chainIds": "",
    "venues": [
      {
        "id": "4a9037fef964a5209b1620e3",
        "name": "Einstein Bros Bagels",
        "location": {
          "address": "400 N Dearborn St",
          "crossStreet": "",
          "city": "Chicago",
          "state": "IL",
          "postalCode": "60654",
          "country": "US",
          "lat": 41.8893897,
          "lng": -87.6297896
        },
        "categories": [
          {
            "id": "4bf58dd8d48988d179941735",
            "name": "Bagel Shop",
            "pluralName": "Bagel Shops",
            "shortName": "Bagels",
            "icon": {
              "prefix": "https://ss3.4sqi.net/img/categories_v2/food/bagels_",
              "suffix": ".png"
            }
          },
          {
            "id": "4bf58dd8d48988d1e0931735",
            "name": "Coffee Shop",
            "pluralName": "Coffee Shops",
            "shortName": "Coffee Shop",
            "icon": {
              "prefix": "https://ss3.4sqi.net/img/categories_v2/food/coffeeshop_",
              "suffix": ".png"
            }
          }
        ],
        "venueChains": [
          {
            "id": "556ce8d9aceaff43eb0588d6",
            "name": "Einstein Bros."
          }
        ]
      }
    ]
  },
  "lat": 41.889335266663636,
  "lng": -87.62856037188838,
  "user": {
    "adid": "40C23EBD-E21A-4ACC-A915-B1C80BDAF4FE",
    "userId": "CB25EFFB-C2B2-4E0F-B0E9-55C05BEFA4D4"
  },
  "installId": "CB25EFFB-C2B2-4E0F-B0E9-55C05BEFA4D4",
  "sdkType": "iossdk"
}

Webhook Examples
Exit

{
  "eventType": "geofenceExit",
  "timestamp": 1545091987992,
  "geofenceEvent": {
    "eventType": "exit",
    "eventLat": 41.8893897,
    "eventLng": -87.6297896,
    "radius": 100.0,
    "geofenceId": "5c649335af2c3c526c06a898",
    "geofenceProperties": {
      "customGeofenceKey": "Custom Geofence Value"
    },
    "venueId": "4a9037fef964a5209b1620e3",
    "categoryIds": "4bf58dd8d48988d1e0931735",
    "chainIds": "",
    "partnerVenueId": "",
    "venues": [
      {
        "id": "4a9037fef964a5209b1620e3",
        "name": "Einstein Bros Bagels",
        "location": {
          "address": "400 N Dearborn St",
          "crossStreet": "",
          "city": "Chicago",
          "state": "IL",
          "postalCode": "60654",
          "country": "US",
          "lat": 41.8893897,
          "lng": -87.6297896
        },
        "categories": [
          {
            "id": "4bf58dd8d48988d179941735",
            "name": "Bagel Shop",
            "pluralName": "Bagel Shops",
            "shortName": "Bagels",
            "icon": {
              "prefix": "https://ss3.4sqi.net/img/categories_v2/food/bagels_",
              "suffix": ".png"
            }
          },
          {
            "id": "4bf58dd8d48988d1e0931735",
            "name": "Coffee Shop",
            "pluralName": "Coffee Shops",
            "shortName": "Coffee Shop",
            "icon": {
              "prefix": "https://ss3.4sqi.net/img/categories_v2/food/coffeeshop_",
              "suffix": ".png"
            }
          }
        ],
        "venueChains": [
          {
            "id": "556ce8d9aceaff43eb0588d6",
            "name": "Einstein Bros."
          }
        ]
      }
    ]
  },
  "lat": 41.88709567122635,
  "lng": -87.63105850113463,
  "user": {
    "adid": "40C23EBD-E21A-4ACC-A915-B1C80BDAF4FE",
    "userId": "CB25EFFB-C2B2-4E0F-B0E9-55C05BEFA4D4"
  },
  "installId": "CB25EFFB-C2B2-4E0F-B0E9-55C05BEFA4D4",
  "sdkType": "iossdk",
  "sdkBuild": "2.4.2"
}

Webhook Examples
Presence

{
  "eventType": "geofencePresence",
  "timestamp": 1549553712000,
  "geofenceEvent": {
    "eventType": "presence",
    "eventLat": 41.8893897,
    "eventLng": -87.6297896,
    "radius": 100.0,
    "geofenceId": "5c649335af2c3c526c06a898",
    "geofenceProperties": {
      "customGeofenceKey": "Custom Geofence Value"
    },
    "venueId": "4a9037fef964a5209b1620e3",
    "categoryIds": "4bf58dd8d48988d1e0931735",
    "chainIds": "",
    "partnerVenueId": "",
    "venues": [
      {
        "id": "4a9037fef964a5209b1620e3",
        "name": "Einstein Bros Bagels",
        "location": {
          "address": "400 N Dearborn St",
          "crossStreet": "",
          "city": "Chicago",
          "state": "IL",
          "postalCode": "60654",
          "country": "US",
          "lat": 41.8893897,
          "lng": -87.6297896
        },
        "categories": [
          {
            "id": "4bf58dd8d48988d179941735",
            "name": "Bagel Shop",
            "pluralName": "Bagel Shops",
            "shortName": "Bagels",
            "icon": {
              "prefix": "https://ss3.4sqi.net/img/categories_v2/food/bagels_",
              "suffix": ".png"
            },
            "primary": true
          },
          {
            "id": "4bf58dd8d48988d1e0931735",
            "name": "Coffee Shop",
            "pluralName": "Coffee Shops",
            "shortName": "Coffee Shop",
            "icon": {
              "prefix": "https://ss3.4sqi.net/img/categories_v2/food/coffeeshop_",
              "suffix": ".png"
            }
          }
        ],
        "venueChains": [
          {
            "id": "556ce8d9aceaff43eb0588d6",
            "name": "Einstein Bros."
          }
        ]
      }
    ]
  },
  "lat": 41.889356,
  "lng": -87.628725,
  "user": {
    "adid": "40C23EBD-E21A-4ACC-A915-B1C80BDAF4FE",
    "userId": "CB25EFFB-C2B2-4E0F-B0E9-55C05BEFA4D4"
  },
  "installId": "CB25EFFB-C2B2-4E0F-B0E9-55C05BEFA4D4",
  "sdkType": "iossdk",
  "sdkBuild": "2.4.2"
}

Geofence Management

Geofences can be managed through the Geofence builder in your Developer Console or via our Geofence API.

Set Custom User Data

The Movement SDK enables developers to set custom user unique identifiers in addition to or as a replacement. This is common if you want to tie an SDK event to a specific user in your own database or in a third-party integration.

Where to set custom user data

Generally, we recommend that you set custom user info or user unique identifiers, as soon as you have a way to identify your user i.e upon logging in or once you have their session in the app.

While you do not need to worry about modifying the Movement SDK when a user revokes location permissions, if a user logs out of your app, you should call MovementSdk.clearAllData() to reset any unique identifiers that you may have configured.

Examples

UserInfo userInfo = new UserInfo();
userInfo.setUserId(myCustomUserId);
userInfo.put("+123456789", "phoneNumber");
userInfo.put("jdoe@example.com", "emailAddress");
MovementSdk.get().setUserInfo(userInfo, persisted: true);
MovementSdk.get().setUserInfo(null, persisted: true);

Testing

There are two ways to test and confirm that you're successfully setting custom user data, via:

Access User States

User states allow you to more accurately interact, or not interact, with your users based on their state. For example, you might not want to send a notification to any users that are at home but you may want to remind them of a great promotion while they are on their way to work or on vacation.

Home and Work

As users of your app begin to establish regular behavior (usually after 3-7 weekdays), the Movement SDK will attempt to determine their home and work locations. This is a foundational feature of the SDK that is often used in the calculation of other user states.

To check if a user has configured their home/work locations, you can see if this has been set by checking the hasHomeOrWork property:

FrequentLocations.hasHomeOrWork(context)

Additional States

Non Home and Work user states are accessible via the lastKnownUserState property on the MovementSDKManager instance. This object will have a coordinate, timestamp and a state property.

Travel

We calculate the travel state by observing users home/work visit patterns. States trigger when users deviate from those patterns in a way that signifies traveling.

Commute

The SDK defines commuting as when a user is moving some distance between one's home and place of work on a regular basis.

Provide Visit Feedback

One of the best ways to help improve the accuracy of the Movement SDK and where we think devices are located is by providing feedback about a user's visit. The more feedback we get, the smarter, faster and more accurate visits from the SDK become.

Confirm and Deny a Visit

public static void leaveVisitFeedback(@NonNull String VisitId, @NonNull VisitFeedback feedback, @Nullable String actualVenueId)
Feedback Description
VisitFeedback.CONFIRM The visit was at the correct venue.
VisitFeedback.FALSE_STOP The user did not stop anywhere.
VisitFeedback.WRONG_VENUE The wrong venue was detected.
VisitFeedback.DENY Generic feedback that something was incorrect.
MovementSdk.leaveVisitFeedback(visitId, VisitFeedback.WRONG_VENUE, "4ed92126f5b92139871ce962");

Check-in at a FSQ Venue

checkInWithVenueId(@NonNull final String venueId)
final String foursquareHQVenueId = "4ef0e7cf7beb5932d5bdeb4e";
MovementSdk.checkInWithVenueId(foursquareHQVenueId);