> ## 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 設定ガイド

## システム要件

| 項目 | 要件 |
| - | - |
| OS | Android 17（API 37）以上（アプリの最小ターゲットは Android 8.0・API 26） |
| 対応端末 | UWB DL-TDoA 対応端末・Bluetooth LE |
| 開発環境 | `compileSdk`・`targetSdk` 37・JVM target 1.8 以上 |
| 言語 | Kotlin 1.9 以上・Java 8 以上 |
| パッケージ管理 | Gradle（Maven Central） |

測位情報を受信できない端末では SDK の測位だけが動作せず、アプリはそのまま動作します。\

ただし `initialize` はキー・通信エラーを、`session.begin()` は非対応端末エラーを投げるため、下のサンプルのように `try/catch`（Java は `onError`）で受け取ってください。

## SDK のインストール

**Step 1**

`settings.gradle(.kts)` に Maven Central リポジトリを追加します。

```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**

アプリモジュールのビルドファイルに OneS1ght SDK を追加します。

```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**

JVM target のバージョンと `BuildConfig` オプションを確認します。JVM は 1.8 以上を推奨します。

```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 以上 — 11・17 でも可
        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 以上 — 11・17 でも可
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    buildFeatures { buildConfig = true }
}
```

## Android 権限設定

測位に必要な権限は、SDK のマニフェストが**アプリに自動でマージ**します。

| 権限 | 用途 |
| - | - |
| `android.permission.RANGING` | UWB 測位権限 |
| `android.permission.ACCESS_FINE_LOCATION` | 正確な位置情報権限（UWB/BLE） |
| `android.permission.ACCESS_COARSE_LOCATION` | おおよその位置情報権限 |
| `android.permission.BLUETOOTH_SCAN` | 近くの BLE ロケーター受信権限 |
| `android.permission.INTERNET` | 外部ネットワーク（インターネット）通信権限 |
| `android.permission.ACCESS_NETWORK_STATE` | ネットワーク状態の確認（測位エンジンが要求） |
| `android.permission.CHANGE_NETWORK_STATE` | ネットワーク状態の変更（測位エンジンが要求） |

ランタイム権限は `requestPermission(activity)` 1 回でまとめてリクエストします。\

すでに許可済みならダイアログなしで即座に返り、`initialize` の前にも呼べます。\

ユーザーが拒否するか 30 秒以内に応答しない場合は `DENIED` です。システムがダイアログを表示しなくなった場合は、設定アプリへ案内してください。

| 関数・プロパティ | 説明 | 戻り値 |
| - | - | - |
| `requestPermission(activity)` | `ComponentActivity` をパラメータに渡すと SDK が必要な権限をまとめてリクエストします。 | `PermissionStatus`  `AUTHORIZED`: 許可  `DENIED`: 拒否（30 秒無応答を含む）  `UNSUPPORTED`: 端末非対応 |

```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 {                                               // Activity(ComponentActivity) の中で
    when (OneS1ght.requestPermission(this@MainActivity)) {
        PermissionStatus.AUTHORIZED  -> Unit
        PermissionStatus.DENIED      -> showSettingsGuide()           // 拒否・無応答 — ダイアログが出なくなったら設定アプリへ
        PermissionStatus.UNSUPPORTED -> showUnsupportedNotice()       // 測位非対応端末
        else                         -> Unit
    }
}
```

```java Java icon="java" theme={null}
OneS1ght.requestPermission(activity, new Callback<PermissionStatus>() {   // Activity(ComponentActivity) の中で
    @Override public void onSuccess(PermissionStatus status) {
        if (status == PermissionStatus.DENIED)      { showSettingsGuide(); }       // 拒否・無応答 — ダイアログが出なくなったら設定アプリへ
        if (status == PermissionStatus.UNSUPPORTED) { showUnsupportedNotice(); }   // 測位非対応端末
    }
    @Override public void onError(Throwable error) { }
});
```

## SDK の初期化

測位エンジンを稼働させるための初期化を開始します。

