> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ones1ght.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Android Setup Guide

## System requirements

| Item | Requirement |
| - | - |
| OS | Android 17 (API 37) or later (minimum app target is Android 8.0 · API 26) |
| Supported devices | Devices with UWB DL-TDoA support · Bluetooth LE |
| Development environment | `compileSdk` · `targetSdk` 37 · JVM target 1.8 or later |
| Language | Kotlin 1.9 or later · Java 8 or later |
| Package manager | Gradle (Maven Central) |

On devices that cannot receive positioning data, only SDK positioning stops working; the rest of your app keeps running. \

Note that `initialize` throws key and network errors, and `session.begin()` throws unsupported-device errors, so handle them with `try/catch` (`onError` in Java) as in the samples below.

## Installing the SDK

**Step 1**

Add the Maven Central repository to `settings.gradle(.kts)`.

```kotlin settings.gradle.kts icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
}
```

**Step 2**

Add the OneS1ght SDK to your app module's build file.

```kotlin build.gradle.kts icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
dependencies {
    implementation("com.ones1ght.sdk:android:0.0.11")
}
```

```groovy build.gradle theme={null}
dependencies {
    implementation 'com.ones1ght.sdk:android:0.0.11'
}
```

**Step 3**

Check the JVM target version and the `BuildConfig` option. JVM 1.8 or later is recommended.

```kotlin build.gradle.kts icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
android {
    compileSdk = 37
    defaultConfig {
        minSdk = 26
        targetSdk = 37
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8   // 1.8 or later — 11 and 17 also work
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    buildFeatures { buildConfig = true }
}
```

```groovy build.gradle theme={null}
android {
    compileSdk = 37
    defaultConfig {
        minSdk = 26
        targetSdk = 37
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8   // 1.8 or later — 11 and 17 also work
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    buildFeatures { buildConfig = true }
}
```

## Android permissions

The permissions required for positioning are **merged into your app automatically** from the SDK manifest.

| Permission | Purpose |
| - | - |
| `android.permission.RANGING` | UWB positioning |
| `android.permission.ACCESS_FINE_LOCATION` | Precise location (UWB/BLE) |
| `android.permission.ACCESS_COARSE_LOCATION` | Approximate location |
| `android.permission.BLUETOOTH_SCAN` | Scanning nearby BLE locators |
| `android.permission.INTERNET` | Network (internet) access |
| `android.permission.ACCESS_NETWORK_STATE` | Reading network state (required by the positioning engine) |
| `android.permission.CHANGE_NETWORK_STATE` | Changing network state (required by the positioning engine) |

Runtime permissions are requested together with a single `requestPermission(activity)` call. \

If they are already granted it returns immediately without a prompt, and it can be called before `initialize`. \

If the user denies the request or does not answer within 30 seconds, the result is `DENIED`. If the system no longer shows the prompt, guide the user to the Settings app.

