Native Core
The com.videntrix.geofence.core module is a Kotlin Android library with React Native dependencies explicitly excluded.
Integrate encrypted geofence checks, continuous verification, persisted fingerprints, and native device signals directly in a Kotlin application.
GeoFenceClient.getInstance(context) is the high-level native entry point. It owns configuration, encryption, pings, continuous checks, and fingerprint submission while retaining only the application context.
The com.videntrix.geofence.core module is a Kotlin Android library with React Native dependencies explicitly excluded.
Obtain the singleton with an Android Context, start it before calling ping or training methods, and stop ongoing work when the protected flow ends.
The Android Core SDK is not currently distributed as a standalone public Maven artifact. There are no public Maven coordinates or repository installation commands to use.
The current source module is configured with minSdk 23, compileSdk 34, targetSdk 34, Java 17, and Kotlin JVM target 17. These facts describe the source module, not a public release.
After Kade supplies the native package, add it according to the delivery-specific instructions. Do not invent a remote dependency coordinate or include the React Native bridge in a native-only app.
android {
compileSdk 34
defaultConfig {
minSdk 23
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
}When Kade supplies Core as source, its module declares coroutines, AndroidX Core, Security Crypto, Lifecycle Process, Google Play services Location and Block Store, Play Integrity, and OkHttp dependencies. Follow the supplied packaging instructions to determine whether your host must declare any of them directly.
Obtain the singleton, start it asynchronously, and issue a ping only after the startup callback succeeds.
import com.videntrix.geofence.core.GeoFenceClient
import com.videntrix.geofence.core.GeoFenceConfig
import com.videntrix.geofence.core.PingOptions
val client = GeoFenceClient.getInstance(applicationContext)
val config = GeoFenceConfig(
apiToken = "your-api-token",
regionId = 123,
)
client.start(config) { startResult ->
startResult.fold(
onSuccess = {
client.ping(
options = PingOptions(
userId = "player-123",
source = "login",
email = "player@example.com",
),
) { pingResult ->
pingResult.fold(
onSuccess = { response ->
if (response.blocked == true) {
println("Blocked: ${response.code ?: "unknown"}")
} else {
println("Inside: ${response.inside}")
println("Area: ${response.area?.name ?: "none"}")
}
},
onFailure = { error ->
println("Kade ping failed: $error")
},
)
}
},
onFailure = { error ->
println("Kade startup failed: $error")
},
)
}start initializes on the SDK's background coroutine scope, hydrates persisted client identity, derives the encryption key, and then invokes (Result<Unit>) -> Unit. Do not treat the client as ready before a successful callback.Core declares network and foreground location permissions. Android still requires the host app to request dangerous location permissions at runtime.
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>private val locationPermission =
registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions(),
) { grants ->
val precise =
grants[Manifest.permission.ACCESS_FINE_LOCATION] == true
if (precise) {
beginVerification()
} else {
showPreciseLocationRequired()
}
}
fun requestKadeLocation() {
locationPermission.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
),
)
}If your Kade policy permits a server-side check without device location, set skipLocation = true in PingOptions. Confirm that policy with Kade; this is not a network-estimate API.
These signatures are from the native Kotlin Core module, not its React Native bridge.
skipLocation is true.visitorId.val fingerprint = client.currentFingerprint()
println("Visitor ID: ${fingerprint.visitorId}")
client.sendFingerprintForTraining("player-123") { result ->
result.fold(
onSuccess = { json -> println("Server response: $json") },
onFailure = { error -> println("Submission failed: $error") },
)
}| Field | Type and default | Behavior |
|---|---|---|
apiToken | String, required | Workspace API token. |
serverUrl | String, https://api.videntrix.com | API base URL. |
regionId | Int?, null | Default region; ping-level value overrides it. |
signingSecret | String?, null | Enables request signing when non-empty. |
locationStrategy | LocationStrategy, RACE | Location retrieval strategy. |
locationMaxAgeMs | Long, 120,000 | Maximum preferred cached-fix age. |
locationMaxStalenessRadius | Double, 100.0 | Staleness radius used by location selection. |
locationAccuracy | LocationAccuracy, HIGH | HIGH, BALANCED, or LOW. |
debug | Boolean, false | Enables verbose SDK logging. |
val options = PingOptions(
userId = "player-123",
silent = false,
skipLocation = false,
source = "deposit",
regionId = 123,
email = "player@example.com",
firstName = "Player",
lastName = "One",
)Every option has a default. source is a free-form flow tag, regionId overrides configuration for that ping, and identity fields are associated with the server-side client record.
Register handlers before starting continuous checks and stop them when the protected flow ends. Core includes AndroidX process lifecycle support for its continuous controller.
client.onPingResponse = { response ->
runOnUiThread {
updateAccessState(response)
}
}
client.onPingError = { error ->
runOnUiThread {
showVerificationError(error)
}
}
try {
client.startContinuousPing(
PingOptions(
userId = "player-123",
source = "session",
),
)
} catch (error: GeoFenceSdkException) {
handleLocationSetupError(error.errorCode)
}
// When the verified flow ends:
client.stopContinuousPing()
// When disposing the entire integration:
client.destroy()startContinuousPing returns before doing anything if startup has not completed.destroy() leaves the singleton available for a later successful start.Branch on blocked first, tolerate nullable fields, and keep failed Result values separate from server verdicts.
fun handle(response: PingResponse) {
if (response.blocked == true) {
denyAccess(
code = response.code,
message = response.message,
)
return
}
if (response.inside) {
allowAccess(response.area?.name)
} else {
showOutsideRegion(response.distanceToBorder)
}
if (response.requestLocation == true) {
// Revisit permission and location services before the next check.
}
}| Field | Type | Meaning |
|---|---|---|
inside | Boolean | Whether the server resolved the client inside the relevant geofence. |
status | Int | Server response status value. |
message, code | String? | Optional response detail and machine-readable code. |
interval | Int | Server-provided interval; model default is 10,000. |
distanceToBorder | Double | Distance value returned by the server. |
regionId | Int? | Resolved region when present. |
area | PingArea? | Matched area with id and name. |
blocked | Boolean? | Explicit block decision when supplied. |
requestLocation | Boolean? | Whether the response asks the client to obtain location. |
deviceTrustTtl | Int? | Optional device-trust lifetime supplied by the server. |
fun handleLocationSetupError(code: String) {
when (code) {
GeoFenceErrorCodes.LOCATION_PERMISSION_MISSING ->
requestLocationPermission()
GeoFenceErrorCodes.LOCATION_PERMISSION_PRECISE_REQUIRED ->
explainPreciseLocation()
GeoFenceErrorCodes.LOCATION_SERVICES_DISABLED ->
promptToEnableLocationServices()
else ->
showVerificationUnavailable()
}
}Talk to Kade for SDK access, approved packaging, credentials, and integration guidance for your application.