Docs/Getting Started/Android Quick Start

Android Quick Start

From zero to a YES verdict on an Android device in ten minutes.

Note

Work through Prerequisites first. You will need a license key and a plan for runtime permissions.


1. Add the Maven repo

In your project's root settings.gradle.kts:

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven {
            url = uri("https://raw.githubusercontent.com/octetproof/octet-sdk-android/mvn-repo")
        }
    }
}

The OctetSDK Maven artifacts live on the mvn-repo orphan branch of the public octet-sdk-android repository.


2. Add the dependency

In your app build.gradle.kts:

dependencies {
    implementation("com.octetproof:sdk:1.1.0")
}

Sync Gradle. The SDK's AndroidManifest.xml permissions merge into your manifest automatically. No manual permission declarations needed.


3. Request runtime permissions

The SDK refuses to start without ACCESS_FINE_LOCATION. Motion-classification confidence degrades without ACTIVITY_RECOGNITION. Request both before calling Octet.start(...):

ActivityCompat.requestPermissions(
    this,
    arrayOf(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACTIVITY_RECOGNITION,
    ),
    REQUEST_CODE
)

Wait for onRequestPermissionsResult(...) to confirm ACCESS_FINE_LOCATION was granted before continuing.


4. Start the SDK

Don't have a key yet?

licenseKey is a placeholder. Get a free 90-day trial key (no credit card) at sdk.octetproof.com/signup, then paste it in.

import com.octetproof.sdk.api.Octet
import com.octetproof.sdk.api.OctetConfig

lifecycleScope.launch {
    val sdk = Octet.start(
        context = applicationContext,
        config = OctetConfig(licenseKey = "octet_live_v4.public.…")
    )
    // sdk is ready
}

Octet.start(...) is a suspend function. Call it from a coroutine scope (lifecycleScope, viewModelScope, or your own). On first launch the SDK verifies the license key locally, exchanges it for an activation token via api.octetproof.com/v1/activate, caches the token in EncryptedSharedPreferences, and brings up the proof pipeline.

Any license problem throws a typed LicenseError. See License Types.


5. Ask your first question

import com.octetproof.sdk.api.OctetRegion
import com.octetproof.sdk.api.OctetVerdict
import java.time.Instant

val verdict = sdk.loc.isWithin(
    region = OctetRegion.country("US"),
    atTime = Instant.now()
)

when (verdict.result) {
    OctetVerdict.Result.YES ->
        println("YES, proof attached: ${verdict.proof != null}")
    OctetVerdict.Result.NO ->
        println("NO, provable negative")
    OctetVerdict.Result.INDETERMINATE ->
        println("INDETERMINATE, reason: ${verdict.reason}")
}

The predicate returns an OctetVerdict. Never treat INDETERMINATE as NO.


6. What to expect

  • On a real device, outdoors, with cellular and GPS available, isWithin(country("US")) typically returns YES with an attached proof.
  • On the Android emulator the verdict will always be INDETERMINATE / NO_FIX with the message running on emulator — location proofs are unavailable in this environment. This is by design. The emulator's mock-location flag blocks proof generation. Run on hardware to see the full flow.
  • On a real device, indoors, the first proof on Android usually arrives quickly via the cell-tower MCC signal even without GPS. isWithin(country(...)) typically succeeds with HIGH confidence indoors on Android. Finer-grained predicates (city, polygon) may need a GPS fix. See Concepts: Verdicts.

7. From here