| Function · Property | Description | Returns |
| - | - | - |
| `requestPermission(activity)` | Pass a `ComponentActivity` and the SDK requests every permission it needs at once. | `PermissionStatus`  `AUTHORIZED`: granted  `DENIED`: denied (including no answer within 30 seconds)  `UNSUPPORTED`: unsupported device |

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
lifecycleScope.launch {                                               // inside an Activity (ComponentActivity)
    when (OneS1ght.requestPermission(this@MainActivity)) {
        PermissionStatus.AUTHORIZED  -> Unit
        PermissionStatus.DENIED      -> showSettingsGuide()           // denied or no answer — if the prompt no longer appears, send the user to Settings
        PermissionStatus.UNSUPPORTED -> showUnsupportedNotice()       // device without positioning support
        else                         -> Unit
    }
}
```

```java Java icon="java" theme={null}
OneS1ght.requestPermission(activity, new Callback<PermissionStatus>() {   // inside an Activity (ComponentActivity)
    @Override public void onSuccess(PermissionStatus status) {
        if (status == PermissionStatus.DENIED)      { showSettingsGuide(); }       // denied or no answer — if the prompt no longer appears, send the user to Settings
        if (status == PermissionStatus.UNSUPPORTED) { showUnsupportedNotice(); }   // device without positioning support
    }
    @Override public void onError(Throwable error) { }
});
```

## Initializing the SDK

Runs the initialization needed to start the positioning engine.

| Function · Property | Description | Returns |
| - | - | - |
| `initialize(context, sdkKey)` | Validates the key and fetches the configuration. Once at app launch  Add the `baseUrl` argument only if you run your own server | None |
| `isInitialized` | Whether initialization succeeded | `Boolean` |
| `SDK_VERSION` | SDK version | `String` (e.g. `"0.0.11"`) |

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
import co.onecheck.ones1ght.android.*
import co.onecheck.ones1ght.android.model.*
import co.onecheck.ones1ght.android.network.ApiError

val sdkKey = if (BuildConfig.DEBUG) "YOUR_DEVELOPMENT_API_KEY"   // for development (test) builds
             else                   "YOUR_PRODUCTION_API_KEY"    // for production builds
try {
    OneS1ght.initialize(context = applicationContext, sdkKey = sdkKey)   // inside a coroutine
} catch (e: ApiError) {
    // invalid key (E1002), network (E5001), etc. — calling again retries
} catch (e: SdkError) {
    // positioning is turned off for this tenant (PositioningDisabled, E1003)
}
```

```java Java icon="java" theme={null}
import co.onecheck.ones1ght.android.*;
import co.onecheck.ones1ght.android.model.*;

String sdkKey = BuildConfig.DEBUG ? "YOUR_DEVELOPMENT_API_KEY"   // for development (test) builds
                                  : "YOUR_PRODUCTION_API_KEY";   // for production builds
OneS1ght.initialize(getApplicationContext(), sdkKey, new Callback<Void>() {
    @Override public void onSuccess(Void result) { }
    @Override public void onError(Throwable error) { }   // handle failure
});
```

## Profiles

Each user (account or device) is tracked as a **profile**. \

Profile IDs are unique and cannot be recovered once lost, so store them carefully.

| Function · Property | Description | Returns |
| - | - | - |
| `createProfile(attributes)` | Creates a profile from attributes | `String` (profile ID) |
| `identify(profileId)` | Links this device's positioning to the profile.  Pass `null` to unlink | None |
| `fetchProfile(profileId)` | Fetches attributes by profile ID | `Map<String, String>` |
| `replaceProfile(profileId, attributes)` | Replaces all attributes | None |
| `deleteProfile(profileId)` | Deletes the profile | None |

### Creating a profile

Creating a profile returns its profile ID. Attributes are used as segments in reports and metrics, and there are no restrictions on the key-value pairs.\

Store the returned ID in your app (or on your server) and link it with `identify` before positioning on every launch.

> [!WARNING]
>
> Do not put sensitive data such as names, phone numbers or member IDs in profile attributes.\\
>
> Use only non-identifying values such as an age band (`"20s"`).

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
val profileId = OneS1ght.createProfile(mapOf("gender" to "F", "ageBand" to "20s"))   // create a profile → returns the profile ID
save(profileId)                                                                     // store in the app or on your server

OneS1ght.identify(savedProfileId)   // link before positioning on every launch; null on logout
```

```java Java icon="java" theme={null}
Map<String, String> attributes = new HashMap<>();
attributes.put("gender", "F");
attributes.put("ageBand", "20s");
OneS1ght.createProfile(attributes, new Callback<String>() {                  // create a profile → returns the profile ID
    @Override public void onSuccess(String profileId) { save(profileId); }   // store in the app or on your server
    @Override public void onError(Throwable error) { }
});

OneS1ght.identify(savedProfileId);   // link before positioning on every launch; null on logout
```

### Fetching a profile

Fetches the personalization attributes stored for a profile ID.

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
val attributes = OneS1ght.fetchProfile(profileId)   // {gender=F, ageBand=20s}
```