| 関数・プロパティ | 説明 | 戻り値 |
| - | - | - |
| `initialize(context, sdkKey)` | キーの検証と設定の取得。アプリ起動時に 1 回  自社サーバーを構築した場合のみ `baseUrl` 引数を追加 | なし |
| `isInitialized` | 初期化に成功したか | `Boolean` |
| `SDK_VERSION` | SDK バージョン | `String`（例: `"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"   // 開発（テスト）モードで使用する場合
             else                   "YOUR_PRODUCTION_API_KEY"    // 本番モードで使用する場合
try {
    OneS1ght.initialize(context = applicationContext, sdkKey = sdkKey)   // コルーチンの中で
} catch (e: ApiError) {
    // キー無効（E1002）・ネットワーク（E5001）など — 再度呼ぶとリトライ
} catch (e: SdkError) {
    // この顧客企業の測位がオフ（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"   // 開発（テスト）モードで使用する場合
                                  : "YOUR_PRODUCTION_API_KEY";   // 本番モードで使用する場合
OneS1ght.initialize(getApplicationContext(), sdkKey, new Callback<Void>() {
    @Override public void onSuccess(Void result) { }
    @Override public void onError(Throwable error) { }   // 失敗処理
});
```

## プロフィール

ユーザー（アカウントまたは端末）1 人の動線は**プロフィール**単位で管理されます。\

プロフィール ID は一意で、紛失すると復元できないため保管に注意してください。

| 関数・プロパティ | 説明 | 戻り値 |
| - | - | - |
| `createProfile(attributes)` | 属性からプロフィールを作成 | `String`（プロフィール ID） |
| `identify(profileId)` | この端末の測位をプロフィールに紐付け。 解除する場合は `null` | なし |
| `fetchProfile(profileId)` | プロフィール ID で属性を取得 | `Map<String, String>` |
| `replaceProfile(profileId, attributes)` | 属性をすべて置き換え | なし |
| `deleteProfile(profileId)` | プロフィールを削除 | なし |

### プロフィールの作成

新しいプロフィールを作成するとプロフィール ID が返ります。属性はレポート・指標のセグメントとして使われ、Key-Value に制約はありません。\

発行された ID はアプリ（または自社サーバー）に保管し、起動のたびに測位前に `identify` で紐付けます。

> [!WARNING]
>
> プロフィール属性に氏名・電話番号・会員 ID のような機微な情報を入れないでください。\\
>
> 年代（`"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"))   // 新しいプロフィールを作成 → プロフィール ID を返す
save(profileId)                                                                     // アプリまたは自社サーバーに保管

OneS1ght.identify(savedProfileId)   // 起動のたびに測位前に紐付け。ログアウト時は null
```

```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>() {                  // 新しいプロフィールを作成 → プロフィール ID を返す
    @Override public void onSuccess(String profileId) { save(profileId); }   // アプリまたは自社サーバーに保管
    @Override public void onError(Throwable error) { }
});

OneS1ght.identify(savedProfileId);   // 起動のたびに測位前に紐付け。ログアウト時は null
```

### プロフィール情報の取得

