Android
Use Nosmai Moderation in a native Android (Kotlin) app: image, video, text and live-camera moderation.
Install
- Download the latest
nosmai-detection.aarfrom the releases page. - Put it in your app module’s
libs/folder (for exampleapp/libs/nosmai-detection.aar). - 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.