```java Java icon="java" theme={null}
OneS1ght.fetchProfile(profileId, new Callback<Map<String, String>>() {
    @Override public void onSuccess(Map<String, String> attributes) { }
    @Override public void onError(Throwable error) { }   // unknown ID
});
```

### Updating a profile

Personalization attributes are **replaced as a whole**. The previous values are deleted and cannot be restored.

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
OneS1ght.replaceProfile(profileId, mapOf("gender" to "F", "ageBand" to "30s"))   // replace all attributes
```

```java Java icon="java" theme={null}
Map<String, String> attributes = new HashMap<>();
attributes.put("gender", "F");
attributes.put("ageBand", "30s");
OneS1ght.replaceProfile(profileId, attributes, new Callback<Void>() {   // replace all attributes
    @Override public void onSuccess(Void result) { }
    @Override public void onError(Throwable error) { }
});
```

### Deleting a profile

Deletes the profile's personalization attributes. Deleted attributes cannot be restored.

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
OneS1ght.deleteProfile(profileId)
```

```java Java icon="java" theme={null}
OneS1ght.deleteProfile(profileId, new Callback<Void>() {
    @Override public void onSuccess(Void result) { }
    @Override public void onError(Throwable error) { }
});
```

## Selecting a building and floor

To receive position coordinates, select the building and floor where the locators are installed.

| Function · Property | Description | Returns |
| - | - | - |
| `floorSession()` | Positioning session | `FloorSession` |
| `session.onFloorDetected` | Called when the engine finds a floor.  `null` when the floor is lost | `SessionFloorListener` — `(String?)` (floor ID) |
| `buildings()` | Buildings registered in the console | `List<Building>`  `id`: building ID  `name`: building name  `floorCount`: number of floors |
| `floors(buildingId)` | Floors of a building (without floor plan image) | `List<Floor>` |
| `floor(buildingId, floorId)` | One floor (with floor plan image) | `Floor` |
| `setFloorMap(floor, buildingId)` | Sets the floor  `null` to clear | None |
| `session.floor` | Currently set floor | `Floor?` |

### Selecting from BLE

When the SDK's positioning engine finds a floor from locator BLE signals, the floor ID arrives in `onFloorDetected`. \

`null` arrives when the floor is lost.

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
val session = OneS1ght.floorSession()                        // get the positioning session
session.onFloorDetected = SessionFloorListener { floorId ->   // the engine passes the floor ID when it finds a floor
    if (floorId == null) return@SessionFloorListener
    lifecycleScope.launch {
        val floor = OneS1ght.floor(buildingId, floorId)        // fetch the floor
        OneS1ght.setFloorMap(floor, buildingId)                // set the floor
    }
}
```

```java Java icon="java" theme={null}
FloorSession session = OneS1ght.floorSession();              // get the positioning session
session.setOnFloorDetected(floorId -> {                      // the engine passes the floor ID when it finds a floor
    if (floorId == null) return;
    OneS1ght.floor(buildingId, floorId, new Callback<Floor>() {                           // fetch the floor
        @Override public void onSuccess(Floor floor) {
            OneS1ght.setFloorMap(floor, buildingId, new Callback<Void>() {                // set the floor
                @Override public void onSuccess(Void result) { }
                @Override public void onError(Throwable error) { }
            });
        }
        @Override public void onError(Throwable error) { }
    });
});
```

### Selecting manually

Set the building and floor yourself when the user picks the floor, or when you want to draw the map before positioning starts.

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
val buildings = OneS1ght.buildings()                   // buildings registered in the console
val floors    = OneS1ght.floors(buildings[0].id)       // floors of the building

OneS1ght.setFloorMap(floors[0], buildings[0].id)       // set the floor
OneS1ght.setFloorMap(null)                             // clear the floor
```