プロフィール 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) { }   // 存在しない 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}
OneS1ght.replaceProfile(profileId, mapOf("gender" to "F", "ageBand" to "30s"))   // 属性をすべて置き換え
```

```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>() {   // 属性をすべて置き換え
    @Override public void onSuccess(Void result) { }
    @Override public void onError(Throwable error) { }
});
```

### プロフィールの削除

パーソナライズ属性を削除します。削除した属性は元に戻せません。

```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) { }
});
```

## 建物・フロアの選択

測位座標を受信するには、ロケーターが設置された建物とフロアを選択する必要があります。

| 関数・プロパティ | 説明 | 戻り値 |
| - | - | - |
| `floorSession()` | 測位セッション | `FloorSession` |
| `session.onFloorDetected` | エンジンがフロアを検出すると呼ばれる。 フロアを見失うと `null` | `SessionFloorListener` — `(String?)`（フロア ID） |
| `buildings()` | コンソールに登録された建物一覧 | `List<Building>`  `id`: 建物 ID  `name`: 建物名  `floorCount`: フロア数 |
| `floors(buildingId)` | 建物のフロア一覧（図面画像なし） | `List<Floor>` |
| `floor(buildingId, floorId)` | フロア 1 件（図面画像あり） | `Floor` |
| `setFloorMap(floor, buildingId)` | フロアを指定  解除する場合は `null` | なし |
| `session.floor` | 現在指定されているフロア | `Floor?` |

### BLE 受信で選択

SDK の測位エンジンがロケーターの BLE 信号からフロアを検出すると、`onFloorDetected` にフロア ID が届きます。\

フロアを見失うと `null` が届きます。

```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.onFloorDetected = SessionFloorListener { floorId ->   // エンジンがフロアを検出するとフロア ID を渡す
    if (floorId == null) return@SessionFloorListener
    lifecycleScope.launch {
        val floor = OneS1ght.floor(buildingId, floorId)        // フロア情報を取得
        OneS1ght.setFloorMap(floor, buildingId)                // フロアを指定
    }
}
```

```java Java icon="java" theme={null}
FloorSession session = OneS1ght.floorSession();              // 測位セッションを取得
session.setOnFloorDetected(floorId -> {                      // エンジンがフロアを検出するとフロア ID を渡す
    if (floorId == null) return;
    OneS1ght.floor(buildingId, floorId, new Callback<Floor>() {                           // フロア情報を取得
        @Override public void onSuccess(Floor floor) {
            OneS1ght.setFloorMap(floor, buildingId, new Callback<Void>() {                // フロアを指定
                @Override public void onSuccess(Void result) { }
                @Override public void onError(Throwable error) { }
            });
        }
        @Override public void onError(Throwable error) { }
    });
});
```

### 手動で選択

ユーザーにフロアを選ばせる場合や、測位前に地図を先に描画する場合は、建物とフロアを直接指定します。

```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()                   // コンソールに登録された建物一覧
val floors    = OneS1ght.floors(buildings[0].id)       // 建物のフロア一覧

