Developer Guide

How to integrate the xg.glass SDK in your app.

Prerequisites

Minimum environment:

  • JDK 17
  • Android SDK + platform tools (you need adb on your PATH)
  • An Android phone (USB debugging enabled)
  • Android emulator (if you want to use the simulation mode)
  • Python 3.9+ (if you use the CLI)

Platform-specific notes:

  • Android: the SDK minSdk is 28. Meta support and the RayNeo glasses host require minSdk 29.
  • iOS: the Swift Package requires iOS 16+.
  • Frame (current implementation): Android source/CLI workflows require Flutter on the developer machine because the SDK embeds a Flutter module at build time. Frame on iOS is sample-only through Flutter add-to-app.

Installation

xg.glass 0.3.0 is available through three install channels. Choose the one that matches your target platform.

Android: Maven Central

Use Maven Central for normal Android apps. SDK artifacts use group io.github.hkust-spark, version 0.3.0, and prefixed artifact IDs such as xgglass-universal, xgglass-core, and xgglass-device-meta. Kotlin packages in code remain com.xgglass.*.

The main Android artifact is xgglass-universal. It includes Rokid, RayNeo, INMO Air3, Simulator, Omi, Even Realities G1, and the Frame bridge API. RayNeo also needs the vendor AARs from RayNeo DevDocs; see third_party/rayneo/aar/README.md in the SDK repository.

// settings.gradle.kts
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        exclusiveContent {
            forRepository {
                maven { url = uri("https://maven.rokid.com/repository/maven-public/") }
            }
            filter {
                includeGroupByRegex("com\\.rokid(\\..+)?")
            }
        }
    }
}

// your app module's build.gradle.kts
dependencies {
    implementation("io.github.hkust-spark:xgglass-universal:0.3.0")
}

Meta is intentionally opt-in. Add xgglass-device-meta plus Meta's GitHub Packages repository, and provide a GitHub token with read:packages scope.

# ~/.gradle/gradle.properties
github_user=YOUR_GITHUB_USERNAME
github_token=ghp_xxxxxxxxxxxxx
// settings.gradle.kts
val metaGithubUser = providers.gradleProperty("github_user").orNull
    ?: providers.environmentVariable("GITHUB_ACTOR").orNull
    ?: ""
val metaGithubToken = providers.gradleProperty("github_token").orNull
    ?: providers.environmentVariable("GITHUB_TOKEN").orNull
    ?: ""

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven {
            url = uri("https://maven.pkg.github.com/facebook/meta-wearables-dat-android")
            credentials {
                username = metaGithubUser
                password = metaGithubToken
            }
        }
    }
}

// your app module's build.gradle.kts
dependencies {
    implementation("io.github.hkust-spark:xgglass-device-meta:0.3.0")
}

iOS: Swift Package

Add the SDK package from GitHub. XgGlass provides the core API plus Simulator, Omi, and Even Realities G1 adapters, and XgGlassMeta adds the Meta adapter with Meta DAT 0.8.0, including the iOS microphone path. The XgGlassKit binary downloads automatically from the GitHub Release for the version you depend on.

// Package.swift
dependencies: [
    .package(url: "https://github.com/hkust-spark/xg-glass-sdk", from: "0.3.0")
],
targets: [
    .target(
        name: "YourApp",
        dependencies: [
            .product(name: "XgGlass", package: "xg-glass-sdk"),
            .product(name: "XgGlassMeta", package: "xg-glass-sdk")
        ]
    )
]

Rokid and RayNeo are not available on iOS today. Frame on iOS is sample-only through Flutter add-to-app. See the SDK repository's docs/swift-package.md for the current Swift Package details.

CLI: PyPI

Use the CLI for generated Android projects and source-based workflows, especially when you need the current Frame integration.

pip install xg-glass
xg-glass --help

Run xg-glass doctor to check the Python, JDK, Android SDK, adb, emulator, Flutter, SDK cache, and network setup that the CLI will use.

Inside an already-generated project that contains xg-glass.yaml, the PyPI CLI can build, install, and run without an SDK checkout:

xg-glass build
xg-glass install
xg-glass run

Creating a project or quick-running a single Kotlin file needs SDK source/templates. By default the PyPI CLI downloads the matching tagged SDK archive into ~/.xg-glass/sdk/ on first use; pass --sdk to force a local checkout instead:

git clone https://github.com/hkust-spark/xg-glass-sdk
xg-glass init ./myapp --sdk /path/to/xg-glass-sdk
xg-glass run /path/to/MyEntry.kt --sdk /path/to/xg-glass-sdk

The CLI also manages missing Java, Android SDK, and Frame/Flutter prerequisites where possible. Managed Android command-line tools downloads are checked against pinned SHA-256 hashes before extraction.

Device support in 0.3.0