```java Java icon="java" theme={null}
OneS1ght.buildings(new Callback<List<Building>>() {                                   // buildings registered in the console
    @Override public void onSuccess(List<Building> buildings) {
        String buildingId = buildings.get(0).getId();
        OneS1ght.floors(buildingId, new Callback<List<Floor>>() {                     // floors of the building
            @Override public void onSuccess(List<Floor> floors) {
                OneS1ght.setFloorMap(floors.get(0), buildingId, new Callback<Void>() {   // set the floor
                    @Override public void onSuccess(Void result) { }
                    @Override public void onError(Throwable error) { }
                });
            }
            @Override public void onError(Throwable error) { }
        });
    }
    @Override public void onError(Throwable error) { }
});
OneS1ght.setFloorMap(null, new Callback<Void>() {                                     // clear the floor
    @Override public void onSuccess(Void result) { }
    @Override public void onError(Throwable error) { }
});
```

## Receiving live positions

Get a positioning session with `floorSession()`, start it with `begin()` and stop it with `end()`. \

Positions arrive in `onPosition` as coordinates in meters, at the rate the engine produces them. Uploads to the server are downsampled to the console setting (4 per second by default).

| Situation | What to do |
| - | - |
| App goes to the background | Nothing special — the SDK stops positioning and restarts it automatically when the app returns to the foreground.  The zone the user was in is treated as exited (`onZoneExit`) and the live stream is cut; on return, `ResyncNeeded` arrives. |
| User leaves the screen briefly | Stop with `pause()` and continue with `resume()`. The engine stays on, so positioning picks up right away. |
| Stopping positioning for good | `end()`. The engine shuts down too, so the next `begin()` searches for locators from scratch. |

| Function · Property | Description | Returns |
| - | - | - |
| `session.onPosition` | Live coordinates | `PositionListener` — `(Coordinates)`  `x` · `y`: meters on the floor plan  `z`: height |
| `session.begin()` | Starts positioning | None |
| `session.end()` | Stops positioning and uploads the remaining coordinates | None |
| `session.pause()` / `resume()` | Pause / resume | None |
| `session.isRunning` / `isPaused` | Whether positioning is running / paused | `Boolean` |

> [!NOTE]
>
> Call `begin()` after permissions are granted. Without permissions it starts without throwing, but no coordinates arrive and `E2003` is logged. \\
>
> Calling `begin()` again while positioning is running is ignored (WARN log), so once the user grants permissions, call `end()` and then `begin()` again.

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
val session = OneS1ght.floorSession()
session.onPosition = PositionListener { coord -> updateMyLocationMarker(coord.x, coord.y) }   // coord.z: height

try {
    session.begin()   // start positioning (suspend)
} catch (e: SdkError) {
    // OsVersionTooLow · DeviceNotSupported · NotInitialized · NotIdentified
    hidePositioningFeatures()
}
session.end()     // stop positioning and upload the remaining coordinates (suspend)
```

```java Java icon="java" theme={null}
FloorSession session = OneS1ght.floorSession();
session.setOnPosition(coord -> updateMyLocationMarker(coord.getX(), coord.getY()));   // coord.getZ(): height

