# Geolocation

The Geolocation module requests location permission, then captures the precise physical location of the user's device, using its GPS sensor to record coordinates and location fields such as country, state, and city. You can configure whether the user is allowed to skip the step when a location fix is not available.

For an overview of this module and how it works, see [Geolocation](https://developer.incode.com/docs/geolocation-2).

How you use this module depends on your [integration pattern](https://developer.incode.com/docs/android-common-implementation-patterns#integration-patterns). In Patterns 1 and 2, you add the module to a `FlowConfig` in code as shown on this page. In Pattern 3, you define your modules and configuration in Dashboard as a Flow or Workflow and reference it by ID with `startFlow()` or `startWorkflow()` as shown on [Run Flows Configured in Dashboard](https://developer.incode.com/docs/android-run-flows-configured-online).

## Add Geolocation

Add the module with `addGeolocation()` (default config) or `addGeolocation(geolocation)` (custom config). Geolocation requests location permission at runtime. If the user denies permission, the outcome depends on whether you set the module as skippable.&#x20;

```kotlin
flowConfigBuilder.addGeolocation()
// or with a configured instance:
flowConfigBuilder.addGeolocation(geolocation)
```
```java
flowConfigBuilder.addGeolocation();
// or with a configured instance:
flowConfigBuilder.addGeolocation(geolocation);
```

Geolocation ships both a v1 (legacy View) and a v2 (Jetpack Compose) capture screen; the active one depends on your Incode configuration. Contact your Incode representative to enable v2.

### Example

The example below builds a `Geolocation` module that is not skippable, adds it to the flow, and listens for both the fetched result and the unavailable case.

```kotlin
val geolocation = Geolocation.Builder()
    .setSkippable(false)
    .build()

val flowConfig = FlowConfig.Builder()
    .addGeolocation(geolocation)
    .build()

val onboardingListener = object : OnboardingListener() {
    override fun onGeolocationFetched(geolocationResult: GeolocationResult) {
        // Location captured. Process the result.
    }

    override fun onGeolocationUnavailable(error: Throwable) {
        // No location fix was obtained.
    }
}

IncodeWelcome.getInstance()
    .startOnboarding(activityContext, sessionConfig, flowConfig, onboardingListener)
```
```java
Geolocation geolocation = new Geolocation.Builder()
    .setSkippable(false)
    .build();

FlowConfig flowConfig = new FlowConfig.Builder()
    .addGeolocation(geolocation)
    .build();

IncodeWelcome.OnboardingListener onboardingListener = new IncodeWelcome.OnboardingListener() {
    @Override
    public void onGeolocationFetched(@NonNull GeolocationResult geolocationResult) {
        // Location captured. Process the result.
    }

    @Override
    public void onGeolocationUnavailable(@NonNull Throwable error) {
        // No location fix was obtained.
    }
};

IncodeWelcome.getInstance()
    .startOnboarding(activityContext, sessionConfig, flowConfig, onboardingListener);
```

## Configuration Options

Configure the module with `Geolocation.Builder`. The table below lists the available option.

| Setting                 | Description                                                                                                                                                                                                                                                                                       |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `setSkippable(Boolean)` | Controls the outcome when the user exits without a location fix, whether by denying permission and skipping, or by exhausting retries on the unavailable screen. If `true`, the flow continues with `ResultCode.SUCCESS`. If `false`, the flow ends with `ResultCode.ERROR`. Defaults to `false`. |

For the complete option set, see `Geolocation.Builder` in [API Reference](https://developer.incode.com/docs/android-api-reference).

## Result

Geolocation delivers a `GeolocationResult` to the `onGeolocationFetched(GeolocationResult)` callback on the `OnboardingListener`. When no location fix is obtained, `onGeolocationUnavailable(Throwable)` is called instead. Key fields include:

- `addressFields`: The captured location broken down into address fields (`AddressFields`); `null` when unavailable.
- `latitude`: The latitude of the captured location, in degrees.
- `longitude`: The longitude of the captured location, in degrees.
- Fields inherited from `BaseResult`:
  - `resultCode`: The `ResultCode` for this result.
  - `error`: When `resultCode` is `ERROR`, the `Throwable` that caused it; otherwise, `null`.
  - `deviceStats`: The `DeviceStats` snapshot of the device state when the result was produced.
  - `motionStatus`: Device motion assessment during capture. One of the following:
    - `UNCLEAR`: Motion could not be determined; the default.
    - `PASS`: Device motion was within acceptable limits.
    - `FAIL`: Excessive device motion was detected.

Errors surface through `OnboardingListener.onError(Throwable)`. The`Throwable` passed to `onGeolocationUnavailable` is a `GeolocationUnavailableException` when no location fix could be determined. It is a `LocationPermissionDeniedException` when the user denies location permission and exits the step.

For all fields, see `GeolocationResult` in [API Reference](https://developer.incode.com/docs/android-api-reference).