Device familyAndroidiOSNotes
RokidYesNoAndroid phone adapter.
RayNeoYesNoUses the xg.glass on-glasses runtime and RayNeo vendor AARs.
FrameSource/CLISample-onlyFlutter add-to-app path; not part of the lean Maven aggregate.
MetaOpt-inYesAndroid uses xgglass-device-meta; iOS includes microphone capture in XgGlassMeta.
OmiYesYesBLE adapter.
Even Realities G1YesYesAdapter shipped; hardware validation pending.
INMO Air3YesNoOn-glasses Android runtime adapter shipped; hardware validation pending.
SimulatorYesYesDevelopment/testing adapter.

Generated apps default to the all-device demo path. For smaller production projects, choose the adapters up front:

xg-glass init ./myapp --devices even,simulator
xg-glass init ./myapp --devices rokid,rayneo
xg-glass init ./myapp --devices frame,simulator

Valid values are rokid, rayneo, meta, frame, omi, even, inmo, simulator, and all; --sim adds simulator to concrete selections. Quick mode supports the same filter only when a .kt file is provided:

xg-glass run /path/to/MyEntry.kt --devices even,simulator

Workflows (CLI)

Start with quick mode, then graduate to project mode when you want a stable workspace.

Quick mode: run a single Kotlin file

Let the CLI generate a temporary project and run it. The PyPI CLI resolves the matching SDK automatically; pass --sdk when you want to force a local checkout:

xg-glass run /path/to/MyEntry.kt
xg-glass run /path/to/MyEntry.kt --devices even,simulator
xg-glass run /path/to/MyEntry.kt --sdk /path/to/xg-glass-sdk

From a repository checkout, you can use the repo-local launcher:

/path/to/xg-glass-sdk/xg-glass run /path/to/MyEntry.kt

Your entry file must follow a small format contract (documented in the CLI Reference).

Project mode: xg-glass init

Generate a minimal Android project and iterate:

xg-glass init ./myapp --devices even,simulator
cd ./myapp
xg-glass build
xg-glass install
xg-glass run

Add --sdk /path/to/xg-glass-sdk to init when you want to force a local checkout instead of the managed SDK cache.

From a repository checkout, the local launcher can infer the SDK path:

/path/to/xg-glass-sdk/xg-glass init ./myapp
cd ./myapp
xg-glass build
xg-glass install
xg-glass run

Your app logic lives in:

  • xgglass_app_logic/src/main/java/.../ExampleAppEntry.kt

App logic

When you write your app logic (ExampleAppEntry.kt), you mainly use:

  • Capture a photo: capturePhoto(...)
  • Display text: display(text, ...)
  • Stream video frames: call startVideoStream(...) when capabilities.canStreamVideo is true; collect VideoStreamSession.frames. The stream buffer keeps latency low with drop-oldest behavior, and droppedFrameCount reports evicted frames.
  • Display an image: call displayImage(DisplayImage(bytes, ImageEncoding.PNG), ...) or ImageEncoding.JPEG when capabilities.canDisplayImages is true.
  • Record audio: startMicrophone(...)
  • Observe device input and battery: collect events for GlassesEvent.Tap(count), GlassesEvent.LongPress when capabilities.supportsLongPressEvents is true, and GlassesEvent.BatteryLevel(percent) when capabilities.supportsBatteryEvents is true.
  • Handle Omi buttons: Omi tap capability is enabled only after button-service discovery; legacy firmware may still emit GlassesEvent.LongPress while supportsLongPressEvents remains false.
  • Watch event backpressure: BaseGlassesClient.droppedEventCount reports rejected event emissions for adapters that use suspending event buffers.

The host app (generated/provided by the SDK template) owns the connection lifecycle and UI.

if (client.capabilities.canStreamVideo) {
    val session = client.startVideoStream().getOrThrow()
    try {
        session.frames.collect { frame ->
            if (!frame.endOfStream) {
                // frame.bytes contains the encoded frame payload.
            }
        }
    } finally {
        session.stop()
    }
    val dropped = session.droppedFrameCount
}

During an active video stream, adapters that share the stream source can satisfy capturePhoto() from the latest streamed frame instead of opening a second camera path:

val stream = client.startVideoStream().getOrThrow()
val still = client.capturePhoto().getOrThrow()
stream.stop()
if (client.capabilities.canDisplayImages) {
    client.displayImage(
        DisplayImage(
            bytes = pngOrJpegBytes,
            encoding = ImageEncoding.PNG
        )
    ).getOrThrow()
}
client.events.collect { event ->
    when (event) {
        is GlassesEvent.BatteryLevel -> updateBattery(event.percent)
        else -> Unit
    }
}

See the detailed API surface and code examples in API Reference. The generated API reference at https://hkust-spark.github.io/xg-glass-sdk/ is generated from KDoc on every release and stays in sync with the code.