session.begin(new Callback<Void>() {   // start positioning
    @Override public void onSuccess(Void result) { }
    @Override public void onError(Throwable error) { hidePositioningFeatures(); }   // SdkError — unsupported device, not initialized, etc.
});
session.end(new Callback<Void>() {     // stop positioning and upload the remaining coordinates
    @Override public void onSuccess(Void result) { }
    @Override public void onError(Throwable error) { }
});
```

## Zone events

Receive enter, exit and dwell events for the zones drawn in the console's [Space Management](/en/console/areas). Detection happens on the device, so events arrive immediately. \

Campaigns attached to a zone (coupons, signage, etc.) are matched by the server and delivered through `onTriggers`.

| Function · Property | Description | Returns |
| - | - | - |
| `session.onZoneEnter` / `onZoneExit` | Zone entered / exited | `ZoneListener` — `(Zone)`  `id`: zone ID  `name`: zone name  `polygon`: zone shape (list of vertices)  `dwellSeconds`: dwell threshold (seconds) |
| `session.onZoneDwell` | Dwelling in a zone  Once per visit when `dwellSeconds` is reached | `DwellListener` — `(Zone, Double)`  zone, dwell time (seconds) |
| `session.onTriggers` | Personalized actions matched by the server | `TriggersListener` `(String, List<Trigger>)`: zone ID, list of triggers  (`triggerId`: trigger ID  `type`: type `payload`: extra data per type) |
| `session.onConfigChanged` | Zone settings changed in the console | `ConfigChangeListener` — `(ConfigChange)`  `ZonesChanged`: zones changed  `PlanChanged`: floor plan changed  `RulesChanged`: campaigns changed  `SdkConfigChanged`: SDK remote config changed  `ResyncNeeded`: refetch everything (after a reconnect, or when events may have been missed) |
| `refreshZones()` | Refreshes zone data | `List<Zone>` |

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
session.onZoneEnter = ZoneListener { zone -> showBanner("Entered: ${zone.name}") }
session.onZoneExit  = ZoneListener { zone -> hideBanner() }
session.onZoneDwell = DwellListener { zone, seconds -> showBanner("Dwell: ${zone.name} — ${seconds.toInt()}s") }
session.onTriggers  = TriggersListener { zoneId, triggers -> triggers.forEach(::handle) }   // type: signage · coupon · tracking · merch · generic
```

```java Java icon="java" theme={null}
session.setOnZoneEnter(zone -> showBanner("Entered: " + zone.getName()));
session.setOnZoneExit(zone -> hideBanner());
session.setOnZoneDwell((zone, seconds) -> showBanner("Dwell: " + zone.getName() + " — " + (int) seconds + "s"));
session.setOnTriggers((zoneId, triggers) -> {
    for (Trigger trigger : triggers) { handle(trigger); }   // triggerId · type · payload
});
```

### Refreshing zone data

`onConfigChanged` is called when zone data changes in the console. If several arrive in a row, batch them for about a second and call `refreshZones()` once.

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
var refreshJob: Job? = null
session.onConfigChanged = ConfigChangeListener { change ->
    when (change) {
        is ConfigChange.ZonesChanged, is ConfigChange.ResyncNeeded -> {
            refreshJob?.cancel()                    // cancel the pending refresh if another change arrives
            refreshJob = lifecycleScope.launch {
                delay(1_000)                        // batch for one second
                OneS1ght.refreshZones()             // then refresh once
            }
        }
        is ConfigChange.PlanChanged -> reloadFloorPlan()   // floor plan changed
        else -> Unit
    }
}
```

```java Java icon="java" theme={null}
Handler handler = new Handler(Looper.getMainLooper());
Runnable refresh = () -> OneS1ght.refreshZones(new Callback<List<Zone>>() {
    @Override public void onSuccess(List<Zone> zones) { }
    @Override public void onError(Throwable error) { }
});
session.setOnConfigChanged(change -> {
    if (change instanceof ConfigChange.ZonesChanged || change instanceof ConfigChange.ResyncNeeded) {
        handler.removeCallbacks(refresh);           // cancel the pending refresh if another change arrives
        handler.postDelayed(refresh, 1000);         // batch for one second, then refresh once
    } else if (change instanceof ConfigChange.PlanChanged) {
        reloadFloorPlan();   // floor plan changed
    }
});
```

## Displaying the floor plan

You can display the floor plan the locators are installed on, together with the user's current position. \

If no floor plan is set, the background is shown as a grid.

| Function · Property | Description | Returns |
| - | - | - |
| `floor(buildingId, floorId)` | Floor details | `Floor` `image: ByteArray?`: floor plan image `hasPlan`: whether a floor plan is registered `originX`: floor plan origin X (meters) `originY`: floor plan origin Y (meters) `widthM`: width (meters) `heightM`: height (meters) `minX`: minimum X `minY`: minimum Y `maxX`: maximum X `maxY`: maximum Y |
| `locators(buildingId, floorId)` | Locators on the floor | `FloorLocators` `locators`: locator data (`address`: last 2 bytes of the UWB MAC (`Int`),  `x` · `y` · `z`: locator coordinates) `sessionId`: UWB session ID `positioningReady`: whether positioning is possible |

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
val floor = OneS1ght.floor(buildingId, floorId)
planView.setImageBitmap(floor.image?.let { BitmapFactory.decodeByteArray(it, 0, it.size) })
setBounds(floor.minX, floor.minY, floor.maxX, floor.maxY)
```

