Docs / Nosmai Moderation / Platform guides / Android

Android

Use Nosmai Moderation in a native Android (Kotlin) app: image, video, text and live-camera moderation.

Install

  1. Download the latest nosmai-detection.aar from the releases page.
  2. Put it in your app module’s libs/ folder (for example app/libs/nosmai-detection.aar).
  3. Reference it in Gradle (below). The SDK is self-contained: everything runs on-device and all models are bundled, so there are no extra dependencies to add.
android {
    defaultConfig {
        minSdk = 24
        ndk { abiFilters += "arm64-v8a" }   // the SDK ships arm64-v8a
    }
}

dependencies {
    implementation(files("libs/nosmai-detection.aar"))
    // CameraX, only needed for the live-camera path
    val camerax = "1.4.2"
    implementation("androidx.camera:camera-core:$camerax")
    implementation("androidx.camera:camera-camera2:$camerax")
    implementation("androidx.camera:camera-lifecycle:$camerax")
    implementation("androidx.camera:camera-view:$camerax")
}

Permissions (AndroidManifest.xml): INTERNET for the license check and CAMERA for the live path only.

Initialize

init is blocking (network plus model load), so call it off the main thread. It returns false if the license is invalid or expired.

import com.nosmai.detection.NosmaiSDK

Executors.newSingleThreadExecutor().execute {
    val ok = NosmaiSDK.init(context, "NOSMAI-XXXX")
}

Moderate an image

val bitmap = BitmapFactory.decodeFile(path)
val r = NosmaiSDK.analyzeImage(bitmap)          // NosmaiResult
if (r.isUnsafe) {
    r.detections.forEach { Log.d("Mod", "${it.category} ${it.confidence}") }
    // r.nsfw -> SAFE / WARN / BLOCK
}

Moderate a video

NosmaiSDK.analyzeVideo(
    context, uri, frameIntervalMs = 500L,
    onProgress = { p -> /* 0..1 */ },
    onComplete = { v -> /* v.isUnsafe, v.categories, v.flags */ },
)

Moderate text

Call init first, since it validates the license. initText needs that before it can load the text model.

NosmaiSDK.initText(context)                      // after init(), off the main thread
val t = NosmaiSDK.moderateText("some message")   // NosmaiTextResult
if (t.blocked) Log.d("Mod", "${t.category} via ${t.layer} (${t.matchedWord})")

Live camera (CameraX)

Feed CameraX frames to pushFrame. Results arrive on the main thread via NosmaiListener.

NosmaiSDK.startStream(object : NosmaiListener {
    override fun onResult(r: NosmaiResult) { /* every frame */ }
    override fun onUnsafe(r: NosmaiResult) { /* turned unsafe */ }
    override fun onSafe() { /* recovered */ }
})

val analysis = ImageAnalysis.Builder()
    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
    .setOutputImageRotationEnabled(true)
    .build()
    .also { it.setAnalyzer(executor) { proxy -> NosmaiSDK.pushFrame(proxy) } }

cameraProvider.bindToLifecycle(owner, CameraSelector.DEFAULT_BACK_CAMERA, preview, analysis)

// on leave
NosmaiSDK.stopStream()

Thresholds and performance

NosmaiSDK.setThreshold(NosmaiCategory.WEAPON, 0.7f)
NosmaiSDK.setNsfwThreshold(NosmaiNsfwClass.EXPLICIT, 0.45f) // BLOCK
NosmaiSDK.setNsfwThreshold(NosmaiNsfwClass.SEXY, 0.55f)     // WARN

// Live only: how often the heavier object detector runs. NSFW always runs
// every frame. HIGH is snappiest, LOW saves battery.
NosmaiSDK.setPerformanceMode(NosmaiPerformanceMode.HIGH)

Cleanup

NosmaiSDK.shutdown()

NOTE

All NosmaiListener callbacks are delivered on the main thread, so you can update UI directly.

Nosmai

We make advanced camera and AI technology accessible to every developer. By packaging hard problems into simple

developers
legal
newsletter

Product updates and release notes. No spam.

© 2026 nosmai, inc · all rights reserved