Native Android apps use the Maven artifact com.mentra:bluetooth-sdk.

Requirements

  • Android min SDK 28 or newer.
  • Java 17.
  • Android Studio or Gradle.
  • A physical Android phone for Bluetooth, camera, microphone, direct phone photo, and direct phone WebRTC testing.

Gradle Setup

// settings.gradle.kts
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven("https://www.jitpack.io")
    }
}
// app/build.gradle.kts
android {
    defaultConfig {
        minSdk = 28
    }

    packaging {
        jniLibs {
            pickFirsts += "**/libc++_shared.so"
            pickFirsts += "**/libonnxruntime.so"
            pickFirsts += "**/libonnxruntime4j_jni.so"
        }
    }
}

dependencies {
    implementation("com.mentra:bluetooth-sdk:0.1.5")
}
The packaging rules avoid duplicate native library conflicts from the SDK audio and transcription stack.

Permissions

Declare the platform permissions your app uses:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Request runtime permissions before scanning. Some Android 12+ devices require Location permission and Location services before BLE scan callbacks are delivered.

Basic Flow

import android.content.Context
import com.mentra.bluetoothsdk.Device
import com.mentra.bluetoothsdk.DeviceModel
import com.mentra.bluetoothsdk.GlassesRuntimeState
import com.mentra.bluetoothsdk.MentraBluetoothSdk
import com.mentra.bluetoothsdk.MentraBluetoothSdkCallback

class GlassesController(context: Context) : MentraBluetoothSdkCallback() {
    private val sdk = MentraBluetoothSdk.create(
        context = context.applicationContext,
        listener = this,
    )
    private var selectedDevice: Device? = null

    fun scan() {
        sdk.scan(DeviceModel.MENTRA_LIVE, timeoutMs = 10_000) { devices ->
            renderDevicePicker(devices, onSelect = { selectedDevice = it })
        }
    }

    fun connect() {
        selectedDevice?.let { sdk.connect(it) }
    }

    fun refreshStatus() {
        sdk.requestVersionInfo()
        val glasses = sdk.getGlasses()
        if (glasses is GlassesRuntimeState.Connected) {
            val model = glasses.device.deviceModel?.deviceType ?: "glasses"
            val battery = glasses.battery.level?.toString() ?: "unknown"
            println("Connected to $model, battery=$battery%")
        }
    }

    override fun onGlassesChanged(glasses: GlassesRuntimeState) {
        // Keep app UI derived from SDK status.
        println("Glasses changed: $glasses")
    }

    private fun renderDevicePicker(devices: List<Device>, onSelect: (Device) -> Unit) {
        // Render devices in SDK-provided order and call onSelect with the user's choice.
    }

    fun close() {
        sdk.close()
    }
}

Local SDK Override

Use this when testing SDK source changes locally:
cd /path/to/MentraOS/mobile/android
./gradlew :lc3Lib:publishToMavenLocal :mentra-bluetooth-sdk:publishToMavenLocal
Then include mavenLocal() before mavenCentral() in the app’s repositories while testing the local artifact.