```java Java icon="java" theme={null}
OneS1ght.floor(buildingId, floorId, new Callback<Floor>() {
    @Override public void onSuccess(Floor floor) {
        byte[] png = floor.getImage();
        planView.setImageBitmap(png == null ? null : BitmapFactory.decodeByteArray(png, 0, png.length));
        setBounds(floor.getMinX(), floor.getMinY(), floor.getMaxX(), floor.getMaxY());
    }
    @Override public void onError(Throwable error) { }
});
```

## Other options

| Function · Property | Description | Returns |
| - | - | - |
| `uploadPendingPositions()` | Uploads the coordinates buffered in the app right away | None |
| `discardPendingPositions()` | Discards the coordinates buffered in the app | None |
| `reset()` | Resets initialization — discards the session, stops positioning, clears the stored key and building context  Call `initialize` again afterwards | None |
| `session.onStopped` | Session stopped | `SessionStoppedListener` — `(StopReason)`: why the session stopped  `ENDED`: ended normally  `ENGINE_FAILED`: engine failure |
| `deviceAvailability` | Device support (detailed) — read after `initialize()` | `DeviceAvailability` `AVAILABLE`: positioning available  `OS_VERSION_TOO_LOW`: unsupported Android OS version `DEVICE_NOT_SUPPORTED`: unsupported device |
| `isDeviceAvailable` | Device support (summary) | `Boolean` |
| `OneS1ght.onDebugLog` | SDK debugging hook | `DebugLogListener` — `(LogLevel, String)` log output |

### Controlling data upload

Coordinates are uploaded automatically **when 300 have been buffered, or 60 seconds after the last upload**. If the app is force-quit, buffered coordinates are discarded.

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
OneS1ght.uploadPendingPositions()   // upload the buffered coordinates now (suspend)
OneS1ght.discardPendingPositions()  // discard without uploading
```

```java Java icon="java" theme={null}
OneS1ght.uploadPendingPositions(new Callback<Void>() {   // upload the buffered coordinates now
    @Override public void onSuccess(Void result) { }
    @Override public void onError(Throwable error) { }
});
OneS1ght.discardPendingPositions();                      // discard without uploading
```

### Resetting the session

Discards the session, stops positioning, and clears the stored SDK key and building context (the building from `setFloorMap`). It does not create a new session. \

After that, most APIs throw `SdkError.NotInitialized` (`E1001`), so call `initialize` again. Use it to switch keys at runtime. \

The profile linked with `identify` is kept.

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
OneS1ght.reset()                                                          // discard session, key and building context
OneS1ght.initialize(context = applicationContext, sdkKey = newSdkKey)     // initialize again before using the APIs
```

```java Java icon="java" theme={null}
OneS1ght.reset(new Callback<Void>() {
    @Override public void onSuccess(Void result) { }   // call initialize again afterwards
    @Override public void onError(Throwable error) { }
});
```

### Handling positioning shutdown