OneS1ght.setFloorMap(floors[0], buildings[0].id)       // フロアを指定
OneS1ght.setFloorMap(null)                             // フロア指定を解除
```

```java Java icon="java" theme={null}
OneS1ght.buildings(new Callback<List<Building>>() {                                   // コンソールに登録された建物一覧
    @Override public void onSuccess(List<Building> buildings) {
        String buildingId = buildings.get(0).getId();
        OneS1ght.floors(buildingId, new Callback<List<Floor>>() {                     // 建物のフロア一覧
            @Override public void onSuccess(List<Floor> floors) {
                OneS1ght.setFloorMap(floors.get(0), buildingId, new Callback<Void>() {   // フロアを指定
                    @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>() {                                     // フロア指定を解除
    @Override public void onSuccess(Void result) { }
    @Override public void onError(Throwable error) { }
});
```

## リアルタイム測位情報の受信

`floorSession()` で測位セッションを取得し、`begin()` で開始、`end()` で終了します。\

位置は `onPosition` で受け取り、座標（メートル）はエンジンが出す速度のまま届きます。サーバーへはコンソールの設定値（デフォルトは毎秒 4 回）に間引いて送信されます。

| 状況 | 対応 |
| - | - |
| アプリがバックグラウンドへ移行 | 特になし — SDK が測位を止め、フォアグラウンドに戻ると自動で再開します。  滞在中のゾーンは退出（`onZoneExit`）として扱われ、リアルタイム受信が切れます。復帰すると `ResyncNeeded` が届きます。 |
| 画面を一時的に離れるとき | `pause()` で停止し、`resume()` で再開します。エンジンは動いたままなので、すぐに続きます。 |
| 測位を完全に終えるとき | `end()`。エンジンも停止するため、次の `begin()` はロケーターを最初から探し直します。 |

| 関数・プロパティ | 説明 | 戻り値 |
| - | - | - |
| `session.onPosition` | リアルタイム座標 | `PositionListener` — `(Coordinates)`  `x`・`y`: 図面基準のメートル  `z`: 高さ |
| `session.begin()` | 測位開始 | なし |
| `session.end()` | 測位終了 + 残りの座標を送信 | なし |
| `session.pause()` / `resume()` | 一時停止 / 再開 | なし |
| `session.isRunning` / `isPaused` | 測位中 / 一時停止中か | `Boolean` |

> [!NOTE]
>
> `begin()` は権限を得てから呼んでください。権限なしで呼ぶとエラーなしで開始しますが、座標は届かず `E2003` が記録されます。\\
>
> 測位中に再度呼んだ `begin()` は無視（WARN ログ）されるため、ユーザーが権限を許可したら `end()` を呼んでから `begin()` を呼び直してください。

```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: 高さ

try {
    session.begin()   // 測位開始 (suspend)
} catch (e: SdkError) {
    // OsVersionTooLow · DeviceNotSupported · NotInitialized · NotIdentified
    hidePositioningFeatures()
}
session.end()     // 測位終了 + 残りの座標を送信 (suspend)
```

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

session.begin(new Callback<Void>() {   // 測位開始
    @Override public void onSuccess(Void result) { }
    @Override public void onError(Throwable error) { hidePositioningFeatures(); }   // SdkError — 非対応端末・初期化前など
});
session.end(new Callback<Void>() {     // 測位終了 + 残りの座標を送信
    @Override public void onSuccess(Void result) { }
    @Override public void onError(Throwable error) { }
});
```

## ゾーンイベント

コンソールの[空間管理](/ja/console/areas)で描いたゾーンの進入・退出・滞在イベントを受け取ります。判定は端末内で行われるため、すぐに届きます。\

ゾーンに紐付いた施策（クーポン・サイネージなど）はサーバーがマッチングし、`onTriggers` で届きます。

| 関数・プロパティ | 説明 | 戻り値 |
| - | - | - |
| `session.onZoneEnter` / `onZoneExit` | ゾーン進入 / 退出 | `ZoneListener` — `(Zone)`  `id`: ゾーン ID  `name`: ゾーン名  `polygon`: ゾーン形状（頂点リスト）  `dwellSeconds`: 滞在判定の基準（秒） |
| `session.onZoneDwell` | ゾーン滞在時  `dwellSeconds` 到達時に訪問ごとに 1 回 | `DwellListener` — `(Zone, Double)`  ゾーン、滞在時間（秒） |
| `session.onTriggers` | サーバーがマッチングしたパーソナライズアクション | `TriggersListener` `(String, List<Trigger>)`: ゾーン ID、トリガー一覧 （`triggerId`: トリガー ID  `type`: 種類 `payload`: 種類ごとの付加情報） |
| `session.onConfigChanged` | コンソールでゾーン設定が変更されたとき | `ConfigChangeListener` — `(ConfigChange)`  `ZonesChanged`: ゾーン変更  `PlanChanged`: 図面変更  `RulesChanged`: 施策変更  `SdkConfigChanged`: SDK リモート設定変更  `ResyncNeeded`: 全体を再取得 （再接続後、またはイベントを取りこぼした可能性がある場合） |
| `refreshZones()` | ゾーン情報を最新化 | `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("進入: ${zone.name}") }
session.onZoneExit  = ZoneListener { zone -> hideBanner() }
session.onZoneDwell = DwellListener { zone, seconds -> showBanner("滞在: ${zone.name} — ${seconds.toInt()}秒") }
session.onTriggers  = TriggersListener { zoneId, triggers -> triggers.forEach(::handle) }   // type: signage · coupon · tracking · merch · generic
```

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

### ゾーン情報の更新

コンソールでゾーン情報が変わると `onConfigChanged` が呼ばれます。連続して届く場合は 1 秒ほどまとめて、`refreshZones()` を 1 回だけ呼びます。

```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()                    // 連続して届いたら前の予約を取り消す
            refreshJob = lifecycleScope.launch {
                delay(1_000)                        // 1 秒まとめて
                OneS1ght.refreshZones()             // 1 回だけ再取得
            }
        }
        is ConfigChange.PlanChanged -> reloadFloorPlan()   // 図面が変わった
        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);           // 連続して届いたら前の予約を取り消す
        handler.postDelayed(refresh, 1000);         // 1 秒まとめて 1 回だけ再取得
    } else if (change instanceof ConfigChange.PlanChanged) {
        reloadFloorPlan();   // 図面が変わった
    }
});
```

## 図面の表示

ロケーターに紐付く図面と、ユーザーの現在位置を表示できます。\

図面を設定しない場合、背景はグリッドで表示されます。

| 関数・プロパティ | 説明 | 戻り値 |
| - | - | - |
| `floor(buildingId, floorId)` | フロア詳細 | `Floor` `image: ByteArray?`: 図面画像 `hasPlan`: 図面登録の有無 `originX`: 図面原点 X（メートル） `originY`: 図面原点 Y（メートル） `widthM`: 横幅（メートル） `heightM`: 縦幅（メートル） `minX`: X の最小値 `minY`: Y の最小値 `maxX`: X の最大値 `maxY`: Y の最大値 |
| `locators(buildingId, floorId)` | フロアのロケーター情報 | `FloorLocators` `locators`: ロケーター情報 （`address`: UWB MAC の下位 2 バイト（`Int`）、  `x`・`y`・`z`: ロケーター座標） `sessionId`: UWB セッション ID `positioningReady`: 測位可能か |

```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) { }
});
```

## その他のオプション

| 関数・プロパティ | 説明 | 戻り値 |
| - | - | - |
| `uploadPendingPositions()` | アプリ内に溜めた座標を即時送信 | なし |
| `discardPendingPositions()` | アプリ内に溜めた座標を破棄 | なし |
| `reset()` | 初期化のリセット — セッション破棄・測位停止・保存されたキーと建物情報の消去  その後 `initialize` を再度呼ぶ必要があります | なし |
| `session.onStopped` | セッション停止 | `SessionStoppedListener` — `(StopReason)`: 停止理由  `ENDED`: セッション終了（正常）  `ENGINE_FAILED`: エンジン異常（失敗） |
| `deviceAvailability` | 測位可否の確認（詳細）— `initialize()` の後に読む | `DeviceAvailability` `AVAILABLE`: 測位可能  `OS_VERSION_TOO_LOW`: 非対応の Android OS バージョン `DEVICE_NOT_SUPPORTED`: 非対応端末 |
| `isDeviceAvailable` | 測位可否の確認（要約） | `Boolean` |
| `OneS1ght.onDebugLog` | SDK デバッグ用 | `DebugLogListener` — `(LogLevel, String)` ログを返す |

### データ送信の制御

座標は **300 件たまるか、前回の送信から 60 秒経過すると**自動で送信されます。ただし、アプリが強制終了されると座標は破棄されます。

```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()   // 溜めた座標を今すぐ送信 (suspend)
OneS1ght.discardPendingPositions()  // 送信せずに破棄
```

```java Java icon="java" theme={null}
OneS1ght.uploadPendingPositions(new Callback<Void>() {   // 溜めた座標を今すぐ送信
    @Override public void onSuccess(Void result) { }
    @Override public void onError(Throwable error) { }
});
OneS1ght.discardPendingPositions();                      // 送信せずに破棄
```

### セッションのリセット

セッションを破棄して測位を止め、保存された SDK キーと建物情報（`setFloorMap` の建物）を消去します。新しいセッションは作りません。\

その後はほとんどの API が `SdkError.NotInitialized`（`E1001`）を投げるため、`initialize` を再度呼んでください。実行中にキーを切り替えるときに使います。\

`identify` で紐づけたプロフィールは残ります。

```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()                                                          // セッション・キー・建物情報を破棄
OneS1ght.initialize(context = applicationContext, sdkKey = newSdkKey)     // 再初期化すると API が使える
```

```java Java icon="java" theme={null}
OneS1ght.reset(new Callback<Void>() {
    @Override public void onSuccess(Void result) { }   // その後 initialize を再度呼ぶ
    @Override public void onError(Throwable error) { }
});
```

### 測位終了時の処理

測位エンジンが終了すると `onStopped` イベントが発生し、測位可能な状態へ戻せます。

```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()   // 原因を解消して begin() をやり直す
}
```

```java Java icon="java" theme={null}
session.setOnStopped(reason -> {
    if (reason == FloorSession.StopReason.ENGINE_FAILED) { showRetrySearch(); }   // 原因を解消して begin() をやり直す
});
```

### 対応端末の確認

`deviceAvailability` はネットワーク（インターネット）接続なしで即時に確認できます。\

ただしチップの確認にアプリの `Context` が必要なため、**`initialize()`（または `requestPermission(activity)`）の後に読んでください**。\

それより前に読むと Android 17 以上の端末でも `DEVICE_NOT_SUPPORTED` が返り、`onDebugLog` に WARN が残ります（Android 17 未満は常に `OS_VERSION_TOO_LOW`）。

| 関数・プロパティ | 説明 | 戻り値 |
| - | - | - |
| `deviceAvailability` | 測位可否の確認（詳細）— `initialize()` の後に読む | `DeviceAvailability` `AVAILABLE`: 測位可能  `OS_VERSION_TOO_LOW`: 非対応の Android OS バージョン `DEVICE_NOT_SUPPORTED`: 非対応端末 |
| `isDeviceAvailable` | 測位可否の確認（要約） | `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}
// OneS1ght.initialize(...) を呼んだ後（失敗していても可）
when (OneS1ght.deviceAvailability) {
    DeviceAvailability.AVAILABLE            -> Unit
    DeviceAvailability.OS_VERSION_TOO_LOW   -> showNotice("Android 17 以上が必要です")
    DeviceAvailability.DEVICE_NOT_SUPPORTED -> hidePositioningFeatures()   // 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(); }   // initialize が失敗しても端末判定は可能
});

void checkDevice() {   // initialize の後に読む
    if (OneS1ght.getDeviceAvailability() != DeviceAvailability.AVAILABLE) {   // OS_VERSION_TOO_LOW · DEVICE_NOT_SUPPORTED
        hidePositioningFeatures();
    }
}
```

## 付録

### Java から使う

すべての非同期 API は、**同じ名前で 2 つの形**を提供します。

| 言語 | 形 | 例 |
| - | - | - |
| Kotlin | `suspend fun` — コルーチンから呼ぶ | `OneS1ght.buildings()` |
| Java | 最後の引数に `Callback<T>` — 結果はメインスレッドで届く | `OneS1ght.buildings(callback)` |

* 戻り値のない API は `Callback<Void>` を使います。
* イベントコールバック（`ZoneListener`・`PositionListener` など）は `fun interface` なので、Java のラムダでそのまま登録します — `session.setOnZoneEnter(zone -> …)`。
* 状態値は getter で読みます — `OneS1ght.isInitialized()`、`OneS1ght.getDeviceAvailability()`、`session.isRunning()`。
* コールバックプロパティは setter で登録します — `session.setOnStopped(…)`、`OneS1ght.setOnDebugLog(…)`。
* Java アプリは `compileOptions` を 1.8 以上にすればよく、Kotlin の設定は不要です。

### デバッグログ

デバッグログを確認するには、SDK の初期化（`initialize`）の**直前**に登録してください。\

ログは Android Studio の **Logcat**（フィルター `tag:OneS1ght`）または `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));
}
```

`adb` で取得する場合

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

## トラブルシューティング

> **[エラーコード](/ja/developers/sdk/troubleshooting/error-codes)**
>
> 開発中に発生するエラーの内容を案内します。


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