When the positioning engine shuts down, `onStopped` fires and you can bring the app back to a state where positioning can start again.

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
session.onStopped = SessionStoppedListener { reason ->
    if (reason == FloorSession.StopReason.ENGINE_FAILED) showRetrySearch()   // fix the cause, then call begin() again
}
```

```java Java icon="java" theme={null}
session.setOnStopped(reason -> {
    if (reason == FloorSession.StopReason.ENGINE_FAILED) { showRetrySearch(); }   // fix the cause, then call begin() again
});
```

### Checking device support

`deviceAvailability` can be read right away without a network (internet) connection. \

However, checking the chip needs the app `Context`, so **read it after `initialize()` (or `requestPermission(activity)`)**. \

If you read it earlier, even Android 17+ devices get `DEVICE_NOT_SUPPORTED` and a WARN is logged to `onDebugLog` (below Android 17 it is always `OS_VERSION_TOO_LOW`).

| Function · Property | Description | Returns |
| - | - | - |
| `deviceAvailability` | Device support (detailed) — read after `initialize()` | `DeviceAvailability` `AVAILABLE`: positioning available  `OS_VERSION_TOO_LOW`: unsupported Android OS version `DEVICE_NOT_SUPPORTED`: unsupported device |
| `isDeviceAvailable` | Device support (summary) | `Boolean` |

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
// after calling OneS1ght.initialize(...) (even if it failed)
when (OneS1ght.deviceAvailability) {
    DeviceAvailability.AVAILABLE            -> Unit
    DeviceAvailability.OS_VERSION_TOO_LOW   -> showNotice("Android 17 or later is required")
    DeviceAvailability.DEVICE_NOT_SUPPORTED -> hidePositioningFeatures()   // device without UWB
    else                                    -> Unit
}
```

```java Java icon="java" theme={null}
OneS1ght.initialize(getApplicationContext(), sdkKey, new Callback<Void>() {
    @Override public void onSuccess(Void result) { checkDevice(); }
    @Override public void onError(Throwable error) { checkDevice(); }   // device check works even if initialize failed
});

void checkDevice() {   // read after initialize
    if (OneS1ght.getDeviceAvailability() != DeviceAvailability.AVAILABLE) {   // OS_VERSION_TOO_LOW · DEVICE_NOT_SUPPORTED
        hidePositioningFeatures();
    }
}
```

## Appendix

### Using the SDK from Java

Every asynchronous API comes in **two forms with the same name**.

| Language | Form | Example |
| - | - | - |
| Kotlin | `suspend fun` — call from a coroutine | `OneS1ght.buildings()` |
| Java | `Callback<T>` as the last argument — results are delivered on the main thread | `OneS1ght.buildings(callback)` |

* APIs with no return value take a `Callback<Void>`.
* Event listeners (`ZoneListener`, `PositionListener`, etc.) are `fun interface`s, so register them directly with Java lambdas — `session.setOnZoneEnter(zone -> …)`.
* Read state through getters — `OneS1ght.isInitialized()`, `OneS1ght.getDeviceAvailability()`, `session.isRunning()`.
* Register callback properties through setters — `session.setOnStopped(…)`, `OneS1ght.setOnDebugLog(…)`.
* Java apps only need `compileOptions` set to 1.8 or later; no Kotlin configuration is required.

### Debug logs

To see debug logs, register the listener **right before** calling `initialize`. \

View them in Android Studio's **Logcat** (filter `tag:OneS1ght`) or with `adb`.

```kotlin Kotlin icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
if (BuildConfig.DEBUG) {
    OneS1ght.onDebugLog = DebugLogListener { level, message -> Log.d("OneS1ght", "[$level] $message") }
}
```

```java Java icon="java" theme={null}
if (BuildConfig.DEBUG) {
    OneS1ght.setOnDebugLog((level, message) -> Log.d("OneS1ght", "[" + level + "] " + message));
}
```

Reading with `adb`

```bash theme={null}
adb logcat -c
adb logcat -s OneS1ght > onesight-log.txt
```

## Troubleshooting

> **[Error codes](/en/developers/sdk/troubleshooting/error-codes)**
>
> Explains the errors you may run into during development.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.